dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.46k stars 4.76k forks source link

Add support System.Numerics.Vectors types with double precision #24168

Open jkotas opened 7 years ago

jkotas commented 7 years ago

The approved API is here: https://github.com/dotnet/runtime/issues/24168#issuecomment-642847557

Due to the size of the approved surface area, it is being split into multiple sections for people who want to help implement it. You can see https://github.com/dotnet/runtime/issues/24168#issuecomment-642894470 for more details


Vector2, Vector3, Vector4, Matrix3x2, Matrix4x4, Plane, Quaternion are currently implemented with single precission numbers. Theare are requests (above) and some miror implementations of System.Numerics.* can be found across Internet where double is supported.

These classes should support float and double precission and maybe in future more types. One of possile solution is to miror them as generics with support for both double and single precission numbers.

Rationale and Usage

This is request that developers ask about, one of usages is CAD software.

For example, to use Vector3 and Matrix4x4 with double precission we will be able to write:

var v1 = new Vector3<double>(4,5,4);
var v2 = new Vector3<double>(1,3,2);
double d = Vector3<double>.Dot(v1, v2);

Matrix4x4<double> xRot = Matrix4x4<double>.CreateTranslation(v1);
Vector3<double> tr = Vector3<double>.Transform(v2,xRot);

Proposed API (mirrored from Vector2, Vector3, Vector4, Matrix3x2, Matrix4x4, Plane)

The following APIs are duplicates of the existing float based APIs, but with float replaced with T to allow for float, double, and in the future System.Half.

File Vector2OfT.cs

public partial struct Vector2<T> : IEquatable<Vector2<T>>, IFormattable
{
    public static Vector2<T> Zero { get; }

    public static Vector2<T> One { get; }

    public static Vector2<T> UnitX { get; }

    public static Vector2<T> UnitY { get; }

    public override int GetHashCode();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public override bool Equals(object obj);

    public override string ToString();

    public string ToString(string format);

    public string ToString(string format, IFormatProvider formatProvider);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public T Length();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public T LengthSquared();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T Distance(Vector2<T> value1, Vector2<T> value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T DistanceSquared(Vector2<T> value1, Vector2<T> value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Normalize(Vector2<T> value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Reflect(Vector2<T> vector, Vector2<T> normal);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Clamp(Vector2<T> value1, Vector2<T> min, Vector2<T> max);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Lerp(Vector2<T> value1, Vector2<T> value2, T amount);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Transform(Vector2<T> position, Matrix3x2<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Transform(Vector2<T> position, Matrix4x4<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> TransformNormal(Vector2<T> normal, Matrix3x2<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> TransformNormal(Vector2<T> normal, Matrix4x4<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Transform(Vector2<T> value, Quaternion<T> rotation);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Add(Vector2<T> left, Vector2<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Subtract(Vector2<T> left, Vector2<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Multiply(Vector2<T> left, Vector2<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Multiply(Vector2<T> left, T right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Multiply(T left, Vector2<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Divide(Vector2<T> left, Vector2<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Divide(Vector2<T> left, T divisor);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Negate(Vector2<T> value);
}

File Vector2_IntrinsicsOfT.cs

public partial struct Vector2<T>
{
    public T X;

    public T Y;

    [JitIntrinsic]
    public Vector2(T value);

    [JitIntrinsic]
    public Vector2(T x, T y);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void CopyTo(T[] array);

    public void CopyTo(T[] array, int index);

    [JitIntrinsic]
    public bool Equals(global::System.Numerics.Vector2<T> other);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T Dot(global::System.Numerics.Vector2<T> value1, global::System.Numerics.Vector2<T> value2);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> Min(global::System.Numerics.Vector2<T> value1, global::System.Numerics.Vector2<T> value2);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> Max(global::System.Numerics.Vector2<T> value1, global::System.Numerics.Vector2<T> value2);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> Abs(global::System.Numerics.Vector2<T> value);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> SquareRoot(global::System.Numerics.Vector2<T> value);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> operator +(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> operator -(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> operator *(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> operator *(T left, global::System.Numerics.Vector2<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> operator *(global::System.Numerics.Vector2<T> left, T right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> operator /(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> operator /(global::System.Numerics.Vector2<T> value1, T value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> operator -(global::System.Numerics.Vector2<T> value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator ==(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator !=(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static explicit operator Vector2<T>(Vector2 value);
}

File Vector3OfT.cs

public partial struct Vector3<T> : IEquatable<Vector3<T>>, IFormattable
{
    public static Vector3<T> Zero { get; }

    public static Vector3<T> One { get; }

    public static Vector3<T> UnitX { get; }

    public static Vector3<T> UnitY { get; }

    public static Vector3<T> UnitZ { get; }

    public override int GetHashCode();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public override bool Equals(object obj);

    public override string ToString();

    public string ToString(string format);

    public string ToString(string format, IFormatProvider formatProvider);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public T Length();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public T LengthSquared();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T Distance(Vector3<T> value1, Vector3<T> value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T DistanceSquared(Vector3<T> value1, Vector3<T> value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Normalize(Vector3<T> value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Cross(Vector3<T> vector1, Vector3<T> vector2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Reflect(Vector3<T> vector, Vector3<T> normal);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Clamp(Vector3<T> value1, Vector3<T> min, Vector3<T> max);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Lerp(Vector3<T> value1, Vector3<T> value2, T amount);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Transform(Vector3<T> position, Matrix4x4<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> TransformNormal(Vector3<T> normal, Matrix4x4<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Transform(Vector3<T> value, Quaternion<T> rotation);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Add(Vector3<T> left, Vector3<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Subtract(Vector3<T> left, Vector3<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Multiply(Vector3<T> left, Vector3<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Multiply(Vector3<T> left, T right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Multiply(T left, Vector3<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Divide(Vector3<T> left, Vector3<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Divide(Vector3<T> left, T divisor);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Negate(Vector3<T> value);
}

File Vector3_IntrinsicsOfT.cs

public partial struct Vector3<T>
{
    public T X;

    public T Y;

    public T Z;

    [JitIntrinsic]
    public Vector3(T value);

    public Vector3(Vector2<T> value, T z);

    [JitIntrinsic]
    public Vector3(T x, T y, T z);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void CopyTo(T[] array);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void CopyTo(T[] array, int index);

    [JitIntrinsic]
    public bool Equals(Vector3<T> other);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T Dot(Vector3<T> vector1, Vector3<T> vector2);

    [JitIntrinsic]
    public static Vector3<T> Min(Vector3<T> value1, Vector3<T> value2);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Max(Vector3<T> value1, Vector3<T> value2);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Abs(Vector3<T> value);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> SquareRoot(Vector3<T> value);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> operator +(Vector3<T> left, Vector3<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> operator -(Vector3<T> left, Vector3<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> operator *(Vector3<T> left, Vector3<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> operator *(Vector3<T> left, T right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> operator *(T left, Vector3<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> operator /(Vector3<T> left, Vector3<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> operator /(Vector3<T> value1, T value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> operator -(Vector3<T> value);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator ==(Vector3<T> left, Vector3<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator !=(Vector3<T> left, Vector3<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static explicit operator Vector3<T>(Vector3 value);
}

File Vector4OfT.cs

public partial struct Vector4<T> : IEquatable<Vector4<T>>, IFormattable
{
    public static Vector4<T> Zero { get; }

    public static Vector4<T> One { get; }

    public static Vector4<T> UnitX { get; }

    public static Vector4<T> UnitY { get ; }

    public static Vector4<T> UnitZ { get; }

    public static Vector4<T> UnitW { get; };

    public override int GetHashCode();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public override bool Equals(object obj);

    public override string ToString();

    public string ToString(string format);

    public string ToString(string format, IFormatProvider formatProvider);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public T Length();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public T LengthSquared();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T Distance(Vector4<T> value1, Vector4<T> value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T DistanceSquared(Vector4<T> value1, Vector4<T> value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Normalize(Vector4<T> vector);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Clamp(Vector4<T> value1, Vector4<T> min, Vector4<T> max);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Lerp(Vector4<T> value1, Vector4<T> value2, T amount);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Transform(Vector2<T> position, Matrix4x4<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Transform(Vector3<T> position, Matrix4x4<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Transform(Vector4<T> vector, Matrix4x4<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Transform(Vector2<T> value, Quaternion<T> rotation);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Transform(Vector3<T> value, Quaternion<T> rotation);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Transform(Vector4<T> value, Quaternion<T> rotation);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Add(Vector4<T> left, Vector4<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Subtract(Vector4<T> left, Vector4<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Multiply(Vector4<T> left, Vector4<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Multiply(Vector4<T> left, T right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Multiply(T left, Vector4<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Divide(Vector4<T> left, Vector4<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Divide(Vector4<T> left, T divisor);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Negate(Vector4<T> value);
}

File Vector4OfT_Intrinsics.cs

public partial struct Vector4<T>
{
    public T X;

    public T Y;

    public T Z;

    public T W;

    [JitIntrinsic]
    public Vector4(T value);

    [JitIntrinsic]
    public Vector4(T x, T y, T z, T w);

    public Vector4(Vector2<T> value, T z, T w);

    public Vector4(Vector3<T> value, T w);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void CopyTo(T[] array);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void CopyTo(T[] array, int index);

    [JitIntrinsic]
    public bool Equals(Vector4<T> other);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T Dot(Vector4<T> vector1, Vector4<T> vector2);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Min(Vector4<T> value1, Vector4<T> value2);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Max(Vector4<T> value1, Vector4<T> value2);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Abs(Vector4<T> value);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> SquareRoot(Vector4<T> value);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> operator +(Vector4<T> left, Vector4<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> operator -(Vector4<T> left, Vector4<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> operator *(Vector4<T> left, Vector4<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> operator *(Vector4<T> left, T right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> operator *(T left, Vector4<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> operator /(Vector4<T> left, Vector4<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> operator /(Vector4<T> value1, T value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> operator -(Vector4<T> value);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator ==(Vector4<T> left, Vector4<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator !=(Vector4<T> left, Vector4<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static explicit operator Vector4<T>(Vector4 value);
}

File Matrix3x2OfT.cs

public struct Matrix3x2<T> : IEquatable<Matrix3x2<T>>
{
    public T M11;

    public T M12;

    public T M21;

    public T M22;

    public T M31;

    public T M32;

    public static Matrix3x2<T> Identity { get; }

    public bool IsIdentity { get; }

    public Vector2<T> Translation { get; set; }

    public Matrix3x2(T m11, T m12, T m21, T m22, T m31, T m32);

    public static Matrix3x2<T> CreateTranslation(Vector2<T> position);

    public static Matrix3x2<T> CreateTranslation(T xPosition, T yPosition);

    public static Matrix3x2<T> CreateScale(T xScale, T yScale);

    public static Matrix3x2<T> CreateScale(T xScale, T yScale, Vector2<T> centerPoint);

    public static Matrix3x2<T> CreateScale(Vector2<T> scales);

    public static Matrix3x2<T> CreateScale(Vector2<T> scales, Vector2<T> centerPoint);

    public static Matrix3x2<T> CreateScale(T scale);

    public static Matrix3x2<T> CreateScale(T scale, Vector2<T> centerPoint);

    public static Matrix3x2<T> CreateSkew(T radiansX, T radiansY);

    public static Matrix3x2<T> CreateSkew(T radiansX, T radiansY, Vector2<T> centerPoint);

    public static Matrix3x2<T> CreateRotation(T radians);

    public static Matrix3x2<T> CreateRotation(T radians, Vector2<T> centerPoint);

    public T GetDeterminant();

    public static bool Invert(Matrix3x2<T> matrix, out Matrix3x2<T> result);

    public static Matrix3x2<T> Lerp(Matrix3x2<T> matrix1, Matrix3x2<T> matrix2, T amount);

    public static Matrix3x2<T> Negate(Matrix3x2<T> value);

    public static Matrix3x2<T> Add(Matrix3x2<T> value1, Matrix3x2<T> value2);

    public static Matrix3x2<T> Subtract(Matrix3x2<T> value1, Matrix3x2<T> value2);

    public static Matrix3x2<T> Multiply(Matrix3x2<T> value1, Matrix3x2<T> value2);

    public static Matrix3x2<T> Multiply(Matrix3x2<T> value1, T value2);

    public static Matrix3x2<T> operator -(Matrix3x2<T> value);

    public static Matrix3x2<T> operator +(Matrix3x2<T> value1, Matrix3x2<T> value2);

    public static Matrix3x2<T> operator -(Matrix3x2<T> value1, Matrix3x2<T> value2);

    public static Matrix3x2<T> operator *(Matrix3x2<T> value1, Matrix3x2<T> value2);

    public static Matrix3x2<T> operator *(Matrix3x2<T> value1, T value2);

    public static bool operator ==(Matrix3x2<T> value1, Matrix3x2<T> value2);

    public static bool operator !=(Matrix3x2<T> value1, Matrix3x2<T> value2);

    public bool Equals(Matrix3x2<T> other);

    public override bool Equals(object obj);

    public override string ToString();

    public override int GetHashCode();

    public static explicit operator Matrix3x2<T>(Matrix3x2 value);
}

File Matrix4x4ofT.cs

public struct Matrix4x4<T> : IEquatable<Matrix4x4<T>>
{
    public T M11;

    public T M12;

    public T M13;

    public T M14;

    public T M21;

    public T M22;

    public T M23;

    public T M24;

    public T M31;

    public T M32;

    public T M33;

    public T M34;

    public T M41;

    public T M42;

    public T M43;

    public T M44;

    public static Matrix4x4<T> Identity { get; }

    public bool IsIdentity { get; }

    public Vector3<T> Translation { get; set; }

    public Matrix4x4(T m11, T m12, T m13, T m14, T m21, T m22, T m23, T m24, T m31, T m32, T m33, T m34, T m41, T m42, T m43, T m44);

    public Matrix4x4(Matrix3x2<T> value);

    public static Matrix4x4<T> CreateBillboard(Vector3<T> objectPosition, Vector3<T> cameraPosition, Vector3<T> cameraUpVector, Vector3<T> cameraForwardVector);

    public static Matrix4x4<T> CreateConstrainedBillboard(Vector3<T> objectPosition, Vector3<T> cameraPosition, Vector3<T> rotateAxis, Vector3<T> cameraForwardVector, Vector3<T> objectForwardVector);

    public static Matrix4x4<T> CreateTranslation(Vector3<T> position);

    public static Matrix4x4<T> CreateTranslation(T xPosition, T yPosition, T zPosition);

    public static Matrix4x4<T> CreateScale(T xScale, T yScale, T zScale);

    public static Matrix4x4<T> CreateScale(T xScale, T yScale, T zScale, Vector3<T> centerPoint);

    public static Matrix4x4<T> CreateScale(Vector3<T> scales);

    public static Matrix4x4<T> CreateScale(Vector3<T> scales, Vector3<T> centerPoint);

    public static Matrix4x4<T> CreateScale(T scale);

    public static Matrix4x4<T> CreateScale(T scale, Vector3<T> centerPoint);

    public static Matrix4x4<T> CreateRotationX(T radians);

    public static Matrix4x4<T> CreateRotationX(T radians, Vector3<T> centerPoint);

    public static Matrix4x4<T> CreateRotationY(T radians);

    public static Matrix4x4<T> CreateRotationY(T radians, Vector3<T> centerPoint);

    public static Matrix4x4<T> CreateRotationZ(T radians);

    public static Matrix4x4<T> CreateRotationZ(T radians, Vector3<T> centerPoint);

    public static Matrix4x4<T> CreateFromAxisAngle(Vector3<T> axis, T angle);

    public static Matrix4x4<T> CreatePerspectiveFieldOfView(T fieldOfView, T aspectRatio, T nearPlaneDistance, T farPlaneDistance);

    public static Matrix4x4<T> CreatePerspective(T width, T height, T nearPlaneDistance, T farPlaneDistance);

    public static Matrix4x4<T> CreatePerspectiveOffCenter(T left, T right, T bottom, T top, T nearPlaneDistance, T farPlaneDistance);

    public static Matrix4x4<T> CreateOrthographic(T width, T height, T zNearPlane, T zFarPlane);

    public static Matrix4x4<T> CreateOrthographicOffCenter(T left, T right, T bottom, T top, T zNearPlane, T zFarPlane);

    public static Matrix4x4<T> CreateLookAt(Vector3<T> cameraPosition, Vector3<T> cameraTarget, Vector3<T> cameraUpVector);

    public static Matrix4x4<T> CreateWorld(Vector3<T> position, Vector3<T> forward, Vector3<T> up);

    public static Matrix4x4<T> CreateFromQuaternion(Quaternion<T> quaternion);

    public static Matrix4x4<T> CreateFromYawPitchRoll(T yaw, T pitch, T roll);

    public static Matrix4x4<T> CreateShadow(Vector3<T> lightDirection, Plane<T> plane);

    public static Matrix4x4<T> CreateReflection(Plane<T> value);

    public T GetDeterminant();

    public static bool Invert(Matrix4x4<T> matrix, out Matrix4x4<T> result);

    public static bool Decompose(Matrix4x4<T> matrix, out Vector3<T> scale, out Quaternion<T> rotation, out Vector3<T> translation);

    public static Matrix4x4<T> Transform(Matrix4x4<T> value, Quaternion<T> rotation);

    public static Matrix4x4<T> Transpose(Matrix4x4<T> matrix);

    public static Matrix4x4<T> Lerp(Matrix4x4<T> matrix1, Matrix4x4<T> matrix2, T amount);

    public static Matrix4x4<T> Negate(Matrix4x4<T> value);

    public static Matrix4x4<T> Add(Matrix4x4<T> value1, Matrix4x4<T> value2);

    public static Matrix4x4<T> Subtract(Matrix4x4<T> value1, Matrix4x4<T> value2);

    public static Matrix4x4<T> Multiply(Matrix4x4<T> value1, Matrix4x4<T> value2);

    public static Matrix4x4<T> Multiply(Matrix4x4<T> value1, T value2);

    public static Matrix4x4<T> operator -(Matrix4x4<T> value);

    public static Matrix4x4<T> operator +(Matrix4x4<T> value1, Matrix4x4<T> value2);

    public static Matrix4x4<T> operator -(Matrix4x4<T> value1, Matrix4x4<T> value2);

    public static Matrix4x4<T> operator *(Matrix4x4<T> value1, Matrix4x4<T> value2);

    public static Matrix4x4<T> operator *(Matrix4x4<T> value1, T value2);

    public static bool operator ==(Matrix4x4<T> value1, Matrix4x4<T> value2);

    public static bool operator !=(Matrix4x4<T> value1, Matrix4x4<T> value2);

    public bool Equals(Matrix4x4<T> other);

    public override bool Equals(object obj);

    public override string ToString();

    public override int GetHashCode();

    public static explicit operator Matrix4x4<T>(Matrix4x4 value);
}

File PlaneOfT.cs

public struct Plane<T> : IEquatable<Plane<T>>
{
    public Vector3<T> Normal;

    public T D;

    public Plane(T x, T y, T z, T d);

    public Plane(Vector3<T> normal, T d);

    public Plane(Vector4<T> value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Plane<T> CreateFromVertices(Vector3<T> point1, Vector3<T> point2, Vector3<T> point3);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Plane<T> Normalize(Plane<T> value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Plane<T> Transform(Plane<T> plane, Matrix4x4<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Plane<T> Transform(Plane<T> plane, Quaternion<T> rotation);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T Dot(Plane<T> plane, Vector4<T> value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T DotCoordinate(Plane<T> plane, Vector3<T> value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T DotNormal(Plane<T> plane, Vector3<T> value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator ==(Plane<T> value1, Plane<T> value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator !=(Plane<T> value1, Plane<T> value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool Equals(Plane<T> other);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public override bool Equals(object obj);

    public override string ToString();

    public override int GetHashCode();

    public static explicit operator Plane<T>(Plane value);
}

File QuaternionOfT.cs

public struct Quaternion<T> : IEquatable<Quaternion<T>>
{
    public T X;

    public T Y;

    public T Z;

    public T W;

    public static Quaternion<T> Identity { get; }

    public bool IsIdentity { get; }

    public Quaternion(T x, T y, T z, T w);

    public Quaternion(Vector3<T> vectorPart, T scalarPart);

    public T Length();

    public T LengthSquared();

    public static Quaternion<T> Normalize(Quaternion<T> value);

    public static Quaternion<T> Conjugate(Quaternion<T> value);

    public static Quaternion<T> Inverse(Quaternion<T> value);

    public static Quaternion<T> CreateFromAxisAngle(Vector3<T> axis, T angle);

    public static Quaternion<T> CreateFromYawPitchRoll(T yaw, T pitch, T roll);

    public static Quaternion<T> CreateFromRotationMatrix(Matrix4x4<T> matrix);

    public static T Dot(Quaternion<T> quaternion1, Quaternion<T> quaternion2);

    public static Quaternion<T> Slerp(Quaternion<T> quaternion1, Quaternion<T> quaternion2, T amount);

    public static Quaternion<T> Lerp(Quaternion<T> quaternion1, Quaternion<T> quaternion2, T amount);

    public static Quaternion<T> Concatenate(Quaternion<T> value1, Quaternion<T> value2);

    public static Quaternion<T> Negate(Quaternion<T> value);

    public static Quaternion<T> Add(Quaternion<T> value1, Quaternion<T> value2);

    public static Quaternion<T> Subtract(Quaternion<T> value1, Quaternion<T> value2);

    public static Quaternion<T> Multiply(Quaternion<T> value1, Quaternion<T> value2);

    public static Quaternion<T> Multiply(Quaternion<T> value1, T value2);

    public static Quaternion<T> Divide(Quaternion<T> value1, Quaternion<T> value2);

    public static Quaternion<T> operator -(Quaternion<T> value);

    public static Quaternion<T> operator +(Quaternion<T> value1, Quaternion<T> value2);

    public static Quaternion<T> operator -(Quaternion<T> value1, Quaternion<T> value2);

    public static Quaternion<T> operator *(Quaternion<T> value1, Quaternion<T> value2);

    public static Quaternion<T> operator *(Quaternion<T> value1, T value2);

    public static Quaternion<T> operator /(Quaternion<T> value1, Quaternion<T> value2);

    public static bool operator ==(Quaternion<T> value1, Quaternion<T> value2);

    public static bool operator !=(Quaternion<T> value1, Quaternion<T> value2);

    public bool Equals(Quaternion<T> other);

    public override bool Equals(object obj);

    public override string ToString();

    public override int GetHashCode();

    public static explicit operator Quaternion<T>(Quaternion value);
}

class Vector2 extended with:

public static implicit operator Vector2(Vector2<double> value);
public static implicit operator Vector2(Vector2<float> value);

class Vector3 extended with:

public static implicit operator Vector3(Vector3<double> value);
public static implicit operator Vector3(Vector3<float> value);

class Vector4 extended with:

public static implicit operator Vector4(Vector4<double> value);
public static implicit operator Vector4(Vector4<float> value);

class Matrix3x2 extended with:

public static implicit operator Matrix3x2(Matrix3x2<double> value);
public static implicit operator Matrix3x2(Matrix3x2<float> value);

class Matrix4x4 extended with:

public static implicit operator Matrix4x4(Matrix4x4<double> value);
public static implicit operator Matrix4x4(Matrix4x4<float> value);

class Quaternion extended with:

public static implicit operator Quaternion(Quaternion<double> value);
public static implicit operator Quaternion(Quaternion<float> value);

class Plane extended with:

public static implicit operator Plane(Plane<double> value);
public static implicit operator Plane(Plane<float> value);

Original Text

From @acecebov on August 25, 2017 16:50

Add support System.Numerics.Vectors types with double precision: Vector2d, Vector3d, Vector4d, Matrix3x2d, Matrix4x4d, PlaneD, QuaternionD.

There are lots of scientific/gaming cases when we want to work with double precision and benefit of SIMD!

Copied from original issue: dotnet/coreclr#13591

jkotas commented 7 years ago

From @danmosemsft on August 27, 2017 19:55

@acecebov thanks for the suggestion, can you move the issue to the corefx repo? New api proposals go there.

bradphelan commented 6 years ago

We needed double as we need the precision for a CAD application so we wrote a port of System.Numerics.Vectors. Mostly just VIM macros replacing float with double everywhere. No SIMD support however.

https://github.com/Weingartner/System.Numerics.DoubleVectors https://www.nuget.org/packages/System.DoubleNumerics/

eerhardt commented 6 years ago

Need to add a proposed API, get it reviewed, and then implement the API. After that, need to update the JIT to support SIMD on these new types.

Another feature that is coming in .NET Core 2.1 that will help here is Hardware/Platform Intrinsics. See https://github.com/dotnet/designs/blob/master/accepted/platform-intrinsics.md. This exposes the underlying SIMD instructions to C# and allows you to call the SIMD instructions directly without needing types to be exposed in System.Numerics.Vectors.

tannergooding commented 6 years ago

@eerhardt, rather than updating the JIT, might it be better to just wait for Hardware/Platform intrinsics?

That is, just have it be purely software and add the additional intrinsic code paths once @fiigii (and others working on this) finishes adding the JIT support for those.

ARM support could be implemented in the same way, as their is currently work starting to add intrinsics for it as well.

eerhardt commented 6 years ago

If we wait for the Hardware intrinsics, would it be better to just close this as "won't fix" and consumers can just take advantage of the underlying Hardware intrinsic APIs? I am assuming that was the point of directly exposing the intrinsics - so we didn't need to keep growing our API abstraction over the top of it.

tannergooding commented 6 years ago

would it be better to just close this as "won't fix"

No, I don't think that should happen.

I don't think the intrinsics are meant to exist so that we don't have to grow our API abstraction. I think they are meant to exist so that library/framework authors can write more performant libraries that better take advantage of the underlying hardware.

That is, even if hardware intrinsics existed today, requests to expose System.Numerics.Vector for float and double would still exist. The Vector types are general purpose APIs that are useful in a number of applications. Having the additional code paths to take advantage of the underlying hardware and be more performant is a bonus.

My proposal was to expose these APIs today, but with software only implementations. Then, as the hardware intrinsics come online, we can add the more performant code paths (which would be useful for https://github.com/dotnet/coreclr/issues/15490)

mmatras commented 6 years ago

Hello, I would like to try to work on this issue, can you please assign me to it?

tannergooding commented 6 years ago

@mmatras, This (ideally) needs a proper API proposal and should go through the API review process before any work starts on it.

Another big decision is whether this should be implemented the same way as the existing intrinsics (adding codgen support directly to the JIT) or if it can use the new hardware intrinsics functionality (which is still a WIP). The two implementations are widely different and going one vs the other may result in a lot of throw-away work.

eerhardt commented 6 years ago

@mmatras - as @tannergooding says, this needs to go through the API proposal process. You can start working on this by filling out the original post with the proposed API. Then we can schedule it for a review.

mmatras commented 6 years ago

Hello @eerhardt and @tannergooding , I've started working on API proposition, but got stuck and I don't know in which direction I should go. For clarity let's stick to Vector2: My first propositon is to change X and Y types to object, and to add all needed overloads for float and double:

[JitIntrinsic]
public Vector2(Single x, Single y)
[JitIntrinsic]
public Vector2(Double x, Double y)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CopyTo(Single[] array);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CopyTo(Double[] array);

etc.

My second option is to use generic type: public partial struct Vector2, and rewrite all methods to generic. Which option is better in your opinion?

tannergooding commented 6 years ago

We actually can't do either of those, as it would be a breaking API change.

I think we could probably go two directions here:

  1. Just add Vector2d, Vector3d, and Vector4d types, which mirror the existing classes but use double
  2. Add generic Vector2<T>, Vector3<T>, and Vector4<T> types, which mirror the existing classes and use T (which would allow the use of the Vector in generic algorithms, and better extension to other types in the future, if that is desirable)

@eerhardt, thoughts?

mmatras commented 6 years ago

In my second option I thought the same as you but I didn't express myself well, maybe this is the way. I will prepare API proposal in the coming week.

eerhardt commented 6 years ago

I guess I was under the assumption that we were going to do @tannergooding's (1.) proposal - "Just add Vector2d, Vector3d, and Vector4d types, which mirror the existing classes but use double".

If we went with the generic approach, would we support more types than just float and double? Say decimal or short?

/cc @CarolEidt

eerhardt commented 6 years ago

Also, with the generic approach, would not having a fixed struct size be a problem? (I don't know if there is - just bringing it up)

tannergooding commented 6 years ago

If we went with the generic approach, would we support more types than just float and double

My thought on a generic approach is that the vector operations are generally valid on any type of numeric data and it might make extension to support future types easier, if that was desired.

Currently the support is System.Single (existing code) and System.Double (this proposal). However, we are looking at adding at least one other floating-point type (System.Numerics.Half) and could look at adding others in the future. If it was desirable to also support Vector operations for those types, it might be better to just extend an existing Vector2<T> (with a static Vector2<T>.IsSupported check) then also implement a new Vector2h type.

A downside is that not all math operations required to support vector operations (such as Sqrt) are available for all numeric types and there may be weird rounding issues when doing certain operations with integer types (although integer types would be useful for something like Vector2<int>, which equates to the POINT struct on Windows and similar structs on other platforms).

Also, with the generic approach, would not having a fixed struct size be a problem

I don't think having a fixed struct size would be a problem, if you needed access to the raw bits you would just offset with Unsafe.SizeOf<Vector2<T>> instead of some fixed size (16 for single, 32 for double, etc).

mmatras commented 6 years ago

Vector2, Vector3, Vector4, Matrix3x2, Matrix4x4, Plane, Quaternion are currently implemented with single precission numbers. Theare are requests (above) and some miror implementations of System.Numerics.* can be found across Internet where double is supported.

These classes should support float and double precission and maybe in future more types. One of possile solution is to miror them as generics with support for both double and single precission numbers.

Rationale and Usage

This is request that developers ask about, one of usages is CAD software.

For example, to use Vector3 and Matrix4x4 with double precission we will be able to write:

var v1 = new Vector3<double>(4,5,4);
var v2 = new Vector3<double>(1,3,2);
double d = Vector3<double>.Dot(v1, v2);

Matrix4x4<double> xRot = Matrix4x4<double>.CreateTranslation(v1);
Vector3<double> tr = Vector3<double>.Transform(v2,xRot);

Proposed API (mirrored from Vector2, Vector3, Vector4, Matrix3x2, Matrix4x4, Plane)

File Vector2OfT.cs

public partial struct Vector2<T> : IEquatable<Vector2<T>>, IFormattable
{
    public static Vector2<T> Zero { get; }

    public static Vector2<T> One { get; }

    public static Vector2<T> UnitX { get; }

    public static Vector2<T> UnitY { get; }

    public override int GetHashCode();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public override bool Equals(object obj);

    public override string ToString();

    public string ToString(string format);

    public string ToString(string format, IFormatProvider formatProvider);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public T Length();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public T LengthSquared();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T Distance(Vector2<T> value1, Vector2<T> value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T DistanceSquared(Vector2<T> value1, Vector2<T> value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Normalize(Vector2<T> value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Reflect(Vector2<T> vector, Vector2<T> normal);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Clamp(Vector2<T> value1, Vector2<T> min, Vector2<T> max);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Lerp(Vector2<T> value1, Vector2<T> value2, T amount);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Transform(Vector2<T> position, Matrix3x2<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Transform(Vector2<T> position, Matrix4x4<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> TransformNormal(Vector2<T> normal, Matrix3x2<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> TransformNormal(Vector2<T> normal, Matrix4x4<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Transform(Vector2<T> value, Quaternion<T> rotation);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Add(Vector2<T> left, Vector2<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Subtract(Vector2<T> left, Vector2<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Multiply(Vector2<T> left, Vector2<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Multiply(Vector2<T> left, T right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Multiply(T left, Vector2<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Divide(Vector2<T> left, Vector2<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Divide(Vector2<T> left, T divisor);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Negate(Vector2<T> value);
}

File Vector2_IntrinsicsOfT.cs

public partial struct Vector2<T>
{
    public T X;

    public T Y;

    [JitIntrinsic]
    public Vector2(T value);

    [JitIntrinsic]
    public Vector2(T x, T y);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void CopyTo(T[] array);

    public void CopyTo(T[] array, int index);

    [JitIntrinsic]
    public bool Equals(global::System.Numerics.Vector2<T> other);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T Dot(global::System.Numerics.Vector2<T> value1, global::System.Numerics.Vector2<T> value2);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> Min(global::System.Numerics.Vector2<T> value1, global::System.Numerics.Vector2<T> value2);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> Max(global::System.Numerics.Vector2<T> value1, global::System.Numerics.Vector2<T> value2);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> Abs(global::System.Numerics.Vector2<T> value);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> SquareRoot(global::System.Numerics.Vector2<T> value);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> operator +(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> operator -(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> operator *(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> operator *(T left, global::System.Numerics.Vector2<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> operator *(global::System.Numerics.Vector2<T> left, T right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> operator /(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> operator /(global::System.Numerics.Vector2<T> value1, T value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> operator -(global::System.Numerics.Vector2<T> value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator ==(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator !=(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static explicit operator Vector2<T>(Vector2 value);
}

File Vector3OfT.cs

public partial struct Vector3<T> : IEquatable<Vector3<T>>, IFormattable
{
    public static Vector3<T> Zero { get; }

    public static Vector3<T> One { get; }

    public static Vector3<T> UnitX { get; }

    public static Vector3<T> UnitY { get; }

    public static Vector3<T> UnitZ { get; }

    public override int GetHashCode();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public override bool Equals(object obj);

    public override string ToString();

    public string ToString(string format);

    public string ToString(string format, IFormatProvider formatProvider);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public T Length();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public T LengthSquared();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T Distance(Vector3<T> value1, Vector3<T> value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T DistanceSquared(Vector3<T> value1, Vector3<T> value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Normalize(Vector3<T> value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Cross(Vector3<T> vector1, Vector3<T> vector2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Reflect(Vector3<T> vector, Vector3<T> normal);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Clamp(Vector3<T> value1, Vector3<T> min, Vector3<T> max);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Lerp(Vector3<T> value1, Vector3<T> value2, T amount);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Transform(Vector3<T> position, Matrix4x4<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> TransformNormal(Vector3<T> normal, Matrix4x4<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Transform(Vector3<T> value, Quaternion<T> rotation);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Add(Vector3<T> left, Vector3<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Subtract(Vector3<T> left, Vector3<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Multiply(Vector3<T> left, Vector3<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Multiply(Vector3<T> left, T right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Multiply(T left, Vector3<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Divide(Vector3<T> left, Vector3<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Divide(Vector3<T> left, T divisor);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Negate(Vector3<T> value);
}

File Vector3_IntrinsicsOfT.cs

public partial struct Vector3<T>
{
    public T X;

    public T Y;

    public T Z;

    [JitIntrinsic]
    public Vector3(T value);

    public Vector3(Vector2<T> value, T z);

    [JitIntrinsic]
    public Vector3(T x, T y, T z);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void CopyTo(T[] array);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void CopyTo(T[] array, int index);

    [JitIntrinsic]
    public bool Equals(Vector3<T> other);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T Dot(Vector3<T> vector1, Vector3<T> vector2);

    [JitIntrinsic]
    public static Vector3<T> Min(Vector3<T> value1, Vector3<T> value2);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Max(Vector3<T> value1, Vector3<T> value2);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Abs(Vector3<T> value);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> SquareRoot(Vector3<T> value);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> operator +(Vector3<T> left, Vector3<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> operator -(Vector3<T> left, Vector3<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> operator *(Vector3<T> left, Vector3<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> operator *(Vector3<T> left, T right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> operator *(T left, Vector3<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> operator /(Vector3<T> left, Vector3<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> operator /(Vector3<T> value1, T value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> operator -(Vector3<T> value);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator ==(Vector3<T> left, Vector3<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator !=(Vector3<T> left, Vector3<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static explicit operator Vector3<T>(Vector3 value);
}

File Vector4OfT.cs

public partial struct Vector4<T> : IEquatable<Vector4<T>>, IFormattable
{
    public static Vector4<T> Zero { get; }

    public static Vector4<T> One { get; }

    public static Vector4<T> UnitX { get; }

    public static Vector4<T> UnitY { get ; }

    public static Vector4<T> UnitZ { get; }

    public static Vector4<T> UnitW { get; };

    public override int GetHashCode();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public override bool Equals(object obj);

    public override string ToString();

    public string ToString(string format);

    public string ToString(string format, IFormatProvider formatProvider);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public T Length();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public T LengthSquared();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T Distance(Vector4<T> value1, Vector4<T> value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T DistanceSquared(Vector4<T> value1, Vector4<T> value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Normalize(Vector4<T> vector);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Clamp(Vector4<T> value1, Vector4<T> min, Vector4<T> max);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Lerp(Vector4<T> value1, Vector4<T> value2, T amount);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Transform(Vector2<T> position, Matrix4x4<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Transform(Vector3<T> position, Matrix4x4<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Transform(Vector4<T> vector, Matrix4x4<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Transform(Vector2<T> value, Quaternion<T> rotation);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Transform(Vector3<T> value, Quaternion<T> rotation);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Transform(Vector4<T> value, Quaternion<T> rotation);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Add(Vector4<T> left, Vector4<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Subtract(Vector4<T> left, Vector4<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Multiply(Vector4<T> left, Vector4<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Multiply(Vector4<T> left, T right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Multiply(T left, Vector4<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Divide(Vector4<T> left, Vector4<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Divide(Vector4<T> left, T divisor);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Negate(Vector4<T> value);
}

File Vector4OfT_Intrinsics.cs

public partial struct Vector4<T>
{
    public T X;

    public T Y;

    public T Z;

    public T W;

    [JitIntrinsic]
    public Vector4(T value);

    [JitIntrinsic]
    public Vector4(T x, T y, T z, T w);

    public Vector4(Vector2<T> value, T z, T w);

    public Vector4(Vector3<T> value, T w);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void CopyTo(T[] array);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void CopyTo(T[] array, int index);

    [JitIntrinsic]
    public bool Equals(Vector4<T> other);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T Dot(Vector4<T> vector1, Vector4<T> vector2);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Min(Vector4<T> value1, Vector4<T> value2);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Max(Vector4<T> value1, Vector4<T> value2);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Abs(Vector4<T> value);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> SquareRoot(Vector4<T> value);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> operator +(Vector4<T> left, Vector4<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> operator -(Vector4<T> left, Vector4<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> operator *(Vector4<T> left, Vector4<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> operator *(Vector4<T> left, T right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> operator *(T left, Vector4<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> operator /(Vector4<T> left, Vector4<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> operator /(Vector4<T> value1, T value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> operator -(Vector4<T> value);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator ==(Vector4<T> left, Vector4<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator !=(Vector4<T> left, Vector4<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static explicit operator Vector4<T>(Vector4 value);
}

File Matrix3x2OfT.cs

public struct Matrix3x2<T> : IEquatable<Matrix3x2<T>>
{
    public T M11;

    public T M12;

    public T M21;

    public T M22;

    public T M31;

    public T M32;

    public static Matrix3x2<T> Identity { get; }

    public bool IsIdentity { get; }

    public Vector2<T> Translation { get; set; }

    public Matrix3x2(T m11, T m12, T m21, T m22, T m31, T m32);

    public static Matrix3x2<T> CreateTranslation(Vector2<T> position);

    public static Matrix3x2<T> CreateTranslation(T xPosition, T yPosition);

    public static Matrix3x2<T> CreateScale(T xScale, T yScale);

    public static Matrix3x2<T> CreateScale(T xScale, T yScale, Vector2<T> centerPoint);

    public static Matrix3x2<T> CreateScale(Vector2<T> scales);

    public static Matrix3x2<T> CreateScale(Vector2<T> scales, Vector2<T> centerPoint);

    public static Matrix3x2<T> CreateScale(T scale);

    public static Matrix3x2<T> CreateScale(T scale, Vector2<T> centerPoint);

    public static Matrix3x2<T> CreateSkew(T radiansX, T radiansY);

    public static Matrix3x2<T> CreateSkew(T radiansX, T radiansY, Vector2<T> centerPoint);

    public static Matrix3x2<T> CreateRotation(T radians);

    public static Matrix3x2<T> CreateRotation(T radians, Vector2<T> centerPoint);

    public T GetDeterminant();

    public static bool Invert(Matrix3x2<T> matrix, out Matrix3x2<T> result);

    public static Matrix3x2<T> Lerp(Matrix3x2<T> matrix1, Matrix3x2<T> matrix2, T amount);

    public static Matrix3x2<T> Negate(Matrix3x2<T> value);

    public static Matrix3x2<T> Add(Matrix3x2<T> value1, Matrix3x2<T> value2);

    public static Matrix3x2<T> Subtract(Matrix3x2<T> value1, Matrix3x2<T> value2);

    public static Matrix3x2<T> Multiply(Matrix3x2<T> value1, Matrix3x2<T> value2);

    public static Matrix3x2<T> Multiply(Matrix3x2<T> value1, T value2);

    public static Matrix3x2<T> operator -(Matrix3x2<T> value);

    public static Matrix3x2<T> operator +(Matrix3x2<T> value1, Matrix3x2<T> value2);

    public static Matrix3x2<T> operator -(Matrix3x2<T> value1, Matrix3x2<T> value2);

    public static Matrix3x2<T> operator *(Matrix3x2<T> value1, Matrix3x2<T> value2);

    public static Matrix3x2<T> operator *(Matrix3x2<T> value1, T value2);

    public static bool operator ==(Matrix3x2<T> value1, Matrix3x2<T> value2);

    public static bool operator !=(Matrix3x2<T> value1, Matrix3x2<T> value2);

    public bool Equals(Matrix3x2<T> other);

    public override bool Equals(object obj);

    public override string ToString();

    public override int GetHashCode();

    public static explicit operator Matrix3x2<T>(Matrix3x2 value);
}

File Matrix4x4ofT.cs

public struct Matrix4x4<T> : IEquatable<Matrix4x4<T>>
{
    public T M11;

    public T M12;

    public T M13;

    public T M14;

    public T M21;

    public T M22;

    public T M23;

    public T M24;

    public T M31;

    public T M32;

    public T M33;

    public T M34;

    public T M41;

    public T M42;

    public T M43;

    public T M44;

    public static Matrix4x4<T> Identity { get; }

    public bool IsIdentity { get; }

    public Vector3<T> Translation { get; set; }

    public Matrix4x4(T m11, T m12, T m13, T m14, T m21, T m22, T m23, T m24, T m31, T m32, T m33, T m34, T m41, T m42, T m43, T m44);

    public Matrix4x4(Matrix3x2<T> value);

    public static Matrix4x4<T> CreateBillboard(Vector3<T> objectPosition, Vector3<T> cameraPosition, Vector3<T> cameraUpVector, Vector3<T> cameraForwardVector);

    public static Matrix4x4<T> CreateConstrainedBillboard(Vector3<T> objectPosition, Vector3<T> cameraPosition, Vector3<T> rotateAxis, Vector3<T> cameraForwardVector, Vector3<T> objectForwardVector);

    public static Matrix4x4<T> CreateTranslation(Vector3<T> position);

    public static Matrix4x4<T> CreateTranslation(T xPosition, T yPosition, T zPosition);

    public static Matrix4x4<T> CreateScale(T xScale, T yScale, T zScale);

    public static Matrix4x4<T> CreateScale(T xScale, T yScale, T zScale, Vector3<T> centerPoint);

    public static Matrix4x4<T> CreateScale(Vector3<T> scales);

    public static Matrix4x4<T> CreateScale(Vector3<T> scales, Vector3<T> centerPoint);

    public static Matrix4x4<T> CreateScale(T scale);

    public static Matrix4x4<T> CreateScale(T scale, Vector3<T> centerPoint);

    public static Matrix4x4<T> CreateRotationX(T radians);

    public static Matrix4x4<T> CreateRotationX(T radians, Vector3<T> centerPoint);

    public static Matrix4x4<T> CreateRotationY(T radians);

    public static Matrix4x4<T> CreateRotationY(T radians, Vector3<T> centerPoint);

    public static Matrix4x4<T> CreateRotationZ(T radians);

    public static Matrix4x4<T> CreateRotationZ(T radians, Vector3<T> centerPoint);

    public static Matrix4x4<T> CreateFromAxisAngle(Vector3<T> axis, T angle);

    public static Matrix4x4<T> CreatePerspectiveFieldOfView(T fieldOfView, T aspectRatio, T nearPlaneDistance, T farPlaneDistance);

    public static Matrix4x4<T> CreatePerspective(T width, T height, T nearPlaneDistance, T farPlaneDistance);

    public static Matrix4x4<T> CreatePerspectiveOffCenter(T left, T right, T bottom, T top, T nearPlaneDistance, T farPlaneDistance);

    public static Matrix4x4<T> CreateOrthographic(T width, T height, T zNearPlane, T zFarPlane);

    public static Matrix4x4<T> CreateOrthographicOffCenter(T left, T right, T bottom, T top, T zNearPlane, T zFarPlane);

    public static Matrix4x4<T> CreateLookAt(Vector3<T> cameraPosition, Vector3<T> cameraTarget, Vector3<T> cameraUpVector);

    public static Matrix4x4<T> CreateWorld(Vector3<T> position, Vector3<T> forward, Vector3<T> up);

    public static Matrix4x4<T> CreateFromQuaternion(Quaternion<T> quaternion);

    public static Matrix4x4<T> CreateFromYawPitchRoll(T yaw, T pitch, T roll);

    public static Matrix4x4<T> CreateShadow(Vector3<T> lightDirection, Plane<T> plane);

    public static Matrix4x4<T> CreateReflection(Plane<T> value);

    public T GetDeterminant();

    public static bool Invert(Matrix4x4<T> matrix, out Matrix4x4<T> result);

    public static bool Decompose(Matrix4x4<T> matrix, out Vector3<T> scale, out Quaternion<T> rotation, out Vector3<T> translation);

    public static Matrix4x4<T> Transform(Matrix4x4<T> value, Quaternion<T> rotation);

    public static Matrix4x4<T> Transpose(Matrix4x4<T> matrix);

    public static Matrix4x4<T> Lerp(Matrix4x4<T> matrix1, Matrix4x4<T> matrix2, T amount);

    public static Matrix4x4<T> Negate(Matrix4x4<T> value);

    public static Matrix4x4<T> Add(Matrix4x4<T> value1, Matrix4x4<T> value2);

    public static Matrix4x4<T> Subtract(Matrix4x4<T> value1, Matrix4x4<T> value2);

    public static Matrix4x4<T> Multiply(Matrix4x4<T> value1, Matrix4x4<T> value2);

    public static Matrix4x4<T> Multiply(Matrix4x4<T> value1, T value2);

    public static Matrix4x4<T> operator -(Matrix4x4<T> value);

    public static Matrix4x4<T> operator +(Matrix4x4<T> value1, Matrix4x4<T> value2);

    public static Matrix4x4<T> operator -(Matrix4x4<T> value1, Matrix4x4<T> value2);

    public static Matrix4x4<T> operator *(Matrix4x4<T> value1, Matrix4x4<T> value2);

    public static Matrix4x4<T> operator *(Matrix4x4<T> value1, T value2);

    public static bool operator ==(Matrix4x4<T> value1, Matrix4x4<T> value2);

    public static bool operator !=(Matrix4x4<T> value1, Matrix4x4<T> value2);

    public bool Equals(Matrix4x4<T> other);

    public override bool Equals(object obj);

    public override string ToString();

    public override int GetHashCode();

    public static explicit operator Matrix4x4<T>(Matrix4x4 value);
}

File PlaneOfT.cs

public struct Plane<T> : IEquatable<Plane<T>>
{
    public Vector3<T> Normal;

    public T D;

    public Plane(T x, T y, T z, T d);

    public Plane(Vector3<T> normal, T d);

    public Plane(Vector4<T> value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Plane<T> CreateFromVertices(Vector3<T> point1, Vector3<T> point2, Vector3<T> point3);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Plane<T> Normalize(Plane<T> value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Plane<T> Transform(Plane<T> plane, Matrix4x4<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Plane<T> Transform(Plane<T> plane, Quaternion<T> rotation);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T Dot(Plane<T> plane, Vector4<T> value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T DotCoordinate(Plane<T> plane, Vector3<T> value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T DotNormal(Plane<T> plane, Vector3<T> value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator ==(Plane<T> value1, Plane<T> value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator !=(Plane<T> value1, Plane<T> value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool Equals(Plane<T> other);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public override bool Equals(object obj);

    public override string ToString();

    public override int GetHashCode();

    public static explicit operator Plane<T>(Plane value);
}

File QuaternionOfT.cs

public struct Quaternion<T> : IEquatable<Quaternion<T>>
{
    public T X;

    public T Y;

    public T Z;

    public T W;

    public static Quaternion<T> Identity { get; }

    public bool IsIdentity { get; }

    public Quaternion(T x, T y, T z, T w);

    public Quaternion(Vector3<T> vectorPart, T scalarPart);

    public T Length();

    public T LengthSquared();

    public static Quaternion<T> Normalize(Quaternion<T> value);

    public static Quaternion<T> Conjugate(Quaternion<T> value);

    public static Quaternion<T> Inverse(Quaternion<T> value);

    public static Quaternion<T> CreateFromAxisAngle(Vector3<T> axis, T angle);

    public static Quaternion<T> CreateFromYawPitchRoll(T yaw, T pitch, T roll);

    public static Quaternion<T> CreateFromRotationMatrix(Matrix4x4<T> matrix);

    public static T Dot(Quaternion<T> quaternion1, Quaternion<T> quaternion2);

    public static Quaternion<T> Slerp(Quaternion<T> quaternion1, Quaternion<T> quaternion2, T amount);

    public static Quaternion<T> Lerp(Quaternion<T> quaternion1, Quaternion<T> quaternion2, T amount);

    public static Quaternion<T> Concatenate(Quaternion<T> value1, Quaternion<T> value2);

    public static Quaternion<T> Negate(Quaternion<T> value);

    public static Quaternion<T> Add(Quaternion<T> value1, Quaternion<T> value2);

    public static Quaternion<T> Subtract(Quaternion<T> value1, Quaternion<T> value2);

    public static Quaternion<T> Multiply(Quaternion<T> value1, Quaternion<T> value2);

    public static Quaternion<T> Multiply(Quaternion<T> value1, T value2);

    public static Quaternion<T> Divide(Quaternion<T> value1, Quaternion<T> value2);

    public static Quaternion<T> operator -(Quaternion<T> value);

    public static Quaternion<T> operator +(Quaternion<T> value1, Quaternion<T> value2);

    public static Quaternion<T> operator -(Quaternion<T> value1, Quaternion<T> value2);

    public static Quaternion<T> operator *(Quaternion<T> value1, Quaternion<T> value2);

    public static Quaternion<T> operator *(Quaternion<T> value1, T value2);

    public static Quaternion<T> operator /(Quaternion<T> value1, Quaternion<T> value2);

    public static bool operator ==(Quaternion<T> value1, Quaternion<T> value2);

    public static bool operator !=(Quaternion<T> value1, Quaternion<T> value2);

    public bool Equals(Quaternion<T> other);

    public override bool Equals(object obj);

    public override string ToString();

    public override int GetHashCode();

    public static explicit operator Quaternion<T>(Quaternion value);
}

class Vector2 extended with:

public static implicit operator Vector2(Vector2<double> value);
public static implicit operator Vector2(Vector2<float> value);

class Vector3 extended with:

public static implicit operator Vector3(Vector3<double> value);
public static implicit operator Vector3(Vector3<float> value);

class Vector4 extended with:

public static implicit operator Vector4(Vector4<double> value);
public static implicit operator Vector4(Vector4<float> value);

class Matrix3x2 extended with:

public static implicit operator Matrix3x2(Matrix3x2<double> value);
public static implicit operator Matrix3x2(Matrix3x2<float> value);

class Matrix4x4 extended with:

public static implicit operator Matrix4x4(Matrix4x4<double> value);
public static implicit operator Matrix4x4(Matrix4x4<float> value);

class Quaternion extended with:

public static implicit operator Quaternion(Quaternion<double> value);
public static implicit operator Quaternion(Quaternion<float> value);

class Plane extended with:

public static implicit operator Plane(Plane<double> value);
public static implicit operator Plane(Plane<float> value);
tannergooding commented 6 years ago

@eerhardt, I think the proposed API looks reasonable (it looks like everything already exposed, but with float replaced with T).

I think the biggest debate might be whether it should be generic (for easy extension in the future, if desired) or if it should be restricted to just double.

tannergooding commented 6 years ago

@mmatras, it may also be worth considering whether conversion between Vector2<double> and Vector2 should be allowed.

mmatras commented 6 years ago

Yes, casting to and from generic types seems reasonable: I will extend previous API proposal comment with below methods declarations:

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Vector2<T>(Vector2 value);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Vector3<T>(Vector3 value);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Vector4<T>(Vector4 value);

public static explicit operator Matrix3x2<T>(Matrix3x2 value);

public static explicit operator Matrix4x4<T>(Matrix4x4 value);

public static explicit operator Plane<T>(Plane value);

public static explicit operator Quaternion<T>(Quaternion value);

The non generic types can be extended with:

Vector2:

public static implicit operator Vector2(Vector2<double> value);
public static implicit operator Vector2(Vector2<float> value);

Vector3:

public static implicit operator Vector3(Vector3<double> value);
public static implicit operator Vector3(Vector3<float> value);

Vector4:

public static implicit operator Vector4(Vector4<double> value);
public static implicit operator Vector4(Vector4<float> value);

Matrix3x2:

public static implicit operator Matrix3x2(Matrix3x2<double> value);
public static implicit operator Matrix3x2(Matrix3x2<float> value);

Matrix4x4:

public static implicit operator Matrix4x4(Matrix4x4<double> value);
public static implicit operator Matrix4x4(Matrix4x4<float> value);

Quaternion:

public static implicit operator Quaternion(Quaternion<double> value);
public static implicit operator Quaternion(Quaternion<float> value);

Plane:

public static implicit operator Plane(Plane<double> value);
public static implicit operator Plane(Plane<float> value);
tannergooding commented 6 years ago

If the operators are purely generic, they should be explicit.

If they are "exploded", we can allow implicit in a few scenarios (such as float->double).

mmatras commented 6 years ago

fixed

mmatras commented 6 years ago

@tannergooding, how we can continue with this issue? I was waiting for some feedback or discussion but it took a while now and it got stuck?

tannergooding commented 6 years ago

@eerhardt is the "area owner" and is the person who should mark the API as "ready-to-review".

Provided he agrees that the proposed API looks good, we can update the top post and marg it with the appropriate tags.

eerhardt commented 6 years ago

Marking as ready to review.

/cc @CarolEidt @jkotas - in case they want to chime in on the proposed API.

SparkieLabs commented 6 years ago

This looks like an idea case to use in parameters and maybe to make them readonly structs?

tannergooding commented 6 years ago

This looks like an idea case to use in parameters and maybe to make them readonly structs?

Not really, these are the type of structs that should be taking advantage of the underlying hardware and that should be getting passed around in the SIMD registers.

CarolEidt commented 6 years ago

Not really, these are the type of structs that should be taking advantage of the underlying hardware and that should be getting passed around in the SIMD registers.

And even if not passed in registers, the best performance should be achieved by leaving them as by-value (see https://github.com/dotnet/coreclr/issues/16619)

CarolEidt commented 6 years ago

I think the proposed API is reasonable; I think that the generic form is more appealing than Vectord, and leaves room for additional types in future.

aobatact commented 6 years ago

Can't we use "in" at operators in Matrix4x4 ?

SparkieLabs commented 6 years ago

The common use case that benefits the most from optimization is transforming an array of points with a single matrix, c.f. DirectXMath's:

So I think we need something like:

public static Vector4 Transform(Vector4<T>[] positions, Matrix4x4 matrix);

public static Vector2 Transform(Vector2<T>[] positions, Matrix3x2 matrix);

The large matrix param is then only passed once, so the in/not-in debate becomes mute.

benaadams commented 6 years ago

Can't we use "in" at operators in Matrix4x4 ?

No in only works for readonly struct types

eerhardt commented 6 years ago

No in only works for readonly struct types

@benaadams - I don't think that is true. I think they work great together, but the in keyword doesn't require the parameter to be a readonly struct.

https://docs.microsoft.com/en-us/dotnet/csharp/reference-semantics-with-value-types

tannergooding commented 6 years ago

I think they work great together

Right, for implicit in, you should get better IL for readonly struct (as the compiler should not need to create a copy of the struct). However, you can get the same IL for any struct by explicitly specifying in (which tells the compiler you are explicitly opting into the behavior).

The use of in parameters avoids the potential performance costs of making copies. It does not change the semantics of any method call. Therefore, you do not need to specify the in modifier at the call site. However, omitting the in modifier at the call site informs the compiler that it is allowed to make a copy of the argument for the following reasons:

  • There is an implicit conversion but not an identity conversion from the argument type to the parameter type.
  • The argument is an expression but does not have a known storage variable.
  • An overload exists that differs by the presence or absence of in. In that case, the by value overload is a better match.
benaadams commented 6 years ago

No in only works for readonly struct types

@benaadams - I don't think that is true. I think they work great together, but the in keyword doesn't require the parameter to be a readonly struct.

@eerhardt only if you stay away from the instance methods in the proposal; else it will do a copy when they are called

tannergooding commented 6 years ago

@benaadams, it (the emitted IL) should never create a copy if you explicitly specify in at the callsite, as per the language specification (see above in the quoted section I copied over).

SparkieLabs commented 6 years ago

Excellent blog post on the reality here The ‘in’-modifier and the readonly structs in C#

I'd vote for these structs being immutable.

benaadams commented 6 years ago

@benaadams, it (the emitted IL) should never create a copy if you explicitly specify in at the callsite, as per the language specification (see above in the quoted section I copied over).

It still will make a copy in the called function when a property is accessed or method call made on the variable so it does not potentially change the original?

I'd vote for these structs being immutable.

Then you have the flip side; if you want to change a single value in the struct you now have to copy 16 doubles (128 bytes)

SparkieLabs commented 6 years ago

I don’t think changing one element of a transform matrix is a common use case. They usually change as a result of an operation on the whole matrix.

benaadams commented 6 years ago

It would still be better pass by value and and support vectorcall

When any of the first six arguments in order from left to right are vector type arguments, they are passed by value in SSE vector registers 0 to 5 according to argument position. Floating-point and m128 types are passed in XMM registers, and m256 types are passed in YMM registers. This differs from the standard x64 calling convention, because the vector types are passed by value instead of by reference, and additional registers are used.

SparkieLabs commented 6 years ago

It’s a shame that whether to use in or not in the language is down to performance and not to whether the method mutates the parameter.

Either way it would be beneficial for performance to be able to pass an array of points and a single matrix to the transform method.

ghost commented 6 years ago

I'm jumping in. I have basically the need to develop this (at least for myself) right away, I was planing doing something simple at first, but I could ideally help @mmatras if he wants to.

Couple of observations/questions though:

xoofx commented 6 years ago

AFAIK there is no generic constraint such as "numeric data type" to allow the usage of arithmetic operators, so how the implementation of such generic classes should be ideally done?

That's a good question. Part of the implementation could be done in IL by using directly mul/add instructions... but there are IL instructions that are not generic-able for this (e.g ldc_r4/ldc_r8), so I'm wondering how this would be handled here in practice (It is like we would like to have "generic" specialization here :speak_no_evil: )

Are you sure the in keyword is not useful when passing a Matrix as parameter? 16 double values may be a lot on some CPU don't you think?

I concur. in works even on non readonly structs assuming that you don't use methods on them (which would be the case for a Matrix multiplication where you access only fields) but not sure you can use in with indirect generics (like in this issue)

Overall, not sure I like the idea of a generic version. As it would be mainly viable for float and double. As we have already Vector3 for float, It would feel awkward to have a new generic type that could also accept float and double... while having Vector3d would be straightforward enough.

On the subject, we could introduce instead float2/float3/float4/double2/double3/double3 as native types instead (so to coreclr, not corefx)... these types would not contain anything more than there fields (and the swizzles and the default operators)... all other operations would be stored in a different API. I remember suggesting to the .NET team at the time Vector3 was designed to use types like float3/float4 instead 😅 that would have made double3 more balanced than Vector3d while we don't have a Vector3f... We did something like this for Unity.Mathematics and while there is a resistance for it (lowercase naming), if it was part of the core types, people would not complain about the lowercasing anymore...

We could argue that we could use the Vector<T> in some generic code, sure, it would be nice, but as we can't do anything about the T in our own code (due to the non existence of numeric constraints), I'm failing to see the value of a generic version...

Side question for codegen for SSE4 likely to be convoluted as there are no 4xdouble hardware registers (compare to AVX)

ghost commented 6 years ago

Introducing native types would be a great thing imho, but I fear that we double the total count. But having float4, double4, (u)int4, (u)long4 among others would be very easy for us to build dedicated operations in different API because integer based types won't have the same API surface than floating ones I think.

My own (I say it, it's subjectif) reference point is comparing what we want to the OpenGL Mathematics library, I don't care about the highp/mediump/lowp precision but I do like the fact that we have a very broad set of types that I can have confidence into how they will be compiled to asm down the road. It's not important if some operations are missing they will be added and compiled into very efficient code.

I think @xoofx and I may be a little biased as we're seeing this from the prism of 3D programming but things like swizzles would be huge!

Down the road I think what we need is (basic or not) data types that will have a very good IL/ASM generation otherwise people will keep tweaking things to get some extra juice and I believe that is the number 1 thing to address right here. The API surface can be (should be?) a separate concern as it's hard to please everyone on this one without implementing a lot of APIs.

Mathematics will be more and more present in the future and taking advantage of a broad set of types and APIs, high optimized, will matter a lot.

Perksey commented 6 years ago

Any update on this?

tannergooding commented 6 years ago

This has been pushed back the past couple of review sessions as we want to spend some more in-depth time reviewing it (due to its size).

CC. @terrajobst

Perksey commented 6 years ago

Ah okay. As long as it's happening soon :) Do you reckon this will release alongside .NET Core 3?

EDIT: I know it's not part of that milestone, but can we expect it around that date?

tannergooding commented 6 years ago

Once reviewed, and if approved, I would guess it will be in the next release for the given branch following its implementation being merged (if it is merged to master, and master is still targeting 3.0, it should make the 3.0 release).

varon commented 5 years ago

The OpenTK team is still hugely interested in this. Would be an enormous boon for our performance!

tannergooding commented 5 years ago

Thanks for the feedback @varon. I definitely want to see this get done, we just didn't have enough resources to do it for 3.0.

Once we start reviewing issues for .NET 5, I'll be sure to try and push this through API review again (we previously determined it requires a dedicated review session) and then, once approved, work can begin on the API.

Perksey commented 5 years ago

It's good to see that this has been added to the .NET 5 milestone :)