diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index a7c295ac9..31a904aab 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -3679,6 +3679,38 @@ of the six functions read inside a power as it is
| `"(csc(x) - sin(x))^(5/2)".ToEntity().Integrate("x")` | `integral(...)` | powers of `sin(x)` times `(cos(x)^2/sin(x))^(5/2)` |
| `"1/(sec(x) - cos(x))^(3/2)".ToEntity().Integrate("x")` | `integral(...)` | a root of `cos(x)` with a logarithm and an arctangent, times `(sin(x)^2/cos(x))^(-3/2)` |
+### Fractional trigonometric powers two apart, and a sine raised from nothing, are integrated as derivatives
+
+**Answers where there were none.** Rubi's trigonometric integrands of these shapes were declined or ran out of time
+([#233](https://github.com/asc-community/AngouriMath/issues/233)):
+- A sum of powers of one sine, cosine, secant or cosecant of a linear, the powers not whole and two apart, by the
+ reduction formula from the highest down: `(cos sin^(n - 1))' = (n - 1) sin^(n - 2) - n sin^n`, and its kind for the
+ others. Each power alone is elliptic, so split, `(3 + 3 sec(c + d x)^2)/sqrt(sec(c + d x))` was two declined
+ integrals. Beside x, by parts once: `x/csc(c + d x)^(3/2) - x sqrt(csc(c + d x))/3`. The written function is kept.
+- The derivative of a sine raised from nothing times powers of the cosine and sums in it, or of a cosine beside the
+ sine: `(a + a cos(x))^(3/2)/cos(x)^(5/4)` is `(4 a^2 sin(x)/(cos(x)^(1/4) sqrt(a + a cos(x))))'`. Beside x, by parts
+ once: `x (b + a sin(x))/(a + b sin(x))^2` is `x G - integral G` for `G = -cos(x)/(a + b sin(x))`.
+- A cosine beside a function of the sine with a power of it that is not rational, in `s = sin(x)`:
+ `cos(x)/(sin(x) + sin(x)^sqrt(2))` is `1/(s + s^sqrt(2))`.
+- `(c sin(a + b x)^n)^(1/n)` is `K sin(a + b x)` with `K = (c sin(a + b x)^n)^(1/n)/sin(a + b x)`, constant between the
+ zeros of the sine, and so for the cosine.
+- Under the homogeneous substitution, factors written alike on both sides are cancelled where the quotient was
+ simplified: `sin(c + d x)^6/(a + b sin(c + d x)^2)^2`. Under the tangent, a whole power of a product is distributed
+ over its factors: `cot(c + d x)^2/(a + b sec(c + d x)^2)^2`. And beside a root of `1 - cos(x)`, the root of
+ `1 - cos(x)^2` is the product of the roots of `1 - cos(x)` and `1 + cos(x)`: `sqrt(1 - cos(x))/sqrt(a - cos(x))`.
+
+| Input | Was (2.5.0) | Now |
+|---|---|---|
+| `"(3+3*sec(c+d*x)^2)/sec(c+d*x)^(1/2)".ToEntity().Integrate("x")` | `integral(...)` | 60 characters |
+| `"x/csc(c+d*x)^(3/2)-1/3*x*sqrt(csc(c+d*x))".ToEntity().Integrate("x")` | `integral(...)` | 131 characters |
+| `"(a+a*cos(c+d*x))^(3/2)/cos(c+d*x)^(5/4)".ToEntity().Integrate("x")` | `integral(...)` | 96 characters |
+| `"x*(b+a*sin(x))/(a+b*sin(x))^2".ToEntity().Integrate("x")` | `integral(...)` | 64 characters |
+| `"cos(x)/(sin(x)+sin(x)^sqrt(2))".ToEntity().Integrate("x")` | `integral(...)` | 86 characters |
+| `"(c*sin(a+b*x)^n)^(1/n)".ToEntity().Integrate("x")` | `integral(...)` | 77 characters |
+| `"sin(c+d*x)^6/(a+b*sin(c+d*x)^2)^2".ToEntity().Integrate("x")` | `integral(...)` | 2,169 characters |
+| `"cot(c+d*x)^2/(a+b*sec(c+d*x)^2)^2".ToEntity().Integrate("x")` | `integral(...)` | 1,963 characters |
+| `"sqrt(1-cos(x))/sqrt(a-cos(x))".ToEntity().Integrate("x")` | `integral(...)` | 70 characters |
+
### A product of powers of the trigonometric functions, some not whole, is integrated as sine and cosine powers
**Answers where there were none.** `(a sin(x))^(5/2) sqrt(b sec(x))` was declined, and so were
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
index a1d92a989..51d563899 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
@@ -13314,11 +13314,16 @@ private static (Entity Coefficient, Entity Degree)? TheMonomial(Entity expr, Ent
return null;
var t = Variable.CreateUnique(expr, "t_homog");
- var inT = Functions.SingleQuotient.Combine(
+ var combined = Functions.SingleQuotient.Combine(
above.Substitute(HomogeneousTangent, t).Substitute(HomogeneousCosine, 1)
/ below.Substitute(HomogeneousTangent, t).Substitute(HomogeneousCosine, 1)
* MathS.Pow(1 + MathS.Sqr(t), Number.Integer.Create(difference / 2 - 1))
- / rate).Simplify();
+ / rate);
+ // With symbols in it, the factors written alike on both sides cancelled and the rest kept
+ // as written, not simplified: `sin(x)^6/(a + b sin(x)^2)^2` spent past eight seconds in the
+ // simplification of `t^6 (1 + t^2)^2/((1 + t^2)^3 (a (1 + t^2) + b t^2)^2 (1 + t^2))`, and the
+ // cancelled quotient is answered in a twentieth of one.
+ var inT = combined.Vars.Any(v => v != t && v != x) ? WithTheFactorsWrittenOnBothSidesCancelled(t, combined.InnerSimplified) : combined.Simplify();
if (inT is Providedf(var inner, _))
inT = inner;
if (inT.ContainsNode(x))
@@ -14418,6 +14423,10 @@ static bool IsTwice(Entity argument, Entity.Variable x)
// neighbours to one power below the bar gathered: Rubi's 4.3.4.2
// `(c + d u)^3 (A + B u + C u^2)/((a + b u)^3 (1 + u^2))` spent 13 of its 14 s being
// simplified to itself. See IsAQuotientOfDistinctSumsOfSymbolsInLowestTerms.
+ // A whole power of a product with a sum among its factors, as the simplification writes
+ // `u^2 (a + b (1 + u^2))^2`, distributed over the factors: `1/((u (a + b (1 + u^2)))^2 (1 + u^2))`
+ // from `cot(x)^2/(a + b sec(x)^2)^2` was a search the rules below spent the budget declining,
+ // where `1/(u^2 (a + b (1 + u^2))^2 (1 + u^2))` is answered in a thirtieth of a second.
var integrand = ByTheSumsConjugate(inU, uSub) is { } overTheConjugate
? overTheConjugate
: Functions.SingleQuotient.Combine(inU / (1 + MathS.Sqr(uSub))) is var overOneBar
@@ -14427,7 +14436,11 @@ static bool IsTwice(Entity argument, Entity.Variable x)
// with its ones above: see AQuotientOfDistinctSumsOverAPowerOfTheVariable.
: AQuotientOfDistinctSumsOverAPowerOfTheVariable(overOneBar, uSub) is { } overThePower
? overThePower
- : overOneBar.Simplify();
+ : overOneBar.Simplify().Replace(node =>
+ node is Powf(Mulf product, Number.Integer { EInteger.Sign: > 0 } whole)
+ && Mulf.LinearChildren(product).Any(factor => factor is Sumf or Minusf && factor.ContainsNode(uSub))
+ ? Mulf.LinearChildren(product).Aggregate((Entity)Number.Integer.One, (distributed, factor) => distributed * MathS.Pow(factor, whole))
+ : node);
if (integrand is Providedf(var withoutCondition, _))
integrand = withoutCondition;
// Asked as the same question: the rewrite renames the variable and adds no step of
@@ -34223,6 +34236,59 @@ private static (Entity Numerator, Entity Denominator)? WithASharedPowerCancelled
return answer.Nodes.Any(node => node == MathS.NaN) ? null : answer;
}
+ ///
+ /// A power of a constant times a power of a function of x whose exponents multiply to a whole
+ /// number with a symbol in them, the function taken out: (c sin(u)^n)^(1/n) is
+ /// K sin(u) with K = (c sin(u)^n)^(1/n)/sin(u), constant between the zeros of the
+ /// sine, carried through the integration as a symbol and written back. Rubi's 4.1.7 and 4.2.0
+ /// (c sin(a + b x)^n)^(1/n), -cot(a + b x) (c sin(a + b x)^n)^(1/n)/b, was declined.
+ ///
+ ///
+ /// For a real f of one sign on an interval, f^m is |f|^m times a constant
+ /// phase, so c f^m keeps its argument there and its principal power is
+ /// (c f^m)^r = K f^(m r) with K constant: the logarithmic derivatives of the two
+ /// sides are both m r f'/f. The rule above reads numbers for the exponents and writes
+ /// the sign; a symbol leaves the constant as written, as Rubi does. Not of a polynomial, whose
+ /// powers the rules for x^m (a + b x^n)^p take apart. At the top only, as above.
+ /// https://github.com/asc-community/AngouriMath/issues/233
+ ///
+ internal static Entity? SolveByTakingAFunctionOutOfASymbolicRootOfItsPower(Entity expr, Entity.Variable x, bool integrateByParts)
+ {
+ if (!Integration.AnsweringTheQuestionAsked)
+ return null;
+ Variable? constant = null;
+ Entity? writtenOut = null;
+ var rewritten = expr.Replace(node =>
+ {
+ if (constant is not null || node is not Powf(var radicand, var r) || r.ContainsNode(x) || !r.Vars.Any() || !radicand.ContainsNode(x))
+ return node;
+ Entity? function = null;
+ Entity? m = null;
+ foreach (var (factor, _) in FactorsOfTheIntegrand(radicand))
+ {
+ if (!factor.ContainsNode(x))
+ continue;
+ if (function is not null || factor is not Powf(var f, var e) || e.ContainsNode(x))
+ return node;
+ (function, m) = (f, e);
+ }
+ if (function is null || m is null || TreeAnalyzer.TryGetPolynomial(function, x, out _)
+ || Functions.PartialFractions.Bare((m * r).Simplify()) is not Number.Integer { EInteger: var whole } || whole.IsZero
+ || !whole.CanFitInInt32())
+ return node;
+ constant = Variable.CreateUnique(expr, "k_root");
+ var power = whole.Equals(EInteger.One) ? function : MathS.Pow(function, Number.Integer.Create(whole));
+ writtenOut = node / power;
+ return constant * power;
+ });
+ if (constant is null || writtenOut is null
+ || Integration.ComputeAsAQuestionOfItsOwn(rewritten, x, integrateByParts) is not { } result
+ || result.Nodes.Any(node => node == MathS.NaN))
+ return null;
+ var answer = result.Substitute(constant, writtenOut);
+ return Functions.PartialFractions.DerivativeHoldsAtSampledPoints(answer, expr, x) ? answer : null;
+ }
+
///
/// A denominator factor that is a sum or difference of two square roots of
/// polynomials, multiplied above and below by its conjugate:
@@ -38383,6 +38449,468 @@ Piecewise piecewise when piecewise.Cases.Any() => piecewise.Cases.First().Expres
[System.ThreadStatic] private static bool integratingAPowerOverAQuadraticBesideAnInverse;
+ ///
+ /// A sum of powers of one sine, cosine, secant or cosecant of a linear u, the powers
+ /// numbers that are not whole and that differ by whole multiples of two, by the reduction
+ /// formula: with u' = 1, (cos sin^(n - 1))' = (n - 1) sin^(n - 2) - n sin^n,
+ /// (sin cos^(n - 1))' = n cos^n - (n - 1) cos^(n - 2),
+ /// (sin sec^(n - 1))' = (n - 1) sec^n - (n - 2) sec^(n - 2) and
+ /// (cos csc^(n - 1))' = (n - 2) csc^(n - 2) - (n - 1) csc^n. Each power is written as
+ /// such a derivative and the power two below it, from the highest down, and the sum is
+ /// answered where nothing is left of the lowest. Beside x, by parts once: the sum is
+ /// Q' for Q a sum of cos sin^(n - 1), whose integral is
+ /// sin^n/n, so x Q' is (x Q)' - Q, closed.
+ ///
+ ///
+ /// A power that is not whole is elliptic on its own, and so is each term of Rubi's 4.5.4.1
+ /// (3 + 3 sec(c + d x)^2)/sqrt(sec(c + d x)) and of 4.1.10
+ /// x/csc(e + f x)^(3/2) - x sqrt(csc(e + f x))/3: split, both were declined.
+ /// reads a
+ /// symbolic exponent and two terms. The identities are the chain rule for the principal
+ /// power, which holds wherever the function is not zero, on either side of it, and the
+ /// written function is kept: a cosecant's answer is in the cosecant. Not through the
+ /// integrator for Q: it wrote sin(x)/sqrt(sec(x))'s integral as a power of
+ /// the cosine, which differs from the secant's where the cosine is negative. At most twelve
+ /// steps, and x to the first power only, since beside x^2 the remainder x Q is
+ /// elliptic again.
+ /// https://github.com/asc-community/AngouriMath/issues/233
+ ///
+ internal static Entity? SolveFractionalPowersOfATrigonometricFunctionTwoApartByTheReduction(Entity expr, Entity.Variable x)
+ {
+ if (expr is not (Sumf or Minusf or Mulf or Divf))
+ return null;
+ var function = expr.Nodes.FirstOrDefault(node => node is Sinf or Cosf or Secantf or Cosecantf && node.ContainsNode(x));
+ if (function is null || TrigonometricArgument(function) is not { } argument
+ || !TreeAnalyzer.TryGetPolyLinear(argument, x, out var slope, out _) || slope.ContainsNode(x)
+ || Functions.PartialFractions.IsZeroAsAValue(slope))
+ return null;
+ // The terms, with a product of sums distributed twice at most: `x (sin^(3/2) - sin^(-1/2)/3)`.
+ static IEnumerable Terms(Entity e, int depth)
+ {
+ var distributed = DistributedOverTheSum(e);
+ return depth == 0 || distributed.Count == 1 ? distributed : distributed.SelectMany(term => Terms(term, depth - 1));
+ }
+ // The coefficients of f^n beside x^0 and beside x^1.
+ var coefficients = new[] { new Dictionary(), new Dictionary() };
+ var bases = new Entity[] { function, x };
+ foreach (var term in Terms(expr, 2))
+ {
+ var (rest, exponents) = GatheredOverTheBases(term, bases);
+ var power = exponents[0].ToLowestTerms();
+ if (rest.ContainsNode(x) || power.Denominator.Equals(EInteger.One)
+ || !exponents[1].IsInteger() || exponents[1].Sign < 0 || exponents[1].CompareTo(ERational.One) > 0)
+ return null;
+ var ofX = coefficients[exponents[1].IsZero ? 0 : 1];
+ ofX[power] = ofX.TryGetValue(power, out var sofar) ? sofar + rest : rest;
+ }
+ if (coefficients[0].Count + coefficients[1].Count < 2)
+ return null;
+ var two = ERational.FromInt32(2);
+ var anExponent = (coefficients[0].Count > 0 ? coefficients[0] : coefficients[1]).Keys.First();
+ if (coefficients.SelectMany(ofX => ofX.Keys)
+ .Any(power => !power.Subtract(anExponent).Divide(two).ToLowestTerms().Denominator.Equals(EInteger.One)))
+ return null;
+ var partner = function is Sinf or Cosecantf ? MathS.Cos(argument) : MathS.Sin(argument);
+ // The pieces c partner f^(n - 1) of Q with Q' the sum, f^n read with u' = 1; null where
+ // something of the lowest power is left.
+ List<(Entity Coefficient, ERational Exponent)>? Reduced(Dictionary written)
+ {
+ var pieces = new List<(Entity, ERational)>();
+ if (written.Count == 0)
+ return pieces;
+ var lowest = written.Keys.Min()!;
+ var highest = written.Keys.Max()!;
+ if (highest.Subtract(lowest).Divide(two).CompareTo(ERational.FromInt32(12)) > 0)
+ return null;
+ Entity carried = Number.Integer.Zero;
+ for (var n = highest; n.CompareTo(lowest) > 0; n = n.Subtract(two))
+ {
+ var total = written.TryGetValue(n, out var c) ? c + carried : carried;
+ // (partner f^(n - 1))' = alpha f^n + beta f^(n - 2), and alpha is not zero for n not whole.
+ var (alpha, beta) = function switch
+ {
+ Sinf => (n.Negate(), n.Subtract(ERational.One)),
+ Cosf => (n, ERational.One.Subtract(n)),
+ Secantf => (n.Subtract(ERational.One), two.Subtract(n)),
+ _ => (ERational.One.Subtract(n), n.Subtract(two)),
+ };
+ pieces.Add((total / Number.Rational.Create(alpha), n));
+ carried = -total * Number.Rational.Create(beta.Divide(alpha));
+ }
+ return VanishesIdentically(written[lowest] + carried) ? pieces : null;
+ }
+ if (Reduced(coefficients[0]) is not { } withoutX || Reduced(coefficients[1]) is not { } besideX)
+ return null;
+ Entity Sum(IEnumerable parts) => parts.Aggregate((Entity)Number.Integer.Zero, (sum, part) => sum + part);
+ Entity Piece(ERational n) => partner * MathS.Pow(function, Number.Rational.Create(n.Subtract(ERational.One)));
+ // The integral of partner f^(n - 1): sin^n/n, -cos^n/n, sec^(n - 2)/(n - 2), -csc^(n - 2)/(n - 2).
+ Entity IntegralOfPiece(ERational n) => function switch
+ {
+ Sinf => MathS.Pow(function, Number.Rational.Create(n)) / Number.Rational.Create(n),
+ Cosf => -MathS.Pow(function, Number.Rational.Create(n)) / Number.Rational.Create(n),
+ Secantf => MathS.Pow(function, Number.Rational.Create(n.Subtract(two))) / Number.Rational.Create(n.Subtract(two)),
+ _ => -MathS.Pow(function, Number.Rational.Create(n.Subtract(two))) / Number.Rational.Create(n.Subtract(two)),
+ };
+ var answer = (Sum(withoutX.Select(piece => piece.Coefficient * Piece(piece.Exponent))) / slope
+ + x * Sum(besideX.Select(piece => piece.Coefficient * Piece(piece.Exponent))) / slope
+ - Sum(besideX.Select(piece => piece.Coefficient * IntegralOfPiece(piece.Exponent))) / MathS.Sqr(slope)).InnerSimplified;
+ return Functions.PartialFractions.DerivativeHoldsAtSampledPoints(answer, expr, x) ? answer : null;
+ }
+
+ ///
+ /// A cosine of a linear u beside a function of its sine, with a power of the sine whose
+ /// exponent is not a rational number in it, integrated in s = sin(u); a sine beside a
+ /// function of the cosine the same in c = cos(u). Rubi's 4.7.7
+ /// cos(x)/(sin(x) + sin(x)^sqrt(2)) is 1/(s + s^sqrt(2)) in the sine, which is
+ /// answered, and as written it was declined.
+ ///
+ ///
+ /// ds = cos(u) u' dx and the rest is a function of the sine as written, so the
+ /// integrand is exactly that function of s over u', with the principal powers
+ /// of the same values. Only past the rationals: a rational power of the sine is the
+ /// substitution search's, and answered there.
+ /// https://github.com/asc-community/AngouriMath/issues/233
+ ///
+ internal static Entity? SolveACosineBesideAFunctionOfTheSineWithAnIrrationalPowerInIt(Entity expr, Entity.Variable x, bool integrateByParts)
+ {
+ if (!Integration.AnsweringTheQuestionAskedOrOneBelow)
+ return null;
+ var power = expr.Nodes.OfType().FirstOrDefault(node => node.Base is Sinf or Cosf && node.Base.ContainsNode(x)
+ && !node.Exponent.ContainsNode(x) && node.Exponent is not Number.Rational);
+ if (power is null || TrigonometricArgument(power.Base) is not { } argument
+ || !TreeAnalyzer.TryGetPolyLinear(argument, x, out var slope, out _) || slope.ContainsNode(x)
+ || Functions.PartialFractions.IsZeroAsAValue(slope))
+ return null;
+ var function = power.Base;
+ var partner = function is Sinf ? MathS.Cos(argument) : MathS.Sin(argument);
+ // The partner once above the bar, and the rest a function of the function alone.
+ Entity rest = Number.Integer.One;
+ var found = false;
+ foreach (var (factor, underneath) in FactorsOfTheIntegrand(expr))
+ {
+ if (!found && !underneath && factor == partner)
+ {
+ found = true;
+ continue;
+ }
+ rest = underneath ? rest / factor : rest * factor;
+ }
+ if (!found)
+ return null;
+ var s = Variable.CreateUnique(expr, "s_trig");
+ var inS = rest.Substitute(function, s);
+ if (inS.ContainsNode(x))
+ return null;
+ var rate = function is Sinf ? slope : -slope;
+ if (Integration.ComputeAsAQuestionOfItsOwn(inS / rate, s, integrateByParts) is not { } integral
+ || integral.Nodes.Any(node => node == MathS.NaN))
+ return null;
+ var answer = integral.Substitute(s, function);
+ return Functions.PartialFractions.DerivativeHoldsAtSampledPoints(answer, expr, x) ? answer : null;
+ }
+
+ ///
+ /// The derivative of sin(u) prod B_i^(e_i), for B_i the powers of cosines,
+ /// secants and sums of them in the integrand, or of cos(u) prod B_i^(e_i) beside sines
+ /// and cosecants: the sine raised from nothing, and each e_i from two below the
+ /// integrand's exponent to one above it. Rubi's (a + a cos(u))^(3/2)/cos(u)^(5/4) is
+ /// (4 a^2 sin(u)/(cos(u)^(1/4) sqrt(a + a cos(u))))'. Beside x, by parts once:
+ /// x (b + a sin(x))/(a + b sin(x))^2 is x G' for G = -cos(x)/(a + b sin(x)),
+ /// so x G - integral G, and the integral of a cosine beside a function of the sine is
+ /// closed.
+ ///
+ ///
+ ///
+ /// With G = s prod B_i^(e_i), G'/G = s'/s + sum e_i B_i'/B_i, and the integrand
+ /// over G is a product of whole powers of the B_i, so their quotient is a
+ /// rational function of the sine and the cosine, constant where the integrand is the
+ /// derivative. Decided at sampled points for each candidate, every symbol pinned, and then
+ /// the constant read at the angle whose cosine is 3/5 and sine 4/5, where a rational
+ /// function of the two that is constant has the value it has everywhere; the answer is
+ /// checked by differentiating it back.
+ ///
+ ///
+ /// Nothing raised a sine the integrand does not have: the first was declined, and parts on
+ /// the second integrated G' by the half angle into a function of tan(x/2),
+ /// whose integral ran past the budget. Only
+ /// where the integrand has no power of the sine itself (or of the cosine, beside sines),
+ /// which is what the product-of-powers rule and parts already read, and without x only
+ /// for two powers or more, one of them not whole: sqrt(a + a cos(u)) is the half
+ /// angle's, and a power of the cosine alone the reduction's. Of the sine and the cosine
+ /// only, not the tangent: a product of the two and their reciprocals is the substitution's.
+ /// https://github.com/asc-community/AngouriMath/issues/233
+ ///
+ ///
+ internal static Entity? SolveAsTheDerivativeOfASineRaisedFromNothingBesidePowersOfTheCosine(Entity expr, Entity.Variable x, bool integrateByParts)
+ {
+ if (!Integration.AnsweringTheQuestionAskedOrOneBelow || expr is not (Sumf or Minusf or Mulf or Divf or Powf))
+ return null;
+ // One argument, linear in x, and either cosines and secants or sines and cosecants.
+ Entity? argument = null;
+ bool? ofTheCosine = null;
+ foreach (var node in expr.Nodes)
+ {
+ if (!node.ContainsNode(x))
+ continue;
+ bool? cosine = node switch { Cosf or Secantf => true, Sinf or Cosecantf => false, _ => null };
+ if (cosine is { } isCosine)
+ {
+ var a = TrigonometricArgument(node)!;
+ if (argument is null)
+ (argument, ofTheCosine) = (a, isCosine);
+ else if (a != argument || ofTheCosine != isCosine)
+ return null;
+ continue;
+ }
+ if (node is not (Sumf or Minusf or Mulf or Divf or Variable) && !(node is Powf(_, var e) && !e.ContainsNode(x)))
+ return null;
+ }
+ if (argument is null || !TreeAnalyzer.TryGetPolyLinear(argument, x, out var slope, out _) || slope.ContainsNode(x)
+ || Functions.PartialFractions.IsZeroAsAValue(slope))
+ return null;
+ var partner = ofTheCosine == true ? MathS.Sin(argument) : MathS.Cos(argument);
+ bool OnlyThroughTheFunctions(Entity e)
+ => !e.Replace(node => node is Sinf or Cosf or Secantf or Cosecantf ? Number.Integer.One : node).ContainsNode(x);
+ static IEnumerable Terms(Entity e, int depth)
+ {
+ var distributed = DistributedOverTheSum(e);
+ return depth == 0 || distributed.Count == 1 ? distributed : distributed.SelectMany(term => Terms(term, depth - 1));
+ }
+ // The terms beside x^0 and beside x^1, each read as a constant and powers of bases with x
+ // in them, a power of a power with a whole exponent outside as one power.
+ var groups = new[] { new List<(Entity Constant, Dictionary Powers)>(), new List<(Entity Constant, Dictionary Powers)>() };
+ foreach (var term in Terms(expr, 2))
+ {
+ Entity constant = Number.Integer.One;
+ var powers = new Dictionary();
+ var ofX = 0;
+ foreach (var (factor, underneath) in FactorsOfTheIntegrand(term))
+ {
+ if (!factor.ContainsNode(x))
+ {
+ constant = underneath ? constant / factor : constant * factor;
+ continue;
+ }
+ var (@base, exponent) = factor is Powf(var b, var e) && !e.ContainsNode(x) ? (b, e) : (factor, (Entity)Number.Integer.One);
+ if (@base is Powf(var inner, var innerExponent) && !innerExponent.ContainsNode(x) && exponent is Number.Integer)
+ (@base, exponent) = (inner, innerExponent * exponent);
+ if (underneath)
+ exponent = -exponent;
+ if (@base == x)
+ {
+ if (exponent is not Number.Integer { EInteger: var power } || !power.CanFitInInt32())
+ return null;
+ ofX += power.ToInt32Unchecked();
+ continue;
+ }
+ // The sine raised from nothing: a power of it is the other rules'.
+ if (@base == partner || !OnlyThroughTheFunctions(@base))
+ return null;
+ powers[@base] = powers.TryGetValue(@base, out var before) ? (before + exponent).InnerSimplified : exponent.InnerSimplified;
+ }
+ if (ofX is < 0 or > 1)
+ return null;
+ groups[ofX].Add((constant, powers));
+ }
+ // Without x, a power that is not whole; beside it, one of a base below the bar.
+ if (groups[1].Count == 0
+ ? groups[0].All(term => term.Powers.Values.All(exponent => exponent is Number.Integer))
+ : !groups[1].Any(term => term.Powers.Values.Any(exponent => exponent is not Number.Integer { EInteger.Sign: >= 0 })))
+ return null;
+ var besideX = groups[1].Count > 0;
+ Entity? withoutX = groups[0].Count == 0 ? Number.Integer.Zero : Antiderivative(groups[0], needsTwoPowers: true);
+ if (withoutX is null)
+ return null;
+ Entity answer = withoutX;
+ if (besideX)
+ {
+ if (Antiderivative(groups[1], needsTwoPowers: false) is not { } g
+ || Integration.ComputeAsAQuestionOfItsOwn(g, x, integrateByParts) is not { } integralOfG
+ || integralOfG.Nodes.Any(node => node == MathS.NaN))
+ return null;
+ answer = answer + x * g - integralOfG;
+ }
+ answer = answer.InnerSimplified;
+ return Functions.PartialFractions.DerivativeHoldsAtSampledPoints(answer, expr, x) ? answer : null;
+
+ // G with G' the sum of the terms, or null.
+ Entity? Antiderivative(List<(Entity Constant, Dictionary Powers)> read, bool needsTwoPowers)
+ {
+ // The bases in every term, each with the least of its exponents, the others more by
+ // whole numbers; a base in some terms only is a whole power, a bracket.
+ var common = read[0].Powers.Keys.Where(@base => read.All(term => term.Powers.ContainsKey(@base))).ToList();
+ var least = new Dictionary();
+ var above = new List>();
+ foreach (var term in read)
+ above.Add(new Dictionary());
+ foreach (var @base in common)
+ {
+ var reference = read[0].Powers[@base];
+ var differences = new List();
+ foreach (var term in read)
+ {
+ if (Functions.PartialFractions.Bare((term.Powers[@base] - reference).Simplify()) is not Number.Integer { EInteger: var difference }
+ || !difference.CanFitInInt32())
+ return null;
+ differences.Add(difference.ToInt32Unchecked());
+ }
+ var lowest = differences.Min();
+ least[@base] = (reference + lowest).InnerSimplified;
+ for (var t = 0; t < read.Count; t++)
+ above[t][@base] = differences[t] - lowest;
+ }
+ if (common.Count == 0 || common.Count > 3
+ || read.Any(term => term.Powers.Any(pair => !common.Contains(pair.Key) && pair.Value is not Number.Integer)))
+ return null;
+ if (needsTwoPowers && (common.Count < 2 || common.All(@base => least[@base] is Number.Integer)))
+ return null;
+ // Every symbol pinned, and the integrand, the bases, their logarithmic derivatives and
+ // the sine's, at sampled points in decimals.
+ Entity sum = Number.Integer.Zero;
+ foreach (var (termConstant, termPowers) in read)
+ {
+ Entity term = termConstant;
+ foreach (var pair in termPowers)
+ term *= MathS.Pow(pair.Key, pair.Value);
+ sum = sum == Number.Integer.Zero ? term : sum + term;
+ }
+ var symbols = sum.Vars.Where(v => v != x).Distinct().ToList();
+ Entity Pinned(Entity e)
+ {
+ for (var k = 0; k < symbols.Count; k++)
+ e = e.Substitute(symbols[k], Number.Real.Create(EDecimal.FromString((k % 3) switch { 0 => "1.37", 1 => "2.71", _ => "0.83" }).Add(EDecimal.FromInt32(k))));
+ return e;
+ }
+ static System.Numerics.Complex? At(Entity e, Entity.Variable x, string point)
+ {
+ using var _ = MathS.Settings.DowncastingEnabled.Set(false);
+ // A negative zero for the imaginary part puts a negative base on the other side of
+ // the cut for System.Numerics' power: adding a zero makes it positive.
+ return e.Substitute(x, Number.Real.Create(EDecimal.FromString(point))).Evaled is Number.Complex { IsNaN: false } value
+ ? new System.Numerics.Complex(value.ToNumerics().Real + 0.0, value.ToNumerics().Imaginary + 0.0) : null;
+ }
+ var points = new[] { "0.29", "1.43", "3.17", "0.61", "2.23" };
+ var values = new List<(System.Numerics.Complex Sum, System.Numerics.Complex Sine, System.Numerics.Complex OfTheSine, System.Numerics.Complex[] Bases, System.Numerics.Complex[] OfTheBases)>();
+ var pinnedSum = Pinned(sum);
+ var pinnedSine = Pinned(partner);
+ var pinnedOfTheSine = Pinned(partner.Differentiate(x) / partner);
+ var pinnedBases = common.Select(@base => Pinned(@base)).ToList();
+ var pinnedOfTheBases = common.Select(@base => Pinned(@base.Differentiate(x) / @base)).ToList();
+ var pinnedLeast = new List();
+ foreach (var @base in common)
+ {
+ if (Pinned(least[@base]).Evaled is not Number.Complex exponent || exponent.IsNaN)
+ return null;
+ pinnedLeast.Add((double)exponent.RealPart);
+ }
+ foreach (var point in points)
+ {
+ if (At(pinnedSum, x, point) is not { } value || At(pinnedSine, x, point) is not { } sine || At(pinnedOfTheSine, x, point) is not { } ofTheSine)
+ continue;
+ var bases = new System.Numerics.Complex[common.Count];
+ var ofTheBases = new System.Numerics.Complex[common.Count];
+ var defined = true;
+ for (var k = 0; k < common.Count && defined; k++)
+ {
+ if (At(pinnedBases[k], x, point) is { } b && At(pinnedOfTheBases[k], x, point) is { } l && b != System.Numerics.Complex.Zero)
+ (bases[k], ofTheBases[k]) = (b, l);
+ else
+ defined = false;
+ }
+ if (defined)
+ values.Add((value, sine, ofTheSine, bases, ofTheBases));
+ }
+ if (values.Count < 3)
+ return null;
+ var shifts = new int[common.Count];
+ for (var candidate = 0; candidate < 1 << (2 * common.Count); candidate++)
+ {
+ for (var k = 0; k < common.Count; k++)
+ shifts[k] = ((candidate >> (2 * k)) & 3) - 2;
+ // The quotient of the integrand by G' at each point, which is constant for the one.
+ System.Numerics.Complex? ratio = null;
+ var proportional = true;
+ foreach (var (value, sine, ofTheSine, bases, ofTheBases) in values)
+ {
+ var g = sine;
+ var logarithmic = ofTheSine;
+ for (var k = 0; k < common.Count; k++)
+ {
+ var e = pinnedLeast[k] + shifts[k];
+ g *= System.Numerics.Complex.Pow(bases[k], e);
+ logarithmic += e * ofTheBases[k];
+ }
+ var derivative = g * logarithmic;
+ if (derivative.Magnitude < 1e-30)
+ {
+ proportional = false;
+ break;
+ }
+ var here = value / derivative;
+ if (ratio is null)
+ ratio = here;
+ // Loosely: a point near a zero of a base loses digits in decimals, and a candidate that
+ // is not the one is off by a quotient of functions, not in the eighth digit.
+ else if ((here - ratio.Value).Magnitude > 1e-6 * System.Math.Max(1, ratio.Value.Magnitude))
+ {
+ proportional = false;
+ break;
+ }
+ }
+ if (!proportional || ratio is null || ratio.Value.Magnitude < 1e-12)
+ continue;
+ // The constant: the integrand over G, whole powers only, over G'/G, at cos = 3/5, sin = 4/5.
+ var exponents = common.Select((@base, k) => Functions.PartialFractions.Bare((least[@base] + shifts[k]).Simplify())).ToList();
+ Entity overG = Number.Integer.Zero;
+ for (var t = 0; t < read.Count; t++)
+ {
+ Entity part = read[t].Constant / partner;
+ foreach (var pair in read[t].Powers)
+ {
+ var power = common.IndexOf(pair.Key) is var k and >= 0 ? above[t][pair.Key] - shifts[k] : ((Number.Integer)pair.Value).EInteger.ToInt32Checked();
+ if (power != 0)
+ part *= MathS.Pow(pair.Key, Number.Integer.Create(power));
+ }
+ overG = overG == Number.Integer.Zero ? part : overG + part;
+ }
+ Entity ofG = partner.Differentiate(x) / partner;
+ for (var k = 0; k < common.Count; k++)
+ ofG += exponents[k] * common[k].Differentiate(x) / common[k];
+ Entity? constant = null;
+ foreach (var (cosine, sine) in new[] { (Number.Rational.Create(3, 5), Number.Rational.Create(4, 5)), (Number.Rational.Create(5, 13), Number.Rational.Create(12, 13)) })
+ {
+ var atTheAngle = (overG / ofG).Replace(node => node switch
+ {
+ Sinf(var a) when a.ContainsNode(x) => sine,
+ Cosf(var a) when a.ContainsNode(x) => cosine,
+ Secantf(var a) when a.ContainsNode(x) => 1 / cosine,
+ Cosecantf(var a) when a.ContainsNode(x) => 1 / sine,
+ Tanf(var a) when a.ContainsNode(x) => sine / cosine,
+ Cotanf(var a) when a.ContainsNode(x) => cosine / sine,
+ _ => node,
+ });
+ if (atTheAngle.ContainsNode(x))
+ return null;
+ // Over one bar and in lowest terms by the gcd: simplified, the quotient took seven seconds.
+ var simplified = Functions.PartialFractions.InLowestTermsOverTheSymbols(
+ atTheAngle.Replace(node => node is Providedf(var inner, _) ? inner : node));
+ if (simplified.Nodes.Any(node => node == MathS.NaN) || VanishesIdentically(simplified))
+ continue;
+ constant = simplified;
+ break;
+ }
+ if (constant is null)
+ continue;
+ Entity antiderivative = constant * partner;
+ for (var k = 0; k < common.Count; k++)
+ if (!(exponents[k].Evaled is Number.Complex { IsZero: true }))
+ antiderivative *= exponents[k] == Number.Integer.One ? common[k] : MathS.Pow(common[k], exponents[k]);
+ return antiderivative;
+ }
+ return null;
+ }
+ }
+
///
/// x^m (a + b atan(c x))^p over a power of d (1 +- i c x), under x = tan(u)/c, where
/// 1 + i tan(u) is e^(i u)/cos(u): the integrand in u is
@@ -43838,7 +44366,20 @@ private static Entity WithTheComplementInEvenPowers(Entity expr, Entity u, Entit
.All(factor => !factor.ContainsNode(complement) || factor == complement || factor is Powf(var oddBase, Number.Integer) && oddBase == complement))
continue;
var sign = Variable.CreateUnique(inU, "sgn_c");
- var withTheSign = inU.Replace(node => node == complement ? sign * MathS.Sqrt(1 - MathS.Sqr(uSub)) : node);
+ // Beside a root of 1 - u or 1 + u, the root of 1 - u^2 as the product of the two, which it
+ // is for u in [-1, 1]: Rubi's `sqrt(1 - cos(x))/sqrt(a - cos(x))` is then
+ // `-sgn(sin(x))/(sqrt(1 + u) sqrt(a - u))`, where with the root of `1 - u^2` beside the
+ // root of `1 - u` the quotient of the two was left as written and nothing read it.
+ var besideARootOfOnePlusMinusU = inU.Nodes.Any(node => node is Powf(var radicand, Number.Rational r) && r is not Number.Integer
+ && TreeAnalyzer.TryGetPolyLinear(radicand, uSub, out var rootSlope, out var rootOffset)
+ && !rootSlope.ContainsNode(uSub) && !rootOffset.ContainsNode(uSub)
+ && (Functions.PartialFractions.IsZeroAsAValue((rootSlope - rootOffset).InnerSimplified)
+ || Functions.PartialFractions.IsZeroAsAValue((rootSlope + rootOffset).InnerSimplified))
+ && !Functions.PartialFractions.IsZeroAsAValue(rootOffset));
+ var rootOfTheComplement = besideARootOfOnePlusMinusU
+ ? MathS.Sqrt(1 - uSub) * MathS.Sqrt(1 + uSub)
+ : MathS.Sqrt(1 - MathS.Sqr(uSub));
+ var withTheSign = inU.Replace(node => node == complement ? sign * rootOfTheComplement : node);
if (withTheSign.ContainsNode(x) || !IsAlgebraicIn(withTheSign, uSub))
continue;
withTheSign = withTheSign.Simplify(1);
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
index a35a41750..f50f3bcb6 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
@@ -684,6 +684,17 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
// A symbolic power of a sine, cosine, secant or cosecant times `alpha + beta f^2`, where it is
// the derivative of `cos sin^(m + 1)`, `sin cos^(m + 1)`, `tan sec^m` or `cot csc^m`.
if ((answer = IndefiniteIntegralSolver.SolveASymbolicPowerOfATrigonometricFunctionAsTheDerivativeOfAProduct(expr, x)) is { }) return answer;
+ // And numbers not whole for the exponents, two apart, by the reduction from the highest
+ // down: split, `1/csc(u)^(3/2) - sqrt(csc(u))/3` is two elliptic integrals.
+ if ((answer = IndefiniteIntegralSolver.SolveFractionalPowersOfATrigonometricFunctionTwoApartByTheReduction(expr, x)) is { }) return answer;
+ // And the derivative of a sine raised from nothing times powers of the cosine and sums in it,
+ // or of a cosine beside the sine, alone or beside x by parts: the half angle declined
+ // `(a + a cos(u))^(3/2)/cos(u)^(5/4)`, and parts integrated `x (b + a sin(x))/(a + b sin(x))^2`'s
+ // derivative into a function of tan(x/2) and ran out of time.
+ if ((answer = IndefiniteIntegralSolver.SolveAsTheDerivativeOfASineRaisedFromNothingBesidePowersOfTheCosine(expr, x, integrateByParts)) is { }) return answer;
+ // A cosine beside a function of the sine with a power off the rationals in it, in the sine:
+ // the substitution search declined `cos(x)/(sin(x) + sin(x)^sqrt(2))`.
+ if ((answer = IndefiniteIntegralSolver.SolveACosineBesideAFunctionOfTheSineWithAnIrrationalPowerInIt(expr, x, integrateByParts)) is { }) return answer;
// A power of a sine, cosine or hyperbolic one over a binomial in a power of it, over the
// binomial's roots, before the rules that spent the budget on `sinh(y)^4/(a + b sinh(y)^3)`.
if ((answer = IndefiniteIntegralSolver.SolveAPowerOverABinomialInAPowerOfASineOverItsRoots(expr, x)) is { }) return answer;
@@ -1096,6 +1107,9 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
// A fractional power of a constant times an even power of a function, the function
// taken out with its sign: `(a sin(x)^2)^(5/2)` is `a^(5/2) sgn(sin(x)) sin(x)^5`.
if ((answer = IndefiniteIntegralSolver.SolveByTakingAFunctionOutOfAPowerOfItsEvenPower(expr, x, integrateByParts)) is { }) return answer;
+ // And a symbolic root of a power, `(c sin(u)^n)^(1/n)`, the function out and the rest a
+ // constant written back.
+ if ((answer = IndefiniteIntegralSolver.SolveByTakingAFunctionOutOfASymbolicRootOfItsPower(expr, x, integrateByParts)) is { }) return answer;
// A sum of two roots below the bar, multiplied above and below by its conjugate:
// `1/(sqrt(A) - sqrt(B))` is `(sqrt(A) + sqrt(B))/(A - B)`.
if ((answer = IndefiniteIntegralSolver.SolveByRationalisingASumOfRoots(expr, x, integrateByParts)) is { }) return answer;
diff --git a/Sources/Tests/UnitTests/Calculus/ACosineBesideAFunctionOfTheSineWithAnIrrationalPowerIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/ACosineBesideAFunctionOfTheSineWithAnIrrationalPowerIntegralTest.cs
new file mode 100644
index 000000000..87da685a7
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/ACosineBesideAFunctionOfTheSineWithAnIrrationalPowerIntegralTest.cs
@@ -0,0 +1,49 @@
+//
+// 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 cosine beside a function of the sine with a power of it off the rationals, integrated in the
+ /// sine, and a sine beside the cosine the same: Rubi's 4.7.7 cos(x)/(sin(x) + sin(x)^sqrt(2)).
+ /// #233
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class ACosineBesideAFunctionOfTheSineWithAnIrrationalPowerIntegralTest
+ {
+ [Theory]
+ [InlineData("cos(x)/(sin(x)+sin(x)^sqrt(2))")]
+ [InlineData("cos(c+d*x)/(sin(c+d*x)+sin(c+d*x)^p)")]
+ [InlineData("sin(c+d*x)*cos(c+d*x)^pi*(1+cos(c+d*x))")]
+ public void IsAnsweredAndDifferentiatesBack(string integrand)
+ {
+ var integral = integrand.ToEntity().Integrate("x");
+ var text = integral.Stringize();
+ Assert.DoesNotContain("integral(", text);
+ Entity Pinned(Entity e) => e.Substitute("c", 0.3).Substitute("d", 0.75).Substitute("p", 2.5);
+ var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
+ var original = Pinned(integrand.ToEntity());
+ var compared = 0;
+ foreach (var at in new[] { -2.3, -1.1, -0.7, 0.4, 1.9, 2.8 })
+ {
+ var want = original.Substitute("x", at).EvalNumerical();
+ var got = derivative.Substitute("x", at).EvalNumerical();
+ if (want.IsNaN)
+ continue;
+ compared++;
+ 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}");
+ }
+ Assert.True(compared >= 5, $"only {compared} points could be compared for {integrand}");
+ }
+ }
+}
diff --git a/Sources/Tests/UnitTests/Calculus/AFunctionOutOfASymbolicRootOfItsPowerIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/AFunctionOutOfASymbolicRootOfItsPowerIntegralTest.cs
new file mode 100644
index 000000000..7b1e34d58
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/AFunctionOutOfASymbolicRootOfItsPowerIntegralTest.cs
@@ -0,0 +1,60 @@
+//
+// 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 symbolic root of a constant times a power of a function, (c sin(u)^n)^(1/n), as the
+ /// function times a factor constant between its zeros, written back: Rubi's 4.1.7 and 4.2.0.
+ /// #233
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class AFunctionOutOfASymbolicRootOfItsPowerIntegralTest
+ {
+ [Theory]
+ [InlineData("(c*sin(a+b*x)^n)^(1/n)")]
+ [InlineData("(c*cos(a+b*x)^m)^(1/m)")]
+ [InlineData("x*(c*cos(x)^m)^(1/m)")]
+ [InlineData("(c*sin(x)^n)^(2/n)")]
+ public void IsAnsweredAndDifferentiatesBack(string integrand)
+ {
+ var integral = integrand.ToEntity().Integrate("x");
+ var text = integral.Stringize();
+ Assert.DoesNotContain("integral(", text);
+ Entity Pinned(Entity e) => e.Substitute("a", 0.3).Substitute("b", 0.75).Substitute("c", 1.3).Substitute("n", 2.5).Substitute("m", 0.4);
+ var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
+ var original = Pinned(integrand.ToEntity());
+ var compared = 0;
+ foreach (var at in new[] { -2.3, -1.1, -0.7, 0.4, 1.9, 2.8 })
+ {
+ var want = original.Substitute("x", at).EvalNumerical();
+ var got = derivative.Substitute("x", at).EvalNumerical();
+ if (want.IsNaN)
+ continue;
+ compared++;
+ 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}");
+ }
+ Assert.True(compared >= 5, $"only {compared} points could be compared for {integrand}");
+ }
+
+ [Fact]
+ public void LeavesAPowerOfXToTheRulesForBinomials()
+ {
+ // (c x^n)^(1/n) is the rule's for a symbolic power of a monomial: `x (c x^n)^(1/n)/2`,
+ // whose derivative is `(c x^n)^(1/n)/2 + x (c x^n)^(1/n)/(2 x)`, the integrand on every
+ // branch, since d/dx (c x^n)^(1/n) is (c x^n)^(1/n)/x on every one.
+ var integral = "(c*x^n)^(1/n)".ToEntity().Integrate("x").Stringize();
+ Assert.Equal("x * (c * x ^ n) ^ (1 / n) / 2 + C", integral);
+ }
+ }
+}
diff --git a/Sources/Tests/UnitTests/Calculus/AQuotientInTheTangentWithItsSymbolsCancelledIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/AQuotientInTheTangentWithItsSymbolsCancelledIntegralTest.cs
new file mode 100644
index 000000000..5c3c7bf15
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/AQuotientInTheTangentWithItsSymbolsCancelledIntegralTest.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
+{
+ ///
+ /// A quotient of sines and cosines with symbols in it, written in the tangent with the factors alike
+ /// on both sides of the bar cancelled rather than simplified: Rubi's 4.1.7 and 4.5.7, whose
+ /// simplification ran past the budget.
+ /// #233
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class AQuotientInTheTangentWithItsSymbolsCancelledIntegralTest
+ {
+ [Theory]
+ [InlineData("sin(c+d*x)^6/(a+b*sin(c+d*x)^2)^2")]
+ [InlineData("csc(c+d*x)^2/(a-b*sin(c+d*x)^4)^2")]
+ [InlineData("cot(c+d*x)^2/(a+b*sec(c+d*x)^2)^2")]
+ [InlineData("sin(x)^4/(a+b*sin(x)^2)^2")]
+ public void IsAnsweredAndDifferentiatesBack(string integrand)
+ {
+ var integral = integrand.ToEntity().Integrate("x");
+ var text = integral.Stringize();
+ Assert.DoesNotContain("integral(", text);
+ Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("b", 0.7).Substitute("c", 0.3).Substitute("d", 0.75);
+ var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
+ var original = Pinned(integrand.ToEntity());
+ var compared = 0;
+ foreach (var at in new[] { -2.3, -1.1, -0.7, 0.4, 1.9, 2.8 })
+ {
+ var want = original.Substitute("x", at).EvalNumerical();
+ var got = derivative.Substitute("x", at).EvalNumerical();
+ if (want.IsNaN)
+ continue;
+ compared++;
+ 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}");
+ }
+ Assert.True(compared >= 5, $"only {compared} points could be compared for {integrand}");
+ }
+ }
+}
diff --git a/Sources/Tests/UnitTests/Calculus/ASineRaisedFromNothingBesidePowersOfTheCosineIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/ASineRaisedFromNothingBesidePowersOfTheCosineIntegralTest.cs
new file mode 100644
index 000000000..89d5bb52d
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/ASineRaisedFromNothingBesidePowersOfTheCosineIntegralTest.cs
@@ -0,0 +1,65 @@
+//
+// 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
+{
+ ///
+ /// The derivative of a sine raised from nothing times powers of the cosine, the secant and sums
+ /// in them, or of a cosine beside the sine, alone or beside x by parts: Rubi's 4.2.2.1, 4.5.1.2,
+ /// 4.5.4.2 and 4.7.7.
+ /// #233
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class ASineRaisedFromNothingBesidePowersOfTheCosineIntegralTest
+ {
+ // Points where the cosine is negative too, where the integrand is complex and the answer's
+ // principal powers still differentiate to it.
+ [Theory]
+ [InlineData("(a+a*cos(c+d*x))^(3/2)/cos(c+d*x)^(5/4)")]
+ [InlineData("(a+a*cos(c+d*x))^(3/2)*sec(c+d*x)^(5/4)")]
+ [InlineData("(a+a*sec(c+d*x))^(3/2)/sec(c+d*x)^(1/4)")]
+ [InlineData("(a+a*sec(c+d*x))^n*(-a*A*n-a*Q*(1+n)*sec(c+d*x))/(a*(1+n)*sec(c+d*x)^n)+sec(c+d*x)^(-1-n)*(a+a*sec(c+d*x))^n*(A+Q*sec(c+d*x)^2)")]
+ [InlineData("x*(b+a*sin(x))/(a+b*sin(x))^2")]
+ [InlineData("x*(b+a*cos(c+d*x))/(a+b*cos(c+d*x))^2")]
+ public void IsAnsweredAndDifferentiatesBack(string integrand)
+ {
+ var integral = integrand.ToEntity().Integrate("x");
+ var text = integral.Stringize();
+ Assert.DoesNotContain("integral(", text);
+ Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("b", 0.7).Substitute("c", 0.3).Substitute("d", 0.75)
+ .Substitute("A", 0.6).Substitute("Q", 1.25).Substitute("n", 0.4);
+ var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
+ var original = Pinned(integrand.ToEntity());
+ var compared = 0;
+ foreach (var at in new[] { -2.3, -1.1, -0.7, 0.4, 1.9, 2.8 })
+ {
+ var want = original.Substitute("x", at).EvalNumerical();
+ var got = derivative.Substitute("x", at).EvalNumerical();
+ if (want.IsNaN)
+ continue;
+ compared++;
+ 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}");
+ }
+ Assert.True(compared >= 5, $"only {compared} points could be compared for {integrand}");
+ }
+
+ [Fact]
+ public void IsTheRaisedProductInClosedForm()
+ {
+ // 4 a^2 sin/(cos^(1/4) sqrt(a + a cos)), Rubi's, with no half angle in it.
+ var integral = "(a+a*cos(x))^(3/2)/cos(x)^(5/4)".ToEntity().Integrate("x").Stringize();
+ Assert.DoesNotContain("x / 2", integral);
+ Assert.Contains("cos(x) ^ (-1/4)", integral);
+ }
+ }
+}
diff --git a/Sources/Tests/UnitTests/Calculus/FractionalPowersOfATrigonometricFunctionTwoApartIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/FractionalPowersOfATrigonometricFunctionTwoApartIntegralTest.cs
new file mode 100644
index 000000000..c25a11583
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/FractionalPowersOfATrigonometricFunctionTwoApartIntegralTest.cs
@@ -0,0 +1,65 @@
+//
+// 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 sum of powers of one sine, cosine, secant or cosecant, not whole and two apart, by the
+ /// reduction formula, and beside x by parts once: Rubi's 4.1.10, 4.2.10 and 4.5.4.1, each term
+ /// of which is elliptic on its own.
+ /// #233
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class FractionalPowersOfATrigonometricFunctionTwoApartIntegralTest
+ {
+ // Points on both sides of the function's zeros: the identities hold for the principal
+ // powers where the integrand is complex too.
+ [Theory]
+ [InlineData("x/csc(c+d*x)^(3/2)-1/3*x*sqrt(csc(c+d*x))")]
+ [InlineData("x/csc(c+d*x)^(7/2)-5/21*x*sqrt(csc(c+d*x))")]
+ [InlineData("x/sin(c+d*x)^(7/2)+3/5*x*sqrt(sin(c+d*x))")]
+ [InlineData("x/sec(x)^(5/2)-3/5*x/sqrt(sec(x))")]
+ [InlineData("x/cos(x)^(7/2)+3/5*x*sqrt(cos(x))")]
+ [InlineData("(3+3*sec(c+d*x)^2)/sec(c+d*x)^(1/2)")]
+ [InlineData("1/csc(x)^(7/2)-5/21*sqrt(csc(x))")]
+ [InlineData("(a+b*x)*(cos(x)^(1/3)+2*cos(x)^(-5/3))")]
+ public void IsAnsweredAndDifferentiatesBack(string integrand)
+ {
+ var integral = integrand.ToEntity().Integrate("x");
+ var text = integral.Stringize();
+ Assert.DoesNotContain("integral(", text);
+ Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("b", 0.7).Substitute("c", 0.3).Substitute("d", 0.75);
+ var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
+ var original = Pinned(integrand.ToEntity());
+ var compared = 0;
+ foreach (var at in new[] { -2.3, -1.1, -0.7, 0.4, 1.9, 2.8 })
+ {
+ var want = original.Substitute("x", at).EvalNumerical();
+ var got = derivative.Substitute("x", at).EvalNumerical();
+ if (want.IsNaN)
+ continue;
+ compared++;
+ 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}");
+ }
+ Assert.True(compared >= 5, $"only {compared} points could be compared for {integrand}");
+ }
+
+ [Fact]
+ public void IsNotClaimedBesideTheSquareOfX()
+ {
+ // By parts the remainder is x times the sum's antiderivative, elliptic again.
+ var integral = "x^2*(1/sec(x)^(3/2)-1/3*sqrt(sec(x)))".ToEntity().Integrate("x").Stringize();
+ Assert.Contains("integral(", integral);
+ }
+ }
+}
diff --git a/Sources/Tests/UnitTests/Calculus/TheRootOfOneMinusTheCosineOverTheSignOfTheSineIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/TheRootOfOneMinusTheCosineOverTheSignOfTheSineIntegralTest.cs
new file mode 100644
index 000000000..a979d6f9d
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/TheRootOfOneMinusTheCosineOverTheSignOfTheSineIntegralTest.cs
@@ -0,0 +1,49 @@
+//
+// 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 root of 1 - cos(x) beside a function of the cosine, under u = cos(x) with the sine
+ /// written as its sign times sqrt(1 - u) sqrt(1 + u): Rubi's 4.2.2.1
+ /// sqrt(1 - cos(x))/sqrt(a - cos(x)), which is -2 sgn(sin(x)) arctan(sqrt(1 + cos(x))/sqrt(a - cos(x))).
+ /// #233
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class TheRootOfOneMinusTheCosineOverTheSignOfTheSineIntegralTest
+ {
+ [Theory]
+ [InlineData("sqrt(1-cos(x))/sqrt(a-cos(x))")]
+ [InlineData("sqrt(1+cos(c+d*x))/sqrt(a+cos(c+d*x))")]
+ public void IsAnsweredAndDifferentiatesBack(string integrand)
+ {
+ var integral = integrand.ToEntity().Integrate("x");
+ var text = integral.Stringize();
+ Assert.DoesNotContain("integral(", text);
+ Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("c", 0.3).Substitute("d", 0.75);
+ var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
+ var original = Pinned(integrand.ToEntity());
+ var compared = 0;
+ foreach (var at in new[] { -2.3, -1.1, -0.7, 0.4, 1.9, 2.8 })
+ {
+ var want = original.Substitute("x", at).EvalNumerical();
+ var got = derivative.Substitute("x", at).EvalNumerical();
+ if (want.IsNaN)
+ continue;
+ compared++;
+ 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}");
+ }
+ Assert.True(compared >= 5, $"only {compared} points could be compared for {integrand}");
+ }
+ }
+}