From 0a3790ee55317f9fd6b79e6bb5dd50d0df53bafc Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Fri, 23 Oct 2026 11:13:57 +0000 Subject: [PATCH] Integrate a power of x beside a power of a logarithm of a binomial in that binomial x^k beside a power that is not whole of a logarithm of one binomial a + b x^q is written in t = a + b x^q where N = (k + 1)/q is a whole number above 0. Then x^k dx is (t - a)^(N - 1) dt/(q b^N) on every branch, since x^(k + 1) = (x^q)^N and x^(k + 1 - q) = (x^q)^(N - 1) are whole powers of x^q, and the integrand in t is a polynomial beside the logarithm's power, which the rules answer: (a + b ln(c (d + e/sqrt(x))))^p/x^2 is -2/e^2 times the integral of (t - d) (a + b ln(c t))^p. An exponential of a binomial is left to the exponential rules, which write it in its own base. Part of #2075. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- .../Integration/IndefiniteIntegralSolver.cs | 115 ++++++++++++++++++ .../Integration/Integration.Definition.cs | 1 + ...BesideAnExponentialOfAPowerIntegralTest.cs | 10 ++ 3 files changed, 126 insertions(+) diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 37f4b799c..de6928d45 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -18199,6 +18199,121 @@ bool Read(Entity node, bool insideASum) return back.Nodes.Any(node => node == MathS.NaN) ? null : back; } + /// + /// A power x^k beside a power of a logarithm of one binomial a + b x^q, written in + /// t = a + b x^q, where N = (k + 1)/q is a whole number above 0. Then x^k dx is + /// (t - a)^(N - 1) dt/(q b^N) on every branch, since x^(k + 1) = (x^q)^N and + /// x^(k + 1 - q) = (x^q)^(N - 1) are whole powers of x^q, and what is left is a polynomial in t beside + /// the function of t: (a + b ln(c (d + e/sqrt(x))))^p/x^2 is -2/e^2 times the integral of + /// (t - d) (a + b ln(c t))^p. A linear a + b x is the rule above's. Rubi's 3.4. + /// https://github.com/asc-community/AngouriMath/issues/2075 + /// + internal static Entity? SolveAPowerOfXBesideATranscendentalPowerOfABinomial(Entity expr, Entity.Variable x, bool integrateByParts) + { + if (!Integration.AnsweringTheQuestionAskedOrOneBelow || expr is not (Mulf or Divf)) + return null; + var (numerator, denominator) = expr is Divf(var above, var below) ? (above, below) : (expr, (Entity)Number.Integer.One); + Entity? transcendental = null; + // A power that is not whole of something holding a logarithm: an exponential of a binomial is the exponential + // rules', which write it in its own base, f^(a + b/x). + foreach (var factor in Mulf.LinearChildren(numerator)) + if (factor is Powf(var @base, var exponent) && !exponent.ContainsNode(x) && exponent is not Number.Integer + && @base.Nodes.Any(node => node is Logf(_, var antilogarithm) && antilogarithm.ContainsNode(x))) + { + if (transcendental is not null) + return null; + transcendental = factor; + } + if (transcendental is null || TheOneBinomialIn(transcendental, x) is not var (binomial, offset, scale, q) + || q == Number.Integer.One || VanishesIdentically(offset)) + return null; + // Beside it, constants and powers of x, whose exponents sum to k. + Entity constant = Number.Integer.One; + Entity k = Number.Integer.Zero; + for (var side = 0; side < 2; side++) + foreach (var factor in Mulf.LinearChildren(side == 0 ? numerator : denominator)) + { + if (ReferenceEquals(factor, transcendental)) + continue; + if (!factor.ContainsNode(x)) + constant = side == 0 ? constant * factor : constant / factor; + else if (ThePowerOfXItIs(factor, x) is { } power) + k = side == 0 ? k + power : k - power; + else + return null; + } + if (((k + 1) / q).InnerSimplified is not Number.Integer { EInteger: var whole } || whole.Sign <= 0 || whole.CompareTo(EInteger.FromInt32(30)) > 0) + return null; + var t = Variable.CreateUnique(expr, "t_binomial"); + var inT = transcendental.Replace(node => node == binomial ? t : node); + if (inT.ContainsNode(x)) + return null; + var integrand = constant * MathS.Pow(t - offset, Number.Integer.Create(whole.Subtract(EInteger.One))) * inT / (q * MathS.Pow(scale, Number.Integer.Create(whole))); + if (Integration.ComputeAsAQuestionOfItsOwn(integrand.InnerSimplified, t, integrateByParts) is not { } answer) + return null; + var back = answer.Substitute(t, binomial); + return back.Nodes.Any(node => node == MathS.NaN) ? null : back; + } + + /// The exponent of as a power of , free of it, or . + private static Entity? ThePowerOfXItIs(Entity factor, Entity.Variable x) => factor switch + { + _ when factor == x => Number.Integer.One, + Powf(var @base, var exponent) when @base == x && !exponent.ContainsNode(x) => exponent, + _ => null + }; + + /// + /// The one binomial a + b x^q that every in stands in, with + /// a, b and q free of x and q a number, read as a sum of terms free of x and one term + /// b x^q, or . + /// + private static (Entity Binomial, Entity Offset, Entity Scale, Entity Exponent)? TheOneBinomialIn(Entity expr, Entity.Variable x) + { + (Entity Binomial, Entity Offset, Entity Scale, Entity Exponent)? found = null; + bool Read(Entity node) + { + if (!node.ContainsNode(x)) + return true; + if (node is Sumf or Minusf && ABinomial(node, x) is { } read) + { + found ??= read; + return node == found.Value.Binomial; + } + if (node == x) + return false; + return node.DirectChildren.All(Read); + } + return Read(expr) ? found : null; + } + + private static (Entity Binomial, Entity Offset, Entity Scale, Entity Exponent)? ABinomial(Entity sum, Entity.Variable x) + { + Entity offset = Number.Integer.Zero; + Entity? term = null; + foreach (var summand in Sumf.LinearChildren(sum is Minusf(var left, var right) ? left + -right : sum)) + if (!summand.ContainsNode(x)) + offset += summand; + else if (term is null) + term = summand; + else + return null; + if (term is null) + return null; + var (above, below) = term is Divf(var n, var d) ? (n, d) : (term, (Entity)Number.Integer.One); + Entity scale = Number.Integer.One; + Entity? exponent = null; + for (var side = 0; side < 2; side++) + foreach (var factor in Mulf.LinearChildren(side == 0 ? above : below)) + if (!factor.ContainsNode(x)) + scale = side == 0 ? scale * factor : scale / factor; + else if (exponent is null && ThePowerOfXItIs(factor, x) is Number.Rational power && !power.IsZero) + exponent = side == 0 ? power : -power; + else + return null; + return exponent is null ? null : (sum, offset, scale, exponent.InnerSimplified); + } + /// /// Whether is a power that is not whole of something holding a logarithm of something in /// , or an exponential of something in that is not linear in it. diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs index 7a3f52a12..8c5187315 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs @@ -937,6 +937,7 @@ internal static bool ASumOverRootsWouldAnswer if ((answer = IndefiniteIntegralSolver.SolveAPolynomialBesideErrorFunctionsOfAShiftedLinear(expr, x, integrateByParts)) is { }) return answer; // And beside a symbolic power of a logarithm of one, or an exponential of a power of one, the same way. if ((answer = IndefiniteIntegralSolver.SolveAPolynomialBesideATranscendentalPowerOfAShiftedLinear(expr, x, integrateByParts)) is { }) return answer; + if ((answer = IndefiniteIntegralSolver.SolveAPowerOfXBesideATranscendentalPowerOfABinomial(expr, x, integrateByParts)) is { }) return answer; // A half-odd power of a ± a sin(y) beside powers of other linears in the sine, by the half // angle, before the polynomial term: beneath it the substitution search ran out of time. if ((answer = IndefiniteIntegralSolver.SolveAHalfOddPowerOfOnePlusMinusASineBesidePowersOfLinearsInIt(expr, x, integrateByParts)) is { }) return answer; diff --git a/Sources/Tests/UnitTests/Calculus/APowerBesideAnExponentialOfAPowerIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/APowerBesideAnExponentialOfAPowerIntegralTest.cs index d913ebd4d..35f7d0ee6 100644 --- a/Sources/Tests/UnitTests/Calculus/APowerBesideAnExponentialOfAPowerIntegralTest.cs +++ b/Sources/Tests/UnitTests/Calculus/APowerBesideAnExponentialOfAPowerIntegralTest.cs @@ -147,6 +147,16 @@ public void APositiveRateIsTakenOutOfThePower() [InlineData("x*(d + f*acosh(f*x))^n/(sqrt(f*x - 1)*sqrt(f*x + 1))")] public void ASymbolicPowerOfAnInverseHyperbolicFunction(string integrand) => DifferentiatesBack(integrand); + /// + /// A power x^k beside a power of a logarithm of one binomial d + f x^q, where (k + 1)/q is a whole + /// number, written in t = d + f x^q: a polynomial in t beside the logarithm's power. Rubi's 3.4. + /// + [Theory] + [InlineData("(a - b*ln(c*(d + f/sqrt(x))))^n/x^2")] + [InlineData("x^3*(a - b*ln(c*(d + f*x^(2/3))))^n")] + [InlineData("(a - b*ln(c*(d + f/x^(1/3))^2))^n/x^2")] + public void APowerOfXBesideAFunctionOfABinomial(string integrand) => DifferentiatesBack(integrand); + /// /// Not where the integral is elementary, an exponential integral or an error function: those keep the form /// they had.