From 0c253baec9eef7e2bccec2de8c3c129c78bac0a4 Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Mon, 12 Oct 2026 02:39:45 +0000 Subject: [PATCH 1/2] A secant or a cosecant beside a root is read as the cosine or the sine the other way up Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- .../Integration/IndefiniteIntegralSolver.cs | 21 +++++++- ...ecantOrACosecantBesideARootIntegralTest.cs | 48 +++++++++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 Sources/Tests/UnitTests/Calculus/ASecantOrACosecantBesideARootIntegralTest.cs diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 5f5414950..2f13f7a0e 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -11134,6 +11134,11 @@ static bool IsTwice(Entity argument, Entity.Variable x) Secantf(var a) when a == x => sign * rootOfSecantSquared, Powf(Secantf(var a), Number.Integer k) when a == x && k.EInteger.CanFitInInt32() && !k.EInteger.IsEven => MathS.Pow(sign * rootOfSecantSquared, k), + // And the cosecant the sine: `sqrt(1 + tan(x)) csc(x) sec(x)` is + // `sqrt(1 + u) (1 + u^2)/u`, the two signs one. + Cosecantf(var a) when a == x => sign * rootOfSecantSquared / tangent, + Powf(Cosecantf(var a), Number.Integer k) when a == x && k.EInteger.CanFitInInt32() && !k.EInteger.IsEven + => MathS.Pow(sign * rootOfSecantSquared / tangent, k), _ => node, }).Substitute(tangent, uSub); if (odd.ContainsNode(x) || !IsAlgebraicIn(odd, uSub)) @@ -14090,8 +14095,20 @@ static bool More(Entity difference) var raisable = new List<(Entity Base, Entity Exponent)>(); var logarithmicDerivativesKept = new List(); Entity kept = Number.Integer.One; - foreach (var (factor, underneath) in FactorsOfTheIntegrand(expr)) - { + foreach (var (written, writtenUnderneath) in FactorsOfTheIntegrand(expr)) + { + // A secant is a power of the cosine, and a cosecant of the sine, below the bar, + // where raising it by one takes it out of the candidate: `sec(x) sqrt(sec(x) + tan(x))` + // is `(2 sqrt(sec(x) + tan(x)))'`, and read as a power of the secant it could + // only be raised to a square. + var (factor, underneath) = written switch + { + Secantf(var a) => (MathS.Cos(a), !writtenUnderneath), + Cosecantf(var a) => (MathS.Sin(a), !writtenUnderneath), + Powf(Secantf(var a), var p) when !p.ContainsNode(x) => (MathS.Pow(MathS.Cos(a), p), !writtenUnderneath), + Powf(Cosecantf(var a), var p) when !p.ContainsNode(x) => (MathS.Pow(MathS.Sin(a), p), !writtenUnderneath), + _ => (written, writtenUnderneath) + }; if (!factor.ContainsNode(x)) { constant = underneath ? constant / factor : constant * factor; diff --git a/Sources/Tests/UnitTests/Calculus/ASecantOrACosecantBesideARootIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/ASecantOrACosecantBesideARootIntegralTest.cs new file mode 100644 index 000000000..22ee4d85b --- /dev/null +++ b/Sources/Tests/UnitTests/Calculus/ASecantOrACosecantBesideARootIntegralTest.cs @@ -0,0 +1,48 @@ +// +// 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 secant or a cosecant beside a root, read as the cosine or the sine the other way up: + /// sec(x) sqrt(sec(x) + tan(x)) is (2 sqrt(sec(x) + tan(x)))', and + /// sqrt(1 + tan(x)) csc(x) sec(x) is a function of the tangent. Rubi's 4.7.7. + /// #718 + /// + [Trait("Area", "Calculus")] + public sealed class ASecantOrACosecantBesideARootIntegralTest + { + [Theory] + [InlineData("sec(x)*sqrt(sec(x) + tan(x))", 100)] + [InlineData("sec(x)*sqrt(sec(x) - tan(x))", 100)] + [InlineData("csc(x)*sqrt(csc(x) + cot(x))", 100)] + [InlineData("sec(a + b*x)*(sec(a + b*x) + tan(a + b*x))^c", 200)] + [InlineData("sqrt(1 + tan(x))*csc(x)*sec(x)", 400)] + public void DifferentiatesBack(string integrand, int atMost) + { + var integral = integrand.ToEntity().Integrate("x"); + var text = integral.Stringize(); + Assert.DoesNotContain("integral(", text); + Assert.True(text.Length < atMost, $"{text.Length} characters of answer for {integrand}"); + Entity Pinned(Entity e) => e.Substitute("a", 2).Substitute("b", 3).Substitute("c", "5/4".ToEntity()); + var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x"); + var original = Pinned(integrand.ToEntity()); + foreach (var at in new[] { "0.35", "0.6", "1.1", "1.4" }) + { + var want = original.Substitute("x", at.ToEntity()).EvalNumerical(); + var got = derivative.Substitute("x", at.ToEntity()).EvalNumerical(); + Assert.True(Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart) + < 1e-9 * Math.Max(1, Math.Abs((double)want.RealPart)), + $"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}"); + } + } + } +} From fe1db03467e405cf3bf364783f6d7b40e321a9e1 Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Mon, 12 Oct 2026 06:36:48 +0000 Subject: [PATCH 2/2] Record the secant and cosecant read as powers of the cosine and the sine Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- BREAKING-CHANGES.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index 32cb2980c..e392225da 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -519,6 +519,22 @@ left ([#718](https://github.com/asc-community/AngouriMath/issues/718)). | `"1/(a + b*cot(x) + c*csc(x))".ToEntity().Integrate("x")` | `integral(...)` | 2,738 characters | | `"1/(a + c*sec(2*x + 1) + b*tan(2*x + 1))".ToEntity().Integrate("x")` | `integral(...)`; past 290 s on the unreleased master | 2,890 characters | +### A secant or a cosecant beside a power of a sum of them is read as a power of the cosine or the sine + +**Answers where there were none.** `sec(x) sqrt(sec(x) + tan(x))` was declined, with the rest of Rubi's 4.7.7 +that puts a secant beside a power of `sec + tan`: the rule that recognises a product of powers as a derivative +read `sec(x)` as a function of its own, not as `cos(x)^(-1)`, and the tangent substitution wrote an odd power of +the secant through the root of `1 + t^2` but not one of the cosecant. Both are read so now, and the integrand is +the derivative of a power of the sum +([#718](https://github.com/asc-community/AngouriMath/issues/718)). + +| Input | Was (2.5.0) | Now | +|---|---|---| +| `"sec(x)*sqrt(sec(x) + tan(x))".ToEntity().Integrate("x")` | `integral(...)` | `2 * sqrt(sec(x) + tan(x))` | +| `"csc(x)*sqrt(csc(x) + cot(x))".ToEntity().Integrate("x")` | `integral(...)` | `(-2) * sqrt(csc(x) + cotan(x))` | +| `"sec(a + b*x)*(sec(a + b*x) + tan(a + b*x))^c".ToEntity().Integrate("x")` | `integral(...)` | `1 / (b * c) * (sec(a + b * x) + tan(a + b * x)) ^ c` | +| `"sqrt(1 + tan(x))*csc(x)*sec(x)".ToEntity().Integrate("x")` | `integral(...)` | 94 characters | + ### `a cos + i a sin` beside powers of the secant is written over the cosine **Answers where there were none, and shorter ones.** `sec(c + d x)^5/(a cos(c + d x) + i a sin(c + d x))^2` was declined,