diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index 2d8c7ac2b..de2d9f405 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -738,6 +738,23 @@ Rubi's 4.5.3.1:640 was wrong on that region
|---|---|---|
| `"(1+sec(x))^(5/2)*sqrt(cos(x))".Integrate("x")` | `integral((1 + sec(x)) ^ (5/2) * sqrt(cos(x)), x)` — left unevaluated | an antiderivative with `sgn(sin(x))` in it, `provided cos(x) >= 0` |
+### An answer that took an even root real without reading its sign says where it holds
+
+The same step's answer is checked where the rules below read no sign of `u` and still took it real:
+`SolveByCombiningRadicals` writes `sqrt(1 + 1/u^2) sqrt(1 - u^4)` as
+`sqrt(u^4 - u^8 + u^2 - u^6)/u^2`, which reads `sqrt(u^4)` as `u^2`, the other root wherever `u` is
+imaginary. Where the integrand holds a second even root, as it must to be real where the radicand
+is negative, the answer is differentiated and compared with the integrand where the integrand is
+real. Where it is not the antiderivative there, it now carries `provided t >= 0` as well.
+`sqrt(cos(x))/sqrt(a + a sec(x))` and six of its kin in Rubi's 4.5.1.2 and 4.5.3.1 were answered
+negated wherever the cosine is negative; the half-angle rule above answers those seven first now,
+and this is the same answer reached through the even root
+([#1581](https://github.com/asc-community/AngouriMath/issues/1581)).
+
+| Input | Was (2.5.0) | Now |
+|---|---|---|
+| `"sqrt(x)/(sqrt(1/x + 1)*sqrt(1 - x^2))".Integrate("x")` | `integral(...)` | an antiderivative in `sqrt(x)`, `provided x >= 0` |
+
### The inverse hyperbolic tangent's answer is given on its domain, and says so
`1/((1 - a^2 x^2)^(5/2) artanh(a x))` was left unevaluated, although the substitution `a x = tanh(u)`
diff --git a/Sources/AngouriMath/Functions/Algebra/Polynomials/PartialFractions.cs b/Sources/AngouriMath/Functions/Algebra/Polynomials/PartialFractions.cs
index b88f44d50..cfa49a3f3 100644
--- a/Sources/AngouriMath/Functions/Algebra/Polynomials/PartialFractions.cs
+++ b/Sources/AngouriMath/Functions/Algebra/Polynomials/PartialFractions.cs
@@ -1869,6 +1869,19 @@ internal static bool DerivativeHoldsAtSampledPoints(Entity antiderivative, Entit
return HoldsAtSampledPoints(Bare(pinnedAntiderivative).Differentiate(x), pinnedIntegrand, x);
}
+ ///
+ /// Whether the derivative of differs from
+ /// at one of the default points where the integrand is
+ /// real, as tells, with the symbols pinned before the
+ /// differentiation, as pins them.
+ ///
+ internal static bool DerivativeDiffersWhereReal(Entity antiderivative, Entity integrand, Variable x)
+ {
+ using var _ = MathS.Settings.DowncastingEnabled.Set(false);
+ var (pinnedAntiderivative, pinnedIntegrand) = Pinned(antiderivative, integrand, x);
+ return DiffersWhereReal(Bare(pinnedAntiderivative).Differentiate(x), pinnedIntegrand, x);
+ }
+
///
/// The two with every symbol but pinned to a fixed value:
/// distinct values, off the integers, so that no two factors coincide by accident and
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
index 3abeafc79..e965d2e48 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
@@ -19089,6 +19089,20 @@ private static Entity SimplifiedWithoutTheImaginaryUnit(Entity written, Entity i
/// Attempts to solve an integral using u-substitution.
/// Looks for patterns where f(g(x)) * g'(x) can be integrated as F(g(x)).
///
+ ///
+ /// Whether holds an even root of something in
+ /// other than : the only way it can be real where the radicand is
+ /// negative and its own root imaginary.
+ ///
+ private static bool HoldsAnEvenRootBesides(Entity expr, Entity radicand, Entity.Variable x)
+ {
+ foreach (var node in expr.Nodes)
+ if (node is Powf(var @base, Number.Rational power) && power.ERational.Denominator.IsEven
+ && @base != radicand && @base.ContainsNode(x))
+ return true;
+ return false;
+ }
+
internal static Entity? SolveBySubstitution(Entity expr, Entity.Variable x, bool integrateByParts = true)
{
// A rational function over written linear factors with symbols in their
@@ -19325,6 +19339,17 @@ private static Entity SimplifiedWithoutTheImaginaryUnit(Entity written, Entity i
// radicand, as the factoring below conditions its own.
if (asTheRoot != resultInU)
return Functions.PartialFractions.Bare(asTheRoot).Substitute(uSub, u).Provided(rootOf >= Number.Integer.Zero);
+ // And a rule below can take u real without reading its sign: `sqrt(u^4)`
+ // written as `u^2` is the other root wherever u is imaginary. That matters
+ // only where the integrand is real with the radicand negative, which takes
+ // a second even root imaginary beside this one, so there the answer is
+ // checked where the integrand is real: `sqrt(cos(x))/sqrt(1 + sec(x))` under
+ // `t = cos(x)` and `u = sqrt(t)` came back negated wherever the cosine is
+ // negative. https://github.com/asc-community/AngouriMath/issues/1581
+ var answer = resultInU.Substitute(uSub, u);
+ if (HoldsAnEvenRootBesides(expr, rootOf, x) && Functions.PartialFractions.DerivativeDiffersWhereReal(answer, expr, x))
+ return Functions.PartialFractions.Bare(answer).Provided(rootOf >= Number.Integer.Zero);
+ return answer;
}
// Substitute back: replace u with g(x)
return resultInU.Substitute(uSub, u);
diff --git a/Sources/Tests/UnitTests/Calculus/LinearRadicalSubstitutionIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/LinearRadicalSubstitutionIntegralTest.cs
index dff6f81d4..6a14ceae5 100644
--- a/Sources/Tests/UnitTests/Calculus/LinearRadicalSubstitutionIntegralTest.cs
+++ b/Sources/Tests/UnitTests/Calculus/LinearRadicalSubstitutionIntegralTest.cs
@@ -148,6 +148,38 @@ public void TheSearchsEvenRootSaysWhereItHolds()
Assert.True(derivative.Substitute("x", 2.8).Evaled.IsNaN, "the answer claims a derivative outside its condition");
}
+ ///
+ /// And where the rules below read no sign at all, the answer is checked where the radicand
+ /// is negative. sqrt(cos(x))/sqrt(1 + sec(x)) is real where the cosine is negative,
+ /// two of its roots imaginary there; under t = cos(x) and u = sqrt(t) the
+ /// product of roots below the bar was written with sqrt(u^4) = u^2, and the answer
+ /// was negated wherever the cosine is negative. Rubi's 4.5.1.2, and the same in t.
+ /// https://github.com/asc-community/AngouriMath/issues/1581
+ ///
+ [Theory]
+ [InlineData("sqrt(cos(x))/sqrt(1 + sec(x))", "provided cos(x) >= 0", new[] { 0.3, 0.9, -0.7 }, 2.4)]
+ [InlineData("sqrt(x)/(sqrt(1/x + 1)*sqrt(1 - x^2))", "provided x >= 0", new[] { 0.2, 0.5, 0.8 }, -0.6)]
+ public void AnEvenRootTakenRealWithoutASignSaysWhereItHolds(string written, string condition, double[] inside, double outsideAt)
+ {
+ var integrand = written.ToEntity();
+ var integral = integrand.Integrate("x");
+ Assert.DoesNotContain("integral(", integral.Stringize());
+ Assert.Contains(condition, integral.Stringize());
+ var derivative = integral.Substitute("C", 0).Differentiate("x");
+ foreach (var at in inside)
+ {
+ var got = derivative.Substitute("x", at).EvalNumerical();
+ var want = integrand.Substitute("x", at).EvalNumerical();
+ Assert.False(got.IsNaN, $"no answer at x = {at}, inside the condition");
+ Assert.True(Math.Abs((double)(got - want).RealPart) < 1e-9 * Math.Max(1, Math.Abs((double)want.RealPart)),
+ $"d/dx of the antiderivative is {got} at x = {at}, where the integrand is {want}");
+ }
+ // Beyond it the integrand is real and the answer claims nothing.
+ var outside = integrand.Substitute("x", outsideAt).EvalNumerical();
+ Assert.True(Math.Abs((double)outside.ImaginaryPart) < 1e-12, $"the integrand at x = {outsideAt} is {outside}, not real");
+ Assert.True(derivative.Substitute("x", outsideAt).Evaled.IsNaN, "the answer claims a derivative outside its condition");
+ }
+
///
/// A polynomial over a square root of something linear. None of these had an
/// antiderivative, and each is a first-year exercise.