Skip to content

Commit 1763843

Browse files
Split a symbolic trinomial in x^2 over its factors beside a root of a quadratic (#1886)
* Take a symbolic trinomial in x^2 apart over its roots beside a root of a quadratic a + b x^2 + c x^4 below the bar beside a root of a quadratic is written c (x^2 - y1)(x^2 - y2) with q = sqrt(b^2 - 4ac) a symbol of its own where the roots in x^2 are real, and c (x^2 + p x + m)(x^2 - p x + m) where they are a conjugate pair, and the integrator is asked each; the answer is the piecewise of the two on the sign of the discriminant. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura * Name the trinomial's roots, read a function of x^2 only, and split before the search The roots in x^2 are named r and s where they are real, and the split is asked directly rather than the whole search; a sqrt(x) beside the trinomial is not a root of a quadratic. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura * Record the answers a trinomial in x^2 split over its factors gives Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent acd4702 commit 1763843

4 files changed

Lines changed: 181 additions & 0 deletions

File tree

‎BREAKING-CHANGES.md‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,24 @@ root of a linear, the linear radical substitution gets there first
429429
| `"sin(a + b/sqrt(c + d*x))/(e + f*x)".ToEntity().Integrate("x")` | `integral(...)` | 1,114 characters |
430430
| `"sin(a + b/sqrt(c + d*x))/(e + f*x)^2".ToEntity().Integrate("x")` | `integral(...)` | 2,288 characters |
431431

432+
### A symbolic trinomial in x^2 beside a root of a quadratic is split over its factors
433+
434+
**Answers where there were none.** `sqrt(d + k x^2)/(a + b x^2 + c x^4)` and its kin were declined, most of them
435+
past any budget. The trinomial is written over its factors in x^2 now, with its roots named: `c (x^2 - r)(x^2 - s)`
436+
where they are real, and `c (x^2 + p x + m)(x^2 - p x + m)` where they are a conjugate pair. Each factor is a quadratic
437+
the existing rules close beside the root. The answer is a piecewise on the sign of `b^2 - 4 a c`, and the second
438+
case is most of its length ([#718](https://github.com/asc-community/AngouriMath/issues/718)).
439+
440+
| Input | Was (2.5.0) | Now |
441+
|---|---|---|
442+
| `"x^2*sqrt(d + k*x^2)/(a + b*x^2 + c*x^4)".ToEntity().Integrate("x")` | `integral(...)` | 29,139 characters |
443+
| `"sqrt(d + k*x^2)/(x^2*(a + b*x^2 + c*x^4))".ToEntity().Integrate("x")` | `integral(...)` | 10,736 characters |
444+
| `"(d + k*x^2)^(3/2)/(a + b*x^2 + c*x^4)".ToEntity().Integrate("x")` | `integral(...)` | 31,242 characters |
445+
| `"x^4*(d + k*x^2)^(3/2)/(a + b*x^2 + c*x^4)".ToEntity().Integrate("x")` | `integral(...)` | 41,265 characters |
446+
| `"x^2*sqrt(1 - x^2)/(a + b*x^2 + c*x^4)".ToEntity().Integrate("x")` | `integral(...)` | 8,502 characters |
447+
| `"x^4/((a + b*x^2 + c*x^4)*sqrt(d + k*x^2))".ToEntity().Integrate("x")` | `integral(...)` | 28,551 characters |
448+
| `"1/(x^2*(a + b*x^2 + c*x^4)*sqrt(d + k*x^2))".ToEntity().Integrate("x")` | `integral(...)` | 10,194 characters |
449+
432450
### A power of the cosine beside a secant binomial is integrated in the cosine
433451

434452
**Answers where there were none.** `cos(c + d x)^2/(a + b sec(c + d x))` was declined, and so were the cosine's powers

‎Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs‎

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18722,6 +18722,104 @@ static Entity WithoutConditions(Entity coefficient)
1872218722
return answer.Nodes.Any(node => node is Number.Complex { IsNaN: true }) ? null : answer;
1872318723
}
1872418724

18725+
/// <summary>
18726+
/// A rational function with a trinomial in the square below the bar, beside a root of a
18727+
/// quadratic, taken apart over the trinomial's factors in the square: where its roots in
18728+
/// x^2 are real, <c>a + b x^2 + c x^4</c> is <c>c (x^2 - r) (x^2 - s)</c> with
18729+
/// <c>r, s = (-b -+ sqrt(b^2 - 4 a c))/(2c)</c>, and where they are a conjugate pair it is
18730+
/// <c>c (x^2 + p x + m) (x^2 - p x + m)</c> with <c>m = sqrt(a/c)</c> and
18731+
/// <c>p = sqrt(2 m - b/c)</c>. Each factor is a quadratic the rule before this one splits
18732+
/// the integrand over and closes beside the root, and the answer is the piecewise of the
18733+
/// two on the sign of the discriminant. Rubi's 1.2.2.4
18734+
/// <c>sqrt(d + e x^2)/(a + b x^2 + c x^4)</c> was declined.
18735+
/// </summary>
18736+
/// <remarks>
18737+
/// Asked with <c>r</c>, <c>s</c>, <c>m</c> and <c>p</c> symbols of their own and written
18738+
/// back after: the integrand so written is an integrand for every value of them, and the
18739+
/// one asked for at theirs. Named, each stays one atom through the split: with
18740+
/// <c>(sqrt(b^2 - 4 a c) - b)/(2c)</c> in place of <c>r</c> the split declined
18741+
/// <c>x^2 sqrt(d + e x^2)/(a + b x^2 + c x^4)</c>, and over <c>x^2 - r</c> it is 1,458
18742+
/// characters (<a href="https://github.com/asc-community/AngouriMath/issues/1877">#1877</a>
18743+
/// was the same with a quadratic). The real roots' writing alone is not enough: with
18744+
/// <c>r</c> complex the pieces' cases read the sign of a number off the real line, and the
18745+
/// answer was NaN wherever <c>b^2 &lt; 4 a c</c>. Not where the discriminant vanishes,
18746+
/// the generic case's. With a symbol in the trinomial only, and in a function of x^2 only.
18747+
/// https://github.com/asc-community/AngouriMath/issues/718
18748+
/// </remarks>
18749+
internal static Entity? SolveATrinomialInTheSquareBesideTheRootOfAQuadratic(Entity expr, Entity.Variable x)
18750+
{
18751+
// A function of x^2: every x in an even power of it, the root's quadratic among them.
18752+
// `sqrt(x)/(a + b x^2 + c x^4)^2` is not one, and a power of x to an odd degree below
18753+
// the bar is a function of x^2 after u = x^2, which reads it more shortly.
18754+
if (!expr.Nodes.Any(node => node is Powf(var radicand, Number.Rational { ERational.Denominator: var half })
18755+
&& half.Equals(EInteger.FromInt32(2)) && radicand.ContainsNode(x)
18756+
&& TreeAnalyzer.TryGetPolyQuadratic(radicand, x, out var squared, out _, out _) && !VanishesIdentically(squared))
18757+
|| expr.Nodes.Count(node => node == x) != expr.Nodes.Count(node => node is Powf(var @base, Number.Integer { EInteger.IsEven: true }) && @base == x))
18758+
return null;
18759+
var (_, denominator) = Functions.SingleQuotient.Of(expr);
18760+
Entity? trinomial = null;
18761+
(Entity A, Entity B, Entity C)? read = null;
18762+
foreach (var factor in Mulf.LinearChildren(denominator))
18763+
{
18764+
var @base = factor is Powf(var repeated, Number.Integer { EInteger.Sign: > 0 }) ? repeated : factor;
18765+
if (@base is not (Sumf or Minusf) || !@base.ContainsNode(x) || !@base.Vars.Any(symbol => symbol != x)
18766+
|| !TreeAnalyzer.TryGetPolynomial(@base, x, out var monomials)
18767+
|| monomials.Count != 3
18768+
|| !monomials.TryGetValue(EInteger.Zero, out var a)
18769+
|| !monomials.TryGetValue(EInteger.FromInt32(2), out var b)
18770+
|| !monomials.TryGetValue(EInteger.FromInt32(4), out var c))
18771+
continue;
18772+
if (trinomial is not null && trinomial != @base)
18773+
return null;
18774+
trinomial = @base;
18775+
read = (a, b, c);
18776+
}
18777+
if (trinomial is null || read is not var (constant, middle, leading)
18778+
|| VanishesIdentically(leading) || VanishesIdentically(middle))
18779+
return null;
18780+
var discriminant = (middle * middle - 4 * constant * leading).InnerSimplified;
18781+
if (VanishesIdentically(discriminant))
18782+
return null;
18783+
// Two writings of the trinomial, each real where it is taken, its roots and
18784+
// coefficients named so that each stays one atom through the split. Two real roots
18785+
// in x^2: c (x^2 - r) (x^2 - s). None: the roots are a conjugate pair and the pieces' cases
18786+
// read the sign of an imaginary q, so it is the product of two real quadratics
18787+
// c (x^2 + p x + m) (x^2 - p x + m) with m = sqrt(a/c) and p = sqrt(2 m - b/c), both
18788+
// real there since b^2 < 4 a c makes a/c positive and |b/c| < 2 m. Each is an
18789+
// identity in its symbols, so each answer is an antiderivative wherever its symbols
18790+
// are what they stand for. A root twice, where the discriminant vanishes, is the
18791+
// case the generic answer leaves, as `ln(a x + b)/a` leaves a = 0.
18792+
var square = MathS.Pow(x, Number.Integer.Create(2));
18793+
var r = Variable.CreateUnique(expr, "r");
18794+
var s = Variable.CreateUnique(expr + r, "s");
18795+
var m = Variable.CreateUnique(expr + r + s, "m");
18796+
var p = Variable.CreateUnique(expr + r + s + m, "p");
18797+
var q = MathS.Sqrt(discriminant);
18798+
var writings = new (Entity Written, Entity[] Named, Entity[] Values, Entity Condition)[]
18799+
{
18800+
(leading * (square - r) * (square - s),
18801+
new Entity[] { r, s }, new[] { (q - middle) / (2 * leading), (-q - middle) / (2 * leading) }, discriminant > 0),
18802+
(leading * (square + p * x + m) * (square - p * x + m),
18803+
new Entity[] { m, p }, new[] { MathS.Sqrt(constant / leading), MathS.Sqrt(2 * MathS.Sqrt(constant / leading) - middle / leading) }, discriminant < 0),
18804+
};
18805+
var cases = new List<Providedf>();
18806+
foreach (var (overTheRoots, named, values, condition) in writings)
18807+
{
18808+
var written = expr.Replace(node => node == trinomial ? overTheRoots : node);
18809+
if (SolveARationalFunctionBesideTheRootOfAQuadratic(written, x) is not { } inNamed)
18810+
return null;
18811+
var answered = inNamed;
18812+
for (var i = named.Length - 1; i >= 0; i--)
18813+
answered = answered.Substitute(named[i], values[i]);
18814+
answered = answered.InnerSimplified;
18815+
if (answered.Nodes.Any(node => node is Number.Complex { IsNaN: true }))
18816+
return null;
18817+
cases.Add(new Providedf(answered, condition));
18818+
}
18819+
var answer = MathS.Piecewise(cases);
18820+
return Functions.PartialFractions.DerivativeHoldsAtSampledPoints(answer, expr, x) ? answer : null;
18821+
}
18822+
1872518823
/// <summary>
1872618824
/// A piece over a whole power of <paramref name="radicand"/>, beside its root, as the one
1872718825
/// half-odd power the two are: <c>(g + h x)/(Q^j sqrt(Q))</c> is

‎Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,10 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
666666
// the question asked only, and the polynomial term below writes `1/u` as `u^(-1)` and
667667
// asks that a level down: `1/(e^atanh(a x) (c - c/(a x))^(3/2))` reached it nowhere.
668668
if ((answer = IndefiniteIntegralSolver.SolveByWritingEachLinearOnce(expr, x, integrateByParts)) is { }) return answer;
669+
// A symbolic trinomial in x^2 below the bar beside a root of a quadratic, over its roots
670+
// in x^2, before anything reads the trinomial whole: asked of it, the routes below ran
671+
// past a minute on Rubi's 1.2.2.4 `sqrt(d + e x^2)/(x^2 (a + b x^2 + c x^4))`.
672+
if ((answer = IndefiniteIntegralSolver.SolveATrinomialInTheSquareBesideTheRootOfAQuadratic(expr, x)) is { }) return answer;
669673
// A linear under a power inside a sum, written as the variable: `x^3/(a + b (c + d x)^3)`.
670674
// Before the polynomial term, which takes the leading coefficient out of a power of
671675
// the sum and writes what is left out in powers of x, where the linear is gone; and
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//
2+
// Copyright (c) 2019-2026 Angouri.
3+
// AngouriMath is licensed under MIT.
4+
// Details: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.
5+
// Website: https://am.angouri.org.
6+
//
7+
8+
using System;
9+
using AngouriMath.Extensions;
10+
using Xunit;
11+
12+
namespace AngouriMath.Tests.Calculus
13+
{
14+
/// <summary>
15+
/// A rational function with a symbolic trinomial in x^2 below the bar, beside a root of a
16+
/// quadratic: taken apart over the trinomial's roots in x^2 where they are real, and over two
17+
/// real quadratics where they are a conjugate pair. Rubi's 1.2.2.4, written here with
18+
/// <c>k</c> for <c>e</c>, which is Euler's number.
19+
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
20+
/// </summary>
21+
[Trait("Area", "Calculus")]
22+
public sealed class ATrinomialInTheSquareBesideARootIntegralTest
23+
{
24+
private static void DifferentiatesBack(string integrand, params (string, double)[] pins)
25+
{
26+
var integral = integrand.ToEntity().Integrate("x");
27+
Assert.DoesNotContain("integral(", integral.Stringize());
28+
29+
// Pinned before it is differentiated: the answer is a piecewise on the sign of the
30+
// discriminant, read where it is a number.
31+
var pinned = integral.Substitute("C", 0);
32+
Entity original = integrand.ToEntity();
33+
foreach (var (name, value) in pins)
34+
{
35+
pinned = pinned.Substitute(name, value);
36+
original = original.Substitute(name, value);
37+
}
38+
var derivative = pinned.Differentiate("x");
39+
foreach (var at in new[] { -0.7, -0.3, 0.4, 0.9 })
40+
{
41+
var got = derivative.Substitute("x", at).EvalNumerical();
42+
var want = original.Substitute("x", at).EvalNumerical();
43+
var difference = Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart);
44+
var scale = Math.Max(1.0, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart));
45+
Assert.True(difference / scale < 1e-8,
46+
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
47+
}
48+
}
49+
50+
[Theory]
51+
[InlineData("x^2*sqrt(d+k*x^2)/(a+b*x^2+c*x^4)")]
52+
[InlineData("sqrt(d+k*x^2)/(x^2*(a+b*x^2+c*x^4))")]
53+
[InlineData("sqrt(1-x^2)/(x^2*(a+b*x^2+c*x^4))")]
54+
public void IsAnsweredAndDifferentiatesBack(string integrand)
55+
{
56+
// Two real roots in x^2, then a conjugate pair.
57+
DifferentiatesBack(integrand, ("a", 1), ("b", 5), ("c", 2), ("d", 1.5), ("k", 2));
58+
DifferentiatesBack(integrand, ("a", 2), ("b", 1), ("c", 1.5), ("d", 1.25), ("k", 3));
59+
}
60+
}
61+
}

0 commit comments

Comments
 (0)