Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
jkotas opened this issue Nov 18, 2017 · 129 comments
Open

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

jkotas opened this issue Nov 18, 2017 · 129 comments
Labels
area-System.Numerics Cost:L Work that requires one engineer up to 4 weeks
Milestone

Comments

@jkotas
Copy link
Member

jkotas commented Nov 18, 2017

The approved API is here: #24168 (comment)

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 #24168 (comment) 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
Copy link
Member Author

jkotas commented Nov 18, 2017

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
Copy link

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
Copy link
Member

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
Copy link
Member

@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
Copy link
Member

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
Copy link
Member

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
Copy link

mmatras commented Jan 15, 2018

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

@tannergooding
Copy link
Member

@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
Copy link
Member

@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
Copy link

mmatras commented Jan 21, 2018

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
Copy link
Member

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
Copy link

mmatras commented Jan 21, 2018

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
Copy link
Member

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
Copy link
Member

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
Copy link
Member

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
Copy link

mmatras commented Jan 30, 2018

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
Copy link
Member

tannergooding commented Jan 30, 2018

@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
Copy link
Member

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

@mmatras
Copy link

mmatras commented Feb 1, 2018

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
Copy link
Member

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
Copy link

mmatras commented Feb 1, 2018

fixed

@mmatras
Copy link

mmatras commented Feb 14, 2018

@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
Copy link
Member

@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
Copy link
Member

Marking as ready to review.

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

@SparkieLabs
Copy link

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

@tannergooding
Copy link
Member

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
Copy link
Contributor

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
Copy link
Contributor

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
Copy link

aobatact commented Apr 5, 2018

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

@SparkieLabs
Copy link

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

  • XMVector2TransformStream
  • XMVector3TransformCoordStream

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.

@tannergooding
Copy link
Member

Why we still have no simd accelerated double precision vectors, matrices, etc.? This is important for games and scientific applications.

The API surface needs to be re-reviewed to account for generic math. It is also a non-trivial amount of work, especially if it is to be hardware accelerated as well.

@Aniobodo
Copy link

Vector4 as double. Any hope in .NET 8?

@tannergooding
Copy link
Member

This is still something I am very interested in finishing an pushing through. Unfortunately, due to competing priorities its not likely going to be in for .NET 8.

There is significant design work required to ensure the API surface is correct and forward compatible. The implementation cost, particularly to ensure it is performant, is likewise non-trivial.

@SparkieLabs
Copy link

If the BCL contained Vector2, Vector3, Vector4 together with simple Matrix3x2 and Matrix4x4 types wrapping them it would at least enable libraries to exchange data with each other.
They don't need to have all the 3D graphics functions, people can add them with extension methods if needed (especially when roles lands).
SIMD support would be great but doesn't have to be there initially.
The basic shape of these types (without all the helper methods) should be fairly non-contentious.

@Perksey
Copy link
Member

Perksey commented Apr 14, 2023

@SparkieLabs The design for this, while currently approved, does not account for generic maths (originally this was going to be a fixed API surface, and manually specialized implementations which would throw if an unsupported T is used) which vastly changes the implications of expanding System.Numerics. It is true that noone will object to the currently approved surface, but at the same time given recent developments more can definitely be done here. There are other libraries that fill in these blanks (such as Silk.NET.Maths) in the meantime.

@SparkieLabs
Copy link

Agreed they should be generic.
The problem isn't a lack of community libraries with these types its the abundance of them (I've even got my own generic version). Different libraries all having there own types adds friction to interworking say Silk.NET and geometry4Sharp), that's what I think the focus should be on solving.

@tannergooding
Copy link
Member

There has to be a consideration for versioning such APIs over time, however.

Simply throwing up a few types is simple. Ensuring those types have the right shape so that they can be made performant, such that they don't lead to UB, such that they can support the types developers need, etc. That takes time and effort to design.

In the interim, we have APIs like TTo Unsafe.BitCast<TFrom, TTo>(TFrom value) which allow safely and efficiently reinterpreting (without making it address taken) same sized value types which can be used to ease the interop of the various types custom libraries have.

Exposing another type in the BCL doesn't fix the issue itself, it just compounds to their being "yet another" type that you have to consider, the one difference is that its "in box" so it can be the common interchange between them, but ultimately you would still need to do "two casts" to go from JanesVector4Double to JimsVector4Double (where-as BitCast gives it to you in 1).

@vukovinski
Copy link

Quick look into Intel architecture document shows SIMD instructions for double precision floating points were available with Pentium 4 (see SSE2 for details).

@Aniobodo
Copy link

@vukovinski Thanks for that info.
The hardware company knows that SIMD with double is a priority, only Microsoft thinks otherswise.

@tannergooding
Copy link
Member

tannergooding commented Apr 16, 2023

We already provide all the necessary APIs and functionality for someone to roll their own fully SIMD accelerated double-precision Vector2/3/4 types.

Additionally, no one has said that double isn't a "priority". It's simply been stated that there is quite a substantial amount of design work that needs to be done before the proposal can move forward again.

@Aniobodo
Copy link

@tannergooding
Your last comment is interesting:
We already provide all the necessary APIs and functionality for someone to roll their own fully SIMD accelerated double-precision Vector2/3/4 types.

Is there any description for that?
I tried with attribute [Intrinsic] but it didn’t work.

@vpenades
Copy link

vpenades commented Apr 16, 2023

@tannergooding I think there's two separate cases that need to be handled by numeric types: maths and interop.

I agree that in order to support generic maths requires extensive R&D, but not so for interop, and I don't think having a few more commonly used types would harm.

So what I think what people is asking is that while we wait for perfection, it could be desirable to have some temporary solution, something in between.

I understand that temporary solution could not fit into system.numeric.vectors.. So I would like to propose to have a CommunityToolkit.Numeric.Vectors or something like that. After all, CommunityToolkit is being used to fill the gap of things that are commonly needed by the community but don't quite fit into the CLR

I know silk.net has been proposed, and it's not bad at all, but it's not so widely adopted either to become a type exchange library.

@tannergooding
Copy link
Member

I tried with attribute [Intrinsic] but it didn’t work.

@Aniobodo, [Intrinsic] is an internal only attribute for the runtime so that the runtime can specialize semantics internally. Such as providing custom type layout or specialized node handling to help with overall JIT throughput.

User code has access to the System.Runtime.Intrinsics namespace which provides access to platform-specific (since .NET Core 3.1) and cross-platform (since .NET 7) hardware intrinsics, much like C/C++ provide.

Interested parties would utilize this in their own types, such as https://source.terrafx.dev/#TerraFX/Numerics/Vector4.cs,927e43c5390e4e41, to provide the underlying acceleration appropriate.

@tannergooding
Copy link
Member

I understand that temporary solution could not fit into system.numeric.vectors.. So I would like to propose to have a CommunityToolkit.Numeric.Vectors or something like that. After all, CommunityToolkit is being used to fill the gap of things that are commonly needed by the community but don't quite fit into the CLR

@vpenades, I already touched on this above.

If we were to expose some CommunityToolkit.Numerics.Vector purely for interop, it would effectively just be identical to a named version of ValueTuple<double, double> for Vector2. This wouldn't then be usable as the interchange type when the System.Numerics.Vector2<T> comes about in the future, due to layering, and so it does in fact ultimately hurt things more in the long run.

Exposing one more type doesn't fix the root interchange issue. Nor does it fix the issue of differing libraries actually adopting the "one more type" as the actual interchange type. There will always be a need for users to work around a lack of common type, either because the library in question isn't using it or because of performance.

APIs like ref TTo Unsafe.As<TFrom, TTo>(ref TFrom value) and as of .NET 8 TTo Unsafe.BitCast<TFrom, TTo>(TFrom value) exist specifically to help solve this scenario and allow reinterpreting bits of type TFrom as bits of type TTo.

Value Tuples (particularly with the general language decomposition support) are another good option, as is the Vector128<T> (particularly from the perf perspective) from the System.Runtime.Intrinsics namespace. You can also utilize the Vector256<T> or in .NET 8 the Vector512<T> type for similar benefits, this is particularly the case on x86/x64, or in .NET 8 on "all platforms" since they are 2x and 4x Vector128<T>, respectively.

I am personally committed to ensuring this happens, but it needs to be done correctly and needs to be done in relation to other important work that is more broadly impactful to .NET in general.

  • In .NET 7 this functionality got pushed out in favor of Generic Math, which is a generally beneficial feature allowing massive reductions in code duplication and broader support for correctly constraining and using generics meaningfully with arithmetic. Such a feature directly benefits the design of Vector2/3/4<T>.
  • For .NET 8, the main focus is exposing Vector512<T> and the corresponding AVX512 support. This will have general benefits to modern hardware throughout the entirety of the BCL. Such functionality not only allows improving existing SIMD accelerated workloads (such as Span, LINQ, text transcoding, etc), but it also will improve almost all floating-point related code due to powerful new instructions. -- This is true for scalar, 128-bit, 256-bit, and 512-bit code.
  • Other "large" features on the general backlog include this proposal, IEEE 754 decimal types, and a BigInteger rewrite.

@SparkieLabs
Copy link

With the advent of generic math this domain is at an inflection point. If Microsoft delivers an offering in .NET 9 the community will rally around it. It they don’t the .NET generic vector space will be the same fragmented mess as the non-generic space is today.

I am told that the future is the metaverse and the fundamental particles of the metaverse are vectors, matrices and quaternions.

@SparkieLabs
Copy link

I should add that the current System.Numerics.Vectors float implementations are excellent and thanks for all the work you do on them.

@vukovinski
Copy link

@SparkieLabs also, choice operators, but thats another story

@KingKrouch
Copy link

Are there any updates on the status of this?

@tannergooding
Copy link
Member

There is a lot of ongoing work in the various spaces of intrinsics and only so much time to get it all done.

This work item in particular is still pending an updated and forward thinking proposal that takes into account generic math, includes newly introduced API surface, and factors in the considerations of what does or doesn't make sense in a generic context.

The last bit is notably the part that needs the most thought because there is a desire from the community to ensure that Vector2<T> works with integers or floating-point types. However, many of the concepts around these types of Euclidean vectors only make sense for floating-point types and so we really need to think on how this should be split/exposed. This includes thinking around if we should expose both Vector2I<T> where T : IBinaryInteger<T> and Vector2F<T> where T : IFloatingPointIeee754<T> (but maybe with better names), etc.

API design is not trivial and it requires significant investment to ensure that we end up with a viable API surface that won't require us to introduce yet another type that doesn't quite mesh in the future and while the immediate ask of supporting just double is more simple, we can't only consider that when thinking about what to do here since the needs in this space are known to be broader in practice.

@tannergooding
Copy link
Member

If someone is willing to help do the work to drive an updated proposal, I'd be happy to set aside some time to sync with them on a basic outline that I believe would work and then follow up with them after they've gotten the design doc (which would include rationale, examples, and needs elaborated on) written up. That would unblock this from going to API review and then the basic implementation being provided

@malstraem
Copy link

malstraem commented Jan 30, 2024

However, many of the concepts around these types of Euclidean vectors only make sense for floating-point types and so we really need to think on how this should be split/exposed.

Should we really limit the implementation to the meaning of Euclidean vectors? Maybe there is a workaround that will allow us to implement vectors that are constrainted by interfaces only, but still use their own logic for integers or floatings? This will definitely allow people to do a lot of useful custom things based on generic math interfaces.

I'm just interesting, cause it's obvious that after dividing the vectors according to Euclidean, there will be no way back.

@tannergooding
Copy link
Member

These are already Euclidean vectors, by definition. General purpose mathematical vectors or explicit SIMD vectors are different concepts and already exposed or are being exposed by a different set of APIs.

@tannergooding
Copy link
Member

That is, Vector2/3/4, Matrix4x4, Quaternion, Plane, etc are explicit specializations designed for and around things like graphics and physics. There isn't really a desire to make it more broad outside those concepts because things like System.Runtime.Intrinsics.Vector64/128/256/512<T> and System.Numerics.Vector<T> exist for SIMD, while System.Numerics.Tensors.TensorPrimitives and the likely eventual Tensor<T> (or similarly shaped API) are designed for the broader mathematical/AI/ML usage of scalars/vectors/matrices/tensors

@micampbell
Copy link

in exploring how Vector3 is written in .NET 9, it seems that many calls are now going to AsVector128Unsafe(). For double precision, would one be able to just copy this class and make a double precision Vector3 by changing these to AsVector512Unsafe()?

@tannergooding
Copy link
Member

It'd be AsVector256Unsafe, but it's a bit more complicated in practice.

Unlike Vector128 which is largely the core SIMD primitive that exists on all hardware with SIMD support, Vector256 tends to be more limited and x86/x64 specific. As such, you'd really need to detect acceleration and use either 1x Vector256 or 2x Vector128 operations to do the work for Vector3/4. For Vector2 it'd always be 1x Vector128 operation.

This will cause most of the right things to happen, but notably there's still some minor special handling for the System.Numerics.Vector* types that can't be provided to user-defined structs and so some other minor nuances might also exist leading to it being subpar. Ideally we'll get to a point where it isn't unique to in-box types, but there's a lot of work needed before that is possible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-System.Numerics Cost:L Work that requires one engineer up to 4 weeks
Projects
None yet
Development

No branches or pull requests