using System.Collections; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; using System.Runtime.Serialization; using System.Text.Unicode; namespace GenericVector.Generated; // Vector2D [StructLayout(LayoutKind.Sequential), DataContract, Serializable] public readonly partial struct Vector2D : IVector, T>, IVectorAlso, T>, IEquatable, ISpanFormattable where T : INumberBase { /// The X component of the vector. [DataMember] public readonly T X; /// The Y component of the vector. [DataMember] public readonly T Y; internal const int Count = 2; /// Creates a new object whose two elements have the same value. /// The value to assign to all two elements. public Vector2D(T value) : this(value, value) { } /// Creates a vector whose elements have the specified values. /// The value to assign to the field. /// The value to assign to the field. public Vector2D(T x, T y) { Unsafe.SkipInit(out this); X = x; Y = y; } /// Constructs a vector from the given . The span must contain at least 4 elements. /// The span of elements to assign to the vector. public Vector2D(ReadOnlySpan values) { Unsafe.SkipInit(out this); ArgumentOutOfRangeException.ThrowIfLessThan(values.Length, Count, nameof(values)); this = Unsafe.ReadUnaligned>(ref Unsafe.As(ref MemoryMarshal.GetReference(values))); } /// Gets a vector whose 4 elements are equal to zero. /// A vector whose two elements are equal to zero (that is, it returns the vector (0,0). public static Vector2D Zero => new(T.Zero); /// Gets a vector whose 4 elements are equal to one. /// Returns . /// A vector whose two elements are equal to one (that is, it returns the vector (1,1). public static Vector2D One => new(T.One); /// Gets the vector (1,0)). /// The vector (1,0). public static Vector2D UnitX => new(T.One, T.Zero); /// Gets the vector (0,1)). /// The vector (0,1). public static Vector2D UnitY => new(T.Zero, T.One); public ReadOnlySpan Components { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in X), Count); } [MethodImpl(MethodImplOptions.AggressiveInlining)] static Vector2D IVector, T>.CreateFromRepeatingComponent(T scalar) => new(scalar); /// Gets or sets the element at the specified index. /// The index of the element to get or set. /// The the element at . /// was less than zero or greater than the number of elements. public T this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Components[index]; } #region Operators /// Adds two vectors together. /// The first vector to add. /// The second vector to add. /// The summed vector. /// The method defines the addition operation for objects. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2D operator +(Vector2D left, Vector2D right) { return new Vector2D( left.X + right.X, left.Y + right.Y ); } /// Subtracts the second vector from the first. /// The first vector. /// The second vector. /// The vector that results from subtracting from . /// The method defines the subtraction operation for objects. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2D operator -(Vector2D left, Vector2D right) { return new Vector2D( left.X - right.X, left.Y - right.Y ); } /// Negates the specified vector. /// The vector to negate. /// The negated vector. /// The method defines the unary negation operation for objects. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2D operator -(Vector2D value) { return Zero - value; } /// Returns a new vector whose values are the product of each pair of elements in two specified vectors. /// The first vector. /// The second vector. /// The element-wise product vector. /// The method defines the multiplication operation for objects. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2D operator *(Vector2D left, Vector2D right) { return new Vector2D( left.X * right.X, left.Y * right.Y ); } /// Multiplies the specified vector by the specified scalar value. /// The vector. /// The scalar value. /// The scaled vector. /// The method defines the multiplication operation for objects. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2D operator *(Vector2D left, T right) { return left * new Vector2D(right); } /// Multiplies the scalar value by the specified vector. /// The vector. /// The scalar value. /// The scaled vector. /// The method defines the multiplication operation for objects. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2D operator *(T left, Vector2D right) { return right * left; } /// Divides the first vector by the second. /// The first vector. /// The second vector. /// The vector that results from dividing by . /// The method defines the division operation for objects. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2D operator /(Vector2D left, Vector2D right) { return new Vector2D( left.X / right.X, left.Y / right.Y ); } /// Divides the specified vector by a specified scalar value. /// The vector. /// The scalar value. /// The result of the division. /// The method defines the division operation for objects. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2D operator /(Vector2D value1, T value2) { return value1 / new Vector2D(value2); } /// Returns a value that indicates whether each pair of elements in two specified vectors is equal. /// The first vector to compare. /// The second vector to compare. /// if and are equal; otherwise, . /// Two objects are equal if each element in is equal to the corresponding element in . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Vector2D left, Vector2D right) { return left.X == right.X && left.Y == right.Y; } /// Returns a value that indicates whether two specified vectors are not equal. /// The first vector to compare. /// The second vector to compare. /// if and are not equal; otherwise, . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Vector2D left, Vector2D right) { return !(left == right); } #endregion #region CopyTo /// Copies the elements of the vector to a specified array. /// The destination array. /// must have at least two elements. The method copies the vector's elements starting at index 0. /// is . /// The number of elements in the current instance is greater than in the array. /// is multidimensional. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void CopyTo(T[] array) { // We explicitly don't check for `null` because historically this has thrown `NullReferenceException` for perf reasons ArgumentOutOfRangeException.ThrowIfLessThan(array.Length, Count, nameof(array)); Unsafe.WriteUnaligned(ref Unsafe.As(ref array[0]), this); } /// Copies the elements of the vector to a specified array starting at a specified index position. /// The destination array. /// The index at which to copy the first element of the vector. /// must have a sufficient number of elements to accommodate the two vector elements. In other words, elements through + 2 must already exist in . /// is . /// The number of elements in the current instance is greater than in the array. /// is less than zero. /// -or- /// is greater than or equal to the array length. /// is multidimensional. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void CopyTo(T[] array, int index) { // We explicitly don't check for `null` because historically this has thrown `NullReferenceException` for perf reasons ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)array.Length); ArgumentOutOfRangeException.ThrowIfLessThan((array.Length - index), Count); Unsafe.WriteUnaligned(ref Unsafe.As(ref array[index]), this); } /// Copies the vector to the given . The length of the destination span must be at least 4. /// The destination span which the values are copied into. /// If number of elements in source vector is greater than those available in destination span. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void CopyTo(Span destination) { ArgumentOutOfRangeException.ThrowIfLessThan(destination.Length, Count, nameof(destination)); Unsafe.WriteUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(destination)), this); } /// Attempts to copy the vector to the given . The length of the destination span must be at least 4. /// The destination span which the values are copied into. /// if the source vector was successfully copied to . if is not large enough to hold the source vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryCopyTo(Span destination) { if (destination.Length < Count) { return false; } Unsafe.WriteUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(destination)), this); return true; } #endregion #region Equality /// Returns a value that indicates whether this instance and another vector are equal. /// The other vector. /// if the two vectors are equal; otherwise, . /// Two vectors are equal if their , , , and elements are equal. [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Vector2D other) { return SpeedHelpers.FastEqualsUpTo4, T>(this, other); } /// Returns a value that indicates whether this instance and a specified object are equal. /// The object to compare with the current instance. /// if the current instance and are equal; otherwise, . If is , the method returns . /// The current instance and are equal if is a object and their corresponding elements are equal. [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals([NotNullWhen(true)] object? obj) { return (obj is Vector2D other) && Equals(other); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Vector2 other) { if (typeof(T) == typeof(float) && Vector128.IsHardwareAccelerated) { return Unsafe.BitCast, Vector2>(this).AsVector128().Equals(other.AsVector128()); } return float.CreateTruncating(X).Equals(other.X) && float.CreateTruncating(Y).Equals(other.Y); } /// Returns the hash code for this instance. /// The hash code. public override int GetHashCode() { return HashCode.Combine(X, Y); } #endregion #region Format /// Returns the string representation of the current instance using default formatting. /// The string representation of the current instance. /// This method returns a string in which each element of the vector is formatted using the "G" (general) format string and the formatting conventions of the current thread culture. The "<" and ">" characters are used to begin and end the string, and the current culture's property followed by a space is used to separate each element. public override string ToString() { return ToString("G", CultureInfo.CurrentCulture); } /// Returns the string representation of the current instance using the specified format string to format individual elements. /// A standard or custom numeric format string that defines the format of individual elements. /// The string representation of the current instance. /// This method returns a string in which each element of the vector is formatted using and the current culture's formatting conventions. The "<" and ">" characters are used to begin and end the string, and the current culture's property followed by a space is used to separate each element. /// Standard Numeric Format Strings /// Custom Numeric Format Strings public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format) { return ToString(format, CultureInfo.CurrentCulture); } /// Returns the string representation of the current instance using the specified format string to format individual elements and the specified format provider to define culture-specific formatting. /// A standard or custom numeric format string that defines the format of individual elements. /// A format provider that supplies culture-specific formatting information. /// The string representation of the current instance. /// This method returns a string in which each element of the vector is formatted using and . The "<" and ">" characters are used to begin and end the string, and the format provider's property followed by a space is used to separate each element. /// Standard Numeric Format Strings /// Custom Numeric Format Strings public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? formatProvider) { var separator = NumberFormatInfo.GetInstance(formatProvider).NumberGroupSeparator; Span initialBuffer = stackalloc char[Math.Min((2 + (Count - 1) + (separator.Length * (Count - 1)) + (Count * 2)), 256)]; // We can't use an interpolated string here because it won't allow us to pass `format` var handler = new DefaultInterpolatedStringHandler( 4 + (separator.Length * 2), Count, formatProvider, initialBuffer ); handler.AppendLiteral("<"); handler.AppendFormatted(X, format); handler.AppendLiteral(separator); handler.AppendLiteral(" "); handler.AppendFormatted(Y, format); handler.AppendLiteral(">"); return handler.ToStringAndClear(); } public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) { // Possible fast path for failure case: // if (destination.Length < 4) return false; var separator = NumberFormatInfo.GetInstance(provider).NumberGroupSeparator; // We can't use an interpolated string here because it won't allow us to pass `format` var handler = new MemoryExtensions.TryWriteInterpolatedStringHandler( 4 + (separator.Length * 2), Count, destination, provider, out var shouldAppend ); if (!shouldAppend) { charsWritten = 0; return false; } // Annoyingly we need to turn the span into a string for the string handler string? formatString = format.Length > 0 ? new string(format) : null; _ = handler.AppendLiteral("<") && handler.AppendFormatted(X, formatString) && handler.AppendLiteral(separator) && handler.AppendLiteral(" ") && handler.AppendFormatted(Y, formatString) && handler.AppendLiteral(">"); return destination.TryWrite(ref handler, out charsWritten); } #endregion #region Casts public Vector2D As() where TOther : INumberBase { if (SpeedHelpers.TryFastConvert, T, Vector2D, TOther>(this, out var result)) { return result; } return new Vector2D( TOther.CreateTruncating(X), TOther.CreateTruncating(Y) ); } private Vector2D AsChecked() where TOther : INumberBase { return new Vector2D( TOther.CreateChecked(X), TOther.CreateChecked(Y) ); } public static explicit operator Vector2D(Vector2D self) => self.As(); public static explicit operator Vector2D(Vector2D self) => self.As(); public static explicit operator Vector2D(Vector2D self) => self.As(); public static explicit operator Vector2D(Vector2D self) => self.As(); public static explicit operator Vector2D(Vector2D self) => self.As(); public static explicit operator Vector2D(Vector2D self) => self.As(); public static explicit operator Vector2D(Vector2D self) => self.As(); public static explicit operator Vector2D(Vector2D self) => self.As(); public static explicit operator Vector2D(Vector2D self) => self.As(); public static explicit operator Vector2D(Vector2D self) => self.As(); public static explicit operator Vector2D(Vector2D self) => self.As(); public static explicit operator Vector2D(Vector2D self) => self.As(); public static explicit operator Vector2D(Vector2D self) => self.As(); public static explicit operator Vector2D(Vector2D self) => self.As(); public static explicit operator Vector2D(Vector2D self) => self.As(); public static explicit operator Vector2D(Vector2D self) => self.As(); public static explicit operator checked Vector2D(Vector2D self) => self.AsChecked(); public static explicit operator checked Vector2D(Vector2D self) => self.AsChecked(); public static explicit operator checked Vector2D(Vector2D self) => self.AsChecked(); public static explicit operator checked Vector2D(Vector2D self) => self.AsChecked(); public static explicit operator checked Vector2D(Vector2D self) => self.AsChecked(); public static explicit operator checked Vector2D(Vector2D self) => self.AsChecked(); public static explicit operator checked Vector2D(Vector2D self) => self.AsChecked(); public static explicit operator checked Vector2D(Vector2D self) => self.AsChecked(); public static explicit operator checked Vector2D(Vector2D self) => self.AsChecked(); public static explicit operator checked Vector2D(Vector2D self) => self.AsChecked(); public static explicit operator checked Vector2D(Vector2D self) => self.AsChecked(); public static explicit operator checked Vector2D(Vector2D self) => self.AsChecked(); public static explicit operator checked Vector2D(Vector2D self) => self.AsChecked(); public static explicit operator checked Vector2D(Vector2D self) => self.AsChecked(); public static explicit operator checked Vector2D(Vector2D self) => self.AsChecked(); public static explicit operator checked Vector2D(Vector2D self) => self.AsChecked(); // Cast from System.Numerics.Vector2 public static explicit operator Vector2D(Vector2 self) => new(T.CreateTruncating(self.X), T.CreateTruncating(self.Y)); public static explicit operator checked Vector2D(Vector2 self) => new(T.CreateChecked(self.X), T.CreateChecked(self.Y)); // Cast to System.Numerics.Vector2 public static explicit operator Vector2(Vector2D self) => new(float.CreateTruncating(self.X), float.CreateTruncating(self.Y)); public static explicit operator checked Vector2(Vector2D self) => new(float.CreateChecked(self.X), float.CreateChecked(self.Y)); // Downcast // Upcast public static explicit operator Vector3D(Vector2D self) => new(self, T.Zero); public static explicit operator Vector4D(Vector2D self) => new(self, T.Zero, T.Zero); // Upcast from System.Numerics.Vector < 2 // Downcast from System.Numerics.Vector > 2 public static explicit operator Vector2D(Vector3 self) => new(T.CreateTruncating(self.X), T.CreateTruncating(self.Y)); public static explicit operator checked Vector2D(Vector3 self) => new(T.CreateChecked(self.X), T.CreateChecked(self.Y)); public static implicit operator Vector2D((T X, T Y) components) => new(components.X, components.Y); #endregion public void Deconstruct(out T x, out T y) { x = X; y = Y; } } file interface IVec2 { // Returns null if incompatible. Throws OverflowException if overflowing Vector2D? GetChecked() where T : INumberBase; Vector2D? GetSaturating() where T : INumberBase; Vector2D? GetTruncating() where T : INumberBase; } // Vector2D.INumber public readonly partial struct Vector2D : IDivisionOperators, T, Vector2D>, IMultiplyOperators, T, Vector2D>, INumberBase>, IVec2 { /// Returns a vector whose elements are the absolute values of each of the specified vector's elements. /// A vector. /// The absolute value vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] static Vector2D INumberBase>.Abs(Vector2D value) => Vector2D.Abs(value); static Vector2D IParsable>.Parse(string s, IFormatProvider? provider) => Parse(s.AsSpan(), NumberStyles.None, provider); static Vector2D ISpanParsable>.Parse(ReadOnlySpan s, IFormatProvider? provider) => Parse(s, NumberStyles.None, provider); public static Vector2D Parse(string s, NumberStyles style = default, IFormatProvider? provider = null) => Parse(s.AsSpan(), style, provider); public static Vector2D Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.None, IFormatProvider? provider = null) => TryParse(s, style, provider, out var result) ? result : throw new ArgumentException($"Failed to parse {nameof(Vector2)}<{typeof(T)}>"); public static bool TryParse(string? s, IFormatProvider? provider, out Vector2D result) => TryParse(s.AsSpan(), NumberStyles.None, provider, out result); public static bool TryParse(string? s, NumberStyles style, IFormatProvider? provider, out Vector2D result) => TryParse(s.AsSpan(), style, provider, out result); public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out Vector2D result) => TryParse(s, NumberStyles.None, provider, out result); public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out Vector2D result) { result = default; if (s[0] != '<') return false; if (s[^1] != '>') return false; var separator = NumberFormatInfo.GetInstance(provider).NumberGroupSeparator; s = s[1..^1]; T? x, y; { if (s.Length == 0) return false; var nextNumber = s.IndexOf(separator); if (nextNumber == -1) { return false; } if (!T.TryParse(s[..nextNumber], style, provider, out x)) return false; s = s[(nextNumber + separator.Length)..]; } { if (s.Length == 0) return false; if (!T.TryParse(s, style,provider, out y)) return false; } result = new Vector2D(x, y); return true; } static Vector2D IAdditiveIdentity, Vector2D>.AdditiveIdentity => Zero; static Vector2D IMultiplicativeIdentity, Vector2D>.MultiplicativeIdentity => One; static Vector2D IDecrementOperators>.operator --(Vector2D value) => value - One; static Vector2D IIncrementOperators>.operator ++(Vector2D value) => value + One; static Vector2D IUnaryPlusOperators, Vector2D>.operator +(Vector2D value) => value; static bool INumberBase>.IsCanonical(Vector2D value) => T.IsCanonical(value.X) && T.IsCanonical(value.Y); static bool INumberBase>.IsComplexNumber(Vector2D value) => T.IsComplexNumber(value.X) || T.IsComplexNumber(value.Y); static bool INumberBase>.IsEvenInteger(Vector2D value) => T.IsEvenInteger(value.X) && T.IsEvenInteger(value.Y); static bool INumberBase>.IsFinite(Vector2D value) => T.IsFinite(value.X) && T.IsFinite(value.Y); static bool INumberBase>.IsImaginaryNumber(Vector2D value) => T.IsImaginaryNumber(value.X) || T.IsImaginaryNumber(value.Y); static bool INumberBase>.IsInfinity(Vector2D value) => T.IsInfinity(value.X) && T.IsInfinity(value.Y); static bool INumberBase>.IsInteger(Vector2D value) => T.IsInteger(value.X) && T.IsInteger(value.Y); static bool INumberBase>.IsNaN(Vector2D value) => T.IsNaN(value.X) || T.IsNaN(value.Y); static bool INumberBase>.IsNegative(Vector2D value) => T.IsNegative(value.X) && T.IsNegative(value.Y); static bool INumberBase>.IsNegativeInfinity(Vector2D value) => T.IsNegativeInfinity(value.X) && T.IsNegativeInfinity(value.Y); static bool INumberBase>.IsNormal(Vector2D value) => T.IsNormal(value.X) && T.IsNormal(value.Y); static bool INumberBase>.IsOddInteger(Vector2D value) => T.IsOddInteger(value.X) && T.IsOddInteger(value.Y); static bool INumberBase>.IsPositive(Vector2D value) => T.IsPositive(value.X) && T.IsPositive(value.Y); static bool INumberBase>.IsPositiveInfinity(Vector2D value) => T.IsPositiveInfinity(value.X) && T.IsPositiveInfinity(value.Y); static bool INumberBase>.IsRealNumber(Vector2D value) => T.IsRealNumber(value.X) && T.IsRealNumber(value.Y); static bool INumberBase>.IsSubnormal(Vector2D value) => T.IsSubnormal(value.X) && T.IsSubnormal(value.Y); static bool INumberBase>.IsZero(Vector2D value) => T.IsZero(value.X) && T.IsZero(value.Y); static Vector2D INumberBase>.MaxMagnitude(Vector2D x, Vector2D y) { return new Vector2D(T.MaxMagnitude(x.X, y.X), T.MaxMagnitude(x.Y, y.Y)); } static Vector2D INumberBase>.MaxMagnitudeNumber(Vector2D x, Vector2D y) { return new Vector2D(T.MaxMagnitudeNumber(x.X, y.X), T.MaxMagnitudeNumber(x.Y, y.Y)); } static Vector2D INumberBase>.MinMagnitude(Vector2D x, Vector2D y) { return new Vector2D(T.MinMagnitude(x.X, y.X), T.MinMagnitude(x.Y, y.Y)); } static Vector2D INumberBase>.MinMagnitudeNumber(Vector2D x, Vector2D y) { return new Vector2D(T.MinMagnitudeNumber(x.X, y.X), T.MinMagnitudeNumber(x.Y, y.Y)); } public static bool TryConvertFromChecked(TOther value, out Vector2D result) where TOther : INumberBase { if (value is Vector2D v) { result = v; return true; } if (value is IVec2 IVec2 && IVec2.GetChecked() is {} r) { result = r; return true; } result = default; return false; } public static bool TryConvertFromSaturating(TOther value, out Vector2D result) where TOther : INumberBase { if (value is Vector2D v) { result = v; return true; } if (value is IVec2 IVec2 && IVec2.GetSaturating() is {} r) { result = r; return true; } result = default; return false; } public static bool TryConvertFromTruncating(TOther value, out Vector2D result) where TOther : INumberBase { if (value is Vector2D v) { result = v; return true; } if (value is IVec2 IVec2 && IVec2.GetTruncating() is {} r) { result = r; return true; } result = default; return false; } public static bool TryConvertToChecked(Vector2D value, [MaybeNullWhen(false)] out TOther result) where TOther : INumberBase { return TOther.TryConvertFromChecked(value, out result); } public static bool TryConvertToSaturating(Vector2D value, [MaybeNullWhen(false)] out TOther result) where TOther : INumberBase { return TOther.TryConvertFromSaturating(value, out result); } public static bool TryConvertToTruncating(Vector2D value, [MaybeNullWhen(false)]out TOther result) where TOther : INumberBase { return TOther.TryConvertFromTruncating(value, out result); } static int INumberBase>.Radix => T.Radix; Vector2D? IVec2.GetChecked() => T1.TryConvertFromChecked(X, out var x) ? new(x, T1.CreateChecked(Y)) : null; Vector2D? IVec2.GetSaturating() => T1.TryConvertFromSaturating(X, out var x) ? new(x, T1.CreateSaturating(Y)) : null; Vector2D? IVec2.GetTruncating() => T1.TryConvertFromTruncating(X, out var x) ? new(x, T1.CreateTruncating(Y)) : null; } // Vector2D.IReadOnlyList public readonly partial struct Vector2D : IReadOnlyList { IEnumerator IEnumerable.GetEnumerator() { yield return X; yield return Y; } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } int IReadOnlyCollection.Count => Count; } // Vector2D.IUtf8SpanParsableFormattable public partial struct Vector2D : IUtf8SpanFormattable, IUtf8SpanParsable> { public bool TryFormat(Span utf8Destination, out int bytesWritten, ReadOnlySpan format, IFormatProvider? provider) { // Possible fast path for failure case: // if (destination.Length < 4) return false; var separator = NumberFormatInfo.GetInstance(provider).NumberGroupSeparator; // We can't use an interpolated string here because it won't allow us to pass `format` var handler = new Utf8.TryWriteInterpolatedStringHandler( 4 + (separator.Length * 2), Count, utf8Destination, provider, out var shouldAppend ); if (!shouldAppend) { bytesWritten = 0; return false; } // Annoyingly we need to turn the span into a string for the string handler string? formatString = format.Length > 0 ? new string(format) : null; // JIT will automagically convert literals to utf8 _ = handler.AppendLiteral("<") && handler.AppendFormatted(X, formatString) && handler.AppendLiteral(separator) && handler.AppendLiteral(" ") && handler.AppendFormatted(Y, formatString) && handler.AppendLiteral(">"); return Utf8.TryWrite(utf8Destination, ref handler, out bytesWritten); } public static Vector2D Parse(ReadOnlySpan utf8Text, IFormatProvider? provider) => TryParse(utf8Text, provider, out var result) ? result : throw new ArgumentException($"Failed to parse {nameof(Vector2D)}<{typeof(T)}>"); public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out Vector2D result) { result = default; if (s[0] != (byte)'<') return false; if (s[^1] != (byte)'>') return false; var separator = NumberGroupSeparatorTChar(NumberFormatInfo.GetInstance(provider)); s = s[1..^1]; T? x, y; { if (s.Length == 0) return false; var nextNumber = s.IndexOf(separator); if (nextNumber == -1) { return false; } if (!T.TryParse(s[..nextNumber], provider, out x)) return false; s = s[(nextNumber + separator.Length)..]; } { if (s.Length == 0) return false; if (!T.TryParse(s, provider, out y)) return false; } result = new Vector2D(x, y); return true; [UnsafeAccessor(UnsafeAccessorKind.Method, Name = nameof(NumberGroupSeparatorTChar))] static extern ReadOnlySpan NumberGroupSeparatorTChar(NumberFormatInfo? c) where TChar : unmanaged; } } // Vector3D [StructLayout(LayoutKind.Sequential), DataContract, Serializable] public readonly partial struct Vector3D : IVector, T>, IVectorAlso, T>, IEquatable, ISpanFormattable where T : INumberBase { /// The X component of the vector. [DataMember] public readonly T X; /// The Y component of the vector. [DataMember] public readonly T Y; /// The Z component of the vector. [DataMember] public readonly T Z; internal const int Count = 3; /// Creates a new object whose three elements have the same value. /// The value to assign to all three elements. public Vector3D(T value) : this(value, value, value) { } /// Creates a new object from the specified object Z and a W component. /// The vector to use for the X and Y components. /// The Z component. public Vector3D(Vector2D value, T z) : this(value.X, value.Y, z) { } /// Creates a vector whose elements have the specified values. /// The value to assign to the field. /// The value to assign to the field. /// The value to assign to the field. public Vector3D(T x, T y, T z) { Unsafe.SkipInit(out this); X = x; Y = y; Z = z; } /// Constructs a vector from the given . The span must contain at least 4 elements. /// The span of elements to assign to the vector. public Vector3D(ReadOnlySpan values) { Unsafe.SkipInit(out this); ArgumentOutOfRangeException.ThrowIfLessThan(values.Length, Count, nameof(values)); this = Unsafe.ReadUnaligned>(ref Unsafe.As(ref MemoryMarshal.GetReference(values))); } /// Gets a vector whose 4 elements are equal to zero. /// A vector whose three elements are equal to zero (that is, it returns the vector (0,0,0). public static Vector3D Zero => new(T.Zero); /// Gets a vector whose 4 elements are equal to one. /// Returns . /// A vector whose three elements are equal to one (that is, it returns the vector (1,1,1). public static Vector3D One => new(T.One); /// Gets the vector (1,0,0)). /// The vector (1,0,0). public static Vector3D UnitX => new(T.One, T.Zero, T.Zero); /// Gets the vector (0,1,0)). /// The vector (0,1,0). public static Vector3D UnitY => new(T.Zero, T.One, T.Zero); /// Gets the vector (0,0,1)). /// The vector (0,0,1). public static Vector3D UnitZ => new(T.Zero, T.Zero, T.One); public ReadOnlySpan Components { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in X), Count); } [MethodImpl(MethodImplOptions.AggressiveInlining)] static Vector3D IVector, T>.CreateFromRepeatingComponent(T scalar) => new(scalar); /// Gets or sets the element at the specified index. /// The index of the element to get or set. /// The the element at . /// was less than zero or greater than the number of elements. public T this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Components[index]; } #region Operators /// Adds two vectors together. /// The first vector to add. /// The second vector to add. /// The summed vector. /// The method defines the addition operation for objects. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3D operator +(Vector3D left, Vector3D right) { return new Vector3D( left.X + right.X, left.Y + right.Y, left.Z + right.Z ); } /// Subtracts the second vector from the first. /// The first vector. /// The second vector. /// The vector that results from subtracting from . /// The method defines the subtraction operation for objects. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3D operator -(Vector3D left, Vector3D right) { return new Vector3D( left.X - right.X, left.Y - right.Y, left.Z - right.Z ); } /// Negates the specified vector. /// The vector to negate. /// The negated vector. /// The method defines the unary negation operation for objects. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3D operator -(Vector3D value) { return Zero - value; } /// Returns a new vector whose values are the product of each pair of elements in two specified vectors. /// The first vector. /// The second vector. /// The element-wise product vector. /// The method defines the multiplication operation for objects. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3D operator *(Vector3D left, Vector3D right) { return new Vector3D( left.X * right.X, left.Y * right.Y, left.Z * right.Z ); } /// Multiplies the specified vector by the specified scalar value. /// The vector. /// The scalar value. /// The scaled vector. /// The method defines the multiplication operation for objects. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3D operator *(Vector3D left, T right) { return left * new Vector3D(right); } /// Multiplies the scalar value by the specified vector. /// The vector. /// The scalar value. /// The scaled vector. /// The method defines the multiplication operation for objects. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3D operator *(T left, Vector3D right) { return right * left; } /// Divides the first vector by the second. /// The first vector. /// The second vector. /// The vector that results from dividing by . /// The method defines the division operation for objects. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3D operator /(Vector3D left, Vector3D right) { return new Vector3D( left.X / right.X, left.Y / right.Y, left.Z / right.Z ); } /// Divides the specified vector by a specified scalar value. /// The vector. /// The scalar value. /// The result of the division. /// The method defines the division operation for objects. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3D operator /(Vector3D value1, T value2) { return value1 / new Vector3D(value2); } /// Returns a value that indicates whether each pair of elements in two specified vectors is equal. /// The first vector to compare. /// The second vector to compare. /// if and are equal; otherwise, . /// Two objects are equal if each element in is equal to the corresponding element in . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Vector3D left, Vector3D right) { return left.X == right.X && left.Y == right.Y && left.Z == right.Z; } /// Returns a value that indicates whether two specified vectors are not equal. /// The first vector to compare. /// The second vector to compare. /// if and are not equal; otherwise, . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Vector3D left, Vector3D right) { return !(left == right); } #endregion #region CopyTo /// Copies the elements of the vector to a specified array. /// The destination array. /// must have at least three elements. The method copies the vector's elements starting at index 0. /// is . /// The number of elements in the current instance is greater than in the array. /// is multidimensional. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void CopyTo(T[] array) { // We explicitly don't check for `null` because historically this has thrown `NullReferenceException` for perf reasons ArgumentOutOfRangeException.ThrowIfLessThan(array.Length, Count, nameof(array)); Unsafe.WriteUnaligned(ref Unsafe.As(ref array[0]), this); } /// Copies the elements of the vector to a specified array starting at a specified index position. /// The destination array. /// The index at which to copy the first element of the vector. /// must have a sufficient number of elements to accommodate the three vector elements. In other words, elements through + 3 must already exist in . /// is . /// The number of elements in the current instance is greater than in the array. /// is less than zero. /// -or- /// is greater than or equal to the array length. /// is multidimensional. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void CopyTo(T[] array, int index) { // We explicitly don't check for `null` because historically this has thrown `NullReferenceException` for perf reasons ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)array.Length); ArgumentOutOfRangeException.ThrowIfLessThan((array.Length - index), Count); Unsafe.WriteUnaligned(ref Unsafe.As(ref array[index]), this); } /// Copies the vector to the given . The length of the destination span must be at least 4. /// The destination span which the values are copied into. /// If number of elements in source vector is greater than those available in destination span. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void CopyTo(Span destination) { ArgumentOutOfRangeException.ThrowIfLessThan(destination.Length, Count, nameof(destination)); Unsafe.WriteUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(destination)), this); } /// Attempts to copy the vector to the given . The length of the destination span must be at least 4. /// The destination span which the values are copied into. /// if the source vector was successfully copied to . if is not large enough to hold the source vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryCopyTo(Span destination) { if (destination.Length < Count) { return false; } Unsafe.WriteUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(destination)), this); return true; } #endregion #region Equality /// Returns a value that indicates whether this instance and another vector are equal. /// The other vector. /// if the two vectors are equal; otherwise, . /// Two vectors are equal if their , , , and elements are equal. [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Vector3D other) { return SpeedHelpers.FastEqualsUpTo4, T>(this, other); } /// Returns a value that indicates whether this instance and a specified object are equal. /// The object to compare with the current instance. /// if the current instance and are equal; otherwise, . If is , the method returns . /// The current instance and are equal if is a object and their corresponding elements are equal. [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals([NotNullWhen(true)] object? obj) { return (obj is Vector3D other) && Equals(other); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Vector3 other) { if (typeof(T) == typeof(float) && Vector128.IsHardwareAccelerated) { return Unsafe.BitCast, Vector3>(this).AsVector128().Equals(other.AsVector128()); } return float.CreateTruncating(X).Equals(other.X) && float.CreateTruncating(Y).Equals(other.Y) && float.CreateTruncating(Z).Equals(other.Z); } /// Returns the hash code for this instance. /// The hash code. public override int GetHashCode() { return HashCode.Combine(X, Y, Z); } #endregion #region Format /// Returns the string representation of the current instance using default formatting. /// The string representation of the current instance. /// This method returns a string in which each element of the vector is formatted using the "G" (general) format string and the formatting conventions of the current thread culture. The "<" and ">" characters are used to begin and end the string, and the current culture's property followed by a space is used to separate each element. public override string ToString() { return ToString("G", CultureInfo.CurrentCulture); } /// Returns the string representation of the current instance using the specified format string to format individual elements. /// A standard or custom numeric format string that defines the format of individual elements. /// The string representation of the current instance. /// This method returns a string in which each element of the vector is formatted using and the current culture's formatting conventions. The "<" and ">" characters are used to begin and end the string, and the current culture's property followed by a space is used to separate each element. /// Standard Numeric Format Strings /// Custom Numeric Format Strings public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format) { return ToString(format, CultureInfo.CurrentCulture); } /// Returns the string representation of the current instance using the specified format string to format individual elements and the specified format provider to define culture-specific formatting. /// A standard or custom numeric format string that defines the format of individual elements. /// A format provider that supplies culture-specific formatting information. /// The string representation of the current instance. /// This method returns a string in which each element of the vector is formatted using and . The "<" and ">" characters are used to begin and end the string, and the format provider's property followed by a space is used to separate each element. /// Standard Numeric Format Strings /// Custom Numeric Format Strings public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? formatProvider) { var separator = NumberFormatInfo.GetInstance(formatProvider).NumberGroupSeparator; Span initialBuffer = stackalloc char[Math.Min((2 + (Count - 1) + (separator.Length * (Count - 1)) + (Count * 2)), 256)]; // We can't use an interpolated string here because it won't allow us to pass `format` var handler = new DefaultInterpolatedStringHandler( 4 + (separator.Length * 2), Count, formatProvider, initialBuffer ); handler.AppendLiteral("<"); handler.AppendFormatted(X, format); handler.AppendLiteral(separator); handler.AppendLiteral(" "); handler.AppendFormatted(Y, format); handler.AppendLiteral(separator); handler.AppendLiteral(" "); handler.AppendFormatted(Z, format); handler.AppendLiteral(">"); return handler.ToStringAndClear(); } public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) { // Possible fast path for failure case: // if (destination.Length < 4) return false; var separator = NumberFormatInfo.GetInstance(provider).NumberGroupSeparator; // We can't use an interpolated string here because it won't allow us to pass `format` var handler = new MemoryExtensions.TryWriteInterpolatedStringHandler( 4 + (separator.Length * 2), Count, destination, provider, out var shouldAppend ); if (!shouldAppend) { charsWritten = 0; return false; } // Annoyingly we need to turn the span into a string for the string handler string? formatString = format.Length > 0 ? new string(format) : null; _ = handler.AppendLiteral("<") && handler.AppendFormatted(X, formatString) && handler.AppendLiteral(separator) && handler.AppendLiteral(" ") && handler.AppendFormatted(Y, formatString) && handler.AppendLiteral(separator) && handler.AppendLiteral(" ") && handler.AppendFormatted(Z, formatString) && handler.AppendLiteral(">"); return destination.TryWrite(ref handler, out charsWritten); } #endregion #region Casts public Vector3D As() where TOther : INumberBase { if (SpeedHelpers.TryFastConvert, T, Vector3D, TOther>(this, out var result)) { return result; } return new Vector3D( TOther.CreateTruncating(X), TOther.CreateTruncating(Y), TOther.CreateTruncating(Z) ); } private Vector3D AsChecked() where TOther : INumberBase { return new Vector3D( TOther.CreateChecked(X), TOther.CreateChecked(Y), TOther.CreateChecked(Z) ); } public static explicit operator Vector3D(Vector3D self) => self.As(); public static explicit operator Vector3D(Vector3D self) => self.As(); public static explicit operator Vector3D(Vector3D self) => self.As(); public static explicit operator Vector3D(Vector3D self) => self.As(); public static explicit operator Vector3D(Vector3D self) => self.As(); public static explicit operator Vector3D(Vector3D self) => self.As(); public static explicit operator Vector3D(Vector3D self) => self.As(); public static explicit operator Vector3D(Vector3D self) => self.As(); public static explicit operator Vector3D(Vector3D self) => self.As(); public static explicit operator Vector3D(Vector3D self) => self.As(); public static explicit operator Vector3D(Vector3D self) => self.As(); public static explicit operator Vector3D(Vector3D self) => self.As(); public static explicit operator Vector3D(Vector3D self) => self.As(); public static explicit operator Vector3D(Vector3D self) => self.As(); public static explicit operator Vector3D(Vector3D self) => self.As(); public static explicit operator Vector3D(Vector3D self) => self.As(); public static explicit operator checked Vector3D(Vector3D self) => self.AsChecked(); public static explicit operator checked Vector3D(Vector3D self) => self.AsChecked(); public static explicit operator checked Vector3D(Vector3D self) => self.AsChecked(); public static explicit operator checked Vector3D(Vector3D self) => self.AsChecked(); public static explicit operator checked Vector3D(Vector3D self) => self.AsChecked(); public static explicit operator checked Vector3D(Vector3D self) => self.AsChecked(); public static explicit operator checked Vector3D(Vector3D self) => self.AsChecked(); public static explicit operator checked Vector3D(Vector3D self) => self.AsChecked(); public static explicit operator checked Vector3D(Vector3D self) => self.AsChecked(); public static explicit operator checked Vector3D(Vector3D self) => self.AsChecked(); public static explicit operator checked Vector3D(Vector3D self) => self.AsChecked(); public static explicit operator checked Vector3D(Vector3D self) => self.AsChecked(); public static explicit operator checked Vector3D(Vector3D self) => self.AsChecked(); public static explicit operator checked Vector3D(Vector3D self) => self.AsChecked(); public static explicit operator checked Vector3D(Vector3D self) => self.AsChecked(); public static explicit operator checked Vector3D(Vector3D self) => self.AsChecked(); // Cast from System.Numerics.Vector3 public static explicit operator Vector3D(Vector3 self) => new(T.CreateTruncating(self.X), T.CreateTruncating(self.Y), T.CreateTruncating(self.Z)); public static explicit operator checked Vector3D(Vector3 self) => new(T.CreateChecked(self.X), T.CreateChecked(self.Y), T.CreateChecked(self.Z)); // Cast to System.Numerics.Vector3 public static explicit operator Vector3(Vector3D self) => new(float.CreateTruncating(self.X), float.CreateTruncating(self.Y), float.CreateTruncating(self.Z)); public static explicit operator checked Vector3(Vector3D self) => new(float.CreateChecked(self.X), float.CreateChecked(self.Y), float.CreateChecked(self.Z)); // Downcast public static explicit operator Vector2D(Vector3D self) => new(self.X, self.Y); // Upcast public static explicit operator Vector4D(Vector3D self) => new(self, T.Zero); // Upcast from System.Numerics.Vector < 3 public static explicit operator Vector3D(Vector2 self) => new(T.CreateTruncating(self.X), T.CreateTruncating(self.Y), T.Zero); public static explicit operator checked Vector3D(Vector2 self) => new(T.CreateChecked(self.X), T.CreateChecked(self.Y), T.Zero); // Downcast from System.Numerics.Vector > 3 public static implicit operator Vector3D((T X, T Y, T Z) components) => new(components.X, components.Y, components.Z); #endregion public void Deconstruct(out T x, out T y, out T z) { x = X; y = Y; z = Z; } } file interface IVec3 { // Returns null if incompatible. Throws OverflowException if overflowing Vector3D? GetChecked() where T : INumberBase; Vector3D? GetSaturating() where T : INumberBase; Vector3D? GetTruncating() where T : INumberBase; } // Vector3D.INumber public readonly partial struct Vector3D : IDivisionOperators, T, Vector3D>, IMultiplyOperators, T, Vector3D>, INumberBase>, IVec3 { /// Returns a vector whose elements are the absolute values of each of the specified vector's elements. /// A vector. /// The absolute value vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] static Vector3D INumberBase>.Abs(Vector3D value) => Vector3D.Abs(value); static Vector3D IParsable>.Parse(string s, IFormatProvider? provider) => Parse(s.AsSpan(), NumberStyles.None, provider); static Vector3D ISpanParsable>.Parse(ReadOnlySpan s, IFormatProvider? provider) => Parse(s, NumberStyles.None, provider); public static Vector3D Parse(string s, NumberStyles style = default, IFormatProvider? provider = null) => Parse(s.AsSpan(), style, provider); public static Vector3D Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.None, IFormatProvider? provider = null) => TryParse(s, style, provider, out var result) ? result : throw new ArgumentException($"Failed to parse {nameof(Vector3)}<{typeof(T)}>"); public static bool TryParse(string? s, IFormatProvider? provider, out Vector3D result) => TryParse(s.AsSpan(), NumberStyles.None, provider, out result); public static bool TryParse(string? s, NumberStyles style, IFormatProvider? provider, out Vector3D result) => TryParse(s.AsSpan(), style, provider, out result); public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out Vector3D result) => TryParse(s, NumberStyles.None, provider, out result); public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out Vector3D result) { result = default; if (s[0] != '<') return false; if (s[^1] != '>') return false; var separator = NumberFormatInfo.GetInstance(provider).NumberGroupSeparator; s = s[1..^1]; T? x, y, z; { if (s.Length == 0) return false; var nextNumber = s.IndexOf(separator); if (nextNumber == -1) { return false; } if (!T.TryParse(s[..nextNumber], style, provider, out x)) return false; s = s[(nextNumber + separator.Length)..]; } { if (s.Length == 0) return false; var nextNumber = s.IndexOf(separator); if (nextNumber == -1) { return false; } if (!T.TryParse(s[..nextNumber], style, provider, out y)) return false; s = s[(nextNumber + separator.Length)..]; } { if (s.Length == 0) return false; if (!T.TryParse(s, style,provider, out z)) return false; } result = new Vector3D(x, y, z); return true; } static Vector3D IAdditiveIdentity, Vector3D>.AdditiveIdentity => Zero; static Vector3D IMultiplicativeIdentity, Vector3D>.MultiplicativeIdentity => One; static Vector3D IDecrementOperators>.operator --(Vector3D value) => value - One; static Vector3D IIncrementOperators>.operator ++(Vector3D value) => value + One; static Vector3D IUnaryPlusOperators, Vector3D>.operator +(Vector3D value) => value; static bool INumberBase>.IsCanonical(Vector3D value) => T.IsCanonical(value.X) && T.IsCanonical(value.Y) && T.IsCanonical(value.Z); static bool INumberBase>.IsComplexNumber(Vector3D value) => T.IsComplexNumber(value.X) || T.IsComplexNumber(value.Y) || T.IsComplexNumber(value.Z); static bool INumberBase>.IsEvenInteger(Vector3D value) => T.IsEvenInteger(value.X) && T.IsEvenInteger(value.Y) && T.IsEvenInteger(value.Z); static bool INumberBase>.IsFinite(Vector3D value) => T.IsFinite(value.X) && T.IsFinite(value.Y) && T.IsFinite(value.Z); static bool INumberBase>.IsImaginaryNumber(Vector3D value) => T.IsImaginaryNumber(value.X) || T.IsImaginaryNumber(value.Y) || T.IsImaginaryNumber(value.Z); static bool INumberBase>.IsInfinity(Vector3D value) => T.IsInfinity(value.X) && T.IsInfinity(value.Y) && T.IsInfinity(value.Z); static bool INumberBase>.IsInteger(Vector3D value) => T.IsInteger(value.X) && T.IsInteger(value.Y) && T.IsInteger(value.Z); static bool INumberBase>.IsNaN(Vector3D value) => T.IsNaN(value.X) || T.IsNaN(value.Y) || T.IsNaN(value.Z); static bool INumberBase>.IsNegative(Vector3D value) => T.IsNegative(value.X) && T.IsNegative(value.Y) && T.IsNegative(value.Z); static bool INumberBase>.IsNegativeInfinity(Vector3D value) => T.IsNegativeInfinity(value.X) && T.IsNegativeInfinity(value.Y) && T.IsNegativeInfinity(value.Z); static bool INumberBase>.IsNormal(Vector3D value) => T.IsNormal(value.X) && T.IsNormal(value.Y) && T.IsNormal(value.Z); static bool INumberBase>.IsOddInteger(Vector3D value) => T.IsOddInteger(value.X) && T.IsOddInteger(value.Y) && T.IsOddInteger(value.Z); static bool INumberBase>.IsPositive(Vector3D value) => T.IsPositive(value.X) && T.IsPositive(value.Y) && T.IsPositive(value.Z); static bool INumberBase>.IsPositiveInfinity(Vector3D value) => T.IsPositiveInfinity(value.X) && T.IsPositiveInfinity(value.Y) && T.IsPositiveInfinity(value.Z); static bool INumberBase>.IsRealNumber(Vector3D value) => T.IsRealNumber(value.X) && T.IsRealNumber(value.Y) && T.IsRealNumber(value.Z); static bool INumberBase>.IsSubnormal(Vector3D value) => T.IsSubnormal(value.X) && T.IsSubnormal(value.Y) && T.IsSubnormal(value.Z); static bool INumberBase>.IsZero(Vector3D value) => T.IsZero(value.X) && T.IsZero(value.Y) && T.IsZero(value.Z); static Vector3D INumberBase>.MaxMagnitude(Vector3D x, Vector3D y) { return new Vector3D(T.MaxMagnitude(x.X, y.X), T.MaxMagnitude(x.Y, y.Y), T.MaxMagnitude(x.Z, y.Z)); } static Vector3D INumberBase>.MaxMagnitudeNumber(Vector3D x, Vector3D y) { return new Vector3D(T.MaxMagnitudeNumber(x.X, y.X), T.MaxMagnitudeNumber(x.Y, y.Y), T.MaxMagnitudeNumber(x.Z, y.Z)); } static Vector3D INumberBase>.MinMagnitude(Vector3D x, Vector3D y) { return new Vector3D(T.MinMagnitude(x.X, y.X), T.MinMagnitude(x.Y, y.Y), T.MinMagnitude(x.Z, y.Z)); } static Vector3D INumberBase>.MinMagnitudeNumber(Vector3D x, Vector3D y) { return new Vector3D(T.MinMagnitudeNumber(x.X, y.X), T.MinMagnitudeNumber(x.Y, y.Y), T.MinMagnitudeNumber(x.Z, y.Z)); } public static bool TryConvertFromChecked(TOther value, out Vector3D result) where TOther : INumberBase { if (value is Vector3D v) { result = v; return true; } if (value is IVec3 IVec3 && IVec3.GetChecked() is {} r) { result = r; return true; } result = default; return false; } public static bool TryConvertFromSaturating(TOther value, out Vector3D result) where TOther : INumberBase { if (value is Vector3D v) { result = v; return true; } if (value is IVec3 IVec3 && IVec3.GetSaturating() is {} r) { result = r; return true; } result = default; return false; } public static bool TryConvertFromTruncating(TOther value, out Vector3D result) where TOther : INumberBase { if (value is Vector3D v) { result = v; return true; } if (value is IVec3 IVec3 && IVec3.GetTruncating() is {} r) { result = r; return true; } result = default; return false; } public static bool TryConvertToChecked(Vector3D value, [MaybeNullWhen(false)] out TOther result) where TOther : INumberBase { return TOther.TryConvertFromChecked(value, out result); } public static bool TryConvertToSaturating(Vector3D value, [MaybeNullWhen(false)] out TOther result) where TOther : INumberBase { return TOther.TryConvertFromSaturating(value, out result); } public static bool TryConvertToTruncating(Vector3D value, [MaybeNullWhen(false)]out TOther result) where TOther : INumberBase { return TOther.TryConvertFromTruncating(value, out result); } static int INumberBase>.Radix => T.Radix; Vector3D? IVec3.GetChecked() => T1.TryConvertFromChecked(X, out var x) ? new(x, T1.CreateChecked(Y), T1.CreateChecked(Z)) : null; Vector3D? IVec3.GetSaturating() => T1.TryConvertFromSaturating(X, out var x) ? new(x, T1.CreateSaturating(Y), T1.CreateSaturating(Z)) : null; Vector3D? IVec3.GetTruncating() => T1.TryConvertFromTruncating(X, out var x) ? new(x, T1.CreateTruncating(Y), T1.CreateTruncating(Z)) : null; } // Vector3D.IReadOnlyList public readonly partial struct Vector3D : IReadOnlyList { IEnumerator IEnumerable.GetEnumerator() { yield return X; yield return Y; yield return Z; } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } int IReadOnlyCollection.Count => Count; } // Vector3D.IUtf8SpanParsableFormattable public partial struct Vector3D : IUtf8SpanFormattable, IUtf8SpanParsable> { public bool TryFormat(Span utf8Destination, out int bytesWritten, ReadOnlySpan format, IFormatProvider? provider) { // Possible fast path for failure case: // if (destination.Length < 4) return false; var separator = NumberFormatInfo.GetInstance(provider).NumberGroupSeparator; // We can't use an interpolated string here because it won't allow us to pass `format` var handler = new Utf8.TryWriteInterpolatedStringHandler( 4 + (separator.Length * 2), Count, utf8Destination, provider, out var shouldAppend ); if (!shouldAppend) { bytesWritten = 0; return false; } // Annoyingly we need to turn the span into a string for the string handler string? formatString = format.Length > 0 ? new string(format) : null; // JIT will automagically convert literals to utf8 _ = handler.AppendLiteral("<") && handler.AppendFormatted(X, formatString) && handler.AppendLiteral(separator) && handler.AppendLiteral(" ") && handler.AppendFormatted(Y, formatString) && handler.AppendLiteral(separator) && handler.AppendLiteral(" ") && handler.AppendFormatted(Z, formatString) && handler.AppendLiteral(">"); return Utf8.TryWrite(utf8Destination, ref handler, out bytesWritten); } public static Vector3D Parse(ReadOnlySpan utf8Text, IFormatProvider? provider) => TryParse(utf8Text, provider, out var result) ? result : throw new ArgumentException($"Failed to parse {nameof(Vector3D)}<{typeof(T)}>"); public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out Vector3D result) { result = default; if (s[0] != (byte)'<') return false; if (s[^1] != (byte)'>') return false; var separator = NumberGroupSeparatorTChar(NumberFormatInfo.GetInstance(provider)); s = s[1..^1]; T? x, y, z; { if (s.Length == 0) return false; var nextNumber = s.IndexOf(separator); if (nextNumber == -1) { return false; } if (!T.TryParse(s[..nextNumber], provider, out x)) return false; s = s[(nextNumber + separator.Length)..]; } { if (s.Length == 0) return false; var nextNumber = s.IndexOf(separator); if (nextNumber == -1) { return false; } if (!T.TryParse(s[..nextNumber], provider, out y)) return false; s = s[(nextNumber + separator.Length)..]; } { if (s.Length == 0) return false; if (!T.TryParse(s, provider, out z)) return false; } result = new Vector3D(x, y, z); return true; [UnsafeAccessor(UnsafeAccessorKind.Method, Name = nameof(NumberGroupSeparatorTChar))] static extern ReadOnlySpan NumberGroupSeparatorTChar(NumberFormatInfo? c) where TChar : unmanaged; } } // Vector4D [StructLayout(LayoutKind.Sequential), DataContract, Serializable] public readonly partial struct Vector4D : IVector, T>, IVectorAlso, T>, IEquatable, ISpanFormattable where T : INumberBase { /// The X component of the vector. [DataMember] public readonly T X; /// The Y component of the vector. [DataMember] public readonly T Y; /// The Z component of the vector. [DataMember] public readonly T Z; /// The W component of the vector. [DataMember] public readonly T W; internal const int Count = 4; /// Creates a new object whose four elements have the same value. /// The value to assign to all four elements. public Vector4D(T value) : this(value, value, value, value) { } /// Creates a new object from the specified object Z and a W component. /// The vector to use for the X and Y components. /// The Z component. /// The W component. public Vector4D(Vector2D value, T z, T w) : this(value.X, value.Y, z, w) { } /// Creates a new object from the specified object W component. /// The vector to use for the X, Y and Z components. /// The W component. public Vector4D(Vector3D value, T w) : this(value.X, value.Y, value.Z, w) { } /// Creates a vector whose elements have the specified values. /// The value to assign to the field. /// The value to assign to the field. /// The value to assign to the field. /// The value to assign to the field. public Vector4D(T x, T y, T z, T w) { Unsafe.SkipInit(out this); X = x; Y = y; Z = z; W = w; } /// Constructs a vector from the given . The span must contain at least 4 elements. /// The span of elements to assign to the vector. public Vector4D(ReadOnlySpan values) { Unsafe.SkipInit(out this); ArgumentOutOfRangeException.ThrowIfLessThan(values.Length, Count, nameof(values)); this = Unsafe.ReadUnaligned>(ref Unsafe.As(ref MemoryMarshal.GetReference(values))); } /// Gets a vector whose 4 elements are equal to zero. /// A vector whose four elements are equal to zero (that is, it returns the vector (0,0,0,0). public static Vector4D Zero => new(T.Zero); /// Gets a vector whose 4 elements are equal to one. /// Returns . /// A vector whose four elements are equal to one (that is, it returns the vector (1,1,1,1). public static Vector4D One => new(T.One); /// Gets the vector (1,0,0,0)). /// The vector (1,0,0,0). public static Vector4D UnitX => new(T.One, T.Zero, T.Zero, T.Zero); /// Gets the vector (0,1,0,0)). /// The vector (0,1,0,0). public static Vector4D UnitY => new(T.Zero, T.One, T.Zero, T.Zero); /// Gets the vector (0,0,1,0)). /// The vector (0,0,1,0). public static Vector4D UnitZ => new(T.Zero, T.Zero, T.One, T.Zero); /// Gets the vector (0,0,0,1)). /// The vector (0,0,0,1). public static Vector4D UnitW => new(T.Zero, T.Zero, T.Zero, T.One); public ReadOnlySpan Components { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in X), Count); } [MethodImpl(MethodImplOptions.AggressiveInlining)] static Vector4D IVector, T>.CreateFromRepeatingComponent(T scalar) => new(scalar); /// Gets or sets the element at the specified index. /// The index of the element to get or set. /// The the element at . /// was less than zero or greater than the number of elements. public T this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Components[index]; } #region Operators /// Adds two vectors together. /// The first vector to add. /// The second vector to add. /// The summed vector. /// The method defines the addition operation for objects. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4D operator +(Vector4D left, Vector4D right) { return new Vector4D( left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W ); } /// Subtracts the second vector from the first. /// The first vector. /// The second vector. /// The vector that results from subtracting from . /// The method defines the subtraction operation for objects. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4D operator -(Vector4D left, Vector4D right) { return new Vector4D( left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W ); } /// Negates the specified vector. /// The vector to negate. /// The negated vector. /// The method defines the unary negation operation for objects. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4D operator -(Vector4D value) { return Zero - value; } /// Returns a new vector whose values are the product of each pair of elements in two specified vectors. /// The first vector. /// The second vector. /// The element-wise product vector. /// The method defines the multiplication operation for objects. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4D operator *(Vector4D left, Vector4D right) { return new Vector4D( left.X * right.X, left.Y * right.Y, left.Z * right.Z, left.W * right.W ); } /// Multiplies the specified vector by the specified scalar value. /// The vector. /// The scalar value. /// The scaled vector. /// The method defines the multiplication operation for objects. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4D operator *(Vector4D left, T right) { return left * new Vector4D(right); } /// Multiplies the scalar value by the specified vector. /// The vector. /// The scalar value. /// The scaled vector. /// The method defines the multiplication operation for objects. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4D operator *(T left, Vector4D right) { return right * left; } /// Divides the first vector by the second. /// The first vector. /// The second vector. /// The vector that results from dividing by . /// The method defines the division operation for objects. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4D operator /(Vector4D left, Vector4D right) { return new Vector4D( left.X / right.X, left.Y / right.Y, left.Z / right.Z, left.W / right.W ); } /// Divides the specified vector by a specified scalar value. /// The vector. /// The scalar value. /// The result of the division. /// The method defines the division operation for objects. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4D operator /(Vector4D value1, T value2) { return value1 / new Vector4D(value2); } /// Returns a value that indicates whether each pair of elements in two specified vectors is equal. /// The first vector to compare. /// The second vector to compare. /// if and are equal; otherwise, . /// Two objects are equal if each element in is equal to the corresponding element in . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Vector4D left, Vector4D right) { return left.X == right.X && left.Y == right.Y && left.Z == right.Z && left.W == right.W; } /// Returns a value that indicates whether two specified vectors are not equal. /// The first vector to compare. /// The second vector to compare. /// if and are not equal; otherwise, . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Vector4D left, Vector4D right) { return !(left == right); } #endregion #region CopyTo /// Copies the elements of the vector to a specified array. /// The destination array. /// must have at least four elements. The method copies the vector's elements starting at index 0. /// is . /// The number of elements in the current instance is greater than in the array. /// is multidimensional. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void CopyTo(T[] array) { // We explicitly don't check for `null` because historically this has thrown `NullReferenceException` for perf reasons ArgumentOutOfRangeException.ThrowIfLessThan(array.Length, Count, nameof(array)); Unsafe.WriteUnaligned(ref Unsafe.As(ref array[0]), this); } /// Copies the elements of the vector to a specified array starting at a specified index position. /// The destination array. /// The index at which to copy the first element of the vector. /// must have a sufficient number of elements to accommodate the four vector elements. In other words, elements through + 4 must already exist in . /// is . /// The number of elements in the current instance is greater than in the array. /// is less than zero. /// -or- /// is greater than or equal to the array length. /// is multidimensional. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void CopyTo(T[] array, int index) { // We explicitly don't check for `null` because historically this has thrown `NullReferenceException` for perf reasons ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)array.Length); ArgumentOutOfRangeException.ThrowIfLessThan((array.Length - index), Count); Unsafe.WriteUnaligned(ref Unsafe.As(ref array[index]), this); } /// Copies the vector to the given . The length of the destination span must be at least 4. /// The destination span which the values are copied into. /// If number of elements in source vector is greater than those available in destination span. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void CopyTo(Span destination) { ArgumentOutOfRangeException.ThrowIfLessThan(destination.Length, Count, nameof(destination)); Unsafe.WriteUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(destination)), this); } /// Attempts to copy the vector to the given . The length of the destination span must be at least 4. /// The destination span which the values are copied into. /// if the source vector was successfully copied to . if is not large enough to hold the source vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryCopyTo(Span destination) { if (destination.Length < Count) { return false; } Unsafe.WriteUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(destination)), this); return true; } #endregion #region Equality /// Returns a value that indicates whether this instance and another vector are equal. /// The other vector. /// if the two vectors are equal; otherwise, . /// Two vectors are equal if their , , , and elements are equal. [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Vector4D other) { return SpeedHelpers.FastEqualsUpTo4, T>(this, other); } /// Returns a value that indicates whether this instance and a specified object are equal. /// The object to compare with the current instance. /// if the current instance and are equal; otherwise, . If is , the method returns . /// The current instance and are equal if is a object and their corresponding elements are equal. [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals([NotNullWhen(true)] object? obj) { return (obj is Vector4D other) && Equals(other); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Vector4 other) { if (typeof(T) == typeof(float) && Vector128.IsHardwareAccelerated) { return Unsafe.BitCast, Vector4>(this).AsVector128().Equals(other.AsVector128()); } return float.CreateTruncating(X).Equals(other.X) && float.CreateTruncating(Y).Equals(other.Y) && float.CreateTruncating(Z).Equals(other.Z) && float.CreateTruncating(W).Equals(other.W); } /// Returns the hash code for this instance. /// The hash code. public override int GetHashCode() { return HashCode.Combine(X, Y, Z, W); } #endregion #region Format /// Returns the string representation of the current instance using default formatting. /// The string representation of the current instance. /// This method returns a string in which each element of the vector is formatted using the "G" (general) format string and the formatting conventions of the current thread culture. The "<" and ">" characters are used to begin and end the string, and the current culture's property followed by a space is used to separate each element. public override string ToString() { return ToString("G", CultureInfo.CurrentCulture); } /// Returns the string representation of the current instance using the specified format string to format individual elements. /// A standard or custom numeric format string that defines the format of individual elements. /// The string representation of the current instance. /// This method returns a string in which each element of the vector is formatted using and the current culture's formatting conventions. The "<" and ">" characters are used to begin and end the string, and the current culture's property followed by a space is used to separate each element. /// Standard Numeric Format Strings /// Custom Numeric Format Strings public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format) { return ToString(format, CultureInfo.CurrentCulture); } /// Returns the string representation of the current instance using the specified format string to format individual elements and the specified format provider to define culture-specific formatting. /// A standard or custom numeric format string that defines the format of individual elements. /// A format provider that supplies culture-specific formatting information. /// The string representation of the current instance. /// This method returns a string in which each element of the vector is formatted using and . The "<" and ">" characters are used to begin and end the string, and the format provider's property followed by a space is used to separate each element. /// Standard Numeric Format Strings /// Custom Numeric Format Strings public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? formatProvider) { var separator = NumberFormatInfo.GetInstance(formatProvider).NumberGroupSeparator; Span initialBuffer = stackalloc char[Math.Min((2 + (Count - 1) + (separator.Length * (Count - 1)) + (Count * 2)), 256)]; // We can't use an interpolated string here because it won't allow us to pass `format` var handler = new DefaultInterpolatedStringHandler( 4 + (separator.Length * 2), Count, formatProvider, initialBuffer ); handler.AppendLiteral("<"); handler.AppendFormatted(X, format); handler.AppendLiteral(separator); handler.AppendLiteral(" "); handler.AppendFormatted(Y, format); handler.AppendLiteral(separator); handler.AppendLiteral(" "); handler.AppendFormatted(Z, format); handler.AppendLiteral(separator); handler.AppendLiteral(" "); handler.AppendFormatted(W, format); handler.AppendLiteral(">"); return handler.ToStringAndClear(); } public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) { // Possible fast path for failure case: // if (destination.Length < 4) return false; var separator = NumberFormatInfo.GetInstance(provider).NumberGroupSeparator; // We can't use an interpolated string here because it won't allow us to pass `format` var handler = new MemoryExtensions.TryWriteInterpolatedStringHandler( 4 + (separator.Length * 2), Count, destination, provider, out var shouldAppend ); if (!shouldAppend) { charsWritten = 0; return false; } // Annoyingly we need to turn the span into a string for the string handler string? formatString = format.Length > 0 ? new string(format) : null; _ = handler.AppendLiteral("<") && handler.AppendFormatted(X, formatString) && handler.AppendLiteral(separator) && handler.AppendLiteral(" ") && handler.AppendFormatted(Y, formatString) && handler.AppendLiteral(separator) && handler.AppendLiteral(" ") && handler.AppendFormatted(Z, formatString) && handler.AppendLiteral(separator) && handler.AppendLiteral(" ") && handler.AppendFormatted(W, formatString) && handler.AppendLiteral(">"); return destination.TryWrite(ref handler, out charsWritten); } #endregion #region Casts public Vector4D As() where TOther : INumberBase { if (SpeedHelpers.TryFastConvert, T, Vector4D, TOther>(this, out var result)) { return result; } return new Vector4D( TOther.CreateTruncating(X), TOther.CreateTruncating(Y), TOther.CreateTruncating(Z), TOther.CreateTruncating(W) ); } private Vector4D AsChecked() where TOther : INumberBase { return new Vector4D( TOther.CreateChecked(X), TOther.CreateChecked(Y), TOther.CreateChecked(Z), TOther.CreateChecked(W) ); } public static explicit operator Vector4D(Vector4D self) => self.As(); public static explicit operator Vector4D(Vector4D self) => self.As(); public static explicit operator Vector4D(Vector4D self) => self.As(); public static explicit operator Vector4D(Vector4D self) => self.As(); public static explicit operator Vector4D(Vector4D self) => self.As(); public static explicit operator Vector4D(Vector4D self) => self.As(); public static explicit operator Vector4D(Vector4D self) => self.As(); public static explicit operator Vector4D(Vector4D self) => self.As(); public static explicit operator Vector4D(Vector4D self) => self.As(); public static explicit operator Vector4D(Vector4D self) => self.As(); public static explicit operator Vector4D(Vector4D self) => self.As(); public static explicit operator Vector4D(Vector4D self) => self.As(); public static explicit operator Vector4D(Vector4D self) => self.As(); public static explicit operator Vector4D(Vector4D self) => self.As(); public static explicit operator Vector4D(Vector4D self) => self.As(); public static explicit operator Vector4D(Vector4D self) => self.As(); public static explicit operator checked Vector4D(Vector4D self) => self.AsChecked(); public static explicit operator checked Vector4D(Vector4D self) => self.AsChecked(); public static explicit operator checked Vector4D(Vector4D self) => self.AsChecked(); public static explicit operator checked Vector4D(Vector4D self) => self.AsChecked(); public static explicit operator checked Vector4D(Vector4D self) => self.AsChecked(); public static explicit operator checked Vector4D(Vector4D self) => self.AsChecked(); public static explicit operator checked Vector4D(Vector4D self) => self.AsChecked(); public static explicit operator checked Vector4D(Vector4D self) => self.AsChecked(); public static explicit operator checked Vector4D(Vector4D self) => self.AsChecked(); public static explicit operator checked Vector4D(Vector4D self) => self.AsChecked(); public static explicit operator checked Vector4D(Vector4D self) => self.AsChecked(); public static explicit operator checked Vector4D(Vector4D self) => self.AsChecked(); public static explicit operator checked Vector4D(Vector4D self) => self.AsChecked(); public static explicit operator checked Vector4D(Vector4D self) => self.AsChecked(); public static explicit operator checked Vector4D(Vector4D self) => self.AsChecked(); public static explicit operator checked Vector4D(Vector4D self) => self.AsChecked(); // Cast from System.Numerics.Vector4 public static explicit operator Vector4D(Vector4 self) => new(T.CreateTruncating(self.X), T.CreateTruncating(self.Y), T.CreateTruncating(self.Z), T.CreateTruncating(self.W)); public static explicit operator checked Vector4D(Vector4 self) => new(T.CreateChecked(self.X), T.CreateChecked(self.Y), T.CreateChecked(self.Z), T.CreateChecked(self.W)); // Cast to System.Numerics.Vector4 public static explicit operator Vector4(Vector4D self) => new(float.CreateTruncating(self.X), float.CreateTruncating(self.Y), float.CreateTruncating(self.Z), float.CreateTruncating(self.W)); public static explicit operator checked Vector4(Vector4D self) => new(float.CreateChecked(self.X), float.CreateChecked(self.Y), float.CreateChecked(self.Z), float.CreateChecked(self.W)); // Downcast public static explicit operator Vector2D(Vector4D self) => new(self.X, self.Y); public static explicit operator Vector3D(Vector4D self) => new(self.X, self.Y, self.Z); // Upcast // Upcast from System.Numerics.Vector < 4 public static explicit operator Vector4D(Vector2 self) => new(T.CreateTruncating(self.X), T.CreateTruncating(self.Y), T.Zero, T.Zero); public static explicit operator checked Vector4D(Vector2 self) => new(T.CreateChecked(self.X), T.CreateChecked(self.Y), T.Zero, T.Zero); public static explicit operator Vector4D(Vector3 self) => new(T.CreateTruncating(self.X), T.CreateTruncating(self.Y), T.CreateTruncating(self.Z), T.Zero); public static explicit operator checked Vector4D(Vector3 self) => new(T.CreateChecked(self.X), T.CreateChecked(self.Y), T.CreateChecked(self.Z), T.Zero); // Downcast from System.Numerics.Vector > 4 public static explicit operator Vector4D(Vector5 self) => new(T.CreateTruncating(self.X), T.CreateTruncating(self.Y), T.CreateTruncating(self.Z), T.CreateTruncating(self.W)); public static explicit operator checked Vector4D(Vector5 self) => new(T.CreateChecked(self.X), T.CreateChecked(self.Y), T.CreateChecked(self.Z), T.CreateChecked(self.W)); public static implicit operator Vector4D((T X, T Y, T Z, T W) components) => new(components.X, components.Y, components.Z, components.W); #endregion public void Deconstruct(out T x, out T y, out T z, out T w) { x = X; y = Y; z = Z; w = W; } } file interface IVec4 { // Returns null if incompatible. Throws OverflowException if overflowing Vector4D? GetChecked() where T : INumberBase; Vector4D? GetSaturating() where T : INumberBase; Vector4D? GetTruncating() where T : INumberBase; } // Vector4D.INumber public readonly partial struct Vector4D : IDivisionOperators, T, Vector4D>, IMultiplyOperators, T, Vector4D>, INumberBase>, IVec4 { /// Returns a vector whose elements are the absolute values of each of the specified vector's elements. /// A vector. /// The absolute value vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] static Vector4D INumberBase>.Abs(Vector4D value) => Vector4D.Abs(value); static Vector4D IParsable>.Parse(string s, IFormatProvider? provider) => Parse(s.AsSpan(), NumberStyles.None, provider); static Vector4D ISpanParsable>.Parse(ReadOnlySpan s, IFormatProvider? provider) => Parse(s, NumberStyles.None, provider); public static Vector4D Parse(string s, NumberStyles style = default, IFormatProvider? provider = null) => Parse(s.AsSpan(), style, provider); public static Vector4D Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.None, IFormatProvider? provider = null) => TryParse(s, style, provider, out var result) ? result : throw new ArgumentException($"Failed to parse {nameof(Vector4)}<{typeof(T)}>"); public static bool TryParse(string? s, IFormatProvider? provider, out Vector4D result) => TryParse(s.AsSpan(), NumberStyles.None, provider, out result); public static bool TryParse(string? s, NumberStyles style, IFormatProvider? provider, out Vector4D result) => TryParse(s.AsSpan(), style, provider, out result); public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out Vector4D result) => TryParse(s, NumberStyles.None, provider, out result); public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out Vector4D result) { result = default; if (s[0] != '<') return false; if (s[^1] != '>') return false; var separator = NumberFormatInfo.GetInstance(provider).NumberGroupSeparator; s = s[1..^1]; T? x, y, z, w; { if (s.Length == 0) return false; var nextNumber = s.IndexOf(separator); if (nextNumber == -1) { return false; } if (!T.TryParse(s[..nextNumber], style, provider, out x)) return false; s = s[(nextNumber + separator.Length)..]; } { if (s.Length == 0) return false; var nextNumber = s.IndexOf(separator); if (nextNumber == -1) { return false; } if (!T.TryParse(s[..nextNumber], style, provider, out y)) return false; s = s[(nextNumber + separator.Length)..]; } { if (s.Length == 0) return false; var nextNumber = s.IndexOf(separator); if (nextNumber == -1) { return false; } if (!T.TryParse(s[..nextNumber], style, provider, out z)) return false; s = s[(nextNumber + separator.Length)..]; } { if (s.Length == 0) return false; if (!T.TryParse(s, style,provider, out w)) return false; } result = new Vector4D(x, y, z, w); return true; } static Vector4D IAdditiveIdentity, Vector4D>.AdditiveIdentity => Zero; static Vector4D IMultiplicativeIdentity, Vector4D>.MultiplicativeIdentity => One; static Vector4D IDecrementOperators>.operator --(Vector4D value) => value - One; static Vector4D IIncrementOperators>.operator ++(Vector4D value) => value + One; static Vector4D IUnaryPlusOperators, Vector4D>.operator +(Vector4D value) => value; static bool INumberBase>.IsCanonical(Vector4D value) => T.IsCanonical(value.X) && T.IsCanonical(value.Y) && T.IsCanonical(value.Z) && T.IsCanonical(value.W); static bool INumberBase>.IsComplexNumber(Vector4D value) => T.IsComplexNumber(value.X) || T.IsComplexNumber(value.Y) || T.IsComplexNumber(value.Z) || T.IsComplexNumber(value.W); static bool INumberBase>.IsEvenInteger(Vector4D value) => T.IsEvenInteger(value.X) && T.IsEvenInteger(value.Y) && T.IsEvenInteger(value.Z) && T.IsEvenInteger(value.W); static bool INumberBase>.IsFinite(Vector4D value) => T.IsFinite(value.X) && T.IsFinite(value.Y) && T.IsFinite(value.Z) && T.IsFinite(value.W); static bool INumberBase>.IsImaginaryNumber(Vector4D value) => T.IsImaginaryNumber(value.X) || T.IsImaginaryNumber(value.Y) || T.IsImaginaryNumber(value.Z) || T.IsImaginaryNumber(value.W); static bool INumberBase>.IsInfinity(Vector4D value) => T.IsInfinity(value.X) && T.IsInfinity(value.Y) && T.IsInfinity(value.Z) && T.IsInfinity(value.W); static bool INumberBase>.IsInteger(Vector4D value) => T.IsInteger(value.X) && T.IsInteger(value.Y) && T.IsInteger(value.Z) && T.IsInteger(value.W); static bool INumberBase>.IsNaN(Vector4D value) => T.IsNaN(value.X) || T.IsNaN(value.Y) || T.IsNaN(value.Z) || T.IsNaN(value.W); static bool INumberBase>.IsNegative(Vector4D value) => T.IsNegative(value.X) && T.IsNegative(value.Y) && T.IsNegative(value.Z) && T.IsNegative(value.W); static bool INumberBase>.IsNegativeInfinity(Vector4D value) => T.IsNegativeInfinity(value.X) && T.IsNegativeInfinity(value.Y) && T.IsNegativeInfinity(value.Z) && T.IsNegativeInfinity(value.W); static bool INumberBase>.IsNormal(Vector4D value) => T.IsNormal(value.X) && T.IsNormal(value.Y) && T.IsNormal(value.Z) && T.IsNormal(value.W); static bool INumberBase>.IsOddInteger(Vector4D value) => T.IsOddInteger(value.X) && T.IsOddInteger(value.Y) && T.IsOddInteger(value.Z) && T.IsOddInteger(value.W); static bool INumberBase>.IsPositive(Vector4D value) => T.IsPositive(value.X) && T.IsPositive(value.Y) && T.IsPositive(value.Z) && T.IsPositive(value.W); static bool INumberBase>.IsPositiveInfinity(Vector4D value) => T.IsPositiveInfinity(value.X) && T.IsPositiveInfinity(value.Y) && T.IsPositiveInfinity(value.Z) && T.IsPositiveInfinity(value.W); static bool INumberBase>.IsRealNumber(Vector4D value) => T.IsRealNumber(value.X) && T.IsRealNumber(value.Y) && T.IsRealNumber(value.Z) && T.IsRealNumber(value.W); static bool INumberBase>.IsSubnormal(Vector4D value) => T.IsSubnormal(value.X) && T.IsSubnormal(value.Y) && T.IsSubnormal(value.Z) && T.IsSubnormal(value.W); static bool INumberBase>.IsZero(Vector4D value) => T.IsZero(value.X) && T.IsZero(value.Y) && T.IsZero(value.Z) && T.IsZero(value.W); static Vector4D INumberBase>.MaxMagnitude(Vector4D x, Vector4D y) { return new Vector4D(T.MaxMagnitude(x.X, y.X), T.MaxMagnitude(x.Y, y.Y), T.MaxMagnitude(x.Z, y.Z), T.MaxMagnitude(x.W, y.W)); } static Vector4D INumberBase>.MaxMagnitudeNumber(Vector4D x, Vector4D y) { return new Vector4D(T.MaxMagnitudeNumber(x.X, y.X), T.MaxMagnitudeNumber(x.Y, y.Y), T.MaxMagnitudeNumber(x.Z, y.Z), T.MaxMagnitudeNumber(x.W, y.W)); } static Vector4D INumberBase>.MinMagnitude(Vector4D x, Vector4D y) { return new Vector4D(T.MinMagnitude(x.X, y.X), T.MinMagnitude(x.Y, y.Y), T.MinMagnitude(x.Z, y.Z), T.MinMagnitude(x.W, y.W)); } static Vector4D INumberBase>.MinMagnitudeNumber(Vector4D x, Vector4D y) { return new Vector4D(T.MinMagnitudeNumber(x.X, y.X), T.MinMagnitudeNumber(x.Y, y.Y), T.MinMagnitudeNumber(x.Z, y.Z), T.MinMagnitudeNumber(x.W, y.W)); } public static bool TryConvertFromChecked(TOther value, out Vector4D result) where TOther : INumberBase { if (value is Vector4D v) { result = v; return true; } if (value is IVec4 IVec4 && IVec4.GetChecked() is {} r) { result = r; return true; } result = default; return false; } public static bool TryConvertFromSaturating(TOther value, out Vector4D result) where TOther : INumberBase { if (value is Vector4D v) { result = v; return true; } if (value is IVec4 IVec4 && IVec4.GetSaturating() is {} r) { result = r; return true; } result = default; return false; } public static bool TryConvertFromTruncating(TOther value, out Vector4D result) where TOther : INumberBase { if (value is Vector4D v) { result = v; return true; } if (value is IVec4 IVec4 && IVec4.GetTruncating() is {} r) { result = r; return true; } result = default; return false; } public static bool TryConvertToChecked(Vector4D value, [MaybeNullWhen(false)] out TOther result) where TOther : INumberBase { return TOther.TryConvertFromChecked(value, out result); } public static bool TryConvertToSaturating(Vector4D value, [MaybeNullWhen(false)] out TOther result) where TOther : INumberBase { return TOther.TryConvertFromSaturating(value, out result); } public static bool TryConvertToTruncating(Vector4D value, [MaybeNullWhen(false)]out TOther result) where TOther : INumberBase { return TOther.TryConvertFromTruncating(value, out result); } static int INumberBase>.Radix => T.Radix; Vector4D? IVec4.GetChecked() => T1.TryConvertFromChecked(X, out var x) ? new(x, T1.CreateChecked(Y), T1.CreateChecked(Z), T1.CreateChecked(W)) : null; Vector4D? IVec4.GetSaturating() => T1.TryConvertFromSaturating(X, out var x) ? new(x, T1.CreateSaturating(Y), T1.CreateSaturating(Z), T1.CreateSaturating(W)) : null; Vector4D? IVec4.GetTruncating() => T1.TryConvertFromTruncating(X, out var x) ? new(x, T1.CreateTruncating(Y), T1.CreateTruncating(Z), T1.CreateTruncating(W)) : null; } // Vector4D.IReadOnlyList public readonly partial struct Vector4D : IReadOnlyList { IEnumerator IEnumerable.GetEnumerator() { yield return X; yield return Y; yield return Z; yield return W; } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } int IReadOnlyCollection.Count => Count; } // Vector4D.IUtf8SpanParsableFormattable public partial struct Vector4D : IUtf8SpanFormattable, IUtf8SpanParsable> { public bool TryFormat(Span utf8Destination, out int bytesWritten, ReadOnlySpan format, IFormatProvider? provider) { // Possible fast path for failure case: // if (destination.Length < 4) return false; var separator = NumberFormatInfo.GetInstance(provider).NumberGroupSeparator; // We can't use an interpolated string here because it won't allow us to pass `format` var handler = new Utf8.TryWriteInterpolatedStringHandler( 4 + (separator.Length * 2), Count, utf8Destination, provider, out var shouldAppend ); if (!shouldAppend) { bytesWritten = 0; return false; } // Annoyingly we need to turn the span into a string for the string handler string? formatString = format.Length > 0 ? new string(format) : null; // JIT will automagically convert literals to utf8 _ = handler.AppendLiteral("<") && handler.AppendFormatted(X, formatString) && handler.AppendLiteral(separator) && handler.AppendLiteral(" ") && handler.AppendFormatted(Y, formatString) && handler.AppendLiteral(separator) && handler.AppendLiteral(" ") && handler.AppendFormatted(Z, formatString) && handler.AppendLiteral(separator) && handler.AppendLiteral(" ") && handler.AppendFormatted(W, formatString) && handler.AppendLiteral(">"); return Utf8.TryWrite(utf8Destination, ref handler, out bytesWritten); } public static Vector4D Parse(ReadOnlySpan utf8Text, IFormatProvider? provider) => TryParse(utf8Text, provider, out var result) ? result : throw new ArgumentException($"Failed to parse {nameof(Vector4D)}<{typeof(T)}>"); public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out Vector4D result) { result = default; if (s[0] != (byte)'<') return false; if (s[^1] != (byte)'>') return false; var separator = NumberGroupSeparatorTChar(NumberFormatInfo.GetInstance(provider)); s = s[1..^1]; T? x, y, z, w; { if (s.Length == 0) return false; var nextNumber = s.IndexOf(separator); if (nextNumber == -1) { return false; } if (!T.TryParse(s[..nextNumber], provider, out x)) return false; s = s[(nextNumber + separator.Length)..]; } { if (s.Length == 0) return false; var nextNumber = s.IndexOf(separator); if (nextNumber == -1) { return false; } if (!T.TryParse(s[..nextNumber], provider, out y)) return false; s = s[(nextNumber + separator.Length)..]; } { if (s.Length == 0) return false; var nextNumber = s.IndexOf(separator); if (nextNumber == -1) { return false; } if (!T.TryParse(s[..nextNumber], provider, out z)) return false; s = s[(nextNumber + separator.Length)..]; } { if (s.Length == 0) return false; if (!T.TryParse(s, provider, out w)) return false; } result = new Vector4D(x, y, z, w); return true; [UnsafeAccessor(UnsafeAccessorKind.Method, Name = nameof(NumberGroupSeparatorTChar))] static extern ReadOnlySpan NumberGroupSeparatorTChar(NumberFormatInfo? c) where TChar : unmanaged; } }