| | | 1 | | //======================================================================= |
| | | 2 | | // CircularRimContactAlgebra.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 | | |
| | | 8 | | using System; |
| | | 9 | | using System.Runtime.CompilerServices; |
| | | 10 | | using static FixedMathSharp.Geometry.CylinderContactAlgebra; |
| | | 11 | | |
| | | 12 | | namespace FixedMathSharp.Geometry; |
| | | 13 | | |
| | | 14 | | /// <summary>Exact circular-rim chart, value and direction algebra on a normal plane.</summary> |
| | | 15 | | internal static class CircularRimContactAlgebra |
| | | 16 | | { |
| | | 17 | | internal const int ValueWords = 56; |
| | | 18 | | |
| | | 19 | | [MethodImpl(MethodImplOptions.NoInlining)] |
| | | 20 | | internal static void BuildParameter(in WideDirection3d offset, Signed576 radius, Signed320 rawScale, |
| | | 21 | | in WideDirection3d first, in WideDirection3d second, Span<ulong> data, Span<sbyte> signs, |
| | | 22 | | bool useRigidBounds = false) |
| | | 23 | | { |
| | 12572 | 24 | | if (useRigidBounds) |
| | | 25 | | { |
| | | 26 | | // The rigid triangle owner retains its original bounds even after |
| | | 27 | | // an endpoint-core shift: offsets/radius <232, chart axes <198, |
| | | 28 | | // and RawScale <164 bits. Reuse its existing coefficient path; |
| | | 29 | | // exact scaled source frames must keep the wider products below. |
| | 8366 | 30 | | BuildParameter(GetRigidAxis(offset), Signed320.NarrowValue(radius), Signed192.NarrowProven(rawScale), |
| | 8366 | 31 | | GetRigidAxis(first), GetRigidAxis(second), data, signs); |
| | 8366 | 32 | | return; |
| | | 33 | | } |
| | 4206 | 34 | | int words = data.Length / signs.Length; |
| | 8412 | 35 | | Span<ulong> inputs = stackalloc ulong[11 * words]; Span<sbyte> inputSigns = stackalloc sbyte[9]; |
| | 8412 | 36 | | inputs.Clear(); inputSigns.Clear(); |
| | 8412 | 37 | | Span<ulong> p = inputs[..(2 * words)], q = inputs.Slice(2 * words, 3 * words), |
| | 8412 | 38 | | metric = inputs.Slice(5 * words, 3 * words), scale = inputs.Slice(8 * words, words), |
| | 8412 | 39 | | square = inputs.Slice(9 * words, words), product = inputs.Slice(10 * words, words); |
| | 4206 | 40 | | Write(p, inputSigns[..2], 0, WideDirection3d.Dot(offset, first), words); |
| | 4206 | 41 | | Write(p, inputSigns[..2], 1, WideDirection3d.Dot(offset, second), words); |
| | 4206 | 42 | | Write(metric, inputSigns.Slice(5, 3), 0, first.SquaredLength, words); |
| | 4206 | 43 | | Write(metric, inputSigns.Slice(5, 3), 1, Twice(WideDirection3d.Dot(first, second)), words); |
| | 4206 | 44 | | Write(metric, inputSigns.Slice(5, 3), 2, second.SquaredLength, words); |
| | 4206 | 45 | | Write(q, inputSigns.Slice(2, 3), 0, RadialDot(first, first), words); |
| | 4206 | 46 | | Write(q, inputSigns.Slice(2, 3), 1, Twice(RadialDot(first, second)), words); |
| | 4206 | 47 | | Write(q, inputSigns.Slice(2, 3), 2, RadialDot(second, second), words); |
| | 4206 | 48 | | Import(WideArithmetic.MultiplySigned576ToSigned832(radius, radius), square); |
| | 33648 | 49 | | for (int index = 0; index < 3; index++) |
| | | 50 | | { |
| | 12618 | 51 | | WideArithmetic.MultiplyMagnitudes(q.Slice(index * words, words), square, product); |
| | 12618 | 52 | | product.CopyTo(q.Slice(index * words, words)); |
| | | 53 | | } |
| | 4206 | 54 | | Import(rawScale, scale); |
| | 4206 | 55 | | CylinderEdgeContactPolynomial.Build(p, inputSigns[..2], q, inputSigns.Slice(2, 3), |
| | 4206 | 56 | | metric, inputSigns.Slice(5, 3), scale, data[..(26 * words)], signs[..26]); |
| | 4206 | 57 | | } |
| | | 58 | | |
| | 25316 | 59 | | private static WideAxis3 GetRigidAxis(in WideDirection3d value) => new( |
| | 25316 | 60 | | Signed320.NarrowValue(value.X), Signed320.NarrowValue(value.Y), Signed320.NarrowValue(value.Z)); |
| | | 61 | | |
| | | 62 | | internal static void GetBasis(in WideDirection3d edge, out WideDirection3d first, out WideDirection3d second) |
| | | 63 | | { |
| | 12088 | 64 | | if (!edge.X.IsZero) |
| | | 65 | | { |
| | 9950 | 66 | | first = new WideDirection3d(WideArithmetic.Negate(edge.Y), edge.X, default); |
| | 9950 | 67 | | second = new WideDirection3d(WideArithmetic.Negate(edge.Z), default, edge.X); |
| | | 68 | | } |
| | | 69 | | else |
| | | 70 | | { |
| | 2138 | 71 | | first = new WideDirection3d(default, WideArithmetic.Negate(edge.Z), edge.Y); |
| | 2138 | 72 | | second = new WideDirection3d(edge.Y.IsZero ? edge.Z : edge.Y, default, default); |
| | | 73 | | } |
| | 2138 | 74 | | } |
| | | 75 | | |
| | 350 | 76 | | internal static WideDirection3d Transform(in WideRationalBasis3d basis, in WideDirection3d value) => value.Transform |
| | 22264 | 77 | | internal static Signed832 RadialDot(in WideDirection3d first, in WideDirection3d second) => WideArithmetic.AddSigned |
| | 22264 | 78 | | WideArithmetic.MultiplySigned576ToSigned832(first.X, second.X), |
| | 22264 | 79 | | WideArithmetic.MultiplySigned576ToSigned832(first.Z, second.Z)); |
| | 8412 | 80 | | internal static Signed832 Twice(Signed832 value) => WideArithmetic.AddSigned832(value, value); |
| | | 81 | | internal static void Write(Span<ulong> values, Span<sbyte> signs, int index, Signed832 value, int words = 0) |
| | | 82 | | { |
| | 125046 | 83 | | if (words == 0) words = values.Length / signs.Length; |
| | 79830 | 84 | | Span<ulong> target = values.Slice(index * words, words); |
| | 159660 | 85 | | WideArithmetic.GetMagnitude(value, target); signs[index] = (sbyte)value.Sign; |
| | 79830 | 86 | | } |
| | | 87 | | |
| | | 88 | | internal static void WriteGradient(in WideDirection3d first, in WideDirection3d second, Span<ulong> values, Span<sby |
| | | 89 | | { |
| | 405 | 90 | | values.Clear(); |
| | 405 | 91 | | int words = values.Length / signs.Length; |
| | 3240 | 92 | | for (int component = 0; component < 3; component++) |
| | | 93 | | { |
| | 2430 | 94 | | Signed576 a = Component(first, component), b = Component(second, component); |
| | 1215 | 95 | | WideArithmetic.GetMagnitude(a, values.Slice(component * 2 * words, words)); |
| | 1215 | 96 | | WideArithmetic.GetMagnitude(b, values.Slice((component * 2 + 1) * words, words)); |
| | 2430 | 97 | | signs[component * 2] = (sbyte)a.Sign; signs[component * 2 + 1] = (sbyte)b.Sign; |
| | | 98 | | } |
| | 405 | 99 | | } |
| | | 100 | | |
| | | 101 | | [MethodImpl(MethodImplOptions.NoInlining)] |
| | | 102 | | internal static void BuildValues(in WideDirection3d edge, in WideDirection3d offset, Signed576 radius, int valueShif |
| | | 103 | | Span<ulong> values, Span<sbyte> signs, bool useRigidBounds = false) |
| | | 104 | | { |
| | 247 | 105 | | if (useRigidBounds) |
| | | 106 | | { |
| | 109 | 107 | | BuildValues(GetRigidAxis(edge), GetRigidAxis(offset), Signed320.NarrowValue(radius), valueShift, values, sig |
| | 109 | 108 | | return; |
| | | 109 | | } |
| | 138 | 110 | | int words = values.Length / signs.Length; |
| | 276 | 111 | | Span<ulong> invariants = stackalloc ulong[8 * words]; Span<sbyte> invariantSigns = stackalloc sbyte[8]; |
| | 276 | 112 | | invariants.Clear(); invariantSigns.Clear(); |
| | 138 | 113 | | Write(invariants, invariantSigns, 0, Signed832.ExtendValue(Signed192.Signed(1)), words); |
| | 138 | 114 | | Write(invariants, invariantSigns, 1, edge.SquaredLength, words); |
| | 138 | 115 | | Write(invariants, invariantSigns, 2, Signed832.ExtendValue(edge.Y), words); |
| | 138 | 116 | | Write(invariants, invariantSigns, 3, offset.SquaredLength, words); |
| | 138 | 117 | | Write(invariants, invariantSigns, 4, Signed832.ExtendValue(offset.Y), words); |
| | 138 | 118 | | Write(invariants, invariantSigns, 5, WideDirection3d.Dot(edge, offset), words); |
| | 138 | 119 | | Write(invariants, invariantSigns, 6, WideArithmetic.MultiplySigned576ToSigned832(radius, radius), words); |
| | 138 | 120 | | CylinderPairSideValuePolynomial.BuildUnshifted(invariants, invariantSigns, words, values, signs); |
| | 138 | 121 | | WideFiniteAxisIntersection.ScaleFiniteAxisPolynomialVariable(values, 5, valueShift); |
| | 138 | 122 | | WideFiniteAxisIntersection.NormalizeFiniteAxisPolynomialPowerOfTwo(values, signs); |
| | 138 | 123 | | } |
| | | 124 | | |
| | | 125 | | /// <summary> |
| | | 126 | | /// Tests whether the radial support point projects within a finite segment. |
| | | 127 | | /// halfAxis must be a positive multiple of axis; radius and Q are positive, |
| | | 128 | | /// Q=radius²|first_h+t*second_h|². Scratch needs seven disjoint forty-word |
| | | 129 | | /// slots and must not overlap root or Q. No stationarity is assumed here. |
| | | 130 | | /// Axis/chart components are below 2^198; offset/halfAxis/radius below 2^232, |
| | | 131 | | /// following the shared circular chart bounds. |
| | | 132 | | /// </summary> |
| | | 133 | | [MethodImpl(MethodImplOptions.NoInlining)] |
| | | 134 | | internal static bool HasSegmentSupport(WideAxis3 axis, WideAxis3 halfAxis, WideAxis3 offset, |
| | | 135 | | Signed320 radius, WideAxis3 first, WideAxis3 second, scoped ref FiniteAxisValueRoot root, |
| | | 136 | | ReadOnlySpan<ulong> q, ReadOnlySpan<sbyte> qSigns, Span<ulong> scratch) |
| | | 137 | | { |
| | | 138 | | // lambda=a+T/sqrt(Q), H=axis.halfAxis>0. All terms use the same |
| | | 139 | | // coordinate scale, which cancels from -H<=lambda<=H. Do not use the |
| | | 140 | | // independently normalized stationary/value polynomials in place of Q. |
| | 112 | 141 | | Signed576 a = WideAxis3.Dot(axis, offset), h = WideAxis3.Dot(axis, halfAxis); |
| | 112 | 142 | | Span<ulong> t = scratch[..(2 * Words)], quadratic = scratch.Slice(2 * Words, 3 * Words); |
| | 112 | 143 | | Span<ulong> square = Slot(scratch, 5), product = Slot(scratch, 6); |
| | 112 | 144 | | Span<sbyte> tSigns = stackalloc sbyte[2], quadraticSigns = stackalloc sbyte[3]; |
| | 56 | 145 | | Write(t, tSigns, 0, RadialDot(axis, first)); |
| | 56 | 146 | | Write(t, tSigns, 1, RadialDot(axis, second)); |
| | 56 | 147 | | Import(WideArithmetic.MultiplySigned320(radius, radius), square); |
| | 336 | 148 | | for (int index = 0; index < 2; index++) |
| | | 149 | | { |
| | 112 | 150 | | WideArithmetic.MultiplyMagnitudes(Slot(t, index), square, product); |
| | 112 | 151 | | product.CopyTo(Slot(t, index)); |
| | | 152 | | } |
| | 56 | 153 | | int tSign = WideFiniteAxisIntersection.GetSignAtFiniteValueRootAndRefine(ref root, t, tSigns); |
| | 312 | 154 | | for (int bound = 0; bound < 2; bound++) |
| | | 155 | | { |
| | 108 | 156 | | Signed576 b = bound == 0 ? WideArithmetic.AddSigned576(a, h) : WideArithmetic.SubtractSigned576(a, h); |
| | | 157 | | int sign; |
| | 117 | 158 | | if (tSign == 0) sign = b.Sign; |
| | 134 | 159 | | else if (b.Sign == 0 || b.Sign == tSign) sign = tSign; |
| | | 160 | | else |
| | | 161 | | { |
| | | 162 | | // Opposite unsquared signs: sign(T+b*sqrt(Q)) is |
| | | 163 | | // sign(T)*sign(T²-b²Q), including exact equality. Equal signs |
| | | 164 | | // must be handled first; squaring them would admit false support. |
| | 64 | 165 | | WideFiniteAxisIntersection.MultiplyFiniteAxisPolynomials(t, tSigns, t, tSigns, |
| | 64 | 166 | | quadratic, quadraticSigns, product); |
| | 64 | 167 | | Import(b, square); |
| | 64 | 168 | | WideArithmetic.MultiplyMagnitudes(square, square, product); |
| | 64 | 169 | | product.CopyTo(square); |
| | 512 | 170 | | for (int index = 0; index < 3; index++) |
| | | 171 | | { |
| | 192 | 172 | | WideArithmetic.MultiplyMagnitudes(Slot(q, index), square, product); |
| | 192 | 173 | | int coefficientSign = quadraticSigns[index]; |
| | 192 | 174 | | Add(product, -qSigns[index], Slot(quadratic, index), ref coefficientSign); |
| | 192 | 175 | | quadraticSigns[index] = (sbyte)coefficientSign; |
| | | 176 | | } |
| | 64 | 177 | | sign = tSign * WideFiniteAxisIntersection.GetSignAtFiniteValueRootAndRefine(ref root, quadratic, quadrat |
| | | 178 | | } |
| | 116 | 179 | | if (bound == 0 ? sign < 0 : sign > 0) return false; |
| | | 180 | | } |
| | | 181 | | // a,H<2^432; T<2^861; comparison coefficients<2^1723 under the |
| | | 182 | | // shared chart bounds. Forty-word arithmetic needs no wider owner. |
| | 48 | 183 | | return true; |
| | | 184 | | } |
| | | 185 | | |
| | | 186 | | [MethodImpl(MethodImplOptions.NoInlining)] |
| | | 187 | | internal static void BuildParameter(in WideAxis3 offset, Signed320 radius, Signed192 rawScale, |
| | | 188 | | in WideAxis3 first, in WideAxis3 second, Span<ulong> data, Span<sbyte> signs) |
| | | 189 | | { |
| | | 190 | | // Shifted core-region coordinates are <2^232 but chart directions |
| | | 191 | | // retain original edges (<2^198), never scaled vertex differences. |
| | | 192 | | // M<2^398,P<2^432,Q<2^852,K<2^832,L<2^1252,W<2^2520, |
| | | 193 | | // N<2^2534,D<2^2396. Forty words give 2560 bits for either region. |
| | 8792 | 194 | | Span<ulong> inputs = stackalloc ulong[11 * Words]; |
| | 8792 | 195 | | Span<sbyte> inputSigns = stackalloc sbyte[9]; |
| | 17584 | 196 | | inputs.Clear(); inputSigns.Clear(); |
| | 17584 | 197 | | Span<ulong> p = inputs[..(2 * Words)], q = inputs.Slice(2 * Words, 3 * Words); |
| | 17584 | 198 | | Span<ulong> metric = inputs.Slice(5 * Words, 3 * Words), scale = Slot(inputs, 8); |
| | 17584 | 199 | | Span<ulong> radiusSquared = Slot(inputs, 9), product = Slot(inputs, 10); |
| | 8792 | 200 | | Write(p, inputSigns[..2], 0, WideAxis3.Dot(offset, first)); |
| | 8792 | 201 | | Write(p, inputSigns[..2], 1, WideAxis3.Dot(offset, second)); |
| | 8792 | 202 | | Write(metric, inputSigns.Slice(5, 3), 0, first.SquaredLength); |
| | 8792 | 203 | | Write(metric, inputSigns.Slice(5, 3), 1, Twice(WideAxis3.Dot(first, second))); |
| | 8792 | 204 | | Write(metric, inputSigns.Slice(5, 3), 2, second.SquaredLength); |
| | 8792 | 205 | | Write(q, inputSigns.Slice(2, 3), 0, RadialDot(first, first)); |
| | 8792 | 206 | | Write(q, inputSigns.Slice(2, 3), 1, Twice(RadialDot(first, second))); |
| | 8792 | 207 | | Write(q, inputSigns.Slice(2, 3), 2, RadialDot(second, second)); |
| | 8792 | 208 | | Import(WideArithmetic.MultiplySigned320(radius, radius), radiusSquared); |
| | 70336 | 209 | | for (int index = 0; index < 3; index++) |
| | | 210 | | { |
| | 26376 | 211 | | WideArithmetic.MultiplyMagnitudes(Slot(q, index), radiusSquared, product); |
| | 26376 | 212 | | product.CopyTo(Slot(q, index)); |
| | | 213 | | } |
| | 8792 | 214 | | Import(Signed320.ExtendValue(rawScale), scale); |
| | 8792 | 215 | | CylinderEdgeContactPolynomial.Build(p, inputSigns[..2], q, inputSigns.Slice(2, 3), |
| | 8792 | 216 | | metric, inputSigns.Slice(5, 3), scale, data[..(26 * Words)], signs[..26]); |
| | | 217 | | |
| | 8792 | 218 | | } |
| | | 219 | | |
| | | 220 | | /// <summary> |
| | | 221 | | /// Reuses a built chart for the swapped basis (second+s*first). Only the |
| | | 222 | | /// 26 shared coefficient slots are changed; caller admission slots remain intact. |
| | | 223 | | /// </summary> |
| | | 224 | | internal static void ReverseParameterChart(Span<ulong> data, Span<sbyte> signs) |
| | | 225 | | { |
| | | 226 | | // t=1/s gives Pbar=s*P(1/s), Qbar=s²*Q(1/s), Mbar=s²*M(1/s). |
| | | 227 | | // Kbar=-s*K(1/s), Lbar=-s²*L(1/s), Jbar=-s²*J(1/s). |
| | | 228 | | // W, N and D reverse as quartics. Fixed nominal degrees retain zero |
| | | 229 | | // ends, and reversal preserves W's and N/D's power-of-two normalization. |
| | 7147 | 230 | | ReverseCoefficients(data, signs, 0, 5); |
| | 7147 | 231 | | ReverseCoefficients(data, signs, 5, 3); |
| | 7147 | 232 | | ReverseCoefficients(data, signs, 8, 2); |
| | 7147 | 233 | | ReverseCoefficients(data, signs, 10, 3); |
| | 7147 | 234 | | ReverseCoefficients(data, signs, 13, 3); |
| | 7147 | 235 | | ReverseCoefficients(data, signs, 16, 5); |
| | 7147 | 236 | | ReverseCoefficients(data, signs, 21, 5); |
| | 185822 | 237 | | for (int index = 8; index < 16; index++) signs[index] = (sbyte)-signs[index]; |
| | 7147 | 238 | | } |
| | | 239 | | |
| | | 240 | | private static void ReverseCoefficients(Span<ulong> data, Span<sbyte> signs, int start, int count) |
| | | 241 | | { |
| | 50029 | 242 | | int words = data.Length / signs.Length; |
| | 50029 | 243 | | signs.Slice(start, count).Reverse(); |
| | 242998 | 244 | | for (int index = 0; index < count / 2; index++) |
| | | 245 | | { |
| | 71470 | 246 | | Span<ulong> first = data.Slice((start + index) * words, words), |
| | 71470 | 247 | | second = data.Slice((start + count - 1 - index) * words, words); |
| | 7538140 | 248 | | for (int word = 0; word < words; word++) |
| | 3697600 | 249 | | (first[word], second[word]) = (second[word], first[word]); |
| | | 250 | | } |
| | 50029 | 251 | | } |
| | | 252 | | |
| | | 253 | | [MethodImpl(MethodImplOptions.NoInlining)] |
| | | 254 | | internal static void BuildValues(in WideAxis3 e, in WideAxis3 c, Signed320 radius, int valueShift, |
| | | 255 | | Span<ulong> values, Span<sbyte> signs) |
| | | 256 | | { |
| | 161 | 257 | | Span<ulong> invariants = stackalloc ulong[8 * ValueWords]; |
| | 161 | 258 | | Span<sbyte> invariantSigns = stackalloc sbyte[8]; |
| | 322 | 259 | | invariants.Clear(); invariantSigns.Clear(); |
| | 161 | 260 | | Write(invariants, invariantSigns, 0, Signed576.ExtendValue(Signed320.ExtendValue(Signed192.Signed(1))), ValueWor |
| | 161 | 261 | | Write(invariants, invariantSigns, 1, e.SquaredLength, ValueWords); |
| | 161 | 262 | | Write(invariants, invariantSigns, 2, Signed576.ExtendValue(e.Y), ValueWords); |
| | 161 | 263 | | Write(invariants, invariantSigns, 3, c.SquaredLength, ValueWords); |
| | 161 | 264 | | Write(invariants, invariantSigns, 4, Signed576.ExtendValue(c.Y), ValueWords); |
| | 161 | 265 | | Write(invariants, invariantSigns, 5, WideAxis3.Dot(e, c), ValueWords); |
| | 161 | 266 | | Write(invariants, invariantSigns, 6, WideArithmetic.MultiplySigned320(radius, radius), ValueWords); |
| | 161 | 267 | | CylinderPairSideValuePolynomial.BuildUnshifted(invariants, invariantSigns, ValueWords, values, signs); |
| | | 268 | | // Retained edges keep B<398,C<198 bits; shifted c gives q<466, |
| | | 269 | | // x<232,y<432,rho<454. At ValueShift<=472, weighted linear |
| | | 270 | | // invariant heights are <872. Quartic convolutions including their |
| | | 271 | | // carries stay below 3500 bits, inside 56 words (3584 bits). |
| | 161 | 272 | | WideFiniteAxisIntersection.ScaleFiniteAxisPolynomialVariable(values, 5, valueShift); |
| | 161 | 273 | | WideFiniteAxisIntersection.NormalizeFiniteAxisPolynomialPowerOfTwo(values, signs); |
| | 161 | 274 | | } |
| | | 275 | | |
| | | 276 | | internal static void GetBasis(WideAxis3 edge, out WideAxis3 first, out WideAxis3 second) |
| | | 277 | | { |
| | 120 | 278 | | if (!edge.X.IsZero) |
| | | 279 | | { |
| | 118 | 280 | | first = new WideAxis3(WideArithmetic.Negate(edge.Y), edge.X, default); |
| | 118 | 281 | | second = new WideAxis3(WideArithmetic.Negate(edge.Z), default, edge.X); |
| | | 282 | | } |
| | | 283 | | else |
| | | 284 | | { |
| | 2 | 285 | | first = new WideAxis3(default, WideArithmetic.Negate(edge.Z), edge.Y); |
| | 2 | 286 | | second = new WideAxis3(edge.Y, default, default); |
| | | 287 | | } |
| | 2 | 288 | | } |
| | | 289 | | |
| | | 290 | | internal static void WriteGradient(WideAxis3 first, WideAxis3 second, Span<ulong> values, Span<sbyte> signs) |
| | | 291 | | { |
| | 36 | 292 | | values.Clear(); |
| | 288 | 293 | | for (int component = 0; component < 3; component++) |
| | | 294 | | { |
| | 216 | 295 | | Signed320 a = Component(first, component), b = Component(second, component); |
| | 216 | 296 | | Span<ulong> firstSlot = values.Slice(component * 10, 5), secondSlot = values.Slice(component * 10 + 5, 5); |
| | 108 | 297 | | WideArithmetic.GetMagnitude(a, out firstSlot[4], out firstSlot[3], out firstSlot[2], out firstSlot[1], out f |
| | 108 | 298 | | WideArithmetic.GetMagnitude(b, out secondSlot[4], out secondSlot[3], out secondSlot[2], out secondSlot[1], o |
| | 216 | 299 | | signs[2 * component] = (sbyte)a.Sign; signs[2 * component + 1] = (sbyte)b.Sign; |
| | | 300 | | } |
| | 36 | 301 | | } |
| | 72 | 302 | | internal static WideAxis3 Transform(WideRationalBasis3d basis, WideAxis3 axis) => new( |
| | 72 | 303 | | TransformComponent(basis.Xx, basis.Yx, basis.Zx, axis), |
| | 72 | 304 | | TransformComponent(basis.Xy, basis.Yy, basis.Zy, axis), |
| | 72 | 305 | | TransformComponent(basis.Xz, basis.Yz, basis.Zz, axis)); |
| | | 306 | | internal static Signed320 TransformComponent(Signed192 x, Signed192 y, Signed192 z, WideAxis3 axis) => |
| | 216 | 307 | | Signed320.NarrowValue(WideAxis3.Dot(new WideAxis3(Signed320.ExtendValue(x), Signed320.ExtendValue(y), Signed320. |
| | 26690 | 308 | | internal static Signed576 RadialDot(in WideAxis3 a, in WideAxis3 b) => WideArithmetic.AddSigned576( |
| | 26690 | 309 | | WideArithmetic.MultiplySigned320(a.X, b.X), WideArithmetic.MultiplySigned320(a.Z, b.Z)); |
| | 17584 | 310 | | internal static Signed576 Twice(Signed576 value) => WideArithmetic.AddSigned576(value, value); |
| | | 311 | | internal static int Sign(ref FiniteAxisValueRoot root, ReadOnlySpan<ulong> data, ReadOnlySpan<sbyte> signs, int star |
| | 28250 | 312 | | WideFiniteAxisIntersection.GetSignAtFiniteValueRootAndRefine(ref root, |
| | 28250 | 313 | | data.Slice(start * (data.Length / signs.Length), count * (data.Length / signs.Length)), signs.Slice(start, c |
| | | 314 | | internal static void Write(Span<ulong> values, Span<sbyte> signs, int index, Signed576 value, int words = Words) |
| | | 315 | | { |
| | 71575 | 316 | | Span<ulong> target = values.Slice(index * words, words); |
| | 214725 | 317 | | target.Clear(); WideArithmetic.GetMagnitude(value, target[..9]); signs[index] = (sbyte)value.Sign; |
| | 71575 | 318 | | } |
| | | 319 | | } |