diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index 883fc378b..250299e8d 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -7316,6 +7316,35 @@ written back is the argument's, which is that of `x` only for a positive slope a
| `"asec(x)^2/(x*sqrt(x^2 - 1))".ToEntity().Integrate("x")` | `integral(...)` | the same |
| `"asec(x + 3)^2/((x + 3)*sqrt((x + 3)^2 - 1))".ToEntity().Integrate("x")` | `integral(...)` | right for `x > -2` and for `x < -4` |
+### An answer through a root taken real holds where a second root makes the integrand real
+
+**Answers where there were none.** An integrand real where a radicand is negative, a second root
+being imaginary there too, was integrated through the root of that radicand as a non-negative real
+`u`, and the answer said `provided` the radicand is not negative: under `u = sqrt(tan(c + d x))` for
+`sqrt(cot(c + d x)) (a + b tan(c + d x))^(5/2) (A + B tan(c + d x))`, twenty-five more of Rubi's
+4.3.3.1 and twelve of its 4.3.2.1, and under `u = sqrt(w - 1)`, `w = x^2`, for the remainder by parts
+leaves from Timofeev's `arcsec(x) sqrt(x^2 - 1)/x^4` and `arcsec(x)/(x^2 - 1)^(5/2)`. On the
+unreleased master these said nothing where `tan(c + d x) < -a/b`, or on `0 < x < 1`, though each
+integrand is real there; 2.5.0 declined them. The answer is measured there now, its derivative
+against the integrand at a spread of points where the radicand is negative, the symbols pinned to
+four patterns of signs. These hold where the integrand is real and not where it is imaginary, so
+beside one other half-odd root the condition is that either radicand has its sign,
+`provided tan(c + d x) >= 0 or a + b tan(c + d x) <= 0`, the integrand's real domain; for the
+arcsecants it is `x^2 - 1 >= 0 or (x^2)^2 - x^2 <= 0`, true for every real `x`. An answer that also
+holds at every point measured where the integrand is imaginary carries no condition. One that differs
+where the integrand is real keeps the old one, and so does one whose integrand is real nowhere beyond
+the radicand's zero, which is not measured: Bondarenko's `sqrt(x + sqrt(1 + x))/(1 + x^2)` keeps
+`provided 1 + x >= 0`, though its answer, a sum over the roots of an octic, holds beyond `x = -1` as a
+complex function ([#233](https://github.com/asc-community/AngouriMath/issues/233)).
+
+| Input | Was (2.5.0) | Now |
+|---|---|---|
+| `"asec(x)*sqrt(-1 + x^2)/x^4".ToEntity().Integrate("x")` | `integral(...)` | 212 characters, `provided x^2 - 1 >= 0 or (x^2)^2 - x^2 <= 0` |
+| `"asec(x)/(-1 + x^2)^(5/2)".ToEntity().Integrate("x")` | `integral(...)` | 506 characters, the same condition twice |
+| `"sqrt(cot(c + d*x))*sqrt(a + b*tan(c + d*x))".ToEntity().Integrate("x")` | `integral(...)` | 523 characters, `provided tan(c + d x) >= 0 or a + b tan(c + d x) <= 0` |
+| `"sqrt(a + b*tan(c + d*x))*(A + B*tan(c + d*x))/tan(c + d*x)^(3/2)".ToEntity().Integrate("x")` | `integral(...)` | 1,067 characters, the same and `not a = 0` |
+| `"sqrt(cot(c + d*x))*(a + b*tan(c + d*x))^(5/2)*(A + B*tan(c + d*x))".ToEntity().Integrate("x")` | past twenty seconds | 2,767 characters, the same |
+
### `x^(4k - 1) (a + b arcsec(c x))/sqrt(1 - c^4 x^4)` and the arccosecant's are integrated on both sides of `|c x| = 1`
**Answers where there were none.** Rubi's 5.5.1 and 5.6.1 `x^(4k - 1) (a + b arcsec(c x))/sqrt(1 - c^4 x^4)`
diff --git a/Sources/AngouriMath/Functions/Algebra/Polynomials/PartialFractions.cs b/Sources/AngouriMath/Functions/Algebra/Polynomials/PartialFractions.cs
index 03f8669c5..1ff2f3dc2 100644
--- a/Sources/AngouriMath/Functions/Algebra/Polynomials/PartialFractions.cs
+++ b/Sources/AngouriMath/Functions/Algebra/Polynomials/PartialFractions.cs
@@ -3063,6 +3063,161 @@ internal static bool DerivativeDiffersWhereReal(Entity antiderivative, Entity in
return DiffersWhereReal(Bare(pinnedAntiderivative).Differentiate(x), pinnedIntegrand, x);
}
+ ///
+ /// Where the derivative of is shown to be
+ /// on the part of the line where is
+ /// negative: at every point there of a spread along the line, compared as complex numbers,
+ /// or at every point there where the integrand is real; at two points or more where it is
+ /// real, and nowhere that it is real does it differ or have no value. An answer built
+ /// through the root of the radicand as a real u -- a sign of u read as one,
+ /// sqrt(u^2 w) as u sqrt(w) -- was conditioned on the radicand for that, and
+ /// beside a second root imaginary there the integrand is real there too:
+ /// sqrt(a + b t)/t^(3/2) for t < -a/b, a product of two imaginary roots.
+ /// Whether the identities still hold there depends on the signs, so it is measured rather
+ /// than assumed: (1 + sec(x))^(5/2) sqrt(cos(x)) under u = sqrt(cos(x)) was the
+ /// integrand's negative wherever the cosine is negative, and sqrt(a + b t)/t^(3/2)
+ /// under u = sqrt(t) is its antiderivative beyond t = -a/b and not between
+ /// -a/b and 0, where the integrand is imaginary.
+ ///
+ ///
+ ///
+ /// The other symbols are pinned as pins them, and
+ /// again with each of three patterns of signs, since the region and the identities both move
+ /// with them. A pattern counts only where the answer holds at a point where the radicand is
+ /// positive: an answer that is not one for those signs at all says nothing of this region.
+ ///
+ ///
+ /// Nothing is differentiated where the integrand is real at no point beyond the radicand's
+ /// zero: the condition is then the integrand's real domain already, or more, and measuring
+ /// the answer off it buys nothing on the real line. Bondarenko's
+ /// sqrt(x + sqrt(1 + x))/(1 + x^2) is such a one, its answer a sum over the roots of
+ /// an octic, and measured at the fifteen points it cost twice the integral's allocation to
+ /// show it holds beyond x = -1 as a complex function. Off the real line the points are
+ /// compared until one differs.
+ ///
+ ///
+ internal static BeyondTheRadicand WhereTheDerivativeHoldsBeyondTheRadicand(Entity antiderivative, Entity integrand, Entity radicand, Variable x)
+ {
+ // Differentiated and evaluated at fifteen points for each of four patterns, an answer of
+ // a hundred thousand characters is a search of its own; it keeps its condition.
+ if (antiderivative.Complexity > LargestAnswerMeasuredBeyondTheRadicand)
+ return BeyondTheRadicand.NotShown;
+ using var _ = MathS.Settings.DowncastingEnabled.Set(false);
+ var parameters = antiderivative.Vars.Concat(integrand.Vars).Concat(radicand.Vars).Where(v => v != x).Distinct().ToList();
+ // Each pattern once for the symbols there are: none, and the four are one. The points of
+ // each sorted first, which asks the integrand and the radicand alone.
+ var measured = new List<(Entity Antiderivative, Entity Integrand, List Before, List RealBeyond, List ComplexBeyond)>();
+ foreach (var pattern in SignPatterns.Where(pattern => parameters.Count > 1 || pattern == 0 || parameters.Count == 1 && pattern == 1))
+ {
+ var (pinnedAntiderivative, pinnedIntegrand, pinnedRadicand) = (Bare(antiderivative), integrand, radicand);
+ for (var k = 0; k < parameters.Count; k++)
+ {
+ var fraction = (k % 3) switch { 0 => "1.37", 1 => "2.71", _ => "0.83" };
+ var magnitude = EDecimal.FromString(fraction).Add(EDecimal.FromInt32(k));
+ var negative = pattern switch { 1 => k % 2 == 0, 2 => k % 2 == 1, 3 => true, _ => false };
+ Entity value = Real.Create(negative ? magnitude.Negate() : magnitude);
+ pinnedAntiderivative = pinnedAntiderivative.Substitute(parameters[k], value);
+ pinnedIntegrand = pinnedIntegrand.Substitute(parameters[k], value);
+ pinnedRadicand = pinnedRadicand.Substitute(parameters[k], value);
+ }
+ // In double intervals, which are cheap beside the decimals: a point either cannot tell
+ // is passed over.
+ var (before, realBeyond, complexBeyond) = (new List(), new List(), new List());
+ foreach (var at in AlongTheLine)
+ {
+ var point = Real.Create(EDecimal.FromString(at));
+ if (IntervalEvaluation.Of(pinnedIntegrand.Substitute(x, point)) is not { IsFinite: true } value
+ || IntervalEvaluation.Of(pinnedRadicand.Substitute(x, point)) is not { IsFinite: true, IsReal: true } radicandValue
+ || radicandValue.Re.ContainsZero)
+ continue;
+ var isReal = value.Im.Magnitude <= 1e-9 * System.Math.Max(1, value.Magnitude);
+ if (radicandValue.Re.Low > 0)
+ {
+ if (isReal)
+ before.Add(point);
+ }
+ else
+ (isReal ? realBeyond : complexBeyond).Add(point);
+ }
+ measured.Add((pinnedAntiderivative, pinnedIntegrand, before, realBeyond, complexBeyond));
+ }
+ if (!measured.Any(pattern => pattern.RealBeyond.Count > 0))
+ return BeyondTheRadicand.NotShown;
+
+ var inDecimals = new System.Lazy[] { new(() => PreciseEvaluation.In(40)), new(() => PreciseEvaluation.In(80)) };
+ // The derivative of a pattern's answer, where it holds at a point before the zero.
+ var derivatives = new Entity?[measured.Count];
+ var controlled = new bool[measured.Count];
+ Entity? Controlled(int i)
+ {
+ if (controlled[i])
+ return derivatives[i];
+ controlled[i] = true;
+ var (pinnedAntiderivative, pinnedIntegrand, before, _, _) = measured[i];
+ var derivative = Bare(pinnedAntiderivative).Differentiate(x);
+ return derivatives[i] = before.Any(point => Agree(derivative.Substitute(x, point), pinnedIntegrand.Substitute(x, point), 1e-9, inDecimals) is true)
+ ? derivative : null;
+ }
+ var real = 0;
+ for (var i = 0; i < measured.Count; i++)
+ {
+ if (measured[i].RealBeyond.Count == 0 || Controlled(i) is not { } derivative)
+ continue;
+ foreach (var point in measured[i].RealBeyond)
+ {
+ if (Agree(derivative.Substitute(x, point), measured[i].Integrand.Substitute(x, point), 1e-9, inDecimals) is not true)
+ return BeyondTheRadicand.NotShown;
+ real++;
+ }
+ }
+ if (real < 2)
+ return BeyondTheRadicand.NotShown;
+ for (var i = 0; i < measured.Count; i++)
+ {
+ if (measured[i].ComplexBeyond.Count == 0 || Controlled(i) is not { } derivative)
+ continue;
+ foreach (var point in measured[i].ComplexBeyond)
+ if (Agree(derivative.Substitute(x, point), measured[i].Integrand.Substitute(x, point), 1e-9, inDecimals) is not true)
+ return BeyondTheRadicand.WhereTheIntegrandIsReal;
+ }
+ return BeyondTheRadicand.Everywhere;
+ }
+
+ ///
+ /// What shows of an answer where the
+ /// radicand is negative.
+ ///
+ internal enum BeyondTheRadicand
+ {
+ /// Nothing: it differs where the integrand is real, too few points compared, or the integrand is real at none beyond the zero.
+ NotShown,
+ /// It holds where the integrand is real, and differs somewhere it is not.
+ WhereTheIntegrandIsReal,
+ /// It holds at every point compared, the integrand real there or not.
+ Everywhere,
+ }
+
+ ///
+ /// The largest answer, by , that
+ /// measures; the answers it was
+ /// written for are a few thousand characters.
+ ///
+ private const int LargestAnswerMeasuredBeyondTheRadicand = 20000;
+
+ ///
+ /// The signs of the pinned symbols: all positive, alternating either way, all negative.
+ ///
+ [ConstantField]
+ private static readonly int[] SignPatterns = { 0, 1, 2, 3 };
+
+ ///
+ /// A spread of points for , on both
+ /// sides of zero and of one, near and far.
+ ///
+ [ConstantField]
+ private static readonly string[] AlongTheLine =
+ { "-7.3", "-3.1", "-1.61", "-0.83", "-0.47", "-0.19", "0.13", "0.29", "0.53", "0.77", "0.94", "1.43", "2.3", "3.17", "6.1" };
+
///
/// Whether has no value at any of the points
/// takes by default where has
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
index 6efc0fec9..4b3929ac1 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
@@ -14296,7 +14296,12 @@ node is Powf(var radical, Number.Rational e) && e is not Number.Integer
if (Integration.ComputeIndefiniteIntegral(integrand, u, integrateByParts) is not { } result)
return null;
var back = result.Substitute(u, MathS.Pow(radicalBase, Number.Rational.Create(1, q)));
- return condition is null ? back : back.Provided(condition);
+ // Beside a second root the integrand can be real where this radicand is negative, and
+ // the condition was the whole of its domain there: the remainder parts leaves from
+ // Timofeev's `arcsec(x) sqrt(x^2 - 1)/x^4`, in `w = x^2`, is
+ // `(w - 1)^(3/2)/(sqrt(w^2 - w) w^2)`, real on `0 < w < 1`, where the answer through an
+ // imaginary u holds. So the condition stays only where the answer does not hold there.
+ return condition is null ? back : ConditionedOnTheRadicand(back, expr, radicalBase, x);
}
///
@@ -41273,6 +41278,63 @@ private static bool HoldsAnEvenRootBesides(Entity expr, Entity radicand, Entity.
return false;
}
+ ///
+ /// , an antiderivative of through the root of
+ /// taken as a non-negative real, conditioned on the radicand only
+ /// as far as that is owed. Beside a second even root the integrand can be real where the
+ /// radicand is negative, a product of two imaginary roots, and there the answer is measured:
+ /// where it holds at every point the condition is not owed at all, and where it holds just
+ /// where the integrand is real, beside one other root of a half-odd power, the condition is
+ /// the radicand's or the other's negative, which is where the integrand is real.
+ ///
+ ///
+ /// The answers to sqrt(cot(c + d x)) (a + b tan(c + d x))^(5/2) (A + B tan(c + d x)), to
+ /// twenty-five more of Rubi's 4.3.3.1 and to twelve of its 4.3.2.1, as
+ /// sqrt(cot(c + d x)) sqrt(a + b tan(c + d x)), said provided tan(c + d x) >= 0, and those to
+ /// Timofeev's arcsec(x) sqrt(x^2 - 1)/x^4 and arcsec(x)/(x^2 - 1)^(5/2)
+ /// provided x^2 - 1 >= 0, each nothing at all on part of the line where the
+ /// integrand is real; each holds there as it stands.
+ /// https://github.com/asc-community/AngouriMath/issues/233
+ ///
+ private static Entity ConditionedOnTheRadicand(Entity answer, Entity expr, Entity radicand, Entity.Variable x)
+ {
+ var condition = radicand >= Number.Integer.Zero;
+ if (!HoldsAnEvenRootBesides(expr, radicand, x))
+ return answer.Provided(condition);
+ switch (Functions.PartialFractions.WhereTheDerivativeHoldsBeyondTheRadicand(answer, expr, radicand, x))
+ {
+ case Functions.PartialFractions.BeyondTheRadicand.Everywhere:
+ return answer;
+ case Functions.PartialFractions.BeyondTheRadicand.WhereTheIntegrandIsReal
+ when TheOneOtherRadicandOfAHalfOddPower(expr, radicand, x) is { } other:
+ return answer.Provided(condition | other <= Number.Integer.Zero);
+ default:
+ return answer.Provided(condition);
+ }
+ }
+
+ ///
+ /// The one base other than under a power of
+ /// that is not whole, where every such power is half-odd; else null.
+ ///
+ private static Entity? TheOneOtherRadicandOfAHalfOddPower(Entity expr, Entity radicand, Entity.Variable x)
+ {
+ Entity? other = null;
+ foreach (var node in expr.Nodes)
+ {
+ if (node is not Powf(var @base, Number.Rational power) || power is Number.Integer || !@base.ContainsNode(x))
+ continue;
+ if (!power.ERational.Denominator.Equals(EInteger.FromInt32(2)))
+ return null;
+ if (@base == radicand)
+ continue;
+ if (other is not null && other != @base)
+ return null;
+ other = @base;
+ }
+ return other;
+ }
+
///
/// Whether every function of in is its tangent
/// or its cotangent: sums, products, quotients and powers of those, and of anything free of
@@ -41693,9 +41755,13 @@ private static bool IsAFunctionOfTheTangentAlone(Entity expr, Entity.Variable u)
// `-2 u^2 (1 + 1/u^2)^(5/2)/sqrt(1 - u^4)`, whose power of a quotient was
// taken apart as `(1 + u^2)^(5/2)/|u|^5`, and was answered wrongly
// wherever the cosine is negative. So the answer is conditioned on the
- // radicand, as the factoring below conditions its own.
+ // radicand, as the factoring below conditions its own -- unless the answer
+ // is measured to hold where the radicand is negative and the integrand real:
+ // `sqrt(a + b t)/(t^(1/2) (1 + t^2))` under `u = sqrt(t)` holds beyond
+ // `t = -a/b`, and the condition left Rubi's `sqrt(cot(c + d x)) (a + b tan(c + d x))^(5/2)`
+ // and the rest of its 4.3.3.1 with half its domain.
if (asTheRoot != resultInU)
- return Functions.PartialFractions.Bare(asTheRoot).Substitute(uSub, u).Provided(rootOf >= Number.Integer.Zero);
+ return ConditionedOnTheRadicand(Functions.PartialFractions.Bare(asTheRoot).Substitute(uSub, u), expr, rootOf, x);
// 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
@@ -41735,9 +41801,10 @@ private static bool IsAFunctionOfTheTangentAlone(Entity expr, Entity.Variable u)
// is imaginary and the identities the factoring used do not hold, so the
// answer is conditioned on the radicand, as the linear-radical substitution
// conditions its own. https://github.com/asc-community/AngouriMath/issues/718
+ // And where the integrand is real with the radicand negative, held beside a second
+ // root, the answer is kept there where it is measured to hold.
if (u is Powf(var radicand, Number.Rational root) && root.ERational.Denominator.IsEven)
- return Functions.PartialFractions.Bare(forPositiveU).Substitute(uSub, u)
- .Provided(radicand >= Number.Integer.Zero);
+ return ConditionedOnTheRadicand(Functions.PartialFractions.Bare(forPositiveU).Substitute(uSub, u), expr, radicand, x);
if (ExtendedByParity(integrandInU, forPositiveU, uSub) is { } onBothSides)
return onBothSides.Substitute(uSub, u);
}
diff --git a/Sources/Tests/UnitTests/Calculus/AnAnswerBeyondANegativeRadicandIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/AnAnswerBeyondANegativeRadicandIntegralTest.cs
new file mode 100644
index 000000000..721849d24
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/AnAnswerBeyondANegativeRadicandIntegralTest.cs
@@ -0,0 +1,97 @@
+//
+// 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
+{
+ ///
+ /// Integrands that are real where a radicand is negative, as a product of two imaginary
+ /// factors, integrated through the root of that radicand taken as a non-negative real. The
+ /// answers said provided the radicand is not negative, and so said nothing on part of
+ /// the line where the integrand is real; they hold there as they stand, and the condition is
+ /// now the integrand's real domain or none.
+ /// #233
+ ///
+ ///
+ /// A point where the integrand is real and the answer has no value is a failure here, not a
+ /// point skipped: skipping it is how a condition false on half the line passed for an answer.
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class AnAnswerBeyondANegativeRadicandIntegralTest
+ {
+ private static void HoldsWhereTheIntegrandIsReal(string integrand, Func pinned, double[] points)
+ {
+ var integral = integrand.ToEntity().Integrate("x");
+ var text = integral.Stringize();
+ Assert.DoesNotContain("integral(", text);
+ Assert.DoesNotContain("NaN", text);
+ var derivative = pinned(integral.Substitute("C", 0)).Differentiate("x");
+ var original = pinned(integrand.ToEntity());
+ foreach (var at in points)
+ {
+ var want = original.Substitute("x", at).EvalNumerical();
+ Assert.False(want.IsNaN, $"{integrand} has no value at x = {at}");
+ Assert.True(Math.Abs((double)want.ImaginaryPart) < 1e-9 * Math.Max(1, Math.Abs((double)want.RealPart)),
+ $"{integrand} is {want} at x = {at}, which is not a point where it is real");
+ var got = derivative.Substitute("x", at).EvalNumerical();
+ Assert.False(got.IsNaN, $"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)),
+ $"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
+ }
+ }
+
+ ///
+ /// Timofeev's arcsecants by parts: the remainder is answered in w = x^2 through
+ /// sqrt(w - 1), and the integrands are real for x < -1, for x > 1 and,
+ /// as the arcsecant and the root are both imaginary, for 0 < x < 1. Two points on
+ /// each of the three intervals.
+ ///
+ [Theory]
+ [InlineData("asec(x)*sqrt(-1 + x^2)/x^4")]
+ [InlineData("asec(x)/(-1 + x^2)^(5/2)")]
+ public void AnArcsecantOnAllThreeIntervals(string integrand)
+ => HoldsWhereTheIntegrandIsReal(integrand, e => e, new[] { -2.7, -1.3, 0.23, 0.59, 1.3, 2.6 });
+
+ ///
+ /// Rubi's 4.3.2.1 and 4.3.3.1, a root of the tangent beside a root of a + b tan(c + d x), through
+ /// u = sqrt(tan(c + d x)): real where the tangent is positive and where it is below
+ /// -a/b, two imaginary roots there. With a = 1.3, b = 0.7,
+ /// c = 0.4, d = 0.9, the tangent is positive for -0.44 < x < 1.30 and
+ /// below -a/b for 1.30 < x < 1.85 and again a period on.
+ ///
+ [Theory]
+ [InlineData("sqrt(cot(c + d*x))*sqrt(a + b*tan(c + d*x))")]
+ [InlineData("1/(sqrt(cot(c + d*x))*(a + b*tan(c + d*x))^(3/2))")]
+ [InlineData("sqrt(a + b*tan(c + d*x))*(A + B*tan(c + d*x))/tan(c + d*x)^(3/2)")]
+ [InlineData("sqrt(cot(c + d*x))*(a + b*tan(c + d*x))^(5/2)*(A + B*tan(c + d*x))")]
+ [InlineData("sqrt(cot(c + d*x))*(A + B*tan(c + d*x))/sqrt(a + b*tan(c + d*x))")]
+ [InlineData("(A + B*tan(c + d*x))/(sqrt(cot(c + d*x))*(a + b*tan(c + d*x))^(3/2))")]
+ public void ARootOfTheTangentBesideARootOfALinearInIt(string integrand)
+ => HoldsWhereTheIntegrandIsReal(integrand,
+ e => e.Substitute("a", 1.3).Substitute("b", 0.7).Substitute("c", 0.4).Substitute("d", 0.9)
+ .Substitute("A", 1.1).Substitute("B", 0.6),
+ new[] { 0.3, 0.9, 1.45, 1.7, 5.0 });
+
+ ///
+ /// Where the integrand is imaginary the answer still claims nothing: between -a/b
+ /// and 0 the tangent's root and the linear's are not both imaginary, and there the answer
+ /// through a real-or-imaginary u is not the integrand's.
+ ///
+ [Fact]
+ public void NothingIsClaimedWhereTheIntegrandIsImaginary()
+ {
+ var integral = "sqrt(a + b*tan(c + d*x))*(A + B*tan(c + d*x))/tan(c + d*x)^(3/2)".ToEntity().Integrate("x");
+ var pinned = integral.Substitute("C", 0).Substitute("a", 1.3).Substitute("b", 0.7).Substitute("c", 0.4)
+ .Substitute("d", 0.9).Substitute("A", 1.1).Substitute("B", 0.6);
+ Assert.True(pinned.Substitute("x", 2.3).EvalNumerical().IsNaN);
+ }
+ }
+}