// =====================================================================================================================
//  fennec, a free and open source game engine
//  Copyright © 2025 - 2026  Medusa Slockbower
//
//  This program 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.
//
//  This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================

///
/// \file fennec/math/vector.h
/// \brief the \ref fennec_math_vector
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///

#ifndef FENNEC_MATH_VECTOR_H
#define FENNEC_MATH_VECTOR_H

///
///
///
/// \page fennec_math_vector Vectors
///
/// \brief The fennec Vector Math Module
///
/// \code #include <fennec/math/vector.h> \endcode
///
///
/// \section vector_types Types
///
/// <table width="100%" class="fieldtable" id="table_fennec_math_vector_types">
/// <tr><th>Type       <th>Corresponding Type <th>Brief
/// <tr><th colspan=3 style="text-align: center;">Floats
/// <tr><td>``\emph{vec2}`` <td>\ref fennec::vec2  <td>\copybrief fennec::vec2
/// <tr><td>``\emph{vec3}`` <td>\ref fennec::vec3  <td>\copybrief fennec::vec3
/// <tr><td>``\emph{vec4}`` <td>\ref fennec::vec4  <td>\copybrief fennec::vec4
/// <tr><th colspan=3 style="text-align: center;">Doubles
/// <tr><td>``\emph{dvec2}``<td>\ref fennec::dvec2 <td>\copybrief fennec::dvec2
/// <tr><td>``\emph{dvec3}``<td>\ref fennec::dvec3 <td>\copybrief fennec::dvec3
/// <tr><td>``\emph{dvec4}``<td>\ref fennec::dvec4 <td>\copybrief fennec::dvec4
/// <tr><th colspan=3 style="text-align: center;">Booleans
/// <tr><td>``\emph{bvec2}`` <td>\ref fennec::bvec2 <td>\copybrief fennec::bvec2
/// <tr><td>``\emph{bvec3}`` <td>\ref fennec::bvec3 <td>\copybrief fennec::bvec3
/// <tr><td>``\emph{bvec4}`` <td>\ref fennec::bvec4 <td>\copybrief fennec::bvec4
/// <tr><th colspan=3 style="text-align: center;">Integers
/// <tr><td>``\emph{ivec2}`` <td>\ref fennec::ivec2 <td>\copybrief fennec::ivec2
/// <tr><td>``\emph{ivec3}`` <td>\ref fennec::ivec3 <td>\copybrief fennec::ivec3
/// <tr><td>``\emph{ivec4}`` <td>\ref fennec::ivec4 <td>\copybrief fennec::ivec4
/// <tr><th colspan=3 style="text-align: center;">Unsigned Integers
/// <tr><td>``\emph{uvec2}`` <td>\ref fennec::uvec2 <td>\copybrief fennec::uvec2
/// <tr><td>``\emph{uvec3}`` <td>\ref fennec::uvec3 <td>\copybrief fennec::uvec3
/// <tr><td>``\emph{uvec4}`` <td>\ref fennec::uvec4 <td>\copybrief fennec::uvec4
/// </table>
///
///
///
///  \section vector_components Components
///
///  Vectors are usually made up of one to four components, named \math{x}, \math{y}, \math{z}, and \math{w}.
///  Each component also has aliases for usage in colors \math{rgba}, and texture coordinates \math{stpq}. Accessing a
///  component outside the vector will cause an error at compile time, for example:
///
///  \code{.cpp}
///  vec2 pos;
///  float height;
///  pos.x;      // is legal
///  pos.z;      // is illegal
///  height.x;   // is legal in glsl, but illegal in c++
///  height.y;   // is illegal
///  \endcode
///
///  Components can also be accessed with indices using the array access operator \ref fennec::vector::operator[] "vector::operator[](size_t)".
///
///
///
///  \section vector_swizzling Swizzling
///
///  The fennec \ref fennec_math_vector allows for the "swizzling" of vectors. Each component in the vector can be
///  used in any combination, with up to 4 components, to create another vector. For example, <br><br>
///
///  let  \math{V = (0, 1, 2)}
///  then \math{V.xy = (0, 1)}
///  and  \math{V.zy = (2, 1)}
///
///  \section section_vectors_more More Info
///  - \subpage fennec_math_vector_traits
///
///
///

#include <fennec/containers/initializer_list.h>
#include <fennec/math/detail/_forward.h>

#include <fennec/math/vector_base.h>
#include <fennec/math/vector_traits.h>

#include <fennec/lang/conditional_types.h>
#include <fennec/lang/type_traits.h>
#include <fennec/lang/utility.h>

namespace fennec
{

///
/// \brief Main \ref fennec_math_vector "vector" template
/// \tparam ScalarT The type of the Components
/// \tparam SizeV   The number of Components
template<typename ScalarT, size_t SizeV>
using vec = decltype(detail::_gen_vector<vector, ScalarT>(make_index_metasequence<SizeV>{}));


///
/// \brief Shorthand for creating a 2-element \ref fennec::vector, `(.*?)`
/// \details Shorthand for creating a 2-element \ref fennec::vector, `(.*?)`
/// \tparam ScalarT The type of the Components
template<typename ScalarT>
using tvec2 = vec<ScalarT, 2>;

///
/// \brief Shorthand for creating a 3-element \ref fennec::vector, `(.*?)`
/// \details Shorthand for creating a 3-element \ref fennec::vector, `(.*?)`
/// \tparam ScalarT The type of the Components
template<typename ScalarT>
using tvec3 = vec<ScalarT, 3>;

///
/// \brief Shorthand for creating a 4-element \ref fennec::vector, `(.*?)`
/// \details Shorthand for creating a 4-element \ref fennec::vector, `(.*?)`
/// \tparam ScalarT The type of the Components
template<typename ScalarT>
using tvec4 = vec<ScalarT, 4>;


using bvec2 = tvec2<bool_t>; //!< \brief A two-component boolean \ref fennec_math_vector "vector"
using bvec3 = tvec3<bool_t>; //!< \brief A three-component boolean \ref fennec_math_vector "vector"
using bvec4 = tvec4<bool_t>; //!< \brief A four-component boolean \ref fennec_math_vector "vector"

using ivec2 = tvec2<int32_t>; //!< \brief A two-component signed integer \ref fennec_math_vector "vector"
using ivec3 = tvec3<int32_t>; //!< \brief A three-component signed integer \ref fennec_math_vector "vector"
using ivec4 = tvec4<int32_t>; //!< \brief A four-component signed integer \ref fennec_math_vector "vector"

using uvec2 = tvec2<uint32_t>; //!< \brief A two-component unsigned integer \ref fennec_math_vector "vector"
using uvec3 = tvec3<uint32_t>; //!< \brief A three-component unsigned integer \ref fennec_math_vector "vector"
using uvec4 = tvec4<uint32_t>; //!< \brief A four-component unsigned integer \ref fennec_math_vector "vector"

using vec2 = tvec2<float_t>;
//!< \brief A two-component single-precision floating-point \ref fennec_math_vector "vector"
using vec3 = tvec3<float_t>;
//!< \brief A three-component single-precision floating-point \ref fennec_math_vector "vector"
using vec4 = tvec4<float_t>;
//!< \brief A four-component single-precision floating-point \ref fennec_math_vector "vector"

using dvec2 = tvec2<double_t>;
//!< \brief A two-component double-precision floating-point \ref fennec_math_vector "vector"
using dvec3 = tvec3<double_t>;
//!< \brief A three-component double-precision floating-point \ref fennec_math_vector "vector"
using dvec4 = tvec4<double_t>;
//!< \brief A four-component double-precision floating-point \ref fennec_math_vector "vector"


///
/// \brief Math Vector Type
/// \tparam ScalarT base \ref scalar type of each Component
/// \tparam IndicesV index of each Component
/// \nosubgrouping
template<typename ScalarT, size_t... IndicesV> requires(is_scalar_v<ScalarT>)
struct vector : detail::vector_base_type<ScalarT, sizeof...(IndicesV)>
{
// Assertions ==========================================================================================================

	static_assert(is_arithmetic_v<ScalarT>);


// Definitions & Constants =============================================================================================


	/// \name Constants
	/// @{

	static constexpr size_t dimension = sizeof...(IndicesV);      //!< \brief dimension of the swizzle
	static constexpr size_t num_components = sizeof...(IndicesV); //!< \brief number of components
	static constexpr size_t size = sizeof...(IndicesV);           //!< \brief size of the swizzle
	static constexpr size_t N = sizeof...(IndicesV);              //!< \brief size of the swizzle

	/// @}


	/// \name Definitions
	/// @{

	///
	/// \brief vector base type
	using base_type = detail::vector_base_type<ScalarT, sizeof...(IndicesV)>;

	///
	/// \brief vector data array
	using base_type::data;

	///
	/// \brief alias for ScalarT
	using scalar_t = ScalarT;

	///
	/// \brief alias for this type
	using vector_t = vector;

	using decay_t = conditional_t<N == 1, scalar_t, vector_t>;
	//!< Type that the \ref fennec_math_vector "vector" should Decay into

	/// @}


// Constructors ========================================================================================================

	/// \name Constructors
	/// @{

	///
	/// \brief default constructor, initializes components with 0
	///
	/// \details ** **
	constexpr vector() {
		data = { 0 };
	}


	///
	/// \brief copy constructor
	///
	/// \details
	/// \param x object to copy
	constexpr vector(const vector_t& x) {
		data = x.data;
	}


	///
	/// \brief move constructor
	///
	/// \details
	/// \param x object to move
	constexpr vector(vector_t&& x) noexcept {
		data = fennec::move(x.data);
	}


	///
	/// \brief scalar constructor
	///
	/// \details
	/// \param s scalar value to initialize with
	explicit constexpr vector(scalar_t s) {
		((data[IndicesV] = s), ...);
	}


	///
	/// \brief int conversion scalar
	///
	/// \details
	/// \param s scalar value to initialize with
	explicit constexpr vector(int_t s) requires(not is_same_v<ScalarT, int_t>) {
		if constexpr (N == 1) data[0] = ScalarT(s);
		else ((data[IndicesV] = ScalarT(s)), ...);
	}


	///
	/// \brief double conversion scalar
	///
	/// \details
	/// \param s scalar value to initialize with
	explicit constexpr vector(double_t s) requires(not is_same_v<ScalarT, double_t>) {
		if constexpr (N == 1) data[0] = ScalarT(s);
		else ((data[IndicesV] = ScalarT(s)), ...);
	}


	// This is not in the GLSL spec, I don't remember writing it, and it's behaviour is strange.
	/////
	///// \brief vector scalar conversion constructor
	/////
	///// \details
	///// \tparam OScalarT scalar Type of the \ref fennec_math_vector "vector" to Convert
	///// \param v \ref fennec_math_vector "vector" to Convert
	//template<typename OScalarT>
	//explicit constexpr vector(const vector<OScalarT, IndicesV...>& v)
	//: data() {
	//	((data[IndicesV] = ScalarT(v[IndicesV])), ...);
	//}


	///
	/// \brief vector conversion constructor
	///
	/// \details
	/// \tparam OScalarT scalar Type of the \ref fennec_math_vector "vector" to Convert
	/// \tparam MoreIndicesStartV
	/// \tparam MoreIndicesV
	/// \param v
	template<typename OScalarT, size_t MoreIndicesStartV, size_t... MoreIndicesV>
	explicit constexpr vector(const vector<OScalarT, IndicesV..., MoreIndicesStartV, MoreIndicesV...>& v) {
		((data[IndicesV] = ScalarT(v[IndicesV])), ...);
	}


	///
	/// \brief swizzle conversion constructor
	///
	/// \details
	/// \tparam SwizzleDataT swizzle Data Type
	/// \tparam SwizzleScalarT swizzle scalar Type
	/// \tparam SwizzleIndicesV swizzle Indices
	/// \param swizzle swizzle object
	template<typename SwizzleDataT, typename SwizzleScalarT, size_t... SwizzleIndicesV>
	explicit constexpr vector(const detail::swizzle_storage<SwizzleDataT, SwizzleScalarT, SwizzleIndicesV...>& swizzle) {
		((data[IndicesV] = ScalarT(swizzle[IndicesV])), ...);
	}


	///
	/// \brief piecewise constructor
	///
	/// \details constructs a vector from a series of scalars, swizzles, and vectors
	/// \tparam ArgsT argument types
	/// \param args arguments
	template<typename... ArgsT> requires(total_component_count_v<ArgsT...> == N)
	explicit constexpr vector(ArgsT&&... args) {
		vector::_construct<0>(args...);
	}

	///
	/// \brief initializer list constructor
	/// \param init the initializer list
	constexpr vector(initializer_list<ScalarT> init) {
		size_t i = 0;
		for (ScalarT x : init) {
			data[i++] = x;
		}
	}

	///
	/// \brief initializer list conversion constructor
	/// \param init the initializer list
	template<typename OScalarT>
	constexpr vector(initializer_list<OScalarT> init) {
		size_t i = 0;
		for (OScalarT x : init) {
			data[i++] = ScalarT(x);
		}
	}

	/// @}


// Utility Functions ===================================================================================================

	/// \name Utility Functions
	/// @{

	///
	/// \brief decay implementation
	///
	/// \details
	///	\returns scalar if \math{N==1}, otherwise, \ref fennec_math_vector "vector"
	decay_t decay() {
		return static_cast<const decay_t&>(*this);
	}

	/// @}


// Access operators ====================================================================================================

	/// \name Access operators
	/// @{

	///
	/// \copydetails vector::operator[](size_t) const
	constexpr scalar_t& operator[](size_t i) {
		return data[i];
	}

	///
	/// \brief indexed access operator
	///
	/// \details
	/// \returns a copy of the component
	/// \param i the index of the component
	constexpr scalar_t operator[](size_t i) const {
		return data[i];
	}

	/// @}


// Assignment operators ================================================================================================

	/// \name Assignment operators
	/// @{

	///
	/// \brief copy assignment
	///
	/// \details
	/// \returns A reference to \c this, after having set \p lhs, such that \math{lhs_i=rhs_i}
	/// \param rhs vector to copy
	constexpr vector_t& operator=(const vector_t& rhs) {
		return ((data[IndicesV] = rhs[IndicesV]), ..., *this);
	}

	///
	/// \brief move assignment
	///
	/// \details
	/// \returns A reference to \c this, after having set \p lhs, such that \math{lhs_i=rhs_i}
	/// \param rhs vector to move
	constexpr vector_t& operator=(vector_t&& rhs) noexcept {
		return ((data[IndicesV] = fennec::move(rhs[IndicesV])), ..., *this);
	}

	/// @}


// Comparison Operators ================================================================================================

	/// \name Comparison Operators
	/// @{

	///
	/// \brief vector equality operator
	///
	/// \details
	/// \returns **true** if every respective component is equivalent, otherwise returns **false**
	/// \param rhs vector to compare with
	constexpr bool_t operator==(const vector_t& rhs) const {
		return ((data[IndicesV] == rhs.data[IndicesV]) && ...);
	}

	///
	/// \brief vector inequality operator
	///
	/// \details
	/// \returns **false** if every respective component is equivalent, otherwise returns **true**
	/// \param rhs vector to compare with
	constexpr bool_t operator!=(const vector_t& rhs) const {
		return ((data[IndicesV] != rhs.data[IndicesV]) || ...);
	}

	/// @}


// Scalar-Vector Arithmetic operators ==================================================================================

	/// \name \ref fennec_math_scalar "scalar" - \ref fennec_math_vector "vector" Arithmetic operators
	/// @{

	///
	/// \brief \ref fennec_math_scalar "scalar" - \ref fennec_math_vector "vector" addition operator
	///
	/// \details
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i+rhs_i}
	/// \param lhs left hand side
	/// \param rhs right hand side
	constexpr friend vector_t operator+(scalar_t lhs, const vector_t& rhs) {
		return vector_t((lhs + rhs[IndicesV])...);
	}

	///
	/// \brief \ref fennec_math_scalar "scalar" - \ref fennec_math_vector "vector" subtraction operator
	///
	/// \details
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i-rhs_i}
	/// \param lhs left hand side
	/// \param rhs right hand side
	constexpr friend vector_t operator-(scalar_t lhs, const vector_t& rhs) {
		return vector_t((lhs - rhs[IndicesV])...);
	}

	///
	/// \brief \ref fennec_math_scalar "scalar" - \ref fennec_math_vector "vector" multiplication operator
	///
	/// \details
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i={lhs_i}\cdot{rhs_i}}
	/// \param lhs left hand side
	/// \param rhs right hand side
	constexpr friend vector_t operator*(scalar_t lhs, const vector_t& rhs) {
		return vector_t((lhs * rhs[IndicesV])...);
	}

	///
	/// \brief \ref fennec_math_scalar "scalar" - \ref fennec_math_vector "vector" division operator
	///
	/// \details
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=\frac{lhs_i}{rhs_i}}
	/// \param lhs left hand side
	/// \param rhs right hand side
	constexpr friend vector_t operator/(scalar_t lhs, const vector_t& rhs) {
		return vector_t((lhs / rhs[IndicesV])...);
	}

	///
	/// \brief \ref fennec_math_scalar "scalar" - \ref fennec_math_vector "vector" integer modulus operator
	///
	/// \details
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i\%rhs_i}
	/// \param lhs left hand side
	/// \param rhs right hand side
	constexpr friend vector_t operator%(scalar_t lhs, const vector_t& rhs) requires(is_integral_v<scalar_t>) {
		return vector_t((lhs % rhs[IndicesV])...);
	}

	/// @}

// Vector-Scalar Arithmetic operators ==================================================================================

	/// \name \ref fennec_math_vector "vector" - \ref fennec_math_scalar "scalar" Arithmetic operators
	/// @{

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_scalar "scalar" addition operator
	///
	/// \details
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i+rhs_i}
	/// \param lhs left hand side
	/// \param rhs right hand side
	constexpr friend vector_t operator+(const vector_t& lhs, scalar_t rhs) {
		return vector_t((lhs[IndicesV] + rhs)...);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_scalar "scalar" subtraction operator
	///
	/// \details
	/// \param lhs left hand side
	/// \param rhs right hand side
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i-rhs_i}
	constexpr friend vector_t operator-(const vector_t& lhs, scalar_t rhs) {
		return vector_t((lhs[IndicesV] - rhs)...);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_scalar "scalar" multiplication operator
	///
	/// \details
	/// \param lhs left hand side
	/// \param rhs right hand side
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i={lhs_i}\cdot{rhs_i}}
	constexpr friend vector_t operator*(const vector_t& lhs, scalar_t rhs) {
		return vector_t((lhs[IndicesV] * rhs)...);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_scalar "scalar" division operator
	///
	/// \details
	/// \param lhs left hand side
	/// \param rhs right hand side
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=\frac{lhs_i}{rhs_i}}
	constexpr friend vector_t operator/(const vector_t& lhs, scalar_t rhs) {
		return vector((lhs[IndicesV] / rhs)...);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_scalar "scalar" integer modulus operator
	///
	/// \details
	/// \param lhs left hand side
	/// \param rhs right hand side
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i\%rhs_i}
	constexpr friend vector_t operator%(const vector_t& lhs, scalar_t rhs) requires(is_integral_v<scalar_t>) {
		return vector((lhs[IndicesV] % rhs)...);
	}

	/// @}

// Vector-Scalar Arithmetic Assignment operators =======================================================================

	/// \name \ref fennec_math_vector "vector" - \ref fennec_math_scalar "scalar" Arithmetic Assignment operators
	/// @{

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_scalar "scalar" addition operator
	///
	/// \details
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i+rhs_i}
	constexpr friend vector_t& operator+=(vector_t& lhs, scalar_t rhs) {
		return ((lhs[IndicesV] += rhs), ..., lhs);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_scalar "scalar" Subtraction operator
	///
	/// \details
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i-rhs_i}
	constexpr friend vector_t& operator-=(vector_t& lhs, scalar_t rhs) {
		return ((lhs[IndicesV] -= rhs), ..., lhs);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_scalar "scalar" multiplication operator
	///
	/// \details
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i={lhs_i}\cdot{rhs_i}}
	constexpr friend vector_t& operator*=(vector_t& lhs, scalar_t rhs) {
		return ((lhs[IndicesV] *= rhs), ..., lhs);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_scalar "scalar" division operator
	///
	/// \details
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=\frac{lhs_i}{rhs_i}}
	constexpr friend vector_t& operator/=(vector_t& lhs, scalar_t rhs) {
		return ((lhs[IndicesV] /= rhs), ..., lhs);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_scalar "scalar" integer modulus operator
	///
	/// \details
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i\%rhs_i}
	constexpr friend vector_t& operator%=(vector_t& lhs, scalar_t rhs) requires(is_integral_v<scalar_t>) {
		return ((lhs[IndicesV] %= rhs), ..., lhs);
	}

	/// @}

// Vector-Vector Arithmetic operators ==================================================================================

	/// \name \ref fennec_math_vector "vector" - \ref fennec_math_vector "vector" Arithmetic operators
	/// @{

	///
	/// \brief negation operator
	///
	/// \details
	/// \param x the vector
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=-x_i}
	constexpr friend vector_t operator-(const vector_t& x) {
		return vector((-x[IndicesV])...);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_vector "vector" addition operator
	///
	/// \details
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i+rhs_i}
	constexpr friend vector_t operator+(const vector_t& lhs, const vector_t& rhs) {
		return vector((lhs[IndicesV] + rhs[IndicesV])...);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_vector "vector" subtraction operator
	///
	/// \details
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i-rhs_i}
	constexpr friend vector_t operator-(const vector_t& lhs, const vector_t& rhs) {
		return vector((lhs[IndicesV] - rhs[IndicesV])...);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_vector "vector" multiplication operator
	///
	/// \details
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i={lhs_i}\cdot{rhs_i}}
	constexpr friend vector_t operator*(const vector_t& lhs, const vector_t& rhs) {
		return vector((lhs[IndicesV] * rhs[IndicesV])...);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_vector "vector" division operator
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=\frac{lhs_i}{rhs_i}}
	constexpr friend vector_t operator/(const vector_t& lhs, const vector_t& rhs) {
		return vector((lhs[IndicesV] / rhs[IndicesV])...);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_vector "vector" integer modulus operator
	///
	/// \details
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i\%rhs_i}
	constexpr friend vector_t operator%(const vector_t& lhs, const vector_t& rhs) requires(is_integral_v<scalar_t>) {
		return vector((lhs[IndicesV] % rhs[IndicesV])...);
	}

	/// @}

// Vector-Vector Arithmetic Assignment operators =======================================================================

	/// \name \ref fennec_math_vector "vector" - \ref fennec_math_vector "vector" Arithmetic Assignment operators
	/// @{

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_vector "vector" addition operator
	///
	/// \details
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i+rhs_i}
	constexpr friend vector_t& operator+=(vector_t& lhs, const vector_t& rhs) {
		return ((lhs[IndicesV] += rhs[IndicesV]), ..., lhs);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_vector "vector" subtraction operator
	///
	/// \details
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i-rhs_i}
	constexpr friend vector_t& operator-=(vector_t& lhs, const vector_t& rhs) {
		return ((lhs[IndicesV] -= rhs[IndicesV]), ..., lhs);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_vector "vector" multiplication operator
	///
	/// \details
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i={lhs_i}\cdot{rhs_i}}
	constexpr friend vector_t& operator*=(vector_t& lhs, const vector_t& rhs) {
		return ((lhs[IndicesV] *= rhs[IndicesV]), ..., lhs);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_vector "vector" division operator
	///
	/// \details
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=\frac{lhs_i}{rhs_i}}
	constexpr friend vector_t& operator/=(vector_t& lhs, const vector_t& rhs) {
		return ((lhs[IndicesV] /= rhs[IndicesV]), ..., lhs);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_vector "vector" integer modulus operator
	///
	/// \details
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i\%rhs_i}
	constexpr friend vector_t& operator%=(vector_t& lhs, const vector_t& rhs) requires(is_integral_v<scalar_t>) {
		return ((lhs[IndicesV] %= rhs[IndicesV]), ..., lhs);
	}

	/// @}

// Boolean Operators ===================================================================================================

	/// \name Boolean Operators
	/// @{

	///
	/// \brief unary boolean not operator
	///
	/// \details
	/// \param x the vector
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=!x_i}
	constexpr friend vector_t operator!(const vector_t& x) {
		return vector_t(!x[IndicesV]...);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_scalar "scalar" logical and operator
	///
	/// \details
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i\&\&rhs_i}
	constexpr friend vector_t operator&&(const vector_t& lhs, scalar_t rhs) requires(is_bool_v<scalar_t>) {
		return vector_t((lhs[IndicesV] && rhs)...);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_vector "vector" logical and operator
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i\&\&rhs_i}
	constexpr friend vector_t operator&&(const vector_t& lhs, const vector_t& rhs) requires(is_bool_v<scalar_t>) {
		return vector_t((lhs[IndicesV] && rhs[IndicesV])...);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_scalar "scalar" logical or operator
	///
	/// \details
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i\|\|rhs_i}
	constexpr friend vector_t operator||(const vector_t& lhs, scalar_t rhs) requires(is_bool_v<scalar_t>) {
		return vector_t((lhs[IndicesV] || rhs)...);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_vector "vector" logical or operator
	///
	/// \details
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i\|\|rhs_i}
	constexpr friend vector_t operator||(const vector_t& lhs, const vector_t& rhs) requires(is_bool_v<scalar_t>) {
		return vector_t((lhs[IndicesV] || rhs[IndicesV])...);
	}

	/// @}

// Bitwise Operators ===================================================================================================

	/// \name Bitwise Operators
	/// @{

	///
	/// \brief \ref fennec_math_scalar "scalar" - \ref fennec_math_vector "vector" bitwise and operator
	///
	/// \details
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i\&rhs_i}
	constexpr friend vector_t operator&(scalar_t rhs, const vector_t& lhs) requires(is_integral_v<scalar_t>) {
		return vector_t((lhs[IndicesV] & rhs)...);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_scalar "scalar" bitwise and operator
	///
	/// \details
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i\&rhs_i}
	constexpr friend vector_t operator&(const vector_t& lhs, scalar_t rhs) requires(is_integral_v<scalar_t>) {
		return vector_t((lhs[IndicesV] & rhs)...);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_scalar "scalar" bitwise and assignment operator
	///
	/// \details
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i\&rhs_i}
	constexpr friend vector_t operator&=(vector_t& lhs, scalar_t rhs) requires(is_integral_v<scalar_t>) {
		return ((lhs[IndicesV] &= rhs), ..., lhs);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_vector "vector" bitwise and operator
	///
	/// \details
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i\&rhs_i}
	constexpr friend vector_t operator&(const vector_t& lhs, const vector_t& rhs) requires(is_integral_v<
		scalar_t>) {
		return vector_t((lhs[IndicesV] & rhs[IndicesV])...);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_vector "vector" bitwise and assignment operator
	///
	/// \details
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i\&rhs_i}
	constexpr friend vector_t operator&=(vector_t& lhs, const vector_t& rhs) requires(is_integral_v<scalar_t>) {
		return ((lhs[IndicesV] &= rhs[IndicesV]), ..., lhs);
	}


	///
	/// \brief \ref fennec_math_scalar "scalar" - \ref fennec_math_vector "vector" bitwise or operator
	///
	/// \details
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i|rhs_i}
	constexpr friend vector_t operator|(scalar_t rhs, const vector_t& lhs) requires(is_integral_v<scalar_t>) {
		return vector_t((lhs[IndicesV] | rhs)...);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_scalar "scalar" bitwise or operator
	///
	/// \details
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i|rhs_i}
	constexpr friend vector_t operator|(const vector_t& lhs, scalar_t rhs) requires(is_integral_v<scalar_t>) {
		return vector_t((lhs[IndicesV] | rhs)...);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_scalar "scalar" bitwise or assignment operator
	///
	/// \details
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i|rhs_i}
	constexpr friend vector_t operator|=(vector_t& lhs, scalar_t rhs) requires(is_integral_v<scalar_t>) {
		return ((lhs[IndicesV] |= rhs), ..., lhs);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_vector "vector" bitwise or operator
	///
	/// \details
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i|rhs_i}
	constexpr friend vector_t operator|(const vector_t& lhs, const vector_t& rhs) requires(is_integral_v<
		scalar_t>) {
		return vector_t((lhs[IndicesV] | rhs[IndicesV])...);
	}

	///
	/// \brief \ref fennec_math_vector "vector" - \ref fennec_math_vector "vector" bitwise or assignment operator
	///
	/// \details
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i|rhs_i}
	constexpr friend vector_t operator|=(vector_t& lhs, const vector_t& rhs) requires(is_integral_v<scalar_t>) {
		return ((lhs[IndicesV] |= rhs[IndicesV]), ..., lhs);
	}


	///
	/// \ref fennec_math_scalar "scalar" - \ref fennec_math_vector "vector" bitwise xor operator
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i\^rhs_i}
	constexpr friend vector_t operator^(scalar_t lhs, const vector_t& rhs) requires(is_integral_v<scalar_t>) {
		return vector_t((lhs ^ rhs[IndicesV])...);
	}

	///
	/// \ref fennec_math_vector "vector" - \ref fennec_math_scalar "scalar" bitwise xor operator
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i\^rhs_i}
	constexpr friend vector_t operator^(const vector_t& lhs, scalar_t rhs) requires(is_integral_v<scalar_t>) {
		return vector_t((lhs[IndicesV] ^ rhs)...);
	}

	///
	/// \ref fennec_math_vector "vector" - \ref fennec_math_scalar "scalar" bitwise xor assignment operator
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i\^rhs_i}
	constexpr friend vector_t operator^=(vector_t& lhs, scalar_t rhs) requires(is_integral_v<scalar_t>) {
		return ((lhs[IndicesV] ^= rhs), ..., lhs);
	}

	///
	/// \ref fennec_math_vector "vector" - \ref fennec_math_vector "vector" bitwise xor operator
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i\^rhs_i}
	constexpr friend vector_t operator^(const vector_t& lhs, const vector_t& rhs) requires(is_integral_v<
		scalar_t>) {
		return vector_t((lhs[IndicesV] ^ rhs[IndicesV])...);
	}

	///
	/// \ref fennec_math_vector "vector" - \ref fennec_math_vector "vector" bitwise xor assignment operator
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i\^rhs_i}
	constexpr friend vector_t operator^=(vector_t& lhs, const vector_t& rhs) requires(is_integral_v<scalar_t>) {
		return ((lhs[IndicesV] ^= rhs[IndicesV]), ..., lhs);
	}


	///
	/// \ref fennec_math_scalar "scalar" - \ref fennec_math_vector "vector" bitwise left-shift operator
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i<<rhs_i}
	constexpr friend vector_t operator<<(scalar_t lhs, const vector_t& rhs) requires(is_integral_v<scalar_t>) {
		return vector_t((lhs << rhs[IndicesV])...);
	}

	///
	/// \ref fennec_math_vector "vector" - \ref fennec_math_scalar "scalar" bitwise left-shift operator
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i<<rhs_i}
	constexpr friend vector_t operator<<(const vector_t& lhs, scalar_t rhs) requires(is_integral_v<scalar_t>) {
		return vector_t((lhs[IndicesV] << rhs)...);
	}

	///
	/// \ref fennec_math_vector "vector" - \ref fennec_math_scalar "scalar" bitwise left-shift assignment operator
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i<<=rhs_i}
	constexpr friend vector_t operator<<=(vector_t& lhs, scalar_t rhs) requires(is_integral_v<scalar_t>) {
		return ((lhs[IndicesV] <<= rhs), ..., lhs);
	}

	///
	/// \ref fennec_math_vector "vector" - \ref fennec_math_vector "vector" bitwise or operator
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i<<rhs_i}
	constexpr friend vector_t operator<<(const vector_t& lhs, const vector_t& rhs) requires(is_integral_v<scalar_t>) {
		return vector_t((lhs[IndicesV] << rhs[IndicesV])...);
	}

	///
	/// \ref fennec_math_vector "vector" - \ref fennec_math_vector "vector" bitwise or assignment operator
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i<<=rhs_i}
	constexpr friend vector_t operator<<=(vector_t& lhs, const vector_t& rhs) requires(is_integral_v<scalar_t>) {
		return ((lhs[IndicesV] <<= rhs[IndicesV]), ..., lhs);
	}


	///
	/// \ref fennec_math_scalar "scalar" - \ref fennec_math_vector "vector" bitwise or operator
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i>>rhs_i}
	constexpr friend vector_t operator>>(scalar_t lhs, const vector_t& rhs) requires(is_integral_v<scalar_t>) {
		return vector_t((lhs >> rhs[IndicesV])...);
	}

	///
	/// \ref fennec_math_vector "vector" - \ref fennec_math_scalar "scalar" bitwise or operator
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i>>rhs_i}
	constexpr friend vector_t operator>>(const vector_t& lhs, scalar_t rhs) requires(is_integral_v<scalar_t>) {
		return vector_t((lhs[IndicesV] >> rhs)...);
	}

	///
	/// \ref fennec_math_vector "vector" - \ref fennec_math_scalar "scalar" bitwise left-shift assignment operator
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i>>=rhs_i}
	constexpr friend vector_t operator>>=(vector_t& lhs, scalar_t rhs) requires(is_integral_v<scalar_t>) {
		return ((lhs[IndicesV] >>= rhs), ..., lhs);
	}

	///
	/// \ref fennec_math_vector "vector" - \ref fennec_math_vector "vector" bitwise or operator
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i>>rhs_i}
	constexpr friend vector_t operator>>(const vector_t& lhs, const vector_t& rhs) requires(is_integral_v<scalar_t>) {
		return vector_t((lhs[IndicesV] >> rhs[IndicesV])...);
	}

	///
	/// \ref fennec_math_vector "vector" - \ref fennec_math_vector "vector" bitwise or assignment operator
	/// \param lhs Left Hand Side of the Expression
	/// \param rhs Right Hand Side of the Expression
	/// \returns A \ref fennec_math_vector "vector" \a v such that, \math{v_i=lhs_i>>=rhs_i}
	constexpr friend vector_t operator>>=(vector_t& lhs, const vector_t& rhs) requires(is_integral_v<scalar_t>) {
		return ((lhs[IndicesV] >>= rhs[IndicesV]), ..., lhs);
	}

	/// @}


// Private Helpers =====================================================================================================

private:
	template<size_t IndexV = 0, typename HeadT, typename... TailT>
	constexpr void _construct(HeadT&& head, TailT&&... rest) {
		vector::_insert<IndexV>(fennec::forward<HeadT>(head));

		if constexpr (sizeof...(TailT) > 0)
			vector::_construct<IndexV + component_count_v<HeadT>>(fennec::forward<TailT>(rest)...);
	}

	template<size_t OffsetV>
	constexpr void _insert(const ScalarT& x) {
		data[OffsetV] = x;
	}

	template<size_t OffsetV, typename OScalarT>
	constexpr void _insert(const OScalarT& x) {
		data[OffsetV] = ScalarT(x);
	}

	template<size_t OffsetV = 0, typename OScalarT, size_t... OIndicesV>
	constexpr void _insert(const vector<OScalarT, OIndicesV...>& vec) {
		((data[OffsetV + OIndicesV] = fennec::forward<OScalarT>(vec[OIndicesV])), ...);
	}

	template<size_t OffsetV = 0, typename OVectorT, typename ODataT, typename OScalarT, size_t... OIndicesV>
	constexpr void _insert(const swizzle<OVectorT, ODataT, OScalarT, OIndicesV...>& vec) {
		size_t i = 0;
		((data[OffsetV + (i++)] = vec.data[OIndicesV]), ...);
	}
};

}

#endif // FENNEC_MATH_VECTOR_H
