diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index 7b8cb4a18..2419e7cc9 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -1622,6 +1622,73 @@ an arctangent ([#718](https://github.com/asc-community/AngouriMath/issues/718)).
| `"1/((d+k*x)*(c^2*d^2-b*c*d*k+b^2*k^2+3*b*c*k^2*x+3*c^2*k^2*x^2)^(1/3))".ToEntity().Integrate("x")` | `integral(...)` | 1,934 characters |
| `"1/((d+k*x)*(-c^2*d^2+b*c*d*k+2*b^2*k^2+9*b*c*k^2*x+9*c^2*k^2*x^2)^(1/3))".ToEntity().Integrate("x")` | `integral(...)` | 1,274 characters |
+### A quotient beside the cube root of a cubic with a linear factor is integrated where it is `T'/(1 - T^3)`
+
+**Answers where there were none.** Welz's `1/(x ((x - 1) (q - 2 q x + x^2))^(1/3))`,
+`(2 - (1 + k) x)/((x (1 - x) (1 - k x))^(1/3) (1 - (1 + k) x))` and
+`(1 - k x)/((1 - (2 - k) x) (x (1 - x) (1 - k x))^(2/3))` were declined. For `P = L Q` with `L` linear, a rational
+function over `P^(1/3)` that is a constant times `W/(Q - mu L^2)`, or over `P^(2/3)` that is one times
+`L W/(L^2 - mu Q)`, `W = 2 Q L' - L Q'`, is a constant times `T'/(1 - T^3)` for `T = m L/P^(1/3)` or `m P^(1/3)/L`,
+`m^3 = mu`, and is answered by two logarithms and an arctangent of `T`. With numbers in `P` the ansatz over
+logarithms of `L - y` answers these, and is asked first ([#233](https://github.com/asc-community/AngouriMath/issues/233)).
+
+| Input | Was (2.5.0) | Now |
+|---|---|---|
+| `"1/(x*((-1+x)*(q-2*q*x+x^2))^(1/3))".ToEntity().Integrate("x")` | `integral(...)` | 385 characters |
+| `"(2-(1+k)*x)/(((1-x)*x*(1-k*x))^(1/3)*(1-(1+k)*x))".ToEntity().Integrate("x")` | `integral(...)` | 319 characters |
+| `"(1-k*x)/((1+(-2+k)*x)*((1-x)*x*(1-k*x))^(2/3))".ToEntity().Integrate("x")` | `integral(...)` | 424 characters |
+
+### A linear over a linear beside the root of a quartic is integrated where it is a function of the numerator's square over the root
+
+**Answers where there were none.** `(1 + x - sqrt(3))/((1 + x + sqrt(3)) sqrt(x^4 + 4 sqrt(3) x^2 - 4))` and its
+conjugate were declined, in Welz's suite and in Rubi's 1.2.2.3, and so were the two with `1 + 2 x` beside them.
+Where `gamma P - mu M W = L^4` for `W = 4 L' P - L P'` and constants `gamma` and `mu`, `L/(M sqrt(P))` is the
+derivative of `(2 mu/c) artanh(L^2/(c sqrt(P)))` with `c^2 = gamma`, an arctangent where `gamma` is negative. The
+ansatz over logarithms of `A - B y` takes its places at rational poles, and these are at `-1 -+ sqrt(3)` ([#233](https://github.com/asc-community/AngouriMath/issues/233)).
+
+| Input | Was (2.5.0) | Now |
+|---|---|---|
+| `"(1+x-sqrt(3))/((1+x+sqrt(3))*sqrt(-4+x^4+4*x^2*sqrt(3)))".ToEntity().Integrate("x")` | `integral(...)` | 246 characters |
+| `"(1+x+sqrt(3))/((1+x-sqrt(3))*sqrt(-4+x^4-4*x^2*sqrt(3)))".ToEntity().Integrate("x")` | `integral(...)` | 151 characters |
+| `"(1+2*x-sqrt(3))/((1+2*x+sqrt(3))*sqrt(-1+4*x^4+4*x^2*sqrt(3)))".ToEntity().Integrate("x")` | `integral(...)` | 261 characters |
+| `"(1+2*x+sqrt(3))/((1+2*x-sqrt(3))*sqrt(-1+4*x^4-4*x^2*sqrt(3)))".ToEntity().Integrate("x")` | `integral(...)` | 159 characters |
+
+### An exponential of a rational function beside the derivative of its exponential integral is integrated
+
+**Answers where there were none.** Hebisch's `(2 + 2x + 3x^2 - x^3 + 2x^4) e^(x/(2 + x^2))/(2x + x^3)` was declined.
+It is `((2 + x^2) e^h)' + e^h h'/h` for `h = x/(2 + x^2)`, and the ansatz for `e^h N/D` alone has no solution. The
+multiple of `h'/h` is the residue at a zero of `h`, the rest is asked of the ansatz, and the answer is
+`e^h N/D + c Ei(h)` ([#233](https://github.com/asc-community/AngouriMath/issues/233)).
+
+| Input | Was (2.5.0) | Now |
+|---|---|---|
+| `"(2+2*x+3*x^2-x^3+2*x^4)*e^(x/(2+x^2))/(2*x+x^3)".ToEntity().Integrate("x")` | `integral(...)` | 61 characters |
+
+### A function of `sqrt(sec(u) + 1)` and `sqrt(sec(u) - 1)` is integrated in their difference
+
+**Answers where there were none.** Charlwood's `sqrt(sqrt(1 + sec(x)) - sqrt(sec(x) - 1))` was declined. Both roots
+and the secant are rational functions of `w = sqrt(sec(u) + 1) - sqrt(sec(u) - 1)`, and `dx` is
+`-8 K w dw/(b (w^4 + 4))` for `u = a + b x` and `K = cot(u) sqrt(sec(u) + 1) sqrt(sec(u) - 1)`, which is 1 or -1. The
+integral in `w` is taken from `sqrt(2)`, where `K` changes sign, so that the answer is continuous wherever the
+integrand is real ([#233](https://github.com/asc-community/AngouriMath/issues/233)).
+
+| Input | Was (2.5.0) | Now |
+|---|---|---|
+| `"sqrt(-sqrt(-1+sec(x))+sqrt(1+sec(x)))".ToEntity().Integrate("x")` | `integral(...)` | 2,635 characters |
+
+### A half-odd power of a linear plus the root of a quadratic is integrated in Euler's substitution where their squares differ by a square
+
+**Answers where there were none.** Bondarenko's `sqrt(1 + sqrt(x) + sqrt(1 + 2 x + 2 sqrt(x)))` and
+`sqrt(sqrt(2) + sqrt(x) + sqrt(2 + 2 x + sqrt(8) sqrt(x)))` were declined. Under `x = t^2` each is a half-odd power of
+`L + sqrt(Q)` with `Q - L^2 = c (t - r)^2`, and in `u = (sqrt(Q) - L)/(t - r)` both `t` and `sqrt(Q)` are rational and
+`L + sqrt(Q)` is a constant over the quadratic `c - 2 b u - u^2`, `b` the slope of `L`: the integrand is rational
+beside a root of a quadratic ([#233](https://github.com/asc-community/AngouriMath/issues/233)).
+
+| Input | Was (2.5.0) | Now |
+|---|---|---|
+| `"sqrt(1+sqrt(x)+sqrt(1+2*x+2*sqrt(x)))".ToEntity().Integrate("x")` | `integral(...)` | 2,261 characters |
+| `"sqrt(sqrt(2)+sqrt(x)+sqrt(2+2*x+sqrt(8)*sqrt(x)))".ToEntity().Integrate("x")` | `integral(...)` | 2,715 characters |
+
### A linear and a quadratic to one power are integrated as one binomial
**Answers where there were none.** `1/((1 + x)^(1/3) (1 - x + x^2)^(1/3))` was declined. A linear and a quadratic to
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
index 5a953b156..9dd262a7f 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
@@ -6994,6 +6994,86 @@ bool IsAPolynomialInU(Entity part)
return expr * inTermsOfW.Substitute(w, wOfX) / inX;
}
+ ///
+ /// A function of p = sqrt(sec(u) + 1) and q = sqrt(sec(u) - 1), u = a + b x,
+ /// in their difference w = p - q: p + q = 2/w, so p = (w^2 + 2)/(2 w),
+ /// q = (2 - w^2)/(2 w) and sec(u) = (w^4 + 4)/(4 w^2), and
+ /// dx = -8 K w dw/(b (w^4 + 4)) for K = cot(u) p q, which is 1 or -1.
+ /// Charlwood's sqrt(sqrt(1 + sec(x)) - sqrt(sec(x) - 1)) is -8 w^(3/2)/(w^4 + 4)
+ /// in w.
+ ///
+ ///
+ /// w' = -b sec(u) tan(u) w/(2 p q) and K tan(u) = p q, so (K/b) w' is
+ /// -sec(u) w/2, which is -(w^4 + 4)/(8 w); K is constant wherever it is
+ /// continuous, its square being cot(u)^2 (sec(u)^2 - 1). Each step uses no more about
+ /// the roots than p^2 = sec(u) + 1 and q^2 = sec(u) - 1, so the answer holds on
+ /// every branch of either. Where the integrand is real, K changes sign only at the
+ /// multiples of 2 pi, where w = sqrt(2): the integral in w is taken from
+ /// there, so that the answer is continuous through them. Declined before, the roots of the two sums
+ /// being no substitution any rule tries. The answer is differentiated back at sampled points.
+ /// https://github.com/asc-community/AngouriMath/issues/233
+ ///
+ internal static Entity? SolveAFunctionOfTheRootsOfTheSecantPlusAndMinusOneInTheirDifference(Entity expr, Entity.Variable x, bool integrateByParts)
+ {
+ Entity? argument = null;
+ foreach (var node in expr.Nodes)
+ {
+ if (TrigonometricArgument(node) is not { } thisArgument || !thisArgument.ContainsNode(x))
+ continue;
+ if (argument is null)
+ argument = thisArgument;
+ else if (argument != thisArgument)
+ return null;
+ }
+ if (argument is null || !TreeAnalyzer.TryGetPolyLinear(argument, x, out var rate, out _)
+ || rate.ContainsNode(x) || VanishesIdentically(rate))
+ return null;
+ var secant = MathS.Sec(argument);
+ static bool IsAHalfOddPower(Entity power) => power is Number.Rational rational and not Number.Integer
+ && rational.ERational.Denominator.Equals(EInteger.FromInt32(2)) && rational.ERational.Numerator.CanFitInInt32();
+ // The side of a radicand that is sec(u) + 1 or sec(u) - 1, read as a linear in the secant.
+ var named = Variable.CreateUnique(expr, "s_secant");
+ int? Side(Entity radicand)
+ => radicand.Replace(node => node == secant ? named : node) is var inS && !inS.ContainsNode(x)
+ && TreeAnalyzer.TryGetPolyLinear(inS, named, out var one, out var offset) && !one.ContainsNode(named) && !offset.ContainsNode(named)
+ && one.Evaled == Number.Integer.One && offset.Evaled is var side && (side == Number.Integer.One || side == Number.Integer.MinusOne)
+ ? (side == Number.Integer.One ? 1 : -1) : null;
+ Entity? plus = null, minus = null;
+ foreach (var node in expr.Nodes)
+ if (node is Powf(var @base, var power) && IsAHalfOddPower(power) && @base.ContainsNode(x))
+ {
+ if (Side(@base) is 1)
+ plus ??= @base;
+ else if (Side(@base) is -1)
+ minus ??= @base;
+ }
+ if (plus is null || minus is null)
+ return null;
+ var w = Variable.CreateUnique(expr, "w_secant");
+ var p = Variable.CreateUnique(expr + w, "p_secant");
+ var q = Variable.CreateUnique(expr + w + p, "q_secant");
+ // The roots first, since a replacement reaches the secant inside them before the roots.
+ var asRoots = expr.Replace(node => node is Powf(var @base, var power) && IsAHalfOddPower(power) && (@base == plus || @base == minus)
+ ? MathS.Pow(@base == plus ? p : q, Number.Integer.Create(((Number.Rational)power).ERational.Numerator))
+ : node);
+ var square = MathS.Pow(w, 2);
+ var fourth = MathS.Pow(w, 4);
+ var inW = asRoots.Replace(node => node == secant ? (fourth + 4) / (4 * square) : node)
+ .Substitute(p, (square + 2) / (2 * w)).Substitute(q, (2 - square) / (2 * w));
+ if (inW.ContainsNode(x))
+ return null;
+ var integrand = Functions.PartialFractions.Bare((inW * -8 * w / (fourth + 4)).Simplify());
+ if (integrand.ContainsNode(p) || integrand.ContainsNode(q)
+ || Integration.ComputeAsAQuestionOfItsOwn(integrand, w, integrateByParts) is not { } inTermsOfW
+ || inTermsOfW.Nodes.Any(node => node is Integralf || node == MathS.NaN))
+ return null;
+ var (rootOfPlus, rootOfMinus) = (MathS.Sqrt(plus), MathS.Sqrt(minus));
+ // From w = sqrt(2), where u is a multiple of 2 pi and K changes sign: continuous there.
+ var answer = (MathS.Cotan(argument) * rootOfPlus * rootOfMinus / rate
+ * (inTermsOfW.Substitute(w, rootOfPlus - rootOfMinus) - inTermsOfW.Substitute(w, MathS.Sqrt(2)))).InnerSimplified;
+ return Functions.PartialFractions.DerivativeHoldsAtSampledPoints(answer, expr, x) ? answer : null;
+ }
+
///
/// sec(y) (a ± a sec(y))^m (c ∓ c sec(y))^n, with n half-odd and m anything,
/// integrated in u = a ± a sec(y): the two sums multiply to -a c tan(y)^2, so the
@@ -8053,6 +8133,80 @@ Entity Symbolic(Entity side) => Mulf.LinearChildren(side).Where(factor => factor
return Functions.PartialFractions.DerivativeHoldsAtSampledPoints(answer, expr, x) ? answer : null;
}
+ ///
+ /// A linear over a linear beside the reciprocal of the square root of a cubic or a quartic,
+ /// L/(M y) with y = sqrt(P), where gamma P - mu M W = L^4 for
+ /// W = 4 L' P - L P' and constants gamma and mu: its integral is
+ /// (2 mu/c) artanh(L^2/(c y)) for c^2 = gamma, which is
+ /// -(2 mu/s) atan(L^2/(s y)) for s^2 = -gamma. Welz's
+ /// (1 + x - sqrt(3))/((1 + x + sqrt(3)) sqrt(x^4 + 4 sqrt(3) x^2 - 4)).
+ ///
+ ///
+ ///
+ /// u = L^2/(c y) has u' = L W/(2 c P y) and 1 - u^2 = (gamma P - L^4)/(gamma P),
+ /// so u'/(1 - u^2) = c L W/(2 y (gamma P - L^4)), which is c L/(2 mu M y) where
+ /// the coefficients agree. That uses no more about the root than y^2 = P, so it holds
+ /// on either branch, and for c = i s the inverse hyperbolic tangent is
+ /// -i atan(L^2/(s y)). The argument holds the square of L because the integrand
+ /// vanishes where L does, as the derivative of a function of L^2/y does. The
+ /// identity is five coefficients linear in gamma and mu.
+ ///
+ ///
+ /// The ansatz over logarithms of A - B y takes its places at rational poles, and
+ /// declined Welz's, whose pole is -1 - sqrt(3); the rule for a cubic binomial reads
+ /// no quartic, and is asked first. The answer is differentiated back at sampled points.
+ /// https://github.com/asc-community/AngouriMath/issues/233
+ ///
+ ///
+ internal static Entity? SolveALinearOverALinearBesideTheRootOfAQuarticByTheSquareOfTheNumerator(Entity expr, Entity.Variable x)
+ {
+ 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 = below == Number.Integer.One ? factor : below * factor;
+ if (radicand is null
+ || !TreeAnalyzer.TryGetPolyLinear(numerator, x, out var slope, out var offset) || slope.ContainsNode(x) || offset.ContainsNode(x)
+ || VanishesIdentically(slope)
+ || !TreeAnalyzer.TryGetPolyLinear(below, x, out var belowSlope, out var belowOffset) || belowSlope.ContainsNode(x) || belowOffset.ContainsNode(x)
+ || VanishesIdentically(belowSlope)
+ || !TreeAnalyzer.TryGetPolynomial(radicand, x, out var p) || p.Any(pair => pair.Key.Sign < 0 || pair.Value.ContainsNode(x))
+ || p.Keys.Max()!.ToInt32Unchecked() is not (3 or 4))
+ return null;
+ var w = 4 * slope * radicand - numerator * radicand.Differentiate(x);
+ if (!TreeAnalyzer.TryGetPolynomial(-below * w, x, out var mw) || !TreeAnalyzer.TryGetPolynomial(MathS.Pow(numerator, 4), x, out var fourth))
+ return null;
+ var powers = p.Keys.Concat(mw.Keys).Concat(fourth.Keys).Distinct().OrderBy(power => power).ToList();
+ Entity At(Dictionary read, EInteger power)
+ => read.TryGetValue(power, out var value) ? value.Expand().InnerSimplified : Number.Integer.Zero;
+ var matrix = powers.Select(power => new[] { At(p, power), At(mw, power) }).ToArray();
+ var rhs = powers.Select(power => At(fourth, power)).ToArray();
+ if (!Functions.PartialFractions.TrySolveLinear(matrix, rhs, out var values))
+ return null;
+ static Entity Tidy(Entity constant) => constant.Vars.Any() ? LowestOverTheSymbols(constant) : Functions.PartialFractions.Bare(constant.Simplify());
+ var (gamma, mu) = (Tidy(values[0]), Tidy(values[1]));
+ if (VanishesIdentically(gamma) || VanishesIdentically(mu))
+ return null;
+ var square = MathS.Pow(numerator, 2);
+ var y = MathS.Sqrt(radicand);
+ var c = MathS.Sqrt(gamma);
+ var s = MathS.Sqrt(Tidy(-gamma));
+ // artanh(L^2/(c y)) as ln(((c y + L^2)/(c y - L^2))^2)/4: the same derivative, and real wherever c y is.
+ var positive = Tidy(mu / (2 * c)) * MathS.Ln(MathS.Pow((c * y + square) / (c * y - square), 2));
+ var negative = Tidy(-2 * mu / s) * MathS.Arctan(square / (s * y));
+ // A number's sign decided by its value, which `6 sqrt(3) - 9` written as a sum is not to the reader of signs.
+ var answer = (gamma.Vars.Any() ? BySign(gamma, positive, negative)
+ : gamma.Evaled is Number.Real { IsNegative: true } ? negative : positive).InnerSimplified;
+ return Functions.PartialFractions.DerivativeHoldsAtSampledPoints(answer, expr, x) ? answer : null;
+ }
+
///
/// The square root of , a positive rational times powers of
/// symbols, each power a multiple of four, as the rational's root times their halves;
@@ -18538,6 +18692,72 @@ private static Entity InTheBaseWritten(Entity answer, Entity integrand, Entity.V
: answer.Replace(node => node is Powf(var e, var exponent) && e == MathS.e && written.TryGetValue(exponent, out var asWritten) ? asWritten : node);
}
+ ///
+ /// An exponential of a rational function h times a rational function R that is
+ /// the derivative of e^h N/D and of a multiple of the exponential integral of h:
+ /// e^h R = (e^h N/D)' + c e^h h'/h, whose integral is e^h N/D + c Ei(h).
+ /// Hebisch's (2 + 2x + 3x^2 - x^3 + 2x^4) e^(x/(2 + x^2))/(2x + x^3) is
+ /// (2 + x^2) e^(x/(2 + x^2)) + Ei(x/(2 + x^2)).
+ ///
+ ///
+ /// At a zero r of h of order k where h has no pole, h'/h has
+ /// the residue k, and (N/D)' + h' N/D none: a pole of N/D there is one of
+ /// order two or more in it. So c is the residue of R at r over k, and
+ /// R - c h'/h is asked of 's ansatz, which
+ /// declines R whole: by Liouville's theorem e^h R has no elementary
+ /// antiderivative where c is not zero. Each linear factor of the numerator of h,
+ /// as written, is a candidate r. The answer is differentiated back at sampled points.
+ /// https://github.com/asc-community/AngouriMath/issues/233
+ ///
+ internal static Entity? SolveAnExponentialBesideAMultipleOfTheDerivativeOfItsExponentialIntegral(Entity expr, Entity.Variable x)
+ {
+ Entity? exponent = null;
+ Entity rest = 0;
+ foreach (var term in Sumf.LinearChildren(expr))
+ {
+ if (ReadOneExponentialTimesTheRest(term, x) is not var (h, r) || h is null)
+ return null;
+ if (exponent is null)
+ exponent = h;
+ else if (exponent != h)
+ return null;
+ rest += r;
+ }
+ if (exponent is null || !IsARationalFunction(exponent, x) || !IsARationalFunction(rest, x))
+ return null;
+ var (p, q) = Functions.SingleQuotient.Of(exponent.InnerSimplified);
+ var (above, below) = Functions.SingleQuotient.Of(Functions.SingleQuotient.Combine(rest).InnerSimplified);
+ if (!below.ContainsNode(x))
+ return null;
+ var logarithmicDerivative = (p.Differentiate(x) * q - p * q.Differentiate(x)) / (p * q);
+ var belowPrime = below.Differentiate(x);
+ foreach (var factor in Mulf.LinearChildren(p))
+ {
+ var (linear, order) = factor is Powf(var @base, Number.Integer { EInteger.Sign: > 0 } power) && power.EInteger.CanFitInInt32()
+ ? (@base, power.EInteger.ToInt32Unchecked()) : (factor, 1);
+ if (!TreeAnalyzer.TryGetPolyLinear(linear, x, out var slope, out var offset) || slope.ContainsNode(x) || offset.ContainsNode(x)
+ || VanishesIdentically(slope))
+ continue;
+ var root = LowestOverTheSymbols(-offset / slope);
+ Entity At(Entity polynomial) => LowestOverTheSymbols(polynomial.Substitute(x, root));
+ // A simple pole of R at a zero of h that is not a pole of it.
+ if (VanishesIdentically(At(q)) || !VanishesIdentically(At(below)) || VanishesIdentically(At(belowPrime)))
+ continue;
+ var c = LowestOverTheSymbols(At(above) / (At(belowPrime) * order));
+ if (VanishesIdentically(c))
+ continue;
+ var (top, bottom) = Functions.SingleQuotient.Of(Functions.SingleQuotient.Combine(rest - c * logarithmicDerivative).InnerSimplified);
+ var remainder = Functions.PolynomialGcd.TryCancel(top, bottom, out var cancelled) && cancelled is not null
+ ? Functions.PartialFractions.Bare(cancelled) : top / bottom;
+ if (IntegrateByAnsatz(exponent, remainder, x) is not { } part)
+ continue;
+ var answer = (part + c * MathS.Ei(exponent)).InnerSimplified;
+ if (Functions.PartialFractions.DerivativeHoldsAtSampledPoints(answer, expr, x))
+ return InTheBaseWritten(answer, expr, x);
+ }
+ return null;
+ }
+
///
/// e^(a x) times a rational function of sin(x) and cos(x), closed by
/// an ansatz in the half-angle tangent: F = e^(a x) P(t)/Q(t) with t = tan(x/2).
@@ -26484,6 +26704,105 @@ Entity ToThe(EInteger k)
/// The largest odd degree of a binomial scales by, and the most terms it writes out.
private const int MostOrderScaledToNumbers = 9, MostTermsScaledToNumbers = 6;
+ ///
+ /// A polynomial over a polynomial beside the reciprocal of the cube root of a quadratic or
+ /// a cubic P with a linear factor L, P = L Q, or of its square: R/y
+ /// with R a constant times W/(Q - mu L^2), or R/y^2 with R a
+ /// constant times L W/(L^2 - mu Q), for y = P^(1/3), W = 2 Q L' - L Q' and
+ /// a constant mu. With m^3 = mu and T = m L/y, or T = m y/L, it
+ /// is a constant times T'/(1 - T^3), whose integral is
+ /// -ln(1 - T)/3 + ln(1 + T + T^2)/6 + atan((1 + 2 T)/sqrt(3))/sqrt(3). Welz's
+ /// 1/(x ((x - 1) (q - 2 q x + x^2))^(1/3)) and
+ /// (1 - k x)/((1 - (2 - k) x) ((1 - x) x (1 - k x))^(2/3)).
+ ///
+ ///
+ ///
+ /// For P = L Q, 3 P L' - L P' is L W, so T = m L/y has
+ /// T' = m L W/(3 P y), and 1 - T^3 = L (Q - mu L^2)/P: the quotient is
+ /// m W/(3 (Q - mu L^2) y). And T = m y/L has T' = -m W/(3 L y^2) and
+ /// 1 - T^3 = (L^2 - mu Q)/L^2: the quotient is -m L W/(3 (L^2 - mu Q) y^2). Each
+ /// uses no more about the root than y^3 = P, and about m than m^3 = mu,
+ /// so it holds on every branch of either. 1 - T is the y - m L of Rubi's
+ /// answers over y, and the norm P - mu L^3 = L (Q - mu L^2) carries the poles of
+ /// the integrand. Matching R is a linear system in the constant's reciprocal and
+ /// mu times it, solved for each linear factor of P as written.
+ ///
+ ///
+ /// With numbers in P the ansatz over logarithms of L - y answers these, and is
+ /// asked first; with symbols in it, as in Welz's, it and every other rule declined them. The
+ /// answer is differentiated back at sampled points.
+ /// https://github.com/asc-community/AngouriMath/issues/233
+ ///
+ ///
+ internal static Entity? SolveAPseudoEllipticQuotientBesideACubeRootWithALinearFactor(Entity expr, Entity.Variable x)
+ {
+ var (numerator, denominator) = Functions.SingleQuotient.Of(expr);
+ Entity? radicand = null;
+ var squared = false;
+ Entity below = Number.Integer.One;
+ foreach (var factor in Mulf.LinearChildren(denominator))
+ if (factor is Powf(var @base, Number.Rational power) && power is not Number.Integer && @base.ContainsNode(x))
+ {
+ var (top, bottom) = (power.ERational.Numerator, power.ERational.Denominator);
+ if (radicand is not null || !bottom.Equals(EInteger.FromInt32(3)) || !top.Equals(EInteger.One) && !top.Equals(EInteger.FromInt32(2)))
+ return null;
+ radicand = @base;
+ squared = top.Equals(EInteger.FromInt32(2));
+ }
+ else
+ below = below == Number.Integer.One ? factor : below * factor;
+ // Coefficients by power, each free of x, of a polynomial in x.
+ Dictionary? Coefficients(Entity polynomial)
+ => TreeAnalyzer.TryGetPolynomial(polynomial, x, out var read) && read.All(pair => pair.Key.Sign >= 0 && !pair.Value.ContainsNode(x))
+ ? read : null;
+ if (radicand is null || Coefficients(numerator) is null || Coefficients(below) is null
+ || Coefficients(radicand) is not { Count: > 0 } p || p.Keys.Max()!.ToInt32Unchecked() is not (2 or 3))
+ return null;
+ var factors = Mulf.LinearChildren(radicand).ToList();
+ var third = Number.Rational.Create(1, 3);
+ var y = MathS.Pow(radicand, third);
+ var rootOfThree = MathS.Sqrt(3);
+ for (var i = 0; i < factors.Count; i++)
+ {
+ if (!TreeAnalyzer.TryGetPolyLinear(factors[i], x, out var slope, out var offset) || slope.ContainsNode(x) || offset.ContainsNode(x)
+ || VanishesIdentically(slope))
+ continue;
+ var linear = factors[i];
+ Entity quotient = Number.Integer.One;
+ for (var k = 0; k < factors.Count; k++)
+ if (k != i)
+ quotient = quotient == Number.Integer.One ? factors[k] : quotient * factors[k];
+ var w = 2 * quotient * slope - linear * quotient.Differentiate(x);
+ var linearSquared = MathS.Pow(linear, 2);
+ // W D = (a Q + b L^2) N, or L W D = (a L^2 + b Q) N, for R = N/D.
+ var (first, second, wanted) = squared
+ ? (linearSquared * numerator, quotient * numerator, linear * w * below)
+ : (quotient * numerator, linearSquared * numerator, w * below);
+ if (Coefficients(first) is not { } firstRead || Coefficients(second) is not { } secondRead || Coefficients(wanted) is not { } wantedRead)
+ continue;
+ var powers = firstRead.Keys.Concat(secondRead.Keys).Concat(wantedRead.Keys).Distinct().OrderBy(power => power).ToList();
+ Entity At(Dictionary read, EInteger power)
+ => read.TryGetValue(power, out var value) ? value.Expand().InnerSimplified : Number.Integer.Zero;
+ var matrix = powers.Select(power => new[] { At(firstRead, power), At(secondRead, power) }).ToArray();
+ var rhs = powers.Select(power => At(wantedRead, power)).ToArray();
+ if (!Functions.PartialFractions.TrySolveLinear(matrix, rhs, out var values))
+ continue;
+ var (a, b) = (LowestOverTheSymbols(values[0]), LowestOverTheSymbols(values[1]));
+ if (VanishesIdentically(a) || VanishesIdentically(b))
+ continue;
+ var mu = LowestOverTheSymbols(-b / a);
+ // Any cube root; the real one of a negative number, so that the answer is real where y is.
+ var m = mu.Evaled is Number.Real { IsNegative: true } ? -MathS.Pow(-mu, third) : MathS.Pow(mu, third);
+ var t = squared ? m * y / linear : m * linear / y;
+ // ln((1 - T)^2)/2 for ln(1 - T): the same derivative, and real wherever T is.
+ var closed = (MathS.Ln(1 + t + MathS.Pow(t, 2)) - MathS.Ln(MathS.Pow(1 - t, 2))) / 6 + MathS.Arctan((1 + 2 * t) / rootOfThree) / rootOfThree;
+ var answer = (LowestOverTheSymbols((squared ? -3 : 3) / a) / m * closed).InnerSimplified;
+ if (Functions.PartialFractions.DerivativeHoldsAtSampledPoints(answer, expr, x))
+ return answer;
+ }
+ return null;
+ }
+
///
/// Powers that are not whole of polynomials that are constant multiples of powers of one
/// linear, gathered into one power of it: B^p = (B^p/L^(k p)) L^(k p) for
@@ -37502,6 +37821,92 @@ private static bool NotRealAt(Entity expr, Entity.Variable x, double at)
return table;
}
+ ///
+ /// A rational function of x and R = sqrt(Q) beside a half-odd power of
+ /// L + R, for a quadratic Q and a linear L with Q - L^2 = c (x - r)^2,
+ /// in Euler's u = (R - L)/(x - r): x - r = 2 l u/D, R = L + u (x - r) and
+ /// L + R = 2 c l/D, for l = L(r), D = c - 2 b u - u^2 and b the slope
+ /// of L, so that the integrand is rational in u beside a half-odd power of the
+ /// quadratic D. Bondarenko's sqrt(1 + sqrt(x) + sqrt(1 + 2x + 2 sqrt(x))) is
+ /// 8 sqrt(2) u (1 + u^2)/D^(7/2) under x = t^2.
+ ///
+ ///
+ /// With s = x - r, R^2 = L^2 + c s^2 and R = L + u s give
+ /// s (c - 2 b u - u^2) = 2 l u, (L + R)(R - L) = c s^2 gives L + R = c s/u,
+ /// and dx = 2 l (c + u^2)/D^2 du. The power (L + R)^(k/2) is asked as
+ /// (2 c l)^(k/2)/D^(k/2), which it is up to a sign, and the answer in u is
+ /// multiplied by the quotient of the two, whose square is one: it holds on every branch.
+ /// Declined before, since no substitution any rule tries makes the nested root rational.
+ /// The answer is differentiated back at sampled points.
+ /// https://github.com/asc-community/AngouriMath/issues/233
+ ///
+ internal static Entity? SolveAHalfOddPowerOfALinearPlusTheRootOfAQuadraticByEulersSubstitution(Entity expr, Entity.Variable x, bool integrateByParts)
+ {
+ static bool IsAHalfOddPower(Entity power) => power is Number.Rational rational and not Number.Integer
+ && rational.ERational.Denominator.Equals(EInteger.FromInt32(2)) && rational.ERational.Numerator.CanFitInInt32();
+ var half = Number.Rational.Create(1, 2);
+ // The one half-odd power of a sum that holds a root.
+ Powf? outer = null;
+ foreach (var node in expr.Nodes)
+ if (node is Powf(var sum, var power) found && IsAHalfOddPower(power) && sum is Sumf or Minusf
+ && sum.Nodes.Any(inner => inner is Powf(var b, var p) && IsAHalfOddPower(p) && b.ContainsNode(x)))
+ {
+ if (outer is not null && outer != found)
+ return null;
+ outer = found;
+ }
+ if (outer is null)
+ return null;
+ Entity? quadratic = null;
+ Entity linear = Number.Integer.Zero;
+ foreach (var term in Sumf.LinearChildren(outer.Base))
+ if (term is Powf(var b, var p) && p == half && b.ContainsNode(x) && quadratic is null)
+ quadratic = b;
+ else
+ linear += term;
+ Entity Coefficient(Dictionary read, int power)
+ => read.TryGetValue(EInteger.FromInt32(power), out var value) ? value : Number.Integer.Zero;
+ if (quadratic is null
+ || !TreeAnalyzer.TryGetPolynomial(linear, x, out var l) || l.Any(pair => pair.Key.Sign < 0 || pair.Key.CompareTo(EInteger.One) > 0 || pair.Value.ContainsNode(x))
+ || !TreeAnalyzer.TryGetPolynomial(quadratic, x, out var q) || q.Any(pair => pair.Key.Sign < 0 || pair.Key.CompareTo(EInteger.FromInt32(2)) > 0 || pair.Value.ContainsNode(x)))
+ return null;
+ var (l0, l1) = (Coefficient(l, 0), Coefficient(l, 1));
+ // Q - L^2 = c x^2 + e x + f, a square.
+ var c = LowestOverTheSymbols(Coefficient(q, 2) - l1 * l1);
+ var e = LowestOverTheSymbols(Coefficient(q, 1) - 2 * l0 * l1);
+ var f = LowestOverTheSymbols(Coefficient(q, 0) - l0 * l0);
+ if (VanishesIdentically(c) || !VanishesIdentically(e * e - 4 * c * f))
+ return null;
+ var r = LowestOverTheSymbols(-e / (2 * c));
+ var atR = LowestOverTheSymbols(l0 + l1 * r);
+ if (VanishesIdentically(atR))
+ return null;
+ var u = Variable.CreateUnique(expr, "u_euler");
+ var placeholder = Variable.CreateUnique(expr + u, "S_euler");
+ var d = c - 2 * l1 * u - MathS.Pow(u, 2);
+ var s = 2 * atR * u / d;
+ var rootInU = atR + (l1 + u) * s;
+ // The power first, since a replacement reaches the root inside it before the power.
+ var named = expr.Replace(node => node == outer ? placeholder : node);
+ if (named.ContainsNode(outer.Base))
+ return null;
+ var inU = named.Replace(node => node is Powf(var b, var p) && b == quadratic && IsAHalfOddPower(p)
+ ? MathS.Pow(rootInU, Number.Integer.Create(((Number.Rational)p).ERational.Numerator))
+ : node)
+ .Substitute(x, r + s);
+ if (inU.ContainsNode(x))
+ return null;
+ var exponent = outer.Exponent;
+ Entity Sigma(Entity at) => MathS.Pow(2 * c * atR, exponent) / MathS.Pow(d.Substitute(u, at), exponent);
+ var integrand = Functions.PartialFractions.Bare(inU.Substitute(placeholder, Sigma(u)) * 2 * atR * (c + MathS.Pow(u, 2)) / MathS.Pow(d, 2));
+ if (Integration.ComputeAsAQuestionOfItsOwn(integrand, u, integrateByParts) is not { } inTermsOfU
+ || inTermsOfU.Nodes.Any(node => node is Integralf || node == MathS.NaN))
+ return null;
+ var uOfX = (MathS.Sqrt(quadratic) - linear) / (x - r);
+ var answer = (outer / Sigma(uOfX) * inTermsOfU.Substitute(u, uOfX)).InnerSimplified;
+ return Functions.PartialFractions.DerivativeHoldsAtSampledPoints(answer, expr, x) ? answer : null;
+ }
+
///
/// A fractional power of a quotient whose denominator is positive for every real
/// is written as the power of the numerator over the power of the
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
index 2e33e1ec9..7d4b943fa 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
@@ -1234,6 +1234,10 @@ internal static bool ASumOverRootsWouldAnswer
// And the secant beside powers of a + a sec(y) and c - c sec(y), whose product is a
// square of the tangent: in u = a + a sec(y), a symbolic power of it too.
if ((answer = IndefiniteIntegralSolver.SolveASecantBesidePowersOfItsConjugateSums(expr, x, integrateByParts)) is { }) return answer;
+ // And a function of the roots of sec(y) + 1 and sec(y) - 1, in their difference w, of
+ // which both and the secant are rational functions: Charlwood's
+ // `sqrt(sqrt(1 + sec(x)) - sqrt(sec(x) - 1))`.
+ if ((answer = IndefiniteIntegralSolver.SolveAFunctionOfTheRootsOfTheSecantPlusAndMinusOneInTheirDifference(expr, x, integrateByParts)) is { }) return answer;
// And an odd power of the tangent beside a function of the secant, in u = sec(y).
if ((answer = IndefiniteIntegralSolver.SolveAnOddPowerOfTheTangentBesideAFunctionOfTheSecant(expr, x, integrateByParts)) is { }) return answer;
// And `a ± a cosh(y)` under a fractional power: `2a cosh(y/2)^2`, `-2a sinh(y/2)^2`.
@@ -1536,6 +1540,10 @@ internal static bool ASumOverRootsWouldAnswer
// apart where its residue, the radicand at its root, is zero.
if ((answer = IndefiniteIntegralSolver.SolveByWritingALinearBelowTheBarOverTheRadicand(expr, x, integrateByParts)) is { }) return answer;
if ((answer = IndefiniteIntegralSolver.SolveByEulerSubstitution(expr, x)) is { }) return answer;
+ // And a half-odd power of L + sqrt(Q) where Q - L^2 is a square, in u = (sqrt(Q) - L)/(x - r),
+ // of which x, the inner root and the outer one's square are rational functions:
+ // Bondarenko's `sqrt(1 + sqrt(x) + sqrt(1 + 2x + 2 sqrt(x)))` under x = t^2.
+ if ((answer = IndefiniteIntegralSolver.SolveAHalfOddPowerOfALinearPlusTheRootOfAQuadraticByEulersSubstitution(expr, x, integrateByParts)) is { }) return answer;
// And a root over the root of a quadratic, or over the roots of two linears with one
// slope, in the variable that rationalises the roots inside: `sqrt(1 - x^2 + x sqrt(x^2 - 1))`.
if ((answer = IndefiniteIntegralSolver.SolveANestedRootByRationalisingTheRootsInIt(expr, x, integrateByParts)) is { }) return answer;
@@ -1558,6 +1566,11 @@ internal static bool ASumOverRootsWouldAnswer
// rationalises it, and the elementary ones are logarithms of `L - y` for linear L
// whose cube agrees with the polynomial at the poles, found by an ansatz.
if ((answer = CubeRootLogarithmAnsatz.Integrate(expr, x)) is { }) return answer;
+ // And a quotient beside the cube root of a polynomial with a linear factor L, at the
+ // coefficients where it is a constant times T'/(1 - T^3) for T = m L/y or m y/L, in
+ // closed form: after the ansatz, which answers the numeric ones and declined Welz's,
+ // with symbols in the radicand.
+ if ((answer = IndefiniteIntegralSolver.SolveAPseudoEllipticQuotientBesideACubeRootWithALinearFactor(expr, x)) is { }) return answer;
// And of one square root of a squarefree quadratic, cubic or quartic -- the latter
// two elliptic, the first rational but with residues Euler's substitutions leave in
// a field of degree four: the elementary ones are logarithms and arctangents of
@@ -1571,6 +1584,11 @@ internal static bool ASumOverRootsWouldAnswer
// 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;
+ // A linear over a linear beside the root of a cubic or a quartic, where the integral is a
+ // function of the numerator's square over the root: Welz's
+ // `(1 + x - sqrt(3))/((1 + x + sqrt(3)) sqrt(x^4 + 4 sqrt(3) x^2 - 4))`, whose pole is
+ // irrational. After the ansatz, which answers the ones with rational poles.
+ if ((answer = IndefiniteIntegralSolver.SolveALinearOverALinearBesideTheRootOfAQuarticByTheSquareOfTheNumerator(expr, x)) is { }) return answer;
// And a power of a product with a factor under a whole power, that power taken out in
// front of a constant: `(c sin(a + b x)^3)^(1/3)` is `K sin(a + b x)`.
if ((answer = IndefiniteIntegralSolver.SolveByTakingTheWholePowersOutOfAPowerOfAProduct(expr, x, integrateByParts)) is { }) return answer;
@@ -1600,6 +1618,10 @@ internal static bool ASumOverRootsWouldAnswer
// an ansatz finds it or nothing does. After the substitutions, which answer the
// linear-exponent cases in their own terms.
if ((answer = IndefiniteIntegralSolver.SolveByExponentialAnsatz(expr, x)) is { }) return answer;
+ // And where it declines for a simple pole at a zero of the exponent, the derivative of a
+ // multiple of Ei(h) taken out first: Hebisch's
+ // `(2 + 2x + 3x^2 - x^3 + 2x^4) e^(x/(2 + x^2))/(2x + x^3)`.
+ if ((answer = IndefiniteIntegralSolver.SolveAnExponentialBesideAMultipleOfTheDerivativeOfItsExponentialIntegral(expr, x)) is { }) return answer;
// An exponential of a polynomial in the reciprocal of a linear that is not a Gaussian,
// under the Gaussian's substitution `u = 1/L`. After the ansatz, which answers some of
// these in the base: `f^(a + b/x)/x^4` is `f^(a + b/x)` times a polynomial in `1/x`.
diff --git a/Sources/Tests/UnitTests/Calculus/AFunctionOfTheRootsOfTheSecantPlusAndMinusOneIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/AFunctionOfTheRootsOfTheSecantPlusAndMinusOneIntegralTest.cs
new file mode 100644
index 000000000..ffdaba188
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/AFunctionOfTheRootsOfTheSecantPlusAndMinusOneIntegralTest.cs
@@ -0,0 +1,49 @@
+//
+// 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
+{
+ ///
+ /// A function of sqrt(sec(u) + 1) and sqrt(sec(u) - 1), integrated in their
+ /// difference, of which both roots and the secant are rational functions. Charlwood's was
+ /// declined.
+ /// #233
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class AFunctionOfTheRootsOfTheSecantPlusAndMinusOneIntegralTest
+ {
+ [Theory]
+ [InlineData("sqrt(-sqrt(-1+sec(x))+sqrt(1+sec(x)))")]
+ [InlineData("1/sqrt(sqrt(1+sec(2*x))-sqrt(sec(2*x)-1))")]
+ public void InTheDifferenceOfTheRoots(string integrand)
+ {
+ var integral = integrand.ToEntity().Integrate("x");
+ var text = integral.Stringize();
+ Assert.DoesNotContain("integral(", text);
+ Assert.True(text.Length < 5000, $"{text.Length} characters of answer for {integrand}");
+ var derivative = integral.Substitute("C", 0).Differentiate("x");
+ var original = integrand.ToEntity();
+ var compared = 0;
+ foreach (var at in new[] { -1.2, -0.7, 0.3, 0.8, 1.3, 2.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}, where the integrand is {want}");
+ }
+ Assert.True(compared >= 5, $"only {compared} points could be compared for {integrand}");
+ }
+ }
+}
diff --git a/Sources/Tests/UnitTests/Calculus/AHalfOddPowerOfALinearPlusTheRootOfAQuadraticIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/AHalfOddPowerOfALinearPlusTheRootOfAQuadraticIntegralTest.cs
new file mode 100644
index 000000000..85474e0b1
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/AHalfOddPowerOfALinearPlusTheRootOfAQuadraticIntegralTest.cs
@@ -0,0 +1,51 @@
+//
+// 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
+{
+ ///
+ /// A half-odd power of L + sqrt(Q) where Q - L^2 is a square, in Euler's
+ /// u = (sqrt(Q) - L)/(x - r), under which the nested root is the root of a quadratic in
+ /// u. Bondarenko's two were declined.
+ /// #233
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class AHalfOddPowerOfALinearPlusTheRootOfAQuadraticIntegralTest
+ {
+ [Theory]
+ [InlineData("sqrt(1+sqrt(x)+sqrt(1+2*x+2*sqrt(x)))")]
+ [InlineData("sqrt(sqrt(2)+sqrt(x)+sqrt(2+2*x+sqrt(8)*sqrt(x)))")]
+ [InlineData("x*sqrt(1+x+sqrt(1+2*x+2*x^2))")]
+ [InlineData("1/sqrt(1+x+sqrt(1+2*x+2*x^2))")]
+ public void InEulersSubstitution(string integrand)
+ {
+ var integral = integrand.ToEntity().Integrate("x");
+ var text = integral.Stringize();
+ Assert.DoesNotContain("integral(", text);
+ Assert.True(text.Length < 5000, $"{text.Length} characters of answer for {integrand}");
+ var derivative = integral.Substitute("C", 0).Differentiate("x");
+ var original = integrand.ToEntity();
+ var compared = 0;
+ foreach (var at in new[] { -1.2, -0.7, 0.3, 0.8, 1.3, 2.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}, where the integrand is {want}");
+ }
+ Assert.True(compared >= 5, $"only {compared} points could be compared for {integrand}");
+ }
+ }
+}
diff --git a/Sources/Tests/UnitTests/Calculus/ALinearOverALinearBesideTheRootOfAQuarticByTheSquareOfTheNumeratorIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/ALinearOverALinearBesideTheRootOfAQuarticByTheSquareOfTheNumeratorIntegralTest.cs
new file mode 100644
index 000000000..418f73563
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/ALinearOverALinearBesideTheRootOfAQuarticByTheSquareOfTheNumeratorIntegralTest.cs
@@ -0,0 +1,51 @@
+//
+// 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
+{
+ ///
+ /// A linear over a linear beside the root of a quartic whose integral is an inverse hyperbolic
+ /// tangent, or an arctangent, of the numerator's square over the root. Welz's two and Rubi's
+ /// four in 1.2.2.3, whose poles are irrational, were declined.
+ /// #233
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class ALinearOverALinearBesideTheRootOfAQuarticByTheSquareOfTheNumeratorIntegralTest
+ {
+ [Theory]
+ [InlineData("(1+x-sqrt(3))/((1+x+sqrt(3))*sqrt(-4+x^4+4*x^2*sqrt(3)))")]
+ [InlineData("(1+x+sqrt(3))/((1+x-sqrt(3))*sqrt(-4+x^4-4*x^2*sqrt(3)))")]
+ [InlineData("(1+2*x-sqrt(3))/((1+2*x+sqrt(3))*sqrt(-1+4*x^4+4*x^2*sqrt(3)))")]
+ [InlineData("(1+2*x+sqrt(3))/((1+2*x-sqrt(3))*sqrt(-1+4*x^4-4*x^2*sqrt(3)))")]
+ public void AsAFunctionOfTheSquareOfTheNumeratorOverTheRoot(string integrand)
+ {
+ var integral = integrand.ToEntity().Integrate("x");
+ var text = integral.Stringize();
+ Assert.DoesNotContain("integral(", text);
+ Assert.True(text.Length < 5000, $"{text.Length} characters of answer for {integrand}");
+ var derivative = integral.Substitute("C", 0).Differentiate("x");
+ var original = integrand.ToEntity();
+ var compared = 0;
+ foreach (var at in new[] { -3.4, -1.2, -0.7, 0.3, 0.8, 1.3, 2.9, 3.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}, where the integrand is {want}");
+ }
+ Assert.True(compared >= 5, $"only {compared} points could be compared for {integrand}");
+ }
+ }
+}
diff --git a/Sources/Tests/UnitTests/Calculus/APseudoEllipticQuotientBesideACubeRootWithALinearFactorIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/APseudoEllipticQuotientBesideACubeRootWithALinearFactorIntegralTest.cs
new file mode 100644
index 000000000..cccc1af26
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/APseudoEllipticQuotientBesideACubeRootWithALinearFactorIntegralTest.cs
@@ -0,0 +1,51 @@
+//
+// 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
+{
+ ///
+ /// A quotient beside the cube root of a polynomial with a linear factor, or of its square,
+ /// that is a constant times T'/(1 - T^3) for T = m L/y or m y/L. With
+ /// symbols in the radicand, Welz's three were declined.
+ /// #233
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class APseudoEllipticQuotientBesideACubeRootWithALinearFactorIntegralTest
+ {
+ [Theory]
+ [InlineData("1/(x*((-1+x)*(q-2*q*x+x^2))^(1/3))")]
+ [InlineData("(2-(1+k)*x)/(((1-x)*x*(1-k*x))^(1/3)*(1-(1+k)*x))")]
+ [InlineData("(1-k*x)/((1+(-2+k)*x)*((1-x)*x*(1-k*x))^(2/3))")]
+ public void InTheCubeRootOverALinearFactor(string integrand)
+ {
+ var integral = integrand.ToEntity().Integrate("x");
+ var text = integral.Stringize();
+ Assert.DoesNotContain("integral(", text);
+ Assert.True(text.Length < 5000, $"{text.Length} characters of answer for {integrand}");
+ Entity Pinned(Entity e) => e.Substitute("q", 1.3).Substitute("k", 0.4);
+ var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
+ var original = Pinned(integrand.ToEntity());
+ var compared = 0;
+ foreach (var at in new[] { -1.2, -0.7, 0.3, 0.8, 1.3, 2.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}, where the integrand is {want}");
+ }
+ Assert.True(compared >= 5, $"only {compared} points could be compared for {integrand}");
+ }
+ }
+}
diff --git a/Sources/Tests/UnitTests/Calculus/AnExponentialBesideTheDerivativeOfItsExponentialIntegralIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/AnExponentialBesideTheDerivativeOfItsExponentialIntegralIntegralTest.cs
new file mode 100644
index 000000000..dfc6cf212
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/AnExponentialBesideTheDerivativeOfItsExponentialIntegralIntegralTest.cs
@@ -0,0 +1,50 @@
+//
+// 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
+{
+ ///
+ /// An exponential of a rational function beside a rational function that is the derivative of
+ /// e^h N/D and of a multiple of Ei(h). The ansatz for e^h N/D alone
+ /// declined Hebisch's, the residue at the zero of h being the exponential integral's.
+ /// #233
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class AnExponentialBesideTheDerivativeOfItsExponentialIntegralIntegralTest
+ {
+ [Theory]
+ [InlineData("(2+2*x+3*x^2-x^3+2*x^4)*e^(x/(2+x^2))/(2*x+x^3)")]
+ [InlineData("(a+x+(2-a)*x^2-x^3+2*x^4)*e^(x/(1+x^2))/(x+x^3)")]
+ public void WithTheExponentialIntegralTakenOut(string integrand)
+ {
+ var integral = integrand.ToEntity().Integrate("x");
+ var text = integral.Stringize();
+ Assert.DoesNotContain("integral(", text);
+ Assert.True(text.Length < 5000, $"{text.Length} characters of answer for {integrand}");
+ Entity Pinned(Entity e) => e.Substitute("a", 1.3);
+ var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
+ var original = Pinned(integrand.ToEntity());
+ var compared = 0;
+ foreach (var at in new[] { -1.2, -0.7, 0.3, 0.8, 1.3, 2.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}, where the integrand is {want}");
+ }
+ Assert.True(compared >= 5, $"only {compared} points could be compared for {integrand}");
+ }
+ }
+}
diff --git a/Sources/Tests/UnitTests/Calculus/CubeRootLogarithmAnsatzTest.cs b/Sources/Tests/UnitTests/Calculus/CubeRootLogarithmAnsatzTest.cs
index 90bbca09a..3635c9234 100644
--- a/Sources/Tests/UnitTests/Calculus/CubeRootLogarithmAnsatzTest.cs
+++ b/Sources/Tests/UnitTests/Calculus/CubeRootLogarithmAnsatzTest.cs
@@ -106,12 +106,19 @@ private static void DifferentiatesBack(string integrand)
public void WithASymbolInTheRadicand(string integrand) => DifferentiatesBack(integrand);
///
- /// Declined, quickly, rather than answered wrongly: a constant q^(1/3) the field
- /// does not hold, and a pole pair over Q(sqrt(3)) whose answer needs lines with
- /// coefficients there.
+ /// A constant q^(1/3) the ansatz's field does not hold, which the ansatz declines and
+ /// the rule for T'/(1 - T^3) answers, T the root over the linear factor
+ /// -1 + x times q^(1/3).
///
[Theory]
[InlineData("1/(x*((-1 + x)*(q - 2*q*x + x^2))^(1/3))")]
+ public void WhereTheFieldIsNotHeldButTheRootOverALinearFactorIs(string integrand) => DifferentiatesBack(integrand);
+
+ ///
+ /// Declined, quickly, rather than answered wrongly: a pole pair over Q(sqrt(3))
+ /// whose answer needs lines with coefficients there.
+ ///
+ [Theory]
[InlineData("1/((3 - x^2)*(2 + x^2)^(1/3))")]
public void DeclinedWhereTheFieldIsNotHeld(string integrand)
=> Assert.Contains("integral(", integrand.ToEntity().Integrate("x").Stringize());