/*
	Copyright 2014 Michele "King_DuckZ" Santullo

	This file is part of CloonelJump.

	CloonelJump is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	CloonelJump is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with CloonelJump.  If not, see <http://www.gnu.org/licenses/>.
*/

namespace cloonel {
	///-------------------------------------------------------------------------
	///-------------------------------------------------------------------------
	template <typename T, uint32_t S>
	Vector<T, S>::Vector (T parValue) noexcept(noexcept(T()) && noexcept(parValue=parValue)) {
		std::fill(m_mem, m_mem + S, parValue);
	}

	///-------------------------------------------------------------------------
	///-------------------------------------------------------------------------
	template <typename T, uint32_t S>
	template <typename U>
	Vector<T, S>::Vector (const Vector<U, S>& parOther) noexcept(noexcept(T()) && noexcept(const_cast<U&>(parOther.m_mem[0])=T())) {
		std::copy(parOther.m_mem, parOther.m_mem + S, m_mem);
	}

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
	///-------------------------------------------------------------------------
	///-------------------------------------------------------------------------
	template <typename T, uint32_t S>
	template <typename U>
	const Vector<T, S>& Vector<T, S>::operator+= (const Vector<U, S>& parOther) {
		for (uint32_t z = 0; z < S; ++z) {
			m_mem[z] += parOther.m_mem[z];
		}
		return *this;
	}
	template <typename T, uint32_t S>
	template <typename U>
	const Vector<T, S>& Vector<T, S>::operator-= (const Vector<U, S>& parOther) {
		for (uint32_t z = 0; z < S; ++z) {
			m_mem[z] -= parOther.m_mem[z];
		}
		return *this;
	}
	template <typename T, uint32_t S>
	template <typename U>
	const Vector<T, S>& Vector<T, S>::operator*= (const Vector<U, S>& parOther) {
		for (uint32_t z = 0; z < S; ++z) {
			m_mem[z] *= parOther.m_mem[z];
		}
		return *this;
	}
	template <typename T, uint32_t S>
	template <typename U>
	const Vector<T, S>& Vector<T, S>::operator/= (const Vector<U, S>& parOther) {
		for (uint32_t z = 0; z < S; ++z) {
			m_mem[z] /= parOther.m_mem[z];
		}
		return *this;
	}
#pragma GCC diagnostic pop

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
	///-------------------------------------------------------------------------
	///-------------------------------------------------------------------------
	template <typename T, uint32_t S>
	template <typename U>
	const Vector<T, S>& Vector<T, S>::operator+= (U parOther) {
		for (uint32_t z = 0; z < S; ++z) {
			m_mem[z] += parOther;
		}
		return *this;
	}
	template <typename T, uint32_t S>
	template <typename U>
	const Vector<T, S>& Vector<T, S>::operator-= (U parOther) {
		for (uint32_t z = 0; z < S; ++z) {
			m_mem[z] -= parOther;
		}
		return *this;
	}
	template <typename T, uint32_t S>
	template <typename U>
	const Vector<T, S>& Vector<T, S>::operator*= (U parOther) {
		for (uint32_t z = 0; z < S; ++z) {
			m_mem[z] *= parOther;
		}
		return *this;
	}
	template <typename T, uint32_t S>
	template <typename U>
	const Vector<T, S>& Vector<T, S>::operator/= (U parOther) {
		for (uint32_t z = 0; z < S; ++z) {
			m_mem[z] /= parOther;
		}
		return *this;
	}
#pragma GCC diagnostic pop

	///-------------------------------------------------------------------------
	///-------------------------------------------------------------------------
	template <typename T, typename U, uint32_t S>
	inline
	Vector<typename std::common_type<T, U>::type, S> operator+ (Vector<T, S> parA, const Vector<U, S>& parB) {
		parA += parB;
		return parA;
	}
	template <typename T, typename U, uint32_t S>
	inline
	Vector<typename std::common_type<T, U>::type, S> operator- (Vector<T, S> parA, const Vector<U, S>& parB) {
		parA -= parB;
		return parA;
	}
	template <typename T, typename U, uint32_t S>
	inline
	Vector<typename std::common_type<T, U>::type, S> operator* (Vector<T, S> parA, const Vector<U, S>& parB) {
		parA *= parB;
		return parA;
	}
	template <typename T, typename U, uint32_t S>
	inline
	Vector<typename std::common_type<T, U>::type, S> operator/ (Vector<T, S> parA, const Vector<U, S>& parB) {
		parA /= parB;
		return parA;
	}

	///-------------------------------------------------------------------------
	///-------------------------------------------------------------------------
	template <typename T, typename U, uint32_t S, typename>
	Vector<typename std::common_type<T, U>::type, S> operator+ (T parA, const Vector<U, S>& parB) {
		return parB + parA;
	}
	template <typename T, typename U, uint32_t S, typename>
	Vector<typename std::common_type<T, U>::type, S> operator- (T parA, const Vector<U, S>& parB) {
		return Vector<T, S>(parA) - parB;
	}
	template <typename T, typename U, uint32_t S, typename>
	Vector<typename std::common_type<T, U>::type, S> operator* (T parA, const Vector<U, S>& parB) {
		return parB * parA;
	}
	template <typename T, typename U, uint32_t S, typename>
	Vector<typename std::common_type<T, U>::type, S> operator/ (T parA, const Vector<U, S>& parB) {
		return Vector<T, S>(parA) / parB;
	}
	template <typename T, typename U, uint32_t S, typename>
	Vector<typename std::common_type<T, U>::type, S> operator+ (const Vector<T, S>& parA, U parB) {
		typedef typename std::common_type<T, U>::type RetType;
		Vector<RetType, S> retVal;
		for (uint32_t z = 0; z < S; ++z) {
			retVal[z] = parA[z] + parB;
		}
		return retVal;
	}
	template <typename T, typename U, uint32_t S, typename>
	Vector<typename std::common_type<T, U>::type, S> operator- (const Vector<T, S>& parA, U parB) {
		typedef typename std::common_type<T, U>::type RetType;
		Vector<RetType, S> retVal;
		for (uint32_t z = 0; z < S; ++z) {
			retVal[z] = parA[z] - parB;
		}
		return retVal;
	}
	template <typename T, typename U, uint32_t S, typename>
	Vector<typename std::common_type<T, U>::type, S> operator* (const Vector<T, S>& parA, U parB) {
		typedef typename std::common_type<T, U>::type RetType;
		Vector<RetType, S> retVal;
		for (uint32_t z = 0; z < S; ++z) {
			retVal[z] = parA[z] * parB;
		}
		return retVal;
	}
	template <typename T, typename U, uint32_t S, typename>
	Vector<typename std::common_type<T, U>::type, S> operator/ (const Vector<T, S>& parA, U parB) {
		typedef typename std::common_type<T, U>::type RetType;
		Vector<RetType, S> retVal;
		for (uint32_t z = 0; z < S; ++z) {
			retVal[z] = parA[z] / parB;
		}
		return retVal;
	}

	///--------------------------------------------------------------------------
	///--------------------------------------------------------------------------
	template <typename T, typename U, uint32_t S>
	inline bool operator< (const Vector<T, S>& parA, const Vector<U, S>& parB) {
		bool retVal = true;
		for (uint32_t z = 0; z < S; ++z) {
			retVal &= static_cast<bool>(parA[z] < parB[z]);
		}
		return retVal;
	}
	template <typename T, typename U, uint32_t S>
	inline bool operator> (const Vector<T, S>& parA, const Vector<U, S>& parB) {
		bool retVal = true;
		for (uint32_t z = 0; z < S; ++z) {
			retVal &= static_cast<bool>(parA[z] > parB[z]);
		}
		return retVal;
	}
	template <typename T, typename U, uint32_t S>
	inline bool operator<= (const Vector<T, S>& parA, const Vector<U, S>& parB) {
		bool retVal = true;
		for (uint32_t z = 0; z < S; ++z) {
			retVal &= static_cast<bool>(parA[z] <= parB[z]);
		}
		return retVal;
	}
	template <typename T, typename U, uint32_t S>
	inline bool operator>= (const Vector<T, S>& parA, const Vector<U, S>& parB) {
		bool retVal = true;
		for (uint32_t z = 0; z < S; ++z) {
			retVal &= static_cast<bool>(parA[z] >= parB[z]);
		}
		return retVal;
	}
	template <typename T, typename U, uint32_t S>
	inline bool operator== (const Vector<T, S>& parA, const Vector<U, S>& parB) {
		bool retVal = true;
		for (uint32_t z = 0; z < S; ++z) {
			retVal &= static_cast<bool>(parA[z] == parB[z]);
		}
		return retVal;
	}
	template <typename T, typename U, uint32_t S>
	inline bool operator!= (const Vector<T, S>& parA, const Vector<U, S>& parB) {
		bool retVal = true;
		for (uint32_t z = 0; z < S; ++z) {
			retVal &= static_cast<bool>(parA[z] != parB[z]);
		}
		return retVal;
	}

	template <typename T, uint32_t S>
	Vector<T, S> operator- (Vector<T, S> parOperand) {
		for (uint32_t z = 0; z < S; ++z) {
			parOperand[z] = -parOperand[z];
		}
		return parOperand;
	}
} //namespace cloonel
