< Summary

Information
Class: FixedMathSharp.Geometry.CurvedPrismRimContacts
Assembly: FixedMathSharp
File(s): /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/CurvedPrismRimContacts.cs
Line coverage
100%
Covered lines: 596
Uncovered lines: 0
Coverable lines: 596
Total lines: 929
Line coverage: 100%
Branch coverage
100%
Covered branches: 378
Total branches: 378
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
GetContact(...)100%4040100%
CompareSquaredDepth(...)100%22100%
KeepChart(...)100%146146100%
HasNoNewStationaryMinimum(...)100%1414100%
TryAdmitDomain(...)100%1414100%
NormalDotSign(...)100%11100%
PointInEdgeWindow(...)100%3030100%
IsPhysicalPoint(...)100%88100%
GetLegacyOrder(...)100%3434100%
HasNoNonnegativeEllipseMinimum(...)100%22100%
IsPhysicalArcConcave(...)100%88100%
IsChartDominated(...)100%1414100%
HasPrismSupport(...)100%88100%
BuildParameter(...)100%1212100%
BuildValues(...)100%11100%
GetMaterials(...)100%11100%
BuildPointPolynomials(...)100%66100%
CreateRootAnchor(...)100%1616100%
RoundRatio(...)100%1010100%
CompareRatio(...)100%44100%
AddProduct(...)100%11100%
RestoreValue(...)100%11100%
GetOffset(...)100%22100%
Twice(...)100%11100%
Component(...)100%44100%
Component(...)100%44100%
Sign(...)100%11100%
Write(...)100%11100%
Multiply(...)100%11100%
Add(...)100%11100%
.ctor(...)100%11100%
Slice(...)100%11100%

File(s)

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

#LineLine coverage
 1//=======================================================================
 2// CurvedPrismRimContacts.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 System.Runtime.CompilerServices;
 10
 11namespace FixedMathSharp.Geometry;
 12
 13/// <summary>Exactly ranked stationary real-prism-edge/circular-rim support gaps.</summary>
 14internal static partial class CurvedPrismRimContacts
 15{
 16    private const int Words = 40;
 17    private const int Slots = 31;
 18    private const int ValueWords = 56;
 19    private const int ValueCellWords = (16 * (ValueWords * 64 + 11) + 255) / 64;
 20
 21    /// <summary>
 22    /// Retains stationary minima after complete closed intersection admission
 23    /// and the rational, vertex and generator boundaries. Exact ties retain
 24    /// those earlier features; every admitted support gap is nonnegative.
 25    /// </summary>
 26    internal static void GetContact(in CurvedPrismContactGeometry geometry,
 27        scoped ConvexContactCandidate analytic, Fixed64 analyticDepth, bool analyticClamped,
 28        out Selection best, out Fixed64 depth, out bool clamped, PrismCurvedContactDomain domain = default, bool hasAnal
 29    {
 67530        best = default; depth = analyticDepth; clamped = analyticClamped;
 31        // A radius-zero primitive is a finite segment, owned by the rational fan.
 22932        if (geometry.Radius.IsZero) return;
 22133        Span<ulong> bestValues = stackalloc ulong[5 * ValueWords];
 22134        Span<sbyte> bestSigns = stackalloc sbyte[5];
 22135        Span<ulong> bestCell = stackalloc ulong[ValueCellWords];
 22136        WideDirection3d axis = new(geometry.Axis);
 586437        for (int edgeIndex = 0; edgeIndex < geometry.EdgeCount; edgeIndex++)
 38        {
 271939            geometry.Edge(edgeIndex, out int start, out int end);
 271940            WideDirection3d edge = CurvedPrismContact.Difference(geometry.Vertex(end), geometry.Vertex(start));
 41            // Parallel projection is circular; perpendicular projection is a
 42            // segment. Their complete boundaries and principal directions are
 43            // in the earlier rational/generator fan, not this ellipse quartic.
 271944            if (edge.IsZero || WideDirection3d.Dot(edge, axis).IsZero
 271945                || WideDirection3d.AreParallel(edge, axis))
 46                continue;
 117747            bool supportCertified = geometry.TryGetEdgeNormalCone(edgeIndex, out WideDirection3d coneFirst, out WideDire
 123548            if (!supportCertified) edge = CurvedPrismContact.ReduceEdgeDirection(edge);
 624849            for (int cap = -1; cap <= (geometry.IsCone ? -1 : 1); cap += 2)
 50            {
 195551                if (supportCertified)
 52                {
 185953                    KeepChart(geometry, edgeIndex, edge, coneFirst, CurvedPrismContact.Difference(coneLast, coneFirst), 
 185954                        analytic, ((ulong)analyticDepth.m_rawValue << 1) | 1UL, hasAnalytic && !analyticClamped, true,
 185955                        bestValues, bestSigns, bestCell, ref best, domain, hasAnalytic);
 187756                    if (best.IsZero) { depth = Fixed64.Zero; clamped = false; return; }
 57                    continue;
 58                }
 9659                CircularRimContactAlgebra.GetBasis(edge, out WideDirection3d u, out WideDirection3d v);
 57260                for (int firstSign = -1; firstSign <= 1; firstSign += 2)
 114461                    for (int secondSign = -1; secondSign <= 1; secondSign += 2)
 228462                        for (int chart = 0; chart < 2; chart++)
 63                        {
 76264                            WideDirection3d first = firstSign < 0 ? -u : u;
 76265                            WideDirection3d second = secondSign < 0 ? -v : v;
 114266                            if (chart != 0) (first, second) = (second, first);
 76267                            KeepChart(geometry, edgeIndex, edge, first, second, cap, analytic,
 76268                                ((ulong)analyticDepth.m_rawValue << 1) | 1UL, hasAnalytic && !analyticClamped, false,
 76269                                bestValues, bestSigns, bestCell, ref best, domain, hasAnalytic);
 76270                            if (best.IsZero)
 71                            {
 672                                depth = Fixed64.Zero; clamped = false; return;
 73                            }
 74                        }
 75            }
 76        }
 37277        if (!best.HasValue) return;
 5478        FiniteAxisValueRoot root = RestoreValue(bestValues, bestSigns, bestCell, best);
 5479        ConvexContactValueRoot.GetRoundedDepth(geometry.RawScale, geometry.ValueShift,
 5480            ref root, out depth, out clamped);
 5481    }
 82
 83    /// <summary>Replays the selected value polynomial, without rounding or rebuilding its witness.</summary>
 84    internal static int CompareSquaredDepth(in CurvedPrismContactGeometry geometry, in Selection selected,
 85        scoped ContactQuadratic numerator, scoped ContactQuadratic denominator, scoped ReadOnlySpan<ulong> radical)
 86    {
 1187        if (selected.IsZero) return -numerator.Sign(radical);
 1888        Span<ulong> values = stackalloc ulong[5 * ValueWords], cell = stackalloc ulong[ValueCellWords];
 989        Span<sbyte> signs = stackalloc sbyte[5];
 990        BuildValues(geometry, selected.Edge, selected.CapSign, values, signs);
 991        bool found = WideFiniteAxisIntersection.TryGetFiniteValueRoot(values, signs, selected.ValueOrdinal, cell, out va
 92        System.Diagnostics.Debug.Assert(found);
 993        return ConvexContactValueRoot.CompareRootSquared(geometry.RawScale, geometry.ValueShift, ref root, numerator, de
 94    }
 95
 96    [MethodImpl(MethodImplOptions.NoInlining)]
 97    private static void KeepChart(in CurvedPrismContactGeometry geometry, int edge,
 98        in WideDirection3d edgeDirection, in WideDirection3d first, in WideDirection3d second, int cap,
 99        scoped ConvexContactCandidate analytic, ulong upperTwiceRaw, bool hasUpperBound, bool supportCertified,
 100        scoped Span<ulong> bestValues, scoped Span<sbyte> bestSigns, scoped Span<ulong> bestCell,
 101        ref Selection best, PrismCurvedContactDomain domain, bool hasAnalytic)
 102    {
 5242103        int firstAxialSign = 0, lastAxialSign = 0;
 2621104        if (!geometry.IsCone)
 105        {
 2102106            Signed832 axial = WideDirection3d.Dot(first, geometry.Axis);
 2102107            Signed832 slope = WideDirection3d.Dot(second, geometry.Axis);
 2102108            firstAxialSign = axial.Sign;
 2102109            lastAxialSign = WideArithmetic.AddSigned832(axial, slope).Sign;
 110            // Nonpositive endpoints leave only an analytic side-boundary
 111            // endpoint. Identically zero would mean the excluded parallel edge.
 3045112            if (firstAxialSign * cap <= 0 && lastAxialSign * cap <= 0) return;
 113        }
 1678114        geometry.Edge(edge, out int start, out _);
 1678115        WideDirection3d vertex = geometry.Vertex(start);
 116        // One strictly negative support-wall value at both chart endpoints
 117        // excludes this entire affine chart before constructing its quartic.
 8964118        for (int index = 0; !supportCertified && index < geometry.VertexCount; index++)
 119        {
 2900120            WideDirection3d delta = CurvedPrismContact.Difference(geometry.Vertex(index), vertex);
 5800121            Signed832 a = WideDirection3d.Dot(delta, first), b = WideDirection3d.Dot(delta, second);
 2996122            if (a.Sign < 0 && WideArithmetic.AddSigned832(a, b).Sign < 0) return;
 123        }
 1582124        WideDirection3d offset = CurvedPrismContact.Difference(cap < 0 ? -geometry.Half : geometry.Half, vertex);
 1582125        if (hasAnalytic && !geometry.IsCone && supportCertified && !domain.Enabled && edge % 3 != 2
 1582126            && firstAxialSign * lastAxialSign < 0
 1658127            && IsPhysicalArcConcave(geometry, edgeDirection, offset, first, second, cap, firstAxialSign)) return;
 1506128        if (hasAnalytic && !geometry.IsCone && supportCertified && !domain.Enabled
 1810129            && HasNoNonnegativeEllipseMinimum(geometry, edgeDirection, offset)) return;
 1202130        if (hasAnalytic && IsChartDominated(geometry, edgeDirection, offset, first, second, analytic,
 1524131                supportCertified && !domain.Enabled && edge % 3 == 2)) return;
 880132        Span<ulong> data = stackalloc ulong[Slots * Words];
 880133        Span<sbyte> signs = stackalloc sbyte[Slots];
 880134        BuildParameter(geometry, edge, first, second, cap, data, signs);
 880135        Polynomial storage = new(data, signs, Words);
 2640136        Polynomial stationary = storage.Slice(0, 5), k = storage.Slice(8, 2), l = storage.Slice(10, 3);
 2640137        Polynomial gap = storage.Slice(13, 3), numerator = storage.Slice(16, 5), denominator = storage.Slice(21, 5);
 885138        if (supportCertified && !domain.Enabled && HasNoNewStationaryMinimum(stationary, k)) return;
 875139        Span<ulong> batchCells = stackalloc ulong[64];
 875140        Span<int> batchShifts = stackalloc int[8];
 875141        FiniteAxisValueRoots roots = WideFiniteAxisIntersection.GetFiniteValueRoots(
 875142            stationary.Values, stationary.Signs, batchCells, batchShifts);
 1271143        if (roots.Count == 0) return;
 144        // At K's interior zero, W=L squared is nonnegative. One root, or
 145        // two starting from W(0)<0, can only be maxima; K=0 is analytic.
 479146        if (supportCertified && !domain.Enabled && !roots.HasRepeatedRoots && stationary.Signs[0] != 0
 479147            && k.Signs[0] > 0 && k.Signs[1] < 0
 479148            && WideArithmetic.CompareMagnitudeEqualLength(k.Values[..Words], k.Values.Slice(Words, Words)) < 0
 575149            && (roots.Count == 1 || roots.Count == 2 && stationary.Signs[0] < 0)) return;
 383150        Span<ulong> cell = stackalloc ulong[WideFiniteAxisIntersection.GetFiniteValueRootCellWords(
 383151            stationary.Values, stationary.Signs)];
 383152        Span<ulong> values = stackalloc ulong[5 * ValueWords];
 383153        Span<sbyte> valueSigns = stackalloc sbyte[5];
 383154        Span<ulong> valueCell = stackalloc ulong[ValueCellWords];
 383155        bool gapReady = false;
 383156        bool valuesReady = false;
 383157        int firstStationarySign = 0;
 383158        if (supportCertified && !domain.Enabled && !roots.HasRepeatedRoots)
 241159            foreach (sbyte sign in stationary.Signs)
 239160                if (sign != 0) { firstStationarySign = sign; break; }
 2024161        for (int ordinal = 0; ordinal < roots.Count; ordinal++)
 162        {
 637163            scoped FiniteAxisValueRoot root = FiniteAxisValueRoots.GetRoot(
 637164                roots, ordinal, stationary.Values, stationary.Signs, cell);
 637165            int kSign = 0;
 637166            if (firstStationarySign != 0)
 167            {
 151168                kSign = Sign(ref root, k);
 169                // For W=L²-K²Q and L=-K sqrt(Q), sign(g'')=-sign(W')*sign(K).
 170                // Simple roots alternate W's sign from its first nonzero coefficient.
 151171                if (firstStationarySign * ((ordinal & 1) == 0 ? 1 : -1) * kSign < 0) continue;
 172            }
 590173            int axialSign = Sign(ref root, storage.Slice(26, 2));
 590174            if (geometry.IsCone
 590175                ? axialSign > 0 && Sign(ref root, storage.Slice(28, 3)) <= 0
 590176                : axialSign < 0)
 177                continue;
 529178            if (!supportCertified && !HasPrismSupport(geometry, vertex, first, second, ref root)) continue;
 935179            if (firstStationarySign == 0) kSign = Sign(ref root, k);
 180            // Q is positive on a nondegenerate ellipse. K=0 is its principal
 181            // direction, already present in the rational feature fan.
 515182            if (kSign == 0 || Sign(ref root, l) != -kSign) continue;
 294183            if (!gapReady)
 184            {
 280185                CylinderEdgeContactPolynomial.CompleteGap(data[..(26 * Words)], signs[..26]);
 280186                gapReady = true;
 187            }
 294188            int gapSign = Sign(ref root, gap) * kSign;
 294189            if (domain.Enabled && gapSign < 0) continue;
 190            System.Diagnostics.Debug.Assert(gapSign >= 0);
 292191            Selection candidate = new()
 292192            {
 292193                HasValue = true,
 292194                Edge = edge,
 292195                CapSign = cap,
 292196                First = first,
 292197                Second = second,
 292198                ParameterOrdinal = ordinal
 292199            };
 292200            if (domain.Enabled)
 201            {
 178202                if (!TryAdmitDomain(geometry, domain, candidate, data, signs, ref root, out bool physical)) continue;
 47203                candidate.Physical = physical;
 204            }
 161205            if (gapSign == 0)
 206            {
 207                // Zero gap and zero derivative place the origin on the strictly
 208                // convex projected ellipse: its unique normal needs no tie rank.
 24209                candidate.IsZero = true; best = candidate; return;
 210            }
 153211            if (hasAnalytic && hasUpperBound && ConvexContactValueRoot.CompareSquaredGapToTwiceRaw(ref root,
 153212                    numerator.Values, numerator.Signs, denominator.Values, denominator.Signs, upperTwiceRaw) >= 0)
 213                continue;
 94214            if (!valuesReady)
 215            {
 184216                BuildValues(geometry, edge, cap, values, valueSigns); valuesReady = true;
 217            }
 94218            scoped FiniteAxisValueRoot value = ConvexContactValueRoot.MapSquaredValue(
 94219                geometry.RawScale, geometry.ValueShift, ref root, numerator.Values, numerator.Signs,
 94220                denominator.Values, denominator.Signs, values, valueSigns, valueCell);
 94221            int comparison = best.HasValue
 94222                ? WideFiniteAxisIntersection.CompareFiniteValueRoots(value, RestoreValue(bestValues, bestSigns, bestCell
 94223                : hasAnalytic ? ConvexContactValueRoot.CompareRootSquared(geometry.RawScale, geometry.ValueShift, ref va
 94224            if (comparison > 0 || comparison == 0 && (!best.HasValue || best.Edge != edge || best.CapSign != cap)) conti
 63225            if (supportCertified)
 226            {
 49227                candidate.Order = GetLegacyOrder(edgeDirection, first, second, ordinal, ref root);
 50228                if (comparison == 0 && candidate.Order >= best.Order) continue;
 229            }
 14230            else if (comparison == 0) continue;
 168231            values.CopyTo(bestValues); valueSigns.CopyTo(bestSigns); valueCell.CopyTo(bestCell);
 112232            candidate.ValueOrdinal = value.Ordinal; candidate.ValueShift = value.DenominatorShift;
 112233            candidate.IsRational = value.IsRational; best = candidate;
 234        }
 375235    }
 236
 237    // At K's positive zero beta, W(beta)=L(beta)^2. Two positive roots
 238    // can only bracket beta (both maxima), or coincide at the analytic K=0.
 239    private static bool HasNoNewStationaryMinimum(Polynomial stationary, Polynomial k)
 240    {
 527241        if (stationary.Signs[0] >= 0 || k.Signs[0] <= 0 || k.Signs[1] >= 0) return false;
 282242        int previous = 0, variations = 0;
 1692243        foreach (int sign in stationary.Signs)
 244        {
 705245            if (sign == 0) continue;
 1248246            if (previous != 0 && previous != sign) variations++;
 703247            previous = sign;
 248        }
 141249        return variations == 2;
 250    }
 251
 252    private static bool TryAdmitDomain(in CurvedPrismContactGeometry geometry, PrismCurvedContactDomain domain,
 253        in Selection candidate, Span<ulong> parameters, Span<sbyte> parameterSigns,
 254        scoped ref FiniteAxisValueRoot root, out bool physical)
 255    {
 178256        physical = false;
 356257        Span<ulong> fields = stackalloc ulong[36 * Words]; Span<sbyte> signs = stackalloc sbyte[36];
 356258        fields.Clear(); signs.Clear();
 534259        Polynomial storage = new(fields, signs, Words), shape = storage.Slice(0, 9), point = storage.Slice(9, 18);
 356260        Polynomial shapeD = storage.Slice(27, 3), pointD = storage.Slice(30, 6);
 178261        BuildPointPolynomials(geometry, candidate, parameters, parameterSigns, shape, point, shapeD, pointD);
 178262        geometry.Edge(candidate.Edge, out int sourceVertex, out _);
 1994263        foreach (var window in domain.Edges)
 264        {
 844265            if (NormalDotSign(candidate, CurvedPrismContact.Difference(geometry.Vertex(window.ReferenceVertex), geometry
 209266            bool admitted = true;
 428267            foreach (var delta in domain.FanCovectors.Slice(window.FanStart, window.FanCount))
 15268                if (NormalDotSign(candidate, delta, ref root) < 0) { admitted = false; break; }
 209269            if (!admitted || !PointInEdgeWindow(geometry, window, point, pointD, ref root)) continue;
 50270            physical = IsPhysicalPoint(geometry, point, pointD, ref root);
 50271            return physical || domain.IncludeFarBoundary;
 272        }
 273        // Every face-normal direction already belongs to the earlier rational
 274        // inventory. Its equal-depth rim representation cannot improve it.
 128275        return false;
 276    }
 277
 278    private static int NormalDotSign(in Selection candidate, in WideDirection3d delta, scoped ref FiniteAxisValueRoot ro
 279    {
 1702280        Span<ulong> values = stackalloc ulong[26]; Span<sbyte> signs = stackalloc sbyte[2];
 851281        Polynomial query = new(values, signs, 13);
 851282        Write(query, 0, WideDirection3d.Dot(delta, candidate.First));
 1702283        Write(query, 1, WideDirection3d.Dot(delta, candidate.Second)); return Sign(ref root, query);
 284    }
 285
 286    private static bool PointInEdgeWindow(in CurvedPrismContactGeometry geometry, in PrismCurvedEdgeWindow window,
 287        scoped Polynomial point, scoped Polynomial denominator, scoped ref FiniteAxisValueRoot root)
 288    {
 205289        geometry.Edge(window.Edge, out int first, out int last);
 410290        var a = geometry.Vertex(first); var edge = CurvedPrismContact.Difference(geometry.Vertex(last), a);
 291        // A retained source coefficient needs <2560 bits. Translating by a
 292        // vertex adds <165, projection <167 and a cut ratio <163: complete
 293        // interval comparisons need <3060 bits, inside 56-word banks.
 294        const int words = 56;
 410295        Span<ulong> values = stackalloc ulong[38 * words]; Span<sbyte> signs = stackalloc sbyte[37];
 615296        values.Clear(); signs.Clear(); Polynomial storage = new(values[..(37 * words)], signs, words);
 615297        Polynomial q = storage.Slice(0, 18), n = storage.Slice(18, 6), d = storage.Slice(24, 6);
 410298        Polynomial work = storage.Slice(30, 6), scalar = storage.Slice(36, 1);
 205299        Span<ulong> product = values[(37 * words)..];
 205300        int pivot = !edge.X.IsZero ? 0 : !edge.Y.IsZero ? 1 : 2;
 1640301        for (int axis = 0; axis < 3; axis++)
 302        {
 8610303            for (int coefficient = 0; coefficient < 6; coefficient++)
 3690304                point.Values.Slice((6 * axis + coefficient) * point.Words, point.Words)
 3690305                    .CopyTo(q.Values.Slice((6 * axis + coefficient) * words, words));
 615306            point.Slice(6 * axis, 6).Signs.CopyTo(q.Slice(6 * axis, 6).Signs);
 615307            Write(scalar, 0, Signed832.ExtendValue(Component(a, axis)));
 615308            AddProduct(q.Slice(6 * axis, 6), denominator, scalar, -1, product);
 309        }
 1158310        for (int axis = 0; axis < 3; axis++)
 311        {
 982312            Write(scalar, 0, Signed832.ExtendValue(Component(edge, pivot))); Multiply(work, q.Slice(6 * axis, 6), scalar
 982313            Write(scalar, 0, Signed832.ExtendValue(Component(edge, axis))); AddProduct(work, q.Slice(6 * pivot, 6), scal
 608314            if (Sign(ref root, work) != 0) return false;
 748315            Write(scalar, 0, Signed832.ExtendValue(Component(edge, axis))); AddProduct(n, q.Slice(6 * axis, 6), scalar, 
 316        }
 176317        Write(scalar, 0, edge.SquaredLength); Multiply(d, denominator, scalar, product);
 88318        int orientation = Sign(ref root, d);
 88319        var clip = window.Interval;
 88320        Write(scalar, 0, Signed832.ExtendValue(Signed576.ExtendValue(clip.IsClipped ? clip.LowerDenominator : Signed320.
 88321        Multiply(work, n, scalar, product);
 88322        Write(scalar, 0, Signed832.ExtendValue(Signed576.ExtendValue(clip.IsClipped ? clip.LowerNumerator : default)));
 88323        AddProduct(work, d, scalar, -1, product);
 177324        int lower = Sign(ref root, work) * orientation; if (lower < 0 || !window.Point && lower == 0) return false;
 87325        Write(scalar, 0, Signed832.ExtendValue(Signed576.ExtendValue(clip.IsClipped ? clip.UpperDenominator : Signed320.
 87326        Multiply(work, n, scalar, product);
 87327        Write(scalar, 0, Signed832.ExtendValue(Signed576.ExtendValue(clip.IsClipped ? clip.UpperNumerator : Signed320.On
 87328        AddProduct(work, d, scalar, -1, product);
 174329        int upper = Sign(ref root, work) * orientation; return upper <= 0 && (window.Point || upper < 0);
 330    }
 331
 332    private static bool IsPhysicalPoint(in CurvedPrismContactGeometry geometry, scoped Polynomial point,
 333        scoped Polynomial denominator, scoped ref FiniteAxisValueRoot root)
 334    {
 335        // Source coefficients fit the existing 2560-bit banks. Squared cone
 336        // admission adds <832 bits, so complete products fit 96-word banks.
 337        const int words = 96;
 100338        Span<ulong> values = stackalloc ulong[53 * words]; Span<sbyte> signs = stackalloc sbyte[52];
 150339        values.Clear(); signs.Clear(); Polynomial fields = new(values[..(52 * words)], signs, words);
 150340        Polynomial axial = fields.Slice(0, 6), radial = fields.Slice(6, 11), work = fields.Slice(17, 11);
 150341        Polynomial limit = fields.Slice(28, 11), maximum = fields.Slice(39, 6), height = fields.Slice(45, 6);
 100342        Polynomial scalar = fields.Slice(51, 1); Span<ulong> product = values[(52 * words)..];
 400343        for (int axis = 0; axis < 3; axis++)
 344        {
 150345            Write(scalar, 0, Signed832.ExtendValue(Signed576.ExtendValue(Component(geometry.Axis, axis))));
 150346            AddProduct(axial, point.Slice(6 * axis, 6), scalar, 1, product);
 300347            Multiply(work, point.Slice(6 * axis, 6), point.Slice(6 * axis, 6), product); Add(radial, work, 1);
 348        }
 50349        Write(scalar, 0, Signed832.ExtendValue(WideArithmetic.MultiplySigned320(geometry.SquaredAxis, geometry.HalfFacto
 50350        Multiply(maximum, denominator, scalar, product);
 150351        maximum.Values.CopyTo(height.Values); maximum.Signs.CopyTo(height.Signs); Add(height, axial, -1);
 50352        int orientation = Sign(ref root, denominator);
 52353        if (Sign(ref root, height) * orientation < 0) return false;
 144354        maximum.Values.CopyTo(work.Slice(0, 6).Values); maximum.Signs.CopyTo(work.Slice(0, 6).Signs); Add(work.Slice(0, 
 52355        if (Sign(ref root, work.Slice(0, 6)) * orientation < 0) return false;
 88356        Write(scalar, 0, Signed832.ExtendValue(geometry.SquaredAxis)); Multiply(work, radial, scalar, product);
 88357        work.Values.CopyTo(radial.Values); work.Signs.CopyTo(radial.Signs);
 88358        Multiply(work, axial, axial, product); Add(radial, work, -1);
 44359        Signed576 radiusSquared = WideArithmetic.MultiplySigned320(geometry.Radius, geometry.Radius);
 44360        if (geometry.IsCone)
 361        {
 36362            Multiply(work, height, height, product); Write(scalar, 0, Signed832.ExtendValue(radiusSquared)); Multiply(li
 12363            Signed576 factor = WideArithmetic.MultiplySigned320(Signed320.ExtendValue(geometry.HalfFactor), geometry.Hal
 12364            factor = Signed576.NarrowValue(WideArithmetic.MultiplySigned576ToSigned832(factor, geometry.SquaredAxis));
 24365            factor = WideArithmetic.AddSigned576(factor, factor); factor = WideArithmetic.AddSigned576(factor, factor);
 24366            Write(scalar, 0, Signed832.ExtendValue(factor)); Multiply(work, radial, scalar, product);
 367        }
 368        else
 369        {
 32370            Multiply(work, denominator, denominator, product);
 64371            Write(scalar, 0, WideArithmetic.MultiplySigned576ToSigned832(radiusSquared, geometry.SquaredAxis)); Multiply
 64372            radial.Values.CopyTo(work.Values); radial.Signs.CopyTo(work.Signs);
 373        }
 88374        Add(work, limit, -1); return Sign(ref root, work) <= 0;
 375    }
 376
 377    private static int GetLegacyOrder(in WideDirection3d edge, in WideDirection3d first,
 378        in WideDirection3d second, int ordinal, scoped ref FiniteAxisValueRoot root)
 379    {
 380        // In the old basis, alpha/beta share a fixed edge pivot. Their signs
 381        // and relative magnitudes identify the signed reciprocal chart; its
 382        // parameter is fractional-linear in t and has a constant derivative sign.
 98383        bool xPivot = !edge.X.IsZero, yPivot = !edge.Y.IsZero;
 98384        int aCoordinate = xPivot ? 1 : yPivot ? 2 : 1, bCoordinate = xPivot ? 2 : 0;
 49385        int pivotSign = (xPivot ? edge.X : yPivot ? edge.Y : edge.Z).Sign;
 98386        Signed576 a0 = Component(first, aCoordinate), a1 = Component(second, aCoordinate);
 98387        Signed576 b0 = Component(first, bCoordinate), b1 = Component(second, bCoordinate);
 49388        if (pivotSign * (xPivot || yPivot ? 1 : -1) < 0)
 86389        { a0 = WideArithmetic.Negate(a0); a1 = WideArithmetic.Negate(a1); }
 131390        if (pivotSign < 0) { b0 = WideArithmetic.Negate(b0); b1 = WideArithmetic.Negate(b1); }
 49391        Span<ulong> data = stackalloc ulong[26];
 49392        Span<sbyte> signs = stackalloc sbyte[2];
 49393        Polynomial query = new(data, signs, 13);
 98394        Write(query, 0, Signed832.ExtendValue(a0)); Write(query, 1, Signed832.ExtendValue(a1));
 49395        int aSign = Sign(ref root, query);
 98396        Write(query, 0, Signed832.ExtendValue(b0)); Write(query, 1, Signed832.ExtendValue(b1));
 49397        int bSign = Sign(ref root, query);
 398        // Old basis-axis directions already belong to the earlier analytic fan.
 399        System.Diagnostics.Debug.Assert(aSign != 0 && bSign != 0);
 49400        Write(query, 0, Signed832.ExtendValue(WideArithmetic.SubtractSigned576(
 49401            aSign < 0 ? WideArithmetic.Negate(a0) : a0, bSign < 0 ? WideArithmetic.Negate(b0) : b0)));
 49402        Write(query, 1, Signed832.ExtendValue(WideArithmetic.SubtractSigned576(
 49403            aSign < 0 ? WideArithmetic.Negate(a1) : a1, bSign < 0 ? WideArithmetic.Negate(b1) : b1)));
 49404        bool reciprocal = Sign(ref root, query) < 0;
 49405        Signed832 determinant = WideArithmetic.SubtractSigned832(
 49406            WideArithmetic.MultiplySigned576ToSigned832(a0, b1), WideArithmetic.MultiplySigned576ToSigned832(a1, b0));
 49407        bool increasing = determinant.Sign * aSign * bSign * (reciprocal ? -1 : 1) > 0;
 49408        return (aSign > 0 ? 16 : 0) + (bSign > 0 ? 8 : 0) + (reciprocal ? 4 : 0)
 49409            + (increasing ? ordinal : 3 - ordinal);
 410    }
 411
 412    [MethodImpl(MethodImplOptions.NoInlining)]
 413    private static bool HasNoNonnegativeEllipseMinimum(in CurvedPrismContactGeometry geometry,
 414        in WideDirection3d edge, in WideDirection3d offset)
 415    {
 416        // Nondegenerate ellipse dispatch gives a=R, b=R*|A.E|/sqrt(K),
 417        // K=q*E² and 0<b<a. At a nonnegative stationary minimum,
 418        // g''=rho-g>=0, so 0<=g<=rho. Thus |Proj(Eperp)c|² cannot exceed
 419        // max(a²,((a²-b²)/b)²), the ellipse/evolute maximum squared norms.
 420        // Physical arc endpoints and cap equators remain in the analytic fan.
 505421        Signed576 edgeSquared = Signed576.NarrowValue(edge.SquaredLength);
 505422        Signed576 offsetSquared = Signed576.NarrowValue(offset.SquaredLength);
 505423        Signed576 projection = Signed576.NarrowValue(WideDirection3d.Dot(edge, offset));
 505424        Signed832 projectedSquared = WideArithmetic.SubtractSigned832(
 505425            WideArithmetic.MultiplySigned576ToSigned832(edgeSquared, offsetSquared),
 505426            WideArithmetic.MultiplySigned576ToSigned832(projection, projection));
 505427        Signed576 radiusSquared = WideArithmetic.MultiplySigned320(geometry.Radius, geometry.Radius);
 505428        Signed832 radiusMetric = WideArithmetic.MultiplySigned576ToSigned832(radiusSquared, edgeSquared);
 429        // C=e²*c²-(e.c)². Strict C>R²*e² first; equality retains fallback.
 654430        if (WideArithmetic.SubtractSigned832(projectedSquared, radiusMetric).Sign <= 0) return false;
 356431        Signed576 axial = Signed576.NarrowValue(WideDirection3d.Dot(edge, geometry.Axis));
 356432        Signed832 axialSquared = WideArithmetic.MultiplySigned576ToSigned832(axial, axial);
 356433        Signed832 metric = WideArithmetic.MultiplySigned832(Signed832.ExtendValue(edgeSquared), geometry.SquaredAxis);
 356434        Signed832 anisotropy = WideArithmetic.SubtractSigned832(metric, axialSquared);
 435        // C*K*d² > R²*e²*(K-d²)². Typed factors fit 832; complete
 436        // products need <1471/<1458 bits. Four 24-word fields retain all carries.
 437        const int words = 24;
 356438        Span<ulong> work = stackalloc ulong[4 * words];
 712439        Span<ulong> left = work[..words], right = work.Slice(words, words);
 712440        Span<ulong> factor = work.Slice(2 * words, words), product = work.Slice(3 * words, words);
 712441        CylinderContactAlgebra.Import(projectedSquared, left); CylinderContactAlgebra.Import(metric, factor);
 356442        WideArithmetic.MultiplyMagnitudes(left, factor, product);
 356443        CylinderContactAlgebra.Import(axialSquared, factor);
 356444        WideArithmetic.MultiplyMagnitudes(product, factor, left);
 356445        CylinderContactAlgebra.Import(anisotropy, factor);
 356446        WideArithmetic.MultiplyMagnitudes(factor, factor, right);
 356447        CylinderContactAlgebra.Import(radiusMetric, factor);
 356448        WideArithmetic.MultiplyMagnitudes(right, factor, product);
 356449        return WideArithmetic.CompareMagnitudeEqualLength(left, product) > 0;
 450    }
 451
 452    private static bool IsPhysicalArcConcave(in CurvedPrismContactGeometry geometry,
 453        in WideDirection3d edge, in WideDirection3d offset, in WideDirection3d first,
 454        in WideDirection3d second, int cap, int firstAxialSign)
 455    {
 456        // Orthogonal certified horizontal endpoints and a strict axial
 457        // crossing place the equator in their positive span. Orient its
 458        // compact E-cross-A ray by its strictly positive dot with first.
 147459        WideDirection3d equator = WideDirection3d.Cross(edge, new WideDirection3d(geometry.Axis));
 194460        if (WideDirection3d.Dot(equator, first).Sign < 0) equator = -equator;
 147461        WideDirection3d physical = firstAxialSign * cap > 0 ? first
 147462            : new(WideArithmetic.AddSigned576(first.X, second.X),
 147463                WideArithmetic.AddSigned576(first.Y, second.Y), WideArithmetic.AddSigned576(first.Z, second.Z));
 160464        if (WideDirection3d.Dot(offset, physical).Sign < 0 || WideDirection3d.Dot(offset, equator).Sign < 0) return fals
 465        // On this arc, |A.n|/|n| is monotone toward the physical endpoint,
 466        // so projected radial support h has its minimum beta there. For an
 467        // ellipse with major radius R and minor radius r, h''=R²r²/h³-h.
 468        // beta^4>=R²r² makes h''<=0 throughout the arc. Since offset.n>=0
 469        // here, g''=-offset.u+h''<=0; earlier analytic endpoints retain minima.
 470        // With Kp=q*|physical|², rp=Kp-(A.physical)², Ke=q*|E|² and d=A.E,
 471        // beta²=R²rp/Kp and r²=R²d²/Ke cancel R: rp²Ke>=d²Kp².
 134472        Signed832 physicalAxial = WideDirection3d.Dot(physical, geometry.Axis);
 134473        Signed832 kPhysical = WideArithmetic.MultiplySigned832(physical.SquaredLength, geometry.SquaredAxis);
 134474        Signed832 axialSquared = WideArithmetic.MultiplySigned576ToSigned832(
 134475            Signed576.NarrowValue(physicalAxial), Signed576.NarrowValue(physicalAxial));
 134476        Signed832 radial = WideArithmetic.SubtractSigned832(kPhysical, axialSquared);
 134477        Signed832 kEdge = WideArithmetic.MultiplySigned832(edge.SquaredLength, geometry.SquaredAxis);
 134478        Signed832 edgeAxial = WideDirection3d.Dot(edge, geometry.Axis);
 134479        Signed832 edgeAxialSquared = WideArithmetic.MultiplySigned576ToSigned832(
 134480            Signed576.NarrowValue(edgeAxial), Signed576.NarrowValue(edgeAxial));
 481        // Native endpoints <165, E <166 and A <33 bits give Kp<397,
 482        // rp<397, Ke<399 and d²<402. Complete products fit 1200 bits.
 483        const int words = 24;
 134484        Span<ulong> work = stackalloc ulong[4 * words];
 268485        Span<ulong> left = work[..words], right = work.Slice(words, words);
 268486        Span<ulong> factor = work.Slice(2 * words, words), product = work.Slice(3 * words, words);
 268487        CylinderContactAlgebra.Import(radial, factor); WideArithmetic.MultiplyMagnitudes(factor, factor, left);
 268488        CylinderContactAlgebra.Import(kEdge, factor); WideArithmetic.MultiplyMagnitudes(left, factor, product);
 268489        CylinderContactAlgebra.Import(kPhysical, factor); WideArithmetic.MultiplyMagnitudes(factor, factor, left);
 268490        CylinderContactAlgebra.Import(edgeAxialSquared, factor); WideArithmetic.MultiplyMagnitudes(left, factor, right);
 134491        return WideArithmetic.CompareMagnitudeEqualLength(product, right) >= 0;
 492    }
 493
 494    [MethodImpl(MethodImplOptions.NoInlining)]
 495    private static bool IsChartDominated(in CurvedPrismContactGeometry geometry,
 496        in WideDirection3d edge, in WideDirection3d offset, in WideDirection3d first,
 497        in WideDirection3d second, scoped ConvexContactCandidate analytic, bool allowConcavity)
 498    {
 877499        WideDirection3d last = new(WideArithmetic.AddSigned576(first.X, second.X),
 877500            WideArithmetic.AddSigned576(first.Y, second.Y), WideArithmetic.AddSigned576(first.Z, second.Z));
 1754501        Signed832 a = WideDirection3d.Dot(offset, first), b = WideDirection3d.Dot(offset, last);
 1244502        if (a.Sign < 0 || b.Sign < 0) return false;
 1020503        Signed832 firstMetric = first.SquaredLength, lastMetric = last.SquaredLength;
 510504        Signed832 aSquared = WideArithmetic.MultiplySigned576ToSigned832(Signed576.NarrowValue(a), Signed576.NarrowValue
 510505        Signed832 bSquared = WideArithmetic.MultiplySigned576ToSigned832(Signed576.NarrowValue(b), Signed576.NarrowValue
 510506        bool useLast = WideArithmetic.CompareNonNegativeProducts(aSquared, lastMetric, bSquared, firstMetric) > 0;
 510507        Signed832 alphaNumerator = useLast ? b : a;
 510508        Signed832 alphaSquared = useLast ? bSquared : aSquared;
 510509        Signed832 alphaMetric = useLast ? lastMetric : firstMetric;
 510
 511        // n(t)=(1-t)*first+t*last. Nonnegative endpoint projections and
 512        // the triangle inequality give offset.n/|n| >= alpha, the smaller
 513        // endpoint projection divided by its exact Euclidean norm.
 514        // On edge-perpendicular normals, the projected disk support is at
 515        // least beta=R*|Axis.edge|/sqrt(q*edge²). Thus (alpha+beta)/S bounds
 516        // every gap from below; beta>0 follows from the outer ellipse dispatch.
 510517        Span<ulong> values = stackalloc ulong[ConvexContactCandidate.Slots * Words];
 510518        Span<int> signs = stackalloc int[ConvexContactCandidate.Slots];
 2040519        values.Clear(); signs.Clear(); values[0] = 1; signs[0] = 1;
 510520        Span<ulong> work = stackalloc ulong[6 * Words];
 1020521        Span<ulong> projection = work[..Words], radial = work.Slice(Words, Words);
 1020522        Span<ulong> metric = work.Slice(2 * Words, Words), k = work.Slice(3 * Words, Words);
 1020523        Span<ulong> temporary = work.Slice(4 * Words, Words), product = work.Slice(5 * Words, Words);
 1020524        Span<ulong> rational = values.Slice(7 * Words, Words), radical = values.Slice(8 * Words, Words);
 1020525        Span<ulong> root = values.Slice(9 * Words, Words), denominator = values.Slice(10 * Words, Words);
 510526        CylinderContactAlgebra.Import(alphaNumerator, projection);
 510527        CylinderContactAlgebra.Import(alphaMetric, metric);
 510528        Signed832 edgeAxial = WideDirection3d.Dot(edge, geometry.Axis);
 510529        Signed832 edgeMetric = WideArithmetic.MultiplySigned832(edge.SquaredLength, geometry.SquaredAxis);
 510530        CylinderContactAlgebra.Import(WideArithmetic.MultiplySigned832(edgeAxial, geometry.Radius), radial);
 510531        CylinderContactAlgebra.Import(edgeMetric, k);
 510532        CylinderContactAlgebra.Import(alphaSquared, temporary);
 510533        WideArithmetic.MultiplyMagnitudes(temporary, k, rational);
 510534        WideArithmetic.MultiplyMagnitudes(radial, radial, temporary);
 510535        WideArithmetic.MultiplyMagnitudes(temporary, metric, product);
 510536        WideArithmetic.AddMagnitudeInto(product, rational);
 510537        WideArithmetic.MultiplyMagnitudes(projection, radial, radical);
 510538        WideArithmetic.AddMagnitudeInto(radical, radical);
 510539        WideArithmetic.MultiplyMagnitudes(k, metric, root);
 510540        CylinderContactAlgebra.Import(Signed576.ExtendValue(geometry.RawScale), projection);
 510541        WideArithmetic.MultiplyMagnitudes(projection, projection, temporary);
 510542        WideArithmetic.MultiplyMagnitudes(temporary, root, denominator);
 1020543        signs[7] = 1; signs[8] = alphaNumerator.Sign;
 544        // With alpha=a/sqrt(M), h=R*|Axis.edge| and K=q*edge², the
 545        // squared bound is (a²K+h²M+2ah*sqrt(KM))/(KM*S²).
 546        // a<2^338, M<2^340, h<2^366 and K<2^400; a² fits Signed832.
 547        // Every squared-candidate field, including sum carries, is <1100 bits.
 548        // The existing forty-word quadratic candidate owner retains them all.
 510549        if (WideConvexPrismRelations.CompareConvexContactCandidates(
 824550                new ConvexContactCandidate(values, signs, 1), analytic) >= 0) return true;
 356551        if (!allowConcavity) return false;
 552
 553        // Only ordinary certified vertical-edge cones reach this second test.
 554        // Their ellipse has semiaxes R and r=R*|A.E|/sqrt(K), K=q*E.E.
 555        // max(h'')=(R*R-r*r)/r; alpha above this makes the gap concave,
 556        // so its physical subarc endpoints in the earlier fan retain minima.
 36557        Signed832 edgeAxialSquared = WideArithmetic.MultiplySigned576ToSigned832(
 36558            Signed576.NarrowValue(edgeAxial), Signed576.NarrowValue(edgeAxial));
 36559        Signed832 crossSquared = WideArithmetic.SubtractSigned832(edgeMetric, edgeAxialSquared);
 560        // a²*K*d² >= R²*(K-d²)²*M. Factors fit 832 bits; complete
 561        // products need<1480 bits. Reuse dead bound scratch in24-word fields.
 562        const int curvatureWords = 24;
 72563        Span<ulong> left = work[..curvatureWords], right = work.Slice(curvatureWords, curvatureWords);
 36564        Span<ulong> factor = work.Slice(2 * curvatureWords, curvatureWords);
 36565        Span<ulong> leftProduct = work.Slice(3 * curvatureWords, curvatureWords);
 72566        CylinderContactAlgebra.Import(alphaSquared, factor); CylinderContactAlgebra.Import(edgeMetric, right);
 36567        WideArithmetic.MultiplyMagnitudes(factor, right, left);
 36568        CylinderContactAlgebra.Import(edgeAxialSquared, factor);
 36569        WideArithmetic.MultiplyMagnitudes(left, factor, leftProduct);
 36570        CylinderContactAlgebra.Import(crossSquared, factor);
 36571        WideArithmetic.MultiplyMagnitudes(factor, factor, right);
 36572        CylinderContactAlgebra.Import(WideArithmetic.MultiplySigned320(geometry.Radius, geometry.Radius), factor);
 36573        WideArithmetic.MultiplyMagnitudes(factor, right, left);
 36574        CylinderContactAlgebra.Import(alphaMetric, factor);
 36575        WideArithmetic.MultiplyMagnitudes(left, factor, right);
 36576        return WideArithmetic.CompareMagnitudeEqualLength(leftProduct, right) >= 0;
 577    }
 578
 579    private static bool HasPrismSupport(in CurvedPrismContactGeometry geometry, in WideDirection3d vertex,
 580        in WideDirection3d first, in WideDirection3d second, scoped ref FiniteAxisValueRoot root)
 581    {
 84582        Span<ulong> query = stackalloc ulong[26];
 84583        Span<sbyte> signs = stackalloc sbyte[2];
 1176584        for (int index = 0; index < geometry.VertexCount; index++)
 585        {
 518586            WideDirection3d delta = CurvedPrismContact.Difference(geometry.Vertex(index), vertex);
 1036587            Signed832 a = WideDirection3d.Dot(delta, first), b = WideDirection3d.Dot(delta, second);
 588            // An affine constraint nonnegative at both endpoints holds on the whole chart.
 518589            if (a.Sign >= 0 && WideArithmetic.AddSigned832(a, b).Sign >= 0) continue;
 34590            WideArithmetic.GetMagnitude(a, query[..13]);
 34591            WideArithmetic.GetMagnitude(b, query[13..]);
 68592            signs[0] = (sbyte)a.Sign; signs[1] = (sbyte)b.Sign;
 48593            if (WideFiniteAxisIntersection.GetSignAtFiniteValueRootAndRefine(ref root, query, signs) < 0) return false;
 594        }
 70595        return true;
 596    }
 597
 598    /// <summary>
 599    /// Writes W,Q,K,L,J,N,D into 0..25; 26..27 are the oriented cap axial
 600    /// projection (unoriented for a cone), and 28..30 the cone's strict base
 601    /// switch. All coefficients use forty-word fields.
 602    /// </summary>
 603    [MethodImpl(MethodImplOptions.NoInlining)]
 604    private static void BuildParameter(in CurvedPrismContactGeometry geometry, int edge,
 605        in WideDirection3d first, in WideDirection3d second, int cap,
 606        Span<ulong> data, Span<sbyte> signs)
 607    {
 608        // |u|<2^33, q<2^65, |e|,|first|,|second|<2^166,
 609        // |c|<2^167, R<2^163 and S<2^99 give M<2^335,P<2^401,
 610        // Q<2^792,K<2^740,L<2^1130,W,N<2^2290,D<2^2150.
 611        // Forty words retain all convolution carries without scalar narrowing.
 935612        Span<ulong> scratch = stackalloc ulong[22 * Words];
 935613        Span<sbyte> scratchSigns = stackalloc sbyte[21];
 3740614        scratch.Clear(); scratchSigns.Clear(); data.Clear(); signs.Clear();
 935615        Polynomial work = new(scratch[..(21 * Words)], scratchSigns, Words);
 2805616        Polynomial p = work.Slice(0, 2), q = work.Slice(2, 1), radiusSquared = work.Slice(3, 1);
 2805617        Polynomial factor = work.Slice(4, 1), axial = work.Slice(5, 2), axialSquared = work.Slice(7, 3);
 2805618        Polynomial metric = work.Slice(10, 3), radial = work.Slice(13, 3), radical = work.Slice(16, 3);
 1870619        Polynomial scale = work.Slice(19, 1), scaledQ = work.Slice(20, 1);
 935620        Span<ulong> product = scratch.Slice(21 * Words, Words);
 935621        WideDirection3d offset = GetOffset(geometry, edge, cap);
 935622        Write(q, 0, Signed832.ExtendValue(geometry.SquaredAxis));
 935623        Write(radiusSquared, 0, Signed832.ExtendValue(WideArithmetic.MultiplySigned320(geometry.Radius, geometry.Radius)
 935624        Write(axial, 0, WideDirection3d.Dot(first, geometry.Axis));
 935625        Write(axial, 1, WideDirection3d.Dot(second, geometry.Axis));
 935626        Write(metric, 0, first.SquaredLength);
 935627        Write(metric, 1, Twice(WideDirection3d.Dot(first, second)));
 935628        Write(metric, 2, second.SquaredLength);
 935629        Multiply(radial, q, metric, product);
 1870630        Multiply(axialSquared, axial, axial, product); Add(radial, axialSquared, -1);
 935631        Multiply(factor, radiusSquared, q, product);
 935632        Multiply(radical, factor, radial, product);
 935633        Write(p, 0, WideArithmetic.MultiplySigned832(WideDirection3d.Dot(offset, first), geometry.SquaredAxis));
 935634        Write(p, 1, WideArithmetic.MultiplySigned832(WideDirection3d.Dot(offset, second), geometry.SquaredAxis));
 935635        Write(scale, 0, Signed832.ExtendValue(geometry.RawScale));
 935636        Multiply(scaledQ, q, scale, product);
 935637        bool reducedMetric = false;
 638        // Both chart vectors are nonzero; their squared lengths are positive.
 639        System.Diagnostics.Debug.Assert(metric.Signs[0] > 0 && metric.Signs[2] > 0);
 935640        if (metric.Signs[1] < 0)
 641        {
 712642            metric.Values[..Words].CopyTo(product);
 712643            WideArithmetic.AddMagnitudeInto(metric.Values[..Words], product);
 712644            reducedMetric = WideArithmetic.CompareMagnitudeEqualLength(product, metric.Values.Slice(Words, Words)) == 0
 712645                && WideArithmetic.CompareMagnitudeEqualLength(product, metric.Values.Slice(2 * Words, Words)) == 0;
 712646            if (reducedMetric)
 647            {
 648                // M=m*(1-2t+2t^2). Retain m/2m in dead factor/product
 649                // scratch, then remove m before the derivative products.
 141650                metric.Values[..Words].CopyTo(factor.Values);
 141651                factor.Signs[0] = 1;
 141652                metric.Values.Clear();
 141653                metric.Values[0] = 1;
 141654                metric.Values[Words] = metric.Values[2 * Words] = 2;
 655            }
 656        }
 935657        CylinderEdgeContactPolynomial.BuildStationary(p.Values, p.Signs, radical.Values, radical.Signs,
 935658            metric.Values, metric.Signs, scaledQ.Values, data[..(26 * Words)], signs[..26]);
 935659        Polynomial storage = new(data, signs, Words);
 935660        if (reducedMetric)
 661        {
 662            // Restore physical M before CompleteGap: K/L/J lost m, so N
 663            // and D both lose m^2. Reduced M here would corrupt the ratio.
 141664            factor.Values.CopyTo(storage.Slice(16, 1).Values);
 141665            product.CopyTo(storage.Slice(17, 1).Values);
 141666            product.CopyTo(storage.Slice(18, 1).Values);
 141667            storage.Signs[16] = storage.Signs[18] = 1;
 141668            storage.Signs[17] = -1;
 669        }
 935670        Add(storage.Slice(26, 2), axial, geometry.IsCone ? 1 : cap);
 1540671        if (!geometry.IsCone) return;
 330672        Polynomial region = storage.Slice(28, 3);
 330673        Multiply(region, radiusSquared, radial, product);
 674        // T may grow when Axis is made primitive; T²*q itself is unchanged.
 330675        Write(scale, 0, Signed832.ExtendValue(WideArithmetic.MultiplySigned320(
 330676            Signed320.ExtendValue(geometry.HalfFactor), geometry.HalfFactor)));
 330677        Multiply(scaledQ, q, scale, product);
 660678        Multiply(radical, scaledQ, axialSquared, product); Add(region, radical, -4);
 330679    }
 680
 681    [MethodImpl(MethodImplOptions.NoInlining)]
 682    private static void BuildValues(in CurvedPrismContactGeometry geometry, int edge, int cap,
 683        Span<ulong> values, Span<sbyte> signs)
 684    {
 101685        geometry.Edge(edge, out int start, out int end);
 101686        WideDirection3d e = CurvedPrismContact.Difference(geometry.Vertex(end), geometry.Vertex(start));
 101687        WideDirection3d c = GetOffset(geometry, edge, cap);
 101688        Span<ulong> invariants = stackalloc ulong[8 * ValueWords];
 101689        Span<sbyte> invariantSigns = stackalloc sbyte[8];
 202690        invariants.Clear(); invariantSigns.Clear();
 101691        Polynomial scalars = new(invariants, invariantSigns, ValueWords);
 101692        Write(scalars, 0, Signed832.ExtendValue(geometry.SquaredAxis));
 101693        Write(scalars, 1, e.SquaredLength);
 101694        Write(scalars, 2, WideDirection3d.Dot(e, geometry.Axis));
 101695        Write(scalars, 3, c.SquaredLength);
 101696        Write(scalars, 4, WideDirection3d.Dot(c, geometry.Axis));
 101697        Write(scalars, 5, WideDirection3d.Dot(e, c));
 101698        Write(scalars, 6, Signed832.ExtendValue(WideArithmetic.MultiplySigned320(geometry.Radius, geometry.Radius)));
 101699        CylinderPairSideValuePolynomial.BuildUnshifted(invariants, invariantSigns, ValueWords, values, signs);
 700        // Under the parameter bounds, weighted l,m,v heights are <750
 701        // after z=2^ValueShift*t (ValueShift<=338). Quartic discriminant
 702        // summands and their carries stay below 3100 bits, within 56 words.
 101703        WideFiniteAxisIntersection.ScaleFiniteAxisPolynomialVariable(values, 5, geometry.ValueShift);
 101704        WideFiniteAxisIntersection.NormalizeFiniteAxisPolynomialPowerOfTwo(values, signs);
 101705    }
 706
 707    /// <summary>Restores one retained parameter and rounds its paired physical witnesses once.</summary>
 708    [MethodImpl(MethodImplOptions.NoInlining)]
 709    internal static void GetMaterials(in CurvedPrismContactGeometry geometry, in Selection selection,
 710        Vector3d center, FixedQuaternion rotation, Vector3d prismOrigin,
 711        out Vector3d normal, out FixedPointAnchor shapeAnchor, out FixedPointAnchor prismAnchor)
 712    {
 55713        Span<ulong> data = stackalloc ulong[Slots * Words];
 55714        Span<sbyte> signs = stackalloc sbyte[Slots];
 55715        BuildParameter(geometry, selection.Edge, selection.First, selection.Second, selection.CapSign, data, signs);
 55716        CylinderEdgeContactPolynomial.CompleteGap(data[..(26 * Words)], signs[..26]);
 55717        Span<ulong> cell = stackalloc ulong[WideFiniteAxisIntersection.GetFiniteValueRootCellWords(
 55718            data[..(5 * Words)], signs[..5])];
 55719        bool found = WideFiniteAxisIntersection.TryGetFiniteValueRoot(data[..(5 * Words)], signs[..5],
 55720            selection.ParameterOrdinal, cell, out FiniteAxisValueRoot root);
 721        System.Diagnostics.Debug.Assert(found);
 55722        Span<ulong> gradient = stackalloc ulong[54];
 55723        Span<sbyte> gradientSigns = stackalloc sbyte[6];
 55724        CircularRimContactAlgebra.WriteGradient(selection.First.Transform(geometry.WorldBasis),
 55725            selection.Second.Transform(geometry.WorldBasis), gradient, gradientSigns);
 55726        normal = ConvexContactValueRoot.GetNormalizedDirection(ref root, gradient, gradientSigns, 1);
 727
 55728        Span<ulong> points = stackalloc ulong[36 * Words];
 55729        Span<sbyte> pointSigns = stackalloc sbyte[36];
 110730        points.Clear(); pointSigns.Clear();
 55731        Polynomial storage = new(points, pointSigns, Words);
 110732        Polynomial shape = storage.Slice(0, 9), prism = storage.Slice(9, 18);
 110733        Polynomial shapeDenominator = storage.Slice(27, 3), prismDenominator = storage.Slice(30, 6);
 55734        BuildPointPolynomials(geometry, selection, data, signs, shape, prism, shapeDenominator, prismDenominator);
 55735        shapeAnchor = CreateRootAnchor(geometry, center, rotation, center, shape, shapeDenominator, ref root);
 55736        prismAnchor = CreateRootAnchor(geometry, center, rotation, prismOrigin, prism, prismDenominator, ref root);
 55737    }
 738
 739    [MethodImpl(MethodImplOptions.NoInlining)]
 740    private static void BuildPointPolynomials(in CurvedPrismContactGeometry geometry, in Selection selection,
 741        Span<ulong> data, Span<sbyte> signs, Polynomial shape, Polynomial prism,
 742        Polynomial shapeDenominator, Polynomial prismDenominator)
 743    {
 233744        Polynomial parameter = new(data, signs, Words);
 699745        Polynomial k = parameter.Slice(8, 2), l = parameter.Slice(10, 3), j = parameter.Slice(13, 3);
 466746        l.Values.CopyTo(shapeDenominator.Values); l.Signs.CopyTo(shapeDenominator.Signs);
 233747        Span<ulong> work = stackalloc ulong[25 * Words];
 233748        Span<sbyte> workSigns = stackalloc sbyte[24];
 466749        work.Clear(); workSigns.Clear();
 233750        Polynomial scratch = new(work[..(24 * Words)], workSigns, Words);
 699751        Polynomial q = scratch.Slice(0, 1), radiusSquared = scratch.Slice(1, 1), metric = scratch.Slice(2, 3);
 699752        Polynomial qk = scratch.Slice(5, 2), qkm = scratch.Slice(7, 4), n = scratch.Slice(11, 2);
 699753        Polynomial axial = scratch.Slice(13, 2), radial = scratch.Slice(15, 2), scalar = scratch.Slice(17, 1);
 233754        Polynomial term = scratch.Slice(18, 6);
 233755        Span<ulong> product = work.Slice(24 * Words, Words);
 233756        Span<ulong> separation = stackalloc ulong[6 * Words];
 233757        Span<sbyte> separationSigns = stackalloc sbyte[6];
 233758        Polynomial separationPolynomial = new(separation, separationSigns, Words);
 233759        Write(q, 0, Signed832.ExtendValue(geometry.SquaredAxis));
 233760        Write(radiusSquared, 0, Signed832.ExtendValue(WideArithmetic.MultiplySigned320(geometry.Radius, geometry.Radius)
 233761        Write(metric, 0, selection.First.SquaredLength);
 233762        Write(metric, 1, Twice(WideDirection3d.Dot(selection.First, selection.Second)));
 233763        Write(metric, 2, selection.Second.SquaredLength);
 233764        Write(axial, 0, WideDirection3d.Dot(selection.First, geometry.Axis));
 233765        Write(axial, 1, WideDirection3d.Dot(selection.Second, geometry.Axis));
 466766        Multiply(qk, q, k, product); Multiply(qkm, qk, metric, product);
 233767        Multiply(prismDenominator, l, qkm, product);
 233768        WideDirection3d half = selection.CapSign < 0 ? -geometry.Half : geometry.Half;
 1864769        for (int coordinate = 0; coordinate < 3; coordinate++)
 770        {
 699771            Write(n, 0, Signed832.ExtendValue(Component(selection.First, coordinate)));
 699772            Write(n, 1, Signed832.ExtendValue(Component(selection.Second, coordinate)));
 699773            Multiply(radial, q, n, product);
 699774            Write(scalar, 0, Signed832.ExtendValue(Component(geometry.Axis, coordinate)));
 1398775            Multiply(term.Slice(0, 2), axial, scalar, product); Add(radial, term.Slice(0, 2), -1);
 776            // sqrt(Q)=-L/K, so the exact rim point is
 777            // capH-R²*K*(q*n-(u.n)*u)/L, with no new radical.
 699778            Multiply(term.Slice(0, 3), k, radial, product);
 699779            Multiply(shape.Slice(3 * coordinate, 3), term.Slice(0, 3), radiusSquared, product);
 5592780            for (int coefficient = 0; coefficient < 3; coefficient++)
 2097781                shape.Signs[3 * coordinate + coefficient] *= -1;
 699782            Write(scalar, 0, Signed832.ExtendValue(Component(half, coordinate)));
 699783            Multiply(term.Slice(0, 3), scalar, l, product);
 699784            Add(shape.Slice(3 * coordinate, 3), term.Slice(0, 3), 1);
 785            // Subtract the exact normal gap J*n/(q*K*M) from that point.
 699786            Multiply(prism.Slice(6 * coordinate, 6), shape.Slice(3 * coordinate, 3), qkm, product);
 699787            Multiply(term.Slice(0, 4), j, n, product);
 699788            Multiply(separationPolynomial, term.Slice(0, 4), l, product);
 699789            Add(prism.Slice(6 * coordinate, 6), separationPolynomial, -1);
 790        }
 233791    }
 792
 793    private static FixedPointAnchor CreateRootAnchor(in CurvedPrismContactGeometry geometry,
 794        Vector3d center, FixedQuaternion rotation, Vector3d targetOrigin,
 795        scoped Polynomial point, scoped Polynomial denominator, scoped ref FiniteAxisValueRoot root)
 796    {
 110797        int count = denominator.Signs.Length;
 110798        Span<ulong> work = stackalloc ulong[(3 * count + 2) * Words];
 110799        Span<sbyte> workSigns = stackalloc sbyte[3 * count + 2];
 220800        work.Clear(); workSigns.Clear();
 110801        Polynomial storage = new(work, workSigns, Words);
 220802        Polynomial scaledDenominator = storage.Slice(0, count), finalDenominator = storage.Slice(count, count);
 220803        Polynomial numerator = storage.Slice(2 * count, count), scalar = storage.Slice(3 * count, 1);
 110804        Span<ulong> product = work.Slice((3 * count + 1) * Words, Words);
 110805        Write(scalar, 0, Signed832.ExtendValue(geometry.RawScale));
 110806        Multiply(scaledDenominator, denominator, scalar, product);
 110807        Write(scalar, 0, Signed832.ExtendValue(geometry.WorldBasis.Denominator));
 110808        Multiply(finalDenominator, scaledDenominator, scalar, product);
 110809        int denominatorSign = Sign(ref root, finalDenominator);
 810        System.Diagnostics.Debug.Assert(denominatorSign != 0);
 110811        WideOrientedBox.GetRelativeLocalPointNumerators(center, targetOrigin, geometry.WorldBasis,
 110812            out Signed192 dx, out Signed192 dy, out Signed192 dz);
 220813        Vector3d first = default, second = default;
 880814        for (int coordinate = 0; coordinate < 3; coordinate++)
 815        {
 816            // Convert P/(S*den) into the target origin's exact primitive-local
 817            // frame: (D*P+delta*den*S)/(D*den*S). Coordinate queries, including
 818            // full raw-domain midpoint products, remain below 2520 bits.
 330819            Write(scalar, 0, Signed832.ExtendValue(geometry.WorldBasis.Denominator));
 330820            Multiply(numerator, point.Slice(coordinate * count, count), scalar, product);
 330821            Write(scalar, 0, Signed832.ExtendValue(coordinate == 0 ? dx : coordinate == 1 ? dy : dz));
 330822            AddProduct(numerator, scaledDenominator, scalar, 1, product);
 330823            long leading = CompareRatio(numerator, finalDenominator, Signed192.Signed(long.MaxValue), denominatorSign, r
 330824                ? long.MaxValue
 330825                : CompareRatio(numerator, finalDenominator, Signed192.Signed(long.MinValue), denominatorSign, ref root) 
 330826                    ? long.MinValue : 0;
 330827            if (leading != 0)
 828            {
 2829                Write(scalar, 0, Signed832.ExtendValue(Signed192.Signed(leading)));
 2830                AddProduct(numerator, finalDenominator, scalar, -1, product);
 831            }
 330832            Fixed64 rounded = RoundRatio(numerator, finalDenominator, denominatorSign, leading, ref root);
 330833            first[coordinate] = leading == 0 ? rounded : Fixed64.FromRaw(leading);
 330834            second[coordinate] = leading == 0 ? Fixed64.Zero : rounded;
 835        }
 836        // Primitive support coordinates are below 3/2 Max; a prism local
 837        // coordinate is below sqrt(3) Max. Two additive terms preserve both.
 110838        return new FixedPointAnchor(targetOrigin, rotation, first, second);
 839    }
 840
 841    private static Fixed64 RoundRatio(scoped Polynomial numerator, scoped Polynomial denominator, int denominatorSign,
 842        long leading, scoped ref FiniteAxisValueRoot root)
 843    {
 660844        long low = long.MinValue, high = long.MaxValue;
 21450845        while (low < high)
 846        {
 21120847            ulong span = unchecked((ulong)high - (ulong)low);
 21120848            long middle = unchecked((long)((ulong)low + (span >> 1) + (span & 1)));
 30086849            if (CompareRatio(numerator, denominator, Signed192.Signed(middle), denominatorSign, ref root) >= 0) low = mi
 12154850            else high = middle - 1;
 851        }
 330852        Signed192 half = WideArithmetic.AddSigned192(
 330853            WideArithmetic.AddSigned192(Signed192.Signed(low), Signed192.Signed(low)), Signed192.Signed(1));
 330854        int comparison = CompareRatio(numerator, denominator, half, denominatorSign, ref root, twiceNumerator: true);
 855        // Ties belong to the complete coordinate, including an odd split term.
 330856        return Fixed64.FromRaw(low + (comparison > 0 || comparison == 0 && ((low ^ leading) & 1) != 0 ? 1 : 0));
 857    }
 858
 859    private static int CompareRatio(scoped Polynomial numerator, scoped Polynomial denominator, Signed192 threshold,
 860        int denominatorSign, scoped ref FiniteAxisValueRoot root, bool twiceNumerator = false)
 861    {
 22109862        int count = denominator.Signs.Length;
 22109863        Span<ulong> data = stackalloc ulong[(count + 2) * Words];
 22109864        Span<sbyte> signs = stackalloc sbyte[count + 1];
 44218865        data.Clear(); signs.Clear();
 22109866        Polynomial storage = new(data[..((count + 1) * Words)], signs, Words);
 44218867        Polynomial query = storage.Slice(0, count), scalar = storage.Slice(count, 1);
 22109868        Span<ulong> product = data.Slice((count + 1) * Words, Words);
 22109869        Write(scalar, 0, Signed832.ExtendValue(threshold));
 22109870        Multiply(query, denominator, scalar, product);
 342685871        for (int index = 0; index < count; index++) query.Signs[index] *= -1;
 22109872        Add(query, numerator, twiceNumerator ? 2 : 1);
 22109873        return Sign(ref root, query) * denominatorSign;
 874    }
 875
 876    private static void AddProduct(Polynomial target, Polynomial first, Polynomial second, int multiplier,
 877        Span<ulong> product)
 878    {
 2137879        Span<ulong> values = stackalloc ulong[target.Values.Length];
 2137880        Span<sbyte> signs = stackalloc sbyte[target.Signs.Length];
 2137881        Polynomial term = new(values, signs, target.Words);
 4274882        Multiply(term, first, second, product); Add(target, term, multiplier);
 2137883    }
 884
 885    private static FiniteAxisValueRoot RestoreValue(ReadOnlySpan<ulong> values,
 93886        ReadOnlySpan<sbyte> signs, Span<ulong> cell, in Selection selection) => new()
 93887        {
 93888            Coefficients = values,
 93889            Signs = signs,
 93890            LowerNumerator = cell,
 93891            DenominatorShift = selection.ValueShift,
 93892            Ordinal = selection.ValueOrdinal,
 93893            IsRational = selection.IsRational
 93894        };
 895
 896    private static WideDirection3d GetOffset(in CurvedPrismContactGeometry geometry, int edge, int cap)
 897    {
 1036898        geometry.Edge(edge, out int start, out _);
 1036899        return CurvedPrismContact.Difference(cap < 0 ? -geometry.Half : geometry.Half, geometry.Vertex(start));
 900    }
 1168901    private static Signed832 Twice(Signed832 value) => WideArithmetic.AddSigned832(value, value);
 902    private static Signed576 Component(in WideDirection3d value, int coordinate) =>
 4264903        coordinate == 0 ? value.X : coordinate == 1 ? value.Y : value.Z;
 904    private static Signed320 Component(in WideAxis3 value, int coordinate) =>
 849905        coordinate == 0 ? value.X : coordinate == 1 ? value.Y : value.Z;
 906    private static int Sign(scoped ref FiniteAxisValueRoot root, scoped Polynomial polynomial) =>
 26225907        WideFiniteAxisIntersection.GetSignAtFiniteValueRootAndRefine(ref root, polynomial.Values, polynomial.Signs);
 908    private static void Write(Polynomial polynomial, int index, Signed832 value)
 909    {
 42510910        Span<ulong> target = polynomial.Values.Slice(index * polynomial.Words, polynomial.Words);
 127530911        target.Clear(); WideArithmetic.GetMagnitude(value, target[..13]); polynomial.Signs[index] = (sbyte)value.Sign;
 42510912    }
 913    private static void Multiply(Polynomial result, Polynomial first, Polynomial second, Span<ulong> product) =>
 37894914        WideFiniteAxisIntersection.MultiplyFiniteAxisPolynomials(first.Values, first.Signs,
 37894915            second.Values, second.Signs, result.Values, result.Signs, product);
 916    private static void Add(Polynomial result, Polynomial source, int multiplier) =>
 28879917        WideFiniteAxisIntersection.AddFiniteAxisPolynomial(source.Values, source.Signs,
 28879918            result.Values, result.Signs, multiplier);
 919    private readonly ref struct Polynomial
 920    {
 921        internal readonly Span<ulong> Values;
 922        internal readonly Span<sbyte> Signs;
 923        internal readonly int Words;
 924        internal Polynomial(Span<ulong> values, Span<sbyte> signs, int words)
 442012925        { Values = values; Signs = signs; Words = words; }
 926        internal Polynomial Slice(int offset, int count) =>
 81209927            new(Values.Slice(offset * Words, count * Words), Signs.Slice(offset, count), Words);
 928    }
 929}

Methods/Properties

GetContact(FixedMathSharp.Geometry.CurvedPrismContactGeometry&,FixedMathSharp.Geometry.ConvexContactCandidate,FixedMathSharp.Fixed64,System.Boolean,FixedMathSharp.Geometry.CurvedPrismRimContacts/Selection&,FixedMathSharp.Fixed64&,System.Boolean&,FixedMathSharp.Geometry.PrismCurvedContactDomain,System.Boolean)
CompareSquaredDepth(FixedMathSharp.Geometry.CurvedPrismContactGeometry&,FixedMathSharp.Geometry.CurvedPrismRimContacts/Selection&,FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,System.ReadOnlySpan`1<System.UInt64>)
KeepChart(FixedMathSharp.Geometry.CurvedPrismContactGeometry&,System.Int32,FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.WideDirection3d&,System.Int32,FixedMathSharp.Geometry.ConvexContactCandidate,System.UInt64,System.Boolean,System.Boolean,System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Span`1<System.UInt64>,FixedMathSharp.Geometry.CurvedPrismRimContacts/Selection&,FixedMathSharp.Geometry.PrismCurvedContactDomain,System.Boolean)
HasNoNewStationaryMinimum(FixedMathSharp.Geometry.CurvedPrismRimContacts/Polynomial,FixedMathSharp.Geometry.CurvedPrismRimContacts/Polynomial)
TryAdmitDomain(FixedMathSharp.Geometry.CurvedPrismContactGeometry&,FixedMathSharp.Geometry.PrismCurvedContactDomain,FixedMathSharp.Geometry.CurvedPrismRimContacts/Selection&,System.Span`1<System.UInt64>,System.Span`1<System.SByte>,FixedMathSharp.Geometry.FiniteAxisValueRoot&,System.Boolean&)
NormalDotSign(FixedMathSharp.Geometry.CurvedPrismRimContacts/Selection&,FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.FiniteAxisValueRoot&)
PointInEdgeWindow(FixedMathSharp.Geometry.CurvedPrismContactGeometry&,FixedMathSharp.Geometry.PrismCurvedEdgeWindow&,FixedMathSharp.Geometry.CurvedPrismRimContacts/Polynomial,FixedMathSharp.Geometry.CurvedPrismRimContacts/Polynomial,FixedMathSharp.Geometry.FiniteAxisValueRoot&)
IsPhysicalPoint(FixedMathSharp.Geometry.CurvedPrismContactGeometry&,FixedMathSharp.Geometry.CurvedPrismRimContacts/Polynomial,FixedMathSharp.Geometry.CurvedPrismRimContacts/Polynomial,FixedMathSharp.Geometry.FiniteAxisValueRoot&)
GetLegacyOrder(FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.WideDirection3d&,System.Int32,FixedMathSharp.Geometry.FiniteAxisValueRoot&)
HasNoNonnegativeEllipseMinimum(FixedMathSharp.Geometry.CurvedPrismContactGeometry&,FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.WideDirection3d&)
IsPhysicalArcConcave(FixedMathSharp.Geometry.CurvedPrismContactGeometry&,FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.WideDirection3d&,System.Int32,System.Int32)
IsChartDominated(FixedMathSharp.Geometry.CurvedPrismContactGeometry&,FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.ConvexContactCandidate,System.Boolean)
HasPrismSupport(FixedMathSharp.Geometry.CurvedPrismContactGeometry&,FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.FiniteAxisValueRoot&)
BuildParameter(FixedMathSharp.Geometry.CurvedPrismContactGeometry&,System.Int32,FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.WideDirection3d&,System.Int32,System.Span`1<System.UInt64>,System.Span`1<System.SByte>)
BuildValues(FixedMathSharp.Geometry.CurvedPrismContactGeometry&,System.Int32,System.Int32,System.Span`1<System.UInt64>,System.Span`1<System.SByte>)
GetMaterials(FixedMathSharp.Geometry.CurvedPrismContactGeometry&,FixedMathSharp.Geometry.CurvedPrismRimContacts/Selection&,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d&,FixedMathSharp.Geometry.FixedPointAnchor&,FixedMathSharp.Geometry.FixedPointAnchor&)
BuildPointPolynomials(FixedMathSharp.Geometry.CurvedPrismContactGeometry&,FixedMathSharp.Geometry.CurvedPrismRimContacts/Selection&,System.Span`1<System.UInt64>,System.Span`1<System.SByte>,FixedMathSharp.Geometry.CurvedPrismRimContacts/Polynomial,FixedMathSharp.Geometry.CurvedPrismRimContacts/Polynomial,FixedMathSharp.Geometry.CurvedPrismRimContacts/Polynomial,FixedMathSharp.Geometry.CurvedPrismRimContacts/Polynomial)
CreateRootAnchor(FixedMathSharp.Geometry.CurvedPrismContactGeometry&,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.CurvedPrismRimContacts/Polynomial,FixedMathSharp.Geometry.CurvedPrismRimContacts/Polynomial,FixedMathSharp.Geometry.FiniteAxisValueRoot&)
RoundRatio(FixedMathSharp.Geometry.CurvedPrismRimContacts/Polynomial,FixedMathSharp.Geometry.CurvedPrismRimContacts/Polynomial,System.Int32,System.Int64,FixedMathSharp.Geometry.FiniteAxisValueRoot&)
CompareRatio(FixedMathSharp.Geometry.CurvedPrismRimContacts/Polynomial,FixedMathSharp.Geometry.CurvedPrismRimContacts/Polynomial,FixedMathSharp.Signed192,System.Int32,FixedMathSharp.Geometry.FiniteAxisValueRoot&,System.Boolean)
AddProduct(FixedMathSharp.Geometry.CurvedPrismRimContacts/Polynomial,FixedMathSharp.Geometry.CurvedPrismRimContacts/Polynomial,FixedMathSharp.Geometry.CurvedPrismRimContacts/Polynomial,System.Int32,System.Span`1<System.UInt64>)
RestoreValue(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Span`1<System.UInt64>,FixedMathSharp.Geometry.CurvedPrismRimContacts/Selection&)
GetOffset(FixedMathSharp.Geometry.CurvedPrismContactGeometry&,System.Int32,System.Int32)
Twice(FixedMathSharp.Signed832)
Component(FixedMathSharp.Geometry.WideDirection3d&,System.Int32)
Component(FixedMathSharp.Geometry.WideAxis3&,System.Int32)
Sign(FixedMathSharp.Geometry.FiniteAxisValueRoot&,FixedMathSharp.Geometry.CurvedPrismRimContacts/Polynomial)
Write(FixedMathSharp.Geometry.CurvedPrismRimContacts/Polynomial,System.Int32,FixedMathSharp.Signed832)
Multiply(FixedMathSharp.Geometry.CurvedPrismRimContacts/Polynomial,FixedMathSharp.Geometry.CurvedPrismRimContacts/Polynomial,FixedMathSharp.Geometry.CurvedPrismRimContacts/Polynomial,System.Span`1<System.UInt64>)
Add(FixedMathSharp.Geometry.CurvedPrismRimContacts/Polynomial,FixedMathSharp.Geometry.CurvedPrismRimContacts/Polynomial,System.Int32)
.ctor(System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Int32)
Slice(System.Int32,System.Int32)