diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index 3adcb9293..dc7f4c50c 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -554,6 +554,23 @@ without it ([#233](https://github.com/asc-community/AngouriMath/issues/233)).
|---|---|---|
| `"x/(acosh(x)*sqrt(-1+x)*sqrt(1+x))".ToEntity().Integrate("x")` | `integral(...)` | 93 characters, `K Chi(acosh(x))` |
+### An inverse sine of a sine of a linear is integrated as linear on each interval
+
+**Answers where there were none, and one that was wrong.** Rubi's 5.1.5 `x/asin(sin(x))` was declined, and so was
+`asin(sin(x))/x`. 2.5.0 integrated `1/asin(sin(x))` to `ln(asin(sin(x)))`, which is wrong wherever `cos(x) < 0`, and master
+declined it before this change. On each interval where `cos(x)` keeps its sign, `asin(sin(x))` is `s x + c`, with
+`s = cos(x)/sqrt(cos(x)^2)` and `c` constant there, and an inverse cosine of a cosine is the same with the sign of the
+sine. Named so, the integrand is a rational function of x. The answer is the generic case's on every interval, as for a
+logarithm whose derivative is a constant. Asked last, so a polynomial beside the inverse sine keeps the answer parts
+gives it ([#718](https://github.com/asc-community/AngouriMath/issues/718)).
+
+| Input | Was (2.5.0) | Now |
+|---|---|---|
+| `"x/asin(sin(x))".ToEntity().Integrate("x")` | `integral(...)` | 111 characters |
+| `"1/asin(sin(x))".ToEntity().Integrate("x")` | `ln(arcsin(sin(x)))`, wrong past `x = pi/2` | 50 characters, `ln(arcsin(sin(x))) cos(x)/sqrt(cos(x)^2)` |
+| `"asin(sin(x))/x".ToEntity().Integrate("x")` | `integral(...)` | 222 characters |
+| `"x/acos(cos(a+b*x))".ToEntity().Integrate("x")` | `integral(...)` | 171 characters |
+
### An arcoth of a linear is integrated as its artanh plus a constant
**Answers where there were none.** `acoth(x)^2/(1 - x^2)^2`, Rubi's 7.4.1, was declined, where
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
index a70cb8786..20d104314 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
@@ -29705,6 +29705,64 @@ node is Powf(var @base, var exponent) && @base == MathS.e && powers.TryGetValue(
return MathS.Piecewise(new[] { new Providedf(whereZero, intercept.EqualTo(Number.Integer.Zero)) }, generic);
}
+ ///
+ /// An inverse sine of a sine of a linear L = a + k x is s k x + c on each interval
+ /// where cos(L) keeps its sign, with s = cos(L)/sqrt(cos(L)^2) that sign and c
+ /// constant there, and an inverse cosine of a cosine the same with the sign of sin(L):
+ /// the derivative of asin(sin(L)) is k cos(L)/sqrt(1 - sin(L)^2). Named so, with
+ /// s and c standing for those, x/asin(sin(x)) is the rational function
+ /// x/(s x + c), which the rules answer, where the inverse sine was an atom no rule read past
+ /// a polynomial beside it. The answer is the generic case's on every interval, as
+ /// 's is, and a whole power of s in
+ /// it is 1 or s. Real only: off the real line the sign is not constant
+ /// anywhere. Rubi's 5.1.5. Asked as a question of its own.
+ /// https://github.com/asc-community/AngouriMath/issues/718
+ ///
+ internal static Entity? SolveByNamingAnInverseSineOfASineLinearOnEachInterval(Entity expr, Entity.Variable x, bool integrateByParts)
+ {
+ Entity? named = null;
+ Entity? sign = null;
+ Entity? slope = null;
+ foreach (var node in expr.Nodes)
+ {
+ (Entity? linear, Entity? signOf) = node switch
+ {
+ Arcsinf(Sinf(var inner)) => (inner, (Entity)MathS.Cos(inner)),
+ Arccosf(Cosf(var inner)) => (inner, MathS.Sin(inner)),
+ _ => (null, null)
+ };
+ if (linear is null || signOf is null || !linear.ContainsNode(x))
+ continue;
+ var rate = linear.Differentiate(x).InnerSimplified;
+ if (rate.ContainsNode(x) || rate is Derivativef || rate == Number.Integer.Zero)
+ continue;
+ (named, sign, slope) = (node, signOf / MathS.Sqrt(MathS.Pow(signOf, 2)), rate);
+ break;
+ }
+ if (named is null || sign is null || slope is null)
+ return null;
+ var s = Variable.CreateUnique(expr, "s");
+ var constant = Variable.CreateUnique(expr + s, "c");
+ var rewritten = expr.Substitute(named, s * slope * x + constant);
+ if (rewritten == expr || rewritten.ContainsNode(named))
+ return null;
+ if (Integration.ComputeAsAQuestionOfItsOwn(rewritten, x, integrateByParts) is not { } answer)
+ return null;
+ // s is 1 or -1, so a whole power of it is 1 or s, and dividing by it is multiplying by it.
+ Entity Folded(Entity e) => e.Replace(node => node switch
+ {
+ Powf(var @base, Number.Integer power) when @base == s => power.EInteger.IsEven ? Number.Integer.One : s,
+ Divf(var above, var below) when below == s => above * s,
+ _ => node
+ }).InnerSimplified;
+ // And a sum that is the name, `s x + c` in a logarithm, is the inverse sine again, where written
+ // back term by term it was `s x + (asin(sin(x)) - s x)`.
+ var linearInX = s * slope * x + constant;
+ var written = Folded(answer).Replace(node => node is Sumf or Minusf && node.ContainsNode(constant)
+ && Functions.PartialFractions.InLowestTermsOverTheSymbols(node - linearInX) == Number.Integer.Zero ? named : node);
+ return Folded(written.Substitute(constant, named - s * slope * x)).Substitute(s, sign);
+ }
+
///
/// The sign of a real-valued function of among the factors of the
/// integrand is constant on every interval between its zeros, and goes in front of the
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
index b2acbfc9d..2568ab2cd 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
@@ -1789,6 +1789,10 @@ internal static bool ASumOverRootsWouldAnswer
// `(atanh(x) + K)^2/(1 - x^2)^2` with `K = acoth(x) - atanh(x)`, constant where it is
// continuous, which the substitution under the hyperbolic tangent answers.
if ((answer = IndefiniteIntegralSolver.SolveByWritingAnArcothAsAnArtanhPlusAConstant(expr, x, integrateByParts)) is { }) return answer;
+ // And an inverse sine of a sine of a linear, which is linear on each interval its cosine keeps
+ // a sign on. Last, so that every one answered as written keeps that answer: parts answers a
+ // polynomial beside it.
+ if ((answer = IndefiniteIntegralSolver.SolveByNamingAnInverseSineOfASineLinearOnEachInterval(expr, x, integrateByParts)) is { }) return answer;
return null;
}
///
diff --git a/Sources/Tests/UnitTests/Calculus/AnInverseSineOfASineLinearOnEachIntervalIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/AnInverseSineOfASineLinearOnEachIntervalIntegralTest.cs
new file mode 100644
index 000000000..e31f3173b
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/AnInverseSineOfASineLinearOnEachIntervalIntegralTest.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
+{
+ ///
+ /// An inverse sine of a sine of a linear, or an inverse cosine of a cosine, is linear on each interval
+ /// where the cosine, or the sine, keeps its sign: asin(sin(x)) is s x + c there, with
+ /// s = cos(x)/sqrt(cos(x)^2). Rubi's 5.1.5 x/asin(sin(x)) was declined, and is
+ /// asin(sin(x)) + (s x - asin(sin(x))) ln(asin(sin(x))) up to a constant on each interval.
+ /// Checked on both sides of a zero of the cosine, and of the sine.
+ /// #718
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class AnInverseSineOfASineLinearOnEachIntervalIntegralTest
+ {
+ [Theory]
+ // Rubi 5.1.5:619.
+ [InlineData("x/asin(sin(x))")]
+ [InlineData("1/asin(sin(x))")]
+ [InlineData("asin(sin(x))/x")]
+ [InlineData("x/acos(cos(a + b*x))")]
+ public void IsAnsweredOnEveryInterval(string integrand)
+ {
+ var integral = integrand.ToEntity().Integrate("x");
+ var text = integral.Stringize();
+ Assert.DoesNotContain("integral(", text);
+ Entity Pinned(Entity e) => e.Substitute("a", 0.4).Substitute("b", 0.7);
+ var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
+ var original = Pinned(integrand.ToEntity());
+ // Past pi/2 and past -pi/2 the cosine of x changes sign, and past 3.9 the sine of 0.4 + 0.7 x.
+ foreach (var at in new[] { -2.3, -0.9, 0.4, 1.3, 2.2, 2.9, 4.6 })
+ {
+ var want = original.Substitute("x", at).EvalNumerical();
+ var got = derivative.Substitute("x", at).EvalNumerical();
+ Assert.False(got.IsNaN, $"d/dx of the antiderivative of {integrand} has no value at x = {at}, where the integrand is {want}");
+ 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}");
+ }
+ }
+ }
+}