The library contains a set of linear algebra classes and function that are optimized for games and other graphic intensive apps. The operations are implemented using a blend of SIMD and regular C/C++, performing as well as, and sometimes outperforming, the DirectXMath library.

It’s used to create the MiniEngine, as an example of how to use this part of the library.

Interior

The classes and functions are designed to make it easier to implement, understand and maintain efficient vectorized solutions, and the Benchmarks says it all when it comes to the efficiency of this implementation.

The purpose of specializations of SIMD::Traits<T, N> is to have a mechanism for selecting implementations that are implemented for T and optimized for N elements, for developing the C++ templates classes and template functions that forms the core of the linear algebra features of the library.

The SIMD::Traits<T, N> specializations are used to implement a set of C++ classes that simplifies SIMD development. These classes are contained in the HCCVectorMath.h header file.

The library exploits the ability of C++ to create zero-overhead abstractions, making efficient SIMD code readable:

using Vector = Math::Vector<float, 4>;

Vector v1( 1.0f, 2.0f, 3.f, 1.0f );
Vector v2( 1.0f, 2.0f, 3.f, 1.0f );
Vector v3 = v1 + v2 + v1 + v2 + v1 + v2;

where the + operator is implemented as:

template<Internal::TupleType T, 
        Internal::TupleType U, 
        typename ResultT = typename T::Simd>
    requires Internal::IsCompatible<T, U>
inline ResultT operator + ( const T& lhs, const U& rhs ) noexcept
{
    using Traits = typename T::Traits;
    return Traits::Add( Traits::Load( lhs.values.data( ) ), 
                            Traits::Load( rhs.values.data( ) ) );
}

Which the compiler turns into:

    using Vector = Math::Vector<float, 4>;

    Vector v1( 1.0f, 2.0f, 3.f, 1.0f );
00007FF6C7F162D8  vmovdqu     xmm4,xmmword ptr [__xmm@3f80000040400000400000003f800000 (07FF6C8387F60h)]  
    Vector v2( 1.0f, 2.0f, 3.f, 1.0f );
    Vector v3 = v1 + v2 + v1 + v2 + v1 + v2;
00007FF6C7F162E0  vaddps      xmm0,xmm4,xmm4  
00007FF6C7F162E4  vaddps      xmm1,xmm0,xmm4  
00007FF6C7F162E8  vaddps      xmm2,xmm1,xmm4  
00007FF6C7F162EC  vaddps      xmm3,xmm2,xmm4  
00007FF6C7F162F0  vaddps      xmm6,xmm3,xmm4  

The compiler detects that the two vectors are identical, and only do a single load of the data into xmm4, and then generates code for the additions.

The important thing is that there are no unnecessary artifacts caused by using the classes.

Rearranging, and grouping, the terms:

Vector v3 = ( v1 + v2 ) + ( v1 + v2 ) + ( v1 + v2 );

improves the generated code significantly:

00007FF6BC1F39E8  vaddps      xmm1,xmm6,xmm6  
00007FF6BC1F39EC  vaddps      xmm0,xmm1,xmm1  
00007FF6BC1F39F0  vaddps      xmm1,xmm0,xmm1 

Reducing the number of vaddps operations to just three.

Doing the same thing using DirectXMath requires a bit more work:


XMFLOAT4A v1( { 1.0f, 2.0f, 3.f, 1.0f } );
XMFLOAT4A v2( { 1.0f, 2.0f, 3.f, 1.0f } );
auto v1Loaded = XMLoadFloat4A( &v1 );
auto v2Loaded = XMLoadFloat4A( &v2 );
        
auto v3Loaded = XMVectorAdd(
        XMVectorAdd( 
            XMVectorAdd( 
                v1Loaded,
                v2Loaded ),
            v1Loaded ),
        XMVectorAdd( 
            XMVectorAdd( 
                v2Loaded,
                v1Loaded ),
            v2Loaded ) );

Benchmarking the above DirectXMath code against

`Vector v3 = ( v1 + v2 ) + ( v1 + v2 ) + ( v1 + v2 );`

yields:

----------------------------------------------------------------------------------
Benchmark                                        Time             CPU   Iterations
----------------------------------------------------------------------------------
BenchmarkVector2MultipleAdds                  4.36 ns         3.35 ns    224000000
BenchmarkVector2MultipleXMVectorAdd           4.56 ns         3.77 ns    165925926

So, there is rarely any overhead to using the library, compared to working directly with the SIMD compiler intrinsic functions.

Ideally, the compiler would generate optimal code for any computations, and it usually comes close - and when enabled using the /arch:AVX, /arch:AVX2, /arch:AVX512 or /arch:AVX10.1 switches, it will utilize SIMD operations to improve performance. This just requires a rebuild of the solution, and will often improve performance significantly.

Note that AVX was introduced with the Sandy Bridge micro architecture back in 2011, while AVX2 came later with the Haswell micro architecture in 2012, and AMD added support for AVX2 in 2015. So it’s generally safe to assume that any modern server, or workstation, supports AVX2.

Vector

Vector is a C++ template class that holds a fixed number of elements:

template<typename ValueT, size_t N>
class alignas( Math::Internal::SIMD::Traits<ValueT,N>::AlignAs ) Vector
{
  ...
};

The template supports the unary -, -, +, *, /, -=, +=, *=, /=, and [] operators. The basic mathematical operators are constexpr implemented, allowing code to be evaluated at compile time, while using SIMD instructions at runtime, but Eigen and Armadillo are far better alternatives for general linear algebra.

Tuple2, Tuple3 and Tuple4

The Tuple2, Tuple3 and Tuple4 templates implements most of the magic, together with the TupleSimd template, required to provide an efficient set of classes and templates that can handle linear algebra for games, graphic apps, and other apps that work with two and/or three dimensional data.

The template arguments are the type derived from the template and the type used to store each coordinate’s value. Since the templates know their derived type, the templates can operate on, and return values of the derived type.

When working with data in two dimensions, it’s a common convention that the type holding two dimensional data has two data fields, x and y. Similarly a type holding three dimensional data is expected to have three data fields named x, y and z.

The Tuple2 template fills this role for two dimensional data:

template<class DerivedT, typename T>
class Tuple2 : public Internal::TupleBase
{
public:
  ...
    union
    {
        ArrayType values;
        struct
        {
            value_type x, y;
        };
    };
  ...
};

where values is a std::array<value_type,2> sharing the location of x and y in memory. Setting v.values[0] = 0 is the same as v.x = 0.

The union between values and struct { value_type x, y; } is important as it provides a generic way to access the coordinates held by the object without explicitly accessing each value through x or y.

DerivedT is required to be a class derived from the Tuple2 template, and value_type is declared as using value_type = T;.

Similarly the Tuple3 template fills this role for three dimensional data:

template<class DerivedT, typename T>
class Tuple3 : public Internal::TupleBase
{
public:
  ...
    union
    {
        ArrayType values;
        struct
        {
            value_type x, y, z;
        };
    };
  ...
};

and here values is a std::array<value_type,3>.

Tuple4 adds an additional field w, and uses std::array<value_type,4> for the values array.

The Tuple2, Tuple3 and Tuple4 templates are derived from the empty Internal::TupleBase struct.

namespace Internal
{
    struct TupleBase
    { };
}

This provides a mechanism used to distinguish between types derived from Tuple2, Tuple3 and Tuple4; and types that are not:

namespace Internal
{
    template<typename T>
    concept TupleType = std::is_base_of_v<TupleBase, T>;
}

To use SIMD on the Intel/AMD x64 architecture, data must, as mentioned, be loaded into a SIMD type that is an abstract representation of an AVX or SSE4 register. The TupleSimd template represents SIMD types using one of the SIMD::Traits specializations combined with a tuple type.

template<typename TraitsT, typename TupleT>
class TupleSimd : public Internal::TupleSimdBase
{
  ...    
};

TupleSimd fills this role for each of the Tuple2, Tuple3 and Tuple4 template classes.

template<class DerivedT, typename T>
class Tuple3 : public Internal::TupleBase
{
public:
    using DerivedType = DerivedT;
  ...
    using Simd = TupleSimd<Traits, DerivedType>;
  ...
};

The above definition of Tuple3::Simd ensures that each class derived from Tuple3 gets a distinct Tuple3::Simd C++ type.

Tuple2, Tuple3 and Tuple4 implementation details

Tuple2, Tuple3 and Tuple4 have similar implementations, following the same pattern:

template<typename DerivedT, typename T>
class Tuple3 : public Internal::TupleBase
{

Internal::TupleBase is used as the base class for the Tuple2, Tuple3 and Tuple4 enabling the use of std::is_base_of_v<,> to distinguish between types that are derived from Tuple2, Tuple3 and Tuple4 and those that are not, and this is used to ensure that the templates designed for Tuple2, Tuple3 and Tuple4 will only be enabled for classes derived from either of them.

Inside the template definition, DerivedType is defined as the class or struct derived from the Tuple3 template, together with value_type and size_type.

public:
    using DerivedType = DerivedT;
    using value_type = T;
    using size_type = size_t;

Tuple3 holds three values, x, y and z, as its main purpose is to be a storage for three dimensional coordinates, and Size specifies the number of values that Tuple3 holds.

    static constexpr size_type Size = 3;

    using Traits = SIMD::Traits<value_type, Size>;
    using SIMDType = typename Traits::SIMDType;

Above Traits is defined for value_type and Size, selecting the SIMD::Traits specialization that fits the requirements for Tuple3.

Next ArrayType is defined:

    using ArrayType = typename Traits::ArrayType;

This is the same as std::array<value_type,Size>, and while x, y and z is the common notation for three dimensional information, values, as defined below, is more convenient for developing templates that will work with Tuple2, Tuple3, Tuple4 and any class derived from either of them.

The mathematical operations are performed using the Simd type which holds a SIMD vector using the TupleSimd instantiated for the Traits and the class derived from the Tuple3 template, ensuring that each derived class gets a unique TupleSimd C++ type.

    using Simd = TupleSimd<Traits, DerivedType>;

The data fields of Tuple3:

    union
    {
        ArrayType values;
        struct
        {
            value_type x, y, z;
        };
    };

The default constructor ensures that x, y and z are initialized to 0.

    Tuple3( ) noexcept
            : x{}, y{}, z{}
    { }

    Tuple3( value_type xv, value_type yv, value_type zv ) noexcept
        : x( xv ), y( yv ), z( zv )
    { }

The next constructor initializes a Tuple3 from a compatible TupleSimd, which is any TupleSimd instantiated for the same SIMD::Traits specialization as Tuple3. SIMD::Traits has a ToArray function that stores the values in the SIMD type in a std::array<> with the same type as ArrayType and returns the data.


    template<Internal::SimdType T>
        requires std::is_same_v<Traits, typename T::Traits>
    Tuple3( const T& other ) noexcept
        : values( Traits::ToArray( other.simd ) )
    { }

    template<Internal::SimdType T>
        requires std::is_same_v<Traits, typename T::Traits>
    DerivedType& operator = ( const T& other ) noexcept
    {
        values = Traits::ToArray( other.simd );
        return static_cast< DerivedType& >( *this );
    }

    constexpr bool operator == ( const Tuple3& other ) const noexcept
    {
        return IsSameValue( x, other.x ) && IsSameValue( y, other.y ) 
                            && IsSameValue( z, other.z );
    }
    constexpr bool operator != ( const Tuple3& other ) const noexcept
    {
        return !IsSameValue( x, other.x ) || !IsSameValue( y, other.y ) 
                            || !IsSameValue( z, other.z );
    }

Compare the Tuple3 with a compatible TupleSimd:


    template<Internal::SimdType T>
        requires std::is_same_v<Traits, typename T::Traits>
    bool operator == ( const T& other ) const noexcept
    {
        return Traits::Equals( Traits::Load( values.data( ) ), other.simd );
    }

    template<Internal::SimdType T>
        requires std::is_same_v<Traits, typename T::Traits>
    bool operator != ( const T& other ) const noexcept
    {
        return Traits::Equals( Traits::Load( values.data( ) ), other.simd ) == false;
    }

Negation loads the data of Tuple3 into a SIMDType, calls Traits::Negate returning the result as a Tuple3::Simd, the TupleSimd specialization for this Tuple3 type:

    Simd operator-( ) const noexcept
    {
        return Traits::Negate( Traits::Load( values.data( ) ) );
    }

Tuple3 overloads the +=, -=, *= and /= operators, and each operator accepts a const reference to a Simd, const reference to a Tuple3, or a scalar - note that the overloads returns a reference to DerivedType:


    DerivedType& operator += ( const Simd& other ) noexcept
    {
        values = Traits::ToArray( Traits::Add( Traits::Load( values ), other.simd ) );
        return static_cast< DerivedType& >(*this );
    }

    DerivedType& operator += ( const Tuple3& other ) noexcept
    {
        x += other.x;
        y += other.y;
        z += other.z;
        return static_cast< DerivedType& >( *this );
    }

    DerivedType& operator += ( const value_type& value ) noexcept
    {
        x += value;
        y += value;
        z += value;
        return static_cast< DerivedType& >( *this );
    }

    DerivedType& operator -= ( const Simd& other ) noexcept
    {
        values = Traits::ToArray( Traits::Sub( Traits::Load( values ), other.simd ) );
        return static_cast< DerivedType& >( *this );
    }

    DerivedType& operator -= ( const Tuple3& other ) noexcept
    {
        x -= other.x;
        y -= other.y;
        z -= other.z;
        return static_cast< DerivedType& >( *this );
    }

    DerivedType& operator -= ( const value_type& value ) noexcept
    {
        x -= value;
        y -= value;
        z -= value;
        return static_cast< DerivedType& >( *this );
    }

    DerivedType& operator *= ( const Simd& other ) noexcept
    {
        values = Traits::ToArray( Traits::Mul( Traits::Load( values ), other.simd ) );
        return static_cast< DerivedType& >( *this );
    }

    DerivedType& operator *= ( const Tuple3& other ) noexcept
    {
        x *= other.x;
        y *= other.y;
        z *= other.z;
        return static_cast< DerivedType& >( *this );
    }

    DerivedType& operator *= ( const value_type& value ) noexcept
    {
        x *= value;
        y *= value;
        z *= value;
        return static_cast< DerivedType& >( *this );
    }


    DerivedType& operator /= ( const Simd& other ) noexcept
    {
        values = Traits::ToArray( Traits::Div( Traits::Load( values ), other.simd ) );
        return static_cast< DerivedType& >( *this );
    }

    DerivedType& operator /= ( const Tuple3& other ) noexcept
    {
        x /= other.x;
        y /= other.y;
        z /= other.z;
        return static_cast< DerivedType& >( *this );
    }

    DerivedType& operator /= ( const value_type& value ) noexcept
    {
        x /= value;
        y /= value;
        z /= value;
        return static_cast< DerivedType& >( *this );
    }

Since Tuple3 is a container, it implements common container member functions:

    const_reference operator[]( size_t index ) const noexcept
    {
        return values[ index ];
    }

    reference operator[]( size_t index ) noexcept
    {
        return values[ index ];
    }

    const_pointer data( ) const noexcept
    {
        return values.data( );
    }
    pointer data( ) noexcept
    {
        return values.data( );
    }

    constexpr size_t size( ) const noexcept
    {
        return Size;
    }

    const_reference front( ) const noexcept
    {
        return values.front( );
    }
    reference front( ) noexcept
    {
        return values.front( );
    }

    const_reference back( ) const noexcept
    {
        return values.back( );
    }
    reference back( ) noexcept
    {
        return values.back( );
    }

    const_iterator begin( ) const noexcept
    {
        return values.begin( );
    }
    const_iterator cbegin( ) const noexcept
    {
        return values.cbegin( );
    }
    iterator begin( ) noexcept
    {
        return values.begin( );
    }
    const_iterator end( ) const noexcept
    {
        return values.end( );
    }
    const_iterator cend( ) const noexcept
    {
        return values.cend( );
    }
    iterator end( ) noexcept
    {
        return values.end( );
    }
    const_reverse_iterator rbegin( ) const noexcept
    {
        return values.rbegin( );
    }
    reverse_iterator rbegin( ) noexcept
    {
        return values.rbegin( );
    }
    const_reverse_iterator rend( ) const noexcept
    {
        return values.rend( );
    }
    reverse_iterator rend( ) noexcept
    {
        return values.rend( );
    }

The above provides a reasonable level of integration with the standard C++ library.

Calling the Assign member functions from derived classes can be more convenient than calling Base::operator = ( arrayOfData ).

    void Assign( value_type xv, value_type yv, value_type zv ) noexcept
    {
        x = xv;
        y = yv;
        z = zv;
    }
    void Assign( const ArrayType& src ) noexcept
    {
        values = src;
    }
    void Assign( SIMDType src ) noexcept
    {
        values = Traits::ToArray( src );
    }

The ability to check for NaN or infinity:


    bool HasNaN( ) const noexcept
    {
        return std::isnan( x ) || std::isnan( y ) || std::isnan( z );
    }

    bool IsFinite( ) const noexcept
    {
        return std::isfinite( x ) && std::isfinite( y ) && std::isfinite( z );
    }

    bool IsInfinite( ) const noexcept
    {
        return std::isinf( x ) || std::isinf( y ) || std::isinf( z );
    }

};

The library defines two internal concepts. Internal::TupleType and Internal::SimdType.

Tuple2, Tuple3 and Tuple4 matches the Internal::TupleType concept, while any TupleSimd derived type matches the Internal::SimdType concept.

In the code below, Internal::IsCompatible<T,U> is used to verify that T and U are compatible types using the same SIMD::Traits specialization.

The first overload will be used when both arguments are compatible TupleSimd based objects, so the implementation can perform the addition without any loading of values into a SIMD datatype/register.

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T,U>
inline T operator + ( const T& lhs, const U& rhs ) noexcept
{
    using Traits = typename T::Traits;
    return Traits::Add( lhs.simd, rhs.simd );
}

The second overload accepts TupleSimd for the left hand argument, and any compatible Tuple2, Tuple3 or Tuple4 as its right hand argument.

The implementation loads the data from the tuple type, before performing the addition, returning T which is a TupleSimd based type.

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline T operator + ( const T& lhs, const U& rhs ) noexcept
{
    using Traits = typename T::Traits;
    return Traits::Add( lhs.simd, Traits::Load( rhs.values.data( ) ) );
}

The third overload accepts the same type of arguments as the second, but takes a Tuple2, Tuple3 or Tuple4 as its left hand argument, and TupleSimd for the right hand argument.

This time the contents of lhs gets loaded before performing the addition.

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline T operator + ( const U& lhs, const T& rhs ) noexcept
{
    using Traits = typename T::Traits;
    return Traits::Add( Traits::Load( lhs.values.data( ) ), rhs.simd );
}

The fourth, and last, overload, accepts compatible Tuple2, Tuple3 or Tuple4 for both the left hand side and the right hand side of the addition, loading data for both arguments before performing the addition.

template<Internal::TupleType T, Internal::TupleType U, 
                            typename ResultT = typename T::Simd>
    requires Internal::IsCompatible<T, U>
inline ResultT operator + ( const T& lhs, const U& rhs ) noexcept
{
    using Traits = typename T::Traits;
    return Traits::Add( Traits::Load( lhs.values.data( ) ), 
                                Traits::Load( rhs.values.data( ) ) );
}

Loading to and storing from the SIMD types is done using the values field of the Tuple2, Tuple3 and Tuple4 types, so they can all share the same operator and function implementations:

template<Internal::SimdType T>
inline T Round( const T& t ) noexcept
{
    using Traits = typename T::Traits;
    return Traits::Round( t.simd );
}
template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Round( const T& t ) noexcept
{
    using Traits = typename T::Traits;
    return Traits::Round( Traits::Load( t.values.data( ) ) );
}

The operators and functions nearly always return a TupleSimd based type, ensuring that data is only loaded into a SIMD type when necessary.

The following operations are currently supported for Tuple2, Tuple3 and Tuple4:

+, -, unary -, *, /, +=, -=, *=, /=, Abs, Min, Max, Sqr, Ceil, Floor, Round, Trunc, Lerp, Saturate, Sqrt, FMA, FMSub, Sin, Cos, Tan, ASin, ACos, ATan, ATan2, SinH, CosH, TanH, ASinH, ACosH, ATanH, Log, Log1P, Log10, Log2, Exp, Exp10, Exp2, ExpM1, Pow, Dot, Hypot, Permute, Cross, LengthSquared, Length Normalize, ReciprocalLength, DistanceSquared, Distance, HProd HSum, DifferenceOfProducts, SumOfProducts

Tuple2, Tuple3, Tuple4 and TupleSimd Functions

The functions that work with the Tuple2, Tuple3, Tuple4 and TupleSimd types generally returns a TupleSimd holding the result of the operation.

There are usually several overloads for each function, allowing the values held by the Tuple2, Tuple3 and Tuple4 derived types to be automatically loaded into a SIMD type/register.

Functions that calculate a single scalar value, returns a TupleSimd where every element holds the calculated value. These functions also have an implementation that returns the scalar as single value. For instance, Dot returns a TupleSimd, while ScalarDot returns a floating point value of the same type as a single element from the TupleSimd type.

HSum and ScalarHSum

Calculates the horizontal sum of the elements in the vector.

template<Internal::SimdType T>
inline T HSum( const T& t ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::value_type>
inline ResultT ScalarHSum( const T& t ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT HSum( const T& t ) noexcept;

HProd and ScalarHProd

Calculates the horizontal product of the elements in the vector.

template<Internal::SimdType T>
inline T HProd( const T& t ) noexcept;

template<Internal::SimdType T>
inline auto ScalarHProd( const T& t ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::value_type>
inline ResultT ScalarHProd( const T& t ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT HProd( const T& t ) noexcept;

Abs

Computes the absolute value of each element held by the argument.

template<Internal::SimdType T>
inline T Abs( const T& t ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Abs( const T& t ) noexcept;

Min

Makes a comparison between the elements held by the two arguments, and returns a TupleSimd containing the smallest elements.

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline T Min( const T& lhs, const U& rhs ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline T Min( const T& lhs, const U& rhs ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline T Min( const U& lhs, const T& rhs ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, 
                typename ResultT = typename T::Simd>
    requires Internal::IsCompatible<T, U>
inline ResultT Min( const T& lhs, const U& rhs ) noexcept;

Max

Makes a comparison between the elements held by the two arguments, and returns a TupleSimd containing the largest elements.

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline T Max( const T& lhs, const U& rhs ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline T Max( const T& lhs, const U& rhs ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline T Max( const U& lhs, const T& rhs ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, 
                typename ResultT = typename T::Simd>
    requires Internal::IsCompatible<T, U>
inline ResultT Max( const T& lhs, const U& rhs ) noexcept;

Sqr

Computes the square value of each element held by the argument.

template<Internal::SimdType T>
inline T Sqr( const T& t ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Sqr( const T& t ) noexcept;

Ceil

Computes the ceiling of each element held by the argument.

template<Internal::SimdType T>
inline T Ceil( const T& t ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Ceil( const T& t ) noexcept;

Floor

Computes the floor of each element held by the argument.

template<Internal::SimdType T>
inline T Floor( const T& t ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Floor( const T& t ) noexcept;

Round

Rounds each element held by the argument towards the nearest even integer.

template<Internal::SimdType T>
inline T Round( const T& t ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Round( const T& t ) noexcept;

Trunc

Rounds each element held by the argument to the nearest integer in the direction of zero.

template<Internal::SimdType T>
inline T Trunc( const T& t ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Trunc( const T& t ) noexcept;

Lerp

Calculates the linear interpolation between the the elements of a and the elements of b, for elements of c is inside [0,1), or the linear extrapolation for elements in c outside [0,1).

template<typename NumberT, Internal::SimdType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT> && Internal::IsCompatible<T, U> 
inline T Lerp( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::TupleType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T Lerp( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline U Lerp( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::TupleType U, 
                                    typename ResultT = typename T::Simd>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline ResultT Lerp( NumberT a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T Lerp( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T Lerp( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U Lerp( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline S Lerp( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T Lerp( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T Lerp( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U Lerp( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, 
                                    typename ResultT = typename T::Simd >
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline ResultT Lerp( const S& a, const T& b, const U& c ) noexcept;

Clamp

Returns the elements of v, if the elements are between their respective boundaries specified the elements of lowerBounds and the elements of upperBounds, otherwise the value of nearest boundary is returned.

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T Clamp( const S& v, const T& lowerBounds, const U& upperBounds ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T Clamp( const S& v, const T& lowerBounds, const U& upperBounds ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U Clamp( const S& v, const T& lowerBounds, const U& upperBounds ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline S Clamp( const S& v, const T& lowerBounds, const U& upperBounds ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T Clamp( const S& v, const T& lowerBounds, const U& upperBounds ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T Clamp( const S& v, const T& lowerBounds, const U& upperBounds ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U Clamp( const S& v, const T& lowerBounds, const U& upperBounds ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, 
                                        typename ResultT = typename S::Simd>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline ResultT Clamp( const S& v, const T& lowerBounds, const U& upperBounds ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::TupleType U, 
                                        typename ResultT = typename T::Simd>
    requires std::is_arithmetic_v<NumberT> && Internal::IsCompatible<T, U>
inline ResultT Clamp( NumberT v, const T& lowerBounds, const U& upperBounds ) noexcept;

Saturate

Saturates the elements of v to the range 0.0 to 1.0.

template<Internal::SimdType T>
inline T Saturate( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Saturate( const T& v ) noexcept;

Sqrt

Calculates the square root of each element in the argument.

template<Internal::SimdType T>
inline T Sqrt( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Sqrt( const T& v ) noexcept;

ReciprocalSqrt

Calculates the reciprocal square root of each element in the argument.

template<Internal::SimdType T>
inline T ReciprocalSqrt( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT ReciprocalSqrt( const T& v ) noexcept;

Reciprocal

Calculates the reciprocal of each element in the argument.

template<Internal::SimdType T>
inline T Reciprocal( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Reciprocal( const T& v ) noexcept;

FMA

Multiplies the corresponding elements of a and b, adding the result to the corresponding element of c.

template<typename NumberT, Internal::SimdType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMA( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::TupleType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMA( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline U FMA( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::TupleType U, 
                                    typename ResultT = typename T::Simd>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline ResultT FMA( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMA( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::TupleType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMA( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline U FMA( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::TupleType U, 
                                    typename ResultT = typename T::Simd>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline ResultT FMA( const T& a, NumberT b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMA( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMA( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U FMA( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline S FMA( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMA( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMA( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U FMA( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, 
                                        typename ResultT = typename T::Simd >
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline ResultT FMA( const S& a, const T& b, const U& c ) noexcept;

FMSub

Performs a set of multiply-subtract computation on a, b, and c. Corresponding values in two operands, a and b, are multiplied and the infinite precision intermediate results are obtained. From the infinite precision intermediate results, the values in the third operand, c, are subtracted. The final results are rounded to the nearest floating point values.

template<typename NumberT, Internal::SimdType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMSub( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::TupleType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMSub( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline U FMSub( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline ResultT FMSub( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMSub( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::TupleType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMSub( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline U FMSub( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline ResultT FMSub( const T& a, NumberT b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U FMSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline S FMSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U FMSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd >
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline ResultT FMSub( const S& a, const T& b, const U& c ) noexcept;

FMAddSub

Performs a set of multiply-add-subtract computation on a, b, and c. Corresponding values in two operands, a and b, are multiplied and infinite precision intermediate results are obtained. The odd values in the third operand, c, are added to the intermediate results while the even values are subtracted from them. The final results are rounded to the nearest floating point values.

template<typename NumberT, Internal::SimdType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMAddSub( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::TupleType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMAddSub( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline U FMAddSub( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline ResultT FMAddSub( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMAddSub( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::TupleType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMAddSub( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline U FMAddSub( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline ResultT FMAddSub( const T& a, NumberT b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMAddSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMAddSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U FMAddSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline S FMAddSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMAddSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMAddSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U FMAddSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd >
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline ResultT FMAddSub( const S& a, const T& b, const U& c ) noexcept;

FMSubAdd

Performs a set of multiply-subtract-add computation on a, b, and c. Corresponding values in two operands, a and b, are multiplied and infinite precision intermediate results are obtained. The odd values in the third operand, c, are subtracted from the intermediate results while the even values are added to them. The final results are rounded to the nearest floating point values.

template<typename NumberT, Internal::SimdType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMSubAdd( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::TupleType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMSubAdd( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline U FMSubAdd( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline ResultT FMSubAdd( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMSubAdd( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::TupleType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMSubAdd( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline U FMSubAdd( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline ResultT FMSubAdd( const T& a, NumberT b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMSubAdd( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMSubAdd( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U FMSubAdd( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline S FMSubAdd( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMSubAdd( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMSubAdd( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U FMSubAdd( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd >
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline ResultT FMSubAdd( const S& a, const T& b, const U& c ) noexcept;

FNMAdd

Performs a set of negated multiply-add computation on a, b, and c. Corresponding values in two operands, a and b, are multiplied and the negated infinite precision intermediate results are added to the values in the third operand, c, after which the final results are rounded to the nearest floating point values.

template<typename NumberT, Internal::SimdType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FNMAdd( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::TupleType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FNMAdd( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline U FNMAdd( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline ResultT FNMAdd( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FNMAdd( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::TupleType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FNMAdd( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline U FNMAdd( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline ResultT FNMAdd( const T& a, NumberT b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FNMAdd( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FNMAdd( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U FNMAdd( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline S FNMAdd( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FNMAdd( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FNMAdd( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U FNMAdd( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd >
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline ResultT FNMAdd( const S& a, const T& b, const U& c ) noexcept;

FNMSub

Performs a set of negated multiply-subtract computation on a, b, and c. The values in two operands, a and b, are multiplied and the negated infinite precision intermediate result is obtained. From this negated intermediate result, the value in the third operand, c, is subtracted. The final result is rounded to the nearest floating point value.

template<typename NumberT, Internal::SimdType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FNMSub( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::TupleType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FNMSub( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline U FNMSub( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline ResultT FNMSub( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FNMSub( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::TupleType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FNMSub( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline U FNMSub( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline ResultT FNMSub( const T& a, NumberT b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FNMSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FNMSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U FNMSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline S FNMSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FNMSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FNMSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U FNMSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd >
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline ResultT FNMSub( const S& a, const T& b, const U& c ) noexcept;

Sin

Calculates the sine of each element in the argument expressed in radians.

template<Internal::SimdType T>
inline T Sin( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Sin( const T& v ) noexcept;

Cos

Calculates the cosine of each element in the argument expressed in radians.

template<Internal::SimdType T>
inline T Cos( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Cos( const T& v ) noexcept;

Tan

Calculates the tangent of each element in the argument expressed in radians.

template<Internal::SimdType T>
inline T Tan( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Tan( const T& v ) noexcept;

ASin

Calculates the inverse sine of each element in the argument, in radians.

template<Internal::SimdType T>
inline T ASin( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT ASin( const T& v ) noexcept;

ACos

Calculates the inverse cosine of each element in the argument, in radians.

template<Internal::SimdType T>
inline T ACos( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT ACos( const T& v ) noexcept;

ATan

Calculates the inverse tangent of each element in the argument, in radians.

template<Internal::SimdType T>
inline T ATan( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT ATan( const T& v ) noexcept;

ATan2

Calculates the inverse tangent of each element in x divided by the corresponding element in y, in radians.

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline T ATan2( const T& x, const U& y ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline T ATan2( const T& x, const U& y ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline T ATan2( const U& x, const T& y ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires Internal::IsCompatible<T, U>
inline ResultT ATan2( const T& x, const U& y ) noexcept;

ModAngles

Calculates the angle modulo \(2\pi\) of each element in the argument.

template<Internal::SimdType T>
inline T ModAngles( const T& angles );

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT ModAngles( const T& v ) noexcept;

AddAngles

Adds the angles in the corresponding elements of v1 and v2. The argument angles must be in the range \([-\pi,\pi)\), and the computed angles will be in the range \([-\pi,\pi)\).

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline T AddAngles( const T& v1, const U& v2 ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline T AddAngles( const T& v1, const U& v2 ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline T AddAngles( const U& v1, const T& v2 ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires Internal::IsCompatible<T, U>
inline ResultT AddAngles( const T& v1, const U& v2 ) noexcept;

SubtractAngles

Subtracts the angles in v2 from the corresponding elements of v1. The argument angles must be in the range \([-\pi,\pi)\), and the computed angles will be in the range \([-\pi,\pi)\)

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline T SubtractAngles( const T& v1, const U& v2 ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline T SubtractAngles( const T& v1, const U& v2 ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline T SubtractAngles( const U& v1, const T& v2 ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires Internal::IsCompatible<T, U>
inline ResultT SubtractAngles( const T& v1, const U& v2 ) noexcept;

SinH

Calculates the hyperbolic sine of each element in the argument expressed in radians.

template<Internal::SimdType T>
inline T SinH( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT SinH( const T& v ) noexcept;

CosH

Calculates the hyperbolic cosine of each element in the argument expressed in radians.

template<Internal::SimdType T>
inline T CosH( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT CosH( const T& v ) noexcept;

TanH

Calculates the hyperbolic tangent of each element in the argument expressed in radians.

template<Internal::SimdType T>
inline T TanH( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT TanH( const T& v ) noexcept;

ASinH

Calculates the inverse hyperbolic sine of each element in the argument, in radians.

template<Internal::SimdType T>
inline T ASinH( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT ASinH( const T& v ) noexcept;

ACosH

Calculates the inverse hyperbolic cosine of each element in the argument, in radians.

template<Internal::SimdType T>
inline T ACosH( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT ACosH( const T& v ) noexcept;

ATanH

Calculates the inverse hyperbolic tangent of each element in the argument, in radians.

template<Internal::SimdType T>
inline T ATanH( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT ATanH( const T& v ) noexcept;

Log

Calculates the natural logarithm of each element in the argument.

template<Internal::SimdType T>
inline T Log( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Log( const T& v ) noexcept;

Log1P

Calculates the natural logarithm of \(1 +\) each element in the argument.

template<Internal::SimdType T>
inline T Log1P( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Log1P( const T& v ) noexcept;

Log10

Calculates the base-10 logarithm, \(log_{10}\), of each element in the argument.

template<Internal::SimdType T>
inline T Log10( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Log10( const T& v ) noexcept;

Log2

Calculates the base-2 logarithm, \(log_{2}\), of each element in the argument.

template<Internal::SimdType T>
inline T Log2( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Log2( const T& v ) noexcept;

Exp

Calculates \(e\) (Euler’s number, 2.7182818…), raised to the power of each element in the argument.

template<Internal::SimdType T>
inline T Exp( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Exp( const T& v ) noexcept;

Exp10

Calculates the base-10 exponential of each element in the argument.

template<Internal::SimdType T>
inline T Exp10( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Exp10( const T& v ) noexcept;

Exp2

Calculates the base-2 exponential of each element in the argument.

template<Internal::SimdType T>
inline T Exp2( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Exp2( const T& v ) noexcept;

ExpM1

Calculates \(e\) (Euler’s number, 2.7182818…), raised to the power of each element in the argument, \(-1.0\).

template<Internal::SimdType T>
inline T ExpM1( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT ExpM1( const T& v ) noexcept;

Pow

Calculates the elements in base raised to the corresponding elements in exponent.

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline T Pow( const T& base, const U& exponent ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline T Pow( const T& base, const U& exponent ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline T Pow( const U& base, const T& exponent ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires Internal::IsCompatible<T, U>
inline ResultT Pow( const T& base, const U& exponent ) noexcept;

Hypot

Calculates the square root of the sum of the squares of each corresponding element in x and y, without undue overflow or underflow at intermediate stages of the computation.

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline T Hypot( const T& x, const U& y ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline T Hypot( const T& x, const U& y ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline T Hypot( const U& x, const T& y ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires Internal::IsCompatible<T, U>
inline ResultT Hypot( const T& x, const U& y ) noexcept;

Hermite

Calculates the Hermite spline interpolation, using the specified arguments.


template<Internal::SimdType T, Internal::SimdType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::SimdType T, Internal::SimdType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::SimdType T, Internal::SimdType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::SimdType T, Internal::SimdType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::SimdType T, Internal::TupleType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::SimdType T, Internal::TupleType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::SimdType T, Internal::TupleType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::SimdType T, Internal::TupleType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::TupleType T, Internal::SimdType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::TupleType T, Internal::SimdType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::TupleType T, Internal::SimdType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::TupleType T, Internal::SimdType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

Dot and ScalarDot

Calculates the dot product between v1 and v2.

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline T Dot( const T& v1, const U& v2 ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline T Dot( const T& v1, const U& v2 ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline T Dot( const U& v1, const T& v2 ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires Internal::IsCompatible<T, U>
inline ResultT Dot( const T& v1, const U& v2 ) noexcept;

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
constexpr inline typename T::value_type ScalarDot( const T& v1, const U& v2 ) noexcept;

template<Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
constexpr inline typename T::value_type ScalarDot( const T& v1, const U& v2 ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
constexpr inline typename T::value_type ScalarDot( const T& v1, const U& v2 ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::value_type>
    requires Internal::IsCompatible<T, U>
constexpr inline ResultT ScalarDot( const T& v1, const U& v2 ) noexcept;

AbsDot and ScalarAbsDot

Calculates the absolute value of the dot product between v1 and v2.

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline T AbsDot( const T& v1, const U& v2 ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline T AbsDot( const T& v1, const U& v2 ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline T AbsDot( const U& v1, const T& v2 ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires Internal::IsCompatible<T, U>
inline ResultT AbsDot( const T& v1, const U& v2 ) noexcept;

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline auto ScalarAbsDot( const T& v1, const U& v2 ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline auto ScalarAbsDot( const T& v1, const U& v2 ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline auto ScalarAbsDot( const U& v1, const T& v2 ) noexcept;

template<Internal::TupleType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline auto ScalarAbsDot( const T& v1, const U& v2 ) noexcept;

Cross

Calculates the cross product between v1 and v2.

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline T Cross( const T& v1, const U& v2 ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline T Cross( const T& v1, const U& v2 ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline T Cross( const U& v1, const T& v2 ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires Internal::IsCompatible<T, U>
inline ResultT Cross( const T& v1, const U& v2 ) noexcept;

LengthSquared and ScalarLengthSquared

Calculates the squared length of v.

template<Internal::SimdType T>
inline T LengthSquared( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT LengthSquared( const T& v ) noexcept;

template<Internal::SimdType T>
inline auto ScalarLengthSquared( const T& v ) noexcept;

template<Internal::TupleType T>
inline auto ScalarLengthSquared( const T& v ) noexcept;

Length and ScalarLength

Calculates the length of v.

template<Internal::SimdType T>
inline T Length( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Length( const T& v ) noexcept;

template<Internal::SimdType T>
inline auto ScalarLength( const T& v ) noexcept;

template<Internal::TupleType T>
inline auto ScalarLength( const T& v ) noexcept;

Normalize

Normalizes v.

template<Internal::SimdType T>
inline T Normalize( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Normalize( const T& v ) noexcept;

ReciprocalLength and ScalarReciprocalLength

Calculates the reciprocal length of v.

template<Internal::SimdType T>
inline T ReciprocalLength( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT ReciprocalLength( const T& v ) noexcept;

template<Internal::SimdType T>
inline auto ScalarReciprocalLength( const T& v ) noexcept;

template<Internal::TupleType T>
inline auto ScalarReciprocalLength( const T& v ) noexcept;

DistanceSquared and ScalarDistanceSquared

Calculates the squared distance between p1 and p2.

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline T DistanceSquared( const T& p1, const U& p2 ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline T DistanceSquared( const T& p1, const U& p2 ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline T DistanceSquared( const U& p1, const T& p2 ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires Internal::IsCompatible<T, U>
inline ResultT DistanceSquared( const T& p1, const U& p2 ) noexcept;

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline auto ScalarDistanceSquared( const T& p1, const U& p2 ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline auto ScalarDistanceSquared( const T& p1, const U& p2 ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline auto ScalarDistanceSquared( const U& p1, const T& p2 ) noexcept;

template<Internal::TupleType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline auto ScalarDistanceSquared( const T& p1, const U& p2 ) noexcept;

Distance and ScalarDistance

Calculates the distance between p1 and p2.

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline T Distance( const T& p1, const U& p2 ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline T Distance( const T& p1, const U& p2 ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline T Distance( const U& p1, const T& p2 ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires Internal::IsCompatible<T, U>
inline ResultT Distance( const T& p1, const U& p2 ) noexcept;

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline auto ScalarDistance( const T& p1, const U& p2 ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline auto ScalarDistance( const T& p1, const U& p2 ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline auto ScalarDistance( const U& p1, const T& p2 ) noexcept;

InBounds

Detects if the elements of a vector are within bounds.

template<Internal::SimdType S, Internal::SimdType T>
    requires Internal::IsCompatible<S, T>
inline S InBounds( const S& v, const T& bounds ) noexcept;

template<Internal::SimdType S, Internal::TupleType T>
    requires Internal::IsCompatible<S, T>
inline S InBounds( const S& v, const T& bounds ) noexcept;

template<Internal::TupleType S, Internal::SimdType T>
    requires Internal::IsCompatible<S, T>
inline T InBounds( const S& v, const T& bounds ) noexcept;

template<Internal::TupleType S, Internal::TupleType T>
    requires Internal::IsCompatible<S, T>
inline typename S::Simd InBounds( const S& v, const T& bounds ) noexcept;

ClampLength

Clamps the length of a vector to a given range.

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T> && Internal::IsCompatible<S, U>
inline S ClampLength( const S& v, const T& lengthMin, const U& lengthMax ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>
inline S ClampLength( const S& v, const T& lengthMin, const U& lengthMax ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>
inline S ClampLength( const S& v, const T& lengthMin, const U& lengthMax ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>
inline S ClampLength( const S& v, const T& lengthMin, const U& lengthMax ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>
inline typename S::Simd ClampLength( const S& v, const T& lengthMin, const U& lengthMax ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>
inline typename S::Simd ClampLength( const S& v, const T& lengthMin, const U& lengthMax ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>
inline typename S::Simd ClampLength( const S& v, const T& lengthMin, const U& lengthMax ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>
inline typename S::Simd ClampLength( const S& v, const T& lengthMin, const U& lengthMax ) noexcept;

template<Internal::SimdType S, typename T, typename U>
    requires IsFloatingPoint<T> && IsFloatingPoint<U>
inline S ClampLength( const S& v, const T lengthMin, const U lengthMax ) noexcept;

template<Internal::TupleType S, typename T, typename U>
    requires IsFloatingPoint<T>&& IsFloatingPoint<U>
inline S ClampLength( const S& v, const T lengthMin, const U lengthMax ) noexcept;

Reflect

Reflects an incident vector across a normal vector.

template<Internal::SimdType S, Internal::SimdType T>
    requires Internal::IsCompatible<S, T>
inline S Reflect( const S& incident, const T& normal ) noexcept;

template<Internal::SimdType S, Internal::TupleType T>
    requires Internal::IsCompatible<S, T>
inline S Reflect( const S& incident, const T& normal ) noexcept;

template<Internal::TupleType S, Internal::SimdType T>
    requires Internal::IsCompatible<S, T>
inline T Reflect( const S& incident, const T& normal ) noexcept;

template<Internal::TupleType S, Internal::TupleType T>
    requires Internal::IsCompatible<S, T>
inline typename S::Simd Reflect( const S& incident, const T& normal ) noexcept;

Refract

Refracts an incident vector across a normal vector.


template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T> && Internal::IsCompatible<S, U>
inline S Refract( const S& incident, const T& normal, const U& refractionIndex ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>
inline S Refract( const S& incident, const T& normal, const U& refractionIndex ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>
inline S Refract( const S& incident, const T& normal, const U& refractionIndex ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>
inline S Refract( const S& incident, const T& normal, const U& refractionIndex ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>
inline typename S::Simd Refract( const S& incident, const T& normal, const U& refractionIndex ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>
inline typename S::Simd Refract( const S& incident, const T& normal, const U& refractionIndex ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>
inline typename S::Simd Refract( const S& incident, const T& normal, const U& refractionIndex ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>
inline typename S::Simd Refract( const S& incident, const T& normal, const U& refractionIndex ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, typename U>
    requires Internal::IsCompatible<S, T>&& IsFloatingPoint<U>
inline S Refract( const S& incident, const T& normal, const U refractionIndex ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, typename U>
    requires Internal::IsCompatible<S, T>&& IsFloatingPoint<U>
inline S Refract( const S& incident, const T& normal, const U refractionIndex ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, typename U>
    requires Internal::IsCompatible<S, T>&& IsFloatingPoint<U>
inline typename S::Simd Refract( const S& incident, const T& normal, const U refractionIndex ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, typename U>
    requires Internal::IsCompatible<S, T>&& IsFloatingPoint<U>
inline typename S::Simd Refract( const S& incident, const T& normal, const U refractionIndex ) noexcept;

Orthogonal

Computes a vector perpendicular to the argument vector.

template<Internal::SimdType S>
inline S Orthogonal( const S& v ) noexcept;

template<Internal::TupleType S>
inline typename S::Simd Orthogonal( const S& v ) noexcept;

DifferenceOfProducts

Calculates the difference between the product of the first and the second argument, and the product of the third and fourth argument.

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, Internal::SimdType V>
    requires Internal::IsCompatible<S, T> && Internal::IsCompatible<S, U> && Internal::IsCompatible<S, V>
inline T DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline S DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U, Internal::SimdType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline S DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline S DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, Internal::SimdType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline S DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline S DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U, Internal::SimdType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline S DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline S DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline T DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U, Internal::SimdType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline T DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline T DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, Internal::SimdType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline U DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline U DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, Internal::SimdType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline V DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline typename S::Simd DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

SumOfProducts

Calculates the sum of the product of the first and the second argument, and the product of the third and fourth argument.

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, Internal::SimdType V>
    requires Internal::IsCompatible<S, T> && Internal::IsCompatible<S, U> && Internal::IsCompatible<S, V>
inline T SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline S SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U, Internal::SimdType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline S SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline S SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, Internal::SimdType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline S SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline S SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U, Internal::SimdType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline S SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline S SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline T SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U, Internal::SimdType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline T SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline T SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, Internal::SimdType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline U SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline U SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, Internal::SimdType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline V SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline typename S::Simd SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

BaryCentric

Calculates a point in Barycentric coordinates, using the specified triangle.

see https://en.wikipedia.org/wiki/Barycentric_coordinate_system

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U> && Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, typename V, typename W>
    requires Internal::IsCompatible<S, T> && Internal::IsCompatible<S, U> && IsFloatingPoint<V> && IsFloatingPoint<W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V f, const W g ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U, typename V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& IsFloatingPoint<V>&& IsFloatingPoint<W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V f, const W g ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, typename V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& IsFloatingPoint<V>&& IsFloatingPoint<W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V f, const W g ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U, typename V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& IsFloatingPoint<V>&& IsFloatingPoint<W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V f, const W g ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U, typename V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& IsFloatingPoint<V>&& IsFloatingPoint<W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V f, const W g ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U, typename V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& IsFloatingPoint<V>&& IsFloatingPoint<W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V f, const W g ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, typename V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& IsFloatingPoint<V>&& IsFloatingPoint<W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V f, const W g ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, typename V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& IsFloatingPoint<V>&& IsFloatingPoint<W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V f, const W g ) noexcept;

CatmullRom

Calculates the Catmull-Rom interpolation, using the specified positions.

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;


template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, Internal::SimdType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, Internal::TupleType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U, Internal::SimdType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U, Internal::TupleType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, Internal::SimdType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, Internal::TupleType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U, Internal::SimdType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U, Internal::TupleType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U, Internal::SimdType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U, Internal::TupleType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U, Internal::SimdType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U, Internal::TupleType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, Internal::SimdType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, Internal::TupleType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, Internal::SimdType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, Internal::TupleType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

MinComponentValue

Retrieves the lowest value held by the argument.

template<Internal::SimdType T, typename ResultT = typename T::value_type >
inline ResultT MinComponentValue( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::value_type>
constexpr inline ResultT MinComponentValue( const T& v ) noexcept;

MaxComponentValue

Retrieves the highest value held by the argument.

template<Internal::SimdType T, typename ResultT = typename T::value_type >
inline ResultT MaxComponentValue( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::value_type>
constexpr inline ResultT MaxComponentValue( const T& v ) noexcept;

MinComponentIndex

Retrieves the offset of the lowest value held by the argument.

template<Internal::SimdType T, typename ResultT = typename T::value_type >
inline ResultT MinComponentIndex( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::value_type>
constexpr inline ResultT MinComponentIndex( const T& v ) noexcept;

MaxComponentIndex

Retrieves the offset of the highest value held by the argument.

template<Internal::SimdType T, typename ResultT = typename T::value_type >
inline ResultT MaxComponentIndex( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::value_type>
constexpr inline ResultT MaxComponentIndex( const T& v ) noexcept;

Vector2f, Vector3f, Vector4f, Vector2i, Vector3i, Vector4i

Vector<float,2>, Vector<float,3> and Vector<float,4> are specializations of Vector<T,N> that supports a wider repertoire of operations than the general Vector<T,N>.

Vector<float,2> is derived from the Tuple2 template, Vector<float,3> is derived from the Tuple3, and Vector<float,4> is derived from Tuple4.

Just a few lines of code are required to create the Vector<float, 3> specialization of Vector<T,N> with the full repertoire of features available for Tuple3:

template<>
class Vector<float, 3> : public Tuple3<Vector<float, 3>,float>
{
public:
    using Base = Tuple3<Vector<float, 3>, float>;
    using Traits = Base::Traits;

    Vector( ) noexcept = default;
    explicit Vector( float v ) noexcept
        : Base( v, v, v )
    { }
    Vector( float xv, float yv, float zv ) noexcept
        : Base( xv, yv, zv )
    { }

    template<typename T>
        requires std::is_same_v<typename T::SIMDType, typename Traits::SIMDType >
    Vector( const T& other ) noexcept
        : Base( other )
    { }
};

Below are two benchmarks, the first using Math::Vector<float, 3>, while the second uses pbrt::Vector3f.

static void BenchmarkVector3( benchmark::State& state )
{
    using namespace Harlinn::Common::Core::Math;
    using Vector = Math::Vector<float, 3>;
    DoubleGenerator.Reset( );

    for ( auto _ : state )
    {
        Vector v1( FloatGenerator( ), FloatGenerator( ), FloatGenerator( ) );
        Vector v2( FloatGenerator( ), FloatGenerator( ), FloatGenerator( ) );
        benchmark::DoNotOptimize( Dot( Abs( Max( Ceil( -v1 ), Floor( v2 ) ) ), v2 ) );
    }
}
BENCHMARK( BenchmarkVector3 );

static void BenchmarkPBRTVector3f( benchmark::State& state )
{
    using namespace pbrt;
    using Vector = pbrt::Vector3f;
    DoubleGenerator.Reset( );


    for ( auto _ : state )
    {
        Vector v1( FloatGenerator( ), FloatGenerator( ), FloatGenerator( ) );
        Vector v2( FloatGenerator( ), FloatGenerator( ), FloatGenerator( ) );
        benchmark::DoNotOptimize( Dot( Abs( Max( Ceil( -v1 ), Floor( v2 ) ) ), v2 ) );
    }
}
BENCHMARK( BenchmarkPBRTVector3f );

BenchmarkVector2 runs 20 % faster than BenchmarkPBRTVector3f which is optimized by the compiler for the AVX2 instruction set:

-------------------------------------------------------------------
Benchmark                         Time             CPU   Iterations
-------------------------------------------------------------------
BenchmarkVector3               5.63 ns         4.85 ns    186666667
BenchmarkPBRTVector3f          7.08 ns         5.86 ns    112000000

Vector functions

AngleBetween

Calculates the angle in radians between two vectors.

AngleBetweenNormals

Calculates the angle in radians between two normalized vectors.

Transform

Transforms a 3D vector by a matrix.

inline Vector<float, 3>::Simd Transform( const Vector<float, 3>::Simd& v, 
                                        const SquareMatrix<float, 4>::Simd& matrix );

inline Vector<float, 3>::Simd Transform( const Vector<float, 3>& v, 
                                        const SquareMatrix<float, 4>::Simd& matrix );

inline Vector<float, 3>::Simd Transform( const Vector<float, 3>::Simd& v, 
                                        const SquareMatrix<float, 4>& matrix );

inline Vector<float, 3>::Simd Transform( const Vector<float, 3>& v, 
                                        const SquareMatrix<float, 4>& matrix );

Normal3f and Normal3i

The surface normal is a vector perpendicular to a surface at a specific position. It can be defined as the cross product of any two nonparallel vectors that are tangent to the surface at a point. Normals are similar to vectors, but it’s important to distinguish between the two of them: since normals are defined in terms of their relationship to a surface, they behave differently than vectors in some situations, particularly when applying transformations.

Normal3f functions

Transform

Transforms the Normal3f object by the given matrix.

inline Normal3f::Simd Transform( const Normal3f::Simd& v, 
                                const SquareMatrix<float, 3>::Simd& matrix );

inline Normal3f::Simd Transform( const Normal3f::Simd& v, 
                                const SquareMatrix<float, 4>::Simd& matrix );

inline Normal3f::Simd Transform( const Normal3f::Simd& v, 
                                const SquareMatrix<float, 4>::Simd& matrix );

inline Normal3f::Simd Transform( const Normal3f& v, 
                                const SquareMatrix<float, 3>::Simd& matrix );

inline Normal3f::Simd Transform( const Normal3f& v, 
                                const SquareMatrix<float, 4>::Simd& matrix );

inline Normal3f::Simd Transform( const Normal3f::Simd& v, 
                                const SquareMatrix<float, 3>& matrix );

inline Normal3f::Simd Transform( const Normal3f::Simd& v, 
                                const SquareMatrix<float, 4>& matrix );

inline Normal3f::Simd Transform( const Normal3f& v, 
                                const SquareMatrix<float, 3>& matrix );

inline Normal3f::Simd Transform( const Normal3f& v, 
                                const SquareMatrix<float, 4>& matrix );

Point3f and Point3i

A point is a zero-dimensional location in 2D or 3D space. The Point2i/f and Point3i/f classes represent points in the obvious way: using \(x, y, z\) (in 3D) coordinates with respect to a coordinate system. Although the same representation is used for vectors, the fact that a point represents a position whereas a vector represents a direction leads to a number of important differences in how they are treated.

Point3f

LinePointDistance

Calculates the minimum distance between a line and a point.

Transform

Transforms the Point3f object by the provided transformation matrix.


inline Point3f::Simd Transform( const Point3f::Simd& v, 
                                const SquareMatrix<float, 4>::Simd& matrix );

inline Point3f::Simd Transform( const Point3f& v, 
                                const SquareMatrix<float, 4>::Simd& matrix );

inline Point3f::Simd Transform( const Point3f::Simd& v, 
                                const SquareMatrix<float, 4>& matrix );

inline Point3f::Simd Transform( const Point3f& v, 
                                const SquareMatrix<float, 4>& matrix );

SquareMatrix

The SquareMatrix template class is used to represent transformations that can be applied to vectors, points and surface normals.

The template is designed as a row oriented matrix, that can be instantiated for single and double precision floating point types. The template can be instantiated as a \(2 x 2\), \(3 x 3\) or \(4 x 4\) matrix.

Internally, computations on \(2 x 2\) matrices are performed using a single SIMD type/register, while operations on \(3 x 3\) and \(4 x 4\) matrices are performed using one SIMD type/register for each row.

SquareMatrix support matrix addition, scalar addition, matrix multiplication, scalar multiplication, the matrices can be transposed using Transpose and inverted using Inverse, and Determinant calculates the determinant. The default constructor creates an identity matrix, so SquareMatrix handles the usual operations associated with small square matrices.

SquareMatrix<float,4> functions

Transpose

Calculates the transpose of the matrix.

inline SquareMatrix<float, 4>::Simd Transpose( const SquareMatrix<float, 4>::Simd& matrix );

inline SquareMatrix<float, 4>::Simd Transpose( const SquareMatrix<float, 4>& matrix );

Inverse

Calculates the inverse of the matrix.

inline SquareMatrix<float, 4>::Simd Inverse( const SquareMatrix<float, 4>::Simd& matrix, 
                                            typename Vector<float, 4>::Simd* determinant = nullptr );

inline typename SquareMatrix<float, 4>::Simd Inverse( const SquareMatrix<float, 4>& matrix, 
                                            typename Vector<float, 4>::Simd* determinant = nullptr );

Determinant and ScalarDeterminant

Calculates the determinant of a matrix.

inline typename Vector<float,4>::Simd Determinant( const typename SquareMatrix<float, 4>::Simd& matrix );

inline typename Vector<float, 4>::Simd Determinant( const SquareMatrix<float, 4>& matrix );

inline float ScalarDeterminant( const SquareMatrix<float, 4>::Simd& matrix );

inline float ScalarDeterminant( const SquareMatrix<float, 4>& matrix );

Translation

Creates a translation matrix using the provided offsets.

inline SquareMatrix<float, 4>::Simd Translation( float offsetX, float offsetY, float offsetZ );

template<Internal::SimdType S>
    requires (S::Size > 2) && std::is_same_v<typename S::value_type, float>
inline SquareMatrix<float, 4>::Simd Translation( const S& offsets );

template<Internal::TupleType S>
    requires ( S::Size > 2 ) && std::is_same_v<typename S::value_type, float>
inline SquareMatrix<float, 4>::Simd Translation( const S& offsets );

Scaling

Creates a transformation matrix for scaling along the x-axis, y-axis, and z-axis.

inline SquareMatrix<float, 4>::Simd Scaling( float scaleX, float scaleY, float scaleZ );

template<Internal::SimdType S>
    requires ( S::Size > 2 )
inline SquareMatrix<float, 4>::Simd Scaling( const S& v ) noexcept;

template<Internal::TupleType S>
    requires ( S::Size > 2 )
inline SquareMatrix<float, 4>::Simd Scaling( const S& v ) noexcept;

Rotation

Creates a transformation matrix that rotates about the y-axis, then the x-axis, and finally the z-axis.

template<Internal::SimdType S>
    requires (S::Size > 2)
inline SquareMatrix<float, 4>::Simd Rotation( const S& v ) noexcept;

template<Internal::TupleType S>
    requires ( S::Size > 2 )
inline SquareMatrix<float, 4>::Simd Rotation( const S& v ) noexcept;

inline SquareMatrix<float, 4>::Simd Rotation( float xAxisRotation, 
                        float yAxisRotation, float zAxisRotation ) noexcept;

RotationNormal

Creates a matrix that rotates around a normalized vector.

template<Internal::SimdType S>
    requires ( S::Size > 2 ) 
inline SquareMatrix<float, 4>::Simd RotationNormal( const S& normalizedAxis, float angle ) noexcept;

template<Internal::TupleType S>
    requires ( S::Size > 2 )
inline SquareMatrix<float, 4>::Simd RotationNormal( const S& normalizedAxis, float angle ) noexcept;

RotationAxis

Creates a matrix that rotates around an arbitrary axis.

template<Internal::SimdType S>
    requires ( S::Size > 2 )
inline SquareMatrix<float, 4>::Simd RotationAxis( const S& axis, float angle ) noexcept;

template<Internal::TupleType S>
    requires ( S::Size > 2 )
inline SquareMatrix<float, 4>::Simd RotationAxis( const S& axis, float angle ) noexcept;

RotationQuaternion

Creates a rotation matrix from a quaternion.

inline SquareMatrix<float, 4>::Simd RotationQuaternion( const QuaternionSimd<Quaternion<float>>& q );

inline SquareMatrix<float, 4>::Simd RotationQuaternion( const Quaternion<float>& q );

TransformationMatrix

Creates a transformation matrix.

inline SquareMatrix<float, 4>::Simd TransformationMatrix( const Point3f::Simd& scalingOrigin, 
                            const QuaternionSimd<Quaternion<float>>& scalingOrientationQuaternion, 
                            const Vector<float,3>::Simd& scaling,
                            const Point3f::Simd& rotationOrigin, 
                            const QuaternionSimd<Quaternion<float>>& rotationQuaternion, 
                            const Vector<float, 3>::Simd& translation ) noexcept;


AffineTransformationMatrix

Creates an affine transformation matrix.

template<Internal::SimdType S, Internal::SimdType T, typename U, Internal::SimdType W>
        requires (S::Size > 2) && (T::Size > 2) && (W::Size > 2) && IsFloatingPoint<U> &&
            std::is_same_v<typename S::value_type,U> && 
                std::is_same_v<typename T::value_type, U> && 
                std::is_same_v<typename W::value_type, U>
    inline SquareMatrix<U, 4>::Simd AffineTransformationMatrix( const S& scaling,
                                        const T& rotationOrigin,
                                        const QuaternionSimd<Quaternion<U>>& rotationQuaternion,
                                        const W& translation ) noexcept;

LookTo

Creates a view matrix using the left-handed coordinate system for the provided camera position, camera direction, and up direction.

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S,T> && Internal::IsCompatible<S, U> && (S::Size == 3)
inline SquareMatrix<typename S::value_type, 4>::Simd LookTo( const S& cameraPosition, 
                                                            const T& cameraDirection, 
                                                            const U& upDirection ) noexcept;

LookAt

Creates a view matrix using the left-handed coordinate system for the provided camera position, focal point, and up direction.

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U> && ( S::Size == 3 )
inline SquareMatrix<typename S::value_type, 4>::Simd LookAt( const S& cameraPosition, 
                                                            const T& focusPosition, 
                                                            const U& upDirection ) noexcept;

PerspectiveProjection

Creates a left-handed perspective projection matrix.

template<typename T>
    requires IsFloatingPoint<T>
inline SquareMatrix<T, 4>::Simd PerspectiveProjection( T viewWidth, 
                                                    T viewHeight, 
                                                    T nearZ, 
                                                    T farZ ) noexcept;

PerspectiveFovProjection

Creates a left-handed perspective projection matrix based on a field of view.

template<typename T>
    requires IsFloatingPoint<T>
inline SquareMatrix<T, 4>::Simd PerspectiveFovProjection( T fovAngleY, 
                                                        T aspectRatio, 
                                                        T nearZ, 
                                                        T farZ ) noexcept;

Transform

Applies a transformation matrix to a 3D vector.

inline Vector<float, 3>::Simd Transform( const Vector<float, 3>::Simd& v, 
                                        const SquareMatrix<float, 4>::Simd& matrix );

inline Vector<float, 3>::Simd Transform( const Vector<float, 3>& v, 
                                        const SquareMatrix<float, 4>::Simd& matrix );

inline Vector<float, 3>::Simd Transform( const Vector<float, 3>::Simd& v, 
                                        const SquareMatrix<float, 4>& matrix );

inline Vector<float, 3>::Simd Transform( const Vector<float, 3>& v, 
                                        const SquareMatrix<float, 4>& matrix );

Applies a transformation matrix to a 3D coordinate.

inline Point3f::Simd Transform( const Point3f::Simd& p, 
                                const SquareMatrix<float, 4>::Simd& matrix );

inline Point3f::Simd Transform( const Point3f& p, 
                                const SquareMatrix<float, 4>::Simd& matrix );

inline Point3f::Simd Transform( const Point3f::Simd& p, 
                                const SquareMatrix<float, 4>& matrix );

inline Point3f::Simd Transform( const Point3f& p, 
                                const SquareMatrix<float, 4>& matrix );

Applies a transformation matrix to a normal.

inline Normal3f::Simd Transform( const Normal3f::Simd& n, 
                                const SquareMatrix<float, 4>::Simd& matrix );

inline Normal3f::Simd Transform( const Normal3f& n, 
                                const SquareMatrix<float, 4>::Simd& matrix );

inline Normal3f::Simd Transform( const Normal3f::Simd& n, 
                                const SquareMatrix<float, 4>& matrix );

inline Normal3f::Simd Transform( const Normal3f& n, 
                                const SquareMatrix<float, 4>& matrix );

Quaternion

Quaternions provides a compact representation of rotations.

The Quaternion template can be used with both single and double precision floating point types.

Like the tuple templates, there is a QuaternionSimd template that represents quaternions loaded into a SIMD register.

Quaternion and QuaternionSimd supports addition, subtraction, multiplication, scalar multiplication and scalar division.

Quaternion member functions

Constructors

constexpr Quaternion( ) noexcept;

The default constructor initializes v.x, v.y, v.z and w to 0.0.

constexpr Quaternion( ValueType xv, ValueType yv, ValueType zv, ValueType wv ) noexcept;

Initializes v.x to xv, v.y to yv, v.z to zv and w to wv.

constexpr Quaternion( const Vector<ValueType,3>& vv, ValueType wv ) noexcept;

Initializes v to xv and w to wv.

Quaternion( const Simd& qsimd ) noexcept

Initializes the quaternion using the values held by qsimd.simd.

Quaternion( ValueType pitch, ValueType yaw, ValueType roll ) noexcept

Creates a quaternion based on the pitch, yaw, and roll (Euler angles), where:

  • pitch is the angle of rotation around the x-axis, in radians.
  • yaw is the angle of rotation around the y-axis, in radians.
  • roll is the angle of rotation around the z-axis, in radians.

Quaternion functions

Dot

Calculates the dot product of two quaternions.

template<typename T>
    requires IsFloatingPoint<T>
typename Vector<T,4>::Simd Dot( const QuaternionSimd<Quaternion<T>>& q1, 
                                const QuaternionSimd<Quaternion<T>>& q2 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
typename Vector<T, 4>::Simd Dot( const Quaternion<T>& q1, 
                                const QuaternionSimd<Quaternion<T>>& q2 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
typename Vector<T, 4>::Simd Dot( const QuaternionSimd<Quaternion<T>>& q1, 
                                const Quaternion<T>& q2 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
typename Vector<T, 4>::Simd Dot( const Quaternion<T>& q1, 
                                const Quaternion<T>& q2 ) noexcept;

Length and ScalarLength

Calculates the magnitude of a quaternion.

template<typename T>
    requires IsFloatingPoint<T>
typename Vector<T, 4>::Simd Length( const QuaternionSimd<Quaternion<T>>& q1 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
typename Vector<T, 4>::Simd Length( const Quaternion<T>& q1 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
typename T ScalarLength( const QuaternionSimd<Quaternion<T>>& q1 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
typename T ScalarLength( const Quaternion<T>& q1 ) noexcept;

LengthSquared and ScalarLengthSquared

Calculates the square of the magnitude of a quaternion.

template<typename T>
    requires IsFloatingPoint<T>
typename Vector<T, 4>::Simd LengthSquared( const QuaternionSimd<Quaternion<T>>& q1 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
typename Vector<T, 4>::Simd LengthSquared( const Quaternion<T>& q1 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
typename T ScalarLengthSquared( const QuaternionSimd<Quaternion<T>>& q1 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
typename T ScalarLengthSquared( const Quaternion<T>& q1 ) noexcept;

ReciprocalLength and ScalarReciprocalLength

Calculates the reciprocal of the magnitude of a quaternion.

template<typename T>
    requires IsFloatingPoint<T>
typename Vector<T, 4>::Simd ReciprocalLength( const QuaternionSimd<Quaternion<T>>& q1 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
typename Vector<T, 4>::Simd ReciprocalLength( const Quaternion<T>& q1 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
typename T ScalarReciprocalLength( const QuaternionSimd<Quaternion<T>>& q1 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
typename T ScalarReciprocalLength( const Quaternion<T>& q1 ) noexcept;

Conjugate

Calculates the conjugate of a quaternion.

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Conjugate( const Quaternion<T>& q1 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Normalize( const QuaternionSimd<Quaternion<T>>& q1 ) noexcept;

Normalize

Normalizes a quaternion.

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Normalize( const QuaternionSimd<Quaternion<T>>& q1 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Normalize( const Quaternion<T>& q1 ) noexcept;

Inverse

Calculates the inverse of a quaternion.

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Inverse( const QuaternionSimd<Quaternion<T>>& q1 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Inverse( const Quaternion<T>& q1 ) noexcept;

Log

Calculates the natural logarithm of a unit quaternion.

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Log( const QuaternionSimd<Quaternion<T>>& q1 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Log( const Quaternion<T>& q1 ) noexcept;

Slerp

Spherical linear interpolation between two unit quaternions.

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Slerp( const QuaternionSimd<Quaternion<T>>& q1, 
                                    const QuaternionSimd<Quaternion<T>>& q2, 
                                    const typename Vector<T, 4>::Simd& t ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Slerp( const QuaternionSimd<Quaternion<T>>& q1, 
                                    const QuaternionSimd<Quaternion<T>>& q2, 
                                    const Vector<T, 4>& t ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Slerp( const QuaternionSimd<Quaternion<T>>& q1, 
                                    const Quaternion<T>& q2, 
                                    const typename Vector<T, 4>::Simd& t ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Slerp( const QuaternionSimd<Quaternion<T>>& q1, 
                                    const Quaternion<T>& q2, 
                                    const Vector<T, 4>& t ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Slerp( const Quaternion<T>& q1, 
                                    const QuaternionSimd<Quaternion<T>>& q2, 
                                    const typename Vector<T, 4>::Simd& t ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Slerp( const Quaternion<T>& q1, 
                                    const QuaternionSimd<Quaternion<T>>& q2, 
                                    const Vector<T, 4>& t ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Slerp( const Quaternion<T>& q1, 
                                    const Quaternion<T>& q2, 
                                    const typename Vector<T, 4>::Simd& t ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Slerp( const Quaternion<T>& q1, 
                                    const Quaternion<T>& q2, 
                                    const Vector<T, 4>& t ) noexcept;

Transform

A transform is a mapping from vectors to vectors or points to points.

Taking a point, vector, or normal defined with respect to one coordinate frame and finding its coordinate values with respect to another coordinate frame is a useful capability.

Benchmarks

--------------------------------------------------------------------------------------
Benchmark                                            Time             CPU   Iterations
--------------------------------------------------------------------------------------
BenchmarkPointRotationAxis                        13.4 ns         10.5 ns     64000000
BenchmarkPointXMMatrixRotationAxis                20.2 ns         16.5 ns     56000000
BenchmarkVectorRotationAxis                       13.5 ns         10.5 ns     64000000
BenchmarkVectorXMMatrixRotationAxis               19.3 ns         16.1 ns     40727273
BenchmarkNormalRotationAxis                       13.2 ns         10.9 ns     74334815
BenchmarkNormalXMMatrixRotationAxis               19.8 ns         17.1 ns     44800000
BenchmarkPointTranslation                         3.53 ns         2.85 ns    263529412
BenchmarkPointXMMatrixTranslation                 3.85 ns         2.98 ns    235789474
BenchmarkVectorTranslation                        2.89 ns         2.51 ns    298666667
BenchmarkVectorXMMatrixTranslation                3.93 ns         3.31 ns    235789474
BenchmarkNormalTranslation                        2.21 ns         1.83 ns    298666667
BenchmarkNormalXMMatrixTranslation                2.78 ns         2.02 ns    263529412
BenchmarkPointScaling                             3.48 ns         2.90 ns    280000000
BenchmarkPointXMMatrixScaling                     3.76 ns         3.28 ns    224000000
BenchmarkVectorScaling                            3.59 ns         3.20 ns    248888889
BenchmarkVectorXMMatrixScaling                    4.51 ns         3.85 ns    194782609
BenchmarkNormalScaling                            3.58 ns         2.92 ns    203636364
BenchmarkNormalXMMatrixScaling                    3.80 ns         2.76 ns    248888889
BenchmarkPointTransformationMatrix                3.28 ns         2.58 ns    344615385
BenchmarkPointXMMatrixTransformation              3.25 ns         2.68 ns    280000000
BenchmarkVectorTransformationMatrix               3.20 ns         2.61 ns    263529412
BenchmarkVectorXMMatrixTransformation             3.35 ns         2.92 ns    235789474
BenchmarkNormalTransformationMatrix               3.33 ns         2.55 ns    263529412
BenchmarkNormalXMMatrixTransformation             3.04 ns         2.73 ns    320000000
BenchmarkPointAffineTransformationMatrix          34.0 ns         24.6 ns     40727273
BenchmarkPointXMMatrixAffineTransformation        30.7 ns         27.3 ns     32000000
BenchmarkVectorAffineTransformationMatrix         33.6 ns         28.5 ns     26352941
BenchmarkVectorXMMatrixAffineTransformation       31.6 ns         24.5 ns     24888889
BenchmarkNormalAffineTransformationMatrix         32.9 ns         26.4 ns     24888889
BenchmarkNormalXMMatrixAffineTransformation       30.7 ns         25.6 ns     29866667
BenchmarkPointLookTo                              17.9 ns         15.4 ns     64000000
BenchmarkPointXMMatrixLookToLH                    24.2 ns         17.6 ns     32000000
BenchmarkVectorLookTo                             17.5 ns         13.7 ns     56000000
BenchmarkVectorXMMatrixLookToLH                   23.5 ns         20.3 ns     40727273
BenchmarkNormalLookTo                             17.6 ns         14.4 ns     64000000
BenchmarkNormalXMMatrixLookToLH                   23.1 ns         20.5 ns     32000000
BenchmarkPointLookAt                              19.5 ns         16.2 ns     56000000
BenchmarkPointXMMatrixLookAtLH                    28.8 ns         26.2 ns     29866667
BenchmarkVectorLookAt                             19.3 ns         15.7 ns     74666667
BenchmarkVectorXMMatrixLookAtLH                   26.8 ns         20.5 ns     32000000
BenchmarkNormalLookAt                             18.5 ns         15.1 ns     49777778
BenchmarkNormalXMMatrixLookAtLH                   27.9 ns         23.1 ns     34461538
BenchmarkPointPerspectiveProjection               3.11 ns         2.57 ns    248888889
BenchmarkPointXMMatrixPerspectiveLH               4.58 ns         2.68 ns    186666667
BenchmarkVectorPerspectiveProjection              3.31 ns         2.79 ns    280000000
BenchmarkVectorXMMatrixPerspectiveLH              4.14 ns         3.93 ns    194782609
BenchmarkVectorPerspectiveProjection              3.30 ns         2.76 ns    248888889
BenchmarkNormalXMMatrixPerspectiveLH              3.42 ns         3.08 ns    213333333
BenchmarkPointPerspectiveFovProjection            11.9 ns         8.54 ns     64000000
BenchmarkPointXMMatrixPerspectiveFovLH            16.6 ns         12.8 ns     56000000
BenchmarkVectorPerspectiveFovProjection           11.3 ns         8.28 ns    100000000
BenchmarkVectorXMMatrixPerspectiveFovLH           15.6 ns         14.2 ns     56000000
BenchmarkNormalPerspectiveFovProjection           10.5 ns         8.59 ns    100000000
BenchmarkNormalXMMatrixPerspectiveFovLH           15.6 ns         11.5 ns     64000000
BenchmarkPointProject                             43.9 ns         36.8 ns     20363636
BenchmarkPointXMVector3Project                    57.6 ns         47.4 ns     11200000