/* +------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)            |
   |                          https://www.mrpt.org/                         |
   |                                                                        |
   | Copyright (c) 2005-2024, Individual contributors, see AUTHORS file     |
   | See: https://www.mrpt.org/Authors - All rights reserved.               |
   | Released under BSD License. See: https://www.mrpt.org/License          |
   +------------------------------------------------------------------------+ */
#pragma once

#include <mrpt/math/CMatrixDynamic.h>
#include <mrpt/math/CVectorFixed.h>
#include <mrpt/serialization/CArchive.h>

#include <iomanip>   // for setprecision(), etc.
#include <iterator>  // std::ostream_iterator

// Many of the functions originally in this file are now in ops_containers.h
#include <mrpt/math/ops_containers.h>

namespace mrpt::math
{
/** \addtogroup container_ops_grp
 *  @{ */

/** \name Generic std::vector element-wise operations
 * @{
 */

/** a*=b (element-wise multiplication) */
template <typename T1, typename T2>
inline std::vector<T1>& operator*=(std::vector<T1>& a, const std::vector<T2>& b)
{
  ASSERT_EQUAL_(a.size(), b.size());
  const size_t N = a.size();
  for (size_t i = 0; i < N; i++) a[i] *= b[i];
  return a;
}

/** a*=k (multiplication by a constant) */
template <typename T1>
inline std::vector<T1>& operator*=(std::vector<T1>& a, const T1 b)
{
  const size_t N = a.size();
  for (size_t i = 0; i < N; i++) a[i] *= b;
  return a;
}

/** a*b (element-wise multiplication) */
template <typename T1, typename T2>
inline std::vector<T1> operator*(const std::vector<T1>& a, const std::vector<T2>& b)
{
  ASSERT_EQUAL_(a.size(), b.size());
  const size_t N = a.size();
  std::vector<T1> ret(N);
  for (size_t i = 0; i < N; i++) ret[i] = a[i] * b[i];
  return ret;
}

/** a+=b (element-wise sum) */
template <typename T1, typename T2>
inline std::vector<T1>& operator+=(std::vector<T1>& a, const std::vector<T2>& b)
{
  ASSERT_EQUAL_(a.size(), b.size());
  const size_t N = a.size();
  for (size_t i = 0; i < N; i++) a[i] += b[i];
  return a;
}

/** a+=b (sum a constant) */
template <typename T1>
inline std::vector<T1>& operator+=(std::vector<T1>& a, const T1 b)
{
  const size_t N = a.size();
  for (size_t i = 0; i < N; i++) a[i] += b;
  return a;
}

/** a+b (element-wise sum) */
template <typename T1, typename T2>
inline std::vector<T1> operator+(const std::vector<T1>& a, const std::vector<T2>& b)
{
  ASSERT_EQUAL_(a.size(), b.size());
  const size_t N = a.size();
  std::vector<T1> ret(N);
  for (size_t i = 0; i < N; i++) ret[i] = a[i] + b[i];
  return ret;
}

template <typename T1, typename T2>
inline std::vector<T1> operator-(const std::vector<T1>& v1, const std::vector<T2>& v2)
{
  ASSERT_EQUAL_(v1.size(), v2.size());
  std::vector<T1> res(v1.size());
  for (size_t i = 0; i < v1.size(); i++) res[i] = v1[i] - v2[i];
  return res;
}

/** @} */

/** A template function for printing out the contents of a std::vector variable.
 */
template <class T>
std::ostream& operator<<(std::ostream& out, const std::vector<T>& d)
{
  const std::streamsize old_pre = out.precision();
  const std::ios_base::fmtflags old_flags = out.flags();
  out << "[" << std::fixed << std::setprecision(4);
  std::copy(d.begin(), d.end(), std::ostream_iterator<T>(out, " "));
  out << "]";
  out.flags(old_flags);
  out.precision(old_pre);
  return out;
}

/** A template function for printing out the contents of a std::vector variable.
 */
template <class T>
std::ostream& operator<<(std::ostream& out, std::vector<T>* d)
{
  const std::streamsize old_pre = out.precision();
  const std::ios_base::fmtflags old_flags = out.flags();
  out << "[" << std::fixed << std::setprecision(4);
  copy(d->begin(), d->end(), std::ostream_iterator<T>(out, " "));
  out << "]";
  out.flags(old_flags);
  out.precision(old_pre);
  return out;
}

/** Binary dump of a CVectorFixed<T,N> to a stream. */
template <typename T, size_t N>
mrpt::serialization::CArchive& operator<<(
    mrpt::serialization::CArchive& ostrm, const CVectorFixed<T, N>& a)
{
  ostrm << mrpt::typemeta::TTypeName<CVectorFixed<T, N>>::get();
  if (N) ostrm.WriteBufferFixEndianness<T>(&a[0], N);
  return ostrm;
}

/** Binary read of a CVectorFixed<T,N> from a stream. */
template <typename T, size_t N>
mrpt::serialization::CArchive& operator>>(
    mrpt::serialization::CArchive& istrm, CVectorFixed<T, N>& a)
{
  static const std::string namExpect = mrpt::typemeta::TTypeName<CVectorFixed<T, N>>::get();
  std::string nam;
  istrm >> nam;
  ASSERTMSG_(
      nam == namExpect,
      format("Error deserializing: expected '%s', got '%s'", namExpect.c_str(), nam.c_str()));
  if (N) istrm.ReadBufferFixEndianness<T>(&a[0], N);
  return istrm;
}

/**  @} */  // end of grouping

}  // namespace mrpt::math
