@@ -32210,6 +32210,145 @@ private static bool IsAnImaginaryNumber(Entity expr)
3221032210 return answer.InnerSimplified;
3221132211 }
3221232212
32213+ /// <summary>
32214+ /// A power of the variable times a secant or cosecant power of a logarithm, beside the
32215+ /// power two below it, where the pair is the derivative of one product. With
32216+ /// <c>u = a + b ln(c x^n)</c>, so that <c>u' = B/x</c> for <c>B = b n</c>, and <c>k = m + 1</c>,
32217+ /// <c>d/dx [x^k sec(u)^(p - 2) (B (p - 2) tan(u) - k)] = x^m (D sec(u)^p - E sec(u)^(p - 2))</c>
32218+ /// with <c>D = B^2 (p - 1)(p - 2)</c> and <c>E = B^2 (p - 2)^2 + k^2</c>, so
32219+ /// <c>x^m (alpha sec(u)^p + beta sec(u)^(p - 2))</c> integrates to <c>alpha/D</c> times the
32220+ /// product wherever <c>beta D + alpha E = 0</c>. The cosecant's product is
32221+ /// <c>-x^k csc(u)^(p - 2) (B (p - 2) cot(u) + k)</c>, with the same <c>D</c> and <c>E</c>.
32222+ /// </summary>
32223+ /// <remarks>
32224+ /// Rubi's reduction for <c>x^m sec(a + b ln(c x^n))^p</c> lowers <c>p</c> by two and leaves
32225+ /// <c>E/D</c> times the lower power, which alone is a hypergeometric function; where
32226+ /// <c>E</c> is zero -- <c>b^2 n^2 (p - 2)^2 + (m + 1)^2 = 0</c> -- or the lower power is the
32227+ /// integrand's other term, the product is the whole answer. Declined before: the
32228+ /// exponentials are written for a numerical imaginary <c>b</c> only, and
32229+ /// <c>sec(a + i ln(c x^n)/(n (p - 2)))^p</c>, <c>x^m sec(a + 2 ln(c x^(sqrt(-(1 + m)^2)/2)))^3</c>
32230+ /// and <c>-(1 + b^2 n^2) sec(u) + 2 b^2 n^2 sec(u)^3</c> were unanswered or ran out of time.
32231+ /// The derivative uses only <c>z^(p - 2) z^2 = z^p</c>, <c>x^m x = x^k</c> and
32232+ /// <c>ln(c x^n)' = n/x</c>, which hold for principal powers wherever the base is not zero,
32233+ /// so the answer holds wherever the integrand is defined. Not for <c>k = 0</c>, where the
32234+ /// logarithm's own derivative is beside it and the substitution answers. Rubi's 4.7.5.
32235+ /// https://github.com/asc-community/AngouriMath/issues/233
32236+ /// </remarks>
32237+ internal static Entity? SolveSecantPowersOfALogarithmTwoApartAsTheDerivativeOfAProduct(Entity expr, Entity.Variable x)
32238+ {
32239+ static Entity? ArgumentOf(Entity node) => node switch { Secantf(var y) => y, Cosecantf(var y) => y, _ => null };
32240+ if (!expr.Nodes.Any(node => ArgumentOf(node) is { } y && y.Nodes.Any(inner => inner is Logf && inner.ContainsNode(x))))
32241+ return null;
32242+ Entity? argument = null;
32243+ var isSecant = false;
32244+ var monomials = Read(expr);
32245+ if (monomials is null || monomials.Count == 0 || argument is null)
32246+ return null;
32247+ // Gathered by their powers, and those whose coefficients cancel dropped.
32248+ var gathered = new List<(Entity Coefficient, Entity OfX, Entity OfF)>();
32249+ foreach (var (coefficient, ofX, ofF) in monomials)
32250+ {
32251+ var index = gathered.FindIndex(g => VanishesIdentically(g.OfX - ofX) && VanishesIdentically(g.OfF - ofF));
32252+ if (index < 0)
32253+ gathered.Add((coefficient, ofX.InnerSimplified, ofF.InnerSimplified));
32254+ else
32255+ gathered[index] = (gathered[index].Coefficient + coefficient, gathered[index].OfX, gathered[index].OfF);
32256+ }
32257+ gathered.RemoveAll(g => VanishesIdentically(g.Coefficient));
32258+ if (gathered.Count is 0 or > 2 || gathered.Count == 2 && !VanishesIdentically(gathered[0].OfX - gathered[1].OfX))
32259+ return null;
32260+ var (alpha, m, p) = gathered[0];
32261+ Entity beta = Number.Integer.Zero;
32262+ if (gathered.Count == 2)
32263+ {
32264+ var (otherCoefficient, _, otherPower) = gathered[1];
32265+ if (VanishesIdentically(otherPower - p + 2))
32266+ beta = otherCoefficient;
32267+ else if (VanishesIdentically(p - otherPower + 2))
32268+ (alpha, beta, p) = (otherCoefficient, alpha, otherPower);
32269+ else
32270+ return null;
32271+ }
32272+ // `x u'`, the constant `B`.
32273+ var rate = Functions.PartialFractions.Bare((argument.Differentiate(x) * x).InnerSimplified.Simplify());
32274+ var k = (m + 1).InnerSimplified;
32275+ if (rate.ContainsNode(x) || VanishesIdentically(rate) || VanishesIdentically(k))
32276+ return null;
32277+ var d = (rate * rate * (p - 1) * (p - 2)).InnerSimplified;
32278+ var e = (rate * rate * (p - 2) * (p - 2) + k * k).InnerSimplified;
32279+ if (VanishesIdentically(d) || !VanishesIdentically(beta * d + alpha * e))
32280+ return null;
32281+ var lowered = (p - 2).InnerSimplified;
32282+ // Folded, as `2 b^2 n^2/((b n)^2 2)` and `i/(p - 2) (p - 2)` are not by the simplification
32283+ // the answer gets.
32284+ var scale = Functions.PartialFractions.Bare((alpha / d).Simplify());
32285+ var slope = Functions.PartialFractions.Bare((rate * lowered).Simplify());
32286+ var product = isSecant
32287+ ? MathS.Pow(x, k) * MathS.Pow(MathS.Sec(argument), lowered) * (slope * MathS.Tan(argument) - k)
32288+ : -MathS.Pow(x, k) * MathS.Pow(MathS.Cosec(argument), lowered) * (slope * MathS.Cotan(argument) + k);
32289+ return (scale * product).InnerSimplified;
32290+
32291+ // The integrand as monomials `coefficient x^m f(u)^p`, one `f` of one `u` throughout.
32292+ List<(Entity Coefficient, Entity OfX, Entity OfF)>? Read(Entity node)
32293+ {
32294+ if (!node.ContainsNode(x))
32295+ return new() { (node, Number.Integer.Zero, Number.Integer.Zero) };
32296+ if (node == x)
32297+ return new() { (Number.Integer.One, Number.Integer.One, Number.Integer.Zero) };
32298+ switch (node)
32299+ {
32300+ case Powf(var @base, var exponent) when @base == x && !exponent.ContainsNode(x):
32301+ return new() { (Number.Integer.One, exponent, Number.Integer.Zero) };
32302+ case Secantf or Cosecantf:
32303+ return Takes(node) ? new() { (Number.Integer.One, Number.Integer.Zero, Number.Integer.One) } : null;
32304+ case Powf(var function, var exponent) when function is Secantf or Cosecantf && !exponent.ContainsNode(x):
32305+ return Takes(function) ? new() { (Number.Integer.One, Number.Integer.Zero, exponent) } : null;
32306+ case Mulf(var left, var right):
32307+ return Product(Read(left), Read(right));
32308+ case Divf(var numerator, var denominator):
32309+ if (Read(denominator) is not { Count: 1 } below)
32310+ return null;
32311+ var (c, ofX, ofF) = below[0];
32312+ return Product(Read(numerator), new() { (Number.Integer.One / c, -ofX, -ofF) });
32313+ case Sumf(var augend, var addend):
32314+ return Sum(Read(augend), Read(addend), Number.Integer.One);
32315+ case Minusf(var minuend, var subtrahend):
32316+ return Sum(Read(minuend), Read(subtrahend), Number.Integer.MinusOne);
32317+ default:
32318+ return null;
32319+ }
32320+ }
32321+
32322+ bool Takes(Entity function)
32323+ {
32324+ var (inner, secant) = (ArgumentOf(function)!, function is Secantf);
32325+ if (argument is null)
32326+ (argument, isSecant) = (inner, secant);
32327+ return argument == inner && isSecant == secant;
32328+ }
32329+
32330+ static List<(Entity, Entity, Entity)>? Product(List<(Entity Coefficient, Entity OfX, Entity OfF)>? left, List<(Entity Coefficient, Entity OfX, Entity OfF)>? right)
32331+ {
32332+ if (left is null || right is null || left.Count * right.Count > 8)
32333+ return null;
32334+ var product = new List<(Entity, Entity, Entity)>();
32335+ foreach (var l in left)
32336+ foreach (var r in right)
32337+ product.Add((l.Coefficient * r.Coefficient, l.OfX + r.OfX, l.OfF + r.OfF));
32338+ return product;
32339+ }
32340+
32341+ static List<(Entity, Entity, Entity)>? Sum(List<(Entity Coefficient, Entity OfX, Entity OfF)>? left, List<(Entity Coefficient, Entity OfX, Entity OfF)>? right, Entity sign)
32342+ {
32343+ if (left is null || right is null || left.Count + right.Count > 8)
32344+ return null;
32345+ var sum = new List<(Entity, Entity, Entity)>(left);
32346+ foreach (var r in right)
32347+ sum.Add((sign * r.Coefficient, r.OfX, r.OfF));
32348+ return sum;
32349+ }
32350+ }
32351+
3221332352 /// <summary>
3221432353 /// A sum as the constant written in every one of its terms times the sum without it:
3221532354 /// <c>a - a sin(x)^2</c> is <c>a (1 - sin(x)^2)</c>, and <c>a + a sin(x)</c> is
0 commit comments