< Summary

Information
Class: FixedMathSharp.Geometry.RationalSegmentInterval
Assembly: FixedMathSharp
File(s): /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/FiniteAxis/RationalSegmentInterval.cs
Line coverage
100%
Covered lines: 145
Uncovered lines: 0
Coverable lines: 145
Total lines: 269
Line coverage: 100%
Branch coverage
100%
Covered branches: 136
Total branches: 136
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_LowerNumerator()100%11100%
get_LowerDenominator()100%11100%
get_UpperNumerator()100%11100%
get_UpperDenominator()100%11100%
get_IsClipped()100%11100%
GetLegacyBound(...)100%22100%
GetEndpoint(...)100%1414100%
TryMapToSegment(...)100%4040100%
TryMapWide(...)100%2020100%
PackReducedParameter(...)100%11100%
CompareParameters(...)100%11100%
CompareParameters(...)100%11100%
TryClipHalfPlane(...)100%1616100%
.ctor(...)100%11100%
.ctor(...)100%2424100%
Contains(...)100%44100%
FitsLegacy(...)100%1616100%
ScaleParameter(...)100%11100%
GetWideCoordinate(...)100%11100%

File(s)

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

#LineLine coverage
 1//=======================================================================
 2// RationalSegmentInterval.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/// <summary>
 13/// An exact closed rational subinterval of a segment's unit parameter. The
 14/// default value is the complete segment; borrowed calls avoid enlarging events.
 15/// </summary>
 16internal readonly struct RationalSegmentInterval
 17{
 18    internal readonly Signed832 WideLowerNumerator, WideLowerDenominator, WideUpperNumerator, WideUpperDenominator;
 19    internal bool HasWideBounds { get; }
 91420    internal Signed320 LowerNumerator => GetLegacyBound(WideLowerNumerator);
 86221    internal Signed320 LowerDenominator => GetLegacyBound(WideLowerDenominator);
 73922    internal Signed320 UpperNumerator => GetLegacyBound(WideUpperNumerator);
 74323    internal Signed320 UpperDenominator => GetLegacyBound(WideUpperDenominator);
 3461724    internal bool IsClipped => !WideLowerDenominator.IsZero;
 25
 26    private Signed320 GetLegacyBound(Signed832 value)
 27    {
 326228        if (HasWideBounds) throw new OverflowException("This interval requires its exact wide bounds.");
 325429        return new Signed320(value.Word4, value.Word3, value.Word2, value.Word1, value.Word0);
 30    }
 31
 32    /// <summary>
 33    /// Materializes one endpoint on the original 2D edge, rounding each local
 34    /// coordinate once to nearest-even. The retained interval, not this final
 35    /// point, remains authoritative for incidence and admission.
 36    /// </summary>
 37    internal Vector2d GetEndpoint(FixedSegment2d edge, bool upper)
 38    {
 11739        if (!IsClipped) return upper ? edge.End : edge.Start;
 9740        if (HasWideBounds)
 41        {
 2642            Signed832 wideN = upper ? WideUpperNumerator : WideLowerNumerator;
 2643            Signed832 wideD = upper ? WideUpperDenominator : WideLowerDenominator;
 2644            return new Vector2d(GetWideCoordinate(edge.Start.X, edge.End.X, wideN, wideD),
 2645                GetWideCoordinate(edge.Start.Y, edge.End.Y, wideN, wideD));
 46        }
 7147        Signed320 n = upper ? UpperNumerator : LowerNumerator;
 7148        Signed320 d = upper ? UpperDenominator : LowerDenominator;
 49        // Full raw endpoints and a unit Signed320 ratio need fewer than 386
 50        // coefficient bits; existing Signed576 interpolation preserves them.
 7151        return new Vector2d(
 7152            WideConvex2dRelations.GetProjectedOffsetCoordinate(edge.Start.X,
 7153                WideArithmetic.Difference(edge.End.X, edge.Start.X), n, d),
 7154            WideConvex2dRelations.GetProjectedOffsetCoordinate(edge.Start.Y,
 7155                WideArithmetic.Difference(edge.End.Y, edge.Start.Y), n, d));
 56    }
 57
 58    /// <summary>
 59    /// Maps this source interval onto a collinear target segment in the same
 60    /// local frame, preserving closed endpoints without coordinate rounding.
 61    /// Degenerate, off-line and disjoint segments return false. The endpoint
 62    /// half-plane coefficients retain their full transient products. Reduced
 63    /// retained bounds must fit Signed832; unsupported widths throw rather than truncate.
 64    /// </summary>
 65    internal bool TryMapToSegment(FixedSegment source, FixedSegment target, out RationalSegmentInterval mapped)
 66    {
 6267        mapped = default;
 6468        if (source.Start == source.End || target.Start == target.End) return false;
 18069        Span<Signed192> direction = stackalloc Signed192[3], first = stackalloc Signed192[3], second = stackalloc Signed
 6070        int axis = 0;
 48071        for (int coordinate = 0; coordinate < 3; coordinate++)
 72        {
 18073            direction[coordinate] = WideArithmetic.Difference(source.End[coordinate], source.Start[coordinate]);
 18074            first[coordinate] = WideArithmetic.Difference(target.Start[coordinate], source.Start[coordinate]);
 18075            second[coordinate] = WideArithmetic.Difference(target.End[coordinate], source.Start[coordinate]);
 24076            if (!direction[coordinate].IsZero) axis = coordinate;
 77        }
 46878        for (int coordinate = 0; coordinate < 3; coordinate++)
 17779            if (!WideArithmetic.MultiplySubtract(direction[axis], first[coordinate], direction[coordinate], first[axis])
 17780                || !WideArithmetic.MultiplySubtract(direction[axis], second[coordinate], direction[coordinate], second[a
 381                return false;
 82
 8683        if (HasWideBounds) return TryMapWide(direction[axis], first[axis], second[axis], out mapped);
 84
 11285        Signed320 lowerN = default, lowerD = Signed320.One, upperN = Signed320.One, upperD = Signed320.One;
 14086        for (int bound = 0; bound < 2; bound++)
 87        {
 5088            Signed320 n = IsClipped ? (bound == 0 ? LowerNumerator : UpperNumerator) : bound == 0 ? default : Signed320.
 5089            Signed320 d = IsClipped ? (bound == 0 ? LowerDenominator : UpperDenominator) : Signed320.One;
 90            // A full raw difference and a Signed320 ratio need <386 bits.
 91            // Compare in Signed576 before enforcing the clipping bank's width.
 5092            Signed576 offset = WideArithmetic.MultiplySigned320(n, direction[axis]);
 5093            Signed576 a = WideArithmetic.SubtractSigned576(WideArithmetic.MultiplySigned320(d, first[axis]), offset);
 5094            Signed576 b = WideArithmetic.SubtractSigned576(WideArithmetic.MultiplySigned320(d, second[axis]), offset);
 5095            if ((direction[axis].Sign < 0) != (bound != 0))
 4496            { a = WideArithmetic.SubtractSigned576(default, a); b = WideArithmetic.SubtractSigned576(default, b); }
 5097            Signed576 difference = WideArithmetic.SubtractSigned576(a, b);
 7898            if (difference.Sign < 0) difference = WideArithmetic.SubtractSigned576(default, difference);
 5099            if (!Signed320.TryNarrowSigned(a, out Signed320 start) || !Signed320.TryNarrowSigned(b, out Signed320 end)
 50100                || !Signed320.TryNarrowSigned(difference, out _))
 4101                return TryMapWide(direction[axis], first[axis], second[axis], out mapped);
 50102            if (!TryClipHalfPlane(start, end, ref lowerN, ref lowerD, ref upperN, ref upperD)) return false;
 103        }
 20104        mapped = new RationalSegmentInterval(lowerN, lowerD, upperN, upperD);
 20105        return true;
 106    }
 107
 108    private bool TryMapWide(Signed192 direction, Signed192 first, Signed192 second, out RationalSegmentInterval mapped)
 109    {
 33110        mapped = default;
 132111        Signed832 lowerN = default, lowerD = Signed832.ExtendValue(Signed192.One), upperN = lowerD, upperD = lowerD;
 112        const int words = 15;
 66113        Span<ulong> values = stackalloc ulong[14 * words]; Span<int> signs = stackalloc int[14];
 33114        var parameter = ContactQuadratic.At(values, signs, 0, words);
 33115        var denominator = ContactQuadratic.At(values, signs, 1, words);
 33116        var offset = ContactQuadratic.At(values, signs, 2, words);
 33117        var a = ContactQuadratic.At(values, signs, 3, words);
 33118        var b = ContactQuadratic.At(values, signs, 4, words);
 33119        var rootN = ContactQuadratic.At(values, signs, 5, words);
 33120        var rootD = ContactQuadratic.At(values, signs, 6, words);
 180121        for (int bound = 0; bound < 2; bound++)
 122        {
 63123            parameter.Set(bound == 0 ? WideLowerNumerator : WideUpperNumerator);
 63124            denominator.Set(bound == 0 ? WideLowerDenominator : WideUpperDenominator);
 63125            ContactQuadratic.Scale(parameter, Signed576.ExtendValue(Signed320.ExtendValue(direction)), offset);
 126126            ContactQuadratic.Scale(denominator, Signed576.ExtendValue(Signed320.ExtendValue(first)), a); a.Add(offset, -
 126127            ContactQuadratic.Scale(denominator, Signed576.ExtendValue(Signed320.ExtendValue(second)), b); b.Add(offset, 
 127128            if ((direction.Sign < 0) != (bound != 0)) { a.MultiplySign(-1); b.MultiplySign(-1); }
 63129            if (a.Signs[0] >= 0 && b.Signs[0] >= 0) continue;
 41130            if (a.Signs[0] < 0 && b.Signs[0] < 0) return false;
 93131            a.CopyTo(rootN); a.CopyTo(rootD); rootD.Add(b, -1);
 63132            if (rootD.Signs[0] < 0) { rootN.MultiplySign(-1); rootD.MultiplySign(-1); }
 31133            PackReducedParameter(rootN.Rational, rootD.Rational, out Signed832 n, out Signed832 d);
 134            // A collinear nondegenerate target has a monotone source
 135            // parameter, in either direction. The two ordered source bounds
 136            // therefore clip different target endpoints, each at most once.
 137            // A crossing root lies strictly inside its rejected endpoint;
 138            // disjoint targets were rejected above by their two negative signs.
 60139            if (a.Signs[0] < 0) { lowerN = n; lowerD = d; }
 30140            else { upperN = n; upperD = d; }
 141        }
 27142        mapped = new RationalSegmentInterval(lowerN, lowerD, upperN, upperD);
 27143        return true;
 144    }
 145
 146    internal static void PackReducedParameter(ReadOnlySpan<ulong> numerator, ReadOnlySpan<ulong> denominator,
 147        out Signed832 n, out Signed832 d)
 148    {
 95149        int words = numerator.Length;
 190150        Span<ulong> gcd = stackalloc ulong[words], quotient = stackalloc ulong[words];
 190151        Span<ulong> remainder = stackalloc ulong[words], scratch = stackalloc ulong[2 * words + 1];
 190152        numerator.CopyTo(gcd); denominator.CopyTo(quotient);
 95153        WideArithmetic.GetMagnitudeGreatestCommonDivisor(gcd, quotient, scratch);
 190154        WideArithmetic.DivideMagnitudes(numerator, gcd, quotient, remainder, scratch); n = Pack(quotient);
 188155        WideArithmetic.DivideMagnitudes(denominator, gcd, quotient, remainder, scratch); d = Pack(quotient);
 156        static Signed832 Pack(ReadOnlySpan<ulong> magnitude)
 157        {
 158            if (WideArithmetic.GetActiveMagnitudeLength(magnitude) > 13 || (magnitude[12] & (1UL << 63)) != 0)
 159                throw new OverflowException("Mapped segment bounds must fit Signed832 after exact reduction.");
 160            return new Signed832(magnitude[12], magnitude[11], magnitude[10], magnitude[9], magnitude[8], magnitude[7],
 161                magnitude[6], magnitude[5], magnitude[4], magnitude[3], magnitude[2], magnitude[1], magnitude[0]);
 162        }
 94163    }
 164
 165    /// <summary>Compares nonnegative unit parameters with positive denominators without narrowing products.</summary>
 166    internal static int CompareParameters(Signed320 firstN, Signed320 firstD, Signed320 secondN, Signed320 secondD) =>
 1716167        WideArithmetic.CompareNonNegativeProducts(Signed832.ExtendValue(Signed576.ExtendValue(firstN)),
 1716168            Signed832.ExtendValue(Signed576.ExtendValue(secondD)), Signed832.ExtendValue(Signed576.ExtendValue(secondN))
 1716169            Signed832.ExtendValue(Signed576.ExtendValue(firstD)));
 170
 171    internal static int CompareParameters(Signed832 firstN, Signed832 firstD, Signed832 secondN, Signed832 secondD) =>
 5026172        WideArithmetic.CompareNonNegativeProducts(firstN, secondD, secondN, firstD);
 173
 174    /// <summary>
 175    /// Clips a unit interval by the linear half-plane value at its original
 176    /// endpoints. Bounds have positive denominators; first-second must fit
 177    /// Signed320 (current framed coverage callers require fewer than 268 bits).
 178    /// </summary>
 179    internal static bool TryClipHalfPlane(Signed320 first, Signed320 second,
 180        ref Signed320 lowerN, ref Signed320 lowerD, ref Signed320 upperN, ref Signed320 upperD)
 181    {
 1193182        if (first.Sign >= 0 && second.Sign >= 0) return true;
 545183        if (first.Sign < 0 && second.Sign < 0) return false;
 946184        Signed320 n = first, d = WideArithmetic.SubtractSigned320(first, second);
 959185        if (d.Sign < 0) { n = WideArithmetic.Negate(n); d = WideArithmetic.Negate(d); }
 473186        if (first.Sign < 0)
 187        {
 717188            if (CompareParameters(n, d, lowerN, lowerD) > 0) { lowerN = n; lowerD = d; }
 189        }
 678190        else if (CompareParameters(n, d, upperN, upperD) < 0) { upperN = n; upperD = d; }
 473191        return CompareParameters(lowerN, lowerD, upperN, upperD) <= 0;
 192    }
 193
 194    internal RationalSegmentInterval(Signed320 lowerNumerator, Signed320 lowerDenominator,
 195        Signed320 upperNumerator, Signed320 upperDenominator)
 1170196        : this(Signed832.ExtendValue(Signed576.ExtendValue(lowerNumerator)),
 1170197            Signed832.ExtendValue(Signed576.ExtendValue(lowerDenominator)),
 1170198            Signed832.ExtendValue(Signed576.ExtendValue(upperNumerator)),
 1170199            Signed832.ExtendValue(Signed576.ExtendValue(upperDenominator)))
 200    {
 1163201    }
 202
 203    internal RationalSegmentInterval(Signed832 lowerNumerator, Signed832 lowerDenominator,
 204        Signed832 upperNumerator, Signed832 upperDenominator)
 205    {
 3386206        if (lowerDenominator.Sign <= 0 || upperDenominator.Sign <= 0)
 2207            throw new ArgumentOutOfRangeException(nameof(lowerDenominator), "Interval denominators must be positive.");
 3384208        if (lowerNumerator.Sign < 0 || upperNumerator.Sign < 0
 3384209            || WideArithmetic.SubtractSigned832(lowerDenominator, lowerNumerator).Sign < 0
 3384210            || WideArithmetic.SubtractSigned832(upperDenominator, upperNumerator).Sign < 0)
 4211            throw new ArgumentOutOfRangeException(nameof(lowerNumerator), "Interval bounds must lie in [0,1].");
 3380212        if (CompareParameters(lowerNumerator, lowerDenominator, upperNumerator, upperDenominator) > 0)
 1213            throw new ArgumentException("The lower interval bound must not exceed the upper bound.");
 3379214        this = default;
 5244215        if (lowerNumerator.IsZero && upperNumerator.Equals(upperDenominator)) return;
 3028216        WideLowerNumerator = lowerNumerator; WideLowerDenominator = lowerDenominator;
 3028217        WideUpperNumerator = upperNumerator; WideUpperDenominator = upperDenominator;
 1514218        HasWideBounds = !FitsLegacy(lowerNumerator) || !FitsLegacy(lowerDenominator)
 1514219            || !FitsLegacy(upperNumerator) || !FitsLegacy(upperDenominator);
 1514220    }
 221
 222    /// <summary>Restricts an already admitted unit parameter; requires a clipped interval.</summary>
 223    internal bool Contains(scoped ContactQuadratic numerator, scoped ContactQuadratic denominator,
 224        scoped ReadOnlySpan<ulong> root)
 225    {
 226        System.Diagnostics.Debug.Assert(IsClipped);
 227        // Only the retained parameter width changes; its event radicand is unchanged.
 708228        int words = Math.Max(ContactQuadratic.ActiveWords(numerator), ContactQuadratic.ActiveWords(denominator))
 708229            + (HasWideBounds ? 14 : 6);
 1416230        Span<ulong> values = stackalloc ulong[4 * words]; Span<int> signs = stackalloc int[4];
 708231        var query = ContactQuadratic.At(values, signs, 0, words);
 708232        var term = ContactQuadratic.At(values, signs, 1, words);
 708233        ScaleParameter(numerator, WideLowerDenominator, query);
 1416234        ScaleParameter(denominator, WideLowerNumerator, term); query.Add(term, -1);
 818235        if (query.Sign(root) < 0) return false;
 598236        ScaleParameter(denominator, WideUpperNumerator, query);
 1196237        ScaleParameter(numerator, WideUpperDenominator, term); query.Add(term, -1);
 598238        return query.Sign(root) >= 0;
 239    }
 240
 241    private static bool FitsLegacy(Signed832 value)
 242    {
 5686243        ulong extension = (value.Word4 & (1UL << 63)) != 0 ? ulong.MaxValue : 0UL;
 5686244        return value.Word5 == extension && value.Word6 == extension && value.Word7 == extension
 5686245            && value.Word8 == extension && value.Word9 == extension && value.Word10 == extension
 5686246            && value.Word11 == extension && value.Word12 == extension;
 247    }
 248
 249    private static void ScaleParameter(ContactQuadratic value, Signed832 parameter, ContactQuadratic result)
 250    {
 5224251        Span<ulong> magnitude = stackalloc ulong[13]; WideArithmetic.GetMagnitude(parameter, magnitude);
 2612252        ContactQuadratic.Scale(value, magnitude, parameter.Sign, result);
 2612253    }
 254
 255    private static Fixed64 GetWideCoordinate(Fixed64 start, Fixed64 end, Signed832 n, Signed832 d)
 256    {
 257        const int words = 15;
 104258        Span<ulong> values = stackalloc ulong[8 * words]; Span<int> signs = stackalloc int[8];
 52259        var parameter = ContactQuadratic.At(values, signs, 0, words);
 52260        var denominator = ContactQuadratic.At(values, signs, 1, words);
 52261        var numerator = ContactQuadratic.At(values, signs, 2, words);
 52262        var term = ContactQuadratic.At(values, signs, 3, words);
 104263        parameter.Set(n); denominator.Set(d);
 52264        ContactQuadratic.Scale(denominator, Signed576.ExtendValue(Signed320.ExtendValue(Signed192.Raw(start))), numerato
 52265        ContactQuadratic.Scale(parameter, Signed576.ExtendValue(Signed320.ExtendValue(WideArithmetic.Difference(end, sta
 52266        numerator.Add(term);
 52267        return ContactQuadratic.RoundRatio(numerator, denominator, ReadOnlySpan<ulong>.Empty);
 268    }
 269}

Methods/Properties

get_LowerNumerator()
get_LowerDenominator()
get_UpperNumerator()
get_UpperDenominator()
get_IsClipped()
GetLegacyBound(FixedMathSharp.Signed832)
GetEndpoint(FixedMathSharp.Geometry.FixedSegment2d,System.Boolean)
TryMapToSegment(FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Geometry.RationalSegmentInterval&)
TryMapWide(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Geometry.RationalSegmentInterval&)
PackReducedParameter(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.UInt64>,FixedMathSharp.Signed832&,FixedMathSharp.Signed832&)
CompareParameters(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320)
CompareParameters(FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832)
TryClipHalfPlane(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&)
.ctor(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320)
.ctor(FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832)
Contains(FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,System.ReadOnlySpan`1<System.UInt64>)
FitsLegacy(FixedMathSharp.Signed832)
ScaleParameter(FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Signed832,FixedMathSharp.Geometry.ContactQuadratic)
GetWideCoordinate(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Signed832,FixedMathSharp.Signed832)