diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index 590cd4f7e..7b1c16722 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -1425,6 +1425,25 @@ with `K = sqrt(L1) sqrt(L2)/sqrt(M)` in front of the answer, constant wherever i Rubi's 7.1.4 and 7.1.5, all 376 problems that count: 308 to 345, no row lost, 56 timeouts to 27. +### The radicand an inverse sine or cosine holds is read as one + +`(d + c d x)^(1/2) (f - c f x)^(3/2) (a + b arcsin(c x))` ran out of time. `arcsin(u)` holds a +radicand without writing it -- its derivative is `u'/sqrt(1 - u^2)` -- so beside `arcsin(c x)` +the factors `d + c d x` and `f - c f x`, whose product is `d f (1 - c^2 x^2)`, are the case of the +two entries above with `1 - c^2 x^2` for the radicand. That radicand is now read from an inverse +sine or cosine of a linear, and two factors whose product is the radicand itself -- `sqrt(1 + c x) +sqrt(1 - c x)` beside `arccos(c x)` -- are written as its one root too. A single base that is a +multiple of it, `(d - c^2 d x^2)^(3/2)`, is written over it the same way, with +`K = sqrt(d - c^2 d x^2)/sqrt(1 - c^2 x^2)` in front, which is `sqrt(d)` for a positive `d`. + +| Input | Was (2.5.0) | Now | +|---|---|---| +| `"(d+c*d*x)^(1/2)*(f-c*f*x)^(3/2)*(a+b*asin(c*x))".Integrate("x")` | left unevaluated | in `arcsin(c x)` and `1 - c^2 x^2`, with `K` in front | +| `"(d-c^2*d*x^2)^(3/2)*(a+b*asin(c*x))".Integrate("x")` | left unevaluated | the same | + +Rubi's 5.1.4, 5.1.5, 5.2.4 and 5.2.5, all 504 problems that count: 405 to 463, no row lost, 77 +timeouts to 19. + ### `binomial(n, k)` is a function **Addition, not silent.** The binomial coefficient is a node, `Entity.Binomialf`, spelled diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 3a7bfbbec..9dd36ac1a 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -14992,6 +14992,17 @@ static bool Through(Entity node, Entity.Variable x) && radicand.ContainsNode(x) && TreeAnalyzer.TryGetPolynomial(radicand, x, out var read) && read.Keys.Max() >= EInteger.One && !references.Contains(radicand)) references.Add(radicand); + // And the radicand an inverse sine or cosine of a linear holds without writing it: + // `arcsin(u)'` is `u'/sqrt(1 - u^2)`, so `(d + c d x)^(1/2) (f - c f x)^(3/2)` beside + // `arcsin(c x)` is the same case as beside `arcosh`, with `1 - c^2 x^2` for the radicand. + foreach (var node in expr.Nodes) + if (node switch { Arcsinf(var inner) => inner, Arccosf(var inner) => inner, _ => null } is { } argument + && argument.ContainsNode(x) && TreeAnalyzer.TryGetPolyLinear(argument, x, out _, out _)) + { + var radicand = (1 - MathS.Sqr(argument)).Expand().InnerSimplified; + if (!references.Contains(radicand)) + references.Add(radicand); + } if (references.Count == 0) return null; var multiple = Variable.CreateUnique(expr, "k_rad"); @@ -15078,7 +15089,10 @@ private static bool TryWriteAPairOfLinearsOverARadicand(Entity expr, Entity.Vari var product = (first * second).Expand(); foreach (var candidate in references) { - if (TryReadAsAConstantMultiple(product, candidate, x) is not { } lambda) + // A multiple of one counts here, where it does not for a single base: two + // factors whose product *is* the radicand -- `sqrt(1 + c x) sqrt(1 - c x)` + // beside `arcsin(c x)` -- still want writing as its one root. + if (TryReadAsAConstantMultiple(product, candidate, x, allowOne: true) is not { } lambda) continue; Entity together; if (half is Number.Integer) @@ -15149,7 +15163,7 @@ private static bool HasTheSameRatioAtTwoPoints(Entity expr, Entity reference, En /// polynomials in of the same degree: the ratio of the leading /// coefficients, where every other coefficient agrees with it once simplified. /// - private static Entity? TryReadAsAConstantMultiple(Entity expr, Entity reference, Entity.Variable x) + private static Entity? TryReadAsAConstantMultiple(Entity expr, Entity reference, Entity.Variable x, bool allowOne = false) { // Read as written, or simplified first: the normalisation makes a power of a // polynomial with a symbolic leading coefficient monic and writes the constant term as @@ -15178,7 +15192,7 @@ private static bool HasTheSameRatioAtTwoPoints(Entity expr, Entity reference, En var lambda = (leading / theirs[top]).InnerSimplified; if (lambda.Vars.Any()) lambda = Functions.PartialFractions.Bare(lambda.Simplify()); - if (lambda.Evaled is Number.Complex { IsZero: true } || lambda == Number.Integer.One) + if (lambda.Evaled is Number.Complex { IsZero: true } || lambda == Number.Integer.One && !allowOne) return null; foreach (var pair in theirs) { diff --git a/Sources/Tests/UnitTests/Calculus/InverseTrigonometricSubstitutionTest.cs b/Sources/Tests/UnitTests/Calculus/InverseTrigonometricSubstitutionTest.cs index c9a11c82f..6b990befe 100644 --- a/Sources/Tests/UnitTests/Calculus/InverseTrigonometricSubstitutionTest.cs +++ b/Sources/Tests/UnitTests/Calculus/InverseTrigonometricSubstitutionTest.cs @@ -80,6 +80,22 @@ private static void DifferentiatesBack(string integrand, double[] points) [InlineData("x^3*arcsin(x)/(1 - x^2)^(3/2)")] public void TheSineAndTheCosine(string integrand) => DifferentiatesBack(integrand, InsideTheUnitInterval); + /// + /// The radicand an inverse sine or cosine holds without writing it: arcsin(u)' is + /// u'/sqrt(1 - u^2), so beside arcsin(2x) the factors (3 + 6x)(5 - 10x), + /// whose product is 15 (1 - 4x^2), are that radicand written as two, and + /// (5 - 20x^2)^(3/2) a multiple of it written as one. Rubi's 5.1.4 and 5.2.4; the + /// points are inside |2x| < 1, where every integrand here is real. + /// + [Theory] + [InlineData("sqrt(3 + 6*x)*(5 - 10*x)^(3/2)*(1 + arcsin(2*x))")] + [InlineData("x*(1 + arcsin(2*x))^2/(sqrt(3 + 6*x)*sqrt(5 - 10*x))")] + [InlineData("(3 + 6*x)^(3/2)*(1 + arcsin(2*x))/(5 - 10*x)^(3/2)")] + [InlineData("(5 - 20*x^2)^(3/2)*(1 + arcsin(2*x))")] + [InlineData("sqrt(1 + 2*x)*sqrt(1 - 2*x)*(1 + arccos(2*x))")] + public void TheRadicandOfAnInverseSineOrCosine(string integrand) + => DifferentiatesBack(integrand, new[] { -0.4, -0.1, 0.2, 0.35, 0.45 }); + /// /// The tangent, whose radical is sqrt(1 + x^2) and becomes the secant. ///