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
20 changes: 20 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -1371,6 +1371,26 @@ and those rows want partial fractions over the two linear factors instead.

Rubi's 4.7.2: 215 to 237 of 290, no row lost, 20 timeouts to 16.

### `A cos + i A sin` below the bar is written as the exponential it is

`cos(x)^2/(a cos(x) + i a sin(x))^3` was declined after twelve seconds. `A cos(y) + i A sin(y)` is
`A e^(i y)`, and `A cos(y) - i A sin(y)` is `A e^(-i y)`; written so, the integrand is an
exponential times a power of a cosine, which the closed rules answer at once. As a sum of a
cosine and a sine it has no real rotation -- `A^2 + (i A)^2` is zero -- so the rule for
`a cos + b sin` has nothing to do with it. A whole power of the product written is split,
`(A e^(i y))^n` as `A^n e^(i n y)`, since the rule that distributes such powers takes a symbolic
constant and leaves a number: `cos(x)^2/(2 e^(i x))^3` was declined where
`cos(x)^2/(a e^(i x))^3` was not.

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"cos(x)^2/(a*cos(x)+i*a*sin(x))^3".Integrate("x")` | left unevaluated | exponentials of `-3 i x` times `cos(2x)`, `sin(2x)` |
| `"1/(a*cos(x)+i*a*sin(x))^3".Integrate("x")` | left unevaluated | `a^(-3) e^(-3 i x)/(-3 i)` |

A secant above the bar is still declined: `sec(x)^2 e^(-3 i x)` is rational in `e^(i x)` with an
imaginary rate, which nothing answers yet. Rubi's 4.7.2: 237 to 249 of 290, no row lost, 16
timeouts to 14.

### `binomial(n, k)` is a function

**Addition, not silent.** The binomial coefficient is a node, `Entity.Binomialf`, spelled
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15432,6 +15432,54 @@ static bool IsAWholeNumberOfSteps(ERational difference)
return rewritten == below ? null : Integration.ComputeAsTheSameQuestion((above / rewritten).InnerSimplified, x, integrateByParts);
}

/// <summary>
/// <c>A cos(y) + i A sin(y)</c> below the bar, written as the exponential it is:
/// <c>A e^(i y)</c>, and <c>A cos(y) - i A sin(y)</c> as <c>A e^(-i y)</c>. Beside a power
/// of the cosine above the bar that is an exponential times a power of a cosine, which
/// the closed rules answer at once; as written it is a sum of a cosine and a sine whose
/// coefficients have no real rotation -- <c>A^2 + (i A)^2</c> is zero -- and
/// <c>cos(x)^2/(a cos(x) + i a sin(x))^3</c> was declined after twelve seconds.
/// </summary>
/// <remarks>
/// The sibling of <see cref="SolveByWritingAnImaginaryTangentAsAnExponential"/>, which reads
/// <c>A + i A tan(y)</c>; below the bar only, for the same reason.
/// https://github.com/asc-community/AngouriMath/issues/718
/// </remarks>
internal static Entity? SolveByWritingAnImaginarySumOfACosineAndASineAsAnExponential(Entity expr, Entity.Variable x, bool integrateByParts)
{
var (above, below) = Functions.SingleQuotient.Of(Functions.SingleQuotient.Combine(expr));
if (!below.ContainsNode(x))
return null;
var rewritten = below.Replace(node =>
{
if (node is not (Sumf or Minusf) || !node.ContainsNode(x)
|| !TryReadACosineAndASine(node, x, out var cosineCoefficient, out var sineCoefficient, out var argument))
return node;
// The ratio is the imaginary unit one way or the other, and nothing else. Bare:
// `i a/a` simplifies to `i provided not a = 0`, and a condition is not a number
// to compare against.
var ratio = Functions.PartialFractions.Bare((sineCoefficient / cosineCoefficient).InnerSimplified);
if (ratio.Evaled is not Number.Complex)
ratio = Functions.PartialFractions.Bare(ratio.Simplify());
var plus = ratio.Evaled == MathS.i.Evaled;
if (!plus && ratio.Evaled != (-MathS.i).Evaled)
return node;
return cosineCoefficient * MathS.Pow(MathS.e, ((plus ? MathS.i : -MathS.i) * argument).InnerSimplified);
});
if (rewritten == below)
return null;
// And a whole power of what was written split, `(A e^(i y))^n` as `A^n e^(i n y)`, so
// that the exponential stands on its own for the rules that read one: the rule that
// distributes such powers takes a constant with a symbol in it and leaves a number,
// and `cos(x)^2/(2 e^(i x))^3` was declined where `cos(x)^2/(a e^(i x))^3` was not.
rewritten = rewritten.Replace(node =>
node is Powf(Mulf(var left, var right), Number.Integer power)
&& (left is Powf(var leftBase, _) && leftBase == MathS.e || right is Powf(var rightBase, _) && rightBase == MathS.e)
? MathS.Pow(left, power) * MathS.Pow(right, power)
: node);
return Integration.ComputeAsTheSameQuestion((above / rewritten).InnerSimplified, x, integrateByParts);
}

/// <summary>
/// Bioche's first two rules for the hyperbolic functions: a rational function of
/// <c>sinh(y)</c> and <c>cosh(y)</c> that is odd in the hyperbolic sine is a rational
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,8 @@ private static Entity Normalized(Entity expr, Entity.Variable x) =>
// `A + i A tan(z)` is `A e^(i z)/cos(z)`, which beside a polynomial is a shape the
// closed rules answer, where the imaginary unit in the coefficient is read by none.
if ((answer = IndefiniteIntegralSolver.SolveByWritingAnImaginaryTangentAsAnExponential(expr, x, integrateByParts)) is { }) return answer;
// And `A cos(z) + i A sin(z)`, which is `A e^(i z)`, where no rotation is real.
if ((answer = IndefiniteIntegralSolver.SolveByWritingAnImaginarySumOfACosineAndASineAsAnExponential(expr, x, integrateByParts)) is { }) return answer;
// A fractional power of a perfect square is the power of the modulus, sgn(P) P^(2r).
if ((answer = IndefiniteIntegralSolver.SolveByTakingARootOfAPerfectSquare(expr, x, integrateByParts)) is { }) return answer;
// x^(n - 1) g(x^n) with a symbolic n is g(u)/n under u = x^n.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//
//
// Copyright (c) 2019-2026 Angouri.
// AngouriMath is licensed under MIT.
// Details: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.
Expand Down Expand Up @@ -263,6 +263,37 @@ public void TheResonanceIsSeenSymbolically(string integrand, string? symbol)
[InlineData("(1 + 2*x)/(3 + 3*i*tan(1/2 + x))^2")]
public void AnImaginaryTangentBelowTheBarIsAnExponential(string integrand) => DifferentiatesBack(integrand);

/// <summary>
/// And its sibling, <c>A cos(y) + i A sin(y)</c>, which is <c>A e^(i y)</c>: a sum of a
/// cosine and a sine with no real rotation, since <c>A^2 + (i A)^2</c> is zero, and beside
/// a power of the cosine an exponential times that power once written so. Rubi's 4.7.2,
/// fourteen rows; <c>cos(x)^2/(a cos(x) + i a sin(x))^3</c> was declined after twelve
/// seconds.
/// </summary>
[Theory]
[InlineData("cos(x)^2/(2*cos(x) + 2*i*sin(x))^3")]
[InlineData("cos(x)^5/(2*cos(x) + 2*i*sin(x))^2")]
[InlineData("1/(3*cos(1/2 + x) + 3*i*sin(1/2 + x))^3")]
[InlineData("cos(x)/(2*cos(x) - 2*i*sin(x))")]
public void AnImaginarySumOfACosineAndASineBelowTheBarIsAnExponential(string integrand) => DifferentiatesBack(integrand);

/// <summary>The same with the coefficient a symbol, pinned only after integrating.</summary>
[Fact]
public void AnImaginarySumWithASymbolicCoefficient()
{
var integral = "cos(x)^2/(a*cos(x) + i*a*sin(x))^3".ToEntity().Integrate("x");
Assert.DoesNotContain("integral(", integral.Stringize());
var derivative = integral.Substitute("C", 0).Substitute("a", 0.9).Differentiate("x");
var original = "cos(x)^2/(a*cos(x) + i*a*sin(x))^3".ToEntity().Substitute("a", 0.9);
foreach (var at in Points)
{
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);
Assert.True(difference < 1e-9, $"at x = {at}: {got} for {want}");
}
}

/// <summary>
/// A base with symbols in it, and an exponent whose constant part has them too:
/// <c>F^(c (a + b x))</c> leaves <c>F^(a c)</c> as a constant factor, and that factor
Expand Down
Loading