< Summary

Line coverage
100%
Covered lines: 293
Uncovered lines: 0
Coverable lines: 293
Total lines: 521
Line coverage: 100%
Branch coverage
100%
Covered branches: 138
Total branches: 138
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/ContactQuadratic.cs

#LineLine coverage
 1//=======================================================================
 2// ContactQuadratic.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 System;
 9using static FixedMathSharp.Geometry.CylinderContactAlgebra;
 10
 11namespace FixedMathSharp.Geometry;
 12
 13/// <summary>Borrowed exact quadratic-field values and final nearest-even ratios.</summary>
 14/// <content>Owns quadratic-field arithmetic; ratio comparison and materialization share this borrowed layout.</content>
 15internal readonly ref partial struct ContactQuadratic
 16{
 17    internal readonly Span<ulong> Values;
 18    internal readonly Span<int> Signs;
 2159890419    internal int FieldWords => Values.Length / 2;
 1190256220    internal Span<ulong> Rational => Values[..FieldWords];
 883110921    internal Span<ulong> Radical => Values[FieldWords..];
 1054098022    internal ContactQuadratic(Span<ulong> values, Span<int> signs) { Values = values; Signs = signs; }
 126591023    internal void Clear() { Values.Clear(); Signs.Clear(); }
 24    internal void Set(Signed576 value)
 25    {
 133492826        Import(value, Rational); Radical.Clear(); Signs[0] = value.Sign; Signs[1] = 0;
 33373227    }
 28    internal void Set(in Signed832 value)
 29    {
 23929330        if (FieldWords >= 13)
 31        {
 32            // Plane-ray banks already hold the full scalar width. Import it
 33            // directly rather than prepare and rescan a second magnitude bank.
 23384534            WideArithmetic.GetMagnitude(value, Rational);
 70153535            Radical.Clear(); Signs[0] = value.Sign; Signs[1] = 0;
 23384536            return;
 37        }
 544838        Span<ulong> magnitude = stackalloc ulong[13];
 544839        WideArithmetic.GetMagnitude(value, magnitude);
 544840        int active = WideArithmetic.GetActiveMagnitudeLength(magnitude);
 544841        magnitude[..active].CopyTo(Rational);
 1089642        Rational[active..].Clear(); Radical.Clear();
 1089643        Signs[0] = value.Sign; Signs[1] = 0;
 544844    }
 45    internal void CopyTo(ContactQuadratic destination, int multiplier = 1)
 46    {
 47        // Signs are authoritative: a zero field can retain stale scratch bytes.
 48        // Both complete active fields must fit the destination's borrowed banks.
 83848549        int rationalWords = Signs[0] == 0 ? 0 : WideArithmetic.GetActiveMagnitudeLength(Rational);
 83848550        int radicalWords = Signs[1] == 0 ? 0 : WideArithmetic.GetActiveMagnitudeLength(Radical);
 51        // A nonzero retained sign on an empty magnitude is still exact zero.
 52        // Reuse the active lengths above to canonicalize it without a rescan.
 83848553        int rationalSign = rationalWords == 0 ? 0 : Signs[0] * multiplier;
 83848554        int radicalSign = radicalWords == 0 ? 0 : Signs[1] * multiplier;
 167697055        Rational[..rationalWords].CopyTo(destination.Rational); Radical[..radicalWords].CopyTo(destination.Radical);
 167697056        destination.Rational[rationalWords..].Clear(); destination.Radical[radicalWords..].Clear();
 167697057        destination.Signs[0] = rationalSign; destination.Signs[1] = radicalSign;
 83848558    }
 44134559    internal void MultiplySign(int sign) { Signs[0] *= sign; Signs[1] *= sign; }
 60    internal void Add(ContactQuadratic value, int multiplier = 1)
 61    {
 256459862        int rationalSign = Signs[0], radicalSign = Signs[1];
 128229963        CylinderContactAlgebra.Add(value.Rational, value.Signs[0] * multiplier, Rational, ref rationalSign);
 128229964        CylinderContactAlgebra.Add(value.Radical, value.Signs[1] * multiplier, Radical, ref radicalSign);
 256459865        Signs[0] = rationalSign; Signs[1] = radicalSign;
 128229966    }
 67    internal int Sign(ReadOnlySpan<ulong> root) =>
 46579768        WideConvexPrismRelations.GetConvexContactCandidateQuadraticSign(Rational, Signs[0], Radical, Signs[1], root);
 69
 70    internal static ContactQuadratic At(Span<ulong> values, Span<int> signs, int index, int words = Words) =>
 350458471        new(values.Slice(index * 2 * words, 2 * words), signs.Slice(index * 2, 2));
 72
 73    /// <summary>Retains one branch of a*u²+2*b*u+c=0 without rounding.</summary>
 74    /// <remarks>
 75    /// Inputs are rational integer coefficients; the caller proves the complete discriminant fits the supplied root sto
 76    /// Outputs borrow caller storage. A zero polynomial denotes a family and
 77    /// returns false; callers must retain its interval/endpoints separately.
 78    /// Branches are -1 and +1; the linear and repeated cases use +1 only.
 79    /// </remarks>
 80    internal static bool TryGetParameter(ContactQuadratic a, ContactQuadratic b, ContactQuadratic c,
 81        int branch, Span<ulong> root, ContactQuadratic numerator, ContactQuadratic denominator)
 82    {
 14590883        root.Clear(); numerator.Clear(); denominator.Clear();
 4863684        if (a.Signs[0] == 0)
 85        {
 345686            if (b.Signs[0] == 0 || branch < 0)
 246287                return false;
 99488            c.CopyTo(numerator, -b.Signs[0]);
 198889            b.CopyTo(denominator, b.Signs[0]); denominator.Add(denominator);
 99490            return true;
 91        }
 92        // A coefficient bank need not fit its square. Preserve both complete
 93        // active products and subtraction carry before copying the discriminant.
 4518094        int words = Math.Max(2 * ActiveWords(b), ActiveWords(a) + ActiveWords(c)) + 1;
 4518095        Span<ulong> work = stackalloc ulong[4 * words];
 4518096        Span<int> signs = stackalloc int[4];
 4518097        ContactQuadratic discriminant = ContactQuadratic.At(work, signs, 0, words);
 4518098        ContactQuadratic product = ContactQuadratic.At(work, signs, 1, words);
 4518099        ContactQuadratic.Multiply(b, b, root, discriminant);
 45180100        ContactQuadratic.Multiply(a, c, root, product);
 45180101        discriminant.Add(product, -1);
 45180102        if (discriminant.Signs[0] < 0 || discriminant.Signs[0] == 0 && branch < 0)
 24036103            return false;
 21144104        int active = WideArithmetic.GetActiveMagnitudeLength(discriminant.Rational);
 105        System.Diagnostics.Debug.Assert(active <= root.Length);
 21144106        discriminant.Rational[..active].CopyTo(root);
 21144107        int orientation = a.Signs[0];
 21144108        b.CopyTo(numerator, -orientation);
 21144109        numerator.Radical[0] = 1;
 21144110        numerator.Signs[1] = branch;
 21144111        a.CopyTo(denominator, orientation);
 21144112        return true;
 113    }
 114
 115    internal static Fixed64 RoundRatio(ContactQuadratic numerator, ContactQuadratic denominator, ReadOnlySpan<ulong> roo
 116        long low = long.MinValue, long high = long.MaxValue, long parityOffset = 0)
 117    {
 22179118        if (numerator.Signs[1] == 0 && denominator.Signs[1] == 0 && (parityOffset & 1) == 0)
 119        {
 12191120            bool represented = Fixed64.TryGetSignedRawRatio(numerator.Rational, denominator.Rational,
 12191121                numerator.Signs[0] < 0, out Fixed64 result);
 122            System.Diagnostics.Debug.Assert(represented);
 12191123            return result;
 124        }
 9988125        RefineRoundRatioBounds(numerator, denominator, root, ref low, ref high);
 9988126        int words = Math.Max(ActiveWords(numerator), ActiveWords(denominator)) + 2;
 9988127        Span<ulong> values = stackalloc ulong[4 * words];
 9988128        Span<int> signs = stackalloc int[4];
 19976129        ContactQuadratic doubled = At(values, signs, 0, words), query = At(values, signs, 1, words);
 130        // Retained fields are padded to their worst-case proof width. Scaling
 131        // uses only complete active products; two extra limbs cover doubling
 132        // and every signed 65-bit threshold without copying padded tails.
 9988133        Scale(numerator, Signed576.ExtendValue(Signed320.ExtendValue(Signed192.Signed(2))), doubled);
 22897134        while (low < high)
 135        {
 12909136            ulong span = unchecked((ulong)high - (ulong)low);
 12909137            long midpoint = unchecked((long)((ulong)low + (span >> 1) + (span & 1)));
 12909138            Signed192 threshold = WideArithmetic.AddSigned192(Signed192.Signed(midpoint), Signed192.Signed(midpoint));
 12909139            Scale(denominator, Signed576.ExtendValue(Signed320.ExtendValue(threshold)), query);
 25818140            query.MultiplySign(-1); query.Add(doubled);
 20710141            if (query.Sign(root) >= 0) low = midpoint;
 5108142            else high = midpoint - 1;
 143        }
 9988144        Signed192 half = WideArithmetic.AddSigned192(WideArithmetic.AddSigned192(Signed192.Signed(low), Signed192.Signed
 9988145        Scale(denominator, Signed576.ExtendValue(Signed320.ExtendValue(half)), query);
 19976146        query.MultiplySign(-1); query.Add(doubled);
 9988147        int comparison = query.Sign(root);
 9988148        return Fixed64.FromRaw(low + (comparison > 0 || comparison == 0 && ((low ^ parityOffset) & 1) != 0 ? 1 : 0));
 149    }
 150
 151    internal static void RefineRoundRatioBounds(ContactQuadratic numerator, ContactQuadratic denominator,
 152        ReadOnlySpan<ulong> root, ref long low, ref long high, bool squaredDepth = false)
 153    {
 11309154        WideArithmetic.GetMagnitudeSquareRootBounds(root,
 11309155            out Signed192 lowerRoot, out Signed192 upperRoot, out int shift);
 11309156        Span<ulong> roots = stackalloc ulong[4]
 11309157            { lowerRoot.Low, lowerRoot.Middle, upperRoot.Low, upperRoot.Middle };
 11309158        int words = Math.Max(2, Math.Max(Math.Max(ActiveWords(numerator), ActiveWords(denominator)),
 11309159            Math.Max(WideArithmetic.GetActiveMagnitudeLength(numerator.Radical),
 11309160                WideArithmetic.GetActiveMagnitudeLength(denominator.Radical)) + 2 + ((shift + 63) >> 6))) + 1;
 11309161        Span<ulong> bounds = stackalloc ulong[4 * words];
 22618162        Span<ulong> nMin = bounds[..words], nMax = bounds.Slice(words, words);
 22618163        Span<ulong> dMin = bounds.Slice(2 * words, words), dMax = bounds[(3 * words)..];
 11309164        WideConvexPrismRelations.GetConvexContactCandidateQuadraticBounds(numerator.Rational, numerator.Signs[0],
 11309165            numerator.Radical, numerator.Signs[1], roots, shift, nMin, nMax, out int nMinSign, out int nMaxSign);
 11309166        WideConvexPrismRelations.GetConvexContactCandidateQuadraticBounds(denominator.Rational, denominator.Signs[0],
 11309167            denominator.Radical, denominator.Signs[1], roots, shift, dMin, dMax, out int dMinSign, out _);
 11309168        if (squaredDepth)
 169        {
 1085170            if (dMinSign <= 0) return;
 171            // N/D is four times depth squared. Positive interval endpoints
 172            // bound its floor; exact range and half-raw signs still own rounding.
 1081173            long floorLower = nMinSign <= 0 ? 0 : (long)(WideArithmetic.GetRatioFloorSquareRoot(nMin, dMax, ulong.MaxVal
 1081174            long upper = (long)(WideArithmetic.GetRatioFloorSquareRoot(nMax, dMin, ulong.MaxValue) >> 1);
 1081175            low = Math.Min(high, Math.Max(low, floorLower));
 1081176            high = Math.Max(low, Math.Min(high, upper));
 1081177            return;
 178        }
 179        // Division by a strictly positive interval is monotone in N; the
 180        // denominator endpoint reverses when N is negative. Cancellation can
 181        // make the enclosure unusable, so exact full-range search remains valid.
 10226182        if (dMinSign <= 0
 10226183            || !Fixed64.TryGetSignedRawRatio(nMin, nMinSign < 0 ? dMin : dMax, nMinSign < 0, out Fixed64 roundedMin)
 10316184            || !Fixed64.TryGetSignedRawRatio(nMax, nMaxSign < 0 ? dMax : dMin, nMaxSign < 0, out Fixed64 roundedMax)) re
 185        // floor(x) lies in [roundEven(lower)-1, roundEven(upper)]. Preserve
 186        // the caller's clipped floor domain, including values outside it; the
 187        // original exact integer and half-raw signs still decide every output.
 10136188        long lower = roundedMin.m_rawValue == long.MinValue ? long.MinValue : roundedMin.m_rawValue - 1;
 10136189        low = Math.Min(high, Math.Max(low, lower));
 10136190        high = Math.Max(low, Math.Min(high, roundedMax.m_rawValue));
 10136191    }
 192
 193    internal static void Scale(ContactQuadratic value, Signed576 scalar, ContactQuadratic result)
 194    {
 795244195        Span<ulong> magnitude = stackalloc ulong[9];
 795244196        Import(scalar, magnitude);
 795244197        Scale(value, magnitude, scalar.Sign, result);
 795244198    }
 199    internal static void Scale(ContactQuadratic value, Signed832 scalar, ContactQuadratic result)
 200    {
 559270201        Span<ulong> magnitude = stackalloc ulong[13];
 559270202        WideArithmetic.GetMagnitude(scalar, magnitude);
 559270203        Scale(value, magnitude, scalar.Sign, result);
 559270204    }
 205
 206    // The caller proves that result has room for both complete products and
 207    // does not alias the source coefficient or scalar storage.
 208    internal static void Scale(ContactQuadratic value, ReadOnlySpan<ulong> magnitude,
 209        int scalarSign, ContactQuadratic result)
 210    {
 211        // Primitive plane covectors and reduced homogeneous scales often
 212        // supply integer +/-1. Copy the same exact fields without multiplying
 213        // padded banks; CopyTo also clears stale bytes under zero field signs.
 1369765214        if (scalarSign != 0 && WideArithmetic.GetActiveMagnitudeLength(magnitude) == 1 && magnitude[0] == 1UL)
 215        {
 156685216            value.CopyTo(result, scalarSign);
 156685217            return;
 218        }
 2426160219        int rationalSign = value.Signs[0] * scalarSign, radicalSign = value.Signs[1] * scalarSign;
 220        // Retained sign zero is authoritative even when borrowed bank bytes
 221        // are stale. Clear output rather than scan/multiply a coefficient
 222        // that has no mathematical contribution.
 1213080223        if (rationalSign == 0)
 224        {
 911862225            result.Rational.Clear(); result.Signs[0] = 0;
 226        }
 227        else
 228        {
 757149229            WideArithmetic.MultiplyMagnitudes(value.Rational, magnitude, result.Rational);
 757149230            result.Signs[0] = IsZero(result.Rational) ? 0 : rationalSign;
 231        }
 1213080232        if (radicalSign == 0)
 233        {
 2239406234            result.Radical.Clear(); result.Signs[1] = 0;
 235        }
 236        else
 237        {
 93377238            WideArithmetic.MultiplyMagnitudes(value.Radical, magnitude, result.Radical);
 93377239            result.Signs[1] = IsZero(result.Radical) ? 0 : radicalSign;
 240        }
 93377241    }
 242
 243    internal static void Multiply(ContactQuadratic first, ContactQuadratic second, ReadOnlySpan<ulong> root, ContactQuad
 244    {
 521348245        int words = result.FieldWords;
 521348246        if (first.Signs[1] == 0 && second.Signs[1] == 0 || IsZero(root))
 247        {
 248            // A zero radicand also removes both radical contributions. Rational
 249            // events need one complete product. Keep separate scratch
 250            // because result may alias either borrowed input; clear the unused
 251            // radical bank so later reuse cannot retain an old field.
 480139252            Span<ulong> rationalProduct = stackalloc ulong[words];
 480139253            int sign = first.Signs[0] * second.Signs[0];
 480139254            WideArithmetic.MultiplyMagnitudes(first.Rational, second.Rational, rationalProduct);
 960278255            rationalProduct.CopyTo(result.Rational); result.Radical.Clear();
 960278256            result.Signs[0] = IsZero(rationalProduct) ? 0 : sign; result.Signs[1] = 0;
 480139257            return;
 258        }
 41209259        Span<ulong> scratch = stackalloc ulong[3 * words];
 123627260        Span<ulong> rational = scratch[..words], radical = scratch.Slice(words, words), product = scratch[(2 * words)..]
 41209261        WideArithmetic.MultiplyMagnitudes(first.Rational, second.Rational, rational);
 41209262        int rationalSign = IsZero(rational) ? 0 : first.Signs[0] * second.Signs[0];
 41209263        WideArithmetic.MultiplyMagnitudes(first.Radical, second.Radical, product);
 41209264        WideArithmetic.MultiplyMagnitudes(product, root, radical);
 41209265        CylinderContactAlgebra.Add(radical, first.Signs[1] * second.Signs[1], rational, ref rationalSign);
 41209266        WideArithmetic.MultiplyMagnitudes(first.Rational, second.Radical, radical);
 41209267        int radicalSign = IsZero(radical) ? 0 : first.Signs[0] * second.Signs[1];
 41209268        WideArithmetic.MultiplyMagnitudes(first.Radical, second.Rational, product);
 41209269        CylinderContactAlgebra.Add(product, first.Signs[1] * second.Signs[0], radical, ref radicalSign);
 82418270        rational.CopyTo(result.Rational); radical.CopyTo(result.Radical);
 82418271        result.Signs[0] = rationalSign; result.Signs[1] = radicalSign;
 41209272    }
 273
 274    internal static void MultiplyRoot(ContactQuadratic value, ReadOnlySpan<ulong> root, ContactQuadratic result)
 275    {
 7364276        WideArithmetic.MultiplyMagnitudes(value.Radical, root, result.Rational);
 7364277        value.Rational.CopyTo(result.Radical);
 7364278        result.Radical[value.FieldWords..].Clear();
 7364279        result.Signs[0] = IsZero(result.Rational) ? 0 : value.Signs[1];
 7364280        result.Signs[1] = value.Signs[0];
 7364281    }
 282
 283}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/ContactQuadratic.Ratios.cs

#LineLine coverage
 1//=======================================================================
 2// ContactQuadratic.Ratios.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 System;
 9using static FixedMathSharp.Geometry.CylinderContactAlgebra;
 10
 11namespace FixedMathSharp.Geometry;
 12
 13/// <content>Exact comparison of fractions in two possibly different quadratic fields.</content>
 14internal readonly ref partial struct ContactQuadratic
 15{
 16    /// <summary>Rounds one complete coordinate into two additive scalar terms without premature saturation.</summary>
 17    internal static void SplitRound(ContactQuadratic numerator, ContactQuadratic denominator,
 18        ReadOnlySpan<ulong> root, out Fixed64 first, out Fixed64 second)
 19    {
 457520        int words = Math.Max(numerator.FieldWords, denominator.FieldWords);
 457521        Span<ulong> work = stackalloc ulong[4 * words];
 457522        Span<int> signs = stackalloc int[4];
 915023        ContactQuadratic bound = At(work, signs, 0, words), difference = At(work, signs, 1, words);
 457524        first = Fixed64.Zero;
 457525        Scale(denominator, Signed576.ExtendValue(Signed192.Signed(long.MaxValue)), bound);
 915026        numerator.CopyTo(difference); difference.Add(bound, -1);
 457727        if (difference.Sign(root) > 0) first = Fixed64.MaxValue;
 28        else
 29        {
 457330            Scale(denominator, Signed576.ExtendValue(Signed192.Signed(long.MinValue)), bound);
 914631            numerator.CopyTo(difference); difference.Add(bound, -1);
 457632            if (difference.Sign(root) < 0) first = Fixed64.MinValue;
 33        }
 457534        Scale(denominator, Signed576.ExtendValue(Signed192.Signed(first.m_rawValue)), bound);
 915035        numerator.CopyTo(difference); difference.Add(bound, -1);
 457536        Fixed64 rounded = RoundRatio(difference, denominator, root, parityOffset: first.m_rawValue);
 457537        second = first == Fixed64.Zero ? Fixed64.Zero : rounded;
 914538        if (first == Fixed64.Zero) first = rounded;
 457539    }
 40
 41    /// <summary>Compares two exact quadratic fractions with nonzero denominators.</summary>
 42    internal static int CompareRatios(ContactQuadratic firstNumerator, ContactQuadratic firstDenominator,
 43        ReadOnlySpan<ulong> firstRoot, ContactQuadratic secondNumerator, ContactQuadratic secondDenominator,
 44        ReadOnlySpan<ulong> secondRoot)
 45    {
 3797146        if ((firstNumerator.Signs[1] | firstDenominator.Signs[1] | secondNumerator.Signs[1] | secondDenominator.Signs[1]
 47        {
 48            // Rational events need only their two signed cross products. The
 49            // root banks and four-term quadratic field carry no contribution.
 2996250            int firstFractionSign = firstNumerator.Signs[0] * firstDenominator.Signs[0];
 2996251            int secondFractionSign = secondNumerator.Signs[0] * secondDenominator.Signs[0];
 52            System.Diagnostics.Debug.Assert(firstDenominator.Signs[0] != 0 && secondDenominator.Signs[0] != 0);
 2996253            if (firstFractionSign != secondFractionSign)
 1007654                return firstFractionSign.CompareTo(secondFractionSign);
 1988655            if (firstFractionSign == 0)
 530556                return 0;
 1458157            int productWords = Math.Max(
 1458158                WideArithmetic.GetActiveMagnitudeLength(firstNumerator.Rational) + WideArithmetic.GetActiveMagnitudeLeng
 1458159                WideArithmetic.GetActiveMagnitudeLength(secondNumerator.Rational) + WideArithmetic.GetActiveMagnitudeLen
 1458160            Span<ulong> products = stackalloc ulong[2 * productWords];
 2916261            Span<ulong> firstProduct = products[..productWords], secondProduct = products[productWords..];
 1458162            WideArithmetic.MultiplyMagnitudes(firstNumerator.Rational, secondDenominator.Rational, firstProduct);
 1458163            WideArithmetic.MultiplyMagnitudes(secondNumerator.Rational, firstDenominator.Rational, secondProduct);
 1458164            return firstFractionSign * WideArithmetic.CompareMagnitudeEqualLength(firstProduct, secondProduct);
 65        }
 1601866        int firstSign = firstDenominator.Sign(firstRoot), secondSign = secondDenominator.Sign(secondRoot);
 67        System.Diagnostics.Debug.Assert(firstSign != 0 && secondSign != 0);
 800968        int words = 2 * Math.Max(Math.Max(ActiveWords(firstNumerator), ActiveWords(firstDenominator)),
 800969            Math.Max(ActiveWords(secondNumerator), ActiveWords(secondDenominator))) + 1;
 800970        Span<ulong> coefficients = stackalloc ulong[4 * words];
 800971        Span<ulong> product = stackalloc ulong[words];
 800972        Span<int> signs = stackalloc int[4];
 73        // Cross multiplication has four coefficients A+B*sqrt(K)+C*sqrt(L)+D*sqrt(KL).
 74        // Each is a difference of two complete products; one extra limb covers its carry.
 4805475        for (int left = 0; left < 2; left++)
 9610876        for (int right = 0; right < 2; right++)
 77        {
 3203678            int index = left + 2 * right;
 3203679            Span<ulong> coefficient = coefficients.Slice(index * words, words);
 3203680            WideArithmetic.MultiplyMagnitudes(Part(firstNumerator, left), Part(secondDenominator, right), coefficient);
 3203681            int sign = IsZero(coefficient) ? 0 : firstNumerator.Signs[left] * secondDenominator.Signs[right];
 3203682            WideArithmetic.MultiplyMagnitudes(Part(firstDenominator, left), Part(secondNumerator, right), product);
 3203683            CylinderContactAlgebra.Add(product, -firstDenominator.Signs[left] * secondNumerator.Signs[right], coefficien
 3203684            signs[index] = sign;
 85        }
 800986        return firstSign * secondSign * FourTermSign(coefficients, signs, words, firstRoot, secondRoot);
 87    }
 88
 89    private static int FourTermSign(Span<ulong> coefficients, Span<int> signs, int words,
 90        ReadOnlySpan<ulong> firstRoot, ReadOnlySpan<ulong> secondRoot)
 91    {
 800992        ContactQuadratic first = At(coefficients, signs, 0, words);
 800993        ContactQuadratic second = At(coefficients, signs, 1, words);
 800994        int firstSign = first.Sign(firstRoot);
 800995        if (IsZero(secondRoot))
 158996            return firstSign;
 642097        int secondSign = second.Sign(firstRoot);
 642098        if (firstSign == 0)
 81299            return secondSign;
 5608100        if (secondSign == 0 || firstSign == secondSign)
 1993101            return firstSign;
 102
 103        // X and Y*sqrt(L) have opposite signs. The sign of X²-LY² chooses
 104        // the larger magnitude, with exact zero covering dependent-root ties.
 105        // Squaring needs 2W+|K| limbs; scaling adds |L|, then one carry limb.
 3615106        int squareWords = 2 * words + WideArithmetic.GetActiveMagnitudeLength(firstRoot)
 3615107            + WideArithmetic.GetActiveMagnitudeLength(secondRoot) + 1;
 3615108        Span<ulong> values = stackalloc ulong[6 * squareWords];
 3615109        Span<int> squareSigns = stackalloc int[6];
 3615110        ContactQuadratic firstSquare = At(values, squareSigns, 0, squareWords);
 3615111        ContactQuadratic secondSquare = At(values, squareSigns, 1, squareWords);
 3615112        ContactQuadratic scaled = At(values, squareSigns, 2, squareWords);
 3615113        Multiply(first, first, firstRoot, firstSquare);
 3615114        Multiply(second, second, firstRoot, secondSquare);
 3615115        Scale(secondSquare, secondRoot, 1, scaled);
 3615116        firstSquare.Add(scaled, -1);
 3615117        return firstSign * firstSquare.Sign(firstRoot);
 118    }
 119
 293370120    internal static int ActiveWords(ContactQuadratic value) => Math.Max(
 293370121        WideArithmetic.GetActiveMagnitudeLength(value.Rational), WideArithmetic.GetActiveMagnitudeLength(value.Radical))
 122
 123    private static ReadOnlySpan<ulong> Part(ContactQuadratic value, int index) =>
 128144124        index == 0 ? value.Rational : value.Radical;
 125
 126    /// <summary>
 127    /// Rounds the nonnegative raw ratio N*sqrt(metric)/(D*scale) to nearest-even,
 128    /// returning false when that rounded raw value cannot fit Fixed64.
 129    /// </summary>
 130    /// <remarks>D and scale are positive. All comparisons retain the original field.</remarks>
 131    internal static bool TryRoundRootRatio(ContactQuadratic numerator, ContactQuadratic denominator,
 132        ReadOnlySpan<ulong> root, ReadOnlySpan<ulong> metric, ReadOnlySpan<ulong> scale, out Fixed64 result)
 133    {
 3319134        if (numerator.Signs[1] == 0 && denominator.Signs[1] == 0
 3319135            && WideArithmetic.GetActiveMagnitudeLength(metric) == 1 && metric[0] == 1UL)
 136        {
 137            // With a unit metric and rational fields the depth is exactly
 138            // N/(D*scale). Reuse raw nearest-even division, including its
 139            // overflow boundary, without squaring or searching a root. The
 140            // disjoint product reserves both complete active operand lengths.
 872141            Span<ulong> divisor = stackalloc ulong[WideArithmetic.GetActiveMagnitudeLength(denominator.Rational)
 872142                + WideArithmetic.GetActiveMagnitudeLength(scale)];
 872143            WideArithmetic.MultiplyMagnitudes(denominator.Rational, scale, divisor);
 872144            return Fixed64.TryGetSignedRawRatio(numerator.Rational, divisor, negative: false, out result);
 145        }
 146        // Each square needs 2W+|K| limbs. Metric/scale products and a
 147        // 65-bit doubled raw threshold add the bounded suffix below.
 2447148        int words = 2 * Math.Max(ActiveWords(numerator), ActiveWords(denominator))
 2447149            + WideArithmetic.GetActiveMagnitudeLength(root)
 2447150            + Math.Max(WideArithmetic.GetActiveMagnitudeLength(metric),
 2447151                2 * WideArithmetic.GetActiveMagnitudeLength(scale) + 3) + 2;
 2447152        Span<ulong> values = stackalloc ulong[6 * words];
 2447153        Span<int> signs = stackalloc int[6];
 4894154        ContactQuadratic square = At(values, signs, 0, words), left = At(values, signs, 1, words);
 2447155        ContactQuadratic right = At(values, signs, 2, words);
 2447156        Span<ulong> scaleSquare = stackalloc ulong[words];
 2447157        Multiply(numerator, numerator, root, square);
 2447158        Scale(square, metric, 1, left);
 4894159        left.Add(left); left.Add(left); // Compare doubled depth to raw half-unit thresholds.
 2447160        Multiply(denominator, denominator, root, square);
 2447161        WideArithmetic.MultiplyMagnitudes(scale, scale, scaleSquare);
 2447162        Scale(square, scaleSquare, 1, right);
 2447163        return TryRoundSquaredRatio(left, right, root, out result);
 164    }
 165
 166    /// <summary>Rounds sqrt(N/D) in raw units, retaining the exact original field.</summary>
 167    /// <remarks>N is nonnegative and D is positive; false means rounded overflow.</remarks>
 168    internal static bool TryRoundSquareRootRatio(ContactQuadratic numerator, ContactQuadratic denominator,
 169        ReadOnlySpan<ulong> root, out Fixed64 result)
 170    {
 4454171        int words = numerator.FieldWords + 1;
 4454172        Span<ulong> values = stackalloc ulong[2 * words];
 4454173        Span<int> signs = stackalloc int[2];
 4454174        ContactQuadratic left = At(values, signs, 0, words);
 13362175        numerator.CopyTo(left); left.Add(left); left.Add(left);
 4454176        return TryRoundSquaredRatio(left, denominator, root, out result);
 177    }
 178
 179    private static bool TryRoundSquaredRatio(ContactQuadratic left, ContactQuadratic right,
 180        ReadOnlySpan<ulong> root, out Fixed64 result)
 181    {
 182        // Complete threshold multiplication adds at most 130 bits. The third
 183        // extra limb also covers subtraction's carry; padded source tails do
 184        // not increase the transient buffer or the binary-search work.
 6901185        int words = Math.Max(ActiveWords(left), ActiveWords(right)) + 3;
 6901186        Span<ulong> values = stackalloc ulong[2 * words];
 6901187        Span<int> signs = stackalloc int[2];
 6901188        ContactQuadratic query = At(values, signs, 0, words);
 6901189        result = default;
 6901190        Signed192 upperHalf = WideArithmetic.AddSigned192(
 6901191            WideArithmetic.AddSigned192(Signed192.Signed(long.MaxValue), Signed192.Signed(long.MaxValue)), Signed192.Sig
 192        // MaxValue is odd: its exact upper half rounds to the unrepresentable
 193        // next even integer. Values below that half still round to MaxValue.
 6901194        if (CompareDoubledRootRatio(left, right, root, upperHalf, query) >= 0)
 41195            return false;
 6860196        long low = 0;
 6860197        if (left.Signs[1] == 0 && right.Signs[1] == 0)
 198        {
 199            // L/R is four times the raw depth squared. Reuse the exact
 200            // clipped integer root: floor(sqrt(4*d*d))/2 = floor(d).
 201            // The range check above proves the clipped result loses no bits.
 5777202            int width = Math.Max(2, Math.Max(ActiveWords(left), ActiveWords(right)));
 5777203            Span<ulong> rational = stackalloc ulong[2 * width];
 5777204            rational.Clear();
 5777205            left.Rational[..Math.Min(width, left.FieldWords)].CopyTo(rational[..width]);
 5777206            right.Rational[..Math.Min(width, right.FieldWords)].CopyTo(rational[width..]);
 5777207            low = (long)(WideArithmetic.GetRatioFloorSquareRoot(rational[..width], rational[width..], ulong.MaxValue) >>
 208        }
 209        else
 210        {
 1083211            long high = long.MaxValue;
 1083212            RefineRoundRatioBounds(left, right, root, ref low, ref high, squaredDepth: true);
 1574213            while (low < high)
 214            {
 491215                long midpoint = low + ((high - low) >> 1) + ((high - low) & 1);
 491216                Signed192 threshold = WideArithmetic.AddSigned192(Signed192.Signed(midpoint), Signed192.Signed(midpoint)
 491217                if (CompareDoubledRootRatio(left, right, root, threshold, query) >= 0)
 169218                    low = midpoint;
 219                else
 322220                    high = midpoint - 1;
 221            }
 222        }
 6860223        Signed192 half = WideArithmetic.AddSigned192(
 6860224            WideArithmetic.AddSigned192(Signed192.Signed(low), Signed192.Signed(low)), Signed192.Signed(1));
 6860225        int comparison = CompareDoubledRootRatio(left, right, root, half, query);
 6860226        result = Fixed64.FromRaw(low + (comparison > 0 || comparison == 0 && (low & 1) != 0 ? 1 : 0));
 6860227        return true;
 228    }
 229
 230    private static int CompareDoubledRootRatio(ContactQuadratic left, ContactQuadratic right,
 231        ReadOnlySpan<ulong> root, Signed192 threshold, ContactQuadratic query)
 232    {
 14252233        Scale(right, Signed576.ExtendValue(WideArithmetic.MultiplySigned192(threshold, threshold)), query);
 28504234        query.MultiplySign(-1); query.Add(left);
 14252235        return query.Sign(root);
 236    }
 237
 238}

Methods/Properties

get_FieldWords()
get_Rational()
get_Radical()
.ctor(System.Span`1<System.UInt64>,System.Span`1<System.Int32>)
Clear()
Set(FixedMathSharp.Signed576)
Set(FixedMathSharp.Signed832&)
CopyTo(FixedMathSharp.Geometry.ContactQuadratic,System.Int32)
MultiplySign(System.Int32)
Add(FixedMathSharp.Geometry.ContactQuadratic,System.Int32)
Sign(System.ReadOnlySpan`1<System.UInt64>)
At(System.Span`1<System.UInt64>,System.Span`1<System.Int32>,System.Int32,System.Int32)
TryGetParameter(FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,System.Int32,System.Span`1<System.UInt64>,FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic)
RoundRatio(FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,System.ReadOnlySpan`1<System.UInt64>,System.Int64,System.Int64,System.Int64)
RefineRoundRatioBounds(FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,System.ReadOnlySpan`1<System.UInt64>,System.Int64&,System.Int64&,System.Boolean)
Scale(FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Signed576,FixedMathSharp.Geometry.ContactQuadratic)
Scale(FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Signed832,FixedMathSharp.Geometry.ContactQuadratic)
Scale(FixedMathSharp.Geometry.ContactQuadratic,System.ReadOnlySpan`1<System.UInt64>,System.Int32,FixedMathSharp.Geometry.ContactQuadratic)
Multiply(FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,System.ReadOnlySpan`1<System.UInt64>,FixedMathSharp.Geometry.ContactQuadratic)
MultiplyRoot(FixedMathSharp.Geometry.ContactQuadratic,System.ReadOnlySpan`1<System.UInt64>,FixedMathSharp.Geometry.ContactQuadratic)
SplitRound(FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,System.ReadOnlySpan`1<System.UInt64>,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
CompareRatios(FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,System.ReadOnlySpan`1<System.UInt64>,FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,System.ReadOnlySpan`1<System.UInt64>)
FourTermSign(System.Span`1<System.UInt64>,System.Span`1<System.Int32>,System.Int32,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.UInt64>)
ActiveWords(FixedMathSharp.Geometry.ContactQuadratic)
Part(FixedMathSharp.Geometry.ContactQuadratic,System.Int32)
TryRoundRootRatio(FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.UInt64>,FixedMathSharp.Fixed64&)
TryRoundSquareRootRatio(FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,System.ReadOnlySpan`1<System.UInt64>,FixedMathSharp.Fixed64&)
TryRoundSquaredRatio(FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,System.ReadOnlySpan`1<System.UInt64>,FixedMathSharp.Fixed64&)
CompareDoubledRootRatio(FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,System.ReadOnlySpan`1<System.UInt64>,FixedMathSharp.Signed192,FixedMathSharp.Geometry.ContactQuadratic)