diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 18bcbfe71..8811b1000 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -21546,6 +21546,26 @@ private static bool IsProperByDegree(Entity numerator, Entity denominator, Entit && TreeAnalyzer.TryGetPolynomial(denominator, x, out var below) && below.Count > 0 && above.Keys.Max()!.CompareTo(below.Keys.Max()!) < 0; + /// Whether stands in only under even whole powers. + private static bool IsEvenAsWritten(Entity expr, Entity.Variable x) + => expr.Nodes.Count(node => node == x) + == expr.Nodes.Count(node => node is Powf(var @base, Number.Integer power) && @base == x && power.EInteger.IsEven); + + /// + /// The degree in of a polynomial as it is written, without expanding + /// it: of a power its base's times the exponent, of a product the sum, of a sum the most. + /// + private static int DegreeAsWritten(Entity expr, Entity.Variable x) => expr switch + { + _ when !expr.ContainsNode(x) => 0, + Variable => 1, + Powf(var @base, Number.Integer power) when power.EInteger.CanFitInInt32() => DegreeAsWritten(@base, x) * power.EInteger.ToInt32Checked(), + Mulf(var left, var right) => DegreeAsWritten(left, x) + DegreeAsWritten(right, x), + Sumf(var left, var right) => System.Math.Max(DegreeAsWritten(left, x), DegreeAsWritten(right, x)), + Minusf(var left, var right) => System.Math.Max(DegreeAsWritten(left, x), DegreeAsWritten(right, x)), + _ => 0, + }; + /// Whether every node of holding is a sum, a product, a quotient or a whole power. private static bool IsARationalFunction(Entity expr, Entity.Variable x) => expr.Nodes.All(node => !node.ContainsNode(x) || node is Variable or Sumf or Minusf or Mulf or Divf || node is Powf(_, Number.Integer)); @@ -28928,6 +28948,15 @@ private static bool IsAFunctionOfTheTangentAlone(Entity expr, Entity.Variable u) // page of piecewise on the discriminant of the quadratic the other two make. if (IsARationalFunction(expr, x) && TryReadAsQuotient(expr, out _, out var writtenBelow) && IsAProductOfSymbolicLinearFactors(writtenBelow, x)) return null; + // And an even one with symbols in it, past a sextic below the bar: the derivative of + // anything even is odd, so no polynomial candidate is the substitution for it, and + // the search spent thirty seconds simplifying the quotient by each to decline them all + // on `(1 + x^2)^2/((c + d + (c + 3d) x^2 + 2d x^4)^3 (1 + 2x^2))`, which the half-angle + // tangent makes of `1/((c + d sec(y))^3 sqrt(a + a sec(y)))`, where the partial + // fractions answer it in under one. + if (IsARationalFunction(expr, x) && IsEvenAsWritten(expr, x) && expr.Nodes.Any(node => node is Variable symbol && symbol != x) + && TryReadAsQuotient(expr, out _, out var evenBelow) && DegreeAsWritten(evenBelow, x) >= 6) + return null; // A rational function of exponentials of linears in x with a whole power of a // sum of them in it is the exponential substitution's, exactly and at once, and // this search is not the tool for it: `tanh(x)^5/sech(x)^4` arrives as a fifth diff --git a/Sources/Tests/UnitTests/Calculus/AnEvenRationalFunctionIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/AnEvenRationalFunctionIntegralTest.cs new file mode 100644 index 000000000..0de10dc86 --- /dev/null +++ b/Sources/Tests/UnitTests/Calculus/AnEvenRationalFunctionIntegralTest.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 even rational function with symbols in it and a denominator past a sextic, by the + /// partial fractions without the substitution search in front of them. Rubi's 1.2.2.6, and + /// what the half-angle tangent makes of 4.5.2.1's. + /// #718 + /// + [Trait("Area", "Calculus")] + public sealed class AnEvenRationalFunctionIntegralTest + { + [Theory] + [InlineData("(1 + x^2)^2/((c + d + (c + 3*d)*x^2 + 2*d*x^4)^3*(1 + 2*x^2))")] + [InlineData("(d + g*x^2 + f*x^4)/(x^4*(a + b*x^2 + c*x^4)^2)")] + public void ByThePartialFractions(string integrand) + { + var integral = integrand.ToEntity().Integrate("x"); + var text = integral.Stringize(); + Assert.DoesNotContain("integral(", text); + Assert.True(text.Length < 40000, $"{text.Length} characters of answer for {integrand}"); + Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("b", 0.4).Substitute("c", 0.9).Substitute("d", 0.6).Substitute("f", 0.7).Substitute("g", 0.2); + var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x"); + var original = Pinned(integrand.ToEntity()); + var compared = 0; + foreach (var at in new[] { -2.1, -0.4, 0.3, 0.6, 1.6, 2.5 }) + { + 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}"); + } + } +}