< Summary

Information
Class: FixedMathSharp.WideNormalization
Assembly: FixedMathSharp
File(s): /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Numerics/Wide/WideNormalization.cs
Line coverage
100%
Covered lines: 269
Uncovered lines: 0
Coverable lines: 269
Total lines: 430
Line coverage: 100%
Branch coverage
100%
Covered branches: 70
Total branches: 70
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
GetDirection(...)100%11100%
GetNormalized(...)100%44100%
GetNormalized(...)100%66100%
GetNormalized(...)100%44100%
GetNormalized(...)100%44100%
GetNormalized(...)100%88100%
GetDirection(...)100%11100%
GetNormalized(...)100%66100%
GetNormalized(...)100%66100%
GetNormalized(...)100%66100%
GetNormalized(...)100%88100%
GetNormalized(...)100%88100%
GetNormalized(...)100%44100%
GetNormalized(...)100%22100%
GetPositiveMagnitude(...)100%22100%
GetPositiveMagnitude(...)100%11100%
GetMagnitudeBitLength(...)100%11100%
Max(...)100%22100%

File(s)

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Numerics/Wide/WideNormalization.cs

#LineLine coverage
 1//=======================================================================
 2// WideNormalization.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2024-present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using FixedMathSharp.Geometry;
 9
 10using System;
 11
 12namespace FixedMathSharp;
 13
 14/// <summary>
 15/// Provides high-precision normalization and direction calculations
 16/// for geometric vectors using wide (extended-precision) arithmetic.
 17/// </summary>
 18internal static class WideNormalization
 19{
 20    /// <summary>
 21    /// Returns the normalized direction between two 2D endpoints without
 22    /// narrowing their component differences.
 23    /// </summary>
 24    internal static Vector2d GetDirection(Vector2d start, Vector2d end)
 25    {
 25726        Signed192 x = WideArithmetic.Difference(end.X, start.X);
 25727        Signed192 y = WideArithmetic.Difference(end.Y, start.Y);
 25728        return GetNormalized(x, y);
 29    }
 30
 31    /// <summary>
 32    /// Returns the nearest representable normalized direction for a nonzero 2D
 33    /// vector using its exact raw components.
 34    /// </summary>
 35    internal static Vector2d GetNormalized(Vector2d value)
 36    {
 37        // Cardinal vectors (including zero) need neither a square root nor
 38        // division, even when the nonzero raw component is MinValue.
 57339        if (value.X == Fixed64.Zero || value.Y == Fixed64.Zero)
 32740            return new Vector2d((Fixed64)Fixed64.Sign(value.X), (Fixed64)Fixed64.Sign(value.Y));
 41
 24642        return GetNormalized(
 24643            Signed192.Signed(value.X.m_rawValue),
 24644            Signed192.Signed(value.Y.m_rawValue));
 45    }
 46
 47    internal static Vector2d GetNormalized(Signed192 x, Signed192 y)
 48    {
 604749        Signed320 squaredMagnitude = WideGeometry.GetSquaredMagnitude(
 604750            x,
 604751            y,
 604752            default,
 604753            out Signed320 xSquare,
 604754            out Signed320 ySquare,
 604755            out _);
 604756        if (squaredMagnitude.IsZero)
 126757            return Vector2d.Zero;
 58
 478059        if (Max(GetMagnitudeBitLength(x), GetMagnitudeBitLength(y))
 478060            <= FixedMath.SHIFT_AMOUNT_I + 1)
 61        {
 55162            return Vector2d.GetScaleNormalized(new Vector2d(
 55163                Fixed64.FromRaw(unchecked((long)x.Low)),
 55164                Fixed64.FromRaw(unchecked((long)y.Low))));
 65        }
 66
 422967        Signed192 magnitude = WideArithmetic.GetFloorSquareRoot(
 422968            squaredMagnitude,
 422969            out Signed192 remainder);
 422970        Signed192 ceilingMagnitude = remainder.IsZero
 422971            ? magnitude
 422972            : WideArithmetic.AddSigned192(
 422973                magnitude,
 422974                Signed192.Signed(1L));
 422975        return new Vector2d(
 422976            Fixed64.NormalizeWideComponent(
 422977                x,
 422978                xSquare,
 422979                ceilingMagnitude,
 422980                squaredMagnitude),
 422981            Fixed64.NormalizeWideComponent(
 422982                y,
 422983                ySquare,
 422984                ceilingMagnitude,
 422985                squaredMagnitude));
 86    }
 87
 88    /// <summary>
 89    /// Returns the nearest representable normalized direction for exact 2D
 90    /// components wider than the public scalar domain.
 91    /// </summary>
 92    internal static Vector2d GetNormalized(Signed320 x, Signed320 y)
 93    {
 794        Signed320 largest =
 795            WideArithmetic.CompareMagnitude(x, y) >= 0 ? x : y;
 796        if (largest.IsZero)
 197            return Vector2d.Zero;
 98
 699        Signed320 scale = GetPositiveMagnitude(largest);
 6100        return Vector2d.GetScaleNormalized(new Vector2d(
 6101            Fixed64.GetSignedRatio(x, scale),
 6102            Fixed64.GetSignedRatio(y, scale)));
 103    }
 104
 105    /// <summary>
 106    /// Returns the nearest representable normalized direction for exact 2D
 107    /// components in the nine-word finite-axis domain.
 108    /// </summary>
 109    internal static Vector2d GetNormalized(Signed576 x, Signed576 y)
 110    {
 1389111        Signed576 xMagnitude = GetPositiveMagnitude(x);
 1389112        Signed576 yMagnitude = GetPositiveMagnitude(y);
 1389113        Signed576 scale =
 1389114            WideArithmetic.CompareNonNegative(xMagnitude, yMagnitude) >= 0
 1389115                ? xMagnitude
 1389116                : yMagnitude;
 1389117        if (scale.IsZero)
 735118            return Vector2d.Zero;
 119
 654120        Signed320 fixedScale = Signed320.ExtendValue(
 654121            Signed192.Signed(Fixed64.One.m_rawValue));
 654122        Signed704 denominator = Signed704.ExtendValue(scale);
 654123        _ = Fixed64.TryGetSignedRawRatio(
 654124            WideArithmetic.MultiplySigned576ToSigned704(x, fixedScale),
 654125            denominator,
 654126            out Fixed64 scaledX);
 654127        _ = Fixed64.TryGetSignedRawRatio(
 654128            WideArithmetic.MultiplySigned576ToSigned704(y, fixedScale),
 654129            denominator,
 654130            out Fixed64 scaledY);
 654131        return Vector2d.GetScaleNormalized(new Vector2d(scaledX, scaledY));
 132    }
 133
 134    /// <summary>Normalizes exact scaled-frame 2D components using nearest-even bounded component ratios.</summary>
 135    internal static Vector2d GetNormalized(Signed832 x, Signed832 y)
 136    {
 11137        Signed832 ax = x.Sign < 0 ? WideArithmetic.SubtractSigned832(default, x) : x;
 11138        Signed832 ay = y.Sign < 0 ? WideArithmetic.SubtractSigned832(default, y) : y;
 11139        Signed832 scale = WideArithmetic.SubtractSigned832(ax, ay).Sign >= 0 ? ax : ay;
 12140        if (scale.IsZero) return Vector2d.Zero;
 141        // Keep the Q32 rescale transient so even full-width inputs cannot
 142        // overflow the persistent component carrier.
 20143        Span<ulong> values = stackalloc ulong[6 * 14]; Span<int> signs = stackalloc int[6];
 10144        var numerator = ContactQuadratic.At(values, signs, 0, 14);
 10145        var denominator = ContactQuadratic.At(values, signs, 1, 14);
 10146        var scaled = ContactQuadratic.At(values, signs, 2, 14);
 20147        denominator.Set(scale); numerator.Set(x);
 10148        ContactQuadratic.Scale(numerator, Signed576.ExtendValue(Signed192.Raw(Fixed64.One)), scaled);
 10149        _ = Fixed64.TryGetSignedRawRatio(scaled.Rational, denominator.Rational, scaled.Signs[0] < 0, out Fixed64 sx);
 10150        numerator.Set(y);
 10151        ContactQuadratic.Scale(numerator, Signed576.ExtendValue(Signed192.Raw(Fixed64.One)), scaled);
 10152        _ = Fixed64.TryGetSignedRawRatio(scaled.Rational, denominator.Rational, scaled.Signs[0] < 0, out Fixed64 sy);
 10153        return Vector2d.GetScaleNormalized(new Vector2d(sx, sy));
 154    }
 155
 156    /// <summary>
 157    /// Returns the normalized direction between two 3D endpoints without
 158    /// narrowing their component differences.
 159    /// </summary>
 160    internal static Vector3d GetDirection(Vector3d start, Vector3d end)
 161    {
 4162        Signed192 x = WideArithmetic.Difference(end.X, start.X);
 4163        Signed192 y = WideArithmetic.Difference(end.Y, start.Y);
 4164        Signed192 z = WideArithmetic.Difference(end.Z, start.Z);
 4165        return GetNormalized(x, y, z);
 166    }
 167
 168    /// <summary>
 169    /// Returns the nearest representable normalized direction for a nonzero 3D
 170    /// vector using its exact raw components.
 171    /// </summary>
 172    internal static Vector3d GetNormalized(Vector3d value)
 173    {
 11207174        bool zeroX = value.X == Fixed64.Zero;
 11207175        bool zeroY = value.Y == Fixed64.Zero;
 11207176        bool zeroZ = value.Z == Fixed64.Zero;
 11207177        if ((zeroX && zeroY) || (zeroX && zeroZ) || (zeroY && zeroZ))
 8180178            return new Vector3d((Fixed64)Fixed64.Sign(value.X),
 8180179                (Fixed64)Fixed64.Sign(value.Y), (Fixed64)Fixed64.Sign(value.Z));
 180
 3027181        return GetNormalized(
 3027182            Signed192.Signed(value.X.m_rawValue),
 3027183            Signed192.Signed(value.Y.m_rawValue),
 3027184            Signed192.Signed(value.Z.m_rawValue));
 185    }
 186
 187    internal static Vector3d GetNormalized(
 188        Signed192 x,
 189        Signed192 y,
 190        Signed192 z)
 191    {
 3048192        Signed320 squaredMagnitude = WideGeometry.GetSquaredMagnitude(
 3048193            x,
 3048194            y,
 3048195            z,
 3048196            out Signed320 xSquare,
 3048197            out Signed320 ySquare,
 3048198            out Signed320 zSquare);
 3048199        if (squaredMagnitude.IsZero)
 1200            return Vector3d.Zero;
 201
 3047202        if (Max(
 3047203                Max(
 3047204                    GetMagnitudeBitLength(x),
 3047205                    GetMagnitudeBitLength(y)),
 3047206                GetMagnitudeBitLength(z))
 3047207            <= FixedMath.SHIFT_AMOUNT_I + 1)
 208        {
 3026209            return Vector3d.GetScaleNormalized(new Vector3d(
 3026210                Fixed64.FromRaw(unchecked((long)x.Low)),
 3026211                Fixed64.FromRaw(unchecked((long)y.Low)),
 3026212                Fixed64.FromRaw(unchecked((long)z.Low))));
 213        }
 214
 21215        Signed192 magnitude = WideArithmetic.GetFloorSquareRoot(
 21216            squaredMagnitude,
 21217            out Signed192 remainder);
 21218        Signed192 ceilingMagnitude = remainder.IsZero
 21219            ? magnitude
 21220            : WideArithmetic.AddSigned192(
 21221                magnitude,
 21222                Signed192.Signed(1L));
 21223        return new Vector3d(
 21224            Fixed64.NormalizeWideComponent(
 21225                x,
 21226                xSquare,
 21227                ceilingMagnitude,
 21228                squaredMagnitude),
 21229            Fixed64.NormalizeWideComponent(
 21230                y,
 21231                ySquare,
 21232                ceilingMagnitude,
 21233                squaredMagnitude),
 21234            Fixed64.NormalizeWideComponent(
 21235                z,
 21236                zSquare,
 21237                ceilingMagnitude,
 21238                squaredMagnitude));
 239    }
 240
 241    /// <summary>
 242    /// Returns the nearest representable normalized direction for exact 3D
 243    /// components wider than the public scalar domain.
 244    /// </summary>
 245    internal static Vector3d GetNormalized(
 246        Signed320 x,
 247        Signed320 y,
 248        Signed320 z)
 249    {
 2006250        Signed320 largest =
 2006251            WideArithmetic.CompareMagnitude(x, y) >= 0 ? x : y;
 2006252        if (WideArithmetic.CompareMagnitude(z, largest) > 0)
 455253            largest = z;
 2006254        if (largest.IsZero)
 23255            return Vector3d.Zero;
 256
 1983257        Signed320 scale = GetPositiveMagnitude(largest);
 1983258        return Vector3d.GetScaleNormalized(new Vector3d(
 1983259            Fixed64.GetSignedRatio(x, scale),
 1983260            Fixed64.GetSignedRatio(y, scale),
 1983261            Fixed64.GetSignedRatio(z, scale)));
 262    }
 263
 264    /// <summary>
 265    /// Returns the nearest representable normalized direction for exact 3D
 266    /// components in the nine-word finite-axis domain.
 267    /// </summary>
 268    internal static Vector3d GetNormalized(
 269        Signed576 x,
 270        Signed576 y,
 271        Signed576 z)
 272    {
 1783273        Signed576 xMagnitude = GetPositiveMagnitude(x);
 1783274        Signed576 yMagnitude = GetPositiveMagnitude(y);
 1783275        Signed576 zMagnitude = GetPositiveMagnitude(z);
 1783276        Signed576 scale =
 1783277            WideArithmetic.CompareNonNegative(xMagnitude, yMagnitude) >= 0
 1783278                ? xMagnitude
 1783279                : yMagnitude;
 1783280        if (WideArithmetic.CompareNonNegative(zMagnitude, scale) > 0)
 457281            scale = zMagnitude;
 1783282        if (scale.IsZero)
 50283            return Vector3d.Zero;
 284
 285        // A positive scale fitting Signed320 bounds every component away from
 286        // its signed minimum; the smaller owner uses the same nearest-even ratios.
 1733287        if (Signed320.TryNarrowSigned(scale, out _))
 1701288            return GetNormalized(Signed320.NarrowValue(x), Signed320.NarrowValue(y), Signed320.NarrowValue(z));
 289
 32290        Signed320 fixedScale = Signed320.ExtendValue(
 32291            Signed192.Signed(Fixed64.One.m_rawValue));
 32292        Signed704 denominator = Signed704.ExtendValue(scale);
 32293        _ = Fixed64.TryGetSignedRawRatio(
 32294            WideArithmetic.MultiplySigned576ToSigned704(x, fixedScale),
 32295            denominator,
 32296            out Fixed64 scaledX);
 32297        _ = Fixed64.TryGetSignedRawRatio(
 32298            WideArithmetic.MultiplySigned576ToSigned704(y, fixedScale),
 32299            denominator,
 32300            out Fixed64 scaledY);
 32301        _ = Fixed64.TryGetSignedRawRatio(
 32302            WideArithmetic.MultiplySigned576ToSigned704(z, fixedScale),
 32303            denominator,
 32304            out Fixed64 scaledZ);
 32305        return Vector3d.GetScaleNormalized(
 32306            new Vector3d(scaledX, scaledY, scaledZ));
 307    }
 308
 309    /// <summary>Normalizes exact 13-word components through one common scale before scalar normalization.</summary>
 310    internal static Vector3d GetNormalized(Signed832 x, Signed832 y, Signed832 z)
 311    {
 41312        Span<ulong> values = stackalloc ulong[4 * 13];
 164313        Span<ulong> mx = values[..13], my = values.Slice(13, 13), mz = values.Slice(26, 13), maximum = values.Slice(39, 
 123314        WideArithmetic.GetMagnitude(x, mx); WideArithmetic.GetMagnitude(y, my); WideArithmetic.GetMagnitude(z, mz);
 41315        (WideArithmetic.CompareMagnitudeEqualLength(mx, my) >= 0 ? mx : my).CopyTo(maximum);
 44316        if (WideArithmetic.CompareMagnitudeEqualLength(mz, maximum) > 0) mz.CopyTo(maximum);
 50317        if (WideArithmetic.GetActiveMagnitudeLength(maximum) == 0) return Vector3d.Zero;
 32318        Span<ulong> numerator = stackalloc ulong[14];
 32319        Vector3d result = default;
 32320        Span<int> signs = stackalloc int[3] { x.Sign, y.Sign, z.Sign };
 256321        for (int axis = 0; axis < 3; axis++)
 322        {
 192323            numerator.Clear(); int sign = 0;
 96324            WideArithmetic.AddShiftedSignedMagnitude(values.Slice(axis * 13, 13), 1, 32, numerator, ref sign);
 96325            _ = Fixed64.TryGetSignedRawRatio(numerator, maximum, signs[axis] < 0, out Fixed64 component);
 96326            result[axis] = component;
 327        }
 32328        return Vector3d.GetScaleNormalized(result);
 329    }
 330    /// <summary>
 331    /// Returns the nearest representable normalized direction for a nonzero 4D
 332    /// vector using its exact raw components.
 333    /// </summary>
 334    internal static Vector4d GetNormalized(Vector4d value)
 335    {
 15336        Signed192 x = Signed192.Signed(value.X.m_rawValue);
 15337        Signed192 y = Signed192.Signed(value.Y.m_rawValue);
 15338        Signed192 z = Signed192.Signed(value.Z.m_rawValue);
 15339        Signed192 w = Signed192.Signed(value.W.m_rawValue);
 15340        Signed320 squaredMagnitude = WideGeometry.GetSquaredMagnitude(
 15341            x,
 15342            y,
 15343            z,
 15344            out Signed320 xSquare,
 15345            out Signed320 ySquare,
 15346            out Signed320 zSquare);
 15347        Signed320 wSquare = WideArithmetic.MultiplySigned192(w, w);
 15348        squaredMagnitude =
 15349            WideArithmetic.AddSigned320(squaredMagnitude, wSquare);
 15350        if (squaredMagnitude.IsZero)
 2351            return Vector4d.Zero;
 352
 13353        Signed192 magnitude = WideArithmetic.GetFloorSquareRoot(
 13354            squaredMagnitude,
 13355            out Signed192 remainder);
 13356        Signed192 ceilingMagnitude = remainder.IsZero
 13357            ? magnitude
 13358            : WideArithmetic.AddSigned192(
 13359                magnitude,
 13360                Signed192.Signed(1L));
 13361        return new Vector4d(
 13362            Fixed64.NormalizeWideComponent(
 13363                x,
 13364                xSquare,
 13365                ceilingMagnitude,
 13366                squaredMagnitude),
 13367            Fixed64.NormalizeWideComponent(
 13368                y,
 13369                ySquare,
 13370                ceilingMagnitude,
 13371                squaredMagnitude),
 13372            Fixed64.NormalizeWideComponent(
 13373                z,
 13374                zSquare,
 13375                ceilingMagnitude,
 13376                squaredMagnitude),
 13377            Fixed64.NormalizeWideComponent(
 13378                w,
 13379                wSquare,
 13380                ceilingMagnitude,
 13381                squaredMagnitude));
 382    }
 383
 384    /// <summary>
 385    /// Returns the nearest representable unit quaternion for nonzero raw
 386    /// components. The zero quaternion retains its public identity fallback.
 387    /// </summary>
 388    internal static FixedQuaternion GetNormalized(FixedQuaternion value)
 389    {
 9390        Vector4d normalized = GetNormalized(
 9391            new Vector4d(value.X, value.Y, value.Z, value.W));
 9392        return normalized.IsZero
 9393            ? FixedQuaternion.Identity
 9394            : new FixedQuaternion(
 9395                normalized.X,
 9396                normalized.Y,
 9397                normalized.Z,
 9398                normalized.W);
 399    }
 400
 401    private static Signed576 GetPositiveMagnitude(Signed576 value) =>
 8127402        value.Sign < 0
 8127403            ? WideArithmetic.SubtractSigned576(default, value)
 8127404            : value;
 405
 406    private static Signed320 GetPositiveMagnitude(Signed320 value)
 407    {
 1989408        WideArithmetic.GetMagnitude(
 1989409            value,
 1989410            out ulong word4,
 1989411            out ulong word3,
 1989412            out ulong word2,
 1989413            out ulong word1,
 1989414            out ulong word0);
 1989415        return new Signed320(word4, word3, word2, word1, word0);
 416    }
 417
 418    private static int GetMagnitudeBitLength(Signed192 value)
 419    {
 18701420        WideArithmetic.GetMagnitude(
 18701421            value,
 18701422            out ulong high,
 18701423            out ulong middle,
 18701424            out ulong low);
 18701425        return WideArithmetic.GetBitLength(high, middle, low);
 426    }
 427
 428    private static int Max(int left, int right) =>
 10874429        left >= right ? left : right;
 430}