@@ -18321,6 +18321,254 @@ private static bool IsAWholeOrHalfOddMultiple(Entity timesN)
1832118321 internal static bool IsAPerfectSquareDiscriminant(Entity a, Entity b, Entity c)
1832218322 => VanishesIdentically(b * b - Number.Integer.Create(4) * a * c);
1832318323
18324+ /// <summary>
18325+ /// Four nested roots with a closed form, Rubi's 1.3.3: <c>sqrt(a + b sqrt(c + d x^2))</c>
18326+ /// with <c>a^2 = b^2 c</c>; <c>sqrt(c x^2 + d sqrt(a + b x^4))/sqrt(a + b x^4)</c> with
18327+ /// <c>c^2 = b d^2</c>; <c>1/((a + b x^n) sqrt(c x^2 + d (a + b x^n)^(2/n)))</c>; and
18328+ /// <c>sqrt(a x^2 + b x sqrt(c + d x^2))/(x sqrt(c + d x^2))</c> with <c>a^2 = b^2 d</c> and
18329+ /// <c>b^2 c + a = 0</c>.
18330+ /// </summary>
18331+ /// <remarks>
18332+ /// <para>
18333+ /// The first is <c>2 b^2 d x^3/(3 S^(3/2)) + 2 a x/sqrt(S)</c> for the root <c>S</c>. The
18334+ /// second and third are the derivative of <c>w = x/sqrt(S)</c> over <c>1 - 2c w^2</c> and
18335+ /// <c>1 - c w^2</c>: <c>sqrt(S)/R</c> for the inner root <c>R</c> is <c>d w'/(1 - 2c w^2)</c>, since
18336+ /// <c>S' = 2 c x S/(d R)</c> where <c>c^2 = b d^2</c>, and <c>1 - 2c w^2 = d R/S</c>. The fourth is
18337+ /// <c>sqrt(2) b/a</c> times <c>1/sqrt(1 + t^2/a)</c> in <c>t = a x + b sqrt(c + d x^2)</c>. The
18338+ /// integral in <c>w</c> or <c>t</c> is asked of the integrator with its coefficient named, and
18339+ /// the answer is differentiated back before it is returned. Rubi's 1.3.2 and the Welz and
18340+ /// Timofeev suites declined all of them.
18341+ /// https://github.com/asc-community/AngouriMath/issues/718
18342+ /// </para>
18343+ /// </remarks>
18344+ internal static Entity? SolveANestedRootByItsClosedForm(Entity expr, Entity.Variable x, bool integrateByParts)
18345+ {
18346+ // The factors with x in them as powers, those below the bar negated, and the constant
18347+ // in front: `sqrt(S)/sqrt(P)` arrives as `sqrt(S) (sqrt(P))^(-1)`.
18348+ Entity constant = Number.Integer.One;
18349+ var factors = new List<Entity>();
18350+ var (above, below) = Functions.SingleQuotient.Of(expr);
18351+ foreach (var (side, sign) in new[] { (above, 1), (below, -1) })
18352+ foreach (var factor in Mulf.LinearChildren(side))
18353+ {
18354+ if (!factor.ContainsNode(x))
18355+ {
18356+ if (factor == Number.Integer.One)
18357+ continue;
18358+ var k = sign > 0 ? factor : 1 / factor;
18359+ constant = constant == Number.Integer.One ? k : constant * k;
18360+ continue;
18361+ }
18362+ factors.Add(factor is Powf(var b, var p) && !p.ContainsNode(x)
18363+ ? MathS.Pow(b, sign > 0 ? p : (-p).InnerSimplified)
18364+ : sign > 0 ? factor : MathS.Pow(factor, Number.Integer.MinusOne));
18365+ }
18366+ if (factors.Count is < 1 or > 3)
18367+ return null;
18368+ var half = Number.Rational.Create(1, 2);
18369+ var answer = factors.Count switch
18370+ {
18371+ 1 => ARootOfAConstantPlusARootOfAQuadratic(factors[0]),
18372+ 2 => ARootOverTheRootInsideIt(factors) ?? AReciprocalOfTheRootInsideIt(factors),
18373+ _ => AProductRootOverTheRootInsideIt(factors),
18374+ };
18375+ if (answer is null)
18376+ return null;
18377+ answer = constant == Number.Integer.One ? answer : constant * answer;
18378+ return Functions.PartialFractions.DerivativeHoldsAtSampledPoints(answer, expr, x) ? answer : null;
18379+
18380+ // sqrt(a + b sqrt(c + d x^2)), a^2 = b^2 c: 2 b^2 d x^3/(3 S^(3/2)) + 2 a x/sqrt(S).
18381+ Entity? ARootOfAConstantPlusARootOfAQuadratic(Entity factor)
18382+ {
18383+ if (factor is not Powf(var sum, var power) || power != half || ConstantPlusMultipleOfARoot(sum) is not var (a, b, inner)
18384+ || Coefficients(inner, 0, 2) is not [var c, var d])
18385+ return null;
18386+ if (!Functions.PartialFractions.IsZeroAsAValue(a * a - b * b * c))
18387+ return null;
18388+ return 2 * b * b * d * MathS.Pow(x, 3) / (3 * MathS.Pow(sum, Number.Rational.Create(3, 2))) + 2 * a * x / MathS.Pow(sum, half);
18389+ }
18390+
18391+ // sqrt(c x^2 + d sqrt(P))/sqrt(P), P = a + b x^4, c^2 = b d^2: d G(x/sqrt(S)), G' = 1/(1 - 2c w^2).
18392+ Entity? ARootOverTheRootInsideIt(List<Entity> pair)
18393+ {
18394+ if (pair.Find(f => f is Powf(_, var p) && p == half) is not Powf(var sum, _)
18395+ || pair.Find(f => f is Powf(_, var p) && p == -half) is not Powf(var radicand, _)
18396+ || Coefficients(radicand, 0, 4) is not [_, var b]
18397+ || SquareOfXPlusMultipleOfRoot(sum, radicand, half) is not var (c, d)
18398+ || !Functions.PartialFractions.IsZeroAsAValue(c * c - b * d * d))
18399+ return null;
18400+ return InW(d, 2 * c, x / MathS.Pow(sum, half));
18401+ }
18402+
18403+ // 1/((a + b x^n) sqrt(c x^2 + d (a + b x^n)^(2/n))): (1/a) H(x/sqrt(S)), H' = 1/(1 - c w^2).
18404+ Entity? AReciprocalOfTheRootInsideIt(List<Entity> pair)
18405+ {
18406+ if (pair.Find(f => f is Powf(_, var p) && p == -half) is not Powf(var sum, _)
18407+ || pair.Find(f => f is Powf(_, var p) && p == Number.Integer.MinusOne) is not Powf(var outer, _)
18408+ || Sumf.LinearChildren(outer) is not { Count: 2 } terms)
18409+ return null;
18410+ var a = terms.FirstOrDefault(t => !t.ContainsNode(x));
18411+ var power = terms.FirstOrDefault(t => t.ContainsNode(x)) is { } t2 && ConstantTimesAWholePowerOfX(t2) is var (_, n) ? n : null;
18412+ if (a is null || power is null)
18413+ return null;
18414+ if (SquareOfXPlusMultipleOfRoot(sum, outer, Number.Rational.Create(2, power.EInteger)) is not var (c, _))
18415+ return null;
18416+ return InW(1 / a, c, x / MathS.Pow(sum, half));
18417+ }
18418+
18419+ // sqrt(a x^2 + b x sqrt(Q))/(x sqrt(Q)), Q = c + d x^2, a^2 = b^2 d, b^2 c + a = 0:
18420+ // sqrt(2) b/a K(a x + b sqrt(Q)), K' = 1/sqrt(1 + t^2/a).
18421+ Entity? AProductRootOverTheRootInsideIt(List<Entity> three)
18422+ {
18423+ if (three.Find(f => f is Powf(_, var p) && p == half) is not Powf(var sum, _)
18424+ || three.Find(f => f is Powf(_, var p) && p == -half) is not Powf(var radicand, _)
18425+ || !three.Any(f => f is Powf(var bx, var p) && bx == x && p == Number.Integer.MinusOne)
18426+ || Coefficients(radicand, 0, 2) is not [var c, var d])
18427+ return null;
18428+ // a x^2 + b x sqrt(Q), as two terms.
18429+ Entity? a = null, b = null;
18430+ foreach (var term in Sumf.LinearChildren(sum))
18431+ {
18432+ if (ConstantTimesAWholePowerOfX(term) is var (k, two) && two.EInteger.Equals(EInteger.FromInt32(2)))
18433+ a = k;
18434+ else if (MultipleOf(term, MathS.Pow(x, 1) * MathS.Pow(radicand, half)) is { } m)
18435+ b = m;
18436+ else
18437+ return null;
18438+ }
18439+ if (a is null || b is null || !Functions.PartialFractions.IsZeroAsAValue(a * a - b * b * d)
18440+ || !Functions.PartialFractions.IsZeroAsAValue(b * b * c + a))
18441+ return null;
18442+ var t = Variable.CreateUnique(expr, "t_nested");
18443+ var reciprocal = Lowest(1 / a);
18444+ Entity q = reciprocal.Vars.Any() ? Variable.CreateUnique(expr, "q_nested") : reciprocal;
18445+ if (Integration.ComputeIndefiniteIntegral(1 / MathS.Sqrt(1 + MathS.Pow(t, 2) * q), t, integrateByParts) is not { } inT)
18446+ return null;
18447+ var inX = inT.Substitute(t, a * x + b * MathS.Pow(radicand, half));
18448+ return MathS.Sqrt(2) * b / a * (q is Variable named ? inX.Substitute(named, reciprocal) : inX);
18449+ }
18450+
18451+ // multiple times the integral of 1/(1 - q w^2) at w = at, with q named while it is asked.
18452+ Entity? InW(Entity multiple, Entity coefficient, Entity at)
18453+ {
18454+ // Named only where it has symbols in it: a number put in afterwards is not folded,
18455+ // and `sqrt(4 q)` at `q = 2` was written `2/1 * 2^(1/2)`.
18456+ var w = Variable.CreateUnique(expr, "w_nested");
18457+ var lowest = Lowest(coefficient);
18458+ Entity q = lowest.Vars.Any() ? Variable.CreateUnique(expr, "q_nested") : lowest;
18459+ if (Integration.ComputeIndefiniteIntegral(1 / (1 - q * MathS.Pow(w, 2)), w, integrateByParts) is not { } inW)
18460+ return null;
18461+ var written = inW.Substitute(w, at);
18462+ return Lowest(multiple) * (q is Variable named ? written.Substitute(named, lowest) : written);
18463+ }
18464+
18465+ // A constant in lowest terms over its symbols, and a number as the number it is: the
18466+ // lowest terms of `2` are `2/1`.
18467+ static Entity Lowest(Entity constant)
18468+ => constant.Vars.Any() ? Functions.PartialFractions.InLowestTermsOverTheSymbols(constant) : constant.InnerSimplified;
18469+
18470+ // c x^2 + d R^power, with R as given: (c, d).
18471+ (Entity C, Entity D)? SquareOfXPlusMultipleOfRoot(Entity sum, Entity root, Entity power)
18472+ {
18473+ Entity? c = null, d = null;
18474+ foreach (var term in Sumf.LinearChildren(sum))
18475+ {
18476+ if (ConstantTimesAWholePowerOfX(term) is var (k, two) && two.EInteger.Equals(EInteger.FromInt32(2)))
18477+ c = c is null ? k : null;
18478+ else if (MultipleOf(term, MathS.Pow(root, power)) is { } m)
18479+ d = d is null ? m : null;
18480+ else
18481+ return null;
18482+ }
18483+ return c is null || d is null ? null : (c, d);
18484+ }
18485+
18486+ // a + b sqrt(Q), with Q holding x: (a, b, Q).
18487+ (Entity A, Entity B, Entity Inner)? ConstantPlusMultipleOfARoot(Entity sum)
18488+ {
18489+ Entity? a = null, b = null, inner = null;
18490+ foreach (var term in Sumf.LinearChildren(sum))
18491+ {
18492+ if (!term.ContainsNode(x))
18493+ {
18494+ a = a is null ? term : a + term;
18495+ continue;
18496+ }
18497+ if (inner is not null)
18498+ return null;
18499+ Entity k = Number.Integer.One;
18500+ foreach (var part in Mulf.LinearChildren(term))
18501+ if (!part.ContainsNode(x))
18502+ k = k == Number.Integer.One ? part : k * part;
18503+ else if (inner is null && part is Powf(var r, var p) && p == half)
18504+ inner = r;
18505+ else
18506+ return null;
18507+ b = k;
18508+ }
18509+ return a is null || b is null || inner is null ? null : (a, b, inner);
18510+ }
18511+
18512+ // term = m times the given product of factors with x, m free of x.
18513+ Entity? MultipleOf(Entity term, Entity product)
18514+ {
18515+ var wanted = Mulf.LinearChildren(product).Where(f => f != Number.Integer.One).ToList();
18516+ Entity m = Number.Integer.One;
18517+ foreach (var part in Mulf.LinearChildren(term))
18518+ {
18519+ if (!part.ContainsNode(x))
18520+ {
18521+ m = m == Number.Integer.One ? part : m * part;
18522+ continue;
18523+ }
18524+ var at = wanted.FindIndex(w => w == part || (part == x && w is Powf(var wb, var wp) && wb == x && wp == Number.Integer.One));
18525+ if (at < 0)
18526+ return null;
18527+ wanted.RemoveAt(at);
18528+ }
18529+ return wanted.Count == 0 ? m : null;
18530+ }
18531+
18532+ // c x^n with n whole: (c, n); a constant is n = 0.
18533+ (Entity Coefficient, Number.Integer Power)? ConstantTimesAWholePowerOfX(Entity term)
18534+ {
18535+ Entity k = Number.Integer.One;
18536+ Number.Integer? n = null;
18537+ foreach (var part in Mulf.LinearChildren(term))
18538+ {
18539+ if (!part.ContainsNode(x))
18540+ {
18541+ k = k == Number.Integer.One ? part : k * part;
18542+ continue;
18543+ }
18544+ if (n is not null)
18545+ return null;
18546+ n = part == x ? Number.Integer.One : part is Powf(var bx, Number.Integer e) && bx == x ? e : null;
18547+ if (n is null)
18548+ return null;
18549+ }
18550+ return (k, n ?? Number.Integer.Zero);
18551+ }
18552+
18553+ // The coefficients of a polynomial in x with terms of the two degrees given and no other.
18554+ Entity[]? Coefficients(Entity polynomial, int low, int high)
18555+ {
18556+ Entity? lowCoefficient = null, highCoefficient = null;
18557+ foreach (var term in Sumf.LinearChildren(polynomial))
18558+ {
18559+ if (ConstantTimesAWholePowerOfX(term) is not var (k, n))
18560+ return null;
18561+ if (n.EInteger.Equals(EInteger.FromInt32(low)))
18562+ lowCoefficient = lowCoefficient is null ? k : lowCoefficient + k;
18563+ else if (n.EInteger.Equals(EInteger.FromInt32(high)))
18564+ highCoefficient = highCoefficient is null ? k : highCoefficient + k;
18565+ else
18566+ return null;
18567+ }
18568+ return lowCoefficient is null || highCoefficient is null ? null : new[] { lowCoefficient, highCoefficient };
18569+ }
18570+ }
18571+
1832418572 /// <summary>
1832518573 /// A square root of a perfect square in <paramref name="x"/> is the modulus:
1832618574 /// <c>sqrt(x^2)</c> is <c>|x|</c>, which for a real <c>x</c> is <c>sgn(x) x</c>, and
0 commit comments