/* +------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)            |
   |                          https://www.mrpt.org/                         |
   |                                                                        |
   | Copyright (c) 2005-2022, 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
