using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Misaki.HighPerformance.Mathematics.SPMD; [StructLayout(LayoutKind.Sequential)] public readonly unsafe struct ScalarLane : ISPMD, TNumber> where TNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators { public readonly TNumber value; public static int LaneWidth { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => 1; } public static ScalarLane Zero { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new(TNumber.Zero); } public static ScalarLane One { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new(TNumber.One); } public static ScalarLane MinValue { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new(TNumber.MinValue); } public static ScalarLane MaxValue { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new(TNumber.MaxValue); } public readonly TNumber this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value; } public ScalarLane(TNumber value) { this.value = value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Create(TNumber value) { return new(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Create(params ReadOnlySpan values) { return new(values[0]); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Create(Vector value) { return new(value[0]); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Sequence(TNumber start, TNumber step) { return new(start); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Load(ref TNumber value) { return new(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Load(TNumber* pValue) { return new(*pValue); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly void Store(ref TNumber destination) { destination = value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly void Store(TNumber* pDestination) { *pDestination = value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int CompressStore(ScalarLane mask, ref TNumber destination) { return CompressStore(mask, (TNumber*)Unsafe.AsPointer(in destination)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int CompressStore(ScalarLane mask, TNumber* pDestination) { if (mask.value != TNumber.Zero) { *pDestination = value; return 1; } return 0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector AsVector() { return Vector.Create(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane operator +(ScalarLane a, ScalarLane b) { return new(a.value + b.value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane operator -(ScalarLane a, ScalarLane b) { return new(a.value - b.value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane operator *(ScalarLane a, ScalarLane b) { return new(a.value * b.value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane operator /(ScalarLane a, ScalarLane b) { return new(a.value / b.value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane operator %(ScalarLane a, ScalarLane b) { return new(a.value % b.value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane operator -(ScalarLane a) { return new(-a.value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane operator &(ScalarLane a, ScalarLane b) { return new(a.value & b.value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane operator |(ScalarLane a, ScalarLane b) { return new(a.value | b.value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane operator ^(ScalarLane a, ScalarLane b) { return new(a.value ^ b.value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane operator ~(ScalarLane a) { return new(~a.value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane operator ==(ScalarLane a, ScalarLane b) { return Equal(a, b); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane operator !=(ScalarLane a, ScalarLane b) { return ~Equal(a, b); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane operator >(ScalarLane a, ScalarLane b) { return GreaterThan(a, b); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane operator >=(ScalarLane a, ScalarLane b) { return GreaterThanOrEqual(a, b); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane operator <(ScalarLane a, ScalarLane b) { return LessThan(a, b); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane operator <=(ScalarLane a, ScalarLane b) { return LessThanOrEqual(a, b); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator ScalarLane(TNumber value) { return new(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Abs(ScalarLane value) { return new(TNumber.Abs(value.value)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Floor(ScalarLane value) { // Note: INumber does not provide Floor method, so we need to handle float and double specifically. // This is acceptable for performance because JIT generates specialized code for each TLane as long as they are struct. // Which mean for ScalarLane, typeof(TLane) == typeof(float) is always true and jit will optimize away the other branches. if (typeof(TNumber) == typeof(float)) { var f = Unsafe.As, float>(ref value); var result = MathF.Floor(f); return Unsafe.As>(ref result); } else if (typeof(TNumber) == typeof(double)) { var d = Unsafe.As, double>(ref value); var result = Math.Floor(d); return Unsafe.As>(ref result); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Frac(ScalarLane value) { return new(value.value - TNumber.CreateTruncating(value.value)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Sqrt(ScalarLane value) { if (typeof(TNumber) == typeof(float)) { var f = Unsafe.As, float>(ref value); var result = MathF.Sqrt(f); return Unsafe.As>(ref result); } else if (typeof(TNumber) == typeof(double)) { var d = Unsafe.As, double>(ref value); var result = Math.Sqrt(d); return Unsafe.As>(ref result); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Lerp(ScalarLane a, ScalarLane b, ScalarLane t) { return new(a.value + (b.value - a.value) * t.value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane MultipleAdd(ScalarLane a, ScalarLane b, ScalarLane c) { return new(TNumber.MultiplyAddEstimate(a.value, b.value, c.value)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Min(ScalarLane a, ScalarLane b) { return new(TNumber.Min(a.value, b.value)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Max(ScalarLane a, ScalarLane b) { return new(TNumber.Max(a.value, b.value)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Clamp(ScalarLane value, ScalarLane min, ScalarLane max) { return new(TNumber.Clamp(value.value, min.value, max.value)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Saturate(ScalarLane value) { return Clamp(value, new(TNumber.Zero), new(TNumber.One)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Sin(ScalarLane value) { if (typeof(TNumber) == typeof(float)) { var f = Unsafe.As, float>(ref value); var result = MathF.Sin(f); return Unsafe.As>(ref result); } else if (typeof(TNumber) == typeof(double)) { var d = Unsafe.As, double>(ref value); var result = Math.Sin(d); return Unsafe.As>(ref result); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Cos(ScalarLane value) { if (typeof(TNumber) == typeof(float)) { var f = Unsafe.As, float>(ref value); var result = MathF.Cos(f); return Unsafe.As>(ref result); } else if (typeof(TNumber) == typeof(double)) { var d = Unsafe.As, double>(ref value); var result = Math.Cos(d); return Unsafe.As>(ref result); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (ScalarLane sin, ScalarLane cos) SinCos(ScalarLane value) { return (Sin(value), Cos(value)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Tan(ScalarLane value) { if (typeof(TNumber) == typeof(float)) { var f = Unsafe.As, float>(ref value); var result = MathF.Tan(f); return Unsafe.As>(ref result); } else if (typeof(TNumber) == typeof(double)) { var d = Unsafe.As, double>(ref value); var result = Math.Tan(d); return Unsafe.As>(ref result); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Asin(ScalarLane value) { if (typeof(TNumber) == typeof(float)) { var f = Unsafe.As, float>(ref value); var result = MathF.Asin(f); return Unsafe.As>(ref result); } else if (typeof(TNumber) == typeof(double)) { var d = Unsafe.As, double>(ref value); var result = Math.Asin(d); return Unsafe.As>(ref result); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Acos(ScalarLane value) { if (typeof(TNumber) == typeof(float)) { var f = Unsafe.As, float>(ref value); var result = MathF.Acos(f); return Unsafe.As>(ref result); } else if (typeof(TNumber) == typeof(double)) { var d = Unsafe.As, double>(ref value); var result = Math.Acos(d); return Unsafe.As>(ref result); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Atan(ScalarLane value) { if (typeof(TNumber) == typeof(float)) { var f = Unsafe.As, float>(ref value); var result = MathF.Atan(f); return Unsafe.As>(ref result); } else if (typeof(TNumber) == typeof(double)) { var d = Unsafe.As, double>(ref value); var result = Math.Atan(d); return Unsafe.As>(ref result); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Atan2(ScalarLane y, ScalarLane x) { if (typeof(TNumber) == typeof(float)) { var fy = Unsafe.As, float>(ref y); var fx = Unsafe.As, float>(ref x); var result = MathF.Atan2(fy, fx); return Unsafe.As>(ref result); } else if (typeof(TNumber) == typeof(double)) { var dy = Unsafe.As, double>(ref y); var dx = Unsafe.As, double>(ref x); var result = Math.Atan2(dy, dx); return Unsafe.As>(ref result); } return y; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Pow(ScalarLane x, ScalarLane y) { if (typeof(TNumber) == typeof(float)) { var fx = Unsafe.As, float>(ref x); var fy = Unsafe.As, float>(ref y); var result = MathF.Pow(fx, fy); return Unsafe.As>(ref result); } else if (typeof(TNumber) == typeof(double)) { var dx = Unsafe.As, double>(ref x); var dy = Unsafe.As, double>(ref y); var result = Math.Pow(dx, dy); return Unsafe.As>(ref result); } return x; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Exp(ScalarLane value) { if (typeof(TNumber) == typeof(float)) { var f = Unsafe.As, float>(ref value); var result = MathF.Exp(f); return Unsafe.As>(ref result); } else if (typeof(TNumber) == typeof(double)) { var d = Unsafe.As, double>(ref value); var result = Math.Log(d); return Unsafe.As>(ref result); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Exp2(ScalarLane value) { return Pow(new ScalarLane(TNumber.CreateTruncating(2)), value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Log(ScalarLane value) { if (typeof(TNumber) == typeof(float)) { var f = Unsafe.As, float>(ref value); var result = MathF.Log(f); return Unsafe.As>(ref result); } else if (typeof(TNumber) == typeof(double)) { var d = Unsafe.As, double>(ref value); var result = Math.Log(d); return Unsafe.As>(ref result); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Log2(ScalarLane value) { if (typeof(TNumber) == typeof(float)) { var f = Unsafe.As, float>(ref value); var result = MathF.Log2(f); return Unsafe.As>(ref result); } else if (typeof(TNumber) == typeof(double)) { var d = Unsafe.As, double>(ref value); var result = Math.Log2(d); return Unsafe.As>(ref result); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Ceil(ScalarLane value) { if (typeof(TNumber) == typeof(float)) { var f = Unsafe.As, float>(ref value); var result = MathF.Ceiling(f); return Unsafe.As>(ref result); } else if (typeof(TNumber) == typeof(double)) { var d = Unsafe.As, double>(ref value); var result = Math.Ceiling(d); return Unsafe.As>(ref result); } else if (typeof(TNumber) == typeof(decimal)) { var d = Unsafe.As, decimal>(ref value); var result = Math.Ceiling(d); return Unsafe.As>(ref result); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Round(ScalarLane value) { if (typeof(TNumber) == typeof(float)) { var f = Unsafe.As, float>(ref value); var result = MathF.Round(f); return Unsafe.As>(ref result); } else if (typeof(TNumber) == typeof(double)) { var d = Unsafe.As, double>(ref value); var result = Math.Round(d); return Unsafe.As>(ref result); } else if (typeof(TNumber) == typeof(decimal)) { var d = Unsafe.As, decimal>(ref value); var result = Math.Round(d); return Unsafe.As>(ref result); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Trunc(ScalarLane value) { if (typeof(TNumber) == typeof(float)) { var f = Unsafe.As, float>(ref value); var result = MathF.Truncate(f); return Unsafe.As>(ref result); } else if (typeof(TNumber) == typeof(double)) { var d = Unsafe.As, double>(ref value); var result = Math.Truncate(d); return Unsafe.As>(ref result); } else if (typeof(TNumber) == typeof(decimal)) { var d = Unsafe.As, decimal>(ref value); var result = Math.Truncate(d); return Unsafe.As>(ref result); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Sign(ScalarLane value) { return new((value.value > TNumber.Zero) ? TNumber.One : (value.value < TNumber.Zero) ? ~TNumber.Zero : TNumber.Zero); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane CopySign(ScalarLane magnitude, ScalarLane sign) { return new(TNumber.CopySign(magnitude.value, sign.value)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Rcp(ScalarLane value) { return new(TNumber.One / value.value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Rsqrt(ScalarLane value) { return Sqrt(Rcp(value)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Select(ScalarLane conditionMask, ScalarLane ifTrue, ScalarLane ifFalse) { return new(conditionMask.value != TNumber.Zero ? ifTrue.value : ifFalse.value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane GreaterThan(ScalarLane a, ScalarLane b) { return new(a.value > b.value ? ~TNumber.Zero : TNumber.Zero); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane GreaterThanOrEqual(ScalarLane a, ScalarLane b) { return new(a.value >= b.value ? ~TNumber.Zero : TNumber.Zero); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane LessThan(ScalarLane a, ScalarLane b) { return new(a.value < b.value ? ~TNumber.Zero : TNumber.Zero); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane LessThanOrEqual(ScalarLane a, ScalarLane b) { return new(a.value <= b.value ? ~TNumber.Zero : TNumber.Zero); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Equal(ScalarLane a, ScalarLane b) { return new(a.value == b.value ? ~TNumber.Zero : TNumber.Zero); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool Any(ScalarLane mask) { return mask.value != TNumber.Zero; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool All(ScalarLane mask) { return mask.value != TNumber.Zero; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool None(ScalarLane mask) { return mask.value == TNumber.Zero; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(ScalarLane other) { return value.Equals(other.value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object? obj) { return obj is ScalarLane other && value.Equals(other.value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return value.GetHashCode(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() { return value.ToString() ?? string.Empty; } }