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}"); + } + } + } +}