diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index 20f9a22be..ef7bfd38f 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -3004,6 +3004,31 @@ leading coefficient once expanded, and declined: it is read expanded and bare no
| `"sin(x)/(a+b*sin(x))^3".Integrate("x")`, Rubi's 4.1.2.1 row 242 | `integral(...)` | the antiderivative |
| `"4*x*(1+x^2)/(a*x^2+2*b*x+a)^3".Integrate("x")` | `integral(...)` | the antiderivative |
+### Three pseudo-elliptic shapes from Welz's problems are integrated in closed form
+
+**Answers where there were none.** Seven of Welz's problems were declined. Each is now answered by a closed form for
+its shape, differentiated back at sampled points
+([#233](https://github.com/asc-community/AngouriMath/issues/233)):
+- `(x + A - 2 r)/((x - A) sqrt(C))`, for a cubic `C` that vanishes at `r`, midway between the pole and that zero, and
+ for which `C + m (x - r)^2` is `lambda (x - r)(x - A)^2` with `m` a constant and `lambda` the leading coefficient.
+ The integral is `-(2/sqrt(m)) atan(sqrt(m) (x - r)/sqrt(C))`, which uses only `sqrt(C)^2 = C` and so holds on either
+ branch. With a symbol in the integrand only: with numbers the other rules answer.
+- `sqrt(a + b x^2 + c x^4)/(d + e x^4)` where `c d + a e = 0` and `a c < 0`. Under `u = x/R`, `R` the root, it is
+ `(a/d) du/(1 - 2 b u^2 + w^2 u^4)` with `w = sqrt(b^2 - 4 a c)`, an inverse hyperbolic tangent and an arctangent of
+ `x (b + 2 c x^2 ± w)/R`, whose arguments reach `±1` and `±i` only where the binomial or the trinomial vanishes.
+- `(x + s)/((x^2 + s x + s^2) (a + b x^3)^(1/3))`. Multiplied above and below by `s - x` it is
+ `(s^2 - x^2)/((s^3 - x^3) y)`, `y` the cube root: a rational function of `t = x/y` beside `dt`, and a function of
+ `x^3` beside its derivative. The answer is a logarithm of a quotient, continuous at `x = s`, and two arctangents.
+ Only over `x + s`: any other linear leaves a part that is not elementary.
+
+| Input | Was (2.5.0) | Now |
+|---|---|---|
+| `"(a + x)/((-a + x)*sqrt(a^2*x - (1 + a^2)*x^2 + x^3))".ToEntity().Integrate("x")` | `integral(...)` | 94 characters |
+| `"(-a + x - sqrt(1 + a^2))/((-a + x + sqrt(1 + a^2))*sqrt((-a + x)*(1 + x^2)))".ToEntity().Integrate("x")` | `integral(...)` | 529 characters |
+| `"sqrt(1 - x^4)/(1 + x^4)".ToEntity().Integrate("x")` | `integral(...)` | 198 characters |
+| `"sqrt(1 + p*x^2 - x^4)/(1 + x^4)".ToEntity().Integrate("x")` | `integral(...)` | 490 characters |
+| `"(1 + x)/((1 + x + x^2)*(a + b*x^3)^(1/3))".ToEntity().Integrate("x")` | `integral(...)` | 323 characters |
+
### A linear over a linear beside the root of a cubic binomial is integrated where that is elementary
**Answers where there were none.** `(1 + x + sqrt(3))/((1 + x - sqrt(3)) sqrt(1 + x^3))` and the rest
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
index 91e57daa8..14a3b95cd 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
@@ -7128,6 +7128,122 @@ Entity OverOnePlus(Entity k)
return holds ? answer : null;
}
+ ///
+ /// A linear over a linear beside the reciprocal of the square root of a cubic C,
+ /// (x + A - 2 r)/((x - A) sqrt(C)), where C vanishes at r, midway between
+ /// the pole and the zero, and C + m (x - r)^2 is lambda (x - r) (x - A)^2 for a
+ /// constant m, lambda the leading coefficient: its integral is
+ /// -(2/sqrt(m)) atan(sqrt(m) (x - r)/sqrt(C)). Welz's
+ /// (a + x)/((x - a) sqrt(a^2 x - (1 + a^2) x^2 + x^3)).
+ ///
+ ///
+ ///
+ /// With z = (x - r)/sqrt(C), 1 + m z^2 is (C + m (x - r)^2)/C, which is
+ /// lambda (x - r) (x - A)^2/C, and 2 C - (x - r) C' is
+ /// -lambda (x - r) (x - A) (x + A - 2 r): so
+ /// d/dx atan(sqrt(m) z) = -sqrt(m) (x + A - 2 r)/(2 (x - A) sqrt(C)). That uses only
+ /// sqrt(C)^2 = C, so it holds on either branch of the root and of sqrt(m), and
+ /// the arctangent's argument is i or -i only where C or x - A
+ /// vanishes. Where m is negative the same is atanh(sqrt(-m) z)/sqrt(-m). A
+ /// linear P in atan(sqrt(m) P/sqrt(C)) gives a linear over a linear in one
+ /// other way, where C + m P^2 is lambda (x - A)^3, which is not read here.
+ ///
+ ///
+ /// Rubi's answer to Welz's (x - a - sqrt(1 + a^2))/((x - a + sqrt(1 + a^2)) sqrt((x - a) (1 + x^2)))
+ /// is this, and to the one in the summary this with the root split as sqrt(x) sqrt(C/x)
+ /// and the factor sqrt(x) sqrt(C/x)/sqrt(C) beside it. With numbers for the
+ /// coefficients the root is split so, x taken out of it, or the ansatz over Padé
+ /// approximants answers; with symbols neither did, and the search past them ran out of time
+ /// on Welz's at r = a^2/(2 a - 1). So with a symbol in the integrand only, the
+ /// conditions decided by value, and the answer differentiated back at sampled points.
+ /// https://github.com/asc-community/AngouriMath/issues/233
+ ///
+ ///
+ internal static Entity? SolveAPseudoEllipticQuotientOverTheRootOfACubicWithARootMidway(Entity expr, Entity.Variable x)
+ {
+ if (!expr.Vars.Any(symbol => symbol != x) || !HasASquareRootOf(expr, x))
+ return null;
+ var (numerator, denominator) = Functions.SingleQuotient.Of(expr);
+ Entity? radicand = null;
+ Entity below = Number.Integer.One;
+ foreach (var factor in Mulf.LinearChildren(denominator))
+ if (factor is Powf(var @base, Number.Rational half) && half.ERational.Equals(ERational.Create(1, 2)) && @base.ContainsNode(x))
+ {
+ if (radicand is not null)
+ return null;
+ radicand = @base;
+ }
+ else
+ below *= factor;
+ if (radicand is null || CoefficientsOfDegree(radicand, 3) is not { } cubic
+ || CoefficientsOfDegree(numerator, 1) is not { } above || CoefficientsOfDegree(below, 1) is not { } under)
+ return null;
+ // Over one bar first: the simplification leaves `a^2/(2 a - 1) - a^2/(-1 + 2 a)` as two terms.
+ static bool Zero(Entity constant) => VanishesIdentically(LowestOverTheSymbols(constant));
+ var pole = LowestOverTheSymbols(-under[0] / under[1]);
+ var zero = LowestOverTheSymbols(-above[0] / above[1]);
+ if (Zero(pole - zero))
+ return null;
+ var r = LowestOverTheSymbols((pole + zero) / 2);
+ var (c0, c1, c2, c3) = (cubic[0], cubic[1], cubic[2], cubic[3]);
+ if (!Zero(((c3 * r + c2) * r + c1) * r + c0))
+ return null;
+ // C/(c3 (x - r)) = x^2 + q1 x + q0, and that plus s (x - r) is (x - A)^2.
+ var q1 = c2 / c3 + r;
+ var q0 = c1 / c3 + r * q1;
+ var s = -2 * pole - q1;
+ if (Zero(s) || !Zero(q0 - s * r - pole * pole))
+ return null;
+ var m = LowestOverTheSymbols(c3 * s);
+ var z = (x - r) / MathS.Sqrt(radicand);
+ // atan(w z)/w for w^2 = m, which is even in w: a square of a linear in the symbol is
+ // taken as that linear, so that (1 - a)^2 gives a real form for every a.
+ Entity closed = ALinearWhoseSquareIs(m) is { } w ? MathS.Arctan(w * z) / w
+ : ALinearWhoseSquareIs(LowestOverTheSymbols(-m)) is { } v ? MathS.Hyperbolic.Artanh(v * z) / v
+ : BySign(m, MathS.Arctan(MathS.Sqrt(m) * z) / MathS.Sqrt(m),
+ MathS.Hyperbolic.Artanh(MathS.Sqrt(LowestOverTheSymbols(-m)) * z) / MathS.Sqrt(LowestOverTheSymbols(-m)));
+ var answer = (-2 * LowestOverTheSymbols(above[1] / under[1]) * closed).InnerSimplified;
+ return Functions.PartialFractions.DerivativeHoldsAtSampledPoints(answer, expr, x) ? answer : null;
+
+ // The coefficients of a polynomial in x of the degree, each free of x.
+ Entity[]? CoefficientsOfDegree(Entity polynomial, int degree)
+ {
+ if (!TreeAnalyzer.TryGetPolynomial(polynomial, x, out var read))
+ return null;
+ var coefficients = Enumerable.Repeat(Number.Integer.Zero, degree + 1).ToArray();
+ foreach (var pair in read)
+ {
+ if (!pair.Key.CanFitInInt32() || pair.Key.Sign < 0 || pair.Key.ToInt32Unchecked() > degree || pair.Value.ContainsNode(x))
+ return null;
+ coefficients[pair.Key.ToInt32Unchecked()] = pair.Value;
+ }
+ return VanishesIdentically(coefficients[degree]) ? null : coefficients;
+ }
+ }
+
+ /// Whether a half-odd power of something with in it is in .
+ private static bool HasASquareRootOf(Entity expr, Entity.Variable x)
+ => expr.Nodes.Any(node => node is Powf(var @base, Number.Rational { ERational.Denominator: var two })
+ && two.Equals(EInteger.FromInt32(2)) && @base.ContainsNode(x));
+
+ ///
+ /// w with w^2 = m where m is the square of a linear in its one symbol,
+ /// 1 - 2 a + a^2 as a - 1, with a positive leading coefficient; null otherwise.
+ ///
+ private static Entity? ALinearWhoseSquareIs(Entity m)
+ {
+ var symbols = m.Vars.ToList();
+ if (symbols.Count != 1 || !TreeAnalyzer.TryGetPolynomial(m, symbols[0], out var read)
+ || read.Keys.Any(power => power.Sign < 0 || power.CompareTo(EInteger.FromInt32(2)) > 0)
+ || !read.TryGetValue(EInteger.FromInt32(2), out var leading) || leading.Evaled is not Number.Real { IsPositive: true })
+ return null;
+ var middle = read.TryGetValue(EInteger.One, out var linear) ? linear : Number.Integer.Zero;
+ var constant = read.TryGetValue(EInteger.Zero, out var free) ? free : Number.Integer.Zero;
+ if (!VanishesIdentically(middle * middle - 4 * leading * constant))
+ return null;
+ return (MathS.Sqrt(leading) * (symbols[0] + middle / (2 * leading))).InnerSimplified;
+ }
+
///
/// x/((a + b x^3) sqrt(c + d x^3)) at the three ratios where its integral is elementary,
/// Rubi's 1.1.3.4: 4 b c = a d, 8 b c + a d = 0 and
@@ -7531,6 +7647,80 @@ Entity ConstantNegative()
internal static Entity? SolveABinomialDifferentialWithSymbols(Entity expr, Entity.Variable x)
=> SolveABinomialDifferential(expr, x, withSymbols: true);
+ ///
+ /// A linear over the quadratic factor of a difference of cubes, beside the cube root of a
+ /// binomial in x^3, (x + s)/((x^2 + s x + s^2) y) with y = (a + b x^3)^(1/3):
+ /// for c a cube root of a + b s^3, its integral is
+ /// ln((c - y)/(c x/s - y))/(2 c) + (atan((1 + 2 c x/(s y))/sqrt(3)) + atan((1 + 2 y/c)/sqrt(3)))/(sqrt(3) c).
+ /// Welz's (1 + x)/((1 + x + x^2) (a + b x^3)^(1/3)).
+ ///
+ ///
+ ///
+ /// Multiplied above and below by s - x it is (s^2 - x^2)/((s^3 - x^3) y): the
+ /// first term is dt/(s^3 - (a + b s^3) t^3) under t = x/y, the second a function
+ /// of x^3 beside its derivative, and the sum of their integrals is Rubi's, at
+ /// s = 1, and the form above at any s under x = s u. Both logarithms
+ /// of Rubi's vanish at x = s, where the integrand has no pole, and written apart they
+ /// jump there by an imaginary constant; their quotient tends to -b s^3/a and does
+ /// not. The identity uses no more about c than c^3 = a + b s^3, and holds for
+ /// every cube root; the principal one is y at x = s, so that the two
+ /// logarithms vanish together there whatever the sign of a + b s^3. With the real
+ /// root at a + b s^3 < 0, 1 + 2 y/c is sqrt(3) i at x = s, an
+ /// arctangent's singularity, and the answer jumped there; with the principal one it was
+ /// continuous on either side of the binomial's real zero, at thirty digits, at every sign
+ /// of a, b and a + b s^3 tried. The linear has to be x + s: any other leaves
+ /// x/((s^3 - x^3) y), which is not elementary -- Rubi's answer to Welz's
+ /// (2 + x)/((1 + x + x^2) (2 + x^3)^(1/3)) has an Appell function in it.
+ ///
+ ///
+ /// With numbers in the binomial the ansatz over logarithms of L - y answers most of
+ /// these, and is asked first; with symbols in it, as Welz's, it and every rule declined
+ /// them. The answer is differentiated back at sampled points.
+ /// https://github.com/asc-community/AngouriMath/issues/233
+ ///
+ ///
+ internal static Entity? SolveALinearOverTheQuadraticFactorOfADifferenceOfCubesBesideACubeRoot(Entity expr, Entity.Variable x)
+ {
+ var (numerator, denominator) = Functions.SingleQuotient.Of(expr);
+ Entity? binomial = null;
+ Entity below = Number.Integer.One;
+ foreach (var factor in Mulf.LinearChildren(denominator))
+ if (factor is Powf(var @base, Number.Rational third) && third.ERational.Equals(ERational.Create(1, 3)) && @base.ContainsNode(x))
+ {
+ if (binomial is not null)
+ return null;
+ binomial = @base;
+ }
+ else
+ below *= factor;
+ if (binomial is null || !TreeAnalyzer.TryGetPolynomial(binomial, x, out var cubic) || cubic.Count != 2
+ || !cubic.TryGetValue(EInteger.Zero, out var a) || !cubic.TryGetValue(EInteger.FromInt32(3), out var b)
+ || a.ContainsNode(x) || b.ContainsNode(x)
+ || !TreeAnalyzer.TryGetPolyLinear(numerator, x, out var n1, out var n0) || n1.ContainsNode(x) || n0.ContainsNode(x)
+ || !TreeAnalyzer.TryGetPolyQuadratic(below, x, out var q2, out var q1, out var q0)
+ || q2.ContainsNode(x) || q1.ContainsNode(x) || q0.ContainsNode(x))
+ return null;
+ static bool Zero(Entity constant) => VanishesIdentically(LowestOverTheSymbols(constant));
+ if (Zero(n1) || Zero(q2) || Zero(a) || Zero(b))
+ return null;
+ // q2 (x^2 + s x + s^2) below, and n1 (x + s) above.
+ var s = LowestOverTheSymbols(q1 / q2);
+ if (Zero(s) || !Zero(q0 / q2 - s * s) || !Zero(n0 / n1 - s))
+ return null;
+ var k = LowestOverTheSymbols(a + b * MathS.Pow(s, 3));
+ if (Zero(k))
+ return null;
+ // The principal root, which is y at x = s.
+ var cubeRoot = Number.Rational.Create(1, 3);
+ var c = MathS.Pow(k, cubeRoot);
+ var y = MathS.Pow(binomial, cubeRoot);
+ var sqrtOfThree = MathS.Sqrt(3);
+ var answer = (LowestOverTheSymbols(n1 / q2)
+ * (MathS.Ln((c - y) / (c * x / s - y)) / (2 * c)
+ + (MathS.Arctan((1 + 2 * c * x / (s * y)) / sqrtOfThree) + MathS.Arctan((1 + 2 * y / c) / sqrtOfThree)) / (sqrtOfThree * c))).InnerSimplified;
+ return Functions.PartialFractions.DerivativeHoldsAtSampledPoints(answer, expr, x) ? answer : null;
+ }
+
private static Entity? SolveABinomialDifferential(Entity expr, Entity.Variable x, bool withSymbols)
{
if (!TryReadABinomialDifferential(expr, x, out var power, out var exponent,
@@ -20836,6 +21026,87 @@ static Entity WithoutConditions(Entity coefficient)
return Functions.PartialFractions.DerivativeHoldsAtSampledPoints(answer, expr, x) ? answer : null;
}
+ ///
+ /// The root of a trinomial in the square over a binomial in the fourth power,
+ /// sqrt(a + b x^2 + c x^4)/(d + e x^4), where c d + a e = 0 and a c < 0:
+ /// with w = sqrt(b^2 - 4 a c), k = sqrt(2 (w + b)) and l = sqrt(2 (w - b)),
+ /// its integral is
+ /// (a/d) (atanh(x (b + 2 c x^2 + w)/(k R))/k - atan(x (b + 2 c x^2 - w)/(l R))/l),
+ /// R the root. Welz's sqrt(1 - x^4)/(1 + x^4) and sqrt(1 + p x^2 - x^4)/(1 + x^4).
+ ///
+ ///
+ ///
+ /// The binomial is (d/a) (a - c x^4), and u = x/R has
+ /// u' = (a - c x^4)/R^3, so the integrand is (a/d) du/(1 - 2 b u^2 + w^2 u^4), a
+ /// rational function of u -- Rubi's 1.2.2.6. Where a c is negative, w is
+ /// real and greater than |b|, and k and l are real; the two terms are
+ /// Rubi's, written for any a. k^2 R^2 - x^2 (b + 2 c x^2 + w)^2 is
+ /// 2 (a - c x^4) (b + 2 c x^2 + w) and l^2 R^2 + x^2 (b + 2 c x^2 - w)^2 is
+ /// -2 (a - c x^4) (b + 2 c x^2 - w), so the inverse hyperbolic tangent's argument is
+ /// 1 or -1, and the arctangent's i or -i, only at a zero of the binomial, none of them real,
+ /// or of the trinomial: the answer has no jump between two zeros of the trinomial. Each term
+ /// uses no more about the root than R^2 = a + b x^2 + c x^4, and so holds where the
+ /// trinomial is negative as well. Where a c is positive the substitution that writes
+ /// the even integrand in x - 1/x already answers it, and is left to:
+ /// sqrt(1 + x^4)/(1 - x^4). Declined before, since x/R is no substitution any
+ /// rule tries. The answer is differentiated back at sampled points.
+ /// https://github.com/asc-community/AngouriMath/issues/233
+ ///
+ ///
+ internal static Entity? SolveTheRootOfATrinomialInTheSquareOverABinomialInTheFourthPower(Entity expr, Entity.Variable x)
+ {
+ if (!HasASquareRootOf(expr, x))
+ return null;
+ var (numerator, denominator) = Functions.SingleQuotient.Of(expr);
+ Entity? radicand = null;
+ Entity multiple = Number.Integer.One;
+ foreach (var factor in Mulf.LinearChildren(numerator))
+ if (factor is Powf(var @base, Number.Rational half) && half.ERational.Equals(ERational.Create(1, 2)) && @base.ContainsNode(x))
+ {
+ if (radicand is not null)
+ return null;
+ radicand = @base;
+ }
+ else if (factor.ContainsNode(x))
+ return null;
+ else
+ multiple *= factor;
+ static bool Zero(Entity constant) => VanishesIdentically(LowestOverTheSymbols(constant));
+ if (radicand is null || EvenCoefficients(radicand, 3) is not { } trinomial || EvenCoefficients(denominator, 3) is not { } binomial
+ || !Zero(binomial[1]))
+ return null;
+ var (a, b, c) = (trinomial[0], trinomial[1], trinomial[2]);
+ var (d, e) = (binomial[0], binomial[2]);
+ if (Zero(a) || Zero(d) || !Zero(c * d + a * e) || SignOfANumberTimesEvenPowers(LowestOverTheSymbols(a * c)) is not < 0)
+ return null;
+ var w = MathS.Sqrt(LowestOverTheSymbols(b * b - 4 * a * c));
+ var k = MathS.Sqrt(2 * (w + b));
+ var l = MathS.Sqrt(2 * (w - b));
+ var root = MathS.Sqrt(radicand);
+ var square = MathS.Pow(x, Number.Integer.Create(2));
+ var answer = (LowestOverTheSymbols(multiple * a / d)
+ * (MathS.Hyperbolic.Artanh(x * (b + 2 * c * square + w) / (k * root)) / k
+ - MathS.Arctan(x * (b + 2 * c * square - w) / (l * root)) / l)).InnerSimplified;
+ return Functions.PartialFractions.DerivativeHoldsAtSampledPoints(answer, expr, x) ? answer : null;
+
+ // The coefficients of x^0, x^2, ... x^(2 count - 2) of a polynomial in x^2, each free of
+ // x; the first and the last not zero.
+ Entity[]? EvenCoefficients(Entity polynomial, int count)
+ {
+ if (!TreeAnalyzer.TryGetPolynomial(polynomial, x, out var read))
+ return null;
+ var coefficients = Enumerable.Repeat(Number.Integer.Zero, count).ToArray();
+ foreach (var pair in read)
+ {
+ if (!pair.Key.CanFitInInt32() || pair.Key.Sign < 0 || !pair.Key.IsEven
+ || pair.Key.ToInt32Unchecked() > 2 * count - 2 || pair.Value.ContainsNode(x))
+ return null;
+ coefficients[pair.Key.ToInt32Unchecked() / 2] = pair.Value;
+ }
+ return VanishesIdentically(coefficients[0]) || VanishesIdentically(coefficients[count - 1]) ? null : coefficients;
+ }
+ }
+
///
/// A piece over a whole power of , beside its root, as the one
/// half-odd power the two are: (g + h x)/(Q^j sqrt(Q)) is
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
index aac987b4a..a1fe58fb4 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
@@ -775,6 +775,9 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
// in x^2, before anything reads the trinomial whole: asked of it, the routes below ran
// past a minute on Rubi's 1.2.2.4 `sqrt(d + e x^2)/(x^2 (a + b x^2 + c x^4))`.
if ((answer = IndefiniteIntegralSolver.SolveATrinomialInTheSquareBesideTheRootOfAQuadratic(expr, x)) is { }) return answer;
+ // And the root of such a trinomial over a binomial in x^4 matched to it, c d + a e = 0, at
+ // a c < 0, under u = x/sqrt(a + b x^2 + c x^4): Welz's `sqrt(1 - x^4)/(1 + x^4)`.
+ if ((answer = IndefiniteIntegralSolver.SolveTheRootOfATrinomialInTheSquareOverABinomialInTheFourthPower(expr, x)) is { }) return answer;
// A linear under a power inside a sum, written as the variable: `x^3/(a + b (c + d x)^3)`.
// Before the polynomial term, which takes the leading coefficient out of a power of
// the sum and writes what is left out in powers of x, where the linear is gone; and
@@ -893,6 +896,11 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
// And any power of a square in any power of x, as the power of its root times a factor
// constant where the root is not zero: `x^2 (a^2 + 2 a b x^3 + b^2 x^6)^p`.
if ((answer = IndefiniteIntegralSolver.SolveByWritingAPowerOfASquareAsAPowerOfItsRoot(expr, x, integrateByParts)) is { }) return answer;
+ // A linear over a linear beside the root of a symbolic cubic that vanishes midway between
+ // the pole and the zero, as one arctangent: Welz's
+ // `(a + x)/((x - a) sqrt(a^2 x - (1 + a^2) x^2 + x^3))`. Before x is taken out of the
+ // root, after which the search ran out of time on the symbolic ones.
+ if ((answer = IndefiniteIntegralSolver.SolveAPseudoEllipticQuotientOverTheRootOfACubicWithARootMidway(expr, x)) is { }) return answer;
// And a fractional power of a sum whose every term has x in it, the power of x taken out
// with the factor that is constant where it is not zero: `1/sqrt(a x^2 + b x^5)`.
if ((answer = IndefiniteIntegralSolver.SolveByTakingAPowerOfXOutOfAFractionalPower(expr, x, integrateByParts)) is { }) return answer;
@@ -1389,6 +1397,10 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
// `A - B y` whose A/B agrees with a branch of y at the poles, found by an ansatz
// over Padé approximants.
if ((answer = SquareRootLogarithmAnsatz.Integrate(expr, x)) is { }) return answer;
+ // `(x + s)/((x^2 + s x + s^2) (a + b x^3)^(1/3))`, `(s^2 - x^2)/((s^3 - x^3) (a + b x^3)^(1/3))`
+ // in two elementary terms, in closed form: after the ansatz over a cube root, which
+ // answers most of the numeric spellings and declined Welz's, with symbols in the binomial.
+ if ((answer = IndefiniteIntegralSolver.SolveALinearOverTheQuadraticFactorOfADifferenceOfCubesBesideACubeRoot(expr, x)) is { }) return answer;
// And a sum with the root below the bar, over its conjugate: after every rule above,
// which answer the numeric spellings, `1/(a c + b c x^2 + d sqrt(a + b x^2))` among them.
if ((answer = IndefiniteIntegralSolver.SolveByTheConjugateOfASumWithARootOfAQuadraticBelowTheBar(expr, x, integrateByParts)) is { }) return answer;
diff --git a/Sources/Tests/UnitTests/Calculus/ALinearOverTheQuadraticFactorOfADifferenceOfCubesBesideACubeRootIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/ALinearOverTheQuadraticFactorOfADifferenceOfCubesBesideACubeRootIntegralTest.cs
new file mode 100644
index 000000000..a74efe9b9
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/ALinearOverTheQuadraticFactorOfADifferenceOfCubesBesideACubeRootIntegralTest.cs
@@ -0,0 +1,59 @@
+//
+// Copyright (c) 2019-2026 Angouri.
+// AngouriMath is licensed under MIT.
+// Details: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.
+// Website: https://am.angouri.org.
+//
+
+using System;
+using AngouriMath.Extensions;
+using Xunit;
+
+namespace AngouriMath.Tests.Calculus
+{
+ ///
+ /// (x + s)/((x^2 + s x + s^2) (a + b x^3)^(1/3)), which is
+ /// (s^2 - x^2)/((s^3 - x^3) (a + b x^3)^(1/3)): a logarithm and two arctangents. Welz's
+ /// (1 + x)/((1 + x + x^2) (a + b x^3)^(1/3)) was declined with the symbols in it.
+ /// #233
+ ///
+ ///
+ /// Checked by differentiating back on either side of the binomial's real zero and of
+ /// x = s, at signs of a, b and a + b s^3 both ways.
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class ALinearOverTheQuadraticFactorOfADifferenceOfCubesBesideACubeRootIntegralTest
+ {
+ [Theory]
+ [InlineData("(1 + x)/((1 + x + x^2)*(a + b*x^3)^(1/3))")]
+ [InlineData("(2 + 2*x)/((3 + 3*x + 3*x^2)*(a - b*x^3)^(1/3))")]
+ // s = -1.
+ [InlineData("(x - 1)/((1 - x + x^2)*(a + b*x^3)^(1/3))")]
+ [InlineData("(1 + x)/((1 + x + x^2)*(2 + 3*x^3)^(1/3))")]
+ public void IsIntegrated(string integrand)
+ {
+ var integral = integrand.ToEntity().Integrate("x");
+ var text = integral.Stringize();
+ Assert.DoesNotContain("integral(", text);
+ Assert.True(text.Length < 2000, $"{text.Length} characters of answer for {integrand}");
+ foreach (var (a, b) in new[] { (1.3, 0.7), (-0.9, 2.1), (0.4, -2.2) })
+ {
+ var derivative = integral.Substitute("C", 0).Substitute("a", a).Substitute("b", b).Differentiate("x");
+ var original = integrand.ToEntity().Substitute("a", a).Substitute("b", b);
+ var compared = 0;
+ foreach (var at in new[] { -2.3, -1.2, -0.4, 0.3, 0.9, 1.1, 1.9, 3.2 })
+ {
+ var want = original.Substitute("x", at).EvalNumerical();
+ var got = derivative.Substitute("x", at).EvalNumerical();
+ if (want.IsNaN)
+ continue;
+ compared++;
+ Assert.True(Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart)
+ < 1e-9 * Math.Max(1, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart)),
+ $"d/dx of the antiderivative of {integrand} is {got} at x = {at}, a = {a}, b = {b}, where the integrand is {want}");
+ }
+ Assert.True(compared >= 5, $"only {compared} points could be compared for {integrand} at a = {a}, b = {b}");
+ }
+ }
+ }
+}
diff --git a/Sources/Tests/UnitTests/Calculus/PseudoEllipticQuotientOverTheRootOfACubicWithARootMidwayIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/PseudoEllipticQuotientOverTheRootOfACubicWithARootMidwayIntegralTest.cs
new file mode 100644
index 000000000..aa18bc414
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/PseudoEllipticQuotientOverTheRootOfACubicWithARootMidwayIntegralTest.cs
@@ -0,0 +1,64 @@
+//
+// Copyright (c) 2019-2026 Angouri.
+// AngouriMath is licensed under MIT.
+// Details: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.
+// Website: https://am.angouri.org.
+//
+
+using System;
+using AngouriMath.Extensions;
+using Xunit;
+
+namespace AngouriMath.Tests.Calculus
+{
+ ///
+ /// (x + A - 2 r)/((x - A) sqrt(C)) for a symbolic cubic C with a root r midway
+ /// between the pole and the zero, where C + m (x - r)^2 is a multiple of
+ /// (x - r) (x - A)^2: one arctangent of sqrt(m) (x - r)/sqrt(C). Welz's, which the
+ /// search ran out of time on with the symbol in them.
+ /// #233
+ ///
+ ///
+ /// Checked by differentiating back at points on either side of every root and of the pole,
+ /// where the radicand is positive and where it is negative, at two values of a.
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class PseudoEllipticQuotientOverTheRootOfACubicWithARootMidwayIntegralTest
+ {
+ [Theory]
+ // m = (1 - a)^2, r = 0.
+ [InlineData("(a + x)/((-a + x)*sqrt(a^2*x - (1 + a^2)*x^2 + x^3))")]
+ [InlineData("3*(a + x)/((-a + x)*sqrt(a^2*x - (1 + a^2)*x^2 + x^3))")]
+ // m = -a^2, r = 1.
+ [InlineData("(-2 + a + x)/((-a + x)*sqrt((2 - a)*a*x + (-1 - 2*a + a^2)*x^2 + x^3))")]
+ // m = -(2 a - 1)^2, r = a^2/(2 a - 1), the leading coefficient 2 a - 1.
+ [InlineData("(-a + (-1 + 2*a)*x)/((-a + x)*sqrt(a^2*x - (-1 + 2*a + a^2)*x^2 + (-1 + 2*a)*x^3))")]
+ // m = 2 sqrt(1 + a^2) - 2 a, r = a, the cubic written as a product.
+ [InlineData("(-a + x - sqrt(1 + a^2))/((-a + x + sqrt(1 + a^2))*sqrt((-a + x)*(1 + x^2)))")]
+ public void IsIntegrated(string integrand)
+ {
+ var integral = integrand.ToEntity().Integrate("x");
+ var text = integral.Stringize();
+ Assert.DoesNotContain("integral(", text);
+ Assert.True(text.Length < 2000, $"{text.Length} characters of answer for {integrand}");
+ foreach (var a in new[] { 1.7, -0.8 })
+ {
+ var derivative = integral.Substitute("C", 0).Substitute("a", a).Differentiate("x");
+ var original = integrand.ToEntity().Substitute("a", a);
+ var compared = 0;
+ foreach (var at in new[] { -2.3, -0.6, 0.3, 0.8, 1.4, 2.2, 3.9 })
+ {
+ var want = original.Substitute("x", at).EvalNumerical();
+ var got = derivative.Substitute("x", at).EvalNumerical();
+ if (want.IsNaN)
+ continue;
+ compared++;
+ Assert.True(Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart)
+ < 1e-9 * Math.Max(1, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart)),
+ $"d/dx of the antiderivative of {integrand} is {got} at x = {at}, a = {a}, where the integrand is {want}");
+ }
+ Assert.True(compared >= 5, $"only {compared} points could be compared for {integrand} at a = {a}");
+ }
+ }
+ }
+}
diff --git a/Sources/Tests/UnitTests/Calculus/TheRootOfATrinomialInTheSquareOverABinomialInTheFourthPowerIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/TheRootOfATrinomialInTheSquareOverABinomialInTheFourthPowerIntegralTest.cs
new file mode 100644
index 000000000..db79e4b1d
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/TheRootOfATrinomialInTheSquareOverABinomialInTheFourthPowerIntegralTest.cs
@@ -0,0 +1,59 @@
+//
+// Copyright (c) 2019-2026 Angouri.
+// AngouriMath is licensed under MIT.
+// Details: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.
+// Website: https://am.angouri.org.
+//
+
+using System;
+using AngouriMath.Extensions;
+using Xunit;
+
+namespace AngouriMath.Tests.Calculus
+{
+ ///
+ /// sqrt(a + b x^2 + c x^4)/(d + e x^4) with c d + a e = 0 and a c < 0, a
+ /// rational function of u = x/sqrt(a + b x^2 + c x^4): an arctangent and an inverse
+ /// hyperbolic tangent. Welz's sqrt(1 - x^4)/(1 + x^4) was declined.
+ /// #233
+ ///
+ ///
+ /// Checked by differentiating back inside the trinomial's real zeros and outside them, where
+ /// the integrand is imaginary, at two values of p.
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class TheRootOfATrinomialInTheSquareOverABinomialInTheFourthPowerIntegralTest
+ {
+ [Theory]
+ [InlineData("sqrt(1 - x^4)/(1 + x^4)")]
+ [InlineData("sqrt(1 + p*x^2 - x^4)/(1 + x^4)")]
+ [InlineData("sqrt(2 - x^4)/(2 + x^4)")]
+ // a < 0 < c, and a multiple above and below.
+ [InlineData("3*sqrt(-1 + x^2 + 2*x^4)/(5 + 10*x^4)")]
+ public void IsIntegrated(string integrand)
+ {
+ var integral = integrand.ToEntity().Integrate("x");
+ var text = integral.Stringize();
+ Assert.DoesNotContain("integral(", text);
+ Assert.True(text.Length < 2000, $"{text.Length} characters of answer for {integrand}");
+ foreach (var p in new[] { 1.3, -2.5 })
+ {
+ var derivative = integral.Substitute("C", 0).Substitute("p", p).Differentiate("x");
+ var original = integrand.ToEntity().Substitute("p", p);
+ var compared = 0;
+ foreach (var at in new[] { -1.7, -0.6, -0.2, 0.3, 0.55, 1.4, 2.6 })
+ {
+ var want = original.Substitute("x", at).EvalNumerical();
+ var got = derivative.Substitute("x", at).EvalNumerical();
+ if (want.IsNaN)
+ continue;
+ compared++;
+ Assert.True(Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart)
+ < 1e-9 * Math.Max(1, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart)),
+ $"d/dx of the antiderivative of {integrand} is {got} at x = {at}, p = {p}, where the integrand is {want}");
+ }
+ Assert.True(compared >= 5, $"only {compared} points could be compared for {integrand} at p = {p}");
+ }
+ }
+ }
+}