< Summary

Information
Class: FixedMathSharp.Geometry.WideFiniteAxisIntersection
Assembly: FixedMathSharp
File(s): File 1: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredAxes.cs
File 2: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredAxes.Solver.cs
File 3: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredAxisTriangle.cs
File 4: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredAxisTriangle.Materialization.cs
File 5: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredCapsule.cs
File 6: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredCapsuleAnchor.cs
File 7: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredCapsuleDistance.cs
File 8: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredCapsuleStrictOverlap.cs
File 9: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredCapsuleSweep2d.cs
File 10: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredCapsuleTriangleRay.cs
File 11: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredConeSurfaceAnchors.cs
File 12: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredCylinderPairsStrictOverlap.cs
File 13: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredSphereStrictOverlap.cs
File 14: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredSurfaceAnchors.cs
File 15: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredSurfaces.cs
File 16: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredSurfaceTriangle.cs
File 17: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.ClippedAxisSegment.cs
File 18: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.Containment.cs
File 19: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.cs
File 20: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.Distance.cs
File 21: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.DistanceSolver.cs
File 22: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.FiniteDiskStrictOverlap.cs
File 23: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.NarrowSolver.cs
File 24: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.PolynomialArithmetic.cs
File 25: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.PolynomialRoot.cs
File 26: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.PolynomialRootSigns.cs
File 27: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.Ray.cs
File 28: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.RigidPointRelation.cs
File 29: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.RootSolver.cs
File 30: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.RoundedBox.cs
File 31: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.RoundedCylinder.cs
File 32: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.RoundedCylinderInteger.cs
File 33: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.RoundedCylinderRootSolver.cs
File 34: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.ScaledAxisTriangle.cs
File 35: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.ScaledAxisTriangleCandidate.cs
File 36: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.StrictCirclePolynomial.cs
File 37: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.StrictPolynomial.cs
File 38: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.StrictQuadraticField.cs
File 39: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.ValueRoot.cs
File 40: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.ValueRootBernstein.cs
File 41: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.ValueRootFactors.cs
File 42: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.ValueRootRadicalOffset.cs
File 43: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.ValueRootReciprocal.cs
File 44: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.ValueRootRefinement.cs
File 45: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.ValueRootSign.cs
File 46: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.Witness.cs
Line coverage
100%
Covered lines: 11465
Uncovered lines: 0
Coverable lines: 11465
Total lines: 18820
Line coverage: 100%
Branch coverage
100%
Covered branches: 2678
Total branches: 2678
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
File 1: .ctor(...)100%11100%
File 1: .ctor(...)100%11100%
File 1: TryGetDistanceToCenteredAxis(...)100%11100%
File 1: TryGetDistanceToCenteredAxis(...)100%11100%
File 1: TryGetDistanceBetweenCenteredAxes(...)100%11100%
File 1: TryGetDistanceBetweenCenteredAxes(...)100%11100%
File 1: DoCenteredCapsulesOverlap(...)100%11100%
File 1: DoCenteredCapsulesOverlap(...)100%11100%
File 1: TryGetCenteredCapsulesContact(...)100%66100%
File 1: TryGetCenteredCapsulesContact(...)100%44100%
File 1: TryGetClosestPointsBetweenCenteredAxes(...)100%22100%
File 1: TryGetClosestPointsBetweenCenteredAxes(...)100%22100%
File 1: TryGetClosestOffsetsBetweenCenteredAxes(...)100%11100%
File 1: GetClosestDirectionBetweenCenteredAxes(...)100%11100%
File 2: GetClosestCenteredAxisParameters(...)100%1212100%
File 2: GetCenteredAxesSquaredDistance(...)100%11100%
File 2: GetCenteredAxesCandidate(...)100%44100%
File 2: GetCenteredAxesSquaredDistance(...)100%11100%
File 2: GetCenteredAxesCandidate(...)100%44100%
File 2: IsWithinCombinedRadius(...)100%22100%
File 2: ProjectSecondFromCenteredAxisCap(...)100%11100%
File 2: ProjectFirstFromCenteredAxisCap(...)100%11100%
File 2: ClampCenteredAxisParameter(...)100%44100%
File 2: SetCenteredAxisCap(...)100%22100%
File 2: TryRoundCenteredAxesDistance(...)100%44100%
File 2: GetHalfToEvenIncrement(...)100%11100%
File 2: TryGetCenteredAxisPointCoordinate(...)100%22100%
File 2: GetCenteredAxisPointNumerator(...)100%11100%
File 2: GetCenteredAxisLocalOffset(...)100%44100%
File 2: GetLocalRadialFeature(...)100%11100%
File 2: GetCenteredCapsulesDepth(...)100%44100%
File 2: GetCenteredRadiusDepth(...)100%88100%
File 2: CompareSquaredDistanceToCenteredDepthThreshold(...)100%22100%
File 3: .ctor(...)100%11100%
File 3: TryGetClosestPointsToCenteredAxis(...)100%11100%
File 3: DoesCenteredCapsuleTriangleOverlap(...)100%11100%
File 3: TryGetCenteredCapsuleTriangleContact(...)100%1616100%
File 3: TryGetCenteredCapsuleTriangleLocalContact(...)100%2828100%
File 3: GetClosestCenteredAxisTriangleCandidate(...)100%1212100%
File 3: GetBarycentricCoordinateNumerator(...)100%11100%
File 3: TryGetCenteredAxisTriangleFaceCandidate(...)100%1212100%
File 3: TryCreateCenteredAxisTriangleFaceCandidate(...)100%44100%
File 3: KeepCenteredAxisTriangleEdgeCandidate(...)100%1010100%
File 3: GetClosestCenteredAxisSegmentParameters(...)100%2424100%
File 3: ClampUnitParameter(...)100%44100%
File 3: ProjectEdgeFromCenteredCap(...)100%11100%
File 3: ProjectAxisFromEdgeCap(...)100%11100%
File 3: SetUnitCapAtCenteredDenominator(...)100%22100%
File 3: GetRationalPoint(...)100%11100%
File 3: GetRationalSegmentPoint(...)100%11100%
File 3: GetRationalCoordinate(...)100%11100%
File 3: CreateAxisTriangleCandidate(...)100%11100%
File 3: GetCenteredCapsuleTriangleDepth(...)100%11100%
File 4: ContainsTriangleProjection(...)100%1010100%
File 4: GetTriangleEdgeSideSign(...)100%11100%
File 4: SubtractVertex(...)100%11100%
File 4: SubtractVertex(...)100%11100%
File 4: GetWideDot(...)100%11100%
File 4: GetTriangleNormal(...)100%11100%
File 4: TryMaterialize(...)100%22100%
File 4: TryOffsetPoint(...)100%22100%
File 4: TryGetRelativeCoordinate(...)100%11100%
File 5: TryGetSurfacePointOnCenteredCapsule(...)100%44100%
File 5: TryGetSurfaceOffsetOnCenteredCapsule(...)100%22100%
File 5: TryGetSurfaceOffsetOnCenteredCapsule(...)100%22100%
File 5: TryGetSurfacePointOnCenteredCapsule(...)100%66100%
File 5: ContainsPointInCenteredCapsule(...)100%11100%
File 5: ContainsPointInCenteredCapsule(...)100%11100%
File 5: GetDirectionFromCenteredAxis(...)100%22100%
File 5: GetDirectionFromCenteredAxis(...)100%22100%
File 5: TryGetCapsuleInterval(...)100%22100%
File 5: TryGetCapsuleInterval(...)100%22100%
File 5: IsCenteredCapsulePointContained(...)100%66100%
File 5: IsCenteredCapsulePointContained(...)100%66100%
File 5: GetCenteredCap(...)100%22100%
File 5: MergeCenteredCapInterval(...)100%11100%
File 5: MergeCenteredCapInterval(...)100%11100%
File 5: GetCenteredCapPolynomial(...)100%11100%
File 5: GetCenteredCapPolynomial(...)100%11100%
File 5: GetCenteredCapConstant(...)100%11100%
File 5: GetCenteredCapConstant(...)100%11100%
File 5: GetCenteredCapOffset(...)100%22100%
File 5: GetRadialComponent(...)100%11100%
File 5: GetClosestCenteredAxisRatio(...)100%44100%
File 5: GetClosestCenteredAxisRatio(...)100%44100%
File 5: TryGetCenteredCapsuleSurfaceCoordinate(...)100%11100%
File 5: MultiplyThreeToSigned320(...)100%11100%
File 5: GetCenteredAxisOffsetComponent(...)100%11100%
File 5: GetComponentDifference(...)100%11100%
File 5: GetScaledDifference(...)100%11100%
File 5: ScaleByCenteredAxis(...)100%11100%
File 5: TrySolveUnitQuadratic(...)100%11100%
File 6: GetPointToCenteredCapsuleCoreDirection(...)100%44100%
File 6: GetSurfaceAnchorOnCenteredCapsule(...)100%11100%
File 6: GetSurfaceAnchorOnCenteredCapsule(...)100%11100%
File 6: GetSurfaceAnchorOnCenteredCapsule(...)100%11100%
File 7: TryGetDistanceToCenteredCapsule(...)100%11100%
File 7: TryGetDistanceToCenteredCapsule(...)100%11100%
File 7: TryGetDistanceToCenteredCapsule(...)100%1010100%
File 8: .ctor(...)100%11100%
File 8: DoesCenteredFiniteCylinderPenetrateCapsule(...)100%1212100%
File 8: DoesCenteredFiniteConePenetrateCapsule(...)100%1212100%
File 8: CreateStrictFiniteChord(...)100%11100%
File 8: GetStrictChordCoordinate(...)100%11100%
File 8: GetStrictRadialPolynomial(...)100%11100%
File 8: DoesStrictChordEnterCylinder(...)100%22100%
File 8: GetStrictPointDistancePolynomial(...)100%11100%
File 8: DoesStrictChordEnterExpandedDisk(...)100%1414100%
File 8: HasNegativeStrictQuadratic(...)100%11100%
File 8: AccumulateStrictRimProduct(...)100%44100%
File 8: DoesStrictChordEnterOffsetConeSide(...)100%44100%
File 9: .ctor(...)100%11100%
File 9: TryGetSweptCenteredCapsuleSegmentFirstDistance(...)100%11100%
File 9: TryGetSweptCenteredCapsuleOriginOffsetSegmentFirstDistance(...)100%11100%
File 9: TryGetSweptOriginOffsetSegmentsFirstDistance(...)100%11100%
File 9: TryGetSweptCenteredCapsulesFirstDistance(...)100%11100%
File 9: TryGetSweptAxesFirstDistance(...)100%88100%
File 9: TryKeepEndpointAxisSweep(...)100%1010100%
File 9: GetAxisSweepFallbackNormal(...)100%44100%
File 9: TryKeepCircleSweep(...)100%11100%
File 9: TryKeepSideSweep(...)100%1010100%
File 9: IsWithinAxisAtDistance(...)100%22100%
File 9: KeepFirstSweepRoot(...)100%66100%
File 9: GetEndpointAxisNormal(...)100%44100%
File 9: GetPointAtDistance(...)100%11100%
File 9: AxesIntersect(...)100%1818100%
File 9: Orientation(...)100%11100%
File 9: IsWithinBounds(...)100%22100%
File 9: IsBetween(...)100%44100%
File 9: GetSweepVelocity(...)100%22100%
File 9: ScaleSweepCoordinate(...)100%11100%
File 9: GetCenteredAxisEndpoints(...)100%11100%
File 9: GetAuthoredAxisEndpoints(...)100%11100%
File 9: GetRotatedOriginOffsetAxisEndpoints(...)100%11100%
File 9: GetRotatedOriginOffsetCoordinate(...)100%11100%
File 9: GetCenteredEndpointNumerator(...)100%22100%
File 9: ScaleAuthoredCoordinate(...)100%11100%
File 10: RayExtend(...)100%11100%
File 10: RayExtend(...)100%11100%
File 10: RayField(...)100%11100%
File 10: GetRetainedRayDot(...)100%11100%
File 10: GetCoincidentCapsuleRayExit(...)100%11100%
File 10: GetCoincidentCapsuleRayExit(...)100%2424100%
File 10: GetCapsuleRayDepth(...)100%22100%
File 10: TryMaterializeCapsuleRayPoint(...)100%66100%
File 10: MaterializeCapsuleRayComponents(...)100%1818100%
File 11: TryGetClosestCenteredFiniteConeSurfaceAnchor(...)100%22100%
File 11: GetClosestCenteredFiniteConeSurfaceAnchor(...)100%22100%
File 11: GetRigidCenteredConeMeridian(...)100%22100%
File 11: GetClosestCenteredConeSideCoordinates(...)100%44100%
File 11: GetCenteredConeSideApproximation(...)100%22100%
File 11: RoundBoundedCenteredConeCoordinate(...)100%1010100%
File 11: CompareCenteredConeCoordinateToRaw(...)100%11100%
File 11: GetCenteredConeHeightRelation(...)100%11100%
File 11: IsPointContainedInRigidCenteredCone(...)100%22100%
File 11: IsCenteredConeSideNearest(...)100%44100%
File 11: GetCenteredConeSideParameterNumerator(...)100%11100%
File 11: CompareCenteredConeSideParameterToEndpoint(...)100%22100%
File 11: CompareCenteredConeApexToBaseDistance(...)100%44100%
File 11: CompareCenteredConeInteriorSideToBaseDistance(...)100%44100%
File 12: DoesCenteredFiniteCylindersPenetrate(...)100%88100%
File 12: DoStrictCylinderLateralInteriorsMeet(...)100%88100%
File 12: IsStrictCylinderAxialParameterInterior(...)100%22100%
File 12: DoesCenteredFiniteCylinderPenetrateCone(...)100%1010100%
File 12: DoStrictCylinderConeLateralInteriorsMeet(...)100%2020100%
File 12: IsStrictCylinderConeFieldParameterInterior(...)100%22100%
File 12: GetStrictCylinderConeFieldSign(...)100%11100%
File 12: IsStrictConeGeneratorBelowBase(...)100%11100%
File 12: IsStrictConeGeneratorDistanceLess(...)100%11100%
File 12: DoesStrictParallelConeGeneratorEnterCylinder(...)100%88100%
File 13: DoesCenteredFiniteCylinderPenetrateSphere(...)100%1010100%
File 13: DoesCenteredFiniteConePenetrateSphere(...)100%1616100%
File 13: GetStrictSphereLocalPoint(...)100%11100%
File 13: IsStrictSphereDiskDistanceLess(...)100%22100%
File 13: TwiceStrictSphereValue(...)100%11100%
File 13: CompareStrictSphereRadical(...)100%1212100%
File 14: TryGetClosestCenteredCapsuleSurfaceAnchor(...)100%22100%
File 14: GetClosestCenteredCapsuleSurfaceAnchor(...)100%11100%
File 14: GetLocalCapsuleSurfaceDirection(...)100%22100%
File 14: GetLocalCapsuleCapDelta(...)100%11100%
File 14: TryGetClosestCenteredFiniteCylinderSurfaceAnchor(...)100%22100%
File 14: GetClosestCenteredFiniteCylinderSurfaceAnchor(...)100%2222100%
File 14: TryGetSignedSurfaceDistance(...)100%66100%
File 14: CreateCenteredCylinderSideAnchor(...)100%44100%
File 14: CreateCenteredCapsuleAnchors(...)100%44100%
File 14: GetClosestCenteredRigidAxisDistance(...)100%44100%
File 14: CreateCenteredSurfaceAnchor(...)100%11100%
File 14: TryGetCenteredRadialDistance(...)100%66100%
File 14: GetNonNegativeComparisonIncrement(...)100%11100%
File 14: GetLocalRadialDirection(...)100%22100%
File 14: GetProjection(...)100%11100%
File 14: GetCenteredCap(...)100%22100%
File 14: GetCenteredRadialBound(...)100%11100%
File 14: GetLocalRadialComponents(...)100%11100%
File 14: GetLocalRadialComponent(...)100%11100%
File 14: CompareSigned576(...)100%11100%
File 14: CompareMagnitudeSigned576(...)100%22100%
File 15: DoesCenteredFiniteConeOverlapSphere(...)100%44100%
File 15: TryGetClosestPointOnCenteredFiniteConeSurface(...)100%44100%
File 15: TryGetClosestCenteredFiniteConeSurfaceOffset(...)100%44100%
File 15: TryGetClosestPointOnCenteredFiniteCylinderSurface(...)100%44100%
File 15: TryGetClosestCenteredFiniteCylinderSurfaceOffset(...)100%44100%
File 15: GetCenteredConeSideNormal(...)100%11100%
File 16: TryGetTriangleSupportPointContact(...)100%11100%
File 16: TryGetTriangleSupportContactOffsets(...)100%1010100%
File 16: GetSupportDifference(...)100%11100%
File 16: GetProjectedCoordinateNumerator(...)100%11100%
File 16: GetAbsoluteCoordinateNumerator(...)100%11100%
File 16: TryGetTriangleOriginOffsetCoordinate(...)100%11100%
File 17: GetClosestClippedAxisSegment(...)100%2020100%
File 17: Multiply(...)100%11100%
File 17: Coordinate(...)100%11100%
File 17: SelectInteriorParameter(...)100%44100%
File 18: AreSegmentEndpointDistancesAtLeast(...)100%1010100%
File 18: IsPointSegmentDistanceAtLeast(...)100%11100%
File 18: ContainsPointInCenteredFiniteCylinder(...)100%11100%
File 18: ContainsPointInCenteredFiniteCylinder(...)100%11100%
File 18: IsCapsulePointContained(...)100%66100%
File 18: IsFiniteCylinderPointContained(...)100%1010100%
File 18: IsCenteredFiniteCylinderPointContained(...)100%1010100%
File 18: IsWithinRadius(...)100%22100%
File 18: GetRadialConstant(...)100%11100%
File 19: .cctor()100%11100%
File 19: .ctor(...)100%11100%
File 19: .ctor(...)100%11100%
File 19: TryGetCapsuleInterval(...)100%44100%
File 19: TryGetCapsuleInterval(...)100%22100%
File 19: TryGetFiniteCylinderInterval(...)100%11100%
File 19: TryGetFiniteCylinderInterval(...)100%22100%
File 19: TryGetFiniteAxisInterval(...)100%11100%
File 19: TryGetFiniteAxisInterval(...)100%3232100%
File 19: TryGetAxialInterval(...)100%1414100%
File 19: TryGetCenteredAxialInterval(...)100%1414100%
File 19: GetCenteredAxialExtent(...)100%11100%
File 19: GetCenteredAxialExtent(...)100%11100%
File 19: Normalize(...)100%22100%
File 19: Normalize(...)100%22100%
File 19: Compare(...)100%11100%
File 19: Compare(...)100%11100%
File 19: GetMaximumBound(...)100%11100%
File 19: GetMaximumBound320(...)100%11100%
File 19: Round(...)100%11100%
File 19: Round(...)100%11100%
File 19: TryGetCircleInterval(...)100%11100%
File 19: TryGetSphereInterval(...)100%11100%
File 19: GetExpandedRadius(...)100%11100%
File 19: GetSquaredRadius(...)100%11100%
File 19: GetDot(...)100%11100%
File 19: GetDot(...)100%11100%
File 19: Merge(...)100%88100%
File 19: TrySolveBoundedQuadratic(...)100%3030100%
File 20: TryGetCircleDirectionDistanceInterval(...)100%11100%
File 20: TryGetSphereDirectionDistanceInterval(...)100%11100%
File 20: TryGetCircleDistanceInterval(...)100%11100%
File 20: TryGetSphereDistanceInterval(...)100%11100%
File 20: TryGetCapsuleDistanceInterval(...)100%22100%
File 20: TryGetCapsuleDistanceInterval(...)100%22100%
File 20: TryGetCapsuleDistanceInterval(...)100%22100%
File 20: TryGetCapsuleDistanceInterval(...)100%22100%
File 20: TryGetFiniteCylinderDistanceInterval(...)100%11100%
File 20: TryGetFiniteCylinderDistanceInterval(...)100%11100%
File 20: TryGetFiniteCylinderDistanceInterval(...)100%22100%
File 20: TryGetFiniteAxisDistanceInterval(...)100%22100%
File 20: TryGetFiniteAxisDistanceInterval(...)100%11100%
File 20: TryGetCircleDistanceInterval(...)100%11100%
File 20: TryGetSphereDistanceInterval(...)100%11100%
File 20: MergeCenteredCapDistanceInterval(...)100%11100%
File 20: MergeCenteredCapDistanceInterval(...)100%11100%
File 21: TrySolveUnitQuadraticAtDistance(...)100%11100%
File 21: TrySolveBoundedQuadraticAtDistance(...)100%2424100%
File 21: RoundLowerRootAtDistance(...)100%66100%
File 21: RoundUpperRootAtDistance(...)100%66100%
File 21: RoundAtDistance(...)100%11100%
File 21: SquareRaw(...)100%11100%
File 22: DoesFiniteCapDiskEnterSolid(...)100%1414100%
File 22: CapCoordinate(...)100%11100%
File 22: DoesStrictDiskPointEnterSolid(...)100%66100%
File 22: BuildStrictDiskCirclePolynomials(...)100%2020100%
File 22: IsStrictSolidSectionWitnessInDisk(...)100%1212100%
File 22: TryGetStrictDiskQuadraticWitness(...)100%1818100%
File 22: IsStrictDiskRationalWitnessContained(...)100%22100%
File 22: StrictDiskRawSquare(...)100%11100%
File 22: TwiceStrictDiskValue(...)100%11100%
File 22: AbsoluteStrictDiskValue(...)100%22100%
File 23: TryGetNarrowRadialCoefficients(...)100%44100%
File 23: GetMagnitudeBitLength(...)100%22100%
File 23: GetTrailingZeroCount(...)100%66100%
File 23: CountTrailingZeroes(...)100%11100%
File 23: ShiftRightExact(...)100%11100%
File 23: GetShiftedWord(...)100%22100%
File 23: GetSignedWord(...)100%88100%
File 24: AddFiniteAxisPolynomial(...)100%1212100%
File 24: MultiplyFiniteAxisPolynomials(...)100%1010100%
File 24: ScaleFiniteAxisPolynomialVariable(...)100%22100%
File 24: NormalizeFiniteAxisPolynomialPowerOfTwo(...)100%1212100%
File 24: NormalizeQuadraticPointPowerOfTwo(...)100%3434100%
File 24: DifferentiateFiniteAxisPolynomial(...)100%44100%
File 25: GetFiniteAxisRootCellWords(...)100%11100%
File 25: TryGetLargestPositiveFiniteAxisRoot(...)100%2828100%
File 25: GetSignAtFiniteAxisRoot(...)100%1616100%
File 25: GetReducedSignAtFiniteAxisRoot(...)100%2828100%
File 25: EvaluateFiniteRootAtPositiveFraction(...)100%22100%
File 25: RefineFiniteCrossingRoot(...)100%11100%
File 25: RefineFiniteCrossingRoot(...)100%88100%
File 25: ReduceFiniteRootQuery(...)100%88100%
File 25: ReduceFiniteRootQueryStep(...)100%44100%
File 25: GetFiniteAxisRootRatioWords(...)100%1212100%
File 25: ReduceFiniteAxisRootRatio(...)100%1010100%
File 25: BuildFiniteRootSturm(...)100%22100%
File 25: RefineFiniteRoot(...)100%44100%
File 25: GetFiniteRootVariations(...)100%1616100%
File 25: EvaluateFiniteRootPolynomial(...)100%22100%
File 25: EvaluateFiniteRootPolynomial(...)100%22100%
File 25: GetFiniteRootEvaluationWords(...)100%11100%
File 25: EvaluateFiniteRootPolynomialCore(...)100%1010100%
File 25: GetFiniteAxisRootSquareRootBounds(...)100%44100%
File 25: GetFiniteRootIntervalSign(...)100%44100%
File 25: GetFiniteRootPolynomialBounds(...)100%1414100%
File 25: GetFiniteRootCoefficientBits(...)100%22100%
File 25: GetFiniteRootBits(...)100%22100%
File 25: CopyFiniteRootMagnitude(...)100%11100%
File 25: ShiftFiniteRootLeft(...)100%88100%
File 26: GetFiniteRootHermiteIntervalSign(...)100%1616100%
File 26: GetFiniteRootHermiteSignature(...)100%88100%
File 26: BuildFiniteRootTraces(...)100%66100%
File 26: GetFiniteRootSymmetricSignature(...)100%2828100%
File 26: GetFiniteRootMatrixIndex(...)100%22100%
File 26: MoveNextFiniteRootPermutation(...)100%1010100%
File 27: TryGetCapsuleInterval(...)100%44100%
File 27: TryGetCapsuleInterval(...)100%44100%
File 27: TryGetCapsuleInterval(...)100%22100%
File 27: TryGetCapsuleInterval(...)100%22100%
File 27: TryGetFiniteCylinderInterval(...)100%11100%
File 27: TryGetFiniteCylinderInterval(...)100%22100%
File 27: IsCapsuleContainedAtParameter(...)100%66100%
File 27: IsCapsuleContainedAtParameter(...)100%66100%
File 27: IsFiniteCylinderContainedAtParameter(...)100%44100%
File 27: IsCenteredFiniteCylinderContainedAtParameter(...)100%44100%
File 27: IsCenteredCapsuleContainedAtParameter(...)100%22100%
File 27: IsCenteredCapsuleContainedAtParameter(...)100%22100%
File 27: GetCenteredCap(...)100%22100%
File 27: MergeCenteredCapInterval(...)100%11100%
File 27: MergeCenteredCapInterval(...)100%11100%
File 27: GetCenteredCapPolynomial(...)100%11100%
File 27: GetCenteredCapPolynomial(...)100%11100%
File 27: EvaluateLinear(...)100%11100%
File 27: GetRadialPolynomial(...)100%11100%
File 27: IsQuadraticContained(...)100%11100%
File 27: IsQuadraticContained(...)100%11100%
File 27: TryGetCircleInterval(...)100%11100%
File 27: TryGetSphereInterval(...)100%11100%
File 27: GetDirectionDot(...)100%11100%
File 27: GetDirectionDot(...)100%11100%
File 27: GetDirectionDot(...)100%11100%
File 27: GetDirectionDot(...)100%11100%
File 28: .ctor(...)100%11100%
File 28: GetRigidLocalPointRelation(...)100%11100%
File 29: RoundLowerRoot(...)100%66100%
File 29: RoundUpperRoot(...)100%66100%
File 29: ShiftLeftByFixed64FractionalBits(...)100%11100%
File 29: EvaluatePolynomial(...)100%11100%
File 29: EvaluateDerivative(...)100%11100%
File 29: EvaluatePolynomial(...)100%11100%
File 29: EvaluateDerivative(...)100%11100%
File 30: .ctor(...)100%11100%
File 30: TryGetSphericallyExpandedBoxFirstDistance(...)100%1414100%
File 30: TryGetSphericallyExpandedProjectedBoxFirstDistance(...)100%1818100%
File 30: AddAxisBreakpoints(...)100%1616100%
File 30: AddProjectedAxisBreakpoints(...)100%1616100%
File 30: AddBreakpoint(...)100%44100%
File 30: AddProjectedBreakpoint(...)100%44100%
File 30: SortBreakpoints(...)100%66100%
File 30: GetBoxDistancePolynomial(...)100%11100%
File 30: GetProjectedBoxDistancePolynomial(...)100%11100%
File 30: AddBoxAxisPolynomial(...)100%44100%
File 30: AddProjectedBoxAxisPolynomial(...)100%44100%
File 30: IsWithinProjectedRoundedBox(...)100%11100%
File 30: GetProjectedBoxAxisOffset(...)100%44100%
File 31: .ctor(...)100%11100%
File 31: .ctor(...)100%11100%
File 31: .cctor()100%11100%
File 31: .ctor(...)100%11100%
File 31: DoesCenteredFiniteCylinderOverlapSphere(...)100%11100%
File 31: TryGetSphericallyExpandedFiniteCylinderDistanceInterval(...)100%11100%
File 31: TryGetSphericallyExpandedFiniteCylinderFirstDistance(...)100%11100%
File 31: TryGetSphericallyExpandedFiniteCylinderFirstDistanceFromHalfAxisLength(...)100%11100%
File 31: TryGetSphericallyExpandedFiniteCylinderDirectionFirstDistanceFromHalfAxisLength(...)100%22100%
File 31: TryGetSphericallyExpandedFiniteCylinderDistanceIntervalWithFullAxisLength(...)100%44100%
File 31: TryGetSphericallyExpandedFiniteCylinderDistanceInterval(...)100%22100%
File 31: MergeSphericallyExpandedFiniteCylinderRoundedIntervals(...)100%44100%
File 31: TryGetRoundedCylinderCapCoreDistanceInterval(...)100%2020100%
File 31: RoundedCylinderRimBoundsIntersect(...)100%11100%
File 31: CreateRoundedCylinderQuadraticBound(...)100%66100%
File 31: MergeRoundedCylinderRimInterval(...)100%11100%
File 31: IsPointInRoundedCylinderRimTube(...)100%1414100%
File 31: TryGetFiniteCylinderDirectionFirstDistance(...)100%22100%
File 31: CreateRoundedCylinderBaseData(...)100%11100%
File 31: CreateRoundedCylinderBaseData(...)100%11100%
File 31: CreateRoundedCylinderBaseData(...)100%11100%
File 31: CreateRoundedCylinderTorusPolynomial(...)100%22100%
File 31: MultiplyToSigned320(...)100%11100%
File 31: MultiplyToSigned320(...)100%11100%
File 31: Twice(...)100%11100%
File 31: Four(...)100%11100%
File 31: Twice(...)100%11100%
File 31: AddFive(...)100%11100%
File 32: ImportRoundedCylinderCoefficient(...)100%11100%
File 32: ImportRoundedCylinderCoefficient(...)100%11100%
File 32: GetRoundedCylinderCoefficient(...)100%11100%
File 32: GetRoundedCylinderCoefficientIndex(...)100%11100%
File 32: AddRoundedCylinderSigned(...)100%66100%
File 32: SubtractRoundedCylinderSigned(...)100%1818100%
File 32: MultiplyRoundedCylinderWide(...)100%44100%
File 32: MultiplyRoundedCylinderWideByWord(...)100%44100%
File 32: AddRoundedCylinderWord(...)100%22100%
File 32: MultiplyRoundedCylinderSigns(...)100%44100%
File 32: GetRoundedCylinderWideLength(...)100%44100%
File 32: CopyRoundedCylinderWide(...)100%11100%
File 32: CountRoundedCylinderTrailingZeroes(...)100%44100%
File 32: ShiftRoundedCylinderWideRight(...)100%66100%
File 33: CountProjectionDomainQuarticRoots(...)100%66100%
File 33: GetRoundedCylinderInfinityVariations(...)100%1010100%
File 33: TryGetRoundedCylinderRimDistanceInterval(...)100%1818100%
File 33: FindRoundedCylinderRoot(...)100%2222100%
File 33: FindRoundedCylinderSignChangingRoot(...)100%1010100%
File 33: HomogenizeRoundedCylinderSturmSequence(...)100%66100%
File 33: IsRoundedCylinderEntryBeforeOrEqualToExit(...)100%1212100%
File 33: GetRoundedCylinderRadicalSquare(...)100%11100%
File 33: ImportRoundedCylinderWide(...)100%11100%
File 33: ImportRoundedCylinderWide(...)100%11100%
File 33: GetRoundedCylinderSignVariations(...)100%88100%
File 33: EvaluateRoundedCylinderPolynomialSign(...)100%44100%
File 33: EvaluateRoundedCylinderPolynomialSignAtInteger(...)100%22100%
File 33: BuildRoundedCylinderSturmSequence(...)100%66100%
File 33: BuildRoundedCylinderQuarticSturmSequence(...)100%1616100%
File 33: SetRoundedCylinderProduct(...)100%11100%
File 33: SetRoundedCylinderScaledProduct(...)100%11100%
File 33: SetRoundedCylinderTripleProduct(...)100%11100%
File 33: BuildRoundedCylinderNegativeRemainder(...)100%1818100%
File 33: RemoveRoundedCylinderCommonPowerOfTwo(...)100%88100%
File 33: TrimRoundedCylinderPolynomial(...)100%44100%
File 34: TryGetScaledCenteredCapsuleTriangleContact(...)100%3030100%
File 34: GetClosestScaledAxisTriangleCandidate(...)100%11100%
File 34: GetClosestScaledAxisTriangleCandidate(...)100%88100%
File 34: TryGetScaledCoincidentCapsuleContact(...)100%1010100%
File 34: GetScaledCoreEndpoint(...)100%11100%
File 34: TryRoundScaledAxisCoordinate(...)100%11100%
File 34: AccumulateScaledAxisFace(...)100%2626100%
File 34: AccumulateScaledAxisEdge(...)100%6060100%
File 34: KeepScaledAxisPair(...)100%88100%
File 34: ScaledAxisField(...)100%11100%
File 34: ScaledAxisRelative(...)100%44100%
File 34: ScaledAxisDifference(...)100%11100%
File 34: ClampScaledAxisParameter(...)100%44100%
File 34: ScaledAxisParameterInInterval(...)100%22100%
File 34: GetScaledAxisIntervalEndpoint(...)100%22100%
File 34: SetScaledAxisMediant(...)100%11100%
File 34: NormalizeScaledAxisRatio(...)100%11100%
File 34: GetScaledAxisPrimitiveSeparation(...)100%1616100%
File 35: TriangleCoordinate(...)100%11100%
File 35: AxisCoordinate(...)100%11100%
File 35: get_Denominator()100%11100%
File 35: .ctor(...)100%11100%
File 35: At(...)100%11100%
File 35: Keep(...)100%44100%
File 35: Complete()100%22100%
File 36: .ctor(...)100%11100%
File 36: .ctor(...)100%88100%
File 36: HasStrictCirclePolynomialPoint(...)100%1818100%
File 36: AddStrictCircleBounds(...)100%88100%
File 36: InsertStrictCircleBound(...)100%88100%
File 36: CompareStrictCircleBounds(...)100%11100%
File 36: GetStrictCircleQuadraticRightSign(...)100%66100%
File 36: GetStrictCirclePolynomialRightSign(...)100%11100%
File 36: GetStrictCirclePolynomialLimitSign(...)100%1818100%
File 36: CountStrictCircleOddRoots(...)100%2828100%
File 36: GetStrictCircleVariations(...)100%66100%
File 36: EvaluateStrictCirclePolynomial(...)100%1616100%
File 36: StrictCircleCoefficient(...)100%11100%
File 36: StrictCircleCoefficient(...)100%11100%
File 37: HasNegativeFiniteAxisPolynomial(...)100%2424100%
File 37: BuildFiniteAxisPolynomialSturmSequence(...)100%1414100%
File 37: GetStrictPolynomialVariations(...)100%66100%
File 37: GetStrictPolynomialEndpointSign(...)100%1414100%
File 37: GetStrictPolynomialValueAtOne(...)100%44100%
File 38: .cctor()100%11100%
File 38: .ctor(...)100%66100%
File 38: NegateStrict(...)100%11100%
File 38: GetStrictChordBounds(...)100%1212100%
File 38: HasNegativeStrictFieldQuadratic(...)100%1010100%
File 38: CompareStrictFieldBounds(...)100%11100%
File 38: GetStrictFieldQuadraticSign(...)100%11100%
File 38: AccumulateStrictProduct(...)100%44100%
File 38: GetStrictFieldSign(...)100%1010100%
File 39: GetFiniteValueSturmArenaBits(...)100%22100%
File 39: GetFiniteValueRootCellWords(...)100%11100%
File 39: TryGetFiniteValueRoot(...)100%1010100%
File 39: GetFiniteValueRoots(...)100%11100%
File 39: TryGetFiniteValueRoots(...)100%88100%
File 39: TryIsolateFiniteValueRootCells(...)100%1818100%
File 39: GetKnownFiniteValueRoot(...)100%11100%
File 39: GetFiniteValueRootCount(...)100%11100%
File 39: IsolateFiniteValueRoot(...)100%44100%
File 39: IsolateFiniteValueCell(...)100%1212100%
File 39: RefineFiniteValueRoot(...)100%1616100%
File 39: RefineFiniteValueCell(...)100%66100%
File 39: GetFiniteValueCellUpperSign(...)100%66100%
File 39: BuildFiniteValueSturm(...)100%1616100%
File 39: UpdateFiniteValueSubresultantScalars(...)100%88100%
File 39: PowerFiniteValueMagnitude(...)100%22100%
File 39: BuildFiniteValueSubresultant(...)100%2828100%
File 39: NormalizeFiniteValueContent(...)100%1212100%
File 39: GetFiniteValueVariations(...)100%1616100%
File 40: TryGetFiniteValueRootsBernstein(...)100%4040100%
File 40: SplitFiniteValueBernstein(...)100%66100%
File 40: HasFiniteValueSquareFreeCertificate(...)100%2828100%
File 41: GetFiniteValueRepeatedRootFactor(...)100%1010100%
File 41: GetFiniteValueDerivativeGcd(...)100%22100%
File 41: DivideFiniteValueFactors(...)100%1010100%
File 42: MapKnownFiniteValueRootRadicalOffset(...)100%1212100%
File 42: ClassifyRadicalOffsetCells(...)100%1414100%
File 42: CompareRadicalOffsetEndpoints(...)100%1414100%
File 43: GetFiniteValueReciprocalRoots(...)100%1616100%
File 43: ReverseFiniteValueSturm(...)100%1212100%
File 44: TryRefineFiniteValueCrossingRoot(...)100%4646100%
File 44: KeepFiniteValueRefinementSingleton(...)100%11100%
File 45: CompareFiniteValueRootToDyadic(...)100%2222100%
File 45: CompareFiniteValueRoots(...)100%11100%
File 45: CompareFiniteValueRoots(...)100%11100%
File 45: CompareFiniteValueRootsCore(...)100%4848100%
File 45: HaveProportionalScaledValuePolynomials(...)100%1616100%
File 45: GetSignAtFiniteValueRoot(...)100%11100%
File 45: GetSignAtFiniteValueRootAndRefine(...)100%11100%
File 45: GetSignAtFiniteValueRootCore(...)100%4646100%
File 45: GetFiniteValueApproximateSign(...)100%11100%
File 45: GetFiniteValueApproximateSign(...)100%88100%
File 45: GetFiniteValueRootNonnegativeBounds(...)100%22100%
File 45: GetFiniteValueApproximateSignCore(...)100%1414100%
File 45: GetFiniteValueCeilingLog2(...)100%11100%
File 46: .cctor()100%11100%
File 46: TryGetCenteredAxisEndpoint(...)100%11100%
File 46: TryGetCenteredAxisEndpoint(...)100%11100%
File 46: TryGetCenteredCapsuleSupport(...)100%22100%
File 46: GetCenteredCapsuleSupportAnchor(...)100%88100%
File 46: TryGetCenteredCapsuleSupport(...)100%22100%
File 46: TryGetCenteredFiniteCylinderSupport(...)100%11100%
File 46: TryGetCenteredFiniteConeSupport(...)100%1010100%
File 46: TryGetCenteredSupport(...)100%22100%
File 46: TryGetCenteredSupport(...)100%22100%
File 46: TryGetSupportCoordinate(...)100%2222100%
File 46: CompareSupportCoordinateToMidpoint(...)100%66100%
File 46: IsCenteredFiniteConeApexSupport(...)100%11100%
File 46: IsCenteredFiniteConeApexSupportCore(...)100%66100%
File 46: GetDirection(...)100%11100%
File 46: GetDirection(...)100%11100%
File 46: GetPlaneDirection(...)100%11100%
File 46: GetMinimumMidpoint()100%11100%
File 46: GetMaximumMidpoint()100%11100%

File(s)

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredAxes.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.CenteredAxes.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
 8namespace FixedMathSharp.Geometry;
 9
 10/// <content>
 11/// Contains logic for computing closest-point intersections between finite axes
 12/// defined relative to centered (origin-relative) coordinate frames, using
 13/// wide-precision (Signed320/576/832) arithmetic for numerical stability.
 14/// </content>
 15internal static partial class WideFiniteAxisIntersection
 16{
 17    #region NestedTypes
 18
 19    private readonly struct CenteredAxesCandidate
 20    {
 21        internal readonly Signed576 FirstX;
 22        internal readonly Signed576 FirstY;
 23        internal readonly Signed576 FirstZ;
 24        internal readonly Signed576 SecondX;
 25        internal readonly Signed576 SecondY;
 26        internal readonly Signed576 SecondZ;
 27        internal readonly Signed320 FirstParameterNumerator;
 28        internal readonly Signed320 SecondParameterNumerator;
 29        internal readonly Signed320 ParameterDenominator;
 30        internal readonly Signed832 SquaredDistanceNumerator;
 31
 32        internal CenteredAxesCandidate(
 33            Signed576 firstX,
 34            Signed576 firstY,
 35            Signed576 firstZ,
 36            Signed576 secondX,
 37            Signed576 secondY,
 38            Signed576 secondZ,
 39            Signed320 firstParameterNumerator,
 40            Signed320 secondParameterNumerator,
 41            Signed320 parameterDenominator,
 42            Signed832 squaredDistanceNumerator)
 43        {
 89344            FirstX = firstX;
 89345            FirstY = firstY;
 89346            FirstZ = firstZ;
 89347            SecondX = secondX;
 89348            SecondY = secondY;
 89349            SecondZ = secondZ;
 89350            FirstParameterNumerator = firstParameterNumerator;
 89351            SecondParameterNumerator = secondParameterNumerator;
 89352            ParameterDenominator = parameterDenominator;
 89353            SquaredDistanceNumerator = squaredDistanceNumerator;
 89354        }
 55    }
 56
 57    private readonly struct CenteredAxesCandidate2d
 58    {
 59        internal readonly Signed576 FirstX;
 60        internal readonly Signed576 FirstY;
 61        internal readonly Signed576 SecondX;
 62        internal readonly Signed576 SecondY;
 63        internal readonly Signed320 ParameterDenominator;
 64        internal readonly Signed832 SquaredDistanceNumerator;
 65
 66        internal CenteredAxesCandidate2d(
 67            Signed576 firstX,
 68            Signed576 firstY,
 69            Signed576 secondX,
 70            Signed576 secondY,
 71            Signed320 parameterDenominator,
 72            Signed832 squaredDistanceNumerator)
 73        {
 958674            FirstX = firstX;
 958675            FirstY = firstY;
 958676            SecondX = secondX;
 958677            SecondY = secondY;
 958678            ParameterDenominator = parameterDenominator;
 958679            SquaredDistanceNumerator = squaredDistanceNumerator;
 958680        }
 81    }
 82
 83    #endregion
 84
 85    internal static bool TryGetDistanceToCenteredAxis(
 86        Vector2d point,
 87        Vector2d center,
 88        Vector2d axis,
 89        Fixed64 axisLength,
 90        out Fixed64 distance) =>
 291        TryGetDistanceToCenteredCapsule(
 292            point,
 293            center,
 294            axis,
 295            axisLength,
 296            Fixed64.Zero,
 297            out distance);
 98
 99    internal static bool TryGetDistanceToCenteredAxis(
 100        Vector3d point,
 101        Vector3d center,
 102        Vector3d axis,
 103        Fixed64 axisLength,
 104        out Fixed64 distance) =>
 3105        TryGetDistanceToCenteredCapsule(
 3106            point,
 3107            center,
 3108            axis,
 3109            axisLength,
 3110            Fixed64.Zero,
 3111            out distance);
 112
 113    internal static bool TryGetDistanceBetweenCenteredAxes(
 114        Vector2d firstCenter,
 115        Vector2d firstAxis,
 116        Fixed64 firstLength,
 117        Vector2d secondCenter,
 118        Vector2d secondAxis,
 119        Fixed64 secondLength,
 120        out Fixed64 distance)
 121    {
 4610122        GetCenteredAxesSquaredDistance(
 4610123            firstCenter,
 4610124            firstAxis,
 4610125            firstLength,
 4610126            secondCenter,
 4610127            secondAxis,
 4610128            secondLength,
 4610129            out Signed832 squaredNumerator,
 4610130            out Signed320 denominator);
 4610131        return TryRoundCenteredAxesDistance(
 4610132            squaredNumerator,
 4610133            denominator,
 4610134            out distance);
 135    }
 136
 137    internal static bool TryGetDistanceBetweenCenteredAxes(
 138        Vector3d firstCenter,
 139        Vector3d firstAxis,
 140        Fixed64 firstLength,
 141        Vector3d secondCenter,
 142        Vector3d secondAxis,
 143        Fixed64 secondLength,
 144        out Fixed64 distance)
 145    {
 263146        GetCenteredAxesSquaredDistance(
 263147            firstCenter,
 263148            firstAxis,
 263149            firstLength,
 263150            secondCenter,
 263151            secondAxis,
 263152            secondLength,
 263153            out Signed832 squaredNumerator,
 263154            out Signed320 denominator);
 263155        return TryRoundCenteredAxesDistance(
 263156            squaredNumerator,
 263157            denominator,
 263158            out distance);
 159    }
 160
 161    internal static bool DoCenteredCapsulesOverlap(
 162        Vector2d firstCenter,
 163        Vector2d firstAxis,
 164        Fixed64 firstLength,
 165        Fixed64 firstRadius,
 166        Vector2d secondCenter,
 167        Vector2d secondAxis,
 168        Fixed64 secondLength,
 169        Fixed64 secondRadius,
 170        bool strict = false)
 171    {
 4947172        GetCenteredAxesSquaredDistance(
 4947173            firstCenter,
 4947174            firstAxis,
 4947175            firstLength,
 4947176            secondCenter,
 4947177            secondAxis,
 4947178            secondLength,
 4947179            out Signed832 squaredNumerator,
 4947180            out Signed320 denominator);
 4947181        return IsWithinCombinedRadius(
 4947182            squaredNumerator,
 4947183            denominator,
 4947184            firstRadius,
 4947185            secondRadius,
 4947186            strict);
 187    }
 188
 189    internal static bool DoCenteredCapsulesOverlap(
 190        Vector3d firstCenter,
 191        Vector3d firstAxis,
 192        Fixed64 firstLength,
 193        Fixed64 firstRadius,
 194        Vector3d secondCenter,
 195        Vector3d secondAxis,
 196        Fixed64 secondLength,
 197        Fixed64 secondRadius,
 198        bool strict = false)
 199    {
 589200        GetCenteredAxesSquaredDistance(
 589201            firstCenter,
 589202            firstAxis,
 589203            firstLength,
 589204            secondCenter,
 589205            secondAxis,
 589206            secondLength,
 589207            out Signed832 squaredNumerator,
 589208            out Signed320 denominator);
 589209        return IsWithinCombinedRadius(
 589210            squaredNumerator,
 589211            denominator,
 589212            firstRadius,
 589213            secondRadius,
 589214            strict);
 215    }
 216
 217    internal static bool TryGetCenteredCapsulesContact(
 218        Vector2d firstCenter,
 219        Fixed64 firstAnchorRotation,
 220        Vector2d firstAxis,
 221        Fixed64 firstLength,
 222        Fixed64 firstRadius,
 223        Vector2d secondCenter,
 224        Fixed64 secondAnchorRotation,
 225        Vector2d secondAxis,
 226        Fixed64 secondLength,
 227        Fixed64 secondRadius,
 228        Vector2d fallbackNormal,
 229        out FixedContactAnchors2d contact)
 230    {
 29231        GetCenteredAxesCandidate(
 29232            firstCenter,
 29233            firstAxis,
 29234            firstLength,
 29235            secondCenter,
 29236            secondAxis,
 29237            secondLength,
 29238            out CenteredAxesCandidate2d candidate);
 29239        if (!IsWithinCombinedRadius(
 29240                candidate.SquaredDistanceNumerator,
 29241                candidate.ParameterDenominator,
 29242                firstRadius,
 29243                secondRadius))
 244        {
 5245            contact = default;
 5246            return false;
 247        }
 248
 24249        Vector2d normal = WideNormalization.GetNormalized(
 24250            WideArithmetic.SubtractSigned576(candidate.SecondX, candidate.FirstX),
 24251            WideArithmetic.SubtractSigned576(candidate.SecondY, candidate.FirstY));
 24252        if (normal == Vector2d.Zero)
 6253            normal = fallbackNormal;
 254
 24255        GetLocalRadialFeature(
 24256            normal,
 24257            firstRadius,
 24258            firstAnchorRotation,
 24259            out Vector2d firstLocalRadialDirection,
 24260            out Vector2d firstRadialOffset);
 24261        GetLocalRadialFeature(
 24262            -normal,
 24263            secondRadius,
 24264            secondAnchorRotation,
 24265            out Vector2d secondLocalRadialDirection,
 24266            out Vector2d secondRadialOffset);
 24267        Vector2d firstAxisOffset = GetCenteredAxisLocalOffset(
 24268            candidate.FirstX,
 24269            candidate.FirstY,
 24270            firstCenter,
 24271            candidate.ParameterDenominator,
 24272            firstAnchorRotation);
 24273        Vector2d secondAxisOffset = GetCenteredAxisLocalOffset(
 24274            candidate.SecondX,
 24275            candidate.SecondY,
 24276            secondCenter,
 24277            candidate.ParameterDenominator,
 24278            secondAnchorRotation);
 24279        GetCenteredCapsulesDepth(
 24280            candidate.SquaredDistanceNumerator,
 24281            candidate.ParameterDenominator,
 24282            firstRadius,
 24283            secondRadius,
 24284            firstLength == Fixed64.Zero && secondLength == Fixed64.Zero,
 24285            out Fixed64 depth,
 24286            out bool depthIsClamped);
 24287        contact = new FixedContactAnchors2d(
 24288            new FixedPointAnchor2d(
 24289                firstCenter,
 24290                firstAnchorRotation,
 24291                firstAxisOffset,
 24292                firstRadialOffset,
 24293                FixedPointAnchorTerm2d.CreateRadialSupport(
 24294                    firstLocalRadialDirection,
 24295                    firstRadius,
 24296                    firstRadialOffset)),
 24297            new FixedPointAnchor2d(
 24298                secondCenter,
 24299                secondAnchorRotation,
 24300                secondAxisOffset,
 24301                secondRadialOffset,
 24302                FixedPointAnchorTerm2d.CreateRadialSupport(
 24303                    secondLocalRadialDirection,
 24304                    secondRadius,
 24305                    secondRadialOffset)),
 24306            normal,
 24307            depth,
 24308            depthIsClamped);
 24309        return true;
 310    }
 311
 312    internal static bool TryGetCenteredCapsulesContact(
 313        Vector3d firstCenter,
 314        FixedQuaternion firstRotation,
 315        Vector3d firstLocalAxis,
 316        Vector3d firstAxis,
 317        Fixed64 firstLength,
 318        Fixed64 firstRadius,
 319        Vector3d secondCenter,
 320        FixedQuaternion secondRotation,
 321        Vector3d secondLocalAxis,
 322        Vector3d secondAxis,
 323        Fixed64 secondLength,
 324        Fixed64 secondRadius,
 325        Vector3d fallbackNormal,
 326        out FixedContactAnchors contact)
 327    {
 40328        GetCenteredAxesCandidate(
 40329            firstCenter,
 40330            firstAxis,
 40331            firstLength,
 40332            secondCenter,
 40333            secondAxis,
 40334            secondLength,
 40335            out CenteredAxesCandidate candidate);
 40336        Signed576 differenceX = WideArithmetic.SubtractSigned576(
 40337            candidate.SecondX,
 40338            candidate.FirstX);
 40339        Signed576 differenceY = WideArithmetic.SubtractSigned576(
 40340            candidate.SecondY,
 40341            candidate.FirstY);
 40342        Signed576 differenceZ = WideArithmetic.SubtractSigned576(
 40343            candidate.SecondZ,
 40344            candidate.FirstZ);
 40345        Vector3d normal = WideNormalization.GetNormalized(
 40346            differenceX,
 40347            differenceY,
 40348            differenceZ);
 40349        if (normal.IsZero)
 6350            normal = fallbackNormal;
 351
 40352        _ = TryGetCenteredAxisPointCoordinate(
 40353            Fixed64.Zero,
 40354            firstLocalAxis.X,
 40355            candidate.FirstParameterNumerator,
 40356            candidate.ParameterDenominator,
 40357            out Fixed64 firstX);
 40358        _ = TryGetCenteredAxisPointCoordinate(
 40359            Fixed64.Zero,
 40360            firstLocalAxis.Y,
 40361            candidate.FirstParameterNumerator,
 40362            candidate.ParameterDenominator,
 40363            out Fixed64 firstY);
 40364        _ = TryGetCenteredAxisPointCoordinate(
 40365            Fixed64.Zero,
 40366            firstLocalAxis.Z,
 40367            candidate.FirstParameterNumerator,
 40368            candidate.ParameterDenominator,
 40369            out Fixed64 firstZ);
 40370        _ = TryGetCenteredAxisPointCoordinate(
 40371            Fixed64.Zero,
 40372            secondLocalAxis.X,
 40373            candidate.SecondParameterNumerator,
 40374            candidate.ParameterDenominator,
 40375            out Fixed64 secondX);
 40376        _ = TryGetCenteredAxisPointCoordinate(
 40377            Fixed64.Zero,
 40378            secondLocalAxis.Y,
 40379            candidate.SecondParameterNumerator,
 40380            candidate.ParameterDenominator,
 40381            out Fixed64 secondY);
 40382        _ = TryGetCenteredAxisPointCoordinate(
 40383            Fixed64.Zero,
 40384            secondLocalAxis.Z,
 40385            candidate.SecondParameterNumerator,
 40386            candidate.ParameterDenominator,
 40387            out Fixed64 secondZ);
 40388        GetCenteredCapsulesDepth(
 40389            candidate.SquaredDistanceNumerator,
 40390            candidate.ParameterDenominator,
 40391            firstRadius,
 40392            secondRadius,
 40393            firstLength == Fixed64.Zero && secondLength == Fixed64.Zero,
 40394            out Fixed64 depth,
 40395            out bool depthIsClamped);
 40396        depth = FixedMath.Max(depth, Fixed64.Zero);
 40397        Vector3d firstLocalRadialDirection = WideNormalization.GetNormalized(
 40398            firstRotation.Inverse().Rotate(normal));
 40399        Vector3d secondLocalRadialDirection = WideNormalization.GetNormalized(
 40400            secondRotation.Inverse().Rotate(-normal));
 40401        Vector3d firstRadialOffset =
 40402            firstLocalRadialDirection * firstRadius;
 40403        Vector3d secondRadialOffset =
 40404            secondLocalRadialDirection * secondRadius;
 40405        contact = new FixedContactAnchors(
 40406            new FixedPointAnchor(
 40407                firstCenter,
 40408                firstRotation,
 40409                new Vector3d(firstX, firstY, firstZ),
 40410                firstRadialOffset,
 40411                FixedPointAnchorTerm3d.CreateRadialSupport(
 40412                    firstLocalRadialDirection,
 40413                    firstRadius,
 40414                    firstRadialOffset)),
 40415            new FixedPointAnchor(
 40416                secondCenter,
 40417                secondRotation,
 40418                new Vector3d(secondX, secondY, secondZ),
 40419                secondRadialOffset,
 40420                FixedPointAnchorTerm3d.CreateRadialSupport(
 40421                    secondLocalRadialDirection,
 40422                    secondRadius,
 40423                    secondRadialOffset)),
 40424            normal,
 40425            depth,
 40426            depthIsClamped);
 40427        return true;
 428    }
 429
 430    internal static bool TryGetClosestPointsBetweenCenteredAxes(
 431        Vector2d firstCenter,
 432        Vector2d firstAxis,
 433        Fixed64 firstLength,
 434        Vector2d secondCenter,
 435        Vector2d secondAxis,
 436        Fixed64 secondLength,
 437        out Vector2d firstPoint,
 438        out Vector2d secondPoint)
 439    {
 4440        GetClosestCenteredAxisParameters(
 4441            GetDot(firstAxis, Vector2d.Zero, firstAxis, Vector2d.Zero),
 4442            GetDot(firstAxis, Vector2d.Zero, secondAxis, Vector2d.Zero),
 4443            GetDot(secondAxis, Vector2d.Zero, secondAxis, Vector2d.Zero),
 4444            GetDot(firstCenter, secondCenter, firstAxis, Vector2d.Zero),
 4445            GetDot(firstCenter, secondCenter, secondAxis, Vector2d.Zero),
 4446            firstLength,
 4447            secondLength,
 4448            out Signed320 firstNumerator,
 4449            out Signed320 secondNumerator,
 4450            out Signed320 denominator);
 451
 4452        bool representable = TryGetCenteredAxisPointCoordinate(
 4453            firstCenter.X, firstAxis.X, firstNumerator, denominator, out Fixed64 firstX);
 4454        representable &= TryGetCenteredAxisPointCoordinate(
 4455            firstCenter.Y, firstAxis.Y, firstNumerator, denominator, out Fixed64 firstY);
 4456        representable &= TryGetCenteredAxisPointCoordinate(
 4457            secondCenter.X, secondAxis.X, secondNumerator, denominator, out Fixed64 secondX);
 4458        representable &= TryGetCenteredAxisPointCoordinate(
 4459            secondCenter.Y, secondAxis.Y, secondNumerator, denominator, out Fixed64 secondY);
 4460        if (!representable)
 461        {
 3462            firstPoint = default;
 3463            secondPoint = default;
 3464            return false;
 465        }
 466
 1467        firstPoint = new Vector2d(firstX, firstY);
 1468        secondPoint = new Vector2d(secondX, secondY);
 1469        return true;
 470    }
 471
 472    internal static bool TryGetClosestPointsBetweenCenteredAxes(
 473        Vector3d firstCenter,
 474        Vector3d firstAxis,
 475        Fixed64 firstLength,
 476        Vector3d secondCenter,
 477        Vector3d secondAxis,
 478        Fixed64 secondLength,
 479        out Vector3d firstPoint,
 480        out Vector3d secondPoint)
 481    {
 137482        GetClosestCenteredAxisParameters(
 137483            GetDot(firstAxis, Vector3d.Zero, firstAxis, Vector3d.Zero),
 137484            GetDot(firstAxis, Vector3d.Zero, secondAxis, Vector3d.Zero),
 137485            GetDot(secondAxis, Vector3d.Zero, secondAxis, Vector3d.Zero),
 137486            GetDot(firstCenter, secondCenter, firstAxis, Vector3d.Zero),
 137487            GetDot(firstCenter, secondCenter, secondAxis, Vector3d.Zero),
 137488            firstLength,
 137489            secondLength,
 137490            out Signed320 firstNumerator,
 137491            out Signed320 secondNumerator,
 137492            out Signed320 denominator);
 493
 137494        bool representable = TryGetCenteredAxisPointCoordinate(
 137495            firstCenter.X, firstAxis.X, firstNumerator, denominator, out Fixed64 firstX);
 137496        representable &= TryGetCenteredAxisPointCoordinate(
 137497            firstCenter.Y, firstAxis.Y, firstNumerator, denominator, out Fixed64 firstY);
 137498        representable &= TryGetCenteredAxisPointCoordinate(
 137499            firstCenter.Z, firstAxis.Z, firstNumerator, denominator, out Fixed64 firstZ);
 137500        representable &= TryGetCenteredAxisPointCoordinate(
 137501            secondCenter.X, secondAxis.X, secondNumerator, denominator, out Fixed64 secondX);
 137502        representable &= TryGetCenteredAxisPointCoordinate(
 137503            secondCenter.Y, secondAxis.Y, secondNumerator, denominator, out Fixed64 secondY);
 137504        representable &= TryGetCenteredAxisPointCoordinate(
 137505            secondCenter.Z, secondAxis.Z, secondNumerator, denominator, out Fixed64 secondZ);
 137506        if (!representable)
 507        {
 2508            firstPoint = default;
 2509            secondPoint = default;
 2510            return false;
 511        }
 512
 135513        firstPoint = new Vector3d(firstX, firstY, firstZ);
 135514        secondPoint = new Vector3d(secondX, secondY, secondZ);
 135515        return true;
 516    }
 517
 518    internal static bool TryGetClosestOffsetsBetweenCenteredAxes(
 519        Vector3d firstCenter,
 520        Vector3d firstAxis,
 521        Fixed64 firstLength,
 522        Vector3d secondCenter,
 523        Vector3d secondAxis,
 524        Fixed64 secondLength,
 525        out Vector3d firstCenterOffset,
 526        out Vector3d secondCenterOffset)
 527    {
 1528        GetClosestCenteredAxisParameters(
 1529            GetDot(firstAxis, Vector3d.Zero, firstAxis, Vector3d.Zero),
 1530            GetDot(firstAxis, Vector3d.Zero, secondAxis, Vector3d.Zero),
 1531            GetDot(secondAxis, Vector3d.Zero, secondAxis, Vector3d.Zero),
 1532            GetDot(firstCenter, secondCenter, firstAxis, Vector3d.Zero),
 1533            GetDot(firstCenter, secondCenter, secondAxis, Vector3d.Zero),
 1534            firstLength,
 1535            secondLength,
 1536            out Signed320 firstNumerator,
 1537            out Signed320 secondNumerator,
 1538            out Signed320 denominator);
 539
 540        // A centered-axis offset cannot exceed half of its representable full
 541        // length, so valid normalized axes always narrow successfully here.
 1542        _ = TryGetCenteredAxisPointCoordinate(
 1543            Fixed64.Zero, firstAxis.X, firstNumerator, denominator, out Fixed64 firstX);
 1544        _ = TryGetCenteredAxisPointCoordinate(
 1545            Fixed64.Zero, firstAxis.Y, firstNumerator, denominator, out Fixed64 firstY);
 1546        _ = TryGetCenteredAxisPointCoordinate(
 1547            Fixed64.Zero, firstAxis.Z, firstNumerator, denominator, out Fixed64 firstZ);
 1548        _ = TryGetCenteredAxisPointCoordinate(
 1549            Fixed64.Zero, secondAxis.X, secondNumerator, denominator, out Fixed64 secondX);
 1550        _ = TryGetCenteredAxisPointCoordinate(
 1551            Fixed64.Zero, secondAxis.Y, secondNumerator, denominator, out Fixed64 secondY);
 1552        _ = TryGetCenteredAxisPointCoordinate(
 1553            Fixed64.Zero, secondAxis.Z, secondNumerator, denominator, out Fixed64 secondZ);
 554
 1555        firstCenterOffset = new Vector3d(firstX, firstY, firstZ);
 1556        secondCenterOffset = new Vector3d(secondX, secondY, secondZ);
 1557        return true;
 558    }
 559
 560    internal static Vector3d GetClosestDirectionBetweenCenteredAxes(
 561        Vector3d firstCenter,
 562        Vector3d firstAxis,
 563        Fixed64 firstLength,
 564        Vector3d secondCenter,
 565        Vector3d secondAxis,
 566        Fixed64 secondLength)
 567    {
 1568        GetCenteredAxesCandidate(
 1569            firstCenter,
 1570            firstAxis,
 1571            firstLength,
 1572            secondCenter,
 1573            secondAxis,
 1574            secondLength,
 1575            out CenteredAxesCandidate candidate);
 1576        return WideNormalization.GetNormalized(
 1577            WideArithmetic.SubtractSigned576(candidate.SecondX, candidate.FirstX),
 1578            WideArithmetic.SubtractSigned576(candidate.SecondY, candidate.FirstY),
 1579            WideArithmetic.SubtractSigned576(candidate.SecondZ, candidate.FirstZ));
 580    }
 581}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredAxes.Solver.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.CenteredAxes.Solver.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
 8namespace FixedMathSharp.Geometry;
 9
 10/// <content>
 11/// Solver logic for finding the closest points between two finite axes
 12/// defined relative to their centers, using high-precision wide arithmetic.
 13/// </content>
 14internal static partial class WideFiniteAxisIntersection
 15{
 16    private static void GetClosestCenteredAxisParameters(
 17        Signed192 firstLengthSquared,
 18        Signed192 directionsDot,
 19        Signed192 secondLengthSquared,
 20        Signed192 firstDirectionDotDifference,
 21        Signed192 secondDirectionDotDifference,
 22        Fixed64 firstLength,
 23        Fixed64 secondLength,
 24        out Signed320 firstNumerator,
 25        out Signed320 secondNumerator,
 26        out Signed320 denominator)
 27    {
 902228        Signed320 determinant = WideArithmetic.MultiplySubtract(
 902229            firstLengthSquared,
 902230            secondLengthSquared,
 902231            directionsDot,
 902232            directionsDot);
 902233        if (determinant.Sign > 0)
 34        {
 668535            firstNumerator = WideArithmetic.MultiplySubtract(
 668536                directionsDot,
 668537                secondDirectionDotDifference,
 668538                secondLengthSquared,
 668539                firstDirectionDotDifference);
 668540            secondNumerator = WideArithmetic.MultiplySubtract(
 668541                firstLengthSquared,
 668542                secondDirectionDotDifference,
 668543                directionsDot,
 668544                firstDirectionDotDifference);
 668545            denominator = determinant;
 668546            if (!ClampCenteredAxisParameter(
 668547                    ref firstNumerator,
 668548                    ref denominator,
 668549                    firstLength))
 50            {
 264151                if (!ClampCenteredAxisParameter(
 264152                        ref secondNumerator,
 264153                        ref denominator,
 264154                        secondLength))
 55                {
 205956                    return;
 57                }
 58
 58259                ProjectFirstFromCenteredAxisCap(
 58260                    secondNumerator,
 58261                    firstLengthSquared,
 58262                    directionsDot,
 58263                    firstDirectionDotDifference,
 58264                    out firstNumerator,
 58265                    out denominator);
 58266                if (!ClampCenteredAxisParameter(
 58267                        ref firstNumerator,
 58268                        ref denominator,
 58269                        firstLength))
 70                {
 39071                    secondNumerator = WideArithmetic.MultiplySigned192(
 39072                        new Signed192(
 39073                            secondNumerator.Word2,
 39074                            secondNumerator.Word1,
 39075                            secondNumerator.Word0),
 39076                        firstLengthSquared);
 39077                    return;
 78                }
 79
 19280                SetCenteredAxisCap(ref secondNumerator, ref denominator, secondLength);
 19281                return;
 82            }
 83        }
 84        else
 85        {
 233786            firstNumerator = Signed320.ExtendValue(
 233787                Signed192.Signed(-firstLength.m_rawValue));
 233788            denominator = Signed320.ExtendValue(DoubleParameterScale);
 89        }
 90
 638191        ProjectSecondFromCenteredAxisCap(
 638192            firstNumerator,
 638193            directionsDot,
 638194            secondLengthSquared,
 638195            secondDirectionDotDifference,
 638196            out secondNumerator,
 638197            out denominator);
 638198        if (!ClampCenteredAxisParameter(
 638199                ref secondNumerator,
 6381100                ref denominator,
 6381101                secondLength))
 102        {
 1667103            firstNumerator = WideArithmetic.MultiplySigned192(
 1667104                new Signed192(
 1667105                    firstNumerator.Word2,
 1667106                    firstNumerator.Word1,
 1667107                    firstNumerator.Word0),
 1667108                secondLengthSquared);
 1667109            return;
 110        }
 111
 4714112        ProjectFirstFromCenteredAxisCap(
 4714113            secondNumerator,
 4714114            firstLengthSquared,
 4714115            directionsDot,
 4714116            firstDirectionDotDifference,
 4714117            out firstNumerator,
 4714118            out denominator);
 4714119        if (!ClampCenteredAxisParameter(
 4714120                ref firstNumerator,
 4714121                ref denominator,
 4714122                firstLength))
 123        {
 835124            secondNumerator = WideArithmetic.MultiplySigned192(
 835125                new Signed192(
 835126                    secondNumerator.Word2,
 835127                    secondNumerator.Word1,
 835128                    secondNumerator.Word0),
 835129                firstLengthSquared);
 835130            return;
 131        }
 132
 3879133        SetCenteredAxisCap(ref secondNumerator, ref denominator, secondLength);
 3879134    }
 135
 136    private static void GetCenteredAxesSquaredDistance(
 137        Vector2d firstCenter,
 138        Vector2d firstAxis,
 139        Fixed64 firstLength,
 140        Vector2d secondCenter,
 141        Vector2d secondAxis,
 142        Fixed64 secondLength,
 143        out Signed832 squaredNumerator,
 144        out Signed320 denominator)
 145    {
 9557146        GetCenteredAxesCandidate(
 9557147            firstCenter,
 9557148            firstAxis,
 9557149            firstLength,
 9557150            secondCenter,
 9557151            secondAxis,
 9557152            secondLength,
 9557153            out CenteredAxesCandidate2d candidate);
 9557154        squaredNumerator = candidate.SquaredDistanceNumerator;
 9557155        denominator = candidate.ParameterDenominator;
 9557156    }
 157
 158    private static void GetCenteredAxesCandidate(
 159        Vector2d firstCenter,
 160        Vector2d firstAxis,
 161        Fixed64 firstLength,
 162        Vector2d secondCenter,
 163        Vector2d secondAxis,
 164        Fixed64 secondLength,
 165        out CenteredAxesCandidate2d candidate)
 166    {
 167        // Point cores have no axis parameters. Keep their raw centers over
 168        // denominator one rather than introducing and later cancelling an
 169        // arbitrary axis-dependent scale. Two squared raw differences fit 129 bits.
 9586170        if (firstLength == Fixed64.Zero && secondLength == Fixed64.Zero)
 171        {
 1309172            candidate = new CenteredAxesCandidate2d(
 1309173                Signed576.ExtendValue(Signed320.ExtendValue(Signed192.Raw(firstCenter.X))),
 1309174                Signed576.ExtendValue(Signed320.ExtendValue(Signed192.Raw(firstCenter.Y))),
 1309175                Signed576.ExtendValue(Signed320.ExtendValue(Signed192.Raw(secondCenter.X))),
 1309176                Signed576.ExtendValue(Signed320.ExtendValue(Signed192.Raw(secondCenter.Y))),
 1309177                Signed320.ExtendValue(Scale),
 1309178                Signed832.ExtendValue(GetDot(firstCenter, secondCenter, firstCenter, secondCenter)));
 1309179            return;
 180        }
 181
 8277182        GetClosestCenteredAxisParameters(
 8277183            GetDot(firstAxis, Vector2d.Zero, firstAxis, Vector2d.Zero),
 8277184            GetDot(firstAxis, Vector2d.Zero, secondAxis, Vector2d.Zero),
 8277185            GetDot(secondAxis, Vector2d.Zero, secondAxis, Vector2d.Zero),
 8277186            GetDot(firstCenter, secondCenter, firstAxis, Vector2d.Zero),
 8277187            GetDot(firstCenter, secondCenter, secondAxis, Vector2d.Zero),
 8277188            firstLength,
 8277189            secondLength,
 8277190            out Signed320 firstNumerator,
 8277191            out Signed320 secondNumerator,
 8277192            out Signed320 denominator);
 8277193        Signed576 firstX = GetCenteredAxisPointNumerator(
 8277194            firstCenter.X,
 8277195            firstAxis.X,
 8277196            firstNumerator,
 8277197            denominator);
 8277198        Signed576 firstY = GetCenteredAxisPointNumerator(
 8277199            firstCenter.Y,
 8277200            firstAxis.Y,
 8277201            firstNumerator,
 8277202            denominator);
 8277203        Signed576 secondX = GetCenteredAxisPointNumerator(
 8277204            secondCenter.X,
 8277205            secondAxis.X,
 8277206            secondNumerator,
 8277207            denominator);
 8277208        Signed576 secondY = GetCenteredAxisPointNumerator(
 8277209            secondCenter.Y,
 8277210            secondAxis.Y,
 8277211            secondNumerator,
 8277212            denominator);
 8277213        Signed576 differenceX = WideArithmetic.SubtractSigned576(firstX, secondX);
 8277214        Signed576 differenceY = WideArithmetic.SubtractSigned576(firstY, secondY);
 8277215        candidate = new CenteredAxesCandidate2d(
 8277216            firstX,
 8277217            firstY,
 8277218            secondX,
 8277219            secondY,
 8277220            denominator,
 8277221            WideArithmetic.AddSigned832(
 8277222                WideArithmetic.MultiplySigned576ToSigned832(differenceX, differenceX),
 8277223                WideArithmetic.MultiplySigned576ToSigned832(differenceY, differenceY)));
 8277224    }
 225
 226    private static void GetCenteredAxesSquaredDistance(
 227        Vector3d firstCenter,
 228        Vector3d firstAxis,
 229        Fixed64 firstLength,
 230        Vector3d secondCenter,
 231        Vector3d secondAxis,
 232        Fixed64 secondLength,
 233        out Signed832 squaredNumerator,
 234        out Signed320 denominator)
 235    {
 852236        GetCenteredAxesCandidate(
 852237            firstCenter,
 852238            firstAxis,
 852239            firstLength,
 852240            secondCenter,
 852241            secondAxis,
 852242            secondLength,
 852243            out CenteredAxesCandidate candidate);
 852244        squaredNumerator = candidate.SquaredDistanceNumerator;
 852245        denominator = candidate.ParameterDenominator;
 852246    }
 247
 248    private static void GetCenteredAxesCandidate(
 249        Vector3d firstCenter,
 250        Vector3d firstAxis,
 251        Fixed64 firstLength,
 252        Vector3d secondCenter,
 253        Vector3d secondAxis,
 254        Fixed64 secondLength,
 255        out CenteredAxesCandidate candidate)
 256    {
 257        // Three squared raw endpoint differences fit 130 bits, independently
 258        // of the irrelevant axes and rotations of these point cores.
 893259        if (firstLength == Fixed64.Zero && secondLength == Fixed64.Zero)
 260        {
 290261            candidate = new CenteredAxesCandidate(
 290262                Signed576.ExtendValue(Signed320.ExtendValue(Signed192.Raw(firstCenter.X))),
 290263                Signed576.ExtendValue(Signed320.ExtendValue(Signed192.Raw(firstCenter.Y))),
 290264                Signed576.ExtendValue(Signed320.ExtendValue(Signed192.Raw(firstCenter.Z))),
 290265                Signed576.ExtendValue(Signed320.ExtendValue(Signed192.Raw(secondCenter.X))),
 290266                Signed576.ExtendValue(Signed320.ExtendValue(Signed192.Raw(secondCenter.Y))),
 290267                Signed576.ExtendValue(Signed320.ExtendValue(Signed192.Raw(secondCenter.Z))),
 290268                default,
 290269                default,
 290270                Signed320.ExtendValue(Scale),
 290271                Signed832.ExtendValue(GetDot(firstCenter, secondCenter, firstCenter, secondCenter)));
 290272            return;
 273        }
 274
 603275        GetClosestCenteredAxisParameters(
 603276            GetDot(firstAxis, Vector3d.Zero, firstAxis, Vector3d.Zero),
 603277            GetDot(firstAxis, Vector3d.Zero, secondAxis, Vector3d.Zero),
 603278            GetDot(secondAxis, Vector3d.Zero, secondAxis, Vector3d.Zero),
 603279            GetDot(firstCenter, secondCenter, firstAxis, Vector3d.Zero),
 603280            GetDot(firstCenter, secondCenter, secondAxis, Vector3d.Zero),
 603281            firstLength,
 603282            secondLength,
 603283            out Signed320 firstNumerator,
 603284            out Signed320 secondNumerator,
 603285            out Signed320 denominator);
 603286        Signed576 firstX = GetCenteredAxisPointNumerator(
 603287            firstCenter.X,
 603288            firstAxis.X,
 603289            firstNumerator,
 603290            denominator);
 603291        Signed576 firstY = GetCenteredAxisPointNumerator(
 603292            firstCenter.Y,
 603293            firstAxis.Y,
 603294            firstNumerator,
 603295            denominator);
 603296        Signed576 firstZ = GetCenteredAxisPointNumerator(
 603297            firstCenter.Z,
 603298            firstAxis.Z,
 603299            firstNumerator,
 603300            denominator);
 603301        Signed576 secondX = GetCenteredAxisPointNumerator(
 603302            secondCenter.X,
 603303            secondAxis.X,
 603304            secondNumerator,
 603305            denominator);
 603306        Signed576 secondY = GetCenteredAxisPointNumerator(
 603307            secondCenter.Y,
 603308            secondAxis.Y,
 603309            secondNumerator,
 603310            denominator);
 603311        Signed576 secondZ = GetCenteredAxisPointNumerator(
 603312            secondCenter.Z,
 603313            secondAxis.Z,
 603314            secondNumerator,
 603315            denominator);
 603316        Signed576 differenceX = WideArithmetic.SubtractSigned576(firstX, secondX);
 603317        Signed576 differenceY = WideArithmetic.SubtractSigned576(firstY, secondY);
 603318        Signed576 differenceZ = WideArithmetic.SubtractSigned576(firstZ, secondZ);
 603319        Signed832 squaredDistance = WideArithmetic.AddSigned832(
 603320            WideArithmetic.AddSigned832(
 603321                WideArithmetic.MultiplySigned576ToSigned832(differenceX, differenceX),
 603322                WideArithmetic.MultiplySigned576ToSigned832(differenceY, differenceY)),
 603323            WideArithmetic.MultiplySigned576ToSigned832(differenceZ, differenceZ));
 603324        candidate = new CenteredAxesCandidate(
 603325            firstX,
 603326            firstY,
 603327            firstZ,
 603328            secondX,
 603329            secondY,
 603330            secondZ,
 603331            firstNumerator,
 603332            secondNumerator,
 603333            denominator,
 603334            squaredDistance);
 603335    }
 336
 337    private static bool IsWithinCombinedRadius(
 338        Signed832 squaredDistanceNumerator,
 339        Signed320 distanceDenominator,
 340        Fixed64 firstRadius,
 341        Fixed64 secondRadius,
 342        bool strict = false)
 343    {
 5565344        Signed192 combinedRadius = WideArithmetic.AddSigned192(
 5565345            Signed192.Signed(firstRadius.m_rawValue),
 5565346            Signed192.Signed(secondRadius.m_rawValue));
 5565347        Signed576 radiusNumerator = WideArithmetic.MultiplySigned320(
 5565348            distanceDenominator,
 5565349            Signed320.ExtendValue(combinedRadius));
 5565350        int comparison = WideArithmetic.SubtractSigned832(
 5565351            squaredDistanceNumerator,
 5565352            WideArithmetic.MultiplySigned576ToSigned832(
 5565353                radiusNumerator,
 5565354                radiusNumerator)).Sign;
 5565355        return strict ? comparison < 0 : comparison <= 0;
 356    }
 357
 358    private static void ProjectSecondFromCenteredAxisCap(
 359        Signed320 firstCapNumerator,
 360        Signed192 directionsDot,
 361        Signed192 secondLengthSquared,
 362        Signed192 secondDirectionDotDifference,
 363        out Signed320 secondNumerator,
 364        out Signed320 denominator)
 365    {
 6381366        Signed192 capNumerator = new(
 6381367            firstCapNumerator.Word2,
 6381368            firstCapNumerator.Word1,
 6381369            firstCapNumerator.Word0);
 6381370        secondNumerator = WideArithmetic.AddSigned320(
 6381371            WideArithmetic.MultiplySigned192(
 6381372                DoubleParameterScale,
 6381373                secondDirectionDotDifference),
 6381374            WideArithmetic.MultiplySigned192(capNumerator, directionsDot));
 6381375        denominator = WideArithmetic.MultiplySigned192(
 6381376            DoubleParameterScale,
 6381377            secondLengthSquared);
 6381378    }
 379
 380    private static void ProjectFirstFromCenteredAxisCap(
 381        Signed320 secondCapNumerator,
 382        Signed192 firstLengthSquared,
 383        Signed192 directionsDot,
 384        Signed192 firstDirectionDotDifference,
 385        out Signed320 firstNumerator,
 386        out Signed320 denominator)
 387    {
 5296388        Signed192 capNumerator = new(
 5296389            secondCapNumerator.Word2,
 5296390            secondCapNumerator.Word1,
 5296391            secondCapNumerator.Word0);
 5296392        firstNumerator = WideArithmetic.SubtractSigned320(
 5296393            WideArithmetic.MultiplySigned192(capNumerator, directionsDot),
 5296394            WideArithmetic.MultiplySigned192(
 5296395                DoubleParameterScale,
 5296396                firstDirectionDotDifference));
 5296397        denominator = WideArithmetic.MultiplySigned192(
 5296398            DoubleParameterScale,
 5296399            firstLengthSquared);
 5296400    }
 401
 402    private static bool ClampCenteredAxisParameter(
 403        ref Signed320 numerator,
 404        ref Signed320 denominator,
 405        Fixed64 axisLength)
 406    {
 21129407        Signed576 scaledNumerator = WideArithmetic.MultiplySigned320(
 21129408            numerator,
 21129409            Signed320.ExtendValue(DoubleParameterScale));
 21129410        Signed576 limit = WideArithmetic.MultiplySigned320(
 21129411            denominator,
 21129412            Signed320.ExtendValue(
 21129413                Signed192.Signed(axisLength.m_rawValue)));
 21129414        if (WideArithmetic.SubtractSigned576(
 21129415                scaledNumerator,
 21129416                WideArithmetic.SubtractSigned576(default, limit)).Sign < 0)
 417        {
 6953418            numerator = Signed320.ExtendValue(
 6953419                Signed192.Signed(-axisLength.m_rawValue));
 6953420            denominator = Signed320.ExtendValue(DoubleParameterScale);
 6953421            return true;
 422        }
 14176423        if (WideArithmetic.SubtractSigned576(scaledNumerator, limit).Sign > 0)
 424        {
 6500425            numerator = Signed320.ExtendValue(
 6500426                Signed192.Signed(axisLength.m_rawValue));
 6500427            denominator = Signed320.ExtendValue(DoubleParameterScale);
 6500428            return true;
 429        }
 430
 7676431        return false;
 432    }
 433
 434    private static void SetCenteredAxisCap(
 435        ref Signed320 numerator,
 436        ref Signed320 denominator,
 437        Fixed64 axisLength)
 438    {
 4071439        bool positive = numerator.Sign > 0;
 4071440        numerator = Signed320.ExtendValue(
 4071441            Signed192.Signed(
 4071442                positive ? axisLength.m_rawValue : -axisLength.m_rawValue));
 4071443        denominator = Signed320.ExtendValue(DoubleParameterScale);
 4071444    }
 445
 446    private static bool TryRoundCenteredAxesDistance(
 447        Signed832 squaredNumerator,
 448        Signed320 denominator,
 449        out Fixed64 distance)
 450    {
 4873451        Signed576 root = WideArithmetic.GetFloorSquareRootOfProduct(
 4873452            squaredNumerator,
 4873453            Scale);
 4873454        Signed576 wideDenominator = Signed576.ExtendValue(denominator);
 4873455        distance = Fixed64.GetNonNegativeRawRatioFloor(
 4873456            Signed704.ExtendValue(root),
 4873457            Signed704.ExtendValue(wideDenominator));
 458
 4873459        Signed192 doubledMidpoint = new(
 4873460            0UL,
 4873461            unchecked((ulong)distance.m_rawValue) >> 63,
 4873462            (unchecked((ulong)distance.m_rawValue) << 1) | 1UL);
 4873463        Signed576 midpointNumerator = WideArithmetic.MultiplySigned320(
 4873464            denominator,
 4873465            Signed320.ExtendValue(doubledMidpoint));
 4873466        Signed832 fourSquaredNumerator = WideArithmetic.AddSigned832(
 4873467            WideArithmetic.AddSigned832(squaredNumerator, squaredNumerator),
 4873468            WideArithmetic.AddSigned832(squaredNumerator, squaredNumerator));
 4873469        int midpointComparison = WideArithmetic.SubtractSigned832(
 4873470            fourSquaredNumerator,
 4873471            WideArithmetic.MultiplySigned576ToSigned832(
 4873472                midpointNumerator,
 4873473                midpointNumerator)).Sign;
 4873474        long increment =
 4873475            GetHalfToEvenIncrement(midpointComparison, distance.m_rawValue);
 4873476        if (distance == Fixed64.MaxValue && increment != 0L)
 2477            return false;
 478
 4871479        distance = Fixed64.FromRaw(distance.m_rawValue + increment);
 4871480        return true;
 481    }
 482
 483    private static long GetHalfToEvenIncrement(
 484        int midpointComparison,
 485        long lowerRaw)
 486    {
 4998487        int negative = (int)((uint)midpointComparison >> 31);
 4998488        int nonzero = (int)(
 4998489            (uint)(midpointComparison | -midpointComparison) >> 31);
 4998490        int positive = nonzero & (negative ^ 1);
 4998491        int zero = nonzero ^ 1;
 4998492        return (long)positive | ((long)zero & (lowerRaw & 1L));
 493    }
 494
 495    private static bool TryGetCenteredAxisPointCoordinate(
 496        Fixed64 center,
 497        Fixed64 axis,
 498        Signed320 parameterNumerator,
 499        Signed320 parameterDenominator,
 500        out Fixed64 coordinate)
 501    {
 1084502        if (parameterNumerator.IsZero)
 503        {
 188504            coordinate = center;
 188505            return true;
 506        }
 507
 896508        Signed576 numerator = GetCenteredAxisPointNumerator(
 896509            center,
 896510            axis,
 896511            parameterNumerator,
 896512            parameterDenominator);
 896513        return Fixed64.TryGetSignedRawRatio(
 896514            numerator,
 896515            Signed576.ExtendValue(parameterDenominator),
 896516            out coordinate);
 517    }
 518
 519    private static Signed576 GetCenteredAxisPointNumerator(
 520        Fixed64 center,
 521        Fixed64 axis,
 522        Signed320 parameterNumerator,
 523        Signed320 parameterDenominator) =>
 37622524        WideArithmetic.AddSigned576(
 37622525            WideArithmetic.MultiplySigned320(
 37622526                parameterDenominator,
 37622527                Signed320.ExtendValue(
 37622528                    Signed192.Signed(center.m_rawValue))),
 37622529            WideArithmetic.MultiplySigned320(
 37622530                parameterNumerator,
 37622531                Signed320.ExtendValue(
 37622532                    Signed192.Signed(axis.m_rawValue))));
 533
 534    private static Vector2d GetCenteredAxisLocalOffset(
 535        Signed576 axisPointX,
 536        Signed576 axisPointY,
 537        Vector2d center,
 538        Signed320 axisPointDenominator,
 539        Fixed64 frameRotation)
 540    {
 48541        Signed576 centerX = WideArithmetic.MultiplySigned320(
 48542            axisPointDenominator,
 48543            Signed320.ExtendValue(
 48544                Signed192.Signed(center.X.m_rawValue)));
 48545        Signed576 centerY = WideArithmetic.MultiplySigned320(
 48546            axisPointDenominator,
 48547            Signed320.ExtendValue(
 48548                Signed192.Signed(center.Y.m_rawValue)));
 48549        Signed576 deltaX =
 48550            WideArithmetic.SubtractSigned576(axisPointX, centerX);
 48551        Signed576 deltaY =
 48552            WideArithmetic.SubtractSigned576(axisPointY, centerY);
 48553        if (deltaX.IsZero && deltaY.IsZero)
 37554            return Vector2d.Zero;
 555
 11556        Fixed64 cosine = FixedMath.Cos(frameRotation);
 11557        Fixed64 sine = FixedMath.Sin(frameRotation);
 11558        Signed192 cosineRaw =
 11559            Signed192.Signed(cosine.m_rawValue);
 11560        Signed192 sineRaw =
 11561            Signed192.Signed(sine.m_rawValue);
 11562        Signed576 xNumerator = WideArithmetic.AddSigned576(
 11563            WideArithmetic.MultiplySigned576(deltaX, cosineRaw),
 11564            WideArithmetic.MultiplySigned576(deltaY, sineRaw));
 11565        Signed576 yNumerator = WideArithmetic.SubtractSigned576(
 11566            WideArithmetic.MultiplySigned576(deltaY, cosineRaw),
 11567            WideArithmetic.MultiplySigned576(deltaX, sineRaw));
 11568        Signed192 fixedScale =
 11569            Signed192.Signed(Fixed64.One.m_rawValue);
 11570        xNumerator = WideArithmetic.MultiplySigned576(
 11571            xNumerator,
 11572            fixedScale);
 11573        yNumerator = WideArithmetic.MultiplySigned576(
 11574            yNumerator,
 11575            fixedScale);
 11576        Signed320 rotationDenominator = WideArithmetic.AddSigned320(
 11577            WideArithmetic.MultiplySigned192(cosineRaw, cosineRaw),
 11578            WideArithmetic.MultiplySigned192(sineRaw, sineRaw));
 11579        Signed576 denominator = WideArithmetic.MultiplySigned320(
 11580            axisPointDenominator,
 11581            rotationDenominator);
 11582        _ = Fixed64.TryGetSignedRawRatio(
 11583            xNumerator,
 11584            denominator,
 11585            out Fixed64 x);
 11586        _ = Fixed64.TryGetSignedRawRatio(
 11587            yNumerator,
 11588            denominator,
 11589            out Fixed64 y);
 11590        return new Vector2d(x, y);
 591    }
 592
 593    private static void GetLocalRadialFeature(
 594        Vector2d worldDirection,
 595        Fixed64 radius,
 596        Fixed64 frameRotation,
 597        out Vector2d localDirection,
 598        out Vector2d roundedLocalOffset)
 599    {
 48600        _ = Vector2d.TryRotate(
 48601            worldDirection,
 48602            -frameRotation,
 48603            out localDirection);
 48604        localDirection = WideNormalization.GetNormalized(localDirection);
 48605        roundedLocalOffset = localDirection * radius;
 48606    }
 607
 608    private static void GetCenteredCapsulesDepth(
 609        Signed832 squaredDistanceNumerator,
 610        Signed320 distanceDenominator,
 611        Fixed64 firstRadius,
 612        Fixed64 secondRadius,
 613        bool pointCores,
 614        out Fixed64 depth,
 615        out bool depthIsClamped)
 616    {
 64617        Signed192 combinedRadius = WideArithmetic.AddSigned192(
 64618            Signed192.Signed(firstRadius.m_rawValue),
 64619            Signed192.Signed(secondRadius.m_rawValue));
 64620        if (pointCores)
 621        {
 622            // The canonical point candidate has denominator one and at most
 623            // 130 squared-distance bits. Keep the root wide: a valid radius
 624            // sum and center span can both exceed the public scalar range.
 44625            Signed192 root = WideArithmetic.GetFloorSquareRoot(
 44626                new Signed320(0UL, 0UL, squaredDistanceNumerator.Word2,
 44627                    squaredDistanceNumerator.Word1, squaredDistanceNumerator.Word0),
 44628                out Signed192 remainder);
 44629            Signed192 upperDepth = WideArithmetic.SubtractSigned192(combinedRadius, root);
 630            // sqrt(n) - floor(sqrt(n)) is strictly below one, so this integer
 631            // upper bound exceeds Max exactly when the conceptual depth does.
 44632            depthIsClamped = WideArithmetic.SubtractSigned192(
 44633                upperDepth, Signed192.Signed(long.MaxValue)).Sign > 0;
 44634            if (depthIsClamped)
 635            {
 6636                depth = Fixed64.MaxValue;
 6637                return;
 638            }
 639
 640            // Integer radicands cannot have half-integer roots. As in
 641            // WideGeometry.TryRoundDistance, sqrt(n) rounds up iff remainder > root.
 642            // Overlap admission guarantees a nonnegative final depth.
 38643            depth = Fixed64.FromRaw((long)upperDepth.Low
 38644                - (WideArithmetic.CompareMagnitude(remainder, root) > 0 ? 1L : 0L));
 38645            return;
 646        }
 647
 20648        GetCenteredRadiusDepth(
 20649            squaredDistanceNumerator,
 20650            Signed576.ExtendValue(distanceDenominator),
 20651            combinedRadius,
 20652            out depth,
 20653            out depthIsClamped);
 20654    }
 655
 656    private static void GetCenteredRadiusDepth(
 657        Signed832 squaredDistanceNumerator,
 658        Signed576 distanceDenominator,
 659        Signed192 radius,
 660        out Fixed64 depth,
 661        out bool depthIsClamped)
 662    {
 83663        Signed192 maximumThreshold = WideArithmetic.SubtractSigned192(
 83664            radius,
 83665            Signed192.Signed(Fixed64.MaxValue.m_rawValue));
 83666        if (maximumThreshold.Sign > 0
 83667            && CompareSquaredDistanceToCenteredDepthThreshold(
 83668                squaredDistanceNumerator,
 83669                distanceDenominator,
 83670                maximumThreshold) < 0)
 671        {
 1672            depth = Fixed64.MaxValue;
 1673            depthIsClamped = true;
 1674            return;
 675        }
 676
 82677        Signed576 root = WideArithmetic.GetFloorSquareRootOfProduct(
 82678            squaredDistanceNumerator,
 82679            Scale);
 82680        Signed576 radiusNumerator = WideArithmetic.MultiplySigned576(
 82681            distanceDenominator,
 82682            radius);
 82683        Signed576 approximateDepthNumerator =
 82684            WideArithmetic.SubtractSigned576(radiusNumerator, root);
 82685        depth = Fixed64.GetNonNegativeRawRatioFloor(
 82686            Signed704.ExtendValue(approximateDepthNumerator),
 82687            Signed704.ExtendValue(distanceDenominator));
 688
 82689        Signed192 floorThreshold = WideArithmetic.SubtractSigned192(
 82690            radius,
 82691            Signed192.Signed(depth.m_rawValue));
 82692        if (CompareSquaredDistanceToCenteredDepthThreshold(
 82693                squaredDistanceNumerator,
 82694                distanceDenominator,
 82695                floorThreshold) > 0)
 696        {
 15697            depth = Fixed64.FromRaw(depth.m_rawValue - 1L);
 698        }
 699
 82700        Signed192 doubledRadius = WideArithmetic.AddSigned192(
 82701            radius,
 82702            radius);
 82703        Signed192 doubledMidpoint = WideArithmetic.AddSigned192(
 82704            WideArithmetic.AddSigned192(
 82705                Signed192.Signed(depth.m_rawValue),
 82706                Signed192.Signed(depth.m_rawValue)),
 82707            Scale);
 82708        Signed192 distanceThreshold = WideArithmetic.SubtractSigned192(
 82709            doubledRadius,
 82710            doubledMidpoint);
 82711        if (distanceThreshold.Sign <= 0)
 712        {
 7713            depthIsClamped = false;
 7714            return;
 715        }
 716
 75717        int comparison = CompareSquaredDistanceToCenteredDepthThreshold(
 75718            squaredDistanceNumerator,
 75719            distanceDenominator,
 75720            distanceThreshold,
 75721            quadrupleDistance: true);
 75722        depth = Fixed64.FromRaw(
 75723            depth.m_rawValue
 75724            + GetHalfToEvenIncrement(-comparison, depth.m_rawValue));
 725
 75726        depthIsClamped = false;
 75727    }
 728
 729    private static int CompareSquaredDistanceToCenteredDepthThreshold(
 730        Signed832 squaredDistanceNumerator,
 731        Signed576 distanceDenominator,
 732        Signed192 distanceThreshold,
 733        bool quadrupleDistance = false)
 734    {
 158735        Signed576 thresholdNumerator = WideArithmetic.MultiplySigned576(
 158736            distanceDenominator,
 158737            distanceThreshold);
 158738        Signed832 left = squaredDistanceNumerator;
 158739        if (quadrupleDistance)
 740        {
 75741            left = WideArithmetic.AddSigned832(
 75742                WideArithmetic.AddSigned832(left, left),
 75743                WideArithmetic.AddSigned832(left, left));
 744        }
 158745        return WideArithmetic.SubtractSigned832(
 158746            left,
 158747            WideArithmetic.MultiplySigned576ToSigned832(
 158748                thresholdNumerator,
 158749                thresholdNumerator)).Sign;
 750    }
 751}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredAxisTriangle.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.CenteredAxisTriangle.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12
 13
 14/// <content>
 15/// Provides high-precision closest-point and overlap tests between a
 16/// <see cref="FixedTriangle"/> and a centered, finite-length axis segment
 17/// (e.g. a capsule's core axis), using wide fixed-point arithmetic.
 18/// </content>
 19internal static partial class WideFiniteAxisIntersection
 20{
 21    #region Nested Types
 22
 23    private readonly struct AxisTriangleCandidate
 24    {
 25        internal readonly Signed576 TriangleX;
 26        internal readonly Signed576 TriangleY;
 27        internal readonly Signed576 TriangleZ;
 28        internal readonly Signed576 AxisX;
 29        internal readonly Signed576 AxisY;
 30        internal readonly Signed576 AxisZ;
 31        internal readonly Signed576 Denominator;
 32        internal readonly Signed832 SquaredDistanceNumerator;
 33        internal readonly int EdgeMask;
 34
 35        internal AxisTriangleCandidate(
 36            Signed576 triangleX,
 37            Signed576 triangleY,
 38            Signed576 triangleZ,
 39            Signed576 axisX,
 40            Signed576 axisY,
 41            Signed576 axisZ,
 42            Signed576 denominator,
 43            Signed832 squaredDistanceNumerator,
 44            int edgeMask)
 45        {
 19546            TriangleX = triangleX;
 19547            TriangleY = triangleY;
 19548            TriangleZ = triangleZ;
 19549            AxisX = axisX;
 19550            AxisY = axisY;
 19551            AxisZ = axisZ;
 19552            Denominator = denominator;
 19553            SquaredDistanceNumerator = squaredDistanceNumerator;
 19554            EdgeMask = edgeMask;
 19555        }
 56    }
 57
 58    #endregion
 59
 60    internal static bool TryGetClosestPointsToCenteredAxis(
 61        FixedTriangle triangle,
 62        Vector3d center,
 63        Vector3d axisDirection,
 64        Fixed64 axisLength,
 65        out Vector3d pointOnTriangle,
 66        out Vector3d pointOnAxis)
 67    {
 1368        GetClosestCenteredAxisTriangleCandidate(
 1369            triangle,
 1370            center,
 1371            axisDirection,
 1372            axisLength,
 1373            out AxisTriangleCandidate candidate);
 1374        return TryMaterialize(
 1375            candidate,
 1376            out pointOnTriangle,
 1377            out pointOnAxis);
 78    }
 79
 80    internal static bool DoesCenteredCapsuleTriangleOverlap(
 81        FixedTriangle triangle,
 82        Vector3d center,
 83        Vector3d axisDirection,
 84        Fixed64 axisLength,
 85        Fixed64 radius)
 86    {
 387        GetClosestCenteredAxisTriangleCandidate(
 388            triangle,
 389            center,
 390            axisDirection,
 391            axisLength,
 392            out AxisTriangleCandidate candidate);
 393        Signed576 radiusNumerator = WideArithmetic.MultiplySigned576(
 394            candidate.Denominator,
 395            Signed192.Signed(radius.m_rawValue));
 396        return WideArithmetic.SubtractSigned832(
 397            candidate.SquaredDistanceNumerator,
 398            WideArithmetic.MultiplySigned576ToSigned832(
 399                radiusNumerator,
 3100                radiusNumerator)).Sign <= 0;
 101    }
 102
 103    internal static bool TryGetCenteredCapsuleTriangleContact(
 104        FixedTriangle triangle,
 105        Vector3d center,
 106        Vector3d axisDirection,
 107        Fixed64 axisLength,
 108        Fixed64 radius,
 109        Vector3d fallbackNormal,
 110        out Vector3d pointOnTriangle,
 111        out Vector3d pointOnCapsule,
 112        out Vector3d normal,
 113        out Fixed64 depth)
 114    {
 17115        GetClosestCenteredAxisTriangleCandidate(
 17116            triangle,
 17117            center,
 17118            axisDirection,
 17119            axisLength,
 17120            out AxisTriangleCandidate candidate);
 17121        Signed576 radiusNumerator = WideArithmetic.MultiplySigned576(
 17122            candidate.Denominator,
 17123            Signed192.Signed(radius.m_rawValue));
 17124        if (WideArithmetic.SubtractSigned832(
 17125                candidate.SquaredDistanceNumerator,
 17126                WideArithmetic.MultiplySigned576ToSigned832(
 17127                    radiusNumerator,
 17128                    radiusNumerator)).Sign > 0
 17129            || !TryMaterialize(candidate, out pointOnTriangle, out Vector3d pointOnAxis))
 130        {
 1131            pointOnTriangle = default;
 1132            pointOnCapsule = default;
 1133            normal = default;
 1134            depth = default;
 1135            return false;
 136        }
 137
 16138        normal = WideNormalization.GetNormalized(
 16139            WideArithmetic.SubtractSigned576(candidate.AxisX, candidate.TriangleX),
 16140            WideArithmetic.SubtractSigned576(candidate.AxisY, candidate.TriangleY),
 16141            WideArithmetic.SubtractSigned576(candidate.AxisZ, candidate.TriangleZ));
 16142        if (normal.IsZero)
 12143            normal = fallbackNormal;
 16144        if (candidate.SquaredDistanceNumerator.IsZero && axisLength != Fixed64.Zero)
 145        {
 10146            Span<ulong> values = stackalloc ulong[4 * CapsuleTriangleRayWords];
 10147            Span<int> signs = stackalloc int[4];
 10148            Span<ulong> root = stackalloc ulong[CapsuleTriangleRayRootWords];
 20149            ContactQuadratic n = RayField(values, signs, 0), d = RayField(values, signs, 1);
 10150            GetCoincidentCapsuleRayExit(candidate, center, axisDirection, axisLength, radius,
 10151                -normal, n, d, root, out _);
 10152            GetCapsuleRayDepth(n, d, root, normal, out depth, out bool clamped);
 10153            if (!clamped && TryMaterializeCapsuleRayPoint(candidate, -normal, n, d, root, out pointOnCapsule))
 8154                return true;
 2155            pointOnTriangle = pointOnCapsule = normal = default;
 2156            depth = default;
 2157            return false;
 158        }
 6159        if (!TryOffsetPoint(pointOnAxis, -normal, radius, out pointOnCapsule))
 160        {
 1161            pointOnTriangle = default;
 1162            pointOnCapsule = default;
 1163            normal = default;
 1164            depth = default;
 1165            return false;
 166        }
 5167        GetCenteredCapsuleTriangleDepth(candidate, radius, out depth, out _);
 168
 5169        return true;
 170    }
 171
 172    internal static bool TryGetCenteredCapsuleTriangleLocalContact(
 173        FixedTriangle triangle,
 174        Vector3d center,
 175        Vector3d axisDirection,
 176        Fixed64 axisLength,
 177        Fixed64 radius,
 178        Vector3d fallbackNormal,
 179        FixedQuaternion radialSourceRotation,
 180        FixedQuaternion radialTargetRotation,
 181        out Vector3d pointOnTriangle,
 182        out Fixed64 axisParameter,
 183        out Vector3d normal,
 184        out Fixed64 depth,
 185        out bool depthIsClamped,
 186        bool retainFeature,
 187        out AxisTriangleContactFeature feature,
 188        out Vector3d rayRadialOffset,
 189        out int rayEndpointSign,
 190        out bool hasBoundaryRay,
 191        out int intervalEndpoint,
 192        in RationalSegmentInterval interval = default)
 193    {
 90194        feature = default;
 90195        rayRadialOffset = default;
 90196        rayEndpointSign = 0;
 90197        hasBoundaryRay = false;
 90198        intervalEndpoint = 0;
 199        AxisTriangleCandidate candidate;
 90200        if (interval.IsClipped)
 19201            candidate = GetClosestClippedAxisSegment(triangle.A, triangle.B, center, axisDirection,
 19202                axisLength, interval, out intervalEndpoint);
 203        else
 71204            GetClosestCenteredAxisTriangleCandidate(triangle, center, axisDirection, axisLength, out candidate);
 90205        Signed576 radiusNumerator = WideArithmetic.MultiplySigned576(
 90206            candidate.Denominator,
 90207            Signed192.Signed(radius.m_rawValue));
 90208        if (WideArithmetic.SubtractSigned832(
 90209                candidate.SquaredDistanceNumerator,
 90210                WideArithmetic.MultiplySigned576ToSigned832(
 90211                    radiusNumerator,
 90212                    radiusNumerator)).Sign > 0)
 213        {
 4214            pointOnTriangle = default;
 4215            axisParameter = default;
 4216            normal = default;
 4217            depth = default;
 4218            depthIsClamped = default;
 4219            return false;
 220        }
 221
 86222        Signed576 differenceX = WideArithmetic.SubtractSigned576(
 86223            candidate.AxisX,
 86224            candidate.TriangleX);
 86225        Signed576 differenceY = WideArithmetic.SubtractSigned576(
 86226            candidate.AxisY,
 86227            candidate.TriangleY);
 86228        Signed576 differenceZ = WideArithmetic.SubtractSigned576(
 86229            candidate.AxisZ,
 86230            candidate.TriangleZ);
 86231        if (retainFeature)
 232        {
 59233            int vertex = candidate.EdgeMask == 5 ? 0 : candidate.EdgeMask == 3 ? 1 : candidate.EdgeMask == 6 ? 2 : -1;
 59234            if (candidate.EdgeMask == 7)
 30235                vertex = IsVertex(triangle.A) ? 0 : IsVertex(triangle.B) ? 1 : IsVertex(triangle.C) ? 2 : -1;
 59236            feature = new AxisTriangleContactFeature(candidate.EdgeMask, differenceX, differenceY, differenceZ, vertex);
 237
 238            bool IsVertex(Vector3d point) =>
 239                SubtractVertex(candidate.TriangleX, point.X, candidate.Denominator).IsZero
 240                && SubtractVertex(candidate.TriangleY, point.Y, candidate.Denominator).IsZero
 241                && SubtractVertex(candidate.TriangleZ, point.Z, candidate.Denominator).IsZero;
 242        }
 243
 86244        normal = WideNormalization.GetNormalized(
 86245            differenceX,
 86246            differenceY,
 86247            differenceZ);
 86248        if (normal.IsZero)
 31249            normal = fallbackNormal;
 250
 86251        hasBoundaryRay = candidate.SquaredDistanceNumerator.IsZero && axisLength != Fixed64.Zero;
 86252        if (hasBoundaryRay)
 253        {
 28254            Span<ulong> values = stackalloc ulong[4 * CapsuleTriangleRayWords];
 28255            Span<int> signs = stackalloc int[4];
 28256            Span<ulong> root = stackalloc ulong[CapsuleTriangleRayRootWords];
 56257            ContactQuadratic n = RayField(values, signs, 0), d = RayField(values, signs, 1);
 28258            GetCoincidentCapsuleRayExit(candidate, center, axisDirection, axisLength, radius,
 28259                -normal, n, d, root, out rayEndpointSign);
 28260            GetCapsuleRayDepth(n, d, root, normal, out depth, out depthIsClamped);
 28261            MaterializeCapsuleRayComponents(candidate, center, axisDirection, axisLength,
 28262                -normal, rayEndpointSign, n, d, root, radialSourceRotation, radialTargetRotation,
 28263                out axisParameter, out rayRadialOffset);
 28264            _ = TryMaterialize(candidate, out pointOnTriangle, out _);
 28265            return true;
 266        }
 267
 268        // Both witnesses are convex points on representable local features:
 269        // the triangle point stays within its vertex component intervals and
 270        // the axis offset stays within the representable half length.
 58271        _ = TryGetRelativeCoordinate(
 58272            candidate.TriangleX,
 58273            Fixed64.Zero,
 58274            candidate.Denominator,
 58275            out Fixed64 triangleX);
 58276        _ = TryGetRelativeCoordinate(
 58277            candidate.TriangleY,
 58278            Fixed64.Zero,
 58279            candidate.Denominator,
 58280            out Fixed64 triangleY);
 58281        _ = TryGetRelativeCoordinate(
 58282            candidate.TriangleZ,
 58283            Fixed64.Zero,
 58284            candidate.Denominator,
 58285            out Fixed64 triangleZ);
 58286        axisParameter = Fixed64.Zero;
 58287        if (axisLength != Fixed64.Zero)
 288        {
 16289            _ = TryGetRelativeCoordinate(
 16290                candidate.AxisX,
 16291                center.X,
 16292                candidate.Denominator,
 16293                out Fixed64 axisX);
 16294            _ = TryGetRelativeCoordinate(
 16295                candidate.AxisY,
 16296                center.Y,
 16297                candidate.Denominator,
 16298                out Fixed64 axisY);
 16299            _ = TryGetRelativeCoordinate(
 16300                candidate.AxisZ,
 16301                center.Z,
 16302                candidate.Denominator,
 16303                out Fixed64 axisZ);
 16304            axisParameter = Vector3d.Dot(new Vector3d(axisX, axisY, axisZ), axisDirection);
 305        }
 58306        GetCenteredCapsuleTriangleDepth(
 58307            candidate,
 58308            radius,
 58309            out depth,
 58310            out depthIsClamped);
 311
 58312        pointOnTriangle = new Vector3d(
 58313            triangleX,
 58314            triangleY,
 58315            triangleZ);
 58316        return true;
 317    }
 318
 319    private static void GetClosestCenteredAxisTriangleCandidate(
 320        FixedTriangle triangle,
 321        Vector3d center,
 322        Vector3d axisDirection,
 323        Fixed64 axisLength,
 324        out AxisTriangleCandidate best)
 325    {
 104326        if (axisLength == Fixed64.Zero && triangle.TryGetClosestPointBarycentrics(center,
 104327            out Signed320 numeratorB, out Signed320 numeratorC, out Signed320 pointDenominator, out int edgeMask))
 328        {
 329            // Select the sphere's single feature before constructing any witness.
 330            // Common barycentric scale is exact and has no geometric meaning.
 28331            Span<Signed320> weights = stackalloc Signed320[3] { numeratorB, numeratorC, pointDenominator };
 28332            WideArithmetic.ReduceCommonScale(weights);
 84333            numeratorB = weights[0]; numeratorC = weights[1]; pointDenominator = weights[2];
 28334            best = CreateAxisTriangleCandidate(
 28335                GetBarycentricCoordinateNumerator(triangle.A.X, triangle.B.X, triangle.C.X, numeratorB, numeratorC, poin
 28336                GetBarycentricCoordinateNumerator(triangle.A.Y, triangle.B.Y, triangle.C.Y, numeratorB, numeratorC, poin
 28337                GetBarycentricCoordinateNumerator(triangle.A.Z, triangle.B.Z, triangle.C.Z, numeratorB, numeratorC, poin
 28338                WideArithmetic.MultiplySigned320(Signed320.ExtendValue(Signed192.Raw(center.X)), pointDenominator),
 28339                WideArithmetic.MultiplySigned320(Signed320.ExtendValue(Signed192.Raw(center.Y)), pointDenominator),
 28340                WideArithmetic.MultiplySigned320(Signed320.ExtendValue(Signed192.Raw(center.Z)), pointDenominator),
 28341                Signed576.ExtendValue(pointDenominator), edgeMask);
 28342            return;
 343        }
 76344        bool found = TryGetCenteredAxisTriangleFaceCandidate(
 76345            triangle,
 76346            center,
 76347            axisDirection,
 76348            axisLength,
 76349            out best,
 76350            out bool degenerate);
 351        // The closest segment point to the infinite plane is a lower bound
 352        // for every triangle point; an admitted projection cannot lose to an edge.
 116353        if (found) return;
 36354        KeepCenteredAxisTriangleEdgeCandidate(
 36355            triangle.A,
 36356            triangle.B,
 36357            center,
 36358            axisDirection,
 36359            axisLength,
 36360            degenerate ? 7 : 1, 5, 3,
 36361            ref found,
 36362            ref best);
 36363        KeepCenteredAxisTriangleEdgeCandidate(
 36364            triangle.B,
 36365            triangle.C,
 36366            center,
 36367            axisDirection,
 36368            axisLength,
 36369            degenerate ? 7 : 2, 3, 6,
 36370            ref found,
 36371            ref best);
 36372        KeepCenteredAxisTriangleEdgeCandidate(
 36373            triangle.C,
 36374            triangle.A,
 36375            center,
 36376            axisDirection,
 36377            axisLength,
 36378            degenerate ? 7 : 4, 6, 5,
 36379            ref found,
 36380            ref best);
 36381    }
 382
 383    private static Signed576 GetBarycentricCoordinateNumerator(Fixed64 a, Fixed64 b, Fixed64 c,
 384        Signed320 numeratorB, Signed320 numeratorC, Signed320 denominator) =>
 385        // Full-domain differences are 65 bits, barycentrics at most 262 bits;
 386        // each coordinate and its squared separation fit the existing 576/832 domains.
 84387        WideArithmetic.AddSigned576(WideArithmetic.AddSigned576(
 84388            WideArithmetic.MultiplySigned320(Signed320.ExtendValue(Signed192.Raw(a)), denominator),
 84389            WideArithmetic.MultiplySigned320(Signed320.ExtendValue(GetComponentDifference(b, a)), numeratorB)),
 84390            WideArithmetic.MultiplySigned320(Signed320.ExtendValue(GetComponentDifference(c, a)), numeratorC));
 391
 392    private static bool TryGetCenteredAxisTriangleFaceCandidate(
 393        FixedTriangle triangle,
 394        Vector3d center,
 395        Vector3d axisDirection,
 396        Fixed64 axisLength,
 397        out AxisTriangleCandidate candidate,
 398        out bool degenerate)
 399    {
 76400        GetTriangleNormal(
 76401            triangle,
 76402            out Signed192 normalX,
 76403            out Signed192 normalY,
 76404            out Signed192 normalZ);
 76405        Signed320 normalSquared = WideGeometry.GetSquaredMagnitude(
 76406            normalX,
 76407            normalY,
 76408            normalZ,
 76409            out _,
 76410            out _,
 76411            out _);
 76412        degenerate = normalSquared.IsZero;
 76413        if (degenerate)
 414        {
 27415            candidate = default;
 27416            return false;
 417        }
 418
 419        // A positive common normal scale carries no plane geometry; cancel
 420        // it before it multiplies every rational distance and depth coefficient.
 49421        Span<Signed320> primitive = stackalloc Signed320[3]
 49422        {
 49423            Signed320.ExtendValue(normalX), Signed320.ExtendValue(normalY), Signed320.ExtendValue(normalZ)
 49424        };
 49425        WideArithmetic.ReduceCommonScale(primitive);
 49426        normalX = Signed192.NarrowProven(primitive[0]);
 49427        normalY = Signed192.NarrowProven(primitive[1]);
 49428        normalZ = Signed192.NarrowProven(primitive[2]);
 49429        normalSquared = WideGeometry.GetSquaredMagnitude(normalX, normalY, normalZ, out _, out _, out _);
 430
 49431        Signed320 planeProjection = GetWideDot(
 49432            normalX,
 49433            normalY,
 49434            normalZ,
 49435            center,
 49436            triangle.A);
 49437        Signed320 axisProjection = GetWideDot(
 49438            normalX,
 49439            normalY,
 49440            normalZ,
 49441            axisDirection,
 49442            Vector3d.Zero);
 49443        if (axisProjection.IsZero)
 444        {
 445            // A parallel core has constant plane distance. Test center and
 446            // exact finite endpoints before edges cover any projected crossings.
 12447            if (TryCreateCenteredAxisTriangleFaceCandidate(triangle, center, axisDirection,
 12448                normalX, normalY, normalZ, normalSquared, default,
 18449                Signed320.ExtendValue(Signed192.Signed(1)), out candidate)) return true;
 7450            if (axisLength == Fixed64.Zero) return false;
 5451            Signed320 capDenominator = Signed320.ExtendValue(DoubleParameterScale);
 5452            if (TryCreateCenteredAxisTriangleFaceCandidate(triangle, center, axisDirection,
 5453                normalX, normalY, normalZ, normalSquared,
 5454                Signed320.ExtendValue(Signed192.Signed(-axisLength.m_rawValue)),
 7455                capDenominator, out candidate)) return true;
 3456            return TryCreateCenteredAxisTriangleFaceCandidate(triangle, center, axisDirection,
 3457                normalX, normalY, normalZ, normalSquared,
 3458                Signed320.ExtendValue(Signed192.Raw(axisLength)), capDenominator, out candidate);
 459        }
 460
 37461        Signed320 parameterNumerator =
 37462            WideArithmetic.SubtractSigned320(default, planeProjection);
 37463        Signed320 parameterDenominator = axisProjection;
 37464        if (parameterDenominator.Sign < 0)
 465        {
 34466            parameterNumerator =
 34467                WideArithmetic.SubtractSigned320(default, parameterNumerator);
 34468            parameterDenominator =
 34469                WideArithmetic.SubtractSigned320(default, parameterDenominator);
 470        }
 37471        ClampCenteredAxisParameter(
 37472            ref parameterNumerator,
 37473            ref parameterDenominator,
 37474            axisLength);
 475
 37476        return TryCreateCenteredAxisTriangleFaceCandidate(triangle, center, axisDirection,
 37477            normalX, normalY, normalZ, normalSquared, parameterNumerator, parameterDenominator, out candidate);
 478    }
 479
 480    private static bool TryCreateCenteredAxisTriangleFaceCandidate(
 481        FixedTriangle triangle, Vector3d center, Vector3d axisDirection,
 482        Signed192 normalX, Signed192 normalY, Signed192 normalZ, Signed320 normalSquared,
 483        Signed320 parameterNumerator, Signed320 parameterDenominator,
 484        out AxisTriangleCandidate candidate)
 485    {
 486        // Zero axial displacement has denominator one; retain no artificial
 487        // parameter scale in every downstream plane-distance coefficient.
 57488        if (parameterNumerator.IsZero)
 29489            parameterDenominator = Signed320.ExtendValue(Signed192.Signed(1));
 57490        GetRationalPoint(
 57491            center,
 57492            axisDirection,
 57493            parameterNumerator,
 57494            parameterDenominator,
 57495            out Signed576 axisX,
 57496            out Signed576 axisY,
 57497            out Signed576 axisZ);
 57498        Signed576 wideParameterDenominator =
 57499            Signed576.ExtendValue(parameterDenominator);
 57500        if (!ContainsTriangleProjection(
 57501                triangle,
 57502                normalX,
 57503                normalY,
 57504                normalZ,
 57505                axisX,
 57506                axisY,
 57507                axisZ,
 57508                wideParameterDenominator,
 57509                out int edgeMask))
 510        {
 17511            candidate = default;
 17512            return false;
 513        }
 514
 40515        Signed576 planeDistanceNumerator = WideArithmetic.AddSigned576(
 40516            WideArithmetic.AddSigned576(
 40517                WideArithmetic.MultiplySigned576(
 40518                    SubtractVertex(axisX, triangle.A.X, parameterDenominator),
 40519                    normalX),
 40520                WideArithmetic.MultiplySigned576(
 40521                    SubtractVertex(axisY, triangle.A.Y, parameterDenominator),
 40522                    normalY)),
 40523            WideArithmetic.MultiplySigned576(
 40524                SubtractVertex(axisZ, triangle.A.Z, parameterDenominator),
 40525                normalZ));
 526        // A full-domain center scaled by the face parameter denominator plus
 527        // one normalized-axis parameter product needs fewer than 320 signed bits.
 40528        Signed320 narrowAxisX = new(
 40529            axisX.Word4,
 40530            axisX.Word3,
 40531            axisX.Word2,
 40532            axisX.Word1,
 40533            axisX.Word0);
 40534        Signed320 narrowAxisY = new(
 40535            axisY.Word4,
 40536            axisY.Word3,
 40537            axisY.Word2,
 40538            axisY.Word1,
 40539            axisY.Word0);
 40540        Signed320 narrowAxisZ = new(
 40541            axisZ.Word4,
 40542            axisZ.Word3,
 40543            axisZ.Word2,
 40544            axisZ.Word1,
 40545            axisZ.Word0);
 40546        Signed576 denominator = WideArithmetic.MultiplySigned320(
 40547            parameterDenominator,
 40548            normalSquared);
 40549        Signed576 scaledAxisX = WideArithmetic.MultiplySigned320(
 40550            narrowAxisX,
 40551            normalSquared);
 40552        Signed576 scaledAxisY = WideArithmetic.MultiplySigned320(
 40553            narrowAxisY,
 40554            normalSquared);
 40555        Signed576 scaledAxisZ = WideArithmetic.MultiplySigned320(
 40556            narrowAxisZ,
 40557            normalSquared);
 40558        Signed576 triangleX = WideArithmetic.SubtractSigned576(
 40559            scaledAxisX,
 40560            WideArithmetic.MultiplySigned576(planeDistanceNumerator, normalX));
 40561        Signed576 triangleY = WideArithmetic.SubtractSigned576(
 40562            scaledAxisY,
 40563            WideArithmetic.MultiplySigned576(planeDistanceNumerator, normalY));
 40564        Signed576 triangleZ = WideArithmetic.SubtractSigned576(
 40565            scaledAxisZ,
 40566            WideArithmetic.MultiplySigned576(planeDistanceNumerator, normalZ));
 40567        candidate = CreateAxisTriangleCandidate(
 40568            triangleX,
 40569            triangleY,
 40570            triangleZ,
 40571            scaledAxisX,
 40572            scaledAxisY,
 40573            scaledAxisZ,
 40574            denominator,
 40575            edgeMask);
 40576        return true;
 577    }
 578
 579    private static void KeepCenteredAxisTriangleEdgeCandidate(
 580        Vector3d edgeStart,
 581        Vector3d edgeEnd,
 582        Vector3d center,
 583        Vector3d axisDirection,
 584        Fixed64 axisLength,
 585        int edgeMask, int startMask, int endMask,
 586        ref bool found,
 587        ref AxisTriangleCandidate best)
 588    {
 108589        GetClosestCenteredAxisSegmentParameters(
 108590            center,
 108591            axisDirection,
 108592            axisLength,
 108593            edgeStart,
 108594            edgeEnd,
 108595            out Signed320 axisNumerator,
 108596            out Signed320 edgeNumerator,
 108597            out Signed320 denominator);
 108598        GetRationalPoint(
 108599            center,
 108600            axisDirection,
 108601            axisNumerator,
 108602            denominator,
 108603            out Signed576 axisX,
 108604            out Signed576 axisY,
 108605            out Signed576 axisZ);
 108606        GetRationalSegmentPoint(
 108607            edgeStart,
 108608            edgeEnd,
 108609            edgeNumerator,
 108610            denominator,
 108611            out Signed576 edgeX,
 108612            out Signed576 edgeY,
 108613            out Signed576 edgeZ);
 614        // Retain the exact clamped edge feature before anchor rounding. A
 615        // degenerate triangle has no trusted face and keeps the conservative mask.
 108616        if (edgeMask != 7)
 617        {
 33618            if (edgeNumerator.IsZero) edgeMask = startMask;
 28619            else if (WideArithmetic.SubtractSigned320(edgeNumerator, denominator).IsZero) edgeMask = endMask;
 620        }
 108621        AxisTriangleCandidate candidate = CreateAxisTriangleCandidate(
 108622            edgeX,
 108623            edgeY,
 108624            edgeZ,
 108625            axisX,
 108626            axisY,
 108627            axisZ,
 108628            Signed576.ExtendValue(denominator),
 108629            edgeMask);
 108630        if (found
 108631            && WideArithmetic.CompareNonNegativeSquaredRatios(
 108632                candidate.SquaredDistanceNumerator,
 108633                candidate.Denominator,
 108634                best.SquaredDistanceNumerator,
 108635                best.Denominator) >= 0)
 636        {
 68637            return;
 638        }
 639
 40640        found = true;
 40641        best = candidate;
 40642    }
 643
 644    private static void GetClosestCenteredAxisSegmentParameters(
 645        Vector3d center,
 646        Vector3d axis,
 647        Fixed64 axisLength,
 648        Vector3d edgeStart,
 649        Vector3d edgeEnd,
 650        out Signed320 axisNumerator,
 651        out Signed320 edgeNumerator,
 652        out Signed320 denominator)
 653    {
 127654        Signed192 axisSquared = GetDot(axis, Vector3d.Zero, axis, Vector3d.Zero);
 127655        Signed192 directionsDot = GetDot(axis, Vector3d.Zero, edgeEnd, edgeStart);
 127656        Signed192 edgeSquared = GetDot(edgeEnd, edgeStart, edgeEnd, edgeStart);
 127657        Signed192 axisDotDifference = GetDot(center, edgeStart, axis, Vector3d.Zero);
 127658        Signed192 edgeDotDifference = GetDot(center, edgeStart, edgeEnd, edgeStart);
 127659        if (edgeSquared.IsZero)
 660        {
 50661            axisNumerator = Signed320.ExtendValue(
 50662                WideArithmetic.SubtractSigned192(default, axisDotDifference));
 50663            denominator = Signed320.ExtendValue(axisSquared);
 50664            ClampCenteredAxisParameter(
 50665                ref axisNumerator,
 50666                ref denominator,
 50667                axisLength);
 50668            edgeNumerator = default;
 50669            return;
 670        }
 671
 77672        if (axisLength == Fixed64.Zero)
 673        {
 674            // A sphere core is one point: its exact edge fraction needs no
 675            // line-line determinant, axial clamp or endpoint reprojection.
 27676            axisNumerator = default;
 27677            edgeNumerator = Signed320.ExtendValue(edgeDotDifference);
 27678            denominator = Signed320.ExtendValue(edgeSquared);
 27679            ClampUnitParameter(ref edgeNumerator, ref denominator);
 27680            Span<Signed320> ratio = stackalloc Signed320[2] { edgeNumerator, denominator };
 27681            WideArithmetic.ReduceCommonScale(ratio);
 54682            edgeNumerator = ratio[0]; denominator = ratio[1];
 27683            return;
 684        }
 685
 50686        Signed320 determinant = WideArithmetic.MultiplySubtract(
 50687            axisSquared,
 50688            edgeSquared,
 50689            directionsDot,
 50690            directionsDot);
 50691        if (determinant.Sign > 0)
 692        {
 22693            axisNumerator = WideArithmetic.MultiplySubtract(
 22694                directionsDot,
 22695                edgeDotDifference,
 22696                edgeSquared,
 22697                axisDotDifference);
 22698            edgeNumerator = WideArithmetic.MultiplySubtract(
 22699                axisSquared,
 22700                edgeDotDifference,
 22701                directionsDot,
 22702                axisDotDifference);
 22703            denominator = determinant;
 22704            if (!ClampCenteredAxisParameter(
 22705                    ref axisNumerator,
 22706                    ref denominator,
 22707                    axisLength))
 708            {
 15709                if (!ClampUnitParameter(ref edgeNumerator, ref denominator))
 11710                    return;
 711
 4712                ProjectAxisFromEdgeCap(
 4713                    edgeNumerator,
 4714                    axisSquared,
 4715                    directionsDot,
 4716                    axisDotDifference,
 4717                    out axisNumerator,
 4718                    out denominator);
 4719                if (!ClampCenteredAxisParameter(
 4720                        ref axisNumerator,
 4721                        ref denominator,
 4722                        axisLength))
 723                {
 3724                    edgeNumerator = WideArithmetic.MultiplySigned192(
 3725                        new Signed192(
 3726                            edgeNumerator.Word2,
 3727                            edgeNumerator.Word1,
 3728                            edgeNumerator.Word0),
 3729                        axisSquared);
 3730                    return;
 731                }
 732
 1733                SetUnitCapAtCenteredDenominator(
 1734                    ref edgeNumerator,
 1735                    ref denominator);
 1736                return;
 737            }
 738        }
 739        else
 740        {
 741            // Parallel finite segments have a whole closest-point interval.
 742            // Keep its exact midpoint when the interval has positive length;
 743            // selecting an arbitrary cap would turn an interior edge family
 744            // into a corner contact. All pre-product terms fit Signed192
 745            // (normalized axis <=34 bits, full-domain differences <=65 bits).
 28746            Signed320 first = WideArithmetic.MultiplySigned192(DoubleParameterScale,
 28747                WideArithmetic.SubtractSigned192(default, axisDotDifference));
 28748            Signed320 second = WideArithmetic.MultiplySigned192(DoubleParameterScale,
 28749                WideArithmetic.SubtractSigned192(directionsDot, axisDotDifference));
 28750            if (WideArithmetic.SubtractSigned320(first, second).Sign > 0)
 13751                (first, second) = (second, first);
 28752            Signed320 cap = WideArithmetic.MultiplySigned192(Signed192.Raw(axisLength), axisSquared);
 28753            Signed320 negativeCap = WideArithmetic.SubtractSigned320(default, cap);
 36754            if (WideArithmetic.SubtractSigned320(first, negativeCap).Sign < 0) first = negativeCap;
 35755            if (WideArithmetic.SubtractSigned320(second, cap).Sign > 0) second = cap;
 28756            if (WideArithmetic.SubtractSigned320(second, first).Sign > 0)
 757            {
 16758                Signed192 midpoint = Signed192.NarrowProven(WideArithmetic.AddSigned320(first, second));
 16759                Signed320 common = WideArithmetic.MultiplySigned192(DoubleParameterScale, axisSquared);
 16760                Signed192 twiceCommon = Signed192.NarrowProven(WideArithmetic.AddSigned320(common, common));
 16761                edgeNumerator = WideArithmetic.AddSigned320(
 16762                    WideArithmetic.MultiplySigned192(edgeDotDifference, twiceCommon),
 16763                    WideArithmetic.MultiplySigned192(directionsDot, midpoint));
 16764                axisNumerator = WideArithmetic.MultiplySigned192(midpoint, edgeSquared);
 16765                denominator = WideArithmetic.MultiplySigned192(twiceCommon, edgeSquared);
 16766                return;
 767            }
 12768            axisNumerator = Signed320.ExtendValue(
 12769                Signed192.Signed(-axisLength.m_rawValue));
 12770            denominator = Signed320.ExtendValue(DoubleParameterScale);
 771        }
 772
 19773        ProjectEdgeFromCenteredCap(
 19774            axisNumerator,
 19775            directionsDot,
 19776            edgeSquared,
 19777            edgeDotDifference,
 19778            out edgeNumerator,
 19779            out denominator);
 19780        if (!ClampUnitParameter(ref edgeNumerator, ref denominator))
 781        {
 6782            axisNumerator = WideArithmetic.MultiplySigned192(
 6783                new Signed192(
 6784                    axisNumerator.Word2,
 6785                    axisNumerator.Word1,
 6786                    axisNumerator.Word0),
 6787                edgeSquared);
 6788            return;
 789        }
 790
 13791        ProjectAxisFromEdgeCap(
 13792            edgeNumerator,
 13793            axisSquared,
 13794            directionsDot,
 13795            axisDotDifference,
 13796            out axisNumerator,
 13797            out denominator);
 13798        if (!ClampCenteredAxisParameter(
 13799                ref axisNumerator,
 13800                ref denominator,
 13801                axisLength))
 802        {
 2803            edgeNumerator = WideArithmetic.MultiplySigned192(
 2804                new Signed192(
 2805                    edgeNumerator.Word2,
 2806                    edgeNumerator.Word1,
 2807                    edgeNumerator.Word0),
 2808                axisSquared);
 2809            return;
 810        }
 811
 11812        SetUnitCapAtCenteredDenominator(
 11813            ref edgeNumerator,
 11814            ref denominator);
 11815    }
 816
 817    private static bool ClampUnitParameter(
 818        ref Signed320 numerator,
 819        ref Signed320 denominator)
 820    {
 61821        if (numerator.Sign < 0)
 822        {
 15823            numerator = default;
 15824            denominator = Signed320.ExtendValue(ParameterScale);
 15825            return true;
 826        }
 46827        if (WideArithmetic.SubtractSigned320(numerator, denominator).Sign > 0)
 828        {
 12829            numerator = Signed320.ExtendValue(ParameterScale);
 12830            denominator = Signed320.ExtendValue(ParameterScale);
 12831            return true;
 832        }
 833
 34834        return false;
 835    }
 836
 837    private static void ProjectEdgeFromCenteredCap(
 838        Signed320 axisCapNumerator,
 839        Signed192 directionsDot,
 840        Signed192 edgeSquared,
 841        Signed192 edgeDotDifference,
 842        out Signed320 edgeNumerator,
 843        out Signed320 denominator)
 844    {
 19845        Signed192 capNumerator = new(
 19846            axisCapNumerator.Word2,
 19847            axisCapNumerator.Word1,
 19848            axisCapNumerator.Word0);
 19849        edgeNumerator = WideArithmetic.AddSigned320(
 19850            WideArithmetic.MultiplySigned192(
 19851                DoubleParameterScale,
 19852                edgeDotDifference),
 19853            WideArithmetic.MultiplySigned192(
 19854                capNumerator,
 19855                directionsDot));
 19856        denominator = WideArithmetic.MultiplySigned192(
 19857            DoubleParameterScale,
 19858            edgeSquared);
 19859    }
 860
 861    private static void ProjectAxisFromEdgeCap(
 862        Signed320 edgeCapNumerator,
 863        Signed192 axisSquared,
 864        Signed192 directionsDot,
 865        Signed192 axisDotDifference,
 866        out Signed320 axisNumerator,
 867        out Signed320 denominator)
 868    {
 17869        Signed192 capNumerator = new(
 17870            edgeCapNumerator.Word2,
 17871            edgeCapNumerator.Word1,
 17872            edgeCapNumerator.Word0);
 17873        axisNumerator = WideArithmetic.SubtractSigned320(
 17874            WideArithmetic.MultiplySigned192(
 17875                capNumerator,
 17876                directionsDot),
 17877            WideArithmetic.MultiplySigned192(
 17878                ParameterScale,
 17879                axisDotDifference));
 17880        denominator = WideArithmetic.MultiplySigned192(
 17881            ParameterScale,
 17882            axisSquared);
 17883    }
 884
 885    private static void SetUnitCapAtCenteredDenominator(
 886        ref Signed320 numerator,
 887        ref Signed320 denominator)
 888    {
 12889        bool positive = numerator.Sign > 0;
 12890        numerator = positive
 12891            ? Signed320.ExtendValue(DoubleParameterScale)
 12892            : default;
 12893        denominator = Signed320.ExtendValue(DoubleParameterScale);
 12894    }
 895
 896    private static void GetRationalPoint(
 897        Vector3d center,
 898        Vector3d direction,
 899        Signed320 parameterNumerator,
 900        Signed320 denominator,
 901        out Signed576 x,
 902        out Signed576 y,
 903        out Signed576 z)
 904    {
 171905        x = GetRationalCoordinate(
 171906            center.X,
 171907            Signed192.Signed(direction.X.m_rawValue),
 171908            parameterNumerator,
 171909            denominator);
 171910        y = GetRationalCoordinate(
 171911            center.Y,
 171912            Signed192.Signed(direction.Y.m_rawValue),
 171913            parameterNumerator,
 171914            denominator);
 171915        z = GetRationalCoordinate(
 171916            center.Z,
 171917            Signed192.Signed(direction.Z.m_rawValue),
 171918            parameterNumerator,
 171919            denominator);
 171920    }
 921
 922    private static void GetRationalSegmentPoint(
 923        Vector3d start,
 924        Vector3d end,
 925        Signed320 parameterNumerator,
 926        Signed320 denominator,
 927        out Signed576 x,
 928        out Signed576 y,
 929        out Signed576 z)
 930    {
 114931        x = GetRationalCoordinate(
 114932            start.X,
 114933            GetComponentDifference(end.X, start.X),
 114934            parameterNumerator,
 114935            denominator);
 114936        y = GetRationalCoordinate(
 114937            start.Y,
 114938            GetComponentDifference(end.Y, start.Y),
 114939            parameterNumerator,
 114940            denominator);
 114941        z = GetRationalCoordinate(
 114942            start.Z,
 114943            GetComponentDifference(end.Z, start.Z),
 114944            parameterNumerator,
 114945            denominator);
 114946    }
 947
 948    private static Signed576 GetRationalCoordinate(
 949        Fixed64 origin,
 950        Signed192 direction,
 951        Signed320 parameterNumerator,
 952        Signed320 denominator) =>
 855953        WideArithmetic.AddSigned576(
 855954            WideArithmetic.MultiplySigned320(
 855955                Signed320.ExtendValue(
 855956                    Signed192.Signed(origin.m_rawValue)),
 855957                denominator),
 855958            WideArithmetic.MultiplySigned320(
 855959                Signed320.ExtendValue(direction),
 855960                parameterNumerator));
 961
 962    private static AxisTriangleCandidate CreateAxisTriangleCandidate(
 963        Signed576 triangleX,
 964        Signed576 triangleY,
 965        Signed576 triangleZ,
 966        Signed576 axisX,
 967        Signed576 axisY,
 968        Signed576 axisZ,
 969        Signed576 denominator,
 970        int edgeMask)
 971    {
 195972        Signed576 differenceX =
 195973            WideArithmetic.SubtractSigned576(axisX, triangleX);
 195974        Signed576 differenceY =
 195975            WideArithmetic.SubtractSigned576(axisY, triangleY);
 195976        Signed576 differenceZ =
 195977            WideArithmetic.SubtractSigned576(axisZ, triangleZ);
 195978        Signed832 squaredDistance = WideArithmetic.AddSigned832(
 195979            WideArithmetic.AddSigned832(
 195980                WideArithmetic.MultiplySigned576ToSigned832(
 195981                    differenceX,
 195982                    differenceX),
 195983                WideArithmetic.MultiplySigned576ToSigned832(
 195984                    differenceY,
 195985                    differenceY)),
 195986            WideArithmetic.MultiplySigned576ToSigned832(
 195987                differenceZ,
 195988                differenceZ));
 195989        return new AxisTriangleCandidate(
 195990            triangleX,
 195991            triangleY,
 195992            triangleZ,
 195993            axisX,
 195994            axisY,
 195995            axisZ,
 195996            denominator,
 195997            squaredDistance,
 195998            edgeMask);
 999    }
 1000
 1001
 1002    private static void GetCenteredCapsuleTriangleDepth(
 1003        AxisTriangleCandidate candidate,
 1004        Fixed64 radius,
 1005        out Fixed64 depth,
 1006        out bool depthIsClamped)
 1007    {
 631008        GetCenteredRadiusDepth(
 631009            candidate.SquaredDistanceNumerator,
 631010            candidate.Denominator,
 631011            Signed192.Signed(radius.m_rawValue),
 631012            out depth,
 631013            out depthIsClamped);
 631014    }
 1015}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredAxisTriangle.Materialization.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.CenteredAxisTriangle.Materialization.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
 8namespace FixedMathSharp.Geometry;
 9
 10/// <content>
 11/// Helpers for materializing wide-precision axis/triangle intersection
 12/// candidates back into <see cref="Fixed64"/>/<see cref="Vector3d"/> results,
 13/// including triangle containment tests and normal/edge computations.
 14/// </content>
 15internal static partial class WideFiniteAxisIntersection
 16{
 17    private static bool ContainsTriangleProjection(
 18        FixedTriangle triangle,
 19        Signed192 normalX,
 20        Signed192 normalY,
 21        Signed192 normalZ,
 22        Signed576 pointX,
 23        Signed576 pointY,
 24        Signed576 pointZ,
 25        Signed576 denominator,
 26        out int edgeMask)
 27    {
 6428        edgeMask = 0;
 6429        int ab = GetTriangleEdgeSideSign(triangle.A, triangle.B, normalX, normalY, normalZ,
 6430            pointX, pointY, pointZ, denominator);
 6831        if (ab < 0) return false;
 6032        int bc = GetTriangleEdgeSideSign(triangle.B, triangle.C, normalX, normalY, normalZ,
 6033            pointX, pointY, pointZ, denominator);
 6934        if (bc < 0) return false;
 5135        int ca = GetTriangleEdgeSideSign(triangle.C, triangle.A, normalX, normalY, normalZ,
 5136            pointX, pointY, pointZ, denominator);
 5737        if (ca < 0) return false;
 38        // Orthogonal projection preserves these edge halfspaces, so admission
 39        // also supplies exact feature identity without testing the final witness again.
 4540        edgeMask = (ab == 0 ? 1 : 0) | (bc == 0 ? 2 : 0) | (ca == 0 ? 4 : 0);
 4541        return true;
 42    }
 43
 44    private static int GetTriangleEdgeSideSign(
 45        Vector3d start,
 46        Vector3d end,
 47        Signed192 normalX,
 48        Signed192 normalY,
 49        Signed192 normalZ,
 50        Signed576 pointX,
 51        Signed576 pointY,
 52        Signed576 pointZ,
 53        Signed576 denominator)
 54    {
 17555        Signed192 edgeX = GetComponentDifference(end.X, start.X);
 17556        Signed192 edgeY = GetComponentDifference(end.Y, start.Y);
 17557        Signed192 edgeZ = GetComponentDifference(end.Z, start.Z);
 17558        Signed320 inwardX = WideArithmetic.MultiplySubtract(
 17559            normalY,
 17560            edgeZ,
 17561            normalZ,
 17562            edgeY);
 17563        Signed320 inwardY = WideArithmetic.MultiplySubtract(
 17564            normalZ,
 17565            edgeX,
 17566            normalX,
 17567            edgeZ);
 17568        Signed320 inwardZ = WideArithmetic.MultiplySubtract(
 17569            normalX,
 17570            edgeY,
 17571            normalY,
 17572            edgeX);
 17573        Signed704 side = WideArithmetic.AddSigned704(
 17574            WideArithmetic.AddSigned704(
 17575                WideArithmetic.MultiplySigned576ToSigned704(
 17576                    SubtractVertex(pointX, start.X, denominator),
 17577                    inwardX),
 17578                WideArithmetic.MultiplySigned576ToSigned704(
 17579                    SubtractVertex(pointY, start.Y, denominator),
 17580                    inwardY)),
 17581            WideArithmetic.MultiplySigned576ToSigned704(
 17582                SubtractVertex(pointZ, start.Z, denominator),
 17583                inwardZ));
 17584        return side.Sign;
 85    }
 86
 87    private static Signed576 SubtractVertex(
 88        Signed576 point,
 89        Fixed64 vertex,
 90        Signed320 denominator) =>
 12091        WideArithmetic.SubtractSigned576(
 12092            point,
 12093            WideArithmetic.MultiplySigned320(
 12094                Signed320.ExtendValue(
 12095                    Signed192.Signed(vertex.m_rawValue)),
 12096                denominator));
 97
 98    private static Signed576 SubtractVertex(
 99        Signed576 point,
 100        Fixed64 vertex,
 101        Signed576 denominator) =>
 875102        WideArithmetic.SubtractSigned576(
 875103            point,
 875104            WideArithmetic.MultiplySigned576(
 875105                denominator,
 875106                Signed192.Signed(vertex.m_rawValue)));
 107
 108    private static Signed320 GetWideDot(
 109        Signed192 x,
 110        Signed192 y,
 111        Signed192 z,
 112        Vector3d end,
 113        Vector3d start) =>
 98114        WideArithmetic.AddSigned320(
 98115            WideArithmetic.AddSigned320(
 98116                WideArithmetic.MultiplySigned192(
 98117                    x,
 98118                    GetComponentDifference(end.X, start.X)),
 98119                WideArithmetic.MultiplySigned192(
 98120                    y,
 98121                    GetComponentDifference(end.Y, start.Y))),
 98122            WideArithmetic.MultiplySigned192(
 98123                z,
 98124                GetComponentDifference(end.Z, start.Z)));
 125
 126    private static void GetTriangleNormal(
 127        FixedTriangle triangle,
 128        out Signed192 x,
 129        out Signed192 y,
 130        out Signed192 z) =>
 85131        WideGeometry.GetDifferenceCrossProduct3D(
 85132            triangle.B.X,
 85133            triangle.A.X,
 85134            triangle.B.Y,
 85135            triangle.A.Y,
 85136            triangle.B.Z,
 85137            triangle.A.Z,
 85138            triangle.C.X,
 85139            triangle.A.X,
 85140            triangle.C.Y,
 85141            triangle.A.Y,
 85142            triangle.C.Z,
 85143            triangle.A.Z,
 85144            out x,
 85145            out y,
 85146            out z);
 147
 148    private static bool TryMaterialize(
 149        AxisTriangleCandidate candidate,
 150        out Vector3d pointOnTriangle,
 151        out Vector3d pointOnAxis)
 152    {
 153        // A selected triangle candidate is a convex combination of authored
 154        // vertices, so its coordinates remain representable.
 57155        _ = Fixed64.TryGetSignedRawRatio(
 57156            candidate.TriangleX,
 57157            candidate.Denominator,
 57158            out Fixed64 triangleX);
 57159        _ = Fixed64.TryGetSignedRawRatio(
 57160            candidate.TriangleY,
 57161            candidate.Denominator,
 57162            out Fixed64 triangleY);
 57163        _ = Fixed64.TryGetSignedRawRatio(
 57164            candidate.TriangleZ,
 57165            candidate.Denominator,
 57166            out Fixed64 triangleZ);
 57167        bool representable = Fixed64.TryGetSignedRawRatio(
 57168            candidate.AxisX,
 57169            candidate.Denominator,
 57170            out Fixed64 axisX);
 57171        representable &= Fixed64.TryGetSignedRawRatio(
 57172            candidate.AxisY,
 57173            candidate.Denominator,
 57174            out Fixed64 axisY);
 57175        representable &= Fixed64.TryGetSignedRawRatio(
 57176            candidate.AxisZ,
 57177            candidate.Denominator,
 57178            out Fixed64 axisZ);
 57179        if (!representable)
 180        {
 1181            pointOnTriangle = default;
 1182            pointOnAxis = default;
 1183            return false;
 184        }
 185
 56186        pointOnTriangle = new Vector3d(triangleX, triangleY, triangleZ);
 56187        pointOnAxis = new Vector3d(axisX, axisY, axisZ);
 56188        return true;
 189    }
 190
 191    private static bool TryOffsetPoint(
 192        Vector3d point,
 193        Vector3d direction,
 194        Fixed64 distance,
 195        out Vector3d result)
 196    {
 6197        bool representable =
 6198            Fixed64.TryMultiplyAdd(direction.X, distance, point.X, out Fixed64 x)
 6199            & Fixed64.TryMultiplyAdd(direction.Y, distance, point.Y, out Fixed64 y)
 6200            & Fixed64.TryMultiplyAdd(direction.Z, distance, point.Z, out Fixed64 z);
 6201        result = representable ? new Vector3d(x, y, z) : default;
 6202        return representable;
 203    }
 204
 205    private static bool TryGetRelativeCoordinate(
 206        Signed576 pointNumerator,
 207        Fixed64 origin,
 208        Signed576 denominator,
 209        out Fixed64 coordinate)
 210    {
 222211        Signed576 originNumerator = WideArithmetic.MultiplySigned576(
 222212            denominator,
 222213            Signed192.Signed(origin.m_rawValue));
 222214        return Fixed64.TryGetSignedRawRatio(
 222215            WideArithmetic.SubtractSigned576(
 222216                pointNumerator,
 222217                originNumerator),
 222218            denominator,
 222219            out coordinate);
 220    }
 221
 222}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredCapsule.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.CenteredCapsule.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
 8namespace FixedMathSharp.Geometry;
 9
 10/// <content>
 11/// Provides high-precision surface point and offset calculations for capsules
 12/// centered at the origin (or a given center), using a fixed axis and radius.
 13/// </content>
 14internal static partial class WideFiniteAxisIntersection
 15{
 16    internal static bool TryGetSurfacePointOnCenteredCapsule(
 17        Vector2d point,
 18        Vector2d center,
 19        Vector2d axisDirection,
 20        Fixed64 axisLength,
 21        Fixed64 radius,
 22        Vector2d normal,
 23        out Vector2d surfacePoint)
 24    {
 825        GetClosestCenteredAxisRatio(
 826            point,
 827            center,
 828            axisDirection,
 829            axisLength,
 830            out Signed192 numerator,
 831            out Signed192 denominator);
 832        if (!TryGetCenteredCapsuleSurfaceCoordinate(
 833                center.X, axisDirection.X, numerator, denominator, normal.X, radius, out Fixed64 x)
 834            || !TryGetCenteredCapsuleSurfaceCoordinate(
 835                center.Y, axisDirection.Y, numerator, denominator, normal.Y, radius, out Fixed64 y))
 36        {
 337            surfacePoint = default;
 338            return false;
 39        }
 40
 541        surfacePoint = new Vector2d(x, y);
 542        return true;
 43    }
 44
 45    internal static bool TryGetSurfaceOffsetOnCenteredCapsule(
 46        Vector2d point,
 47        Vector2d center,
 48        Vector2d axisDirection,
 49        Fixed64 axisLength,
 50        Fixed64 radius,
 51        Vector2d normal,
 52        out Vector2d surfaceOffset)
 53    {
 454        GetClosestCenteredAxisRatio(
 455            point,
 456            center,
 457            axisDirection,
 458            axisLength,
 459            out Signed192 numerator,
 460            out Signed192 denominator);
 461        bool representable =
 462            TryGetCenteredCapsuleSurfaceCoordinate(
 463                Fixed64.Zero,
 464                axisDirection.X,
 465                numerator,
 466                denominator,
 467                normal.X,
 468                radius,
 469                out Fixed64 x)
 470            & TryGetCenteredCapsuleSurfaceCoordinate(
 471                Fixed64.Zero,
 472                axisDirection.Y,
 473                numerator,
 474                denominator,
 475                normal.Y,
 476                radius,
 477                out Fixed64 y);
 478        surfaceOffset = representable ? new Vector2d(x, y) : default;
 479        return representable;
 80    }
 81
 82    internal static bool TryGetSurfaceOffsetOnCenteredCapsule(
 83        Vector3d point,
 84        Vector3d center,
 85        Vector3d axisDirection,
 86        Fixed64 axisLength,
 87        Fixed64 radius,
 88        Vector3d normal,
 89        out Vector3d surfaceOffset)
 90    {
 391        GetClosestCenteredAxisRatio(
 392            point,
 393            center,
 394            axisDirection,
 395            axisLength,
 396            out Signed192 numerator,
 397            out Signed192 denominator);
 398        bool representable =
 399            TryGetCenteredCapsuleSurfaceCoordinate(
 3100                Fixed64.Zero, axisDirection.X, numerator, denominator, normal.X, radius, out Fixed64 x)
 3101            & TryGetCenteredCapsuleSurfaceCoordinate(
 3102                Fixed64.Zero, axisDirection.Y, numerator, denominator, normal.Y, radius, out Fixed64 y)
 3103            & TryGetCenteredCapsuleSurfaceCoordinate(
 3104                Fixed64.Zero, axisDirection.Z, numerator, denominator, normal.Z, radius, out Fixed64 z);
 3105        surfaceOffset = representable ? new Vector3d(x, y, z) : default;
 3106        return representable;
 107    }
 108
 109    internal static bool TryGetSurfacePointOnCenteredCapsule(
 110        Vector3d point,
 111        Vector3d center,
 112        Vector3d axisDirection,
 113        Fixed64 axisLength,
 114        Fixed64 radius,
 115        Vector3d normal,
 116        out Vector3d surfacePoint)
 117    {
 6118        GetClosestCenteredAxisRatio(
 6119            point,
 6120            center,
 6121            axisDirection,
 6122            axisLength,
 6123            out Signed192 numerator,
 6124            out Signed192 denominator);
 6125        if (!TryGetCenteredCapsuleSurfaceCoordinate(
 6126                center.X, axisDirection.X, numerator, denominator, normal.X, radius, out Fixed64 x)
 6127            || !TryGetCenteredCapsuleSurfaceCoordinate(
 6128                center.Y, axisDirection.Y, numerator, denominator, normal.Y, radius, out Fixed64 y)
 6129            || !TryGetCenteredCapsuleSurfaceCoordinate(
 6130                center.Z, axisDirection.Z, numerator, denominator, normal.Z, radius, out Fixed64 z))
 131        {
 4132            surfacePoint = default;
 4133            return false;
 134        }
 135
 2136        surfacePoint = new Vector3d(x, y, z);
 2137        return true;
 138    }
 139
 140    internal static bool ContainsPointInCenteredCapsule(
 141        Vector2d point,
 142        Vector2d center,
 143        Vector2d axisDirection,
 144        Fixed64 axisLength,
 145        Fixed64 radius,
 146        Fixed64 radiusExpansion,
 147        bool strict)
 148    {
 18149        Signed192 expandedRadius = GetExpandedRadius(radius, radiusExpansion);
 18150        Signed192 axisLengthSquared = GetDot(axisDirection, Vector2d.Zero, axisDirection, Vector2d.Zero);
 18151        Signed192 axisProjection = GetDot(point, center, axisDirection, Vector2d.Zero);
 18152        return IsCenteredCapsulePointContained(
 18153            point,
 18154            center,
 18155            axisDirection,
 18156            axisLength,
 18157            GetDot(point, center, point, center),
 18158            axisProjection,
 18159            axisLengthSquared,
 18160            GetSquaredRadius(expandedRadius),
 18161            expandedRadius,
 18162            GetCenteredAxialExtent(axisLengthSquared, axisLength, Fixed64.Zero),
 18163            strict);
 164    }
 165
 166    internal static bool ContainsPointInCenteredCapsule(
 167        Vector3d point,
 168        Vector3d center,
 169        Vector3d axisDirection,
 170        Fixed64 axisLength,
 171        Fixed64 radius,
 172        Fixed64 radiusExpansion,
 173        bool strict)
 174    {
 10175        Signed192 expandedRadius = GetExpandedRadius(radius, radiusExpansion);
 10176        Signed192 axisLengthSquared = GetDot(axisDirection, Vector3d.Zero, axisDirection, Vector3d.Zero);
 10177        Signed192 axisProjection = GetDot(point, center, axisDirection, Vector3d.Zero);
 10178        return IsCenteredCapsulePointContained(
 10179            point,
 10180            center,
 10181            axisDirection,
 10182            axisLength,
 10183            GetDot(point, center, point, center),
 10184            axisProjection,
 10185            axisLengthSquared,
 10186            GetSquaredRadius(expandedRadius),
 10187            expandedRadius,
 10188            GetCenteredAxialExtent(axisLengthSquared, axisLength, Fixed64.Zero),
 10189            strict);
 190    }
 191
 192    internal static Vector2d GetDirectionFromCenteredAxis(
 193        Vector2d point,
 194        Vector2d center,
 195        Vector2d axisDirection,
 196        Fixed64 axisLength)
 197    {
 6198        Signed192 differenceX = GetComponentDifference(point.X, center.X);
 6199        Signed192 differenceY = GetComponentDifference(point.Y, center.Y);
 6200        Signed192 axisLengthSquared = GetDot(axisDirection, Vector2d.Zero, axisDirection, Vector2d.Zero);
 6201        Signed192 axisProjection = GetDot(point, center, axisDirection, Vector2d.Zero);
 6202        int cap = GetCenteredCap(
 6203            axisProjection,
 6204            GetCenteredAxialExtent(axisLengthSquared, axisLength, Fixed64.Zero));
 6205        if (cap != 0)
 206        {
 1207            return WideNormalization.GetNormalized(
 1208                GetCenteredCapOffset(point.X, center.X, axisDirection.X, axisLength, cap > 0),
 1209                GetCenteredCapOffset(point.Y, center.Y, axisDirection.Y, axisLength, cap > 0));
 210        }
 211
 5212        Signed192 axisX = Signed192.Signed(axisDirection.X.m_rawValue);
 5213        Signed192 axisY = Signed192.Signed(axisDirection.Y.m_rawValue);
 5214        return WideNormalization.GetNormalized(
 5215            GetRadialComponent(axisLengthSquared, differenceX, axisX, axisProjection),
 5216            GetRadialComponent(axisLengthSquared, differenceY, axisY, axisProjection));
 217    }
 218
 219    internal static Vector3d GetDirectionFromCenteredAxis(
 220        Vector3d point,
 221        Vector3d center,
 222        Vector3d axisDirection,
 223        Fixed64 axisLength)
 224    {
 4225        Signed192 differenceX = GetComponentDifference(point.X, center.X);
 4226        Signed192 differenceY = GetComponentDifference(point.Y, center.Y);
 4227        Signed192 differenceZ = GetComponentDifference(point.Z, center.Z);
 4228        Signed192 axisLengthSquared = GetDot(axisDirection, Vector3d.Zero, axisDirection, Vector3d.Zero);
 4229        Signed192 axisProjection = GetDot(point, center, axisDirection, Vector3d.Zero);
 4230        int cap = GetCenteredCap(
 4231            axisProjection,
 4232            GetCenteredAxialExtent(axisLengthSquared, axisLength, Fixed64.Zero));
 4233        if (cap != 0)
 234        {
 1235            return WideNormalization.GetNormalized(
 1236                GetCenteredCapOffset(point.X, center.X, axisDirection.X, axisLength, cap > 0),
 1237                GetCenteredCapOffset(point.Y, center.Y, axisDirection.Y, axisLength, cap > 0),
 1238                GetCenteredCapOffset(point.Z, center.Z, axisDirection.Z, axisLength, cap > 0));
 239        }
 240
 3241        Signed192 axisX = Signed192.Signed(axisDirection.X.m_rawValue);
 3242        Signed192 axisY = Signed192.Signed(axisDirection.Y.m_rawValue);
 3243        Signed192 axisZ = Signed192.Signed(axisDirection.Z.m_rawValue);
 3244        return WideNormalization.GetNormalized(
 3245            GetRadialComponent(axisLengthSquared, differenceX, axisX, axisProjection),
 3246            GetRadialComponent(axisLengthSquared, differenceY, axisY, axisProjection),
 3247            GetRadialComponent(axisLengthSquared, differenceZ, axisZ, axisProjection));
 248    }
 249
 250    internal static bool TryGetCapsuleInterval(
 251        FixedSegment2d query,
 252        Vector2d center,
 253        Vector2d axisDirection,
 254        Fixed64 axisLength,
 255        Fixed64 radius,
 256        Fixed64 radiusExpansion,
 257        out Fixed64 entry,
 258        out Fixed64 exit,
 259        out bool startContained,
 260        out bool endContainedStrict)
 261    {
 143262        Signed192 expandedRadius = GetExpandedRadius(radius, radiusExpansion);
 143263        Signed192 squaredRadius = GetSquaredRadius(expandedRadius);
 143264        Signed192 axisLengthSquared = GetDot(axisDirection, Vector2d.Zero, axisDirection, Vector2d.Zero);
 143265        Signed192 queryLengthSquared = GetDot(query.End, query.Start, query.End, query.Start);
 143266        Signed192 startDistanceSquared = GetDot(query.Start, center, query.Start, center);
 143267        Signed192 directionsDot = GetDot(query.End, query.Start, axisDirection, Vector2d.Zero);
 143268        Signed192 startAxisProjection = GetDot(query.Start, center, axisDirection, Vector2d.Zero);
 143269        Signed192 endAxisProjection = GetDot(query.End, center, axisDirection, Vector2d.Zero);
 143270        Signed192 startDirectionProjection = GetDot(query.Start, center, query.End, query.Start);
 143271        Signed320 axialExtent = GetCenteredAxialExtent(
 143272            axisLengthSquared,
 143273            axisLength,
 143274            Fixed64.Zero);
 275
 143276        startContained = IsCenteredCapsulePointContained(
 143277            query.Start,
 143278            center,
 143279            axisDirection,
 143280            axisLength,
 143281            startDistanceSquared,
 143282            startAxisProjection,
 143283            axisLengthSquared,
 143284            squaredRadius,
 143285            expandedRadius,
 143286            axialExtent,
 143287            strict: false);
 143288        endContainedStrict = IsCenteredCapsulePointContained(
 143289            query.End,
 143290            center,
 143291            axisDirection,
 143292            axisLength,
 143293            GetDot(query.End, center, query.End, center),
 143294            endAxisProjection,
 143295            axisLengthSquared,
 143296            squaredRadius,
 143297            expandedRadius,
 143298            axialExtent,
 143299            strict: true);
 300
 143301        bool found = false;
 143302        entry = default;
 143303        exit = default;
 143304        if (TryGetCenteredAxialInterval(
 143305                startAxisProjection,
 143306                directionsDot,
 143307                axialExtent,
 143308                Fixed64.One,
 143309                out RationalBound320 lower,
 143310                out RationalBound320 upper))
 311        {
 139312            found = TryGetFiniteAxisInterval(
 139313                queryLengthSquared,
 139314                axisLengthSquared,
 139315                startDistanceSquared,
 139316                directionsDot,
 139317                startAxisProjection,
 139318                startDirectionProjection,
 139319                expandedRadius,
 139320                lower,
 139321                upper,
 139322                Fixed64.One,
 139323                out entry,
 139324                out exit);
 325        }
 326
 143327        MergeCenteredCapInterval(
 143328            query,
 143329            center,
 143330            axisDirection,
 143331            axisLength,
 143332            expandedRadius,
 143333            positiveCap: false,
 143334            ref found,
 143335            ref entry,
 143336            ref exit);
 143337        MergeCenteredCapInterval(
 143338            query,
 143339            center,
 143340            axisDirection,
 143341            axisLength,
 143342            expandedRadius,
 143343            positiveCap: true,
 143344            ref found,
 143345            ref entry,
 143346            ref exit);
 143347        return found;
 348    }
 349
 350    internal static bool TryGetCapsuleInterval(
 351        FixedSegment query,
 352        Vector3d center,
 353        Vector3d axisDirection,
 354        Fixed64 axisLength,
 355        Fixed64 radius,
 356        Fixed64 radiusExpansion,
 357        out Fixed64 entry,
 358        out Fixed64 exit,
 359        out bool startContained,
 360        out bool endContainedStrict)
 361    {
 6362        Signed192 expandedRadius = GetExpandedRadius(radius, radiusExpansion);
 6363        Signed192 squaredRadius = GetSquaredRadius(expandedRadius);
 6364        Signed192 axisLengthSquared = GetDot(axisDirection, Vector3d.Zero, axisDirection, Vector3d.Zero);
 6365        Signed192 queryLengthSquared = GetDot(query.End, query.Start, query.End, query.Start);
 6366        Signed192 startDistanceSquared = GetDot(query.Start, center, query.Start, center);
 6367        Signed192 directionsDot = GetDot(query.End, query.Start, axisDirection, Vector3d.Zero);
 6368        Signed192 startAxisProjection = GetDot(query.Start, center, axisDirection, Vector3d.Zero);
 6369        Signed192 endAxisProjection = GetDot(query.End, center, axisDirection, Vector3d.Zero);
 6370        Signed192 startDirectionProjection = GetDot(query.Start, center, query.End, query.Start);
 6371        Signed320 axialExtent = GetCenteredAxialExtent(
 6372            axisLengthSquared,
 6373            axisLength,
 6374            Fixed64.Zero);
 375
 6376        startContained = IsCenteredCapsulePointContained(
 6377            query.Start,
 6378            center,
 6379            axisDirection,
 6380            axisLength,
 6381            startDistanceSquared,
 6382            startAxisProjection,
 6383            axisLengthSquared,
 6384            squaredRadius,
 6385            expandedRadius,
 6386            axialExtent,
 6387            strict: false);
 6388        endContainedStrict = IsCenteredCapsulePointContained(
 6389            query.End,
 6390            center,
 6391            axisDirection,
 6392            axisLength,
 6393            GetDot(query.End, center, query.End, center),
 6394            endAxisProjection,
 6395            axisLengthSquared,
 6396            squaredRadius,
 6397            expandedRadius,
 6398            axialExtent,
 6399            strict: true);
 400
 6401        bool found = false;
 6402        entry = default;
 6403        exit = default;
 6404        if (TryGetCenteredAxialInterval(
 6405                startAxisProjection,
 6406                directionsDot,
 6407                axialExtent,
 6408                Fixed64.One,
 6409                out RationalBound320 lower,
 6410                out RationalBound320 upper))
 411        {
 4412            found = TryGetFiniteAxisInterval(
 4413                queryLengthSquared,
 4414                axisLengthSquared,
 4415                startDistanceSquared,
 4416                directionsDot,
 4417                startAxisProjection,
 4418                startDirectionProjection,
 4419                expandedRadius,
 4420                lower,
 4421                upper,
 4422                Fixed64.One,
 4423                out entry,
 4424                out exit);
 425        }
 426
 6427        MergeCenteredCapInterval(
 6428            query,
 6429            center,
 6430            axisDirection,
 6431            axisLength,
 6432            expandedRadius,
 6433            positiveCap: false,
 6434            ref found,
 6435            ref entry,
 6436            ref exit);
 6437        MergeCenteredCapInterval(
 6438            query,
 6439            center,
 6440            axisDirection,
 6441            axisLength,
 6442            expandedRadius,
 6443            positiveCap: true,
 6444            ref found,
 6445            ref entry,
 6446            ref exit);
 6447        return found;
 448    }
 449
 450    private static bool IsCenteredCapsulePointContained(
 451        Vector2d point,
 452        Vector2d center,
 453        Vector2d axisDirection,
 454        Fixed64 axisLength,
 455        Signed192 distanceSquared,
 456        Signed192 axisProjection,
 457        Signed192 axisLengthSquared,
 458        Signed192 squaredRadius,
 459        Signed192 expandedRadius,
 460        Signed320 axialExtent,
 461        bool strict)
 462    {
 318463        int cap = GetCenteredCap(axisProjection, axialExtent);
 318464        if (cap == 0)
 465        {
 292466            int radialSign = GetRadialConstant(
 292467                distanceSquared,
 292468                axisProjection,
 292469                axisLengthSquared,
 292470                squaredRadius).Sign;
 292471            return strict ? radialSign < 0 : radialSign <= 0;
 472        }
 473
 26474        int capSign = GetCenteredCapConstant(
 26475            point,
 26476            center,
 26477            axisDirection,
 26478            axisLength,
 26479            expandedRadius,
 26480            positiveCap: cap > 0).Sign;
 26481        return strict ? capSign < 0 : capSign <= 0;
 482    }
 483
 484    private static bool IsCenteredCapsulePointContained(
 485        Vector3d point,
 486        Vector3d center,
 487        Vector3d axisDirection,
 488        Fixed64 axisLength,
 489        Signed192 distanceSquared,
 490        Signed192 axisProjection,
 491        Signed192 axisLengthSquared,
 492        Signed192 squaredRadius,
 493        Signed192 expandedRadius,
 494        Signed320 axialExtent,
 495        bool strict)
 496    {
 59497        int cap = GetCenteredCap(axisProjection, axialExtent);
 59498        if (cap == 0)
 499        {
 19500            int radialSign = GetRadialConstant(
 19501                distanceSquared,
 19502                axisProjection,
 19503                axisLengthSquared,
 19504                squaredRadius).Sign;
 19505            return strict ? radialSign < 0 : radialSign <= 0;
 506        }
 507
 40508        int capSign = GetCenteredCapConstant(
 40509            point,
 40510            center,
 40511            axisDirection,
 40512            axisLength,
 40513            expandedRadius,
 40514            positiveCap: cap > 0).Sign;
 40515        return strict ? capSign < 0 : capSign <= 0;
 516    }
 517
 518    private static int GetCenteredCap(Signed192 axisProjection, Signed320 axialExtent)
 519    {
 431520        Signed320 scaledProjection = WideArithmetic.MultiplySigned192(DoubleParameterScale, axisProjection);
 431521        if (WideArithmetic.AddSigned320(scaledProjection, axialExtent).Sign <= 0)
 38522            return -1;
 393523        return WideArithmetic.SubtractSigned320(scaledProjection, axialExtent).Sign >= 0
 393524            ? 1
 393525            : 0;
 526    }
 527
 528    private static void MergeCenteredCapInterval(
 529        FixedSegment2d query,
 530        Vector2d center,
 531        Vector2d axisDirection,
 532        Fixed64 axisLength,
 533        Signed192 expandedRadius,
 534        bool positiveCap,
 535        ref bool found,
 536        ref Fixed64 entry,
 537        ref Fixed64 exit)
 538    {
 286539        GetCenteredCapPolynomial(
 286540            query,
 286541            center,
 286542            axisDirection,
 286543            axisLength,
 286544            expandedRadius,
 286545            positiveCap,
 286546            out Signed320 coefficient,
 286547            out Signed320 projection,
 286548            out Signed320 constant);
 286549        Merge(
 286550            TrySolveUnitQuadratic(coefficient, projection, constant, out Fixed64 capEntry, out Fixed64 capExit),
 286551            capEntry,
 286552            capExit,
 286553            ref found,
 286554            ref entry,
 286555            ref exit);
 286556    }
 557
 558    private static void MergeCenteredCapInterval(
 559        FixedSegment query,
 560        Vector3d center,
 561        Vector3d axisDirection,
 562        Fixed64 axisLength,
 563        Signed192 expandedRadius,
 564        bool positiveCap,
 565        ref bool found,
 566        ref Fixed64 entry,
 567        ref Fixed64 exit)
 568    {
 12569        GetCenteredCapPolynomial(
 12570            query,
 12571            center,
 12572            axisDirection,
 12573            axisLength,
 12574            expandedRadius,
 12575            positiveCap,
 12576            out Signed320 coefficient,
 12577            out Signed320 projection,
 12578            out Signed320 constant);
 12579        Merge(
 12580            TrySolveUnitQuadratic(coefficient, projection, constant, out Fixed64 capEntry, out Fixed64 capExit),
 12581            capEntry,
 12582            capExit,
 12583            ref found,
 12584            ref entry,
 12585            ref exit);
 12586    }
 587
 588    private static void GetCenteredCapPolynomial(
 589        FixedSegment2d query,
 590        Vector2d center,
 591        Vector2d axisDirection,
 592        Fixed64 axisLength,
 593        Signed192 expandedRadius,
 594        bool positiveCap,
 595        out Signed320 coefficient,
 596        out Signed320 projection,
 597        out Signed320 constant)
 598    {
 296599        Signed192 x = GetCenteredCapOffset(
 296600            query.Start.X,
 296601            center.X,
 296602            axisDirection.X,
 296603            axisLength,
 296604            positiveCap);
 296605        Signed192 y = GetCenteredCapOffset(
 296606            query.Start.Y,
 296607            center.Y,
 296608            axisDirection.Y,
 296609            axisLength,
 296610            positiveCap);
 296611        Signed192 velocityX = GetScaledDifference(query.End.X, query.Start.X);
 296612        Signed192 velocityY = GetScaledDifference(query.End.Y, query.Start.Y);
 296613        Signed192 scaledRadius = ScaleByCenteredAxis(expandedRadius);
 614
 296615        coefficient = WideArithmetic.AddProducts(velocityX, velocityX, velocityY, velocityY);
 296616        projection = WideArithmetic.AddProducts(x, velocityX, y, velocityY);
 296617        constant = WideArithmetic.SubtractSigned320(
 296618            WideArithmetic.AddProducts(x, x, y, y),
 296619            WideArithmetic.MultiplySigned192(scaledRadius, scaledRadius));
 296620    }
 621
 622    private static void GetCenteredCapPolynomial(
 623        FixedSegment query,
 624        Vector3d center,
 625        Vector3d axisDirection,
 626        Fixed64 axisLength,
 627        Signed192 expandedRadius,
 628        bool positiveCap,
 629        out Signed320 coefficient,
 630        out Signed320 projection,
 631        out Signed320 constant)
 632    {
 44633        Signed192 x = GetCenteredCapOffset(
 44634            query.Start.X,
 44635            center.X,
 44636            axisDirection.X,
 44637            axisLength,
 44638            positiveCap);
 44639        Signed192 y = GetCenteredCapOffset(
 44640            query.Start.Y,
 44641            center.Y,
 44642            axisDirection.Y,
 44643            axisLength,
 44644            positiveCap);
 44645        Signed192 z = GetCenteredCapOffset(
 44646            query.Start.Z,
 44647            center.Z,
 44648            axisDirection.Z,
 44649            axisLength,
 44650            positiveCap);
 44651        Signed192 velocityX = GetScaledDifference(query.End.X, query.Start.X);
 44652        Signed192 velocityY = GetScaledDifference(query.End.Y, query.Start.Y);
 44653        Signed192 velocityZ = GetScaledDifference(query.End.Z, query.Start.Z);
 44654        Signed192 scaledRadius = ScaleByCenteredAxis(expandedRadius);
 655
 44656        coefficient = WideArithmetic.AddProducts(velocityX, velocityX, velocityY, velocityY, velocityZ, velocityZ);
 44657        projection = WideArithmetic.AddProducts(x, velocityX, y, velocityY, z, velocityZ);
 44658        constant = WideArithmetic.SubtractSigned320(
 44659            WideArithmetic.AddProducts(x, x, y, y, z, z),
 44660            WideArithmetic.MultiplySigned192(scaledRadius, scaledRadius));
 44661    }
 662
 663    private static Signed320 GetCenteredCapConstant(
 664        Vector2d point,
 665        Vector2d center,
 666        Vector2d axisDirection,
 667        Fixed64 axisLength,
 668        Signed192 expandedRadius,
 669        bool positiveCap)
 670    {
 26671        Signed192 x = GetCenteredCapOffset(
 26672            point.X,
 26673            center.X,
 26674            axisDirection.X,
 26675            axisLength,
 26676            positiveCap);
 26677        Signed192 y = GetCenteredCapOffset(
 26678            point.Y,
 26679            center.Y,
 26680            axisDirection.Y,
 26681            axisLength,
 26682            positiveCap);
 26683        Signed192 scaledRadius = ScaleByCenteredAxis(expandedRadius);
 26684        return WideArithmetic.SubtractSigned320(
 26685            WideArithmetic.AddProducts(x, x, y, y),
 26686            WideArithmetic.MultiplySigned192(scaledRadius, scaledRadius));
 687    }
 688
 689    private static Signed320 GetCenteredCapConstant(
 690        Vector3d point,
 691        Vector3d center,
 692        Vector3d axisDirection,
 693        Fixed64 axisLength,
 694        Signed192 expandedRadius,
 695        bool positiveCap)
 696    {
 40697        Signed192 x = GetCenteredCapOffset(
 40698            point.X,
 40699            center.X,
 40700            axisDirection.X,
 40701            axisLength,
 40702            positiveCap);
 40703        Signed192 y = GetCenteredCapOffset(
 40704            point.Y,
 40705            center.Y,
 40706            axisDirection.Y,
 40707            axisLength,
 40708            positiveCap);
 40709        Signed192 z = GetCenteredCapOffset(
 40710            point.Z,
 40711            center.Z,
 40712            axisDirection.Z,
 40713            axisLength,
 40714            positiveCap);
 40715        Signed192 scaledRadius = ScaleByCenteredAxis(expandedRadius);
 40716        return WideArithmetic.SubtractSigned320(
 40717            WideArithmetic.AddProducts(x, x, y, y, z, z),
 40718            WideArithmetic.MultiplySigned192(scaledRadius, scaledRadius));
 719    }
 720
 721    private static Signed192 GetCenteredCapOffset(
 722        Fixed64 point,
 723        Fixed64 center,
 724        Fixed64 axisDirection,
 725        Fixed64 axisLength,
 726        bool positiveCap)
 727    {
 962728        Signed192 difference = WideArithmetic.SubtractSigned192(
 962729            Signed192.Signed(point.m_rawValue),
 962730            Signed192.Signed(center.m_rawValue));
 962731        Signed320 scaledDifference = WideArithmetic.MultiplySigned192(difference, DoubleParameterScale);
 962732        Signed320 directionalOffset = WideArithmetic.MultiplySigned192(
 962733            Signed192.Signed(axisDirection.m_rawValue),
 962734            Signed192.Signed(axisLength.m_rawValue));
 962735        return Signed192.NarrowValue(positiveCap
 962736            ? WideArithmetic.SubtractSigned320(scaledDifference, directionalOffset)
 962737            : WideArithmetic.AddSigned320(scaledDifference, directionalOffset));
 738    }
 739
 740    private static Signed320 GetRadialComponent(
 741        Signed192 axisLengthSquared,
 742        Signed192 difference,
 743        Signed192 axisComponent,
 744        Signed192 axisProjection) =>
 19745        WideArithmetic.SubtractSigned320(
 19746            WideArithmetic.MultiplySigned192(axisLengthSquared, difference),
 19747            WideArithmetic.MultiplySigned192(axisComponent, axisProjection));
 748
 749    private static void GetClosestCenteredAxisRatio(
 750        Vector2d point,
 751        Vector2d center,
 752        Vector2d axisDirection,
 753        Fixed64 axisLength,
 754        out Signed192 numerator,
 755        out Signed192 denominator)
 756    {
 27757        Signed192 axisLengthSquared = GetDot(axisDirection, Vector2d.Zero, axisDirection, Vector2d.Zero);
 27758        Signed192 axisProjection = GetDot(point, center, axisDirection, Vector2d.Zero);
 27759        int cap = GetCenteredCap(
 27760            axisProjection,
 27761            GetCenteredAxialExtent(axisLengthSquared, axisLength, Fixed64.Zero));
 27762        if (cap == 0)
 763        {
 18764            numerator = axisProjection;
 18765            denominator = axisLengthSquared;
 18766            return;
 767        }
 768
 9769        numerator = Signed192.Signed(
 9770            cap < 0 ? -axisLength.m_rawValue : axisLength.m_rawValue);
 9771        denominator = DoubleParameterScale;
 9772    }
 773
 774    private static void GetClosestCenteredAxisRatio(
 775        Vector3d point,
 776        Vector3d center,
 777        Vector3d axisDirection,
 778        Fixed64 axisLength,
 779        out Signed192 numerator,
 780        out Signed192 denominator)
 781    {
 17782        Signed192 axisLengthSquared = GetDot(axisDirection, Vector3d.Zero, axisDirection, Vector3d.Zero);
 17783        Signed192 axisProjection = GetDot(point, center, axisDirection, Vector3d.Zero);
 17784        int cap = GetCenteredCap(
 17785            axisProjection,
 17786            GetCenteredAxialExtent(axisLengthSquared, axisLength, Fixed64.Zero));
 17787        if (cap == 0)
 788        {
 9789            numerator = axisProjection;
 9790            denominator = axisLengthSquared;
 9791            return;
 792        }
 793
 8794        numerator = Signed192.Signed(
 8795            cap < 0 ? -axisLength.m_rawValue : axisLength.m_rawValue);
 8796        denominator = DoubleParameterScale;
 8797    }
 798
 799    private static bool TryGetCenteredCapsuleSurfaceCoordinate(
 800        Fixed64 center,
 801        Fixed64 axisDirection,
 802        Signed192 axisNumerator,
 803        Signed192 axisDenominator,
 804        Fixed64 normal,
 805        Fixed64 radius,
 806        out Fixed64 coordinate)
 807    {
 50808        Signed320 centerTerm = MultiplyThreeToSigned320(
 50809            Signed192.Signed(center.m_rawValue),
 50810            axisDenominator,
 50811            ParameterScale);
 50812        Signed320 axisTerm = MultiplyThreeToSigned320(
 50813            Signed192.Signed(axisDirection.m_rawValue),
 50814            axisNumerator,
 50815            ParameterScale);
 50816        Signed320 radialTerm = MultiplyThreeToSigned320(
 50817            Signed192.Signed(normal.m_rawValue),
 50818            Signed192.Signed(radius.m_rawValue),
 50819            axisDenominator);
 50820        Signed320 denominator = MultiplyThreeToSigned320(
 50821            axisDenominator,
 50822            ParameterScale,
 50823            ParameterScale);
 50824        Signed320 numerator = WideArithmetic.AddSigned320(
 50825            WideArithmetic.AddSigned320(centerTerm, axisTerm),
 50826            radialTerm);
 50827        return Fixed64.TryGetSignedRatio(numerator, denominator, out coordinate);
 828    }
 829
 830    private static Signed320 MultiplyThreeToSigned320(
 831        Signed192 first,
 832        Signed192 second,
 833        Signed192 third) =>
 834        // Centered-capsule surface terms use at most 194 signed bits. The upper
 835        // four words are therefore sign extension before this mechanical narrow.
 355836        Signed320.NarrowValue(WideArithmetic.MultiplySigned320(
 355837            WideArithmetic.MultiplySigned192(first, second),
 355838            Signed320.ExtendValue(third)));
 839
 840    private static Signed320 GetCenteredAxisOffsetComponent(
 841        Fixed64 point,
 842        Fixed64 center,
 843        Fixed64 axisDirection,
 844        Signed192 axisNumerator,
 845        Signed192 axisDenominator) =>
 45846        WideArithmetic.MultiplySubtract(
 45847            GetComponentDifference(point, center),
 45848            axisDenominator,
 45849            Signed192.Signed(axisDirection.m_rawValue),
 45850            axisNumerator);
 851
 852    private static Signed192 GetComponentDifference(Fixed64 point, Fixed64 center) =>
 1482853        WideArithmetic.SubtractSigned192(
 1482854            Signed192.Signed(point.m_rawValue),
 1482855            Signed192.Signed(center.m_rawValue));
 856
 857    private static Signed192 GetScaledDifference(Fixed64 end, Fixed64 start) =>
 724858        ScaleByCenteredAxis(WideArithmetic.SubtractSigned192(
 724859            Signed192.Signed(end.m_rawValue),
 724860            Signed192.Signed(start.m_rawValue)));
 861
 862    private static Signed192 ScaleByCenteredAxis(Signed192 value) =>
 863        // A doubled full-domain coordinate difference plus one normalized-axis
 864        // component times a nonnegative Fixed64 full length uses at most 98 signed
 865        // bits. The upper two words are therefore sign extension.
 1215866        Signed192.NarrowValue(WideArithmetic.MultiplySigned192(value, DoubleParameterScale));
 867
 868    private static bool TrySolveUnitQuadratic(
 869        Signed320 coefficient,
 870        Signed320 projection,
 871        Signed320 constant,
 872        out Fixed64 entry,
 873        out Fixed64 exit)
 874    {
 298875        Signed320 one = Scale320;
 298876        return TrySolveBoundedQuadratic(
 298877            coefficient,
 298878            projection,
 298879            constant,
 298880            new RationalBound320(default, one),
 298881            new RationalBound320(one, one),
 298882            Fixed64.One,
 298883            out entry,
 298884            out exit);
 885    }
 886
 887}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredCapsuleAnchor.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.CenteredCapsuleAnchor.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
 8namespace FixedMathSharp.Geometry;
 9
 10/// <content>
 11/// Provides methods for computing surface anchors on capsules centered at a given point,
 12/// supporting both 2D and 3D representations with rotation and axis-aligned variants.
 13/// </content>
 14internal static partial class WideFiniteAxisIntersection
 15{
 16    /// <summary>Returns an exact world direction from an anchored point toward the finite core in a rigid frame.</summa
 17    internal static WideDirection3d GetPointToCenteredCapsuleCoreDirection(in FixedPointAnchor point,
 18        Vector3d center, FixedQuaternion rotation, Vector3d localAxis, Fixed64 length)
 19    {
 820        WideTriangleRelations.GetPointLocalNumerators(point, center, rotation,
 821            out Signed576 x, out Signed576 y, out Signed576 z, out Signed320 denominator);
 822        Signed192 squared = GetDot(localAxis, Vector3d.Zero, localAxis, Vector3d.Zero);
 823        Signed576 projection = WideArithmetic.AddSigned576(WideArithmetic.AddSigned576(
 824            WideArithmetic.MultiplySigned576(x, Signed192.Raw(localAxis.X)),
 825            WideArithmetic.MultiplySigned576(y, Signed192.Raw(localAxis.Y))),
 826            WideArithmetic.MultiplySigned576(z, Signed192.Raw(localAxis.Z)));
 827        Signed576 scaled = WideArithmetic.MultiplySigned576(projection, DoubleParameterScale),
 828            extent = WideArithmetic.MultiplySigned576(WideArithmetic.MultiplySigned320(denominator, squared), Signed192.
 829        int cap = WideArithmetic.SubtractSigned576(scaled, extent).Sign > 0 ? 1
 830            : WideArithmetic.AddSigned576(scaled, extent).Sign < 0 ? -1 : 0;
 831        return new WideDirection3d(Component(x, localAxis.X), Component(y, localAxis.Y), Component(z, localAxis.Z))
 832            .Transform(new WideRationalBasis3d(rotation));
 33
 34        Signed576 Component(Signed576 coordinate, Fixed64 axis)
 35        {
 36            // Complete anchor/local-frame numerators need <230 bits. Even a
 37            // full scalar axis and final rigid transform keep products <430.
 38            if (squared.IsZero) return WideArithmetic.SubtractSigned576(default, coordinate);
 39            if (cap == 0) return WideArithmetic.SubtractSigned576(
 40                WideArithmetic.MultiplySigned576(projection, Signed192.Raw(axis)),
 41                WideArithmetic.MultiplySigned576(coordinate, squared));
 42            Signed192 axial = cap > 0 ? Signed192.Raw(length) : WideArithmetic.Negate(Signed192.Raw(length));
 43            return WideArithmetic.SubtractSigned576(WideArithmetic.MultiplySigned576(
 44                    WideArithmetic.MultiplySigned320(denominator, axial), Signed192.Raw(axis)),
 45                WideArithmetic.MultiplySigned576(coordinate, DoubleParameterScale));
 46        }
 47    }
 48
 49    internal static FixedPointAnchor2d GetSurfaceAnchorOnCenteredCapsule(
 50        Vector2d point,
 51        Vector2d center,
 52        Fixed64 frameRotation,
 53        Vector2d localAxisDirection,
 54        Fixed64 axisLength,
 55        Fixed64 radius,
 56        Vector2d localNormal)
 57    {
 358        _ = Vector2d.TryRotate(
 359            localAxisDirection,
 360            frameRotation,
 361            out Vector2d axisDirection);
 362        GetClosestCenteredAxisRatio(
 363            point,
 364            center,
 365            axisDirection,
 366            axisLength,
 367            out Signed192 numerator,
 368            out Signed192 denominator);
 369        _ = TryGetCenteredCapsuleSurfaceCoordinate(
 370            Fixed64.Zero,
 371            Fixed64.One,
 372            numerator,
 373            denominator,
 374            Fixed64.Zero,
 375            Fixed64.Zero,
 376            out Fixed64 axialDistance);
 377        Vector2d roundedRadialOffset = localNormal * radius;
 378        return new FixedPointAnchor2d(
 379            center,
 380            frameRotation,
 381            localAxisDirection * axialDistance,
 382            roundedRadialOffset,
 383            FixedPointAnchorTerm2d.CreateRadialSupport(
 384                localNormal,
 385                radius,
 386                roundedRadialOffset));
 87    }
 88
 89    internal static FixedPointAnchor GetSurfaceAnchorOnCenteredCapsule(
 90        Vector3d point,
 91        Vector3d center,
 92        FixedQuaternion frameRotation,
 93        Vector3d localAxisDirection,
 94        Fixed64 axisLength,
 95        Fixed64 radius,
 96        Vector3d localNormal)
 97    {
 15198        GetRigidLocalPointRelation(
 15199            point,
 151100            center,
 151101            frameRotation,
 151102            localAxisDirection,
 151103            out RigidLocalPointRelation relation);
 151104        int cap = GetCenteredCap(relation, axisLength);
 151105        Fixed64 axialDistance = GetClosestCenteredRigidAxisDistance(
 151106            relation,
 151107            axisLength,
 151108            cap);
 151109        Vector3d roundedRadialOffset = localNormal * radius;
 151110        return new FixedPointAnchor(
 151111            center,
 151112            frameRotation,
 151113            localAxisDirection * axialDistance,
 151114            roundedRadialOffset,
 151115            FixedPointAnchorTerm3d.CreateRadialSupport(
 151116                localNormal,
 151117                radius,
 151118                roundedRadialOffset));
 119    }
 120
 121    internal static FixedPointAnchor GetSurfaceAnchorOnCenteredCapsule(
 122        Vector3d point,
 123        Vector3d center,
 124        Vector3d axisDirection,
 125        Fixed64 axisLength,
 126        Fixed64 radius,
 127        Vector3d surfaceDirection)
 128    {
 1129        GetClosestCenteredAxisRatio(
 1130            point,
 1131            center,
 1132            axisDirection,
 1133            axisLength,
 1134            out Signed192 numerator,
 1135            out Signed192 denominator);
 1136        _ = TryGetCenteredCapsuleSurfaceCoordinate(
 1137            Fixed64.Zero,
 1138            axisDirection.X,
 1139            numerator,
 1140            denominator,
 1141            Fixed64.Zero,
 1142            Fixed64.Zero,
 1143            out Fixed64 axialX);
 1144        _ = TryGetCenteredCapsuleSurfaceCoordinate(
 1145            Fixed64.Zero,
 1146            axisDirection.Y,
 1147            numerator,
 1148            denominator,
 1149            Fixed64.Zero,
 1150            Fixed64.Zero,
 1151            out Fixed64 axialY);
 1152        _ = TryGetCenteredCapsuleSurfaceCoordinate(
 1153            Fixed64.Zero,
 1154            axisDirection.Z,
 1155            numerator,
 1156            denominator,
 1157            Fixed64.Zero,
 1158            Fixed64.Zero,
 1159            out Fixed64 axialZ);
 1160        Vector3d roundedRadialOffset = surfaceDirection * radius;
 1161        return new FixedPointAnchor(
 1162            center,
 1163            FixedQuaternion.Identity,
 1164            new Vector3d(axialX, axialY, axialZ),
 1165            roundedRadialOffset,
 1166            FixedPointAnchorTerm3d.CreateRadialSupport(
 1167                surfaceDirection,
 1168                radius,
 1169                roundedRadialOffset));
 170    }
 171}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredCapsuleDistance.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.CenteredCapsuleDistance.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
 8namespace FixedMathSharp.Geometry;
 9
 10/// <content>
 11/// High-precision distance computations between a point and a centered capsule
 12/// (a finite axis segment with a radius), using wide integer arithmetic to
 13/// avoid overflow and rounding error in intermediate squared-distance terms.
 14/// </content>
 15internal static partial class WideFiniteAxisIntersection
 16{
 17    internal static bool TryGetDistanceToCenteredCapsule(
 18        Vector2d point,
 19        Vector2d center,
 20        Vector2d axisDirection,
 21        Fixed64 axisLength,
 22        Fixed64 radius,
 23        out Fixed64 distance)
 24    {
 1225        GetClosestCenteredAxisRatio(
 1226            point, center, axisDirection, axisLength, out Signed192 numerator, out Signed192 denominator);
 1227        Signed320 x = GetCenteredAxisOffsetComponent(
 1228            point.X, center.X, axisDirection.X, numerator, denominator);
 1229        Signed320 y = GetCenteredAxisOffsetComponent(
 1230            point.Y, center.Y, axisDirection.Y, numerator, denominator);
 1231        return TryGetDistanceToCenteredCapsule(
 1232            WideArithmetic.AddSigned576(
 1233                WideArithmetic.MultiplySigned320(x, x),
 1234                WideArithmetic.MultiplySigned320(y, y)),
 1235            denominator,
 1236            radius,
 1237            out distance);
 38    }
 39
 40    internal static bool TryGetDistanceToCenteredCapsule(
 41        Vector3d point,
 42        Vector3d center,
 43        Vector3d axisDirection,
 44        Fixed64 axisLength,
 45        Fixed64 radius,
 46        out Fixed64 distance)
 47    {
 748        GetClosestCenteredAxisRatio(
 749            point, center, axisDirection, axisLength, out Signed192 numerator, out Signed192 denominator);
 750        Signed320 x = GetCenteredAxisOffsetComponent(
 751            point.X, center.X, axisDirection.X, numerator, denominator);
 752        Signed320 y = GetCenteredAxisOffsetComponent(
 753            point.Y, center.Y, axisDirection.Y, numerator, denominator);
 754        Signed320 z = GetCenteredAxisOffsetComponent(
 755            point.Z, center.Z, axisDirection.Z, numerator, denominator);
 756        return TryGetDistanceToCenteredCapsule(
 757            WideArithmetic.AddSigned576(
 758                WideArithmetic.AddSigned576(
 759                    WideArithmetic.MultiplySigned320(x, x),
 760                    WideArithmetic.MultiplySigned320(y, y)),
 761                WideArithmetic.MultiplySigned320(z, z)),
 762            denominator,
 763            radius,
 764            out distance);
 65    }
 66
 67    internal static bool TryGetDistanceToCenteredCapsule(
 68        Signed576 squaredAxisDistanceNumerator,
 69        Signed192 axisDenominator,
 70        Fixed64 radius,
 71        out Fixed64 distance)
 72    {
 3273        Signed320 radiusAxis = WideArithmetic.MultiplySigned192(
 3274            Signed192.Signed(radius.m_rawValue),
 3275            axisDenominator);
 3276        Signed576 radiusAxisSquared = WideArithmetic.MultiplySigned320(radiusAxis, radiusAxis);
 3277        if (WideArithmetic.CompareNonNegative(
 3278                squaredAxisDistanceNumerator,
 3279                radiusAxisSquared) <= 0)
 80        {
 281            distance = Fixed64.Zero;
 282            return true;
 83        }
 84
 3085        Signed320 scaledAxisDistance = WideArithmetic.GetFloorSquareRootScaledByFixed64(
 3086            squaredAxisDistanceNumerator);
 3087        Signed320 radiusDistance = MultiplyThreeToSigned320(
 3088            Signed192.Signed(radius.m_rawValue),
 3089            axisDenominator,
 3090            ParameterScale);
 3091        Signed320 approximateSurfaceDistance = WideArithmetic.SubtractSigned320(
 3092            scaledAxisDistance,
 3093            radiusDistance);
 3094        if (approximateSurfaceDistance.Sign <= 0)
 95        {
 196            distance = Fixed64.Zero;
 197            return true;
 98        }
 99
 29100        Signed320 scale = MultiplyThreeToSigned320(axisDenominator, ParameterScale, ParameterScale);
 29101        if (!Fixed64.TryGetSignedRatio(approximateSurfaceDistance, scale, out distance))
 102        {
 7103            distance = Fixed64.MaxValue;
 7104            return false;
 105        }
 106
 107        // A MaxValue candidate cannot require correction: every exact
 108        // half-raw midpoint maps to an integer restoring-root numerator. If
 109        // the floored numerator is below that midpoint, the exact root is not
 110        // above it; at or above it, TryGetSignedRatio already rejects the
 111        // positive overflow tie.
 22112        if (distance == Fixed64.MaxValue)
 2113            return true;
 114
 115        // The restoring root is floor(sqrt(N) * S), so the approximate gap is
 116        // below the exact gap by less than one/(axisDenominator * S) raw unit.
 117        // Its half-even result can therefore only remain unchanged or advance
 118        // by one. Compare the exact squared distance with the midpoint above
 119        // the candidate; importantly, radius participates before the parity
 120        // decision because subtracting an odd raw radius reverses tie parity.
 20121        ulong axisDistanceCandidateRaw = unchecked((ulong)radius.m_rawValue)
 20122            + unchecked((ulong)distance.m_rawValue);
 20123        Signed192 doubledMidpoint = new(
 20124            0UL,
 20125            axisDistanceCandidateRaw >> 63,
 20126            (axisDistanceCandidateRaw << 1) | 1UL);
 20127        Signed320 midpointNumerator = WideArithmetic.MultiplySigned192(
 20128            axisDenominator,
 20129            doubledMidpoint);
 20130        Signed576 midpointSquared = WideArithmetic.MultiplySigned320(
 20131            midpointNumerator,
 20132            midpointNumerator);
 20133        Signed576 fourSquaredDistance = WideArithmetic.AddSigned576(
 20134            WideArithmetic.AddSigned576(
 20135                squaredAxisDistanceNumerator,
 20136                squaredAxisDistanceNumerator),
 20137            WideArithmetic.AddSigned576(
 20138                squaredAxisDistanceNumerator,
 20139                squaredAxisDistanceNumerator));
 20140        int midpointComparison = WideArithmetic.CompareNonNegative(
 20141            fourSquaredDistance,
 20142            midpointSquared);
 20143        if (midpointComparison <= 0)
 19144            return true;
 145
 1146        distance = Fixed64.FromRaw(distance.m_rawValue + 1L);
 1147        return true;
 148    }
 149}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredCapsuleStrictOverlap.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.CenteredCapsuleStrictOverlap.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>
 13/// Exact centered-capsule overlap with finite local-+Y solids. The core chord
 14/// remains rational in both authoritative rigid frames; rim decisions retain
 15/// complete polynomial signs instead of sampling separating directions.
 16/// </content>
 17internal static partial class WideFiniteAxisIntersection
 18{
 19    private readonly struct StrictFiniteChord
 20    {
 21        internal readonly Signed320 X, Y, Z, Dx, Dy, Dz;
 22        internal readonly Signed192 Scale;
 23        internal readonly Signed320 HalfHeight;
 24
 25        internal StrictFiniteChord(Signed320 x, Signed320 y, Signed320 z,
 26            Signed320 dx, Signed320 dy, Signed320 dz, Signed192 scale, Signed320 halfHeight)
 27        {
 361828            X = x; Y = y; Z = z; Dx = dx; Dy = dy; Dz = dz;
 120629            Scale = scale; HalfHeight = halfHeight;
 60330        }
 31    }
 32
 33    /// <summary>
 34    /// Tests positive separating translation for a capsule and a finite
 35    /// cylinder. The solid uses local +Y; rotations and the capsule local axis
 36    /// are admitted unit inputs, height is positive, and radii/length are nonnegative.
 37    /// </summary>
 38    internal static bool DoesCenteredFiniteCylinderPenetrateCapsule(
 39        Vector3d center, FixedQuaternion rotation, Fixed64 height, Fixed64 radius,
 40        Vector3d capsuleCenter, FixedQuaternion capsuleRotation,
 41        Vector3d capsuleLocalAxis, Fixed64 capsuleLength, Fixed64 capsuleRadius)
 42    {
 20543        bool centerPenetrates = DoesCenteredFiniteCylinderPenetrateSphere(
 20544            center, rotation, height, radius, capsuleCenter, capsuleRadius);
 20545        if (centerPenetrates || capsuleLength == Fixed64.Zero)
 6346            return centerPenetrates;
 14247        if (radius == Fixed64.Zero)
 248            return WideOrientedBox.DoCenteredRigidCapsulesOverlap(center, rotation, Vector3d.Up,
 249                height, Fixed64.Zero, capsuleCenter, capsuleRotation, capsuleLocalAxis,
 250                capsuleLength, capsuleRadius, strict: true);
 14051        StrictFiniteChord chord = CreateStrictFiniteChord(center, rotation, height,
 14052            capsuleCenter, capsuleRotation, capsuleLocalAxis, capsuleLength);
 14053        Signed320 r = WideArithmetic.MultiplySigned192(Signed192.Raw(radius), chord.Scale);
 14054        Signed320 s = WideArithmetic.MultiplySigned192(Signed192.Raw(capsuleRadius), chord.Scale);
 14055        Span<Signed576> radial = stackalloc Signed576[3];
 14056        GetStrictRadialPolynomial(chord, radial);
 14057        if (DoesStrictChordEnterCylinder(chord, radial,
 14058                WideArithmetic.SubtractSigned320(default, chord.HalfHeight), chord.HalfHeight,
 14059                WideArithmetic.AddSigned320(r, s)))
 360            return true;
 13761        if (capsuleRadius == Fixed64.Zero)
 6462            return false;
 7363        return DoesStrictChordEnterExpandedDisk(chord, radial, r, s, chord.HalfHeight)
 7364            || DoesStrictChordEnterExpandedDisk(chord, radial, r, s,
 7365                WideArithmetic.SubtractSigned320(default, chord.HalfHeight));
 66    }
 67
 68    /// <summary>
 69    /// Tests positive separating translation against a cone with base at
 70    /// local -height/2 and apex at +height/2, under the cylinder method's
 71    /// admitted rigid-frame and nonnegative-size preconditions.
 72    /// </summary>
 73    internal static bool DoesCenteredFiniteConePenetrateCapsule(
 74        Vector3d center, FixedQuaternion rotation, Fixed64 height, Fixed64 radius,
 75        Vector3d capsuleCenter, FixedQuaternion capsuleRotation,
 76        Vector3d capsuleLocalAxis, Fixed64 capsuleLength, Fixed64 capsuleRadius)
 77    {
 54678        bool centerPenetrates = DoesCenteredFiniteConePenetrateSphere(
 54679            center, rotation, height, radius, capsuleCenter, capsuleRadius);
 54680        if (centerPenetrates || capsuleLength == Fixed64.Zero)
 8181            return centerPenetrates;
 46582        if (radius == Fixed64.Zero)
 283            return WideOrientedBox.DoCenteredRigidCapsulesOverlap(center, rotation, Vector3d.Up,
 284                height, Fixed64.Zero, capsuleCenter, capsuleRotation, capsuleLocalAxis,
 285                capsuleLength, capsuleRadius, strict: true);
 46386        StrictFiniteChord chord = CreateStrictFiniteChord(center, rotation, height,
 46387            capsuleCenter, capsuleRotation, capsuleLocalAxis, capsuleLength);
 46388        Signed320 r = WideArithmetic.MultiplySigned192(Signed192.Raw(radius), chord.Scale);
 46389        Signed320 s = WideArithmetic.MultiplySigned192(Signed192.Raw(capsuleRadius), chord.Scale);
 46390        Span<Signed576> radial = stackalloc Signed576[3];
 46391        GetStrictRadialPolynomial(chord, radial);
 46392        if (capsuleRadius > Fixed64.Zero)
 93        {
 6694            if (DoesStrictChordEnterExpandedDisk(chord, radial, r, s,
 6695                    WideArithmetic.SubtractSigned320(default, chord.HalfHeight)))
 196                return true;
 6597            Span<Signed576> apex = stackalloc Signed576[3];
 6598            GetStrictPointDistancePolynomial(chord, radial, chord.HalfHeight, apex);
 6599            apex[0] = WideArithmetic.SubtractSigned576(apex[0], WideArithmetic.MultiplySigned320(s, s));
 65100            if (HasNegativeStrictQuadratic(apex))
 1101                return true;
 102        }
 461103        return DoesStrictChordEnterOffsetConeSide(chord, radial, height, radius, s);
 104    }
 105
 106    private static StrictFiniteChord CreateStrictFiniteChord(Vector3d center, FixedQuaternion rotation,
 107        Fixed64 height, Vector3d capsuleCenter, FixedQuaternion capsuleRotation,
 108        Vector3d localAxis, Fixed64 length)
 109    {
 603110        WideRationalBasis3d basis = new(rotation);
 603111        WideRationalBasis3d capsuleBasis = new(capsuleRotation);
 603112        WideRationalBasis3d relative = WideRationalBasis3d.CreateRelative(basis, capsuleBasis);
 603113        WideAxis3 axis = WideRigidProjection.TransformLocalAxis(relative,
 603114            Signed192.Raw(localAxis.X), Signed192.Raw(localAxis.Y), Signed192.Raw(localAxis.Z));
 603115        WideOrientedBox.GetRelativeLocalPointNumerators(capsuleCenter, center, rotation,
 603116            out Signed192 x, out Signed192 y, out Signed192 z, out _);
 603117        Signed320 centerScale = WideArithmetic.MultiplySigned192(capsuleBasis.Denominator, Signed192.Raw(Fixed64.Two));
 603118        Signed192 halfScale = Signed192.NarrowValue(WideArithmetic.MultiplySigned192(
 603119            relative.Denominator, Signed192.Raw(Fixed64.One)));
 603120        Signed192 scale = WideArithmetic.AddSigned192(halfScale, halfScale);
 603121        GetStrictChordCoordinate(x, axis.X, centerScale, length, out Signed320 px, out Signed320 dx);
 603122        GetStrictChordCoordinate(y, axis.Y, centerScale, length, out Signed320 py, out Signed320 dy);
 603123        GetStrictChordCoordinate(z, axis.Z, centerScale, length, out Signed320 pz, out Signed320 dz);
 124        // D_b,D_c<2^66 and admitted |u|<2^33. Chord coordinates are below
 125        // 2^232, scale below2^165, and scaled dimensions below2^230. These
 126        // bounds retain 65-bit origin differences and half-raw endpoints.
 603127        return new StrictFiniteChord(px, py, pz, dx, dy, dz, scale,
 603128            WideArithmetic.MultiplySigned192(Signed192.Raw(height), halfScale));
 129    }
 130
 131    private static void GetStrictChordCoordinate(Signed192 translation, Signed320 axis,
 132        Signed320 centerScale, Fixed64 length, out Signed320 start, out Signed320 direction)
 133    {
 1809134        Signed576 center = WideArithmetic.MultiplySigned320(Signed320.ExtendValue(translation), centerScale);
 1809135        Signed576 half = WideArithmetic.MultiplySigned320(axis, Signed192.Raw(length));
 1809136        start = Signed320.NarrowValue(WideArithmetic.SubtractSigned576(center, half));
 1809137        direction = Signed320.NarrowValue(WideArithmetic.AddSigned576(half, half));
 1809138    }
 139
 140    private static void GetStrictRadialPolynomial(StrictFiniteChord chord, Span<Signed576> result)
 141    {
 603142        result[0] = WideArithmetic.AddSigned576(WideArithmetic.MultiplySigned320(chord.X, chord.X),
 603143            WideArithmetic.MultiplySigned320(chord.Z, chord.Z));
 603144        result[1] = WideArithmetic.MultiplySigned576(WideArithmetic.AddSigned576(
 603145            WideArithmetic.MultiplySigned320(chord.X, chord.Dx),
 603146            WideArithmetic.MultiplySigned320(chord.Z, chord.Dz)), 2);
 603147        result[2] = WideArithmetic.AddSigned576(WideArithmetic.MultiplySigned320(chord.Dx, chord.Dx),
 603148            WideArithmetic.MultiplySigned320(chord.Dz, chord.Dz));
 603149    }
 150
 151    private static bool DoesStrictChordEnterCylinder(StrictFiniteChord chord, ReadOnlySpan<Signed576> radial,
 152        Signed320 lowerY, Signed320 upperY, Signed320 radius)
 153    {
 347154        Signed192 one = Signed192.Signed(1);
 347155        if (!GetStrictChordBounds(chord.Y, chord.Dy, lowerY, upperY, default, one,
 347156                out StrictFieldBound lower, out StrictFieldBound upper))
 297157            return false;
 50158        return HasNegativeStrictFieldQuadratic(Signed832.ExtendValue(WideArithmetic.SubtractSigned576(
 50159                radial[0], WideArithmetic.MultiplySigned320(radius, radius))),
 50160            Signed832.ExtendValue(radial[1]), Signed832.ExtendValue(radial[2]), default, default, one, lower, upper);
 161    }
 162
 163    private static void GetStrictPointDistancePolynomial(StrictFiniteChord chord, ReadOnlySpan<Signed576> radial,
 164        Signed320 axialPoint, Span<Signed576> result)
 165    {
 271166        Signed320 y = WideArithmetic.SubtractSigned320(chord.Y, axialPoint);
 271167        result[0] = WideArithmetic.AddSigned576(radial[0], WideArithmetic.MultiplySigned320(y, y));
 271168        result[1] = WideArithmetic.AddSigned576(radial[1], WideArithmetic.MultiplySigned576(
 271169            WideArithmetic.MultiplySigned320(y, chord.Dy), 2));
 271170        result[2] = WideArithmetic.AddSigned576(radial[2], WideArithmetic.MultiplySigned320(chord.Dy, chord.Dy));
 271171    }
 172
 173    private static bool DoesStrictChordEnterExpandedDisk(StrictFiniteChord chord, ReadOnlySpan<Signed576> radial,
 174        Signed320 radius, Signed320 expansion, Signed320 cap)
 175    {
 207176        if (DoesStrictChordEnterCylinder(chord, radial,
 207177                WideArithmetic.SubtractSigned320(cap, expansion), WideArithmetic.AddSigned320(cap, expansion), radius))
 1178            return true;
 206179        Span<Signed576> n = stackalloc Signed576[3];
 206180        GetStrictPointDistancePolynomial(chord, radial, cap, n);
 206181        Signed576 radiusSquared = WideArithmetic.MultiplySigned320(radius, radius);
 206182        n[0] = WideArithmetic.AddSigned576(n[0], WideArithmetic.SubtractSigned576(radiusSquared,
 206183            WideArithmetic.MultiplySigned320(expansion, expansion)));
 184        // Circle-tube membership is N-2R√Q<0, equivalently N<0 or
 185        // H=N²-4R²Q<0. Full tubes and disk cores form the expanded disk;
 186        // no squared-root branch or rim separating direction is discarded.
 206187        if (HasNegativeStrictQuadratic(n))
 1188            return true;
 205189        Span<ulong> coefficients = stackalloc ulong[5 * StrictPolynomialInputWords];
 205190        Span<sbyte> signs = stackalloc sbyte[5];
 205191        coefficients.Clear();
 205192        signs.Clear();
 2460193        for (int i = 0; i < 5; i++)
 194        {
 1025195            Span<ulong> destination = coefficients.Slice(i * StrictPolynomialInputWords, StrictPolynomialInputWords);
 8200196            for (int j = 0; j < 3; j++)
 197            {
 3075198                if (i - j >= 0 && i - j < 3)
 1845199                    AccumulateStrictRimProduct(destination, ref signs[i], n[j], n[i - j], 1);
 200            }
 1025201            if (i < 3)
 615202                AccumulateStrictRimProduct(destination, ref signs[i], radiusSquared, radial[i], -4);
 203        }
 204        // N coefficients use fewer than468bits; convolution and the radial
 205        // subtraction leave every H coefficient below2^960, inside16words.
 205206        return HasNegativeFiniteAxisPolynomial(coefficients, signs);
 207    }
 208
 209    private static bool HasNegativeStrictQuadratic(ReadOnlySpan<Signed576> polynomial) =>
 271210        HasNegativeStrictFieldQuadratic(Signed832.ExtendValue(polynomial[0]),
 271211            Signed832.ExtendValue(polynomial[1]), Signed832.ExtendValue(polynomial[2]),
 271212            default, default, Signed192.Signed(1),
 271213            new StrictFieldBound(default, default, StrictOne),
 271214            new StrictFieldBound(StrictOne, default, StrictOne));
 215
 216    private static void AccumulateStrictRimProduct(Span<ulong> total, ref sbyte sign,
 217        Signed576 first, Signed576 second, int scale)
 218    {
 2460219        if (first.IsZero || second.IsZero)
 357220            return;
 2103221        Span<ulong> a = stackalloc ulong[StrictPolynomialInputWords];
 2103222        Span<ulong> b = stackalloc ulong[StrictPolynomialInputWords];
 2103223        Span<ulong> product = stackalloc ulong[StrictPolynomialInputWords];
 2103224        Span<ulong> scaled = stackalloc ulong[StrictPolynomialInputWords];
 2103225        Span<ulong> sum = stackalloc ulong[StrictPolynomialInputWords];
 4206226        a.Clear(); b.Clear();
 2103227        WideArithmetic.GetMagnitude(first, a[..9]);
 2103228        WideArithmetic.GetMagnitude(second, b[..9]);
 2103229        MultiplyRoundedCylinderWide(a, b, product);
 2103230        MultiplyRoundedCylinderWideByWord(product, (ulong)Math.Abs(scale), scaled);
 2103231        sbyte productSign = (sbyte)(first.Sign * second.Sign * Math.Sign(scale));
 2103232        AddRoundedCylinderSigned(total, sign, scaled, productSign, sum, out sign);
 2103233        sum.CopyTo(total);
 2103234    }
 235
 236    private static bool DoesStrictChordEnterOffsetConeSide(StrictFiniteChord chord,
 237        ReadOnlySpan<Signed576> radial, Fixed64 height, Fixed64 radius, Signed320 expansion)
 238    {
 922239        Signed192 h = Signed192.Raw(height), r = Signed192.Raw(radius);
 461240        Signed192 hh = Signed192.NarrowValue(WideArithmetic.MultiplySigned192(h, h));
 461241        Signed192 rr = Signed192.NarrowValue(WideArithmetic.MultiplySigned192(r, r));
 461242        Signed192 k = WideArithmetic.AddSigned192(hh, rr);
 461243        Signed832 shift = Signed832.ExtendValue(WideArithmetic.MultiplySigned320(expansion, r));
 461244        if (!GetStrictChordBounds(chord.Y, chord.Dy,
 461245                WideArithmetic.SubtractSigned320(default, chord.HalfHeight), chord.HalfHeight,
 461246                shift, k, out StrictFieldBound lower, out StrictFieldBound upper))
 352247            return false;
 248        // The expanded cone is the union of its expanded base disk, apex
 249        // sphere, and this offset-side frustum. Its y shift is s*r/√k and
 250        // side inequality is h*ρ+r*(y-h/2)<s√k. Within these bounds the
 251        // radial right side is positive, so squaring preserves the sign.
 109252        Signed320 y = WideArithmetic.SubtractSigned320(chord.Y, chord.HalfHeight);
 109253        Span<Signed576> axial = stackalloc Signed576[3]
 109254        {
 109255            WideArithmetic.MultiplySigned320(y, y),
 109256            WideArithmetic.MultiplySigned576(WideArithmetic.MultiplySigned320(y, chord.Dy), 2),
 109257            WideArithmetic.MultiplySigned320(chord.Dy, chord.Dy)
 109258        };
 109259        Span<Signed832> a = stackalloc Signed832[3];
 872260        for (int i = 0; i < 3; i++)
 327261            a[i] = WideArithmetic.SubtractSigned832(
 327262                WideArithmetic.MultiplySigned832(Signed832.ExtendValue(radial[i]), hh),
 327263                WideArithmetic.MultiplySigned832(Signed832.ExtendValue(axial[i]), rr));
 109264        a[0] = WideArithmetic.SubtractSigned832(a[0], WideArithmetic.MultiplySigned832(
 109265            Signed832.ExtendValue(WideArithmetic.MultiplySigned320(expansion, expansion)), k));
 109266        Signed832 b0 = Signed832.ExtendValue(WideArithmetic.MultiplySigned576(
 109267            WideArithmetic.MultiplySigned576(WideArithmetic.MultiplySigned320(expansion, y), r), 2));
 109268        Signed832 b1 = Signed832.ExtendValue(WideArithmetic.MultiplySigned576(
 109269            WideArithmetic.MultiplySigned576(WideArithmetic.MultiplySigned320(expansion, chord.Dy), r), 2));
 109270        return HasNegativeStrictFieldQuadratic(a[0], a[1], a[2], b0, b1, k, lower, upper);
 271    }
 272
 273}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredCapsuleSweep2d.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.CenteredCapsuleSweep2d.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
 8namespace FixedMathSharp.Geometry;
 9
 10/// <content>
 11/// Provides 2D swept intersection tests between a center-defined capsule (axis + radius)
 12/// moving along a direction and a target segment-based capsule, using wide (Signed192) precision.
 13/// </content>
 14internal static partial class WideFiniteAxisIntersection
 15{
 16    #region Nested Types
 17
 18    private readonly struct WideAxis2d
 19    {
 20        internal readonly Signed192 StartX;
 21        internal readonly Signed192 StartY;
 22        internal readonly Signed192 EndX;
 23        internal readonly Signed192 EndY;
 24
 25        internal WideAxis2d(Signed192 startX, Signed192 startY, Signed192 endX, Signed192 endY)
 26        {
 610627            StartX = startX;
 610628            StartY = startY;
 610629            EndX = endX;
 610630            EndY = endY;
 610631        }
 32    }
 33
 34    #endregion
 35
 36    internal static bool TryGetSweptCenteredCapsuleSegmentFirstDistance(
 37        Vector2d center,
 38        Vector2d axis,
 39        Fixed64 axisLength,
 40        Fixed64 radius,
 41        Vector2d direction,
 42        Fixed64 maximumDistance,
 43        FixedSegment2d targetAxis,
 44        Fixed64 targetRadius,
 45        out Fixed64 distance,
 46        out Vector2d normal,
 47        bool authoredDisplacement = false)
 48    {
 849        GetCenteredAxisEndpoints(center, axis, axisLength, out WideAxis2d mover);
 850        GetAuthoredAxisEndpoints(targetAxis, out WideAxis2d target);
 851        return TryGetSweptAxesFirstDistance(
 852            mover,
 853            radius,
 854            direction,
 855            maximumDistance,
 856            target,
 857            targetRadius,
 858            axis,
 859            out distance,
 860            out normal,
 861            authoredDisplacement);
 62    }
 63
 64    internal static bool TryGetSweptCenteredCapsuleOriginOffsetSegmentFirstDistance(
 65        Vector2d center,
 66        Vector2d axis,
 67        Fixed64 axisLength,
 68        Fixed64 radius,
 69        Vector2d direction,
 70        Fixed64 maximumDistance,
 71        Vector2d targetOrigin,
 72        Fixed64 targetRotation,
 73        Vector2d targetStartOffset,
 74        Vector2d targetEndOffset,
 75        Fixed64 targetRadius,
 76        out Fixed64 distance,
 77        out Vector2d normal,
 78        bool authoredDisplacement = false)
 79    {
 144280        GetCenteredAxisEndpoints(center, axis, axisLength, out WideAxis2d mover);
 144281        GetRotatedOriginOffsetAxisEndpoints(
 144282            targetOrigin,
 144283            targetRotation,
 144284            targetStartOffset,
 144285            targetEndOffset,
 144286            out WideAxis2d target);
 144287        return TryGetSweptAxesFirstDistance(
 144288            mover,
 144289            radius,
 144290            direction,
 144291            maximumDistance,
 144292            target,
 144293            targetRadius,
 144294            axis,
 144295            out distance,
 144296            out normal,
 144297            authoredDisplacement);
 98    }
 99
 100    internal static bool TryGetSweptOriginOffsetSegmentsFirstDistance(
 101        Vector2d moverOrigin,
 102        Fixed64 moverRotation,
 103        Vector2d moverStartOffset,
 104        Vector2d moverEndOffset,
 105        Fixed64 moverRadius,
 106        Vector2d direction,
 107        Fixed64 maximumDistance,
 108        Vector2d targetOrigin,
 109        Fixed64 targetRotation,
 110        Vector2d targetStartOffset,
 111        Vector2d targetEndOffset,
 112        Fixed64 targetRadius,
 113        out Fixed64 distance,
 114        out Vector2d normal,
 115        bool authoredDisplacement = false)
 116    {
 1590117        GetRotatedOriginOffsetAxisEndpoints(
 1590118            moverOrigin,
 1590119            moverRotation,
 1590120            moverStartOffset,
 1590121            moverEndOffset,
 1590122            out WideAxis2d mover);
 1590123        GetRotatedOriginOffsetAxisEndpoints(
 1590124            targetOrigin,
 1590125            targetRotation,
 1590126            targetStartOffset,
 1590127            targetEndOffset,
 1590128            out WideAxis2d target);
 1590129        return TryGetSweptAxesFirstDistance(
 1590130            mover,
 1590131            moverRadius,
 1590132            direction,
 1590133            maximumDistance,
 1590134            target,
 1590135            targetRadius,
 1590136            WideConvex2dRelations.GetTransformedEdgeDirection(
 1590137                moverRotation,
 1590138                moverStartOffset,
 1590139                moverEndOffset),
 1590140            out distance,
 1590141            out normal,
 1590142            authoredDisplacement);
 143    }
 144
 145    internal static bool TryGetSweptCenteredCapsulesFirstDistance(
 146        Vector2d center,
 147        Vector2d axis,
 148        Fixed64 axisLength,
 149        Fixed64 radius,
 150        Vector2d direction,
 151        Fixed64 maximumDistance,
 152        Vector2d targetCenter,
 153        Vector2d targetAxis,
 154        Fixed64 targetAxisLength,
 155        Fixed64 targetRadius,
 156        out Fixed64 distance,
 157        out Vector2d normal,
 158        bool authoredDisplacement = false)
 159    {
 13160        GetCenteredAxisEndpoints(center, axis, axisLength, out WideAxis2d mover);
 13161        GetCenteredAxisEndpoints(targetCenter, targetAxis, targetAxisLength, out WideAxis2d target);
 13162        return TryGetSweptAxesFirstDistance(
 13163            mover,
 13164            radius,
 13165            direction,
 13166            maximumDistance,
 13167            target,
 13168            targetRadius,
 13169            axis,
 13170            out distance,
 13171            out normal,
 13172            authoredDisplacement);
 173    }
 174
 175    private static bool TryGetSweptAxesFirstDistance(
 176        WideAxis2d mover,
 177        Fixed64 moverRadius,
 178        Vector2d direction,
 179        Fixed64 maximumDistance,
 180        WideAxis2d target,
 181        Fixed64 targetRadius,
 182        Vector2d moverAxis,
 183        out Fixed64 distance,
 184        out Vector2d normal,
 185        bool authoredDisplacement)
 186    {
 3053187        Signed192 radius = WideArithmetic.AddSigned192(
 3053188            Signed192.Signed(moverRadius.m_rawValue),
 3053189            Signed192.Signed(targetRadius.m_rawValue));
 3053190        Signed192 scaledRadius = Signed192.NarrowValue(
 3053191            WideArithmetic.MultiplySigned192(radius, DoubleParameterScale));
 3053192        GetSweepVelocity(
 3053193            direction,
 3053194            maximumDistance,
 3053195            authoredDisplacement,
 3053196            out Signed192 displacementPerRawX,
 3053197            out Signed192 displacementPerRawY,
 3053198            out Signed192 velocityX,
 3053199            out Signed192 velocityY);
 3053200        Signed192 pointDenominator = Signed192.Signed(
 3053201            authoredDisplacement ? maximumDistance.m_rawValue : 1L);
 202
 3053203        bool found = false;
 3053204        distance = default;
 3053205        normal = default;
 3053206        TryKeepEndpointAxisSweep(
 3053207            mover.StartX, mover.StartY,
 3053208            displacementPerRawX, displacementPerRawY,
 3053209            velocityX, velocityY,
 3053210            target, scaledRadius, maximumDistance, pointDenominator, reverseNormal: false,
 3053211            ref found, ref distance, ref normal);
 3053212        TryKeepEndpointAxisSweep(
 3053213            mover.EndX, mover.EndY,
 3053214            displacementPerRawX, displacementPerRawY,
 3053215            velocityX, velocityY,
 3053216            target, scaledRadius, maximumDistance, pointDenominator, reverseNormal: false,
 3053217            ref found, ref distance, ref normal);
 3053218        TryKeepEndpointAxisSweep(
 3053219            target.StartX, target.StartY,
 3053220            WideArithmetic.SubtractSigned192(default, displacementPerRawX),
 3053221            WideArithmetic.SubtractSigned192(default, displacementPerRawY),
 3053222            WideArithmetic.SubtractSigned192(default, velocityX),
 3053223            WideArithmetic.SubtractSigned192(default, velocityY),
 3053224            mover, scaledRadius, maximumDistance, pointDenominator, reverseNormal: true,
 3053225            ref found, ref distance, ref normal);
 3053226        TryKeepEndpointAxisSweep(
 3053227            target.EndX, target.EndY,
 3053228            WideArithmetic.SubtractSigned192(default, displacementPerRawX),
 3053229            WideArithmetic.SubtractSigned192(default, displacementPerRawY),
 3053230            WideArithmetic.SubtractSigned192(default, velocityX),
 3053231            WideArithmetic.SubtractSigned192(default, velocityY),
 3053232            mover, scaledRadius, maximumDistance, pointDenominator, reverseNormal: true,
 3053233            ref found, ref distance, ref normal);
 234
 3053235        if (AxesIntersect(mover, target) && (!found || distance > Fixed64.Zero))
 236        {
 1237            distance = Fixed64.Zero;
 1238            normal = new Vector2d(-moverAxis.Y, moverAxis.X);
 1239            return true;
 240        }
 241
 3052242        return found;
 243    }
 244
 245    private static void TryKeepEndpointAxisSweep(
 246        Signed192 startX,
 247        Signed192 startY,
 248        Signed192 displacementPerRawX,
 249        Signed192 displacementPerRawY,
 250        Signed192 velocityX,
 251        Signed192 velocityY,
 252        WideAxis2d target,
 253        Signed192 scaledRadius,
 254        Fixed64 maximumDistance,
 255        Signed192 pointDenominator,
 256        bool reverseNormal,
 257        ref bool found,
 258        ref Fixed64 bestDistance,
 259        ref Vector2d bestNormal)
 260    {
 12212261        bool candidateFound = false;
 12212262        Fixed64 candidateDistance = default;
 12212263        TryKeepCircleSweep(
 12212264            startX, startY, velocityX, velocityY,
 12212265            target.StartX, target.StartY, scaledRadius, maximumDistance,
 12212266            ref candidateFound, ref candidateDistance);
 12212267        TryKeepCircleSweep(
 12212268            startX, startY, velocityX, velocityY,
 12212269            target.EndX, target.EndY, scaledRadius, maximumDistance,
 12212270            ref candidateFound, ref candidateDistance);
 12212271        TryKeepSideSweep(
 12212272            startX, startY,
 12212273            displacementPerRawX, displacementPerRawY,
 12212274            velocityX, velocityY,
 12212275            target, scaledRadius, maximumDistance, pointDenominator,
 12212276            ref candidateFound, ref candidateDistance);
 12212277        if (!candidateFound || (found && candidateDistance >= bestDistance))
 9827278            return;
 279
 2385280        found = true;
 2385281        bestDistance = candidateDistance;
 2385282        bestNormal = GetEndpointAxisNormal(
 2385283            startX,
 2385284            startY,
 2385285            displacementPerRawX,
 2385286            displacementPerRawY,
 2385287            candidateDistance,
 2385288            target,
 2385289            pointDenominator);
 2385290        if (bestNormal == Vector2d.Zero)
 291        {
 1547292            bestNormal = GetAxisSweepFallbackNormal(
 1547293                target,
 1547294                velocityX,
 1547295                velocityY);
 296        }
 2385297        if (reverseNormal)
 17298            bestNormal = -bestNormal;
 2385299    }
 300
 301    private static Vector2d GetAxisSweepFallbackNormal(
 302        WideAxis2d target,
 303        Signed192 velocityX,
 304        Signed192 velocityY)
 305    {
 1547306        Vector2d edgeDirection = WideNormalization.GetNormalized(
 1547307            WideArithmetic.SubtractSigned192(target.EndX, target.StartX),
 1547308            WideArithmetic.SubtractSigned192(target.EndY, target.StartY));
 1547309        Vector2d normal = edgeDirection.RightHandNormal;
 1547310        if (normal == Vector2d.Zero)
 193311            normal = WideNormalization.GetNormalized(velocityX, velocityY);
 312
 1547313        Signed320 velocityProjection = WideArithmetic.AddProducts(
 1547314            Signed192.Signed(normal.X.m_rawValue),
 1547315            velocityX,
 1547316            Signed192.Signed(normal.Y.m_rawValue),
 1547317            velocityY);
 1547318        return velocityProjection.Sign < 0 ? -normal : normal;
 319    }
 320
 321    private static void TryKeepCircleSweep(
 322        Signed192 startX,
 323        Signed192 startY,
 324        Signed192 velocityX,
 325        Signed192 velocityY,
 326        Signed192 circleX,
 327        Signed192 circleY,
 328        Signed192 scaledRadius,
 329        Fixed64 maximumDistance,
 330        ref bool found,
 331        ref Fixed64 distance)
 332    {
 24424333        Signed192 x = WideArithmetic.SubtractSigned192(startX, circleX);
 24424334        Signed192 y = WideArithmetic.SubtractSigned192(startY, circleY);
 24424335        Signed320 coefficient = WideArithmetic.AddProducts(velocityX, velocityX, velocityY, velocityY);
 24424336        Signed320 projection = WideArithmetic.AddProducts(x, velocityX, y, velocityY);
 24424337        Signed320 constant = WideArithmetic.SubtractSigned320(
 24424338            WideArithmetic.AddProducts(x, x, y, y),
 24424339            WideArithmetic.MultiplySigned192(scaledRadius, scaledRadius));
 24424340        KeepFirstSweepRoot(
 24424341            Signed576.ExtendValue(coefficient),
 24424342            Signed576.ExtendValue(projection),
 24424343            Signed576.ExtendValue(constant),
 24424344            maximumDistance,
 24424345            ref found,
 24424346            ref distance);
 24424347    }
 348
 349    private static void TryKeepSideSweep(
 350        Signed192 startX,
 351        Signed192 startY,
 352        Signed192 displacementPerRawX,
 353        Signed192 displacementPerRawY,
 354        Signed192 velocityX,
 355        Signed192 velocityY,
 356        WideAxis2d target,
 357        Signed192 scaledRadius,
 358        Fixed64 maximumDistance,
 359        Signed192 pointDenominator,
 360        ref bool found,
 361        ref Fixed64 distance)
 362    {
 12212363        Signed192 deltaX = WideArithmetic.SubtractSigned192(target.EndX, target.StartX);
 12212364        Signed192 deltaY = WideArithmetic.SubtractSigned192(target.EndY, target.StartY);
 12212365        Signed192 offsetX = WideArithmetic.SubtractSigned192(startX, target.StartX);
 12212366        Signed192 offsetY = WideArithmetic.SubtractSigned192(startY, target.StartY);
 12212367        Signed320 startCross = WideArithmetic.MultiplySubtract(
 12212368            offsetX, deltaY, offsetY, deltaX);
 12212369        Signed320 velocityCross = WideArithmetic.MultiplySubtract(
 12212370            velocityX, deltaY, velocityY, deltaX);
 12212371        Signed320 deltaSquared = WideArithmetic.AddProducts(deltaX, deltaX, deltaY, deltaY);
 12212372        if (deltaSquared.IsZero)
 2422373            return;
 374
 9790375        Signed576 coefficient = WideArithmetic.MultiplySigned320(velocityCross, velocityCross);
 9790376        Signed576 projection = WideArithmetic.MultiplySigned320(startCross, velocityCross);
 9790377        Signed576 constant = WideArithmetic.SubtractSigned576(
 9790378            WideArithmetic.MultiplySigned320(startCross, startCross),
 9790379            WideArithmetic.MultiplySigned320(
 9790380                WideArithmetic.MultiplySigned192(scaledRadius, scaledRadius),
 9790381                deltaSquared));
 9790382        bool sideFound = WideFiniteConeIntersection.TrySolveUnitPolynomial(
 9790383            coefficient,
 9790384            projection,
 9790385            constant,
 9790386            maximumDistance,
 9790387            out Fixed64 entry,
 9790388            out _);
 9790389        if (!sideFound
 9790390            || !IsWithinAxisAtDistance(
 9790391                startX,
 9790392                startY,
 9790393                displacementPerRawX,
 9790394                displacementPerRawY,
 9790395                entry,
 9790396                target,
 9790397                deltaX,
 9790398                deltaY,
 9790399                deltaSquared,
 9790400                pointDenominator)
 9790401            || (found && entry >= distance))
 402        {
 7608403            return;
 404        }
 405
 2182406        found = true;
 2182407        distance = entry;
 2182408    }
 409
 410    private static bool IsWithinAxisAtDistance(
 411        Signed192 startX,
 412        Signed192 startY,
 413        Signed192 displacementPerRawX,
 414        Signed192 displacementPerRawY,
 415        Fixed64 distance,
 416        WideAxis2d target,
 417        Signed192 deltaX,
 418        Signed192 deltaY,
 419        Signed320 deltaSquared,
 420        Signed192 pointDenominator)
 421    {
 6160422        GetPointAtDistance(
 6160423            startX,
 6160424            startY,
 6160425            displacementPerRawX,
 6160426            displacementPerRawY,
 6160427            distance,
 6160428            pointDenominator,
 6160429            out Signed192 pointX,
 6160430            out Signed192 pointY);
 6160431        Signed320 projection = WideArithmetic.AddProducts(
 6160432            WideArithmetic.SubtractSigned192(pointX, ScaleSweepCoordinate(target.StartX, pointDenominator)),
 6160433            deltaX,
 6160434            WideArithmetic.SubtractSigned192(pointY, ScaleSweepCoordinate(target.StartY, pointDenominator)),
 6160435            deltaY);
 6160436        return projection.Sign >= 0
 6160437            && WideArithmetic.SubtractSigned576(Signed576.ExtendValue(projection),
 6160438                WideArithmetic.MultiplySigned320(deltaSquared, Signed320.ExtendValue(pointDenominator))).Sign <= 0;
 439    }
 440
 441    private static void KeepFirstSweepRoot(
 442        Signed576 coefficient,
 443        Signed576 projection,
 444        Signed576 constant,
 445        Fixed64 maximumDistance,
 446        ref bool found,
 447        ref Fixed64 distance)
 448    {
 24424449        if (!WideFiniteConeIntersection.TrySolveUnitPolynomial(
 24424450                coefficient,
 24424451                projection,
 24424452                constant,
 24424453                maximumDistance,
 24424454                out Fixed64 entry,
 24424455                out _)
 24424456            || (found && entry >= distance))
 457        {
 21006458            return;
 459        }
 460
 3418461        found = true;
 3418462        distance = entry;
 3418463    }
 464
 465    private static Vector2d GetEndpointAxisNormal(
 466        Signed192 startX,
 467        Signed192 startY,
 468        Signed192 displacementPerRawX,
 469        Signed192 displacementPerRawY,
 470        Fixed64 distance,
 471        WideAxis2d target,
 472        Signed192 pointDenominator)
 473    {
 2385474        GetPointAtDistance(
 2385475            startX,
 2385476            startY,
 2385477            displacementPerRawX,
 2385478            displacementPerRawY,
 2385479            distance,
 2385480            pointDenominator,
 2385481            out Signed192 pointX,
 2385482            out Signed192 pointY);
 2385483        Signed192 deltaX = WideArithmetic.SubtractSigned192(target.EndX, target.StartX);
 2385484        Signed192 deltaY = WideArithmetic.SubtractSigned192(target.EndY, target.StartY);
 2385485        Signed320 deltaSquared = WideArithmetic.AddProducts(deltaX, deltaX, deltaY, deltaY);
 486        // Chord points carry one extra raw-length denominator: coordinates fit
 487        // Signed192, projections fit Signed320, and closest-point products fit Signed576.
 2385488        Signed192 targetStartX = ScaleSweepCoordinate(target.StartX, pointDenominator);
 2385489        Signed192 targetStartY = ScaleSweepCoordinate(target.StartY, pointDenominator);
 2385490        Signed320 projection = WideArithmetic.AddProducts(
 2385491            WideArithmetic.SubtractSigned192(pointX, targetStartX),
 2385492            deltaX,
 2385493            WideArithmetic.SubtractSigned192(pointY, targetStartY),
 2385494            deltaY);
 2385495        if (projection.Sign <= 0)
 496        {
 636497            return WideNormalization.GetNormalized(
 636498                WideArithmetic.SubtractSigned192(targetStartX, pointX),
 636499                WideArithmetic.SubtractSigned192(targetStartY, pointY));
 500        }
 1749501        if (WideArithmetic.SubtractSigned576(Signed576.ExtendValue(projection),
 1749502                WideArithmetic.MultiplySigned320(deltaSquared, Signed320.ExtendValue(pointDenominator))).Sign >= 0)
 503        {
 426504            return WideNormalization.GetNormalized(
 426505                WideArithmetic.SubtractSigned192(ScaleSweepCoordinate(target.EndX, pointDenominator), pointX),
 426506                WideArithmetic.SubtractSigned192(ScaleSweepCoordinate(target.EndY, pointDenominator), pointY));
 507        }
 508
 1323509        Signed576 closestX = WideArithmetic.SubtractSigned576(
 1323510            WideArithmetic.AddSigned576(
 1323511                WideArithmetic.MultiplySigned320(
 1323512                    Signed320.ExtendValue(targetStartX),
 1323513                    deltaSquared),
 1323514                WideArithmetic.MultiplySigned320(
 1323515                    Signed320.ExtendValue(deltaX),
 1323516                    projection)),
 1323517            WideArithmetic.MultiplySigned320(
 1323518                Signed320.ExtendValue(pointX),
 1323519                deltaSquared));
 1323520        Signed576 closestY = WideArithmetic.SubtractSigned576(
 1323521            WideArithmetic.AddSigned576(
 1323522                WideArithmetic.MultiplySigned320(
 1323523                    Signed320.ExtendValue(targetStartY),
 1323524                    deltaSquared),
 1323525                WideArithmetic.MultiplySigned320(
 1323526                    Signed320.ExtendValue(deltaY),
 1323527                    projection)),
 1323528            WideArithmetic.MultiplySigned320(
 1323529                Signed320.ExtendValue(pointY),
 1323530                deltaSquared));
 1323531        return WideNormalization.GetNormalized(closestX, closestY);
 532    }
 533
 534    private static void GetPointAtDistance(
 535        Signed192 startX,
 536        Signed192 startY,
 537        Signed192 displacementPerRawX,
 538        Signed192 displacementPerRawY,
 539        Fixed64 distance,
 540        Signed192 pointDenominator,
 541        out Signed192 pointX,
 542        out Signed192 pointY)
 543    {
 8545544        Signed192 rawDistance = Signed192.Signed(distance.m_rawValue);
 8545545        pointX = Signed192.NarrowValue(WideArithmetic.AddSigned320(
 8545546            WideArithmetic.MultiplySigned192(startX, pointDenominator),
 8545547            WideArithmetic.MultiplySigned192(displacementPerRawX, rawDistance)));
 8545548        pointY = Signed192.NarrowValue(WideArithmetic.AddSigned320(
 8545549            WideArithmetic.MultiplySigned192(startY, pointDenominator),
 8545550            WideArithmetic.MultiplySigned192(displacementPerRawY, rawDistance)));
 8545551    }
 552
 553    private static bool AxesIntersect(WideAxis2d first, WideAxis2d second)
 554    {
 3053555        int firstStart = Orientation(first.StartX, first.StartY, first.EndX, first.EndY, second.StartX, second.StartY);
 3053556        int firstEnd = Orientation(first.StartX, first.StartY, first.EndX, first.EndY, second.EndX, second.EndY);
 3053557        int secondStart = Orientation(second.StartX, second.StartY, second.EndX, second.EndY, first.StartX, first.StartY
 3053558        int secondEnd = Orientation(second.StartX, second.StartY, second.EndX, second.EndY, first.EndX, first.EndY);
 3053559        return (firstStart == 0 && IsWithinBounds(first, second.StartX, second.StartY))
 3053560            || (firstEnd == 0 && IsWithinBounds(first, second.EndX, second.EndY))
 3053561            || (secondStart == 0 && IsWithinBounds(second, first.StartX, first.StartY))
 3053562            || (secondEnd == 0 && IsWithinBounds(second, first.EndX, first.EndY))
 3053563            || (firstStart != firstEnd && secondStart != secondEnd);
 564    }
 565
 566    private static int Orientation(
 567        Signed192 startX,
 568        Signed192 startY,
 569        Signed192 endX,
 570        Signed192 endY,
 571        Signed192 pointX,
 572        Signed192 pointY) =>
 12212573        WideArithmetic.MultiplySubtract(
 12212574            WideArithmetic.SubtractSigned192(endX, startX),
 12212575            WideArithmetic.SubtractSigned192(pointY, startY),
 12212576            WideArithmetic.SubtractSigned192(endY, startY),
 12212577            WideArithmetic.SubtractSigned192(pointX, startX)).Sign;
 578
 579    private static bool IsWithinBounds(WideAxis2d axis, Signed192 x, Signed192 y) =>
 3166580        IsBetween(x, axis.StartX, axis.EndX)
 3166581        && IsBetween(y, axis.StartY, axis.EndY);
 582
 583    private static bool IsBetween(Signed192 value, Signed192 first, Signed192 second)
 584    {
 3282585        if (WideArithmetic.SubtractSigned192(first, second).Sign > 0)
 247586            (first, second) = (second, first);
 3282587        return WideArithmetic.SubtractSigned192(value, first).Sign >= 0
 3282588            && WideArithmetic.SubtractSigned192(value, second).Sign <= 0;
 589    }
 590
 591    private static void GetSweepVelocity(
 592        Vector2d direction,
 593        Fixed64 maximumDistance,
 594        bool authoredDisplacement,
 595        out Signed192 displacementPerRawX,
 596        out Signed192 displacementPerRawY,
 597        out Signed192 x,
 598        out Signed192 y)
 599    {
 3053600        Signed192 rawX = Signed192.Signed(direction.X.m_rawValue);
 3053601        Signed192 rawY = Signed192.Signed(direction.Y.m_rawValue);
 3053602        displacementPerRawX = WideArithmetic.AddSigned192(rawX, rawX);
 3053603        displacementPerRawY = WideArithmetic.AddSigned192(rawY, rawY);
 604        // Distance is only an output scale for an authored chord. Retain its
 605        // exact endpoint difference in the polynomial and homogeneous point.
 3053606        if (authoredDisplacement)
 607        {
 76608            displacementPerRawX = Signed192.NarrowValue(WideArithmetic.MultiplySigned192(displacementPerRawX, ParameterS
 76609            displacementPerRawY = Signed192.NarrowValue(WideArithmetic.MultiplySigned192(displacementPerRawY, ParameterS
 76610            x = displacementPerRawX;
 76611            y = displacementPerRawY;
 76612            return;
 613        }
 2977614        Signed192 distance = Signed192.Signed(maximumDistance.m_rawValue);
 2977615        x = Signed192.NarrowValue(WideArithmetic.MultiplySigned192(displacementPerRawX, distance));
 2977616        y = Signed192.NarrowValue(WideArithmetic.MultiplySigned192(displacementPerRawY, distance));
 2977617    }
 618
 619    private static Signed192 ScaleSweepCoordinate(Signed192 coordinate, Signed192 denominator) =>
 17942620        Signed192.NarrowValue(WideArithmetic.MultiplySigned192(coordinate, denominator));
 621
 622    private static void GetCenteredAxisEndpoints(
 623        Vector2d center,
 624        Vector2d axis,
 625        Fixed64 axisLength,
 626        out WideAxis2d endpoints)
 627    {
 1476628        Signed192 startX = GetCenteredEndpointNumerator(center.X, axis.X, axisLength, positive: false);
 1476629        Signed192 startY = GetCenteredEndpointNumerator(center.Y, axis.Y, axisLength, positive: false);
 1476630        Signed192 endX = GetCenteredEndpointNumerator(center.X, axis.X, axisLength, positive: true);
 1476631        Signed192 endY = GetCenteredEndpointNumerator(center.Y, axis.Y, axisLength, positive: true);
 1476632        endpoints = new WideAxis2d(startX, startY, endX, endY);
 1476633    }
 634
 635    private static void GetAuthoredAxisEndpoints(FixedSegment2d axis, out WideAxis2d endpoints) =>
 8636        endpoints = new WideAxis2d(
 8637            ScaleAuthoredCoordinate(axis.Start.X),
 8638            ScaleAuthoredCoordinate(axis.Start.Y),
 8639            ScaleAuthoredCoordinate(axis.End.X),
 8640            ScaleAuthoredCoordinate(axis.End.Y));
 641
 642    private static void GetRotatedOriginOffsetAxisEndpoints(
 643        Vector2d origin,
 644        Fixed64 rotation,
 645        Vector2d startOffset,
 646        Vector2d endOffset,
 647        out WideAxis2d endpoints)
 648    {
 4622649        GetRotatedOriginOffsetCoordinate(
 4622650            origin,
 4622651            rotation,
 4622652            startOffset,
 4622653            out Signed192 startX,
 4622654            out Signed192 startY);
 4622655        GetRotatedOriginOffsetCoordinate(
 4622656            origin,
 4622657            rotation,
 4622658            endOffset,
 4622659            out Signed192 endX,
 4622660            out Signed192 endY);
 4622661        endpoints = new WideAxis2d(startX, startY, endX, endY);
 4622662    }
 663
 664    private static void GetRotatedOriginOffsetCoordinate(
 665        Vector2d origin,
 666        Fixed64 rotation,
 667        Vector2d offset,
 668        out Signed192 x,
 669        out Signed192 y)
 670    {
 9244671        Signed192 cosine =
 9244672            Signed192.Signed(
 9244673                FixedMath.Cos(rotation).m_rawValue);
 9244674        Signed192 sine =
 9244675            Signed192.Signed(
 9244676                FixedMath.Sin(rotation).m_rawValue);
 9244677        Signed320 rotatedX = WideArithmetic.SubtractSigned320(
 9244678            WideArithmetic.MultiplySigned192(
 9244679                Signed192.Signed(offset.X.m_rawValue),
 9244680                cosine),
 9244681            WideArithmetic.MultiplySigned192(
 9244682                Signed192.Signed(offset.Y.m_rawValue),
 9244683                sine));
 9244684        Signed320 rotatedY = WideArithmetic.AddSigned320(
 9244685            WideArithmetic.MultiplySigned192(
 9244686                Signed192.Signed(offset.X.m_rawValue),
 9244687                sine),
 9244688            WideArithmetic.MultiplySigned192(
 9244689                Signed192.Signed(offset.Y.m_rawValue),
 9244690                cosine));
 9244691        Signed320 originX = WideArithmetic.MultiplySigned192(
 9244692            Signed192.Signed(origin.X.m_rawValue),
 9244693            DoubleParameterScale);
 9244694        Signed320 originY = WideArithmetic.MultiplySigned192(
 9244695            Signed192.Signed(origin.Y.m_rawValue),
 9244696            DoubleParameterScale);
 9244697        x = Signed192.NarrowValue(
 9244698            WideArithmetic.AddSigned320(
 9244699                originX,
 9244700                WideArithmetic.AddSigned320(rotatedX, rotatedX)));
 9244701        y = Signed192.NarrowValue(
 9244702            WideArithmetic.AddSigned320(
 9244703                originY,
 9244704                WideArithmetic.AddSigned320(rotatedY, rotatedY)));
 9244705    }
 706
 707    private static Signed192 GetCenteredEndpointNumerator(
 708        Fixed64 center,
 709        Fixed64 axis,
 710        Fixed64 axisLength,
 711        bool positive)
 712    {
 5904713        Signed320 centerTerm = WideArithmetic.MultiplySigned192(
 5904714            Signed192.Signed(center.m_rawValue),
 5904715            DoubleParameterScale);
 5904716        Signed320 axisTerm = WideArithmetic.MultiplySigned192(
 5904717            Signed192.Signed(axis.m_rawValue),
 5904718            Signed192.Signed(axisLength.m_rawValue));
 5904719        return Signed192.NarrowValue(positive
 5904720            ? WideArithmetic.AddSigned320(centerTerm, axisTerm)
 5904721            : WideArithmetic.SubtractSigned320(centerTerm, axisTerm));
 722    }
 723
 724    private static Signed192 ScaleAuthoredCoordinate(Fixed64 value) =>
 32725        Signed192.NarrowValue(WideArithmetic.MultiplySigned192(
 32726            Signed192.Signed(value.m_rawValue),
 32727            DoubleParameterScale));
 728}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredCapsuleTriangleRay.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.CenteredCapsuleTriangleRay.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>Exact finite capsule boundary rays through retained coincident core/triangle witnesses.</content>
 13internal static partial class WideFiniteAxisIntersection
 14{
 15    private const int CapsuleTriangleRayWords = 64;
 16    private const int CapsuleTriangleRayRootWords = 40;
 86917    private static Signed576 RayExtend(Signed192 value) => Signed576.ExtendValue(Signed320.ExtendValue(value));
 3218    private static Signed576 RayExtend(Signed320 value) => Signed576.ExtendValue(value);
 19    private static ContactQuadratic RayField(Span<ulong> values, Span<int> signs, int index) =>
 44120        ContactQuadratic.At(values, signs, index, CapsuleTriangleRayWords);
 21
 22    private static Signed576 GetRetainedRayDot(Signed576 x, Signed576 y, Signed576 z, Vector3d direction) =>
 1223        WideArithmetic.AddSigned576(WideArithmetic.AddSigned576(
 1224            WideArithmetic.MultiplySigned576(x, Signed192.Raw(direction.X)),
 1225            WideArithmetic.MultiplySigned576(y, Signed192.Raw(direction.Y))),
 1226            WideArithmetic.MultiplySigned576(z, Signed192.Raw(direction.Z)));
 27
 28    private static void GetCoincidentCapsuleRayExit(in AxisTriangleCandidate point, Vector3d center,
 29        Vector3d axis, Fixed64 length, Fixed64 radius, Vector3d direction,
 30        ContactQuadratic numerator, ContactQuadratic denominator, Span<ulong> root,
 31        out int endpointSign)
 32    {
 7633        Span<ulong> values = stackalloc ulong[8 * CapsuleTriangleRayWords]; Span<int> signs = stackalloc int[8];
 7634        var x = RayField(values, signs, 0); var y = RayField(values, signs, 1);
 7635        var z = RayField(values, signs, 2); var d = RayField(values, signs, 3);
 3836        x.Set(SubtractVertex(point.AxisX, center.X, point.Denominator));
 3837        y.Set(SubtractVertex(point.AxisY, center.Y, point.Denominator));
 7638        z.Set(SubtractVertex(point.AxisZ, center.Z, point.Denominator)); d.Set(point.Denominator);
 3839        GetCoincidentCapsuleRayExit(x, y, z, d, axis, length, radius, direction,
 3840            numerator, denominator, root, out endpointSign);
 3841    }
 42
 43    /// <summary>First body/cap exit from an exact rational core point translated about the capsule center.</summary>
 44    private static void GetCoincidentCapsuleRayExit(scoped ContactQuadratic px, scoped ContactQuadratic py,
 45        scoped ContactQuadratic pz, scoped ContactQuadratic pointD, Vector3d axis, Fixed64 length,
 46        Fixed64 radius, Vector3d direction, scoped ContactQuadratic numerator, scoped ContactQuadratic denominator,
 47        scoped Span<ulong> root, out int endpointSign)
 48    {
 4649        Signed192 aa = GetDot(axis, Vector3d.Zero, axis, Vector3d.Zero),
 4650            ad = GetDot(axis, Vector3d.Zero, direction, Vector3d.Zero),
 4651            dd = GetDot(direction, Vector3d.Zero, direction, Vector3d.Zero);
 4652        Signed320 perpendicular = WideArithmetic.MultiplySubtract(aa, dd, ad, ad);
 4653        int words = numerator.FieldWords;
 9254        Span<ulong> values = stackalloc ulong[24 * words]; Span<int> signs = stackalloc int[24];
 9255        var a = ContactQuadratic.At(values, signs, 0, words); var b = ContactQuadratic.At(values, signs, 1, words);
 9256        var c = ContactQuadratic.At(values, signs, 2, words); var term = ContactQuadratic.At(values, signs, 3, words);
 9257        var work = ContactQuadratic.At(values, signs, 4, words); var bound = ContactQuadratic.At(values, signs, 5, words
 9258        var projection = ContactQuadratic.At(values, signs, 6, words); var f = ContactQuadratic.At(values, signs, 7, wor
 9259        var x = ContactQuadratic.At(values, signs, 8, words); var y = ContactQuadratic.At(values, signs, 9, words);
 9260        var z = ContactQuadratic.At(values, signs, 10, words); var cap = ContactQuadratic.At(values, signs, 11, words);
 4661        ContactQuadratic.Scale(px, RayExtend(Signed192.Raw(axis.X)), projection);
 9262        ContactQuadratic.Scale(py, RayExtend(Signed192.Raw(axis.Y)), term); projection.Add(term);
 9263        ContactQuadratic.Scale(pz, RayExtend(Signed192.Raw(axis.Z)), term); projection.Add(term);
 4664        Signed320 radiusSquared = WideArithmetic.MultiplySigned192(Signed192.Raw(radius), Signed192.Raw(radius));
 4665        if (!perpendicular.IsZero)
 66        {
 6867            a.Set(Signed576.ExtendValue(perpendicular)); b.Clear(); c.Set(WideArithmetic.MultiplySigned320(radiusSquared
 1768            _ = ContactQuadratic.TryGetParameter(a, b, c, 1, root, numerator, denominator);
 1769            ContactQuadratic.Scale(pointD, RayExtend(ad), term);
 1770            ContactQuadratic.Scale(term, RayExtend(DoubleParameterScale), f);
 1771            ContactQuadratic.Multiply(numerator, f, root, work);
 1772            ContactQuadratic.Scale(projection, RayExtend(DoubleParameterScale), f);
 3473            ContactQuadratic.Multiply(denominator, f, root, term); work.Add(term);
 1774            ContactQuadratic.Scale(pointD, RayExtend(Signed192.Raw(length)), f);
 1775            ContactQuadratic.Scale(f, RayExtend(aa), cap);
 1776            ContactQuadratic.Multiply(denominator, cap, root, bound);
 5177            bound.CopyTo(term); term.Add(work, -1); bool belowUpper = term.Sign(root) >= 0;
 3478            bound.CopyTo(term); term.Add(work);
 4579            if (belowUpper && term.Sign(root) >= 0) { endpointSign = 0; return; }
 80        }
 3281        endpointSign = ad.Sign;
 82        System.Diagnostics.Debug.Assert(endpointSign != 0);
 3283        ContactQuadratic.Scale(pointD, RayExtend(DoubleParameterScale), f);
 3284        ContactQuadratic.Scale(pointD, RayExtend(Signed192.Raw(endpointSign > 0 ? length : -length)), cap);
 25685        for (int coordinate = 0; coordinate < 3; coordinate++)
 86        {
 9687            ContactQuadratic source = coordinate == 0 ? px : coordinate == 1 ? py : pz;
 9688            ContactQuadratic target = coordinate == 0 ? x : coordinate == 1 ? y : z;
 9689            ContactQuadratic.Scale(source, RayExtend(DoubleParameterScale), target);
 19290            ContactQuadratic.Scale(cap, RayExtend(Signed192.Raw(axis[coordinate])), term); target.Add(term, -1);
 91        }
 3292        ContactQuadratic.Multiply(f, f, ReadOnlySpan<ulong>.Empty, work);
 3293        ContactQuadratic.Scale(work, RayExtend(dd), a);
 3294        ContactQuadratic.Scale(x, RayExtend(Signed192.Raw(direction.X)), projection);
 6495        ContactQuadratic.Scale(y, RayExtend(Signed192.Raw(direction.Y)), term); projection.Add(term);
 6496        ContactQuadratic.Scale(z, RayExtend(Signed192.Raw(direction.Z)), term); projection.Add(term);
 3297        ContactQuadratic.Multiply(f, projection, ReadOnlySpan<ulong>.Empty, b);
 3298        c.Clear();
 25699        for (int coordinate = 0; coordinate < 3; coordinate++)
 100        {
 96101            ContactQuadratic component = coordinate == 0 ? x : coordinate == 1 ? y : z;
 192102            ContactQuadratic.Multiply(component, component, ReadOnlySpan<ulong>.Empty, term); c.Add(term);
 103        }
 64104        ContactQuadratic.Scale(work, RayExtend(radiusSquared), term); c.Add(term, -1);
 32105        _ = ContactQuadratic.TryGetParameter(a, b, c, 1, root, numerator, denominator);
 32106    }
 107    private static void GetCapsuleRayDepth(ContactQuadratic numerator, ContactQuadratic denominator,
 108        ReadOnlySpan<ulong> root, Vector3d direction, out Fixed64 depth, out bool clamped)
 109    {
 46110        Span<ulong> metric = stackalloc ulong[3];
 46111        WideArithmetic.GetMagnitude(GetDot(direction, Vector3d.Zero, direction, Vector3d.Zero),
 46112            out metric[2], out metric[1], out metric[0]);
 46113        Span<ulong> one = stackalloc ulong[1] { 1UL };
 46114        clamped = !ContactQuadratic.TryRoundRootRatio(numerator, denominator, root, metric, one, out depth);
 53115        if (clamped) depth = Fixed64.MaxValue;
 46116    }
 117
 118    private static bool TryMaterializeCapsuleRayPoint(in AxisTriangleCandidate point, Vector3d direction,
 119        ContactQuadratic numerator, ContactQuadratic denominator, ReadOnlySpan<ulong> root,
 120        out Vector3d boundary)
 121    {
 9122        Span<ulong> values = stackalloc ulong[12 * CapsuleTriangleRayWords];
 9123        Span<int> signs = stackalloc int[12];
 18124        ContactQuadratic x = RayField(values, signs, 0), y = RayField(values, signs, 1);
 18125        ContactQuadratic z = RayField(values, signs, 2), d = RayField(values, signs, 3);
 9126        ContactQuadratic term = RayField(values, signs, 4);
 9127        Span<Signed576> coordinates = stackalloc Signed576[3] { point.AxisX, point.AxisY, point.AxisZ };
 72128        for (int coordinate = 0; coordinate < 3; coordinate++)
 129        {
 27130            ContactQuadratic target = coordinate == 0 ? x : coordinate == 1 ? y : z;
 27131            ContactQuadratic.Scale(denominator, coordinates[coordinate], target);
 27132            ContactQuadratic.Scale(numerator, WideArithmetic.MultiplySigned576(point.Denominator,
 54133                Signed192.Raw(direction[coordinate])), term); target.Add(term);
 134        }
 9135        ContactQuadratic.Scale(denominator, point.Denominator, d);
 9136        return QuadraticPointMaterialization.TryGetWorldPoint(Vector3d.Zero, FixedQuaternion.Identity,
 9137            x, y, z, d, root, out boundary);
 138    }
 139
 140    private static void MaterializeCapsuleRayComponents(in AxisTriangleCandidate point, Vector3d center,
 141        Vector3d axis, Fixed64 length, Vector3d direction, int endpointSign,
 142        ContactQuadratic numerator, ContactQuadratic denominator, ReadOnlySpan<ulong> root,
 143        FixedQuaternion sourceRotation, FixedQuaternion targetRotation,
 144        out Fixed64 axisParameter, out Vector3d radialOffset)
 145    {
 146        // Match the exit to its closest finite core point. Both components are
 147        // bounded separately even when their sum is outside the scalar domain.
 28148        Signed192 aa = GetDot(axis, Vector3d.Zero, axis, Vector3d.Zero);
 28149        Signed192 ad = GetDot(axis, Vector3d.Zero, direction, Vector3d.Zero);
 28150        Signed576 rx = SubtractVertex(point.AxisX, center.X, point.Denominator);
 28151        Signed576 ry = SubtractVertex(point.AxisY, center.Y, point.Denominator);
 28152        Signed576 rz = SubtractVertex(point.AxisZ, center.Z, point.Denominator);
 28153        Span<ulong> values = stackalloc ulong[12 * CapsuleTriangleRayWords];
 28154        Span<int> signs = stackalloc int[12];
 56155        ContactQuadratic x = RayField(values, signs, 0), y = RayField(values, signs, 1);
 56156        ContactQuadratic z = RayField(values, signs, 2), d = RayField(values, signs, 3);
 56157        ContactQuadratic term = RayField(values, signs, 4), parameter = RayField(values, signs, 5);
 28158        if (endpointSign == 0)
 159        {
 12160            Signed576 projection = GetRetainedRayDot(rx, ry, rz, axis);
 12161            ContactQuadratic.Scale(denominator, projection, parameter);
 12162            ContactQuadratic.Scale(numerator, WideArithmetic.MultiplySigned576(point.Denominator, ad), term);
 12163            parameter.Add(term);
 12164            ContactQuadratic.Scale(parameter, RayExtend(ParameterScale), x);
 12165            ContactQuadratic.Scale(denominator, WideArithmetic.MultiplySigned576(point.Denominator, aa), d);
 12166            axisParameter = ContactQuadratic.RoundRatio(x, d, root);
 96167            for (int coordinate = 0; coordinate < 3; coordinate++)
 168            {
 36169                Signed320 radial = WideArithmetic.MultiplySubtract(
 36170                    Signed192.Raw(direction[coordinate]), aa, Signed192.Raw(axis[coordinate]), ad);
 36171                ContactQuadratic.Scale(numerator, Signed576.ExtendValue(radial),
 36172                    coordinate == 0 ? x : coordinate == 1 ? y : z);
 173            }
 12174            ContactQuadratic.Scale(denominator, RayExtend(aa), d);
 175        }
 176        else
 177        {
 16178            axisParameter = (endpointSign > 0 ? length : -length) / Fixed64.Two;
 16179            Signed576 f = WideArithmetic.MultiplySigned576(point.Denominator, DoubleParameterScale);
 16180            Signed576 cap = WideArithmetic.MultiplySigned576(point.Denominator,
 16181                Signed192.Raw(endpointSign > 0 ? length : -length));
 16182            Span<Signed576> relative = stackalloc Signed576[3] { rx, ry, rz };
 128183            for (int coordinate = 0; coordinate < 3; coordinate++)
 184            {
 48185                Signed576 w = WideArithmetic.SubtractSigned576(
 48186                    WideArithmetic.MultiplySigned576(relative[coordinate], DoubleParameterScale),
 48187                    WideArithmetic.MultiplySigned576(cap, Signed192.Raw(axis[coordinate])));
 48188                ContactQuadratic target = coordinate == 0 ? x : coordinate == 1 ? y : z;
 48189                ContactQuadratic.Scale(denominator, w, target);
 48190                ContactQuadratic.Scale(numerator, WideArithmetic.MultiplySigned576(f,
 96191                    Signed192.Raw(direction[coordinate])), term); target.Add(term);
 192            }
 16193            ContactQuadratic.Scale(denominator, f, d);
 194        }
 195        // Keep the exact radial coordinates through the target-frame transform.
 196        // Rounding in the chart first can turn a Max-radius component into Max+1.
 28197        bool represented = QuadraticPointMaterialization.TryGetOffsetInFrame(sourceRotation,
 28198            targetRotation, x, y, z, d, root, out radialOffset);
 199        System.Diagnostics.Debug.Assert(represented);
 28200    }
 201}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredConeSurfaceAnchors.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.CenteredConeSurfaceAnchors.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
 8namespace FixedMathSharp.Geometry;
 9
 10/// <content>
 11/// Provides helpers for computing the closest surface anchor, outward normal,
 12/// and signed distance from a point to a centered finite cone's lateral surface.
 13/// </content>
 14internal static partial class WideFiniteAxisIntersection
 15{
 16    internal static bool TryGetClosestCenteredFiniteConeSurfaceAnchor(
 17        Vector3d point,
 18        Vector3d center,
 19        FixedQuaternion frameRotation,
 20        Vector3d localAxisDirection,
 21        Fixed64 height,
 22        Fixed64 radius,
 23        Vector3d localFallbackRadialDirection,
 24        out FixedPointAnchor surfaceAnchor,
 25        out Vector3d outwardNormal,
 26        out Fixed64 signedDistance)
 27    {
 2728        surfaceAnchor =
 2729            GetClosestCenteredFiniteConeSurfaceAnchor(
 2730                point,
 2731                center,
 2732                frameRotation,
 2733                localAxisDirection,
 2734                height,
 2735                radius,
 2736                localFallbackRadialDirection,
 2737                out outwardNormal,
 2738                out bool contained);
 2739        if (TryGetSignedSurfaceDistance(
 2740                point,
 2741                surfaceAnchor,
 2742                contained,
 2743                out signedDistance))
 44        {
 2045            return true;
 46        }
 47
 748        surfaceAnchor = default;
 749        outwardNormal = default;
 750        signedDistance = default;
 751        return false;
 52    }
 53
 54    internal static FixedPointAnchor
 55        GetClosestCenteredFiniteConeSurfaceAnchor(
 56            Vector3d point,
 57            Vector3d center,
 58            FixedQuaternion frameRotation,
 59            Vector3d localAxisDirection,
 60            Fixed64 height,
 61            Fixed64 radius,
 62            Vector3d localFallbackRadialDirection,
 63            out Vector3d outwardNormal,
 64            out bool contained)
 65    {
 2866        GetRigidLocalPointRelation(
 2867            point,
 2868            center,
 2869            frameRotation,
 2870            localAxisDirection,
 2871            out RigidLocalPointRelation relation);
 2872        Vector3d localRadialDirection = GetLocalRadialDirection(
 2873            relation,
 2874            localAxisDirection,
 2875            localFallbackRadialDirection);
 2876        GetRigidCenteredConeMeridian(
 2877            relation,
 2878            height,
 2879            radius,
 2880            out Fixed64 surfaceRadius,
 2881            out Fixed64 signedAxisLength,
 2882            out bool side,
 2883            out contained);
 84
 2885        FixedPointAnchor surfaceAnchor = CreateCenteredSurfaceAnchor(
 2886            center,
 2887            frameRotation,
 2888            localAxisDirection,
 2889            signedAxisLength,
 2890            localRadialDirection,
 2891            surfaceRadius);
 2892        Vector3d localNormal = side
 2893            ? GetCenteredConeSideNormal(
 2894                localRadialDirection,
 2895                localAxisDirection,
 2896                height,
 2897                radius)
 2898            : -localAxisDirection;
 2899        _ = frameRotation.TryRotate(
 28100            localNormal,
 28101            out Vector3d rotatedNormal);
 28102        outwardNormal = WideNormalization.GetNormalized(rotatedNormal);
 28103        return surfaceAnchor;
 104    }
 105
 106    private static void GetRigidCenteredConeMeridian(
 107        RigidLocalPointRelation relation,
 108        Fixed64 height,
 109        Fixed64 radius,
 110        out Fixed64 surfaceRadius,
 111        out Fixed64 signedAxisLength,
 112        out bool side,
 113        out bool contained)
 114    {
 28115        GetCenteredConeHeightRelation(
 28116            relation,
 28117            height,
 28118            out Signed576 heightFromBaseNumerator,
 28119            out Signed320 heightFromBaseDenominator);
 28120        Signed192 radiusRaw = Signed192.Signed(
 28121            radius.m_rawValue);
 28122        int radialToRadius = WideArithmetic
 28123            .CompareNonNegativeRadicalToRatio(
 28124                relation.RadialNumerator,
 28125                relation.RadialDenominator,
 28126                Signed320.ExtendValue(radiusRaw),
 28127                Scale320);
 28128        side = IsCenteredConeSideNearest(
 28129            relation,
 28130            height,
 28131            radius,
 28132            heightFromBaseNumerator,
 28133            heightFromBaseDenominator);
 28134        if (side)
 135        {
 22136            GetClosestCenteredConeSideCoordinates(
 22137                relation,
 22138                height,
 22139                radius,
 22140                heightFromBaseNumerator,
 22141                heightFromBaseDenominator,
 22142                out surfaceRadius,
 22143                out signedAxisLength);
 144        }
 145        else
 146        {
 147            // A base selection proves the radial projection lies inside the
 148            // disk; otherwise the shared rim is also the nearest side feature.
 6149            _ = TryGetCenteredRadialDistance(
 6150                relation,
 6151                out surfaceRadius);
 6152            signedAxisLength = -height;
 153        }
 28154        contained = IsPointContainedInRigidCenteredCone(
 28155            relation,
 28156            height,
 28157            radius);
 28158    }
 159
 160    private static void GetClosestCenteredConeSideCoordinates(
 161        RigidLocalPointRelation relation,
 162        Fixed64 height,
 163        Fixed64 radius,
 164        Signed576 heightFromBaseNumerator,
 165        Signed320 heightFromBaseDenominator,
 166        out Fixed64 surfaceRadius,
 167        out Fixed64 signedAxisLength)
 168    {
 22169        Signed192 heightRaw = Signed192.Signed(
 22170            height.m_rawValue);
 22171        Signed192 radiusRaw = Signed192.Signed(
 22172            radius.m_rawValue);
 22173        Signed192 sideSquared = WideArithmetic.AddSigned192(
 22174            GetSquaredRadius(heightRaw),
 22175            GetSquaredRadius(radiusRaw));
 22176        Signed576 parameterNumerator =
 22177            GetCenteredConeSideParameterNumerator(
 22178                heightFromBaseNumerator,
 22179                heightFromBaseDenominator,
 22180                heightRaw,
 22181                radiusRaw,
 22182                GetSquaredRadius(radiusRaw));
 22183        int startComparison = CompareCenteredConeSideParameterToEndpoint(
 22184            relation,
 22185            parameterNumerator,
 22186            heightFromBaseDenominator,
 22187            radiusRaw,
 22188            sideSquared,
 22189            endpoint: 0);
 22190        if (startComparison <= 0)
 191        {
 5192            surfaceRadius = radius;
 5193            signedAxisLength = -height;
 5194            return;
 195        }
 196
 17197        int endComparison = CompareCenteredConeSideParameterToEndpoint(
 17198            relation,
 17199            parameterNumerator,
 17200            heightFromBaseDenominator,
 17201            radiusRaw,
 17202            sideSquared,
 17203            endpoint: 1);
 17204        if (endComparison >= 0)
 205        {
 7206            surfaceRadius = Fixed64.Zero;
 7207            signedAxisLength = height;
 7208            return;
 209        }
 210
 10211        Signed576 denominator = WideArithmetic.MultiplySigned320(
 10212            heightFromBaseDenominator,
 10213            Signed320.ExtendValue(sideSquared));
 10214        Signed576 radicalCoefficient = WideArithmetic.MultiplySigned320(
 10215            heightFromBaseDenominator,
 10216            Signed320.ExtendValue(
 10217                WideArithmetic.SubtractSigned192(default, radiusRaw)));
 10218        GetCenteredConeSideApproximation(
 10219            relation,
 10220            height,
 10221            radius,
 10222            heightFromBaseNumerator,
 10223            heightFromBaseDenominator,
 10224            out Fixed64 approximateRadius,
 10225            out Fixed64 approximateAxisLength);
 226
 10227        surfaceRadius = RoundBoundedCenteredConeCoordinate(
 10228            WideArithmetic.MultiplySigned576(
 10229                WideArithmetic.SubtractSigned576(
 10230                    denominator,
 10231                    parameterNumerator),
 10232                radiusRaw),
 10233            WideArithmetic.MultiplySigned576(
 10234                radicalCoefficient,
 10235                WideArithmetic.SubtractSigned192(default, radiusRaw)),
 10236            relation,
 10237            denominator,
 10238            Fixed64.Zero,
 10239            radius,
 10240            approximateRadius);
 241
 10242        Signed576 heightAndParameter = WideArithmetic.MultiplySigned576(
 10243            parameterNumerator,
 10244            heightRaw);
 10245        Signed576 heightAndDenominator = WideArithmetic.MultiplySigned576(
 10246            denominator,
 10247            heightRaw);
 10248        Signed576 heightAndRadical = WideArithmetic.MultiplySigned576(
 10249            radicalCoefficient,
 10250            heightRaw);
 10251        signedAxisLength = RoundBoundedCenteredConeCoordinate(
 10252            WideArithmetic.SubtractSigned576(
 10253                WideArithmetic.AddSigned576(
 10254                    heightAndParameter,
 10255                    heightAndParameter),
 10256                heightAndDenominator),
 10257            WideArithmetic.AddSigned576(
 10258                heightAndRadical,
 10259                heightAndRadical),
 10260            relation,
 10261            denominator,
 10262            -height,
 10263            height,
 10264            approximateAxisLength);
 10265    }
 266
 267    private static void GetCenteredConeSideApproximation(
 268        RigidLocalPointRelation relation,
 269        Fixed64 height,
 270        Fixed64 radius,
 271        Signed576 heightFromBaseNumerator,
 272        Signed320 heightFromBaseDenominator,
 273        out Fixed64 surfaceRadius,
 274        out Fixed64 signedAxisLength)
 275    {
 10276        bool representable = TryGetCenteredRadialDistance(
 10277            relation,
 10278            out Fixed64 radialDistance);
 10279        representable &= Fixed64.TryGetSignedRawRatio(
 10280            heightFromBaseNumerator,
 10281            Signed576.ExtendValue(
 10282                heightFromBaseDenominator),
 10283            out Fixed64 heightFromBase);
 10284        if (!representable)
 285        {
 2286            surfaceRadius = radius / Fixed64.Two;
 2287            signedAxisLength = Fixed64.Zero;
 2288            return;
 289        }
 290
 8291        Vector2d start = new(radius, Fixed64.Zero);
 8292        Vector2d end = new(Fixed64.Zero, height);
 8293        Signed192 denominator = WideGeometry.GetDifferenceDotProduct2D(
 8294            end.X, start.X, end.Y, start.Y,
 8295            end.X, start.X, end.Y, start.Y);
 8296        Signed192 numerator = WideGeometry.GetDifferenceDotProduct2D(
 8297            radialDistance, start.X, heightFromBase, start.Y,
 8298            end.X, start.X, end.Y, start.Y);
 8299        Signed192 remaining = WideArithmetic.SubtractSigned192(
 8300            denominator,
 8301            numerator);
 8302        _ = Fixed64.TryGetSignedRawRatio(
 8303            Signed576.ExtendValue(
 8304                WideArithmetic.MultiplySigned192(
 8305                    Signed192.Signed(radius.m_rawValue),
 8306                    remaining)),
 8307            Signed576.ExtendValue(
 8308                Signed320.ExtendValue(denominator)),
 8309            out surfaceRadius);
 8310        _ = Fixed64.TryGetSignedRawRatio(
 8311            Signed576.ExtendValue(
 8312                WideArithmetic.MultiplySigned192(
 8313                    Signed192.Signed(height.m_rawValue),
 8314                    numerator)),
 8315            Signed576.ExtendValue(
 8316                Signed320.ExtendValue(denominator)),
 8317            out Fixed64 sideHeight);
 8318        _ = Fixed64.TrySubtractProducts(
 8319            Fixed64.Two,
 8320            sideHeight,
 8321            height,
 8322            Fixed64.One,
 8323            out signedAxisLength);
 8324    }
 325
 326    private static Fixed64 RoundBoundedCenteredConeCoordinate(
 327        Signed576 rational,
 328        Signed576 radicalCoefficient,
 329        RigidLocalPointRelation relation,
 330        Signed576 denominator,
 331        Fixed64 minimum,
 332        Fixed64 maximum,
 333        Fixed64 approximation)
 334    {
 20335        long low = minimum.m_rawValue;
 20336        long high = maximum.m_rawValue;
 20337        long candidate = approximation.Clamp(minimum, maximum).m_rawValue;
 20338        int candidateComparison = CompareCenteredConeCoordinateToRaw(
 20339            rational,
 20340            radicalCoefficient,
 20341            relation,
 20342            denominator,
 20343            candidate);
 20344        if (candidateComparison >= 0)
 345        {
 13346            ulong distanceToHigh = unchecked((ulong)high - (ulong)candidate);
 13347            long nextCandidate = unchecked(
 13348                candidate + (long)((distanceToHigh | (0UL - distanceToHigh)) >> 63));
 13349            if (CompareCenteredConeCoordinateToRaw(
 13350                    rational,
 13351                    radicalCoefficient,
 13352                    relation,
 13353                    denominator,
 13354                    nextCandidate) < 0)
 355            {
 10356                low = candidate;
 357            }
 358            else
 359            {
 3360                low = nextCandidate;
 361            }
 362        }
 363        else
 364        {
 7365            high = candidate - 1L;
 7366            if (CompareCenteredConeCoordinateToRaw(
 7367                    rational,
 7368                    radicalCoefficient,
 7369                    relation,
 7370                    denominator,
 7371                    high) >= 0)
 372            {
 5373                low = high;
 374            }
 375        }
 376
 502377        while (low < high)
 378        {
 482379            ulong span = unchecked((ulong)high - (ulong)low);
 482380            long midpoint = unchecked(
 482381                (long)((ulong)low + ((span + 1UL) >> 1)));
 482382            if (CompareCenteredConeCoordinateToRaw(
 482383                    rational,
 482384                    radicalCoefficient,
 482385                    relation,
 482386                    denominator,
 482387                    midpoint) >= 0)
 388            {
 62389                low = midpoint;
 390            }
 391            else
 392            {
 420393                high = midpoint - 1L;
 394            }
 395        }
 396
 20397        Signed192 doubledMidpoint = WideArithmetic.AddSigned192(
 20398            WideArithmetic.AddSigned192(
 20399                Signed192.Signed(low),
 20400                Signed192.Signed(low)),
 20401            Scale);
 20402        Signed576 midpointRational = WideArithmetic.SubtractSigned576(
 20403            WideArithmetic.AddSigned576(rational, rational),
 20404            WideArithmetic.MultiplySigned576(
 20405                denominator,
 20406                doubledMidpoint));
 20407        int midpointComparison =
 20408            WideArithmetic.CompareSignedLinearRadicalToZero(
 20409                Signed832.ExtendValue(midpointRational),
 20410                Signed704.ExtendValue(
 20411                    WideArithmetic.AddSigned576(
 20412                        radicalCoefficient,
 20413                        radicalCoefficient)),
 20414                relation.RadialNumerator,
 20415                relation.RadialDenominator);
 20416        return Fixed64.FromRaw(
 20417            low + GetHalfToEvenIncrement(midpointComparison, low));
 418    }
 419
 420    private static int CompareCenteredConeCoordinateToRaw(
 421        Signed576 rational,
 422        Signed576 radicalCoefficient,
 423        RigidLocalPointRelation relation,
 424        Signed576 denominator,
 425        long raw)
 426    {
 522427        Signed832 candidate = WideArithmetic.MultiplySigned576ToSigned832(
 522428            denominator,
 522429            Signed320.ExtendValue(
 522430                Signed192.Signed(raw)));
 522431        return WideArithmetic.CompareSignedLinearRadicalToZero(
 522432            WideArithmetic.SubtractSigned832(
 522433                Signed832.ExtendValue(rational),
 522434                candidate),
 522435            Signed704.ExtendValue(radicalCoefficient),
 522436            relation.RadialNumerator,
 522437            relation.RadialDenominator);
 438    }
 439
 440    private static void GetCenteredConeHeightRelation(
 441        RigidLocalPointRelation relation,
 442        Fixed64 height,
 443        out Signed576 numerator,
 444        out Signed320 denominator)
 445    {
 28446        Signed320 denominatorAndAxis = WideArithmetic.MultiplySigned192(
 28447            relation.Denominator,
 28448            relation.AxisSquared);
 28449        Signed576 heightTerm = WideArithmetic.MultiplySigned320(
 28450            denominatorAndAxis,
 28451            Signed320.ExtendValue(
 28452                Signed192.Signed(
 28453                    height.m_rawValue)));
 28454        Signed576 projectionTerm = WideArithmetic.MultiplySigned320(
 28455            relation.Projection,
 28456            Signed320.ExtendValue(
 28457                Signed192.Signed(
 28458                    Fixed64.One.m_rawValue)));
 28459        numerator = WideArithmetic.AddSigned576(
 28460            heightTerm,
 28461            WideArithmetic.AddSigned576(
 28462                projectionTerm,
 28463                projectionTerm));
 28464        denominator = WideArithmetic.AddSigned320(
 28465            denominatorAndAxis,
 28466            denominatorAndAxis);
 28467    }
 468
 469    private static bool IsPointContainedInRigidCenteredCone(
 470        RigidLocalPointRelation relation,
 471        Fixed64 height,
 472        Fixed64 radius)
 473    {
 28474        if (GetCenteredCap(relation, height) != 0)
 13475            return false;
 476
 15477        Signed320 denominatorAndAxis = WideArithmetic.MultiplySigned192(
 15478            relation.Denominator,
 15479            relation.AxisSquared);
 15480        Signed576 remaining = WideArithmetic.SubtractSigned576(
 15481            WideArithmetic.MultiplySigned320(
 15482                denominatorAndAxis,
 15483                Signed320.ExtendValue(
 15484                    Signed192.Signed(
 15485                        height.m_rawValue))),
 15486            WideArithmetic.AddSigned576(
 15487                WideArithmetic.MultiplySigned320(
 15488                    relation.Projection,
 15489                    Signed320.ExtendValue(
 15490                        Signed192.Signed(
 15491                            Fixed64.One.m_rawValue))),
 15492                WideArithmetic.MultiplySigned320(
 15493                    relation.Projection,
 15494                    Signed320.ExtendValue(
 15495                        Signed192.Signed(
 15496                            Fixed64.One.m_rawValue)))));
 15497        _ = Signed320.TryNarrowSigned(
 15498            remaining,
 15499            out Signed320 narrowRemaining);
 15500        Signed192 heightSquared = GetSquaredRadius(
 15501            Signed192.Signed(
 15502                height.m_rawValue));
 15503        Signed320 axisAndHeightSquared =
 15504            WideArithmetic.MultiplySigned192(
 15505                relation.AxisSquared,
 15506                heightSquared);
 15507        Signed704 left = WideArithmetic.MultiplySigned576ToSigned704(
 15508            relation.RadialNumerator,
 15509            axisAndHeightSquared);
 15510        left = WideArithmetic.AddSigned704(left, left);
 15511        left = WideArithmetic.AddSigned704(left, left);
 512
 15513        Signed576 remainingSquared = WideArithmetic.MultiplySigned320(
 15514            narrowRemaining,
 15515            narrowRemaining);
 15516        Signed192 radiusSquared = GetSquaredRadius(
 15517            Signed192.Signed(
 15518                radius.m_rawValue));
 15519        Signed704 right = Signed704.ExtendValue(
 15520            WideArithmetic.MultiplySigned576(
 15521                remainingSquared,
 15522                radiusSquared));
 15523        return WideArithmetic.CompareNonNegative(left, right) <= 0;
 524    }
 525
 526    private static bool IsCenteredConeSideNearest(
 527        RigidLocalPointRelation relation,
 528        Fixed64 height,
 529        Fixed64 radius,
 530        Signed576 heightFromBaseNumerator,
 531        Signed320 heightFromBaseDenominator)
 532    {
 28533        Signed192 heightRaw = Signed192.Signed(
 28534            height.m_rawValue);
 28535        Signed192 radiusRaw = Signed192.Signed(
 28536            radius.m_rawValue);
 28537        Signed192 heightSquared = GetSquaredRadius(heightRaw);
 28538        Signed192 radiusSquared = GetSquaredRadius(radiusRaw);
 28539        Signed192 sideSquared = WideArithmetic.AddSigned192(
 28540            heightSquared,
 28541            radiusSquared);
 28542        Signed576 sideParameterNumerator =
 28543            GetCenteredConeSideParameterNumerator(
 28544                heightFromBaseNumerator,
 28545                heightFromBaseDenominator,
 28546                heightRaw,
 28547                radiusRaw,
 28548                radiusSquared);
 28549        int startComparison = CompareCenteredConeSideParameterToEndpoint(
 28550            relation,
 28551            sideParameterNumerator,
 28552            heightFromBaseDenominator,
 28553            radiusRaw,
 28554            sideSquared,
 28555            endpoint: 0);
 28556        int radialToRadius = WideArithmetic
 28557            .CompareNonNegativeRadicalToRatio(
 28558                relation.RadialNumerator,
 28559                relation.RadialDenominator,
 28560                Signed320.ExtendValue(radiusRaw),
 28561                Scale320);
 28562        if (startComparison <= 0)
 9563            return radialToRadius >= 0;
 564
 19565        int endComparison = CompareCenteredConeSideParameterToEndpoint(
 19566            relation,
 19567            sideParameterNumerator,
 19568            heightFromBaseDenominator,
 19569            radiusRaw,
 19570            sideSquared,
 19571            endpoint: 1);
 19572        bool baseInterior = radialToRadius <= 0;
 19573        if (endComparison >= 0)
 574        {
 7575            return CompareCenteredConeApexToBaseDistance(
 7576                relation,
 7577                heightFromBaseNumerator,
 7578                heightFromBaseDenominator,
 7579                heightRaw,
 7580                radiusRaw,
 7581                heightSquared,
 7582                radiusSquared,
 7583                baseInterior) <= 0;
 584        }
 585
 12586        return CompareCenteredConeInteriorSideToBaseDistance(
 12587            relation,
 12588            heightFromBaseNumerator,
 12589            heightFromBaseDenominator,
 12590            heightRaw,
 12591            radiusRaw,
 12592            heightSquared,
 12593            radiusSquared,
 12594            sideSquared,
 12595            baseInterior) <= 0;
 596    }
 597
 598    private static Signed576 GetCenteredConeSideParameterNumerator(
 599        Signed576 heightFromBaseNumerator,
 600        Signed320 heightFromBaseDenominator,
 601        Signed192 height,
 602        Signed192 radius,
 603        Signed192 radiusSquared) =>
 50604        WideArithmetic.AddSigned576(
 50605            WideArithmetic.MultiplySigned320(
 50606                heightFromBaseDenominator,
 50607                Signed320.ExtendValue(radiusSquared)),
 50608            WideArithmetic.MultiplySigned576(
 50609                heightFromBaseNumerator,
 50610                height));
 611
 612    private static int CompareCenteredConeSideParameterToEndpoint(
 613        RigidLocalPointRelation relation,
 614        Signed576 sideParameterNumerator,
 615        Signed320 heightFromBaseDenominator,
 616        Signed192 radius,
 617        Signed192 sideSquared,
 618        int endpoint)
 619    {
 86620        Signed576 rational = sideParameterNumerator;
 86621        if (endpoint != 0)
 622        {
 36623            rational = WideArithmetic.SubtractSigned576(
 36624                rational,
 36625                WideArithmetic.MultiplySigned320(
 36626                    heightFromBaseDenominator,
 36627                    Signed320.ExtendValue(
 36628                        sideSquared)));
 629        }
 86630        Signed576 radialCoefficient = WideArithmetic.MultiplySigned320(
 86631            heightFromBaseDenominator,
 86632            Signed320.ExtendValue(
 86633                WideArithmetic.SubtractSigned192(
 86634                    default,
 86635                    radius)));
 86636        return WideArithmetic.CompareSignedLinearRadicalToZero(
 86637            Signed832.ExtendValue(rational),
 86638            Signed704.ExtendValue(radialCoefficient),
 86639            relation.RadialNumerator,
 86640            relation.RadialDenominator);
 641    }
 642
 643    private static int CompareCenteredConeApexToBaseDistance(
 644        RigidLocalPointRelation relation,
 645        Signed576 heightFromBaseNumerator,
 646        Signed320 heightFromBaseDenominator,
 647        Signed192 height,
 648        Signed192 radius,
 649        Signed192 heightSquared,
 650        Signed192 radiusSquared,
 651        bool baseInterior)
 652    {
 7653        Signed576 twiceHeightAndY = WideArithmetic.AddSigned576(
 7654            WideArithmetic.MultiplySigned576(
 7655                heightFromBaseNumerator,
 7656                height),
 7657            WideArithmetic.MultiplySigned576(
 7658                heightFromBaseNumerator,
 7659                height));
 7660        Signed576 rational = WideArithmetic.SubtractSigned576(
 7661            WideArithmetic.MultiplySigned320(
 7662                heightFromBaseDenominator,
 7663                Signed320.ExtendValue(
 7664                    baseInterior
 7665                        ? heightSquared
 7666                        : WideArithmetic.SubtractSigned192(
 7667                            heightSquared,
 7668                            radiusSquared))),
 7669            twiceHeightAndY);
 7670        if (baseInterior)
 671        {
 3672            Signed704 result = WideArithmetic.AddSigned704(
 3673                WideArithmetic.MultiplySigned576ToSigned704(
 3674                    relation.RadialNumerator,
 3675                    heightFromBaseDenominator),
 3676                WideArithmetic.MultiplySigned576ToSigned704(
 3677                    rational,
 3678                    relation.RadialDenominator));
 3679            return result.Sign;
 680        }
 681
 4682        Signed576 radialCoefficient = WideArithmetic.MultiplySigned320(
 4683            heightFromBaseDenominator,
 4684            Signed320.ExtendValue(
 4685                WideArithmetic.AddSigned192(
 4686                    radius,
 4687                    radius)));
 4688        return WideArithmetic.CompareSignedLinearRadicalToZero(
 4689            Signed832.ExtendValue(rational),
 4690            Signed704.ExtendValue(radialCoefficient),
 4691            relation.RadialNumerator,
 4692            relation.RadialDenominator);
 693    }
 694
 695    private static int CompareCenteredConeInteriorSideToBaseDistance(
 696        RigidLocalPointRelation relation,
 697        Signed576 heightFromBaseNumerator,
 698        Signed320 heightFromBaseDenominator,
 699        Signed192 height,
 700        Signed192 radius,
 701        Signed192 heightSquared,
 702        Signed192 radiusSquared,
 703        Signed192 sideSquared,
 704        bool baseInterior)
 705    {
 12706        Signed576 denominatorSquared = WideArithmetic.MultiplySigned320(
 12707            heightFromBaseDenominator,
 12708            heightFromBaseDenominator);
 12709        _ = Signed320.TryNarrowSigned(
 12710            denominatorSquared,
 12711            out Signed320 narrowDenominatorSquared);
 12712        Signed576 radialAndHeightSquared =
 12713            WideArithmetic.MultiplySigned576(
 12714                relation.RadialNumerator,
 12715                heightSquared);
 12716        Signed832 rational = WideArithmetic.MultiplySigned576ToSigned832(
 12717            radialAndHeightSquared,
 12718            denominatorSquared);
 719
 12720        Signed576 heightRadius = Signed576.ExtendValue(
 12721            WideArithmetic.MultiplySigned192(
 12722                height,
 12723                radius));
 724        // The normalized rigid-frame and local-axis denominators are both
 725        // Q32.32 squared magnitudes, so their product remains Signed192-wide.
 12726        _ = Signed192.TryNarrowSigned(
 12727            Signed576.ExtendValue(
 12728                heightFromBaseDenominator),
 12729            out Signed192 narrowHeightDenominator);
 12730        Signed576 meridianOffset = WideArithmetic.SubtractSigned576(
 12731            WideArithmetic.MultiplySigned576(
 12732                heightFromBaseNumerator,
 12733                radius),
 12734            WideArithmetic.MultiplySigned576(
 12735                heightRadius,
 12736                narrowHeightDenominator));
 12737        _ = Signed320.TryNarrowSigned(
 12738            meridianOffset,
 12739            out Signed320 narrowMeridianOffset);
 12740        Signed576 meridianOffsetSquared =
 12741            WideArithmetic.MultiplySigned320(
 12742                narrowMeridianOffset,
 12743                narrowMeridianOffset);
 12744        rational = WideArithmetic.AddSigned832(
 12745            rational,
 12746            Signed832.ExtendValue(
 12747                WideArithmetic.MultiplySigned576ToSigned704(
 12748                    meridianOffsetSquared,
 12749                    relation.RadialDenominator)));
 750
 12751        Signed576 heightNumeratorSquared =
 12752            WideArithmetic.MultiplySigned320(
 12753                Signed320.NarrowValue(heightFromBaseNumerator),
 12754                Signed320.NarrowValue(heightFromBaseNumerator));
 12755        Signed576 denominatorAndSide = WideArithmetic.MultiplySigned320(
 12756            relation.RadialDenominator,
 12757            Signed320.ExtendValue(sideSquared));
 12758        rational = WideArithmetic.SubtractSigned832(
 12759            rational,
 12760            WideArithmetic.MultiplySigned576ToSigned832(
 12761                heightNumeratorSquared,
 12762                denominatorAndSide));
 763
 12764        if (!baseInterior)
 765        {
 4766            Signed576 radialAndSideSquared =
 4767                WideArithmetic.MultiplySigned576(
 4768                    relation.RadialNumerator,
 4769                    sideSquared);
 4770            rational = WideArithmetic.SubtractSigned832(
 4771                rational,
 4772                WideArithmetic.MultiplySigned576ToSigned832(
 4773                    radialAndSideSquared,
 4774                    denominatorSquared));
 4775            Signed320 radiusAndSideSquared =
 4776                WideArithmetic.MultiplySigned192(
 4777                    radiusSquared,
 4778                    sideSquared);
 4779            rational = WideArithmetic.SubtractSigned832(
 4780                rational,
 4781                Signed832.ExtendValue(
 4782                    WideArithmetic.MultiplySigned320(
 4783                        radiusAndSideSquared,
 4784                        narrowDenominatorSquared,
 4785                        relation.RadialDenominator)));
 786        }
 787
 12788        Signed576 heightAndDenominator =
 12789            WideArithmetic.MultiplySigned320(
 12790                heightFromBaseDenominator,
 12791                Signed320.ExtendValue(height));
 12792        _ = Signed320.TryNarrowSigned(
 12793            heightAndDenominator,
 12794            out Signed320 narrowHeightAndDenominator);
 12795        Signed704 radicalCoefficient =
 12796            WideArithmetic.MultiplySigned320(
 12797                narrowHeightAndDenominator,
 12798                narrowMeridianOffset,
 12799                relation.RadialDenominator);
 12800        radicalCoefficient = WideArithmetic.AddSigned704(
 12801            radicalCoefficient,
 12802            radicalCoefficient);
 12803        if (!baseInterior)
 804        {
 4805            Signed320 radiusAndSide = WideArithmetic.MultiplySigned192(
 4806                radius,
 4807                sideSquared);
 4808            Signed704 baseCoefficient = WideArithmetic.MultiplySigned320(
 4809                radiusAndSide,
 4810                narrowDenominatorSquared,
 4811                relation.RadialDenominator);
 4812            baseCoefficient = WideArithmetic.AddSigned704(
 4813                baseCoefficient,
 4814                baseCoefficient);
 4815            radicalCoefficient = WideArithmetic.AddSigned704(
 4816                radicalCoefficient,
 4817                baseCoefficient);
 818        }
 819
 12820        return WideArithmetic.CompareSignedLinearRadicalToZero(
 12821            rational,
 12822            radicalCoefficient,
 12823            relation.RadialNumerator,
 12824            relation.RadialDenominator);
 825    }
 826
 827}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredCylinderPairsStrictOverlap.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.CenteredCylinderPairsStrictOverlap.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>
 13/// Exact strict centered finite-cylinder pair classification, without contact
 14/// materialization or a finite sampled separating-axis acceptance rule.
 15/// </content>
 16internal static partial class WideFiniteAxisIntersection
 17{
 18    /// <summary>
 19    /// Tests positive penetration of two positive-radius, positive-height
 20    /// local-+Y cylinders in authoritative normalized rigid frames.
 21    /// </summary>
 22    internal static bool DoesCenteredFiniteCylindersPenetrate(
 23        Vector3d firstCenter, FixedQuaternion firstRotation, Fixed64 firstHeight, Fixed64 firstRadius,
 24        Vector3d secondCenter, FixedQuaternion secondRotation, Fixed64 secondHeight, Fixed64 secondRadius)
 25    {
 26        // A minimum radial distance either lies on a cap of either solid or
 27        // on two smooth lateral surfaces. Their common normal is perpendicular
 28        // to both axes; in that case the infinite-core feet must lie inside
 29        // both finite axial intervals. The parallel case is an interval family.
 63030        if (DoStrictCylinderLateralInteriorsMeet(firstCenter, firstRotation, firstHeight, firstRadius,
 63031                secondCenter, secondRotation, secondHeight, secondRadius))
 32        {
 3133            return true;
 34        }
 59935        return DoesFiniteCapDiskEnterSolid(firstCenter, firstRotation, firstHeight, firstRadius, -1,
 59936                secondCenter, secondRotation, secondHeight, secondRadius, false)
 59937            || DoesFiniteCapDiskEnterSolid(firstCenter, firstRotation, firstHeight, firstRadius, 1,
 59938                secondCenter, secondRotation, secondHeight, secondRadius, false)
 59939            || DoesFiniteCapDiskEnterSolid(secondCenter, secondRotation, secondHeight, secondRadius, -1,
 59940                firstCenter, firstRotation, firstHeight, firstRadius, false)
 59941            || DoesFiniteCapDiskEnterSolid(secondCenter, secondRotation, secondHeight, secondRadius, 1,
 59942                firstCenter, firstRotation, firstHeight, firstRadius, false);
 43    }
 44
 45    private static bool DoStrictCylinderLateralInteriorsMeet(
 46        Vector3d firstCenter, FixedQuaternion firstRotation, Fixed64 firstHeight, Fixed64 firstRadius,
 47        Vector3d secondCenter, FixedQuaternion secondRotation, Fixed64 secondHeight, Fixed64 secondRadius)
 48    {
 63049        WideRationalBasis3d firstBasis = new(firstRotation);
 63050        WideRationalBasis3d secondBasis = new(secondRotation);
 63051        WideRationalBasis3d relative = WideRationalBasis3d.CreateRelative(firstBasis, secondBasis);
 63052        WideOrientedBox.GetRelativeLocalPointNumerators(secondCenter, firstCenter, firstRotation,
 63053            out Signed192 dx, out Signed192 dy, out Signed192 dz, out Signed192 pointDenominator);
 63054        Signed192 bx = relative.Yx;
 63055        Signed192 by = relative.Yy;
 63056        Signed192 bz = relative.Yz;
 63057        Signed192 axisDenominator = relative.Denominator;
 63058        Signed320 sineSquared = WideArithmetic.AddSigned320(
 63059            WideArithmetic.MultiplySigned192(bx, bx), WideArithmetic.MultiplySigned192(bz, bz));
 63060        Signed192 radiusSum = WideArithmetic.AddSigned192(Signed192.Raw(firstRadius), Signed192.Raw(secondRadius));
 63061        Signed320 scaledRadius = WideArithmetic.MultiplySigned192(radiusSum, pointDenominator);
 63062        if (sineSquared.IsZero)
 63        {
 2664            Signed192 heightSum = WideArithmetic.AddSigned192(Signed192.Raw(firstHeight), Signed192.Raw(secondHeight));
 2665            Signed320 axialExtent = WideArithmetic.MultiplySigned192(heightSum, pointDenominator);
 2666            Signed320 axialDistance = Signed320.ExtendValue(WideArithmetic.Double(WideArithmetic.Absolute(dy)));
 2667            if (WideArithmetic.SubtractSigned320(axialExtent, axialDistance).Sign <= 0)
 1268                return false;
 1469            Signed320 radialDistance = WideArithmetic.AddSigned320(
 1470                WideArithmetic.MultiplySigned192(dx, dx), WideArithmetic.MultiplySigned192(dz, dz));
 1471            return WideArithmetic.SubtractSigned576(WideArithmetic.MultiplySigned320(scaledRadius, scaledRadius),
 1472                Signed576.ExtendValue(radialDistance)).Sign > 0;
 73        }
 60474        Signed320 projection = WideArithmetic.AddSigned320(
 60475            WideArithmetic.AddSigned320(WideArithmetic.MultiplySigned192(dx, bx),
 60476                WideArithmetic.MultiplySigned192(dy, by)), WideArithmetic.MultiplySigned192(dz, bz));
 60477        Signed320 axisSquared = WideArithmetic.MultiplySigned192(axisDenominator, axisDenominator);
 60478        Signed576 firstParameter = WideArithmetic.SubtractSigned576(
 60479            WideArithmetic.MultiplySigned320(axisSquared, dy), WideArithmetic.MultiplySigned320(projection, by));
 60480        Signed576 secondParameter = WideArithmetic.MultiplySigned320(
 60481            WideArithmetic.SubtractSigned320(WideArithmetic.MultiplySigned192(by, dy), projection), axisDenominator);
 60482        Signed576 parameterDenominator = WideArithmetic.MultiplySigned320(sineSquared, pointDenominator);
 60483        if (!IsStrictCylinderAxialParameterInterior(firstParameter, parameterDenominator, firstHeight)
 60484            || !IsStrictCylinderAxialParameterInterior(secondParameter, parameterDenominator, secondHeight))
 85        {
 55786            return false;
 87        }
 4788        Signed320 crossProjection = WideArithmetic.SubtractSigned320(
 4789            WideArithmetic.MultiplySigned192(dx, bz), WideArithmetic.MultiplySigned192(dz, bx));
 4790        Signed576 distanceNumerator = WideArithmetic.MultiplySigned320(crossProjection, crossProjection);
 4791        Signed832 bound = WideArithmetic.MultiplySigned576ToSigned832(
 4792            WideArithmetic.MultiplySigned320(scaledRadius, scaledRadius), sineSquared);
 4793        return WideArithmetic.SubtractSigned832(bound, Signed832.ExtendValue(distanceNumerator)).Sign > 0;
 94    }
 95
 96    private static bool IsStrictCylinderAxialParameterInterior(Signed576 numerator,
 97        Signed576 denominator, Fixed64 height)
 98    {
 76799        if (numerator.Sign < 0)
 304100            numerator = WideArithmetic.SubtractSigned576(default, numerator);
 767101        return WideArithmetic.SubtractSigned576(
 767102            WideArithmetic.MultiplySigned576(denominator, Signed192.Raw(height)),
 767103            WideArithmetic.AddSigned576(numerator, numerator)).Sign > 0;
 104    }
 105
 106    /// <summary>
 107    /// Tests positive penetration between a positive local-+Y cylinder and a
 108    /// positive local-+Y cone, whose apex is at local +height/2.
 109    /// </summary>
 110    internal static bool DoesCenteredFiniteCylinderPenetrateCone(
 111        Vector3d cylinderCenter, FixedQuaternion cylinderRotation, Fixed64 cylinderHeight, Fixed64 cylinderRadius,
 112        Vector3d coneCenter, FixedQuaternion coneRotation, Fixed64 coneHeight, Fixed64 coneRadius)
 113    {
 114        // Core entry covers zero radial minima. An apex inside the cylinder
 115        // covers the cone's nonsmooth vertex; the three disks cover cap-active
 116        // minima. Every remaining minimum lies on a smooth cone generator with
 117        // normal perpendicular to the cylinder axis, enumerated exactly below.
 349118        if (DoesCenteredFiniteConePenetrateCapsule(coneCenter, coneRotation, coneHeight, coneRadius,
 349119                cylinderCenter, cylinderRotation, Vector3d.Up, cylinderHeight, Fixed64.Zero)
 349120            || DoesFiniteCapDiskEnterSolid(coneCenter, coneRotation, coneHeight, Fixed64.Zero, 1,
 349121                cylinderCenter, cylinderRotation, cylinderHeight, cylinderRadius, false)
 349122            || DoStrictCylinderConeLateralInteriorsMeet(cylinderCenter, cylinderRotation,
 349123                cylinderHeight, cylinderRadius, coneCenter, coneRotation, coneHeight, coneRadius))
 124        {
 134125            return true;
 126        }
 215127        return DoesFiniteCapDiskEnterSolid(cylinderCenter, cylinderRotation, cylinderHeight, cylinderRadius, -1,
 215128                coneCenter, coneRotation, coneHeight, coneRadius, true)
 215129            || DoesFiniteCapDiskEnterSolid(cylinderCenter, cylinderRotation, cylinderHeight, cylinderRadius, 1,
 215130                coneCenter, coneRotation, coneHeight, coneRadius, true)
 215131            || DoesFiniteCapDiskEnterSolid(coneCenter, coneRotation, coneHeight, coneRadius, -1,
 215132                cylinderCenter, cylinderRotation, cylinderHeight, cylinderRadius, false);
 133    }
 134
 135    private static bool DoStrictCylinderConeLateralInteriorsMeet(
 136        Vector3d cylinderCenter, FixedQuaternion cylinderRotation, Fixed64 cylinderHeight, Fixed64 cylinderRadius,
 137        Vector3d coneCenter, FixedQuaternion coneRotation, Fixed64 coneHeight, Fixed64 coneRadius)
 138    {
 317139        WideRationalBasis3d coneBasis = new(coneRotation);
 317140        WideRationalBasis3d cylinderBasis = new(cylinderRotation);
 317141        WideRationalBasis3d relative = WideRationalBasis3d.CreateRelative(coneBasis, cylinderBasis);
 317142        WideOrientedBox.GetRelativeLocalPointNumerators(cylinderCenter, coneCenter, coneRotation,
 317143            out Signed192 cx, out Signed192 cy, out Signed192 cz, out Signed192 centerDenominator);
 951144        Signed192 ax = relative.Yx, ay = relative.Yy, az = relative.Yz;
 317145        Signed192 axisDenominator = relative.Denominator;
 146        // d points from the cylinder center to the cone apex in raw coordinates.
 317147        Signed192 dx = WideArithmetic.Negate(WideArithmetic.Double(cx));
 317148        Signed192 dy = WideArithmetic.SubtractSigned192(
 317149            Signed192.NarrowProven(WideArithmetic.MultiplySigned192(Signed192.Raw(coneHeight), centerDenominator)),
 317150            WideArithmetic.Double(cy));
 317151        Signed192 dz = WideArithmetic.Negate(WideArithmetic.Double(cz));
 317152        Signed192 pointDenominator = WideArithmetic.Double(centerDenominator);
 317153        Signed320 s = WideArithmetic.AddSigned320(WideArithmetic.MultiplySigned192(ax, ax),
 317154            WideArithmetic.MultiplySigned192(az, az));
 317155        Signed192 hSquared = StrictDiskRawSquare(coneHeight);
 317156        Signed192 rSquared = StrictDiskRawSquare(coneRadius);
 317157        Signed192 lSquared = WideArithmetic.AddSigned192(hSquared, rSquared);
 317158        Signed576 discriminant = WideArithmetic.SubtractSigned576(
 317159            WideArithmetic.MultiplySigned320(s, hSquared),
 317160            WideArithmetic.MultiplySigned320(WideArithmetic.MultiplySigned192(ay, ay), rSquared));
 317161        if (discriminant.Sign < 0)
 1162            return false;
 316163        Signed320 radialDot = WideArithmetic.AddSigned320(WideArithmetic.MultiplySigned192(dx, ax),
 316164            WideArithmetic.MultiplySigned192(dz, az));
 316165        Signed320 axisDot = WideArithmetic.AddSigned320(radialDot, WideArithmetic.MultiplySigned192(dy, ay));
 316166        if (discriminant.IsZero)
 167        {
 8168            return DoesStrictParallelConeGeneratorEnterCylinder(dx, dy, dz, pointDenominator,
 8169                ax, ay, az, axisDenominator, axisDot, lSquared, cylinderHeight, cylinderRadius, coneHeight);
 170        }
 171
 308172        Signed320 cross = WideArithmetic.SubtractSigned320(WideArithmetic.MultiplySigned192(dx, az),
 308173            WideArithmetic.MultiplySigned192(dz, ax));
 308174        Signed576 v = WideArithmetic.SubtractSigned576(WideArithmetic.MultiplySigned320(s, dy),
 308175            WideArithmetic.MultiplySigned320(radialDot, ay));
 308176        Signed320 axisSquared = WideArithmetic.MultiplySigned192(axisDenominator, axisDenominator);
 308177        Signed192 r = Signed192.Raw(coneRadius);
 308178        Signed832 k = Signed832.ExtendValue(discriminant);
 308179        Signed832 uRational = WideArithmetic.MultiplySigned576ToSigned832(discriminant, v);
 308180        Signed832 uRadical = WideArithmetic.MultiplySigned576ToSigned832(
 308181            WideArithmetic.MultiplySigned320(cross, r), axisSquared);
 308182        Signed832 tRational = WideArithmetic.MultiplySigned832(
 308183            WideArithmetic.MultiplySigned576ToSigned832(discriminant, radialDot), axisDenominator);
 308184        Signed832 tRadical = WideArithmetic.MultiplySigned576ToSigned832(
 308185            WideArithmetic.MultiplySigned320(WideArithmetic.MultiplySigned192(r, axisDenominator), ay), cross);
 308186        Signed576 pointScale = WideArithmetic.MultiplySigned320(s, pointDenominator);
 308187        Signed832 tDenominator = WideArithmetic.MultiplySigned576ToSigned832(pointScale, discriminant);
 308188        Signed576 distanceRational = WideArithmetic.MultiplySigned576(v, r);
 189
 190        // In cone coordinates b=-Y. With D the relative-axis denominator,
 191        // S=ax²+az² and K=H²S-R²ay², the two normals are
 192        // m=(-R*ay*ax +/- az*sqrt(K), R*S,
 193        //    -R*ay*az -/+ ax*sqrt(K))/S, with |m|²=H²+R².
 194        // The generator g=(H²+R²)b+Rm has |a x g|²=(H²+R²)K/D².
 195        // Both closest parameters therefore stay linear in sqrt(K).
 1480196        for (int sign = -1; sign <= 1; sign += 2)
 197        {
 532198            Signed832 signedT = sign < 0 ? NegateStrict(tRadical) : tRadical;
 532199            Signed832 signedU = sign > 0 ? NegateStrict(uRadical) : uRadical;
 532200            if (!IsStrictCylinderConeFieldParameterInterior(tRational, signedT, tDenominator, cylinderHeight, k)
 532201                || GetStrictCylinderConeFieldSign(uRational, signedU, k) <= 0
 532202                || !IsStrictConeGeneratorBelowBase(uRational, signedU, pointDenominator,
 532203                    s, lSquared, discriminant, coneHeight, k))
 204            {
 205                continue;
 206            }
 296207            Signed832 distanceRadical = Signed832.ExtendValue(Signed576.ExtendValue(cross));
 296208            if (sign < 0)
 186209                distanceRadical = NegateStrict(distanceRadical);
 296210            if (IsStrictConeGeneratorDistanceLess(distanceRational, distanceRadical,
 296211                    pointScale, cylinderRadius, lSquared, k))
 212            {
 100213                return true;
 214            }
 215        }
 208216        return false;
 217    }
 218
 219    private static bool IsStrictCylinderConeFieldParameterInterior(Signed832 rational,
 220        Signed832 radical, Signed832 denominator, Fixed64 height, Signed832 k)
 221    {
 532222        Signed832 extent = WideArithmetic.MultiplySigned832(denominator, Signed192.Raw(height));
 532223        rational = TwiceStrictDiskValue(rational);
 532224        radical = TwiceStrictDiskValue(radical);
 532225        return GetStrictCylinderConeFieldSign(WideArithmetic.SubtractSigned832(extent, rational), NegateStrict(radical),
 532226            && GetStrictCylinderConeFieldSign(WideArithmetic.AddSigned832(extent, rational), radical, k) > 0;
 227    }
 228
 229    private static int GetStrictCylinderConeFieldSign(Signed832 rational, Signed832 radical, Signed832 k)
 230    {
 1584231        Span<ulong> a = stackalloc ulong[StrictFieldWords];
 1584232        Span<ulong> b = stackalloc ulong[StrictFieldWords];
 1584233        WideArithmetic.GetMagnitude(rational, a);
 1584234        WideArithmetic.GetMagnitude(radical, b);
 1584235        return GetStrictFieldSign(a, (sbyte)rational.Sign, b, (sbyte)radical.Sign, k);
 236    }
 237
 238    private static bool IsStrictConeGeneratorBelowBase(Signed832 rational, Signed832 radical,
 239        Signed192 pointDenominator, Signed320 s, Signed192 lSquared, Signed576 discriminant,
 240        Fixed64 height, Signed832 k)
 241    {
 371242        Span<ulong> a = stackalloc ulong[StrictFieldWords];
 371243        Span<ulong> b = stackalloc ulong[StrictFieldWords];
 371244        a.Clear();
 371245        b.Clear();
 742246        sbyte aSign = 0, bSign = 0;
 371247        AccumulateStrictProduct(a, ref aSign, rational, Signed832.ExtendValue(Signed192.Raw(height)), StrictOne, StrictO
 371248        AccumulateStrictProduct(a, ref aSign, NegateStrict(Signed832.ExtendValue(pointDenominator)),
 371249            Signed832.ExtendValue(Signed576.ExtendValue(s)), Signed832.ExtendValue(lSquared), Signed832.ExtendValue(disc
 371250        AccumulateStrictProduct(b, ref bSign, radical, Signed832.ExtendValue(Signed192.Raw(height)), StrictOne, StrictOn
 371251        return GetStrictFieldSign(a, aSign, b, bSign, k) < 0;
 252    }
 253
 254    private static bool IsStrictConeGeneratorDistanceLess(Signed576 rational, Signed832 radical,
 255        Signed576 pointScale, Fixed64 radius, Signed192 lSquared, Signed832 k)
 256    {
 296257        Span<ulong> a = stackalloc ulong[StrictFieldWords];
 296258        Span<ulong> b = stackalloc ulong[StrictFieldWords];
 296259        a.Clear();
 296260        b.Clear();
 592261        sbyte aSign = 0, bSign = 0;
 296262        Signed832 n = Signed832.ExtendValue(rational);
 296263        Signed832 scale = Signed832.ExtendValue(pointScale);
 296264        AccumulateStrictProduct(a, ref aSign, n, n, StrictOne, StrictOne);
 296265        AccumulateStrictProduct(a, ref aSign, radical, radical, k, StrictOne);
 296266        AccumulateStrictProduct(a, ref aSign, NegateStrict(scale), scale,
 296267            Signed832.ExtendValue(StrictDiskRawSquare(radius)), Signed832.ExtendValue(lSquared));
 296268        AccumulateStrictProduct(b, ref bSign, TwiceStrictDiskValue(n), radical, StrictOne, StrictOne);
 269        // K<2^394; the rational and radical terms here need fewer than 932
 270        // and 736 bits, so their squared sign comparison fits 4096 bits.
 296271        return GetStrictFieldSign(a, aSign, b, bSign, k) < 0;
 272    }
 273
 274    private static bool DoesStrictParallelConeGeneratorEnterCylinder(
 275        Signed192 dx, Signed192 dy, Signed192 dz, Signed192 pointDenominator,
 276        Signed192 ax, Signed192 ay, Signed192 az, Signed192 axisDenominator,
 277        Signed320 axisDot, Signed192 lSquared, Fixed64 cylinderHeight, Fixed64 cylinderRadius, Fixed64 coneHeight)
 278    {
 8279        Signed576 start = WideArithmetic.MultiplySigned320(axisDot, Signed192.Raw(coneHeight));
 8280        Signed576 end = WideArithmetic.SubtractSigned576(start,
 8281            WideArithmetic.MultiplySigned320(WideArithmetic.MultiplySigned192(lSquared, ay), pointDenominator));
 8282        Signed576 denominator = WideArithmetic.MultiplySigned320(
 8283            WideArithmetic.MultiplySigned192(pointDenominator, axisDenominator), Signed192.Raw(coneHeight));
 8284        Signed576 extent = WideArithmetic.MultiplySigned576(denominator, Signed192.Raw(cylinderHeight));
 8285        Signed576 low = WideArithmetic.SubtractSigned576(start, end).Sign < 0 ? start : end;
 8286        Signed576 high = WideArithmetic.SubtractSigned576(start, end).Sign < 0 ? end : start;
 8287        if (WideArithmetic.SubtractSigned576(extent, WideArithmetic.AddSigned576(low, low)).Sign <= 0
 8288            || WideArithmetic.AddSigned576(extent, WideArithmetic.AddSigned576(high, high)).Sign <= 0)
 289        {
 2290            return false;
 291        }
 6292        Signed320 x = WideArithmetic.SubtractSigned320(WideArithmetic.MultiplySigned192(dy, az),
 6293            WideArithmetic.MultiplySigned192(dz, ay));
 6294        Signed320 y = WideArithmetic.SubtractSigned320(WideArithmetic.MultiplySigned192(dz, ax),
 6295            WideArithmetic.MultiplySigned192(dx, az));
 6296        Signed320 z = WideArithmetic.SubtractSigned320(WideArithmetic.MultiplySigned192(dx, ay),
 6297            WideArithmetic.MultiplySigned192(dy, ax));
 6298        Signed576 distance = WideArithmetic.AddSigned576(
 6299            WideArithmetic.AddSigned576(WideArithmetic.MultiplySigned320(x, x), WideArithmetic.MultiplySigned320(y, y)),
 6300            WideArithmetic.MultiplySigned320(z, z));
 6301        Signed576 radius = WideArithmetic.MultiplySigned320(
 6302            WideArithmetic.MultiplySigned192(pointDenominator, axisDenominator), Signed192.Raw(cylinderRadius));
 6303        Signed832 bound = WideArithmetic.MultiplySigned576ToSigned832(radius, radius);
 6304        return WideArithmetic.SubtractSigned832(bound, Signed832.ExtendValue(distance)).Sign > 0;
 305    }
 306}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredSphereStrictOverlap.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.CenteredSphereStrictOverlap.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
 8namespace FixedMathSharp.Geometry;
 9
 10/// <content>
 11/// Exact strict sphere overlap with local-+Y finite solids, without materializing
 12/// surface coordinates, distances, normals, or contact anchors.
 13/// </content>
 14internal static partial class WideFiniteAxisIntersection
 15{
 16    /// <summary>
 17    /// Tests positive penetration into a centered finite cylinder in its
 18    /// authoritative rigid frame. Height is positive, radii are nonnegative,
 19    /// and the quaternion is normalized by the owning collider.
 20    /// </summary>
 21    /// <remarks>
 22    /// A zero-radius sphere penetrates only a strict interior point. A zero-radius
 23    /// cylinder is a segment and therefore has no strict interior for that case.
 24    /// </remarks>
 25    internal static bool DoesCenteredFiniteCylinderPenetrateSphere(
 26        Vector3d center, FixedQuaternion rotation, Fixed64 height, Fixed64 radius,
 27        Vector3d sphereCenter, Fixed64 sphereRadius)
 28    {
 36129        GetStrictSphereLocalPoint(sphereCenter, center, rotation,
 36130            out Signed192 y, out Signed192 denominator, out Signed320 radialSquared);
 36131        Signed192 extent = Signed192.NarrowProven(WideArithmetic.MultiplySigned192(
 36132            Signed192.Raw(height), denominator));
 36133        Signed192 doubledY = WideArithmetic.AddSigned192(y, y);
 36134        if (doubledY.Sign < 0)
 16135            doubledY = WideArithmetic.SubtractSigned192(default, doubledY);
 36136        Signed192 axialGap = WideArithmetic.SubtractSigned192(doubledY, extent);
 36137        Signed192 scaledRadius = Signed192.NarrowProven(WideArithmetic.MultiplySigned192(
 36138            Signed192.Raw(radius), denominator));
 36139        Signed320 radiusSquared = WideArithmetic.MultiplySigned192(scaledRadius, scaledRadius);
 36140        int radialComparison = WideArithmetic.SubtractSigned320(radialSquared, radiusSquared).Sign;
 36141        if (axialGap.Sign < 0 && radialComparison < 0)
 8242            return true;
 27943        if (sphereRadius == Fixed64.Zero)
 7044            return false;
 45
 20946        if (axialGap.Sign <= 0)
 47        {
 48            // Within the cap interval the exact distance is max(rho-r,0).
 49            // Comparing rho < r+s avoids the rim radical altogether.
 7450            Signed192 radiusSum = WideArithmetic.AddSigned192(
 7451                Signed192.Raw(radius), Signed192.Raw(sphereRadius));
 7452            Signed192 expandedRadius = Signed192.NarrowProven(
 7453                WideArithmetic.MultiplySigned192(radiusSum, denominator));
 7454            return WideArithmetic.SubtractSigned320(radialSquared,
 7455                WideArithmetic.MultiplySigned192(expandedRadius, expandedRadius)).Sign < 0;
 56        }
 57
 13558        Signed192 scaledSphereRadius = Signed192.NarrowProven(WideArithmetic.MultiplySigned192(
 13559            Signed192.Raw(sphereRadius), denominator));
 13560        return IsStrictSphereDiskDistanceLess(radialSquared, scaledRadius,
 13561            radiusSquared, axialGap, scaledSphereRadius);
 62    }
 63
 64    /// <summary>
 65    /// Tests positive penetration into a centered finite cone whose base is at
 66    /// local -height/2 and apex at +height/2. Height is positive, radii are
 67    /// nonnegative, and the quaternion is normalized by the owning collider.
 68    /// </summary>
 69    /// <remarks>
 70    /// A zero-radius sphere penetrates only a strict interior point. A zero-radius
 71    /// cone is a segment and therefore has no strict interior for that case.
 72    /// </remarks>
 73    internal static bool DoesCenteredFiniteConePenetrateSphere(
 74        Vector3d center, FixedQuaternion rotation, Fixed64 height, Fixed64 radius,
 75        Vector3d sphereCenter, Fixed64 sphereRadius)
 76    {
 70477        GetStrictSphereLocalPoint(sphereCenter, center, rotation,
 70478            out Signed192 y, out Signed192 denominator, out Signed320 radialSquared);
 70479        Signed192 h = Signed192.Raw(height);
 70480        Signed192 r = Signed192.Raw(radius);
 70481        Signed192 extent = Signed192.NarrowProven(WideArithmetic.MultiplySigned192(h, denominator));
 70482        Signed192 doubledY = WideArithmetic.AddSigned192(y, y);
 70483        Signed192 aboveBase = WideArithmetic.AddSigned192(doubledY, extent);
 70484        Signed192 belowApex = WideArithmetic.SubtractSigned192(extent, doubledY);
 70485        Signed192 heightSquared = Signed192.NarrowProven(WideArithmetic.MultiplySigned192(h, h));
 70486        Signed192 radiusSquared = Signed192.NarrowProven(WideArithmetic.MultiplySigned192(r, r));
 70487        Signed320 apexAxialSquared = WideArithmetic.MultiplySigned192(belowApex, belowApex);
 70488        Signed576 radialHeightSquared = WideArithmetic.MultiplySigned576(
 70489            WideArithmetic.MultiplySigned320(radialSquared, heightSquared), 4L);
 70490        if (aboveBase.Sign > 0 && belowApex.Sign > 0
 70491            && WideArithmetic.SubtractSigned576(radialHeightSquared,
 70492                WideArithmetic.MultiplySigned320(apexAxialSquared, radiusSquared)).Sign < 0)
 93        {
 7694            return true;
 95        }
 62896        if (sphereRadius == Fixed64.Zero)
 40197            return false;
 98
 22799        Signed192 scaledRadius = Signed192.NarrowProven(WideArithmetic.MultiplySigned192(r, denominator));
 227100        Signed192 scaledSphereRadius = Signed192.NarrowProven(WideArithmetic.MultiplySigned192(
 227101            Signed192.Raw(sphereRadius), denominator));
 227102        if (IsStrictSphereDiskDistanceLess(radialSquared, scaledRadius,
 227103                WideArithmetic.MultiplySigned192(scaledRadius, scaledRadius),
 227104                aboveBase, scaledSphereRadius))
 105        {
 18106            return true;
 107        }
 108        // Below the base, its disk (including the rim) is the nearest feature.
 209109        if (aboveBase.Sign <= 0)
 66110            return false;
 111
 143112        Signed192 sideSquared = WideArithmetic.AddSigned192(heightSquared, radiusSquared);
 143113        Signed320 parameter = WideArithmetic.AddSigned320(
 143114            WideArithmetic.MultiplySigned192(h, aboveBase),
 143115            WideArithmetic.MultiplySigned192(
 143116                WideArithmetic.AddSigned192(radiusSquared, radiusSquared), denominator));
 143117        Signed320 parameterRadical = Signed320.ExtendValue(WideArithmetic.SubtractSigned192(
 143118            default, WideArithmetic.AddSigned192(r, r)));
 119        // Side projection from the base rim: h*(2*y+h*D)+2*r*r*D-2*r*sqrt(Q).
 143120        if (CompareStrictSphereRadical(Signed576.ExtendValue(parameter),
 143121                parameterRadical, radialSquared) <= 0)
 122        {
 27123            return false; // The base-disk test already covered the shared rim.
 124        }
 116125        Signed320 endpoint = WideArithmetic.MultiplySigned192(
 116126            WideArithmetic.AddSigned192(sideSquared, sideSquared), denominator);
 116127        if (CompareStrictSphereRadical(Signed576.ExtendValue(
 116128                WideArithmetic.SubtractSigned320(parameter, endpoint)),
 116129                parameterRadical, radialSquared) >= 0)
 130        {
 17131            Signed320 apexDistance = WideArithmetic.AddSigned320(
 17132                TwiceStrictSphereValue(TwiceStrictSphereValue(radialSquared)), apexAxialSquared);
 17133            Signed320 sphereBound = TwiceStrictSphereValue(TwiceStrictSphereValue(
 17134                WideArithmetic.MultiplySigned192(scaledSphereRadius, scaledSphereRadius)));
 17135            return WideArithmetic.SubtractSigned320(apexDistance, sphereBound).Sign < 0;
 136        }
 137
 138        // Interior side: [2*h*sqrt(Q) - r*(h*D-2*y)]^2
 139        //                  < 4*(s*D)^2*(h*h+r*r).
 99140        Signed320 offset = WideArithmetic.MultiplySigned192(r,
 99141            WideArithmetic.SubtractSigned192(default, belowApex));
 99142        Signed576 rational = WideArithmetic.AddSigned576(radialHeightSquared,
 99143            WideArithmetic.MultiplySigned320(offset, offset));
 99144        Signed576 bound = WideArithmetic.MultiplySigned320(
 99145            WideArithmetic.MultiplySigned192(scaledSphereRadius, scaledSphereRadius), sideSquared);
 99146        rational = WideArithmetic.SubtractSigned576(rational,
 99147            WideArithmetic.MultiplySigned576(bound, 4L));
 99148        Signed576 coefficient = WideArithmetic.MultiplySigned576(
 99149            WideArithmetic.MultiplySigned320(offset, h), 4L);
 150        // The coefficient is below 2^261; narrowing does not discard any value.
 99151        return CompareStrictSphereRadical(rational, Signed320.NarrowValue(coefficient), radialSquared) < 0;
 152    }
 153
 154    private static void GetStrictSphereLocalPoint(
 155        Vector3d point, Vector3d center, FixedQuaternion rotation,
 156        out Signed192 y, out Signed192 denominator, out Signed320 radialSquared)
 157    {
 1065158        WideOrientedBox.GetRelativeLocalPointNumerators(point, center, rotation,
 1065159            out Signed192 x, out y, out Signed192 z, out denominator);
 1065160        radialSquared = WideArithmetic.AddSigned320(
 1065161            WideArithmetic.MultiplySigned192(x, x), WideArithmetic.MultiplySigned192(z, z));
 1065162    }
 163
 164    private static bool IsStrictSphereDiskDistanceLess(
 165        Signed320 radialSquared, Signed192 scaledRadius, Signed320 radiusSquared,
 166        Signed192 doubledAxialDistance, Signed192 scaledSphereRadius)
 167    {
 362168        Signed320 axialSquared = WideArithmetic.MultiplySigned192(
 362169            doubledAxialDistance, doubledAxialDistance);
 362170        Signed320 sphereBound = TwiceStrictSphereValue(TwiceStrictSphereValue(
 362171            WideArithmetic.MultiplySigned192(scaledSphereRadius, scaledSphereRadius)));
 362172        if (WideArithmetic.SubtractSigned320(radialSquared, radiusSquared).Sign <= 0)
 62173            return WideArithmetic.SubtractSigned320(axialSquared, sphereBound).Sign < 0;
 174
 300175        Signed320 rational = WideArithmetic.SubtractSigned320(
 300176            WideArithmetic.AddSigned320(axialSquared,
 300177                TwiceStrictSphereValue(TwiceStrictSphereValue(
 300178                    WideArithmetic.AddSigned320(radialSquared, radiusSquared)))), sphereBound);
 300179        Signed192 negativeRadius = WideArithmetic.SubtractSigned192(default, scaledRadius);
 300180        Signed320 coefficient = TwiceStrictSphereValue(TwiceStrictSphereValue(
 300181            TwiceStrictSphereValue(Signed320.ExtendValue(negativeRadius))));
 300182        return CompareStrictSphereRadical(Signed576.ExtendValue(rational), coefficient, radialSquared) < 0;
 183    }
 184
 185    private static Signed320 TwiceStrictSphereValue(Signed320 value) =>
 2292186        WideArithmetic.AddSigned320(value, value);
 187
 188    private static int CompareStrictSphereRadical(
 189        Signed576 rational, Signed320 coefficient, Signed320 radicand)
 190    {
 658191        int rationalSign = rational.Sign;
 658192        int coefficientSign = radicand.IsZero ? 0 : coefficient.Sign;
 658193        if (rationalSign == 0)
 2194            return coefficientSign;
 656195        if (coefficientSign == 0 || coefficientSign == rationalSign)
 130196            return rationalSign;
 197
 198        // A normalized quaternion has D < 2^66, including its normalization
 199        // tolerance. Orthogonality bounds full-domain
 200        // projected numerators below 2^131, hence Q below 2^263. The largest
 201        // cone rational is below 2^394 and its radical coefficient below 2^261.
 202        // Thus both squared comparison terms fit below 2^788, within Signed832.
 526203        Signed832 rationalSquared = WideArithmetic.MultiplySigned576ToSigned832(rational, rational);
 526204        Signed576 coefficientSquared = WideArithmetic.MultiplySigned320(coefficient, coefficient);
 526205        Signed832 radicalSquared = WideArithmetic.MultiplySigned576ToSigned832(coefficientSquared, radicand);
 526206        int comparison = WideArithmetic.SubtractSigned832(rationalSquared, radicalSquared).Sign;
 526207        return comparison == 0 ? 0 : comparison > 0 ? rationalSign : coefficientSign;
 208    }
 209}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredSurfaceAnchors.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.CenteredSurfaceAnchors.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
 8namespace FixedMathSharp.Geometry;
 9
 10/// <content>
 11/// Provides methods for computing the closest surface anchor points on centered,
 12/// finite-axis shapes (e.g. capsules) relative to a given world-space point.
 13/// </content>
 14internal static partial class WideFiniteAxisIntersection
 15{
 16    internal static bool TryGetClosestCenteredCapsuleSurfaceAnchor(
 17        Vector3d point,
 18        Vector3d center,
 19        FixedQuaternion frameRotation,
 20        Vector3d localAxisDirection,
 21        Fixed64 axisLength,
 22        Fixed64 radius,
 23        Vector3d localFallbackRadialDirection,
 24        out FixedPointAnchor surfaceAnchor,
 25        out Vector3d outwardNormal,
 26        out Fixed64 signedDistance)
 27    {
 528        surfaceAnchor = GetClosestCenteredCapsuleSurfaceAnchor(
 529            point,
 530            center,
 531            frameRotation,
 532            localAxisDirection,
 533            axisLength,
 534            radius,
 535            localFallbackRadialDirection,
 536            out outwardNormal,
 537            out bool contained);
 538        if (TryGetSignedSurfaceDistance(
 539                point,
 540                surfaceAnchor,
 541                contained,
 542                out signedDistance))
 43        {
 344            return true;
 45        }
 46
 247        surfaceAnchor = default;
 248        outwardNormal = default;
 249        signedDistance = default;
 250        return false;
 51    }
 52
 53    internal static FixedPointAnchor GetClosestCenteredCapsuleSurfaceAnchor(
 54        Vector3d point,
 55        Vector3d center,
 56        FixedQuaternion frameRotation,
 57        Vector3d localAxisDirection,
 58        Fixed64 axisLength,
 59        Fixed64 radius,
 60        Vector3d localFallbackRadialDirection,
 61        out Vector3d outwardNormal,
 62        out bool contained)
 63    {
 664        GetRigidLocalPointRelation(
 665            point,
 666            center,
 667            frameRotation,
 668            localAxisDirection,
 669            out RigidLocalPointRelation relation);
 670        int cap = GetCenteredCap(relation, axisLength);
 671        Vector3d localSurfaceDirection = GetLocalCapsuleSurfaceDirection(
 672            relation,
 673            localAxisDirection,
 674            axisLength,
 675            cap,
 676            localFallbackRadialDirection);
 677        CreateCenteredCapsuleAnchors(
 678            center,
 679            frameRotation,
 680            localAxisDirection,
 681            axisLength,
 682            radius,
 683            localSurfaceDirection,
 684            cap,
 685            relation,
 686            out FixedPointAnchor axisAnchor,
 687            out FixedPointAnchor surfaceAnchor);
 88
 689        var pointAnchor = new FixedPointAnchor(
 690            point,
 691            FixedQuaternion.Identity,
 692            Vector3d.Zero);
 693        contained = axisAnchor.CompareSquaredDistance(
 694            pointAnchor,
 695            surfaceAnchor) <= 0;
 696        _ = frameRotation.TryRotate(
 697            localSurfaceDirection,
 698            out Vector3d rotatedNormal);
 699        outwardNormal = WideNormalization.GetNormalized(rotatedNormal);
 6100        return surfaceAnchor;
 101    }
 102
 103    private static Vector3d GetLocalCapsuleSurfaceDirection(
 104        RigidLocalPointRelation relation,
 105        Vector3d localAxisDirection,
 106        Fixed64 axisLength,
 107        int cap,
 108        Vector3d localFallbackDirection)
 109    {
 6110        if (cap == 0)
 111        {
 3112            return GetLocalRadialDirection(
 3113                relation,
 3114                localAxisDirection,
 3115                localFallbackDirection);
 116        }
 117
 3118        Signed192 axisLengthRaw = Signed192.Signed(
 3119            axisLength.m_rawValue);
 3120        Signed192 axisX = Signed192.Signed(
 3121            localAxisDirection.X.m_rawValue);
 3122        Signed192 axisY = Signed192.Signed(
 3123            localAxisDirection.Y.m_rawValue);
 3124        Signed192 axisZ = Signed192.Signed(
 3125            localAxisDirection.Z.m_rawValue);
 3126        Signed192 sign = Signed192.Signed(cap);
 3127        Signed192 pointScale = Signed192.Signed(
 3128            Fixed64.Two.m_rawValue);
 3129        Signed320 x = GetLocalCapsuleCapDelta(
 3130            relation.X,
 3131            relation.Denominator,
 3132            pointScale,
 3133            axisX,
 3134            axisLengthRaw,
 3135            sign);
 3136        Signed320 y = GetLocalCapsuleCapDelta(
 3137            relation.Y,
 3138            relation.Denominator,
 3139            pointScale,
 3140            axisY,
 3141            axisLengthRaw,
 3142            sign);
 3143        Signed320 z = GetLocalCapsuleCapDelta(
 3144            relation.Z,
 3145            relation.Denominator,
 3146            pointScale,
 3147            axisZ,
 3148            axisLengthRaw,
 3149            sign);
 3150        return WideNormalization.GetNormalized(x, y, z);
 151    }
 152
 153    private static Signed320 GetLocalCapsuleCapDelta(
 154        Signed192 pointNumerator,
 155        Signed192 denominator,
 156        Signed192 pointScale,
 157        Signed192 axisComponent,
 158        Signed192 axisLength,
 159        Signed192 sign)
 160    {
 9161        Signed320 scaledPoint = WideArithmetic.MultiplySigned192(
 9162            pointNumerator,
 9163            pointScale);
 9164        _ = Signed192.TryNarrowSigned(
 9165            WideArithmetic.MultiplySigned192(
 9166                axisComponent,
 9167                sign),
 9168            out Signed192 signedAxisComponent);
 9169        Signed320 axisAndLength = WideArithmetic.MultiplySigned192(
 9170            signedAxisComponent,
 9171            axisLength);
 9172        _ = Signed192.TryNarrowSigned(
 9173            axisAndLength,
 9174            out Signed192 narrowAxisAndLength);
 9175        Signed320 endpoint = WideArithmetic.MultiplySigned192(
 9176            denominator,
 9177            narrowAxisAndLength);
 9178        return WideArithmetic.SubtractSigned320(
 9179            scaledPoint,
 9180            endpoint);
 181    }
 182
 183    internal static bool TryGetClosestCenteredFiniteCylinderSurfaceAnchor(
 184        Vector3d point,
 185        Vector3d center,
 186        FixedQuaternion frameRotation,
 187        Vector3d localAxisDirection,
 188        Fixed64 axisLength,
 189        Fixed64 radius,
 190        Vector3d localFallbackRadialDirection,
 191        out FixedPointAnchor surfaceAnchor,
 192        out Vector3d outwardNormal,
 193        out Fixed64 signedDistance)
 194    {
 27195        surfaceAnchor =
 27196            GetClosestCenteredFiniteCylinderSurfaceAnchor(
 27197                point,
 27198                center,
 27199                frameRotation,
 27200                localAxisDirection,
 27201                axisLength,
 27202                radius,
 27203                localFallbackRadialDirection,
 27204                out outwardNormal,
 27205                out bool contained);
 27206        if (TryGetSignedSurfaceDistance(
 27207                point,
 27208                surfaceAnchor,
 27209                contained,
 27210                out signedDistance))
 211        {
 23212            return true;
 213        }
 214
 4215        surfaceAnchor = default;
 4216        outwardNormal = default;
 4217        signedDistance = default;
 4218        return false;
 219    }
 220
 221    internal static FixedPointAnchor
 222        GetClosestCenteredFiniteCylinderSurfaceAnchor(
 223            Vector3d point,
 224            Vector3d center,
 225            FixedQuaternion frameRotation,
 226            Vector3d localAxisDirection,
 227            Fixed64 axisLength,
 228            Fixed64 radius,
 229            Vector3d localFallbackRadialDirection,
 230            out Vector3d outwardNormal,
 231            out bool contained)
 232    {
 28233        GetRigidLocalPointRelation(
 28234            point,
 28235            center,
 28236            frameRotation,
 28237            localAxisDirection,
 28238            out RigidLocalPointRelation relation);
 28239        int cap = GetCenteredCap(relation, axisLength);
 28240        Signed576 radialBound = GetCenteredRadialBound(
 28241            relation,
 28242            radius);
 28243        bool radialOutside = CompareSigned576(
 28244            relation.RadialNumerator,
 28245            radialBound) > 0;
 28246        contained =
 28247            cap == 0
 28248            && CompareSigned576(
 28249                relation.RadialNumerator,
 28250                radialBound) <= 0;
 28251        Vector3d localRadialDirection = GetLocalRadialDirection(
 28252            relation,
 28253            localAxisDirection,
 28254            localFallbackRadialDirection);
 28255        FixedPointAnchor sideAnchor = CreateCenteredCylinderSideAnchor(
 28256            center,
 28257            frameRotation,
 28258            localAxisDirection,
 28259            axisLength,
 28260            radius,
 28261            localRadialDirection,
 28262            cap,
 28263            relation);
 264
 28265        bool side = radialOutside || cap == 0;
 28266        FixedPointAnchor capAnchor = default;
 28267        if (!side || contained)
 268        {
 14269            _ = TryGetCenteredRadialDistance(
 14270                relation,
 14271                out Fixed64 radialDistance);
 14272            capAnchor = CreateCenteredSurfaceAnchor(
 14273                center,
 14274                frameRotation,
 14275                localAxisDirection,
 14276                relation.Projection.Sign >= 0
 14277                    ? axisLength
 14278                    : -axisLength,
 14279                localRadialDirection,
 14280                radialDistance);
 281        }
 28282        if (contained)
 283        {
 11284            var pointAnchor = new FixedPointAnchor(
 11285                point,
 11286                FixedQuaternion.Identity,
 11287                Vector3d.Zero);
 11288            side = pointAnchor.CompareSquaredDistance(
 11289                sideAnchor,
 11290                capAnchor) <= 0;
 291        }
 28292        FixedPointAnchor surfaceAnchor =
 28293            side ? sideAnchor : capAnchor;
 294
 28295        var reference = new FixedPointAnchor(
 28296            point,
 28297            FixedQuaternion.Identity,
 28298            Vector3d.Zero);
 28299        if (side && cap != 0 && radialOutside)
 300        {
 7301            outwardNormal = WidePointAnchor3d.GetDirection(
 7302                reference,
 7303                surfaceAnchor);
 304        }
 305        else
 306        {
 21307            Vector3d localNormal = side
 21308                ? localRadialDirection
 21309                : (cap > 0 || relation.Projection.Sign >= 0
 21310                    ? localAxisDirection
 21311                    : -localAxisDirection);
 21312            _ = frameRotation.TryRotate(
 21313                localNormal,
 21314                out Vector3d rotatedNormal);
 21315            outwardNormal = WideNormalization.GetNormalized(rotatedNormal);
 316        }
 28317        return surfaceAnchor;
 318    }
 319
 320    private static bool TryGetSignedSurfaceDistance(
 321        Vector3d point,
 322        in FixedPointAnchor surfaceAnchor,
 323        bool contained,
 324        out Fixed64 signedDistance)
 325    {
 59326        var reference = new FixedPointAnchor(
 59327            point,
 59328            FixedQuaternion.Identity,
 59329            Vector3d.Zero);
 59330        if (!reference.TryGetOffsetFrom(
 59331                surfaceAnchor,
 59332                out Vector3d surfaceToPoint)
 59333            || !Vector3d.TryGetMagnitude(
 59334                surfaceToPoint,
 59335                out Fixed64 distance))
 336        {
 13337            signedDistance = default;
 13338            return false;
 339        }
 340
 46341        signedDistance = contained ? -distance : distance;
 46342        return true;
 343    }
 344
 345    private static FixedPointAnchor CreateCenteredCylinderSideAnchor(
 346        Vector3d center,
 347        FixedQuaternion frameRotation,
 348        Vector3d localAxisDirection,
 349        Fixed64 axisLength,
 350        Fixed64 radius,
 351        Vector3d localRadialDirection,
 352        int cap,
 353        RigidLocalPointRelation relation)
 354    {
 28355        if (cap != 0)
 356        {
 10357            return CreateCenteredSurfaceAnchor(
 10358                center,
 10359                frameRotation,
 10360                localAxisDirection,
 10361                cap > 0 ? axisLength : -axisLength,
 10362                localRadialDirection,
 10363                radius);
 364        }
 365
 18366        Fixed64 axialDistance =
 18367            GetClosestCenteredRigidAxisDistance(
 18368                relation,
 18369                axisLength,
 18370                cap);
 18371        Vector3d roundedRadialOffset = localRadialDirection * radius;
 18372        return new FixedPointAnchor(
 18373            center,
 18374            frameRotation,
 18375            localAxisDirection * axialDistance,
 18376            roundedRadialOffset,
 18377            FixedPointAnchorTerm3d.CreateRadialSupport(
 18378                localRadialDirection,
 18379                radius,
 18380                roundedRadialOffset));
 381    }
 382
 383    private static void CreateCenteredCapsuleAnchors(
 384        Vector3d center,
 385        FixedQuaternion frameRotation,
 386        Vector3d localAxisDirection,
 387        Fixed64 axisLength,
 388        Fixed64 radius,
 389        Vector3d localRadialDirection,
 390        int cap,
 391        RigidLocalPointRelation relation,
 392        out FixedPointAnchor axisAnchor,
 393        out FixedPointAnchor surfaceAnchor)
 394    {
 6395        if (cap != 0)
 396        {
 3397            Fixed64 signedLength = cap > 0
 3398                ? axisLength
 3399                : -axisLength;
 3400            axisAnchor = CreateCenteredSurfaceAnchor(
 3401                center,
 3402                frameRotation,
 3403                localAxisDirection,
 3404                signedLength,
 3405                localRadialDirection,
 3406                Fixed64.Zero);
 3407            surfaceAnchor = CreateCenteredSurfaceAnchor(
 3408                center,
 3409                frameRotation,
 3410                localAxisDirection,
 3411                signedLength,
 3412                localRadialDirection,
 3413                radius);
 3414            return;
 415        }
 416
 3417        Fixed64 axialDistance =
 3418            GetClosestCenteredRigidAxisDistance(
 3419                relation,
 3420                axisLength,
 3421                cap);
 3422        Vector3d localAxisOffset =
 3423            localAxisDirection * axialDistance;
 3424        axisAnchor = new FixedPointAnchor(
 3425            center,
 3426            frameRotation,
 3427            localAxisOffset);
 3428        Vector3d roundedRadialOffset =
 3429            localRadialDirection * radius;
 3430        surfaceAnchor = new FixedPointAnchor(
 3431            center,
 3432            frameRotation,
 3433            localAxisOffset,
 3434            roundedRadialOffset,
 3435            FixedPointAnchorTerm3d.CreateRadialSupport(
 3436                localRadialDirection,
 3437                radius,
 3438                roundedRadialOffset));
 3439    }
 440
 441    private static Fixed64 GetClosestCenteredRigidAxisDistance(
 442        RigidLocalPointRelation relation,
 443        Fixed64 axisLength,
 444        int cap)
 445    {
 172446        if (cap != 0)
 447        {
 20448            return cap > 0
 20449                ? axisLength / Fixed64.Two
 20450                : -(axisLength / Fixed64.Two);
 451        }
 452
 152453        Signed576 numerator = WideArithmetic.MultiplySigned320(
 152454            relation.Projection,
 152455            Signed320.ExtendValue(
 152456                Signed192.Signed(
 152457                    Fixed64.One.m_rawValue)));
 152458        Signed320 denominator = WideArithmetic.MultiplySigned192(
 152459            relation.Denominator,
 152460            relation.AxisSquared);
 461        // Interior classification proves the ratio lies within the centered
 462        // axis's representable half-length.
 152463        _ = Fixed64.TryGetSignedRawRatio(
 152464            numerator,
 152465            Signed576.ExtendValue(denominator),
 152466            out Fixed64 axialDistance);
 152467        return axialDistance;
 468    }
 469
 470    private static FixedPointAnchor CreateCenteredSurfaceAnchor(
 471        Vector3d center,
 472        FixedQuaternion frameRotation,
 473        Vector3d localAxisDirection,
 474        Fixed64 signedAxisLength,
 475        Vector3d localRadialDirection,
 476        Fixed64 radialDistance)
 477    {
 58478        Vector3d roundedAxialOffset =
 58479            localAxisDirection * (signedAxisLength / Fixed64.Two);
 58480        Vector3d roundedRadialOffset =
 58481            localRadialDirection * radialDistance;
 58482        return new FixedPointAnchor(
 58483            center,
 58484            frameRotation,
 58485            roundedAxialOffset,
 58486            roundedRadialOffset,
 58487            FixedPointAnchorTerm3d.CreateCenteredAxisSupport(
 58488                localAxisDirection,
 58489                signedAxisLength,
 58490                localRadialDirection,
 58491                radialDistance,
 58492                roundedAxialOffset,
 58493                roundedRadialOffset));
 494    }
 495
 496    private static bool TryGetCenteredRadialDistance(
 497        RigidLocalPointRelation relation,
 498        out Fixed64 radialDistance)
 499    {
 30500        Signed576 numeratorRoot = WideArithmetic.GetFloorSquareRoot(
 30501            Signed704.ExtendValue(
 30502                relation.RadialNumerator));
 30503        Signed576 denominatorRootWide =
 30504            WideArithmetic.GetFloorSquareRoot(
 30505                Signed704.ExtendValue(
 30506                    Signed576.ExtendValue(
 30507                        relation.RadialDenominator)));
 30508        Fixed64 candidate = Fixed64.GetNonNegativeRawRatioFloor(
 30509            Signed704.ExtendValue(numeratorRoot),
 30510            Signed704.ExtendValue(denominatorRootWide));
 511
 30512        Signed320 one = Signed320.ExtendValue(
 30513            Signed192.Signed(1L));
 30514        Signed320 candidateRaw = Signed320.ExtendValue(
 30515            Signed192.Signed(candidate.m_rawValue));
 30516        int candidateComparison =
 30517            WideArithmetic.CompareNonNegativeRadicalToRatio(
 30518                relation.RadialNumerator,
 30519                relation.RadialDenominator,
 30520                candidateRaw,
 30521                one);
 30522        candidate = Fixed64.FromRaw(
 30523            candidate.m_rawValue + (candidateComparison >> 31));
 30524        if (candidate != Fixed64.MaxValue)
 525        {
 28526            Signed320 next = Signed320.ExtendValue(
 28527                Signed192.Signed(candidate.m_rawValue + 1L));
 28528            int nextComparison =
 28529                WideArithmetic.CompareNonNegativeRadicalToRatio(
 28530                    relation.RadialNumerator,
 28531                    relation.RadialDenominator,
 28532                    next,
 28533                    one);
 28534            candidate = Fixed64.FromRaw(
 28535                candidate.m_rawValue
 28536                + GetNonNegativeComparisonIncrement(nextComparison));
 537        }
 538
 30539        Signed192 doubledMidpoint = WideArithmetic.AddSigned192(
 30540            WideArithmetic.AddSigned192(
 30541                Signed192.Signed(candidate.m_rawValue),
 30542                Signed192.Signed(candidate.m_rawValue)),
 30543            Signed192.Signed(1L));
 30544        int midpointComparison =
 30545            WideArithmetic.CompareNonNegativeRadicalToRatio(
 30546                relation.RadialNumerator,
 30547                relation.RadialDenominator,
 30548                Signed320.ExtendValue(doubledMidpoint),
 30549                    Signed320.ExtendValue(
 30550                        Signed192.Signed(2L)));
 30551        long increment =
 30552            GetHalfToEvenIncrement(midpointComparison, candidate.m_rawValue);
 30553        if (candidate == Fixed64.MaxValue && increment != 0L)
 554        {
 2555            radialDistance = default;
 2556            return false;
 557        }
 558
 28559        radialDistance =
 28560            Fixed64.FromRaw(candidate.m_rawValue + increment);
 28561        return true;
 562    }
 563
 564    private static long GetNonNegativeComparisonIncrement(int comparison) =>
 28565        1L ^ ((uint)comparison >> 31);
 566
 567    private static Vector3d GetLocalRadialDirection(
 568        RigidLocalPointRelation relation,
 569        Vector3d localAxisDirection,
 570        Vector3d localFallbackRadialDirection)
 571    {
 59572        GetLocalRadialComponents(
 59573            relation.X,
 59574            relation.Y,
 59575            relation.Z,
 59576            relation.Projection,
 59577            relation.AxisSquared,
 59578            localAxisDirection,
 59579            out Signed320 radialX,
 59580            out Signed320 radialY,
 59581            out Signed320 radialZ);
 59582        Vector3d radial = WideNormalization.GetNormalized(
 59583            radialX,
 59584            radialY,
 59585            radialZ);
 59586        if (!radial.IsZero)
 39587            return radial;
 588
 20589        Signed192 fallbackX = Signed192.Signed(
 20590            localFallbackRadialDirection.X.m_rawValue);
 20591        Signed192 fallbackY = Signed192.Signed(
 20592            localFallbackRadialDirection.Y.m_rawValue);
 20593        Signed192 fallbackZ = Signed192.Signed(
 20594            localFallbackRadialDirection.Z.m_rawValue);
 20595        Signed320 fallbackProjection = GetProjection(
 20596            fallbackX,
 20597            fallbackY,
 20598            fallbackZ,
 20599            localAxisDirection);
 20600        GetLocalRadialComponents(
 20601            fallbackX,
 20602            fallbackY,
 20603            fallbackZ,
 20604            fallbackProjection,
 20605            relation.AxisSquared,
 20606            localAxisDirection,
 20607            out radialX,
 20608            out radialY,
 20609            out radialZ);
 20610        return WideNormalization.GetNormalized(radialX, radialY, radialZ);
 611    }
 612
 613    private static Signed320 GetProjection(
 614        Signed192 x,
 615        Signed192 y,
 616        Signed192 z,
 617        Vector3d localAxisDirection) =>
 233618        WideArithmetic.AddSigned320(
 233619            WideArithmetic.AddSigned320(
 233620                WideArithmetic.MultiplySigned192(
 233621                    x,
 233622                    Signed192.Signed(
 233623                        localAxisDirection.X.m_rawValue)),
 233624                WideArithmetic.MultiplySigned192(
 233625                    y,
 233626                    Signed192.Signed(
 233627                        localAxisDirection.Y.m_rawValue))),
 233628            WideArithmetic.MultiplySigned192(
 233629                z,
 233630                Signed192.Signed(
 233631                    localAxisDirection.Z.m_rawValue)));
 632
 633    private static int GetCenteredCap(
 634        RigidLocalPointRelation relation,
 635        Fixed64 axisLength)
 636    {
 213637        Signed576 scaledProjection = WideArithmetic.MultiplySigned320(
 213638            relation.Projection,
 213639            Signed320.ExtendValue(
 213640                Signed192.Signed(
 213641                    Fixed64.One.m_rawValue)));
 213642        scaledProjection = WideArithmetic.AddSigned576(
 213643            scaledProjection,
 213644            scaledProjection);
 213645        Signed320 denominatorAndAxis = WideArithmetic.MultiplySigned192(
 213646            relation.Denominator,
 213647            relation.AxisSquared);
 213648        Signed576 axialExtent = WideArithmetic.MultiplySigned320(
 213649            denominatorAndAxis,
 213650            Signed320.ExtendValue(
 213651                Signed192.Signed(
 213652                    axisLength.m_rawValue)));
 213653        return CompareMagnitudeSigned576(
 213654            scaledProjection,
 213655            axialExtent) > 0
 213656            ? relation.Projection.Sign
 213657            : 0;
 658    }
 659
 660    private static Signed576 GetCenteredRadialBound(
 661        RigidLocalPointRelation relation,
 662        Fixed64 radius)
 663    {
 28664        Signed192 squaredRadius = GetSquaredRadius(
 28665            Signed192.Signed(radius.m_rawValue));
 28666        return WideArithmetic.MultiplySigned320(
 28667            relation.RadialDenominator,
 28668            Signed320.ExtendValue(squaredRadius));
 669    }
 670
 671    private static void GetLocalRadialComponents(
 672        Signed192 x,
 673        Signed192 y,
 674        Signed192 z,
 675        Signed320 projection,
 676        Signed192 axisSquared,
 677        Vector3d localAxisDirection,
 678        out Signed320 radialX,
 679        out Signed320 radialY,
 680        out Signed320 radialZ)
 681    {
 79682        radialX = GetLocalRadialComponent(
 79683            x,
 79684            projection,
 79685            axisSquared,
 79686            localAxisDirection.X);
 79687        radialY = GetLocalRadialComponent(
 79688            y,
 79689            projection,
 79690            axisSquared,
 79691            localAxisDirection.Y);
 79692        radialZ = GetLocalRadialComponent(
 79693            z,
 79694            projection,
 79695            axisSquared,
 79696            localAxisDirection.Z);
 79697    }
 698
 699    private static Signed320 GetLocalRadialComponent(
 700        Signed192 pointComponent,
 701        Signed320 projection,
 702        Signed192 axisSquared,
 703        Fixed64 axisComponent)
 704    {
 237705        Signed576 projectedAxis = WideArithmetic.MultiplySigned320(
 237706            projection,
 237707            Signed320.ExtendValue(
 237708                Signed192.Signed(
 237709                    axisComponent.m_rawValue)));
 237710        _ = Signed320.TryNarrowSigned(
 237711            projectedAxis,
 237712            out Signed320 narrowProjectedAxis);
 237713        return WideArithmetic.SubtractSigned320(
 237714            WideArithmetic.MultiplySigned192(
 237715                pointComponent,
 237716                axisSquared),
 237717            narrowProjectedAxis);
 718    }
 719
 720    private static int CompareSigned576(
 721        Signed576 left,
 722        Signed576 right) =>
 46723        WideArithmetic.SubtractSigned576(left, right).Sign;
 724
 725    private static int CompareMagnitudeSigned576(
 726        Signed576 left,
 727        Signed576 right)
 728    {
 213729        if (left.Sign < 0)
 47730            left = WideArithmetic.SubtractSigned576(default, left);
 213731        return WideArithmetic.CompareNonNegative(left, right);
 732    }
 733
 734}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredSurfaces.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.CenteredSurfaces.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
 8namespace FixedMathSharp.Geometry;
 9
 10/// <content>
 11/// Provides overlap and closest-surface-point queries for axis-aligned (origin-centered)
 12/// finite cones and cylinders, including surface anchors, normals, and signed distances.
 13/// </content>
 14internal static partial class WideFiniteAxisIntersection
 15{
 16    internal static bool DoesCenteredFiniteConeOverlapSphere(
 17        Vector3d coneCenter,
 18        Vector3d axisDirection,
 19        Fixed64 height,
 20        Fixed64 radius,
 21        Vector3d sphereCenter,
 22        Fixed64 sphereRadius)
 23    {
 524        if (WideFiniteConeIntersection.ContainsPointInCenteredCone(
 525                sphereCenter,
 526                coneCenter,
 527                axisDirection,
 528                height,
 529                radius,
 530                strict: false))
 31        {
 132            return true;
 33        }
 34
 435        return TryGetClosestCenteredFiniteConeSurfaceAnchor(
 436                sphereCenter,
 437                coneCenter,
 438                FixedQuaternion.Identity,
 439                axisDirection,
 440                height,
 441                radius,
 442                Vector3d.Zero,
 443                out _,
 444                out _,
 445                out Fixed64 distance)
 446            && distance <= sphereRadius;
 47    }
 48
 49    internal static bool TryGetClosestPointOnCenteredFiniteConeSurface(
 50        Vector3d point,
 51        Vector3d center,
 52        Vector3d axisDirection,
 53        Fixed64 height,
 54        Fixed64 radius,
 55        Vector3d fallbackRadialDirection,
 56        out Vector3d surfacePoint,
 57        out Vector3d outwardNormal,
 58        out Fixed64 signedDistance)
 59    {
 1160        if (!TryGetClosestCenteredFiniteConeSurfaceOffset(
 1161                point,
 1162                center,
 1163                axisDirection,
 1164                height,
 1165                radius,
 1166                fallbackRadialDirection,
 1167                out Vector3d surfaceOffset,
 1168                out outwardNormal,
 1169                out signedDistance)
 1170            || !Vector3d.TryAdd(center, surfaceOffset, out surfacePoint))
 71        {
 572            surfacePoint = default;
 573            outwardNormal = default;
 574            signedDistance = default;
 575            return false;
 76        }
 77
 678        return true;
 79    }
 80
 81    internal static bool TryGetClosestCenteredFiniteConeSurfaceOffset(
 82        Vector3d point,
 83        Vector3d center,
 84        Vector3d axisDirection,
 85        Fixed64 height,
 86        Fixed64 radius,
 87        Vector3d fallbackRadialDirection,
 88        out Vector3d surfaceOffset,
 89        out Vector3d outwardNormal,
 90        out Fixed64 signedDistance)
 91    {
 1492        if (!TryGetClosestCenteredFiniteConeSurfaceAnchor(
 1493                point,
 1494                center,
 1495                FixedQuaternion.Identity,
 1496                axisDirection,
 1497                height,
 1498                radius,
 1499                fallbackRadialDirection,
 14100                out FixedPointAnchor anchor,
 14101                out outwardNormal,
 14102                out signedDistance)
 14103            || !anchor.TryGetLocalPointIn(
 14104                center,
 14105                FixedQuaternion.Identity,
 14106                out surfaceOffset))
 107        {
 5108            surfaceOffset = default;
 5109            outwardNormal = default;
 5110            signedDistance = default;
 5111            return false;
 112        }
 9113        return true;
 114    }
 115
 116    internal static bool TryGetClosestPointOnCenteredFiniteCylinderSurface(
 117        Vector3d point,
 118        Vector3d center,
 119        Vector3d axisDirection,
 120        Fixed64 axisLength,
 121        Fixed64 radius,
 122        Vector3d fallbackRadialDirection,
 123        out Vector3d surfacePoint,
 124        out Vector3d outwardNormal,
 125        out Fixed64 signedDistance)
 126    {
 13127        if (!TryGetClosestCenteredFiniteCylinderSurfaceOffset(
 13128                point,
 13129                center,
 13130                axisDirection,
 13131                axisLength,
 13132                radius,
 13133                fallbackRadialDirection,
 13134                out Vector3d surfaceOffset,
 13135                out outwardNormal,
 13136                out signedDistance)
 13137            || !Vector3d.TryAdd(center, surfaceOffset, out surfacePoint))
 138        {
 3139            surfacePoint = default;
 3140            outwardNormal = default;
 3141            signedDistance = default;
 3142            return false;
 143        }
 144
 10145        return true;
 146    }
 147
 148    internal static bool TryGetClosestCenteredFiniteCylinderSurfaceOffset(
 149        Vector3d point,
 150        Vector3d center,
 151        Vector3d axisDirection,
 152        Fixed64 axisLength,
 153        Fixed64 radius,
 154        Vector3d fallbackRadialDirection,
 155        out Vector3d surfaceOffset,
 156        out Vector3d outwardNormal,
 157        out Fixed64 signedDistance)
 158    {
 16159        if (TryGetClosestCenteredFiniteCylinderSurfaceAnchor(
 16160                point,
 16161                center,
 16162                FixedQuaternion.Identity,
 16163                axisDirection,
 16164                axisLength,
 16165                radius,
 16166                fallbackRadialDirection,
 16167                out FixedPointAnchor anchor,
 16168                out outwardNormal,
 16169                out signedDistance)
 16170            && anchor.TryGetLocalPointIn(
 16171                center,
 16172                FixedQuaternion.Identity,
 16173                out surfaceOffset))
 174        {
 14175            return true;
 176        }
 177
 2178        surfaceOffset = default;
 2179        outwardNormal = default;
 2180        signedDistance = default;
 2181        return false;
 182    }
 183
 184    private static Vector3d GetCenteredConeSideNormal(
 185        Vector3d radialNormal,
 186        Vector3d axisDirection,
 187        Fixed64 height,
 188        Fixed64 radius)
 189    {
 22190        Signed320 x = WideArithmetic.AddSigned320(
 22191            WideArithmetic.MultiplySigned192(
 22192                Signed192.Signed(radialNormal.X.m_rawValue),
 22193                Signed192.Signed(height.m_rawValue)),
 22194            WideArithmetic.MultiplySigned192(
 22195                Signed192.Signed(axisDirection.X.m_rawValue),
 22196                Signed192.Signed(radius.m_rawValue)));
 22197        Signed320 y = WideArithmetic.AddSigned320(
 22198            WideArithmetic.MultiplySigned192(
 22199                Signed192.Signed(radialNormal.Y.m_rawValue),
 22200                Signed192.Signed(height.m_rawValue)),
 22201            WideArithmetic.MultiplySigned192(
 22202                Signed192.Signed(axisDirection.Y.m_rawValue),
 22203                Signed192.Signed(radius.m_rawValue)));
 22204        Signed320 z = WideArithmetic.AddSigned320(
 22205            WideArithmetic.MultiplySigned192(
 22206                Signed192.Signed(radialNormal.Z.m_rawValue),
 22207                Signed192.Signed(height.m_rawValue)),
 22208            WideArithmetic.MultiplySigned192(
 22209                Signed192.Signed(axisDirection.Z.m_rawValue),
 22210                Signed192.Signed(radius.m_rawValue)));
 22211        return WideNormalization.GetNormalized(x, y, z);
 212    }
 213}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.CenteredSurfaceTriangle.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.CenteredSurfaceTriangle.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
 8namespace FixedMathSharp.Geometry;
 9
 10/// <content>
 11/// Computes wide-precision contact points and penetration depth between a
 12/// support point along an axis normal and a triangle surface, using centered
 13/// (relative) coordinates to preserve precision.
 14/// </content>
 15internal static partial class WideFiniteAxisIntersection
 16{
 17    internal static bool TryGetTriangleSupportPointContact(
 18        FixedTriangle triangle,
 19        Vector3d supportPoint,
 20        Vector3d normal,
 21        out Vector3d pointOnTriangle,
 22        out Fixed64 depth,
 23        out bool depthIsClamped) =>
 1024        TryGetTriangleSupportContactOffsets(
 1025            triangle,
 1026            Vector3d.Zero,
 1027            supportPoint,
 1028            Vector3d.Zero,
 1029            normal,
 1030            out pointOnTriangle,
 1031            out depth,
 1032            out depthIsClamped);
 33
 34    private static bool TryGetTriangleSupportContactOffsets(
 35        FixedTriangle triangle,
 36        Vector3d triangleOrigin,
 37        Vector3d center,
 38        Vector3d centerOffset,
 39        Vector3d normal,
 40        out Vector3d triangleOriginOffset,
 41        out Fixed64 depth,
 42        out bool depthIsClamped)
 43    {
 1044        Signed192 relativeX = GetSupportDifference(
 1045            center.X,
 1046            triangle.A.X,
 1047            centerOffset.X);
 1048        Signed192 relativeY = GetSupportDifference(
 1049            center.Y,
 1050            triangle.A.Y,
 1051            centerOffset.Y);
 1052        Signed192 relativeZ = GetSupportDifference(
 1053            center.Z,
 1054            triangle.A.Z,
 1055            centerOffset.Z);
 1056        Signed192 normalX = Signed192.Signed(normal.X.m_rawValue);
 1057        Signed192 normalY = Signed192.Signed(normal.Y.m_rawValue);
 1058        Signed192 normalZ = Signed192.Signed(normal.Z.m_rawValue);
 1059        Signed320 normalSquared = WideGeometry.GetSquaredMagnitude(
 1060            normalX,
 1061            normalY,
 1062            normalZ,
 1063            out _,
 1064            out _,
 1065            out _);
 1066        Signed320 signedDistanceNumerator = WideArithmetic.AddSigned320(
 1067            WideArithmetic.AddSigned320(
 1068                WideArithmetic.MultiplySigned192(relativeX, normalX),
 1069                WideArithmetic.MultiplySigned192(relativeY, normalY)),
 1070            WideArithmetic.MultiplySigned192(relativeZ, normalZ));
 1071        if (signedDistanceNumerator.Sign > 0
 1072            && WideArithmetic.GetRoundedNonNegativeNormalizedDepth(
 1073                Signed576.ExtendValue(signedDistanceNumerator),
 1074                Signed576.ExtendValue(normalSquared),
 1075                Scale320,
 1076                out _) > Fixed64.Epsilon)
 77        {
 178            triangleOriginOffset = default;
 179            depth = default;
 180            depthIsClamped = default;
 181            return false;
 82        }
 83
 984        Signed320 projectionDenominator = normalSquared;
 985        Signed576 projectedX = GetProjectedCoordinateNumerator(
 986            relativeX,
 987            normalX,
 988            signedDistanceNumerator,
 989            projectionDenominator);
 990        Signed576 projectedY = GetProjectedCoordinateNumerator(
 991            relativeY,
 992            normalY,
 993            signedDistanceNumerator,
 994            projectionDenominator);
 995        Signed576 projectedZ = GetProjectedCoordinateNumerator(
 996            relativeZ,
 997            normalZ,
 998            signedDistanceNumerator,
 999            projectionDenominator);
 9100        Signed576 wideProjectionDenominator =
 9101            Signed576.ExtendValue(projectionDenominator);
 9102        GetTriangleNormal(
 9103            triangle,
 9104            out Signed192 triangleNormalX,
 9105            out Signed192 triangleNormalY,
 9106            out Signed192 triangleNormalZ);
 9107        if (WideGeometry.GetSquaredMagnitude(
 9108                triangleNormalX,
 9109                triangleNormalY,
 9110                triangleNormalZ,
 9111                out _,
 9112                out _,
 9113                out _).IsZero)
 114        {
 2115            triangleOriginOffset = default;
 2116            depth = default;
 2117            depthIsClamped = default;
 2118            return false;
 119        }
 7120        if (!ContainsTriangleProjection(
 7121                triangle,
 7122                triangleNormalX,
 7123                triangleNormalY,
 7124                triangleNormalZ,
 7125                GetAbsoluteCoordinateNumerator(
 7126                    projectedX,
 7127                    triangle.A.X,
 7128                    wideProjectionDenominator),
 7129                GetAbsoluteCoordinateNumerator(
 7130                    projectedY,
 7131                    triangle.A.Y,
 7132                    wideProjectionDenominator),
 7133                GetAbsoluteCoordinateNumerator(
 7134                    projectedZ,
 7135                    triangle.A.Z,
 7136                    wideProjectionDenominator),
 7137                wideProjectionDenominator,
 7138                out _))
 139        {
 2140            triangleOriginOffset = default;
 2141            depth = default;
 2142            depthIsClamped = default;
 2143            return false;
 144        }
 145
 146        // A point contained by a triangle of representable local vertices is
 147        // representable in the same local chart.
 5148        _ = TryGetTriangleOriginOffsetCoordinate(
 5149            projectedX,
 5150            triangle.A.X,
 5151            triangleOrigin.X,
 5152            wideProjectionDenominator,
 5153            out Fixed64 triangleX);
 5154        _ = TryGetTriangleOriginOffsetCoordinate(
 5155            projectedY,
 5156            triangle.A.Y,
 5157            triangleOrigin.Y,
 5158            wideProjectionDenominator,
 5159            out Fixed64 triangleY);
 5160        _ = TryGetTriangleOriginOffsetCoordinate(
 5161            projectedZ,
 5162            triangle.A.Z,
 5163            triangleOrigin.Z,
 5164            wideProjectionDenominator,
 5165            out Fixed64 triangleZ);
 166
 5167        triangleOriginOffset = new Vector3d(triangleX, triangleY, triangleZ);
 5168        if (signedDistanceNumerator.Sign >= 0)
 169        {
 2170            depth = Fixed64.Zero;
 2171            depthIsClamped = false;
 2172            return true;
 173        }
 174
 3175        depth = WideArithmetic.GetRoundedNonNegativeNormalizedDepth(
 3176            Signed576.ExtendValue(
 3177                WideArithmetic.SubtractSigned320(
 3178                    default,
 3179                    signedDistanceNumerator)),
 3180            Signed576.ExtendValue(normalSquared),
 3181            Scale320,
 3182            out depthIsClamped);
 3183        return true;
 184    }
 185
 186    private static Signed192 GetSupportDifference(
 187        Fixed64 center,
 188        Fixed64 trianglePoint,
 189        Fixed64 centerOffset) =>
 30190        WideArithmetic.AddSigned192(
 30191            GetComponentDifference(center, trianglePoint),
 30192            Signed192.Signed(centerOffset.m_rawValue));
 193
 194    private static Signed576 GetProjectedCoordinateNumerator(
 195        Signed192 relative,
 196        Signed192 normal,
 197        Signed320 signedDistanceNumerator,
 198        Signed320 projectionDenominator) =>
 27199        WideArithmetic.SubtractSigned576(
 27200            WideArithmetic.MultiplySigned320(
 27201                Signed320.ExtendValue(relative),
 27202                projectionDenominator),
 27203            WideArithmetic.MultiplySigned320(
 27204                Signed320.ExtendValue(normal),
 27205                signedDistanceNumerator));
 206
 207    private static Signed576 GetAbsoluteCoordinateNumerator(
 208        Signed576 relativeToTriangleA,
 209        Fixed64 triangleA,
 210        Signed576 denominator) =>
 21211        WideArithmetic.AddSigned576(
 21212            relativeToTriangleA,
 21213            WideArithmetic.MultiplySigned576(
 21214                denominator,
 21215                Signed192.Signed(triangleA.m_rawValue)));
 216
 217    private static bool TryGetTriangleOriginOffsetCoordinate(
 218        Signed576 relativeToTriangleA,
 219        Fixed64 triangleA,
 220        Fixed64 triangleOrigin,
 221        Signed576 denominator,
 222        out Fixed64 coordinate)
 223    {
 15224        Signed192 originDifference =
 15225            GetComponentDifference(triangleA, triangleOrigin);
 15226        Signed576 numerator = WideArithmetic.AddSigned576(
 15227            relativeToTriangleA,
 15228            WideArithmetic.MultiplySigned576(denominator, originDifference));
 15229        return Fixed64.TryGetSignedRawRatio(
 15230            numerator,
 15231            denominator,
 15232            out coordinate);
 233    }
 234}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.ClippedAxisSegment.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.ClippedAxisSegment.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>Closest finite-axis witnesses on exact rational subsegments.</content>
 13internal static partial class WideFiniteAxisIntersection
 14{
 15    // Rigid coverage ratios use <268 bits (<269 after a narrow-family mediant).
 16    // Normalized-axis projection adds <70 bits: denominators use <339 bits,
 17    // coordinates/separations <405, and squared separations <810. Existing
 18    // Signed576/Signed832 candidates and finite-ray bounds therefore suffice.
 19    private static AxisTriangleCandidate GetClosestClippedAxisSegment(Vector3d first, Vector3d second,
 20        Vector3d center, Vector3d axis, Fixed64 length, in RationalSegmentInterval clip, out int endpoint)
 21    {
 1922        GetClosestCenteredAxisSegmentParameters(center, axis, length, first, second,
 1923            out Signed320 axisN, out Signed320 edgeN, out Signed320 d);
 3824        Signed320 lowerN = clip.LowerNumerator, lowerD = clip.LowerDenominator;
 3825        Signed320 upperN = clip.UpperNumerator, upperD = clip.UpperDenominator;
 1926        Signed192 aa = GetDot(axis, Vector3d.Zero, axis, Vector3d.Zero);
 1927        Signed192 ae = GetDot(axis, Vector3d.Zero, second, first);
 1928        Signed192 ee = GetDot(second, first, second, first);
 1929        Signed192 aw = GetDot(center, first, axis, Vector3d.Zero);
 1930        endpoint = 0;
 1931        if (length != Fixed64.Zero && !ee.IsZero && WideArithmetic.MultiplySubtract(aa, ee, ae, ae).IsZero)
 32        {
 33            // Intersect the whole parallel closest family before choosing an
 34            // interior witness; clamping its old midpoint would invent a corner.
 1035            Signed320 cap = WideArithmetic.MultiplySigned192(Signed192.Raw(length), aa);
 1036            Signed320 start = WideArithmetic.MultiplySigned192(DoubleParameterScale, aw);
 1037            Signed320 delta = WideArithmetic.MultiplySigned192(DoubleParameterScale, ae);
 1038            Signed320 f0 = WideArithmetic.AddSigned320(cap, start);
 1039            Signed320 f1 = WideArithmetic.SubtractSigned320(f0, delta);
 1040            Signed320 g0 = WideArithmetic.SubtractSigned320(cap, start);
 1041            Signed320 g1 = WideArithmetic.AddSigned320(g0, delta);
 1042            if (RationalSegmentInterval.TryClipHalfPlane(f0, f1, ref lowerN, ref lowerD, ref upperN, ref upperD)
 1043                && RationalSegmentInterval.TryClipHalfPlane(g0, g1, ref lowerN, ref lowerD, ref upperN, ref upperD)
 1044                && RationalSegmentInterval.CompareParameters(lowerN, lowerD, upperN, upperD) < 0)
 45            {
 1046                if (RationalSegmentInterval.CompareParameters(edgeN, d, lowerN, lowerD) <= 0
 1047                    || RationalSegmentInterval.CompareParameters(edgeN, d, upperN, upperD) >= 0)
 48                {
 949                    SelectInteriorParameter(lowerN, lowerD, upperN, upperD, out edgeN, out d);
 950                    return AtParameter(edgeN, d);
 51                }
 152                return NativeCandidate();
 53            }
 54        }
 955        int low = RationalSegmentInterval.CompareParameters(edgeN, d, clip.LowerNumerator, clip.LowerDenominator);
 956        int high = RationalSegmentInterval.CompareParameters(edgeN, d, clip.UpperNumerator, clip.UpperDenominator);
 1557        if (low <= 0) { endpoint = -1; return AtParameter(clip.LowerNumerator, clip.LowerDenominator); }
 858        if (high >= 0) { endpoint = 1; return AtParameter(clip.UpperNumerator, clip.UpperDenominator); }
 559        return NativeCandidate();
 60
 61        AxisTriangleCandidate NativeCandidate()
 62        {
 63            GetRationalPoint(center, axis, axisN, d, out Signed576 x, out Signed576 y, out Signed576 z);
 64            GetRationalSegmentPoint(first, second, edgeN, d, out Signed576 px, out Signed576 py, out Signed576 pz);
 65            return CreateAxisTriangleCandidate(px, py, pz, x, y, z, Signed576.ExtendValue(d), 7);
 66        }
 67
 68        AxisTriangleCandidate AtParameter(Signed320 n, Signed320 denominator)
 69        {
 70            Signed576 common = Signed576.ExtendValue(denominator), edge = Signed576.ExtendValue(n);
 71            Signed576 t = default;
 72            if (length != Fixed64.Zero)
 73            {
 74                t = WideArithmetic.SubtractSigned576(Multiply(n, ae), Multiply(denominator, aw));
 75                common = Multiply(denominator, aa);
 76                edge = Multiply(n, aa);
 77                Signed576 scaled = WideArithmetic.MultiplySigned576(t, DoubleParameterScale);
 78                Signed576 limit = WideArithmetic.MultiplySigned576(common, Signed192.Raw(length));
 79                if (WideArithmetic.SubtractSigned576(scaled, WideArithmetic.SubtractSigned576(default, limit)).Sign < 0
 80                    || WideArithmetic.SubtractSigned576(scaled, limit).Sign > 0)
 81                {
 82                    // Reproject the constrained point onto the finite core cap.
 83                    t = Multiply(denominator, scaled.Sign < 0 ? Signed192.Signed(-length.m_rawValue) : Signed192.Raw(len
 84                    common = Multiply(denominator, DoubleParameterScale);
 85                    edge = Multiply(n, DoubleParameterScale);
 86                }
 87            }
 88            return CreateAxisTriangleCandidate(
 89                Coordinate(first.X, GetComponentDifference(second.X, first.X), edge, common),
 90                Coordinate(first.Y, GetComponentDifference(second.Y, first.Y), edge, common),
 91                Coordinate(first.Z, GetComponentDifference(second.Z, first.Z), edge, common),
 92                Coordinate(center.X, Signed192.Raw(axis.X), t, common),
 93                Coordinate(center.Y, Signed192.Raw(axis.Y), t, common),
 94                Coordinate(center.Z, Signed192.Raw(axis.Z), t, common), common, 7);
 95        }
 96    }
 97
 98    private static Signed576 Multiply(Signed320 a, Signed192 b) =>
 6199        WideArithmetic.MultiplySigned320(a, Signed320.ExtendValue(b));
 100
 101    private static Signed576 Coordinate(Fixed64 origin, Signed192 direction, Signed576 n, Signed576 d) =>
 78102        WideArithmetic.AddSigned576(WideArithmetic.MultiplySigned576(d, Signed192.Raw(origin)),
 78103            WideArithmetic.MultiplySigned576(n, direction));
 104
 105    private static void SelectInteriorParameter(Signed320 ln, Signed320 ld, Signed320 un, Signed320 ud,
 106        out Signed320 n, out Signed320 d)
 107    {
 9108        Signed192 grid = new(1, 0, 0); // 2^128; parameter precision, not a geometry tolerance.
 9109        Signed192 lo = WideArithmetic.AddSigned192(WideArithmetic.DivideSigned576ToSigned192(Multiply(ln, grid), ld), Si
 9110        Signed192 hi = WideArithmetic.DivideSigned576ToSigned192(Multiply(un, grid), ud);
 9111        if (RationalSegmentInterval.CompareParameters(Signed320.ExtendValue(hi), Signed320.ExtendValue(grid), un, ud) ==
 1112            hi = WideArithmetic.SubtractSigned192(hi, Signed192.Signed(1));
 9113        if (WideArithmetic.SubtractSigned192(hi, lo).Sign >= 0)
 114        {
 115            // The exact midpoint of the first/last interior grid indices is
 116            // reversal symmetric and within 2^-128 of the true family midpoint.
 5117            n = Signed320.ExtendValue(WideArithmetic.AddSigned192(lo, hi));
 5118            d = Signed320.ExtendValue(new Signed192(2, 0, 0));
 5119            return;
 120        }
 121        // No grid point: the entire family is no wider than 2^-128. A reduced
 122        // mediant is strictly interior; its possible anchor shift is sub-raw
 123        // even for full-domain edges. Reduction removes representation weighting.
 8124        Span<Signed320> lower = stackalloc Signed320[2] { ln, ld }, upper = stackalloc Signed320[2] { un, ud };
 8125        WideArithmetic.ReduceCommonScale(lower); WideArithmetic.ReduceCommonScale(upper);
 4126        n = WideArithmetic.AddSigned320(lower[0], upper[0]);
 4127        d = WideArithmetic.AddSigned320(lower[1], upper[1]);
 4128    }
 129}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.Containment.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.Containment.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
 8namespace FixedMathSharp.Geometry;
 9
 10/// <content>
 11/// Provides high-precision containment tests for points against finite cylinder
 12/// and capsule shapes, using wide (multi-word) arithmetic to avoid overflow
 13/// and precision loss when checking axial and radial bounds.
 14/// </content>
 15internal static partial class WideFiniteAxisIntersection
 16{
 17    internal static bool AreSegmentEndpointDistancesAtLeast(
 18        FixedSegment2d first,
 19        FixedSegment2d second,
 20        Fixed64 minimumDistance)
 21    {
 56722        if (second.Start == second.End)
 23        {
 1624            FixedSegment2d temporary = first;
 1625            first = second;
 1626            second = temporary;
 27        }
 28
 56729        Signed192 squaredRadius = GetSquaredRadius(Signed192.Raw(minimumDistance));
 56730        bool firstSeparated = IsPointSegmentDistanceAtLeast(first.Start, second, squaredRadius);
 31        // For a point, this is the complete minimum distance. Distances to the
 32        // other segment's endpoints cannot be smaller, and repeating the point adds nothing.
 56733        if (!firstSeparated || first.Start == first.End)
 3534            return firstSeparated;
 35
 53236        return IsPointSegmentDistanceAtLeast(first.End, second, squaredRadius)
 53237            && IsPointSegmentDistanceAtLeast(second.Start, first, squaredRadius)
 53238            && IsPointSegmentDistanceAtLeast(second.End, first, squaredRadius);
 39    }
 40
 41    private static bool IsPointSegmentDistanceAtLeast(
 42        Vector2d point,
 43        FixedSegment2d segment,
 44        Signed192 squaredRadius) =>
 45        // Negating strict capsule containment keeps equality without rounding a distance.
 215546        !IsCapsulePointContained(
 215547            GetDot(point, segment.Start, point, segment.Start),
 215548            GetDot(point, segment.End, point, segment.End),
 215549            GetDot(point, segment.Start, segment.End, segment.Start),
 215550            GetDot(segment.End, segment.Start, segment.End, segment.Start),
 215551            squaredRadius,
 215552            strict: true);
 53
 54    internal static bool ContainsPointInCenteredFiniteCylinder(
 55        Vector3d point,
 56        Vector3d center,
 57        Vector3d axisDirection,
 58        Fixed64 axisLength,
 59        Fixed64 radius,
 60        bool strict)
 61    {
 1062        Signed192 axisLengthSquared = GetDot(
 1063            axisDirection,
 1064            Vector3d.Zero,
 1065            axisDirection,
 1066            Vector3d.Zero);
 1067        Signed192 distanceSquared = GetDot(point, center, point, center);
 1068        Signed192 axisProjection = GetDot(
 1069            point,
 1070            center,
 1071            axisDirection,
 1072            Vector3d.Zero);
 1073        Signed192 rawRadius = Signed192.Signed(radius.m_rawValue);
 1074        return IsCenteredFiniteCylinderPointContained(
 1075            distanceSquared,
 1076            axisProjection,
 1077            axisLengthSquared,
 1078            GetSquaredRadius(rawRadius),
 1079            GetCenteredAxialExtent(axisLengthSquared, axisLength, Fixed64.Zero),
 1080            strict);
 81    }
 82
 83    internal static bool ContainsPointInCenteredFiniteCylinder(
 84        Vector3d point,
 85        Vector3d center,
 86        Vector3d axisDirection,
 87        Fixed64 axisLength,
 88        Fixed64 radius,
 89        Fixed64 axialTolerance,
 90        Fixed64 radialTolerance,
 91        bool strict)
 92    {
 393        Signed192 axisLengthSquared = GetDot(
 394            axisDirection,
 395            Vector3d.Zero,
 396            axisDirection,
 397            Vector3d.Zero);
 398        Signed192 distanceSquared = GetDot(point, center, point, center);
 399        Signed192 axisProjection = GetDot(
 3100            point,
 3101            center,
 3102            axisDirection,
 3103            Vector3d.Zero);
 3104        Signed192 expandedRadius = GetExpandedRadius(radius, radialTolerance);
 3105        return IsCenteredFiniteCylinderPointContained(
 3106            distanceSquared,
 3107            axisProjection,
 3108            axisLengthSquared,
 3109            GetSquaredRadius(expandedRadius),
 3110            GetCenteredAxialExtent(axisLengthSquared, axisLength, axialTolerance),
 3111            strict);
 112    }
 113
 114    private static bool IsCapsulePointContained(
 115        Signed192 startDistanceSquared,
 116        Signed192 endDistanceSquared,
 117        Signed192 axisProjection,
 118        Signed192 axisLengthSquared,
 119        Signed192 squaredRadius,
 120        bool strict)
 121    {
 2207122        if (axisProjection.Sign <= 0)
 1073123            return IsWithinRadius(startDistanceSquared, squaredRadius, strict);
 1134124        if (WideArithmetic.SubtractSigned192(axisProjection, axisLengthSquared).Sign >= 0)
 1056125            return IsWithinRadius(endDistanceSquared, squaredRadius, strict);
 126
 78127        int radialSign = GetRadialConstant(
 78128            startDistanceSquared,
 78129            axisProjection,
 78130            axisLengthSquared,
 78131            squaredRadius).Sign;
 78132        return strict ? radialSign < 0 : radialSign <= 0;
 133    }
 134
 135    private static bool IsFiniteCylinderPointContained(
 136        Signed192 distanceSquared,
 137        Signed192 axisProjection,
 138        Signed192 axisLengthSquared,
 139        Signed192 squaredRadius,
 140        bool strict)
 141    {
 119142        int maximumProjectionSign = WideArithmetic.SubtractSigned192(
 119143            axisProjection,
 119144            axisLengthSquared).Sign;
 119145        if (strict
 119146                ? axisProjection.Sign <= 0 || maximumProjectionSign >= 0
 119147                : axisProjection.Sign < 0 || maximumProjectionSign > 0)
 148        {
 24149            return false;
 150        }
 151
 95152        int radialSign = GetRadialConstant(
 95153            distanceSquared,
 95154            axisProjection,
 95155            axisLengthSquared,
 95156            squaredRadius).Sign;
 95157        return strict ? radialSign < 0 : radialSign <= 0;
 158    }
 159
 160    private static bool IsCenteredFiniteCylinderPointContained(
 161        Signed192 distanceSquared,
 162        Signed192 axisProjection,
 163        Signed192 axisLengthSquared,
 164        Signed192 squaredRadius,
 165        Signed320 axialExtent,
 166        bool strict)
 167    {
 169168        Signed320 scaledProjection = WideArithmetic.MultiplySigned192(DoubleParameterScale, axisProjection);
 169169        Signed320 minimumProjection = WideArithmetic.SubtractSigned320(default, axialExtent);
 169170        int minimumSign = WideArithmetic.SubtractSigned320(scaledProjection, minimumProjection).Sign;
 169171        int maximumSign = WideArithmetic.SubtractSigned320(scaledProjection, axialExtent).Sign;
 169172        if (strict
 169173                ? minimumSign <= 0 || maximumSign >= 0
 169174                : minimumSign < 0 || maximumSign > 0)
 175        {
 102176            return false;
 177        }
 178
 67179        int radialSign = GetRadialConstant(
 67180            distanceSquared,
 67181            axisProjection,
 67182            axisLengthSquared,
 67183            squaredRadius).Sign;
 67184        return strict ? radialSign < 0 : radialSign <= 0;
 185    }
 186
 187    private static bool IsWithinRadius(
 188        Signed192 distanceSquared,
 189        Signed192 squaredRadius,
 190        bool strict)
 191    {
 42780192        int differenceSign = WideArithmetic.SubtractSigned192(distanceSquared, squaredRadius).Sign;
 42780193        return strict ? differenceSign < 0 : differenceSign <= 0;
 194    }
 195
 196    private static Signed320 GetRadialConstant(
 197        Signed192 distanceSquared,
 198        Signed192 axisProjection,
 199        Signed192 axisLengthSquared,
 200        Signed192 squaredRadius) =>
 855201        WideArithmetic.SubtractSigned320(
 855202            WideArithmetic.MultiplySubtract(
 855203                distanceSquared,
 855204                axisLengthSquared,
 855205                axisProjection,
 855206                axisProjection),
 855207            WideArithmetic.MultiplySigned192(squaredRadius, axisLengthSquared));
 208}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.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
 8namespace FixedMathSharp.Geometry;
 9
 10/// <summary>
 11/// Owns exact full-domain query-segment intersections with finite radial axes.
 12/// </summary>
 13internal static partial class WideFiniteAxisIntersection
 14{
 115    private static readonly Signed192 Scale = Signed192.Signed(1L);
 116    private static readonly Signed320 Scale320 = Signed320.ExtendValue(Scale);
 117    private static readonly Signed192 ParameterScale = Signed192.Signed(FixedMath.ONE_L);
 118    private static readonly Signed192 DoubleParameterScale = Signed192.Signed(FixedMath.ONE_L * 2L);
 19
 20    #region Nested Types
 21
 22    private readonly struct RationalBound
 23    {
 24        internal readonly Signed192 Numerator;
 25        internal readonly Signed192 Denominator;
 26
 27        internal RationalBound(Signed192 numerator, Signed192 denominator)
 28        {
 22629            Numerator = numerator;
 22630            Denominator = denominator;
 22631        }
 32    }
 33
 34    private readonly struct RationalBound320
 35    {
 36        internal readonly Signed320 Numerator;
 37        internal readonly Signed320 Denominator;
 38
 39        internal RationalBound320(Signed320 numerator, Signed320 denominator)
 40        {
 4249841            Numerator = numerator;
 4249842            Denominator = denominator;
 4249843        }
 44    }
 45
 46    #endregion
 47
 48    internal static bool TryGetCapsuleInterval(
 49        FixedSegment2d query,
 50        FixedSegment2d axis,
 51        Fixed64 radius,
 52        Fixed64 radiusExpansion,
 53        out Fixed64 entry,
 54        out Fixed64 exit,
 55        out bool startContained,
 56        out bool endContainedStrict)
 57    {
 53058        Signed192 expandedRadius = GetExpandedRadius(radius, radiusExpansion);
 53059        Signed192 squaredRadius = GetSquaredRadius(expandedRadius);
 53060        Signed192 axisLengthSquared = GetDot(axis.End, axis.Start, axis.End, axis.Start);
 53061        Signed192 startDistanceSquared = GetDot(query.Start, axis.Start, query.Start, axis.Start);
 53062        Signed192 endDistanceSquared = GetDot(query.End, axis.Start, query.End, axis.Start);
 53063        Signed192 startAxisProjection = GetDot(query.Start, axis.Start, axis.End, axis.Start);
 53064        Signed192 endAxisProjection = GetDot(query.End, axis.Start, axis.End, axis.Start);
 65
 53066        if (axisLengthSquared.IsZero)
 67        {
 51668            startContained = IsWithinRadius(startDistanceSquared, squaredRadius, strict: false);
 51669            endContainedStrict = IsWithinRadius(endDistanceSquared, squaredRadius, strict: true);
 51670            return TryGetCircleInterval(
 51671                query.Start,
 51672                query.End,
 51673                axis.Start,
 51674                expandedRadius,
 51675                out entry,
 51676                out exit);
 77        }
 78
 1479        startContained = IsCapsulePointContained(
 1480            startDistanceSquared,
 1481            GetDot(query.Start, axis.End, query.Start, axis.End),
 1482            startAxisProjection,
 1483            axisLengthSquared,
 1484            squaredRadius,
 1485            strict: false);
 1486        endContainedStrict = IsCapsulePointContained(
 1487            endDistanceSquared,
 1488            GetDot(query.End, axis.End, query.End, axis.End),
 1489            endAxisProjection,
 1490            axisLengthSquared,
 1491            squaredRadius,
 1492            strict: true);
 93        // A capsule is convex: these already-certified endpoints enclose the
 94        // complete query, so neither the finite-axis nor cap solves add evidence.
 1495        if (startContained && endContainedStrict)
 96        {
 797            entry = Fixed64.Zero;
 798            exit = Fixed64.One;
 799            return true;
 100        }
 101
 7102        Signed192 queryLengthSquared = GetDot(query.End, query.Start, query.End, query.Start);
 7103        Signed192 directionsDot = GetDot(query.End, query.Start, axis.End, axis.Start);
 7104        Signed192 startDirectionProjection = GetDot(query.Start, axis.Start, query.End, query.Start);
 7105        bool found = TryGetFiniteAxisInterval(
 7106            queryLengthSquared,
 7107            axisLengthSquared,
 7108            startDistanceSquared,
 7109            directionsDot,
 7110            startAxisProjection,
 7111            startDirectionProjection,
 7112            expandedRadius,
 7113            Fixed64.One,
 7114            out entry,
 7115            out exit);
 7116        Merge(
 7117            TryGetCircleInterval(query.Start, query.End, axis.Start, expandedRadius, out Fixed64 startEntry, out Fixed64
 7118            startEntry,
 7119            startExit,
 7120            ref found,
 7121            ref entry,
 7122            ref exit);
 7123        Merge(
 7124            TryGetCircleInterval(query.Start, query.End, axis.End, expandedRadius, out Fixed64 endEntry, out Fixed64 end
 7125            endEntry,
 7126            endExit,
 7127            ref found,
 7128            ref entry,
 7129            ref exit);
 7130        return found;
 131    }
 132
 133    internal static bool TryGetCapsuleInterval(
 134        FixedSegment query,
 135        FixedSegment axis,
 136        Fixed64 radius,
 137        Fixed64 radiusExpansion,
 138        out Fixed64 entry,
 139        out Fixed64 exit,
 140        out bool startContained,
 141        out bool endContainedStrict)
 142    {
 9143        Signed192 expandedRadius = GetExpandedRadius(radius, radiusExpansion);
 9144        Signed192 squaredRadius = GetSquaredRadius(expandedRadius);
 9145        Signed192 axisLengthSquared = GetDot(axis.End, axis.Start, axis.End, axis.Start);
 9146        Signed192 queryLengthSquared = GetDot(query.End, query.Start, query.End, query.Start);
 9147        Signed192 startDistanceSquared = GetDot(query.Start, axis.Start, query.Start, axis.Start);
 9148        Signed192 endDistanceSquared = GetDot(query.End, axis.Start, query.End, axis.Start);
 9149        Signed192 directionsDot = GetDot(query.End, query.Start, axis.End, axis.Start);
 9150        Signed192 startAxisProjection = GetDot(query.Start, axis.Start, axis.End, axis.Start);
 9151        Signed192 endAxisProjection = GetDot(query.End, axis.Start, axis.End, axis.Start);
 9152        Signed192 startDirectionProjection = GetDot(query.Start, axis.Start, query.End, query.Start);
 153
 9154        if (axisLengthSquared.IsZero)
 155        {
 3156            startContained = IsWithinRadius(startDistanceSquared, squaredRadius, strict: false);
 3157            endContainedStrict = IsWithinRadius(endDistanceSquared, squaredRadius, strict: true);
 3158            return TryGetSphereInterval(
 3159                query.Start,
 3160                query.End,
 3161                axis.Start,
 3162                expandedRadius,
 3163                out entry,
 3164                out exit);
 165        }
 166
 6167        startContained = IsCapsulePointContained(
 6168            startDistanceSquared,
 6169            GetDot(query.Start, axis.End, query.Start, axis.End),
 6170            startAxisProjection,
 6171            axisLengthSquared,
 6172            squaredRadius,
 6173            strict: false);
 6174        endContainedStrict = IsCapsulePointContained(
 6175            endDistanceSquared,
 6176            GetDot(query.End, axis.End, query.End, axis.End),
 6177            endAxisProjection,
 6178            axisLengthSquared,
 6179            squaredRadius,
 6180            strict: true);
 6181        bool found = TryGetFiniteAxisInterval(
 6182            queryLengthSquared,
 6183            axisLengthSquared,
 6184            startDistanceSquared,
 6185            directionsDot,
 6186            startAxisProjection,
 6187            startDirectionProjection,
 6188            expandedRadius,
 6189            Fixed64.One,
 6190            out entry,
 6191            out exit);
 6192        Merge(
 6193            TryGetSphereInterval(query.Start, query.End, axis.Start, expandedRadius, out Fixed64 startEntry, out Fixed64
 6194            startEntry,
 6195            startExit,
 6196            ref found,
 6197            ref entry,
 6198            ref exit);
 6199        Merge(
 6200            TryGetSphereInterval(query.Start, query.End, axis.End, expandedRadius, out Fixed64 endEntry, out Fixed64 end
 6201            endEntry,
 6202            endExit,
 6203            ref found,
 6204            ref entry,
 6205            ref exit);
 6206        return found;
 207    }
 208
 209    internal static bool TryGetFiniteCylinderInterval(
 210        FixedSegment query,
 211        FixedSegment axis,
 212        Fixed64 radius,
 213        Fixed64 radiusExpansion,
 214        out Fixed64 entry,
 215        out Fixed64 exit,
 216        out bool startContained,
 217        out bool endContainedStrict)
 218    {
 55219        Signed192 expandedRadius = GetExpandedRadius(radius, radiusExpansion);
 55220        Signed192 squaredRadius = GetSquaredRadius(expandedRadius);
 55221        Signed192 axisLengthSquared = GetDot(axis.End, axis.Start, axis.End, axis.Start);
 55222        Signed192 queryLengthSquared = GetDot(query.End, query.Start, query.End, query.Start);
 55223        Signed192 startDistanceSquared = GetDot(query.Start, axis.Start, query.Start, axis.Start);
 55224        Signed192 endDistanceSquared = GetDot(query.End, axis.Start, query.End, axis.Start);
 55225        Signed192 directionsDot = GetDot(query.End, query.Start, axis.End, axis.Start);
 55226        Signed192 startAxisProjection = GetDot(query.Start, axis.Start, axis.End, axis.Start);
 55227        Signed192 endAxisProjection = GetDot(query.End, axis.Start, axis.End, axis.Start);
 55228        Signed192 startDirectionProjection = GetDot(query.Start, axis.Start, query.End, query.Start);
 55229        startContained = IsFiniteCylinderPointContained(
 55230            startDistanceSquared,
 55231            startAxisProjection,
 55232            axisLengthSquared,
 55233            squaredRadius,
 55234            strict: false);
 55235        endContainedStrict = IsFiniteCylinderPointContained(
 55236            endDistanceSquared,
 55237            endAxisProjection,
 55238            axisLengthSquared,
 55239            squaredRadius,
 55240            strict: true);
 55241        return TryGetFiniteAxisInterval(
 55242            queryLengthSquared,
 55243            axisLengthSquared,
 55244            startDistanceSquared,
 55245            directionsDot,
 55246            startAxisProjection,
 55247            startDirectionProjection,
 55248            expandedRadius,
 55249            Fixed64.One,
 55250            out entry,
 55251            out exit);
 252    }
 253
 254    internal static bool TryGetFiniteCylinderInterval(
 255        FixedSegment query,
 256        Vector3d center,
 257        Vector3d axisDirection,
 258        Fixed64 axisLength,
 259        Fixed64 radius,
 260        Fixed64 radiusExpansion,
 261        Fixed64 axialExpansion,
 262        out Fixed64 entry,
 263        out Fixed64 exit,
 264        out bool startContained,
 265        out bool endContainedStrict)
 266    {
 24267        Signed192 queryLengthSquared = GetDot(query.End, query.Start, query.End, query.Start);
 24268        Signed192 axisLengthSquared = GetDot(axisDirection, Vector3d.Zero, axisDirection, Vector3d.Zero);
 24269        Signed192 startDistanceSquared = GetDot(query.Start, center, query.Start, center);
 24270        Signed192 endDistanceSquared = GetDot(query.End, center, query.End, center);
 24271        Signed192 directionsDot = GetDot(query.End, query.Start, axisDirection, Vector3d.Zero);
 24272        Signed192 startAxisProjection = GetDot(query.Start, center, axisDirection, Vector3d.Zero);
 24273        Signed192 endAxisProjection = GetDot(query.End, center, axisDirection, Vector3d.Zero);
 24274        Signed192 startDirectionProjection = GetDot(query.Start, center, query.End, query.Start);
 24275        Signed192 expandedRadius = GetExpandedRadius(radius, radiusExpansion);
 24276        Signed192 squaredRadius = GetSquaredRadius(expandedRadius);
 24277        Signed320 axialExtent = GetCenteredAxialExtent(
 24278            axisLengthSquared,
 24279            axisLength,
 24280            axialExpansion);
 281
 24282        startContained = IsCenteredFiniteCylinderPointContained(
 24283            startDistanceSquared,
 24284            startAxisProjection,
 24285            axisLengthSquared,
 24286            squaredRadius,
 24287            axialExtent,
 24288            strict: false);
 24289        endContainedStrict = IsCenteredFiniteCylinderPointContained(
 24290            endDistanceSquared,
 24291            endAxisProjection,
 24292            axisLengthSquared,
 24293            squaredRadius,
 24294            axialExtent,
 24295            strict: true);
 296
 24297        if (!TryGetCenteredAxialInterval(
 24298                startAxisProjection,
 24299                directionsDot,
 24300                axialExtent,
 24301                Fixed64.One,
 24302                out RationalBound320 lower,
 24303                out RationalBound320 upper))
 304        {
 6305            entry = default;
 6306            exit = default;
 6307            return false;
 308        }
 309
 18310        return TryGetFiniteAxisInterval(
 18311            queryLengthSquared,
 18312            axisLengthSquared,
 18313            startDistanceSquared,
 18314            directionsDot,
 18315            startAxisProjection,
 18316            startDirectionProjection,
 18317            expandedRadius,
 18318            lower,
 18319            upper,
 18320            Fixed64.One,
 18321            out entry,
 18322            out exit);
 323    }
 324
 325    private static bool TryGetFiniteAxisInterval(
 326        Signed192 queryLengthSquared,
 327        Signed192 axisLengthSquared,
 328        Signed192 startDistanceSquared,
 329        Signed192 directionsDot,
 330        Signed192 startAxisProjection,
 331        Signed192 startDirectionProjection,
 332        Signed192 radius,
 333        RationalBound320 lower,
 334        RationalBound320 upper,
 335        Fixed64 maxParameter,
 336        out Fixed64 entry,
 337        out Fixed64 exit)
 338    {
 171339        entry = default;
 171340        exit = default;
 341
 171342        Signed320 radialCoefficient = WideArithmetic.MultiplySubtract(
 171343            queryLengthSquared,
 171344            axisLengthSquared,
 171345            directionsDot,
 171346            directionsDot);
 171347        Signed320 radialProjection = WideArithmetic.MultiplySubtract(
 171348            startDirectionProjection,
 171349            axisLengthSquared,
 171350            startAxisProjection,
 171351            directionsDot);
 171352        Signed320 radialConstant = GetRadialConstant(
 171353            startDistanceSquared,
 171354            startAxisProjection,
 171355            axisLengthSquared,
 171356            GetSquaredRadius(radius));
 171357        return TrySolveBoundedQuadratic(
 171358            radialCoefficient,
 171359            radialProjection,
 171360            radialConstant,
 171361            lower,
 171362            upper,
 171363            maxParameter,
 171364            out entry,
 171365            out exit);
 366    }
 367
 368    private static bool TryGetFiniteAxisInterval(
 369        Signed192 queryLengthSquared,
 370        Signed192 axisLengthSquared,
 371        Signed192 startDistanceSquared,
 372        Signed192 directionsDot,
 373        Signed192 startAxisProjection,
 374        Signed192 startDirectionProjection,
 375        Signed192 radius,
 376        Fixed64 maxParameter,
 377        out Fixed64 entry,
 378        out Fixed64 exit)
 379    {
 79380        entry = default;
 79381        exit = default;
 382
 79383        if (!TryGetAxialInterval(
 79384                startAxisProjection,
 79385                directionsDot,
 79386                axisLengthSquared,
 79387                maxParameter,
 79388                out RationalBound lower,
 79389                out RationalBound upper))
 390        {
 9391            return false;
 392        }
 393
 70394        Signed320 radialCoefficient = WideArithmetic.MultiplySubtract(
 70395            queryLengthSquared,
 70396            axisLengthSquared,
 70397            directionsDot,
 70398            directionsDot);
 70399        Signed320 radialProjection = WideArithmetic.MultiplySubtract(
 70400            startDirectionProjection,
 70401            axisLengthSquared,
 70402            startAxisProjection,
 70403            directionsDot);
 70404        Signed320 radialConstant = GetRadialConstant(
 70405            startDistanceSquared,
 70406            startAxisProjection,
 70407            axisLengthSquared,
 70408            GetSquaredRadius(radius));
 409
 70410        if (radialCoefficient.IsZero)
 411        {
 13412            if (radialConstant.Sign > 0)
 2413                return false;
 414
 11415            entry = Round(lower);
 11416            exit = Round(upper);
 11417            return true;
 418        }
 419
 57420        Signed576 lowerValue = EvaluatePolynomial(
 57421            radialCoefficient,
 57422            radialProjection,
 57423            radialConstant,
 57424            lower.Numerator,
 57425            lower.Denominator);
 57426        Signed576 upperValue = EvaluatePolynomial(
 57427            radialCoefficient,
 57428            radialProjection,
 57429            radialConstant,
 57430            upper.Numerator,
 57431            upper.Denominator);
 57432        int lowerDerivative = EvaluateDerivative(
 57433            radialCoefficient,
 57434            radialProjection,
 57435            lower.Numerator,
 57436            lower.Denominator).Sign;
 57437        int upperDerivative = EvaluateDerivative(
 57438            radialCoefficient,
 57439            radialProjection,
 57440            upper.Numerator,
 57441            upper.Denominator).Sign;
 442
 57443        if ((lowerValue.Sign > 0 && lowerDerivative >= 0)
 57444            || (upperValue.Sign > 0 && upperDerivative <= 0))
 445        {
 2446            return false;
 447        }
 448
 55449        if (lowerValue.Sign <= 0 && upperValue.Sign <= 0)
 450        {
 3451            entry = Round(lower);
 3452            exit = Round(upper);
 3453            return true;
 454        }
 455
 52456        if (TryGetNarrowRadialCoefficients(
 52457                radialCoefficient,
 52458                radialProjection,
 52459                radialConstant,
 52460                out Signed192 narrowCoefficient,
 52461                out Signed192 narrowProjection,
 52462                out Signed192 narrowConstant))
 463        {
 32464            if (!WideRayIntersection.TrySolveInterval(
 32465                    narrowCoefficient,
 32466                    narrowProjection,
 32467                    narrowConstant,
 32468                    maxParameter,
 32469                    out Fixed64 radialEntry,
 32470                    out Fixed64 radialExit))
 471            {
 1472                return false;
 473            }
 474
 31475            entry = lowerValue.Sign <= 0 ? Round(lower) : radialEntry;
 31476            exit = upperValue.Sign <= 0 ? Round(upper) : radialExit;
 31477            return true;
 478        }
 479
 20480        Signed576 discriminant = WideArithmetic.SubtractSigned576(
 20481            WideArithmetic.MultiplySigned320(radialProjection, radialProjection),
 20482            WideArithmetic.MultiplySigned320(radialCoefficient, radialConstant));
 20483        if (discriminant.Sign < 0)
 1484            return false;
 485
 19486        Signed320 scaledSquareRoot = WideArithmetic.GetFloorSquareRootScaledByFixed64(discriminant);
 487
 19488        entry = lowerValue.Sign <= 0
 19489            ? Round(lower)
 19490            : RoundLowerRoot(radialCoefficient, radialProjection, radialConstant, scaledSquareRoot);
 19491        exit = upperValue.Sign <= 0
 19492            ? Round(upper)
 19493            : RoundUpperRoot(radialCoefficient, radialProjection, radialConstant, scaledSquareRoot, maxParameter);
 19494        return true;
 495    }
 496
 497    private static bool TryGetAxialInterval(
 498        Signed192 projection,
 499        Signed192 velocity,
 500        Signed192 axisLengthSquared,
 501        Fixed64 maxParameter,
 502        out RationalBound lower,
 503        out RationalBound upper)
 504    {
 84505        RationalBound maximum = GetMaximumBound(maxParameter);
 84506        lower = new RationalBound(default, Scale);
 84507        upper = maximum;
 508
 84509        if (velocity.IsZero)
 510        {
 55511            return projection.Sign >= 0
 55512                && WideArithmetic.SubtractSigned192(projection, axisLengthSquared).Sign <= 0;
 513        }
 514
 29515        RationalBound first = Normalize(
 29516            WideArithmetic.SubtractSigned192(default, projection),
 29517            velocity);
 29518        RationalBound second = Normalize(
 29519            WideArithmetic.SubtractSigned192(axisLengthSquared, projection),
 29520            velocity);
 29521        if (Compare(first, second) > 0)
 4522            (first, second) = (second, first);
 523
 29524        if (second.Numerator.Sign < 0 || Compare(first, maximum) > 0)
 6525            return false;
 526
 23527        if (first.Numerator.Sign > 0)
 14528            lower = first;
 23529        if (Compare(second, maximum) < 0)
 11530            upper = second;
 23531        return true;
 532    }
 533
 534    private static bool TryGetCenteredAxialInterval(
 535        Signed192 projection,
 536        Signed192 velocity,
 537        Signed320 axialExtent,
 538        Fixed64 maxParameter,
 539        out RationalBound320 lower,
 540        out RationalBound320 upper)
 541    {
 261542        Signed320 scaledProjection = WideArithmetic.MultiplySigned192(DoubleParameterScale, projection);
 261543        Signed320 scaledVelocity = WideArithmetic.MultiplySigned192(DoubleParameterScale, velocity);
 261544        Signed320 minimumProjection = WideArithmetic.SubtractSigned320(default, axialExtent);
 261545        RationalBound320 maximum = GetMaximumBound320(maxParameter);
 261546        lower = new RationalBound320(default, Signed320.ExtendValue(Scale));
 261547        upper = maximum;
 548
 261549        if (scaledVelocity.IsZero)
 550        {
 76551            return WideArithmetic.SubtractSigned320(scaledProjection, minimumProjection).Sign >= 0
 76552                && WideArithmetic.SubtractSigned320(scaledProjection, axialExtent).Sign <= 0;
 553        }
 554
 185555        RationalBound320 first = Normalize(
 185556            WideArithmetic.SubtractSigned320(minimumProjection, scaledProjection),
 185557            scaledVelocity);
 185558        RationalBound320 second = Normalize(
 185559            WideArithmetic.SubtractSigned320(axialExtent, scaledProjection),
 185560            scaledVelocity);
 185561        if (Compare(first, second) > 0)
 19562            (first, second) = (second, first);
 563
 185564        if (second.Numerator.Sign < 0 || Compare(first, maximum) > 0)
 9565            return false;
 566
 176567        if (first.Numerator.Sign > 0)
 36568            lower = first;
 176569        if (Compare(second, maximum) < 0)
 30570            upper = second;
 176571        return true;
 572    }
 573
 574    private static Signed320 GetCenteredAxialExtent(
 575        Signed192 axisLengthSquared,
 576        Fixed64 axisLength,
 577        Fixed64 axialExpansion) =>
 303578        GetCenteredAxialExtent(
 303579            axisLengthSquared,
 303580            Signed192.Signed(axisLength.m_rawValue),
 303581            axialExpansion);
 582
 583    private static Signed320 GetCenteredAxialExtent(
 584        Signed192 axisLengthSquared,
 585        Signed192 axisLength,
 586        Fixed64 axialExpansion) =>
 356587        WideArithmetic.AddSigned320(
 356588            WideArithmetic.MultiplySigned192(
 356589                axisLength,
 356590                axisLengthSquared),
 356591            WideArithmetic.MultiplySigned192(
 356592                WideArithmetic.AddSigned192(
 356593                    Signed192.Signed(axialExpansion.m_rawValue),
 356594                    Signed192.Signed(axialExpansion.m_rawValue)),
 356595                axisLengthSquared));
 596
 597    private static RationalBound Normalize(Signed192 numerator, Signed192 denominator)
 598    {
 58599        if (denominator.Sign >= 0)
 50600            return new RationalBound(numerator, denominator);
 601
 8602        return new RationalBound(
 8603            WideArithmetic.SubtractSigned192(default, numerator),
 8604            WideArithmetic.SubtractSigned192(default, denominator));
 605    }
 606
 607    private static RationalBound320 Normalize(Signed320 numerator, Signed320 denominator)
 608    {
 650609        if (denominator.Sign >= 0)
 360610            return new RationalBound320(numerator, denominator);
 611
 290612        return new RationalBound320(
 290613            WideArithmetic.SubtractSigned320(default, numerator),
 290614            WideArithmetic.SubtractSigned320(default, denominator));
 615    }
 616
 617    private static int Compare(RationalBound left, RationalBound right) =>
 80618        WideArithmetic.MultiplySubtract(
 80619            left.Numerator,
 80620            right.Denominator,
 80621            right.Numerator,
 80622            left.Denominator).Sign;
 623
 624    private static int Compare(RationalBound320 left, RationalBound320 right) =>
 820625        WideArithmetic.SubtractSigned576(
 820626            WideArithmetic.MultiplySigned320(left.Numerator, right.Denominator),
 820627            WideArithmetic.MultiplySigned320(right.Numerator, left.Denominator)).Sign;
 628
 629    private static RationalBound GetMaximumBound(Fixed64 maxParameter) =>
 84630        new(Signed192.Signed(maxParameter.m_rawValue), ParameterScale);
 631
 632    private static RationalBound320 GetMaximumBound320(Fixed64 maxParameter) =>
 279633        new(
 279634            Signed320.ExtendValue(
 279635                Signed192.Signed(maxParameter.m_rawValue)),
 279636            Signed320.ExtendValue(ParameterScale));
 637
 638    private static Fixed64 Round(RationalBound value) =>
 47639        Fixed64.GetSignedRatio(value.Numerator, value.Denominator);
 640
 641    private static Fixed64 Round(RationalBound320 value) =>
 33642        Fixed64.GetSignedRatio(value.Numerator, value.Denominator);
 643
 644    private static bool TryGetCircleInterval(
 645        Vector2d start,
 646        Vector2d end,
 647        Vector2d center,
 648        Signed192 radius,
 649        out Fixed64 entry,
 650        out Fixed64 exit)
 651    {
 530652        Signed192 directionLengthSquared = GetDot(end, start, end, start);
 530653        Signed192 projection = GetDot(start, center, end, start);
 530654        Signed192 distanceSquared = GetDot(start, center, start, center);
 530655        return WideRayIntersection.TrySolveInterval(
 530656            directionLengthSquared,
 530657            projection,
 530658            WideArithmetic.SubtractSigned192(distanceSquared, GetSquaredRadius(radius)),
 530659            Fixed64.One,
 530660            out entry,
 530661            out exit);
 662    }
 663
 664    private static bool TryGetSphereInterval(
 665        Vector3d start,
 666        Vector3d end,
 667        Vector3d center,
 668        Signed192 radius,
 669        out Fixed64 entry,
 670        out Fixed64 exit)
 671    {
 15672        Signed192 directionLengthSquared = GetDot(end, start, end, start);
 15673        Signed192 projection = GetDot(start, center, end, start);
 15674        Signed192 distanceSquared = GetDot(start, center, start, center);
 15675        return WideRayIntersection.TrySolveInterval(
 15676            directionLengthSquared,
 15677            projection,
 15678            WideArithmetic.SubtractSigned192(distanceSquared, GetSquaredRadius(radius)),
 15679            Fixed64.One,
 15680            out entry,
 15681            out exit);
 682    }
 683
 684    private static Signed192 GetExpandedRadius(Fixed64 radius, Fixed64 radiusExpansion) =>
 20716685        WideArithmetic.AddSigned192(
 20716686            Signed192.Signed(radius.m_rawValue),
 20716687            Signed192.Signed(radiusExpansion.m_rawValue));
 688
 689    private static Signed192 GetSquaredRadius(Signed192 radius)
 690    {
 42165691        WideArithmetic.GetMagnitude(radius, out _, out _, out ulong rawRadius);
 42165692        Fixed64.Multiply64To128(rawRadius, rawRadius, out ulong middle, out ulong low);
 42165693        return new Signed192(0UL, middle, low);
 694    }
 695
 696    private static Signed192 GetDot(
 697        Vector2d leftEnd,
 698        Vector2d leftStart,
 699        Vector2d rightEnd,
 700        Vector2d rightStart) =>
 255323701        WideGeometry.GetDifferenceDotProduct2D(
 255323702            leftEnd.X,
 255323703            leftStart.X,
 255323704            leftEnd.Y,
 255323705            leftStart.Y,
 255323706            rightEnd.X,
 255323707            rightStart.X,
 255323708            rightEnd.Y,
 255323709            rightStart.Y);
 710
 711    private static Signed192 GetDot(
 712        Vector3d leftEnd,
 713        Vector3d leftStart,
 714        Vector3d rightEnd,
 715        Vector3d rightStart) =>
 23161716        WideGeometry.GetDifferenceDotProduct3D(
 23161717            leftEnd.X,
 23161718            leftStart.X,
 23161719            leftEnd.Y,
 23161720            leftStart.Y,
 23161721            leftEnd.Z,
 23161722            leftStart.Z,
 23161723            rightEnd.X,
 23161724            rightStart.X,
 23161725            rightEnd.Y,
 23161726            rightStart.Y,
 23161727            rightEnd.Z,
 23161728            rightStart.Z);
 729
 730    private static void Merge(
 731        bool candidateFound,
 732        Fixed64 candidateEntry,
 733        Fixed64 candidateExit,
 734        ref bool found,
 735        ref Fixed64 entry,
 736        ref Fixed64 exit)
 737    {
 542738        if (!candidateFound)
 379739            return;
 740
 163741        if (!found)
 742        {
 18743            found = true;
 18744            entry = candidateEntry;
 18745            exit = candidateExit;
 18746            return;
 747        }
 748
 145749        if (candidateEntry < entry)
 22750            entry = candidateEntry;
 145751        if (candidateExit > exit)
 13752            exit = candidateExit;
 145753    }
 754
 755    private static bool TrySolveBoundedQuadratic(
 756        Signed320 coefficient,
 757        Signed320 projection,
 758        Signed320 constant,
 759        RationalBound320 lower,
 760        RationalBound320 upper,
 761        Fixed64 maxParameter,
 762        out Fixed64 entry,
 763        out Fixed64 exit)
 764    {
 487765        entry = default;
 487766        exit = default;
 767
 487768        if (coefficient.IsZero)
 769        {
 20770            if (constant.Sign > 0)
 9771                return false;
 772
 11773            entry = Round(lower);
 11774            exit = Round(upper);
 11775            return true;
 776        }
 777
 467778        Signed704 lowerValue = EvaluatePolynomial(
 467779            coefficient,
 467780            projection,
 467781            constant,
 467782            lower.Numerator,
 467783            lower.Denominator);
 467784        Signed704 upperValue = EvaluatePolynomial(
 467785            coefficient,
 467786            projection,
 467787            constant,
 467788            upper.Numerator,
 467789            upper.Denominator);
 467790        int lowerDerivative = EvaluateDerivative(
 467791            coefficient,
 467792            projection,
 467793            lower.Numerator,
 467794            lower.Denominator).Sign;
 467795        int upperDerivative = EvaluateDerivative(
 467796            coefficient,
 467797            projection,
 467798            upper.Numerator,
 467799            upper.Denominator).Sign;
 800
 467801        if ((lowerValue.Sign > 0 && lowerDerivative >= 0)
 467802            || (upperValue.Sign > 0 && upperDerivative <= 0))
 803        {
 16804            return false;
 805        }
 806
 451807        if (lowerValue.Sign <= 0 && upperValue.Sign <= 0)
 808        {
 2809            entry = Round(lower);
 2810            exit = Round(upper);
 2811            return true;
 812        }
 813
 449814        if (TryGetNarrowRadialCoefficients(
 449815                coefficient,
 449816                projection,
 449817                constant,
 449818                out Signed192 narrowCoefficient,
 449819                out Signed192 narrowProjection,
 449820                out Signed192 narrowConstant))
 821        {
 316822            if (!WideRayIntersection.TrySolveInterval(
 316823                    narrowCoefficient,
 316824                    narrowProjection,
 316825                    narrowConstant,
 316826                    maxParameter,
 316827                    out Fixed64 radialEntry,
 316828                    out Fixed64 radialExit))
 829            {
 272830                return false;
 831            }
 832
 44833            entry = lowerValue.Sign <= 0 ? Round(lower) : radialEntry;
 44834            exit = upperValue.Sign <= 0 ? Round(upper) : radialExit;
 44835            return true;
 836        }
 837
 133838        Signed576 discriminant = WideArithmetic.SubtractSigned576(
 133839            WideArithmetic.MultiplySigned320(projection, projection),
 133840            WideArithmetic.MultiplySigned320(coefficient, constant));
 133841        if (discriminant.Sign < 0)
 1842            return false;
 843
 132844        Signed320 scaledSquareRoot = WideArithmetic.GetFloorSquareRootScaledByFixed64(discriminant);
 132845        entry = lowerValue.Sign <= 0
 132846            ? Round(lower)
 132847            : RoundLowerRoot(coefficient, projection, constant, scaledSquareRoot);
 132848        exit = upperValue.Sign <= 0
 132849            ? Round(upper)
 132850            : RoundUpperRoot(coefficient, projection, constant, scaledSquareRoot, maxParameter);
 132851        return true;
 852    }
 853}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.Distance.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.Distance.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
 8namespace FixedMathSharp.Geometry;
 9
 10/// <content>
 11/// Physical-distance intervals for radial and finite-axis intersection tests.
 12/// </content>
 13internal static partial class WideFiniteAxisIntersection
 14{
 15    internal static bool TryGetCircleDirectionDistanceInterval(
 16        Vector2d position,
 17        Vector2d direction,
 18        FixedBoundCircle circle,
 19        Fixed64 radiusExpansion,
 20        Fixed64 totalDistance,
 21        out Fixed64 entryDistance,
 22        out Fixed64 exitDistance)
 23    {
 224        Signed192 expandedRadius = GetExpandedRadius(circle.Radius, radiusExpansion);
 225        return TrySolveUnitQuadraticAtDistance(
 226            Signed320.ExtendValue(GetDirectionDot(direction, direction)),
 227            Signed320.ExtendValue(GetDirectionDot(direction, position, circle.Center)),
 228            Signed320.ExtendValue(WideArithmetic.SubtractSigned192(
 229                GetDot(position, circle.Center, position, circle.Center),
 230                GetSquaredRadius(expandedRadius))),
 231            totalDistance,
 232            out entryDistance,
 233            out exitDistance);
 34    }
 35
 36    internal static bool TryGetSphereDirectionDistanceInterval(
 37        Vector3d position,
 38        Vector3d direction,
 39        FixedBoundSphere sphere,
 40        Fixed64 radiusExpansion,
 41        Fixed64 totalDistance,
 42        out Fixed64 entryDistance,
 43        out Fixed64 exitDistance)
 44    {
 245        Signed192 expandedRadius = GetExpandedRadius(sphere.Radius, radiusExpansion);
 246        return TrySolveUnitQuadraticAtDistance(
 247            Signed320.ExtendValue(GetDirectionDot(direction, direction)),
 248            Signed320.ExtendValue(GetDirectionDot(direction, position, sphere.Center)),
 249            Signed320.ExtendValue(WideArithmetic.SubtractSigned192(
 250                GetDot(position, sphere.Center, position, sphere.Center),
 251                GetSquaredRadius(expandedRadius))),
 252            totalDistance,
 253            out entryDistance,
 254            out exitDistance);
 55    }
 56
 57    internal static bool TryGetCircleDistanceInterval(
 58        FixedSegment2d query,
 59        FixedBoundCircle circle,
 60        Fixed64 radiusExpansion,
 61        Fixed64 segmentLength,
 62        out Fixed64 entry,
 63        out Fixed64 exit,
 64        out bool startContained,
 65        out bool endContainedStrict)
 66    {
 1967        Signed192 expandedRadius = GetExpandedRadius(circle.Radius, radiusExpansion);
 1968        Signed192 squaredRadius = GetSquaredRadius(expandedRadius);
 1969        startContained = IsWithinRadius(
 1970            GetDot(query.Start, circle.Center, query.Start, circle.Center),
 1971            squaredRadius,
 1972            strict: false);
 1973        endContainedStrict = IsWithinRadius(
 1974            GetDot(query.End, circle.Center, query.End, circle.Center),
 1975            squaredRadius,
 1976            strict: true);
 1977        return TryGetCircleDistanceInterval(
 1978            query.Start,
 1979            query.End,
 1980            circle.Center,
 1981            expandedRadius,
 1982            segmentLength,
 1983            out entry,
 1984            out exit);
 85    }
 86
 87    internal static bool TryGetSphereDistanceInterval(
 88        FixedSegment query,
 89        FixedBoundSphere sphere,
 90        Fixed64 radiusExpansion,
 91        Fixed64 segmentLength,
 92        out Fixed64 entry,
 93        out Fixed64 exit,
 94        out bool startContained,
 95        out bool endContainedStrict)
 96    {
 1997        Signed192 expandedRadius = GetExpandedRadius(sphere.Radius, radiusExpansion);
 1998        Signed192 squaredRadius = GetSquaredRadius(expandedRadius);
 1999        startContained = IsWithinRadius(
 19100            GetDot(query.Start, sphere.Center, query.Start, sphere.Center),
 19101            squaredRadius,
 19102            strict: false);
 19103        endContainedStrict = IsWithinRadius(
 19104            GetDot(query.End, sphere.Center, query.End, sphere.Center),
 19105            squaredRadius,
 19106            strict: true);
 19107        return TryGetSphereDistanceInterval(
 19108            query.Start,
 19109            query.End,
 19110            sphere.Center,
 19111            expandedRadius,
 19112            segmentLength,
 19113            out entry,
 19114            out exit);
 115    }
 116
 117    internal static bool TryGetCapsuleDistanceInterval(
 118        FixedSegment2d query,
 119        FixedSegment2d axis,
 120        Fixed64 radius,
 121        Fixed64 radiusExpansion,
 122        Fixed64 segmentLength,
 123        out Fixed64 entry,
 124        out Fixed64 exit,
 125        out bool startContained,
 126        out bool endContainedStrict)
 127    {
 19765128        Signed192 expandedRadius = GetExpandedRadius(radius, radiusExpansion);
 19765129        Signed192 squaredRadius = GetSquaredRadius(expandedRadius);
 19765130        Signed192 axisLengthSquared = GetDot(axis.End, axis.Start, axis.End, axis.Start);
 19765131        Signed192 queryLengthSquared = GetDot(query.End, query.Start, query.End, query.Start);
 19765132        Signed192 startDistanceSquared = GetDot(query.Start, axis.Start, query.Start, axis.Start);
 19765133        Signed192 endDistanceSquared = GetDot(query.End, axis.Start, query.End, axis.Start);
 19765134        Signed192 directionsDot = GetDot(query.End, query.Start, axis.End, axis.Start);
 19765135        Signed192 startAxisProjection = GetDot(query.Start, axis.Start, axis.End, axis.Start);
 19765136        Signed192 startDirectionProjection = GetDot(query.Start, axis.Start, query.End, query.Start);
 137
 19765138        if (axisLengthSquared.IsZero)
 139        {
 19764140            startContained = IsWithinRadius(startDistanceSquared, squaredRadius, strict: false);
 19764141            endContainedStrict = IsWithinRadius(endDistanceSquared, squaredRadius, strict: true);
 19764142            return TryGetCircleDistanceInterval(
 19764143                query.Start,
 19764144                query.End,
 19764145                axis.Start,
 19764146                expandedRadius,
 19764147                segmentLength,
 19764148                out entry,
 19764149                out exit);
 150        }
 151
 1152        startContained = IsCapsulePointContained(
 1153            startDistanceSquared,
 1154            GetDot(query.Start, axis.End, query.Start, axis.End),
 1155            startAxisProjection,
 1156            axisLengthSquared,
 1157            squaredRadius,
 1158            strict: false);
 1159        endContainedStrict = IsCapsulePointContained(
 1160            endDistanceSquared,
 1161            GetDot(query.End, axis.End, query.End, axis.End),
 1162            GetDot(query.End, axis.Start, axis.End, axis.Start),
 1163            axisLengthSquared,
 1164            squaredRadius,
 1165            strict: true);
 1166        bool found = TryGetFiniteAxisDistanceInterval(
 1167            queryLengthSquared,
 1168            axisLengthSquared,
 1169            startDistanceSquared,
 1170            directionsDot,
 1171            startAxisProjection,
 1172            startDirectionProjection,
 1173            expandedRadius,
 1174            segmentLength,
 1175            out entry,
 1176            out exit);
 1177        Merge(
 1178            TryGetCircleDistanceInterval(query.Start, query.End, axis.Start, expandedRadius, segmentLength, out Fixed64 
 1179            startEntry,
 1180            startExit,
 1181            ref found,
 1182            ref entry,
 1183            ref exit);
 1184        Merge(
 1185            TryGetCircleDistanceInterval(query.Start, query.End, axis.End, expandedRadius, segmentLength, out Fixed64 en
 1186            endEntry,
 1187            endExit,
 1188            ref found,
 1189            ref entry,
 1190            ref exit);
 1191        return found;
 192    }
 193
 194    internal static bool TryGetCapsuleDistanceInterval(
 195        FixedSegment query,
 196        FixedSegment axis,
 197        Fixed64 radius,
 198        Fixed64 radiusExpansion,
 199        Fixed64 segmentLength,
 200        out Fixed64 entry,
 201        out Fixed64 exit,
 202        out bool startContained,
 203        out bool endContainedStrict)
 204    {
 2205        Signed192 expandedRadius = GetExpandedRadius(radius, radiusExpansion);
 2206        Signed192 squaredRadius = GetSquaredRadius(expandedRadius);
 2207        Signed192 axisLengthSquared = GetDot(axis.End, axis.Start, axis.End, axis.Start);
 2208        Signed192 queryLengthSquared = GetDot(query.End, query.Start, query.End, query.Start);
 2209        Signed192 startDistanceSquared = GetDot(query.Start, axis.Start, query.Start, axis.Start);
 2210        Signed192 endDistanceSquared = GetDot(query.End, axis.Start, query.End, axis.Start);
 2211        Signed192 directionsDot = GetDot(query.End, query.Start, axis.End, axis.Start);
 2212        Signed192 startAxisProjection = GetDot(query.Start, axis.Start, axis.End, axis.Start);
 2213        Signed192 startDirectionProjection = GetDot(query.Start, axis.Start, query.End, query.Start);
 214
 2215        if (axisLengthSquared.IsZero)
 216        {
 1217            startContained = IsWithinRadius(startDistanceSquared, squaredRadius, strict: false);
 1218            endContainedStrict = IsWithinRadius(endDistanceSquared, squaredRadius, strict: true);
 1219            return TryGetSphereDistanceInterval(
 1220                query.Start,
 1221                query.End,
 1222                axis.Start,
 1223                expandedRadius,
 1224                segmentLength,
 1225                out entry,
 1226                out exit);
 227        }
 228
 1229        startContained = IsCapsulePointContained(
 1230            startDistanceSquared,
 1231            GetDot(query.Start, axis.End, query.Start, axis.End),
 1232            startAxisProjection,
 1233            axisLengthSquared,
 1234            squaredRadius,
 1235            strict: false);
 1236        endContainedStrict = IsCapsulePointContained(
 1237            endDistanceSquared,
 1238            GetDot(query.End, axis.End, query.End, axis.End),
 1239            GetDot(query.End, axis.Start, axis.End, axis.Start),
 1240            axisLengthSquared,
 1241            squaredRadius,
 1242            strict: true);
 1243        bool found = TryGetFiniteAxisDistanceInterval(
 1244            queryLengthSquared,
 1245            axisLengthSquared,
 1246            startDistanceSquared,
 1247            directionsDot,
 1248            startAxisProjection,
 1249            startDirectionProjection,
 1250            expandedRadius,
 1251            segmentLength,
 1252            out entry,
 1253            out exit);
 1254        Merge(
 1255            TryGetSphereDistanceInterval(query.Start, query.End, axis.Start, expandedRadius, segmentLength, out Fixed64 
 1256            startEntry,
 1257            startExit,
 1258            ref found,
 1259            ref entry,
 1260            ref exit);
 1261        Merge(
 1262            TryGetSphereDistanceInterval(query.Start, query.End, axis.End, expandedRadius, segmentLength, out Fixed64 en
 1263            endEntry,
 1264            endExit,
 1265            ref found,
 1266            ref entry,
 1267            ref exit);
 1268        return found;
 269    }
 270
 271    internal static bool TryGetCapsuleDistanceInterval(
 272        FixedSegment2d query,
 273        Vector2d center,
 274        Vector2d axisDirection,
 275        Fixed64 axisLength,
 276        Fixed64 radius,
 277        Fixed64 radiusExpansion,
 278        Fixed64 segmentLength,
 279        out Fixed64 entry,
 280        out Fixed64 exit,
 281        out bool startContained,
 282        out bool endContainedStrict)
 283    {
 5284        Signed192 expandedRadius = GetExpandedRadius(radius, radiusExpansion);
 5285        Signed192 squaredRadius = GetSquaredRadius(expandedRadius);
 5286        Signed192 axisLengthSquared = GetDot(axisDirection, Vector2d.Zero, axisDirection, Vector2d.Zero);
 5287        Signed192 queryLengthSquared = GetDot(query.End, query.Start, query.End, query.Start);
 5288        Signed192 startDistanceSquared = GetDot(query.Start, center, query.Start, center);
 5289        Signed192 directionsDot = GetDot(query.End, query.Start, axisDirection, Vector2d.Zero);
 5290        Signed192 startAxisProjection = GetDot(query.Start, center, axisDirection, Vector2d.Zero);
 5291        Signed192 endAxisProjection = GetDot(query.End, center, axisDirection, Vector2d.Zero);
 5292        Signed192 startDirectionProjection = GetDot(query.Start, center, query.End, query.Start);
 5293        Signed320 axialExtent = GetCenteredAxialExtent(axisLengthSquared, axisLength, Fixed64.Zero);
 294
 5295        startContained = IsCenteredCapsulePointContained(
 5296            query.Start, center, axisDirection, axisLength,
 5297            startDistanceSquared, startAxisProjection, axisLengthSquared,
 5298            squaredRadius, expandedRadius, axialExtent, strict: false);
 5299        endContainedStrict = IsCenteredCapsulePointContained(
 5300            query.End, center, axisDirection, axisLength,
 5301            GetDot(query.End, center, query.End, center), endAxisProjection,
 5302            axisLengthSquared, squaredRadius, expandedRadius, axialExtent, strict: true);
 303
 5304        bool found = false;
 5305        entry = default;
 5306        exit = default;
 5307        if (TryGetCenteredAxialInterval(
 5308                startAxisProjection,
 5309                directionsDot,
 5310                axialExtent,
 5311                Fixed64.One,
 5312                out RationalBound320 lower,
 5313                out RationalBound320 upper))
 314        {
 4315            found = TryGetFiniteAxisDistanceInterval(
 4316                queryLengthSquared, axisLengthSquared, startDistanceSquared,
 4317                directionsDot, startAxisProjection, startDirectionProjection,
 4318                expandedRadius, lower, upper, segmentLength, out entry, out exit);
 319        }
 320
 5321        MergeCenteredCapDistanceInterval(query, center, axisDirection, axisLength, expandedRadius, segmentLength, false,
 5322        MergeCenteredCapDistanceInterval(query, center, axisDirection, axisLength, expandedRadius, segmentLength, true, 
 5323        return found;
 324    }
 325
 326    internal static bool TryGetCapsuleDistanceInterval(
 327        FixedSegment query,
 328        Vector3d center,
 329        Vector3d axisDirection,
 330        Fixed64 axisLength,
 331        Fixed64 radius,
 332        Fixed64 radiusExpansion,
 333        Fixed64 segmentLength,
 334        out Fixed64 entry,
 335        out Fixed64 exit,
 336        out bool startContained,
 337        out bool endContainedStrict)
 338    {
 16339        Signed192 expandedRadius = GetExpandedRadius(radius, radiusExpansion);
 16340        Signed192 squaredRadius = GetSquaredRadius(expandedRadius);
 16341        Signed192 axisLengthSquared = GetDot(axisDirection, Vector3d.Zero, axisDirection, Vector3d.Zero);
 16342        Signed192 queryLengthSquared = GetDot(query.End, query.Start, query.End, query.Start);
 16343        Signed192 startDistanceSquared = GetDot(query.Start, center, query.Start, center);
 16344        Signed192 directionsDot = GetDot(query.End, query.Start, axisDirection, Vector3d.Zero);
 16345        Signed192 startAxisProjection = GetDot(query.Start, center, axisDirection, Vector3d.Zero);
 16346        Signed192 endAxisProjection = GetDot(query.End, center, axisDirection, Vector3d.Zero);
 16347        Signed192 startDirectionProjection = GetDot(query.Start, center, query.End, query.Start);
 16348        Signed320 axialExtent = GetCenteredAxialExtent(axisLengthSquared, axisLength, Fixed64.Zero);
 349
 16350        startContained = IsCenteredCapsulePointContained(
 16351            query.Start, center, axisDirection, axisLength,
 16352            startDistanceSquared, startAxisProjection, axisLengthSquared,
 16353            squaredRadius, expandedRadius, axialExtent, strict: false);
 16354        endContainedStrict = IsCenteredCapsulePointContained(
 16355            query.End, center, axisDirection, axisLength,
 16356            GetDot(query.End, center, query.End, center), endAxisProjection,
 16357            axisLengthSquared, squaredRadius, expandedRadius, axialExtent, strict: true);
 358
 16359        bool found = false;
 16360        entry = default;
 16361        exit = default;
 16362        if (TryGetCenteredAxialInterval(
 16363                startAxisProjection,
 16364                directionsDot,
 16365                axialExtent,
 16366                Fixed64.One,
 16367                out RationalBound320 lower,
 16368                out RationalBound320 upper))
 369        {
 15370            found = TryGetFiniteAxisDistanceInterval(
 15371                queryLengthSquared, axisLengthSquared, startDistanceSquared,
 15372                directionsDot, startAxisProjection, startDirectionProjection,
 15373                expandedRadius, lower, upper, segmentLength, out entry, out exit);
 374        }
 375
 16376        MergeCenteredCapDistanceInterval(query, center, axisDirection, axisLength, expandedRadius, segmentLength, false,
 16377        MergeCenteredCapDistanceInterval(query, center, axisDirection, axisLength, expandedRadius, segmentLength, true, 
 16378        return found;
 379    }
 380
 381    internal static bool TryGetFiniteCylinderDistanceInterval(
 382        FixedSegment query,
 383        FixedSegment axis,
 384        Fixed64 radius,
 385        Fixed64 radiusExpansion,
 386        Fixed64 segmentLength,
 387        out Fixed64 entry,
 388        out Fixed64 exit,
 389        out bool startContained,
 390        out bool endContainedStrict)
 391    {
 3392        Signed192 expandedRadius = GetExpandedRadius(radius, radiusExpansion);
 3393        Signed192 squaredRadius = GetSquaredRadius(expandedRadius);
 3394        Signed192 axisLengthSquared = GetDot(axis.End, axis.Start, axis.End, axis.Start);
 3395        Signed192 queryLengthSquared = GetDot(query.End, query.Start, query.End, query.Start);
 3396        Signed192 startDistanceSquared = GetDot(query.Start, axis.Start, query.Start, axis.Start);
 3397        Signed192 endDistanceSquared = GetDot(query.End, axis.Start, query.End, axis.Start);
 3398        Signed192 directionsDot = GetDot(query.End, query.Start, axis.End, axis.Start);
 3399        Signed192 startAxisProjection = GetDot(query.Start, axis.Start, axis.End, axis.Start);
 3400        Signed192 startDirectionProjection = GetDot(query.Start, axis.Start, query.End, query.Start);
 3401        startContained = IsFiniteCylinderPointContained(
 3402            startDistanceSquared, startAxisProjection, axisLengthSquared, squaredRadius, strict: false);
 3403        endContainedStrict = IsFiniteCylinderPointContained(
 3404            endDistanceSquared,
 3405            GetDot(query.End, axis.Start, axis.End, axis.Start),
 3406            axisLengthSquared,
 3407            squaredRadius,
 3408            strict: true);
 3409        return TryGetFiniteAxisDistanceInterval(
 3410            queryLengthSquared, axisLengthSquared, startDistanceSquared,
 3411            directionsDot, startAxisProjection, startDirectionProjection,
 3412            expandedRadius, segmentLength, out entry, out exit);
 413    }
 414
 415    internal static bool TryGetFiniteCylinderDistanceInterval(
 416        FixedSegment query,
 417        Vector3d center,
 418        Vector3d axisDirection,
 419        Fixed64 axisLength,
 420        Fixed64 radius,
 421        Fixed64 radiusExpansion,
 422        Fixed64 axialExpansion,
 423        Fixed64 segmentLength,
 424        out Fixed64 entry,
 425        out Fixed64 exit,
 426        out bool startContained,
 427        out bool endContainedStrict) =>
 6428        TryGetFiniteCylinderDistanceInterval(
 6429            query,
 6430            center,
 6431            axisDirection,
 6432            Signed192.Signed(axisLength.m_rawValue),
 6433            radius,
 6434            radiusExpansion,
 6435            axialExpansion,
 6436            segmentLength,
 6437            out entry,
 6438            out exit,
 6439            out startContained,
 6440            out endContainedStrict);
 441
 442    internal static bool TryGetFiniteCylinderDistanceInterval(
 443        FixedSegment query,
 444        Vector3d center,
 445        Vector3d axisDirection,
 446        Signed192 axisLength,
 447        Fixed64 radius,
 448        Fixed64 radiusExpansion,
 449        Fixed64 axialExpansion,
 450        Fixed64 segmentLength,
 451        out Fixed64 entry,
 452        out Fixed64 exit,
 453        out bool startContained,
 454        out bool endContainedStrict)
 455    {
 50456        Signed192 queryLengthSquared = GetDot(query.End, query.Start, query.End, query.Start);
 50457        Signed192 axisLengthSquared = GetDot(axisDirection, Vector3d.Zero, axisDirection, Vector3d.Zero);
 50458        Signed192 startDistanceSquared = GetDot(query.Start, center, query.Start, center);
 50459        Signed192 endDistanceSquared = GetDot(query.End, center, query.End, center);
 50460        Signed192 directionsDot = GetDot(query.End, query.Start, axisDirection, Vector3d.Zero);
 50461        Signed192 startAxisProjection = GetDot(query.Start, center, axisDirection, Vector3d.Zero);
 50462        Signed192 startDirectionProjection = GetDot(query.Start, center, query.End, query.Start);
 50463        Signed192 expandedRadius = GetExpandedRadius(radius, radiusExpansion);
 50464        Signed192 squaredRadius = GetSquaredRadius(expandedRadius);
 50465        Signed320 axialExtent = GetCenteredAxialExtent(axisLengthSquared, axisLength, axialExpansion);
 50466        startContained = IsCenteredFiniteCylinderPointContained(
 50467            startDistanceSquared, startAxisProjection, axisLengthSquared,
 50468            squaredRadius, axialExtent, strict: false);
 50469        endContainedStrict = IsCenteredFiniteCylinderPointContained(
 50470            endDistanceSquared,
 50471            GetDot(query.End, center, axisDirection, Vector3d.Zero),
 50472            axisLengthSquared,
 50473            squaredRadius,
 50474            axialExtent,
 50475            strict: true);
 476
 50477        if (!TryGetCenteredAxialInterval(
 50478                startAxisProjection, directionsDot, axialExtent, Fixed64.One,
 50479                out RationalBound320 lower, out RationalBound320 upper))
 480        {
 19481            entry = default;
 19482            exit = default;
 19483            return false;
 484        }
 485
 31486        return TryGetFiniteAxisDistanceInterval(
 31487            queryLengthSquared, axisLengthSquared, startDistanceSquared,
 31488            directionsDot, startAxisProjection, startDirectionProjection,
 31489            expandedRadius, lower, upper, segmentLength, out entry, out exit);
 490    }
 491
 492    private static bool TryGetFiniteAxisDistanceInterval(
 493        Signed192 queryLengthSquared,
 494        Signed192 axisLengthSquared,
 495        Signed192 startDistanceSquared,
 496        Signed192 directionsDot,
 497        Signed192 startAxisProjection,
 498        Signed192 startDirectionProjection,
 499        Signed192 radius,
 500        Fixed64 segmentLength,
 501        out Fixed64 entry,
 502        out Fixed64 exit)
 503    {
 5504        if (!TryGetAxialInterval(
 5505                startAxisProjection,
 5506                directionsDot,
 5507                axisLengthSquared,
 5508                Fixed64.One,
 5509                out RationalBound lower,
 5510                out RationalBound upper))
 511        {
 1512            entry = default;
 1513            exit = default;
 1514            return false;
 515        }
 516
 4517        return TryGetFiniteAxisDistanceInterval(
 4518            queryLengthSquared,
 4519            axisLengthSquared,
 4520            startDistanceSquared,
 4521            directionsDot,
 4522            startAxisProjection,
 4523            startDirectionProjection,
 4524            radius,
 4525            new RationalBound320(
 4526                Signed320.ExtendValue(lower.Numerator),
 4527                Signed320.ExtendValue(lower.Denominator)),
 4528            new RationalBound320(
 4529                Signed320.ExtendValue(upper.Numerator),
 4530                Signed320.ExtendValue(upper.Denominator)),
 4531            segmentLength,
 4532            out entry,
 4533            out exit);
 534    }
 535
 536    private static bool TryGetFiniteAxisDistanceInterval(
 537        Signed192 queryLengthSquared,
 538        Signed192 axisLengthSquared,
 539        Signed192 startDistanceSquared,
 540        Signed192 directionsDot,
 541        Signed192 startAxisProjection,
 542        Signed192 startDirectionProjection,
 543        Signed192 radius,
 544        RationalBound320 lower,
 545        RationalBound320 upper,
 546        Fixed64 segmentLength,
 547        out Fixed64 entry,
 548        out Fixed64 exit)
 549    {
 56550        Signed320 radialCoefficient = WideArithmetic.MultiplySubtract(
 56551            queryLengthSquared, axisLengthSquared, directionsDot, directionsDot);
 56552        Signed320 radialProjection = WideArithmetic.MultiplySubtract(
 56553            startDirectionProjection, axisLengthSquared, startAxisProjection, directionsDot);
 56554        Signed320 radialConstant = GetRadialConstant(
 56555            startDistanceSquared,
 56556            startAxisProjection,
 56557            axisLengthSquared,
 56558            GetSquaredRadius(radius));
 56559        return TrySolveBoundedQuadraticAtDistance(
 56560            radialCoefficient,
 56561            radialProjection,
 56562            radialConstant,
 56563            lower,
 56564            upper,
 56565            segmentLength,
 56566            out entry,
 56567            out exit);
 568    }
 569
 570    private static bool TryGetCircleDistanceInterval(
 571        Vector2d start,
 572        Vector2d end,
 573        Vector2d center,
 574        Signed192 radius,
 575        Fixed64 segmentLength,
 576        out Fixed64 entry,
 577        out Fixed64 exit) =>
 19785578        TrySolveUnitQuadraticAtDistance(
 19785579            Signed320.ExtendValue(GetDot(end, start, end, start)),
 19785580            Signed320.ExtendValue(GetDot(start, center, end, start)),
 19785581            Signed320.ExtendValue(WideArithmetic.SubtractSigned192(
 19785582                GetDot(start, center, start, center),
 19785583                GetSquaredRadius(radius))),
 19785584            segmentLength,
 19785585            out entry,
 19785586            out exit);
 587
 588    private static bool TryGetSphereDistanceInterval(
 589        Vector3d start,
 590        Vector3d end,
 591        Vector3d center,
 592        Signed192 radius,
 593        Fixed64 segmentLength,
 594        out Fixed64 entry,
 595        out Fixed64 exit) =>
 22596        TrySolveUnitQuadraticAtDistance(
 22597            Signed320.ExtendValue(GetDot(end, start, end, start)),
 22598            Signed320.ExtendValue(GetDot(start, center, end, start)),
 22599            Signed320.ExtendValue(WideArithmetic.SubtractSigned192(
 22600                GetDot(start, center, start, center),
 22601                GetSquaredRadius(radius))),
 22602            segmentLength,
 22603            out entry,
 22604            out exit);
 605
 606    private static void MergeCenteredCapDistanceInterval(
 607        FixedSegment2d query,
 608        Vector2d center,
 609        Vector2d axisDirection,
 610        Fixed64 axisLength,
 611        Signed192 expandedRadius,
 612        Fixed64 segmentLength,
 613        bool positiveCap,
 614        ref bool found,
 615        ref Fixed64 entry,
 616        ref Fixed64 exit)
 617    {
 10618        GetCenteredCapPolynomial(
 10619            query, center, axisDirection, axisLength, expandedRadius, positiveCap,
 10620            out Signed320 coefficient, out Signed320 projection, out Signed320 constant);
 10621        Merge(
 10622            TrySolveUnitQuadraticAtDistance(
 10623                coefficient, projection, constant, segmentLength,
 10624                out Fixed64 capEntry, out Fixed64 capExit),
 10625            capEntry, capExit, ref found, ref entry, ref exit);
 10626    }
 627
 628    private static void MergeCenteredCapDistanceInterval(
 629        FixedSegment query,
 630        Vector3d center,
 631        Vector3d axisDirection,
 632        Fixed64 axisLength,
 633        Signed192 expandedRadius,
 634        Fixed64 segmentLength,
 635        bool positiveCap,
 636        ref bool found,
 637        ref Fixed64 entry,
 638        ref Fixed64 exit)
 639    {
 32640        GetCenteredCapPolynomial(
 32641            query, center, axisDirection, axisLength, expandedRadius, positiveCap,
 32642            out Signed320 coefficient, out Signed320 projection, out Signed320 constant);
 32643        Merge(
 32644            TrySolveUnitQuadraticAtDistance(
 32645                coefficient, projection, constant, segmentLength,
 32646                out Fixed64 capEntry, out Fixed64 capExit),
 32647            capEntry, capExit, ref found, ref entry, ref exit);
 32648    }
 649}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.DistanceSolver.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.DistanceSolver.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
 8namespace FixedMathSharp.Geometry;
 9
 10/// <content>
 11/// Solves bounded quadratic intersection problems and converts the resulting
 12/// parametric roots into actual distances along a segment of a given length.
 13/// </content>
 14internal static partial class WideFiniteAxisIntersection
 15{
 16    private static bool TrySolveUnitQuadraticAtDistance(
 17        Signed320 coefficient,
 18        Signed320 projection,
 19        Signed320 constant,
 20        Fixed64 segmentLength,
 21        out Fixed64 entryDistance,
 22        out Fixed64 exitDistance)
 23    {
 2017324        Signed320 one = Scale320;
 2017325        return TrySolveBoundedQuadraticAtDistance(
 2017326            coefficient,
 2017327            projection,
 2017328            constant,
 2017329            new RationalBound320(default, one),
 2017330            new RationalBound320(one, one),
 2017331            segmentLength,
 2017332            out entryDistance,
 2017333            out exitDistance,
 2017334            unitInterval: true);
 35    }
 36
 37    private static bool TrySolveBoundedQuadraticAtDistance(
 38        Signed320 coefficient,
 39        Signed320 projection,
 40        Signed320 constant,
 41        RationalBound320 lower,
 42        RationalBound320 upper,
 43        Fixed64 segmentLength,
 44        out Fixed64 entryDistance,
 45        out Fixed64 exitDistance,
 46        bool unitInterval = false)
 47    {
 2029148        entryDistance = default;
 2029149        exitDistance = default;
 50
 2029151        if (coefficient.IsZero)
 52        {
 16553            if (constant.Sign > 0)
 6754                return false;
 55
 9856            entryDistance = RoundAtDistance(lower, segmentLength);
 9857            exitDistance = RoundAtDistance(upper, segmentLength);
 9858            return true;
 59        }
 60
 61        int lowerValueSign;
 62        int upperValueSign;
 63        int lowerDerivative;
 64        int upperDerivative;
 2012665        if (unitInterval)
 66        {
 67            // f(t) = A*t² + 2*B*t + C on [0, 1]. Endpoint signs need
 68            // only sums; the general rational evaluations multiply by positive
 69            // denominator powers and therefore have the same signs. Widen the
 70            // sums so even full-width coefficients cannot overflow here.
 2002271            Signed576 wideProjection = Signed576.ExtendValue(projection);
 2002272            Signed576 coefficientAndProjection = WideArithmetic.AddSigned576(
 2002273                Signed576.ExtendValue(coefficient), wideProjection);
 2002274            lowerValueSign = constant.Sign;
 2002275            upperValueSign = WideArithmetic.AddSigned576(
 2002276                WideArithmetic.AddSigned576(coefficientAndProjection, wideProjection),
 2002277                Signed576.ExtendValue(constant)).Sign;
 2002278            lowerDerivative = projection.Sign;
 2002279            upperDerivative = coefficientAndProjection.Sign;
 80        }
 81        else
 82        {
 10483            lowerValueSign = EvaluatePolynomial(
 10484                coefficient,
 10485                projection,
 10486                constant,
 10487                lower.Numerator,
 10488                lower.Denominator).Sign;
 10489            upperValueSign = EvaluatePolynomial(
 10490                coefficient,
 10491                projection,
 10492                constant,
 10493                upper.Numerator,
 10494                upper.Denominator).Sign;
 10495            lowerDerivative = EvaluateDerivative(
 10496                coefficient,
 10497                projection,
 10498                lower.Numerator,
 10499                lower.Denominator).Sign;
 104100            upperDerivative = EvaluateDerivative(
 104101                coefficient,
 104102                projection,
 104103                upper.Numerator,
 104104                upper.Denominator).Sign;
 105        }
 106
 20126107        if ((lowerValueSign > 0 && lowerDerivative >= 0)
 20126108            || (upperValueSign > 0 && upperDerivative <= 0))
 109        {
 56110            return false;
 111        }
 112
 20070113        if (lowerValueSign <= 0 && upperValueSign <= 0)
 114        {
 15115            entryDistance = RoundAtDistance(lower, segmentLength);
 15116            exitDistance = RoundAtDistance(upper, segmentLength);
 15117            return true;
 118        }
 119
 20055120        Signed576 discriminant = WideArithmetic.SubtractSigned576(
 20055121            WideArithmetic.MultiplySigned320(projection, projection),
 20055122            WideArithmetic.MultiplySigned320(coefficient, constant));
 20055123        if (discriminant.Sign < 0)
 13124            return false;
 125
 20042126        Signed192 lengthSquared = SquareRaw(segmentLength.m_rawValue);
 20042127        Signed576 scaledSquareRoot = WideArithmetic.GetFloorSquareRoot(
 20042128            WideArithmetic.MultiplyNonNegative(discriminant, lengthSquared));
 20042129        entryDistance = lowerValueSign <= 0
 20042130            ? RoundAtDistance(lower, segmentLength)
 20042131            : RoundLowerRootAtDistance(
 20042132                coefficient,
 20042133                projection,
 20042134                constant,
 20042135                scaledSquareRoot,
 20042136                segmentLength);
 20042137        exitDistance = upperValueSign <= 0
 20042138            ? RoundAtDistance(upper, segmentLength)
 20042139            : RoundUpperRootAtDistance(
 20042140                coefficient,
 20042141                projection,
 20042142                constant,
 20042143                scaledSquareRoot,
 20042144                segmentLength);
 20042145        return true;
 146    }
 147
 148    private static Fixed64 RoundLowerRootAtDistance(
 149        Signed320 coefficient,
 150        Signed320 projection,
 151        Signed320 constant,
 152        Signed576 scaledSquareRoot,
 153        Fixed64 segmentLength)
 154    {
 10130155        Signed320 lengthRaw = Signed320.ExtendValue(
 10130156            Signed192.Signed(segmentLength.m_rawValue));
 10130157        Signed576 negativeScaledProjection = WideArithmetic.SubtractSigned576(
 10130158            default,
 10130159            WideArithmetic.MultiplySigned320(projection, lengthRaw));
 10130160        Signed576 numerator = WideArithmetic.SubtractSigned576(
 10130161            negativeScaledProjection,
 10130162            scaledSquareRoot);
 10130163        Fixed64.TryGetSignedRawRatio(
 10130164            numerator,
 10130165            Signed576.ExtendValue(coefficient),
 10130166            out Fixed64 candidate);
 167
 10130168        long upperRaw = candidate.m_rawValue;
 10130169        if (upperRaw == 0L)
 110170            return Fixed64.Zero;
 171
 10020172        Signed320 upper = Signed320.ExtendValue(
 10020173            Signed192.Signed(upperRaw));
 10020174        Signed320 midpoint = WideArithmetic.SubtractSigned320(
 10020175            WideArithmetic.AddSigned320(upper, upper),
 10020176            Scale320);
 10020177        Signed320 doubleLength = WideArithmetic.AddSigned320(lengthRaw, lengthRaw);
 10020178        Signed704 value = EvaluatePolynomial(
 10020179            coefficient,
 10020180            projection,
 10020181            constant,
 10020182            midpoint,
 10020183            doubleLength);
 10020184        if (value.IsZero)
 277185            return candidate;
 186
 9743187        return Fixed64.FromRaw(value.Sign > 0
 9743188            ? upperRaw
 9743189            : upperRaw - 1L);
 190    }
 191
 192    private static Fixed64 RoundUpperRootAtDistance(
 193        Signed320 coefficient,
 194        Signed320 projection,
 195        Signed320 constant,
 196        Signed576 scaledSquareRoot,
 197        Fixed64 segmentLength)
 198    {
 10037199        Signed320 lengthRaw = Signed320.ExtendValue(
 10037200            Signed192.Signed(segmentLength.m_rawValue));
 10037201        Signed576 negativeScaledProjection = WideArithmetic.SubtractSigned576(
 10037202            default,
 10037203            WideArithmetic.MultiplySigned320(projection, lengthRaw));
 10037204        Signed576 numerator = WideArithmetic.AddSigned576(
 10037205            negativeScaledProjection,
 10037206            scaledSquareRoot);
 10037207        Fixed64.TryGetSignedRawRatio(
 10037208            numerator,
 10037209            Signed576.ExtendValue(coefficient),
 10037210            out Fixed64 candidate);
 211
 10037212        long lowerRaw = candidate.m_rawValue;
 10037213        if (lowerRaw == segmentLength.m_rawValue)
 87214            return segmentLength;
 215
 9950216        Signed320 lower = Signed320.ExtendValue(
 9950217            Signed192.Signed(lowerRaw));
 9950218        Signed320 midpoint = WideArithmetic.AddSigned320(
 9950219            WideArithmetic.AddSigned320(lower, lower),
 9950220            Scale320);
 9950221        Signed320 doubleLength = WideArithmetic.AddSigned320(lengthRaw, lengthRaw);
 9950222        Signed704 value = EvaluatePolynomial(
 9950223            coefficient,
 9950224            projection,
 9950225            constant,
 9950226            midpoint,
 9950227            doubleLength);
 9950228        if (value.IsZero)
 266229            return candidate;
 230
 9684231        return Fixed64.FromRaw(value.Sign <= 0
 9684232            ? lowerRaw + 1L
 9684233            : lowerRaw);
 234    }
 235
 236    private static Fixed64 RoundAtDistance(RationalBound320 value, Fixed64 segmentLength)
 237    {
 20143238        Signed320 lengthRaw = Signed320.ExtendValue(
 20143239            Signed192.Signed(segmentLength.m_rawValue));
 20143240        Fixed64.TryGetSignedRawRatio(
 20143241            WideArithmetic.MultiplySigned320(value.Numerator, lengthRaw),
 20143242            Signed576.ExtendValue(value.Denominator),
 20143243            out Fixed64 distance);
 20143244        return distance;
 245    }
 246
 247    private static Signed192 SquareRaw(long raw)
 248    {
 20042249        ulong magnitude = (ulong)raw;
 20042250        Fixed64.Multiply64To128(magnitude, magnitude, out ulong high, out ulong low);
 20042251        return new Signed192(0UL, high, low);
 252    }
 253}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.FiniteDiskStrictOverlap.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.FiniteDiskStrictOverlap.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>
 13/// Exact finite-solid cap-disk admission. Rim crossing and two containment
 14/// witnesses completely classify intersection with the other solid's interior.
 15/// </content>
 16internal static partial class WideFiniteAxisIntersection
 17{
 18    internal static bool DoesFiniteCapDiskEnterSolid(
 19        Vector3d capOwnerCenter, FixedQuaternion capRotation, Fixed64 capOwnerHeight,
 20        Fixed64 capRadius, int capSign,
 21        Vector3d solidCenter, FixedQuaternion solidRotation, Fixed64 solidHeight,
 22        Fixed64 solidRadius, bool cone)
 23    {
 308224        WideRationalBasis3d capBasis = new(capRotation);
 308225        WideRationalBasis3d solidBasis = new(solidRotation);
 308226        WideRationalBasis3d relative = WideRationalBasis3d.CreateRelative(solidBasis, capBasis);
 308227        WideOrientedBox.GetRelativeLocalPointNumerators(capOwnerCenter, solidCenter, solidRotation,
 308228            out Signed192 centerX, out Signed192 centerY, out Signed192 centerZ, out _);
 308229        Signed192 denominator = WideArithmetic.Double(relative.Denominator);
 308230        Signed192 doubledCapDenominator = WideArithmetic.Double(capBasis.Denominator);
 308231        Signed192 signedHeight = Signed192.Raw(capOwnerHeight);
 308232        if (capSign < 0)
 149133            signedHeight = WideArithmetic.Negate(signedHeight);
 308234        Span<Signed320> center = stackalloc Signed320[3]
 308235        {
 308236            CapCoordinate(centerX, doubledCapDenominator, relative.Yx, signedHeight),
 308237            CapCoordinate(centerY, doubledCapDenominator, relative.Yy, signedHeight),
 308238            CapCoordinate(centerZ, doubledCapDenominator, relative.Yz, signedHeight),
 308239        };
 308240        if (DoesStrictDiskPointEnterSolid(center, denominator, solidHeight, solidRadius, cone))
 1941            return true;
 42
 306343        Signed192 diameter = WideArithmetic.Double(Signed192.Raw(capRadius));
 306344        Span<Signed320> cosine = stackalloc Signed320[3]
 306345        {
 306346            WideArithmetic.MultiplySigned192(relative.Xx, diameter),
 306347            WideArithmetic.MultiplySigned192(relative.Xy, diameter),
 306348            WideArithmetic.MultiplySigned192(relative.Xz, diameter),
 306349        };
 306350        Span<Signed320> sine = stackalloc Signed320[3]
 306351        {
 306352            WideArithmetic.MultiplySigned192(relative.Zx, diameter),
 306353            WideArithmetic.MultiplySigned192(relative.Zy, diameter),
 306354            WideArithmetic.MultiplySigned192(relative.Zz, diameter),
 306355        };
 306356        Span<Signed320> coordinates = stackalloc Signed320[9];
 2450457        for (int index = 0; index < 3; index++)
 58        {
 918959            coordinates[index * 3] = WideArithmetic.AddSigned320(center[index], cosine[index]);
 918960            coordinates[index * 3 + 1] = WideArithmetic.AddSigned320(sine[index], sine[index]);
 918961            coordinates[index * 3 + 2] = WideArithmetic.SubtractSigned320(center[index], cosine[index]);
 62        }
 306363        Span<Signed832> radial = stackalloc Signed832[5];
 306364        Span<Signed320> lower = stackalloc Signed320[3];
 306365        Span<Signed320> upper = stackalloc Signed320[3];
 306366        BuildStrictDiskCirclePolynomials(coordinates, denominator, solidHeight, solidRadius,
 306367            cone, radial, lower, upper);
 306368        if (radial[4].Sign < 0 && lower[2].Sign > 0 && upper[2].Sign > 0)
 1269            return true; // The projective point omitted by finite t.
 305170        if (HasStrictCirclePolynomialPoint(radial, lower, upper))
 11871            return true;
 72
 73        // If neither boundary crosses the other interior, one convex planar
 74        // region contains the other. The cap center covered one direction;
 75        // any strict point in the target plane section covers the other.
 293376        return IsStrictSolidSectionWitnessInDisk(center, denominator, relative,
 293377            capRadius, solidHeight, solidRadius, cone);
 78    }
 79
 80    private static Signed320 CapCoordinate(Signed192 center, Signed192 denominator,
 81        Signed192 axis, Signed192 height) =>
 924682        WideArithmetic.AddSigned320(WideArithmetic.MultiplySigned192(center, denominator),
 924683            WideArithmetic.MultiplySigned192(axis, height));
 84
 85    private static bool DoesStrictDiskPointEnterSolid(ReadOnlySpan<Signed320> point,
 86        Signed192 denominator, Fixed64 height, Fixed64 radius, bool cone)
 87    {
 315588        Signed320 h = WideArithmetic.MultiplySigned192(Signed192.Raw(height), denominator);
 315589        Signed320 twiceY = WideArithmetic.AddSigned320(point[1], point[1]);
 315590        Signed320 lower = WideArithmetic.AddSigned320(h, twiceY);
 315591        Signed320 upper = WideArithmetic.SubtractSigned320(h, twiceY);
 315592        if (lower.Sign <= 0 || upper.Sign <= 0)
 201293            return false;
 114394        Signed576 radial = WideArithmetic.AddSigned576(
 114395            WideArithmetic.MultiplySigned320(point[0], point[0]),
 114396            WideArithmetic.MultiplySigned320(point[2], point[2]));
 114397        if (!cone)
 98        {
 103599            Signed320 r = WideArithmetic.MultiplySigned192(Signed192.Raw(radius), denominator);
 1035100            return WideArithmetic.SubtractSigned576(radial, WideArithmetic.MultiplySigned320(r, r)).Sign < 0;
 101        }
 108102        Signed192 hSquared = StrictDiskRawSquare(height);
 108103        Signed192 rSquared = StrictDiskRawSquare(radius);
 108104        Signed832 left = WideArithmetic.MultiplySigned832(Signed832.ExtendValue(radial), hSquared);
 108105        left = TwiceStrictDiskValue(TwiceStrictDiskValue(left));
 108106        Signed832 right = WideArithmetic.MultiplySigned832(
 108107            Signed832.ExtendValue(WideArithmetic.MultiplySigned320(upper, upper)), rSquared);
 108108        return WideArithmetic.SubtractSigned832(left, right).Sign < 0;
 109    }
 110
 111    private static void BuildStrictDiskCirclePolynomials(ReadOnlySpan<Signed320> coordinates,
 112        Signed192 denominator, Fixed64 height, Fixed64 radius, bool cone,
 113        Span<Signed832> radial, Span<Signed320> lower, Span<Signed320> upper)
 114    {
 3063115        Signed320 h = WideArithmetic.MultiplySigned192(Signed192.Raw(height), denominator);
 24504116        for (int index = 0; index < 3; index++)
 117        {
 9189118            Signed320 extent = index == 1 ? default : h;
 9189119            Signed320 y = WideArithmetic.AddSigned320(coordinates[3 + index], coordinates[3 + index]);
 9189120            lower[index] = WideArithmetic.AddSigned320(extent, y);
 9189121            upper[index] = WideArithmetic.SubtractSigned320(extent, y);
 122        }
 3063123        Signed192 hSquared = StrictDiskRawSquare(height);
 3063124        Signed192 rSquared = StrictDiskRawSquare(radius);
 3063125        Signed320 scaledRadius = WideArithmetic.MultiplySigned192(Signed192.Raw(radius), denominator);
 3063126        Signed576 radiusBound = WideArithmetic.MultiplySigned320(scaledRadius, scaledRadius);
 36756127        for (int degree = 0; degree < 5; degree++)
 128        {
 15315129            Signed576 radialCoefficient = default;
 15315130            Signed576 apexCoefficient = default;
 122520131            for (int first = 0; first < 3; first++)
 132            {
 45945133                int second = degree - first;
 45945134                if ((uint)second >= 3U)
 135                    continue;
 27567136                radialCoefficient = WideArithmetic.AddSigned576(radialCoefficient,
 27567137                    WideArithmetic.AddSigned576(
 27567138                        WideArithmetic.MultiplySigned320(coordinates[first], coordinates[second]),
 27567139                        WideArithmetic.MultiplySigned320(coordinates[6 + first], coordinates[6 + second])));
 27567140                if (cone)
 3816141                    apexCoefficient = WideArithmetic.AddSigned576(apexCoefficient,
 3816142                        WideArithmetic.MultiplySigned320(upper[first], upper[second]));
 143            }
 15315144            if (cone)
 145            {
 2120146                Signed832 left = WideArithmetic.MultiplySigned832(Signed832.ExtendValue(radialCoefficient), hSquared);
 2120147                radial[degree] = WideArithmetic.SubtractSigned832(
 2120148                    TwiceStrictDiskValue(TwiceStrictDiskValue(left)),
 2120149                    WideArithmetic.MultiplySigned832(Signed832.ExtendValue(apexCoefficient), rSquared));
 150            }
 151            else
 152            {
 13195153                Signed576 bound = degree == 2 ? WideArithmetic.AddSigned576(radiusBound, radiusBound)
 13195154                    : degree == 0 || degree == 4 ? radiusBound : default;
 13195155                radial[degree] = Signed832.ExtendValue(WideArithmetic.SubtractSigned576(radialCoefficient, bound));
 156            }
 157        }
 3063158    }
 159
 160    private static bool IsStrictSolidSectionWitnessInDisk(ReadOnlySpan<Signed320> center,
 161        Signed192 denominator, WideRationalBasis3d relative, Fixed64 capRadius,
 162        Fixed64 height, Fixed64 radius, bool cone)
 163    {
 2933164        Signed192 nx = relative.Yx;
 2933165        Signed192 ny = relative.Yy;
 2933166        Signed192 nz = relative.Yz;
 2933167        Signed320 radialNormalSquared = WideArithmetic.AddSigned320(
 2933168            WideArithmetic.MultiplySigned192(nx, nx), WideArithmetic.MultiplySigned192(nz, nz));
 2933169        if (radialNormalSquared.IsZero)
 170        {
 73171            Span<Signed320> axisPoint = stackalloc Signed320[3] { default, center[1], default };
 73172            if (!DoesStrictDiskPointEnterSolid(axisPoint, denominator, height, radius, cone))
 65173                return false;
 8174            Signed320 r = WideArithmetic.MultiplySigned192(Signed192.Raw(capRadius), denominator);
 8175            Signed576 distance = WideArithmetic.AddSigned576(
 8176                WideArithmetic.MultiplySigned320(center[0], center[0]),
 8177                WideArithmetic.MultiplySigned320(center[2], center[2]));
 8178            return WideArithmetic.SubtractSigned576(distance, WideArithmetic.MultiplySigned320(r, r)).Sign <= 0;
 179        }
 2860180        Signed576 plane = WideArithmetic.AddSigned576(
 2860181            WideArithmetic.AddSigned576(WideArithmetic.MultiplySigned320(center[0], nx),
 2860182                WideArithmetic.MultiplySigned320(center[1], ny)),
 2860183            WideArithmetic.MultiplySigned320(center[2], nz));
 2860184        Signed320 hD = WideArithmetic.MultiplySigned192(Signed192.Raw(height), denominator);
 2860185        Signed576 yTerm = WideArithmetic.MultiplySigned320(hD, ny);
 2860186        Signed576 m0 = WideArithmetic.AddSigned576(WideArithmetic.AddSigned576(plane, plane), yTerm);
 2860187        Signed576 m1 = WideArithmetic.SubtractSigned576(default, WideArithmetic.AddSigned576(yTerm, yTerm));
 2860188        Signed192 hSquared = StrictDiskRawSquare(height);
 2860189        Signed192 rSquared = StrictDiskRawSquare(radius);
 2860190        Signed832 constant = WideArithmetic.MultiplySigned576ToSigned832(m0, m0);
 2860191        Signed832 linear = TwiceStrictDiskValue(WideArithmetic.MultiplySigned576ToSigned832(m0, m1));
 2860192        Signed832 quadratic = WideArithmetic.MultiplySigned576ToSigned832(m1, m1);
 2860193        Signed320 twiceRD = WideArithmetic.MultiplySigned192(
 2860194            WideArithmetic.Double(Signed192.Raw(cone ? height : radius)), denominator);
 2860195        Signed832 bound = WideArithmetic.MultiplySigned576ToSigned832(
 2860196            WideArithmetic.MultiplySigned320(twiceRD, twiceRD), radialNormalSquared);
 2860197        if (cone)
 198        {
 410199            constant = WideArithmetic.MultiplySigned832(constant, hSquared);
 410200            linear = WideArithmetic.MultiplySigned832(linear, hSquared);
 410201            quadratic = WideArithmetic.MultiplySigned832(quadratic, hSquared);
 410202            bound = WideArithmetic.MultiplySigned832(bound, rSquared);
 410203            linear = WideArithmetic.AddSigned832(linear, TwiceStrictDiskValue(bound));
 410204            quadratic = WideArithmetic.SubtractSigned832(quadratic, bound);
 205        }
 2860206        constant = WideArithmetic.SubtractSigned832(constant, bound);
 2860207        if (!TryGetStrictDiskQuadraticWitness(constant, linear, quadratic,
 2860208                out Signed832 parameter, out Signed832 parameterDenominator))
 209        {
 1906210            return false;
 211        }
 212
 954213        Signed576 pointDenominator = WideArithmetic.MultiplySigned320(radialNormalSquared,
 954214            WideArithmetic.Double(denominator));
 954215        Span<Signed576> initial = stackalloc Signed576[3]
 954216        {
 954217            WideArithmetic.MultiplySigned576(m0, nx),
 954218            WideArithmetic.SubtractSigned576(default, WideArithmetic.MultiplySigned320(radialNormalSquared, hD)),
 954219            WideArithmetic.MultiplySigned576(m0, nz),
 954220        };
 954221        Span<Signed576> slope = stackalloc Signed576[3]
 954222        {
 954223            WideArithmetic.MultiplySigned576(m1, nx),
 954224            WideArithmetic.MultiplySigned576(WideArithmetic.MultiplySigned320(radialNormalSquared, hD), 2L),
 954225            WideArithmetic.MultiplySigned576(m1, nz),
 954226        };
 7632227        for (int index = 0; index < 3; index++)
 2862228            initial[index] = WideArithmetic.SubtractSigned576(initial[index],
 2862229                WideArithmetic.MultiplySigned576(WideArithmetic.MultiplySigned320(center[index], radialNormalSquared), 2
 954230        return IsStrictDiskRationalWitnessContained(initial, slope, pointDenominator,
 954231            capRadius, parameter, parameterDenominator);
 232    }
 233
 234    private static bool TryGetStrictDiskQuadraticWitness(Signed832 constant, Signed832 linear,
 235        Signed832 quadratic, out Signed832 numerator, out Signed832 denominator)
 236    {
 2860237        numerator = Signed832.ExtendValue(Signed192.Signed(1L));
 2860238        denominator = TwiceStrictDiskValue(numerator);
 2860239        Signed832 midpoint = WideArithmetic.AddSigned832(
 2860240            TwiceStrictDiskValue(TwiceStrictDiskValue(constant)),
 2860241            WideArithmetic.AddSigned832(TwiceStrictDiskValue(linear), quadratic));
 2860242        if (midpoint.Sign < 0)
 945243            return true;
 1915244        Signed832 twiceQuadratic = TwiceStrictDiskValue(quadratic);
 1915245        Signed832 negativeLinear = WideArithmetic.SubtractSigned832(default, linear);
 1915246        if (quadratic.Sign > 0 && linear.Sign < 0
 1915247            && WideArithmetic.SubtractSigned832(twiceQuadratic, negativeLinear).Sign > 0
 1915248            && (constant.Sign < 0 || WideArithmetic.CompareNonNegativeProducts(
 1915249                quadratic, TwiceStrictDiskValue(TwiceStrictDiskValue(constant)), linear, linear) < 0))
 250        {
 3251            numerator = negativeLinear;
 3252            denominator = twiceQuadratic;
 3253            return true;
 254        }
 1912255        bool reverse = constant.Sign >= 0;
 1912256        if (reverse)
 257        {
 1907258            constant = WideArithmetic.AddSigned832(WideArithmetic.AddSigned832(constant, linear), quadratic);
 1907259            linear = WideArithmetic.SubtractSigned832(default,
 1907260                WideArithmetic.AddSigned832(linear, twiceQuadratic));
 261        }
 1912262        if (constant.Sign >= 0)
 1906263            return false;
 6264        numerator = WideArithmetic.SubtractSigned832(default, constant);
 6265        Signed832 coefficientBound = WideArithmetic.AddSigned832(AbsoluteStrictDiskValue(linear),
 6266            AbsoluteStrictDiskValue(quadratic));
 267        // The nonnegative midpoint gives 2*l+q >= 4*|c|, hence
 268        // |l|+|q| >= 2*|c| > 0. Reversal preserves that midpoint, so
 269        // this denominator is positive and the witness lies strictly inside.
 6270        denominator = TwiceStrictDiskValue(coefficientBound);
 6271        if (reverse)
 1272            numerator = WideArithmetic.SubtractSigned832(denominator, numerator);
 6273        return true;
 274    }
 275
 276    private static bool IsStrictDiskRationalWitnessContained(ReadOnlySpan<Signed576> initial,
 277        ReadOnlySpan<Signed576> slope, Signed576 pointDenominator, Fixed64 radius,
 278        Signed832 parameter, Signed832 parameterDenominator)
 279    {
 280        // Section coefficients are below 2^804; evaluating the rational
 281        // witness produces coordinates below 2^1280 and squares below 2^2563.
 282        const int words = 48;
 954283        Span<ulong> numerator = stackalloc ulong[13];
 954284        Span<ulong> denominator = stackalloc ulong[13];
 954285        Span<ulong> firstCoefficient = stackalloc ulong[9];
 954286        Span<ulong> secondCoefficient = stackalloc ulong[9];
 954287        Span<ulong> first = stackalloc ulong[words];
 954288        Span<ulong> second = stackalloc ulong[words];
 954289        Span<ulong> coordinate = stackalloc ulong[words];
 954290        Span<ulong> square = stackalloc ulong[words];
 954291        Span<ulong> distance = stackalloc ulong[words];
 954292        Span<ulong> sum = stackalloc ulong[words];
 954293        WideArithmetic.GetMagnitude(parameter, numerator);
 954294        WideArithmetic.GetMagnitude(parameterDenominator, denominator);
 954295        distance.Clear();
 7632296        for (int index = 0; index < 3; index++)
 297        {
 2862298            WideArithmetic.GetMagnitude(initial[index], firstCoefficient);
 2862299            WideArithmetic.GetMagnitude(slope[index], secondCoefficient);
 2862300            MultiplyRoundedCylinderWide(firstCoefficient, denominator, first);
 2862301            MultiplyRoundedCylinderWide(secondCoefficient, numerator, second);
 2862302            AddRoundedCylinderSigned(first, (sbyte)initial[index].Sign,
 2862303                second, (sbyte)slope[index].Sign, coordinate, out _);
 2862304            MultiplyRoundedCylinderWide(coordinate, coordinate, square);
 2862305            WideArithmetic.AddEqualMagnitudes(distance, square, sum);
 2862306            sum.CopyTo(distance);
 307        }
 954308        WideArithmetic.GetMagnitude(WideArithmetic.MultiplySigned576(pointDenominator, Signed192.Raw(radius)), firstCoef
 954309        MultiplyRoundedCylinderWide(firstCoefficient, denominator, coordinate);
 954310        MultiplyRoundedCylinderWide(coordinate, coordinate, square);
 954311        return WideArithmetic.CompareMagnitudeEqualLength(distance, square) <= 0;
 312    }
 313
 314    private static Signed192 StrictDiskRawSquare(Fixed64 value) =>
 12992315        Signed192.NarrowProven(WideArithmetic.MultiplySigned192(Signed192.Raw(value), Signed192.Raw(value)));
 316
 22451317    private static Signed832 TwiceStrictDiskValue(Signed832 value) => WideArithmetic.AddSigned832(value, value);
 318
 319    private static Signed832 AbsoluteStrictDiskValue(Signed832 value) =>
 12320        value.Sign < 0 ? WideArithmetic.SubtractSigned832(default, value) : value;
 321}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.NarrowSolver.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.NarrowSolver.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>
 13/// Narrowing helpers that reduce wide 320-bit radial coefficients down to
 14/// 192-bit values when their magnitude and common shift allow it, enabling
 15/// cheaper downstream arithmetic in the narrow intersection solver.
 16/// </content>
 17internal static partial class WideFiniteAxisIntersection
 18{
 19    // Squaring two values below 2^159 and subtracting their products stays
 20    // inside the signed 320-bit discriminant used by WideRayIntersection.
 21    private const int NarrowRadialCoefficientBits = 159;
 22
 23    private static bool TryGetNarrowRadialCoefficients(
 24        Signed320 coefficient,
 25        Signed320 projection,
 26        Signed320 constant,
 27        out Signed192 narrowCoefficient,
 28        out Signed192 narrowProjection,
 29        out Signed192 narrowConstant)
 30    {
 50131        int commonShift = Math.Min(
 50132            GetTrailingZeroCount(coefficient),
 50133            Math.Min(GetTrailingZeroCount(projection), GetTrailingZeroCount(constant)));
 50134        if (commonShift > 0)
 35        {
 34836            coefficient = ShiftRightExact(coefficient, commonShift);
 34837            projection = ShiftRightExact(projection, commonShift);
 34838            constant = ShiftRightExact(constant, commonShift);
 39        }
 40
 50141        int maximumBits = Math.Max(
 50142            GetMagnitudeBitLength(coefficient),
 50143            Math.Max(GetMagnitudeBitLength(projection), GetMagnitudeBitLength(constant)));
 50144        if (maximumBits > NarrowRadialCoefficientBits)
 45        {
 15346            narrowCoefficient = default;
 15347            narrowProjection = default;
 15348            narrowConstant = default;
 15349            return false;
 50        }
 51
 34852        bool coefficientFits = Signed192.TryNarrowSigned(coefficient, out narrowCoefficient);
 34853        bool projectionFits = Signed192.TryNarrowSigned(projection, out narrowProjection);
 34854        bool constantFits = Signed192.TryNarrowSigned(constant, out narrowConstant);
 34855        return coefficientFits && projectionFits && constantFits;
 56    }
 57
 58    private static int GetMagnitudeBitLength(Signed320 value)
 59    {
 150360        if (value.IsZero)
 661            return 0;
 62
 149763        WideArithmetic.GetMagnitude(
 149764            value,
 149765            out ulong word4,
 149766            out ulong word3,
 149767            out ulong word2,
 149768            out ulong word1,
 149769            out ulong word0);
 149770        return WideArithmetic.GetBitLength(word4, word3, word2, word1, word0);
 71    }
 72
 73    private static int GetTrailingZeroCount(Signed320 value)
 74    {
 150375        if (value.Word0 != 0UL)
 46376            return CountTrailingZeroes(value.Word0);
 104077        if (value.Word1 != 0UL)
 85278            return 64 + CountTrailingZeroes(value.Word1);
 18879        if (value.Word2 != 0UL)
 18280            return 128 + CountTrailingZeroes(value.Word2);
 681        return 192;
 82    }
 83
 84    private static int CountTrailingZeroes(ulong value) =>
 149785        63 - Fixed64.CountLeadingZeroes(value & unchecked(0UL - value));
 86
 87    private static Signed320 ShiftRightExact(Signed320 value, int bits)
 88    {
 122489        int wordShift = bits >> 6;
 122490        int bitShift = bits & 63;
 122491        return new Signed320(
 122492            GetShiftedWord(value, 4, wordShift, bitShift),
 122493            GetShiftedWord(value, 3, wordShift, bitShift),
 122494            GetShiftedWord(value, 2, wordShift, bitShift),
 122495            GetShiftedWord(value, 1, wordShift, bitShift),
 122496            GetShiftedWord(value, 0, wordShift, bitShift));
 97    }
 98
 99    private static ulong GetShiftedWord(Signed320 value, int index, int wordShift, int bitShift)
 100    {
 6120101        int sourceIndex = index + wordShift;
 6120102        ulong lower = GetSignedWord(value, sourceIndex);
 6120103        if (bitShift == 0)
 315104            return lower;
 105
 5805106        ulong upper = GetSignedWord(value, sourceIndex + 1);
 5805107        return (lower >> bitShift) | (upper << (64 - bitShift));
 108    }
 109
 11925110    private static ulong GetSignedWord(Signed320 value, int index) => index switch
 11925111    {
 198112        0 => value.Word0,
 1260113        1 => value.Word1,
 2271114        2 => value.Word2,
 2385115        3 => value.Word3,
 2385116        4 => value.Word4,
 3426117        _ => value.Sign < 0 ? ulong.MaxValue : 0UL,
 11925118    };
 119}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.PolynomialArithmetic.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.PolynomialArithmetic.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>
 13/// Construction arithmetic for finite-axis signed magnitude polynomials.
 14/// Coefficients are in ascending degree order with caller-owned storage.
 15/// </content>
 16internal static partial class WideFiniteAxisIntersection
 17{
 18    /// <summary>
 19    /// Accumulates a polynomial multiplied by a signed integer and shifted by
 20    /// a nonnegative degree offset. Source and result have independent uniform
 21    /// strides, canonical signs, and disjoint storage. Every intermediate and
 22    /// nonzero source degree must fit the result. No scratch storage is needed.
 23    /// </summary>
 24    internal static void AddFiniteAxisPolynomial(
 25        ReadOnlySpan<ulong> source, ReadOnlySpan<sbyte> sourceSigns,
 26        Span<ulong> result, Span<sbyte> resultSigns,
 27        int multiplier, int degreeOffset = 0)
 28    {
 68622929        int sourceWords = source.Length / sourceSigns.Length;
 68622930        int resultWords = result.Length / resultSigns.Length;
 68622931        int factorSign = multiplier < 0 ? -1 : 1;
 68622932        uint magnitude = multiplier < 0 ? unchecked(0U - (uint)multiplier) : (uint)multiplier;
 595729833        for (int index = 0; index < sourceSigns.Length; index++)
 34        {
 229242035            if (sourceSigns[index] == 0)
 36                continue;
 99699237            int target = index + degreeOffset;
 99699238            int sign = resultSigns[target];
 99699239            uint factor = magnitude;
 99699240            int shift = 0;
 254493241            while (factor != 0)
 42            {
 154794043                if ((factor & 1) != 0)
 107017944                    WideArithmetic.AddShiftedSignedMagnitude(
 107017945                        source.Slice(index * sourceWords, sourceWords),
 107017946                        sourceSigns[index] * factorSign, shift,
 107017947                        result.Slice(target * resultWords, resultWords), ref sign);
 154794048                factor >>= 1;
 154794049                shift++;
 50            }
 99699251            resultSigns[target] = (sbyte)sign;
 52        }
 68622953    }
 54
 55    /// <summary>
 56    /// Multiplies two polynomials, clearing the result unless accumulation is requested.
 57    /// Each magnitude span has its own uniform stride given by its sign count.
 58    /// </summary>
 59    /// <remarks>
 60    /// Sign spans are nonempty and magnitudes/signs are canonical. Result and
 61    /// product scratch must be disjoint from both inputs and each other. Scratch
 62    /// needs one result coefficient. Every intermediate magnitude and nonzero
 63    /// product degree must fit the result; padded zero input slots may exceed
 64    /// its degree capacity. No content normalization or rounding is performed.
 65    /// </remarks>
 66    internal static void MultiplyFiniteAxisPolynomials(
 67        ReadOnlySpan<ulong> left, ReadOnlySpan<sbyte> leftSigns,
 68        ReadOnlySpan<ulong> right, ReadOnlySpan<sbyte> rightSigns,
 69        Span<ulong> result, Span<sbyte> resultSigns, Span<ulong> product,
 70        bool accumulate = false)
 71    {
 109026472        int leftWords = left.Length / leftSigns.Length;
 109026473        int rightWords = right.Length / rightSigns.Length;
 109026474        int resultWords = result.Length / resultSigns.Length;
 109026475        product = product[..resultWords];
 109026476        if (!accumulate)
 77        {
 85797178            result.Clear();
 85797179            resultSigns.Clear();
 80        }
 596264881        for (int first = 0; first < leftSigns.Length; first++)
 82        {
 189106083            if (leftSigns[first] == 0)
 84                continue;
 1020235685            for (int second = 0; second < rightSigns.Length; second++)
 86            {
 360923387                if (rightSigns[second] == 0)
 88                    continue;
 276010089                WideArithmetic.MultiplyMagnitudes(left.Slice(first * leftWords, leftWords),
 276010090                    right.Slice(second * rightWords, rightWords), product);
 276010091                int index = first + second;
 276010092                int sign = resultSigns[index];
 276010093                WideArithmetic.AddShiftedSignedMagnitude(product,
 276010094                    leftSigns[first] * rightSigns[second], 0,
 276010095                    result.Slice(index * resultWords, resultWords), ref sign);
 276010096                resultSigns[index] = (sbyte)sign;
 97            }
 98        }
 109026499    }
 100
 101    /// <summary>
 102    /// Substitutes S=2^variableShift*t in place by shifting coefficient j by
 103    /// j*variableShift bits. The nonnegative shift must fit every coefficient;
 104    /// coefficientCount is positive and the separate signs remain unchanged.
 105    /// </summary>
 106    internal static void ScaleFiniteAxisPolynomialVariable(
 107        Span<ulong> coefficients, int coefficientCount, int variableShift)
 108    {
 109        System.Diagnostics.Debug.Assert(variableShift >= 0);
 9507110        int words = coefficients.Length / coefficientCount;
 140250111        for (int index = 1; index < coefficientCount; index++)
 60618112            ShiftFiniteRootLeft(coefficients.Slice(index * words, words), checked(index * variableShift));
 9507113    }
 114
 115    /// <summary>
 116    /// Removes the common positive power of two from nonzero coefficients and
 117    /// returns its exponent. A zero polynomial returns zero. Signs are canonical,
 118    /// nonempty, and unchanged; a tuple may be normalized jointly to preserve ratios.
 119    /// </summary>
 120    internal static int NormalizeFiniteAxisPolynomialPowerOfTwo(
 121        Span<ulong> coefficients, ReadOnlySpan<sbyte> signs)
 122    {
 135906123        int words = coefficients.Length / signs.Length;
 135906124        int shift = int.MaxValue;
 2058238125        for (int index = 0; index < signs.Length; index++)
 126        {
 893213127            if (signs[index] != 0)
 787338128                shift = Math.Min(shift, CountRoundedCylinderTrailingZeroes(coefficients.Slice(index * words, words)));
 129        }
 135906130        if (shift == int.MaxValue || shift == 0)
 20380131            return 0;
 1810698132        for (int index = 0; index < signs.Length; index++)
 133        {
 789823134            if (signs[index] != 0)
 706210135                ShiftRoundedCylinderWideRight(coefficients.Slice(index * words, words), shift);
 136        }
 115526137        return shift;
 138    }
 139
 140    /// <summary>
 141    /// Removes exact common powers of two from a borrowed quadratic point without
 142    /// growing coefficients or reducing the existing square-root enclosure precision.
 143    /// Requires a nonzero homogeneous denominator; the point and all field signs remain unchanged.
 144    /// </summary>
 145    internal static void NormalizeQuadraticPointPowerOfTwo(
 146        scoped ContactQuadratic x, scoped ContactQuadratic y,
 147        scoped ContactQuadratic z, scoped ContactQuadratic denominator, Span<ulong> root)
 148    {
 2848149        int rationalPower = int.MaxValue, radicalPower = int.MaxValue;
 14240150        for (int index = 0; index < 4; index++)
 151        {
 5696152            ContactQuadratic field = index == 0 ? x : index == 1 ? y : index == 2 ? z : denominator;
 5696153            if (field.Signs[0] != 0)
 3567154                rationalPower = Math.Min(rationalPower, CountRoundedCylinderTrailingZeroes(field.Rational));
 5696155            if (field.Signs[1] != 0)
 1551156                radicalPower = Math.Min(radicalPower, CountRoundedCylinderTrailingZeroes(field.Radical));
 157        }
 158        // Preserve the prefix used by GetMagnitudeSquareRootBounds. Removing more
 159        // root bits would widen its relative enclosure and increase exact searches.
 1424160        int rootPower = GetRoundedCylinderWideLength(root) == 0 ? 0
 1424161            : Math.Min(CountRoundedCylinderTrailingZeroes(root) / 2,
 1424162                Math.Max(0, (WideArithmetic.GetMagnitudeBitLength(root) - 191) / 2));
 1424163        rootPower = Math.Min(rootPower, rationalPower);
 1424164        int commonPower = Math.Min(rationalPower,
 1424165            radicalPower == int.MaxValue ? int.MaxValue : radicalPower + rootPower);
 2344166        if (commonPower == 0) return;
 5040167        for (int index = 0; index < 4; index++)
 168        {
 2016169            ContactQuadratic field = index == 0 ? x : index == 1 ? y : index == 2 ? z : denominator;
 3319170            if (field.Signs[0] != 0) ShiftRoundedCylinderWideRight(field.Rational, commonPower);
 2016171            if (field.Signs[1] != 0 && commonPower != rootPower)
 640172                ShiftRoundedCylinderWideRight(field.Radical, commonPower - rootPower);
 173        }
 856174        if (rootPower != 0) ShiftRoundedCylinderWideRight(root, 2 * rootPower);
 504175    }
 176
 177    /// <summary>
 178    /// Writes the ordinary derivative in the current variable, clearing padded
 179    /// output slots. Nonempty input/output sign spans determine their separate
 180    /// strides; storage is disjoint and every derivative coefficient must fit.
 181    /// </summary>
 182    internal static void DifferentiateFiniteAxisPolynomial(
 183        ReadOnlySpan<ulong> coefficients, ReadOnlySpan<sbyte> signs,
 184        Span<ulong> derivative, Span<sbyte> derivativeSigns)
 185    {
 10981186        int inputWords = coefficients.Length / signs.Length;
 10981187        int resultWords = derivative.Length / derivativeSigns.Length;
 10981188        derivative.Clear();
 10981189        derivativeSigns.Clear();
 101274190        for (int index = 1; index < signs.Length; index++)
 191        {
 39656192            if (signs[index] == 0)
 193                continue;
 37782194            MultiplyRoundedCylinderWideByWord(coefficients.Slice(index * inputWords, inputWords),
 37782195                (ulong)index, derivative.Slice((index - 1) * resultWords, resultWords));
 37782196            derivativeSigns[index - 1] = signs[index];
 197        }
 10981198    }
 199}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.PolynomialRoot.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.PolynomialRoot.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/// <content>
 14/// Retained positive real roots used by finite-axis contact features. All
 15/// coefficients use caller-sized, low-to-high sign/magnitude slots. No root
 16/// is replaced by a rounded scalar, and exact zero is not a tolerance test.
 17/// </content>
 18internal static partial class WideFiniteAxisIntersection
 19{
 20    /// <summary>
 21    /// Returns storage for an isolating dyadic cell of a degree-at-most-four
 22    /// integer polynomial. The input and cell must outlive the retained root.
 23    /// </summary>
 24    // Isolation needs fewer than 9B+64 numerator bits. Later refinements stop
 25    // at shift 8B+128; root < 2^(B+1) then needs at most 9B+129 bits.
 26    // Reserve at least 9B+192 bits, including the upper endpoint increment.
 27    internal static int GetFiniteAxisRootCellWords(ReadOnlySpan<ulong> coefficients,
 28        ReadOnlySpan<sbyte> signs) =>
 52029        (9 * GetFiniteRootCoefficientBits(coefficients, signs.Length) + 255) / 64;
 30
 31    internal static bool TryGetLargestPositiveFiniteAxisRoot(ReadOnlySpan<ulong> coefficients,
 32        ReadOnlySpan<sbyte> signs, Span<ulong> cell, out FiniteAxisPolynomialRoot root)
 33    {
 68934        root = default;
 68935        int words = coefficients.Length / signs.Length;
 68936        int degree = signs.Length - 1;
 69037        while (degree >= 0 && signs[degree] == 0)
 138            degree--;
 68939        if (degree <= 0)
 340            return false;
 41
 42        // Zero is excluded. Removing its factors also makes zero a valid
 43        // nonroot lower bound for the open isolation interval.
 68644        int first = 0;
 78745        while (signs[first] == 0)
 10146            first++;
 68647        degree -= first;
 68648        if (degree == 0)
 449            return false;
 68250        root.Coefficients = coefficients.Slice(first * words, (degree + 1) * words);
 68251        root.Signs = signs.Slice(first, degree + 1);
 68252        root.CoefficientBits = GetFiniteRootCoefficientBits(root.Coefficients, degree + 1);
 68253        root.LowerNumerator = cell;
 68254        cell.Clear();
 55
 68256        int sturmWords = (7 * root.CoefficientBits + 95) / 64;
 68257        Span<ulong> sturm = stackalloc ulong[25 * sturmWords];
 68258        Span<sbyte> sturmSigns = stackalloc sbyte[25];
 68259        Span<int> degrees = stackalloc int[5];
 68260        int count = BuildFiniteRootSturm(root, sturm, sturmSigns, degrees);
 68261        int infinity = GetRoundedCylinderInfinityVariations(sturmSigns, degrees, count, false);
 68262        int remaining = GetFiniteRootVariations(sturm, sturmSigns, degrees, count,
 68263            cell, 0) - infinity;
 68264        if (remaining == 0)
 65        {
 17566            root = default;
 17567            return false;
 68        }
 69
 70        // Cauchy's bound is |root| < 1 + H/|leading| <= 1 + 2^B.
 71        // Doubling therefore takes at most B+1 steps, but ordinary roots
 72        // do not pay for unused high coefficient bits.
 50773        cell[0] = 1;
 50774        int upperShift = 0;
 140275        while (GetFiniteRootVariations(sturm, sturmSigns, degrees, count,
 140276                   cell, upperShift) != infinity)
 89577            upperShift--;
 50778        root.DenominatorShift = upperShift;
 50779        if (EvaluateFiniteRootPolynomial(root.Coefficients, root.Signs, cell, upperShift, 0) == 0)
 80        {
 1081            root.IsRational = true;
 1082            return true;
 83        }
 49784        cell.Clear();
 85
 86        // The square-free part has height <= 2^4 sqrt(5) H (Mahler's
 87        // factor bound). Its nonzero integer discriminant, together with
 88        // |ri-rj| <= 4Hsf, gives separation > 2^(-8B-58). Starting from
 89        // width <=2^(B+1), fewer than 9B+64 bisections isolate a root.
 90        // This proves both termination and the caller cell's bit capacity.
 91        // A positive lower endpoint also makes this a relative-scale cell.
 92        // Reciprocal Cauchy bounds give root >2^(-B-1), so finding it takes
 93        // at most B+2 additional bisections and stays within the same bound.
 1315994        while (remaining != 1 || GetRoundedCylinderWideLength(cell) == 0
 1315995               || EvaluateFiniteRootPolynomial(root.Coefficients,
 1315996                   root.Signs, cell, root.DenominatorShift, 0) == 0)
 97        {
 1266898            remaining = RefineFiniteRoot(ref root, sturm, sturmSigns, degrees, count, infinity, remaining);
 1266899            if (root.IsRational)
 100                break;
 101        }
 497102        if (!root.IsRational)
 103        {
 491104            Span<ulong> upper = stackalloc ulong[cell.Length];
 491105            cell.CopyTo(upper);
 491106            AddRoundedCylinderWord(upper, 0, 1);
 491107            int lowerSign = EvaluateFiniteRootPolynomial(root.Coefficients, root.Signs,
 491108                cell, root.DenominatorShift, 0);
 491109            int upperSign = EvaluateFiniteRootPolynomial(root.Coefficients, root.Signs,
 491110                upper, root.DenominatorShift, 0);
 491111            root.CrossingSign = lowerSign != upperSign ? (sbyte)lowerSign : (sbyte)0;
 112        }
 497113        return true;
 114    }
 115
 116    /// <summary>
 117    /// Evaluates the exact sign of a degree-at-most-eight integer polynomial
 118    /// at the retained root, including equality and repeated roots.
 119    /// </summary>
 120    internal static int GetSignAtFiniteAxisRoot(ref FiniteAxisPolynomialRoot root,
 121        scoped ReadOnlySpan<ulong> coefficients, scoped ReadOnlySpan<sbyte> signs)
 122    {
 8221123        int degree = signs.Length - 1;
 8407124        while (degree > 0 && signs[degree] == 0)
 186125            degree--;
 8221126        if (degree == 0)
 981127            return signs[0];
 7240128        int words = coefficients.Length / signs.Length;
 7240129        coefficients = coefficients[..((degree + 1) * words)];
 7240130        signs = signs[..(degree + 1)];
 7240131        if (root.IsRational)
 19132            return EvaluateFiniteRootPolynomial(coefficients, signs,
 19133                root.LowerNumerator, root.DenominatorShift, 0);
 7221134        int rootDegree = root.Signs.Length - 1;
 7221135        if (degree < rootDegree)
 4502136            return GetReducedSignAtFiniteAxisRoot(ref root, coefficients, signs);
 137
 138        // Positive pseudo-division preserves Q(alpha)'s sign. Each eliminated
 139        // coefficient multiplies by c=|leading(P)| and subtracts one product,
 140        // growing the height by at most Bp+1 bits. There are <=qDegree-pDegree+1
 141        // eliminations (at most eight), not a general polynomial PRS. This
 142        // often proves equality or a constant sign before any root refinement.
 2719143        int remainderWords = (GetFiniteRootCoefficientBits(coefficients, signs.Length)
 2719144            + (degree - rootDegree + 1) * (root.CoefficientBits + 1) + 127) / 64;
 2719145        Span<ulong> remainder = stackalloc ulong[(degree + 1) * remainderWords];
 2719146        Span<sbyte> remainderSigns = stackalloc sbyte[degree + 1];
 2719147        remainder.Clear();
 27970148        for (int index = 0; index <= degree; index++)
 149        {
 11266150            CopyFiniteRootMagnitude(coefficients.Slice(index * words, words),
 11266151                remainder.Slice(index * remainderWords, remainderWords));
 11266152            remainderSigns[index] = signs[index];
 153        }
 2719154        int remainderDegree = ReduceFiniteRootQuery(root, remainder, remainderSigns, degree);
 2719155        if (remainderDegree < 0)
 10156            return 0;
 2709157        if (remainderDegree == 0)
 1186158            return remainderSigns[0];
 1523159        return GetReducedSignAtFiniteAxisRoot(ref root,
 1523160            remainder[..((remainderDegree + 1) * remainderWords)], remainderSigns[..(remainderDegree + 1)]);
 161    }
 162
 163    private static int GetReducedSignAtFiniteAxisRoot(ref FiniteAxisPolynomialRoot root,
 164        scoped ReadOnlySpan<ulong> coefficients, scoped ReadOnlySpan<sbyte> signs)
 165    {
 6025166        int certified = GetFiniteRootIntervalSign(root, coefficients, signs);
 6025167        if (certified != 0)
 3194168            return certified;
 169
 2831170        if (signs.Length == 2)
 171        {
 172            // Linear interval evaluation is exact. An uncertified sign puts
 173            // its zero -q0/q1 inside this positive closed cell, so q0 and q1
 174            // are nonzero and opposite. The cell has exactly one distinct
 175            // P root and neither endpoint is a root. Evaluate P at that
 176            // positive fraction to prove equality, or locate a crossing root.
 847177            int words = coefficients.Length / 2;
 847178            int boundarySign = EvaluateFiniteRootAtPositiveFraction(root,
 847179                coefficients[..words], coefficients[words..]);
 847180            if (boundarySign == 0)
 78181                return 0;
 769182            if (root.CrossingSign != 0)
 736183                return boundarySign == root.CrossingSign ? signs[1] : -signs[1];
 184        }
 185
 186        // Sixty-four exact refinements are only a fast-path budget, never an
 187        // approximate decision. The Hermite query below handles every
 188        // remaining case, including shared roots and arbitrarily close values.
 2017189        if (root.DenominatorShift < 8 * root.CoefficientBits + 128)
 190        {
 2012191            int sturmWords = root.CrossingSign == 0 ? (7 * root.CoefficientBits + 95) / 64 : 0;
 2012192            Span<ulong> sturm = stackalloc ulong[25 * sturmWords];
 2012193            Span<sbyte> sturmSigns = stackalloc sbyte[25];
 2012194            Span<int> degrees = stackalloc int[5];
 2012195            int count = root.CrossingSign == 0 ? BuildFiniteRootSturm(root, sturm, sturmSigns, degrees) : 0;
 2012196            int infinity = root.CrossingSign == 0
 2012197                ? GetRoundedCylinderInfinityVariations(sturmSigns, degrees, count, false) : 0;
 12395198            for (int attempt = 0; attempt < 64
 18754199                 && root.DenominatorShift < 8 * root.CoefficientBits + 128; attempt++)
 200            {
 10307201                if (root.CrossingSign == 0)
 2467202                    RefineFiniteRoot(ref root, sturm, sturmSigns, degrees, count, infinity, 1);
 203                else
 7840204                    RefineFiniteCrossingRoot(ref root);
 10307205                if (root.IsRational)
 12206                    return EvaluateFiniteRootPolynomial(coefficients, signs,
 12207                        root.LowerNumerator, root.DenominatorShift, 0);
 10295208                certified = GetFiniteRootIntervalSign(root, coefficients, signs);
 10295209                if (certified != 0)
 1924210                    return certified;
 211            }
 212        }
 81213        return GetFiniteRootHermiteIntervalSign(root, coefficients, signs);
 214    }
 215
 216    private static int EvaluateFiniteRootAtPositiveFraction(FiniteAxisPolynomialRoot root,
 217        ReadOnlySpan<ulong> numerator, ReadOnlySpan<ulong> denominator)
 218    {
 847219        int degree = root.Signs.Length - 1;
 847220        int inputWords = root.Coefficients.Length / root.Signs.Length;
 221        // Homogeneous Horner evaluation computes denominator^degree * P(n/d).
 222        // At most five terms need Bp + degree*max(Bn,Bd) + 3 magnitude bits.
 847223        int words = (root.CoefficientBits
 847224            + degree * Math.Max(GetFiniteRootBits(numerator), GetFiniteRootBits(denominator)) + 66) / 64;
 847225        Span<ulong> result = stackalloc ulong[words];
 847226        Span<ulong> denominatorPower = stackalloc ulong[words];
 847227        Span<ulong> first = stackalloc ulong[words];
 847228        Span<ulong> second = stackalloc ulong[words];
 847229        Span<ulong> sum = stackalloc ulong[words];
 847230        CopyFiniteRootMagnitude(root.Coefficients.Slice(degree * inputWords, inputWords), result);
 847231        sbyte resultSign = root.Signs[degree];
 847232        denominatorPower.Clear();
 847233        denominatorPower[0] = 1;
 5490234        for (int index = degree - 1; index >= 0; index--)
 235        {
 1898236            MultiplyRoundedCylinderWide(denominatorPower, denominator, first);
 1898237            first.CopyTo(denominatorPower);
 1898238            MultiplyRoundedCylinderWide(result, numerator, first);
 1898239            MultiplyRoundedCylinderWide(root.Coefficients.Slice(index * inputWords, inputWords),
 1898240                denominatorPower, second);
 1898241            AddRoundedCylinderSigned(first, resultSign, second, root.Signs[index], sum, out resultSign);
 1898242            sum.CopyTo(result);
 243        }
 847244        return resultSign;
 245    }
 246
 247    private static void RefineFiniteCrossingRoot(ref FiniteAxisPolynomialRoot root) =>
 7840248        RefineFiniteCrossingRoot(root.Coefficients, root.Signs, root.LowerNumerator,
 7840249            ref root.DenominatorShift, ref root.IsRational, root.CrossingSign);
 250
 251    private static void RefineFiniteCrossingRoot(scoped ReadOnlySpan<ulong> coefficients,
 252        scoped ReadOnlySpan<sbyte> signs, scoped Span<ulong> lowerNumerator,
 253        ref int denominatorShift, ref bool isRational, int crossingSign,
 254        int unitIntervalCoefficientBits = 0)
 255    {
 256        // Opposite endpoint signs and exactly one distinct enclosed root
 257        // permit ordinary sign bisection, even for an odd multiple root.
 258        // No Sturm sequence is needed again unless the root does not cross.
 653823259        ShiftFiniteRootLeft(lowerNumerator, 1);
 653823260        AddRoundedCylinderWord(lowerNumerator, 0, 1);
 653823261        denominatorShift++;
 262        // A value-root midpoint lies in (0,1). Reuse the normalized Horner
 263        // certificate: coefficient and product truncation together contribute
 264        // less than 2*(degree+1) units. Nonzero results therefore prove the
 265        // exact sign. An uncertain result still uses full integer evaluation,
 266        // including every exact dyadic root and ill-conditioned crossing.
 267        // The wider quartic root domain keeps its existing exact evaluator.
 268        // This precision is a work budget, not an acceptance tolerance. The
 269        // extra degree*k bits cover small-x attenuation; for a retained
 270        // positive cell, k <= min(shift, coefficientBits+3) by Cauchy's bound.
 653823271        int sign = unitIntervalCoefficientBits == 0 ? 0 : GetFiniteValueApproximateSign(
 653823272            lowerNumerator, denominatorShift, coefficients, signs,
 653823273            denominatorShift + FiniteValuePointGuardBits + (signs.Length - 1)
 653823274                * (denominatorShift - GetFiniteRootBits(lowerNumerator) + 1),
 653823275            unitIntervalCoefficientBits);
 653823276        if (sign == 0)
 169902277            sign = EvaluateFiniteRootPolynomial(coefficients, signs,
 169902278                lowerNumerator, denominatorShift, 0);
 653823279        if (sign == 0)
 589280            isRational = true;
 653234281        else if (sign != crossingSign)
 325634282            lowerNumerator[0]--;
 653234283    }
 284
 285    private static int ReduceFiniteRootQuery(FiniteAxisPolynomialRoot root,
 286        Span<ulong> remainder, Span<sbyte> signs, int degree)
 287    {
 6876288        while (degree >= root.Signs.Length - 1)
 289        {
 4167290            ReduceFiniteRootQueryStep(root, remainder, signs, degree);
 9711291            while (degree >= 0 && signs[degree] == 0)
 5544292                degree--;
 4167293            if (degree < 0)
 10294                return -1;
 4157295            NormalizeFiniteAxisPolynomialPowerOfTwo(remainder, signs);
 296        }
 2709297        return degree;
 298    }
 299
 300    [MethodImpl(MethodImplOptions.NoInlining)] // Return the three product buffers after each step.
 301    private static void ReduceFiniteRootQueryStep(FiniteAxisPolynomialRoot root,
 302        Span<ulong> remainder, Span<sbyte> signs, int degree)
 303    {
 4301304        int words = remainder.Length / signs.Length;
 4301305        int rootDegree = root.Signs.Length - 1;
 4301306        int rootWords = root.Coefficients.Length / root.Signs.Length;
 4301307        ReadOnlySpan<ulong> leading = root.Coefficients.Slice(rootDegree * rootWords, rootWords);
 4301308        Span<ulong> factor = stackalloc ulong[words];
 4301309        Span<ulong> first = stackalloc ulong[words];
 4301310        Span<ulong> second = stackalloc ulong[words];
 4301311        remainder.Slice(degree * words, words).CopyTo(factor);
 4301312        sbyte factorSign = (sbyte)(signs[degree] * root.Signs[rootDegree]);
 4301313        int offset = degree - rootDegree;
 49902314        for (int index = 0; index <= degree; index++)
 315        {
 20650316            Span<ulong> coefficient = remainder.Slice(index * words, words);
 20650317            MultiplyRoundedCylinderWide(leading, coefficient, first);
 20650318            sbyte secondSign = 0;
 20650319            second.Clear();
 20650320            if (index >= offset)
 321            {
 16987322                MultiplyRoundedCylinderWide(factor,
 16987323                    root.Coefficients.Slice((index - offset) * rootWords, rootWords), second);
 16987324                secondSign = (sbyte)(factorSign * root.Signs[index - offset]);
 325            }
 20650326            SubtractRoundedCylinderSigned(first, signs[index], second, secondSign,
 20650327                coefficient, out signs[index]);
 328        }
 4301329    }
 330
 331    /// <summary>
 332    /// Sizes a paired positive pseudo-remainder after canceling common powers
 333    /// of the strictly positive root. Inputs have the same padded count and
 334    /// a denominator nonzero at the root. The returned slice applies to both.
 335    /// </summary>
 336    internal static int GetFiniteAxisRootRatioWords(FiniteAxisPolynomialRoot root,
 337        ReadOnlySpan<ulong> numerator, ReadOnlySpan<sbyte> numeratorSigns,
 338        ReadOnlySpan<ulong> denominator, ReadOnlySpan<sbyte> denominatorSigns,
 339        out int first, out int count)
 340    {
 67341        first = 0;
 67342        int last = numeratorSigns.Length - 1;
 70343        while (last > 0 && numeratorSigns[last] == 0 && denominatorSigns[last] == 0)
 3344            last--;
 109345        while (first < last && numeratorSigns[first] == 0 && denominatorSigns[first] == 0)
 42346            first++;
 67347        count = last - first + 1;
 67348        int bits = Math.Max(GetFiniteRootCoefficientBits(numerator, numeratorSigns.Length),
 67349            GetFiniteRootCoefficientBits(denominator, denominatorSigns.Length));
 350        // Each positive pseudo-step adds at most Bp+1 bits, including the
 351        // subtraction carry. Joint degree drops can only reduce this count.
 67352        int steps = Math.Max(0, count - root.Signs.Length + 1);
 67353        return (bits + steps * (root.CoefficientBits + 1) + 127) / 64;
 354    }
 355
 356    /// <summary>
 357    /// Reduces N and D at the same retained root with identical positive
 358    /// scale factors. Paired output storage has two equally sized polynomial
 359    /// halves; each keeps the input count even when the returned count shrinks.
 360    /// </summary>
 361    internal static int ReduceFiniteAxisRootRatio(FiniteAxisPolynomialRoot root,
 362        ReadOnlySpan<ulong> numerator, ReadOnlySpan<sbyte> numeratorSigns,
 363        ReadOnlySpan<ulong> denominator, ReadOnlySpan<sbyte> denominatorSigns,
 364        Span<ulong> pair, Span<sbyte> pairSigns)
 365    {
 67366        int count = numeratorSigns.Length;
 67367        int words = pair.Length / (2 * count);
 67368        int sourceWords = numerator.Length / count;
 67369        int denominatorWords = denominator.Length / count;
 67370        pair.Clear();
 67371        numeratorSigns.CopyTo(pairSigns[..count]);
 67372        denominatorSigns.CopyTo(pairSigns[count..]);
 710373        for (int index = 0; index < count; index++)
 374        {
 288375            CopyFiniteRootMagnitude(numerator.Slice(index * sourceWords, sourceWords),
 288376                pair.Slice(index * words, words));
 288377            CopyFiniteRootMagnitude(denominator.Slice(index * denominatorWords, denominatorWords),
 288378                pair.Slice((count + index) * words, words));
 379        }
 67380        int degree = count - 1;
 134381        while (degree >= root.Signs.Length - 1)
 382        {
 383            // Even a zero leading coefficient must receive |leading(P)|:
 384            // independent reductions would change N/D's relative scale.
 67385            ReduceFiniteRootQueryStep(root, pair[..(count * words)], pairSigns[..count], degree);
 67386            ReduceFiniteRootQueryStep(root, pair[(count * words)..], pairSigns[count..], degree);
 164387            while (degree > 0 && pairSigns[degree] == 0 && pairSigns[count + degree] == 0)
 97388                degree--;
 389        }
 67390        NormalizeFiniteAxisPolynomialPowerOfTwo(pair, pairSigns);
 67391        return degree + 1;
 392    }
 393
 394    private static int BuildFiniteRootSturm(FiniteAxisPolynomialRoot root,
 395        Span<ulong> sturm, Span<sbyte> signs, Span<int> degrees)
 396    {
 744397        sturm.Clear();
 744398        signs.Clear();
 744399        degrees.Clear();
 744400        int words = root.Coefficients.Length / root.Signs.Length;
 8448401        for (int index = 0; index < root.Signs.Length; index++)
 402        {
 3480403            CopyFiniteRootMagnitude(root.Coefficients.Slice(index * words, words), GetRoundedCylinderCoefficient(sturm, 
 3480404            signs[index] = root.Signs[index];
 405        }
 744406        degrees[0] = root.Signs.Length - 1;
 744407        return BuildFiniteAxisPolynomialSturmSequence(sturm, signs, degrees);
 408    }
 409
 410    private static int RefineFiniteRoot(ref FiniteAxisPolynomialRoot root,
 411        scoped Span<ulong> sturm, scoped Span<sbyte> signs, scoped Span<int> degrees,
 412        int count, int infinity, int remaining)
 413    {
 15135414        ShiftFiniteRootLeft(root.LowerNumerator, 1);
 15135415        AddRoundedCylinderWord(root.LowerNumerator, 0, 1);
 15135416        root.DenominatorShift++;
 15135417        int right = GetFiniteRootVariations(sturm, signs, degrees, count,
 15135418            root.LowerNumerator, root.DenominatorShift) - infinity;
 15135419        if (right != 0)
 1814420            return right;
 13321421        if (EvaluateFiniteRootPolynomial(root.Coefficients, root.Signs,
 13321422                root.LowerNumerator, root.DenominatorShift, 0) == 0)
 423        {
 12424            root.IsRational = true;
 12425            return 1;
 426        }
 427        // The midpoint numerator is odd, so this subtraction cannot borrow.
 13309428        root.LowerNumerator[0]--;
 13309429        return remaining;
 430    }
 431
 432    private static int GetFiniteRootVariations(Span<ulong> sturm, Span<sbyte> signs,
 433        Span<int> degrees, int count, ReadOnlySpan<ulong> numerator, int shift)
 434    {
 17219435        int previous = 0;
 17219436        int variations = 0;
 17219437        int words = sturm.Length / 25;
 17219438        int numeratorBits = GetFiniteRootBits(numerator);
 186312439        for (int index = 0; index < count; index++)
 440        {
 75937441            int degree = degrees[index];
 75937442            ReadOnlySpan<ulong> row = sturm.Slice(index * 5 * words, (degree + 1) * words);
 75937443            ReadOnlySpan<sbyte> rowSigns = signs.Slice(index * 5, degree + 1);
 75937444            int sign = 0;
 75937445            if (degree > 0 && numeratorBits > 0 && numeratorBits <= shift)
 446            {
 447                // Reuse the value-root point certificate only in (0,1).
 448                // Nonzero certifies the exact sign; uncertainty must still
 449                // use the first nonzero derivative for Sturm's right limit.
 51458450                int precision = shift + FiniteValuePointGuardBits
 51458451                    + degree * (shift - numeratorBits + 1);
 51458452                sign = GetFiniteValueApproximateSign(numerator, shift, row, rowSigns,
 51458453                    precision, GetFiniteRootCoefficientBits(row, rowSigns.Length));
 454            }
 201892455            for (int order = 0; order <= degree && sign == 0; order++)
 456            {
 25009457                sign = EvaluateFiniteRootPolynomial(row, rowSigns, numerator, shift, order);
 458            }
 75937459            if (previous != 0 && previous != sign)
 14023460                variations++;
 75937461            previous = sign;
 462        }
 17219463        return variations;
 464    }
 465
 466    internal static int EvaluateFiniteRootPolynomial(ReadOnlySpan<ulong> coefficients,
 467        ReadOnlySpan<sbyte> signs, ReadOnlySpan<ulong> numerator, int shift, int derivative)
 468    {
 334883469        if (signs.Length - 1 == derivative)
 14076470            return signs[derivative];
 320807471        int words = GetFiniteRootEvaluationWords(coefficients, signs, numerator, shift, derivative,
 320807472            out int coefficientWords);
 320807473        Span<ulong> scratch = stackalloc ulong[2 * words + coefficientWords + 1];
 320807474        return EvaluateFiniteRootPolynomialCore(coefficients, signs, numerator, shift, derivative,
 320807475            words, coefficientWords, scratch);
 476    }
 477
 478    private static int EvaluateFiniteRootPolynomial(ReadOnlySpan<ulong> coefficients,
 479        ReadOnlySpan<sbyte> signs, ReadOnlySpan<ulong> numerator, int shift, int derivative,
 480        Span<ulong> scratch)
 481    {
 233444482        if (signs.Length - 1 == derivative)
 71740483            return signs[derivative];
 161704484        int words = GetFiniteRootEvaluationWords(coefficients, signs, numerator, shift, derivative,
 161704485            out int coefficientWords);
 161704486        return EvaluateFiniteRootPolynomialCore(coefficients, signs, numerator, shift, derivative,
 161704487            words, coefficientWords, scratch);
 488    }
 489
 490    private static int GetFiniteRootEvaluationWords(ReadOnlySpan<ulong> coefficients,
 491        ReadOnlySpan<sbyte> signs, ReadOnlySpan<ulong> numerator, int shift, int derivative,
 492        out int coefficientWords)
 493    {
 482511494        int boundBits = Math.Max(GetFiniteRootBits(numerator) + Math.Max(-shift, 0), Math.Max(shift, 0) + 1);
 482511495        int coefficientBits = GetFiniteRootCoefficientBits(coefficients, signs.Length);
 482511496        coefficientWords = (coefficientBits + 63) / 64;
 482511497        return (coefficientBits
 482511498            + (signs.Length - 1 - derivative) * boundBits + 95) / 64;
 499    }
 500
 501    private static int EvaluateFiniteRootPolynomialCore(ReadOnlySpan<ulong> coefficients,
 502        ReadOnlySpan<sbyte> signs, ReadOnlySpan<ulong> numerator, int shift, int derivative,
 503        int words, int coefficientWords, Span<ulong> scratch)
 504    {
 482511505        if (GetRoundedCylinderWideLength(numerator) == 0)
 33424506            return signs[derivative];
 449087507        int degree = signs.Length - 1;
 449087508        int inputWords = coefficients.Length / signs.Length;
 449087509        Span<ulong> result = scratch[..words];
 449087510        Span<ulong> product = scratch.Slice(words, words);
 511        // Homogeneous Horner needs only two large accumulators. Borrow the
 512        // numerator and add shifted coefficients directly; derivative factors
 513        // fit in one word, so their temporary needs just one extra input word.
 449087514        Span<ulong> term = scratch.Slice(2 * words, coefficientWords + 1);
 449087515        result.Clear();
 449087516        int resultSign = 0;
 5995980517        for (int index = degree; index >= derivative; index--)
 518        {
 2548903519            WideArithmetic.MultiplyMagnitudes(result, numerator, product);
 2548903520            if (shift < 0)
 16536521                ShiftFiniteRootLeft(product, -shift);
 2548903522            ulong binomial = 1;
 5173294523            for (int factor = 1; factor <= derivative; factor++)
 37744524                binomial = binomial * (ulong)(index - factor + 1) / (ulong)factor;
 2548903525            MultiplyRoundedCylinderWideByWord(coefficients.Slice(index * inputWords, inputWords), binomial, term);
 2548903526            WideArithmetic.AddShiftedSignedMagnitude(term, signs[index],
 2548903527                shift > 0 ? (degree - index) * shift : 0, product, ref resultSign);
 2548903528            Span<ulong> swap = result;
 2548903529            result = product;
 2548903530            product = swap;
 531        }
 449087532        return resultSign;
 533    }
 534
 535    /// <summary>
 536    /// Bounds the integer floor of sqrt(N(alpha)/D(alpha)) at a retained root.
 537    /// The caller proves D(alpha)>0 and 0&lt;=N(alpha)/D(alpha)&lt;=cap^2. Both
 538    /// polynomials retain the same padded coefficient count: their homogeneous
 539    /// interval scales must cancel. An uncertain denominator returns [0,cap].
 540    /// Inputs and root storage are borrowed without mutation.
 541    /// </summary>
 542    [MethodImpl(MethodImplOptions.NoInlining)] // Return interval/division scratch before exact sign queries.
 543    internal static void GetFiniteAxisRootSquareRootBounds(FiniteAxisPolynomialRoot root,
 544        ReadOnlySpan<ulong> numerator, ReadOnlySpan<sbyte> numeratorSigns,
 545        ReadOnlySpan<ulong> denominator, ReadOnlySpan<sbyte> denominatorSigns,
 546        ulong cap, out ulong lowerFloor, out ulong upperFloor)
 547    {
 482548        lowerFloor = 0;
 482549        upperFloor = cap;
 482550        int degree = numeratorSigns.Length - 1;
 482551        int boundBits = Math.Max(GetFiniteRootBits(root.LowerNumerator) + 1
 482552            + Math.Max(-root.DenominatorShift, 0), Math.Max(root.DenominatorShift, 0) + 1);
 482553        int bits = Math.Max(GetFiniteRootCoefficientBits(numerator, numeratorSigns.Length),
 482554            GetFiniteRootCoefficientBits(denominator, denominatorSigns.Length));
 482555        int words = Math.Max(2, (bits + degree * boundBits + 95) / 64);
 482556        Span<ulong> bounds = stackalloc ulong[4 * words];
 964557        Span<ulong> nMin = bounds[..words], nMax = bounds.Slice(words, words);
 964558        Span<ulong> dMin = bounds.Slice(2 * words, words), dMax = bounds.Slice(3 * words, words);
 482559        GetFiniteRootPolynomialBounds(root, numerator, numeratorSigns, nMin, nMax,
 482560            out int minimumSign, out _);
 482561        GetFiniteRootPolynomialBounds(root, denominator, denominatorSigns, dMin, dMax,
 482562            out int denominatorSign, out _);
 482563        if (denominatorSign <= 0)
 2564            return;
 565        // Positive denominator bounds permit independent division bounds.
 566        // A negative N minimum is dependency in the interval, not permission
 567        // to take a square root of it; the proven nonnegative value bounds it by 0.
 480568        if (minimumSign > 0)
 466569            lowerFloor = WideArithmetic.GetRatioFloorSquareRoot(nMin, dMax, cap);
 480570        upperFloor = WideArithmetic.GetRatioFloorSquareRoot(nMax, dMin, cap);
 480571    }
 572
 573    private static int GetFiniteRootIntervalSign(FiniteAxisPolynomialRoot root,
 574        ReadOnlySpan<ulong> coefficients, ReadOnlySpan<sbyte> signs)
 575    {
 16320576        int degree = signs.Length - 1;
 16320577        int boundBits = Math.Max(GetFiniteRootBits(root.LowerNumerator) + 1
 16320578            + Math.Max(-root.DenominatorShift, 0), Math.Max(root.DenominatorShift, 0) + 1);
 16320579        int words = (GetFiniteRootCoefficientBits(coefficients, signs.Length) + degree * boundBits + 95) / 64;
 16320580        Span<ulong> minimum = stackalloc ulong[words];
 16320581        Span<ulong> maximum = stackalloc ulong[words];
 16320582        GetFiniteRootPolynomialBounds(root, coefficients, signs, minimum, maximum,
 16320583            out int minimumSign, out int maximumSign);
 16320584        return minimumSign > 0 ? 1 : maximumSign < 0 ? -1 : 0;
 585    }
 586
 587    private static void GetFiniteRootPolynomialBounds(FiniteAxisPolynomialRoot root,
 588        ReadOnlySpan<ulong> coefficients, ReadOnlySpan<sbyte> signs,
 589        Span<ulong> minimum, Span<ulong> maximum, out int minimumSign, out int maximumSign)
 590    {
 17284591        int degree = signs.Length - 1;
 17284592        int inputWords = coefficients.Length / signs.Length;
 593        // Constants need no interval coordinates. A deeply retained numerator
 594        // may be wider than either coefficient, without affecting this bound.
 17284595        if (degree == 0)
 596        {
 4597            CopyFiniteRootMagnitude(coefficients, minimum);
 4598            minimum.CopyTo(maximum);
 4599            minimumSign = maximumSign = signs[0];
 4600            return;
 601        }
 17280602        int words = minimum.Length;
 17280603        Span<ulong> lower = stackalloc ulong[words];
 17280604        Span<ulong> upper = stackalloc ulong[words];
 17280605        Span<ulong> product = stackalloc ulong[words];
 17280606        Span<ulong> term = stackalloc ulong[words];
 17280607        Span<ulong> sum = stackalloc ulong[words];
 17280608        lower.Clear();
 17280609        root.LowerNumerator[..GetRoundedCylinderWideLength(root.LowerNumerator)].CopyTo(lower);
 17280610        lower.CopyTo(upper);
 17280611        if (!root.IsRational)
 17278612            AddRoundedCylinderWord(upper, 0, 1);
 17280613        if (root.DenominatorShift < 0)
 614        {
 661615            ShiftFiniteRootLeft(lower, -root.DenominatorShift);
 661616            ShiftFiniteRootLeft(upper, -root.DenominatorShift);
 617        }
 17280618        minimum.Clear();
 17280619        maximum.Clear();
 17280620        sbyte minSign = 0;
 17280621        sbyte maxSign = 0;
 144662622        for (int index = degree; index >= 0; index--)
 623        {
 55051624            term.Clear();
 55051625            CopyFiniteRootMagnitude(coefficients.Slice(index * inputWords, inputWords), term);
 55051626            if (root.DenominatorShift > 0)
 52507627                ShiftFiniteRootLeft(term, (degree - index) * root.DenominatorShift);
 55051628            MultiplyRoundedCylinderWide(minimum, minSign < 0 ? upper : lower, product);
 55051629            AddRoundedCylinderSigned(product, minSign, term, signs[index], sum, out minSign);
 55051630            sum.CopyTo(minimum);
 55051631            MultiplyRoundedCylinderWide(maximum, maxSign < 0 ? lower : upper, product);
 55051632            AddRoundedCylinderSigned(product, maxSign, term, signs[index], sum, out maxSign);
 55051633            sum.CopyTo(maximum);
 634        }
 17280635        minimumSign = minSign;
 17280636        maximumSign = maxSign;
 17280637    }
 638
 639    /// <summary>
 640    /// Maximum unsigned coefficient bit length in a uniformly padded,
 641    /// little-endian polynomial with count >= 1; all-zero input returns zero.
 642    /// </summary>
 643    internal static int GetFiniteRootCoefficientBits(ReadOnlySpan<ulong> coefficients, int count)
 644    {
 1353430645        int words = coefficients.Length / count;
 1353430646        int bits = 0;
 16082292647        for (int index = 0; index < count; index++)
 6687716648            bits = Math.Max(bits, GetFiniteRootBits(coefficients.Slice(index * words, words)));
 1353430649        return bits;
 650    }
 651
 652    private static int GetFiniteRootBits(ReadOnlySpan<ulong> value)
 653    {
 9551938654        int words = GetRoundedCylinderWideLength(value);
 9551938655        return words == 0 ? 0 : words * 64 - Fixed64.CountLeadingZeroes(value[words - 1]);
 656    }
 657
 658    private static void CopyFiniteRootMagnitude(ReadOnlySpan<ulong> source, Span<ulong> destination)
 659    {
 5747922660        destination.Clear();
 5747922661        source[..GetRoundedCylinderWideLength(source)].CopyTo(destination);
 5747922662    }
 663
 664    private static void ShiftFiniteRootLeft(Span<ulong> value, int bits)
 665    {
 2385776666        int wordShift = bits >> 6;
 2385776667        int bitShift = bits & 63;
 547759612668        for (int index = value.Length - 1; index >= 0; index--)
 669        {
 271494030670            int source = index - wordShift;
 271494030671            ulong low = source >= 0 ? value[source] : 0;
 271494030672            ulong high = source > 0 ? value[source - 1] : 0;
 271494030673            value[index] = (low << bitShift) | (bitShift == 0 ? 0 : high >> (64 - bitShift));
 674        }
 2385776675    }
 676}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.PolynomialRootSigns.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.PolynomialRootSigns.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>
 13/// Exact sign-at-root fallback via the signature of a four-by-four Hermite
 14/// trace form. This also detects equality at multiple algebraic roots.
 15/// </content>
 16internal static partial class WideFiniteAxisIntersection
 17{
 18    private static int GetFiniteRootHermiteIntervalSign(FiniteAxisPolynomialRoot root,
 19        ReadOnlySpan<ulong> query, ReadOnlySpan<sbyte> querySigns)
 20    {
 8121        int degree = querySigns.Length - 1;
 8122        int queryWords = query.Length / querySigns.Length;
 8123        int boundBits = Math.Max(GetFiniteRootBits(root.LowerNumerator) + 1
 8124            + Math.Max(-root.DenominatorShift, 0), Math.Max(root.DenominatorShift, 0) + 1);
 8125        int words = (GetFiniteRootCoefficientBits(query, querySigns.Length) + boundBits + 65) / 64;
 8126        Span<ulong> weighted = stackalloc ulong[(degree + 2) * words];
 8127        Span<sbyte> weightedSigns = stackalloc sbyte[degree + 2];
 8128        Span<ulong> bound = stackalloc ulong[words];
 8129        Span<ulong> first = stackalloc ulong[words];
 8130        Span<ulong> second = stackalloc ulong[words];
 8131        int lowerQuery = 0;
 8132        int upperQuery = 0;
 48633        for (int endpoint = 0; endpoint < 2; endpoint++)
 34        {
 16235            CopyFiniteRootMagnitude(root.LowerNumerator, bound);
 16236            if (endpoint != 0)
 8137                AddRoundedCylinderWord(bound, 0, 1);
 16238            if (root.DenominatorShift < 0)
 639                ShiftFiniteRootLeft(bound, -root.DenominatorShift);
 16240            weighted.Clear();
 16241            weightedSigns.Clear();
 164042            for (int index = 0; index <= degree + 1; index++)
 43            {
 65844                first.Clear();
 65845                second.Clear();
 65846                sbyte firstSign = 0;
 65847                sbyte secondSign = 0;
 65848                if (index != 0)
 49                {
 49650                    CopyFiniteRootMagnitude(query.Slice((index - 1) * queryWords, queryWords), first);
 49651                    if (root.DenominatorShift > 0)
 46052                        ShiftFiniteRootLeft(first, root.DenominatorShift);
 49653                    firstSign = querySigns[index - 1];
 54                }
 65855                if (index <= degree)
 56                {
 49657                    MultiplyRoundedCylinderWide(query.Slice(index * queryWords, queryWords), bound, second);
 58                    // Both retained-cell endpoints are strictly positive.
 49659                    secondSign = querySigns[index];
 60                }
 65861                SubtractRoundedCylinderSigned(first, firstSign, second, secondSign,
 65862                    weighted.Slice(index * words, words), out weightedSigns[index]);
 63            }
 16264            int signature = GetFiniteRootHermiteSignature(root, weighted, weightedSigns);
 16265            if (endpoint == 0)
 8166                lowerQuery = signature;
 67            else
 8168                upperQuery = signature;
 69        }
 70        // For every other real root, sign(x-a)-sign(x-b) cancels. The
 71        // sole root in (a,b) contributes twice its query sign. Endpoints
 72        // are nonroots by the retained-cell invariant.
 8173        return (lowerQuery - upperQuery) / 2;
 74    }
 75
 76    private static int GetFiniteRootHermiteSignature(FiniteAxisPolynomialRoot root,
 77        ReadOnlySpan<ulong> query, ReadOnlySpan<sbyte> querySigns)
 78    {
 16279        int degree = root.Signs.Length - 1;
 16280        int queryDegree = querySigns.Length - 1;
 16281        int maximumTrace = queryDegree + 2 * degree - 2;
 16282        int polynomialWords = root.Coefficients.Length / root.Signs.Length;
 16283        int queryWords = query.Length / querySigns.Length;
 16284        int pBits = root.CoefficientBits;
 16285        int qBits = GetFiniteRootCoefficientBits(query, querySigns.Length);
 86
 87        // Let c>0 be the leading coefficient. S_k=c^k sum(roots^k)
 88        // is integral by Newton identities. Cauchy's bound gives
 89        // |S_k| <= 4(2H)^k, including complex roots and multiplicities.
 90        // H_ij=sum q_l S_(i+j+l)c^(K-i-j-l) is c^K times the
 91        // Hermite matrix. Thus entries use <=qBits+K(pBits+1)+7 bits;
 92        // every <=4 determinant (<=24 terms) fits 4*entryBits+8 bits.
 93        // All spans use these actual widths, not the caller's padded slots.
 16294        int traceWords = (maximumTrace * (pBits + 1) + 71) / 64;
 16295        int entryBits = qBits + maximumTrace * (pBits + 1) + 7;
 16296        int entryWords = (entryBits + 63) / 64;
 16297        Span<ulong> powers = stackalloc ulong[(maximumTrace + 1) * traceWords];
 16298        Span<ulong> traces = stackalloc ulong[(maximumTrace + 1) * traceWords];
 16299        Span<sbyte> traceSigns = stackalloc sbyte[maximumTrace + 1];
 162100        powers.Clear();
 162101        traces.Clear();
 162102        traceSigns.Clear();
 162103        powers[0] = 1;
 162104        ReadOnlySpan<ulong> leading = root.Coefficients.Slice(degree * polynomialWords, polynomialWords);
 3180105        for (int exponent = 1; exponent <= maximumTrace; exponent++)
 1428106            MultiplyRoundedCylinderWide(powers.Slice((exponent - 1) * traceWords, traceWords),
 1428107                leading, powers.Slice(exponent * traceWords, traceWords));
 162108        BuildFiniteRootTraces(root, powers, traces, traceSigns, traceWords);
 109
 162110        Span<ulong> matrix = stackalloc ulong[degree * (degree + 1) / 2 * entryWords];
 162111        Span<sbyte> matrixSigns = stackalloc sbyte[degree * (degree + 1) / 2];
 162112        Span<ulong> first = stackalloc ulong[entryWords];
 162113        Span<ulong> second = stackalloc ulong[entryWords];
 162114        Span<ulong> sum = stackalloc ulong[entryWords];
 162115        matrix.Clear();
 162116        matrixSigns.Clear();
 1580117        for (int row = 0; row < degree; row++)
 4336118        for (int column = row; column < degree; column++)
 119        {
 1540120            int slot = GetFiniteRootMatrixIndex(degree, row, column);
 1540121            Span<ulong> entry = matrix.Slice(slot * entryWords, entryWords);
 15600122            for (int power = 0; power <= queryDegree; power++)
 123            {
 6260124                int trace = row + column + power;
 6260125                MultiplyRoundedCylinderWide(query.Slice(power * queryWords, queryWords),
 6260126                    traces.Slice(trace * traceWords, traceWords), first);
 6260127                MultiplyRoundedCylinderWide(first,
 6260128                    powers.Slice((maximumTrace - trace) * traceWords, traceWords), second);
 6260129                sbyte sign = (sbyte)(querySigns[power] * traceSigns[trace]);
 6260130                AddRoundedCylinderSigned(entry, matrixSigns[slot], second, sign, sum, out matrixSigns[slot]);
 6260131                sum.CopyTo(entry);
 132            }
 133        }
 162134        return GetFiniteRootSymmetricSignature(matrix, matrixSigns, degree, entryBits);
 135    }
 136
 137    private static void BuildFiniteRootTraces(FiniteAxisPolynomialRoot root,
 138        ReadOnlySpan<ulong> powers, Span<ulong> traces, Span<sbyte> signs, int words)
 139    {
 162140        int degree = root.Signs.Length - 1;
 162141        int polynomialWords = root.Coefficients.Length / root.Signs.Length;
 162142        Span<ulong> first = stackalloc ulong[words];
 162143        Span<ulong> second = stackalloc ulong[words];
 162144        Span<ulong> sum = stackalloc ulong[words];
 162145        traces[0] = (ulong)degree;
 162146        signs[0] = 1;
 3180147        for (int exponent = 1; exponent < signs.Length; exponent++)
 148        {
 1428149            Span<ulong> result = traces.Slice(exponent * words, words);
 12176150            for (int index = 1; index <= Math.Min(exponent, degree); index++)
 151            {
 4660152                MultiplyRoundedCylinderWide(root.Coefficients.Slice((degree - index) * polynomialWords, polynomialWords)
 4660153                    powers.Slice((index - 1) * words, words), first);
 4660154                sbyte sign = (sbyte)(-root.Signs[degree - index] * root.Signs[degree]);
 4660155                if (index == exponent)
 628156                    MultiplyRoundedCylinderWideByWord(first, (ulong)exponent, second);
 157                else
 158                {
 4032159                    MultiplyRoundedCylinderWide(first, traces.Slice((exponent - index) * words, words), second);
 4032160                    sign = (sbyte)(sign * signs[exponent - index]);
 161                }
 4660162                AddRoundedCylinderSigned(result, signs[exponent], second, sign, sum, out signs[exponent]);
 4660163                sum.CopyTo(result);
 164            }
 165        }
 162166    }
 167
 168    private static int GetFiniteRootSymmetricSignature(ReadOnlySpan<ulong> matrix,
 169        ReadOnlySpan<sbyte> signs, int degree, int entryBits)
 170    {
 162171        int entryWords = matrix.Length / signs.Length;
 162172        int words = (degree * entryBits + 79) / 64;
 162173        Span<ulong> coefficient = stackalloc ulong[words];
 162174        Span<ulong> product = stackalloc ulong[words];
 162175        Span<ulong> scratch = stackalloc ulong[words];
 162176        Span<ulong> sum = stackalloc ulong[words];
 162177        Span<int> indices = stackalloc int[4];
 162178        Span<int> permutation = stackalloc int[4];
 162179        int positiveVariations = 0;
 162180        int negativeVariations = 0;
 162181        int previousPositive = 1;
 162182        int previousNegative = 1;
 1580183        for (int order = 1; order <= degree; order++)
 184        {
 628185            coefficient.Clear();
 628186            sbyte coefficientSign = 0;
 187            // The coefficient is the sum of order-by-order principal minors.
 188            // There are at most 15 nonempty subsets and 24 permutations.
 19136189            for (int mask = 1; mask < (1 << degree); mask++)
 190            {
 8940191                int length = 0;
 88560192                for (int index = 0; index < degree; index++)
 193                {
 35340194                    if ((mask & (1 << index)) != 0)
 18896195                        indices[length++] = index;
 196                }
 8940197                if (length != order)
 198                    continue;
 14108199                for (int index = 0; index < order; index++)
 4784200                    permutation[index] = index;
 201                do
 202                {
 9388203                    product.Clear();
 9388204                    product[0] = 1;
 9388205                    sbyte productSign = 1;
 75760206                    for (int row = 0; row < order; row++)
 207                    {
 28492208                        int slot = GetFiniteRootMatrixIndex(degree, indices[row], indices[permutation[row]]);
 28492209                        MultiplyRoundedCylinderWide(product, matrix.Slice(slot * entryWords, entryWords), scratch);
 28492210                        scratch.CopyTo(product);
 28492211                        productSign = (sbyte)(productSign * signs[slot]);
 122696212                        for (int previous = 0; previous < row; previous++)
 213                        {
 32856214                            if (permutation[previous] > permutation[row])
 16428215                                productSign = (sbyte)-productSign;
 216                        }
 217                    }
 9388218                    AddRoundedCylinderSigned(coefficient, coefficientSign, product, productSign, sum, out coefficientSig
 9388219                    sum.CopyTo(coefficient);
 9388220                } while (MoveNextFiniteRootPermutation(permutation[..order]));
 221            }
 628222            if (coefficientSign == 0)
 223                continue;
 276224            int positiveSign = (order & 1) != 0 ? -coefficientSign : coefficientSign;
 276225            if (previousPositive != positiveSign)
 99226                positiveVariations++;
 276227            if (previousNegative != coefficientSign)
 177228                negativeVariations++;
 276229            previousPositive = positiveSign;
 276230            previousNegative = coefficientSign;
 231        }
 232        // A real symmetric matrix has only real eigenvalues, so Descartes'
 233        // variation counts for det(tI-H) and det(tI+H) are exact, not bounds.
 234        // Hermite's signature theorem counts DISTINCT real roots weighted by
 235        // sign(Q). Repeated roots contribute positive multiplicity to the
 236        // rank-one form; complex conjugate blocks have signature zero.
 162237        return positiveVariations - negativeVariations;
 238    }
 239
 240    private static int GetFiniteRootMatrixIndex(int degree, int row, int column)
 241    {
 30032242        if (row > column)
 243        {
 9552244            int temporary = row;
 9552245            row = column;
 9552246            column = temporary;
 247        }
 30032248        return row * (2 * degree - row + 1) / 2 + column - row;
 249    }
 250
 251    private static bool MoveNextFiniteRootPermutation(Span<int> permutation)
 252    {
 9388253        int pivot = permutation.Length - 2;
 15066254        while (pivot >= 0 && permutation[pivot] >= permutation[pivot + 1])
 5678255            pivot--;
 9388256        if (pivot < 0)
 2270257            return false;
 7118258        int successor = permutation.Length - 1;
 8700259        while (permutation[successor] <= permutation[pivot])
 1582260            successor--;
 7118261        int temporary = permutation[pivot];
 7118262        permutation[pivot] = permutation[successor];
 7118263        permutation[successor] = temporary;
 29568264        for (int left = pivot + 1, right = permutation.Length - 1; left < right; left++, right--)
 265        {
 2738266            temporary = permutation[left];
 2738267            permutation[left] = permutation[right];
 2738268            permutation[right] = temporary;
 269        }
 7118270        return true;
 271    }
 272}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.Ray.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.Ray.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
 8namespace FixedMathSharp.Geometry;
 9
 10/// <content>
 11/// Provides ray/segment-based intersection helpers for finite-axis shapes,
 12/// including capsule interval computation against a <see cref="FixedRay2d"/>.
 13/// </content>
 14internal static partial class WideFiniteAxisIntersection
 15{
 16    internal static bool TryGetCapsuleInterval(
 17        FixedRay2d query,
 18        Fixed64 maxParameter,
 19        FixedSegment2d axis,
 20        Fixed64 radius,
 21        Fixed64 radiusExpansion,
 22        out Fixed64 entry,
 23        out Fixed64 exit,
 24        out bool originContained,
 25        out bool maximumContainedStrict)
 26    {
 1027        Signed192 expandedRadius = GetExpandedRadius(radius, radiusExpansion);
 1028        Signed192 squaredRadius = GetSquaredRadius(expandedRadius);
 1029        Signed192 axisLengthSquared = GetDot(axis.End, axis.Start, axis.End, axis.Start);
 1030        Signed192 queryLengthSquared = GetDirectionDot(query.Direction, query.Direction);
 1031        Signed192 startDistanceSquared = GetDot(query.Position, axis.Start, query.Position, axis.Start);
 1032        Signed192 directionsDot = GetDirectionDot(query.Direction, axis.End, axis.Start);
 1033        Signed192 startAxisProjection = GetDot(query.Position, axis.Start, axis.End, axis.Start);
 1034        Signed192 startDirectionProjection = GetDirectionDot(query.Direction, query.Position, axis.Start);
 35
 1036        originContained = axisLengthSquared.IsZero
 1037            ? IsWithinRadius(startDistanceSquared, squaredRadius, strict: false)
 1038            : IsCapsulePointContained(
 1039                startDistanceSquared,
 1040                GetDot(query.Position, axis.End, query.Position, axis.End),
 1041                startAxisProjection,
 1042                axisLengthSquared,
 1043                squaredRadius,
 1044                strict: false);
 1045        maximumContainedStrict = IsCapsuleContainedAtParameter(
 1046            query,
 1047            maxParameter,
 1048            axis,
 1049            expandedRadius,
 1050            queryLengthSquared,
 1051            axisLengthSquared,
 1052            startDistanceSquared,
 1053            directionsDot,
 1054            startAxisProjection,
 1055            startDirectionProjection);
 56
 1057        if (axisLengthSquared.IsZero)
 58        {
 559            return TryGetCircleInterval(
 560                query.Position,
 561                query.Direction,
 562                axis.Start,
 563                expandedRadius,
 564                maxParameter,
 565                out entry,
 566                out exit);
 67        }
 68
 569        bool found = TryGetFiniteAxisInterval(
 570            queryLengthSquared,
 571            axisLengthSquared,
 572            startDistanceSquared,
 573            directionsDot,
 574            startAxisProjection,
 575            startDirectionProjection,
 576            expandedRadius,
 577            maxParameter,
 578            out entry,
 579            out exit);
 580        Merge(
 581            TryGetCircleInterval(
 582                query.Position, query.Direction, axis.Start, expandedRadius, maxParameter,
 583                out Fixed64 startEntry, out Fixed64 startExit),
 584            startEntry, startExit, ref found, ref entry, ref exit);
 585        Merge(
 586            TryGetCircleInterval(
 587                query.Position, query.Direction, axis.End, expandedRadius, maxParameter,
 588                out Fixed64 endEntry, out Fixed64 endExit),
 589            endEntry, endExit, ref found, ref entry, ref exit);
 590        return found;
 91    }
 92
 93    internal static bool TryGetCapsuleInterval(
 94        FixedRay query,
 95        Fixed64 maxParameter,
 96        FixedSegment axis,
 97        Fixed64 radius,
 98        Fixed64 radiusExpansion,
 99        out Fixed64 entry,
 100        out Fixed64 exit,
 101        out bool originContained,
 102        out bool maximumContainedStrict)
 103    {
 5104        Signed192 expandedRadius = GetExpandedRadius(radius, radiusExpansion);
 5105        Signed192 squaredRadius = GetSquaredRadius(expandedRadius);
 5106        Signed192 axisLengthSquared = GetDot(axis.End, axis.Start, axis.End, axis.Start);
 5107        Signed192 queryLengthSquared = GetDirectionDot(query.Direction, query.Direction);
 5108        Signed192 startDistanceSquared = GetDot(query.Position, axis.Start, query.Position, axis.Start);
 5109        Signed192 directionsDot = GetDirectionDot(query.Direction, axis.End, axis.Start);
 5110        Signed192 startAxisProjection = GetDot(query.Position, axis.Start, axis.End, axis.Start);
 5111        Signed192 startDirectionProjection = GetDirectionDot(query.Direction, query.Position, axis.Start);
 112
 5113        originContained = axisLengthSquared.IsZero
 5114            ? IsWithinRadius(startDistanceSquared, squaredRadius, strict: false)
 5115            : IsCapsulePointContained(
 5116                startDistanceSquared,
 5117                GetDot(query.Position, axis.End, query.Position, axis.End),
 5118                startAxisProjection,
 5119                axisLengthSquared,
 5120                squaredRadius,
 5121                strict: false);
 5122        maximumContainedStrict = IsCapsuleContainedAtParameter(
 5123            query,
 5124            maxParameter,
 5125            axis,
 5126            expandedRadius,
 5127            queryLengthSquared,
 5128            axisLengthSquared,
 5129            startDistanceSquared,
 5130            directionsDot,
 5131            startAxisProjection,
 5132            startDirectionProjection);
 133
 5134        if (axisLengthSquared.IsZero)
 135        {
 2136            return TryGetSphereInterval(
 2137                query.Position,
 2138                query.Direction,
 2139                axis.Start,
 2140                expandedRadius,
 2141                maxParameter,
 2142                out entry,
 2143                out exit);
 144        }
 145
 3146        bool found = TryGetFiniteAxisInterval(
 3147            queryLengthSquared,
 3148            axisLengthSquared,
 3149            startDistanceSquared,
 3150            directionsDot,
 3151            startAxisProjection,
 3152            startDirectionProjection,
 3153            expandedRadius,
 3154            maxParameter,
 3155            out entry,
 3156            out exit);
 3157        Merge(
 3158            TryGetSphereInterval(
 3159                query.Position, query.Direction, axis.Start, expandedRadius, maxParameter,
 3160                out Fixed64 startEntry, out Fixed64 startExit),
 3161            startEntry, startExit, ref found, ref entry, ref exit);
 3162        Merge(
 3163            TryGetSphereInterval(
 3164                query.Position, query.Direction, axis.End, expandedRadius, maxParameter,
 3165                out Fixed64 endEntry, out Fixed64 endExit),
 3166            endEntry, endExit, ref found, ref entry, ref exit);
 3167        return found;
 168    }
 169
 170    internal static bool TryGetCapsuleInterval(
 171        FixedRay2d query,
 172        Fixed64 maxParameter,
 173        Vector2d center,
 174        Vector2d axisDirection,
 175        Fixed64 axisLength,
 176        Fixed64 radius,
 177        Fixed64 radiusExpansion,
 178        out Fixed64 entry,
 179        out Fixed64 exit,
 180        out bool originContained,
 181        out bool maximumContainedStrict)
 182    {
 4183        Signed192 expandedRadius = GetExpandedRadius(radius, radiusExpansion);
 4184        Signed192 squaredRadius = GetSquaredRadius(expandedRadius);
 4185        Signed192 axisLengthSquared = GetDot(axisDirection, Vector2d.Zero, axisDirection, Vector2d.Zero);
 4186        Signed192 queryLengthSquared = GetDirectionDot(query.Direction, query.Direction);
 4187        Signed192 startDistanceSquared = GetDot(query.Position, center, query.Position, center);
 4188        Signed192 directionsDot = GetDirectionDot(query.Direction, axisDirection);
 4189        Signed192 startAxisProjection = GetDot(query.Position, center, axisDirection, Vector2d.Zero);
 4190        Signed192 startDirectionProjection = GetDirectionDot(query.Direction, query.Position, center);
 4191        Signed320 axialExtent = GetCenteredAxialExtent(
 4192            axisLengthSquared,
 4193            axisLength,
 4194            Fixed64.Zero);
 195
 4196        originContained = IsCenteredCapsulePointContained(
 4197            query.Position, center, axisDirection, axisLength,
 4198            startDistanceSquared, startAxisProjection, axisLengthSquared,
 4199            squaredRadius, expandedRadius, axialExtent, strict: false);
 4200        maximumContainedStrict = IsCenteredCapsuleContainedAtParameter(
 4201            query,
 4202            maxParameter,
 4203            center,
 4204            axisDirection,
 4205            axisLength,
 4206            expandedRadius,
 4207            queryLengthSquared,
 4208            axisLengthSquared,
 4209            startDistanceSquared,
 4210            directionsDot,
 4211            startAxisProjection,
 4212            startDirectionProjection,
 4213            axialExtent);
 214
 4215        bool found = false;
 4216        entry = default;
 4217        exit = default;
 4218        if (TryGetCenteredAxialInterval(
 4219                startAxisProjection,
 4220                directionsDot,
 4221                axialExtent,
 4222                maxParameter,
 4223                out RationalBound320 lower,
 4224                out RationalBound320 upper))
 225        {
 3226            found = TryGetFiniteAxisInterval(
 3227                queryLengthSquared,
 3228                axisLengthSquared,
 3229                startDistanceSquared,
 3230                directionsDot,
 3231                startAxisProjection,
 3232                startDirectionProjection,
 3233                expandedRadius,
 3234                lower,
 3235                upper,
 3236                maxParameter,
 3237                out entry,
 3238                out exit);
 239        }
 240
 4241        MergeCenteredCapInterval(
 4242            query, maxParameter, center, axisDirection, axisLength,
 4243            expandedRadius, positiveCap: false, ref found, ref entry, ref exit);
 4244        MergeCenteredCapInterval(
 4245            query, maxParameter, center, axisDirection, axisLength,
 4246            expandedRadius, positiveCap: true, ref found, ref entry, ref exit);
 4247        return found;
 248    }
 249
 250    internal static bool TryGetCapsuleInterval(
 251        FixedRay query,
 252        Fixed64 maxParameter,
 253        Vector3d center,
 254        Vector3d axisDirection,
 255        Fixed64 axisLength,
 256        Fixed64 radius,
 257        Fixed64 radiusExpansion,
 258        out Fixed64 entry,
 259        out Fixed64 exit,
 260        out bool originContained,
 261        out bool maximumContainedStrict)
 262    {
 5263        Signed192 expandedRadius = GetExpandedRadius(radius, radiusExpansion);
 5264        Signed192 squaredRadius = GetSquaredRadius(expandedRadius);
 5265        Signed192 axisLengthSquared = GetDot(axisDirection, Vector3d.Zero, axisDirection, Vector3d.Zero);
 5266        Signed192 queryLengthSquared = GetDirectionDot(query.Direction, query.Direction);
 5267        Signed192 startDistanceSquared = GetDot(query.Position, center, query.Position, center);
 5268        Signed192 directionsDot = GetDirectionDot(query.Direction, axisDirection);
 5269        Signed192 startAxisProjection = GetDot(query.Position, center, axisDirection, Vector3d.Zero);
 5270        Signed192 startDirectionProjection = GetDirectionDot(query.Direction, query.Position, center);
 5271        Signed320 axialExtent = GetCenteredAxialExtent(
 5272            axisLengthSquared,
 5273            axisLength,
 5274            Fixed64.Zero);
 275
 5276        originContained = IsCenteredCapsulePointContained(
 5277            query.Position, center, axisDirection, axisLength,
 5278            startDistanceSquared, startAxisProjection, axisLengthSquared,
 5279            squaredRadius, expandedRadius, axialExtent, strict: false);
 5280        maximumContainedStrict = IsCenteredCapsuleContainedAtParameter(
 5281            query,
 5282            maxParameter,
 5283            center,
 5284            axisDirection,
 5285            axisLength,
 5286            expandedRadius,
 5287            queryLengthSquared,
 5288            axisLengthSquared,
 5289            startDistanceSquared,
 5290            directionsDot,
 5291            startAxisProjection,
 5292            startDirectionProjection,
 5293            axialExtent);
 294
 5295        bool found = false;
 5296        entry = default;
 5297        exit = default;
 5298        if (TryGetCenteredAxialInterval(
 5299                startAxisProjection,
 5300                directionsDot,
 5301                axialExtent,
 5302                maxParameter,
 5303                out RationalBound320 lower,
 5304                out RationalBound320 upper))
 305        {
 4306            found = TryGetFiniteAxisInterval(
 4307                queryLengthSquared,
 4308                axisLengthSquared,
 4309                startDistanceSquared,
 4310                directionsDot,
 4311                startAxisProjection,
 4312                startDirectionProjection,
 4313                expandedRadius,
 4314                lower,
 4315                upper,
 4316                maxParameter,
 4317                out entry,
 4318                out exit);
 319        }
 320
 5321        MergeCenteredCapInterval(
 5322            query, maxParameter, center, axisDirection, axisLength,
 5323            expandedRadius, positiveCap: false, ref found, ref entry, ref exit);
 5324        MergeCenteredCapInterval(
 5325            query, maxParameter, center, axisDirection, axisLength,
 5326            expandedRadius, positiveCap: true, ref found, ref entry, ref exit);
 5327        return found;
 328    }
 329
 330    internal static bool TryGetFiniteCylinderInterval(
 331        FixedRay query,
 332        Fixed64 maxParameter,
 333        FixedSegment axis,
 334        Fixed64 radius,
 335        Fixed64 radiusExpansion,
 336        out Fixed64 entry,
 337        out Fixed64 exit,
 338        out bool originContained,
 339        out bool maximumContainedStrict)
 340    {
 3341        Signed192 expandedRadius = GetExpandedRadius(radius, radiusExpansion);
 3342        Signed192 squaredRadius = GetSquaredRadius(expandedRadius);
 3343        Signed192 axisLengthSquared = GetDot(axis.End, axis.Start, axis.End, axis.Start);
 3344        Signed192 queryLengthSquared = GetDirectionDot(query.Direction, query.Direction);
 3345        Signed192 startDistanceSquared = GetDot(query.Position, axis.Start, query.Position, axis.Start);
 3346        Signed192 directionsDot = GetDirectionDot(query.Direction, axis.End, axis.Start);
 3347        Signed192 startAxisProjection = GetDot(query.Position, axis.Start, axis.End, axis.Start);
 3348        Signed192 startDirectionProjection = GetDirectionDot(query.Direction, query.Position, axis.Start);
 349
 3350        originContained = IsFiniteCylinderPointContained(
 3351            startDistanceSquared, startAxisProjection, axisLengthSquared, squaredRadius, strict: false);
 3352        maximumContainedStrict = IsFiniteCylinderContainedAtParameter(
 3353            maxParameter,
 3354            expandedRadius,
 3355            queryLengthSquared,
 3356            axisLengthSquared,
 3357            startDistanceSquared,
 3358            directionsDot,
 3359            startAxisProjection,
 3360            startDirectionProjection);
 3361        return TryGetFiniteAxisInterval(
 3362            queryLengthSquared,
 3363            axisLengthSquared,
 3364            startDistanceSquared,
 3365            directionsDot,
 3366            startAxisProjection,
 3367            startDirectionProjection,
 3368            expandedRadius,
 3369            maxParameter,
 3370            out entry,
 3371            out exit);
 372    }
 373
 374    internal static bool TryGetFiniteCylinderInterval(
 375        FixedRay query,
 376        Fixed64 maxParameter,
 377        Vector3d center,
 378        Vector3d axisDirection,
 379        Fixed64 axisLength,
 380        Fixed64 radius,
 381        Fixed64 radiusExpansion,
 382        Fixed64 axialExpansion,
 383        out Fixed64 entry,
 384        out Fixed64 exit,
 385        out bool originContained,
 386        out bool maximumContainedStrict)
 387    {
 5388        Signed192 queryLengthSquared = GetDirectionDot(query.Direction, query.Direction);
 5389        Signed192 axisLengthSquared = GetDot(axisDirection, Vector3d.Zero, axisDirection, Vector3d.Zero);
 5390        Signed192 startDistanceSquared = GetDot(query.Position, center, query.Position, center);
 5391        Signed192 directionsDot = GetDirectionDot(query.Direction, axisDirection);
 5392        Signed192 startAxisProjection = GetDot(query.Position, center, axisDirection, Vector3d.Zero);
 5393        Signed192 startDirectionProjection = GetDirectionDot(query.Direction, query.Position, center);
 5394        Signed192 expandedRadius = GetExpandedRadius(radius, radiusExpansion);
 5395        Signed192 squaredRadius = GetSquaredRadius(expandedRadius);
 5396        Signed320 axialExtent = GetCenteredAxialExtent(
 5397            axisLengthSquared,
 5398            axisLength,
 5399            axialExpansion);
 400
 5401        originContained = IsCenteredFiniteCylinderPointContained(
 5402            startDistanceSquared, startAxisProjection, axisLengthSquared,
 5403            squaredRadius, axialExtent, strict: false);
 5404        maximumContainedStrict = IsCenteredFiniteCylinderContainedAtParameter(
 5405            maxParameter,
 5406            expandedRadius,
 5407            queryLengthSquared,
 5408            axisLengthSquared,
 5409            startDistanceSquared,
 5410            directionsDot,
 5411            startAxisProjection,
 5412            startDirectionProjection,
 5413            axialExtent);
 414
 5415        if (!TryGetCenteredAxialInterval(
 5416                startAxisProjection,
 5417                directionsDot,
 5418                axialExtent,
 5419                maxParameter,
 5420                out RationalBound320 lower,
 5421                out RationalBound320 upper))
 422        {
 2423            entry = default;
 2424            exit = default;
 2425            return false;
 426        }
 427
 3428        return TryGetFiniteAxisInterval(
 3429            queryLengthSquared,
 3430            axisLengthSquared,
 3431            startDistanceSquared,
 3432            directionsDot,
 3433            startAxisProjection,
 3434            startDirectionProjection,
 3435            expandedRadius,
 3436            lower,
 3437            upper,
 3438            maxParameter,
 3439            out entry,
 3440            out exit);
 441    }
 442
 443    private static bool IsCapsuleContainedAtParameter(
 444        FixedRay2d query,
 445        Fixed64 parameter,
 446        FixedSegment2d axis,
 447        Signed192 radius,
 448        Signed192 queryLengthSquared,
 449        Signed192 axisLengthSquared,
 450        Signed192 startDistanceSquared,
 451        Signed192 directionsDot,
 452        Signed192 startAxisProjection,
 453        Signed192 startDirectionProjection)
 454    {
 10455        if (axisLengthSquared.IsZero)
 456        {
 5457            return IsQuadraticContained(
 5458                queryLengthSquared,
 5459                startDirectionProjection,
 5460                WideArithmetic.SubtractSigned192(startDistanceSquared, GetSquaredRadius(radius)),
 5461                parameter);
 462        }
 463
 5464        Signed320 projection = EvaluateLinear(startAxisProjection, directionsDot, parameter);
 5465        if (projection.Sign <= 0)
 466        {
 3467            return IsQuadraticContained(
 3468                queryLengthSquared,
 3469                startDirectionProjection,
 3470                WideArithmetic.SubtractSigned192(startDistanceSquared, GetSquaredRadius(radius)),
 3471                parameter);
 472        }
 473
 2474        Signed320 maximumProjection = WideArithmetic.MultiplySigned192(axisLengthSquared, ParameterScale);
 2475        if (WideArithmetic.SubtractSigned320(projection, maximumProjection).Sign >= 0)
 476        {
 1477            Signed192 endDistanceSquared = GetDot(query.Position, axis.End, query.Position, axis.End);
 1478            Signed192 endDirectionProjection = GetDirectionDot(query.Direction, query.Position, axis.End);
 1479            return IsQuadraticContained(
 1480                queryLengthSquared,
 1481                endDirectionProjection,
 1482                WideArithmetic.SubtractSigned192(endDistanceSquared, GetSquaredRadius(radius)),
 1483                parameter);
 484        }
 485
 1486        GetRadialPolynomial(
 1487            queryLengthSquared, axisLengthSquared, startDistanceSquared,
 1488            directionsDot, startAxisProjection, startDirectionProjection, radius,
 1489            out Signed320 coefficient, out Signed320 radialProjection, out Signed320 constant);
 1490        return IsQuadraticContained(coefficient, radialProjection, constant, parameter);
 491    }
 492
 493    private static bool IsCapsuleContainedAtParameter(
 494        FixedRay query,
 495        Fixed64 parameter,
 496        FixedSegment axis,
 497        Signed192 radius,
 498        Signed192 queryLengthSquared,
 499        Signed192 axisLengthSquared,
 500        Signed192 startDistanceSquared,
 501        Signed192 directionsDot,
 502        Signed192 startAxisProjection,
 503        Signed192 startDirectionProjection)
 504    {
 5505        if (axisLengthSquared.IsZero)
 506        {
 2507            return IsQuadraticContained(
 2508                queryLengthSquared,
 2509                startDirectionProjection,
 2510                WideArithmetic.SubtractSigned192(startDistanceSquared, GetSquaredRadius(radius)),
 2511                parameter);
 512        }
 513
 3514        Signed320 projection = EvaluateLinear(startAxisProjection, directionsDot, parameter);
 3515        if (projection.Sign <= 0)
 516        {
 1517            return IsQuadraticContained(
 1518                queryLengthSquared,
 1519                startDirectionProjection,
 1520                WideArithmetic.SubtractSigned192(startDistanceSquared, GetSquaredRadius(radius)),
 1521                parameter);
 522        }
 523
 2524        Signed320 maximumProjection = WideArithmetic.MultiplySigned192(axisLengthSquared, ParameterScale);
 2525        if (WideArithmetic.SubtractSigned320(projection, maximumProjection).Sign >= 0)
 526        {
 1527            Signed192 endDistanceSquared = GetDot(query.Position, axis.End, query.Position, axis.End);
 1528            Signed192 endDirectionProjection = GetDirectionDot(query.Direction, query.Position, axis.End);
 1529            return IsQuadraticContained(
 1530                queryLengthSquared,
 1531                endDirectionProjection,
 1532                WideArithmetic.SubtractSigned192(endDistanceSquared, GetSquaredRadius(radius)),
 1533                parameter);
 534        }
 535
 1536        GetRadialPolynomial(
 1537            queryLengthSquared, axisLengthSquared, startDistanceSquared,
 1538            directionsDot, startAxisProjection, startDirectionProjection, radius,
 1539            out Signed320 coefficient, out Signed320 radialProjection, out Signed320 constant);
 1540        return IsQuadraticContained(coefficient, radialProjection, constant, parameter);
 541    }
 542
 543    private static bool IsFiniteCylinderContainedAtParameter(
 544        Fixed64 parameter,
 545        Signed192 radius,
 546        Signed192 queryLengthSquared,
 547        Signed192 axisLengthSquared,
 548        Signed192 startDistanceSquared,
 549        Signed192 directionsDot,
 550        Signed192 startAxisProjection,
 551        Signed192 startDirectionProjection)
 552    {
 3553        Signed320 projection = EvaluateLinear(startAxisProjection, directionsDot, parameter);
 3554        Signed320 maximumProjection = WideArithmetic.MultiplySigned192(axisLengthSquared, ParameterScale);
 3555        int minimumSign = projection.Sign;
 3556        int maximumSign = WideArithmetic.SubtractSigned320(projection, maximumProjection).Sign;
 3557        if (minimumSign <= 0 || maximumSign >= 0)
 558        {
 2559            return false;
 560        }
 561
 1562        GetRadialPolynomial(
 1563            queryLengthSquared, axisLengthSquared, startDistanceSquared,
 1564            directionsDot, startAxisProjection, startDirectionProjection, radius,
 1565            out Signed320 coefficient, out Signed320 radialProjection, out Signed320 constant);
 1566        return IsQuadraticContained(coefficient, radialProjection, constant, parameter);
 567    }
 568
 569    private static bool IsCenteredFiniteCylinderContainedAtParameter(
 570        Fixed64 parameter,
 571        Signed192 radius,
 572        Signed192 queryLengthSquared,
 573        Signed192 axisLengthSquared,
 574        Signed192 startDistanceSquared,
 575        Signed192 directionsDot,
 576        Signed192 startAxisProjection,
 577        Signed192 startDirectionProjection,
 578        Signed320 axialExtent)
 579    {
 5580        Signed320 projection = EvaluateLinear(startAxisProjection, directionsDot, parameter);
 5581        projection = WideArithmetic.AddSigned320(projection, projection);
 5582        Signed320 minimumProjection = WideArithmetic.SubtractSigned320(default, axialExtent);
 5583        int minimumSign = WideArithmetic.SubtractSigned320(projection, minimumProjection).Sign;
 5584        int maximumSign = WideArithmetic.SubtractSigned320(projection, axialExtent).Sign;
 5585        if (minimumSign <= 0 || maximumSign >= 0)
 586        {
 4587            return false;
 588        }
 589
 1590        GetRadialPolynomial(
 1591            queryLengthSquared, axisLengthSquared, startDistanceSquared,
 1592            directionsDot, startAxisProjection, startDirectionProjection, radius,
 1593            out Signed320 coefficient, out Signed320 radialProjection, out Signed320 constant);
 1594        return IsQuadraticContained(coefficient, radialProjection, constant, parameter);
 595    }
 596
 597    private static bool IsCenteredCapsuleContainedAtParameter(
 598        FixedRay2d query,
 599        Fixed64 parameter,
 600        Vector2d center,
 601        Vector2d axisDirection,
 602        Fixed64 axisLength,
 603        Signed192 radius,
 604        Signed192 queryLengthSquared,
 605        Signed192 axisLengthSquared,
 606        Signed192 startDistanceSquared,
 607        Signed192 directionsDot,
 608        Signed192 startAxisProjection,
 609        Signed192 startDirectionProjection,
 610        Signed320 axialExtent)
 611    {
 4612        Signed320 projection = EvaluateLinear(startAxisProjection, directionsDot, parameter);
 4613        projection = WideArithmetic.AddSigned320(projection, projection);
 4614        int cap = GetCenteredCap(projection, axialExtent);
 4615        if (cap != 0)
 616        {
 3617            GetCenteredCapPolynomial(
 3618                query, center, axisDirection, axisLength, radius, cap > 0,
 3619                out Signed320 coefficient, out Signed320 capProjection, out Signed320 constant);
 3620            return IsQuadraticContained(coefficient, capProjection, constant, parameter);
 621        }
 622
 1623        GetRadialPolynomial(
 1624            queryLengthSquared, axisLengthSquared, startDistanceSquared,
 1625            directionsDot, startAxisProjection, startDirectionProjection, radius,
 1626            out Signed320 radialCoefficient, out Signed320 radialProjection, out Signed320 radialConstant);
 1627        return IsQuadraticContained(radialCoefficient, radialProjection, radialConstant, parameter);
 628    }
 629
 630    private static bool IsCenteredCapsuleContainedAtParameter(
 631        FixedRay query,
 632        Fixed64 parameter,
 633        Vector3d center,
 634        Vector3d axisDirection,
 635        Fixed64 axisLength,
 636        Signed192 radius,
 637        Signed192 queryLengthSquared,
 638        Signed192 axisLengthSquared,
 639        Signed192 startDistanceSquared,
 640        Signed192 directionsDot,
 641        Signed192 startAxisProjection,
 642        Signed192 startDirectionProjection,
 643        Signed320 axialExtent)
 644    {
 5645        Signed320 projection = EvaluateLinear(startAxisProjection, directionsDot, parameter);
 5646        projection = WideArithmetic.AddSigned320(projection, projection);
 5647        int cap = GetCenteredCap(projection, axialExtent);
 5648        if (cap != 0)
 649        {
 3650            GetCenteredCapPolynomial(
 3651                query, center, axisDirection, axisLength, radius, cap > 0,
 3652                out Signed320 coefficient, out Signed320 capProjection, out Signed320 constant);
 3653            return IsQuadraticContained(coefficient, capProjection, constant, parameter);
 654        }
 655
 2656        GetRadialPolynomial(
 2657            queryLengthSquared, axisLengthSquared, startDistanceSquared,
 2658            directionsDot, startAxisProjection, startDirectionProjection, radius,
 2659            out Signed320 radialCoefficient, out Signed320 radialProjection, out Signed320 radialConstant);
 2660        return IsQuadraticContained(radialCoefficient, radialProjection, radialConstant, parameter);
 661    }
 662
 663    private static int GetCenteredCap(Signed320 scaledProjection, Signed320 axialExtent)
 664    {
 9665        if (WideArithmetic.AddSigned320(scaledProjection, axialExtent).Sign <= 0)
 4666            return -1;
 5667        return WideArithmetic.SubtractSigned320(scaledProjection, axialExtent).Sign >= 0
 5668            ? 1
 5669            : 0;
 670    }
 671
 672    private static void MergeCenteredCapInterval(
 673        FixedRay2d query,
 674        Fixed64 maxParameter,
 675        Vector2d center,
 676        Vector2d axisDirection,
 677        Fixed64 axisLength,
 678        Signed192 radius,
 679        bool positiveCap,
 680        ref bool found,
 681        ref Fixed64 entry,
 682        ref Fixed64 exit)
 683    {
 8684        GetCenteredCapPolynomial(
 8685            query, center, axisDirection, axisLength, radius, positiveCap,
 8686            out Signed320 coefficient, out Signed320 projection, out Signed320 constant);
 8687        RationalBound320 maximum = GetMaximumBound320(maxParameter);
 8688        Signed320 one = Scale320;
 8689        Merge(
 8690            TrySolveBoundedQuadratic(
 8691                coefficient, projection, constant,
 8692                new RationalBound320(default, one), maximum, maxParameter,
 8693                out Fixed64 capEntry, out Fixed64 capExit),
 8694            capEntry, capExit, ref found, ref entry, ref exit);
 8695    }
 696
 697    private static void MergeCenteredCapInterval(
 698        FixedRay query,
 699        Fixed64 maxParameter,
 700        Vector3d center,
 701        Vector3d axisDirection,
 702        Fixed64 axisLength,
 703        Signed192 radius,
 704        bool positiveCap,
 705        ref bool found,
 706        ref Fixed64 entry,
 707        ref Fixed64 exit)
 708    {
 10709        GetCenteredCapPolynomial(
 10710            query, center, axisDirection, axisLength, radius, positiveCap,
 10711            out Signed320 coefficient, out Signed320 projection, out Signed320 constant);
 10712        RationalBound320 maximum = GetMaximumBound320(maxParameter);
 10713        Signed320 one = Scale320;
 10714        Merge(
 10715            TrySolveBoundedQuadratic(
 10716                coefficient, projection, constant,
 10717                new RationalBound320(default, one), maximum, maxParameter,
 10718                out Fixed64 capEntry, out Fixed64 capExit),
 10719            capEntry, capExit, ref found, ref entry, ref exit);
 10720    }
 721
 722    private static void GetCenteredCapPolynomial(
 723        FixedRay2d query,
 724        Vector2d center,
 725        Vector2d axisDirection,
 726        Fixed64 axisLength,
 727        Signed192 radius,
 728        bool positiveCap,
 729        out Signed320 coefficient,
 730        out Signed320 projection,
 731        out Signed320 constant)
 732    {
 11733        Signed192 x = GetCenteredCapOffset(
 11734            query.Position.X, center.X, axisDirection.X, axisLength, positiveCap);
 11735        Signed192 y = GetCenteredCapOffset(
 11736            query.Position.Y, center.Y, axisDirection.Y, axisLength, positiveCap);
 11737        Signed192 velocityX = ScaleByCenteredAxis(
 11738            Signed192.Signed(query.Direction.X.m_rawValue));
 11739        Signed192 velocityY = ScaleByCenteredAxis(
 11740            Signed192.Signed(query.Direction.Y.m_rawValue));
 11741        Signed192 scaledRadius = ScaleByCenteredAxis(radius);
 11742        coefficient = WideArithmetic.AddProducts(velocityX, velocityX, velocityY, velocityY);
 11743        projection = WideArithmetic.AddProducts(x, velocityX, y, velocityY);
 11744        constant = WideArithmetic.SubtractSigned320(
 11745            WideArithmetic.AddProducts(x, x, y, y),
 11746            WideArithmetic.MultiplySigned192(scaledRadius, scaledRadius));
 11747    }
 748
 749    private static void GetCenteredCapPolynomial(
 750        FixedRay query,
 751        Vector3d center,
 752        Vector3d axisDirection,
 753        Fixed64 axisLength,
 754        Signed192 radius,
 755        bool positiveCap,
 756        out Signed320 coefficient,
 757        out Signed320 projection,
 758        out Signed320 constant)
 759    {
 13760        Signed192 x = GetCenteredCapOffset(
 13761            query.Position.X, center.X, axisDirection.X, axisLength, positiveCap);
 13762        Signed192 y = GetCenteredCapOffset(
 13763            query.Position.Y, center.Y, axisDirection.Y, axisLength, positiveCap);
 13764        Signed192 z = GetCenteredCapOffset(
 13765            query.Position.Z, center.Z, axisDirection.Z, axisLength, positiveCap);
 13766        Signed192 velocityX = ScaleByCenteredAxis(
 13767            Signed192.Signed(query.Direction.X.m_rawValue));
 13768        Signed192 velocityY = ScaleByCenteredAxis(
 13769            Signed192.Signed(query.Direction.Y.m_rawValue));
 13770        Signed192 velocityZ = ScaleByCenteredAxis(
 13771            Signed192.Signed(query.Direction.Z.m_rawValue));
 13772        Signed192 scaledRadius = ScaleByCenteredAxis(radius);
 13773        coefficient = WideArithmetic.AddProducts(
 13774            velocityX, velocityX, velocityY, velocityY, velocityZ, velocityZ);
 13775        projection = WideArithmetic.AddProducts(x, velocityX, y, velocityY, z, velocityZ);
 13776        constant = WideArithmetic.SubtractSigned320(
 13777            WideArithmetic.AddProducts(x, x, y, y, z, z),
 13778            WideArithmetic.MultiplySigned192(scaledRadius, scaledRadius));
 13779    }
 780
 781    private static Signed320 EvaluateLinear(
 782        Signed192 constant,
 783        Signed192 velocity,
 784        Fixed64 parameter) =>
 25785        WideArithmetic.AddSigned320(
 25786            WideArithmetic.MultiplySigned192(constant, ParameterScale),
 25787            WideArithmetic.MultiplySigned192(
 25788                velocity,
 25789                Signed192.Signed(parameter.m_rawValue)));
 790
 791    private static void GetRadialPolynomial(
 792        Signed192 queryLengthSquared,
 793        Signed192 axisLengthSquared,
 794        Signed192 startDistanceSquared,
 795        Signed192 directionsDot,
 796        Signed192 startAxisProjection,
 797        Signed192 startDirectionProjection,
 798        Signed192 radius,
 799        out Signed320 coefficient,
 800        out Signed320 projection,
 801        out Signed320 constant)
 802    {
 7803        coefficient = WideArithmetic.MultiplySubtract(
 7804            queryLengthSquared, axisLengthSquared, directionsDot, directionsDot);
 7805        projection = WideArithmetic.MultiplySubtract(
 7806            startDirectionProjection, axisLengthSquared, startAxisProjection, directionsDot);
 7807        constant = GetRadialConstant(
 7808            startDistanceSquared,
 7809            startAxisProjection,
 7810            axisLengthSquared,
 7811            GetSquaredRadius(radius));
 7812    }
 813
 814    private static bool IsQuadraticContained(
 815        Signed192 coefficient,
 816        Signed192 projection,
 817        Signed192 constant,
 818        Fixed64 parameter) =>
 13819        IsQuadraticContained(
 13820            Signed320.ExtendValue(coefficient),
 13821            Signed320.ExtendValue(projection),
 13822            Signed320.ExtendValue(constant),
 13823            parameter);
 824
 825    private static bool IsQuadraticContained(
 826        Signed320 coefficient,
 827        Signed320 projection,
 828        Signed320 constant,
 829        Fixed64 parameter)
 830    {
 26831        Signed320 numerator = Signed320.ExtendValue(
 26832            Signed192.Signed(parameter.m_rawValue));
 26833        Signed320 denominator = Signed320.ExtendValue(ParameterScale);
 26834        int sign = EvaluatePolynomial(
 26835            coefficient,
 26836            projection,
 26837            constant,
 26838            numerator,
 26839            denominator).Sign;
 26840        return sign < 0;
 841    }
 842
 843    private static bool TryGetCircleInterval(
 844        Vector2d position,
 845        Vector2d direction,
 846        Vector2d center,
 847        Signed192 radius,
 848        Fixed64 maxParameter,
 849        out Fixed64 entry,
 850        out Fixed64 exit) =>
 15851        WideRayIntersection.TrySolveInterval(
 15852            GetDirectionDot(direction, direction),
 15853            GetDirectionDot(direction, position, center),
 15854            WideArithmetic.SubtractSigned192(
 15855                GetDot(position, center, position, center),
 15856                GetSquaredRadius(radius)),
 15857            maxParameter,
 15858            out entry,
 15859            out exit);
 860
 861    private static bool TryGetSphereInterval(
 862        Vector3d position,
 863        Vector3d direction,
 864        Vector3d center,
 865        Signed192 radius,
 866        Fixed64 maxParameter,
 867        out Fixed64 entry,
 868        out Fixed64 exit) =>
 8869        WideRayIntersection.TrySolveInterval(
 8870            GetDirectionDot(direction, direction),
 8871            GetDirectionDot(direction, position, center),
 8872            WideArithmetic.SubtractSigned192(
 8873                GetDot(position, center, position, center),
 8874                GetSquaredRadius(radius)),
 8875            maxParameter,
 8876            out entry,
 8877            out exit);
 878
 879    private static Signed192 GetDirectionDot(Vector2d left, Vector2d right) =>
 35880        GetDot(left, Vector2d.Zero, right, Vector2d.Zero);
 881
 882    private static Signed192 GetDirectionDot(
 883        Vector2d direction,
 884        Vector2d point,
 885        Vector2d origin) =>
 42886        GetDot(direction, Vector2d.Zero, point, origin);
 887
 888    private static Signed192 GetDirectionDot(Vector3d left, Vector3d right) =>
 48889        GetDot(left, Vector3d.Zero, right, Vector3d.Zero);
 890
 891    private static Signed192 GetDirectionDot(
 892        Vector3d direction,
 893        Vector3d point,
 894        Vector3d origin) =>
 42895        GetDot(direction, Vector3d.Zero, point, origin);
 896}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.RigidPointRelation.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.RigidPointRelation.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
 8namespace FixedMathSharp.Geometry;
 9
 10/// <content>
 11/// Computes high-precision point-to-axis relations (projection, radial distance, and
 12/// associated denominators) for a point expressed in the local space of a rotated rigid frame.
 13/// </content>
 14internal static partial class WideFiniteAxisIntersection
 15{
 16    #region Nested Types
 17
 18    private readonly struct RigidLocalPointRelation
 19    {
 20        internal readonly Signed192 X;
 21        internal readonly Signed192 Y;
 22        internal readonly Signed192 Z;
 23        internal readonly Signed192 Denominator;
 24        internal readonly Signed192 AxisSquared;
 25        internal readonly Signed320 Projection;
 26        internal readonly Signed576 RadialNumerator;
 27        internal readonly Signed320 RadialDenominator;
 28
 29        internal RigidLocalPointRelation(
 30            Signed192 x,
 31            Signed192 y,
 32            Signed192 z,
 33            Signed192 denominator,
 34            Signed192 axisSquared,
 35            Signed320 projection,
 36            Signed576 radialNumerator,
 37            Signed320 radialDenominator)
 38        {
 21339            X = x;
 21340            Y = y;
 21341            Z = z;
 21342            Denominator = denominator;
 21343            AxisSquared = axisSquared;
 21344            Projection = projection;
 21345            RadialNumerator = radialNumerator;
 21346            RadialDenominator = radialDenominator;
 21347        }
 48    }
 49
 50    #endregion
 51
 52    private static void GetRigidLocalPointRelation(
 53        Vector3d point,
 54        Vector3d center,
 55        FixedQuaternion frameRotation,
 56        Vector3d localAxisDirection,
 57        out RigidLocalPointRelation relation)
 58    {
 21359        WideOrientedBox.GetRelativeLocalPointNumerators(
 21360            point,
 21361            center,
 21362            frameRotation,
 21363            out Signed192 x,
 21364            out Signed192 y,
 21365            out Signed192 z,
 21366            out Signed192 denominator);
 21367        Signed192 axisSquared = GetDot(
 21368            localAxisDirection,
 21369            Vector3d.Zero,
 21370            localAxisDirection,
 21371            Vector3d.Zero);
 21372        Signed320 projection = GetProjection(
 21373            x,
 21374            y,
 21375            z,
 21376            localAxisDirection);
 21377        Signed320 squaredDistance = WideArithmetic.AddSigned320(
 21378            WideArithmetic.AddSigned320(
 21379                WideArithmetic.MultiplySigned192(x, x),
 21380                WideArithmetic.MultiplySigned192(y, y)),
 21381            WideArithmetic.MultiplySigned192(z, z));
 21382        Signed576 radialNumerator = WideArithmetic.SubtractSigned576(
 21383            WideArithmetic.MultiplySigned320(
 21384                squaredDistance,
 21385                Signed320.ExtendValue(axisSquared)),
 21386            WideArithmetic.MultiplySigned320(
 21387                projection,
 21388                projection));
 21389        Signed320 denominatorSquared = WideArithmetic.MultiplySigned192(
 21390            denominator,
 21391            denominator);
 21392        Signed576 wideRadialDenominator = WideArithmetic.MultiplySigned320(
 21393            denominatorSquared,
 21394            Signed320.ExtendValue(axisSquared));
 21395        _ = Signed320.TryNarrowSigned(
 21396            wideRadialDenominator,
 21397            out Signed320 radialDenominator);
 21398        relation = new RigidLocalPointRelation(
 21399            x,
 213100            y,
 213101            z,
 213102            denominator,
 213103            axisSquared,
 213104            projection,
 213105            radialNumerator,
 213106            radialDenominator);
 213107    }
 108}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.RootSolver.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.RootSolver.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
 8namespace FixedMathSharp.Geometry;
 9
 10/// <content>
 11/// Root-solving helpers for the finite-axis intersection quadratic:
 12/// rounds candidate roots to exact fixed-point precision and evaluates
 13/// the polynomial/derivative using wide integer arithmetic to determine
 14/// correct rounding direction near the roots.
 15/// </content>
 16internal static partial class WideFiniteAxisIntersection
 17{
 18    private static Fixed64 RoundLowerRoot(
 19        Signed320 radialCoefficient,
 20        Signed320 radialProjection,
 21        Signed320 radialConstant,
 22        Signed320 scaledSquareRoot)
 23    {
 14824        Signed320 scaledCoefficient = ShiftLeftByFixed64FractionalBits(radialCoefficient);
 14825        Signed320 negativeScaledProjection = WideArithmetic.SubtractSigned320(
 14826            default,
 14827            ShiftLeftByFixed64FractionalBits(radialProjection));
 14828        Signed320 numerator = WideArithmetic.SubtractSigned320(
 14829            negativeScaledProjection,
 14830            scaledSquareRoot);
 14831        Fixed64 candidate = Fixed64.GetSignedRatio(numerator, scaledCoefficient);
 32
 14833        long upperRaw = candidate.m_rawValue;
 14834        if (upperRaw == 0L)
 235            return Fixed64.Zero;
 36
 14637        long lowerRaw = upperRaw - 1L;
 14638        Signed192 midpoint = Signed192.Signed((upperRaw * 2L) - 1L);
 14639        Signed576 value = EvaluatePolynomial(
 14640            radialCoefficient,
 14641            radialProjection,
 14642            radialConstant,
 14643            midpoint,
 14644            DoubleParameterScale);
 14645        if (value.IsZero)
 146            return candidate;
 47
 48        // The upward-biased lower seed differs from the exact root by less than
 49        // 1 / A, so this midpoint cannot cross the vertex before correction.
 14550        return Fixed64.FromRaw(value.Sign > 0
 14551            ? upperRaw
 14552            : lowerRaw);
 53    }
 54
 55    private static Fixed64 RoundUpperRoot(
 56        Signed320 radialCoefficient,
 57        Signed320 radialProjection,
 58        Signed320 radialConstant,
 59        Signed320 scaledSquareRoot,
 60        Fixed64 maxParameter)
 61    {
 14862        Signed320 scaledCoefficient = ShiftLeftByFixed64FractionalBits(radialCoefficient);
 14863        Signed320 negativeScaledProjection = WideArithmetic.SubtractSigned320(
 14864            default,
 14865            ShiftLeftByFixed64FractionalBits(radialProjection));
 14866        Signed320 numerator = WideArithmetic.AddSigned320(
 14867            negativeScaledProjection,
 14868            scaledSquareRoot);
 14869        Fixed64 candidate = Fixed64.GetSignedRatio(numerator, scaledCoefficient);
 70
 14871        long lowerRaw = candidate.m_rawValue;
 14872        if (lowerRaw == maxParameter.m_rawValue)
 273            return maxParameter;
 74
 14675        long upperRaw = lowerRaw + 1L;
 14676        Signed192 midpoint = Signed192.Signed((lowerRaw * 2L) + 1L);
 14677        Signed576 value = EvaluatePolynomial(
 14678            radialCoefficient,
 14679            radialProjection,
 14680            radialConstant,
 14681            midpoint,
 14682            DoubleParameterScale);
 14683        if (value.IsZero)
 184            return candidate;
 85
 86        // The downward-biased upper seed differs from the exact root by less
 87        // than 1 / A, so this midpoint cannot cross the vertex before correction.
 14588        return Fixed64.FromRaw(value.Sign <= 0
 14589            ? upperRaw
 14590            : lowerRaw);
 91    }
 92
 93    private static Signed320 ShiftLeftByFixed64FractionalBits(Signed320 value) =>
 59294        new(
 59295            (value.Word4 << FixedMath.SHIFT_AMOUNT_I) | (value.Word3 >> FixedMath.SHIFT_AMOUNT_I),
 59296            (value.Word3 << FixedMath.SHIFT_AMOUNT_I) | (value.Word2 >> FixedMath.SHIFT_AMOUNT_I),
 59297            (value.Word2 << FixedMath.SHIFT_AMOUNT_I) | (value.Word1 >> FixedMath.SHIFT_AMOUNT_I),
 59298            (value.Word1 << FixedMath.SHIFT_AMOUNT_I) | (value.Word0 >> FixedMath.SHIFT_AMOUNT_I),
 59299            value.Word0 << FixedMath.SHIFT_AMOUNT_I);
 100
 101    private static Signed576 EvaluatePolynomial(
 102        Signed320 radialCoefficient,
 103        Signed320 radialProjection,
 104        Signed320 radialConstant,
 105        Signed192 numerator,
 106        Signed192 denominator)
 107    {
 406108        Signed320 numeratorSquared = WideArithmetic.MultiplySigned192(numerator, numerator);
 406109        Signed320 numeratorDenominator = WideArithmetic.MultiplySigned192(numerator, denominator);
 406110        Signed320 denominatorSquared = WideArithmetic.MultiplySigned192(denominator, denominator);
 406111        Signed576 first = WideArithmetic.MultiplySigned320(radialCoefficient, numeratorSquared);
 406112        Signed576 second = WideArithmetic.MultiplySigned320(radialProjection, numeratorDenominator);
 406113        Signed576 third = WideArithmetic.MultiplySigned320(radialConstant, denominatorSquared);
 406114        return WideArithmetic.AddSigned576(
 406115            WideArithmetic.AddSigned576(
 406116                first,
 406117                WideArithmetic.AddSigned576(second, second)),
 406118            third);
 119    }
 120
 121    private static Signed576 EvaluateDerivative(
 122        Signed320 radialCoefficient,
 123        Signed320 radialProjection,
 124        Signed192 numerator,
 125        Signed192 denominator) =>
 114126        WideArithmetic.AddSigned576(
 114127            WideArithmetic.MultiplySigned320(
 114128                radialCoefficient,
 114129                Signed320.ExtendValue(numerator)),
 114130            WideArithmetic.MultiplySigned320(
 114131                radialProjection,
 114132                Signed320.ExtendValue(denominator)));
 133
 134    private static Signed704 EvaluatePolynomial(
 135        Signed320 radialCoefficient,
 136        Signed320 radialProjection,
 137        Signed320 radialConstant,
 138        Signed320 numerator,
 139        Signed320 denominator)
 140    {
 21138141        Signed704 first = WideArithmetic.MultiplySigned320(radialCoefficient, numerator, numerator);
 21138142        Signed704 second = WideArithmetic.MultiplySigned320(radialProjection, numerator, denominator);
 21138143        Signed704 third = WideArithmetic.MultiplySigned320(radialConstant, denominator, denominator);
 21138144        return WideArithmetic.AddSigned704(
 21138145            WideArithmetic.AddSigned704(
 21138146                first,
 21138147                WideArithmetic.AddSigned704(second, second)),
 21138148            third);
 149    }
 150
 151    private static Signed576 EvaluateDerivative(
 152        Signed320 radialCoefficient,
 153        Signed320 radialProjection,
 154        Signed320 numerator,
 155        Signed320 denominator) =>
 1142156        WideArithmetic.AddSigned576(
 1142157            WideArithmetic.MultiplySigned320(radialCoefficient, numerator),
 1142158            WideArithmetic.MultiplySigned320(radialProjection, denominator));
 159}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.RoundedBox.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.RoundedBox.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>
 13/// Provides segment-vs-rounded-box intersection tests by computing the first distance
 14/// at which a spherically expanded query segment reaches an axis-aligned bounding box,
 15/// using per-axis breakpoints to evaluate closest-feature distance across intervals.
 16/// </content>
 17internal static partial class WideFiniteAxisIntersection
 18{
 19    private const int BoxAxisCount = 3;
 20    private const int MaximumBoxBreakpointCount = BoxAxisCount * 2;
 21
 22    #region Nested Types
 23
 24    private readonly struct BoxBreakpoint
 25    {
 26        internal readonly RationalBound320 Parameter;
 27        internal readonly byte Axis;
 28        internal readonly sbyte StateAfter;
 29
 30        internal BoxBreakpoint(RationalBound320 parameter, byte axis, sbyte stateAfter)
 31        {
 9432            Parameter = parameter;
 9433            Axis = axis;
 9434            StateAfter = stateAfter;
 9435        }
 36    }
 37
 38    #endregion
 39
 40    internal static bool TryGetSphericallyExpandedBoxFirstDistance(
 41        FixedSegment query,
 42        FixedBoundBox box,
 43        Fixed64 sphericalExpansion,
 44        Fixed64 segmentLength,
 45        out Fixed64 distance)
 46    {
 1447        distance = default;
 1448        if (box.Intersects(new FixedBoundSphere(query.Start, sphericalExpansion)))
 149            return true;
 1350        if (query.Start == query.End)
 151            return false;
 52
 53        // The closest box feature is fixed between coordinate-boundary
 54        // crossings, so squared distance is one quadratic per interval.
 1255        Span<BoxBreakpoint> breakpoints = stackalloc BoxBreakpoint[MaximumBoxBreakpointCount];
 1256        int breakpointCount = 0;
 1257        Span<sbyte> axisStates = stackalloc sbyte[BoxAxisCount];
 1258        AddAxisBreakpoints(
 1259            query.Start.X, query.End.X, box.Min.X, box.Max.X, 0,
 1260            breakpoints, ref breakpointCount, out axisStates[0]);
 1261        AddAxisBreakpoints(
 1262            query.Start.Y, query.End.Y, box.Min.Y, box.Max.Y, 1,
 1263            breakpoints, ref breakpointCount, out axisStates[1]);
 1264        AddAxisBreakpoints(
 1265            query.Start.Z, query.End.Z, box.Min.Z, box.Max.Z, 2,
 1266            breakpoints, ref breakpointCount, out axisStates[2]);
 1267        SortBreakpoints(breakpoints, breakpointCount);
 68
 1269        Signed320 unit = Scale320;
 1270        RationalBound320 lower = new(default, unit);
 1271        RationalBound320 maximum = new(unit, unit);
 1272        int breakpointIndex = 0;
 273        while (true)
 74        {
 1475            RationalBound320 upper = breakpointIndex < breakpointCount
 1476                ? breakpoints[breakpointIndex].Parameter
 1477                : maximum;
 1478            GetBoxDistancePolynomial(
 1479                query,
 1480                box,
 1481                axisStates,
 1482                sphericalExpansion,
 1483                out Signed320 coefficient,
 1484                out Signed320 projection,
 1485                out Signed320 constant);
 1486            if (TrySolveBoundedQuadraticAtDistance(
 1487                    coefficient,
 1488                    projection,
 1489                    constant,
 1490                    lower,
 1491                    upper,
 1492                    segmentLength,
 1493                    out distance,
 1494                    out _))
 95            {
 1096                return true;
 97            }
 98
 499            if (breakpointIndex == breakpointCount)
 2100                return false;
 101
 102            do
 103            {
 2104                BoxBreakpoint breakpoint = breakpoints[breakpointIndex++];
 2105                axisStates[breakpoint.Axis] = breakpoint.StateAfter;
 106            }
 2107            while (breakpointIndex < breakpointCount
 2108                && Compare(breakpoints[breakpointIndex].Parameter, upper) == 0);
 2109            lower = upper;
 110        }
 111    }
 112
 113    internal static bool TryGetSphericallyExpandedProjectedBoxFirstDistance(
 114        Signed192 startX,
 115        Signed192 startY,
 116        Signed192 startZ,
 117        Signed192 endX,
 118        Signed192 endY,
 119        Signed192 endZ,
 120        Signed192 extentX,
 121        Signed192 extentY,
 122        Signed192 extentZ,
 123        Signed192 projectionDenominator,
 124        Fixed64 sphericalExpansion,
 125        Fixed64 segmentLength,
 126        out Fixed64 distance)
 127    {
 47128        distance = default;
 129        // A nonnegative Fixed64 radius scaled by a normalized quaternion-basis
 130        // denominator uses at most 127 signed bits.
 47131        Signed192 radiusNumerator = Signed192.NarrowValue(
 47132            WideArithmetic.MultiplySigned192(
 47133                Signed192.Signed(sphericalExpansion.m_rawValue),
 47134                projectionDenominator));
 47135        if (IsWithinProjectedRoundedBox(
 47136                startX,
 47137                startY,
 47138                startZ,
 47139                extentX,
 47140                extentY,
 47141                extentZ,
 47142                radiusNumerator))
 143        {
 1144            return true;
 145        }
 146
 46147        if (WideArithmetic.SubtractSigned192(endX, startX).IsZero
 46148            && WideArithmetic.SubtractSigned192(endY, startY).IsZero
 46149            && WideArithmetic.SubtractSigned192(endZ, startZ).IsZero)
 1150            return false;
 151
 45152        Span<BoxBreakpoint> breakpoints = stackalloc BoxBreakpoint[MaximumBoxBreakpointCount];
 45153        int breakpointCount = 0;
 45154        Span<sbyte> axisStates = stackalloc sbyte[BoxAxisCount];
 45155        AddProjectedAxisBreakpoints(
 45156            startX, endX, extentX, 0,
 45157            breakpoints, ref breakpointCount, out axisStates[0]);
 45158        AddProjectedAxisBreakpoints(
 45159            startY, endY, extentY, 1,
 45160            breakpoints, ref breakpointCount, out axisStates[1]);
 45161        AddProjectedAxisBreakpoints(
 45162            startZ, endZ, extentZ, 2,
 45163            breakpoints, ref breakpointCount, out axisStates[2]);
 45164        SortBreakpoints(breakpoints, breakpointCount);
 165
 45166        Signed320 unit = Scale320;
 45167        RationalBound320 lower = new(default, unit);
 45168        RationalBound320 maximum = new(unit, unit);
 45169        int breakpointIndex = 0;
 3170        while (true)
 171        {
 48172            RationalBound320 upper = breakpointIndex < breakpointCount
 48173                ? breakpoints[breakpointIndex].Parameter
 48174                : maximum;
 48175            GetProjectedBoxDistancePolynomial(
 48176                startX,
 48177                startY,
 48178                startZ,
 48179                endX,
 48180                endY,
 48181                endZ,
 48182                extentX,
 48183                extentY,
 48184                extentZ,
 48185                axisStates,
 48186                radiusNumerator,
 48187                out Signed320 coefficient,
 48188                out Signed320 projection,
 48189                out Signed320 constant);
 48190            if (TrySolveBoundedQuadraticAtDistance(
 48191                    coefficient,
 48192                    projection,
 48193                    constant,
 48194                    lower,
 48195                    upper,
 48196                    segmentLength,
 48197                    out distance,
 48198                    out _))
 199            {
 40200                return true;
 201            }
 202
 8203            if (breakpointIndex == breakpointCount)
 5204                return false;
 205
 206            do
 207            {
 3208                BoxBreakpoint breakpoint = breakpoints[breakpointIndex++];
 3209                axisStates[breakpoint.Axis] = breakpoint.StateAfter;
 210            }
 3211            while (breakpointIndex < breakpointCount
 3212                && Compare(breakpoints[breakpointIndex].Parameter, upper) == 0);
 3213            lower = upper;
 214        }
 215    }
 216
 217    private static void AddAxisBreakpoints(
 218        Fixed64 start,
 219        Fixed64 end,
 220        Fixed64 minimum,
 221        Fixed64 maximum,
 222        byte axis,
 223        Span<BoxBreakpoint> breakpoints,
 224        ref int count,
 225        out sbyte initialState)
 226    {
 36227        Signed192 direction = WideArithmetic.Difference(end, start);
 36228        initialState = start < minimum || (start == minimum && direction.Sign < 0)
 36229            ? (sbyte)-1
 36230            : start > maximum || (start == maximum && direction.Sign > 0)
 36231                ? (sbyte)1
 36232                : (sbyte)0;
 36233        if (direction.Sign > 0)
 234        {
 2235            AddBreakpoint(start, minimum, direction, axis, 0, breakpoints, ref count);
 2236            AddBreakpoint(start, maximum, direction, axis, 1, breakpoints, ref count);
 237        }
 34238        else if (direction.Sign < 0)
 239        {
 13240            AddBreakpoint(start, maximum, direction, axis, 0, breakpoints, ref count);
 13241            AddBreakpoint(start, minimum, direction, axis, -1, breakpoints, ref count);
 242        }
 34243    }
 244
 245    private static void AddProjectedAxisBreakpoints(
 246        Signed192 start,
 247        Signed192 end,
 248        Signed192 extent,
 249        byte axis,
 250        Span<BoxBreakpoint> breakpoints,
 251        ref int count,
 252        out sbyte initialState)
 253    {
 135254        Signed192 minimum = WideArithmetic.SubtractSigned192(default, extent);
 135255        Signed192 direction = WideArithmetic.SubtractSigned192(end, start);
 135256        int minimumComparison = WideArithmetic.SubtractSigned192(start, minimum).Sign;
 135257        int maximumComparison = WideArithmetic.SubtractSigned192(start, extent).Sign;
 135258        initialState = minimumComparison < 0
 135259            || (minimumComparison == 0 && direction.Sign < 0)
 135260                ? (sbyte)-1
 135261                : maximumComparison > 0
 135262                    || (maximumComparison == 0 && direction.Sign > 0)
 135263                        ? (sbyte)1
 135264                        : (sbyte)0;
 135265        if (direction.Sign > 0)
 266        {
 12267            AddProjectedBreakpoint(
 12268                start, minimum, direction, axis, 0, breakpoints, ref count);
 12269            AddProjectedBreakpoint(
 12270                start, extent, direction, axis, 1, breakpoints, ref count);
 271        }
 123272        else if (direction.Sign < 0)
 273        {
 113274            AddProjectedBreakpoint(
 113275                start, extent, direction, axis, 0, breakpoints, ref count);
 113276            AddProjectedBreakpoint(
 113277                start, minimum, direction, axis, -1, breakpoints, ref count);
 278        }
 123279    }
 280
 281    private static void AddBreakpoint(
 282        Fixed64 start,
 283        Fixed64 boundary,
 284        Signed192 direction,
 285        byte axis,
 286        sbyte stateAfter,
 287        Span<BoxBreakpoint> breakpoints,
 288        ref int count)
 289    {
 30290        RationalBound320 parameter = Normalize(
 30291            Signed320.ExtendValue(WideArithmetic.Difference(boundary, start)),
 30292            Signed320.ExtendValue(direction));
 30293        Signed320 unit = Scale320;
 30294        if (parameter.Numerator.Sign <= 0
 30295            || Compare(parameter, new RationalBound320(unit, unit)) >= 0)
 296        {
 19297            return;
 298        }
 299
 11300        breakpoints[count++] = new BoxBreakpoint(parameter, axis, stateAfter);
 11301    }
 302
 303    private static void AddProjectedBreakpoint(
 304        Signed192 start,
 305        Signed192 boundary,
 306        Signed192 direction,
 307        byte axis,
 308        sbyte stateAfter,
 309        Span<BoxBreakpoint> breakpoints,
 310        ref int count)
 311    {
 250312        RationalBound320 parameter = Normalize(
 250313            Signed320.ExtendValue(
 250314                WideArithmetic.SubtractSigned192(boundary, start)),
 250315            Signed320.ExtendValue(direction));
 250316        Signed320 unit = Scale320;
 250317        if (parameter.Numerator.Sign <= 0
 250318            || Compare(parameter, new RationalBound320(unit, unit)) >= 0)
 319        {
 167320            return;
 321        }
 322
 83323        breakpoints[count++] = new BoxBreakpoint(parameter, axis, stateAfter);
 83324    }
 325
 326    private static void SortBreakpoints(Span<BoxBreakpoint> breakpoints, int count)
 327    {
 204328        for (int index = 1; index < count; index++)
 329        {
 45330            BoxBreakpoint current = breakpoints[index];
 45331            int insertion = index;
 85332            while (insertion > 0
 85333                && Compare(breakpoints[insertion - 1].Parameter, current.Parameter) > 0)
 334            {
 40335                breakpoints[insertion] = breakpoints[insertion - 1];
 40336                insertion--;
 337            }
 338
 45339            breakpoints[insertion] = current;
 340        }
 57341    }
 342
 343    private static void GetBoxDistancePolynomial(
 344        FixedSegment query,
 345        FixedBoundBox box,
 346        Span<sbyte> axisStates,
 347        Fixed64 sphericalExpansion,
 348        out Signed320 coefficient,
 349        out Signed320 projection,
 350        out Signed320 constant)
 351    {
 14352        coefficient = default;
 14353        projection = default;
 14354        Signed192 radius = Signed192.Signed(sphericalExpansion.m_rawValue);
 14355        constant = WideArithmetic.SubtractSigned320(
 14356            default,
 14357            WideArithmetic.MultiplySigned192(radius, radius));
 358
 14359        AddBoxAxisPolynomial(
 14360            query.Start.X, query.End.X, box.Min.X, box.Max.X, axisStates[0],
 14361            ref coefficient, ref projection, ref constant);
 14362        AddBoxAxisPolynomial(
 14363            query.Start.Y, query.End.Y, box.Min.Y, box.Max.Y, axisStates[1],
 14364            ref coefficient, ref projection, ref constant);
 14365        AddBoxAxisPolynomial(
 14366            query.Start.Z, query.End.Z, box.Min.Z, box.Max.Z, axisStates[2],
 14367            ref coefficient, ref projection, ref constant);
 14368    }
 369
 370    private static void GetProjectedBoxDistancePolynomial(
 371        Signed192 startX,
 372        Signed192 startY,
 373        Signed192 startZ,
 374        Signed192 endX,
 375        Signed192 endY,
 376        Signed192 endZ,
 377        Signed192 extentX,
 378        Signed192 extentY,
 379        Signed192 extentZ,
 380        Span<sbyte> axisStates,
 381        Signed192 radiusNumerator,
 382        out Signed320 coefficient,
 383        out Signed320 projection,
 384        out Signed320 constant)
 385    {
 48386        coefficient = default;
 48387        projection = default;
 48388        constant = WideArithmetic.SubtractSigned320(
 48389            default,
 48390            WideArithmetic.MultiplySigned192(radiusNumerator, radiusNumerator));
 391
 48392        AddProjectedBoxAxisPolynomial(
 48393            startX, endX, extentX, axisStates[0],
 48394            ref coefficient, ref projection, ref constant);
 48395        AddProjectedBoxAxisPolynomial(
 48396            startY, endY, extentY, axisStates[1],
 48397            ref coefficient, ref projection, ref constant);
 48398        AddProjectedBoxAxisPolynomial(
 48399            startZ, endZ, extentZ, axisStates[2],
 48400            ref coefficient, ref projection, ref constant);
 48401    }
 402
 403    private static void AddBoxAxisPolynomial(
 404        Fixed64 start,
 405        Fixed64 end,
 406        Fixed64 minimum,
 407        Fixed64 maximum,
 408        sbyte state,
 409        ref Signed320 coefficient,
 410        ref Signed320 projection,
 411        ref Signed320 constant)
 412    {
 42413        if (state == 0)
 18414            return;
 415
 24416        Signed192 direction = WideArithmetic.Difference(end, start);
 24417        Signed192 offset = WideArithmetic.Difference(start, state < 0 ? minimum : maximum);
 24418        coefficient = WideArithmetic.AddSigned320(
 24419            coefficient,
 24420            WideArithmetic.MultiplySigned192(direction, direction));
 24421        projection = WideArithmetic.AddSigned320(
 24422            projection,
 24423            WideArithmetic.MultiplySigned192(offset, direction));
 24424        constant = WideArithmetic.AddSigned320(
 24425            constant,
 24426            WideArithmetic.MultiplySigned192(offset, offset));
 24427    }
 428
 429    private static void AddProjectedBoxAxisPolynomial(
 430        Signed192 start,
 431        Signed192 end,
 432        Signed192 extent,
 433        sbyte state,
 434        ref Signed320 coefficient,
 435        ref Signed320 projection,
 436        ref Signed320 constant)
 437    {
 144438        if (state == 0)
 51439            return;
 440
 93441        Signed192 direction = WideArithmetic.SubtractSigned192(end, start);
 93442        Signed192 boundary = state < 0
 93443            ? WideArithmetic.SubtractSigned192(default, extent)
 93444            : extent;
 93445        Signed192 offset = WideArithmetic.SubtractSigned192(start, boundary);
 93446        coefficient = WideArithmetic.AddSigned320(
 93447            coefficient,
 93448            WideArithmetic.MultiplySigned192(direction, direction));
 93449        projection = WideArithmetic.AddSigned320(
 93450            projection,
 93451            WideArithmetic.MultiplySigned192(offset, direction));
 93452        constant = WideArithmetic.AddSigned320(
 93453            constant,
 93454            WideArithmetic.MultiplySigned192(offset, offset));
 93455    }
 456
 457    private static bool IsWithinProjectedRoundedBox(
 458        Signed192 x,
 459        Signed192 y,
 460        Signed192 z,
 461        Signed192 extentX,
 462        Signed192 extentY,
 463        Signed192 extentZ,
 464        Signed192 radiusNumerator)
 465    {
 47466        Signed192 offsetX = GetProjectedBoxAxisOffset(x, extentX);
 47467        Signed192 offsetY = GetProjectedBoxAxisOffset(y, extentY);
 47468        Signed192 offsetZ = GetProjectedBoxAxisOffset(z, extentZ);
 47469        Signed320 squaredDistance = WideArithmetic.AddSigned320(
 47470            WideArithmetic.AddSigned320(
 47471                WideArithmetic.MultiplySigned192(offsetX, offsetX),
 47472                WideArithmetic.MultiplySigned192(offsetY, offsetY)),
 47473            WideArithmetic.MultiplySigned192(offsetZ, offsetZ));
 47474        Signed320 squaredRadius = WideArithmetic.MultiplySigned192(
 47475            radiusNumerator,
 47476            radiusNumerator);
 47477        return WideArithmetic.SubtractSigned320(
 47478            squaredDistance,
 47479            squaredRadius).Sign <= 0;
 480    }
 481
 482    private static Signed192 GetProjectedBoxAxisOffset(
 483        Signed192 coordinate,
 484        Signed192 extent)
 485    {
 141486        Signed192 minimum = WideArithmetic.SubtractSigned192(default, extent);
 141487        if (WideArithmetic.SubtractSigned192(coordinate, minimum).Sign < 0)
 4488            return WideArithmetic.SubtractSigned192(coordinate, minimum);
 137489        if (WideArithmetic.SubtractSigned192(coordinate, extent).Sign > 0)
 84490            return WideArithmetic.SubtractSigned192(coordinate, extent);
 53491        return default;
 492    }
 493}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.RoundedCylinder.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.RoundedCylinder.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
 8namespace FixedMathSharp.Geometry;
 9
 10/// <content>
 11/// Rounded-cylinder (capsule-swept torus/cylinder) intersection tests for wide, high-precision
 12/// finite-axis geometry, using extended-width fixed-point arithmetic for numerical robustness.
 13/// </content>
 14internal static partial class WideFiniteAxisIntersection
 15{
 16    #region Nested Types
 17
 18    private readonly struct RoundedCylinderBaseData
 19    {
 20        internal readonly Signed192 AxisLengthSquared;
 21        internal readonly Signed192 G0;
 22        internal readonly Signed192 G1;
 23        internal readonly Signed192 G2;
 24        internal readonly Signed192 StartAxisProjection;
 25        internal readonly Signed192 DirectionAxisProjection;
 26        internal readonly Signed320 Q0;
 27        internal readonly Signed320 Q1;
 28        internal readonly Signed320 Q2;
 29        internal readonly Signed192 Radius;
 30        internal readonly Signed192 Expansion;
 31
 32        internal RoundedCylinderBaseData(
 33            Signed192 axisLengthSquared,
 34            Signed192 g0,
 35            Signed192 g1,
 36            Signed192 g2,
 37            Signed192 startAxisProjection,
 38            Signed192 directionAxisProjection,
 39            Signed320 q0,
 40            Signed320 q1,
 41            Signed320 q2,
 42            Signed192 radius,
 43            Signed192 expansion)
 44        {
 4545            AxisLengthSquared = axisLengthSquared;
 4546            G0 = g0;
 4547            G1 = g1;
 4548            G2 = g2;
 4549            StartAxisProjection = startAxisProjection;
 4550            DirectionAxisProjection = directionAxisProjection;
 4551            Q0 = q0;
 4552            Q1 = q1;
 4553            Q2 = q2;
 4554            Radius = radius;
 4555            Expansion = expansion;
 4556        }
 57    }
 58
 59    private readonly struct RoundedCylinderTorusPolynomial
 60    {
 61        internal readonly Signed320 N0;
 62        internal readonly Signed320 N1;
 63        internal readonly Signed320 N2;
 64        internal readonly Signed576 H0;
 65        internal readonly Signed576 H1;
 66        internal readonly Signed576 H2;
 67        internal readonly Signed576 H3;
 68        internal readonly Signed576 H4;
 69
 70        internal RoundedCylinderTorusPolynomial(
 71            Signed320 n0,
 72            Signed320 n1,
 73            Signed320 n2,
 74            Signed576 h0,
 75            Signed576 h1,
 76            Signed576 h2,
 77            Signed576 h3,
 78            Signed576 h4)
 79        {
 4880            N0 = n0;
 4881            N1 = n1;
 4882            N2 = n2;
 4883            H0 = h0;
 4884            H1 = h1;
 4885            H2 = h2;
 4886            H3 = h3;
 4887            H4 = h4;
 4888        }
 89    }
 90
 91    private readonly struct RoundedCylinderQuadraticBound
 92    {
 193        internal static readonly RoundedCylinderQuadraticBound Zero = new(
 194            default,
 195            Scale320,
 196            default,
 197            0);
 198        internal static readonly RoundedCylinderQuadraticBound One = new(
 199            Scale320,
 1100            Scale320,
 1101            default,
 1102            0);
 103
 104        internal readonly Signed320 Numerator;
 105        internal readonly Signed320 Denominator;
 106        internal readonly Signed576 Discriminant;
 107        internal readonly int RadicalSign;
 108
 109        internal RoundedCylinderQuadraticBound(
 110            Signed320 numerator,
 111            Signed320 denominator,
 112            Signed576 discriminant,
 113            int radicalSign)
 114        {
 18115            Numerator = numerator;
 18116            Denominator = denominator;
 18117            Discriminant = discriminant;
 18118            RadicalSign = radicalSign;
 18119        }
 120    }
 121
 122    #endregion
 123
 124    internal static bool DoesCenteredFiniteCylinderOverlapSphere(
 125        Vector3d cylinderCenter,
 126        Vector3d cylinderAxisDirection,
 127        Fixed64 cylinderAxisLength,
 128        Fixed64 cylinderRadius,
 129        Vector3d sphereCenter,
 130        Fixed64 sphereRadius)
 131    {
 2132        _ = TryGetSphericallyExpandedFiniteCylinderDistanceIntervalWithFullAxisLength(
 2133            new FixedSegment(sphereCenter, sphereCenter),
 2134            cylinderCenter,
 2135            cylinderAxisDirection,
 2136            cylinderAxisLength,
 2137            cylinderRadius,
 2138            sphereRadius,
 2139            Fixed64.Zero,
 2140            calculateExit: false,
 2141            out _,
 2142            out _,
 2143            out bool contained,
 2144            out _);
 2145        return contained;
 146    }
 147
 148    internal static bool TryGetSphericallyExpandedFiniteCylinderDistanceInterval(
 149        FixedSegment query,
 150        Vector3d center,
 151        Vector3d axisDirection,
 152        Fixed64 axisLength,
 153        Fixed64 radius,
 154        Fixed64 sphericalExpansion,
 155        Fixed64 segmentLength,
 156        out Fixed64 entry,
 157        out Fixed64 exit,
 158        out bool startContained,
 159        out bool endContainedStrict) =>
 35160        TryGetSphericallyExpandedFiniteCylinderDistanceIntervalWithFullAxisLength(
 35161            query,
 35162            center,
 35163            axisDirection,
 35164            axisLength,
 35165            radius,
 35166            sphericalExpansion,
 35167            segmentLength,
 35168            calculateExit: true,
 35169            out entry,
 35170            out exit,
 35171            out startContained,
 35172            out endContainedStrict);
 173
 174    internal static bool TryGetSphericallyExpandedFiniteCylinderFirstDistance(
 175        FixedSegment query,
 176        Vector3d center,
 177        Vector3d axisDirection,
 178        Fixed64 axisLength,
 179        Fixed64 radius,
 180        Fixed64 sphericalExpansion,
 181        Fixed64 segmentLength,
 182        out Fixed64 distance) =>
 5183        TryGetSphericallyExpandedFiniteCylinderDistanceIntervalWithFullAxisLength(
 5184            query,
 5185            center,
 5186            axisDirection,
 5187            axisLength,
 5188            radius,
 5189            sphericalExpansion,
 5190            segmentLength,
 5191            calculateExit: false,
 5192            out distance,
 5193            out _,
 5194            out _,
 5195            out _);
 196
 197    internal static bool TryGetSphericallyExpandedFiniteCylinderFirstDistanceFromHalfAxisLength(
 198        FixedSegment query,
 199        Vector3d center,
 200        Vector3d axisDirection,
 201        Fixed64 halfAxisLength,
 202        Fixed64 radius,
 203        Fixed64 sphericalExpansion,
 204        Fixed64 segmentLength,
 205        out Fixed64 distance)
 206    {
 5207        Signed192 halfAxisLengthRaw =
 5208            Signed192.Signed(halfAxisLength.m_rawValue);
 5209        Signed192 fullAxisLength = WideArithmetic.AddSigned192(
 5210            halfAxisLengthRaw,
 5211            halfAxisLengthRaw);
 5212        return TryGetSphericallyExpandedFiniteCylinderDistanceInterval(
 5213            query,
 5214            center,
 5215            axisDirection,
 5216            fullAxisLength,
 5217            radius,
 5218            sphericalExpansion,
 5219            segmentLength,
 5220            calculateExit: false,
 5221            out distance,
 5222            out _,
 5223            out _,
 5224            out _);
 225    }
 226
 227    internal static bool TryGetSphericallyExpandedFiniteCylinderDirectionFirstDistanceFromHalfAxisLength(
 228        Vector3d position,
 229        Vector3d direction,
 230        Vector3d center,
 231        Vector3d axisDirection,
 232        Fixed64 halfAxisLength,
 233        Fixed64 radius,
 234        Fixed64 sphericalExpansion,
 235        Fixed64 totalDistance,
 236        out Fixed64 distance)
 237    {
 3238        Signed192 halfAxisLengthRaw =
 3239            Signed192.Signed(halfAxisLength.m_rawValue);
 3240        Signed192 fullAxisLength = WideArithmetic.AddSigned192(
 3241            halfAxisLengthRaw,
 3242            halfAxisLengthRaw);
 243
 3244        if (sphericalExpansion == Fixed64.Zero)
 245        {
 1246            return TryGetFiniteCylinderDirectionFirstDistance(
 1247                position,
 1248                direction,
 1249                center,
 1250                axisDirection,
 1251                fullAxisLength,
 1252                radius,
 1253                Fixed64.Zero,
 1254                Fixed64.Zero,
 1255                totalDistance,
 1256                out distance,
 1257                out _);
 258        }
 259
 2260        bool found = TryGetFiniteCylinderDirectionFirstDistance(
 2261            position,
 2262            direction,
 2263            center,
 2264            axisDirection,
 2265            fullAxisLength,
 2266            radius,
 2267            sphericalExpansion,
 2268            Fixed64.Zero,
 2269            totalDistance,
 2270            out distance,
 2271            out bool sideStartContained);
 2272        RoundedCylinderBaseData baseData = CreateRoundedCylinderBaseData(
 2273            position,
 2274            direction,
 2275            center,
 2276            axisDirection,
 2277            radius,
 2278            sphericalExpansion);
 2279        Fixed64 exit = default;
 2280        return MergeSphericallyExpandedFiniteCylinderRoundedIntervals(
 2281            baseData,
 2282            fullAxisLength,
 2283            totalDistance,
 2284            calculateExit: false,
 2285            ref found,
 2286            ref distance,
 2287            ref exit,
 2288            sideStartContained,
 2289            false,
 2290            out _,
 2291            out _);
 292    }
 293
 294    private static bool TryGetSphericallyExpandedFiniteCylinderDistanceIntervalWithFullAxisLength(
 295        FixedSegment query,
 296        Vector3d center,
 297        Vector3d axisDirection,
 298        Fixed64 axisLength,
 299        Fixed64 radius,
 300        Fixed64 sphericalExpansion,
 301        Fixed64 segmentLength,
 302        bool calculateExit,
 303        out Fixed64 entry,
 304        out Fixed64 exit,
 305        out bool startContained,
 306        out bool endContainedStrict)
 307    {
 42308        if (sphericalExpansion == Fixed64.Zero)
 309        {
 2310            return TryGetFiniteCylinderDistanceInterval(
 2311                query,
 2312                center,
 2313                axisDirection,
 2314                axisLength,
 2315                radius,
 2316                Fixed64.Zero,
 2317                Fixed64.Zero,
 2318                segmentLength,
 2319                out entry,
 2320                out exit,
 2321                out startContained,
 2322                out endContainedStrict);
 323        }
 324
 40325        if (radius == Fixed64.Zero)
 326        {
 1327            return TryGetCapsuleDistanceInterval(
 1328                query,
 1329                center,
 1330                axisDirection,
 1331                axisLength,
 1332                Fixed64.Zero,
 1333                sphericalExpansion,
 1334                segmentLength,
 1335                out entry,
 1336                out exit,
 1337                out startContained,
 1338                out endContainedStrict);
 339        }
 340
 39341        return TryGetSphericallyExpandedFiniteCylinderDistanceInterval(
 39342            query,
 39343            center,
 39344            axisDirection,
 39345            Signed192.Signed(axisLength.m_rawValue),
 39346            radius,
 39347            sphericalExpansion,
 39348            segmentLength,
 39349            calculateExit,
 39350            out entry,
 39351            out exit,
 39352            out startContained,
 39353            out endContainedStrict);
 354    }
 355
 356    private static bool TryGetSphericallyExpandedFiniteCylinderDistanceInterval(
 357        FixedSegment query,
 358        Vector3d center,
 359        Vector3d axisDirection,
 360        Signed192 axisLength,
 361        Fixed64 radius,
 362        Fixed64 sphericalExpansion,
 363        Fixed64 segmentLength,
 364        bool calculateExit,
 365        out Fixed64 entry,
 366        out Fixed64 exit,
 367        out bool startContained,
 368        out bool endContainedStrict)
 369    {
 44370        if (sphericalExpansion == Fixed64.Zero)
 371        {
 1372            return TryGetFiniteCylinderDistanceInterval(
 1373                query,
 1374                center,
 1375                axisDirection,
 1376                axisLength,
 1377                radius,
 1378                Fixed64.Zero,
 1379                Fixed64.Zero,
 1380                segmentLength,
 1381                out entry,
 1382                out exit,
 1383                out startContained,
 1384                out endContainedStrict);
 385        }
 386
 43387        bool found = TryGetFiniteCylinderDistanceInterval(
 43388            query,
 43389            center,
 43390            axisDirection,
 43391            axisLength,
 43392            radius,
 43393            sphericalExpansion,
 43394            Fixed64.Zero,
 43395            segmentLength,
 43396            out entry,
 43397            out exit,
 43398            out bool sideStartContained,
 43399            out bool sideEndContainedStrict);
 400
 43401        RoundedCylinderBaseData baseData = CreateRoundedCylinderBaseData(
 43402            query,
 43403            center,
 43404            axisDirection,
 43405            radius,
 43406            sphericalExpansion);
 43407        return MergeSphericallyExpandedFiniteCylinderRoundedIntervals(
 43408            baseData,
 43409            axisLength,
 43410            segmentLength,
 43411            calculateExit,
 43412            ref found,
 43413            ref entry,
 43414            ref exit,
 43415            sideStartContained,
 43416            sideEndContainedStrict,
 43417            out startContained,
 43418            out endContainedStrict);
 419    }
 420
 421    private static bool MergeSphericallyExpandedFiniteCylinderRoundedIntervals(
 422        RoundedCylinderBaseData baseData,
 423        Signed192 axisLength,
 424        Fixed64 segmentLength,
 425        bool calculateExit,
 426        ref bool found,
 427        ref Fixed64 entry,
 428        ref Fixed64 exit,
 429        bool sideStartContained,
 430        bool sideEndContainedStrict,
 431        out bool startContained,
 432        out bool endContainedStrict)
 433    {
 45434        Merge(
 45435            TryGetRoundedCylinderCapCoreDistanceInterval(
 45436                baseData,
 45437                axisLength,
 45438                baseData.Radius,
 45439                segmentLength,
 45440                positiveCap: false,
 45441                out Fixed64 capEntry,
 45442                out Fixed64 capExit,
 45443                out bool negativeCapStartContained,
 45444                out bool negativeCapEndContainedStrict),
 45445            capEntry,
 45446            capExit,
 45447            ref found,
 45448            ref entry,
 45449            ref exit);
 45450        Merge(
 45451            TryGetRoundedCylinderCapCoreDistanceInterval(
 45452                baseData,
 45453                axisLength,
 45454                baseData.Radius,
 45455                segmentLength,
 45456                positiveCap: true,
 45457                out capEntry,
 45458                out capExit,
 45459                out bool positiveCapStartContained,
 45460                out bool positiveCapEndContainedStrict),
 45461            capEntry,
 45462            capExit,
 45463            ref found,
 45464            ref entry,
 45465            ref exit);
 45466        bool negativeStartContained = false;
 45467        bool negativeEndContainedStrict = false;
 45468        if (RoundedCylinderRimBoundsIntersect(
 45469                baseData,
 45470                axisLength,
 45471                segmentLength,
 45472                positiveCap: false))
 473        {
 12474            MergeRoundedCylinderRimInterval(
 12475                CreateRoundedCylinderTorusPolynomial(baseData, axisLength, positiveCap: false),
 12476                segmentLength,
 12477                ref found,
 12478                ref entry,
 12479                ref exit,
 12480                calculateExit,
 12481                out negativeStartContained,
 12482                out negativeEndContainedStrict);
 483        }
 484
 45485        bool positiveStartContained = false;
 45486        bool positiveEndContainedStrict = false;
 45487        if (RoundedCylinderRimBoundsIntersect(
 45488                baseData,
 45489                axisLength,
 45490                segmentLength,
 45491                positiveCap: true))
 492        {
 36493            MergeRoundedCylinderRimInterval(
 36494                CreateRoundedCylinderTorusPolynomial(baseData, axisLength, positiveCap: true),
 36495                segmentLength,
 36496                ref found,
 36497                ref entry,
 36498                ref exit,
 36499                calculateExit,
 36500                out positiveStartContained,
 36501                out positiveEndContainedStrict);
 502        }
 503
 45504        startContained = sideStartContained
 45505            || negativeCapStartContained
 45506            || positiveCapStartContained
 45507            || negativeStartContained
 45508            || positiveStartContained;
 45509        endContainedStrict = sideEndContainedStrict
 45510            || negativeCapEndContainedStrict
 45511            || positiveCapEndContainedStrict
 45512            || negativeEndContainedStrict
 45513            || positiveEndContainedStrict;
 45514        return found;
 515    }
 516
 517    private static bool TryGetRoundedCylinderCapCoreDistanceInterval(
 518        RoundedCylinderBaseData data,
 519        Signed192 axisLength,
 520        Signed192 radialRadius,
 521        Fixed64 segmentLength,
 522        bool positiveCap,
 523        out Fixed64 entry,
 524        out Fixed64 exit,
 525        out bool startContained,
 526        out bool endContainedStrict)
 527    {
 180528        Signed320 radiusSquared = WideArithmetic.MultiplySigned192(radialRadius, radialRadius);
 180529        Signed320 radialLimit = MultiplyToSigned320(radiusSquared, data.AxisLengthSquared);
 180530        Signed320 radial0 = WideArithmetic.SubtractSigned320(data.Q0, radialLimit);
 180531        Signed320 radialProjection = ShiftRightExact(data.Q1, 1);
 180532        Signed320 radial2 = data.Q2;
 533
 180534        Signed320 scaledStartProjection = WideArithmetic.MultiplySigned192(
 180535            DoubleParameterScale,
 180536            data.StartAxisProjection);
 180537        Signed320 scaledDirectionProjection = WideArithmetic.MultiplySigned192(
 180538            DoubleParameterScale,
 180539            data.DirectionAxisProjection);
 180540        Signed320 capProjection = WideArithmetic.MultiplySigned192(
 180541            axisLength,
 180542            data.AxisLengthSquared);
 180543        Signed320 z0 = positiveCap
 180544            ? WideArithmetic.SubtractSigned320(scaledStartProjection, capProjection)
 180545            : WideArithmetic.AddSigned320(scaledStartProjection, capProjection);
 180546        Signed320 z1 = scaledDirectionProjection;
 180547        Signed320 expansionSquared = WideArithmetic.MultiplySigned192(data.Expansion, data.Expansion);
 180548        Signed320 scaleSquared = WideArithmetic.MultiplySigned192(
 180549            DoubleParameterScale,
 180550            DoubleParameterScale);
 180551        Signed320 axialLimit = MultiplyToSigned320(
 180552            MultiplyToSigned320(expansionSquared, scaleSquared),
 180553            data.AxisLengthSquared);
 180554        Signed320 axial0 = WideArithmetic.SubtractSigned320(
 180555            MultiplyToSigned320(z0, z0),
 180556            axialLimit);
 180557        Signed320 axial1 = MultiplyToSigned320(z0, z1);
 180558        Signed320 axial2 = MultiplyToSigned320(z1, z1);
 559
 180560        int startRadialSign = radial0.Sign;
 180561        int startAxialSign = axial0.Sign;
 180562        int endRadialSign = WideArithmetic.AddSigned320(
 180563            WideArithmetic.AddSigned320(radial0, data.Q1),
 180564            radial2).Sign;
 180565        int endAxialSign = WideArithmetic.AddSigned320(
 180566            WideArithmetic.AddSigned320(axial0, axial1),
 180567            WideArithmetic.AddSigned320(axial1, axial2)).Sign;
 180568        startContained = startRadialSign <= 0 && startAxialSign <= 0;
 180569        endContainedStrict = endRadialSign < 0 && endAxialSign < 0;
 180570        bool endRadialContained = endRadialSign <= 0;
 180571        bool endAxialContained = endAxialSign <= 0;
 572
 180573        if (!TrySolveUnitQuadraticAtDistance(
 180574                radial2,
 180575                radialProjection,
 180576                radial0,
 180577                segmentLength,
 180578                out Fixed64 radialEntry,
 180579                out Fixed64 radialExit)
 180580            || !TrySolveUnitQuadraticAtDistance(
 180581                axial2,
 180582                axial1,
 180583                axial0,
 180584                segmentLength,
 180585                out Fixed64 axialEntry,
 180586                out Fixed64 axialExit))
 587        {
 100588            entry = default;
 100589            exit = default;
 100590            return false;
 591        }
 592
 80593        entry = radialEntry >= axialEntry ? radialEntry : axialEntry;
 80594        exit = radialExit <= axialExit ? radialExit : axialExit;
 80595        if (entry < exit)
 63596            return true;
 17597        if (entry > exit)
 4598            return false;
 599
 13600        RoundedCylinderQuadraticBound radialEntryBound = CreateRoundedCylinderQuadraticBound(
 13601            radial2,
 13602            radialProjection,
 13603            radial0,
 13604            startRadialSign <= 0,
 13605            endRadialContained,
 13606            entryBound: true);
 13607        RoundedCylinderQuadraticBound radialExitBound = CreateRoundedCylinderQuadraticBound(
 13608            radial2,
 13609            radialProjection,
 13610            radial0,
 13611            startRadialSign <= 0,
 13612            endRadialContained,
 13613            entryBound: false);
 13614        RoundedCylinderQuadraticBound axialEntryBound = CreateRoundedCylinderQuadraticBound(
 13615            axial2,
 13616            axial1,
 13617            axial0,
 13618            startAxialSign <= 0,
 13619            endAxialContained,
 13620            entryBound: true);
 13621        RoundedCylinderQuadraticBound axialExitBound = CreateRoundedCylinderQuadraticBound(
 13622            axial2,
 13623            axial1,
 13624            axial0,
 13625            startAxialSign <= 0,
 13626            endAxialContained,
 13627            entryBound: false);
 13628        return IsRoundedCylinderEntryBeforeOrEqualToExit(radialEntryBound, axialExitBound)
 13629            && IsRoundedCylinderEntryBeforeOrEqualToExit(axialEntryBound, radialExitBound);
 630    }
 631
 632    private static bool RoundedCylinderRimBoundsIntersect(
 633        RoundedCylinderBaseData data,
 634        Signed192 axisLength,
 635        Fixed64 segmentLength,
 636        bool positiveCap) =>
 90637        TryGetRoundedCylinderCapCoreDistanceInterval(
 90638            data,
 90639            axisLength,
 90640            WideArithmetic.AddSigned192(data.Radius, data.Expansion),
 90641            segmentLength,
 90642            positiveCap,
 90643            out _,
 90644            out _,
 90645            out _,
 90646            out _);
 647
 648    private static RoundedCylinderQuadraticBound CreateRoundedCylinderQuadraticBound(
 649        Signed320 coefficient,
 650        Signed320 projection,
 651        Signed320 constant,
 652        bool startContained,
 653        bool endContained,
 654        bool entryBound)
 655    {
 52656        if (entryBound && startContained)
 19657            return RoundedCylinderQuadraticBound.Zero;
 33658        if (!entryBound && endContained)
 17659            return RoundedCylinderQuadraticBound.One;
 660
 16661        Signed576 discriminant = WideArithmetic.SubtractSigned576(
 16662            WideArithmetic.MultiplySigned320(projection, projection),
 16663            WideArithmetic.MultiplySigned320(coefficient, constant));
 16664        return new RoundedCylinderQuadraticBound(
 16665            WideArithmetic.SubtractSigned320(default, projection),
 16666            coefficient,
 16667            discriminant,
 16668            entryBound ? -1 : 1);
 669    }
 670
 671    private static void MergeRoundedCylinderRimInterval(
 672        RoundedCylinderTorusPolynomial polynomial,
 673        Fixed64 segmentLength,
 674        ref bool found,
 675        ref Fixed64 entry,
 676        ref Fixed64 exit,
 677        bool calculateExit,
 678        out bool startContained,
 679        out bool endContainedStrict)
 680    {
 48681        startContained = IsPointInRoundedCylinderRimTube(polynomial, atEnd: false, strict: false);
 48682        endContainedStrict = IsPointInRoundedCylinderRimTube(polynomial, atEnd: true, strict: true);
 48683        bool rimFound = TryGetRoundedCylinderRimDistanceInterval(
 48684            polynomial,
 48685            segmentLength,
 48686            startContained,
 48687            IsPointInRoundedCylinderRimTube(polynomial, atEnd: true, strict: false),
 48688            calculateExit,
 48689            out Fixed64 rimEntry,
 48690            out Fixed64 rimExit);
 48691        Merge(rimFound, rimEntry, rimExit, ref found, ref entry, ref exit);
 48692    }
 693
 694    private static bool IsPointInRoundedCylinderRimTube(
 695        RoundedCylinderTorusPolynomial polynomial,
 696        bool atEnd,
 697        bool strict)
 698    {
 144699        Signed320 n = atEnd
 144700            ? WideArithmetic.AddSigned320(
 144701                WideArithmetic.AddSigned320(polynomial.N0, polynomial.N1),
 144702                polynomial.N2)
 144703            : polynomial.N0;
 144704        if (!strict && n.Sign <= 0)
 5705            return true;
 139706        if (strict && n.Sign < 0)
 4707            return true;
 708
 135709        Signed576 h = atEnd
 135710            ? AddFive(
 135711                polynomial.H0,
 135712                polynomial.H1,
 135713                polynomial.H2,
 135714                polynomial.H3,
 135715                polynomial.H4)
 135716            : polynomial.H0;
 135717        return strict ? h.Sign < 0 : h.Sign <= 0;
 718    }
 719
 720    private static bool TryGetFiniteCylinderDirectionFirstDistance(
 721        Vector3d position,
 722        Vector3d direction,
 723        Vector3d center,
 724        Vector3d axisDirection,
 725        Signed192 axisLength,
 726        Fixed64 radius,
 727        Fixed64 radiusExpansion,
 728        Fixed64 axialExpansion,
 729        Fixed64 totalDistance,
 730        out Fixed64 distance,
 731        out bool startContained)
 732    {
 3733        Signed192 queryLengthSquared = GetDirectionDot(direction, direction);
 3734        Signed192 axisLengthSquared = GetDot(
 3735            axisDirection, Vector3d.Zero, axisDirection, Vector3d.Zero);
 3736        Signed192 startDistanceSquared = GetDot(position, center, position, center);
 3737        Signed192 directionsDot = GetDirectionDot(direction, axisDirection);
 3738        Signed192 startAxisProjection = GetDot(
 3739            position, center, axisDirection, Vector3d.Zero);
 3740        Signed192 startDirectionProjection = GetDirectionDot(direction, position, center);
 3741        Signed192 expandedRadius = GetExpandedRadius(radius, radiusExpansion);
 3742        Signed192 squaredRadius = GetSquaredRadius(expandedRadius);
 3743        Signed320 axialExtent = GetCenteredAxialExtent(
 3744            axisLengthSquared, axisLength, axialExpansion);
 3745        startContained = IsCenteredFiniteCylinderPointContained(
 3746            startDistanceSquared, startAxisProjection, axisLengthSquared,
 3747            squaredRadius, axialExtent, strict: false);
 748
 3749        if (!TryGetCenteredAxialInterval(
 3750                startAxisProjection, directionsDot, axialExtent, Fixed64.One,
 3751                out RationalBound320 lower, out RationalBound320 upper))
 752        {
 1753            distance = default;
 1754            return false;
 755        }
 756
 2757        return TryGetFiniteAxisDistanceInterval(
 2758            queryLengthSquared, axisLengthSquared, startDistanceSquared,
 2759            directionsDot, startAxisProjection, startDirectionProjection,
 2760            expandedRadius, lower, upper, totalDistance, out distance, out _);
 761    }
 762
 763    private static RoundedCylinderBaseData CreateRoundedCylinderBaseData(
 764        FixedSegment query,
 765        Vector3d center,
 766        Vector3d axisDirection,
 767        Fixed64 radius,
 768        Fixed64 sphericalExpansion)
 769    {
 43770        Signed192 axisLengthSquared = GetDot(
 43771            axisDirection,
 43772            Vector3d.Zero,
 43773            axisDirection,
 43774            Vector3d.Zero);
 43775        Signed192 startDistanceSquared = GetDot(query.Start, center, query.Start, center);
 43776        Signed192 directionLengthSquared = GetDot(query.End, query.Start, query.End, query.Start);
 43777        Signed192 startDirectionProjection = GetDot(query.Start, center, query.End, query.Start);
 43778        Signed192 startAxisProjection = GetDot(query.Start, center, axisDirection, Vector3d.Zero);
 43779        Signed192 directionAxisProjection = GetDot(query.End, query.Start, axisDirection, Vector3d.Zero);
 780
 43781        return CreateRoundedCylinderBaseData(
 43782            axisLengthSquared,
 43783            startDistanceSquared,
 43784            directionLengthSquared,
 43785            startDirectionProjection,
 43786            startAxisProjection,
 43787            directionAxisProjection,
 43788            radius,
 43789            sphericalExpansion);
 790    }
 791
 792    private static RoundedCylinderBaseData CreateRoundedCylinderBaseData(
 793        Vector3d position,
 794        Vector3d direction,
 795        Vector3d center,
 796        Vector3d axisDirection,
 797        Fixed64 radius,
 798        Fixed64 sphericalExpansion)
 799    {
 2800        Signed192 axisLengthSquared = GetDot(
 2801            axisDirection, Vector3d.Zero, axisDirection, Vector3d.Zero);
 2802        Signed192 startDistanceSquared = GetDot(position, center, position, center);
 2803        Signed192 directionLengthSquared = GetDirectionDot(direction, direction);
 2804        Signed192 startDirectionProjection = GetDirectionDot(direction, position, center);
 2805        Signed192 startAxisProjection = GetDot(position, center, axisDirection, Vector3d.Zero);
 2806        Signed192 directionAxisProjection = GetDirectionDot(direction, axisDirection);
 2807        return CreateRoundedCylinderBaseData(
 2808            axisLengthSquared,
 2809            startDistanceSquared,
 2810            directionLengthSquared,
 2811            startDirectionProjection,
 2812            startAxisProjection,
 2813            directionAxisProjection,
 2814            radius,
 2815            sphericalExpansion);
 816    }
 817
 818    private static RoundedCylinderBaseData CreateRoundedCylinderBaseData(
 819        Signed192 axisLengthSquared,
 820        Signed192 startDistanceSquared,
 821        Signed192 directionLengthSquared,
 822        Signed192 startDirectionProjection,
 823        Signed192 startAxisProjection,
 824        Signed192 directionAxisProjection,
 825        Fixed64 radius,
 826        Fixed64 sphericalExpansion)
 827    {
 45828        Signed320 q0 = WideArithmetic.MultiplySubtract(
 45829            startDistanceSquared,
 45830            axisLengthSquared,
 45831            startAxisProjection,
 45832            startAxisProjection);
 45833        Signed320 q1 = Twice(WideArithmetic.MultiplySubtract(
 45834            startDirectionProjection,
 45835            axisLengthSquared,
 45836            startAxisProjection,
 45837            directionAxisProjection));
 45838        Signed320 q2 = WideArithmetic.MultiplySubtract(
 45839            directionLengthSquared,
 45840            axisLengthSquared,
 45841            directionAxisProjection,
 45842            directionAxisProjection);
 843
 45844        return new RoundedCylinderBaseData(
 45845            axisLengthSquared,
 45846            startDistanceSquared,
 45847            startDirectionProjection,
 45848            directionLengthSquared,
 45849            startAxisProjection,
 45850            directionAxisProjection,
 45851            q0,
 45852            q1,
 45853            q2,
 45854            Signed192.Signed(radius.m_rawValue),
 45855            Signed192.Signed(sphericalExpansion.m_rawValue));
 856    }
 857
 858    private static RoundedCylinderTorusPolynomial CreateRoundedCylinderTorusPolynomial(
 859        RoundedCylinderBaseData data,
 860        Signed192 axisLength,
 861        bool positiveCap)
 862    {
 48863        Signed320 scaleSquared = WideArithmetic.MultiplySigned192(
 48864            DoubleParameterScale,
 48865            DoubleParameterScale);
 48866        Signed320 heightSquared = WideArithmetic.MultiplySigned192(axisLength, axisLength);
 48867        Signed320 radiusSquared = WideArithmetic.MultiplySigned192(data.Radius, data.Radius);
 48868        Signed320 expansionSquared = WideArithmetic.MultiplySigned192(data.Expansion, data.Expansion);
 48869        Signed320 radiusDifference = WideArithmetic.SubtractSigned320(radiusSquared, expansionSquared);
 48870        Signed320 constant = WideArithmetic.AddSigned320(
 48871            MultiplyToSigned320(heightSquared, data.AxisLengthSquared),
 48872            MultiplyToSigned320(radiusDifference, scaleSquared));
 873
 48874        Signed320 axial0 = MultiplyThreeToSigned320(
 48875            DoubleParameterScale,
 48876            axisLength,
 48877            data.StartAxisProjection);
 48878        Signed320 axial1 = MultiplyThreeToSigned320(
 48879            DoubleParameterScale,
 48880            axisLength,
 48881            data.DirectionAxisProjection);
 48882        if (positiveCap)
 883        {
 36884            axial0 = WideArithmetic.SubtractSigned320(default, axial0);
 36885            axial1 = WideArithmetic.SubtractSigned320(default, axial1);
 886        }
 887
 48888        Signed320 n0 = WideArithmetic.AddSigned320(
 48889            WideArithmetic.AddSigned320(
 48890                MultiplyToSigned320(scaleSquared, data.G0),
 48891                Twice(axial0)),
 48892            constant);
 48893        Signed320 n1 = WideArithmetic.AddSigned320(
 48894            Twice(MultiplyToSigned320(scaleSquared, data.G1)),
 48895            Twice(axial1));
 48896        Signed320 n2 = MultiplyToSigned320(scaleSquared, data.G2);
 897
 48898        Signed320 rimScale = Four(MultiplyToSigned320(
 48899            MultiplyToSigned320(radiusSquared, scaleSquared),
 48900            scaleSquared));
 48901        Signed576 h0 = WideArithmetic.SubtractSigned576(
 48902            WideArithmetic.MultiplySigned576(
 48903                WideArithmetic.MultiplySigned320(n0, n0),
 48904                data.AxisLengthSquared),
 48905            WideArithmetic.MultiplySigned320(rimScale, data.Q0));
 48906        Signed576 h1 = WideArithmetic.SubtractSigned576(
 48907            Twice(WideArithmetic.MultiplySigned576(
 48908                WideArithmetic.MultiplySigned320(n0, n1),
 48909                data.AxisLengthSquared)),
 48910            WideArithmetic.MultiplySigned320(rimScale, data.Q1));
 48911        Signed576 h2 = WideArithmetic.SubtractSigned576(
 48912            WideArithmetic.MultiplySigned576(
 48913                WideArithmetic.AddSigned576(
 48914                    WideArithmetic.MultiplySigned320(n1, n1),
 48915                    Twice(WideArithmetic.MultiplySigned320(n0, n2))),
 48916                data.AxisLengthSquared),
 48917            WideArithmetic.MultiplySigned320(rimScale, data.Q2));
 48918        Signed576 h3 = Twice(WideArithmetic.MultiplySigned576(
 48919            WideArithmetic.MultiplySigned320(n1, n2),
 48920            data.AxisLengthSquared));
 48921        Signed576 h4 = WideArithmetic.MultiplySigned576(
 48922            WideArithmetic.MultiplySigned320(n2, n2),
 48923            data.AxisLengthSquared);
 48924        return new RoundedCylinderTorusPolynomial(n0, n1, n2, h0, h1, h2, h3, h4);
 925    }
 926
 927    private static Signed320 MultiplyToSigned320(Signed320 left, Signed192 right) =>
 552928        Signed320.NarrowValue(WideArithmetic.MultiplySigned576(Signed576.ExtendValue(left), right));
 929
 930    private static Signed320 MultiplyToSigned320(Signed320 left, Signed320 right) =>
 864931        Signed320.NarrowValue(WideArithmetic.MultiplySigned320(left, right));
 932
 285933    private static Signed320 Twice(Signed320 value) => WideArithmetic.AddSigned320(value, value);
 934
 48935    private static Signed320 Four(Signed320 value) => Twice(Twice(value));
 936
 144937    private static Signed576 Twice(Signed576 value) => WideArithmetic.AddSigned576(value, value);
 938
 939    private static Signed576 AddFive(
 940        Signed576 first,
 941        Signed576 second,
 942        Signed576 third,
 943        Signed576 fourth,
 944        Signed576 fifth) =>
 88945        WideArithmetic.AddSigned576(
 88946            WideArithmetic.AddSigned576(
 88947                WideArithmetic.AddSigned576(first, second),
 88948                WideArithmetic.AddSigned576(third, fourth)),
 88949            fifth);
 950
 951}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.RoundedCylinderInteger.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.RoundedCylinderInteger.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>
 13/// Signed wide-integer arithmetic helpers (add, subtract, multiply, shift, compare)
 14/// used to evaluate rounded-cylinder intersection polynomial coefficients.
 15/// </content>
 16internal static partial class WideFiniteAxisIntersection
 17{
 18    private static void ImportRoundedCylinderCoefficient(
 19        Signed576 value,
 20        Span<ulong> coefficients,
 21        Span<sbyte> signs,
 22        int polynomialIndex,
 23        int coefficientIndex)
 24    {
 22525        Span<ulong> destination = GetRoundedCylinderCoefficient(
 22526            coefficients,
 22527            polynomialIndex,
 22528            coefficientIndex);
 22529        WideArithmetic.GetMagnitude(value, destination[..9]);
 22530        signs[GetRoundedCylinderCoefficientIndex(polynomialIndex, coefficientIndex)] = (sbyte)value.Sign;
 22531    }
 32
 33    private static void ImportRoundedCylinderCoefficient(
 34        Signed832 value,
 35        Span<ulong> coefficients,
 36        Span<sbyte> signs,
 37        int polynomialIndex,
 38        int coefficientIndex)
 39    {
 56540        Span<ulong> destination = GetRoundedCylinderCoefficient(
 56541            coefficients,
 56542            polynomialIndex,
 56543            coefficientIndex);
 56544        WideArithmetic.GetMagnitude(value, destination[..13]);
 56545        signs[GetRoundedCylinderCoefficientIndex(polynomialIndex, coefficientIndex)] =
 56546            (sbyte)value.Sign;
 56547    }
 48
 49    private static Span<ulong> GetRoundedCylinderCoefficient(
 50        Span<ulong> coefficients,
 51        int polynomialIndex,
 52        int coefficientIndex)
 53    {
 16045154        int index = GetRoundedCylinderCoefficientIndex(polynomialIndex, coefficientIndex);
 16045155        int wordCount = coefficients.Length / (RoundedCylinderPolynomialCount * RoundedCylinderCoefficientCount);
 16045156        return coefficients.Slice(
 16045157            index * wordCount,
 16045158            wordCount);
 59    }
 60
 61    private static int GetRoundedCylinderCoefficientIndex(int polynomialIndex, int coefficientIndex) =>
 33048262        polynomialIndex * RoundedCylinderCoefficientCount + coefficientIndex;
 63
 64    private static void AddRoundedCylinderSigned(
 65        ReadOnlySpan<ulong> left,
 66        sbyte leftSign,
 67        ReadOnlySpan<ulong> right,
 68        sbyte rightSign,
 69        Span<ulong> destination,
 70        out sbyte destinationSign)
 71    {
 43287272        if (leftSign == 0)
 73        {
 25841074            CopyRoundedCylinderWide(right, destination);
 25841075            destinationSign = rightSign;
 25841076            return;
 77        }
 17446278        if (rightSign == 0)
 79        {
 1842680            CopyRoundedCylinderWide(left, destination);
 1842681            destinationSign = leftSign;
 1842682            return;
 83        }
 15603684        if (leftSign != rightSign)
 85        {
 9971986            SubtractRoundedCylinderSigned(
 9971987                left,
 9971988                leftSign,
 9971989                right,
 9971990                (sbyte)-rightSign,
 9971991                destination,
 9971992                out destinationSign);
 9971993            return;
 94        }
 95
 5631796        WideArithmetic.AddEqualMagnitudes(left, right, destination);
 5631797        destinationSign = leftSign;
 5631798    }
 99
 100    private static void SubtractRoundedCylinderSigned(
 101        ReadOnlySpan<ulong> left,
 102        sbyte leftSign,
 103        ReadOnlySpan<ulong> right,
 104        sbyte rightSign,
 105        Span<ulong> destination,
 106        out sbyte destinationSign)
 107    {
 166371108        if (leftSign == 0 || rightSign == 0)
 109        {
 13107110            bool useRight = leftSign == 0;
 13107111            CopyRoundedCylinderWide(useRight ? right : left, destination);
 13107112            destinationSign = useRight ? (sbyte)-rightSign : leftSign;
 13107113            return;
 114        }
 153264115        if (leftSign != rightSign)
 116        {
 7332117            AddRoundedCylinderSigned(
 7332118                left,
 7332119                leftSign,
 7332120                right,
 7332121                (sbyte)-rightSign,
 7332122                destination,
 7332123                out destinationSign);
 7332124            return;
 125        }
 126
 145932127        int comparison = WideArithmetic.CompareMagnitudeEqualLength(left, right);
 145932128        if (comparison == 0)
 129        {
 12571130            destination.Clear();
 12571131            destinationSign = 0;
 12571132            return;
 133        }
 134
 133361135        ReadOnlySpan<ulong> larger = comparison > 0 ? left : right;
 133361136        ReadOnlySpan<ulong> smaller = comparison > 0 ? right : left;
 133361137        WideArithmetic.SubtractEqualMagnitudes(larger, smaller, destination);
 133361138        destinationSign = comparison > 0 ? leftSign : (sbyte)-leftSign;
 133361139    }
 140
 141    private static void MultiplyRoundedCylinderWide(
 142        ReadOnlySpan<ulong> left,
 143        ReadOnlySpan<ulong> right,
 144        Span<ulong> destination)
 145    {
 775270146        destination.Clear();
 775270147        int leftLength = GetRoundedCylinderWideLength(left);
 775270148        int rightLength = GetRoundedCylinderWideLength(right);
 11543874149        for (int leftIndex = 0; leftIndex < leftLength; leftIndex++)
 150        {
 395704724151            for (int rightIndex = 0; rightIndex < rightLength; rightIndex++)
 152            {
 192855695153                Fixed64.Multiply64To128(
 192855695154                    left[leftIndex],
 192855695155                    right[rightIndex],
 192855695156                    out ulong high,
 192855695157                    out ulong low);
 192855695158                AddRoundedCylinderWord(destination, leftIndex + rightIndex, low);
 192855695159                AddRoundedCylinderWord(destination, leftIndex + rightIndex + 1, high);
 160            }
 161        }
 775270162    }
 163
 164    private static void MultiplyRoundedCylinderWideByWord(
 165        ReadOnlySpan<ulong> value,
 166        ulong multiplier,
 167        Span<ulong> destination)
 168    {
 3157422169        destination.Clear();
 3157422170        if (multiplier == 0UL)
 555171            return;
 3156867172        int length = GetRoundedCylinderWideLength(value);
 182925930173        for (int index = 0; index < length; index++)
 174        {
 88306098175            Fixed64.Multiply64To128(value[index], multiplier, out ulong high, out ulong low);
 88306098176            AddRoundedCylinderWord(destination, index, low);
 88306098177            AddRoundedCylinderWord(destination, index + 1, high);
 178        }
 3156867179    }
 180
 181    private static void AddRoundedCylinderWord(Span<ulong> value, int index, ulong word)
 182    {
 702665582183        while (word != 0UL)
 184        {
 138049546185            ulong sum = unchecked(value[index] + word);
 138049546186            word = sum < value[index] ? 1UL : 0UL;
 138049546187            value[index] = sum;
 138049546188            index++;
 189        }
 564616036190    }
 191
 192    private static sbyte MultiplyRoundedCylinderSigns(sbyte left, sbyte right) =>
 118682193        left == 0 || right == 0 ? (sbyte)0 : (sbyte)(left * right);
 194
 195    private static int GetRoundedCylinderWideLength(ReadOnlySpan<ulong> value)
 196    {
 40124787197        int length = value.Length;
 1596782221198        while (length > 0 && value[length - 1] == 0UL)
 1556657434199            length--;
 40124787200        return length;
 201    }
 202
 203    private static void CopyRoundedCylinderWide(
 204        ReadOnlySpan<ulong> source,
 205        Span<ulong> destination) =>
 336663206        source.CopyTo(destination);
 207
 208    private static int CountRoundedCylinderTrailingZeroes(ReadOnlySpan<ulong> value)
 209    {
 855345210        int wordIndex = 0;
 4383768211        while (value[wordIndex] == 0UL)
 3528423212            wordIndex++;
 855345213        ulong word = value[wordIndex];
 855345214        int bits = 0;
 21157733215        while ((word & 1UL) == 0UL)
 216        {
 20302388217            word >>= 1;
 20302388218            bits++;
 219        }
 855345220        return wordIndex * 64 + bits;
 221    }
 222
 223    private static void ShiftRoundedCylinderWideRight(Span<ulong> value, int bits)
 224    {
 17347041225        int wordShift = bits >> 6;
 17347041226        int bitShift = bits & 63;
 925242896227        for (int destinationIndex = 0; destinationIndex < value.Length; destinationIndex++)
 228        {
 445274407229            int sourceIndex = destinationIndex + wordShift;
 445274407230            ulong lower = sourceIndex < value.Length ? value[sourceIndex] : 0UL;
 445274407231            ulong upper = sourceIndex + 1 < value.Length ? value[sourceIndex + 1] : 0UL;
 445274407232            uint nonzero = ((uint)bitShift | (0U - (uint)bitShift)) >> 31;
 445274407233            ulong upperMask = 0UL - nonzero;
 445274407234            value[destinationIndex] = (lower >> bitShift)
 445274407235                | ((upper << ((64 - bitShift) & 63)) & upperMask);
 236        }
 17347041237    }
 238}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.RoundedCylinderRootSolver.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.RoundedCylinderRootSolver.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>
 13/// Implements a wide-precision root solver for rounded-cylinder (capsule-like) torus
 14/// intersection tests, using a Sturm sequence built over arbitrary-precision integer
 15/// coefficients to isolate and count roots within a bounded segment length.
 16/// </content>
 17internal static partial class WideFiniteAxisIntersection
 18{
 19    private const int RoundedCylinderPolynomialCount = 5;
 20    private const int RoundedCylinderCoefficientCount = 5;
 21    // The largest factorized quartic subresultant transient is below 2,772 bits.
 22    private const int RoundedCylinderWideLimbCount = 44;
 23
 24    /// <summary>
 25    /// Counts real roots for quartics whose coefficient growth is bounded by
 26    /// the finite-axis and planar-projection derivations in this assembly.
 27    /// </summary>
 28    internal static int CountProjectionDomainQuarticRoots(
 29        Signed832 constant,
 30        Signed832 linear,
 31        Signed832 quadratic,
 32        Signed832 cubic,
 33        Signed832 quartic)
 34    {
 11435        int leadingSign = quartic.Sign;
 11436        if (leadingSign == 0)
 137            throw new ArgumentOutOfRangeException(nameof(quartic));
 38
 11339        if (leadingSign < 0)
 40        {
 441            constant = WideArithmetic.SubtractSigned832(default, constant);
 442            linear = WideArithmetic.SubtractSigned832(default, linear);
 443            quadratic = WideArithmetic.SubtractSigned832(default, quadratic);
 444            cubic = WideArithmetic.SubtractSigned832(default, cubic);
 445            quartic = WideArithmetic.SubtractSigned832(default, quartic);
 46        }
 47
 11348        Span<ulong> coefficients = stackalloc ulong[
 11349            RoundedCylinderPolynomialCount
 11350            * RoundedCylinderCoefficientCount
 11351            * RoundedCylinderWideLimbCount];
 11352        Span<sbyte> signs = stackalloc sbyte[
 11353            RoundedCylinderPolynomialCount * RoundedCylinderCoefficientCount];
 11354        Span<int> degrees = stackalloc int[RoundedCylinderPolynomialCount];
 11355        coefficients.Clear();
 11356        signs.Clear();
 11357        degrees.Clear();
 11358        ImportRoundedCylinderCoefficient(constant, coefficients, signs, 0, 0);
 11359        ImportRoundedCylinderCoefficient(linear, coefficients, signs, 0, 1);
 11360        ImportRoundedCylinderCoefficient(quadratic, coefficients, signs, 0, 2);
 11361        ImportRoundedCylinderCoefficient(cubic, coefficients, signs, 0, 3);
 11362        ImportRoundedCylinderCoefficient(quartic, coefficients, signs, 0, 4);
 11363        degrees[0] = 4;
 113064        for (int coefficientIndex = 1; coefficientIndex <= 4; coefficientIndex++)
 65        {
 45266            int destinationIndex = coefficientIndex - 1;
 45267            MultiplyRoundedCylinderWideByWord(
 45268                GetRoundedCylinderCoefficient(
 45269                    coefficients,
 45270                    0,
 45271                    coefficientIndex),
 45272                (ulong)coefficientIndex,
 45273                GetRoundedCylinderCoefficient(
 45274                    coefficients,
 45275                    1,
 45276                    destinationIndex));
 45277            signs[GetRoundedCylinderCoefficientIndex(1, destinationIndex)] =
 45278                signs[GetRoundedCylinderCoefficientIndex(
 45279                    0,
 45280                    coefficientIndex)];
 81        }
 11382        degrees[1] = 3;
 83
 11384        int sequenceCount = BuildRoundedCylinderQuarticSturmSequence(
 11385            coefficients,
 11386            signs,
 11387            degrees);
 11388        return GetRoundedCylinderInfinityVariations(
 11389                   signs,
 11390                   degrees,
 11391                   sequenceCount,
 11392                   negative: true)
 11393               - GetRoundedCylinderInfinityVariations(
 11394                   signs,
 11395                   degrees,
 11396                   sequenceCount,
 11397                   negative: false);
 98    }
 99
 100    private static int GetRoundedCylinderInfinityVariations(
 101        ReadOnlySpan<sbyte> signs,
 102        ReadOnlySpan<int> degrees,
 103        int sequenceCount,
 104        bool negative)
 105    {
 970106        int variations = 0;
 970107        int previousSign = 0;
 970108        for (int polynomialIndex = 0;
 5201109             polynomialIndex < sequenceCount;
 4231110             polynomialIndex++)
 111        {
 4231112            int degree = degrees[polynomialIndex];
 4231113            int sign = signs[
 4231114                GetRoundedCylinderCoefficientIndex(
 4231115                    polynomialIndex,
 4231116                    degree)];
 4231117            if (negative && (degree & 1) != 0)
 221118                sign = -sign;
 4231119            if (previousSign != 0 && sign != previousSign)
 854120                variations++;
 4231121            previousSign = sign;
 122        }
 123
 970124        return variations;
 125    }
 126
 127    private static bool TryGetRoundedCylinderRimDistanceInterval(
 128        RoundedCylinderTorusPolynomial polynomial,
 129        Fixed64 segmentLength,
 130        bool startContained,
 131        bool endContained,
 132        bool calculateExit,
 133        out Fixed64 entry,
 134        out Fixed64 exit)
 135    {
 48136        entry = default;
 48137        exit = default;
 48138        if (segmentLength == Fixed64.Zero)
 3139            return startContained;
 140
 45141        Span<ulong> coefficients = stackalloc ulong[
 45142            RoundedCylinderPolynomialCount
 45143            * RoundedCylinderCoefficientCount
 45144            * RoundedCylinderWideLimbCount];
 45145        Span<sbyte> signs = stackalloc sbyte[
 45146            RoundedCylinderPolynomialCount * RoundedCylinderCoefficientCount];
 45147        Span<int> degrees = stackalloc int[RoundedCylinderPolynomialCount];
 45148        coefficients.Clear();
 45149        signs.Clear();
 45150        degrees.Clear();
 151
 45152        ulong maximum = unchecked((ulong)segmentLength.m_rawValue);
 45153        int sequenceCount = BuildRoundedCylinderSturmSequence(
 45154            polynomial,
 45155            coefficients,
 45156            signs,
 45157            degrees);
 45158        if (sequenceCount == 0)
 159        {
 1160            exit = segmentLength;
 1161            return true;
 162        }
 44163        HomogenizeRoundedCylinderSturmSequence(
 44164            coefficients,
 44165            degrees,
 44166            sequenceCount,
 44167            maximum);
 168
 44169        int lowerVariations = GetRoundedCylinderSignVariations(
 44170            coefficients,
 44171            signs,
 44172            degrees,
 44173            sequenceCount,
 44174            0UL,
 44175            1UL);
 44176        int upperVariations = GetRoundedCylinderSignVariations(
 44177            coefficients,
 44178            signs,
 44179            degrees,
 44180            sequenceCount,
 44181            maximum,
 44182            1UL);
 44183        int rootCount = lowerVariations - upperVariations;
 44184        bool rootAtStart = polynomial.H0.IsZero;
 44185        if (rootCount == 0)
 186        {
 7187            if (!startContained && !rootAtStart)
 4188                return false;
 189
 3190            entry = Fixed64.Zero;
 3191            exit = endContained ? segmentLength : Fixed64.Zero;
 3192            return true;
 193        }
 194
 37195        entry = startContained || rootAtStart
 37196            ? Fixed64.Zero
 37197            : FindRoundedCylinderRoot(
 37198                coefficients,
 37199                signs,
 37200                degrees,
 37201                sequenceCount,
 37202                maximum,
 37203                lowerVariations,
 37204                upperVariations,
 37205                rootCount,
 37206                findFirst: true);
 37207        if (!calculateExit)
 208        {
 8209            exit = entry;
 8210            return true;
 211        }
 29212        exit = endContained
 29213            ? segmentLength
 29214            : FindRoundedCylinderRoot(
 29215                coefficients,
 29216                signs,
 29217                degrees,
 29218                sequenceCount,
 29219                maximum,
 29220                lowerVariations,
 29221                upperVariations,
 29222                rootCount,
 29223                findFirst: false);
 29224        return true;
 225    }
 226
 227    private static Fixed64 FindRoundedCylinderRoot(
 228        Span<ulong> coefficients,
 229        Span<sbyte> signs,
 230        Span<int> degrees,
 231        int sequenceCount,
 232        ulong maximum,
 233        int lowerVariations,
 234        int upperVariations,
 235        int totalRootCount,
 236        bool findFirst)
 237    {
 52238        ulong lower = 0UL;
 52239        ulong upper = maximum;
 52240        int intervalRootCount = totalRootCount;
 742241        while (upper - lower > 1UL)
 242        {
 721243            if (intervalRootCount == 1)
 244            {
 593245                int lowerSign = EvaluateRoundedCylinderPolynomialSign(
 593246                    coefficients,
 593247                    signs,
 593248                    degrees[0],
 593249                    0,
 593250                    lower,
 593251                    1UL);
 252                // The global start root is handled before isolation; midpoint
 253                // roots remain on the upper boundary of the retained interval.
 593254                int upperSign = EvaluateRoundedCylinderPolynomialSign(
 593255                    coefficients,
 593256                    signs,
 593257                    degrees[0],
 593258                    0,
 593259                    upper,
 593260                    1UL);
 593261                if (upperSign == 0)
 4262                    return Fixed64.FromRaw((long)upper);
 263
 589264                if (lowerSign != upperSign)
 265                {
 27266                    return FindRoundedCylinderSignChangingRoot(
 27267                        coefficients,
 27268                        signs,
 27269                        degrees[0],
 27270                        maximum,
 27271                        lower,
 27272                        upper,
 27273                        lowerSign);
 274                }
 275            }
 276
 690277            ulong midpoint = lower + ((upper - lower) >> 1);
 690278            int midpointVariations = GetRoundedCylinderSignVariations(
 690279                coefficients,
 690280                signs,
 690281                degrees,
 690282                sequenceCount,
 690283                midpoint,
 690284                1UL);
 690285            int leftRootCount = lowerVariations - midpointVariations;
 690286            if (findFirst ? leftRootCount > 0 : leftRootCount == intervalRootCount)
 287            {
 341288                upper = midpoint;
 341289                upperVariations = midpointVariations;
 341290                intervalRootCount = leftRootCount;
 291            }
 292            else
 293            {
 349294                lower = midpoint;
 349295                lowerVariations = midpointVariations;
 349296                intervalRootCount -= leftRootCount;
 297            }
 298        }
 299
 21300        ulong midpointNumerator = (lower << 1) | 1UL;
 21301        int halfVariations = GetRoundedCylinderSignVariations(
 21302            coefficients,
 21303            signs,
 21304            degrees,
 21305            sequenceCount,
 21306            midpointNumerator,
 21307            2UL);
 21308        int leftHalfRootCount = lowerVariations - halfVariations;
 21309        int midpointSign = EvaluateRoundedCylinderPolynomialSign(
 21310            coefficients,
 21311            signs,
 21312            degrees[0],
 21313            0,
 21314            midpointNumerator,
 21315            2UL);
 316
 317        ulong rounded;
 21318        if (findFirst)
 319        {
 14320            rounded = leftHalfRootCount > 0 ? lower : upper;
 14321            if ((midpointSign == 0) & (leftHalfRootCount == 1))
 1322                rounded = lower + (lower & 1UL);
 323        }
 324        else
 325        {
 7326            int rightHalfRootCount = intervalRootCount - leftHalfRootCount;
 7327            rounded = rightHalfRootCount > 0 ? upper : lower;
 7328            if ((midpointSign == 0) & (rightHalfRootCount == 0))
 1329                rounded = lower + (lower & 1UL);
 330        }
 331
 21332        return Fixed64.FromRaw((long)rounded);
 333    }
 334
 335    private static Fixed64 FindRoundedCylinderSignChangingRoot(
 336        Span<ulong> coefficients,
 337        Span<sbyte> signs,
 338        int degree,
 339        ulong maximum,
 340        ulong lower,
 341        ulong upper,
 342        int lowerSign)
 343    {
 869344        while (upper - lower > 1UL)
 345        {
 847346            ulong midpoint = lower + ((upper - lower) >> 1);
 847347            int midpointSign = EvaluateRoundedCylinderPolynomialSign(
 847348                coefficients,
 847349                signs,
 847350                degree,
 847351                0,
 847352                midpoint,
 847353                1UL);
 847354            if (midpointSign == 0)
 5355                return Fixed64.FromRaw((long)midpoint);
 842356            if (midpointSign == lowerSign)
 445357                lower = midpoint;
 358            else
 397359                upper = midpoint;
 360        }
 361
 22362        ulong midpointNumerator = (lower << 1) | 1UL;
 22363        int halfSign = EvaluateRoundedCylinderPolynomialSign(
 22364            coefficients,
 22365            signs,
 22366            degree,
 22367            0,
 22368            midpointNumerator,
 22369            2UL);
 22370        if (halfSign == 0)
 2371            return Fixed64.FromRaw((long)(lower + (lower & 1UL)));
 20372        return Fixed64.FromRaw((long)(halfSign == lowerSign ? upper : lower));
 373    }
 374
 375    private static void HomogenizeRoundedCylinderSturmSequence(
 376        Span<ulong> coefficients,
 377        Span<int> degrees,
 378        int sequenceCount,
 379        ulong segmentLengthRaw)
 380    {
 44381        Span<ulong> product = stackalloc ulong[RoundedCylinderWideLimbCount];
 420382        for (int polynomialIndex = 0; polynomialIndex < sequenceCount; polynomialIndex++)
 383        {
 166384            int degree = degrees[polynomialIndex];
 1098385            for (int coefficientIndex = 0; coefficientIndex < degree; coefficientIndex++)
 386            {
 383387                Span<ulong> coefficient = GetRoundedCylinderCoefficient(
 383388                    coefficients,
 383389                    polynomialIndex,
 383390                    coefficientIndex);
 383391                int power = degree - coefficientIndex;
 2350392                for (int factor = 0; factor < power; factor++)
 393                {
 792394                    MultiplyRoundedCylinderWideByWord(coefficient, segmentLengthRaw, product);
 792395                    CopyRoundedCylinderWide(product, coefficient);
 396                }
 397            }
 398        }
 44399    }
 400
 401    private static bool IsRoundedCylinderEntryBeforeOrEqualToExit(
 402        RoundedCylinderQuadraticBound entry,
 403        RoundedCylinderQuadraticBound exit)
 404    {
 25405        Signed576 cross = WideArithmetic.SubtractSigned576(
 25406            WideArithmetic.MultiplySigned320(entry.Numerator, exit.Denominator),
 25407            WideArithmetic.MultiplySigned320(exit.Numerator, entry.Denominator));
 25408        bool hasEntryRadical = entry.RadicalSign < 0;
 25409        bool hasExitRadical = exit.RadicalSign > 0;
 25410        if (!hasEntryRadical && !hasExitRadical)
 12411            return cross.Sign <= 0;
 13412        if (cross.Sign <= 0)
 5413            return true;
 414
 8415        Span<ulong> crossMagnitude = stackalloc ulong[RoundedCylinderWideLimbCount];
 8416        Span<ulong> entryTerm = stackalloc ulong[RoundedCylinderWideLimbCount];
 8417        Span<ulong> exitTerm = stackalloc ulong[RoundedCylinderWideLimbCount];
 8418        Span<ulong> crossSquared = stackalloc ulong[RoundedCylinderWideLimbCount];
 8419        Span<ulong> termSum = stackalloc ulong[RoundedCylinderWideLimbCount];
 8420        Span<ulong> difference = stackalloc ulong[RoundedCylinderWideLimbCount];
 8421        Span<ulong> differenceSquared = stackalloc ulong[RoundedCylinderWideLimbCount];
 8422        Span<ulong> termProduct = stackalloc ulong[RoundedCylinderWideLimbCount];
 8423        Span<ulong> right = stackalloc ulong[RoundedCylinderWideLimbCount];
 8424        Span<ulong> first = stackalloc ulong[RoundedCylinderWideLimbCount];
 8425        Span<ulong> second = stackalloc ulong[RoundedCylinderWideLimbCount];
 8426        crossMagnitude.Clear();
 8427        entryTerm.Clear();
 8428        exitTerm.Clear();
 8429        ImportRoundedCylinderWide(cross, crossMagnitude);
 8430        if (hasEntryRadical)
 431        {
 5432            GetRoundedCylinderRadicalSquare(
 5433                exit.Denominator,
 5434                entry.Discriminant,
 5435                entryTerm,
 5436                first,
 5437                second);
 438        }
 8439        if (hasExitRadical)
 440        {
 6441            GetRoundedCylinderRadicalSquare(
 6442                entry.Denominator,
 6443                exit.Discriminant,
 6444                exitTerm,
 6445                first,
 6446                second);
 447        }
 448
 8449        MultiplyRoundedCylinderWide(crossMagnitude, crossMagnitude, crossSquared);
 8450        AddRoundedCylinderSigned(
 8451            entryTerm,
 8452            GetRoundedCylinderWideLength(entryTerm) == 0 ? (sbyte)0 : (sbyte)1,
 8453            exitTerm,
 8454            GetRoundedCylinderWideLength(exitTerm) == 0 ? (sbyte)0 : (sbyte)1,
 8455            termSum,
 8456            out _);
 8457        SubtractRoundedCylinderSigned(
 8458            crossSquared,
 8459            1,
 8460            termSum,
 8461            1,
 8462            difference,
 8463            out sbyte differenceSign);
 8464        if (differenceSign <= 0)
 7465            return true;
 466
 1467        MultiplyRoundedCylinderWide(difference, difference, differenceSquared);
 1468        MultiplyRoundedCylinderWide(entryTerm, exitTerm, termProduct);
 1469        MultiplyRoundedCylinderWideByWord(termProduct, 4UL, right);
 1470        return WideArithmetic.CompareMagnitudeEqualLength(differenceSquared, right) <= 0;
 471    }
 472
 473    private static void GetRoundedCylinderRadicalSquare(
 474        Signed320 coefficient,
 475        Signed576 discriminant,
 476        Span<ulong> destination,
 477        Span<ulong> first,
 478        Span<ulong> second)
 479    {
 11480        ImportRoundedCylinderWide(coefficient, first);
 11481        MultiplyRoundedCylinderWide(first, first, second);
 11482        ImportRoundedCylinderWide(discriminant, first);
 11483        MultiplyRoundedCylinderWide(second, first, destination);
 11484    }
 485
 486    private static void ImportRoundedCylinderWide(Signed320 value, Span<ulong> destination)
 487    {
 11488        destination.Clear();
 11489        WideArithmetic.GetMagnitude(
 11490            value,
 11491            out destination[4],
 11492            out destination[3],
 11493            out destination[2],
 11494            out destination[1],
 11495            out destination[0]);
 11496    }
 497
 498    private static void ImportRoundedCylinderWide(Signed576 value, Span<ulong> destination)
 499    {
 19500        destination.Clear();
 19501        WideArithmetic.GetMagnitude(value, destination[..9]);
 19502    }
 503
 504    private static int GetRoundedCylinderSignVariations(
 505        Span<ulong> coefficients,
 506        Span<sbyte> signs,
 507        Span<int> degrees,
 508        int sequenceCount,
 509        ulong numerator,
 510        ulong denominator)
 511    {
 799512        int variations = 0;
 799513        int previousSign = 0;
 6344514        for (int polynomialIndex = 0; polynomialIndex < sequenceCount; polynomialIndex++)
 515        {
 2373516            int sign = EvaluateRoundedCylinderPolynomialSign(
 2373517                coefficients,
 2373518                signs,
 2373519                degrees[polynomialIndex],
 2373520                polynomialIndex,
 2373521                numerator,
 2373522                denominator);
 2373523            if (sign == 0)
 524                continue;
 2289525            if (previousSign != 0 && sign != previousSign)
 930526                variations++;
 2289527            previousSign = sign;
 528        }
 529
 799530        return variations;
 531    }
 532
 533    private static int EvaluateRoundedCylinderPolynomialSign(
 534        Span<ulong> coefficients,
 535        Span<sbyte> signs,
 536        int degree,
 537        int polynomialIndex,
 538        ulong numerator,
 539        ulong denominator)
 540    {
 4449541        if (denominator == 1UL)
 542        {
 4341543            return EvaluateRoundedCylinderPolynomialSignAtInteger(
 4341544                coefficients,
 4341545                signs,
 4341546                degree,
 4341547                polynomialIndex,
 4341548                numerator);
 549        }
 550
 108551        Span<ulong> result = stackalloc ulong[RoundedCylinderWideLimbCount];
 108552        Span<ulong> scale = stackalloc ulong[RoundedCylinderWideLimbCount];
 108553        Span<ulong> first = stackalloc ulong[RoundedCylinderWideLimbCount];
 108554        Span<ulong> second = stackalloc ulong[RoundedCylinderWideLimbCount];
 108555        Span<ulong> sum = stackalloc ulong[RoundedCylinderWideLimbCount];
 108556        result.Clear();
 108557        scale.Clear();
 108558        CopyRoundedCylinderWide(
 108559            GetRoundedCylinderCoefficient(coefficients, polynomialIndex, degree),
 108560            result);
 108561        sbyte resultSign = signs[GetRoundedCylinderCoefficientIndex(polynomialIndex, degree)];
 108562        scale[0] = denominator;
 563
 926564        for (int coefficientIndex = degree - 1; coefficientIndex >= 0; coefficientIndex--)
 565        {
 355566            MultiplyRoundedCylinderWideByWord(result, numerator, first);
 355567            sbyte firstSign = resultSign;
 355568            MultiplyRoundedCylinderWide(
 355569                GetRoundedCylinderCoefficient(coefficients, polynomialIndex, coefficientIndex),
 355570                scale,
 355571                second);
 355572            sbyte secondSign = signs[GetRoundedCylinderCoefficientIndex(polynomialIndex, coefficientIndex)];
 355573            AddRoundedCylinderSigned(
 355574                first,
 355575                firstSign,
 355576                second,
 355577                secondSign,
 355578                sum,
 355579                out resultSign);
 355580            CopyRoundedCylinderWide(sum, result);
 355581            MultiplyRoundedCylinderWideByWord(scale, denominator, first);
 355582            CopyRoundedCylinderWide(first, scale);
 583        }
 584
 108585        return resultSign;
 586    }
 587
 588    private static int EvaluateRoundedCylinderPolynomialSignAtInteger(
 589        Span<ulong> coefficients,
 590        Span<sbyte> signs,
 591        int degree,
 592        int polynomialIndex,
 593        ulong parameter)
 594    {
 4341595        Span<ulong> result = stackalloc ulong[RoundedCylinderWideLimbCount];
 4341596        Span<ulong> product = stackalloc ulong[RoundedCylinderWideLimbCount];
 4341597        Span<ulong> sum = stackalloc ulong[RoundedCylinderWideLimbCount];
 4341598        result.Clear();
 4341599        CopyRoundedCylinderWide(
 4341600            GetRoundedCylinderCoefficient(coefficients, polynomialIndex, degree),
 4341601            result);
 4341602        sbyte resultSign = signs[GetRoundedCylinderCoefficientIndex(polynomialIndex, degree)];
 603
 37894604        for (int coefficientIndex = degree - 1; coefficientIndex >= 0; coefficientIndex--)
 605        {
 14606606            MultiplyRoundedCylinderWideByWord(result, parameter, product);
 14606607            AddRoundedCylinderSigned(
 14606608                product,
 14606609                resultSign,
 14606610                GetRoundedCylinderCoefficient(coefficients, polynomialIndex, coefficientIndex),
 14606611                signs[GetRoundedCylinderCoefficientIndex(polynomialIndex, coefficientIndex)],
 14606612                sum,
 14606613                out resultSign);
 14606614            CopyRoundedCylinderWide(sum, result);
 615        }
 616
 4341617        return resultSign;
 618    }
 619
 620    private static int BuildRoundedCylinderSturmSequence(
 621        RoundedCylinderTorusPolynomial polynomial,
 622        Span<ulong> coefficients,
 623        Span<sbyte> signs,
 624        Span<int> degrees)
 625    {
 45626        ImportRoundedCylinderCoefficient(polynomial.H0, coefficients, signs, 0, 0);
 45627        ImportRoundedCylinderCoefficient(polynomial.H1, coefficients, signs, 0, 1);
 45628        ImportRoundedCylinderCoefficient(polynomial.H2, coefficients, signs, 0, 2);
 45629        ImportRoundedCylinderCoefficient(polynomial.H3, coefficients, signs, 0, 3);
 45630        ImportRoundedCylinderCoefficient(polynomial.H4, coefficients, signs, 0, 4);
 45631        degrees[0] = TrimRoundedCylinderPolynomial(signs, 0, 4);
 45632        if (degrees[0] < 0)
 1633            return 0;
 44634        if (degrees[0] == 0)
 2635            return 1;
 636
 420637        for (int coefficientIndex = 1; coefficientIndex <= degrees[0]; coefficientIndex++)
 638        {
 168639            int destinationIndex = coefficientIndex - 1;
 168640            MultiplyRoundedCylinderWideByWord(
 168641                GetRoundedCylinderCoefficient(coefficients, 0, coefficientIndex),
 168642                (ulong)coefficientIndex,
 168643                GetRoundedCylinderCoefficient(coefficients, 1, destinationIndex));
 168644            signs[GetRoundedCylinderCoefficientIndex(1, destinationIndex)] =
 168645                signs[GetRoundedCylinderCoefficientIndex(0, coefficientIndex)];
 646        }
 42647        degrees[1] = degrees[0] - 1;
 648
 649        // A nonstationary authored chord makes H4 strictly positive. A
 650        // stationary chord was already reduced to the constant cases above.
 42651        return BuildRoundedCylinderQuarticSturmSequence(coefficients, signs, degrees);
 652    }
 653
 654    private static int BuildRoundedCylinderQuarticSturmSequence(
 655        Span<ulong> coefficients,
 656        Span<sbyte> signs,
 657        Span<int> degrees)
 658    {
 4268659        int wordCount = coefficients.Length / (RoundedCylinderPolynomialCount * RoundedCylinderCoefficientCount);
 4268660        Span<ulong> first = stackalloc ulong[wordCount];
 4268661        Span<ulong> second = stackalloc ulong[wordCount];
 4268662        Span<ulong> scratch = stackalloc ulong[wordCount];
 4268663        Span<ulong> result = stackalloc ulong[wordCount];
 664
 4268665        ReadOnlySpan<ulong> a = GetRoundedCylinderCoefficient(coefficients, 0, 4);
 4268666        ReadOnlySpan<ulong> b = GetRoundedCylinderCoefficient(coefficients, 0, 3);
 4268667        ReadOnlySpan<ulong> c = GetRoundedCylinderCoefficient(coefficients, 0, 2);
 4268668        ReadOnlySpan<ulong> d = GetRoundedCylinderCoefficient(coefficients, 0, 1);
 4268669        ReadOnlySpan<ulong> e = GetRoundedCylinderCoefficient(coefficients, 0, 0);
 4268670        sbyte aSign = signs[GetRoundedCylinderCoefficientIndex(0, 4)];
 4268671        sbyte bSign = signs[GetRoundedCylinderCoefficientIndex(0, 3)];
 4268672        sbyte cSign = signs[GetRoundedCylinderCoefficientIndex(0, 2)];
 4268673        sbyte dSign = signs[GetRoundedCylinderCoefficientIndex(0, 1)];
 4268674        sbyte eSign = signs[GetRoundedCylinderCoefficientIndex(0, 0)];
 675
 676        // Positive-scaled negative remainder of the quartic and its derivative.
 4268677        SetRoundedCylinderScaledProduct(b, bSign, b, bSign, 3UL, first, out sbyte firstSign, scratch);
 4268678        SetRoundedCylinderScaledProduct(a, aSign, c, cSign, 8UL, second, out sbyte secondSign, scratch);
 4268679        Span<ulong> u = GetRoundedCylinderCoefficient(coefficients, 2, 2);
 4268680        SubtractRoundedCylinderSigned(first, firstSign, second, secondSign, u, out sbyte uSign);
 4268681        signs[GetRoundedCylinderCoefficientIndex(2, 2)] = uSign;
 682
 4268683        SetRoundedCylinderProduct(b, bSign, c, cSign, first, out firstSign);
 4268684        SetRoundedCylinderScaledProduct(a, aSign, d, dSign, 6UL, second, out secondSign, scratch);
 4268685        SubtractRoundedCylinderSigned(first, firstSign, second, secondSign, result, out sbyte resultSign);
 4268686        Span<ulong> v = GetRoundedCylinderCoefficient(coefficients, 2, 1);
 4268687        MultiplyRoundedCylinderWideByWord(result, 2UL, v);
 4268688        sbyte vSign = resultSign;
 4268689        signs[GetRoundedCylinderCoefficientIndex(2, 1)] = vSign;
 690
 4268691        SetRoundedCylinderProduct(b, bSign, d, dSign, first, out firstSign);
 4268692        SetRoundedCylinderScaledProduct(a, aSign, e, eSign, 16UL, second, out secondSign, scratch);
 4268693        Span<ulong> w = GetRoundedCylinderCoefficient(coefficients, 2, 0);
 4268694        SubtractRoundedCylinderSigned(first, firstSign, second, secondSign, w, out sbyte wSign);
 4268695        signs[GetRoundedCylinderCoefficientIndex(2, 0)] = wSign;
 4268696        degrees[2] = TrimRoundedCylinderPolynomial(signs, 2, 2);
 4268697        if (degrees[2] < 0)
 77698            return 2;
 4191699        if (degrees[2] == 0)
 700        {
 11701            RemoveRoundedCylinderCommonPowerOfTwo(coefficients, signs, 2, 0);
 11702            return 3;
 703        }
 4180704        if (degrees[2] == 1)
 705        {
 6706            RemoveRoundedCylinderCommonPowerOfTwo(coefficients, signs, 2, 1);
 6707            int finalDegree = BuildRoundedCylinderNegativeRemainder(
 6708                coefficients,
 6709                signs,
 6710                1,
 6711                3,
 6712                2,
 6713                1,
 6714                3);
 6715            if (finalDegree < 0)
 2716                return 3;
 717
 4718            degrees[3] = finalDegree;
 4719            return 4;
 720        }
 4174721        Span<ulong> l = GetRoundedCylinderCoefficient(coefficients, 3, 1);
 4174722        Span<ulong> m = GetRoundedCylinderCoefficient(coefficients, 3, 0);
 723
 724        // This factorization divides the ordinary third Sturm polynomial by
 725        // positive 16a. Both two-bit shifts are algebraically exact.
 4174726        SetRoundedCylinderScaledProduct(c, cSign, c, cSign, 4UL, first, out firstSign, scratch);
 4174727        AddRoundedCylinderSigned(w, wSign, first, firstSign, result, out resultSign);
 4174728        SetRoundedCylinderScaledProduct(b, bSign, d, dSign, 9UL, first, out firstSign, scratch);
 4174729        SubtractRoundedCylinderSigned(result, resultSign, first, firstSign, l, out sbyte xSign);
 730
 4174731        SetRoundedCylinderProduct(c, cSign, d, dSign, first, out firstSign);
 4174732        SetRoundedCylinderScaledProduct(b, bSign, e, eSign, 6UL, second, out secondSign, scratch);
 4174733        SubtractRoundedCylinderSigned(first, firstSign, second, secondSign, m, out sbyte ySign);
 734
 4174735        SetRoundedCylinderProduct(u, uSign, l, xSign, first, out firstSign);
 4174736        SetRoundedCylinderProduct(v, vSign, v, vSign, second, out secondSign);
 4174737        SubtractRoundedCylinderSigned(first, firstSign, second, secondSign, result, out resultSign);
 4174738        ShiftRoundedCylinderWideRight(result, 2);
 4174739        CopyRoundedCylinderWide(result, l);
 4174740        sbyte lSign = resultSign;
 4174741        signs[GetRoundedCylinderCoefficientIndex(3, 1)] = lSign;
 742
 4174743        SetRoundedCylinderProduct(u, uSign, m, ySign, first, out firstSign);
 4174744        MultiplyRoundedCylinderWideByWord(first, 2UL, second);
 4174745        secondSign = firstSign;
 4174746        SetRoundedCylinderProduct(v, vSign, w, wSign, first, out firstSign);
 4174747        SubtractRoundedCylinderSigned(second, secondSign, first, firstSign, result, out resultSign);
 4174748        ShiftRoundedCylinderWideRight(result, 2);
 4174749        CopyRoundedCylinderWide(result, m);
 4174750        sbyte mSign = resultSign;
 4174751        signs[GetRoundedCylinderCoefficientIndex(3, 0)] = mSign;
 752
 4174753        RemoveRoundedCylinderCommonPowerOfTwo(coefficients, signs, 2, 2);
 4174754        uSign = signs[GetRoundedCylinderCoefficientIndex(2, 2)];
 4174755        vSign = signs[GetRoundedCylinderCoefficientIndex(2, 1)];
 4174756        wSign = signs[GetRoundedCylinderCoefficientIndex(2, 0)];
 4174757        degrees[3] = TrimRoundedCylinderPolynomial(signs, 3, 1);
 4174758        if (degrees[3] < 0)
 620759            return 3;
 3554760        RemoveRoundedCylinderCommonPowerOfTwo(coefficients, signs, 3, degrees[3]);
 3554761        if (degrees[3] == 0)
 3762            return 4;
 763
 3551764        Span<ulong> k = GetRoundedCylinderCoefficient(coefficients, 4, 0);
 765        // The last remainder has the quartic discriminant's sign when u != 0.
 766        // Factorized invariants avoid materializing its much wider magnitude.
 3551767        SetRoundedCylinderProduct(c, cSign, c, cSign, first, out firstSign);
 3551768        SetRoundedCylinderScaledProduct(b, bSign, d, dSign, 3UL, second, out secondSign, scratch);
 3551769        SubtractRoundedCylinderSigned(first, firstSign, second, secondSign, result, out resultSign);
 3551770        SetRoundedCylinderScaledProduct(a, aSign, e, eSign, 12UL, first, out firstSign, scratch);
 3551771        AddRoundedCylinderSigned(result, resultSign, first, firstSign, k, out sbyte delta0Sign);
 772
 3551773        SetRoundedCylinderTripleProduct(c, cSign, c, cSign, c, cSign, first, out firstSign, scratch);
 3551774        MultiplyRoundedCylinderWideByWord(first, 2UL, second);
 3551775        secondSign = firstSign;
 3551776        SetRoundedCylinderTripleProduct(b, bSign, c, cSign, d, dSign, first, out firstSign, scratch);
 3551777        MultiplyRoundedCylinderWideByWord(first, 9UL, result);
 3551778        SubtractRoundedCylinderSigned(second, secondSign, result, firstSign, scratch, out secondSign);
 3551779        CopyRoundedCylinderWide(scratch, second);
 3551780        SetRoundedCylinderTripleProduct(b, bSign, b, bSign, e, eSign, first, out firstSign, scratch);
 3551781        MultiplyRoundedCylinderWideByWord(first, 27UL, result);
 3551782        AddRoundedCylinderSigned(second, secondSign, result, firstSign, scratch, out secondSign);
 3551783        CopyRoundedCylinderWide(scratch, second);
 3551784        SetRoundedCylinderTripleProduct(a, aSign, d, dSign, d, dSign, first, out firstSign, scratch);
 3551785        MultiplyRoundedCylinderWideByWord(first, 27UL, result);
 3551786        AddRoundedCylinderSigned(second, secondSign, result, firstSign, scratch, out secondSign);
 3551787        CopyRoundedCylinderWide(scratch, second);
 3551788        SetRoundedCylinderTripleProduct(a, aSign, c, cSign, e, eSign, first, out firstSign, scratch);
 3551789        MultiplyRoundedCylinderWideByWord(first, 72UL, result);
 3551790        SubtractRoundedCylinderSigned(second, secondSign, result, firstSign, scratch, out secondSign);
 3551791        CopyRoundedCylinderWide(scratch, second);
 792
 3551793        SetRoundedCylinderTripleProduct(k, delta0Sign, k, delta0Sign, k, delta0Sign, first, out firstSign, scratch);
 3551794        MultiplyRoundedCylinderWideByWord(first, 4UL, result);
 3551795        SetRoundedCylinderProduct(second, secondSign, second, secondSign, scratch, out sbyte delta1SquaredSign);
 3551796        SubtractRoundedCylinderSigned(result, firstSign, scratch, delta1SquaredSign, first, out sbyte kSign);
 3551797        k.Clear();
 3551798        if (kSign != 0)
 3442799            k[0] = 1UL;
 3551800        signs[GetRoundedCylinderCoefficientIndex(4, 0)] = kSign;
 3551801        if (kSign == 0)
 109802            return 4;
 3442803        degrees[4] = 0;
 3442804        return 5;
 805    }
 806
 807    private static void SetRoundedCylinderProduct(
 808        ReadOnlySpan<ulong> left,
 809        sbyte leftSign,
 810        ReadOnlySpan<ulong> right,
 811        sbyte rightSign,
 812        Span<ulong> destination,
 813        out sbyte destinationSign)
 814    {
 36508815        MultiplyRoundedCylinderWide(left, right, destination);
 36508816        destinationSign = MultiplyRoundedCylinderSigns(leftSign, rightSign);
 36508817    }
 818
 819    private static void SetRoundedCylinderScaledProduct(
 820        ReadOnlySpan<ulong> left,
 821        sbyte leftSign,
 822        ReadOnlySpan<ulong> right,
 823        sbyte rightSign,
 824        ulong scale,
 825        Span<ulong> destination,
 826        out sbyte destinationSign,
 827        Span<ulong> scratch)
 828    {
 36696829        MultiplyRoundedCylinderWide(left, right, scratch);
 36696830        MultiplyRoundedCylinderWideByWord(scratch, scale, destination);
 36696831        destinationSign = MultiplyRoundedCylinderSigns(leftSign, rightSign);
 36696832    }
 833
 834    private static void SetRoundedCylinderTripleProduct(
 835        ReadOnlySpan<ulong> first,
 836        sbyte firstSign,
 837        ReadOnlySpan<ulong> second,
 838        sbyte secondSign,
 839        ReadOnlySpan<ulong> third,
 840        sbyte thirdSign,
 841        Span<ulong> destination,
 842        out sbyte destinationSign,
 843        Span<ulong> scratch)
 844    {
 21306845        MultiplyRoundedCylinderWide(first, second, scratch);
 21306846        MultiplyRoundedCylinderWide(scratch, third, destination);
 21306847        destinationSign = MultiplyRoundedCylinderSigns(
 21306848            MultiplyRoundedCylinderSigns(firstSign, secondSign),
 21306849            thirdSign);
 21306850    }
 851
 852    private static int BuildRoundedCylinderNegativeRemainder(
 853        Span<ulong> coefficients,
 854        Span<sbyte> signs,
 855        int dividendIndex,
 856        int dividendDegree,
 857        int divisorIndex,
 858        int divisorDegree,
 859        int destinationIndex)
 860    {
 2928861        for (int coefficientIndex = 0; coefficientIndex <= dividendDegree; coefficientIndex++)
 862        {
 1126863            CopyRoundedCylinderWide(
 1126864                GetRoundedCylinderCoefficient(coefficients, dividendIndex, coefficientIndex),
 1126865                GetRoundedCylinderCoefficient(coefficients, destinationIndex, coefficientIndex));
 1126866            signs[GetRoundedCylinderCoefficientIndex(destinationIndex, coefficientIndex)] =
 1126867                signs[GetRoundedCylinderCoefficientIndex(dividendIndex, coefficientIndex)];
 868        }
 869
 338870        int wordCount = coefficients.Length / (RoundedCylinderPolynomialCount * RoundedCylinderCoefficientCount);
 338871        Span<ulong> leadingRemainder = stackalloc ulong[wordCount];
 338872        Span<ulong> leadingDivisor = stackalloc ulong[wordCount];
 338873        Span<ulong> first = stackalloc ulong[wordCount];
 338874        Span<ulong> second = stackalloc ulong[wordCount];
 338875        Span<ulong> difference = stackalloc ulong[wordCount];
 338876        CopyRoundedCylinderWide(
 338877            GetRoundedCylinderCoefficient(coefficients, divisorIndex, divisorDegree),
 338878            leadingDivisor);
 338879        sbyte leadingDivisorSign = signs[GetRoundedCylinderCoefficientIndex(divisorIndex, divisorDegree)];
 338880        int remainderDegree = dividendDegree;
 338881        int multiplicationCount = 0;
 754882        while (remainderDegree >= divisorDegree)
 883        {
 539884            CopyRoundedCylinderWide(
 539885                GetRoundedCylinderCoefficient(coefficients, destinationIndex, remainderDegree),
 539886                leadingRemainder);
 539887            sbyte leadingRemainderSign = signs[
 539888                GetRoundedCylinderCoefficientIndex(destinationIndex, remainderDegree)];
 539889            int offset = remainderDegree - divisorDegree;
 890
 4294891            for (int coefficientIndex = 0; coefficientIndex <= remainderDegree; coefficientIndex++)
 892            {
 1608893                Span<ulong> remainderCoefficient = GetRoundedCylinderCoefficient(
 1608894                    coefficients,
 1608895                    destinationIndex,
 1608896                    coefficientIndex);
 1608897                MultiplyRoundedCylinderWide(
 1608898                    leadingDivisor,
 1608899                    remainderCoefficient,
 1608900                    first);
 1608901                sbyte firstSign = MultiplyRoundedCylinderSigns(
 1608902                    leadingDivisorSign,
 1608903                    signs[GetRoundedCylinderCoefficientIndex(destinationIndex, coefficientIndex)]);
 904
 1608905                int divisorCoefficientIndex = coefficientIndex - offset;
 906                sbyte secondSign;
 1608907                if (divisorCoefficientIndex >= 0 && divisorCoefficientIndex <= divisorDegree)
 908                {
 1258909                    MultiplyRoundedCylinderWide(
 1258910                        leadingRemainder,
 1258911                        GetRoundedCylinderCoefficient(coefficients, divisorIndex, divisorCoefficientIndex),
 1258912                        second);
 1258913                    secondSign = MultiplyRoundedCylinderSigns(
 1258914                        leadingRemainderSign,
 1258915                        signs[GetRoundedCylinderCoefficientIndex(divisorIndex, divisorCoefficientIndex)]);
 916                }
 917                else
 918                {
 350919                    second.Clear();
 350920                    secondSign = 0;
 921                }
 922
 1608923                SubtractRoundedCylinderSigned(
 1608924                    first,
 1608925                    firstSign,
 1608926                    second,
 1608927                    secondSign,
 1608928                    difference,
 1608929                    out sbyte differenceSign);
 1608930                CopyRoundedCylinderWide(difference, remainderCoefficient);
 1608931                signs[GetRoundedCylinderCoefficientIndex(destinationIndex, coefficientIndex)] = differenceSign;
 932            }
 933
 539934            multiplicationCount++;
 539935            remainderDegree = TrimRoundedCylinderPolynomial(
 539936                signs,
 539937                destinationIndex,
 539938                remainderDegree - 1);
 539939            if (remainderDegree < 0)
 123940                return -1;
 941        }
 942
 215943        bool pseudoScaleIsPositive = leadingDivisorSign > 0 || (multiplicationCount & 1) == 0;
 215944        if (pseudoScaleIsPositive)
 945        {
 986946            for (int coefficientIndex = 0; coefficientIndex <= remainderDegree; coefficientIndex++)
 947            {
 280948                int signIndex = GetRoundedCylinderCoefficientIndex(destinationIndex, coefficientIndex);
 280949                signs[signIndex] = (sbyte)-signs[signIndex];
 950            }
 951        }
 952
 215953        RemoveRoundedCylinderCommonPowerOfTwo(
 215954            coefficients,
 215955            signs,
 215956            destinationIndex,
 215957            remainderDegree);
 215958        return remainderDegree;
 959    }
 960
 961    private static void RemoveRoundedCylinderCommonPowerOfTwo(
 962        Span<ulong> coefficients,
 963        Span<sbyte> signs,
 964        int polynomialIndex,
 965        int degree)
 966    {
 7960967        int commonShift = int.MaxValue;
 55784968        for (int coefficientIndex = 0; coefficientIndex <= degree; coefficientIndex++)
 969        {
 19932970            if (signs[GetRoundedCylinderCoefficientIndex(polynomialIndex, coefficientIndex)] == 0)
 971                continue;
 19063972            commonShift = Math.Min(
 19063973                commonShift,
 19063974                CountRoundedCylinderTrailingZeroes(
 19063975                    GetRoundedCylinderCoefficient(coefficients, polynomialIndex, coefficientIndex)));
 976        }
 977
 55784978        for (int coefficientIndex = 0; coefficientIndex <= degree; coefficientIndex++)
 979        {
 19932980            if (signs[GetRoundedCylinderCoefficientIndex(polynomialIndex, coefficientIndex)] != 0)
 981            {
 19063982                ShiftRoundedCylinderWideRight(
 19063983                    GetRoundedCylinderCoefficient(coefficients, polynomialIndex, coefficientIndex),
 19063984                    commonShift);
 985            }
 986        }
 7960987    }
 988
 989    private static int TrimRoundedCylinderPolynomial(
 990        Span<sbyte> signs,
 991        int polynomialIndex,
 992        int degree)
 993    {
 11511994        while (degree >= 0
 11511995            && signs[GetRoundedCylinderCoefficientIndex(polynomialIndex, degree)] == 0)
 996        {
 1841997            degree--;
 998        }
 9670999        return degree;
 1000    }
 1001
 1002}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.ScaledAxisTriangle.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.ScaledAxisTriangle.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>Exact closest-feature ingress for affinely scaled triangles and finite capsule cores.</content>
 13internal static partial class WideFiniteAxisIntersection
 14{
 15    // Core endpoints retain the established scalar normalized axis and odd half
 16    // length. Source world points have at most 319 bits, their bounded edges
 17    // at most 305 after a second rigid core frame, and core/edge Gram minors
 18    // at most 1304 bits. Finite face witnesses
 19    // and clipped endpoint witnesses require fewer than 2600 coordinate bits;
 20    // squared distance and the final radius comparison fit these 6144-bit banks.
 21    private const int ScaledAxisWords = 96;
 22
 23
 24
 25    internal static bool TryGetScaledCenteredCapsuleTriangleContact(in FixedTriangle triangle,
 26        in ScaledRigidFrame3d frame, Vector3d center, FixedQuaternion capsuleRotation,
 27        Fixed64 length, Fixed64 radius, Vector3d fallback, out FixedContactAnchors contact,
 28        out AxisTriangleContactFeature feature, out int endpoint, in RationalSegmentInterval interval = default)
 29    {
 14430        contact = default; feature = default; endpoint = 0;
 4831        Vector3d axis = length == Fixed64.Zero ? Vector3d.Up : capsuleRotation.Rotate(Vector3d.Up).Normalized;
 4832        Span<ulong> bestValues = stackalloc ulong[ScaledAxisTriangleCandidate.StorageWords];
 4833        Span<int> bestSigns = stackalloc int[ScaledAxisTriangleCandidate.SignCount];
 4834        var best = new ScaledAxisTriangleCandidate(bestValues, bestSigns);
 4835        GetClosestScaledAxisTriangleCandidate(triangle, frame, center, axis, length, ref best, interval);
 9636        Span<ulong> workValues = stackalloc ulong[8 * ScaledAxisWords]; Span<int> workSigns = stackalloc int[8];
 4837        var n = ContactQuadratic.At(workValues, workSigns, 0, ScaledAxisWords);
 4838        var d = ContactQuadratic.At(workValues, workSigns, 1, ScaledAxisWords);
 4839        var term = ContactQuadratic.At(workValues, workSigns, 2, ScaledAxisWords);
 4840        var threshold = ContactQuadratic.At(workValues, workSigns, 3, ScaledAxisWords);
 4841        ContactQuadratic.Scale(best.At(6), Signed576.ExtendValue(Signed320.ExtendValue(Signed192.Raw(radius))), n);
 4842        ContactQuadratic.Multiply(n, n, ReadOnlySpan<ulong>.Empty, threshold);
 9643        best.At(7).CopyTo(term); term.Add(threshold, -1);
 5544        if (term.Signs[0] > 0) return false;
 4145        Span<Signed832> separation = stackalloc Signed832[3];
 4146        GetScaledAxisPrimitiveSeparation(best, separation);
 4047        int vertex = best.EdgeMask == 5 ? 0 : best.EdgeMask == 3 ? 1 : best.EdgeMask == 6 ? 2 : -1;
 4048        int edgeMask = WideTriangleRelations.GetScaledTriangleLocalNormal(triangle, frame).IsZero ? 7 : best.EdgeMask;
 4049        feature = new AxisTriangleContactFeature(edgeMask, separation[0], separation[1], separation[2], vertex);
 4050        endpoint = best.Endpoint;
 4051        Vector3d normal = WideNormalization.GetNormalized(separation[0], separation[1], separation[2]);
 4952        if (normal.IsZero) normal = fallback;
 4053        Vector3d source = default;
 31654        for (int coordinate = 0; coordinate < 3; coordinate++)
 55        {
 11956            if (!TryRoundScaledAxisCoordinate(best.At(coordinate), best.At(6), out Fixed64 sourceValue,
 12057                    center[coordinate].m_rawValue)) return false;
 11858            source[coordinate] = sourceValue;
 59        }
 3960        if (best.At(7).Signs[0] == 0 && length != Fixed64.Zero)
 861            return TryGetScaledCoincidentCapsuleContact(best, center, axis, length, radius, normal, source, out contact)
 62        // radius - sqrt(distanceSquared) remains one exact quadratic ratio;
 63        // rounding the distance first would reverse nearest-even ties when
 64        // radius.Raw is odd.
 9365        n.Radical.Clear(); n.Radical[0] = 1; n.Signs[1] = -1;
 3166        best.At(6).CopyTo(d);
 3167        Span<ulong> one = stackalloc ulong[1] { 1 };
 3168        bool represented = ContactQuadratic.TryRoundRootRatio(n, d, best.At(7).Rational, one, one, out Fixed64 depth);
 69        // Exact radius admission above bounds this nonnegative depth by the
 70        // already representable radius, including nearest-even materialization.
 71        System.Diagnostics.Debug.Assert(represented);
 72        // Both anchors use the bounded physical chart. Authored vertices may
 73        // lie far outside the scalar chart after inverse scale cancellation.
 6274        Span<ulong> exitValues = stackalloc ulong[8 * ScaledAxisWords]; Span<int> exitSigns = stackalloc int[8];
 6275        var x = ScaledAxisField(exitValues, exitSigns, 0); var y = ScaledAxisField(exitValues, exitSigns, 1);
 6276        var z = ScaledAxisField(exitValues, exitSigns, 2); var exitD = ScaledAxisField(exitValues, exitSigns, 3);
 3177        ContactQuadratic.Scale(best.At(6), RayExtend(ParameterScale), exitD);
 24878        for (int coordinate = 0; coordinate < 3; coordinate++)
 79        {
 9380            var target = coordinate == 0 ? x : coordinate == 1 ? y : z;
 9381            ContactQuadratic.Scale(best.At(coordinate + 3), RayExtend(ParameterScale), target);
 9382            ContactQuadratic.Scale(best.At(6), RayExtend(Fixed64.GetExactRawProduct(normal[coordinate], radius)), term);
 9383            target.Add(term, -1);
 84        }
 3185        if (!QuadraticPointMaterialization.TryGetPointInFrame(center, FixedQuaternion.Identity,
 3286            x, y, z, exitD, ReadOnlySpan<ulong>.Empty, center, out Vector3d exit)) return false;
 3087        contact = new FixedContactAnchors(new FixedPointAnchor(center, FixedQuaternion.Identity, source),
 3088            new FixedPointAnchor(center, FixedQuaternion.Identity, exit), normal, depth, false);
 3089        return true;
 90    }
 91
 92    internal static void GetClosestScaledAxisTriangleCandidate(in FixedTriangle triangle, in ScaledRigidFrame3d frame,
 93        Vector3d center, Vector3d normalizedAxis, Fixed64 length, scoped ref ScaledAxisTriangleCandidate best,
 94        in RationalSegmentInterval interval = default)
 95    {
 5296        RationalPoint3d first = GetScaledCoreEndpoint(center, normalizedAxis, length, -1),
 5297            last = GetScaledCoreEndpoint(center, normalizedAxis, length, 1);
 5298        GetClosestScaledAxisTriangleCandidate(triangle, frame, first, last, center, ref best, interval);
 5299    }
 100
 101    internal static void GetClosestScaledAxisTriangleCandidate(in FixedTriangle triangle, in ScaledRigidFrame3d frame,
 102        in RationalPoint3d first, in RationalPoint3d last, Vector3d center, scoped ref ScaledAxisTriangleCandidate best,
 103        in RationalSegmentInterval interval = default)
 104    {
 90105        Span<ulong> values = stackalloc ulong[ScaledAxisTriangleCandidate.StorageWords];
 90106        Span<int> signs = stackalloc int[ScaledAxisTriangleCandidate.SignCount];
 90107        var candidate = new ScaledAxisTriangleCandidate(values, signs);
 113108        if (!interval.IsClipped && AccumulateScaledAxisFace(triangle, frame, first, last, center, candidate, ref best)) 
 67109        Span<Vector3d> vertices = stackalloc Vector3d[3] { triangle.A, triangle.B, triangle.C };
 67110        int count = interval.IsClipped ? 1 : 3;
 468111        for (int edge = 0; edge < count; edge++)
 167112            AccumulateScaledAxisEdge(frame.GetPoint(vertices[edge]), frame.GetPoint(vertices[(edge + 1) % 3]),
 167113                first, last, center, edge, interval, candidate, ref best);
 114        // Even a point triangle has a finite source endpoint whose projection
 115        // onto the closed core is a closest candidate.
 116        System.Diagnostics.Debug.Assert(best.HasValue);
 67117    }
 118    private static bool TryGetScaledCoincidentCapsuleContact(scoped in ScaledAxisTriangleCandidate candidate,
 119        Vector3d center, Vector3d axis, Fixed64 length, Fixed64 radius, Vector3d normal, Vector3d source,
 120        out FixedContactAnchors contact)
 121    {
 8122        contact = default;
 16123        Span<ulong> values = stackalloc ulong[16 * ScaledAxisWords]; Span<int> signs = stackalloc int[16];
 8124        Span<ulong> root = stackalloc ulong[2 * ScaledAxisWords + 1];
 16125        var n = ScaledAxisField(values, signs, 0); var d = ScaledAxisField(values, signs, 1);
 16126        var x = ScaledAxisField(values, signs, 2); var y = ScaledAxisField(values, signs, 3);
 16127        var z = ScaledAxisField(values, signs, 4); var divisor = ScaledAxisField(values, signs, 5);
 16128        var term = ScaledAxisField(values, signs, 6); var coefficient = ScaledAxisField(values, signs, 7);
 8129        GetCoincidentCapsuleRayExit(candidate.At(3), candidate.At(4), candidate.At(5), candidate.At(6),
 8130            axis, length, radius, -normal, n, d, root, out _);
 8131        GetCapsuleRayDepth(n, d, root, normal, out Fixed64 depth, out bool clamped);
 9132        if (clamped) return false;
 7133        ContactQuadratic.Multiply(candidate.At(6), d, root, divisor);
 56134        for (int coordinate = 0; coordinate < 3; coordinate++)
 135        {
 21136            var target = coordinate == 0 ? x : coordinate == 1 ? y : z;
 21137            ContactQuadratic.Multiply(candidate.At(coordinate + 3), d, root, target);
 21138            ContactQuadratic.Scale(candidate.At(6), RayExtend(Signed192.Raw(-normal[coordinate])), coefficient);
 42139            ContactQuadratic.Multiply(coefficient, n, root, term); target.Add(term);
 140        }
 7141        if (!QuadraticPointMaterialization.TryGetPointInFrame(center, FixedQuaternion.Identity,
 8142            x, y, z, divisor, root, center, out Vector3d exit)) return false;
 6143        contact = new FixedContactAnchors(new FixedPointAnchor(center, FixedQuaternion.Identity, source),
 6144            new FixedPointAnchor(center, FixedQuaternion.Identity, exit), normal, depth, false);
 6145        return true;
 146    }
 147    private static RationalPoint3d GetScaledCoreEndpoint(Vector3d center, Vector3d axis, Fixed64 length, int sign)
 148    {
 104149        Signed320 denominator = new(0, 0, 0, 2, 0);
 150        Signed320 Coordinate(Fixed64 origin, Fixed64 direction)
 151        {
 152            Signed192 product = Fixed64.GetExactRawProduct(direction, length);
 153            Signed320 offset = WideArithmetic.MultiplySigned192(product, Signed192.Signed(1L << 32));
 154            if (sign < 0) offset = WideArithmetic.Negate(offset);
 155            return Signed320.NarrowValue(WideArithmetic.AddSigned576(
 156                WideArithmetic.MultiplySigned320(denominator, Signed192.Raw(origin)), Signed576.ExtendValue(offset)));
 157        }
 104158        return new RationalPoint3d(Coordinate(center.X, axis.X), Coordinate(center.Y, axis.Y),
 104159            Coordinate(center.Z, axis.Z), denominator);
 160    }
 161
 162    private static bool TryRoundScaledAxisCoordinate(ContactQuadratic n, ContactQuadratic d, out Fixed64 value, long par
 119163        Fixed64.TryGetSignedRawRatio(n.Rational, d.Rational, n.Signs[0] < 0, out value, parityOffset);
 164
 165    private static bool AccumulateScaledAxisFace(in FixedTriangle triangle, in ScaledRigidFrame3d frame,
 166        in RationalPoint3d first, in RationalPoint3d last, Vector3d center,
 167        scoped ScaledAxisTriangleCandidate candidate, scoped ref ScaledAxisTriangleCandidate best)
 168    {
 73169        if (!WideTriangleRelations.TryGetProjectedSegmentInterval(triangle, frame, first, last,
 119170            out Signed832 ln, out Signed832 ld, out Signed832 un, out Signed832 ud)) return false;
 27171        WideDirection3d normal = WideTriangleRelations.GetScaledTriangleLocalNormal(triangle, frame).Transform(frame.Geo
 27172        RationalPoint3d reference = frame.GetPoint(triangle.A);
 27173        Signed832 a = WideTriangleRelations.DotScaledDirections(normal,
 27174            WideTriangleRelations.GetScaledPointDifference(first, reference, frame.RotationDenominator));
 27175        Signed832 b = WideTriangleRelations.DotScaledDirections(normal,
 27176            WideTriangleRelations.GetScaledPointDifference(last, reference, frame.RotationDenominator));
 54177        Span<ulong> values = stackalloc ulong[20 * ScaledAxisWords]; Span<int> signs = stackalloc int[20];
 54178        var n = ScaledAxisField(values, signs, 0); var d = ScaledAxisField(values, signs, 1);
 54179        var remaining = ScaledAxisField(values, signs, 2); var plane = ScaledAxisField(values, signs, 3);
 54180        var term = ScaledAxisField(values, signs, 4); var coefficient = ScaledAxisField(values, signs, 5);
 54181        var metric = ScaledAxisField(values, signs, 6); var scale = ScaledAxisField(values, signs, 7);
 54182        var coordinate = ScaledAxisField(values, signs, 8); var query = ScaledAxisField(values, signs, 9);
 27183        Signed832 delta = WideArithmetic.SubtractSigned832(a, b);
 27184        bool lowerBound = delta.IsZero;
 27185        if (delta.IsZero)
 186        {
 187            // A reduced mediant is a reversal-symmetric interior representative
 188            // of this constant-distance face family, without multiplying its
 189            // independently authored interval denominators.
 7190            SetScaledAxisMediant(ln, ld, un, ud, n, d);
 191        }
 192        else
 193        {
 40194            Signed832 pn = a, pd = delta;
 24195            if (pd.Sign < 0) { pn = WideArithmetic.SubtractSigned832(default, pn); pd = WideArithmetic.SubtractSigned832
 20196            Signed832 globalN = pn;
 26197            if (globalN.Sign < 0) globalN = default;
 18198            else if (WideArithmetic.SubtractSigned832(globalN, pd).Sign > 0) globalN = pd;
 20199            lowerBound = RationalSegmentInterval.CompareParameters(globalN, pd, ln, ld) >= 0
 20200                && RationalSegmentInterval.CompareParameters(globalN, pd, un, ud) <= 0;
 201            // The interval comparator accepts nonnegative unit parameters.
 202            // Clamp the unbounded plane foot to the finite core first: a
 203            // negative foot must never be interpreted as an unsigned product.
 20204            pn = globalN;
 24205            if (RationalSegmentInterval.CompareParameters(pn, pd, ln, ld) < 0) { pn = ln; pd = ld; }
 22206            else if (RationalSegmentInterval.CompareParameters(pn, pd, un, ud) > 0) { pn = un; pd = ud; }
 40207            n.Set(pn); d.Set(pd);
 208        }
 54209        d.CopyTo(remaining); remaining.Add(n, -1);
 54210        Span<Signed832> firstWalls = stackalloc Signed832[3], lastWalls = stackalloc Signed832[3];
 27211        _ = WideTriangleRelations.GetScaledTriangleHalfPlanes(triangle, frame, first, firstWalls);
 27212        _ = WideTriangleRelations.GetScaledTriangleHalfPlanes(triangle, frame, last, lastWalls);
 27213        int mask = 0;
 216214        for (int edge = 0; edge < 3; edge++)
 215        {
 81216            ContactQuadratic.Scale(remaining, firstWalls[edge], query);
 162217            ContactQuadratic.Scale(n, lastWalls[edge], term); query.Add(term);
 218            System.Diagnostics.Debug.Assert(query.Signs[0] >= 0);
 86219            if (query.Signs[0] == 0) mask |= 1 << edge;
 220        }
 81221        ContactQuadratic.Scale(remaining, a, plane); ContactQuadratic.Scale(n, b, term); plane.Add(term);
 27222        metric.Set(normal.SquaredLength);
 27223        coefficient.Set(Signed576.ExtendValue(first.Denominator));
 27224        ContactQuadratic.Scale(coefficient, Signed576.ExtendValue(Signed320.ExtendValue(frame.RotationDenominator)), sca
 27225        ContactQuadratic.Multiply(scale, d, ReadOnlySpan<ulong>.Empty, term);
 27226        ContactQuadratic.Multiply(term, metric, ReadOnlySpan<ulong>.Empty, candidate.At(6));
 216227        for (int axis = 0; axis < 3; axis++)
 228        {
 162229            Signed576 start = ScaledAxisRelative(first, center, axis), end = ScaledAxisRelative(last, center, axis);
 243230            ContactQuadratic.Scale(remaining, start, coordinate); ContactQuadratic.Scale(n, end, term); coordinate.Add(t
 81231            ContactQuadratic.Scale(coordinate, Signed576.ExtendValue(Signed320.ExtendValue(frame.RotationDenominator)), 
 81232            ContactQuadratic.Multiply(query, metric, ReadOnlySpan<ulong>.Empty, candidate.At(axis + 3));
 81233            candidate.At(axis + 3).CopyTo(candidate.At(axis));
 81234            ContactQuadratic.Scale(plane, axis == 0 ? normal.X : axis == 1 ? normal.Y : normal.Z, term);
 81235            candidate.At(axis).Add(term, -1);
 236        }
 108237        candidate.EdgeMask = mask; candidate.Endpoint = 0; candidate.Complete(); best.Keep(candidate);
 27238        return lowerBound;
 239    }
 240
 241    private static void AccumulateScaledAxisEdge(in RationalPoint3d first, in RationalPoint3d last,
 242        in RationalPoint3d coreFirst, in RationalPoint3d coreLast, Vector3d center, int edge,
 243        in RationalSegmentInterval interval, scoped ScaledAxisTriangleCandidate candidate, scoped ref ScaledAxisTriangle
 244    {
 334245        Span<ulong> values = stackalloc ulong[14 * ScaledAxisWords]; Span<int> signs = stackalloc int[14];
 334246        var edgeN = ScaledAxisField(values, signs, 0); var coreN = ScaledAxisField(values, signs, 1);
 334247        var d = ScaledAxisField(values, signs, 2); var otherD = ScaledAxisField(values, signs, 3);
 167248        var term = ScaledAxisField(values, signs, 4);
 334249        var n = ScaledAxisField(values, signs, 5); var divisor = ScaledAxisField(values, signs, 6);
 167250        Signed192 sourceR = WideArithmetic.TruncateSigned320By64(first.Denominator),
 167251            coreR = WideArithmetic.TruncateSigned320By64(coreFirst.Denominator);
 167252        WideDirection3d e = ScaledAxisDifference(last, first, coreR),
 167253            f = ScaledAxisDifference(coreLast, coreFirst, sourceR),
 167254            r = WideTriangleRelations.GetScaledPointDifference(first, coreFirst, coreR);
 334255        Signed832 ee = WideTriangleRelations.DotScaledDirections(e, e), ff = WideTriangleRelations.DotScaledDirections(f
 334256        Signed832 er = WideTriangleRelations.DotScaledDirections(e, r), fr = WideTriangleRelations.DotScaledDirections(f
 167257            ef = WideTriangleRelations.DotScaledDirections(e, f);
 167258        if (WideTriangleRelations.TryGetClosestNonparallelSegmentParameters(first, last, coreFirst, coreLast,
 167259            edgeN, d, coreN, otherD) && ScaledAxisParameterInInterval(edgeN, d, interval))
 20260            KeepScaledAxisPair(first, last, coreFirst, coreLast, center, edge, interval, edgeN, coreN, d, candidate, ref
 167261        if (!ee.IsZero && !ff.IsZero && WideDirection3d.AreParallel(e, f))
 262        {
 126263            for (int bound = 0; bound < 2; bound++)
 264            {
 42265                int sourceBound = ef.Sign < 0 ? 1 - bound : bound;
 42266                Signed832 sn = interval.IsClipped ? sourceBound == 0 ? interval.WideLowerNumerator : interval.WideUpperN
 42267                    : sourceBound == 0 ? default : Signed832.ExtendValue(Signed192.Signed(1));
 42268                Signed832 sd = interval.IsClipped ? sourceBound == 0 ? interval.WideLowerDenominator : interval.WideUppe
 42269                    : Signed832.ExtendValue(Signed192.Signed(1));
 84270                n.Set(sn); divisor.Set(sd);
 42271                var parameterN = bound == 0 ? edgeN : coreN;
 42272                var parameterD = bound == 0 ? d : otherD;
 126273                ContactQuadratic.Scale(divisor, fr, parameterN); ContactQuadratic.Scale(n, ef, term); parameterN.Add(ter
 42274                ContactQuadratic.Scale(divisor, ff, parameterD);
 275            }
 40276            if (edgeN.Signs[0] < 0) edgeN.Clear();
 21277            if (ContactQuadratic.CompareRatios(coreN, otherD, ReadOnlySpan<ulong>.Empty,
 25278                    otherD, otherD, ReadOnlySpan<ulong>.Empty) > 0) otherD.CopyTo(coreN);
 21279            if (ContactQuadratic.CompareRatios(edgeN, d, ReadOnlySpan<ulong>.Empty,
 21280                    coreN, otherD, ReadOnlySpan<ulong>.Empty) < 0)
 281            {
 42282                NormalizeScaledAxisRatio(edgeN, d); NormalizeScaledAxisRatio(coreN, otherD);
 84283                edgeN.CopyTo(n); n.Add(coreN); d.CopyTo(divisor); divisor.Add(otherD);
 63284                ContactQuadratic.Scale(n, ff, edgeN); ContactQuadratic.Scale(divisor, fr, term); edgeN.Add(term, -1);
 42285                ContactQuadratic.Scale(n, ef, coreN); ContactQuadratic.Scale(divisor, ef, d);
 57286                if (d.Signs[0] < 0) { d.MultiplySign(-1); edgeN.MultiplySign(-1); coreN.MultiplySign(-1); }
 21287                KeepScaledAxisPair(first, last, coreFirst, coreLast, center, edge, interval,
 21288                    edgeN, coreN, d, candidate, ref best);
 21289                return;
 290            }
 291        }        // Constrained minima are either an unconstrained line foot or a finite
 292        // endpoint projected onto the opposite segment. These candidates also
 293        // cover parallel and point-degenerate cores without division by zero.
 544294        for (int coreEnd = 0; coreEnd < 2; coreEnd++)
 295        {
 209296            if (ee.IsZero) break;
 126297            n.Set(coreEnd == 0 ? WideArithmetic.SubtractSigned832(default, er)
 126298                : WideArithmetic.SubtractSigned832(ef, er));
 252299            d.Set(ee); ClampScaledAxisParameter(n, d);
 126300            if (!ScaledAxisParameterInInterval(n, d, interval)) continue;
 312301            n.CopyTo(edgeN); if (coreEnd == 0) coreN.Clear(); else d.CopyTo(coreN);
 104302            KeepScaledAxisPair(first, last, coreFirst, coreLast, center, edge, interval, edgeN, coreN, d, candidate, ref
 303        }
 876304        for (int sourceEnd = 0; sourceEnd < 2; sourceEnd++)
 305        {
 292306            Signed832 sourceN = interval.IsClipped ? sourceEnd == 0 ? interval.WideLowerNumerator : interval.WideUpperNu
 292307                : sourceEnd == 0 ? default : Signed832.ExtendValue(Signed192.Signed(1));
 292308            Signed832 sourceD = interval.IsClipped ? sourceEnd == 0 ? interval.WideLowerDenominator : interval.WideUpper
 292309                : Signed832.ExtendValue(Signed192.Signed(1));
 584310            n.Set(sourceN); divisor.Set(sourceD);
 292311            if (ff.IsZero)
 216312            { n.CopyTo(edgeN); divisor.CopyTo(d); coreN.Clear(); }
 313            else
 314            {
 660315                ContactQuadratic.Scale(divisor, fr, coreN); ContactQuadratic.Scale(n, ef, term); coreN.Add(term);
 440316                ContactQuadratic.Scale(divisor, ff, d); ClampScaledAxisParameter(coreN, d);
 220317                ContactQuadratic.Scale(n, ff, edgeN);
 318            }
 292319            KeepScaledAxisPair(first, last, coreFirst, coreLast, center, edge, interval, edgeN, coreN, d, candidate, ref
 320        }
 321
 146322    }
 323
 324    private static void KeepScaledAxisPair(in RationalPoint3d first, in RationalPoint3d last,
 325        in RationalPoint3d coreFirst, in RationalPoint3d coreLast, Vector3d center, int edge,
 326        in RationalSegmentInterval interval, scoped ContactQuadratic edgeN, scoped ContactQuadratic coreN, scoped Contac
 327        scoped ScaledAxisTriangleCandidate candidate, scoped ref ScaledAxisTriangleCandidate best)
 328    {
 437329        Signed192 sourceRotationD = WideArithmetic.TruncateSigned320By64(first.Denominator),
 437330            coreRotationD = WideArithmetic.TruncateSigned320By64(coreFirst.Denominator);
 874331        Span<ulong> values = stackalloc ulong[4 * ScaledAxisWords]; Span<int> signs = stackalloc int[4];
 874332        var coefficient = ScaledAxisField(values, signs, 0); var term = ScaledAxisField(values, signs, 1);
 437333            Signed576 common = WideArithmetic.MultiplySigned320(first.Denominator, coreRotationD);
 874334            coefficient.Set(common); ContactQuadratic.Multiply(coefficient, d, ReadOnlySpan<ulong>.Empty, candidate.At(6
 3496335            for (int axis = 0; axis < 3; axis++)
 336            {
 1311337                Signed576 start = WideArithmetic.MultiplySigned576(ScaledAxisRelative(first, center, axis), coreRotation
 1311338                Signed576 end = WideArithmetic.MultiplySigned576(ScaledAxisRelative(last, center, axis), coreRotationD);
 1311339                ContactQuadratic.Scale(d, start, candidate.At(axis));
 2622340                ContactQuadratic.Scale(edgeN, WideArithmetic.SubtractSigned576(end, start), term); candidate.At(axis).Ad
 1311341                start = WideArithmetic.MultiplySigned576(ScaledAxisRelative(coreFirst, center, axis), sourceRotationD);
 1311342                end = WideArithmetic.MultiplySigned576(ScaledAxisRelative(coreLast, center, axis), sourceRotationD);
 1311343                ContactQuadratic.Scale(d, start, candidate.At(axis + 3));
 2622344                ContactQuadratic.Scale(coreN, WideArithmetic.SubtractSigned576(end, start), term); candidate.At(axis + 3
 345            }
 437346            int bit = 1 << edge;
 437347            candidate.EdgeMask = edgeN.Signs[0] == 0 ? bit | (1 << ((edge + 2) % 3))
 437348                : ContactQuadratic.CompareRatios(edgeN, d, ReadOnlySpan<ulong>.Empty,
 437349                    d, d, ReadOnlySpan<ulong>.Empty) == 0 ? bit | (1 << ((edge + 1) % 3)) : bit;
 437350            candidate.Endpoint = interval.IsClipped ? GetScaledAxisIntervalEndpoint(edgeN, d, interval) : 0;
 874351            candidate.Complete(); best.Keep(candidate);
 352
 437353    }
 354
 355    private static ContactQuadratic ScaledAxisField(Span<ulong> values, Span<int> signs, int index) =>
 2601356        ContactQuadratic.At(values, signs, index, ScaledAxisWords);
 357
 358    private static Signed576 ScaledAxisRelative(in RationalPoint3d point, Vector3d center, int axis) =>
 5406359        WideArithmetic.SubtractSigned576(Signed576.ExtendValue(axis == 0 ? point.X : axis == 1 ? point.Y : point.Z),
 5406360            WideArithmetic.MultiplySigned320(point.Denominator, Signed192.Raw(center[axis])));
 361
 334362    private static WideDirection3d ScaledAxisDifference(in RationalPoint3d end, in RationalPoint3d start, Signed192 othe
 334363        WideArithmetic.MultiplySigned320(WideArithmetic.SubtractSigned320(end.X, start.X), otherD),
 334364        WideArithmetic.MultiplySigned320(WideArithmetic.SubtractSigned320(end.Y, start.Y), otherD),
 334365        WideArithmetic.MultiplySigned320(WideArithmetic.SubtractSigned320(end.Z, start.Z), otherD));
 366
 367    private static void ClampScaledAxisParameter(ContactQuadratic n, ContactQuadratic d)
 368    {
 449369        if (n.Signs[0] < 0) n.Clear();
 266370        else if (ContactQuadratic.CompareRatios(n, d, ReadOnlySpan<ulong>.Empty, d, d, ReadOnlySpan<ulong>.Empty) > 0) d
 243371    }
 372
 373    private static bool ScaledAxisParameterInInterval(ContactQuadratic n, ContactQuadratic d, in RationalSegmentInterval
 374    {
 266375        if (!interval.IsClipped) return true;
 30376        return interval.Contains(n, d, ReadOnlySpan<ulong>.Empty);
 377    }
 378
 379    private static int GetScaledAxisIntervalEndpoint(ContactQuadratic n, ContactQuadratic d, in RationalSegmentInterval 
 380    {
 72381        Span<ulong> values = stackalloc ulong[4 * ScaledAxisWords]; Span<int> signs = stackalloc int[4];
 72382        var boundN = ScaledAxisField(values, signs, 0); var boundD = ScaledAxisField(values, signs, 1);
 72383        boundN.Set(interval.WideLowerNumerator); boundD.Set(interval.WideLowerDenominator);
 49384        if (ContactQuadratic.CompareRatios(n, d, ReadOnlySpan<ulong>.Empty, boundN, boundD, ReadOnlySpan<ulong>.Empty) =
 46385        boundN.Set(interval.WideUpperNumerator); boundD.Set(interval.WideUpperDenominator);
 23386        return ContactQuadratic.CompareRatios(n, d, ReadOnlySpan<ulong>.Empty, boundN, boundD, ReadOnlySpan<ulong>.Empty
 387    }
 388
 389    private static void SetScaledAxisMediant(Signed832 ln, Signed832 ld, Signed832 un, Signed832 ud,
 390        ContactQuadratic n, ContactQuadratic d)
 391    {
 14392        Span<ulong> values = stackalloc ulong[8 * ScaledAxisWords]; Span<int> signs = stackalloc int[8];
 14393        var leftN = ScaledAxisField(values, signs, 0); var leftD = ScaledAxisField(values, signs, 1);
 14394        var rightN = ScaledAxisField(values, signs, 2); var rightD = ScaledAxisField(values, signs, 3);
 28395        leftN.Set(ln); leftD.Set(ld); rightN.Set(un); rightD.Set(ud);
 14396        NormalizeScaledAxisRatio(leftN, leftD); NormalizeScaledAxisRatio(rightN, rightD);
 28397        leftN.CopyTo(n); n.Add(rightN); leftD.CopyTo(d); d.Add(rightD);
 7398    }
 399
 400    private static void NormalizeScaledAxisRatio(ContactQuadratic n, ContactQuadratic d)
 401    {
 56402        int words = n.FieldWords;
 56403        Span<ulong> values = stackalloc ulong[4 * words];
 112404        Span<ulong> gcd = values[..words], copy = values.Slice(words, words),
 112405            quotient = values.Slice(2 * words, words), remainder = values.Slice(3 * words, words);
 56406        Span<ulong> scratch = stackalloc ulong[2 * words + 1];
 112407        n.Rational.CopyTo(gcd); d.Rational.CopyTo(copy);
 56408        WideArithmetic.GetMagnitudeGreatestCommonDivisor(gcd, copy, scratch);
 112409        WideArithmetic.DivideMagnitudes(n.Rational, gcd, quotient, remainder, scratch); quotient.CopyTo(n.Rational);
 112410        WideArithmetic.DivideMagnitudes(d.Rational, gcd, quotient, remainder, scratch); quotient.CopyTo(d.Rational);
 56411    }
 412    private static void GetScaledAxisPrimitiveSeparation(scoped in ScaledAxisTriangleCandidate candidate, Span<Signed832
 413    {
 41414        int words = ScaledAxisWords;
 82415        Span<ulong> values = stackalloc ulong[8 * words]; Span<int> signs = stackalloc int[6];
 82416        Span<ulong> gcd = values.Slice(6 * words, words), copy = values.Slice(7 * words, words);
 82417        Span<ulong> scratch = stackalloc ulong[2 * words + 1], remainder = stackalloc ulong[words];
 41418        gcd.Clear();
 328419        for (int axis = 0; axis < 3; axis++)
 420        {
 123421            var component = ContactQuadratic.At(values, signs, axis, words);
 246422            candidate.At(axis + 3).CopyTo(component); component.Add(candidate.At(axis), -1);
 123423            if (component.Signs[0] == 0) continue;
 94424            if (WideArithmetic.GetActiveMagnitudeLength(gcd) == 0) component.Rational.CopyTo(gcd);
 60425            else { component.Rational.CopyTo(copy); WideArithmetic.GetMagnitudeGreatestCommonDivisor(gcd, copy, scratch)
 426        }
 59427        if (WideArithmetic.GetActiveMagnitudeLength(gcd) == 0) { result.Clear(); return; }
 252428        for (int axis = 0; axis < 3; axis++)
 429        {
 95430            var component = ContactQuadratic.At(values, signs, axis, words);
 95431            WideArithmetic.DivideMagnitudes(component.Rational, gcd, copy, remainder, scratch);
 432            // Face directions are the exact scaled cofactor; skew edges use
 433            // their cross product. Endpoint/edge residuals have degree three
 434            // in the bounded geometry and require fewer than 800 bits after
 435            // their common rational parameter factors have been removed.
 95436            if (WideArithmetic.GetActiveMagnitudeLength(copy) > 13 || (copy[12] >> 63) != 0)
 1437                throw new OverflowException("The reduced finite-axis separation must fit Signed832.");
 94438            Signed832 value = new(copy[12], copy[11], copy[10], copy[9], copy[8], copy[7], copy[6], copy[5], copy[4], co
 94439            result[axis] = component.Signs[0] < 0 ? WideArithmetic.SubtractSigned832(default, value) : value;
 440        }
 31441    }
 442}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.ScaledAxisTriangleCandidate.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.ScaledAxisTriangleCandidate.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>ScaledAxisTriangleCandidate declaration for WideFiniteAxisIntersection.</content>
 13internal static partial class WideFiniteAxisIntersection
 14{
 15    internal ref struct ScaledAxisTriangleCandidate
 16    {
 17        internal const int StorageWords = 18 * ScaledAxisWords, SignCount = 18;
 2518        internal ContactQuadratic TriangleCoordinate(int axis) => At(axis);
 1319        internal ContactQuadratic AxisCoordinate(int axis) => At(axis + 3);
 3020        internal ContactQuadratic Denominator => At(6);
 21        private readonly Span<ulong> _values;
 22        private readonly Span<int> _signs;
 23        internal bool HasValue;
 24        internal int EdgeMask, Endpoint;
 25        internal ScaledAxisTriangleCandidate(Span<ulong> values, Span<int> signs)
 74526        { _values = values; _signs = signs; HasValue = false; EdgeMask = Endpoint = 0; }
 1473127        internal ContactQuadratic At(int index) => ContactQuadratic.At(_values, _signs, index, ScaledAxisWords);
 28        internal void Keep(scoped ScaledAxisTriangleCandidate candidate)
 29        {
 46430            if (HasValue && ContactQuadratic.CompareRatios(candidate.At(7), candidate.At(8), ReadOnlySpan<ulong>.Empty,
 85931                At(7), At(8), ReadOnlySpan<ulong>.Empty) >= 0) return;
 13832            candidate._values.CopyTo(_values); candidate._signs.CopyTo(_signs);
 20733            HasValue = true; EdgeMask = candidate.EdgeMask; Endpoint = candidate.Endpoint;
 6934        }
 35        internal void Complete()
 36        {
 92837            Span<ulong> values = stackalloc ulong[4 * ScaledAxisWords]; Span<int> signs = stackalloc int[4];
 46438            var delta = ContactQuadratic.At(values, signs, 0, ScaledAxisWords);
 46439            var square = ContactQuadratic.At(values, signs, 1, ScaledAxisWords);
 46440            At(7).Clear();
 371241            for (int axis = 0; axis < 3; axis++)
 42            {
 278443                At(axis + 3).CopyTo(delta); delta.Add(At(axis), -1);
 278444                ContactQuadratic.Multiply(delta, delta, ReadOnlySpan<ulong>.Empty, square); At(7).Add(square);
 45            }
 46446            ContactQuadratic.Multiply(At(6), At(6), ReadOnlySpan<ulong>.Empty, At(8));
 46447            HasValue = true;
 46448        }
 49    }
 50}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.StrictCirclePolynomial.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.StrictCirclePolynomial.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>
 13/// Classifies a rational circle against a strict quadratic solid and axial
 14/// strip. Algebraic arc endpoints remain exact; no root is rounded to Fixed64.
 15/// </content>
 16internal static partial class WideFiniteAxisIntersection
 17{
 18    // Circle coefficients are below 2^540 and strip coefficients below 2^204.
 19    // The shared Sturm transients need <7*540+32 bits. Homogeneous evaluation
 20    // at a quadratic endpoint adds at most 4*204+8 bits, below 96 words.
 21    private const int StrictCircleWords = 96;
 22
 23    private readonly struct StrictCircleBound
 24    {
 25        internal readonly Signed320 Rational;
 26        internal readonly Signed320 Denominator;
 27        internal readonly Signed576 Radicand;
 28        internal readonly int RadicalSign;
 29        internal readonly int Infinity;
 30
 31        internal StrictCircleBound(int infinity)
 32        {
 681633            Rational = default;
 681634            Denominator = default;
 681635            Radicand = default;
 681636            RadicalSign = 0;
 681637            Infinity = infinity;
 681638        }
 39
 40        internal StrictCircleBound(Signed320 rational, Signed320 denominator,
 41            Signed576 radicand = default, int radicalSign = 0)
 42        {
 413243            bool negate = denominator.Sign < 0;
 413244            Rational = negate ? WideArithmetic.SubtractSigned320(default, rational) : rational;
 413245            Denominator = negate ? WideArithmetic.SubtractSigned320(default, denominator) : denominator;
 413246            Radicand = radicand;
 413247            RadicalSign = radicand.IsZero ? 0 : negate ? -radicalSign : radicalSign;
 413248            Infinity = 0;
 413249        }
 50    }
 51
 52    /// <summary>
 53    /// Tests whether a real projective circle parameter satisfies radial &lt; 0,
 54    /// lower &gt; 0, and upper &gt; 0 simultaneously. Coefficients are ascending.
 55    /// The caller separately tests the omitted projective point at infinity.
 56    /// </summary>
 57    internal static bool HasStrictCirclePolynomialPoint(
 58        ReadOnlySpan<Signed832> radial,
 59        ReadOnlySpan<Signed320> lower,
 60        ReadOnlySpan<Signed320> upper)
 61    {
 340862        Span<StrictCircleBound> bounds = stackalloc StrictCircleBound[6];
 340863        bounds[0] = new StrictCircleBound(-1);
 340864        int boundCount = 1;
 340865        AddStrictCircleBounds(lower, bounds, ref boundCount);
 340866        AddStrictCircleBounds(upper, bounds, ref boundCount);
 340867        bounds[boundCount++] = new StrictCircleBound(1);
 68
 340869        Span<ulong> sequence = stackalloc ulong[25 * StrictCircleWords];
 340870        Span<sbyte> signs = stackalloc sbyte[25];
 340871        Span<int> degrees = stackalloc int[5];
 340872        sequence.Clear();
 340873        signs.Clear();
 340874        degrees.Clear();
 340875        int degree = -1;
 4089676        for (int index = 0; index < 5; index++)
 77        {
 1704078            WideArithmetic.GetMagnitude(radial[index], StrictCircleCoefficient(sequence, 0, index));
 1704079            signs[index] = (sbyte)radial[index].Sign;
 1704080            if (signs[index] != 0)
 1527081                degree = index;
 82        }
 340883        if (degree < 0)
 2184            return false;
 85        // The shared builder normalizes only positive-degree polynomials.
 338786        int originalLeadingSign = degree > 0 ? signs[degree] : 1;
 338787        degrees[0] = degree;
 338788        int sequenceCount = BuildFiniteAxisPolynomialSturmSequence(sequence, signs, degrees);
 89
 2007890        for (int index = 0; index + 1 < boundCount; index++)
 91        {
 697892            StrictCircleBound left = bounds[index];
 697893            StrictCircleBound right = bounds[index + 1];
 697894            if (GetStrictCircleQuadraticRightSign(lower, left) <= 0
 697895                || GetStrictCircleQuadraticRightSign(upper, left) <= 0)
 96            {
 97                continue;
 98            }
 274499            if (GetStrictCirclePolynomialRightSign(sequence, signs, degree, left)
 2744100                    * originalLeadingSign < 0)
 101            {
 178102                return true;
 103            }
 2566104            if (CountStrictCircleOddRoots(sequence, signs, degrees, sequenceCount, left, right) > 0)
 148105                return true;
 106        }
 3061107        return false;
 108    }
 109
 110    private static void AddStrictCircleBounds(ReadOnlySpan<Signed320> polynomial,
 111        Span<StrictCircleBound> bounds, ref int count)
 112    {
 6816113        Signed320 constant = polynomial[0];
 6816114        Signed320 linear = polynomial[1];
 6816115        Signed320 quadratic = polynomial[2];
 6816116        if (quadratic.IsZero)
 117        {
 263118            if (!linear.IsZero)
 18119                InsertStrictCircleBound(new StrictCircleBound(
 18120                    WideArithmetic.SubtractSigned320(default, constant), linear), bounds, ref count);
 263121            return;
 122        }
 6553123        Signed576 discriminant = WideArithmetic.SubtractSigned576(
 6553124            WideArithmetic.MultiplySigned320(linear, linear),
 6553125            WideArithmetic.MultiplySigned576(WideArithmetic.MultiplySigned320(quadratic, constant), 4L));
 6553126        if (discriminant.Sign < 0)
 4391127            return;
 2162128        Signed320 numerator = WideArithmetic.SubtractSigned320(default, linear);
 2162129        Signed320 denominator = WideArithmetic.AddSigned320(quadratic, quadratic);
 2162130        InsertStrictCircleBound(new StrictCircleBound(numerator, denominator, discriminant, -1), bounds, ref count);
 2162131        if (!discriminant.IsZero)
 1952132            InsertStrictCircleBound(new StrictCircleBound(numerator, denominator, discriminant, 1), bounds, ref count);
 2162133    }
 134
 135    private static void InsertStrictCircleBound(StrictCircleBound bound,
 136        Span<StrictCircleBound> bounds, ref int count)
 137    {
 4132138        int index = count;
 5871139        while (index > 1)
 140        {
 3206141            int comparison = CompareStrictCircleBounds(bound, bounds[index - 1]);
 3206142            if (comparison == 0)
 213143                return;
 2993144            if (comparison > 0)
 145                break;
 1739146            index--;
 147        }
 148        // Detect an existing root before moving anything: otherwise an early
 149        // duplicate return can discard a later bound from the counted prefix.
 10892150        for (int shift = count; shift > index; shift--)
 1527151            bounds[shift] = bounds[shift - 1];
 3919152        bounds[index] = bound;
 3919153        count++;
 3919154    }
 155
 156    private static int CompareStrictCircleBounds(StrictCircleBound left, StrictCircleBound right)
 157    {
 3206158        Signed576 rational = WideArithmetic.SubtractSigned576(
 3206159            WideArithmetic.MultiplySigned320(left.Rational, right.Denominator),
 3206160            WideArithmetic.MultiplySigned320(right.Rational, left.Denominator));
 3206161        Signed832 rationalSquared = WideArithmetic.MultiplySigned576ToSigned832(rational, rational);
 3206162        Signed832 leftRadical = WideArithmetic.MultiplySigned576ToSigned832(
 3206163            WideArithmetic.MultiplySigned320(right.Denominator, right.Denominator), left.Radicand);
 3206164        Signed832 rightRadical = WideArithmetic.MultiplySigned576ToSigned832(
 3206165            WideArithmetic.MultiplySigned320(left.Denominator, left.Denominator), right.Radicand);
 3206166        Span<ulong> radicands = stackalloc ulong[39];
 3206167        WideArithmetic.GetMagnitude(rationalSquared, radicands[..13]);
 3206168        WideArithmetic.GetMagnitude(leftRadical, radicands.Slice(13, 13));
 3206169        WideArithmetic.GetMagnitude(rightRadical, radicands.Slice(26, 13));
 3206170        Span<int> signs = stackalloc int[3] { rational.Sign, left.RadicalSign, -right.RadicalSign };
 3206171        return WideArithmetic.GetLinearRadicalSumSign(radicands, 13, signs);
 172    }
 173
 174    private static int GetStrictCircleQuadraticRightSign(ReadOnlySpan<Signed320> polynomial,
 175        StrictCircleBound bound)
 176    {
 11780177        Span<ulong> coefficients = stackalloc ulong[25 * StrictCircleWords];
 11780178        Span<sbyte> signs = stackalloc sbyte[25];
 11780179        coefficients.Clear();
 11780180        signs.Clear();
 11780181        int degree = -1;
 94240182        for (int index = 0; index < 3; index++)
 183        {
 35340184            WideArithmetic.GetMagnitude(Signed576.ExtendValue(polynomial[index]),
 35340185                StrictCircleCoefficient(coefficients, 0, index));
 35340186            signs[index] = (sbyte)polynomial[index].Sign;
 35340187            if (signs[index] != 0)
 23245188                degree = index;
 189        }
 11780190        return degree < 0 ? 0 : GetStrictCirclePolynomialRightSign(coefficients, signs, degree, bound);
 191    }
 192
 193    private static int GetStrictCirclePolynomialRightSign(ReadOnlySpan<ulong> coefficients,
 194        ReadOnlySpan<sbyte> signs, int degree, StrictCircleBound bound) =>
 14514195        GetStrictCirclePolynomialLimitSign(coefficients, signs, 0, degree, bound, 1);
 196
 197    private static int GetStrictCirclePolynomialLimitSign(ReadOnlySpan<ulong> coefficients,
 198        ReadOnlySpan<sbyte> signs, int polynomial, int degree, StrictCircleBound bound, int direction)
 199    {
 37796200        if (bound.Infinity != 0)
 13800201            return signs[polynomial * 5 + degree] * (bound.Infinity < 0 && (degree & 1) != 0 ? -1 : 1);
 202        // The first nonzero derivative determines the right-hand sign at an
 203        // endpoint root; excluded tangent roots never manufacture penetration.
 56426204        for (int derivative = 0; derivative < degree; derivative++)
 205        {
 24813206            int sign = EvaluateStrictCirclePolynomial(coefficients, signs, polynomial, degree, bound, derivative);
 24813207            if (sign != 0)
 20596208                return direction < 0 && (derivative & 1) != 0 ? -sign : sign;
 209        }
 210        // Every caller supplies a trimmed, nonzero polynomial. Its highest
 211        // derivative has the leading coefficient's sign and cannot vanish.
 3400212        int leadingSign = signs[polynomial * 5 + degree];
 3400213        return direction < 0 && (degree & 1) != 0 ? -leadingSign : leadingSign;
 214    }
 215
 216    private static int CountStrictCircleOddRoots(ReadOnlySpan<ulong> coefficients,
 217        ReadOnlySpan<sbyte> signs, ReadOnlySpan<int> degrees, int sequenceCount,
 218        StrictCircleBound left, StrictCircleBound right)
 219    {
 2566220        int degree = degrees[0];
 2566221        if (degree == 0)
 9222            return 0;
 2557223        int distinct = GetStrictCircleVariations(coefficients, signs, degrees, sequenceCount, left, 1)
 2557224            - GetStrictCircleVariations(coefficients, signs, degrees, sequenceCount, right, -1);
 2557225        int last = sequenceCount - 1;
 2557226        int gcdDegree = degrees[last];
 2557227        if (gcdDegree == 0 || distinct == 0)
 2495228            return distinct;
 62229        if (gcdDegree == 1)
 230        {
 231            // This gcd is linear. At infinity its sign is its leading sign
 232            // times the endpoint direction; finite endpoint zeros stay zero.
 25233            int leftSign = left.Infinity != 0 ? signs[last * 5 + 1] * left.Infinity
 25234                : EvaluateStrictCirclePolynomial(coefficients, signs, last, 1, left);
 25235            int rightSign = right.Infinity != 0 ? signs[last * 5 + 1] * right.Infinity
 25236                : EvaluateStrictCirclePolynomial(coefficients, signs, last, 1, right);
 25237            return distinct - (leftSign != 0 && rightSign != 0 && leftSign != rightSign ? 1 : 0);
 238        }
 37239        if (gcdDegree == 3)
 7240            return 0; // A quartic with a cubic gcd is a fourth power.
 30241        if (degree == 3)
 7242            return distinct; // A cubic with a quadratic gcd is a third power.
 243
 244        // A quartic quadratic gcd is either two double roots (no sign changes)
 245        // or one triple root plus one simple root (both change sign).
 23246        Span<ulong> squared = stackalloc ulong[2 * StrictCircleWords];
 23247        Span<ulong> product = stackalloc ulong[2 * StrictCircleWords];
 23248        Span<ulong> scaled = stackalloc ulong[2 * StrictCircleWords];
 23249        ReadOnlySpan<ulong> b = StrictCircleCoefficient(coefficients, last, 1);
 23250        MultiplyRoundedCylinderWide(b, b, squared);
 23251        MultiplyRoundedCylinderWide(StrictCircleCoefficient(coefficients, last, 0),
 23252            StrictCircleCoefficient(coefficients, last, 2), product);
 23253        MultiplyRoundedCylinderWideByWord(product, 4UL, scaled);
 23254        bool nonnegativeProduct = signs[last * 5] == 0 || signs[last * 5] == signs[last * 5 + 2];
 23255        return nonnegativeProduct && WideArithmetic.CompareMagnitudeEqualLength(squared, scaled) == 0 ? distinct : 0;
 256    }
 257
 258    private static int GetStrictCircleVariations(ReadOnlySpan<ulong> coefficients,
 259        ReadOnlySpan<sbyte> signs, ReadOnlySpan<int> degrees, int count, StrictCircleBound bound, int direction)
 260    {
 5114261        int previous = 0;
 5114262        int variations = 0;
 56792263        for (int index = 0; index < count; index++)
 264        {
 23282265            int sign = GetStrictCirclePolynomialLimitSign(coefficients, signs, index, degrees[index], bound, direction);
 23282266            if (previous != 0 && previous != sign)
 9128267                variations++;
 23282268            previous = sign;
 269        }
 5114270        return variations;
 271    }
 272
 273    private static int EvaluateStrictCirclePolynomial(ReadOnlySpan<ulong> coefficients,
 274        ReadOnlySpan<sbyte> signs, int polynomial, int degree, StrictCircleBound bound,
 275        int derivative = 0)
 276    {
 277        // Both callers handle infinity before entering this finite evaluator.
 24836278        Span<ulong> rational = stackalloc ulong[StrictCircleWords];
 24836279        Span<ulong> radical = stackalloc ulong[StrictCircleWords];
 24836280        Span<ulong> denominatorPower = stackalloc ulong[StrictCircleWords];
 24836281        Span<ulong> first = stackalloc ulong[StrictCircleWords];
 24836282        Span<ulong> second = stackalloc ulong[StrictCircleWords];
 24836283        Span<ulong> third = stackalloc ulong[StrictCircleWords];
 24836284        Span<ulong> nextRational = stackalloc ulong[StrictCircleWords];
 24836285        Span<ulong> nextRadical = stackalloc ulong[StrictCircleWords];
 24836286        Span<ulong> numerator = stackalloc ulong[9];
 24836287        Span<ulong> denominator = stackalloc ulong[9];
 24836288        Span<ulong> radicand = stackalloc ulong[9];
 24836289        WideArithmetic.GetMagnitude(Signed576.ExtendValue(bound.Rational), numerator);
 24836290        WideArithmetic.GetMagnitude(Signed576.ExtendValue(bound.Denominator), denominator);
 24836291        WideArithmetic.GetMagnitude(bound.Radicand, radicand);
 24836292        rational.Clear();
 24836293        radical.Clear();
 24836294        denominatorPower.Clear();
 24836295        denominatorPower[0] = 1UL;
 24836296        sbyte rationalSign = 0;
 24836297        sbyte radicalSign = 0;
 211054298        for (int index = degree; index >= derivative; index--)
 299        {
 80691300            MultiplyRoundedCylinderWide(rational, numerator, first);
 80691301            MultiplyRoundedCylinderWide(radical, radicand, second);
 80691302            AddRoundedCylinderSigned(first, (sbyte)(rationalSign * bound.Rational.Sign),
 80691303                second, (sbyte)(radicalSign * bound.RadicalSign), nextRational, out sbyte nextRationalSign);
 80691304            MultiplyRoundedCylinderWide(radical, numerator, first);
 80691305            AddRoundedCylinderSigned(first, (sbyte)(radicalSign * bound.Rational.Sign),
 80691306                rational, (sbyte)(rationalSign * bound.RadicalSign), nextRadical, out sbyte nextRadicalSign);
 80691307            if (index != degree)
 308            {
 55855309                MultiplyRoundedCylinderWide(denominatorPower, denominator, first);
 55855310                first.CopyTo(denominatorPower);
 311            }
 80691312            ulong multiplier = 1UL;
 178140313            for (int order = 0; order < derivative; order++)
 8379314                multiplier *= (ulong)(index - order);
 80691315            MultiplyRoundedCylinderWide(StrictCircleCoefficient(coefficients, polynomial, index),
 80691316                denominatorPower, first);
 80691317            MultiplyRoundedCylinderWideByWord(first, multiplier, second);
 80691318            AddRoundedCylinderSigned(nextRational, nextRationalSign, second,
 80691319                signs[polynomial * 5 + index], third, out rationalSign);
 80691320            third.CopyTo(rational);
 80691321            nextRadical.CopyTo(radical);
 80691322            radicalSign = nextRadicalSign;
 323        }
 24836324        if (rationalSign == 0)
 7581325            return radicalSign;
 17255326        if (radicalSign == 0 || rationalSign == radicalSign)
 10093327            return rationalSign;
 7162328        Span<ulong> rationalSquared = stackalloc ulong[2 * StrictCircleWords + 9];
 7162329        Span<ulong> radicalSquared = stackalloc ulong[2 * StrictCircleWords + 9];
 7162330        Span<ulong> scaledRadicalSquared = stackalloc ulong[2 * StrictCircleWords + 9];
 7162331        MultiplyRoundedCylinderWide(rational, rational, rationalSquared);
 7162332        MultiplyRoundedCylinderWide(radical, radical, radicalSquared);
 7162333        MultiplyRoundedCylinderWide(radicalSquared, radicand, scaledRadicalSquared);
 7162334        int comparison = WideArithmetic.CompareMagnitudeEqualLength(rationalSquared, scaledRadicalSquared);
 7162335        return comparison == 0 ? 0 : comparison > 0 ? rationalSign : radicalSign;
 336    }
 337
 338    private static Span<ulong> StrictCircleCoefficient(Span<ulong> values, int polynomial, int coefficient) =>
 52380339        values.Slice((polynomial * 5 + coefficient) * StrictCircleWords, StrictCircleWords);
 340
 341    private static ReadOnlySpan<ulong> StrictCircleCoefficient(ReadOnlySpan<ulong> values, int polynomial, int coefficie
 80760342        values.Slice((polynomial * 5 + coefficient) * StrictCircleWords, StrictCircleWords);
 343}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.StrictPolynomial.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.StrictPolynomial.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>
 13/// Bounded exact polynomial signs for centered finite-shape overlap. No roots
 14/// are materialized or rounded: even-multiplicity tangencies do not penetrate.
 15/// </content>
 16internal static partial class WideFiniteAxisIntersection
 17{
 18    private const int StrictPolynomialInputWords = 16;
 19    private const int StrictPolynomialWorkWords = 128;
 20
 21    /// <summary>
 22    /// Tests negativity somewhere on [0,1]. The five low-to-high coefficients
 23    /// each have sixteen magnitude words and a separate sign. Inputs are
 24    /// bounded by 1024 bits; the degree is at most four.
 25    /// </summary>
 26    internal static bool HasNegativeFiniteAxisPolynomial(
 27        ReadOnlySpan<ulong> input, ReadOnlySpan<sbyte> inputSigns)
 28    {
 29        // A B-bit quartic's factorized Sturm terms use at most 6B+32 bits.
 30        // The exceptional cubic/linear pseudo-remainder uses at most 7B+32;
 31        // lower-degree Euclidean chains have no larger transient. B<=1024
 32        // therefore fits 8192 bits, including endpoint sums and gcd tests.
 64433        Span<ulong> coefficients = stackalloc ulong[25 * StrictPolynomialWorkWords];
 64434        Span<sbyte> signs = stackalloc sbyte[25];
 64435        Span<int> degrees = stackalloc int[5];
 64436        coefficients.Clear();
 64437        signs.Clear();
 64438        degrees.Clear();
 772839        for (int i = 0; i < 5; i++)
 40        {
 322041            input.Slice(i * StrictPolynomialInputWords, StrictPolynomialInputWords)
 322042                .CopyTo(GetRoundedCylinderCoefficient(coefficients, 0, i));
 322043            signs[i] = inputSigns[i];
 44        }
 64445        int degree = TrimRoundedCylinderPolynomial(signs, 0, 4);
 64446        if (degree < 0)
 147            return false;
 64348        degrees[0] = degree;
 64349        if (GetStrictPolynomialEndpointSign(coefficients, signs, 0, degree, atOne: false) < 0
 64350            || GetStrictPolynomialEndpointSign(coefficients, signs, 0, degree, atOne: true) < 0)
 31651            return true;
 32752        if (degree == 0)
 153            return false;
 54
 32655        int count = BuildFiniteAxisPolynomialSturmSequence(coefficients, signs, degrees);
 32656        int roots = GetStrictPolynomialVariations(coefficients, signs, degrees, count, false)
 32657            - GetStrictPolynomialVariations(coefficients, signs, degrees, count, true);
 32658        if (roots == 0)
 23759            return false;
 60
 8961        int gcdIndex = count - 1;
 8962        int gcdDegree = degrees[gcdIndex];
 8963        if (gcdDegree == 0)
 2164            return true;
 6865        if (gcdDegree == 1)
 66        {
 67            // The sole repeated root has multiplicity two. Remove it from
 68            // the distinct count only if it lies strictly inside (0,1).
 4069            int start = signs[GetRoundedCylinderCoefficientIndex(gcdIndex, 0)];
 4070            int end = GetStrictPolynomialValueAtOne(coefficients, signs, gcdIndex, 1, 0);
 4071            int repeatedInside = start != 0 && end != 0 && start != end ? 1 : 0;
 4072            return roots > repeatedInside;
 73        }
 2874        if (gcdDegree == 2)
 75        {
 76            // A cubic with a quadratic gcd is a cube: an interior root would
 77            // already give a negative endpoint sign. Only quartics reach here.
 78            // Quartic: either two double roots (never a crossing), or one
 79            // triple and one simple root (both are crossings).
 2480            Span<ulong> first = stackalloc ulong[StrictPolynomialWorkWords];
 2481            Span<ulong> second = stackalloc ulong[StrictPolynomialWorkWords];
 2482            Span<ulong> scaled = stackalloc ulong[StrictPolynomialWorkWords];
 2483            Span<ulong> result = stackalloc ulong[StrictPolynomialWorkWords];
 2484            ReadOnlySpan<ulong> b = GetRoundedCylinderCoefficient(coefficients, gcdIndex, 1);
 2485            MultiplyRoundedCylinderWide(b, b, first);
 2486            MultiplyRoundedCylinderWide(
 2487                GetRoundedCylinderCoefficient(coefficients, gcdIndex, 2),
 2488                GetRoundedCylinderCoefficient(coefficients, gcdIndex, 0), second);
 2489            MultiplyRoundedCylinderWideByWord(second, 4, scaled);
 2490            SubtractRoundedCylinderSigned(first,
 2491                signs[GetRoundedCylinderCoefficientIndex(gcdIndex, 1)] == 0 ? (sbyte)0 : (sbyte)1,
 2492                scaled, (sbyte)(signs[GetRoundedCylinderCoefficientIndex(gcdIndex, 2)]
 2493                    * signs[GetRoundedCylinderCoefficientIndex(gcdIndex, 0)]), result, out sbyte sign);
 2494            return sign == 0;
 95        }
 496        return false; // A quartic with a cubic gcd is a fourth power.
 97    }
 98
 99    /// <summary>
 100    /// Builds an exact degree-at-most-four Sturm chain in five polynomial
 101    /// slots of five coefficients each. Word width is selected by the owning
 102    /// bounded geometry caller. Slot zero and degrees[0] contain the input;
 103    /// all remaining coefficient/sign slots must initially be zero.
 104    /// </summary>
 105    internal static int BuildFiniteAxisPolynomialSturmSequence(
 106        Span<ulong> coefficients, Span<sbyte> signs, Span<int> degrees)
 107    {
 4457108        int degree = degrees[0];
 4457109        if (degree <= 0)
 21110            return degree + 1;
 4436111        if (signs[degree] < 0)
 112        {
 5484113            for (int i = 0; i <= degree; i++)
 2254114                signs[i] = (sbyte)-signs[i];
 115        }
 43164116        for (int i = 1; i <= degree; i++)
 117        {
 17146118            MultiplyRoundedCylinderWideByWord(GetRoundedCylinderCoefficient(coefficients, 0, i),
 17146119                (ulong)i, GetRoundedCylinderCoefficient(coefficients, 1, i - 1));
 17146120            signs[GetRoundedCylinderCoefficientIndex(1, i - 1)] = signs[i];
 121        }
 4436122        degrees[1] = degree - 1;
 4436123        if (degree == 4)
 4113124            return BuildRoundedCylinderQuarticSturmSequence(coefficients, signs, degrees);
 323125        int count = 2;
 534126        while (degrees[count - 1] > 0)
 127        {
 332128            int remainder = BuildRoundedCylinderNegativeRemainder(coefficients, signs,
 332129                count - 2, degrees[count - 2], count - 1, degrees[count - 1], count);
 332130            if (remainder < 0)
 131                break;
 211132            degrees[count++] = remainder;
 133        }
 323134        return count;
 135    }
 136
 137    private static int GetStrictPolynomialVariations(Span<ulong> coefficients,
 138        Span<sbyte> signs, Span<int> degrees, int count, bool atOne)
 139    {
 652140        int previous = 0;
 652141        int variations = 0;
 7088142        for (int i = 0; i < count; i++)
 143        {
 2892144            int sign = GetStrictPolynomialEndpointSign(coefficients, signs, i, degrees[i], atOne);
 2892145            if (previous != 0 && previous != sign)
 1096146                variations++;
 2892147            previous = sign;
 148        }
 652149        return variations;
 150    }
 151
 152    // These are inward one-sided signs, so endpoint roots are excluded from
 153    // the Sturm count and a negative interval touching an endpoint is kept.
 154    private static int GetStrictPolynomialEndpointSign(Span<ulong> coefficients,
 155        Span<sbyte> signs, int polynomial, int degree, bool atOne)
 156    {
 157        // Every sequence member has a nonzero leading coefficient. If all
 158        // lower derivatives vanish, the degree-th derivative has that sign.
 9182159        for (int order = 0; order < degree; order++)
 160        {
 4121161            int sign = atOne
 4121162                ? GetStrictPolynomialValueAtOne(coefficients, signs, polynomial, degree, order)
 4121163                : signs[GetRoundedCylinderCoefficientIndex(polynomial, order)];
 4121164            if (sign != 0)
 3493165                return atOne && (order & 1) != 0 ? -sign : sign;
 166        }
 470167        int leading = signs[GetRoundedCylinderCoefficientIndex(polynomial, degree)];
 470168        return atOne && (degree & 1) != 0 ? -leading : leading;
 169    }
 170
 171    private static int GetStrictPolynomialValueAtOne(Span<ulong> coefficients,
 172        Span<sbyte> signs, int polynomial, int degree, int derivativeOrder)
 173    {
 1930174        int words = coefficients.Length / 25;
 1930175        Span<ulong> result = stackalloc ulong[words];
 1930176        Span<ulong> term = stackalloc ulong[words];
 1930177        Span<ulong> sum = stackalloc ulong[words];
 1930178        result.Clear();
 1930179        sbyte resultSign = 0;
 18660180        for (int i = derivativeOrder; i <= degree; i++)
 181        {
 7400182            ulong binomial = 1;
 16800183            for (int j = 1; j <= derivativeOrder; j++)
 1000184                binomial = binomial * (ulong)(i - j + 1) / (ulong)j;
 7400185            MultiplyRoundedCylinderWideByWord(GetRoundedCylinderCoefficient(coefficients, polynomial, i),
 7400186                binomial, term);
 7400187            AddRoundedCylinderSigned(result, resultSign, term,
 7400188                signs[GetRoundedCylinderCoefficientIndex(polynomial, i)], sum, out resultSign);
 7400189            sum.CopyTo(result);
 190        }
 1930191        return resultSign;
 192    }
 193}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.StrictQuadraticField.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.StrictQuadraticField.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>
 13/// Exact bounded quadratic-field endpoint and vertex signs for the offset
 14/// side of a finite cone. This does not isolate or approximate algebraic roots.
 15/// </content>
 16internal static partial class WideFiniteAxisIntersection
 17{
 18    private const int StrictFieldWords = 64;
 119    private static readonly Signed832 StrictOne = Signed832.ExtendValue(Signed192.Signed(1));
 20
 21    private readonly struct StrictFieldBound
 22    {
 23        internal readonly Signed832 Rational;
 24        internal readonly Signed832 Radical;
 25        internal readonly Signed832 Denominator;
 26
 27        internal StrictFieldBound(Signed832 rational, Signed832 radical, Signed832 denominator)
 28        {
 454829            bool negative = denominator.Sign < 0;
 454830            Rational = negative ? NegateStrict(rational) : rational;
 454831            Radical = negative ? NegateStrict(radical) : radical;
 454832            Denominator = negative ? NegateStrict(denominator) : denominator;
 454833        }
 34    }
 35
 36    private static Signed832 NegateStrict(Signed832 value) =>
 842937        WideArithmetic.SubtractSigned832(default, value);
 38
 39    private static bool GetStrictChordBounds(Signed320 y, Signed320 direction,
 40        Signed320 minimum, Signed320 maximum, Signed832 shiftCoefficient, Signed192 radicand,
 41        out StrictFieldBound lower, out StrictFieldBound upper)
 42    {
 80843        lower = new StrictFieldBound(default, default, StrictOne);
 80844        upper = new StrictFieldBound(StrictOne, default, StrictOne);
 80845        Signed832 k = Signed832.ExtendValue(radicand);
 80846        if (direction.IsZero)
 47        {
 37348            var point = new StrictFieldBound(Signed832.ExtendValue(Signed576.ExtendValue(y)), default, StrictOne);
 37349            var low = new StrictFieldBound(Signed832.ExtendValue(WideArithmetic.MultiplySigned320(minimum, radicand)),
 37350                shiftCoefficient, k);
 37351            var high = new StrictFieldBound(Signed832.ExtendValue(WideArithmetic.MultiplySigned320(maximum, radicand)),
 37352                shiftCoefficient, k);
 37353            return CompareStrictFieldBounds(point, low, k) > 0
 37354                && CompareStrictFieldBounds(point, high, k) < 0;
 55        }
 43556        Signed832 denominator = Signed832.ExtendValue(WideArithmetic.MultiplySigned320(direction, radicand));
 43557        var first = new StrictFieldBound(Signed832.ExtendValue(WideArithmetic.MultiplySigned320(
 43558            WideArithmetic.SubtractSigned320(minimum, y), radicand)), shiftCoefficient, denominator);
 43559        var second = new StrictFieldBound(Signed832.ExtendValue(WideArithmetic.MultiplySigned320(
 43560            WideArithmetic.SubtractSigned320(maximum, y), radicand)), shiftCoefficient, denominator);
 43561        StrictFieldBound lowParameter = direction.Sign > 0 ? first : second;
 43562        StrictFieldBound highParameter = direction.Sign > 0 ? second : first;
 43563        if (CompareStrictFieldBounds(lowParameter, lower, k) > 0)
 31264            lower = lowParameter;
 43565        if (CompareStrictFieldBounds(highParameter, upper, k) < 0)
 10766            upper = highParameter;
 43567        return CompareStrictFieldBounds(lower, upper, k) < 0;
 68    }
 69
 70    private static bool HasNegativeStrictFieldQuadratic(Signed832 a0, Signed832 a1, Signed832 a2,
 71        Signed832 b0, Signed832 b1, Signed192 radicand, StrictFieldBound lower, StrictFieldBound upper)
 72    {
 43073        Signed832 k = Signed832.ExtendValue(radicand);
 43074        if (GetStrictFieldQuadraticSign(a0, a1, a2, b0, b1, k, lower) < 0
 43075            || GetStrictFieldQuadraticSign(a0, a1, a2, b0, b1, k, upper) < 0)
 676            return true;
 42477        if (a2.Sign <= 0)
 2378            return false;
 40179        var vertex = new StrictFieldBound(NegateStrict(a1), NegateStrict(b1),
 40180            WideArithmetic.AddSigned832(a2, a2));
 40181        return CompareStrictFieldBounds(vertex, lower, k) > 0
 40182            && CompareStrictFieldBounds(vertex, upper, k) < 0
 40183            && GetStrictFieldQuadraticSign(a0, a1, a2, b0, b1, k, vertex) < 0;
 84    }
 85
 86    private static int CompareStrictFieldBounds(StrictFieldBound first, StrictFieldBound second, Signed832 k)
 87    {
 269688        Span<ulong> rational = stackalloc ulong[StrictFieldWords];
 269689        Span<ulong> radical = stackalloc ulong[StrictFieldWords];
 269690        rational.Clear();
 269691        radical.Clear();
 539292        sbyte rationalSign = 0, radicalSign = 0;
 269693        AccumulateStrictProduct(rational, ref rationalSign, first.Rational, second.Denominator, StrictOne, StrictOne);
 269694        AccumulateStrictProduct(rational, ref rationalSign, NegateStrict(second.Rational), first.Denominator, StrictOne,
 269695        AccumulateStrictProduct(radical, ref radicalSign, first.Radical, second.Denominator, StrictOne, StrictOne);
 269696        AccumulateStrictProduct(radical, ref radicalSign, NegateStrict(second.Radical), first.Denominator, StrictOne, St
 269697        return GetStrictFieldSign(rational, rationalSign, radical, radicalSign, k);
 98    }
 99
 100    private static int GetStrictFieldQuadraticSign(Signed832 a0, Signed832 a1, Signed832 a2,
 101        Signed832 b0, Signed832 b1, Signed832 k, StrictFieldBound parameter)
 102    {
 103        // For t=(n+m√k)/d, multiply by positive d². The rational and
 104        // radical coefficients below are exact, including cancellation.
 2937105        Signed832 n = parameter.Rational, m = parameter.Radical, d = parameter.Denominator;
 979106        Span<ulong> rational = stackalloc ulong[StrictFieldWords];
 979107        Span<ulong> radical = stackalloc ulong[StrictFieldWords];
 979108        rational.Clear();
 979109        radical.Clear();
 1958110        sbyte rationalSign = 0, radicalSign = 0;
 979111        AccumulateStrictProduct(rational, ref rationalSign, a2, n, n, StrictOne);
 979112        AccumulateStrictProduct(rational, ref rationalSign, a2, m, m, k);
 979113        AccumulateStrictProduct(rational, ref rationalSign, a1, n, d, StrictOne);
 979114        AccumulateStrictProduct(rational, ref rationalSign, a0, d, d, StrictOne);
 979115        AccumulateStrictProduct(rational, ref rationalSign, b1, m, k, d);
 979116        AccumulateStrictProduct(radical, ref radicalSign, WideArithmetic.AddSigned832(a2, a2), n, m, StrictOne);
 979117        AccumulateStrictProduct(radical, ref radicalSign, a1, m, d, StrictOne);
 979118        AccumulateStrictProduct(radical, ref radicalSign, b1, n, d, StrictOne);
 979119        AccumulateStrictProduct(radical, ref radicalSign, b0, d, d, StrictOne);
 979120        return GetStrictFieldSign(rational, rationalSign, radical, radicalSign, k);
 121    }
 122
 123    private static void AccumulateStrictProduct(Span<ulong> total, ref sbyte totalSign,
 124        Signed832 first, Signed832 second, Signed832 third, Signed832 fourth)
 125    {
 21892126        int sign = first.Sign * second.Sign * third.Sign * fourth.Sign;
 21892127        if (sign == 0)
 12894128            return;
 8998129        Span<Signed832> factors = stackalloc Signed832[4] { first, second, third, fourth };
 8998130        Span<ulong> factor = stackalloc ulong[StrictFieldWords];
 8998131        Span<ulong> product = stackalloc ulong[StrictFieldWords];
 8998132        Span<ulong> next = stackalloc ulong[StrictFieldWords];
 8998133        product.Clear();
 8998134        product[0] = 1;
 89980135        for (int i = 0; i < factors.Length; i++)
 136        {
 35992137            factor.Clear();
 35992138            WideArithmetic.GetMagnitude(factors[i], factor[..13]);
 35992139            MultiplyRoundedCylinderWide(product, factor, next);
 35992140            next.CopyTo(product);
 141        }
 8998142        AddRoundedCylinderSigned(total, totalSign, product, (sbyte)sign, next, out totalSign);
 8998143        next.CopyTo(total);
 8998144    }
 145
 146    private static int GetStrictFieldSign(ReadOnlySpan<ulong> rational, sbyte rationalSign,
 147        ReadOnlySpan<ulong> radical, sbyte radicalSign, Signed832 radicand)
 148    {
 149        // All callers supply a positive radicand: one for rational queries,
 150        // h^2+r^2 for capsules, or the positive generator discriminant for pairs.
 5926151        if (rationalSign == 0)
 36152            return radicalSign;
 5890153        if (radicalSign == 0 || radicalSign == rationalSign)
 5106154            return rationalSign;
 155        // For the capsule caller, dual-rigid rational coefficients/bound components are
 156        // below 2^604; their radical coefficients are below 2^540 and
 157        // k<2^127. Evaluating the cone quadratic needs fewer than 1820 bits;
 158        // both square comparisons (including k<2^127) fit 4096 bits. The
 159        // cylinder/cone pair caller proves its separate bound at its call site.
 784160        Span<ulong> first = stackalloc ulong[StrictFieldWords];
 784161        Span<ulong> second = stackalloc ulong[StrictFieldWords];
 784162        Span<ulong> k = stackalloc ulong[StrictFieldWords];
 784163        Span<ulong> product = stackalloc ulong[StrictFieldWords];
 784164        k.Clear();
 784165        WideArithmetic.GetMagnitude(radicand, k[..13]);
 784166        MultiplyRoundedCylinderWide(rational, rational, first);
 784167        MultiplyRoundedCylinderWide(radical, radical, second);
 784168        MultiplyRoundedCylinderWide(second, k, product);
 784169        int comparison = WideArithmetic.CompareMagnitudeEqualLength(first, product);
 784170        return comparison == 0 ? 0 : comparison > 0 ? rationalSign : radicalSign;
 171    }
 172}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.ValueRoot.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.ValueRoot.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>
 13/// All-root value isolation, separate from the optimized largest-root quartic
 14/// contract. Signed raw subresultants preserve exact Sturm variations.
 15/// </content>
 16internal static partial class WideFiniteAxisIntersection
 17{
 18    private static int GetFiniteValueSturmArenaBits(int coefficientBits, int degree)
 19    {
 20        // C=B+64. For degree <=4, Brown's retained minors use <=39C;
 21        // graded pseudo-remainders plus two products use <=50C and exact
 22        // division <=52C. The existing eight scalar slots each use <=28C,
 23        // hence construction fits (39+52+8*28)C=315C. Under the unchanged
 24        // isolation shift bound, retained rows plus dyadic evaluation need
 25        // <=170C+318 bits. Callers add the existing 512-word carry padding.
 26        // Deeper refinement separately takes its target evaluation maximum.
 989527        return (degree <= 4 ? 315 : 880) * (coefficientBits + 64);
 28    }
 29
 30    internal static int GetFiniteValueRootCellWords(ReadOnlySpan<ulong> coefficients,
 31        ReadOnlySpan<sbyte> signs) =>
 3047932        (16 * (GetFiniteRootCoefficientBits(coefficients, signs.Length) + 11) + 255) / 64;
 33
 34    internal static bool TryGetFiniteValueRoot(ReadOnlySpan<ulong> coefficients,
 35        ReadOnlySpan<sbyte> signs, int ordinal, Span<ulong> cell, out FiniteAxisValueRoot root)
 36    {
 385937        root = default;
 385938        int degree = signs.Length - 1;
 925939        while (degree >= 0 && signs[degree] == 0)
 540040            degree--;
 385941        if (degree <= 0)
 1942            return false;
 384043        int words = coefficients.Length / signs.Length;
 384044        coefficients = coefficients[..((degree + 1) * words)];
 384045        signs = signs[..(degree + 1)];
 384046        int bits = GetFiniteRootCoefficientBits(coefficients, signs.Length);
 47        // Graded surviving-coefficient slots avoid the uniform-width
 48        // pseudo-remainder blow-up. Construction scratch is released before
 49        // endpoint evaluation; neither phase holds another chain.
 384050        Span<ulong> arena = stackalloc ulong[(GetFiniteValueSturmArenaBits(bits, degree) + 63) / 64 + 512];
 384051        Span<int> offsets = stackalloc int[9];
 384052        Span<int> widths = stackalloc int[9];
 384053        Span<int> degrees = stackalloc int[9];
 384054        Span<sbyte> sturmSigns = stackalloc sbyte[81];
 384055        int count = BuildFiniteValueSturm(coefficients, signs, arena,
 384056            offsets, widths, degrees, sturmSigns, out int retainedWords);
 384057        int rootCount = GetFiniteValueRootCount(cell, arena[..retainedWords], offsets, widths,
 384058            degrees, sturmSigns, count, arena[retainedWords..], out int lowerVariations);
 384059        if (ordinal < 0 || ordinal >= rootCount)
 91460            return false;
 292661        root = IsolateFiniteValueRoot(coefficients, signs, ordinal, rootCount, lowerVariations, cell, int.MaxValue,
 292662            arena[..retainedWords], offsets, widths, degrees, sturmSigns, count,
 292663            arena[retainedWords..], out _);
 292664        return true;
 65    }
 66
 67    internal static FiniteAxisValueRoots GetFiniteValueRoots(ReadOnlySpan<ulong> coefficients,
 68        ReadOnlySpan<sbyte> signs, Span<ulong> cells, Span<int> shifts)
 69    {
 2881770        bool compact = TryGetFiniteValueRoots(coefficients, signs, cells, shifts,
 2881771            out int count, out int rationalMask, out bool repeated);
 2881772        return new FiniteAxisValueRoots(cells, shifts, count, rationalMask, compact, repeated);
 73    }
 74
 75    /// <summary>
 76    /// Isolates all distinct roots in (0,1] with one Sturm construction.
 77    /// Cells supplies eight equal, nonempty numerator slots and shifts supplies
 78    /// eight entries. Bit ordinal of rationalMask identifies singleton cells.
 79    /// Count is the total number of distinct roots on both outcomes. Returns
 80    /// false when compact storage is insufficient; discard all cell metadata
 81    /// on false and materialize valid ordinals with full-size storage.
 82    /// The hasRepeatedRoots flag concerns the whole polynomial, including roots
 83    /// outside (0,1], and stays valid on capacity failure. Zero and constant
 84    /// polynomials report no repeated roots.
 85    /// Inputs must not overlap outputs. Storage affects reuse, not semantics.
 86    /// </summary>
 87    internal static bool TryGetFiniteValueRoots(ReadOnlySpan<ulong> coefficients,
 88        ReadOnlySpan<sbyte> signs, Span<ulong> cells, Span<int> shifts,
 89        out int count, out int rationalMask, out bool hasRepeatedRoots)
 90    {
 2886891        count = rationalMask = 0;
 2886892        hasRepeatedRoots = false;
 2886893        cells.Clear();
 2886894        shifts.Clear();
 2886895        int degree = signs.Length - 1;
 3153096        while (degree >= 0 && signs[degree] == 0)
 266297            degree--;
 2886898        if (degree <= 0)
 62999            return true;
 28239100        int inputWords = coefficients.Length / signs.Length;
 28239101        coefficients = coefficients[..((degree + 1) * inputWords)];
 28239102        signs = signs[..(degree + 1)];
 28239103        int cellWords = cells.Length / 8;
 104        System.Diagnostics.Debug.Assert(cellWords > 0 && cells.Length == 8 * cellWords && shifts.Length == 8);
 28239105        int bits = GetFiniteRootCoefficientBits(coefficients, signs.Length);
 28239106        if (TryGetFiniteValueRootsBernstein(coefficients, signs, cells, shifts, out count))
 24252107            return true;
 108        // The same arena as single-root isolation; the only additional live
 109        // caller state is the compact output (544 bytes for eight-word cells).
 3987110        Span<ulong> arena = stackalloc ulong[(GetFiniteValueSturmArenaBits(bits, degree) + 63) / 64 + 512];
 3987111        Span<int> offsets = stackalloc int[9];
 3987112        Span<int> widths = stackalloc int[9];
 3987113        Span<int> degrees = stackalloc int[9];
 3987114        Span<sbyte> sturmSigns = stackalloc sbyte[81];
 3987115        int chainCount = BuildFiniteValueSturm(coefficients, signs, arena,
 3987116            offsets, widths, degrees, sturmSigns, out int retainedWords);
 117        // The last nonzero row is gcd(F,F') up to a nonzero scalar. Its
 118        // degree answers this globally without normalizing or exporting it.
 3987119        hasRepeatedRoots = degrees[chainCount - 1] > 0;
 3987120        count = GetFiniteValueRootCount(cells[..cellWords], arena[..retainedWords], offsets,
 3987121            widths, degrees, sturmSigns, chainCount, arena[retainedWords..], out int lowerVariations);
 3987122        return TryIsolateFiniteValueRootCells(coefficients, signs, cells, shifts, count, lowerVariations,
 3987123            arena[..retainedWords], offsets, widths, degrees, sturmSigns, chainCount,
 3987124            arena[retainedWords..], out rationalMask);
 125    }
 126
 127    private static bool TryIsolateFiniteValueRootCells(ReadOnlySpan<ulong> coefficients,
 128        ReadOnlySpan<sbyte> signs, Span<ulong> cells, Span<int> shifts, int rootCount, int lowerVariations,
 129        scoped ReadOnlySpan<ulong> chain, scoped ReadOnlySpan<int> offsets, scoped ReadOnlySpan<int> widths,
 130        scoped ReadOnlySpan<int> degrees, scoped ReadOnlySpan<sbyte> sturmSigns, int chainCount,
 131        scoped Span<ulong> evaluation, out int rationalMask)
 132    {
 4295133        rationalMask = 0;
 4295134        if (rootCount == 0)
 1166135            return true;
 3129136        int cellWords = cells.Length / 8;
 137        // A pending node owns the output slot of its first root. Continue left
 138        // and save only a genuine right sibling. Finalizing roots in ascending
 139        // order makes the next ordinal its next pending node, regardless of
 140        // subdivision depth. No second set of numerator cells is live.
 3129141        Span<int> remainingCounts = stackalloc int[8];
 3129142        Span<int> lowerCounts = stackalloc int[8];
 3129143        remainingCounts[0] = rootCount;
 3129144        lowerCounts[0] = lowerVariations;
 3129145        cells[..cellWords].Clear();
 3129146        shifts[0] = 0;
 23224147        for (int ordinal = 0; ordinal < rootCount; ordinal++)
 148        {
 8502149            int remaining = remainingCounts[ordinal];
 8502150            int lowerCount = lowerCounts[ordinal];
 8502151            var root = new FiniteAxisValueRoot
 8502152            {
 8502153                Coefficients = coefficients, Signs = signs, Ordinal = ordinal,
 8502154                LowerNumerator = cells.Slice(ordinal * cellWords, cellWords),
 8502155                DenominatorShift = shifts[ordinal]
 8502156            };
 51923157            while (remaining > 1)
 158            {
 43432159                if (root.DenominatorShift >= cellWords * 64 - 1)
 160                {
 11161                    rationalMask = 0;
 11162                    return false;
 163                }
 43421164                ShiftFiniteRootLeft(root.LowerNumerator, 1);
 43421165                AddRoundedCylinderWord(root.LowerNumerator, 0, 1);
 43421166                root.DenominatorShift++;
 43421167                int midpointCount = GetFiniteValueVariations(chain, offsets, widths, degrees,
 43421168                    sturmSigns, chainCount, root.LowerNumerator, root.DenominatorShift, evaluation);
 43421169                int leftCount = lowerCount - midpointCount;
 43421170                if (leftCount == 0)
 10711171                    lowerCount = midpointCount;
 172                else
 173                {
 32710174                    if (leftCount < remaining)
 175                    {
 5373176                        int rightOrdinal = ordinal + leftCount;
 5373177                        root.LowerNumerator.CopyTo(cells.Slice(rightOrdinal * cellWords, cellWords));
 5373178                        shifts[rightOrdinal] = root.DenominatorShift;
 5373179                        remainingCounts[rightOrdinal] = remaining - leftCount;
 5373180                        lowerCounts[rightOrdinal] = midpointCount;
 181                    }
 32710182                    root.LowerNumerator[0]--; // Odd midpoint; left interval includes it, right excludes it.
 32710183                    remaining = leftCount;
 184                }
 185            }
 186            // A dyadic upper endpoint may have been recognized later than in
 187            // ordinal isolation. Restore its minimal denominator, including one.
 8491188            if (GetFiniteValueCellUpperSign(root, evaluation) == 0)
 189            {
 1408190                AddRoundedCylinderWord(root.LowerNumerator, 0, 1);
 1408191                int power = CountRoundedCylinderTrailingZeroes(root.LowerNumerator);
 1408192                ShiftRoundedCylinderWideRight(root.LowerNumerator, power);
 1408193                root.DenominatorShift -= power;
 1408194                root.IsRational = true;
 195            }
 196            else
 197            {
 7083198                IsolateFiniteValueCell(ref root, 0, 1, lowerCount, cellWords * 64 - 1,
 7083199                    chain, offsets, widths, degrees, sturmSigns, chainCount, evaluation, out bool fits);
 7083200                if (!fits)
 201                {
 8202                    rationalMask = 0;
 8203                    return false;
 204                }
 205            }
 8483206            shifts[ordinal] = root.DenominatorShift;
 8483207            if (root.IsRational)
 1840208                rationalMask |= 1 << ordinal;
 209        }
 3110210        return true;
 211    }
 212
 213    // The caller obtained rootCount for this exact polynomial and supplies a
 214    // valid ordinal and GetFiniteValueRootCellWords-sized storage. The shared
 215    // isolation bound therefore completes without a capacity-failure route.
 216    internal static FiniteAxisValueRoot GetKnownFiniteValueRoot(ReadOnlySpan<ulong> coefficients,
 217        ReadOnlySpan<sbyte> signs, int ordinal, int rootCount, Span<ulong> cell)
 218    {
 36219        int bits = GetFiniteRootCoefficientBits(coefficients, signs.Length);
 36220        Span<ulong> arena = stackalloc ulong[(GetFiniteValueSturmArenaBits(bits, signs.Length - 1) + 63) / 64 + 512];
 36221        Span<int> offsets = stackalloc int[9];
 36222        Span<int> widths = stackalloc int[9];
 36223        Span<int> degrees = stackalloc int[9];
 36224        Span<sbyte> sturmSigns = stackalloc sbyte[81];
 36225        int count = BuildFiniteValueSturm(coefficients, signs, arena,
 36226            offsets, widths, degrees, sturmSigns, out int retainedWords);
 36227        cell.Clear();
 36228        int lowerVariations = GetFiniteValueVariations(arena[..retainedWords], offsets, widths,
 36229            degrees, sturmSigns, count, cell, 0, arena[retainedWords..]);
 36230        return IsolateFiniteValueRoot(coefficients, signs, ordinal, rootCount, lowerVariations, cell, int.MaxValue,
 36231            arena[..retainedWords], offsets, widths, degrees, sturmSigns, count,
 36232            arena[retainedWords..], out _);
 233    }
 234
 235    private static int GetFiniteValueRootCount(Span<ulong> cell, ReadOnlySpan<ulong> chain,
 236        ReadOnlySpan<int> offsets, ReadOnlySpan<int> widths, ReadOnlySpan<int> degrees,
 237        ReadOnlySpan<sbyte> sturmSigns, int count, Span<ulong> evaluation, out int lowerVariations)
 238    {
 8135239        cell.Clear();
 8135240        lowerVariations = GetFiniteValueVariations(chain, offsets, widths,
 8135241            degrees, sturmSigns, count, cell, 0, evaluation);
 8135242        cell[0] = 1;
 8135243        return lowerVariations - GetFiniteValueVariations(chain, offsets, widths,
 8135244            degrees, sturmSigns, count, cell, 0, evaluation);
 245    }
 246
 247    private static FiniteAxisValueRoot IsolateFiniteValueRoot(ReadOnlySpan<ulong> coefficients,
 248        ReadOnlySpan<sbyte> signs, int ordinal, int rootCount, int lowerVariations, Span<ulong> cell, int maximumShift,
 249        scoped ReadOnlySpan<ulong> chain, scoped ReadOnlySpan<int> offsets,
 250        scoped ReadOnlySpan<int> widths, scoped ReadOnlySpan<int> degrees,
 251        scoped ReadOnlySpan<sbyte> sturmSigns, int count, scoped Span<ulong> evaluation,
 252        out bool fits)
 253    {
 2962254        fits = true;
 2962255        cell.Clear();
 2962256        cell[0] = 1;
 2962257        int remaining = rootCount;
 2962258        var root = new FiniteAxisValueRoot
 2962259        {
 2962260            Coefficients = coefficients,
 2962261            Signs = signs,
 2962262            LowerNumerator = cell,
 2962263            Ordinal = ordinal
 2962264        };
 2962265        if (ordinal == remaining - 1
 2962266            && EvaluateFiniteRootPolynomial(coefficients, signs, cell, 0, 0, evaluation) == 0)
 267        {
 60268            root.IsRational = true;
 60269            return root;
 270        }
 2902271        cell.Clear();
 272
 2902273        IsolateFiniteValueCell(ref root, ordinal, remaining, lowerVariations, maximumShift,
 2902274            chain, offsets, widths, degrees, sturmSigns, count, evaluation, out fits);
 2902275        return root;
 276    }
 277
 278    private static void IsolateFiniteValueCell(ref FiniteAxisValueRoot root,
 279        int ordinal, int remaining, int lowerVariations, int maximumShift,
 280        scoped ReadOnlySpan<ulong> chain, scoped ReadOnlySpan<int> offsets,
 281        scoped ReadOnlySpan<int> widths, scoped ReadOnlySpan<int> degrees,
 282        scoped ReadOnlySpan<sbyte> sturmSigns, int count, scoped Span<ulong> evaluation, out bool fits)
 283    {
 9985284        fits = true;
 285
 286        // Distinct roots of degree-eight integer polynomials of height B,
 287        // including their factors, have separation >2^(-16*(B+11)-89).
 288        // The physical value interval is (0,1], so the retained numerator
 289        // needs no extra Cauchy-scale integer part. Zero is excluded.
 48697290        while (remaining != 1 || GetRoundedCylinderWideLength(root.LowerNumerator) == 0
 48697291            || EvaluateFiniteRootPolynomial(root.Coefficients, root.Signs, root.LowerNumerator, root.DenominatorShift, 0
 48697292            || GetFiniteValueCellUpperSign(root, evaluation) == 0)
 293        {
 294            // For x in (0,1), a shift q needs at most q+1 numerator bits
 295            // including the upper endpoint. Stop before shifting beyond the
 296            // compact slot, even when a tiny numerator could fit farther.
 297            // Single-root callers supply the proven full isolation capacity.
 39525298            if (root.DenominatorShift >= maximumShift)
 299            {
 8300                fits = false;
 8301                return;
 302            }
 39517303            RefineFiniteValueCell(ref root, chain, offsets, widths, degrees, sturmSigns,
 39517304                count, evaluation, ref ordinal, ref remaining, ref lowerVariations);
 39517305            if (root.IsRational)
 805306                return;
 307        }
 9172308    }
 309
 310    /// <summary>
 311    /// Refines an already isolated root without changing its ordinal or input
 312    /// polynomial. Caller-owned numerator storage must preserve the existing
 313    /// cell and provide at least targetShift+1 bits for the refined cell and
 314    /// its upper endpoint. Rational roots and already finer cells are unchanged.
 315    /// An optional knownLowerSign of -1 or +1 certifies that this same cell has
 316    /// opposite nonzero endpoint signs; zero requests exact endpoint evaluation.
 317    /// </summary>
 318    internal static void RefineFiniteValueRoot(ref FiniteAxisValueRoot root, int targetShift, int knownLowerSign = 0)
 319    {
 42121320        if (root.IsRational || root.DenominatorShift >= targetShift)
 14556321            return;
 27565322        int coefficientBits = GetFiniteRootCoefficientBits(root.Coefficients, root.Signs.Length);
 27565323        if (TryRefineFiniteValueCrossingRoot(ref root, targetShift, knownLowerSign, coefficientBits))
 27261324            return;
 304325        int degree = root.Signs.Length - 1;
 304326        int bits = coefficientBits;
 304327        int unitBits = bits + 64;
 304328        int retainedBits = (3 * degree + 1) * unitBits;
 304329        int evaluationBits = 0;
 4748330        for (int index = 0; index <= degree; index++)
 331        {
 2070332            int height = index >= degree - 1 ? unitBits : (2 * degree - 2 * index - 1) * unitBits;
 2070333            if (index < degree - 1)
 1462334                retainedBits += (index + 1) * height;
 2070335            evaluationBits = Math.Max(evaluationBits,
 2070336                2 * (height + index * (targetShift + 1) + 95) + height + 128);
 337        }
 338        // Raw subresultant minor bounds control the retained rows. A
 339        // refinement may need more precision than isolation, so separately
 340        // budget exact dyadic evaluation and reuse the construction arena.
 304341        int arenaBits = Math.Max(GetFiniteValueSturmArenaBits(bits, degree), retainedBits + evaluationBits);
 304342        Span<ulong> arena = stackalloc ulong[(arenaBits + 63) / 64 + 512];
 304343        Span<int> offsets = stackalloc int[9];
 304344        Span<int> widths = stackalloc int[9];
 304345        Span<int> degrees = stackalloc int[9];
 304346        Span<sbyte> signs = stackalloc sbyte[81];
 304347        int count = BuildFiniteValueSturm(root.Coefficients, root.Signs, arena,
 304348            offsets, widths, degrees, signs, out int used);
 304349        Span<ulong> chain = arena[..used];
 304350        Span<ulong> evaluation = arena[used..];
 304351        int lowerVariations = GetFiniteValueVariations(chain, offsets, widths,
 304352            degrees, signs, count, root.LowerNumerator, root.DenominatorShift, evaluation);
 304353        int ordinal = 0;
 304354        int remaining = 1;
 13293355        while (!root.IsRational && root.DenominatorShift < targetShift)
 12989356            RefineFiniteValueCell(ref root, chain, offsets, widths, degrees, signs,
 12989357                count, evaluation, ref ordinal, ref remaining, ref lowerVariations);
 304358    }
 359
 360    private static void RefineFiniteValueCell(ref FiniteAxisValueRoot root,
 361        scoped ReadOnlySpan<ulong> chain, scoped ReadOnlySpan<int> offsets, scoped ReadOnlySpan<int> widths,
 362        scoped ReadOnlySpan<int> degrees, scoped ReadOnlySpan<sbyte> signs, int count,
 363        scoped Span<ulong> evaluation, ref int ordinal, ref int remaining, ref int lowerVariations)
 364    {
 52506365        ShiftFiniteRootLeft(root.LowerNumerator, 1);
 52506366        AddRoundedCylinderWord(root.LowerNumerator, 0, 1);
 52506367        root.DenominatorShift++;
 52506368        int midpointVariations = GetFiniteValueVariations(chain, offsets, widths,
 52506369            degrees, signs, count, root.LowerNumerator, root.DenominatorShift, evaluation);
 52506370        int leftCount = lowerVariations - midpointVariations;
 52506371        if (ordinal < leftCount)
 372        {
 39775373            if (ordinal == leftCount - 1
 39775374                && EvaluateFiniteRootPolynomial(root.Coefficients, root.Signs,
 39775375                    root.LowerNumerator, root.DenominatorShift, 0, evaluation) == 0)
 865376                root.IsRational = true;
 377            else
 38910378                root.LowerNumerator[0]--; // Odd midpoint, no borrow.
 39775379            remaining = leftCount;
 380        }
 381        else
 382        {
 12731383            ordinal -= leftCount;
 12731384            remaining -= leftCount;
 12731385            lowerVariations = midpointVariations;
 386        }
 12731387    }
 388
 389    private static int GetFiniteValueCellUpperSign(FiniteAxisValueRoot root, Span<ulong> evaluation = default)
 390    {
 17748391        AddRoundedCylinderWord(root.LowerNumerator, 0, 1);
 17748392        int sign = evaluation.IsEmpty
 17748393            ? EvaluateFiniteRootPolynomial(root.Coefficients, root.Signs,
 17748394                root.LowerNumerator, root.DenominatorShift, 0)
 17748395            : EvaluateFiniteRootPolynomial(root.Coefficients, root.Signs,
 17748396                root.LowerNumerator, root.DenominatorShift, 0, evaluation);
 35612397        for (int index = 0; index < root.LowerNumerator.Length; index++)
 398        {
 17806399            ulong previous = root.LowerNumerator[index];
 17806400            root.LowerNumerator[index] = unchecked(previous - 1);
 17806401            if (previous != 0)
 402                break;
 403        }
 17748404        return sign;
 405    }
 406
 407    private static int BuildFiniteValueSturm(ReadOnlySpan<ulong> coefficients,
 408        ReadOnlySpan<sbyte> signs, Span<ulong> arena, Span<int> offsets,
 409        Span<int> widths, Span<int> degrees, Span<sbyte> sturmSigns, out int used)
 410    {
 9895411        sturmSigns.Clear();
 9895412        int degree = signs.Length - 1;
 9895413        int inputWords = coefficients.Length / signs.Length;
 9895414        int words = Math.Max(1, (GetFiniteRootCoefficientBits(coefficients, signs.Length) + 63) / 64);
 9895415        offsets[0] = 0;
 9895416        widths[0] = words;
 9895417        degrees[0] = degree;
 9895418        used = (degree + 1) * words;
 114548419        for (int index = 0; index <= degree; index++)
 47379420            CopyFiniteRootMagnitude(coefficients.Slice(index * inputWords, inputWords),
 47379421                arena.Slice(index * words, words));
 9895422        signs.CopyTo(sturmSigns);
 9895423        NormalizeFiniteValueContent(arena[..used], degree, words, sturmSigns[..9], arena[used..]);
 9895424        offsets[1] = used;
 9895425        widths[1] = words + 1;
 9895426        degrees[1] = degree - 1;
 9895427        used += degree * (words + 1);
 9895428        DifferentiateFiniteAxisPolynomial(arena[..offsets[1]], sturmSigns[..(degree + 1)],
 9895429            arena.Slice(offsets[1], degree * (words + 1)), sturmSigns.Slice(9, degree));
 9895430        NormalizeFiniteValueContent(arena.Slice(offsets[1], degree * (words + 1)),
 9895431            degree - 1, words + 1, sturmSigns.Slice(9, 9), arena[used..]);
 9895432        int count = 2;
 9895433        if (degree == 1)
 689434            return count;
 435
 436        // Brown's subresultant PRS uses exact scalar divisors instead of a
 437        // content GCD after every pseudo-remainder. Keep its raw signs until
 438        // construction ends, then apply the separate Sturm row orientations.
 439        // For C=B+64 and h(j)=2*n-1-2*j, the retained minors need <=221C
 440        // bits. In a pair of actual degrees d>e, the second row is the
 441        // subresultant of natural index d-1 even after an abnormal drop;
 442        // its height is <=h(d-1)C, not the looser h(e)C. Graded prem slots
 443        // plus two products peak at215C; exact-division scratch peaks172C.
 444        // Scalar powers/divisors need <=28C each. Eight such scratch slots
 445        // give a total <=660C, within880C plus the existing rounding padding.
 446        // Store raw Brown rows as S_i=2^rowPower[i]*T_i, and its scalars
 447        // as c=2^cPower*cOdd, beta=2^betaPower*betaOdd. Only positive
 448        // powers of two are removed, so row signs and Sturm variations stay
 449        // unchanged. Normalized retained rows/scalars only shrink; transient
 450        // quotients still use the existing graded pseudo-remainder storage.
 9206451        int unitBits = GetFiniteRootCoefficientBits(coefficients, signs.Length) + 64;
 9206452        int scalarWords = (28 * unitBits + 63) / 64;
 9206453        int scalarOffset = arena.Length - (8 * scalarWords + 1);
 9206454        Span<ulong> scalarWork = arena[scalarOffset..];
 9206455        Span<ulong> c = scalarWork[..scalarWords];
 9206456        Span<ulong> beta = scalarWork.Slice(scalarWords, scalarWords);
 9206457        CopyFiniteRootMagnitude(arena.Slice(offsets[1] + (degree - 1) * widths[1], widths[1]), c);
 9206458        int cPower = CountRoundedCylinderTrailingZeroes(c);
 9206459        ShiftRoundedCylinderWideRight(c, cPower);
 9206460        int cSign = -sturmSigns[9 + degree - 1];
 9206461        beta.Clear();
 9206462        beta[0] = 1; // Initial degree gap is one: beta=(-1)^(gap+1).
 9206463        int betaSign = 1;
 9206464        int betaPower = 0;
 9206465        Span<int> rowPowers = stackalloc int[9];
 9206466        rowPowers[0] = rowPowers[1] = 0;
 9206467        Span<sbyte> rowSigns = stackalloc sbyte[9];
 9206468        rowSigns[0] = rowSigns[1] = 1;
 31244469        while (degrees[count - 1] > 0)
 470        {
 24905471            int left = count - 2;
 24905472            int right = count - 1;
 24905473            int power = degrees[left] - degrees[right] + 1;
 24905474            int pseudoSign = (power & 1) == 0 ? 1 : sturmSigns[right * 9 + degrees[right]];
 24905475            int nextDegree = BuildFiniteValueSubresultant(
 24905476                arena.Slice(offsets[left], (degrees[left] + 1) * widths[left]),
 24905477                sturmSigns.Slice(left * 9, degrees[left] + 1),
 24905478                arena.Slice(offsets[right], (degrees[right] + 1) * widths[right]),
 24905479                sturmSigns.Slice(right * 9, degrees[right] + 1),
 24905480                beta, betaSign * pseudoSign, arena.Slice(used, scalarOffset - used),
 24905481                sturmSigns.Slice(count * 9, 9), out int nextWords, out int removedPower);
 24905482            if (nextDegree < 0)
 483                break;
 484            // prem is homogeneous: the omitted input scale is
 485            // rowPower[left]+power*rowPower[right]. After division by
 486            // betaOdd, stripping 2^removedPower leaves the raw row exponent
 487            // below. The difference before adding removedPower may be
 488            // negative; integrality guarantees only the final sum is >=0.
 22038489            rowPowers[count] = rowPowers[left] + power * rowPowers[right]
 22038490                - betaPower + removedPower;
 491            System.Diagnostics.Debug.Assert(rowPowers[count] >= 0);
 22038492            offsets[count] = used;
 22038493            widths[count] = nextWords;
 22038494            degrees[count] = nextDegree;
 495            // prem=lc(right)^(gap+1)*rem. Multiplying each completed raw
 496            // row by this sign makes it a positive multiple of -rem(Sturm).
 22038497            rowSigns[count] = (sbyte)(-rowSigns[left] * betaSign * pseudoSign);
 22038498            used += (nextDegree + 1) * nextWords;
 22038499            if (nextDegree > 0)
 15699500                UpdateFiniteValueSubresultantScalars(
 15699501                    arena.Slice(offsets[right] + degrees[right] * widths[right], widths[right]),
 15699502                    sturmSigns[right * 9 + degrees[right]], rowPowers[right],
 15699503                    arena.Slice(offsets[count] + nextDegree * nextWords, nextWords),
 15699504                    sturmSigns[count * 9 + nextDegree], rowPowers[count], degrees[right] - nextDegree,
 15699505                    scalarWork, scalarWords, ref cSign, ref cPower, out betaSign, out betaPower);
 22038506            count++;
 507        }
 62488508        for (int row = 2; row < count; row++)
 176514509            for (int index = 0; index <= degrees[row]; index++)
 66219510                sturmSigns[row * 9 + index] *= rowSigns[row];
 9206511        return count;
 512    }
 513
 514    private static void UpdateFiniteValueSubresultantScalars(ReadOnlySpan<ulong> previousLeading,
 515        int previousLeadingSign, int previousRowPower, ReadOnlySpan<ulong> leading,
 516        int leadingSign, int rowPower, int gap, Span<ulong> work, int words,
 517        ref int cSign, ref int cPower, out int betaSign, out int betaPower)
 518    {
 15699519        Span<ulong> c = work[..words];
 15699520        Span<ulong> beta = work.Slice(words, words);
 15699521        Span<ulong> numerator = work.Slice(2 * words, words);
 15699522        Span<ulong> denominator = work.Slice(3 * words, words);
 15699523        Span<ulong> product = work.Slice(4 * words, words);
 524        // beta=-previousLeading*c^gap; c_next=(-leading)^gap/c^(gap-1).
 525        // Each leading coefficient has both the row's omitted power and
 526        // its stored leading term's power. Track those separately and use
 527        // only odd magnitudes in the unchanged scalar recurrence. The odd
 528        // denominator divides the odd numerator exactly, since it is coprime
 529        // to every omitted power of two, including abnormal degree drops.
 15699530        int previousLeadingPower = CountRoundedCylinderTrailingZeroes(previousLeading);
 15699531        int leadingPower = CountRoundedCylinderTrailingZeroes(leading);
 15699532        betaPower = previousRowPower + previousLeadingPower + gap * cPower;
 15699533        int nextCPower = gap * (rowPower + leadingPower) - (gap - 1) * cPower;
 534        System.Diagnostics.Debug.Assert(betaPower >= 0 && nextCPower >= 0);
 535        // Reuse the denominator slot for leading odd parts before it is
 536        // needed for cOdd^(gap-1); no extra coefficient buffer is retained.
 15699537        CopyFiniteRootMagnitude(previousLeading, denominator);
 15699538        ShiftRoundedCylinderWideRight(denominator, previousLeadingPower);
 15699539        PowerFiniteValueMagnitude(c, gap, numerator, product);
 15699540        WideArithmetic.MultiplyMagnitudes(denominator, numerator, beta);
 15699541        betaSign = -previousLeadingSign * ((gap & 1) == 0 ? 1 : cSign);
 15699542        if (gap == 1)
 543        {
 15671544            CopyFiniteRootMagnitude(leading, c);
 15671545            ShiftRoundedCylinderWideRight(c, leadingPower);
 15671546            cSign = -leadingSign;
 15671547            cPower = nextCPower;
 15671548            return;
 549        }
 28550        CopyFiniteRootMagnitude(leading, denominator);
 28551        ShiftRoundedCylinderWideRight(denominator, leadingPower);
 28552        PowerFiniteValueMagnitude(denominator, gap, numerator, product);
 28553        PowerFiniteValueMagnitude(c, gap - 1, denominator, product);
 28554        int nextSign = ((gap & 1) == 0 ? 1 : -leadingSign)
 28555            * ((gap & 1) == 0 ? cSign : 1);
 28556        Span<ulong> remainder = work.Slice(5 * words, words);
 28557        WideArithmetic.DivideMagnitudes(numerator, denominator, c, remainder, work[(6 * words)..]);
 558        System.Diagnostics.Debug.Assert(GetRoundedCylinderWideLength(remainder) == 0);
 28559        cSign = nextSign;
 28560        cPower = nextCPower;
 28561    }
 562
 563    private static void PowerFiniteValueMagnitude(ReadOnlySpan<ulong> value, int power,
 564        Span<ulong> result, Span<ulong> product)
 565    {
 15755566        result.Clear();
 15755567        result[0] = 1;
 63276568        for (int index = 0; index < power; index++)
 569        {
 15883570            WideArithmetic.MultiplyMagnitudes(result, value, product);
 15883571            product.CopyTo(result);
 572        }
 15755573    }
 574
 575    private static int BuildFiniteValueSubresultant(ReadOnlySpan<ulong> left,
 576        ReadOnlySpan<sbyte> leftSigns, ReadOnlySpan<ulong> right,
 577        ReadOnlySpan<sbyte> rightSigns, ReadOnlySpan<ulong> beta, int resultSign,
 578        Span<ulong> work, Span<sbyte> resultSigns,
 579        out int resultWords, out int removedPower)
 580    {
 24905581        int d = leftSigns.Length - 1;
 24905582        int e = rightSigns.Length - 1;
 24905583        int leftWords = left.Length / (d + 1);
 24905584        int rightWords = right.Length / (e + 1);
 24905585        int leftBits = GetFiniteRootCoefficientBits(left, d + 1);
 24905586        int rightBits = GetFiniteRootCoefficientBits(right, e + 1);
 24905587        Span<int> offsets = stackalloc int[9];
 24905588        Span<int> widths = stackalloc int[9];
 24905589        Span<sbyte> signs = stackalloc sbyte[9];
 24905590        int used = 0;
 24905591        int maximumWords = 0;
 293074592        for (int index = 0; index <= d; index++)
 593        {
 121632594            int cancellations = Math.Min(d - e + 1, d - index);
 121632595            int width = (leftBits + cancellations * (rightBits + 1) + 127) / 64;
 121632596            offsets[index] = used;
 121632597            widths[index] = width;
 121632598            used += width;
 121632599            maximumWords = Math.Max(maximumWords, width);
 121632600            CopyFiniteRootMagnitude(left.Slice(index * leftWords, leftWords),
 121632601                work.Slice(offsets[index], width));
 121632602            signs[index] = leftSigns[index];
 603        }
 24905604        Span<ulong> product = work.Slice(used, maximumWords);
 24905605        Span<ulong> subtract = work.Slice(used + maximumWords, maximumWords);
 24905606        ReadOnlySpan<ulong> divisorLeading = right.Slice(e * rightWords, rightWords);
 149534607        for (int degree = d; degree >= e; degree--)
 608        {
 609            // Even a zero leading term must multiply every remaining slot:
 610            // Brown's prem uses exactly d-e+1 leading-coefficient factors.
 49862611            ReadOnlySpan<ulong> leading = work.Slice(offsets[degree], widths[degree]);
 49862612            int shift = degree - e;
 437294613            for (int index = 0; index < degree; index++)
 614            {
 168785615                Span<ulong> coefficient = work.Slice(offsets[index], widths[index]);
 168785616                WideArithmetic.MultiplyMagnitudes(coefficient, divisorLeading, product);
 168785617                int sign = signs[index];
 168785618                if (signs[degree] != 0 && index >= shift && rightSigns[index - shift] != 0)
 619                {
 139739620                    WideArithmetic.MultiplyMagnitudes(leading,
 139739621                        right.Slice((index - shift) * rightWords, rightWords), subtract);
 139739622                    WideArithmetic.AddShiftedSignedMagnitude(subtract,
 139739623                        -signs[degree] * rightSigns[e] * rightSigns[index - shift], 0, product, ref sign);
 624                }
 168785625                product[..widths[index]].CopyTo(coefficient);
 168785626                signs[index] = (sbyte)sign;
 627            }
 49862628            signs[degree] = 0;
 629        }
 24905630        int remainderDegree = e - 1;
 30456631        while (remainderDegree >= 0 && signs[remainderDegree] == 0)
 5551632            remainderDegree--;
 24905633        resultWords = 0;
 24905634        removedPower = 0;
 24905635        if (remainderDegree < 0)
 2867636            return -1;
 637
 638        // All surviving slots have the same width. Higher discarded slots
 639        // become exact-division scratch; no pseudo-division products remain live.
 22038640        int remainderWords = widths[0];
 22038641        int remainderLength = (remainderDegree + 1) * remainderWords;
 22038642        Span<ulong> remainder = work[..remainderLength];
 643        // beta stores only its odd part. It divides this smaller prem
 644        // exactly: divisibility of the raw Brown numerator cannot depend on
 645        // its omitted power of two when the divisor is odd.
 22038646        int betaWords = GetRoundedCylinderWideLength(beta);
 22038647        if (betaWords != 1 || beta[0] != 1)
 648        {
 10730649            Span<ulong> integerRemainder = work.Slice(remainderLength, remainderWords);
 10730650            Span<ulong> division = work.Slice(remainderLength + remainderWords, 2 * remainderWords + 1);
 77482651            for (int index = 0; index <= remainderDegree; index++)
 652            {
 28011653                Span<ulong> coefficient = remainder.Slice(index * remainderWords, remainderWords);
 28011654                WideArithmetic.DivideMagnitudes(coefficient, beta[..betaWords], coefficient, integerRemainder, division)
 655                System.Diagnostics.Debug.Assert(GetRoundedCylinderWideLength(integerRemainder) == 0);
 656            }
 657        }
 658        // U=prem(T_left,T_right)/betaOdd can exceed the raw next row when
 659        // its omitted scale exponent is negative. It fits the prem scratch,
 660        // but must lose its common power of two BEFORE retained width/used
 661        // accounting; only the normalized T_next is bounded by the raw row.
 22038662        removedPower = NormalizeFiniteAxisPolynomialPowerOfTwo(
 22038663            remainder, signs[..(remainderDegree + 1)]);
 176514664        for (int index = 0; index <= remainderDegree; index++)
 66219665            resultWords = Math.Max(resultWords,
 66219666                GetRoundedCylinderWideLength(remainder.Slice(index * remainderWords, remainderWords)));
 176514667        for (int index = 0; index <= remainderDegree; index++)
 668        {
 66219669            remainder.Slice(index * remainderWords, resultWords)
 66219670                .CopyTo(work.Slice(index * resultWords, resultWords));
 66219671            resultSigns[index] = (sbyte)(resultSign * signs[index]);
 672        }
 22038673        return remainderDegree;
 674    }
 675
 676    private static void NormalizeFiniteValueContent(Span<ulong> coefficients, int degree,
 677        int words, ReadOnlySpan<sbyte> signs, Span<ulong> scratch)
 678    {
 20812679        if (degree == 0)
 680        {
 1080681            coefficients.Clear();
 1080682            coefficients[0] = 1;
 1080683            return;
 684        }
 685        // Variable scaling introduces large shared powers of two in later
 686        // remainders too. Strip those exactly before Euclid works on the
 687        // remaining integer content; roots and signed variations are unchanged.
 19732688        NormalizeFiniteAxisPolynomialPowerOfTwo(coefficients, signs[..(degree + 1)]);
 19732689        Span<ulong> content = scratch[..words];
 19732690        Span<ulong> other = scratch.Slice(words, words);
 19732691        Span<ulong> division = scratch.Slice(2 * words, 2 * words + 1);
 19732692        content.Clear();
 146804693        for (int index = 0; index <= degree; index++)
 694        {
 68541695            if (signs[index] == 0)
 696                continue;
 66524697            coefficients.Slice(index * words, words).CopyTo(other);
 66524698            WideArithmetic.GetMagnitudeGreatestCommonDivisor(content, other, division);
 66524699            if (content[0] == 1 && GetRoundedCylinderWideLength(content) == 1)
 14871700                return;
 701        }
 64780702        for (int index = 0; index <= degree; index++)
 703        {
 27529704            Span<ulong> coefficient = coefficients.Slice(index * words, words);
 27529705            WideArithmetic.DivideMagnitudes(coefficient, content, coefficient, other, division);
 706            System.Diagnostics.Debug.Assert(GetRoundedCylinderWideLength(other) == 0);
 707        }
 4861708    }
 709
 710    private static int GetFiniteValueVariations(ReadOnlySpan<ulong> chain,
 711        ReadOnlySpan<int> offsets, ReadOnlySpan<int> widths, ReadOnlySpan<int> degrees,
 712        ReadOnlySpan<sbyte> signs, int count, ReadOnlySpan<ulong> numerator, int shift,
 713        Span<ulong> evaluation)
 714    {
 113637715        int previous = 0;
 113637716        int variations = 0;
 113637717        int numeratorBits = GetFiniteRootBits(numerator);
 1347948718        for (int index = 0; index < count; index++)
 719        {
 560337720            ReadOnlySpan<ulong> row = chain.Slice(offsets[index], (degrees[index] + 1) * widths[index]);
 560337721            ReadOnlySpan<sbyte> rowSigns = signs.Slice(index * 9, degrees[index] + 1);
 560337722            int sign = 0;
 560337723            if (degrees[index] > 0 && numeratorBits > 0 && numeratorBits <= shift)
 724            {
 725                // A certified point sign can replace homogeneous evaluation.
 726                // Reuse the caller's returning evaluation scratch; cap only
 727                // work precision to fit, never the sign acceptance threshold.
 432295728                int countBits = GetFiniteValueCeilingLog2(degrees[index] + 1);
 432295729                int productWords = (shift + 64) / 64;
 432295730                int maximumPrecision = 64 * ((evaluation.Length - productWords) / 2) - countBits - 64;
 432295731                int precision = Math.Min(maximumPrecision,
 432295732                    shift + FiniteValuePointGuardBits + degrees[index] * (shift - numeratorBits + 1));
 733                System.Diagnostics.Debug.Assert(precision > 0);
 432295734                int words = (precision + countBits + 127) / 64;
 432295735                sign = GetFiniteValueApproximateSignCore(numerator, shift, row, rowSigns,
 432295736                    precision, GetFiniteRootCoefficientBits(row, rowSigns.Length),
 432295737                    evaluation[..words], evaluation.Slice(words, words + productWords), out _);
 738            }
 739            // Uncertainty is not zero evidence. At a vanishing Sturm row,
 740            // its first nonzero derivative supplies the exact right-limit sign.
 1462876741            for (int derivative = 0; derivative <= degrees[index] && sign == 0; derivative++)
 171101742                sign = EvaluateFiniteRootPolynomial(row, rowSigns, numerator, shift, derivative, evaluation);
 560337743            if (previous != 0 && previous != sign)
 179135744                variations++;
 560337745            previous = sign;
 746        }
 113637747        return variations;
 748    }
 749}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.ValueRootBernstein.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.ValueRootBernstein.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/// <content>Bounded exact certificates before general Sturm isolation.</content>
 14internal static partial class WideFiniteAxisIntersection
 15{
 16    private const int FiniteValueBernsteinDepth = 32;
 17    private const int FiniteValueBernsteinNodes = 256;
 18    private const uint FiniteValueSquareFreePrime = 65537;
 19
 20    // This frame must return before the caller allocates its Sturm arena.
 21    // Failure is inconclusive and discards partial cells, never a root count.
 22    [MethodImpl(MethodImplOptions.NoInlining)]
 23    private static bool TryGetFiniteValueRootsBernstein(ReadOnlySpan<ulong> coefficients,
 24        ReadOnlySpan<sbyte> signs, Span<ulong> cells, Span<int> shifts, out int count)
 25    {
 2867926        count = 0;
 2867927        if (!HasFiniteValueSquareFreeCertificate(coefficients, signs))
 316928            return false;
 29
 2551030        int degree = signs.Length - 1;
 2551031        int coefficientCount = degree + 1;
 2551032        int inputWords = coefficients.Length / coefficientCount;
 2551033        int cellWords = cells.Length / 8;
 2551034        int bits = GetFiniteRootCoefficientBits(coefficients, coefficientCount);
 35        // n!*b_j=sum_i<=j a_i*C(j,i)*i!*(n-i)! is integral and
 36        // has height <B+20 for n<=8. A midpoint split multiplies child
 37        // Bernstein coefficients by the common positive scale 2^n, adding
 38        // at most n bits per level. Thus B+20+32*n bits suffice everywhere.
 39        // Current/left plus32 pending right rows require34*(n+1) slots:
 40        // <=296208 bytes at B<=7424, plus <1KiB signs/indices. The existing
 41        // containing frames and64KiB caller headroom remain below1MiB;
 42        // this storage is never live with the general880C Sturm arena.
 2551043        int words = (bits + 20 + FiniteValueBernsteinDepth * degree + 63) / 64;
 2551044        int rowWords = coefficientCount * words;
 2551045        Span<ulong> storage = stackalloc ulong[(FiniteValueBernsteinDepth + 2) * rowWords];
 2551046        Span<sbyte> signStorage = stackalloc sbyte[(FiniteValueBernsteinDepth + 2) * coefficientCount];
 2551047        Span<ulong> pendingNumerators = stackalloc ulong[FiniteValueBernsteinDepth];
 2551048        Span<int> pendingShifts = stackalloc int[FiniteValueBernsteinDepth];
 2551049        Span<ulong> current = storage[..rowWords];
 2551050        Span<sbyte> currentSigns = signStorage[..coefficientCount];
 2551051        Span<ulong> left = storage.Slice(rowWords, rowWords);
 2551052        Span<sbyte> leftSigns = signStorage.Slice(coefficientCount, coefficientCount);
 2551053        current.Clear();
 2551054        currentSigns.Clear();
 2551055        int factorial = 1;
 19888256        for (int index = 2; index <= degree; index++)
 7393157            factorial *= index;
 30092258        for (int index = 0; index <= degree; index++)
 59        {
 12495160            int weight = factorial;
 12495161            int sign = 0;
 12495162            Span<ulong> destination = current.Slice(index * words, words);
 101232063            for (int source = 0; source <= index; source++)
 64            {
 38120965                if (signs[source] != 0)
 66                {
 37954967                    MultiplyRoundedCylinderWideByWord(coefficients.Slice(source * inputWords, inputWords),
 37954968                        (ulong)weight, left[..words]);
 37954969                    WideArithmetic.AddShiftedSignedMagnitude(left[..words], signs[source], 0, destination, ref sign);
 70                }
 38120971                if (source < index)
 25625872                    weight = weight * (index - source) / (degree - source);
 73            }
 12495174            currentSigns[index] = (sbyte)sign;
 75        }
 76
 2551077        int pending = 0;
 2551078        ulong lower = 0;
 2551079        int shift = 0;
 30293480        for (int visited = 0; visited < FiniteValueBernsteinNodes; visited++)
 81        {
 82            // Endpoints are exact Bernstein coefficients. Let Sturm own all
 83            // endpoint/singleton bookkeeping, including the excluded zero.
 15146784            if (currentSigns[0] == 0 || currentSigns[degree] == 0)
 85                break;
 15115286            int variations = 0;
 15115287            int previous = 0;
 196286088            for (int index = 0; index <= degree; index++)
 89            {
 83027890                int sign = currentSigns[index];
 83027891                if (sign == 0)
 92                    continue;
 82980593                if (previous != 0 && previous != sign)
 20837394                    variations++;
 82980595                previous = sign;
 96            }
 97            // Descartes in Bernstein form: variation0 certifies no interior
 98            // root; variation1 certifies exactly one, counting multiplicity.
 99            // Positive lower endpoints preserve the existing root contract.
 151152100            if (variations > 1 || variations == 1 && lower == 0)
 101            {
 67459102                if (shift == FiniteValueBernsteinDepth)
 103                    break;
 67141104                Span<ulong> right = storage.Slice((pending + 2) * rowWords, rowWords);
 67141105                Span<sbyte> rightSigns = signStorage.Slice((pending + 2) * coefficientCount, coefficientCount);
 67141106                SplitFiniteValueBernstein(current, currentSigns, left, leftSigns, right, rightSigns, words);
 67141107                if (leftSigns[degree] == 0) // Exact midpoint; do not lose a rational root.
 108                    break;
 66648109                pendingNumerators[pending] = 2 * lower + 1;
 66648110                pendingShifts[pending] = shift + 1;
 66648111                pending++;
 66648112                left.CopyTo(current);
 66648113                leftSigns.CopyTo(currentSigns);
 66648114                lower *= 2;
 66648115                shift++;
 66648116                continue;
 117            }
 83693118            if (variations == 1)
 119            {
 120                System.Diagnostics.Debug.Assert(count < degree);
 19889121                Span<ulong> destination = cells.Slice(count * cellWords, cellWords);
 19889122                destination.Clear();
 19889123                destination[0] = lower;
 19889124                shifts[count++] = shift;
 125            }
 83693126            if (pending == 0)
 24384127                return true;
 59309128            pending--;
 59309129            storage.Slice((pending + 2) * rowWords, rowWords).CopyTo(current);
 59309130            signStorage.Slice((pending + 2) * coefficientCount, coefficientCount).CopyTo(currentSigns);
 59309131            lower = pendingNumerators[pending];
 59309132            shift = pendingShifts[pending];
 133        }
 1126134        count = 0;
 1126135        return false;
 136    }
 137
 138    private static void SplitFiniteValueBernstein(Span<ulong> triangle, Span<sbyte> triangleSigns,
 139        Span<ulong> left, Span<sbyte> leftSigns, Span<ulong> right, Span<sbyte> rightSigns, int words)
 140    {
 67141141        int degree = triangleSigns.Length - 1;
 142        // Integer de Casteljau: T^(r+1)_i=T^r_i+T^r_(i+1).
 143        // Child edges are2^(n-r)*T^r_0 and2^(n-r)*T^r_(n-r),
 144        // which uniformly scale both children's usual coefficients by2^n.
 888290145        for (int level = 0; level <= degree; level++)
 146        {
 377004147            if (level != 0)
 2595372148                for (int index = 0; index <= degree - level; index++)
 149                {
 987823150                    int sign = triangleSigns[index];
 987823151                    WideArithmetic.AddShiftedSignedMagnitude(triangle.Slice((index + 1) * words, words),
 987823152                        triangleSigns[index + 1], 0, triangle.Slice(index * words, words), ref sign);
 987823153                    triangleSigns[index] = (sbyte)sign;
 154                }
 377004155            int edge = degree - level;
 377004156            Span<ulong> leftCoefficient = left.Slice(level * words, words);
 377004157            triangle[..words].CopyTo(leftCoefficient);
 377004158            ShiftFiniteRootLeft(leftCoefficient, edge);
 377004159            leftSigns[level] = triangleSigns[0];
 377004160            Span<ulong> rightCoefficient = right.Slice(edge * words, words);
 377004161            triangle.Slice(edge * words, words).CopyTo(rightCoefficient);
 377004162            ShiftFiniteRootLeft(rightCoefficient, edge);
 377004163            rightSigns[edge] = triangleSigns[edge];
 164        }
 67141165    }
 166
 167    private static bool HasFiniteValueSquareFreeCertificate(ReadOnlySpan<ulong> coefficients,
 168        ReadOnlySpan<sbyte> signs)
 169    {
 28679170        int degree = signs.Length - 1;
 28679171        int words = coefficients.Length / signs.Length;
 28679172        Span<uint> left = stackalloc uint[9];
 28679173        Span<uint> right = stackalloc uint[9];
 344942174        for (int index = 0; index <= degree; index++)
 175        {
 143792176            int residue = 0;
 143792177            if (signs[index] != 0)
 17446556178                for (int word = 0; word < words; word++)
 179                {
 8580675180                    ulong value = coefficients[index * words + word];
 181                    // 2^16=-1 and2^64=1 modulo the Fermat prime65537.
 8580675182                    residue = (residue + (int)(value & 65535) - (int)((value >> 16) & 65535)
 8580675183                        + (int)((value >> 32) & 65535) - (int)(value >> 48)
 8580675184                        + 2 * (int)FiniteValueSquareFreePrime) % (int)FiniteValueSquareFreePrime;
 185                }
 143792186            left[index] = (uint)(signs[index] < 0 && residue != 0
 143792187                ? (int)FiniteValueSquareFreePrime - residue : residue);
 188        }
 189        // Degree preservation and p>degree preserve both F and F'. If their
 190        // finite-field gcd is constant, the integer resultant is nonzero.
 191        // Failure is merely inconclusive: never claim that F repeats over Q.
 28679192        if (left[degree] == 0)
 1230193            return false;
 275576194        for (int index = 0; index < degree; index++)
 110339195            right[index] = (uint)((ulong)(index + 1) * left[index + 1] % FiniteValueSquareFreePrime);
 27449196        int leftDegree = degree;
 27449197        int rightDegree = degree - 1;
 107114198        while (rightDegree > 0)
 199        {
 200            // Pseudo-division over the field needs no inverses. Nonzero
 201            // leading multipliers cannot change the polynomial gcd.
 81604202            uint leading = right[rightDegree];
 489624203            for (int index = leftDegree; index >= rightDegree; index--)
 204            {
 163208205                uint cancel = left[index];
 163208206                int offset = index - rightDegree;
 1228436207                for (int term = 0; term < index; term++)
 208                {
 451010209                    ulong value = (ulong)left[term] * leading % FiniteValueSquareFreePrime;
 451010210                    ulong subtract = term >= offset
 451010211                        ? (ulong)cancel * right[term - offset] % FiniteValueSquareFreePrime : 0;
 451010212                    left[term] = (uint)((value + FiniteValueSquareFreePrime - subtract) % FiniteValueSquareFreePrime);
 213                }
 163208214                left[index] = 0;
 215            }
 81604216            int remainderDegree = rightDegree - 1;
 84829217            while (remainderDegree >= 0 && left[remainderDegree] == 0)
 3225218                remainderDegree--;
 81604219            if (remainderDegree < 0)
 1939220                return false;
 79665221            Span<uint> swap = left;
 79665222            left = right;
 79665223            right = swap;
 79665224            leftDegree = rightDegree;
 79665225            rightDegree = remainderDegree;
 226        }
 25510227        return true;
 228    }
 229}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.ValueRootFactors.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.ValueRootFactors.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/// <content>
 14/// Primitive square-free factors identifying repeated value roots.
 15/// </content>
 16internal static partial class WideFiniteAxisIntersection
 17{
 18    /// <summary>
 19    /// Writes the positive-leading primitive radical of gcd(F,F') for a
 20    /// degree-at-most-eight polynomial. Returns its degree, zero with factor
 21    /// one when no root repeats, or -1 for the zero polynomial. Output has
 22    /// five equal-width coefficient slots and five signs; unused storage is
 23    /// cleared. Inputs and output must not overlap. A coefficient capacity of
 24    /// ceil((ceil(inputBits/2)+5)/64) words suffices.
 25    /// </summary>
 26    internal static int GetFiniteValueRepeatedRootFactor(ReadOnlySpan<ulong> coefficients,
 27        ReadOnlySpan<sbyte> signs, Span<ulong> factor, Span<sbyte> factorSigns)
 28    {
 51729        factor.Clear();
 51730        factorSigns.Clear();
 51731        int degree = signs.Length - 1;
 52632        while (degree >= 0 && signs[degree] == 0)
 933            degree--;
 51734        if (degree < 0)
 135            return -1;
 51636        if (degree <= 1)
 37        {
 238            factor[0] = 1;
 239            factorSigns[0] = 1;
 240            return 0;
 41        }
 42
 51443        int inputWords = coefficients.Length / signs.Length;
 51444        coefficients = coefficients[..((degree + 1) * inputWords)];
 51445        signs = signs[..(degree + 1)];
 51446        int bits = GetFiniteRootCoefficientBits(coefficients, degree + 1);
 47        // Both intermediate gcds divide F. The degree-eight factor-height
 48        // bound needs fewer than eleven additional bits, independent of the
 49        // much larger transient Sturm coefficients.
 51450        int words = (bits + 11 + 63) / 64;
 51451        Span<ulong> gcd = stackalloc ulong[8 * words];
 51452        Span<sbyte> gcdSigns = stackalloc sbyte[8];
 51453        int gcdDegree = GetFiniteValueDerivativeGcd(coefficients, signs, gcd, gcdSigns);
 51454        if (gcdDegree == 0)
 55        {
 656            factor[0] = 1;
 657            factorSigns[0] = 1;
 658            return 0;
 59        }
 60
 50861        Span<ulong> repeated = stackalloc ulong[8 * words];
 50862        Span<sbyte> repeatedSigns = stackalloc sbyte[8];
 50863        int repeatedDegree = GetFiniteValueDerivativeGcd(
 50864            gcd[..((gcdDegree + 1) * words)], gcdSigns[..(gcdDegree + 1)], repeated, repeatedSigns);
 65        // G/gcd(G,G') retains each repeated irreducible factor of F once.
 66        // Its square divides primitive F, so its degree is at most four.
 50867        return DivideFiniteValueFactors(gcd[..((gcdDegree + 1) * words)],
 50868            gcdSigns[..(gcdDegree + 1)], repeated[..((repeatedDegree + 1) * words)],
 50869            repeatedSigns[..(repeatedDegree + 1)], factor, factorSigns);
 70    }
 71
 72    // Each call releases its construction arena before the next gcd or the
 73    // quotient phase. Only the two small factor buffers survive both calls.
 74    [MethodImpl(MethodImplOptions.NoInlining)]
 75    private static int GetFiniteValueDerivativeGcd(ReadOnlySpan<ulong> coefficients,
 76        ReadOnlySpan<sbyte> signs, Span<ulong> factor, Span<sbyte> factorSigns)
 77    {
 102278        int bits = GetFiniteRootCoefficientBits(coefficients, signs.Length);
 102279        Span<ulong> arena = stackalloc ulong[(GetFiniteValueSturmArenaBits(bits, signs.Length - 1) + 63) / 64 + 512];
 102280        Span<int> offsets = stackalloc int[9];
 102281        Span<int> widths = stackalloc int[9];
 102282        Span<int> degrees = stackalloc int[9];
 102283        Span<sbyte> sturmSigns = stackalloc sbyte[81];
 102284        int count = BuildFiniteValueSturm(coefficients, signs, arena,
 102285            offsets, widths, degrees, sturmSigns, out int used);
 102286        int last = count - 1;
 102287        int degree = degrees[last];
 88        // Sturm construction retains raw subresultants. Only this exported
 89        // gcd needs primitive content, before copying into factor-sized slots.
 102290        NormalizeFiniteValueContent(arena.Slice(offsets[last], (degree + 1) * widths[last]),
 102291            degree, widths[last], sturmSigns.Slice(last * 9, 9), arena[used..]);
 102292        int words = factor.Length / factorSigns.Length;
 102293        int leadingSign = sturmSigns[last * 9 + degree];
 102294        factor.Clear();
 102295        factorSigns.Clear();
 735696        for (int index = 0; index <= degree; index++)
 97        {
 265698            CopyFiniteRootMagnitude(arena.Slice(offsets[last] + index * widths[last], widths[last]),
 265699                factor.Slice(index * words, words));
 2656100            factorSigns[index] = (sbyte)(sturmSigns[last * 9 + index] * leadingSign);
 101        }
 1022102        return degree;
 103    }
 104
 105    private static int DivideFiniteValueFactors(ReadOnlySpan<ulong> numerator,
 106        ReadOnlySpan<sbyte> numeratorSigns, ReadOnlySpan<ulong> divisor,
 107        ReadOnlySpan<sbyte> divisorSigns, Span<ulong> quotient, Span<sbyte> quotientSigns)
 108    {
 508109        int degree = numeratorSigns.Length - 1;
 508110        int divisorDegree = divisorSigns.Length - 1;
 508111        int inputWords = numerator.Length / numeratorSigns.Length;
 508112        int divisorWords = divisor.Length / divisorSigns.Length;
 508113        int outputWords = quotient.Length / quotientSigns.Length;
 508114        int bits = Math.Max(GetFiniteRootCoefficientBits(numerator, numeratorSigns.Length),
 508115            GetFiniteRootCoefficientBits(divisor, divisorSigns.Length));
 116        // A partial remainder is divisor times the unprocessed quotient.
 117        // Factor height and at most eight summands bound its coefficients by
 118        // 2*bits+32 bits. Exact integer division introduces no rational field.
 508119        int words = (2 * bits + 95) / 64;
 508120        Span<ulong> remainder = stackalloc ulong[(degree + 1) * words];
 508121        Span<sbyte> remainderSigns = stackalloc sbyte[8];
 508122        Span<ulong> term = stackalloc ulong[words];
 508123        Span<ulong> product = stackalloc ulong[words];
 508124        Span<ulong> integerRemainder = stackalloc ulong[words];
 508125        Span<ulong> division = stackalloc ulong[2 * words + 1];
 4796126        for (int index = 0; index <= degree; index++)
 127        {
 1890128            CopyFiniteRootMagnitude(numerator.Slice(index * inputWords, inputWords),
 1890129                remainder.Slice(index * words, words));
 1890130            remainderSigns[index] = numeratorSigns[index];
 131        }
 508132        ReadOnlySpan<ulong> leading = divisor.Slice(divisorDegree * divisorWords, divisorWords);
 4292133        for (int index = degree; index >= divisorDegree; index--)
 134        {
 1638135            int sign = remainderSigns[index];
 1638136            if (sign == 0)
 137                continue;
 1605138            int shift = index - divisorDegree;
 1605139            WideArithmetic.DivideMagnitudes(remainder.Slice(index * words, words), leading,
 1605140                term, integerRemainder, division);
 141            System.Diagnostics.Debug.Assert(GetRoundedCylinderWideLength(integerRemainder) == 0);
 1605142            CopyFiniteRootMagnitude(term, quotient.Slice(shift * outputWords, outputWords));
 1605143            quotientSigns[shift] = (sbyte)sign;
 8246144            for (int coefficient = 0; coefficient <= divisorDegree; coefficient++)
 145            {
 2518146                if (divisorSigns[coefficient] == 0)
 147                    continue;
 2494148                WideArithmetic.MultiplyMagnitudes(term,
 2494149                    divisor.Slice(coefficient * divisorWords, divisorWords), product);
 2494150                Span<ulong> current = remainder.Slice((shift + coefficient) * words, words);
 2494151                int currentSign = remainderSigns[shift + coefficient];
 2494152                WideArithmetic.AddShiftedSignedMagnitude(product,
 2494153                    -sign * divisorSigns[coefficient], 0, current, ref currentSign);
 2494154                remainderSigns[shift + coefficient] = (sbyte)currentSign;
 155            }
 156        }
 157        System.Diagnostics.Debug.Assert(GetRoundedCylinderWideLength(remainder) == 0);
 158        // Both operands are primitive and positive-leading. Gauss's lemma
 159        // makes their exact quotient primitive and positive-leading too.
 508160        return degree - divisorDegree;
 161    }
 162}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.ValueRootRadicalOffset.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.ValueRootRadicalOffset.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>
 13/// Identifies a known radius-offset value root by exact mapped dyadic cells.
 14/// No polynomial composition or additional algebraic solver is required.
 15/// </content>
 16internal static partial class WideFiniteAxisIntersection
 17{
 18    /// <summary>
 19    /// Maps alpha to beta=(sqrt(alpha)+sourceGapSign*sqrt(radiusSquared/2^valueShift))².
 20    /// The degree-at-most-four source and degree-at-most-eight target use the
 21    /// same scaled variable. Beta must be a positive target root. The radius
 22    /// square is in [0,2^valueShift]; the original signed gap plus radius is
 23    /// positive, so a negative sourceGapSign implies alpha&lt;radiusSquared/2^valueShift.
 24    /// Known membership is a strict precondition, not an absence query. Source
 25    /// storage is unchanged; the returned root borrows the target storage.
 26    /// </summary>
 27    internal static FiniteAxisValueRoot MapKnownFiniteValueRootRadicalOffset(scoped FiniteAxisValueRoot source,
 28        int sourceGapSign, scoped ReadOnlySpan<ulong> radiusSquared, int valueShift,
 29        ReadOnlySpan<ulong> targetCoefficients, ReadOnlySpan<sbyte> targetSigns,
 30        Span<ulong> targetCell)
 31    {
 32        System.Diagnostics.Debug.Assert(source.Signs.Length <= 5 && Math.Abs(sourceGapSign) == 1);
 48433        int sourceBits = GetFiniteRootCoefficientBits(source.Coefficients, source.Signs.Length);
 48434        int targetBits = GetFiniteRootCoefficientBits(targetCoefficients, targetSigns.Length);
 48435        int targetShift = 16 * (targetBits + 11) + 100;
 48436        int sourceShift = targetShift + sourceBits + 8;
 48437        Span<ulong> sourceCell = stackalloc ulong[(Math.Max(sourceShift, source.DenominatorShift) + 127) / 64];
 48438        CopyFiniteRootMagnitude(source.LowerNumerator, sourceCell);
 48439        source.LowerNumerator = sourceCell;
 40
 48441        Span<ulong> batchCells = stackalloc ulong[8 * 8];
 48442        Span<int> batchShifts = stackalloc int[8];
 48443        FiniteAxisValueRoots roots = GetFiniteValueRoots(targetCoefficients, targetSigns, batchCells, batchShifts);
 44
 45        // Cauchy's reciprocal bound gives alpha>2^(-Bf-2); its positive
 46        // cell lower endpoint exceeds alpha/2. Hence |d beta/d alpha| is
 47        // <2^(Bf+4), even across the negative branch's clipped minimum.
 48        // After refinement the mapped width is <2^(-q-4), target width <=2^-q.
 49        // Distinct target roots have separation >2^(-q+11), so overlapping
 50        // closed cells certify equality, using the known target membership.
 154651        for (int ordinal = 0; ordinal < roots.Count - 1; ordinal++)
 52        {
 76653            FiniteAxisValueRoot candidate = FiniteAxisValueRoots.GetRoot(roots, ordinal, targetCoefficients, targetSigns
 76654            int increment = 8;
 76655            bool refineSource = true;
 76656            int relation = ClassifyRadicalOffsetCells(source, sourceGapSign, radiusSquared, valueShift, candidate);
 57            // A singleton source maps to one known target root, so its exact
 58            // point is either contained by this cell or strictly beyond it.
 59            // Unresolved overlap therefore implies a non-singleton source.
 91560            while (relation == 0 && source.DenominatorShift < sourceShift)
 61            {
 62                // Recheck containment after refining only the source. Refining
 63                // both cells together can keep an identity map's cells equal.
 14964                if (refineSource)
 12865                    RefineFiniteValueRoot(ref source, Math.Min(sourceShift, source.DenominatorShift + increment));
 66                else
 67                {
 2168                    RefineFiniteValueRoot(ref candidate, Math.Min(targetShift, candidate.DenominatorShift + increment));
 2169                    increment = Math.Min(2 * increment, sourceShift);
 70                }
 14971                refineSource = !refineSource;
 14972                relation = ClassifyRadicalOffsetCells(source, sourceGapSign, radiusSquared, valueShift, candidate);
 73            }
 76674            if (relation == 0)
 75            {
 76                // The source now meets its width bound. One final target
 77                // refinement supplies the other bound; rational and already
 78                // finer cells are unchanged. Remaining overlap proves equality.
 179                RefineFiniteValueRoot(ref candidate, targetShift);
 180                relation = ClassifyRadicalOffsetCells(source, sourceGapSign, radiusSquared, valueShift, candidate);
 81            }
 76682            if (relation >= 0)
 47783                return candidate;
 84        }
 85        // Beta is a positive target root by contract. The authoritative count
 86        // and exact rejection of every earlier ordinal identify the last root
 87        // constructively, including when compact isolation exhausted capacity.
 788        return FiniteAxisValueRoots.GetRoot(roots, roots.Count - 1, targetCoefficients, targetSigns, targetCell);
 89    }
 90
 91    // -1: disjoint; 0: unresolved overlap; 1: the known target root is identified.
 92    // A mapped interval strictly inside an isolating cell contains its only root.
 93    // A rational target instead requires exact equality of a mapped point.
 94    private static int ClassifyRadicalOffsetCells(FiniteAxisValueRoot source, int sign,
 95        ReadOnlySpan<ulong> radiusSquared, int valueShift, FiniteAxisValueRoot target)
 96    {
 91697        bool sourceUpper = !source.IsRational;
 91698        bool targetUpper = !target.IsRational;
 91699        bool maximumUpper = sign > 0 && sourceUpper;
 916100        bool minimumUpper = sign < 0 && sourceUpper;
 101        // Ascending known-member search has rejected only earlier ordinals,
 102        // hence targetRoot <= beta. The mapped maximum cannot precede this
 103        // candidate; only the mapped minimum can prove it is an earlier root.
 104        // The admitted negative branch decreases below the radius square.
 105        // If the source cell reaches its minimum, the mapped lower bound is
 106        // zero; do not map an endpoint on the other side as a lower bound.
 916107        bool clipped = sign < 0 && CompareRadicalOffsetEndpoints(source.LowerNumerator, source.DenominatorShift, sourceU
 916108            default, 0, 1, radiusSquared, valueShift, false) >= 0;
 916109        int minimumToUpper = clipped ? -1 : CompareRadicalOffsetEndpoints(source.LowerNumerator, source.DenominatorShift
 916110            radiusSquared, valueShift, sign, target.LowerNumerator, target.DenominatorShift, targetUpper);
 916111        if (minimumToUpper > 0)
 289112            return -1;
 113        // With a singleton source, minimum=maximum=beta. The earlier guard
 114        // proves beta<=target, while ascending known membership gives the
 115        // reverse inequality; no second endpoint comparison is needed.
 627116        if (target.IsRational)
 26117            return source.IsRational ? 1 : 0;
 601118        if (clipped)
 25119            return 0;
 576120        return CompareRadicalOffsetEndpoints(source.LowerNumerator, source.DenominatorShift, minimumUpper,
 576121                radiusSquared, valueShift, sign, target.LowerNumerator, target.DenominatorShift, false) > 0
 576122            && CompareRadicalOffsetEndpoints(source.LowerNumerator, source.DenominatorShift, maximumUpper,
 576123                radiusSquared, valueShift, sign, target.LowerNumerator, target.DenominatorShift, true) < 0 ? 1 : 0;
 124    }
 125
 126    // Endpoints are (numerator + upper)/2^shift. Aligning exponents at L
 127    // gives E=A+tau-G and C=A*tau. The comparison is sign(E+2*sign*sqrt(C)).
 128    // Source and target lower numerators are positive: retained root cells
 129    // exclude zero, and dyadic comparisons handle zero before calling here.
 130    // Endpoints lie in (0,1], so E fits L+2 bits and E²/4C fit 2L+4.
 131    // The five-L-bit scratch frame returns before either root arena is live.
 132    private static int CompareRadicalOffsetEndpoints(ReadOnlySpan<ulong> source, int sourceShift, bool sourceUpper,
 133        ReadOnlySpan<ulong> radiusSquared, int valueShift, int sign,
 134        ReadOnlySpan<ulong> target, int targetShift, bool targetUpper)
 135    {
 33605136        int shift = Math.Max(sourceShift, Math.Max(valueShift, targetShift));
 33605137        int words = (shift + 65) / 64;
 33605138        Span<ulong> rational = stackalloc ulong[words];
 33605139        Span<ulong> one = stackalloc ulong[1] { 1 };
 33605140        rational.Clear();
 33605141        int rationalSign = 0;
 33605142        int radiusSign = GetRoundedCylinderWideLength(radiusSquared) == 0 ? 0 : 1;
 33605143        WideArithmetic.AddShiftedSignedMagnitude(source, 1, shift - sourceShift, rational, ref rationalSign);
 33605144        if (sourceUpper)
 9536145            WideArithmetic.AddShiftedSignedMagnitude(one, 1, shift - sourceShift, rational, ref rationalSign);
 33605146        WideArithmetic.AddShiftedSignedMagnitude(radiusSquared, radiusSign, shift - valueShift, rational, ref rationalSi
 33605147        WideArithmetic.AddShiftedSignedMagnitude(target, -1, shift - targetShift, rational, ref rationalSign);
 33605148        if (targetUpper)
 1380149            WideArithmetic.AddShiftedSignedMagnitude(one, -1, shift - targetShift, rational, ref rationalSign);
 33605150        int radicalSign = radiusSign != 0 ? sign : 0;
 33605151        if (rationalSign == 0)
 2891152            return radicalSign;
 30714153        if (radicalSign == 0 || rationalSign == radicalSign)
 28983154            return rationalSign;
 155
 1731156        Span<ulong> rationalSquared = stackalloc ulong[2 * words];
 1731157        Span<ulong> radicalSquared = stackalloc ulong[2 * words];
 1731158        WideArithmetic.MultiplyMagnitudes(rational, rational, rationalSquared);
 1731159        WideArithmetic.MultiplyMagnitudes(source, radiusSquared, radicalSquared);
 1731160        if (sourceUpper)
 521161            WideArithmetic.AddMagnitudeInto(radiusSquared, radicalSquared);
 1731162        ShiftFiniteRootLeft(radicalSquared, 2 * shift - sourceShift - valueShift + 2);
 1731163        return rationalSign * WideArithmetic.CompareMagnitudeEqualLength(rationalSquared, radicalSquared);
 164    }
 165}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.ValueRootReciprocal.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.ValueRootReciprocal.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/// <content>
 14/// Shared exact value-root isolation for reciprocal polynomial charts.
 15/// </content>
 16internal static partial class WideFiniteAxisIntersection
 17{
 18    /// <summary>
 19    /// Isolates F(t) and its nominal-degree reversal G(s)=s^n F(1/s) on (0,1].
 20    /// Writes G into equally strided caller storage and returns independent
 21    /// compact views. All output spans must be disjoint from each other and F.
 22    /// </summary>
 23    [MethodImpl(MethodImplOptions.NoInlining)] // Release the shared chain before either consumer's returning scratch.
 24    internal static void GetFiniteValueReciprocalRoots(ReadOnlySpan<ulong> coefficients,
 25        ReadOnlySpan<sbyte> signs, Span<ulong> reciprocalCoefficients, Span<sbyte> reciprocalSigns,
 26        Span<ulong> cells, Span<int> shifts, Span<ulong> reciprocalCells, Span<int> reciprocalShifts,
 27        out FiniteAxisValueRoots roots, out FiniteAxisValueRoots reciprocalRoots)
 28    {
 23429        int degree = signs.Length - 1;
 23430        int words = coefficients.Length / signs.Length;
 274431        for (int index = 0; index <= degree; index++)
 32        {
 113833            coefficients.Slice(index * words, words)
 113834                .CopyTo(reciprocalCoefficients.Slice((degree - index) * words, words));
 113835            reciprocalSigns[degree - index] = signs[index];
 36        }
 37        // Nominal leading zeros create a zero factor in G; zero factors in F
 38        // disappear at infinity. Keep their global repeated-root metadata with
 39        // the existing complete owners rather than complicating the shared chain.
 23440        if (degree <= 0 || signs[0] == 0 || signs[degree] == 0)
 41        {
 1442            roots = GetFiniteValueRoots(coefficients, signs, cells, shifts);
 1443            reciprocalRoots = GetFiniteValueRoots(reciprocalCoefficients, reciprocalSigns,
 1444                reciprocalCells, reciprocalShifts);
 1445            return;
 46        }
 88047        cells.Clear(); shifts.Clear(); reciprocalCells.Clear(); reciprocalShifts.Clear();
 22048        bool compact = TryGetFiniteValueRootsBernstein(coefficients, signs, cells, shifts, out int count);
 22049        bool reciprocalCompact = TryGetFiniteValueRootsBernstein(reciprocalCoefficients, reciprocalSigns,
 22050            reciprocalCells, reciprocalShifts, out int reciprocalCount);
 44051        int rationalMask = 0, reciprocalMask = 0;
 22052        bool repeated = false;
 22053        if (!compact || !reciprocalCompact)
 54        {
 15655            int bits = GetFiniteRootCoefficientBits(coefficients, signs.Length);
 15656            Span<ulong> arena = stackalloc ulong[(GetFiniteValueSturmArenaBits(bits, degree) + 63) / 64 + 512];
 15657            Span<int> offsets = stackalloc int[9];
 15658            Span<int> widths = stackalloc int[9];
 15659            Span<int> degrees = stackalloc int[9];
 15660            Span<sbyte> sturmSigns = stackalloc sbyte[81];
 15661            int chainCount = BuildFiniteValueSturm(coefficients, signs, arena,
 15662                offsets, widths, degrees, sturmSigns, out int used);
 15663            repeated = degrees[chainCount - 1] > 0;
 15664            Span<ulong> chain = arena[..used];
 15665            Span<ulong> evaluation = arena[used..];
 15666            if (!compact)
 67            {
 15468                count = GetFiniteValueRootCount(cells[..(cells.Length / 8)], chain, offsets, widths,
 15469                    degrees, sturmSigns, chainCount, evaluation, out int lowerVariations);
 15470                compact = TryIsolateFiniteValueRootCells(coefficients, signs, cells, shifts, count,
 15471                    lowerVariations, chain, offsets, widths, degrees, sturmSigns, chainCount,
 15472                    evaluation, out rationalMask);
 73            }
 15674            if (!reciprocalCompact)
 75            {
 15476                ReverseFiniteValueSturm(chain, offsets, widths, degrees, sturmSigns, chainCount);
 15477                reciprocalCount = GetFiniteValueRootCount(reciprocalCells[..(reciprocalCells.Length / 8)],
 15478                    chain, offsets, widths, degrees, sturmSigns, chainCount, evaluation, out int lowerVariations);
 15479                reciprocalCompact = TryIsolateFiniteValueRootCells(reciprocalCoefficients, reciprocalSigns,
 15480                    reciprocalCells, reciprocalShifts, reciprocalCount, lowerVariations, chain, offsets,
 15481                    widths, degrees, sturmSigns, chainCount, evaluation, out reciprocalMask);
 82            }
 83        }
 22084        roots = new FiniteAxisValueRoots(cells, shifts, count, rationalMask, compact, repeated);
 22085        reciprocalRoots = new FiniteAxisValueRoots(reciprocalCells, reciprocalShifts,
 22086            reciprocalCount, reciprocalMask, reciprocalCompact, repeated);
 22087    }
 88
 89    private static void ReverseFiniteValueSturm(Span<ulong> chain, ReadOnlySpan<int> offsets,
 90        ReadOnlySpan<int> widths, Span<int> degrees, Span<sbyte> signs, int count)
 91    {
 92        // For oriented rows F_i, R_i(s)=(-1)^i s^degree(F_i) F_i(1/s).
 93        // Neighbor signs stay opposite at an internal row zero. At a defining
 94        // root, R_1=s G' (up to the original positive derivative scale).
 95        // Thus variations count distinct roots on positive s, even with abnormal
 96        // degree drops/repetition. Evaluate NEW right limits: reciprocity swaps sides.
 181897        for (int index = 0; index < count; index++)
 98        {
 151099            int degree = degrees[index], words = widths[index];
 755100            Span<ulong> row = chain.Slice(offsets[index], (degree + 1) * words);
 755101            Span<sbyte> rowSigns = signs.Slice(index * 9, degree + 1);
 3330102            for (int coefficient = 0; coefficient < (degree + 1) / 2; coefficient++)
 103            {
 910104                int other = degree - coefficient;
 910105                (rowSigns[coefficient], rowSigns[other]) = (rowSigns[other], rowSigns[coefficient]);
 14022106                for (int word = 0; word < words; word++)
 6101107                    (row[coefficient * words + word], row[other * words + word]) =
 6101108                        (row[other * words + word], row[coefficient * words + word]);
 109            }
 755110            if ((index & 1) != 0)
 2420111                for (int coefficient = 0; coefficient <= degree; coefficient++)
 908112                    rowSigns[coefficient] = (sbyte)-rowSigns[coefficient];
 759113            while (rowSigns[degree] == 0)
 4114                degree--;
 755115            degrees[index] = degree;
 116        }
 154117    }
 118}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.ValueRootRefinement.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.ValueRootRefinement.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>Certified byte-sized refinement of an isolated value-root crossing.</content>
 13internal static partial class WideFiniteAxisIntersection
 14{
 15    // Extra relative-precision bits reduce exact fallback near ill-conditioned
 16    // crossings. This is a measured work budget; it never decides acceptance.
 17    private const int FiniteValuePointGuardBits = 128;
 18
 19    private static bool TryRefineFiniteValueCrossingRoot(ref FiniteAxisValueRoot root,
 20        int targetShift, int lowerSign, int coefficientBits)
 21    {
 22        // Secant values only predict one of 256 child cells. The retained
 23        // parent contains exactly one distinct root; matching opposite signs
 24        // at a child's endpoints therefore certify the very same root.
 25        // No derivative estimate, tolerance, or predictor can admit a cell.
 2756526        bool useByteSteps = targetShift - root.DenominatorShift >= 8;
 2756527        uint lowerHint = 0;
 2756528        uint upperHint = 0;
 2756529        int lowerBits = 0;
 2756530        int upperBits = 0;
 2756531        if (lowerSign == 0 || useByteSteps)
 32        {
 2703833            int shift = root.DenominatorShift;
 2703834            int precision = shift + FiniteValuePointGuardBits + (root.Signs.Length - 1)
 2703835                * (shift - GetFiniteRootBits(root.LowerNumerator) + 1);
 2703836            int lowerCertificate = GetFiniteValueApproximateSign(root.LowerNumerator, shift,
 2703837                root.Coefficients, root.Signs, precision, coefficientBits, out lowerHint, out lowerBits);
 2703838            AddRoundedCylinderWord(root.LowerNumerator, 0, 1);
 2703839            int upperCertificate = GetFiniteValueApproximateSign(root.LowerNumerator, shift,
 2703840                root.Coefficients, root.Signs, precision, coefficientBits, out upperHint, out upperBits);
 2703841            ulong borrow = 1;
 10827242            for (int word = 0; borrow != 0; word++)
 2709843                root.LowerNumerator[word] = WideArithmetic.SubtractWord(root.LowerNumerator[word], 0, ref borrow);
 2703844            if (lowerSign == 0)
 45            {
 46                // The same endpoint certificates both identify an odd crossing
 47                // and supply its prediction hints. An uncertain point is still
 48                // evaluated exactly. Equal signs or a zero parent endpoint
 49                // leave the original cell untouched for the Sturm fallback.
 2133150                lowerSign = lowerCertificate != 0 ? lowerCertificate
 2133151                    : EvaluateFiniteRootPolynomial(root.Coefficients, root.Signs,
 2133152                        root.LowerNumerator, shift, 0);
 2133153                int upperSign = upperCertificate != 0 ? upperCertificate : GetFiniteValueCellUpperSign(root);
 2133154                if (lowerSign == 0 || lowerSign != -upperSign)
 30455                    return false;
 56            }
 2673457            useByteSteps = useByteSteps && lowerCertificate == lowerSign && upperCertificate == -lowerSign;
 58        }
 59
 139051260        while (!root.IsRational && root.DenominatorShift < targetShift)
 61        {
 136367462            if (useByteSteps && targetShift - root.DenominatorShift >= 8)
 63            {
 73189164                int commonBits = Math.Max(lowerBits, upperBits);
 73189165                ulong lowerWeight = commonBits - lowerBits >= 32 ? 0UL : lowerHint >> (commonBits - lowerBits);
 73189166                ulong upperWeight = commonBits - upperBits >= 32 ? 0UL : upperHint >> (commonBits - upperBits);
 67                // At least one aligned hint retains its top bit. The quotient's
 68                // numerator is <2^40 and denominator <2^33, within ulong.
 73189169                ulong child = Math.Min(255UL, (lowerWeight << 8) / (lowerWeight + upperWeight));
 73189170                ShiftFiniteRootLeft(root.LowerNumerator, 8);
 73189171                AddRoundedCylinderWord(root.LowerNumerator, 0, child);
 73189172                int nextShift = root.DenominatorShift + 8;
 73                // This is only a throughput budget, never an acceptance
 74                // epsilon. Both endpoints use exactly this same precision.
 75                // Every candidate is inside the positive parent cell, so
 76                // k<=min(q,B+3), and the existing point scratch bound holds.
 77                // Hints are four scalars; no additional cell/arena is retained.
 73189178                int precision = nextShift + FiniteValuePointGuardBits + (root.Signs.Length - 1)
 73189179                    * (nextShift - GetFiniteRootBits(root.LowerNumerator) + 1);
 73189180                int lowerCertificate = GetFiniteValueApproximateSign(root.LowerNumerator, nextShift,
 73189181                    root.Coefficients, root.Signs, precision, coefficientBits, out lowerHint, out lowerBits);
 73189182                int lowerPointSign = lowerCertificate != 0 ? lowerCertificate
 73189183                    : EvaluateFiniteRootPolynomial(root.Coefficients, root.Signs, root.LowerNumerator, nextShift, 0);
 73189184                if (lowerPointSign == 0)
 85                {
 26186                    KeepFiniteValueRefinementSingleton(ref root, nextShift);
 26187                    return true;
 88                }
 73163089                AddRoundedCylinderWord(root.LowerNumerator, 0, 1);
 73163090                int upperCertificate = GetFiniteValueApproximateSign(root.LowerNumerator, nextShift,
 73163091                    root.Coefficients, root.Signs, precision, coefficientBits, out upperHint, out upperBits);
 73163092                int upperPointSign = upperCertificate != 0 ? upperCertificate
 73163093                    : EvaluateFiniteRootPolynomial(root.Coefficients, root.Signs, root.LowerNumerator, nextShift, 0);
 73163094                if (upperPointSign == 0)
 95                {
 16296                    KeepFiniteValueRefinementSingleton(ref root, nextShift);
 16297                    return true;
 98                }
 73146899                ulong borrow = 1;
 2925890100                for (int word = 0; borrow != 0; word++)
 731477101                    root.LowerNumerator[word] = WideArithmetic.SubtractWord(root.LowerNumerator[word], 0, ref borrow);
 731468102                if (lowerPointSign == lowerSign && upperPointSign == -lowerSign)
 103                {
 717268104                    root.DenominatorShift = nextShift;
 105                    // Exact fallback may validate this cell without providing
 106                    // useful approximate values for another prediction.
 717268107                    useByteSteps = lowerCertificate != 0 && upperCertificate != 0;
 717268108                    continue;
 109                }
 110                // The proposed numerator is 256*oldLower+child, child<256.
 111                // Restore every old word, not merely the low word. Disable
 112                // prediction for the rest of this call after one failed try.
 14200113                ShiftRoundedCylinderWideRight(root.LowerNumerator, 8);
 14200114                useByteSteps = false;
 115            }
 645983116            RefineFiniteCrossingRoot(root.Coefficients, root.Signs, root.LowerNumerator,
 645983117                ref root.DenominatorShift, ref root.IsRational, lowerSign, coefficientBits);
 118        }
 26838119        return true;
 120    }
 121
 122    private static void KeepFiniteValueRefinementSingleton(ref FiniteAxisValueRoot root, int shift)
 123    {
 124        // Bisection discovers the minimal dyadic denominator. A byte step may
 125        // find the same endpoint at a finer grid, so preserve that convention.
 423126        int removed = Math.Min(shift, CountRoundedCylinderTrailingZeroes(root.LowerNumerator));
 423127        ShiftRoundedCylinderWideRight(root.LowerNumerator, removed);
 423128        root.DenominatorShift = shift - removed;
 423129        root.IsRational = true;
 423130    }
 131}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.ValueRootSign.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.ValueRootSign.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>
 13/// Certified signs at retained value roots. A nonzero algebraic-value bound
 14/// makes equality terminate without rounding the root or adding a tolerance.
 15/// </content>
 16internal static partial class WideFiniteAxisIntersection
 17{
 18    /// <summary>
 19    /// Compares the selected root to a nonnegative dyadic threshold. Contact
 20    /// rounding uses at most 462 denominator bits; unlike a general sign
 21    /// query, counting roots at that threshold needs no root refinement.
 22    /// </summary>
 23    internal static int CompareFiniteValueRootToDyadic(FiniteAxisValueRoot root,
 24        ReadOnlySpan<ulong> numerator, int shift)
 25    {
 3224926        int numeratorBits = GetFiniteRootBits(numerator);
 3224927        if (numeratorBits == 0)
 128            return 1;
 3224829        if (numeratorBits > shift)
 30        {
 993131            bool exactlyOne = numeratorBits == shift + 1
 993132                && CountRoundedCylinderTrailingZeroes(numerator) == shift;
 993133            return exactlyOne && root.IsRational && root.DenominatorShift == 0 ? 0 : -1;
 34        }
 35        // Reuse the certified cell before constructing a Sturm chain. With
 36        // zero radius, the shared mapped-endpoint comparison is just an exact
 37        // dyadic comparison. Nonrational cell endpoints are strictly excluded.
 2231738        int lowerComparison = CompareRadicalOffsetEndpoints(root.LowerNumerator, root.DenominatorShift, false,
 2231739            default, 0, 1, numerator, shift, false);
 2231740        if (root.IsRational)
 756041            return lowerComparison;
 1475742        if (lowerComparison >= 0)
 604943            return 1;
 870844        if (CompareRadicalOffsetEndpoints(root.LowerNumerator, root.DenominatorShift, true,
 870845                default, 0, 1, numerator, shift, false) <= 0)
 815846            return -1;
 55047        int bits = GetFiniteRootCoefficientBits(root.Coefficients, root.Signs.Length);
 55048        Span<ulong> arena = stackalloc ulong[(GetFiniteValueSturmArenaBits(bits, root.Signs.Length - 1) + 63) / 64 + 512
 55049        Span<int> offsets = stackalloc int[9];
 55050        Span<int> widths = stackalloc int[9];
 55051        Span<int> degrees = stackalloc int[9];
 55052        Span<sbyte> signs = stackalloc sbyte[81];
 55053        int count = BuildFiniteValueSturm(root.Coefficients, root.Signs, arena,
 55054            offsets, widths, degrees, signs, out int used);
 55055        Span<ulong> chain = arena[..used];
 55056        Span<ulong> evaluation = arena[used..];
 55057        Span<ulong> zero = stackalloc ulong[1] { 0 };
 55058        int throughThreshold = GetFiniteValueVariations(chain, offsets, widths, degrees,
 55059            signs, count, zero, 0, evaluation) - GetFiniteValueVariations(chain, offsets, widths,
 55060            degrees, signs, count, numerator, shift, evaluation);
 55061        if (root.Ordinal >= throughThreshold)
 15062            return 1;
 63        // The threshold is strictly inside this root's isolating cell. Once
 64        // it includes that root, it cannot include another root after it.
 40065        return EvaluateFiniteRootPolynomial(root.Coefficients, root.Signs, numerator, shift, 0, evaluation) == 0
 40066            ? 0 : -1;
 67    }
 68
 69    internal static int CompareFiniteValueRoots(FiniteAxisValueRoot first, FiniteAxisValueRoot second) =>
 123270        CompareFiniteValueRootsCore(first, default, second, default, false);
 71
 72    /// <summary>
 73    /// Compares first/firstDivisor with second/secondDivisor. Divisors are positive
 74    /// integer magnitudes; original defining polynomials and cells remain unchanged.
 75    /// </summary>
 76    internal static int CompareFiniteValueRoots(FiniteAxisValueRoot first, ReadOnlySpan<ulong> firstDivisor,
 77        FiniteAxisValueRoot second, ReadOnlySpan<ulong> secondDivisor) =>
 4478        CompareFiniteValueRootsCore(first, firstDivisor, second, secondDivisor, true);
 79
 80    private static int CompareFiniteValueRootsCore(FiniteAxisValueRoot first, ReadOnlySpan<ulong> firstDivisor,
 81        FiniteAxisValueRoot second, ReadOnlySpan<ulong> secondDivisor, bool certifyScaledIdentity)
 82    {
 127683        bool scaled = false;
 127684        if (certifyScaledIdentity)
 85        {
 4486            firstDivisor = firstDivisor[..WideArithmetic.GetActiveMagnitudeLength(firstDivisor)];
 4487            secondDivisor = secondDivisor[..WideArithmetic.GetActiveMagnitudeLength(secondDivisor)];
 88            System.Diagnostics.Debug.Assert(!firstDivisor.IsEmpty && !secondDivisor.IsEmpty);
 4489            scaled = !firstDivisor.SequenceEqual(secondDivisor);
 90        }
 127691        if (!scaled && first.Signs.SequenceEqual(second.Signs) && first.Coefficients.SequenceEqual(second.Coefficients))
 50692            return first.Ordinal.CompareTo(second.Ordinal);
 77093        if (certifyScaledIdentity)
 94        {
 3895            ReadOnlySpan<ulong> one = stackalloc ulong[1] { 1 };
 96            // Equal divisors cancel even when source polynomials differ by content.
 97            // Keep this new certificate off the existing unscaled hot path.
 3898            if (HaveProportionalScaledValuePolynomials(first, scaled ? firstDivisor : one,
 5499                second, scaled ? secondDivisor : one)) return first.Ordinal.CompareTo(second.Ordinal);
 100        }
 754101        int firstBits = GetFiniteRootCoefficientBits(first.Coefficients, first.Signs.Length);
 754102        int secondBits = GetFiniteRootCoefficientBits(second.Coefficients, second.Signs.Length);
 103        // P(E*x) defines alpha/E without constructing or re-isolating it.
 104        // Degree n and coefficient height B grow by at most n*bitLength(E).
 754105        if (scaled)
 106        {
 22107            firstBits += (first.Signs.Length - 1) * GetFiniteRootBits(firstDivisor);
 22108            secondBits += (second.Signs.Length - 1) * GetFiniteRootBits(secondDivisor);
 109        }
 110        // For unequal selected roots, the resultant/factor separation bound
 111        // is >2^(-8*(Bf+Bg+22)-89), including a shared irreducible factor.
 112        // Equal-width overlapping cells below that gap prove equality.
 754113        int certifiedShift = 8 * (firstBits + secondBits + 22) + 91;
 754114        int shift = Math.Max(first.DenominatorShift, second.DenominatorShift);
 754115        certifiedShift = Math.Max(certifiedShift, shift);
 754116        int words = (certifiedShift + 127) / 64;
 754117        Span<ulong> firstCell = stackalloc ulong[words];
 754118        Span<ulong> secondCell = stackalloc ulong[words];
 754119        int productWords = scaled ? words + Math.Max(firstDivisor.Length, secondDivisor.Length) + 1 : 0;
 754120        Span<ulong> firstProduct = stackalloc ulong[productWords];
 754121        Span<ulong> secondProduct = stackalloc ulong[productWords];
 754122        CopyFiniteRootMagnitude(first.LowerNumerator, firstCell);
 754123        CopyFiniteRootMagnitude(second.LowerNumerator, secondCell);
 754124        var left = new FiniteAxisValueRoot
 754125        {
 754126            Coefficients = first.Coefficients,
 754127            Signs = first.Signs,
 754128            LowerNumerator = firstCell,
 754129            DenominatorShift = first.DenominatorShift,
 754130            IsRational = first.IsRational,
 754131            Ordinal = first.Ordinal
 754132        };
 754133        var right = new FiniteAxisValueRoot
 754134        {
 754135            Coefficients = second.Coefficients,
 754136            Signs = second.Signs,
 754137            LowerNumerator = secondCell,
 754138            DenominatorShift = second.DenominatorShift,
 754139            IsRational = second.IsRational,
 754140            Ordinal = second.Ordinal
 754141        };
 142        int comparison;
 143        bool finished;
 144        do
 145        {
 906146            RefineFiniteValueRoot(ref left, shift);
 906147            RefineFiniteValueRoot(ref right, shift);
 148            // A singleton can be expressed at any finer denominator without
 149            // changing its value. Ordinary cells have exactly this width.
 906150            ShiftFiniteRootLeft(firstCell, shift - left.DenominatorShift);
 906151            ShiftFiniteRootLeft(secondCell, shift - right.DenominatorShift);
 906152            left.DenominatorShift = right.DenominatorShift = shift;
 906153            if (scaled)
 154            {
 155                // Different scaled grids can overlap despite ordered lower endpoints.
 156                // Compare the complete intervals; non-singleton endpoints are open.
 56157                WideArithmetic.MultiplyMagnitudes(firstCell, secondDivisor, firstProduct);
 56158                WideArithmetic.MultiplyMagnitudes(secondCell, firstDivisor, secondProduct);
 56159                comparison = WideArithmetic.CompareMagnitudeEqualLength(firstProduct, secondProduct);
 56160                finished = left.IsRational && right.IsRational;
 56161                if (!finished)
 162                {
 50163                    if (comparison <= 0)
 164                    {
 83165                        if (!left.IsRational) WideArithmetic.AddMagnitudeInto(secondDivisor, firstProduct);
 42166                        finished = WideArithmetic.CompareMagnitudeEqualLength(firstProduct, secondProduct) <= 0;
 42167                        comparison = -1;
 168                    }
 169                    else
 170                    {
 15171                        if (!right.IsRational) WideArithmetic.AddMagnitudeInto(firstDivisor, secondProduct);
 8172                        finished = WideArithmetic.CompareMagnitudeEqualLength(firstProduct, secondProduct) >= 0;
 8173                        comparison = 1;
 174                    }
 54175                    if (!finished && shift == certifiedShift) { comparison = 0; finished = true; }
 176                }
 177            }
 178            else
 179            {
 850180                comparison = WideArithmetic.CompareMagnitudeEqualLength(firstCell, secondCell);
 850181                bool singleton = left.IsRational || right.IsRational;
 850182                if (comparison == 0 && singleton)
 7183                    comparison = left.IsRational == right.IsRational ? 0 : left.IsRational ? -1 : 1;
 850184                finished = comparison != 0 || singleton || shift == certifiedShift;
 185            }
 906186            shift = Math.Min(certifiedShift, Math.Max(32, 2 * shift));
 906187        } while (!finished);
 754188        return comparison;
 189    }
 190
 191    private static bool HaveProportionalScaledValuePolynomials(FiniteAxisValueRoot first, ReadOnlySpan<ulong> firstDivis
 192        FiniteAxisValueRoot second, ReadOnlySpan<ulong> secondDivisor)
 193    {
 42194        if (first.Signs.Length != second.Signs.Length) return false;
 34195        int pivot = 0;
 44196        while (first.Signs[pivot] == 0) pivot++;
 35197        if (second.Signs[pivot] == 0) return false;
 222198        for (int i = 0; i < first.Signs.Length; i++)
 84199            if (first.Signs[i] * second.Signs[pivot] != second.Signs[i] * first.Signs[pivot]) return false;
 30200        int firstWords = first.Coefficients.Length / first.Signs.Length;
 30201        int secondWords = second.Coefficients.Length / second.Signs.Length;
 30202        int powerWords = (first.Signs.Length - pivot - 1) * Math.Max(firstDivisor.Length, secondDivisor.Length);
 30203        int factorWords = Math.Max(firstWords, secondWords) + powerWords + 1;
 30204        int productWords = firstWords + secondWords + powerWords + 1;
 60205        Span<ulong> leftFactor = stackalloc ulong[factorWords], rightFactor = stackalloc ulong[factorWords];
 60206        Span<ulong> left = stackalloc ulong[productWords], right = stackalloc ulong[productWords];
 60207        leftFactor.Clear(); rightFactor.Clear();
 30208        second.Coefficients.Slice(pivot * secondWords, secondWords).CopyTo(leftFactor);
 30209        first.Coefficients.Slice(pivot * firstWords, firstWords).CopyTo(rightFactor);
 210        // With pivot k, proportionality is P[i]*Q[k]*E^(i-k)
 211        // == Q[i]*P[k]*F^(i-k). Positive scaling preserves root ordinals.
 212        // Certifying this identity avoids separation-bound refinement of ties.
 116213        for (int i = pivot + 1; i < first.Signs.Length; i++)
 214        {
 42215            WideArithmetic.MultiplyMagnitudes(leftFactor, firstDivisor, left);
 42216            WideArithmetic.MultiplyMagnitudes(rightFactor, secondDivisor, right);
 84217            left[..factorWords].CopyTo(leftFactor); right[..factorWords].CopyTo(rightFactor);
 42218            if (first.Signs[i] == 0) continue;
 40219            WideArithmetic.MultiplyMagnitudes(first.Coefficients.Slice(i * firstWords, firstWords), leftFactor, left);
 40220            WideArithmetic.MultiplyMagnitudes(second.Coefficients.Slice(i * secondWords, secondWords), rightFactor, righ
 54221            if (WideArithmetic.CompareMagnitudeEqualLength(left, right) != 0) return false;
 222        }
 16223        return true;
 224    }
 225
 226    internal static int GetSignAtFiniteValueRoot(FiniteAxisValueRoot root,
 227        scoped ReadOnlySpan<ulong> coefficients, scoped ReadOnlySpan<sbyte> signs) =>
 1943228        GetSignAtFiniteValueRootCore(ref root, coefficients, signs, retainRefinement: false);
 229
 230    /// <summary>
 231    /// Evaluates a sign and retains certified cell refinement when the caller's
 232    /// numerator storage has room. The numerator and its metadata are updated
 233    /// together; refinements beyond that storage remain local to this query.
 234    /// A knownLowerSign of -1 or +1 certifies opposite nonzero defining-polynomial
 235    /// signs at this cell's endpoints and remains valid for its descendants;
 236    /// zero requests exact endpoint evaluation.
 237    /// </summary>
 238    internal static int GetSignAtFiniteValueRootAndRefine(scoped ref FiniteAxisValueRoot root,
 239        scoped ReadOnlySpan<ulong> coefficients, scoped ReadOnlySpan<sbyte> signs, int knownLowerSign = 0) =>
 185622240        GetSignAtFiniteValueRootCore(ref root, coefficients, signs, retainRefinement: true, knownLowerSign);
 241
 242    private static int GetSignAtFiniteValueRootCore(scoped ref FiniteAxisValueRoot root,
 243        scoped ReadOnlySpan<ulong> coefficients, scoped ReadOnlySpan<sbyte> signs, bool retainRefinement, int knownLower
 244    {
 187565245        int degree = signs.Length - 1;
 187565246        int inputWords = coefficients.Length / signs.Length;
 282453247        while (degree > 0 && signs[degree] == 0)
 94888248            degree--;
 187565249        if (degree == 0)
 17832250            return signs[0];
 169733251        coefficients = coefficients[..((degree + 1) * inputWords)];
 169733252        signs = signs[..(degree + 1)];
 169733253        if (root.IsRational)
 16450254            return EvaluateFiniteRootPolynomial(coefficients, signs,
 16450255                root.LowerNumerator, root.DenominatorShift, 0);
 256
 153283257        int n = root.Signs.Length - 1;
 153283258        int queryBits = GetFiniteRootCoefficientBits(coefficients, degree + 1);
 153283259        int queryCountBits = GetFiniteValueCeilingLog2(degree + 1);
 260        // The entire cell satisfies alpha <= 2^-k: N+1 <= 2^bitLength(N).
 261        // Evaluate z=2^k*alpha in [0,1], with virtual integer coefficients
 262        // c[i]*2^((degree-i)*k). No coefficient or retained cell is rewritten.
 153283263        int variableShift = Math.Max(0, root.DenominatorShift - GetFiniteRootBits(root.LowerNumerator));
 153283264        int evaluationBits = 0;
 1640770265        for (int index = 0; index <= degree; index++)
 667102266            if (signs[index] != 0)
 641790267                evaluationBits = Math.Max(evaluationBits,
 641790268                    GetFiniteRootBits(coefficients.Slice(index * inputWords, inputWords))
 641790269                    + (degree - index) * variableShift);
 153283270        int normalizationBits = evaluationBits - degree * variableShift;
 271        // Scaling also needs k extra cell bits. Use it only when the final
 272        // refinement/scratch bound does not exceed the original one.
 153283273        if (normalizationBits + variableShift > queryBits)
 274        {
 25580275            variableShift = 0;
 25580276            evaluationBits = normalizationBits = queryBits;
 277        }
 278        // Earlier queries may already have narrowed this exact cell. Reuse
 279        // its certified width before copying or refining it: the same error
 280        // proof requires effective shift >= precision + 2*ceilLog2(m+1).
 281        // A nonzero result proves the sign at the retained root; uncertainty
 282        // must still reach the full resultant/equality bound below.
 283        // 59 uses more of the proved cell while retaining the same two-word
 284        // result budget for production queries of degree at most sixteen.
 153283285        int retainedPrecision = Math.Min(59, root.DenominatorShift - variableShift - 2 * queryCountBits);
 153283286        if (retainedPrecision > 0)
 287        {
 113468288            int retainedSign = GetFiniteValueApproximateSign(root.LowerNumerator, root.DenominatorShift,
 113468289                coefficients, signs, retainedPrecision, evaluationBits, variableShift);
 113468290            if (retainedSign != 0)
 88595291                return retainedSign;
 292        }
 64688293        if (degree == 1)
 294        {
 50881295            int lowerSign = EvaluateFiniteRootPolynomial(coefficients, signs,
 50881296                root.LowerNumerator, root.DenominatorShift, 0);
 50881297            Span<ulong> upper = stackalloc ulong[GetRoundedCylinderWideLength(root.LowerNumerator) + 1];
 50881298            CopyFiniteRootMagnitude(root.LowerNumerator, upper);
 50881299            WideArithmetic.AddWord(upper, 0, 1);
 50881300            int upperSign = EvaluateFiniteRootPolynomial(coefficients, signs, upper, root.DenominatorShift, 0);
 301            // A non-singleton root excludes both cell endpoints. A linear
 302            // query with no interior crossing needs no defining-root refinement.
 51636303            if (lowerSign == 0) return upperSign;
 95164304            if (upperSign == 0 || lowerSign == upperSign) return lowerSign;
 305        }
 306        // If Q(alpha)!=0, the integer resultant of Q and alpha's primitive
 307        // minimal polynomial has magnitude >=1. Mahler measure of that
 308        // factor is <=M(F)<=sqrt(n+1)*height(F). Since alpha is in (0,1],
 309        // |Q(alpha)| > 2^-nonzeroBits. No irreducible factor is constructed.
 18895310        int rootBits = GetFiniteRootCoefficientBits(root.Coefficients, n + 1);
 18895311        int nonzeroBits = (n - 1) * (queryBits + queryCountBits)
 18895312            + degree * (rootBits + GetFiniteValueCeilingLog2(n + 1));
 313        // P(z)/2^evaluationBits = Q(alpha)/2^normalizationBits, so the same
 314        // resultant bound applies. normalizationBits may be negative. For an
 315        // integer defining polynomial alpha>2^(-rootBits-1), hence k<=rootBits;
 316        // its leading query term gives normalizationBits>=1-degree*k. The
 317        // resulting certified precision is still strictly positive.
 18895318        int certifiedPrecision = nonzeroBits + normalizationBits + queryCountBits + 4;
 18895319        int maximumShift = Math.Max(root.DenominatorShift,
 18895320            certifiedPrecision + 2 * queryCountBits + variableShift);
 18895321        Span<ulong> cell = stackalloc ulong[(maximumShift + 127) / 64];
 18895322        CopyFiniteRootMagnitude(root.LowerNumerator, cell);
 18895323        var refined = new FiniteAxisValueRoot
 18895324        {
 18895325            Coefficients = root.Coefficients,
 18895326            Signs = root.Signs,
 18895327            LowerNumerator = cell,
 18895328            DenominatorShift = root.DenominatorShift,
 18895329            Ordinal = root.Ordinal
 18895330        };
 18895331        int precision = Math.Min(32, certifiedPrecision);
 332        int result;
 333        bool finished;
 334        do
 335        {
 38827336            RefineFiniteValueRoot(ref refined, precision + 2 * queryCountBits + variableShift, knownLowerSign);
 38827337            if (retainRefinement)
 338            {
 36912339                int activeWords = GetRoundedCylinderWideLength(refined.LowerNumerator);
 36912340                bool fits = activeWords <= root.LowerNumerator.Length;
 36912341                if (fits && !refined.IsRational && activeWords == root.LowerNumerator.Length)
 342                {
 343                    // An open cell also owns its upper endpoint. An all-ones
 344                    // lower numerator fits, but incrementing it would not.
 55345                    int word = 0;
 56346                    while (word < activeWords && refined.LowerNumerator[word] == ulong.MaxValue)
 1347                        word++;
 55348                    fits = word < activeWords;
 349                }
 36912350                if (fits)
 351                {
 352                    // Reuse only a complete proved cell, never a truncated
 353                    // numerator. The local expanded cell still owns all work
 354                    // beyond caller capacity, preserving the same final bound.
 31570355                    refined.LowerNumerator[..activeWords].CopyTo(root.LowerNumerator);
 31570356                    root.LowerNumerator[activeWords..].Clear();
 31570357                    root.DenominatorShift = refined.DenominatorShift;
 31570358                    root.IsRational = refined.IsRational;
 359                }
 360            }
 38827361            if (refined.IsRational)
 860362                return EvaluateFiniteRootPolynomial(coefficients, signs,
 860363                    refined.LowerNumerator, refined.DenominatorShift, 0);
 37967364            result = GetFiniteValueApproximateSign(refined.LowerNumerator, refined.DenominatorShift,
 37967365                coefficients, signs, precision, evaluationBits, variableShift);
 37967366            finished = result != 0 || precision == certifiedPrecision;
 37967367            precision = Math.Min(certifiedPrecision, 2 * precision);
 37967368        } while (!finished);
 18035369        return result;
 370    }
 371
 372    private static int GetFiniteValueApproximateSign(ReadOnlySpan<ulong> numerator, int shift,
 373        ReadOnlySpan<ulong> coefficients, ReadOnlySpan<sbyte> signs, int precision, int coefficientBits,
 374        int variableShift = 0) =>
 848876375        GetFiniteValueApproximateSign(numerator, shift, coefficients, signs, precision, coefficientBits,
 848876376            out _, out _, variableShift);
 377
 378    private static int GetFiniteValueApproximateSign(ReadOnlySpan<ulong> numerator, int shift,
 379        ReadOnlySpan<ulong> coefficients, ReadOnlySpan<sbyte> signs, int precision, int coefficientBits,
 380        out uint leadingMagnitude, out int magnitudeBits, int variableShift = 0)
 381    {
 2366473382        int degree = signs.Length - 1;
 2366473383        int words = (precision + GetFiniteValueCeilingLog2(degree + 1) + 127) / 64;
 2366473384        Span<ulong> result = stackalloc ulong[words];
 2366473385        Span<ulong> product = stackalloc ulong[words + (shift - variableShift + 64) / 64];
 2366473386        int sign = GetFiniteValueApproximateSignCore(numerator, shift, coefficients, signs, precision, coefficientBits,
 2366473387            result, product, out int length, variableShift);
 388        // Only prediction callers need a magnitude hint. Sturm counting borrows
 389        // the same evaluator directly and consumes only its certified sign.
 2366473390        magnitudeBits = length == 0 ? 0 : 64 * length - Fixed64.CountLeadingZeroes(result[length - 1]);
 2366473391        if (magnitudeBits <= 32)
 274083392            leadingMagnitude = magnitudeBits == 0 ? 0U : (uint)result[0] << (32 - magnitudeBits);
 393        else
 394        {
 2092390395            int discarded = magnitudeBits - 32;
 2092390396            int word = discarded / 64;
 2092390397            int offset = discarded % 64;
 2092390398            ulong leading = result[word] >> offset;
 2092390399            if (offset > 32)
 1768484400                leading |= result[word + 1] << (64 - offset);
 2092390401            leadingMagnitude = (uint)leading;
 402        }
 2092390403        return sign;
 404    }
 405
 406    /// <summary>
 407    /// Encloses a polynomial nonnegative at the retained unit-interval root,
 408    /// normalized by 2^coefficientBits, in units of 2^-precision. At most 17
 409    /// coefficients and precision in [1,59] keep both endpoints within ulong:
 410    /// 17*2^59 + 34 is less than 2^64.
 411    /// coefficientBits must cover every coefficient. An open cell requires
 412    /// shift >= precision + 2*ceilLog2(coefficientCount); a singleton needs no
 413    /// cell-width margin. Neither coefficients nor the root are changed.
 414    /// </summary>
 415    internal static void GetFiniteValueRootNonnegativeBounds(FiniteAxisValueRoot root,
 416        ReadOnlySpan<ulong> coefficients, ReadOnlySpan<sbyte> signs, int precision, int coefficientBits,
 417        out ulong minimum, out ulong maximum)
 418    {
 4255419        Span<ulong> value = stackalloc ulong[2];
 4255420        Span<ulong> product = stackalloc ulong[2 + (root.DenominatorShift + 64) / 64];
 4255421        int sign = GetFiniteValueApproximateSignCore(root.LowerNumerator, root.DenominatorShift,
 4255422            coefficients, signs, precision, coefficientBits, value, product, out _);
 423        // The existing compact Horner proof bounds quantization, products and
 424        // cell movement by <2*count units. Reuse its full compact magnitude:
 425        // count*2^precision <2^64 proves the second result word is zero.
 4255426        ulong error = 2UL * (ulong)signs.Length;
 427        // A certified nonzero sign must be positive under the precondition.
 428        // Its magnitude exceeds error; uncertainty retains the nonnegative zero.
 4255429        minimum = sign == 0 ? 0 : value[0] - error;
 4255430        maximum = value[0] + error;
 4255431    }
 432
 433    private static int GetFiniteValueApproximateSignCore(ReadOnlySpan<ulong> numerator, int shift,
 434        ReadOnlySpan<ulong> coefficients, ReadOnlySpan<sbyte> signs, int precision, int coefficientBits,
 435        Span<ulong> result, Span<ulong> product, out int length, int variableShift = 0)
 436    {
 2803023437        int degree = signs.Length - 1;
 2803023438        int inputWords = coefficients.Length / signs.Length;
 2803023439        int words = result.Length;
 2803023440        shift -= variableShift;
 2803023441        result.Clear();
 2803023442        int resultSign = 0;
 443        // Evaluate the virtual polynomial P/2^coefficientBits at the supplied
 444        // dyadic point in [0,1]. Point-refinement callers use variableShift=0.
 445        // Coefficient quantization contributes <degree+1 units and product
 446        // truncation <degree units. A nonzero result certifies the point sign;
 447        // zero alone is uncertain, and midpoint refinement then evaluates exactly.
 448        // The root-query caller additionally enforces
 449        // shift>=precision+2*ceilLog2(m+1), so the normalized derivative
 450        // m(m+1)/2 bounds its cell error by <1/2 unit. Its total error remains
 451        // <2*(degree+1). At that caller's final precision any nonzero
 452        // normalized value exceeds 16*(degree+1) units: an uncertain result
 453        // there proves exact zero, just as in the unnormalized formulation.
 27938014454        for (int index = degree; index >= 0; index--)
 455        {
 11165984456            WideArithmetic.MultiplyMagnitudes(result, numerator, product);
 11165984457            ShiftRoundedCylinderWideRight(product, shift);
 11165984458            product[..words].CopyTo(result);
 11165984459            if (GetRoundedCylinderWideLength(result) == 0)
 2936598460                resultSign = 0;
 11165984461            ReadOnlySpan<ulong> coefficient = coefficients.Slice(index * inputWords, inputWords);
 11165984462            int retainedBits = precision - coefficientBits + (degree - index) * variableShift;
 11165984463            if (retainedBits >= 0)
 464            {
 5777478465                WideArithmetic.AddShiftedSignedMagnitude(coefficient, signs[index],
 5777478466                    retainedBits, result, ref resultSign);
 467            }
 468            else
 469            {
 470                // Drop whole low words before copying, so even a huge input
 471                // coefficient fits the compact product buffer. Only 0..63
 472                // remaining low bits need shifting. Magnitude truncation
 473                // followed by the original sign is truncation toward zero.
 5388506474                int discarded = -retainedBits;
 475                // Virtual normalization can discard the complete stored
 476                // coefficient; an empty source quantizes to zero exactly.
 5388506477                CopyFiniteRootMagnitude(coefficient[Math.Min(discarded / 64, inputWords)..], product);
 5388506478                ShiftRoundedCylinderWideRight(product, discarded % 64);
 5388506479                int coefficientSign = GetRoundedCylinderWideLength(product) == 0 ? 0 : signs[index];
 5388506480                WideArithmetic.AddShiftedSignedMagnitude(product, coefficientSign, 0, result, ref resultSign);
 481            }
 482        }
 2803023483        length = GetRoundedCylinderWideLength(result);
 2803023484        return length > 1 || (length == 1 && result[0] > 2UL * (ulong)(degree + 1)) ? resultSign : 0;
 485    }
 486
 487    private static int GetFiniteValueCeilingLog2(int value) =>
 488        // Only nonconstant defining and query polynomials reach these callers.
 2970946489        64 - Fixed64.CountLeadingZeroes((ulong)(value - 1));
 490}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/WideFiniteAxisIntersection.Witness.cs

#LineLine coverage
 1//=======================================================================
 2// WideFiniteAxisIntersection.Witness.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
 8namespace FixedMathSharp.Geometry;
 9
 10/// <content>
 11/// Provides witness/support point computation for finite axis and capsule
 12/// intersection queries, including centered axis endpoints and capsule supports.
 13/// </content>
 14internal static partial class WideFiniteAxisIntersection
 15{
 116    private static readonly Signed192 DirectionRootScale = new(0UL, 1UL, 0UL);
 17
 18    internal static bool TryGetCenteredAxisEndpoint(
 19        Vector2d center,
 20        Vector2d axis,
 21        Fixed64 axisLength,
 22        bool positive,
 23        out Vector2d endpoint) =>
 224        TryGetCenteredSupport(
 225            center,
 226            axis,
 227            axisLength,
 228            Fixed64.Zero,
 229            default,
 230            default,
 231            default,
 232            positive,
 233            out endpoint);
 34
 35    internal static bool TryGetCenteredAxisEndpoint(
 36        Vector3d center,
 37        Vector3d axis,
 38        Fixed64 axisLength,
 39        bool positive,
 40        out Vector3d endpoint) =>
 26541        TryGetCenteredSupport(
 26542            center,
 26543            axis,
 26544            axisLength,
 26545            Fixed64.Zero,
 26546            default,
 26547            default,
 26548            default,
 26549            default,
 26550            positive,
 26551            out endpoint);
 52
 53    internal static bool TryGetCenteredCapsuleSupport(
 54        Vector2d center,
 55        Vector2d axis,
 56        Fixed64 axisLength,
 57        Fixed64 radius,
 58        Vector2d direction,
 59        out Vector2d support)
 60    {
 461        GetDirection(direction, out Signed192 radialX, out Signed192 radialY, out Signed320 radialSquared);
 462        int axialSign = GetDot(axis, Vector2d.Zero, direction, Vector2d.Zero).Sign;
 463        return TryGetCenteredSupport(
 464            center,
 465            axis,
 466            axialSign == 0 ? Fixed64.Zero : axisLength,
 467            radius,
 468            radialX,
 469            radialY,
 470            radialSquared,
 471            axialSign > 0,
 472            out support);
 73    }
 74
 75    internal static FixedPointAnchor2d GetCenteredCapsuleSupportAnchor(
 76        Vector2d center,
 77        Fixed64 frameRotation,
 78        Vector2d localAxis,
 79        Fixed64 axisLength,
 80        Fixed64 radius,
 81        Vector2d localDirection)
 82    {
 49983        int axialSign =
 49984            GetDot(
 49985                localAxis,
 49986                Vector2d.Zero,
 49987                localDirection,
 49988                Vector2d.Zero).Sign;
 49989        Fixed64 halfLength = axialSign == 0
 49990            ? Fixed64.Zero
 49991            : axisLength / Fixed64.Two;
 49992        Vector2d axialOffset =
 49993            localAxis * (axialSign > 0 ? halfLength : -halfLength);
 49994        Vector2d radialDirection =
 49995            WideNormalization.GetNormalized(localDirection);
 49996        Vector2d radialOffset = radialDirection * radius;
 49997        FixedPointAnchorTerm2d exactLocalTerm =
 49998            FixedPointAnchorTerm2d.CreateCenteredAxisSupport(
 49999                localAxis,
 499100                axialSign == 0
 499101                    ? Fixed64.Zero
 499102                    : axialSign > 0
 499103                        ? axisLength
 499104                        : -axisLength,
 499105                radialDirection,
 499106                radius,
 499107                axialOffset,
 499108                radialOffset);
 499109        return new FixedPointAnchor2d(
 499110            center,
 499111            frameRotation,
 499112            axialOffset,
 499113            radialOffset,
 499114            exactLocalTerm);
 115    }
 116
 117    internal static bool TryGetCenteredCapsuleSupport(
 118        Vector3d center,
 119        Vector3d axis,
 120        Fixed64 axisLength,
 121        Fixed64 radius,
 122        Vector3d direction,
 123        out Vector3d support)
 124    {
 268125        GetDirection(
 268126            direction,
 268127            out Signed192 radialX,
 268128            out Signed192 radialY,
 268129            out Signed192 radialZ,
 268130            out Signed320 radialSquared);
 268131        int axialSign = GetDot(axis, Vector3d.Zero, direction, Vector3d.Zero).Sign;
 268132        return TryGetCenteredSupport(
 268133            center,
 268134            axis,
 268135            axialSign == 0 ? Fixed64.Zero : axisLength,
 268136            radius,
 268137            radialX,
 268138            radialY,
 268139            radialZ,
 268140            radialSquared,
 268141            axialSign > 0,
 268142            out support);
 143    }
 144
 145    internal static bool TryGetCenteredFiniteCylinderSupport(
 146        Vector3d center,
 147        Vector3d axis,
 148        Fixed64 axisLength,
 149        Fixed64 radius,
 150        Vector3d direction,
 151        out Vector3d support)
 152    {
 264153        GetPlaneDirection(
 264154            direction,
 264155            axis,
 264156            out Signed192 radialX,
 264157            out Signed192 radialY,
 264158            out Signed192 radialZ,
 264159            out Signed320 radialSquared);
 264160        return TryGetCenteredSupport(
 264161            center,
 264162            axis,
 264163            axisLength,
 264164            radius,
 264165            radialX,
 264166            radialY,
 264167            radialZ,
 264168            radialSquared,
 264169            GetDot(axis, Vector3d.Zero, direction, Vector3d.Zero).Sign > 0,
 264170            out support);
 171    }
 172
 173    internal static bool TryGetCenteredFiniteConeSupport(
 174        Vector3d center,
 175        Vector3d axis,
 176        Fixed64 height,
 177        Fixed64 radius,
 178        Vector3d direction,
 179        out Vector3d support)
 180    {
 263181        GetPlaneDirection(
 263182            direction,
 263183            axis,
 263184            out Signed192 radialX,
 263185            out Signed192 radialY,
 263186            out Signed192 radialZ,
 263187            out Signed320 radialSquared);
 263188        bool apex = IsCenteredFiniteConeApexSupportCore(
 263189            axis,
 263190            height,
 263191            radius,
 263192            direction,
 263193            radialX,
 263194            radialY,
 263195            radialZ,
 263196            radialSquared);
 263197        return TryGetCenteredSupport(
 263198            center,
 263199            axis,
 263200            height,
 263201            apex ? Fixed64.Zero : radius,
 263202            apex ? default : radialX,
 263203            apex ? default : radialY,
 263204            apex ? default : radialZ,
 263205            apex ? default : radialSquared,
 263206            apex,
 263207            out support);
 208    }
 209
 210    private static bool TryGetCenteredSupport(
 211        Vector2d center,
 212        Vector2d axis,
 213        Fixed64 axisLength,
 214        Fixed64 radius,
 215        Signed192 radialX,
 216        Signed192 radialY,
 217        Signed320 radialSquared,
 218        bool positive,
 219        out Vector2d support)
 220    {
 6221        bool representable = TryGetSupportCoordinate(
 6222                center.X, axis.X, axisLength, radius, radialX, radialSquared, positive, out Fixed64 x)
 6223            & TryGetSupportCoordinate(
 6224                center.Y, axis.Y, axisLength, radius, radialY, radialSquared, positive, out Fixed64 y);
 6225        if (!representable)
 226        {
 2227            support = default;
 2228            return false;
 229        }
 230
 4231        support = new Vector2d(x, y);
 4232        return true;
 233    }
 234
 235    private static bool TryGetCenteredSupport(
 236        Vector3d center,
 237        Vector3d axis,
 238        Fixed64 axisLength,
 239        Fixed64 radius,
 240        Signed192 radialX,
 241        Signed192 radialY,
 242        Signed192 radialZ,
 243        Signed320 radialSquared,
 244        bool positive,
 245        out Vector3d support)
 246    {
 1060247        bool representable = TryGetSupportCoordinate(
 1060248                center.X, axis.X, axisLength, radius, radialX, radialSquared, positive, out Fixed64 x)
 1060249            & TryGetSupportCoordinate(
 1060250                center.Y, axis.Y, axisLength, radius, radialY, radialSquared, positive, out Fixed64 y)
 1060251            & TryGetSupportCoordinate(
 1060252                center.Z, axis.Z, axisLength, radius, radialZ, radialSquared, positive, out Fixed64 z);
 1060253        if (!representable)
 254        {
 10255            support = default;
 10256            return false;
 257        }
 258
 1050259        support = new Vector3d(x, y, z);
 1050260        return true;
 261    }
 262
 263    private static bool TryGetSupportCoordinate(
 264        Fixed64 center,
 265        Fixed64 axis,
 266        Fixed64 axisLength,
 267        Fixed64 radius,
 268        Signed192 radial,
 269        Signed320 radialSquared,
 270        bool positive,
 271        out Fixed64 coordinate)
 272    {
 3192273        Signed192 signedLength = Signed192.Signed(axisLength.m_rawValue);
 3192274        if (!positive)
 2394275            signedLength = WideArithmetic.SubtractSigned192(default, signedLength);
 3192276        Signed320 baseNumerator = WideArithmetic.AddSigned320(
 3192277            WideArithmetic.MultiplySigned192(
 3192278                Signed192.Signed(center.m_rawValue),
 3192279                DoubleParameterScale),
 3192280            WideArithmetic.MultiplySigned192(
 3192281                Signed192.Signed(axis.m_rawValue),
 3192282                signedLength));
 3192283        if (radius == Fixed64.Zero || radialSquared.IsZero)
 284        {
 817285            return Fixed64.TryGetSignedRawRatio(
 817286                Signed576.ExtendValue(baseNumerator),
 817287                Signed576.ExtendValue(
 817288                    Signed320.ExtendValue(DoubleParameterScale)),
 817289                out coordinate);
 290        }
 291
 2375292        Signed320 radialFactor = WideArithmetic.MultiplySigned192(
 2375293            Signed192.Signed(radius.m_rawValue),
 2375294            radial);
 2375295        if (CompareSupportCoordinateToMidpoint(
 2375296                baseNumerator,
 2375297                radialFactor,
 2375298                radialSquared,
 2375299                GetMinimumMidpoint()) < 0
 2375300            || CompareSupportCoordinateToMidpoint(
 2375301                baseNumerator,
 2375302                radialFactor,
 2375303                radialSquared,
 2375304                GetMaximumMidpoint()) >= 0)
 305        {
 8306            coordinate = default;
 8307            return false;
 308        }
 309
 310        // The square-root helper contributes one Q32.32 scale. Pre-scaling
 311        // the radicand by another scale yields floor(sqrt(q) * 2^64), keeping
 312        // the candidate within a sub-raw-unit interval before exact correction.
 2367313        Signed576 scaledSquared = WideArithmetic.MultiplySigned576(
 2367314            Signed576.ExtendValue(radialSquared),
 2367315            DirectionRootScale);
 2367316        Signed320 scaledRoot = WideArithmetic.GetFloorSquareRootScaledByFixed64(scaledSquared);
 2367317        Signed576 numerator = WideArithmetic.AddSigned576(
 2367318            WideArithmetic.MultiplySigned320(baseNumerator, scaledRoot),
 2367319            WideArithmetic.MultiplySigned576(
 2367320                WideArithmetic.MultiplySigned576(
 2367321                    Signed576.ExtendValue(radialFactor),
 2367322                    DirectionRootScale),
 2367323                DoubleParameterScale));
 2367324        Signed576 denominator = WideArithmetic.MultiplySigned320(
 2367325            scaledRoot,
 2367326            Signed320.ExtendValue(DoubleParameterScale));
 2367327        if (!Fixed64.TryGetSignedRawRatio(numerator, denominator, out coordinate))
 328        {
 2329            coordinate = numerator.Sign < 0 ? Fixed64.MinValue : Fixed64.MaxValue;
 330        }
 331
 2367332        long raw = coordinate.m_rawValue;
 333        // A nonzero nonsquare radical cannot equal a rational half-step; when
 334        // the radical is rational, scaledRoot is exact and already rounds ties
 335        // to even. Only a strict midpoint crossing can need correction.
 2367336        if (raw > long.MinValue)
 337        {
 2366338            Signed192 lowerMidpoint = WideArithmetic.SubtractSigned192(
 2366339                WideArithmetic.AddSigned192(
 2366340                    Signed192.Signed(raw),
 2366341                    Signed192.Signed(raw)),
 2366342                Scale);
 2366343            int lowerComparison = CompareSupportCoordinateToMidpoint(
 2366344                baseNumerator,
 2366345                radialFactor,
 2366346                radialSquared,
 2366347                lowerMidpoint);
 2366348            if (lowerComparison < 0)
 349            {
 3350                coordinate = Fixed64.FromRaw(raw - 1L);
 3351                return true;
 352            }
 353        }
 354
 2364355        if (raw < long.MaxValue)
 356        {
 2363357            Signed192 upperMidpoint = WideArithmetic.AddSigned192(
 2363358                WideArithmetic.AddSigned192(
 2363359                    Signed192.Signed(raw),
 2363360                    Signed192.Signed(raw)),
 2363361                Scale);
 2363362            int upperComparison = CompareSupportCoordinateToMidpoint(
 2363363                baseNumerator,
 2363364                radialFactor,
 2363365                radialSquared,
 2363366                upperMidpoint);
 2363367            if (upperComparison > 0)
 3368                coordinate = Fixed64.FromRaw(raw + 1L);
 369        }
 370
 2364371        return true;
 372    }
 373
 374    private static int CompareSupportCoordinateToMidpoint(
 375        Signed320 baseNumerator,
 376        Signed320 radialFactor,
 377        Signed320 radialSquared,
 378        Signed192 midpointTwice)
 379    {
 9478380        Signed320 rational = WideArithmetic.SubtractSigned320(
 9478381            WideArithmetic.AddSigned320(baseNumerator, baseNumerator),
 9478382            WideArithmetic.MultiplySigned192(midpointTwice, DoubleParameterScale));
 9478383        int rationalSign = rational.Sign;
 9478384        int radialSign = radialFactor.Sign;
 9478385        if (rationalSign == 0)
 1386            return radialSign;
 9477387        if (radialSign == 0 || rationalSign == radialSign)
 7101388            return rationalSign;
 389
 2376390        Signed832 rationalSquared = WideArithmetic.MultiplySigned576ToSigned832(
 2376391            WideArithmetic.MultiplySigned320(rational, rational),
 2376392            Signed576.ExtendValue(radialSquared));
 2376393        Signed192 doubleDenominator = WideArithmetic.AddSigned192(
 2376394            DoubleParameterScale,
 2376395            DoubleParameterScale);
 2376396        Signed832 radialSquaredValue = WideArithmetic.MultiplySigned576ToSigned832(
 2376397            WideArithmetic.MultiplySigned320(radialFactor, radialFactor),
 2376398            Signed576.ExtendValue(
 2376399                WideArithmetic.MultiplySigned192(doubleDenominator, doubleDenominator)));
 2376400        int magnitudeComparison = WideArithmetic.SubtractSigned832(
 2376401            rationalSquared,
 2376402            radialSquaredValue).Sign;
 2376403        return rationalSign * magnitudeComparison;
 404    }
 405
 406    internal static bool IsCenteredFiniteConeApexSupport(
 407        Vector3d axis,
 408        Fixed64 height,
 409        Fixed64 radius,
 410        Vector3d direction)
 411    {
 4707412        GetPlaneDirection(
 4707413            direction,
 4707414            axis,
 4707415            out Signed192 radialX,
 4707416            out Signed192 radialY,
 4707417            out Signed192 radialZ,
 4707418            out Signed320 radialSquared);
 4707419        return IsCenteredFiniteConeApexSupportCore(
 4707420            axis,
 4707421            height,
 4707422            radius,
 4707423            direction,
 4707424            radialX,
 4707425            radialY,
 4707426            radialZ,
 4707427            radialSquared);
 428    }
 429
 430    private static bool IsCenteredFiniteConeApexSupportCore(
 431        Vector3d axis,
 432        Fixed64 height,
 433        Fixed64 radius,
 434        Vector3d direction,
 435        Signed192 radialX,
 436        Signed192 radialY,
 437        Signed192 radialZ,
 438        Signed320 radialSquared)
 439    {
 4970440        Signed320 axisTerm = WideArithmetic.MultiplySigned192(
 4970441            GetDot(axis, Vector3d.Zero, direction, Vector3d.Zero),
 4970442            Signed192.Signed(height.m_rawValue));
 4970443        if (axisTerm.Sign <= 0)
 4400444            return false;
 570445        if (radius == Fixed64.Zero || radialSquared.IsZero)
 121446            return true;
 447
 449448        Signed320 radialProjection = WideArithmetic.AddSigned320(
 449449            WideArithmetic.AddSigned320(
 449450                WideArithmetic.MultiplySigned192(
 449451                    radialX,
 449452                    Signed192.Signed(direction.X.m_rawValue)),
 449453                WideArithmetic.MultiplySigned192(
 449454                    radialY,
 449455                    Signed192.Signed(direction.Y.m_rawValue))),
 449456            WideArithmetic.MultiplySigned192(
 449457                radialZ,
 449458                Signed192.Signed(direction.Z.m_rawValue)));
 449459        Signed576 radialTerm = WideArithmetic.MultiplySigned576(
 449460            WideArithmetic.MultiplySigned320(
 449461                radialProjection,
 449462                Signed320.ExtendValue(
 449463                    Signed192.Signed(radius.m_rawValue))),
 449464            ParameterScale);
 449465        Signed832 axisSquared = WideArithmetic.MultiplySigned576ToSigned832(
 449466            WideArithmetic.MultiplySigned320(axisTerm, axisTerm),
 449467            Signed576.ExtendValue(radialSquared));
 449468        Signed832 radialTermSquared = WideArithmetic.MultiplySigned576ToSigned832(
 449469            radialTerm,
 449470            radialTerm);
 449471        return WideArithmetic.SubtractSigned832(axisSquared, radialTermSquared).Sign > 0;
 472    }
 473
 474    private static void GetDirection(
 475        Vector2d direction,
 476        out Signed192 x,
 477        out Signed192 y,
 478        out Signed320 squared)
 479    {
 4480        x = Signed192.Signed(direction.X.m_rawValue);
 4481        y = Signed192.Signed(direction.Y.m_rawValue);
 4482        squared = WideArithmetic.AddSigned320(
 4483            WideArithmetic.MultiplySigned192(x, x),
 4484            WideArithmetic.MultiplySigned192(y, y));
 4485    }
 486
 487    private static void GetDirection(
 488        Vector3d direction,
 489        out Signed192 x,
 490        out Signed192 y,
 491        out Signed192 z,
 492        out Signed320 squared)
 493    {
 268494        x = Signed192.Signed(direction.X.m_rawValue);
 268495        y = Signed192.Signed(direction.Y.m_rawValue);
 268496        z = Signed192.Signed(direction.Z.m_rawValue);
 268497        squared = WideArithmetic.AddSigned320(
 268498            WideArithmetic.AddSigned320(
 268499                WideArithmetic.MultiplySigned192(x, x),
 268500                WideArithmetic.MultiplySigned192(y, y)),
 268501            WideArithmetic.MultiplySigned192(z, z));
 268502    }
 503
 504    private static void GetPlaneDirection(
 505        Vector3d direction,
 506        Vector3d axis,
 507        out Signed192 x,
 508        out Signed192 y,
 509        out Signed192 z,
 510        out Signed320 squared)
 511    {
 5234512        Signed192 axisSquared = GetDot(axis, Vector3d.Zero, axis, Vector3d.Zero);
 5234513        Signed192 projection = GetDot(axis, Vector3d.Zero, direction, Vector3d.Zero);
 5234514        x = Signed192.NarrowValue(WideArithmetic.MultiplySubtract(
 5234515            axisSquared,
 5234516            Signed192.Signed(direction.X.m_rawValue),
 5234517            Signed192.Signed(axis.X.m_rawValue),
 5234518            projection));
 5234519        y = Signed192.NarrowValue(WideArithmetic.MultiplySubtract(
 5234520            axisSquared,
 5234521            Signed192.Signed(direction.Y.m_rawValue),
 5234522            Signed192.Signed(axis.Y.m_rawValue),
 5234523            projection));
 5234524        z = Signed192.NarrowValue(WideArithmetic.MultiplySubtract(
 5234525            axisSquared,
 5234526            Signed192.Signed(direction.Z.m_rawValue),
 5234527            Signed192.Signed(axis.Z.m_rawValue),
 5234528            projection));
 5234529        squared = WideArithmetic.AddSigned320(
 5234530            WideArithmetic.AddSigned320(
 5234531                WideArithmetic.MultiplySigned192(x, x),
 5234532                WideArithmetic.MultiplySigned192(y, y)),
 5234533            WideArithmetic.MultiplySigned192(z, z));
 5234534    }
 535
 536    private static Signed192 GetMinimumMidpoint() =>
 2375537        WideArithmetic.SubtractSigned192(
 2375538            WideArithmetic.AddSigned192(
 2375539                Signed192.Signed(long.MinValue),
 2375540                Signed192.Signed(long.MinValue)),
 2375541            Scale);
 542
 543    private static Signed192 GetMaximumMidpoint() =>
 2374544        WideArithmetic.AddSigned192(
 2374545            WideArithmetic.AddSigned192(
 2374546                Signed192.Signed(long.MaxValue),
 2374547                Signed192.Signed(long.MaxValue)),
 2374548            Scale);
 549}

Methods/Properties

.ctor(FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed832)
.ctor(FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed320,FixedMathSharp.Signed832)
TryGetDistanceToCenteredAxis(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
TryGetDistanceToCenteredAxis(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
TryGetDistanceBetweenCenteredAxes(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
TryGetDistanceBetweenCenteredAxes(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
DoCenteredCapsulesOverlap(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Boolean)
DoCenteredCapsulesOverlap(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Boolean)
TryGetCenteredCapsulesContact(FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Geometry.FixedContactAnchors2d&)
TryGetCenteredCapsulesContact(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.FixedContactAnchors&)
TryGetClosestPointsBetweenCenteredAxes(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d&,FixedMathSharp.Vector2d&)
TryGetClosestPointsBetweenCenteredAxes(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&)
TryGetClosestOffsetsBetweenCenteredAxes(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&)
GetClosestDirectionBetweenCenteredAxes(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64)
GetClosestCenteredAxisParameters(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&)
GetCenteredAxesSquaredDistance(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Signed832&,FixedMathSharp.Signed320&)
GetCenteredAxesCandidate(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.WideFiniteAxisIntersection/CenteredAxesCandidate2d&)
GetCenteredAxesSquaredDistance(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Signed832&,FixedMathSharp.Signed320&)
GetCenteredAxesCandidate(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.WideFiniteAxisIntersection/CenteredAxesCandidate&)
IsWithinCombinedRadius(FixedMathSharp.Signed832,FixedMathSharp.Signed320,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Boolean)
ProjectSecondFromCenteredAxisCap(FixedMathSharp.Signed320,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&)
ProjectFirstFromCenteredAxisCap(FixedMathSharp.Signed320,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&)
ClampCenteredAxisParameter(FixedMathSharp.Signed320&,FixedMathSharp.Signed320&,FixedMathSharp.Fixed64)
SetCenteredAxisCap(FixedMathSharp.Signed320&,FixedMathSharp.Signed320&,FixedMathSharp.Fixed64)
TryRoundCenteredAxesDistance(FixedMathSharp.Signed832,FixedMathSharp.Signed320,FixedMathSharp.Fixed64&)
GetHalfToEvenIncrement(System.Int32,System.Int64)
TryGetCenteredAxisPointCoordinate(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Fixed64&)
GetCenteredAxisPointNumerator(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Signed320,FixedMathSharp.Signed320)
GetCenteredAxisLocalOffset(FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Vector2d,FixedMathSharp.Signed320,FixedMathSharp.Fixed64)
GetLocalRadialFeature(FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d&,FixedMathSharp.Vector2d&)
GetCenteredCapsulesDepth(FixedMathSharp.Signed832,FixedMathSharp.Signed320,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Boolean,FixedMathSharp.Fixed64&,System.Boolean&)
GetCenteredRadiusDepth(FixedMathSharp.Signed832,FixedMathSharp.Signed576,FixedMathSharp.Signed192,FixedMathSharp.Fixed64&,System.Boolean&)
CompareSquaredDistanceToCenteredDepthThreshold(FixedMathSharp.Signed832,FixedMathSharp.Signed576,FixedMathSharp.Signed192,System.Boolean)
.ctor(FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed832,System.Int32)
TryGetClosestPointsToCenteredAxis(FixedMathSharp.Geometry.FixedTriangle,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&)
DoesCenteredCapsuleTriangleOverlap(FixedMathSharp.Geometry.FixedTriangle,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
TryGetCenteredCapsuleTriangleContact(FixedMathSharp.Geometry.FixedTriangle,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&,FixedMathSharp.Fixed64&)
TryGetCenteredCapsuleTriangleLocalContact(FixedMathSharp.Geometry.FixedTriangle,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d&,FixedMathSharp.Fixed64&,FixedMathSharp.Vector3d&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean,FixedMathSharp.Geometry.AxisTriangleContactFeature&,FixedMathSharp.Vector3d&,System.Int32&,System.Boolean&,System.Int32&,FixedMathSharp.Geometry.RationalSegmentInterval&)
GetClosestCenteredAxisTriangleCandidate(FixedMathSharp.Geometry.FixedTriangle,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.WideFiniteAxisIntersection/AxisTriangleCandidate&)
GetBarycentricCoordinateNumerator(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320)
TryGetCenteredAxisTriangleFaceCandidate(FixedMathSharp.Geometry.FixedTriangle,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.WideFiniteAxisIntersection/AxisTriangleCandidate&,System.Boolean&)
TryCreateCenteredAxisTriangleFaceCandidate(FixedMathSharp.Geometry.FixedTriangle,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Geometry.WideFiniteAxisIntersection/AxisTriangleCandidate&)
KeepCenteredAxisTriangleEdgeCandidate(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.Int32,System.Int32,System.Int32,System.Boolean&,FixedMathSharp.Geometry.WideFiniteAxisIntersection/AxisTriangleCandidate&)
GetClosestCenteredAxisSegmentParameters(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&)
ClampUnitParameter(FixedMathSharp.Signed320&,FixedMathSharp.Signed320&)
ProjectEdgeFromCenteredCap(FixedMathSharp.Signed320,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&)
ProjectAxisFromEdgeCap(FixedMathSharp.Signed320,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&)
SetUnitCapAtCenteredDenominator(FixedMathSharp.Signed320&,FixedMathSharp.Signed320&)
GetRationalPoint(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed576&,FixedMathSharp.Signed576&,FixedMathSharp.Signed576&)
GetRationalSegmentPoint(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed576&,FixedMathSharp.Signed576&,FixedMathSharp.Signed576&)
GetRationalCoordinate(FixedMathSharp.Fixed64,FixedMathSharp.Signed192,FixedMathSharp.Signed320,FixedMathSharp.Signed320)
CreateAxisTriangleCandidate(FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,System.Int32)
GetCenteredCapsuleTriangleDepth(FixedMathSharp.Geometry.WideFiniteAxisIntersection/AxisTriangleCandidate,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,System.Boolean&)
ContainsTriangleProjection(FixedMathSharp.Geometry.FixedTriangle,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,System.Int32&)
GetTriangleEdgeSideSign(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576)
SubtractVertex(FixedMathSharp.Signed576,FixedMathSharp.Fixed64,FixedMathSharp.Signed320)
SubtractVertex(FixedMathSharp.Signed576,FixedMathSharp.Fixed64,FixedMathSharp.Signed576)
GetWideDot(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
GetTriangleNormal(FixedMathSharp.Geometry.FixedTriangle,FixedMathSharp.Signed192&,FixedMathSharp.Signed192&,FixedMathSharp.Signed192&)
TryMaterialize(FixedMathSharp.Geometry.WideFiniteAxisIntersection/AxisTriangleCandidate,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&)
TryOffsetPoint(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d&)
TryGetRelativeCoordinate(FixedMathSharp.Signed576,FixedMathSharp.Fixed64,FixedMathSharp.Signed576,FixedMathSharp.Fixed64&)
TryGetSurfacePointOnCenteredCapsule(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d&)
TryGetSurfaceOffsetOnCenteredCapsule(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d&)
TryGetSurfaceOffsetOnCenteredCapsule(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d&)
TryGetSurfacePointOnCenteredCapsule(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d&)
ContainsPointInCenteredCapsule(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Boolean)
ContainsPointInCenteredCapsule(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Boolean)
GetDirectionFromCenteredAxis(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64)
GetDirectionFromCenteredAxis(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64)
TryGetCapsuleInterval(FixedMathSharp.Geometry.FixedSegment2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean&)
TryGetCapsuleInterval(FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean&)
IsCenteredCapsulePointContained(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed320,System.Boolean)
IsCenteredCapsulePointContained(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed320,System.Boolean)
GetCenteredCap(FixedMathSharp.Signed192,FixedMathSharp.Signed320)
MergeCenteredCapInterval(FixedMathSharp.Geometry.FixedSegment2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,System.Boolean,System.Boolean&,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
MergeCenteredCapInterval(FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,System.Boolean,System.Boolean&,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
GetCenteredCapPolynomial(FixedMathSharp.Geometry.FixedSegment2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,System.Boolean,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&)
GetCenteredCapPolynomial(FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,System.Boolean,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&)
GetCenteredCapConstant(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,System.Boolean)
GetCenteredCapConstant(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,System.Boolean)
GetCenteredCapOffset(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Boolean)
GetRadialComponent(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192)
GetClosestCenteredAxisRatio(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Signed192&,FixedMathSharp.Signed192&)
GetClosestCenteredAxisRatio(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Signed192&,FixedMathSharp.Signed192&)
TryGetCenteredCapsuleSurfaceCoordinate(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
MultiplyThreeToSigned320(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192)
GetCenteredAxisOffsetComponent(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,FixedMathSharp.Signed192)
GetComponentDifference(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
GetScaledDifference(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
ScaleByCenteredAxis(FixedMathSharp.Signed192)
TrySolveUnitQuadratic(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
GetPointToCenteredCapsuleCoreDirection(FixedMathSharp.Geometry.FixedPointAnchor&,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64)
GetSurfaceAnchorOnCenteredCapsule(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d)
GetSurfaceAnchorOnCenteredCapsule(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d)
GetSurfaceAnchorOnCenteredCapsule(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d)
TryGetDistanceToCenteredCapsule(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
TryGetDistanceToCenteredCapsule(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
TryGetDistanceToCenteredCapsule(FixedMathSharp.Signed576,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
.ctor(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed192,FixedMathSharp.Signed320)
DoesCenteredFiniteCylinderPenetrateCapsule(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
DoesCenteredFiniteConePenetrateCapsule(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
CreateStrictFiniteChord(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64)
GetStrictChordCoordinate(FixedMathSharp.Signed192,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Fixed64,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&)
GetStrictRadialPolynomial(FixedMathSharp.Geometry.WideFiniteAxisIntersection/StrictFiniteChord,System.Span`1<FixedMathSharp.Signed576>)
DoesStrictChordEnterCylinder(FixedMathSharp.Geometry.WideFiniteAxisIntersection/StrictFiniteChord,System.ReadOnlySpan`1<FixedMathSharp.Signed576>,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320)
GetStrictPointDistancePolynomial(FixedMathSharp.Geometry.WideFiniteAxisIntersection/StrictFiniteChord,System.ReadOnlySpan`1<FixedMathSharp.Signed576>,FixedMathSharp.Signed320,System.Span`1<FixedMathSharp.Signed576>)
DoesStrictChordEnterExpandedDisk(FixedMathSharp.Geometry.WideFiniteAxisIntersection/StrictFiniteChord,System.ReadOnlySpan`1<FixedMathSharp.Signed576>,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320)
HasNegativeStrictQuadratic(System.ReadOnlySpan`1<FixedMathSharp.Signed576>)
AccumulateStrictRimProduct(System.Span`1<System.UInt64>,System.SByte&,FixedMathSharp.Signed576,FixedMathSharp.Signed576,System.Int32)
DoesStrictChordEnterOffsetConeSide(FixedMathSharp.Geometry.WideFiniteAxisIntersection/StrictFiniteChord,System.ReadOnlySpan`1<FixedMathSharp.Signed576>,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Signed320)
.ctor(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192)
TryGetSweptCenteredCapsuleSegmentFirstDistance(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.FixedSegment2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Vector2d&,System.Boolean)
TryGetSweptCenteredCapsuleOriginOffsetSegmentFirstDistance(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Vector2d&,System.Boolean)
TryGetSweptOriginOffsetSegmentsFirstDistance(FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Vector2d&,System.Boolean)
TryGetSweptCenteredCapsulesFirstDistance(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Vector2d&,System.Boolean)
TryGetSweptAxesFirstDistance(FixedMathSharp.Geometry.WideFiniteAxisIntersection/WideAxis2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.WideFiniteAxisIntersection/WideAxis2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64&,FixedMathSharp.Vector2d&,System.Boolean)
TryKeepEndpointAxisSweep(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Geometry.WideFiniteAxisIntersection/WideAxis2d,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,System.Boolean,System.Boolean&,FixedMathSharp.Fixed64&,FixedMathSharp.Vector2d&)
GetAxisSweepFallbackNormal(FixedMathSharp.Geometry.WideFiniteAxisIntersection/WideAxis2d,FixedMathSharp.Signed192,FixedMathSharp.Signed192)
TryKeepCircleSweep(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,System.Boolean&,FixedMathSharp.Fixed64&)
TryKeepSideSweep(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Geometry.WideFiniteAxisIntersection/WideAxis2d,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,System.Boolean&,FixedMathSharp.Fixed64&)
IsWithinAxisAtDistance(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.WideFiniteAxisIntersection/WideAxis2d,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed320,FixedMathSharp.Signed192)
KeepFirstSweepRoot(FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Fixed64,System.Boolean&,FixedMathSharp.Fixed64&)
GetEndpointAxisNormal(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.WideFiniteAxisIntersection/WideAxis2d,FixedMathSharp.Signed192)
GetPointAtDistance(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,FixedMathSharp.Signed192&,FixedMathSharp.Signed192&)
AxesIntersect(FixedMathSharp.Geometry.WideFiniteAxisIntersection/WideAxis2d,FixedMathSharp.Geometry.WideFiniteAxisIntersection/WideAxis2d)
Orientation(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192)
IsWithinBounds(FixedMathSharp.Geometry.WideFiniteAxisIntersection/WideAxis2d,FixedMathSharp.Signed192,FixedMathSharp.Signed192)
IsBetween(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192)
GetSweepVelocity(FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,System.Boolean,FixedMathSharp.Signed192&,FixedMathSharp.Signed192&,FixedMathSharp.Signed192&,FixedMathSharp.Signed192&)
ScaleSweepCoordinate(FixedMathSharp.Signed192,FixedMathSharp.Signed192)
GetCenteredAxisEndpoints(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.WideFiniteAxisIntersection/WideAxis2d&)
GetAuthoredAxisEndpoints(FixedMathSharp.Geometry.FixedSegment2d,FixedMathSharp.Geometry.WideFiniteAxisIntersection/WideAxis2d&)
GetRotatedOriginOffsetAxisEndpoints(FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Geometry.WideFiniteAxisIntersection/WideAxis2d&)
GetRotatedOriginOffsetCoordinate(FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Signed192&,FixedMathSharp.Signed192&)
GetCenteredEndpointNumerator(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Boolean)
ScaleAuthoredCoordinate(FixedMathSharp.Fixed64)
RayExtend(FixedMathSharp.Signed192)
RayExtend(FixedMathSharp.Signed320)
RayField(System.Span`1<System.UInt64>,System.Span`1<System.Int32>,System.Int32)
GetRetainedRayDot(FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Vector3d)
GetCoincidentCapsuleRayExit(FixedMathSharp.Geometry.WideFiniteAxisIntersection/AxisTriangleCandidate&,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,System.Span`1<System.UInt64>,System.Int32&)
GetCoincidentCapsuleRayExit(FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,System.Span`1<System.UInt64>,System.Int32&)
GetCapsuleRayDepth(FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,System.ReadOnlySpan`1<System.UInt64>,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64&,System.Boolean&)
TryMaterializeCapsuleRayPoint(FixedMathSharp.Geometry.WideFiniteAxisIntersection/AxisTriangleCandidate&,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,System.ReadOnlySpan`1<System.UInt64>,FixedMathSharp.Vector3d&)
MaterializeCapsuleRayComponents(FixedMathSharp.Geometry.WideFiniteAxisIntersection/AxisTriangleCandidate&,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,System.Int32,FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,System.ReadOnlySpan`1<System.UInt64>,FixedMathSharp.FixedQuaternion,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64&,FixedMathSharp.Vector3d&)
TryGetClosestCenteredFiniteConeSurfaceAnchor(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.FixedPointAnchor&,FixedMathSharp.Vector3d&,FixedMathSharp.Fixed64&)
GetClosestCenteredFiniteConeSurfaceAnchor(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d&,System.Boolean&)
GetRigidCenteredConeMeridian(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RigidLocalPointRelation,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean&)
GetClosestCenteredConeSideCoordinates(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RigidLocalPointRelation,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Signed576,FixedMathSharp.Signed320,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
GetCenteredConeSideApproximation(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RigidLocalPointRelation,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Signed576,FixedMathSharp.Signed320,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
RoundBoundedCenteredConeCoordinate(FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Geometry.WideFiniteAxisIntersection/RigidLocalPointRelation,FixedMathSharp.Signed576,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
CompareCenteredConeCoordinateToRaw(FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Geometry.WideFiniteAxisIntersection/RigidLocalPointRelation,FixedMathSharp.Signed576,System.Int64)
GetCenteredConeHeightRelation(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RigidLocalPointRelation,FixedMathSharp.Fixed64,FixedMathSharp.Signed576&,FixedMathSharp.Signed320&)
IsPointContainedInRigidCenteredCone(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RigidLocalPointRelation,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
IsCenteredConeSideNearest(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RigidLocalPointRelation,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Signed576,FixedMathSharp.Signed320)
GetCenteredConeSideParameterNumerator(FixedMathSharp.Signed576,FixedMathSharp.Signed320,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192)
CompareCenteredConeSideParameterToEndpoint(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RigidLocalPointRelation,FixedMathSharp.Signed576,FixedMathSharp.Signed320,FixedMathSharp.Signed192,FixedMathSharp.Signed192,System.Int32)
CompareCenteredConeApexToBaseDistance(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RigidLocalPointRelation,FixedMathSharp.Signed576,FixedMathSharp.Signed320,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,System.Boolean)
CompareCenteredConeInteriorSideToBaseDistance(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RigidLocalPointRelation,FixedMathSharp.Signed576,FixedMathSharp.Signed320,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,System.Boolean)
DoesCenteredFiniteCylindersPenetrate(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
DoStrictCylinderLateralInteriorsMeet(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
IsStrictCylinderAxialParameterInterior(FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Fixed64)
DoesCenteredFiniteCylinderPenetrateCone(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
DoStrictCylinderConeLateralInteriorsMeet(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
IsStrictCylinderConeFieldParameterInterior(FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Fixed64,FixedMathSharp.Signed832)
GetStrictCylinderConeFieldSign(FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832)
IsStrictConeGeneratorBelowBase(FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed192,FixedMathSharp.Signed320,FixedMathSharp.Signed192,FixedMathSharp.Signed576,FixedMathSharp.Fixed64,FixedMathSharp.Signed832)
IsStrictConeGeneratorDistanceLess(FixedMathSharp.Signed576,FixedMathSharp.Signed832,FixedMathSharp.Signed576,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,FixedMathSharp.Signed832)
DoesStrictParallelConeGeneratorEnterCylinder(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed320,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
DoesCenteredFiniteCylinderPenetrateSphere(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64)
DoesCenteredFiniteConePenetrateSphere(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64)
GetStrictSphereLocalPoint(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Signed192&,FixedMathSharp.Signed192&,FixedMathSharp.Signed320&)
IsStrictSphereDiskDistanceLess(FixedMathSharp.Signed320,FixedMathSharp.Signed192,FixedMathSharp.Signed320,FixedMathSharp.Signed192,FixedMathSharp.Signed192)
TwiceStrictSphereValue(FixedMathSharp.Signed320)
CompareStrictSphereRadical(FixedMathSharp.Signed576,FixedMathSharp.Signed320,FixedMathSharp.Signed320)
TryGetClosestCenteredCapsuleSurfaceAnchor(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.FixedPointAnchor&,FixedMathSharp.Vector3d&,FixedMathSharp.Fixed64&)
GetClosestCenteredCapsuleSurfaceAnchor(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d&,System.Boolean&)
GetLocalCapsuleSurfaceDirection(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RigidLocalPointRelation,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.Int32,FixedMathSharp.Vector3d)
GetLocalCapsuleCapDelta(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192)
TryGetClosestCenteredFiniteCylinderSurfaceAnchor(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.FixedPointAnchor&,FixedMathSharp.Vector3d&,FixedMathSharp.Fixed64&)
GetClosestCenteredFiniteCylinderSurfaceAnchor(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d&,System.Boolean&)
TryGetSignedSurfaceDistance(FixedMathSharp.Vector3d,FixedMathSharp.Geometry.FixedPointAnchor&,System.Boolean,FixedMathSharp.Fixed64&)
CreateCenteredCylinderSideAnchor(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,System.Int32,FixedMathSharp.Geometry.WideFiniteAxisIntersection/RigidLocalPointRelation)
CreateCenteredCapsuleAnchors(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,System.Int32,FixedMathSharp.Geometry.WideFiniteAxisIntersection/RigidLocalPointRelation,FixedMathSharp.Geometry.FixedPointAnchor&,FixedMathSharp.Geometry.FixedPointAnchor&)
GetClosestCenteredRigidAxisDistance(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RigidLocalPointRelation,FixedMathSharp.Fixed64,System.Int32)
CreateCenteredSurfaceAnchor(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64)
TryGetCenteredRadialDistance(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RigidLocalPointRelation,FixedMathSharp.Fixed64&)
GetNonNegativeComparisonIncrement(System.Int32)
GetLocalRadialDirection(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RigidLocalPointRelation,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
GetProjection(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Vector3d)
GetCenteredCap(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RigidLocalPointRelation,FixedMathSharp.Fixed64)
GetCenteredRadialBound(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RigidLocalPointRelation,FixedMathSharp.Fixed64)
GetLocalRadialComponents(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed320,FixedMathSharp.Signed192,FixedMathSharp.Vector3d,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&)
GetLocalRadialComponent(FixedMathSharp.Signed192,FixedMathSharp.Signed320,FixedMathSharp.Signed192,FixedMathSharp.Fixed64)
CompareSigned576(FixedMathSharp.Signed576,FixedMathSharp.Signed576)
CompareMagnitudeSigned576(FixedMathSharp.Signed576,FixedMathSharp.Signed576)
DoesCenteredFiniteConeOverlapSphere(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64)
TryGetClosestPointOnCenteredFiniteConeSurface(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&,FixedMathSharp.Fixed64&)
TryGetClosestCenteredFiniteConeSurfaceOffset(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&,FixedMathSharp.Fixed64&)
TryGetClosestPointOnCenteredFiniteCylinderSurface(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&,FixedMathSharp.Fixed64&)
TryGetClosestCenteredFiniteCylinderSurfaceOffset(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&,FixedMathSharp.Fixed64&)
GetCenteredConeSideNormal(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
TryGetTriangleSupportPointContact(FixedMathSharp.Geometry.FixedTriangle,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d&,FixedMathSharp.Fixed64&,System.Boolean&)
TryGetTriangleSupportContactOffsets(FixedMathSharp.Geometry.FixedTriangle,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d&,FixedMathSharp.Fixed64&,System.Boolean&)
GetSupportDifference(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
GetProjectedCoordinateNumerator(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed320,FixedMathSharp.Signed320)
GetAbsoluteCoordinateNumerator(FixedMathSharp.Signed576,FixedMathSharp.Fixed64,FixedMathSharp.Signed576)
TryGetTriangleOriginOffsetCoordinate(FixedMathSharp.Signed576,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Signed576,FixedMathSharp.Fixed64&)
GetClosestClippedAxisSegment(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.RationalSegmentInterval&,System.Int32&)
Multiply(FixedMathSharp.Signed320,FixedMathSharp.Signed192)
Coordinate(FixedMathSharp.Fixed64,FixedMathSharp.Signed192,FixedMathSharp.Signed576,FixedMathSharp.Signed576)
SelectInteriorParameter(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&)
AreSegmentEndpointDistancesAtLeast(FixedMathSharp.Geometry.FixedSegment2d,FixedMathSharp.Geometry.FixedSegment2d,FixedMathSharp.Fixed64)
IsPointSegmentDistanceAtLeast(FixedMathSharp.Vector2d,FixedMathSharp.Geometry.FixedSegment2d,FixedMathSharp.Signed192)
ContainsPointInCenteredFiniteCylinder(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Boolean)
ContainsPointInCenteredFiniteCylinder(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Boolean)
IsCapsulePointContained(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,System.Boolean)
IsFiniteCylinderPointContained(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,System.Boolean)
IsCenteredFiniteCylinderPointContained(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed320,System.Boolean)
IsWithinRadius(FixedMathSharp.Signed192,FixedMathSharp.Signed192,System.Boolean)
GetRadialConstant(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192)
.cctor()
.ctor(FixedMathSharp.Signed192,FixedMathSharp.Signed192)
.ctor(FixedMathSharp.Signed320,FixedMathSharp.Signed320)
TryGetCapsuleInterval(FixedMathSharp.Geometry.FixedSegment2d,FixedMathSharp.Geometry.FixedSegment2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean&)
TryGetCapsuleInterval(FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean&)
TryGetFiniteCylinderInterval(FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean&)
TryGetFiniteCylinderInterval(FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean&)
TryGetFiniteAxisInterval(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Geometry.WideFiniteAxisIntersection/RationalBound320,FixedMathSharp.Geometry.WideFiniteAxisIntersection/RationalBound320,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
TryGetFiniteAxisInterval(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
TryGetAxialInterval(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.WideFiniteAxisIntersection/RationalBound&,FixedMathSharp.Geometry.WideFiniteAxisIntersection/RationalBound&)
TryGetCenteredAxialInterval(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed320,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.WideFiniteAxisIntersection/RationalBound320&,FixedMathSharp.Geometry.WideFiniteAxisIntersection/RationalBound320&)
GetCenteredAxialExtent(FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
GetCenteredAxialExtent(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Fixed64)
Normalize(FixedMathSharp.Signed192,FixedMathSharp.Signed192)
Normalize(FixedMathSharp.Signed320,FixedMathSharp.Signed320)
Compare(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RationalBound,FixedMathSharp.Geometry.WideFiniteAxisIntersection/RationalBound)
Compare(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RationalBound320,FixedMathSharp.Geometry.WideFiniteAxisIntersection/RationalBound320)
GetMaximumBound(FixedMathSharp.Fixed64)
GetMaximumBound320(FixedMathSharp.Fixed64)
Round(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RationalBound)
Round(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RationalBound320)
TryGetCircleInterval(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Signed192,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
TryGetSphereInterval(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Signed192,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
GetExpandedRadius(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
GetSquaredRadius(FixedMathSharp.Signed192)
GetDot(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d)
GetDot(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
Merge(System.Boolean,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Boolean&,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
TrySolveBoundedQuadratic(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Geometry.WideFiniteAxisIntersection/RationalBound320,FixedMathSharp.Geometry.WideFiniteAxisIntersection/RationalBound320,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
TryGetCircleDirectionDistanceInterval(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Geometry.FixedBoundCircle,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
TryGetSphereDirectionDistanceInterval(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.FixedBoundSphere,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
TryGetCircleDistanceInterval(FixedMathSharp.Geometry.FixedSegment2d,FixedMathSharp.Geometry.FixedBoundCircle,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean&)
TryGetSphereDistanceInterval(FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Geometry.FixedBoundSphere,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean&)
TryGetCapsuleDistanceInterval(FixedMathSharp.Geometry.FixedSegment2d,FixedMathSharp.Geometry.FixedSegment2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean&)
TryGetCapsuleDistanceInterval(FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean&)
TryGetCapsuleDistanceInterval(FixedMathSharp.Geometry.FixedSegment2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean&)
TryGetCapsuleDistanceInterval(FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean&)
TryGetFiniteCylinderDistanceInterval(FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean&)
TryGetFiniteCylinderDistanceInterval(FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean&)
TryGetFiniteCylinderDistanceInterval(FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean&)
TryGetFiniteAxisDistanceInterval(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
TryGetFiniteAxisDistanceInterval(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Geometry.WideFiniteAxisIntersection/RationalBound320,FixedMathSharp.Geometry.WideFiniteAxisIntersection/RationalBound320,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
TryGetCircleDistanceInterval(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
TryGetSphereDistanceInterval(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
MergeCenteredCapDistanceInterval(FixedMathSharp.Geometry.FixedSegment2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,System.Boolean,System.Boolean&,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
MergeCenteredCapDistanceInterval(FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,System.Boolean,System.Boolean&,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
TrySolveUnitQuadraticAtDistance(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
TrySolveBoundedQuadraticAtDistance(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Geometry.WideFiniteAxisIntersection/RationalBound320,FixedMathSharp.Geometry.WideFiniteAxisIntersection/RationalBound320,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean)
RoundLowerRootAtDistance(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed576,FixedMathSharp.Fixed64)
RoundUpperRootAtDistance(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed576,FixedMathSharp.Fixed64)
RoundAtDistance(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RationalBound320,FixedMathSharp.Fixed64)
SquareRaw(System.Int64)
DoesFiniteCapDiskEnterSolid(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Int32,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Boolean)
CapCoordinate(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192)
DoesStrictDiskPointEnterSolid(System.ReadOnlySpan`1<FixedMathSharp.Signed320>,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Boolean)
BuildStrictDiskCirclePolynomials(System.ReadOnlySpan`1<FixedMathSharp.Signed320>,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Boolean,System.Span`1<FixedMathSharp.Signed832>,System.Span`1<FixedMathSharp.Signed320>,System.Span`1<FixedMathSharp.Signed320>)
IsStrictSolidSectionWitnessInDisk(System.ReadOnlySpan`1<FixedMathSharp.Signed320>,FixedMathSharp.Signed192,FixedMathSharp.Geometry.WideRationalBasis3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Boolean)
TryGetStrictDiskQuadraticWitness(FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832&,FixedMathSharp.Signed832&)
IsStrictDiskRationalWitnessContained(System.ReadOnlySpan`1<FixedMathSharp.Signed576>,System.ReadOnlySpan`1<FixedMathSharp.Signed576>,FixedMathSharp.Signed576,FixedMathSharp.Fixed64,FixedMathSharp.Signed832,FixedMathSharp.Signed832)
StrictDiskRawSquare(FixedMathSharp.Fixed64)
TwiceStrictDiskValue(FixedMathSharp.Signed832)
AbsoluteStrictDiskValue(FixedMathSharp.Signed832)
TryGetNarrowRadialCoefficients(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed192&,FixedMathSharp.Signed192&,FixedMathSharp.Signed192&)
GetMagnitudeBitLength(FixedMathSharp.Signed320)
GetTrailingZeroCount(FixedMathSharp.Signed320)
CountTrailingZeroes(System.UInt64)
ShiftRightExact(FixedMathSharp.Signed320,System.Int32)
GetShiftedWord(FixedMathSharp.Signed320,System.Int32,System.Int32,System.Int32)
GetSignedWord(FixedMathSharp.Signed320,System.Int32)
AddFiniteAxisPolynomial(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Int32,System.Int32)
MultiplyFiniteAxisPolynomials(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Span`1<System.UInt64>,System.Boolean)
ScaleFiniteAxisPolynomialVariable(System.Span`1<System.UInt64>,System.Int32,System.Int32)
NormalizeFiniteAxisPolynomialPowerOfTwo(System.Span`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>)
NormalizeQuadraticPointPowerOfTwo(FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,System.Span`1<System.UInt64>)
DifferentiateFiniteAxisPolynomial(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Span`1<System.UInt64>,System.Span`1<System.SByte>)
GetFiniteAxisRootCellWords(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>)
TryGetLargestPositiveFiniteAxisRoot(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Span`1<System.UInt64>,FixedMathSharp.Geometry.FiniteAxisPolynomialRoot&)
GetSignAtFiniteAxisRoot(FixedMathSharp.Geometry.FiniteAxisPolynomialRoot&,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>)
GetReducedSignAtFiniteAxisRoot(FixedMathSharp.Geometry.FiniteAxisPolynomialRoot&,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>)
EvaluateFiniteRootAtPositiveFraction(FixedMathSharp.Geometry.FiniteAxisPolynomialRoot,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.UInt64>)
RefineFiniteCrossingRoot(FixedMathSharp.Geometry.FiniteAxisPolynomialRoot&)
RefineFiniteCrossingRoot(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Span`1<System.UInt64>,System.Int32&,System.Boolean&,System.Int32,System.Int32)
ReduceFiniteRootQuery(FixedMathSharp.Geometry.FiniteAxisPolynomialRoot,System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Int32)
ReduceFiniteRootQueryStep(FixedMathSharp.Geometry.FiniteAxisPolynomialRoot,System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Int32)
GetFiniteAxisRootRatioWords(FixedMathSharp.Geometry.FiniteAxisPolynomialRoot,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Int32&,System.Int32&)
ReduceFiniteAxisRootRatio(FixedMathSharp.Geometry.FiniteAxisPolynomialRoot,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Span`1<System.UInt64>,System.Span`1<System.SByte>)
BuildFiniteRootSturm(FixedMathSharp.Geometry.FiniteAxisPolynomialRoot,System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Span`1<System.Int32>)
RefineFiniteRoot(FixedMathSharp.Geometry.FiniteAxisPolynomialRoot&,System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Span`1<System.Int32>,System.Int32,System.Int32,System.Int32)
GetFiniteRootVariations(System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Span`1<System.Int32>,System.Int32,System.ReadOnlySpan`1<System.UInt64>,System.Int32)
EvaluateFiniteRootPolynomial(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.Int32)
EvaluateFiniteRootPolynomial(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.Int32,System.Span`1<System.UInt64>)
GetFiniteRootEvaluationWords(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.Int32,System.Int32&)
EvaluateFiniteRootPolynomialCore(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.Int32,System.Int32,System.Int32,System.Span`1<System.UInt64>)
GetFiniteAxisRootSquareRootBounds(FixedMathSharp.Geometry.FiniteAxisPolynomialRoot,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.UInt64,System.UInt64&,System.UInt64&)
GetFiniteRootIntervalSign(FixedMathSharp.Geometry.FiniteAxisPolynomialRoot,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>)
GetFiniteRootPolynomialBounds(FixedMathSharp.Geometry.FiniteAxisPolynomialRoot,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Span`1<System.UInt64>,System.Span`1<System.UInt64>,System.Int32&,System.Int32&)
GetFiniteRootCoefficientBits(System.ReadOnlySpan`1<System.UInt64>,System.Int32)
GetFiniteRootBits(System.ReadOnlySpan`1<System.UInt64>)
CopyFiniteRootMagnitude(System.ReadOnlySpan`1<System.UInt64>,System.Span`1<System.UInt64>)
ShiftFiniteRootLeft(System.Span`1<System.UInt64>,System.Int32)
GetFiniteRootHermiteIntervalSign(FixedMathSharp.Geometry.FiniteAxisPolynomialRoot,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>)
GetFiniteRootHermiteSignature(FixedMathSharp.Geometry.FiniteAxisPolynomialRoot,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>)
BuildFiniteRootTraces(FixedMathSharp.Geometry.FiniteAxisPolynomialRoot,System.ReadOnlySpan`1<System.UInt64>,System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Int32)
GetFiniteRootSymmetricSignature(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Int32,System.Int32)
GetFiniteRootMatrixIndex(System.Int32,System.Int32,System.Int32)
MoveNextFiniteRootPermutation(System.Span`1<System.Int32>)
TryGetCapsuleInterval(FixedMathSharp.Geometry.FixedRay2d,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.FixedSegment2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean&)
TryGetCapsuleInterval(FixedMathSharp.Geometry.FixedRay,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean&)
TryGetCapsuleInterval(FixedMathSharp.Geometry.FixedRay2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean&)
TryGetCapsuleInterval(FixedMathSharp.Geometry.FixedRay,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean&)
TryGetFiniteCylinderInterval(FixedMathSharp.Geometry.FixedRay,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean&)
TryGetFiniteCylinderInterval(FixedMathSharp.Geometry.FixedRay,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean&)
IsCapsuleContainedAtParameter(FixedMathSharp.Geometry.FixedRay2d,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.FixedSegment2d,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192)
IsCapsuleContainedAtParameter(FixedMathSharp.Geometry.FixedRay,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192)
IsFiniteCylinderContainedAtParameter(FixedMathSharp.Fixed64,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192)
IsCenteredFiniteCylinderContainedAtParameter(FixedMathSharp.Fixed64,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed320)
IsCenteredCapsuleContainedAtParameter(FixedMathSharp.Geometry.FixedRay2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed320)
IsCenteredCapsuleContainedAtParameter(FixedMathSharp.Geometry.FixedRay,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed320)
GetCenteredCap(FixedMathSharp.Signed320,FixedMathSharp.Signed320)
MergeCenteredCapInterval(FixedMathSharp.Geometry.FixedRay2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,System.Boolean,System.Boolean&,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
MergeCenteredCapInterval(FixedMathSharp.Geometry.FixedRay,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,System.Boolean,System.Boolean&,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
GetCenteredCapPolynomial(FixedMathSharp.Geometry.FixedRay2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,System.Boolean,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&)
GetCenteredCapPolynomial(FixedMathSharp.Geometry.FixedRay,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,System.Boolean,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&)
EvaluateLinear(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Fixed64)
GetRadialPolynomial(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&)
IsQuadraticContained(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Fixed64)
IsQuadraticContained(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Fixed64)
TryGetCircleInterval(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
TryGetSphereInterval(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
GetDirectionDot(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d)
GetDirectionDot(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d)
GetDirectionDot(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
GetDirectionDot(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
.ctor(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed320,FixedMathSharp.Signed576,FixedMathSharp.Signed320)
GetRigidLocalPointRelation(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.WideFiniteAxisIntersection/RigidLocalPointRelation&)
RoundLowerRoot(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320)
RoundUpperRoot(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Fixed64)
ShiftLeftByFixed64FractionalBits(FixedMathSharp.Signed320)
EvaluatePolynomial(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed192,FixedMathSharp.Signed192)
EvaluateDerivative(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed192,FixedMathSharp.Signed192)
EvaluatePolynomial(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320)
EvaluateDerivative(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320)
.ctor(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RationalBound320,System.Byte,System.SByte)
TryGetSphericallyExpandedBoxFirstDistance(FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Geometry.FixedBoundBox,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
TryGetSphericallyExpandedProjectedBoxFirstDistance(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
AddAxisBreakpoints(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Byte,System.Span`1<FixedMathSharp.Geometry.WideFiniteAxisIntersection/BoxBreakpoint>,System.Int32&,System.SByte&)
AddProjectedAxisBreakpoints(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,System.Byte,System.Span`1<FixedMathSharp.Geometry.WideFiniteAxisIntersection/BoxBreakpoint>,System.Int32&,System.SByte&)
AddBreakpoint(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,System.Byte,System.SByte,System.Span`1<FixedMathSharp.Geometry.WideFiniteAxisIntersection/BoxBreakpoint>,System.Int32&)
AddProjectedBreakpoint(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,System.Byte,System.SByte,System.Span`1<FixedMathSharp.Geometry.WideFiniteAxisIntersection/BoxBreakpoint>,System.Int32&)
SortBreakpoints(System.Span`1<FixedMathSharp.Geometry.WideFiniteAxisIntersection/BoxBreakpoint>,System.Int32)
GetBoxDistancePolynomial(FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Geometry.FixedBoundBox,System.Span`1<System.SByte>,FixedMathSharp.Fixed64,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&)
GetProjectedBoxDistancePolynomial(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,System.Span`1<System.SByte>,FixedMathSharp.Signed192,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&)
AddBoxAxisPolynomial(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.SByte,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&)
AddProjectedBoxAxisPolynomial(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,System.SByte,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&)
IsWithinProjectedRoundedBox(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192)
GetProjectedBoxAxisOffset(FixedMathSharp.Signed192,FixedMathSharp.Signed192)
.ctor(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed192,FixedMathSharp.Signed192)
.ctor(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576)
.cctor()
.ctor(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed576,System.Int32)
DoesCenteredFiniteCylinderOverlapSphere(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64)
TryGetSphericallyExpandedFiniteCylinderDistanceInterval(FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean&)
TryGetSphericallyExpandedFiniteCylinderFirstDistance(FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
TryGetSphericallyExpandedFiniteCylinderFirstDistanceFromHalfAxisLength(FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
TryGetSphericallyExpandedFiniteCylinderDirectionFirstDistanceFromHalfAxisLength(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
TryGetSphericallyExpandedFiniteCylinderDistanceIntervalWithFullAxisLength(FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Boolean,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean&)
TryGetSphericallyExpandedFiniteCylinderDistanceInterval(FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Boolean,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean&)
MergeSphericallyExpandedFiniteCylinderRoundedIntervals(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RoundedCylinderBaseData,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,System.Boolean,System.Boolean&,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean,System.Boolean,System.Boolean&,System.Boolean&)
TryGetRoundedCylinderCapCoreDistanceInterval(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RoundedCylinderBaseData,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,System.Boolean,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean&)
RoundedCylinderRimBoundsIntersect(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RoundedCylinderBaseData,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,System.Boolean)
CreateRoundedCylinderQuadraticBound(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,System.Boolean,System.Boolean,System.Boolean)
MergeRoundedCylinderRimInterval(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RoundedCylinderTorusPolynomial,FixedMathSharp.Fixed64,System.Boolean&,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean,System.Boolean&,System.Boolean&)
IsPointInRoundedCylinderRimTube(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RoundedCylinderTorusPolynomial,System.Boolean,System.Boolean)
TryGetFiniteCylinderDirectionFirstDistance(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,System.Boolean&)
CreateRoundedCylinderBaseData(FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
CreateRoundedCylinderBaseData(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
CreateRoundedCylinderBaseData(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
CreateRoundedCylinderTorusPolynomial(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RoundedCylinderBaseData,FixedMathSharp.Signed192,System.Boolean)
MultiplyToSigned320(FixedMathSharp.Signed320,FixedMathSharp.Signed192)
MultiplyToSigned320(FixedMathSharp.Signed320,FixedMathSharp.Signed320)
Twice(FixedMathSharp.Signed320)
Four(FixedMathSharp.Signed320)
Twice(FixedMathSharp.Signed576)
AddFive(FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576)
ImportRoundedCylinderCoefficient(FixedMathSharp.Signed576,System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Int32,System.Int32)
ImportRoundedCylinderCoefficient(FixedMathSharp.Signed832,System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Int32,System.Int32)
GetRoundedCylinderCoefficient(System.Span`1<System.UInt64>,System.Int32,System.Int32)
GetRoundedCylinderCoefficientIndex(System.Int32,System.Int32)
AddRoundedCylinderSigned(System.ReadOnlySpan`1<System.UInt64>,System.SByte,System.ReadOnlySpan`1<System.UInt64>,System.SByte,System.Span`1<System.UInt64>,System.SByte&)
SubtractRoundedCylinderSigned(System.ReadOnlySpan`1<System.UInt64>,System.SByte,System.ReadOnlySpan`1<System.UInt64>,System.SByte,System.Span`1<System.UInt64>,System.SByte&)
MultiplyRoundedCylinderWide(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.UInt64>,System.Span`1<System.UInt64>)
MultiplyRoundedCylinderWideByWord(System.ReadOnlySpan`1<System.UInt64>,System.UInt64,System.Span`1<System.UInt64>)
AddRoundedCylinderWord(System.Span`1<System.UInt64>,System.Int32,System.UInt64)
MultiplyRoundedCylinderSigns(System.SByte,System.SByte)
GetRoundedCylinderWideLength(System.ReadOnlySpan`1<System.UInt64>)
CopyRoundedCylinderWide(System.ReadOnlySpan`1<System.UInt64>,System.Span`1<System.UInt64>)
CountRoundedCylinderTrailingZeroes(System.ReadOnlySpan`1<System.UInt64>)
ShiftRoundedCylinderWideRight(System.Span`1<System.UInt64>,System.Int32)
CountProjectionDomainQuarticRoots(FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832)
GetRoundedCylinderInfinityVariations(System.ReadOnlySpan`1<System.SByte>,System.ReadOnlySpan`1<System.Int32>,System.Int32,System.Boolean)
TryGetRoundedCylinderRimDistanceInterval(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RoundedCylinderTorusPolynomial,FixedMathSharp.Fixed64,System.Boolean,System.Boolean,System.Boolean,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
FindRoundedCylinderRoot(System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Span`1<System.Int32>,System.Int32,System.UInt64,System.Int32,System.Int32,System.Int32,System.Boolean)
FindRoundedCylinderSignChangingRoot(System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Int32,System.UInt64,System.UInt64,System.UInt64,System.Int32)
HomogenizeRoundedCylinderSturmSequence(System.Span`1<System.UInt64>,System.Span`1<System.Int32>,System.Int32,System.UInt64)
IsRoundedCylinderEntryBeforeOrEqualToExit(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RoundedCylinderQuadraticBound,FixedMathSharp.Geometry.WideFiniteAxisIntersection/RoundedCylinderQuadraticBound)
GetRoundedCylinderRadicalSquare(FixedMathSharp.Signed320,FixedMathSharp.Signed576,System.Span`1<System.UInt64>,System.Span`1<System.UInt64>,System.Span`1<System.UInt64>)
ImportRoundedCylinderWide(FixedMathSharp.Signed320,System.Span`1<System.UInt64>)
ImportRoundedCylinderWide(FixedMathSharp.Signed576,System.Span`1<System.UInt64>)
GetRoundedCylinderSignVariations(System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Span`1<System.Int32>,System.Int32,System.UInt64,System.UInt64)
EvaluateRoundedCylinderPolynomialSign(System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Int32,System.Int32,System.UInt64,System.UInt64)
EvaluateRoundedCylinderPolynomialSignAtInteger(System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Int32,System.Int32,System.UInt64)
BuildRoundedCylinderSturmSequence(FixedMathSharp.Geometry.WideFiniteAxisIntersection/RoundedCylinderTorusPolynomial,System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Span`1<System.Int32>)
BuildRoundedCylinderQuarticSturmSequence(System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Span`1<System.Int32>)
SetRoundedCylinderProduct(System.ReadOnlySpan`1<System.UInt64>,System.SByte,System.ReadOnlySpan`1<System.UInt64>,System.SByte,System.Span`1<System.UInt64>,System.SByte&)
SetRoundedCylinderScaledProduct(System.ReadOnlySpan`1<System.UInt64>,System.SByte,System.ReadOnlySpan`1<System.UInt64>,System.SByte,System.UInt64,System.Span`1<System.UInt64>,System.SByte&,System.Span`1<System.UInt64>)
SetRoundedCylinderTripleProduct(System.ReadOnlySpan`1<System.UInt64>,System.SByte,System.ReadOnlySpan`1<System.UInt64>,System.SByte,System.ReadOnlySpan`1<System.UInt64>,System.SByte,System.Span`1<System.UInt64>,System.SByte&,System.Span`1<System.UInt64>)
BuildRoundedCylinderNegativeRemainder(System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32)
RemoveRoundedCylinderCommonPowerOfTwo(System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Int32,System.Int32)
TrimRoundedCylinderPolynomial(System.Span`1<System.SByte>,System.Int32,System.Int32)
TryGetScaledCenteredCapsuleTriangleContact(FixedMathSharp.Geometry.FixedTriangle&,FixedMathSharp.Geometry.ScaledRigidFrame3d&,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.FixedContactAnchors&,FixedMathSharp.Geometry.AxisTriangleContactFeature&,System.Int32&,FixedMathSharp.Geometry.RationalSegmentInterval&)
GetClosestScaledAxisTriangleCandidate(FixedMathSharp.Geometry.FixedTriangle&,FixedMathSharp.Geometry.ScaledRigidFrame3d&,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.WideFiniteAxisIntersection/ScaledAxisTriangleCandidate&,FixedMathSharp.Geometry.RationalSegmentInterval&)
GetClosestScaledAxisTriangleCandidate(FixedMathSharp.Geometry.FixedTriangle&,FixedMathSharp.Geometry.ScaledRigidFrame3d&,FixedMathSharp.Geometry.RationalPoint3d&,FixedMathSharp.Geometry.RationalPoint3d&,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.WideFiniteAxisIntersection/ScaledAxisTriangleCandidate&,FixedMathSharp.Geometry.RationalSegmentInterval&)
TryGetScaledCoincidentCapsuleContact(FixedMathSharp.Geometry.WideFiniteAxisIntersection/ScaledAxisTriangleCandidate&,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.FixedContactAnchors&)
GetScaledCoreEndpoint(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.Int32)
TryRoundScaledAxisCoordinate(FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Fixed64&,System.Int64)
AccumulateScaledAxisFace(FixedMathSharp.Geometry.FixedTriangle&,FixedMathSharp.Geometry.ScaledRigidFrame3d&,FixedMathSharp.Geometry.RationalPoint3d&,FixedMathSharp.Geometry.RationalPoint3d&,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.WideFiniteAxisIntersection/ScaledAxisTriangleCandidate,FixedMathSharp.Geometry.WideFiniteAxisIntersection/ScaledAxisTriangleCandidate&)
AccumulateScaledAxisEdge(FixedMathSharp.Geometry.RationalPoint3d&,FixedMathSharp.Geometry.RationalPoint3d&,FixedMathSharp.Geometry.RationalPoint3d&,FixedMathSharp.Geometry.RationalPoint3d&,FixedMathSharp.Vector3d,System.Int32,FixedMathSharp.Geometry.RationalSegmentInterval&,FixedMathSharp.Geometry.WideFiniteAxisIntersection/ScaledAxisTriangleCandidate,FixedMathSharp.Geometry.WideFiniteAxisIntersection/ScaledAxisTriangleCandidate&)
KeepScaledAxisPair(FixedMathSharp.Geometry.RationalPoint3d&,FixedMathSharp.Geometry.RationalPoint3d&,FixedMathSharp.Geometry.RationalPoint3d&,FixedMathSharp.Geometry.RationalPoint3d&,FixedMathSharp.Vector3d,System.Int32,FixedMathSharp.Geometry.RationalSegmentInterval&,FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.WideFiniteAxisIntersection/ScaledAxisTriangleCandidate,FixedMathSharp.Geometry.WideFiniteAxisIntersection/ScaledAxisTriangleCandidate&)
ScaledAxisField(System.Span`1<System.UInt64>,System.Span`1<System.Int32>,System.Int32)
ScaledAxisRelative(FixedMathSharp.Geometry.RationalPoint3d&,FixedMathSharp.Vector3d,System.Int32)
ScaledAxisDifference(FixedMathSharp.Geometry.RationalPoint3d&,FixedMathSharp.Geometry.RationalPoint3d&,FixedMathSharp.Signed192)
ClampScaledAxisParameter(FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic)
ScaledAxisParameterInInterval(FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.RationalSegmentInterval&)
GetScaledAxisIntervalEndpoint(FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.RationalSegmentInterval&)
SetScaledAxisMediant(FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic)
NormalizeScaledAxisRatio(FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic)
GetScaledAxisPrimitiveSeparation(FixedMathSharp.Geometry.WideFiniteAxisIntersection/ScaledAxisTriangleCandidate&,System.Span`1<FixedMathSharp.Signed832>)
TriangleCoordinate(System.Int32)
AxisCoordinate(System.Int32)
get_Denominator()
.ctor(System.Span`1<System.UInt64>,System.Span`1<System.Int32>)
At(System.Int32)
Keep(FixedMathSharp.Geometry.WideFiniteAxisIntersection/ScaledAxisTriangleCandidate)
Complete()
.ctor(System.Int32)
.ctor(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed576,System.Int32)
HasStrictCirclePolynomialPoint(System.ReadOnlySpan`1<FixedMathSharp.Signed832>,System.ReadOnlySpan`1<FixedMathSharp.Signed320>,System.ReadOnlySpan`1<FixedMathSharp.Signed320>)
AddStrictCircleBounds(System.ReadOnlySpan`1<FixedMathSharp.Signed320>,System.Span`1<FixedMathSharp.Geometry.WideFiniteAxisIntersection/StrictCircleBound>,System.Int32&)
InsertStrictCircleBound(FixedMathSharp.Geometry.WideFiniteAxisIntersection/StrictCircleBound,System.Span`1<FixedMathSharp.Geometry.WideFiniteAxisIntersection/StrictCircleBound>,System.Int32&)
CompareStrictCircleBounds(FixedMathSharp.Geometry.WideFiniteAxisIntersection/StrictCircleBound,FixedMathSharp.Geometry.WideFiniteAxisIntersection/StrictCircleBound)
GetStrictCircleQuadraticRightSign(System.ReadOnlySpan`1<FixedMathSharp.Signed320>,FixedMathSharp.Geometry.WideFiniteAxisIntersection/StrictCircleBound)
GetStrictCirclePolynomialRightSign(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Int32,FixedMathSharp.Geometry.WideFiniteAxisIntersection/StrictCircleBound)
GetStrictCirclePolynomialLimitSign(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Int32,System.Int32,FixedMathSharp.Geometry.WideFiniteAxisIntersection/StrictCircleBound,System.Int32)
CountStrictCircleOddRoots(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.ReadOnlySpan`1<System.Int32>,System.Int32,FixedMathSharp.Geometry.WideFiniteAxisIntersection/StrictCircleBound,FixedMathSharp.Geometry.WideFiniteAxisIntersection/StrictCircleBound)
GetStrictCircleVariations(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.ReadOnlySpan`1<System.Int32>,System.Int32,FixedMathSharp.Geometry.WideFiniteAxisIntersection/StrictCircleBound,System.Int32)
EvaluateStrictCirclePolynomial(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Int32,System.Int32,FixedMathSharp.Geometry.WideFiniteAxisIntersection/StrictCircleBound,System.Int32)
StrictCircleCoefficient(System.Span`1<System.UInt64>,System.Int32,System.Int32)
StrictCircleCoefficient(System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.Int32)
HasNegativeFiniteAxisPolynomial(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>)
BuildFiniteAxisPolynomialSturmSequence(System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Span`1<System.Int32>)
GetStrictPolynomialVariations(System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Span`1<System.Int32>,System.Int32,System.Boolean)
GetStrictPolynomialEndpointSign(System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Int32,System.Int32,System.Boolean)
GetStrictPolynomialValueAtOne(System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Int32,System.Int32,System.Int32)
.cctor()
.ctor(FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832)
NegateStrict(FixedMathSharp.Signed832)
GetStrictChordBounds(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed832,FixedMathSharp.Signed192,FixedMathSharp.Geometry.WideFiniteAxisIntersection/StrictFieldBound&,FixedMathSharp.Geometry.WideFiniteAxisIntersection/StrictFieldBound&)
HasNegativeStrictFieldQuadratic(FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed192,FixedMathSharp.Geometry.WideFiniteAxisIntersection/StrictFieldBound,FixedMathSharp.Geometry.WideFiniteAxisIntersection/StrictFieldBound)
CompareStrictFieldBounds(FixedMathSharp.Geometry.WideFiniteAxisIntersection/StrictFieldBound,FixedMathSharp.Geometry.WideFiniteAxisIntersection/StrictFieldBound,FixedMathSharp.Signed832)
GetStrictFieldQuadraticSign(FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Geometry.WideFiniteAxisIntersection/StrictFieldBound)
AccumulateStrictProduct(System.Span`1<System.UInt64>,System.SByte&,FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832)
GetStrictFieldSign(System.ReadOnlySpan`1<System.UInt64>,System.SByte,System.ReadOnlySpan`1<System.UInt64>,System.SByte,FixedMathSharp.Signed832)
GetFiniteValueSturmArenaBits(System.Int32,System.Int32)
GetFiniteValueRootCellWords(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>)
TryGetFiniteValueRoot(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Int32,System.Span`1<System.UInt64>,FixedMathSharp.Geometry.FiniteAxisValueRoot&)
GetFiniteValueRoots(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Span`1<System.UInt64>,System.Span`1<System.Int32>)
TryGetFiniteValueRoots(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Span`1<System.UInt64>,System.Span`1<System.Int32>,System.Int32&,System.Int32&,System.Boolean&)
TryIsolateFiniteValueRootCells(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Span`1<System.UInt64>,System.Span`1<System.Int32>,System.Int32,System.Int32,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.Int32>,System.ReadOnlySpan`1<System.Int32>,System.ReadOnlySpan`1<System.Int32>,System.ReadOnlySpan`1<System.SByte>,System.Int32,System.Span`1<System.UInt64>,System.Int32&)
GetKnownFiniteValueRoot(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Int32,System.Int32,System.Span`1<System.UInt64>)
GetFiniteValueRootCount(System.Span`1<System.UInt64>,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.Int32>,System.ReadOnlySpan`1<System.Int32>,System.ReadOnlySpan`1<System.Int32>,System.ReadOnlySpan`1<System.SByte>,System.Int32,System.Span`1<System.UInt64>,System.Int32&)
IsolateFiniteValueRoot(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Int32,System.Int32,System.Int32,System.Span`1<System.UInt64>,System.Int32,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.Int32>,System.ReadOnlySpan`1<System.Int32>,System.ReadOnlySpan`1<System.Int32>,System.ReadOnlySpan`1<System.SByte>,System.Int32,System.Span`1<System.UInt64>,System.Boolean&)
IsolateFiniteValueCell(FixedMathSharp.Geometry.FiniteAxisValueRoot&,System.Int32,System.Int32,System.Int32,System.Int32,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.Int32>,System.ReadOnlySpan`1<System.Int32>,System.ReadOnlySpan`1<System.Int32>,System.ReadOnlySpan`1<System.SByte>,System.Int32,System.Span`1<System.UInt64>,System.Boolean&)
RefineFiniteValueRoot(FixedMathSharp.Geometry.FiniteAxisValueRoot&,System.Int32,System.Int32)
RefineFiniteValueCell(FixedMathSharp.Geometry.FiniteAxisValueRoot&,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.Int32>,System.ReadOnlySpan`1<System.Int32>,System.ReadOnlySpan`1<System.Int32>,System.ReadOnlySpan`1<System.SByte>,System.Int32,System.Span`1<System.UInt64>,System.Int32&,System.Int32&,System.Int32&)
GetFiniteValueCellUpperSign(FixedMathSharp.Geometry.FiniteAxisValueRoot,System.Span`1<System.UInt64>)
BuildFiniteValueSturm(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Span`1<System.UInt64>,System.Span`1<System.Int32>,System.Span`1<System.Int32>,System.Span`1<System.Int32>,System.Span`1<System.SByte>,System.Int32&)
UpdateFiniteValueSubresultantScalars(System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.Int32,System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.Int32,System.Int32,System.Span`1<System.UInt64>,System.Int32,System.Int32&,System.Int32&,System.Int32&,System.Int32&)
PowerFiniteValueMagnitude(System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.Span`1<System.UInt64>,System.Span`1<System.UInt64>)
BuildFiniteValueSubresultant(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Int32&,System.Int32&)
NormalizeFiniteValueContent(System.Span`1<System.UInt64>,System.Int32,System.Int32,System.ReadOnlySpan`1<System.SByte>,System.Span`1<System.UInt64>)
GetFiniteValueVariations(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.Int32>,System.ReadOnlySpan`1<System.Int32>,System.ReadOnlySpan`1<System.Int32>,System.ReadOnlySpan`1<System.SByte>,System.Int32,System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.Span`1<System.UInt64>)
TryGetFiniteValueRootsBernstein(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Span`1<System.UInt64>,System.Span`1<System.Int32>,System.Int32&)
SplitFiniteValueBernstein(System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Int32)
HasFiniteValueSquareFreeCertificate(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>)
GetFiniteValueRepeatedRootFactor(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Span`1<System.UInt64>,System.Span`1<System.SByte>)
GetFiniteValueDerivativeGcd(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Span`1<System.UInt64>,System.Span`1<System.SByte>)
DivideFiniteValueFactors(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Span`1<System.UInt64>,System.Span`1<System.SByte>)
MapKnownFiniteValueRootRadicalOffset(FixedMathSharp.Geometry.FiniteAxisValueRoot,System.Int32,System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Span`1<System.UInt64>)
ClassifyRadicalOffsetCells(FixedMathSharp.Geometry.FiniteAxisValueRoot,System.Int32,System.ReadOnlySpan`1<System.UInt64>,System.Int32,FixedMathSharp.Geometry.FiniteAxisValueRoot)
CompareRadicalOffsetEndpoints(System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.Boolean,System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.Int32,System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.Boolean)
GetFiniteValueReciprocalRoots(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Span`1<System.UInt64>,System.Span`1<System.Int32>,System.Span`1<System.UInt64>,System.Span`1<System.Int32>,FixedMathSharp.Geometry.FiniteAxisValueRoots&,FixedMathSharp.Geometry.FiniteAxisValueRoots&)
ReverseFiniteValueSturm(System.Span`1<System.UInt64>,System.ReadOnlySpan`1<System.Int32>,System.ReadOnlySpan`1<System.Int32>,System.Span`1<System.Int32>,System.Span`1<System.SByte>,System.Int32)
TryRefineFiniteValueCrossingRoot(FixedMathSharp.Geometry.FiniteAxisValueRoot&,System.Int32,System.Int32,System.Int32)
KeepFiniteValueRefinementSingleton(FixedMathSharp.Geometry.FiniteAxisValueRoot&,System.Int32)
CompareFiniteValueRootToDyadic(FixedMathSharp.Geometry.FiniteAxisValueRoot,System.ReadOnlySpan`1<System.UInt64>,System.Int32)
CompareFiniteValueRoots(FixedMathSharp.Geometry.FiniteAxisValueRoot,FixedMathSharp.Geometry.FiniteAxisValueRoot)
CompareFiniteValueRoots(FixedMathSharp.Geometry.FiniteAxisValueRoot,System.ReadOnlySpan`1<System.UInt64>,FixedMathSharp.Geometry.FiniteAxisValueRoot,System.ReadOnlySpan`1<System.UInt64>)
CompareFiniteValueRootsCore(FixedMathSharp.Geometry.FiniteAxisValueRoot,System.ReadOnlySpan`1<System.UInt64>,FixedMathSharp.Geometry.FiniteAxisValueRoot,System.ReadOnlySpan`1<System.UInt64>,System.Boolean)
HaveProportionalScaledValuePolynomials(FixedMathSharp.Geometry.FiniteAxisValueRoot,System.ReadOnlySpan`1<System.UInt64>,FixedMathSharp.Geometry.FiniteAxisValueRoot,System.ReadOnlySpan`1<System.UInt64>)
GetSignAtFiniteValueRoot(FixedMathSharp.Geometry.FiniteAxisValueRoot,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>)
GetSignAtFiniteValueRootAndRefine(FixedMathSharp.Geometry.FiniteAxisValueRoot&,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Int32)
GetSignAtFiniteValueRootCore(FixedMathSharp.Geometry.FiniteAxisValueRoot&,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Boolean,System.Int32)
GetFiniteValueApproximateSign(System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Int32,System.Int32,System.Int32)
GetFiniteValueApproximateSign(System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Int32,System.Int32,System.UInt32&,System.Int32&,System.Int32)
GetFiniteValueRootNonnegativeBounds(FixedMathSharp.Geometry.FiniteAxisValueRoot,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Int32,System.Int32,System.UInt64&,System.UInt64&)
GetFiniteValueApproximateSignCore(System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Int32,System.Int32,System.Span`1<System.UInt64>,System.Span`1<System.UInt64>,System.Int32&,System.Int32)
GetFiniteValueCeilingLog2(System.Int32)
.cctor()
TryGetCenteredAxisEndpoint(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,System.Boolean,FixedMathSharp.Vector2d&)
TryGetCenteredAxisEndpoint(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.Boolean,FixedMathSharp.Vector3d&)
TryGetCenteredCapsuleSupport(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d&)
GetCenteredCapsuleSupportAnchor(FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d)
TryGetCenteredCapsuleSupport(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d&)
TryGetCenteredFiniteCylinderSupport(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d&)
TryGetCenteredFiniteConeSupport(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d&)
TryGetCenteredSupport(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed320,System.Boolean,FixedMathSharp.Vector2d&)
TryGetCenteredSupport(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed320,System.Boolean,FixedMathSharp.Vector3d&)
TryGetSupportCoordinate(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,FixedMathSharp.Signed320,System.Boolean,FixedMathSharp.Fixed64&)
CompareSupportCoordinateToMidpoint(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed192)
IsCenteredFiniteConeApexSupport(FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d)
IsCenteredFiniteConeApexSupportCore(FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed320)
GetDirection(FixedMathSharp.Vector2d,FixedMathSharp.Signed192&,FixedMathSharp.Signed192&,FixedMathSharp.Signed320&)
GetDirection(FixedMathSharp.Vector3d,FixedMathSharp.Signed192&,FixedMathSharp.Signed192&,FixedMathSharp.Signed192&,FixedMathSharp.Signed320&)
GetPlaneDirection(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Signed192&,FixedMathSharp.Signed192&,FixedMathSharp.Signed192&,FixedMathSharp.Signed320&)
GetMinimumMidpoint()
GetMaximumMidpoint()