| | | 1 | | //======================================================================= |
| | | 2 | | // WideArithmetic.Comparison.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 | | |
| | | 10 | | namespace FixedMathSharp; |
| | | 11 | | |
| | | 12 | | /// <content> |
| | | 13 | | /// Comparison helpers for wide fixed-point arithmetic, including normalized |
| | | 14 | | /// depth rounding and comparisons against wide intermediate representations. |
| | | 15 | | /// </content> |
| | | 16 | | internal static partial class WideArithmetic |
| | | 17 | | { |
| | | 18 | | #region Normalized Depth Comparison |
| | | 19 | | |
| | | 20 | | /// <summary> |
| | | 21 | | /// Rounds a nonnegative depth represented as |
| | | 22 | | /// <c>overlap / (commonDenominator * sqrt(squaredAxisLength))</c> |
| | | 23 | | /// to the nearest raw Q32.32 value with ties to even. |
| | | 24 | | /// </summary> |
| | | 25 | | /// <remarks> |
| | | 26 | | /// Squared axis lengths at least <c>(2^31)^2</c> use a constant-time floor |
| | | 27 | | /// root approximation whose error is less than one raw result unit, then |
| | | 28 | | /// one exact midpoint correction. Smaller nonzero axes use an exact binary |
| | | 29 | | /// search over the representable nonnegative raw domain. Both paths apply |
| | | 30 | | /// exact clamping classification and nearest-even midpoint admission. |
| | | 31 | | /// </remarks> |
| | | 32 | | internal static Fixed64 GetRoundedNonNegativeNormalizedDepth( |
| | | 33 | | Signed576 overlap, |
| | | 34 | | Signed576 squaredAxisLength, |
| | | 35 | | Signed320 commonDenominator, |
| | | 36 | | out bool isClamped) |
| | | 37 | | { |
| | 517 | 38 | | Signed192 maximumTwiceRaw = AddSigned192( |
| | 517 | 39 | | Signed192.Signed(long.MaxValue), |
| | 517 | 40 | | Signed192.Signed(long.MaxValue)); |
| | 517 | 41 | | if (CompareNormalizedDepthToTwiceRaw( |
| | 517 | 42 | | overlap, |
| | 517 | 43 | | squaredAxisLength, |
| | 517 | 44 | | commonDenominator, |
| | 517 | 45 | | maximumTwiceRaw) > 0) |
| | | 46 | | { |
| | 11 | 47 | | isClamped = true; |
| | 11 | 48 | | return Fixed64.MaxValue; |
| | | 49 | | } |
| | | 50 | | |
| | 506 | 51 | | Signed576 approximationThreshold = Signed576.ExtendValue( |
| | 506 | 52 | | Signed320.ExtendValue(Signed192.Signed(1L << 62))); |
| | 506 | 53 | | if (CompareNonNegative( |
| | 506 | 54 | | squaredAxisLength, |
| | 506 | 55 | | approximationThreshold) < 0) |
| | | 56 | | { |
| | 39 | 57 | | long low = 0L; |
| | 39 | 58 | | long high = long.MaxValue; |
| | 2496 | 59 | | while (low < high) |
| | | 60 | | { |
| | 2457 | 61 | | long candidate = (long)( |
| | 2457 | 62 | | ((ulong)low + (ulong)high + 1UL) >> 1); |
| | 2457 | 63 | | Signed192 candidateLowerMidpoint = SubtractSigned192( |
| | 2457 | 64 | | AddSigned192( |
| | 2457 | 65 | | Signed192.Signed(candidate), |
| | 2457 | 66 | | Signed192.Signed(candidate)), |
| | 2457 | 67 | | Signed192.Signed(1L)); |
| | 2457 | 68 | | int candidateComparison = CompareNormalizedDepthToTwiceRaw( |
| | 2457 | 69 | | overlap, |
| | 2457 | 70 | | squaredAxisLength, |
| | 2457 | 71 | | commonDenominator, |
| | 2457 | 72 | | candidateLowerMidpoint); |
| | 2457 | 73 | | bool roundsAtLeastCandidate = candidateComparison > 0 |
| | 2457 | 74 | | || (candidateComparison == 0 |
| | 2457 | 75 | | && (candidate & 1L) == 0L); |
| | 2457 | 76 | | if (roundsAtLeastCandidate) |
| | 1189 | 77 | | low = candidate; |
| | | 78 | | else |
| | 1268 | 79 | | high = candidate - 1L; |
| | | 80 | | } |
| | | 81 | | |
| | 39 | 82 | | isClamped = false; |
| | 39 | 83 | | return Fixed64.FromRaw(low); |
| | | 84 | | } |
| | | 85 | | |
| | 467 | 86 | | Signed320 scaledAxisLength = |
| | 467 | 87 | | GetFloorSquareRootScaledByFixed64(squaredAxisLength); |
| | 467 | 88 | | Signed576 scaledDenominator = MultiplySigned320( |
| | 467 | 89 | | scaledAxisLength, |
| | 467 | 90 | | commonDenominator); |
| | 467 | 91 | | if (!Fixed64.TryGetSignedRawRatio( |
| | 467 | 92 | | Signed832.ExtendValue(overlap), |
| | 467 | 93 | | Signed832.ExtendValue(scaledDenominator), |
| | 467 | 94 | | FixedMath.SHIFT_AMOUNT_I, |
| | 467 | 95 | | out Fixed64 depth)) |
| | | 96 | | { |
| | | 97 | | // The floor root can place a representable exact result one raw |
| | | 98 | | // unit above the scalar domain. The exact midpoint correction |
| | | 99 | | // below distinguishes MaxValue from MaxValue - one raw unit. |
| | 1 | 100 | | depth = Fixed64.MaxValue; |
| | | 101 | | } |
| | 467 | 102 | | isClamped = false; |
| | 467 | 103 | | if (depth == Fixed64.Zero) |
| | 7 | 104 | | return depth; |
| | | 105 | | |
| | 460 | 106 | | Signed192 lowerMidpoint = SubtractSigned192( |
| | 460 | 107 | | AddSigned192( |
| | 460 | 108 | | Signed192.Signed(depth.m_rawValue), |
| | 460 | 109 | | Signed192.Signed(depth.m_rawValue)), |
| | 460 | 110 | | Signed192.Signed(1L)); |
| | 460 | 111 | | int comparison = CompareNormalizedDepthToTwiceRaw( |
| | 460 | 112 | | overlap, |
| | 460 | 113 | | squaredAxisLength, |
| | 460 | 114 | | commonDenominator, |
| | 460 | 115 | | lowerMidpoint); |
| | 460 | 116 | | if (comparison < (depth.m_rawValue & 1L)) |
| | | 117 | | { |
| | 34 | 118 | | depth = Fixed64.FromRaw(depth.m_rawValue - 1L); |
| | | 119 | | } |
| | | 120 | | |
| | 460 | 121 | | return depth; |
| | | 122 | | } |
| | | 123 | | |
| | | 124 | | /// <summary> |
| | | 125 | | /// Compares nonnegative depths represented as |
| | | 126 | | /// <c>overlap / (commonDenominator * sqrt(squaredAxisLength))</c>. |
| | | 127 | | /// </summary> |
| | | 128 | | internal static int CompareNonNegativeNormalizedDepths( |
| | | 129 | | Signed576 leftOverlap, |
| | | 130 | | Signed576 leftSquaredAxisLength, |
| | | 131 | | Signed320 leftCommonDenominator, |
| | | 132 | | Signed576 rightOverlap, |
| | | 133 | | Signed576 rightSquaredAxisLength, |
| | | 134 | | Signed320 rightCommonDenominator) |
| | | 135 | | { |
| | 8984 | 136 | | Span<ulong> leftOverlapWords = stackalloc ulong[9]; |
| | 8984 | 137 | | Span<ulong> rightOverlapWords = stackalloc ulong[9]; |
| | 8984 | 138 | | Span<ulong> leftAxisWords = stackalloc ulong[9]; |
| | 8984 | 139 | | Span<ulong> rightAxisWords = stackalloc ulong[9]; |
| | 8984 | 140 | | GetMagnitude(leftOverlap, leftOverlapWords); |
| | 8984 | 141 | | GetMagnitude(rightOverlap, rightOverlapWords); |
| | 8984 | 142 | | GetMagnitude(leftSquaredAxisLength, leftAxisWords); |
| | 8984 | 143 | | GetMagnitude(rightSquaredAxisLength, rightAxisWords); |
| | | 144 | | |
| | 8984 | 145 | | if (leftCommonDenominator.Equals(rightCommonDenominator)) |
| | | 146 | | { |
| | 8983 | 147 | | return CompareSignedNormalizedMagnitudes( |
| | 8983 | 148 | | leftOverlapWords, |
| | 8983 | 149 | | leftOverlap.Sign, |
| | 8983 | 150 | | leftAxisWords, |
| | 8983 | 151 | | rightOverlapWords, |
| | 8983 | 152 | | rightOverlap.Sign, |
| | 8983 | 153 | | rightAxisWords); |
| | | 154 | | } |
| | | 155 | | |
| | 1 | 156 | | Span<ulong> leftCommonWords = stackalloc ulong[5]; |
| | 1 | 157 | | Span<ulong> rightCommonWords = stackalloc ulong[5]; |
| | 1 | 158 | | GetMagnitude( |
| | 1 | 159 | | leftCommonDenominator, |
| | 1 | 160 | | out leftCommonWords[4], |
| | 1 | 161 | | out leftCommonWords[3], |
| | 1 | 162 | | out leftCommonWords[2], |
| | 1 | 163 | | out leftCommonWords[1], |
| | 1 | 164 | | out leftCommonWords[0]); |
| | 1 | 165 | | GetMagnitude( |
| | 1 | 166 | | rightCommonDenominator, |
| | 1 | 167 | | out rightCommonWords[4], |
| | 1 | 168 | | out rightCommonWords[3], |
| | 1 | 169 | | out rightCommonWords[2], |
| | 1 | 170 | | out rightCommonWords[1], |
| | 1 | 171 | | out rightCommonWords[0]); |
| | | 172 | | |
| | 1 | 173 | | Span<ulong> leftOverlapSquared = stackalloc ulong[18]; |
| | 1 | 174 | | Span<ulong> rightOverlapSquared = stackalloc ulong[18]; |
| | 1 | 175 | | Span<ulong> leftCommonSquared = stackalloc ulong[10]; |
| | 1 | 176 | | Span<ulong> rightCommonSquared = stackalloc ulong[10]; |
| | 1 | 177 | | MultiplyMagnitudes( |
| | 1 | 178 | | leftOverlapWords, |
| | 1 | 179 | | leftOverlapWords, |
| | 1 | 180 | | leftOverlapSquared); |
| | 1 | 181 | | MultiplyMagnitudes( |
| | 1 | 182 | | rightOverlapWords, |
| | 1 | 183 | | rightOverlapWords, |
| | 1 | 184 | | rightOverlapSquared); |
| | 1 | 185 | | MultiplyMagnitudes( |
| | 1 | 186 | | leftCommonWords, |
| | 1 | 187 | | leftCommonWords, |
| | 1 | 188 | | leftCommonSquared); |
| | 1 | 189 | | MultiplyMagnitudes( |
| | 1 | 190 | | rightCommonWords, |
| | 1 | 191 | | rightCommonWords, |
| | 1 | 192 | | rightCommonSquared); |
| | | 193 | | |
| | 1 | 194 | | Span<ulong> leftScaledOnce = stackalloc ulong[28]; |
| | 1 | 195 | | Span<ulong> rightScaledOnce = stackalloc ulong[28]; |
| | 1 | 196 | | Span<ulong> leftScaled = stackalloc ulong[40]; |
| | 1 | 197 | | Span<ulong> rightScaled = stackalloc ulong[40]; |
| | 1 | 198 | | MultiplyMagnitudes( |
| | 1 | 199 | | leftOverlapSquared, |
| | 1 | 200 | | rightCommonSquared, |
| | 1 | 201 | | leftScaledOnce); |
| | 1 | 202 | | MultiplyMagnitudes( |
| | 1 | 203 | | rightOverlapSquared, |
| | 1 | 204 | | leftCommonSquared, |
| | 1 | 205 | | rightScaledOnce); |
| | 1 | 206 | | MultiplyMagnitudes( |
| | 1 | 207 | | leftScaledOnce, |
| | 1 | 208 | | rightAxisWords, |
| | 1 | 209 | | leftScaled); |
| | 1 | 210 | | MultiplyMagnitudes( |
| | 1 | 211 | | rightScaledOnce, |
| | 1 | 212 | | leftAxisWords, |
| | 1 | 213 | | rightScaled); |
| | 1 | 214 | | return CompareMagnitudeEqualLength(leftScaled, rightScaled); |
| | | 215 | | } |
| | | 216 | | |
| | | 217 | | private static int CompareNormalizedDepthToTwiceRaw( |
| | | 218 | | Signed576 overlap, |
| | | 219 | | Signed576 squaredAxisLength, |
| | | 220 | | Signed320 commonDenominator, |
| | | 221 | | Signed192 twiceRaw) |
| | | 222 | | { |
| | 3434 | 223 | | Span<ulong> overlapWords = stackalloc ulong[9]; |
| | 3434 | 224 | | Span<ulong> axisWords = stackalloc ulong[9]; |
| | 3434 | 225 | | Span<ulong> commonWords = stackalloc ulong[5]; |
| | 3434 | 226 | | Span<ulong> twiceRawWords = stackalloc ulong[3]; |
| | 3434 | 227 | | GetMagnitude(overlap, overlapWords); |
| | 3434 | 228 | | GetMagnitude(squaredAxisLength, axisWords); |
| | 3434 | 229 | | GetMagnitude( |
| | 3434 | 230 | | commonDenominator, |
| | 3434 | 231 | | out commonWords[4], |
| | 3434 | 232 | | out commonWords[3], |
| | 3434 | 233 | | out commonWords[2], |
| | 3434 | 234 | | out commonWords[1], |
| | 3434 | 235 | | out commonWords[0]); |
| | 3434 | 236 | | GetMagnitude( |
| | 3434 | 237 | | twiceRaw, |
| | 3434 | 238 | | out twiceRawWords[2], |
| | 3434 | 239 | | out twiceRawWords[1], |
| | 3434 | 240 | | out twiceRawWords[0]); |
| | | 241 | | |
| | 3434 | 242 | | Span<ulong> left = stackalloc ulong[25]; |
| | 3434 | 243 | | MultiplyMagnitudes(overlapWords, overlapWords, left); |
| | 3434 | 244 | | ShiftLeftMagnitude(left, 2); |
| | | 245 | | |
| | 3434 | 246 | | Span<ulong> commonSquared = stackalloc ulong[10]; |
| | 3434 | 247 | | Span<ulong> twiceRawSquared = stackalloc ulong[6]; |
| | 3434 | 248 | | Span<ulong> thresholdSquared = stackalloc ulong[16]; |
| | 3434 | 249 | | Span<ulong> right = stackalloc ulong[25]; |
| | 3434 | 250 | | MultiplyMagnitudes(commonWords, commonWords, commonSquared); |
| | 3434 | 251 | | MultiplyMagnitudes( |
| | 3434 | 252 | | twiceRawWords, |
| | 3434 | 253 | | twiceRawWords, |
| | 3434 | 254 | | twiceRawSquared); |
| | 3434 | 255 | | MultiplyMagnitudes( |
| | 3434 | 256 | | commonSquared, |
| | 3434 | 257 | | twiceRawSquared, |
| | 3434 | 258 | | thresholdSquared); |
| | 3434 | 259 | | MultiplyMagnitudes(thresholdSquared, axisWords, right); |
| | 3434 | 260 | | return CompareMagnitudeEqualLength(left, right); |
| | | 261 | | } |
| | | 262 | | |
| | | 263 | | #endregion |
| | | 264 | | |
| | | 265 | | #region Normalized Magnitude Comparison |
| | | 266 | | |
| | | 267 | | /// <summary> |
| | | 268 | | /// Compares signed ratios of the form |
| | | 269 | | /// <c>numerator / sqrt(squaredAxisLength)</c>. |
| | | 270 | | /// </summary> |
| | | 271 | | internal static int CompareSignedNormalizedMagnitudes( |
| | | 272 | | ReadOnlySpan<ulong> leftNumerator, |
| | | 273 | | int leftSign, |
| | | 274 | | ReadOnlySpan<ulong> leftSquaredAxisLength, |
| | | 275 | | ReadOnlySpan<ulong> rightNumerator, |
| | | 276 | | int rightSign, |
| | | 277 | | ReadOnlySpan<ulong> rightSquaredAxisLength) |
| | | 278 | | { |
| | 49693 | 279 | | leftSign = leftSign == 0 || IsZeroMagnitude(leftNumerator) |
| | 49693 | 280 | | ? 0 |
| | 49693 | 281 | | : leftSign; |
| | 49693 | 282 | | rightSign = rightSign == 0 || IsZeroMagnitude(rightNumerator) |
| | 49693 | 283 | | ? 0 |
| | 49693 | 284 | | : rightSign; |
| | 49693 | 285 | | if (leftSign != rightSign) |
| | 1301 | 286 | | return leftSign.CompareTo(rightSign); |
| | 48392 | 287 | | if (leftSign == 0) |
| | 53 | 288 | | return 0; |
| | | 289 | | |
| | 48339 | 290 | | int wordCount = Math.Max( |
| | 48339 | 291 | | Math.Max(leftNumerator.Length, rightNumerator.Length), |
| | 48339 | 292 | | Math.Max( |
| | 48339 | 293 | | leftSquaredAxisLength.Length, |
| | 48339 | 294 | | rightSquaredAxisLength.Length)); |
| | 48339 | 295 | | int productWordCount = wordCount * 3; |
| | 48339 | 296 | | Span<ulong> products = |
| | 48339 | 297 | | stackalloc ulong[productWordCount * 2]; |
| | 48339 | 298 | | Span<ulong> square = stackalloc ulong[wordCount * 2]; |
| | 48339 | 299 | | Span<ulong> paddedNumerator = stackalloc ulong[wordCount]; |
| | 48339 | 300 | | Span<ulong> paddedAxis = stackalloc ulong[wordCount]; |
| | | 301 | | |
| | 48339 | 302 | | paddedNumerator.Clear(); |
| | 48339 | 303 | | leftNumerator.CopyTo(paddedNumerator); |
| | 48339 | 304 | | MultiplyMagnitudes( |
| | 48339 | 305 | | paddedNumerator, |
| | 48339 | 306 | | paddedNumerator, |
| | 48339 | 307 | | square); |
| | 48339 | 308 | | paddedAxis.Clear(); |
| | 48339 | 309 | | rightSquaredAxisLength.CopyTo(paddedAxis); |
| | 48339 | 310 | | MultiplyMagnitudes( |
| | 48339 | 311 | | square, |
| | 48339 | 312 | | paddedAxis, |
| | 48339 | 313 | | products.Slice(0, productWordCount)); |
| | | 314 | | |
| | 48339 | 315 | | paddedNumerator.Clear(); |
| | 48339 | 316 | | rightNumerator.CopyTo(paddedNumerator); |
| | 48339 | 317 | | MultiplyMagnitudes( |
| | 48339 | 318 | | paddedNumerator, |
| | 48339 | 319 | | paddedNumerator, |
| | 48339 | 320 | | square); |
| | 48339 | 321 | | paddedAxis.Clear(); |
| | 48339 | 322 | | leftSquaredAxisLength.CopyTo(paddedAxis); |
| | 48339 | 323 | | MultiplyMagnitudes( |
| | 48339 | 324 | | square, |
| | 48339 | 325 | | paddedAxis, |
| | 48339 | 326 | | products.Slice( |
| | 48339 | 327 | | productWordCount, |
| | 48339 | 328 | | productWordCount)); |
| | | 329 | | |
| | 48339 | 330 | | int comparison = CompareMagnitudeEqualLength( |
| | 48339 | 331 | | products.Slice(0, productWordCount), |
| | 48339 | 332 | | products.Slice(productWordCount, productWordCount)); |
| | 48339 | 333 | | return leftSign > 0 ? comparison : -comparison; |
| | | 334 | | } |
| | | 335 | | |
| | | 336 | | /// <summary> |
| | | 337 | | /// Gets the exact sign of |
| | | 338 | | /// <c>rational + coefficient * sqrt(squaredRadicand)</c>. |
| | | 339 | | /// </summary> |
| | | 340 | | internal static int GetSignedMagnitudeAndSquareRootSign( |
| | | 341 | | ReadOnlySpan<ulong> rational, |
| | | 342 | | int rationalSign, |
| | | 343 | | ReadOnlySpan<ulong> coefficient, |
| | | 344 | | int coefficientSign, |
| | | 345 | | ReadOnlySpan<ulong> squaredRadicand) |
| | | 346 | | { |
| | 4619 | 347 | | rationalSign = rationalSign == 0 || IsZeroMagnitude(rational) |
| | 4619 | 348 | | ? 0 |
| | 4619 | 349 | | : rationalSign; |
| | 4619 | 350 | | coefficientSign = |
| | 4619 | 351 | | coefficientSign == 0 |
| | 4619 | 352 | | || IsZeroMagnitude(coefficient) |
| | 4619 | 353 | | || IsZeroMagnitude(squaredRadicand) |
| | 4619 | 354 | | ? 0 |
| | 4619 | 355 | | : coefficientSign; |
| | 4619 | 356 | | if (rationalSign == 0) |
| | 357 | 357 | | return coefficientSign; |
| | 4262 | 358 | | if (coefficientSign == 0 || rationalSign == coefficientSign) |
| | 1715 | 359 | | return rationalSign; |
| | | 360 | | |
| | 2547 | 361 | | int wordCount = Math.Max( |
| | 2547 | 362 | | Math.Max(rational.Length, coefficient.Length), |
| | 2547 | 363 | | squaredRadicand.Length); |
| | 2547 | 364 | | int productWordCount = wordCount * 3; |
| | 2547 | 365 | | Span<ulong> products = |
| | 2547 | 366 | | stackalloc ulong[productWordCount * 2]; |
| | 2547 | 367 | | products.Clear(); |
| | 2547 | 368 | | Span<ulong> padded = stackalloc ulong[wordCount]; |
| | 2547 | 369 | | Span<ulong> square = stackalloc ulong[wordCount * 2]; |
| | | 370 | | |
| | 2547 | 371 | | padded.Clear(); |
| | 2547 | 372 | | rational.CopyTo(padded); |
| | 2547 | 373 | | MultiplyMagnitudes(padded, padded, square); |
| | 2547 | 374 | | square.CopyTo(products); |
| | | 375 | | |
| | 2547 | 376 | | padded.Clear(); |
| | 2547 | 377 | | coefficient.CopyTo(padded); |
| | 2547 | 378 | | MultiplyMagnitudes(padded, padded, square); |
| | 2547 | 379 | | padded.Clear(); |
| | 2547 | 380 | | squaredRadicand.CopyTo(padded); |
| | 2547 | 381 | | MultiplyMagnitudes( |
| | 2547 | 382 | | square, |
| | 2547 | 383 | | padded, |
| | 2547 | 384 | | products.Slice( |
| | 2547 | 385 | | productWordCount, |
| | 2547 | 386 | | productWordCount)); |
| | | 387 | | |
| | 2547 | 388 | | int comparison = CompareMagnitudeEqualLength( |
| | 2547 | 389 | | products.Slice(0, productWordCount), |
| | 2547 | 390 | | products.Slice(productWordCount, productWordCount)); |
| | 2547 | 391 | | if (comparison == 0) |
| | 1164 | 392 | | return 0; |
| | 1383 | 393 | | return comparison > 0 |
| | 1383 | 394 | | ? rationalSign |
| | 1383 | 395 | | : coefficientSign; |
| | | 396 | | } |
| | | 397 | | |
| | | 398 | | #endregion |
| | | 399 | | |
| | | 400 | | #region Radial Projection Comparison |
| | | 401 | | |
| | | 402 | | private const int RadialProjectionWordCount = 96; |
| | | 403 | | |
| | | 404 | | /// <summary> |
| | | 405 | | /// Compares two nonnegative radial projection depths without materializing |
| | | 406 | | /// either radical. |
| | | 407 | | /// </summary> |
| | | 408 | | /// <remarks> |
| | | 409 | | /// Each depth has the form |
| | | 410 | | /// (rational + common * sqrt(radicandNumerator / radicandDenominator)) / |
| | | 411 | | /// (common * sqrt(axisSquared)). The comparison squares the nonnegative |
| | | 412 | | /// depths and reduces the result to at most two radicals on either side. |
| | | 413 | | /// </remarks> |
| | | 414 | | internal static int CompareRadialProjectionDepths( |
| | | 415 | | Signed576 leftRational, |
| | | 416 | | Signed832 leftRadicandNumerator, |
| | | 417 | | Signed576 leftRadicandDenominator, |
| | | 418 | | Signed576 leftAxisSquared, |
| | | 419 | | Signed576 rightRational, |
| | | 420 | | Signed832 rightRadicandNumerator, |
| | | 421 | | Signed576 rightRadicandDenominator, |
| | | 422 | | Signed576 rightAxisSquared, |
| | | 423 | | Signed192 common) |
| | | 424 | | { |
| | 2161 | 425 | | Signed320 radialCoefficient = Signed320.ExtendValue(common); |
| | 2161 | 426 | | return CompareRadialProjectionDepths( |
| | 2161 | 427 | | Signed704.ExtendValue(leftRational), |
| | 2161 | 428 | | radialCoefficient, |
| | 2161 | 429 | | leftRadicandNumerator, |
| | 2161 | 430 | | leftRadicandDenominator, |
| | 2161 | 431 | | leftAxisSquared, |
| | 2161 | 432 | | Signed704.ExtendValue(rightRational), |
| | 2161 | 433 | | radialCoefficient, |
| | 2161 | 434 | | rightRadicandNumerator, |
| | 2161 | 435 | | rightRadicandDenominator, |
| | 2161 | 436 | | rightAxisSquared); |
| | | 437 | | } |
| | | 438 | | |
| | | 439 | | /// <summary> |
| | | 440 | | /// Compares two nonnegative radial projection depths without materializing |
| | | 441 | | /// either radical. |
| | | 442 | | /// </summary> |
| | | 443 | | /// <remarks> |
| | | 444 | | /// Each depth has the form |
| | | 445 | | /// (rational + radialCoefficient * |
| | | 446 | | /// sqrt(radicandNumerator / radicandDenominator)) / |
| | | 447 | | /// sqrt(axisSquared). Any shared positive denominator has already been |
| | | 448 | | /// omitted because it cannot change the ordering. |
| | | 449 | | /// </remarks> |
| | | 450 | | internal static int CompareRadialProjectionDepths( |
| | | 451 | | Signed704 leftRational, |
| | | 452 | | Signed320 leftRadialCoefficient, |
| | | 453 | | Signed832 leftRadicandNumerator, |
| | | 454 | | Signed576 leftRadicandDenominator, |
| | | 455 | | Signed576 leftAxisSquared, |
| | | 456 | | Signed704 rightRational, |
| | | 457 | | Signed320 rightRadialCoefficient, |
| | | 458 | | Signed832 rightRadicandNumerator, |
| | | 459 | | Signed576 rightRadicandDenominator, |
| | | 460 | | Signed576 rightAxisSquared) |
| | | 461 | | { |
| | 6717 | 462 | | Span<ulong> leftBase = |
| | 6717 | 463 | | stackalloc ulong[RadialProjectionWordCount]; |
| | 6717 | 464 | | Span<ulong> rightBase = |
| | 6717 | 465 | | stackalloc ulong[RadialProjectionWordCount]; |
| | 6717 | 466 | | BuildSquaredRadialDepthNumerator( |
| | 6717 | 467 | | leftRational, |
| | 6717 | 468 | | leftRadialCoefficient, |
| | 6717 | 469 | | leftRadicandNumerator, |
| | 6717 | 470 | | leftRadicandDenominator, |
| | 6717 | 471 | | rightAxisSquared, |
| | 6717 | 472 | | rightRadicandDenominator, |
| | 6717 | 473 | | leftBase); |
| | 6717 | 474 | | BuildSquaredRadialDepthNumerator( |
| | 6717 | 475 | | rightRational, |
| | 6717 | 476 | | rightRadialCoefficient, |
| | 6717 | 477 | | rightRadicandNumerator, |
| | 6717 | 478 | | rightRadicandDenominator, |
| | 6717 | 479 | | leftAxisSquared, |
| | 6717 | 480 | | leftRadicandDenominator, |
| | 6717 | 481 | | rightBase); |
| | | 482 | | |
| | 6717 | 483 | | int baseComparison = CompareMagnitudeEqualLength( |
| | 6717 | 484 | | leftBase, |
| | 6717 | 485 | | rightBase); |
| | 6717 | 486 | | Span<ulong> baseMagnitude = |
| | 6717 | 487 | | stackalloc ulong[RadialProjectionWordCount]; |
| | 6717 | 488 | | if (baseComparison >= 0) |
| | | 489 | | { |
| | 5212 | 490 | | SubtractEqualMagnitudes( |
| | 5212 | 491 | | leftBase, |
| | 5212 | 492 | | rightBase, |
| | 5212 | 493 | | baseMagnitude); |
| | | 494 | | } |
| | | 495 | | else |
| | | 496 | | { |
| | 1505 | 497 | | SubtractEqualMagnitudes( |
| | 1505 | 498 | | rightBase, |
| | 1505 | 499 | | leftBase, |
| | 1505 | 500 | | baseMagnitude); |
| | | 501 | | } |
| | | 502 | | |
| | 6717 | 503 | | Span<ulong> baseRadicand = |
| | 6717 | 504 | | stackalloc ulong[RadialProjectionWordCount]; |
| | 6717 | 505 | | MultiplyMagnitudes( |
| | 6717 | 506 | | baseMagnitude, |
| | 6717 | 507 | | baseMagnitude, |
| | 6717 | 508 | | baseRadicand); |
| | 6717 | 509 | | Span<ulong> leftRadicand = |
| | 6717 | 510 | | stackalloc ulong[RadialProjectionWordCount]; |
| | 6717 | 511 | | BuildRadialDepthCrossRadicand( |
| | 6717 | 512 | | leftRadialCoefficient, |
| | 6717 | 513 | | rightAxisSquared, |
| | 6717 | 514 | | rightRadicandDenominator, |
| | 6717 | 515 | | leftRational, |
| | 6717 | 516 | | leftRadicandNumerator, |
| | 6717 | 517 | | leftRadicandDenominator, |
| | 6717 | 518 | | leftRadicand); |
| | 6717 | 519 | | Span<ulong> rightRadicand = |
| | 6717 | 520 | | stackalloc ulong[RadialProjectionWordCount]; |
| | 6717 | 521 | | BuildRadialDepthCrossRadicand( |
| | 6717 | 522 | | rightRadialCoefficient, |
| | 6717 | 523 | | leftAxisSquared, |
| | 6717 | 524 | | leftRadicandDenominator, |
| | 6717 | 525 | | rightRational, |
| | 6717 | 526 | | rightRadicandNumerator, |
| | 6717 | 527 | | rightRadicandDenominator, |
| | 6717 | 528 | | rightRadicand); |
| | | 529 | | |
| | 6717 | 530 | | int baseSign = IsZeroMagnitude(baseRadicand) |
| | 6717 | 531 | | ? 0 |
| | 6717 | 532 | | : baseComparison; |
| | 6717 | 533 | | int leftSign = IsZeroMagnitude(leftRadicand) |
| | 6717 | 534 | | ? 0 |
| | 6717 | 535 | | : leftRational.Sign; |
| | 6717 | 536 | | int rightSign = IsZeroMagnitude(rightRadicand) |
| | 6717 | 537 | | ? 0 |
| | 6717 | 538 | | : -rightRational.Sign; |
| | 6717 | 539 | | int positiveCount = |
| | 6717 | 540 | | (baseSign > 0 ? 1 : 0) |
| | 6717 | 541 | | + (leftSign > 0 ? 1 : 0) |
| | 6717 | 542 | | + (rightSign > 0 ? 1 : 0); |
| | 6717 | 543 | | int negativeCount = |
| | 6717 | 544 | | (baseSign < 0 ? 1 : 0) |
| | 6717 | 545 | | + (leftSign < 0 ? 1 : 0) |
| | 6717 | 546 | | + (rightSign < 0 ? 1 : 0); |
| | 6717 | 547 | | if (positiveCount == 0) |
| | 1896 | 548 | | return negativeCount == 0 ? 0 : -1; |
| | 4821 | 549 | | if (negativeCount == 0) |
| | 955 | 550 | | return 1; |
| | | 551 | | |
| | 3866 | 552 | | Span<ulong> positiveFirst = |
| | 3866 | 553 | | stackalloc ulong[RadialProjectionWordCount]; |
| | 3866 | 554 | | Span<ulong> positiveSecond = |
| | 3866 | 555 | | stackalloc ulong[RadialProjectionWordCount]; |
| | 3866 | 556 | | Span<ulong> negativeFirst = |
| | 3866 | 557 | | stackalloc ulong[RadialProjectionWordCount]; |
| | 3866 | 558 | | Span<ulong> negativeSecond = |
| | 3866 | 559 | | stackalloc ulong[RadialProjectionWordCount]; |
| | 3866 | 560 | | positiveFirst.Clear(); |
| | 3866 | 561 | | positiveSecond.Clear(); |
| | 3866 | 562 | | negativeFirst.Clear(); |
| | 3866 | 563 | | negativeSecond.Clear(); |
| | 3866 | 564 | | int positiveIndex = 0; |
| | 3866 | 565 | | int negativeIndex = 0; |
| | 3866 | 566 | | AddSignedRadicand( |
| | 3866 | 567 | | baseRadicand, |
| | 3866 | 568 | | baseSign, |
| | 3866 | 569 | | positiveFirst, |
| | 3866 | 570 | | positiveSecond, |
| | 3866 | 571 | | negativeFirst, |
| | 3866 | 572 | | negativeSecond, |
| | 3866 | 573 | | ref positiveIndex, |
| | 3866 | 574 | | ref negativeIndex); |
| | 3866 | 575 | | AddSignedRadicand( |
| | 3866 | 576 | | leftRadicand, |
| | 3866 | 577 | | leftSign, |
| | 3866 | 578 | | positiveFirst, |
| | 3866 | 579 | | positiveSecond, |
| | 3866 | 580 | | negativeFirst, |
| | 3866 | 581 | | negativeSecond, |
| | 3866 | 582 | | ref positiveIndex, |
| | 3866 | 583 | | ref negativeIndex); |
| | 3866 | 584 | | AddSignedRadicand( |
| | 3866 | 585 | | rightRadicand, |
| | 3866 | 586 | | rightSign, |
| | 3866 | 587 | | positiveFirst, |
| | 3866 | 588 | | positiveSecond, |
| | 3866 | 589 | | negativeFirst, |
| | 3866 | 590 | | negativeSecond, |
| | 3866 | 591 | | ref positiveIndex, |
| | 3866 | 592 | | ref negativeIndex); |
| | 3866 | 593 | | return CompareNonNegativeRadicalPairs( |
| | 3866 | 594 | | positiveFirst, |
| | 3866 | 595 | | positiveSecond, |
| | 3866 | 596 | | negativeFirst, |
| | 3866 | 597 | | negativeSecond); |
| | | 598 | | } |
| | | 599 | | |
| | | 600 | | private static void AddSignedRadicand( |
| | | 601 | | ReadOnlySpan<ulong> radicand, |
| | | 602 | | int sign, |
| | | 603 | | Span<ulong> positiveFirst, |
| | | 604 | | Span<ulong> positiveSecond, |
| | | 605 | | Span<ulong> negativeFirst, |
| | | 606 | | Span<ulong> negativeSecond, |
| | | 607 | | ref int positiveIndex, |
| | | 608 | | ref int negativeIndex) |
| | | 609 | | { |
| | 11598 | 610 | | if (sign == 0) |
| | 1937 | 611 | | return; |
| | | 612 | | |
| | 9661 | 613 | | if (sign > 0) |
| | | 614 | | { |
| | 4816 | 615 | | radicand.CopyTo( |
| | 4816 | 616 | | positiveIndex++ == 0 |
| | 4816 | 617 | | ? positiveFirst |
| | 4816 | 618 | | : positiveSecond); |
| | 4816 | 619 | | return; |
| | | 620 | | } |
| | | 621 | | |
| | 4845 | 622 | | radicand.CopyTo( |
| | 4845 | 623 | | negativeIndex++ == 0 |
| | 4845 | 624 | | ? negativeFirst |
| | 4845 | 625 | | : negativeSecond); |
| | 4845 | 626 | | } |
| | | 627 | | |
| | | 628 | | private static void BuildSquaredRadialDepthNumerator( |
| | | 629 | | Signed704 rational, |
| | | 630 | | Signed320 radialCoefficient, |
| | | 631 | | Signed832 radicandNumerator, |
| | | 632 | | Signed576 radicandDenominator, |
| | | 633 | | Signed576 otherAxisSquared, |
| | | 634 | | Signed576 otherRadicandDenominator, |
| | | 635 | | Span<ulong> result) |
| | | 636 | | { |
| | 13434 | 637 | | result.Clear(); |
| | 13434 | 638 | | Span<ulong> rationalWords = stackalloc ulong[11]; |
| | 13434 | 639 | | Span<ulong> radialCoefficientWords = stackalloc ulong[5]; |
| | 13434 | 640 | | Span<ulong> numeratorWords = stackalloc ulong[13]; |
| | 13434 | 641 | | Span<ulong> denominatorWords = stackalloc ulong[9]; |
| | 13434 | 642 | | Span<ulong> axisWords = stackalloc ulong[9]; |
| | 13434 | 643 | | Span<ulong> otherDenominatorWords = stackalloc ulong[9]; |
| | 13434 | 644 | | GetMagnitude(rational, rationalWords); |
| | 13434 | 645 | | GetMagnitude( |
| | 13434 | 646 | | radialCoefficient, |
| | 13434 | 647 | | out radialCoefficientWords[4], |
| | 13434 | 648 | | out radialCoefficientWords[3], |
| | 13434 | 649 | | out radialCoefficientWords[2], |
| | 13434 | 650 | | out radialCoefficientWords[1], |
| | 13434 | 651 | | out radialCoefficientWords[0]); |
| | 13434 | 652 | | GetMagnitude(radicandNumerator, numeratorWords); |
| | 13434 | 653 | | GetMagnitude(radicandDenominator, denominatorWords); |
| | 13434 | 654 | | GetMagnitude(otherAxisSquared, axisWords); |
| | 13434 | 655 | | GetMagnitude( |
| | 13434 | 656 | | otherRadicandDenominator, |
| | 13434 | 657 | | otherDenominatorWords); |
| | | 658 | | |
| | 13434 | 659 | | Span<ulong> rationalSquared = stackalloc ulong[22]; |
| | 13434 | 660 | | Span<ulong> rationalTerm = stackalloc ulong[31]; |
| | 13434 | 661 | | MultiplyMagnitudes( |
| | 13434 | 662 | | rationalWords, |
| | 13434 | 663 | | rationalWords, |
| | 13434 | 664 | | rationalSquared); |
| | 13434 | 665 | | MultiplyMagnitudes( |
| | 13434 | 666 | | rationalSquared, |
| | 13434 | 667 | | denominatorWords, |
| | 13434 | 668 | | rationalTerm); |
| | | 669 | | |
| | 13434 | 670 | | Span<ulong> coefficientSquared = stackalloc ulong[10]; |
| | 13434 | 671 | | Span<ulong> radialTerm = stackalloc ulong[23]; |
| | 13434 | 672 | | MultiplyMagnitudes( |
| | 13434 | 673 | | radialCoefficientWords, |
| | 13434 | 674 | | radialCoefficientWords, |
| | 13434 | 675 | | coefficientSquared); |
| | 13434 | 676 | | MultiplyMagnitudes( |
| | 13434 | 677 | | coefficientSquared, |
| | 13434 | 678 | | numeratorWords, |
| | 13434 | 679 | | radialTerm); |
| | | 680 | | |
| | 13434 | 681 | | Span<ulong> numerator = stackalloc ulong[32]; |
| | 13434 | 682 | | numerator.Clear(); |
| | 13434 | 683 | | AddMagnitudeInto(rationalTerm, numerator); |
| | 13434 | 684 | | AddMagnitudeInto(radialTerm, numerator); |
| | 13434 | 685 | | Span<ulong> axisScaled = stackalloc ulong[41]; |
| | 13434 | 686 | | Span<ulong> fullyScaled = stackalloc ulong[50]; |
| | 13434 | 687 | | MultiplyMagnitudes( |
| | 13434 | 688 | | numerator, |
| | 13434 | 689 | | axisWords, |
| | 13434 | 690 | | axisScaled); |
| | 13434 | 691 | | MultiplyMagnitudes( |
| | 13434 | 692 | | axisScaled, |
| | 13434 | 693 | | otherDenominatorWords, |
| | 13434 | 694 | | fullyScaled); |
| | 13434 | 695 | | fullyScaled.CopyTo(result); |
| | 13434 | 696 | | } |
| | | 697 | | |
| | | 698 | | private static void BuildRadialDepthCrossRadicand( |
| | | 699 | | Signed320 radialCoefficient, |
| | | 700 | | Signed576 otherAxisSquared, |
| | | 701 | | Signed576 otherRadicandDenominator, |
| | | 702 | | Signed704 rational, |
| | | 703 | | Signed832 radicandNumerator, |
| | | 704 | | Signed576 radicandDenominator, |
| | | 705 | | Span<ulong> result) |
| | | 706 | | { |
| | 13434 | 707 | | if (radialCoefficient.IsZero |
| | 13434 | 708 | | || rational.IsZero |
| | 13434 | 709 | | || radicandNumerator.IsZero) |
| | | 710 | | { |
| | 4647 | 711 | | result.Clear(); |
| | 4647 | 712 | | return; |
| | | 713 | | } |
| | | 714 | | |
| | 8787 | 715 | | Span<ulong> radialCoefficientWords = stackalloc ulong[5]; |
| | 8787 | 716 | | Span<ulong> axisWords = stackalloc ulong[9]; |
| | 8787 | 717 | | Span<ulong> otherDenominatorWords = stackalloc ulong[9]; |
| | 8787 | 718 | | Span<ulong> rationalWords = stackalloc ulong[11]; |
| | 8787 | 719 | | Span<ulong> numeratorWords = stackalloc ulong[13]; |
| | 8787 | 720 | | Span<ulong> denominatorWords = stackalloc ulong[9]; |
| | 8787 | 721 | | GetMagnitude( |
| | 8787 | 722 | | radialCoefficient, |
| | 8787 | 723 | | out radialCoefficientWords[4], |
| | 8787 | 724 | | out radialCoefficientWords[3], |
| | 8787 | 725 | | out radialCoefficientWords[2], |
| | 8787 | 726 | | out radialCoefficientWords[1], |
| | 8787 | 727 | | out radialCoefficientWords[0]); |
| | 8787 | 728 | | GetMagnitude(otherAxisSquared, axisWords); |
| | 8787 | 729 | | GetMagnitude( |
| | 8787 | 730 | | otherRadicandDenominator, |
| | 8787 | 731 | | otherDenominatorWords); |
| | 8787 | 732 | | GetMagnitude(rational, rationalWords); |
| | 8787 | 733 | | GetMagnitude(radicandNumerator, numeratorWords); |
| | 8787 | 734 | | GetMagnitude(radicandDenominator, denominatorWords); |
| | | 735 | | |
| | 8787 | 736 | | Span<ulong> coefficientAndAxis = stackalloc ulong[14]; |
| | 8787 | 737 | | Span<ulong> withRational = stackalloc ulong[25]; |
| | 8787 | 738 | | Span<ulong> completeCoefficient = stackalloc ulong[34]; |
| | 8787 | 739 | | Span<ulong> coefficientSquared = stackalloc ulong[68]; |
| | 8787 | 740 | | Span<ulong> withNumerator = stackalloc ulong[81]; |
| | 8787 | 741 | | MultiplyMagnitudes( |
| | 8787 | 742 | | radialCoefficientWords, |
| | 8787 | 743 | | axisWords, |
| | 8787 | 744 | | coefficientAndAxis); |
| | 8787 | 745 | | MultiplyMagnitudes( |
| | 8787 | 746 | | coefficientAndAxis, |
| | 8787 | 747 | | rationalWords, |
| | 8787 | 748 | | withRational); |
| | 8787 | 749 | | MultiplyMagnitudes( |
| | 8787 | 750 | | withRational, |
| | 8787 | 751 | | otherDenominatorWords, |
| | 8787 | 752 | | completeCoefficient); |
| | 8787 | 753 | | MultiplyMagnitudes( |
| | 8787 | 754 | | completeCoefficient, |
| | 8787 | 755 | | completeCoefficient, |
| | 8787 | 756 | | coefficientSquared); |
| | 8787 | 757 | | MultiplyMagnitudes( |
| | 8787 | 758 | | coefficientSquared, |
| | 8787 | 759 | | numeratorWords, |
| | 8787 | 760 | | withNumerator); |
| | 8787 | 761 | | MultiplyMagnitudes( |
| | 8787 | 762 | | withNumerator, |
| | 8787 | 763 | | denominatorWords, |
| | 8787 | 764 | | result); |
| | 8787 | 765 | | ShiftLeftMagnitude(result, 2); |
| | 8787 | 766 | | } |
| | | 767 | | |
| | | 768 | | private static int CompareNonNegativeRadicalPairs( |
| | | 769 | | ReadOnlySpan<ulong> leftFirst, |
| | | 770 | | ReadOnlySpan<ulong> leftSecond, |
| | | 771 | | ReadOnlySpan<ulong> rightFirst, |
| | | 772 | | ReadOnlySpan<ulong> rightSecond) |
| | | 773 | | { |
| | 3866 | 774 | | Span<ulong> leftBase = |
| | 3866 | 775 | | stackalloc ulong[RadialProjectionWordCount]; |
| | 3866 | 776 | | Span<ulong> rightBase = |
| | 3866 | 777 | | stackalloc ulong[RadialProjectionWordCount]; |
| | 3866 | 778 | | AddEqualMagnitudes(leftFirst, leftSecond, leftBase); |
| | 3866 | 779 | | AddEqualMagnitudes(rightFirst, rightSecond, rightBase); |
| | 3866 | 780 | | int baseComparison = CompareMagnitudeEqualLength( |
| | 3866 | 781 | | leftBase, |
| | 3866 | 782 | | rightBase); |
| | 3866 | 783 | | Span<ulong> baseMagnitude = |
| | 3866 | 784 | | stackalloc ulong[RadialProjectionWordCount]; |
| | 3866 | 785 | | if (baseComparison >= 0) |
| | | 786 | | { |
| | 2966 | 787 | | SubtractEqualMagnitudes( |
| | 2966 | 788 | | leftBase, |
| | 2966 | 789 | | rightBase, |
| | 2966 | 790 | | baseMagnitude); |
| | | 791 | | } |
| | | 792 | | else |
| | | 793 | | { |
| | 900 | 794 | | SubtractEqualMagnitudes( |
| | 900 | 795 | | rightBase, |
| | 900 | 796 | | leftBase, |
| | 900 | 797 | | baseMagnitude); |
| | | 798 | | } |
| | | 799 | | |
| | 3866 | 800 | | Span<ulong> leftProduct = |
| | 3866 | 801 | | stackalloc ulong[RadialProjectionWordCount * 2]; |
| | 3866 | 802 | | Span<ulong> rightProduct = |
| | 3866 | 803 | | stackalloc ulong[RadialProjectionWordCount * 2]; |
| | 3866 | 804 | | MultiplyMagnitudes(leftFirst, leftSecond, leftProduct); |
| | 3866 | 805 | | MultiplyMagnitudes(rightFirst, rightSecond, rightProduct); |
| | 3866 | 806 | | if (baseComparison >= 0) |
| | | 807 | | { |
| | 2966 | 808 | | return ComparePositiveRadicalPairDifference( |
| | 2966 | 809 | | baseMagnitude, |
| | 2966 | 810 | | leftProduct, |
| | 2966 | 811 | | rightProduct); |
| | | 812 | | } |
| | | 813 | | |
| | 900 | 814 | | return -ComparePositiveRadicalPairDifference( |
| | 900 | 815 | | baseMagnitude, |
| | 900 | 816 | | rightProduct, |
| | 900 | 817 | | leftProduct); |
| | | 818 | | } |
| | | 819 | | |
| | | 820 | | private static int ComparePositiveRadicalPairDifference( |
| | | 821 | | ReadOnlySpan<ulong> positiveBase, |
| | | 822 | | ReadOnlySpan<ulong> sameSideProduct, |
| | | 823 | | ReadOnlySpan<ulong> oppositeSideProduct) |
| | | 824 | | { |
| | 3866 | 825 | | Span<ulong> baseSquared = |
| | 3866 | 826 | | stackalloc ulong[RadialProjectionWordCount * 2]; |
| | 3866 | 827 | | Span<ulong> fourSame = |
| | 3866 | 828 | | stackalloc ulong[RadialProjectionWordCount * 2]; |
| | 3866 | 829 | | Span<ulong> fourOpposite = |
| | 3866 | 830 | | stackalloc ulong[RadialProjectionWordCount * 2]; |
| | 3866 | 831 | | MultiplyMagnitudes( |
| | 3866 | 832 | | positiveBase, |
| | 3866 | 833 | | positiveBase, |
| | 3866 | 834 | | baseSquared); |
| | 3866 | 835 | | sameSideProduct.CopyTo(fourSame); |
| | 3866 | 836 | | oppositeSideProduct.CopyTo(fourOpposite); |
| | 3866 | 837 | | ShiftLeftMagnitude(fourSame, 2); |
| | 3866 | 838 | | ShiftLeftMagnitude(fourOpposite, 2); |
| | | 839 | | |
| | 3866 | 840 | | Span<ulong> knownLeft = |
| | 3866 | 841 | | stackalloc ulong[RadialProjectionWordCount * 2]; |
| | 3866 | 842 | | AddEqualMagnitudes(baseSquared, fourSame, knownLeft); |
| | 3866 | 843 | | int knownComparison = CompareMagnitudeEqualLength( |
| | 3866 | 844 | | knownLeft, |
| | 3866 | 845 | | fourOpposite); |
| | 3866 | 846 | | if (knownComparison > 0) |
| | 2393 | 847 | | return 1; |
| | 1473 | 848 | | if (knownComparison == 0) |
| | | 849 | | { |
| | | 850 | | // This reducer partitions three signed radical terms. Therefore |
| | | 851 | | // at least one side has a zero product; equality of the known |
| | | 852 | | // squared terms can only occur when the remaining cross term is |
| | | 853 | | // also zero. |
| | 1424 | 854 | | return 0; |
| | | 855 | | } |
| | | 856 | | |
| | 49 | 857 | | Span<ulong> remainder = |
| | 49 | 858 | | stackalloc ulong[RadialProjectionWordCount * 2]; |
| | 49 | 859 | | SubtractEqualMagnitudes( |
| | 49 | 860 | | fourOpposite, |
| | 49 | 861 | | knownLeft, |
| | 49 | 862 | | remainder); |
| | 49 | 863 | | Span<ulong> crossSquared = |
| | 49 | 864 | | stackalloc ulong[RadialProjectionWordCount * 4]; |
| | 49 | 865 | | Span<ulong> remainderSquared = |
| | 49 | 866 | | stackalloc ulong[RadialProjectionWordCount * 4]; |
| | 49 | 867 | | MultiplyMagnitudes( |
| | 49 | 868 | | baseSquared, |
| | 49 | 869 | | sameSideProduct, |
| | 49 | 870 | | crossSquared); |
| | 49 | 871 | | ShiftLeftMagnitude(crossSquared, 4); |
| | 49 | 872 | | MultiplyMagnitudes( |
| | 49 | 873 | | remainder, |
| | 49 | 874 | | remainder, |
| | 49 | 875 | | remainderSquared); |
| | 49 | 876 | | return CompareMagnitudeEqualLength( |
| | 49 | 877 | | crossSquared, |
| | 49 | 878 | | remainderSquared); |
| | | 879 | | } |
| | | 880 | | |
| | | 881 | | #endregion |
| | | 882 | | |
| | | 883 | | #region Radical Comparison |
| | | 884 | | |
| | | 885 | | |
| | | 886 | | /// <summary> |
| | | 887 | | /// Compares <c>sqrt(firstNumerator / firstDenominator) + |
| | | 888 | | /// sqrt(secondNumerator / secondDenominator)</c> with a nonnegative ratio. |
| | | 889 | | /// </summary> |
| | | 890 | | internal static int CompareNonNegativeRadicalSumToRatio( |
| | | 891 | | Signed576 firstNumerator, |
| | | 892 | | Signed192 firstDenominator, |
| | | 893 | | Signed576 secondNumerator, |
| | | 894 | | Signed192 secondDenominator, |
| | | 895 | | Signed320 ratioNumerator, |
| | | 896 | | Signed192 ratioDenominator) |
| | | 897 | | { |
| | 729 | 898 | | bool firstZero = firstNumerator.IsZero; |
| | 729 | 899 | | bool secondZero = secondNumerator.IsZero; |
| | 729 | 900 | | if (firstZero && secondZero) |
| | 39 | 901 | | return ratioNumerator.IsZero ? 0 : -1; |
| | 690 | 902 | | if (firstZero) |
| | | 903 | | { |
| | 238 | 904 | | return CompareNonNegativeRadicalToRatio( |
| | 238 | 905 | | secondNumerator, |
| | 238 | 906 | | secondDenominator, |
| | 238 | 907 | | ratioNumerator, |
| | 238 | 908 | | ratioDenominator); |
| | | 909 | | } |
| | 452 | 910 | | if (secondZero) |
| | | 911 | | { |
| | 165 | 912 | | return CompareNonNegativeRadicalToRatio( |
| | 165 | 913 | | firstNumerator, |
| | 165 | 914 | | firstDenominator, |
| | 165 | 915 | | ratioNumerator, |
| | 165 | 916 | | ratioDenominator); |
| | | 917 | | } |
| | | 918 | | |
| | 287 | 919 | | Signed576 ratioSquared = MultiplySigned320(ratioNumerator, ratioNumerator); |
| | 287 | 920 | | Signed576 denominatorProduct = MultiplySigned320( |
| | 287 | 921 | | Signed320.ExtendValue(firstDenominator), |
| | 287 | 922 | | Signed320.ExtendValue(secondDenominator)); |
| | 287 | 923 | | Signed576 squaredRatioTerm = MultiplyNonNegativeToSigned576( |
| | 287 | 924 | | ratioSquared, |
| | 287 | 925 | | denominatorProduct); |
| | 287 | 926 | | Signed576 radicalSquares = AddSigned576( |
| | 287 | 927 | | MultiplySigned576(firstNumerator, secondDenominator), |
| | 287 | 928 | | MultiplySigned576(secondNumerator, firstDenominator)); |
| | 287 | 929 | | Signed320 ratioDenominatorSquared = MultiplySigned192( |
| | 287 | 930 | | ratioDenominator, |
| | 287 | 931 | | ratioDenominator); |
| | 287 | 932 | | _ = Signed192.TryNarrowSigned( |
| | 287 | 933 | | Signed576.ExtendValue(ratioDenominatorSquared), |
| | 287 | 934 | | out Signed192 narrowRatioDenominatorSquared); |
| | 287 | 935 | | Signed576 radicalSquareTerm = MultiplySigned576( |
| | 287 | 936 | | radicalSquares, |
| | 287 | 937 | | narrowRatioDenominatorSquared); |
| | 287 | 938 | | Signed576 remainder = SubtractSigned576( |
| | 287 | 939 | | squaredRatioTerm, |
| | 287 | 940 | | radicalSquareTerm); |
| | 287 | 941 | | if (remainder.Sign <= 0) |
| | 47 | 942 | | return 1; |
| | | 943 | | |
| | 240 | 944 | | return CompareRadicalCrossTerm( |
| | 240 | 945 | | firstNumerator, |
| | 240 | 946 | | firstDenominator, |
| | 240 | 947 | | secondNumerator, |
| | 240 | 948 | | secondDenominator, |
| | 240 | 949 | | ratioDenominator, |
| | 240 | 950 | | remainder); |
| | | 951 | | } |
| | | 952 | | |
| | | 953 | | /// <summary> |
| | | 954 | | /// Compares a nonnegative radical with a nonnegative ratio whose |
| | | 955 | | /// denominators require the full five-word geometry range. |
| | | 956 | | /// </summary> |
| | | 957 | | internal static int CompareNonNegativeRadicalToRatio( |
| | | 958 | | Signed576 numerator, |
| | | 959 | | Signed320 denominator, |
| | | 960 | | Signed320 ratioNumerator, |
| | | 961 | | Signed320 ratioDenominator) |
| | | 962 | | { |
| | 147 | 963 | | Span<ulong> numeratorWords = stackalloc ulong[9]; |
| | 147 | 964 | | Span<ulong> denominatorWords = stackalloc ulong[5]; |
| | 147 | 965 | | Span<ulong> ratioNumeratorWords = stackalloc ulong[5]; |
| | 147 | 966 | | Span<ulong> ratioDenominatorWords = stackalloc ulong[5]; |
| | 147 | 967 | | GetMagnitude(numerator, numeratorWords); |
| | 147 | 968 | | GetMagnitude( |
| | 147 | 969 | | denominator, |
| | 147 | 970 | | out denominatorWords[4], |
| | 147 | 971 | | out denominatorWords[3], |
| | 147 | 972 | | out denominatorWords[2], |
| | 147 | 973 | | out denominatorWords[1], |
| | 147 | 974 | | out denominatorWords[0]); |
| | 147 | 975 | | GetMagnitude( |
| | 147 | 976 | | ratioNumerator, |
| | 147 | 977 | | out ratioNumeratorWords[4], |
| | 147 | 978 | | out ratioNumeratorWords[3], |
| | 147 | 979 | | out ratioNumeratorWords[2], |
| | 147 | 980 | | out ratioNumeratorWords[1], |
| | 147 | 981 | | out ratioNumeratorWords[0]); |
| | 147 | 982 | | GetMagnitude( |
| | 147 | 983 | | ratioDenominator, |
| | 147 | 984 | | out ratioDenominatorWords[4], |
| | 147 | 985 | | out ratioDenominatorWords[3], |
| | 147 | 986 | | out ratioDenominatorWords[2], |
| | 147 | 987 | | out ratioDenominatorWords[1], |
| | 147 | 988 | | out ratioDenominatorWords[0]); |
| | | 989 | | |
| | 147 | 990 | | Span<ulong> ratioNumeratorSquared = stackalloc ulong[10]; |
| | 147 | 991 | | Span<ulong> ratioDenominatorSquared = stackalloc ulong[10]; |
| | 147 | 992 | | Span<ulong> left = stackalloc ulong[19]; |
| | 147 | 993 | | Span<ulong> right = stackalloc ulong[19]; |
| | 147 | 994 | | MultiplyMagnitudes( |
| | 147 | 995 | | ratioNumeratorWords, |
| | 147 | 996 | | ratioNumeratorWords, |
| | 147 | 997 | | ratioNumeratorSquared); |
| | 147 | 998 | | MultiplyMagnitudes( |
| | 147 | 999 | | ratioDenominatorWords, |
| | 147 | 1000 | | ratioDenominatorWords, |
| | 147 | 1001 | | ratioDenominatorSquared); |
| | 147 | 1002 | | MultiplyMagnitudes( |
| | 147 | 1003 | | numeratorWords, |
| | 147 | 1004 | | ratioDenominatorSquared, |
| | 147 | 1005 | | left); |
| | 147 | 1006 | | MultiplyMagnitudes( |
| | 147 | 1007 | | ratioNumeratorSquared, |
| | 147 | 1008 | | denominatorWords, |
| | 147 | 1009 | | right); |
| | 147 | 1010 | | return CompareMagnitudeEqualLength(left, right); |
| | | 1011 | | } |
| | | 1012 | | |
| | | 1013 | | /// <summary> |
| | | 1014 | | /// Returns the sign of a signed rational term plus a signed coefficient |
| | | 1015 | | /// multiplied by one nonnegative radical. |
| | | 1016 | | /// </summary> |
| | | 1017 | | internal static int CompareSignedLinearRadicalToZero( |
| | | 1018 | | Signed832 rational, |
| | | 1019 | | Signed704 radicalCoefficient, |
| | | 1020 | | Signed576 radicandNumerator, |
| | | 1021 | | Signed320 radicandDenominator) |
| | | 1022 | | { |
| | 740 | 1023 | | int rationalSign = rational.Sign; |
| | 740 | 1024 | | int coefficientSign = radicandNumerator.IsZero |
| | 740 | 1025 | | ? 0 |
| | 740 | 1026 | | : radicalCoefficient.Sign; |
| | 740 | 1027 | | if (rationalSign == 0) |
| | 33 | 1028 | | return coefficientSign; |
| | 707 | 1029 | | if (coefficientSign == 0 || rationalSign == coefficientSign) |
| | 250 | 1030 | | return rationalSign; |
| | | 1031 | | |
| | 457 | 1032 | | Span<ulong> rationalWords = stackalloc ulong[13]; |
| | 457 | 1033 | | Span<ulong> coefficientWords = stackalloc ulong[11]; |
| | 457 | 1034 | | Span<ulong> numeratorWords = stackalloc ulong[9]; |
| | 457 | 1035 | | Span<ulong> denominatorWords = stackalloc ulong[5]; |
| | 457 | 1036 | | GetMagnitude(rational, rationalWords); |
| | 457 | 1037 | | GetMagnitude(radicalCoefficient, coefficientWords); |
| | 457 | 1038 | | GetMagnitude(radicandNumerator, numeratorWords); |
| | 457 | 1039 | | GetMagnitude( |
| | 457 | 1040 | | radicandDenominator, |
| | 457 | 1041 | | out denominatorWords[4], |
| | 457 | 1042 | | out denominatorWords[3], |
| | 457 | 1043 | | out denominatorWords[2], |
| | 457 | 1044 | | out denominatorWords[1], |
| | 457 | 1045 | | out denominatorWords[0]); |
| | | 1046 | | |
| | 457 | 1047 | | Span<ulong> rationalSquared = stackalloc ulong[26]; |
| | 457 | 1048 | | Span<ulong> coefficientSquared = stackalloc ulong[22]; |
| | 457 | 1049 | | Span<ulong> rationalScaled = stackalloc ulong[31]; |
| | 457 | 1050 | | Span<ulong> coefficientScaled = stackalloc ulong[31]; |
| | 457 | 1051 | | MultiplyMagnitudes( |
| | 457 | 1052 | | rationalWords, |
| | 457 | 1053 | | rationalWords, |
| | 457 | 1054 | | rationalSquared); |
| | 457 | 1055 | | MultiplyMagnitudes( |
| | 457 | 1056 | | coefficientWords, |
| | 457 | 1057 | | coefficientWords, |
| | 457 | 1058 | | coefficientSquared); |
| | 457 | 1059 | | MultiplyMagnitudes( |
| | 457 | 1060 | | rationalSquared, |
| | 457 | 1061 | | denominatorWords, |
| | 457 | 1062 | | rationalScaled); |
| | 457 | 1063 | | MultiplyMagnitudes( |
| | 457 | 1064 | | coefficientSquared, |
| | 457 | 1065 | | numeratorWords, |
| | 457 | 1066 | | coefficientScaled); |
| | 457 | 1067 | | int comparison = CompareMagnitudeEqualLength( |
| | 457 | 1068 | | rationalScaled, |
| | 457 | 1069 | | coefficientScaled); |
| | 457 | 1070 | | return comparison == 0 |
| | 457 | 1071 | | ? 0 |
| | 457 | 1072 | | : comparison > 0 |
| | 457 | 1073 | | ? rationalSign |
| | 457 | 1074 | | : coefficientSign; |
| | | 1075 | | } |
| | | 1076 | | |
| | | 1077 | | private static int CompareNonNegativeRadicalToRatio( |
| | | 1078 | | Signed576 numerator, |
| | | 1079 | | Signed192 denominator, |
| | | 1080 | | Signed320 ratioNumerator, |
| | | 1081 | | Signed192 ratioDenominator) |
| | | 1082 | | { |
| | 403 | 1083 | | Signed320 ratioDenominatorSquared = MultiplySigned192( |
| | 403 | 1084 | | ratioDenominator, |
| | 403 | 1085 | | ratioDenominator); |
| | 403 | 1086 | | _ = Signed192.TryNarrowSigned( |
| | 403 | 1087 | | Signed576.ExtendValue(ratioDenominatorSquared), |
| | 403 | 1088 | | out Signed192 narrowRatioDenominatorSquared); |
| | 403 | 1089 | | Signed576 left = MultiplySigned576( |
| | 403 | 1090 | | numerator, |
| | 403 | 1091 | | narrowRatioDenominatorSquared); |
| | 403 | 1092 | | Signed576 right = MultiplySigned576( |
| | 403 | 1093 | | MultiplySigned320(ratioNumerator, ratioNumerator), |
| | 403 | 1094 | | denominator); |
| | 403 | 1095 | | return CompareNonNegative(left, right); |
| | | 1096 | | } |
| | | 1097 | | |
| | | 1098 | | private static int CompareRadicalCrossTerm( |
| | | 1099 | | Signed576 firstNumerator, |
| | | 1100 | | Signed192 firstDenominator, |
| | | 1101 | | Signed576 secondNumerator, |
| | | 1102 | | Signed192 secondDenominator, |
| | | 1103 | | Signed192 ratioDenominator, |
| | | 1104 | | Signed576 remainder) |
| | | 1105 | | { |
| | 240 | 1106 | | Span<ulong> firstNumeratorWords = stackalloc ulong[9]; |
| | 240 | 1107 | | Span<ulong> secondNumeratorWords = stackalloc ulong[9]; |
| | 240 | 1108 | | Span<ulong> firstDenominatorWords = stackalloc ulong[3]; |
| | 240 | 1109 | | Span<ulong> secondDenominatorWords = stackalloc ulong[3]; |
| | 240 | 1110 | | Span<ulong> ratioDenominatorWords = stackalloc ulong[3]; |
| | 240 | 1111 | | Span<ulong> remainderWords = stackalloc ulong[9]; |
| | 240 | 1112 | | GetMagnitude(firstNumerator, firstNumeratorWords); |
| | 240 | 1113 | | GetMagnitude(secondNumerator, secondNumeratorWords); |
| | 240 | 1114 | | GetMagnitude( |
| | 240 | 1115 | | firstDenominator, |
| | 240 | 1116 | | out firstDenominatorWords[2], |
| | 240 | 1117 | | out firstDenominatorWords[1], |
| | 240 | 1118 | | out firstDenominatorWords[0]); |
| | 240 | 1119 | | GetMagnitude( |
| | 240 | 1120 | | secondDenominator, |
| | 240 | 1121 | | out secondDenominatorWords[2], |
| | 240 | 1122 | | out secondDenominatorWords[1], |
| | 240 | 1123 | | out secondDenominatorWords[0]); |
| | 240 | 1124 | | GetMagnitude( |
| | 240 | 1125 | | ratioDenominator, |
| | 240 | 1126 | | out ratioDenominatorWords[2], |
| | 240 | 1127 | | out ratioDenominatorWords[1], |
| | 240 | 1128 | | out ratioDenominatorWords[0]); |
| | 240 | 1129 | | GetMagnitude(remainder, remainderWords); |
| | | 1130 | | |
| | 240 | 1131 | | Span<ulong> numeratorProduct = stackalloc ulong[18]; |
| | 240 | 1132 | | Span<ulong> ratioSquared = stackalloc ulong[6]; |
| | 240 | 1133 | | Span<ulong> ratioFourth = stackalloc ulong[12]; |
| | 240 | 1134 | | Span<ulong> denominatorProduct = stackalloc ulong[6]; |
| | 240 | 1135 | | Span<ulong> numeratorAndRatio = stackalloc ulong[30]; |
| | 240 | 1136 | | Span<ulong> left = stackalloc ulong[36]; |
| | 240 | 1137 | | Span<ulong> right = stackalloc ulong[36]; |
| | 240 | 1138 | | MultiplyMagnitudes( |
| | 240 | 1139 | | firstNumeratorWords, |
| | 240 | 1140 | | secondNumeratorWords, |
| | 240 | 1141 | | numeratorProduct); |
| | 240 | 1142 | | MultiplyMagnitudes( |
| | 240 | 1143 | | ratioDenominatorWords, |
| | 240 | 1144 | | ratioDenominatorWords, |
| | 240 | 1145 | | ratioSquared); |
| | 240 | 1146 | | MultiplyMagnitudes(ratioSquared, ratioSquared, ratioFourth); |
| | 240 | 1147 | | MultiplyMagnitudes( |
| | 240 | 1148 | | firstDenominatorWords, |
| | 240 | 1149 | | secondDenominatorWords, |
| | 240 | 1150 | | denominatorProduct); |
| | 240 | 1151 | | MultiplyMagnitudes( |
| | 240 | 1152 | | numeratorProduct, |
| | 240 | 1153 | | ratioFourth, |
| | 240 | 1154 | | numeratorAndRatio); |
| | 240 | 1155 | | MultiplyMagnitudes( |
| | 240 | 1156 | | numeratorAndRatio, |
| | 240 | 1157 | | denominatorProduct, |
| | 240 | 1158 | | left); |
| | 240 | 1159 | | ShiftLeftMagnitude(left, 2); |
| | 240 | 1160 | | MultiplyMagnitudes(remainderWords, remainderWords, right); |
| | | 1161 | | |
| | 240 | 1162 | | int comparison = CompareMagnitudeEqualLength(left, right); |
| | 240 | 1163 | | return comparison == 0 ? 0 : comparison > 0 ? 1 : -1; |
| | | 1164 | | } |
| | | 1165 | | |
| | | 1166 | | #endregion |
| | | 1167 | | } |