From 79d9f8ef74df2f40c286411c0361f91647079c7b Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Wed, 21 Oct 2026 09:24:51 +0000 Subject: [PATCH] Route the gcd and lowest-terms calls added since #2003 through GreatestCommonDivisors #2003 put the library's 177 gcd and lowest-terms call sites through GreatestCommonDivisors, whose results are PeterO's and whose large-operand case is about 800 times faster. Seventeen sites written since, in PartialFractions, IndefiniteIntegralSolver and RothsteinTrager, called EInteger.Gcd and ERational.ToLowestTerms directly again. On the 35 rows the PRs that wrote them moved, the answers and allocation are identical: the operands those rows see are small. The change keeps the contract in one place for the operands that are not. Part of #233. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- .../Algebra/Polynomials/PartialFractions.cs | 18 +++++++++--------- .../Integration/IndefiniteIntegralSolver.cs | 14 +++++++------- .../Continuous/Integration/RothsteinTrager.cs | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Sources/AngouriMath/Functions/Algebra/Polynomials/PartialFractions.cs b/Sources/AngouriMath/Functions/Algebra/Polynomials/PartialFractions.cs index 6a175312a..ee1f0ff35 100644 --- a/Sources/AngouriMath/Functions/Algebra/Polynomials/PartialFractions.cs +++ b/Sources/AngouriMath/Functions/Algebra/Polynomials/PartialFractions.cs @@ -1170,10 +1170,10 @@ private static (Entity P, Entity Q)[]? OverTheGaussianRoots( || Read(2) is not { } leading || leading.IsZero || Read(1) is not { } linear || Read(0) is not { } free) return null; // M = x^2 + s x + t = (x - ρ)^2 + σ^2. - var s = linear.Divide(leading).ToLowestTerms(); - var t = free.Divide(leading).ToLowestTerms(); - var rho = s.Negate().Divide(ERational.FromInt32(2)).ToLowestTerms(); - var sigmaSquared = t.Subtract(rho.Multiply(rho)).ToLowestTerms(); + var s = linear.Divide(leading).InLowestTerms(); + var t = free.Divide(leading).InLowestTerms(); + var rho = s.Negate().Divide(ERational.FromInt32(2)).InLowestTerms(); + var sigmaSquared = t.Subtract(rho.Multiply(rho)).InLowestTerms(); if (sigmaSquared.Sign <= 0) return null; var (sigmaAbove, sigmaBelow) = (sigmaSquared.Numerator.Sqrt(), sigmaSquared.Denominator.Sqrt()); @@ -1296,8 +1296,8 @@ bool TakeIn(Entity side, bool above) next[m].Im.Add(binomial[m].Im.Multiply(rho.Negate())).Add(binomial[m].Re.Multiply(sigma))); } binomial = next; - real[j] = binomial.Select(c => c.Re.ToLowestTerms()).ToArray(); - imaginary[j] = binomial.Select(c => c.Im.ToLowestTerms()).ToArray(); + real[j] = binomial.Select(c => c.Re.InLowestTerms()).ToArray(); + imaginary[j] = binomial.Select(c => c.Im.InLowestTerms()).ToArray(); } // The digits in powers of M: c_j (x - r)^(-j) and its conjugate are 2 Re(c_j (x - r̄)^j)/M^j, @@ -1343,7 +1343,7 @@ private static ERational Power(ERational value, int exponent) { var result = ERational.One; for (var k = 0; k < exponent; k++) - result = result.Multiply(value).ToLowestTerms(); + result = result.Multiply(value).InLowestTerms(); return result; } @@ -1364,8 +1364,8 @@ private static ERational Power(ERational value, int exponent) { var coefficient = remainder[k]; quotient[k - 2] = coefficient; - remainder[k - 1] = remainder[k - 1].Subtract(coefficient.Multiply(s)).ToLowestTerms(); - remainder[k - 2] = remainder[k - 2].Subtract(coefficient.Multiply(t)).ToLowestTerms(); + remainder[k - 1] = remainder[k - 1].Subtract(coefficient.Multiply(s)).InLowestTerms(); + remainder[k - 2] = remainder[k - 2].Subtract(coefficient.Multiply(t)).InLowestTerms(); } digits.Add((remainder[0], remainder.Length > 1 ? remainder[1] : ERational.Zero)); current = quotient; diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 7fe5f3107..72b266c5b 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -1089,7 +1089,7 @@ static bool APowerOfAQuotientOfLinearsVanishingAtPlusOrMinusOne(Entity inU, Vari var kappa = Functions.PartialFractions.Bare((a2 / (qs * ss)).Simplify()); if (!VanishesIdentically(b2 - kappa * (ps * ss + qs * rs)) || !VanishesIdentically(c2 - kappa * ps * rs) || VanishesIdentically(c2)) return null; - var half = quadraticPower.Negate().Subtract(ERational.FromInt32(1).Divide(ERational.FromInt32(2))).ToLowestTerms(); + var half = quadraticPower.Negate().Subtract(ERational.FromInt32(1).Divide(ERational.FromInt32(2))).InLowestTerms(); if (!half.Denominator.Equals(EInteger.One) || !half.Numerator.CanFitInInt32() || half.Numerator.Abs().CompareTo(EInteger.FromInt32(12)) > 0) return null; var bigK = half.Numerator.ToInt32Checked(); @@ -1178,9 +1178,9 @@ static bool APowerOfAQuotientOfLinearsVanishingAtPlusOrMinusOne(Entity inU, Vari } // L the largest exponent every one is a whole multiple of: the first times g/d, with d the // denominators' least common multiple and g the numerators' greatest common divisor over it. - var denominator = ratios.Aggregate(EInteger.One, (lcm, r) => lcm * r.Denominator / lcm.Gcd(r.Denominator)); + var denominator = ratios.Aggregate(EInteger.One, (lcm, r) => lcm * r.Denominator / lcm.GreatestCommonDivisor(r.Denominator)); var wholes = ratios.Select(r => r.Numerator * (denominator / r.Denominator)).ToList(); - var divisor = wholes.Aggregate(EInteger.Zero, (gcd, n) => gcd.Gcd(n.Abs())); + var divisor = wholes.Aggregate(EInteger.Zero, (gcd, n) => gcd.GreatestCommonDivisor(n.Abs())); if (divisor.IsZero) return null; var unit = Number.Rational.Create(divisor, denominator); @@ -1279,7 +1279,7 @@ static bool AFactorAboveAQuadratic(List> terms, b if (coefficient.Evaled is not Number.Rational rational || !power.CanFitInInt32() || power.ToInt32Checked() > 2 * IntegerPolynomial.MaxDegree) return false; rationals[halved ? power.ToInt32Checked() / 2 : power.ToInt32Checked()] = rational.ERational; - denominator = denominator * rational.ERational.Denominator / denominator.Gcd(rational.ERational.Denominator); + denominator = denominator * rational.ERational.Denominator / denominator.GreatestCommonDivisor(rational.ERational.Denominator); } var whole = new EInteger[rationals.Keys.Max() + 1]; for (var k = 0; k < whole.Length; k++) @@ -18192,7 +18192,7 @@ private static bool IsAFunctionToRaise(Entity factor, Entity.Variable x) var (exponent, coefficient) = (ERational.Zero, term); if (term.ContainsNode(x) && !TryReadAMonomial(term, x, out exponent, out coefficient)) return null; - read.Add((coefficient, exponent.ToLowestTerms())); + read.Add((coefficient, exponent.InLowestTerms())); } if (read.Count < 2) return null; @@ -18204,7 +18204,7 @@ private static bool IsAFunctionToRaise(Entity factor, Entity.Variable x) var (degree, coefficient) = (ERational.Zero, term); if (term.ContainsNode(x) && !TryReadAMonomial(term, x, out degree, out coefficient)) return null; - degree = degree.ToLowestTerms(); + degree = degree.InLowestTerms(); inB[degree] = inB.TryGetValue(degree, out var was) ? was + coefficient : coefficient; } var degrees = inB.Keys.Where(degree => !degree.IsZero).OrderBy(degree => degree).ToList(); @@ -18231,7 +18231,7 @@ private static bool IsAFunctionToRaise(Entity factor, Entity.Variable x) var inU = new Dictionary(); foreach (var (coefficient, exponent) in read) { - var scaled = exponent.Multiply(ERational.FromInt32(k)).Add(ERational.FromInt32(k - 1)).ToLowestTerms(); + var scaled = exponent.Multiply(ERational.FromInt32(k)).Add(ERational.FromInt32(k - 1)).InLowestTerms(); if (!scaled.Denominator.Equals(EInteger.One) || !scaled.Numerator.CanFitInInt32()) return null; var e = scaled.Numerator.ToInt32Unchecked(); diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/RothsteinTrager.cs b/Sources/AngouriMath/Functions/Continuous/Integration/RothsteinTrager.cs index 1b41479d0..e8c7ec7ae 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/RothsteinTrager.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/RothsteinTrager.cs @@ -370,7 +370,7 @@ internal static bool WantsASumOverRootsAlone(Entity numerator, Entity denominato private static bool InAPowerOfAShift(IntegerPolynomial poly) { var n = poly.Degree; - var shift = ERational.Create(poly[n - 1], poly[n].Multiply(EInteger.FromInt32(n))).ToLowestTerms(); + var shift = ERational.Create(poly[n - 1], poly[n].Multiply(EInteger.FromInt32(n))).InLowestTerms(); // Horner in y - s: g = g (y - s) + a_i, from the leading coefficient down. var g = new ERational[n + 1]; for (var i = 0; i <= n; i++)