Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,20 @@ of a linear over a linear, which is answered in a second
| `"1/(sqrt(a + i*a*tan(x))*(c + d*tan(x))^(3/2))".ToEntity().Integrate("x")` | `integral(...)`; past thirty seconds on the unreleased master | 2,217 characters |
| `"1/((a + i*a*tan(x))^(3/2)*(c + d*tan(x))^(5/2))".ToEntity().Integrate("x")` | `integral(...)`; past thirty seconds on the unreleased master | 3,333 characters |

### A power of a cosine over `a + a sin` is cancelled to a polynomial in the sine

**Answers where there were none.** `cos(c + d x)^2/((e + f x) (a + a sin(c + d x)))` was left unevaluated, and so were three
more of Rubi's 4.1.10. `cos^2/(1 + sin)` is `1 - sin` wherever it is defined, so the integrand becomes a sine or cosine
over a power of a linear. That is answered with `Si` and `Ci`
([#718](https://github.com/asc-community/AngouriMath/issues/718)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"cos(c+d*x)^2/((pe+f*x)*(a+a*sin(c+d*x)))".ToEntity().Integrate("x")` | `integral(...)` | 132 characters |
| `"cos(c+d*x)^2/((pe+f*x)^2*(a+a*sin(c+d*x)))".ToEntity().Integrate("x")` | `integral(...)` | 413 characters |
| `"cos(c+d*x)^3/((pe+f*x)*(a+a*sin(c+d*x)))".ToEntity().Integrate("x")` | `integral(...)` | 246 characters |
| `"cos(c+d*x)^3/((pe+f*x)^2*(a+a*sin(c+d*x)))".ToEntity().Integrate("x")` | `integral(...)` | 790 characters |

### Every linear beside a block is split off, a quadratic with rational roots among them

**Answers where there were none.** `sec(c + d x)/(a + b sin(c + d x)^3)` was declined, and so were
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26713,6 +26713,77 @@ static void Add(Dictionary<int, ERational> into, int k, ERational value)
return answer.Nodes.Any(node => node == MathS.NaN) ? null : answer;
}

/// <summary>
/// A power of a cosine over a power of <c>k (1 +- sin(y))</c>, or of a sine over one of
/// <c>k (1 +- cos(y))</c>, cancelled where the power above is at least twice the one below:
/// <c>cos^2 = (1 - sin)(1 + sin)</c>, so <c>cos(y)^m/(k (1 + sin(y)))^n</c> is
/// <c>cos(y)^(m - 2n) (1 - sin(y))^n/k^n</c>. What else is beside it stays as it is.
/// </summary>
/// <remarks>
/// Rubi's 4.1.10 <c>cos(c + d x)^2/((e + f x)(a + a sin(c + d x)))</c> was declined, and
/// <c>(1 - sin(c + d x))/(e + f x)</c> is answered in sine and cosine integrals.
/// https://github.com/asc-community/AngouriMath/issues/718
/// </remarks>
internal static Entity? SolveACosinePowerOverABinomialInTheSineByCancelling(Entity expr, Entity.Variable x)
{
if (!Integration.AnsweringTheQuestionAsked)
return null;
static IEnumerable<(Entity Factor, bool Below)> Factors(Entity e, bool below) => e switch
{
Mulf(var l, var r) => Factors(l, below).Concat(Factors(r, below)),
Divf(var l, var r) => Factors(l, below).Concat(Factors(r, !below)),
_ => new[] { (e, below) }
};
var factors = Factors(expr, false).ToList();
foreach (var (candidate, isBelow) in factors)
{
if (!isBelow)
continue;
var (binomial, n) = candidate is Powf(var b0, Number.Integer { EInteger: var p0 }) && p0.Sign > 0 && p0.CanFitInInt32()
? (b0, p0.ToInt32Unchecked()) : (candidate, 1);
// k (1 + sigma f(y)), f a sine or a cosine.
var functions = binomial.Nodes.Where(node => node is Sinf or Cosf && node.ContainsNode(x)).Distinct().ToList();
if (functions.Count != 1)
continue;
var function = functions[0];
var s0 = Variable.CreateUnique(expr, "s_cs");
if (!TreeAnalyzer.TryGetPolyLinear(binomial.Substitute(function, s0), s0, out var slope, out var k)
|| slope is null || k is null || slope.ContainsNode(x) || k.ContainsNode(x)
|| Functions.PartialFractions.IsZeroAsAValue(k))
continue;
var sigma = (slope / k).InnerSimplified;
int sign;
if (Functions.PartialFractions.IsZeroAsAValue((sigma - 1).InnerSimplified)) sign = 1;
else if (Functions.PartialFractions.IsZeroAsAValue((sigma + 1).InnerSimplified)) sign = -1;
else continue;
var argument = function is Sinf(var ys) ? ys : ((Cosf)function).Argument;
Entity complement = function is Sinf ? MathS.Cos(argument) : MathS.Sin(argument);
// The complement's power above the bar.
var m = 0;
foreach (var (factor, below) in factors)
if (!below && (factor == complement ? 1 : factor is Powf(var b1, Number.Integer { EInteger: var p1 }) && b1 == complement && p1.CanFitInInt32() ? p1.ToInt32Unchecked() : 0) is var power and > 0)
m += power;
if (m < 2 * n)
continue;
// Everything but the binomial and the complement's powers, times what the cancelling leaves.
Entity rest = Number.Integer.One;
foreach (var (factor, below) in factors)
{
if (below && factor == candidate)
continue;
if (!below && (factor == complement || factor is Powf(var b2, Number.Integer) && b2 == complement))
continue;
rest = below ? rest / factor : rest * factor;
}
var left = m - 2 * n;
Entity cancelled = (left == 0 ? Number.Integer.One : left == 1 ? complement : MathS.Pow(complement, Number.Integer.Create(left)))
* (n == 1 ? 1 - sign * function : MathS.Pow(1 - sign * function, Number.Integer.Create(n)))
/ (n == 1 ? k : MathS.Pow(k, Number.Integer.Create(n)));
return Integration.ComputeAsAQuestionOfItsOwn((rest * cancelled).InnerSimplified, x, integrateByParts: true);
}
return null;
}

/// <summary>
/// A rational function of a sine, or of a cosine, over a quadratic in it with a symbol
/// among the coefficients: <c>sin(x)/(a + b sin(x) + c sin(x)^2)</c>, and the same with
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,8 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
// 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;
// A power of a cosine over a power of k (1 +- sin), cancelled: `cos^2/((e + f x)(a + a sin))`.
if ((answer = IndefiniteIntegralSolver.SolveACosinePowerOverABinomialInTheSineByCancelling(expr, x)) is { }) return answer;
// A function of an inverse function of c + d x beside powers of multiples of it, under
// u = c + d x, before the rules that timed out on `(c e + d e x)^3 (a + b arsinh(c + d x))^2`.
if ((answer = IndefiniteIntegralSolver.SolveAFunctionOfAnInverseOfALinearBesideItsPowersUnderTheLinear(expr, x)) is { }) return answer;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//
// 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
{
/// <summary>
/// A power of a cosine over a power of <c>k (1 +- sin)</c>, cancelled: Rubi's 4.1.10
/// <c>cos(c + d x)^2/((e + f x)(a + a sin(c + d x)))</c>, which was declined.
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class ACosinePowerOverABinomialInTheSineIntegralTest
{
private static void DifferentiatesBack(string integrand, params (string, double)[] pins)
{
var integral = integrand.ToEntity().Integrate("x");
Assert.DoesNotContain("integral(", integral.Stringize());

var pinned = integral.Substitute("C", 0);
Entity original = integrand.ToEntity();
foreach (var (name, value) in pins)
{
pinned = pinned.Substitute(name, value);
original = original.Substitute(name, value);
}
var derivative = pinned.Differentiate("x");
foreach (var at in new[] { -2.3, -0.7, 0.4, 1.9 })
{
var got = derivative.Substitute("x", at).EvalNumerical();
var want = original.Substitute("x", at).EvalNumerical();
var difference = Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart);
var scale = Math.Max(1.0, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart));
Assert.True(difference / scale < 1e-8,
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
}
}

// The linear's root kept off the sampled points.
[Theory]
[InlineData("cos(c+d*x)^2/((k+f*x)*(a+a*sin(c+d*x)))")]
[InlineData("cos(c+d*x)^3/((k+f*x)^2*(a+a*sin(c+d*x)))")]
[InlineData("sin(c+d*x)^2/((k+f*x)*(a-a*cos(c+d*x)))")]
public void IsAnsweredAndDifferentiatesBack(string integrand)
{
DifferentiatesBack(integrand, ("a", 1.3), ("c", 0.7), ("d", 1.07), ("k", 5), ("f", 0.3));
DifferentiatesBack(integrand, ("a", -0.5), ("c", -0.25), ("d", 1.5), ("k", -6), ("f", 0.4));
}
}
}
Loading