module dlib.aliases;

// import core.memory;
//import std.stdint;
import dlib.math;

enum string NO_IMPL = "Not yet implemented.";

version(WebAssembly)
{
	enum bool NativeTarget = false;
}
else
{
	enum bool NativeTarget = true;
}

debug
{
	const BUILD_DEBUG = true;
}
else
{
	const BUILD_DEBUG = false;
}

alias i8      = byte;
alias i16     = short;
alias i32     = int;
alias i64     = long;

alias u8      = ubyte;
alias u16     = ushort;
alias u32     = uint;
alias u64     = ulong;

alias f32     = float;
alias f64     = double;

alias b32     = uint;

alias uintptr = size_t;
alias usize   = size_t;

alias Vec2    = Vector!(f32, 2);
alias Vec3    = Vector!(f32, 3);
alias Vec4    = Vector!(f32, 4);

alias DVec2   = Vector!(f64, 2);
alias DVec3   = Vector!(f64, 3);
alias DVec4   = Vector!(f64, 4);

alias IVec2   = Vector!(i32, 2);
alias IVec3   = Vector!(i32, 3);
alias IVec4   = Vector!(i32, 4);

alias I8Vec2  = Vector!(i8, 2);
alias I8Vec3  = Vector!(i8, 3);
alias I8Vec4  = Vector!(i8, 4);

alias I16Vec2 = Vector!(i16, 2);
alias I16Vec3 = Vector!(i16, 3);
alias I16Vec4 = Vector!(i16, 4);

alias I32Vec2 = IVec2;
alias I32Vec3 = IVec3;
alias I32Vec4 = IVec4;

alias I64Vec2 = Vector!(i64, 2);
alias I64Vec3 = Vector!(i64, 3);
alias I64Vec4 = Vector!(i64, 4);

alias UVec2   = Vector!(u32, 2);
alias UVec3   = Vector!(u32, 3);
alias UVec4   = Vector!(u32, 4);

alias U8Vec2  = Vector!(u8, 2);
alias U8Vec3  = Vector!(u8, 3);
alias U8Vec4  = Vector!(u8, 4);

alias U16Vec2 = Vector!(u16, 2);
alias U16Vec3 = Vector!(u16, 3);
alias U16Vec4 = Vector!(u16, 4);

alias U32Vec2 = UVec2;
alias U32Vec3 = UVec3;
alias U32Vec4 = UVec4;

alias U64Vec2 = Vector!(u64, 2);
alias U64Vec3 = Vector!(u64, 3);
alias U64Vec4 = Vector!(u64, 4);

alias Mat2    = Matrix!(f32, 2);
alias Mat3    = Matrix!(f32, 3);
alias Mat4    = Matrix!(f32, 4);

alias DMat2   = Matrix!(f64, 2);
alias DMat3   = Matrix!(f64, 3);
alias DMat4   = Matrix!(f64, 4);

/*

enum bool isFloatingPoint(T) = __traits(isFloating, T) && is(T: real);
enum bool isSigned(T)        = __traits(isArithmetic, T) && !__traits(isUnsigned, T) && is(T: real);

template isIntegral(T)
{
	static if(!__traits(isIntegral, T))
	{
		enum isIntegral = false;
	}
	else static if(is(T U == enum))
	{
		enum isIntegral = isIntegral!U;
	}
	else
	{
		enum isIntegral = __traits(isZeroInit, T) && !is(immutable T == immutable bool) && !is(T == __vector);
	}
}

template isNumeric(T)
{
	static if (!__traits(isArithmetic, T))
	{
		enum isNumeric = false;
	}
	else static if (__traits(isFloating, T))
	{
		enum isNumeric = is(T : real); // Not __vector, imaginary, or complex.
	}
	else static if (is(T U == enum))
	{
		enum isNumeric = isNumeric!U;
	}
	else
	{
		enum isNumeric = __traits(isZeroInit, T) // Not char, wchar, or dchar.
			&& !is(immutable T == immutable bool) && !is(T == __vector);
	}
}

enum RealFormat
{
	ieeeHalf,
	ieeeSingle,
	ieeeDouble,
	ieeeExtended,   // x87 80-bit real
	ieeeExtended53, // x87 real rounded to precision of double.
	ibmExtended,    // IBM 128-bit extended
	ieeeQuadruple,
}

template floatTraits(T)
{
    // EXPMASK is a ushort mask to select the exponent portion (without sign)
    // EXPSHIFT is the number of bits the exponent is left-shifted by in its ushort
    // EXPBIAS is the exponent bias - 1 (exp == EXPBIAS yields ×2^-1).
    // EXPPOS_SHORT is the index of the exponent when represented as a ushort array.
    // SIGNPOS_BYTE is the index of the sign when represented as a ubyte array.
    // RECIP_EPSILON is the value such that (smallest_subnormal) * RECIP_EPSILON == T.min_normal
    enum Unqual!T RECIP_EPSILON = (1/T.epsilon);
    static if (T.mant_dig == 24)
    {
			// Single precision float
			enum ushort EXPMASK = 0x7F80;
			enum ushort EXPSHIFT = 7;
			enum ushort EXPBIAS = 0x3F00;
			enum uint EXPMASK_INT = 0x7F80_0000;
			enum uint MANTISSAMASK_INT = 0x007F_FFFF;
			enum realFormat = RealFormat.ieeeSingle;
			version (LittleEndian)
			{
				enum EXPPOS_SHORT = 1;
				enum SIGNPOS_BYTE = 3;
			}
			else
			{
				enum EXPPOS_SHORT = 0;
				enum SIGNPOS_BYTE = 0;
			}
    }
    else static if (T.mant_dig == 53)
    {
			static if (T.sizeof == 8)
			{
					// Double precision float, or real == double
				enum ushort EXPMASK = 0x7FF0;
				enum ushort EXPSHIFT = 4;
				enum ushort EXPBIAS = 0x3FE0;
				enum uint EXPMASK_INT = 0x7FF0_0000;
				enum uint MANTISSAMASK_INT = 0x000F_FFFF; // for the MSB only
				enum ulong MANTISSAMASK_LONG = 0x000F_FFFF_FFFF_FFFF;
				enum realFormat = RealFormat.ieeeDouble;
				version (LittleEndian)
				{
					enum EXPPOS_SHORT = 3;
					enum SIGNPOS_BYTE = 7;
				}
				else
				{
					enum EXPPOS_SHORT = 0;
					enum SIGNPOS_BYTE = 0;
				}
			}
			else static if (T.sizeof == 12)
			{
				// Intel extended real80 rounded to double
				enum ushort EXPMASK = 0x7FFF;
				enum ushort EXPSHIFT = 0;
				enum ushort EXPBIAS = 0x3FFE;
				enum realFormat = RealFormat.ieeeExtended53;
				version (LittleEndian)
				{
					enum EXPPOS_SHORT = 4;
					enum SIGNPOS_BYTE = 9;
				}
				else
				{
					enum EXPPOS_SHORT = 0;
					enum SIGNPOS_BYTE = 0;
				}
			}
			else
				static assert(false, "No traits support for " ~ T.stringof);
    }
    else static if (T.mant_dig == 64)
    {
			// Intel extended real80
			enum ushort EXPMASK = 0x7FFF;
			enum ushort EXPSHIFT = 0;
			enum ushort EXPBIAS = 0x3FFE;
			enum realFormat = RealFormat.ieeeExtended;
			version (LittleEndian)
			{
				enum EXPPOS_SHORT = 4;
				enum SIGNPOS_BYTE = 9;
			}
			else
			{
				enum EXPPOS_SHORT = 0;
				enum SIGNPOS_BYTE = 0;
			}
    }
    else static if (T.mant_dig == 113)
    {
			// Quadruple precision float
			enum ushort EXPMASK = 0x7FFF;
			enum ushort EXPSHIFT = 0;
			enum ushort EXPBIAS = 0x3FFE;
			enum realFormat = RealFormat.ieeeQuadruple;
			version (LittleEndian)
			{
				enum EXPPOS_SHORT = 7;
				enum SIGNPOS_BYTE = 15;
			}
			else
			{
				enum EXPPOS_SHORT = 0;
				enum SIGNPOS_BYTE = 0;
			}
    }
    else static if (T.mant_dig == 106)
    {
			// IBM Extended doubledouble
			enum ushort EXPMASK = 0x7FF0;
			enum ushort EXPSHIFT = 4;
			enum realFormat = RealFormat.ibmExtended;

			// For IBM doubledouble the larger magnitude double comes first.
			// It's really a double[2] and arrays don't index differently
			// between little and big-endian targets.
			enum DOUBLEPAIR_MSB = 0;
			enum DOUBLEPAIR_LSB = 1;

			// The exponent/sign byte is for most significant part.
			version (LittleEndian)
			{
				enum EXPPOS_SHORT = 3;
				enum SIGNPOS_BYTE = 7;
			}
			else
			{
				enum EXPPOS_SHORT = 0;
				enum SIGNPOS_BYTE = 0;
			}
    }
    else
			static assert(false, "No traits support for " ~ T.stringof);
}

version (StdDdoc)
{
	template Unqual(T)
	{
		import core.internal.traits : CoreUnqual = Unqual;
		alias Unqual = CoreUnqual!(T);
	}
}
else
{
	import core.internal.traits : CoreUnqual = Unqual;
	alias Unqual = CoreUnqual;
}

// These apply to all floating-point types
version (LittleEndian)
{
    enum MANTISSA_LSB = 0;
    enum MANTISSA_MSB = 1;
}
else
{
    enum MANTISSA_LSB = 1;
    enum MANTISSA_MSB = 0;
}


bool isInfinity(X)(X x) @nogc @trusted pure nothrow
if (isFloatingPoint!(X))
{
    alias F = floatTraits!(X);
    static if (F.realFormat == RealFormat.ieeeSingle)
    {
        return ((*cast(uint *)&x) & 0x7FFF_FFFF) == 0x7F80_0000;
    }
    else static if (F.realFormat == RealFormat.ieeeDouble)
    {
        return ((*cast(ulong *)&x) & 0x7FFF_FFFF_FFFF_FFFF)
            == 0x7FF0_0000_0000_0000;
    }
    else static if (F.realFormat == RealFormat.ieeeExtended ||
                    F.realFormat == RealFormat.ieeeExtended53)
    {
        const ushort e = cast(ushort)(F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]);
        const ulong ps = *cast(ulong *)&x;

        // On Motorola 68K, infinity can have hidden bit = 1 or 0. On x86, it is always 1.
        return e == F.EXPMASK && (ps & 0x7FFF_FFFF_FFFF_FFFF) == 0;
    }
    else static if (F.realFormat == RealFormat.ieeeQuadruple)
    {
        const long psLsb = (cast(long *)&x)[MANTISSA_LSB];
        const long psMsb = (cast(long *)&x)[MANTISSA_MSB];
        return (psLsb == 0)
            && (psMsb & 0x7FFF_FFFF_FFFF_FFFF) == 0x7FFF_0000_0000_0000;
    }
    else
    {
        return (x < -X.max) || (X.max < x);
    }
}
	
bool isNaN(X)(X x) @nogc @trusted pure nothrow
if (isFloatingPoint!(X))
{
    version (all)
    {
        return x != x;
    }
    else
    {
        alias F = floatTraits!(X);
        static if (F.realFormat == RealFormat.ieeeSingle)
        {
            const uint p = *cast(uint *)&x;
            // Sign bit (MSB) is irrelevant so mask it out.
            // Next 8 bits should be all set.
            // At least one bit among the least significant 23 bits should be set.
            return (p & 0x7FFF_FFFF) > 0x7F80_0000;
        }
        else static if (F.realFormat == RealFormat.ieeeDouble)
        {
            const ulong  p = *cast(ulong *)&x;
            // Sign bit (MSB) is irrelevant so mask it out.
            // Next 11 bits should be all set.
            // At least one bit among the least significant 52 bits should be set.
            return (p & 0x7FFF_FFFF_FFFF_FFFF) > 0x7FF0_0000_0000_0000;
        }
        else static if (F.realFormat == RealFormat.ieeeExtended ||
                        F.realFormat == RealFormat.ieeeExtended53)
        {
            const ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT];
            const ulong ps = *cast(ulong *)&x;
            return e == F.EXPMASK &&
                ps & 0x7FFF_FFFF_FFFF_FFFF; // not infinity
        }
        else static if (F.realFormat == RealFormat.ieeeQuadruple)
        {
            const ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT];
            const ulong psLsb = (cast(ulong *)&x)[MANTISSA_LSB];
            const ulong psMsb = (cast(ulong *)&x)[MANTISSA_MSB];
            return e == F.EXPMASK &&
                (psLsb | (psMsb& 0x0000_FFFF_FFFF_FFFF)) != 0;
        }
        else
        {
            return x != x;
        }
    }
}

enum bool isInstanceOf(alias S, T) = is(T == S!Args, Args...);

template isInstanceOf(alias S, alias T)
{
	enum impl(alias T : S!Args, Args...) = true;
	enum impl(alias T) = false;
	enum isInstanceOf = impl!T;
}

enum isAssignable(Lhs, Rhs = Lhs)       = isRvalueAssignable!(Lhs, Rhs) && isLvalueAssignable!(Lhs, Rhs);
enum isRvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, { lvalueOf!Lhs = rvalueOf!Rhs; });
enum isLvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, { lvalueOf!Lhs = lvalueOf!Rhs; });

template isDynamicArray(T)
{
    static if (is(T == U[], U))
			enum bool isDynamicArray = true;
    else static if (is(T U == enum))
			// BUG: isDynamicArray / isStaticArray considers enums
			// with appropriate base types as dynamic/static arrays
			// Retain old behaviour for now, see
			// https://github.com/dlang/phobos/pull/7574
			enum bool isDynamicArray = isDynamicArray!U;
    else
			enum bool isDynamicArray = false;
}

enum isStaticArray(T) = __traits(isStaticArray, T);

enum isArray(T) = isStaticArray!T || isDynamicArray!T;

*/
