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
16 changes: 16 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,22 @@ the division's; with it on, the rounding is the setting's own and stays
| `"(-60.5)!"` at 30 digits, downcasting off | `5.86118478907722232671451280188E-81` | `2.93059239453861116335725639905E-81` |
| `"abs(75 + 316.22776601683796i)"`, downcasting off | `325` | `325.000000000000026076735749…` |

### `a + i a tan` beside the tangent's differential is lowered over its conjugate

**Answers where there were none.** `sqrt(a + i a tan(x))/(c + d tan(x))^(3/2)` ran past thirty seconds:
under `u = tan(x)` it is `sqrt(a + i a u)/((c + d u)^(3/2) (1 + u^2))`, a search, where
`(a + i a u)(a - i a u)` is `a^2 (1 + u^2)` and the sum's power is lowered by one over its conjugate, a root
of a linear over a linear, which is answered in a second
([#718](https://github.com/asc-community/AngouriMath/issues/718)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"sqrt(a + i*a*tan(x))/(c + d*tan(x))^(3/2)".ToEntity().Integrate("x")` | `integral(...)`; past thirty seconds on the unreleased master | 1,893 characters |
| `"(a + i*a*tan(x))^(3/2)*(c + d*tan(x))^(5/2)".ToEntity().Integrate("x")` | `integral(...)`; past thirty seconds on the unreleased master | 670 characters |
| `"sqrt(a + i*a*tan(x))/sqrt(c + d*tan(x))".ToEntity().Integrate("x")` | `integral(...)` | 169 characters |
| `"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 |

### One linear twice is not two linear powers

**Answers that had no value.** `(a c + b c x)^(-3 - 2 p) (f + g x) (a^2 + 2 a b x + b^2 x^2)^p` was answered
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10360,8 +10360,14 @@ static bool IsTwice(Entity argument, Entity.Variable x)
// none: `1/(u^2*(1 + u^2))` is integrated and `(1/u)^2/(1 + u^2)`, which is the same
// expression, is not. The half-angle and exponential substitutions comb their
// integrands for the same reason.
var integrand = Functions.SingleQuotient
.Combine(inU / (1 + MathS.Sqr(uSub))).Simplify();
// A power of A + i A u beside the differential: (A + i A u)(A - i A u) is A^2 (1 + u^2),
// so 1/(1 + u^2) is A^2/((A + i A u)(A - i A u)) and the sum's power is lowered by one,
// a root of a linear over a linear. `sqrt(a + i a tan(x))/(c + d tan(x))^(3/2)` was a
// search past thirty seconds as a quotient by 1 + u^2, and in the sum's conjugate it is
// answered in a second.
var integrand = ByTheSumsConjugate(inU, uSub) is { } overTheConjugate
? overTheConjugate
: Functions.SingleQuotient.Combine(inU / (1 + MathS.Sqr(uSub))).Simplify();
if (integrand is Providedf(var withoutCondition, _))
integrand = withoutCondition;
// Asked as the same question: the rewrite renames the variable and adds no step of
Expand All @@ -10373,6 +10379,34 @@ static bool IsTwice(Entity argument, Entity.Variable x)
? result.Substitute(uSub, tangent)
: null;
}

/// <summary>
/// <c>inU/(1 + u^2)</c> written with the conjugate of a factor <c>A +- i A u</c> of
/// <paramref name="inU"/>: <c>inU/(A +- i A u) A^2/(A -+ i A u)</c>. Null where no factor is
/// such a sum.
/// </summary>
private static Entity? ByTheSumsConjugate(Entity inU, Entity.Variable u)
{
// A reciprocal of a product read as the quotient it is: a constant taken out of the
// sum's root leaves `(sqrt(1 + i u) (c + d u)^(3/2))^(-1)`, whose factors are below the bar.
if (inU is Powf(var reciprocal, Number.Integer { EInteger: var minusOne }) && minusOne.Equals(EInteger.FromInt32(-1)))
inU = 1 / reciprocal;
foreach (var (factor, _) in FactorsOfTheIntegrand(inU))
{
// A root of the sum, not a whole power: a polynomial in it beside the cosine is the
// rules' for the sine and the cosine, which answer `cos(x)^6 (a + i a tan(x))` sooner.
if (factor is not Powf(var sum, Number.Rational { ERational.Denominator: var denominator }) || denominator.Equals(EInteger.One))
continue;
if (sum is not Sumf and not Minusf || !TreeAnalyzer.TryGetPolyLinear(sum, u, out var slope, out var intercept)
|| TreeAnalyzer.IsZero(intercept) || TreeAnalyzer.IsZero(slope))
continue;
if (!TreeAnalyzer.IsZero((slope - MathS.i * intercept).Simplify()) && !TreeAnalyzer.IsZero((slope + MathS.i * intercept).Simplify()))
continue;
var conjugate = intercept - slope * u;
return (inU / sum * MathS.Sqr(intercept) / conjugate).InnerSimplified;
}
return null;
}
/// <summary>
/// <c>(prod f_i^(k_i))^(p/q)</c>, <c>q</c> odd, as <c>prod f_i^(k_i p/q)</c> where every
/// <c>f_i</c> is a trigonometric function of <paramref name="x"/> itself or free of it,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//
// 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 <c>a + i a tan(x)</c> beside a root of <c>c + d tan(x)</c>. Under <c>u = tan(x)</c>,
/// <c>dx = du/(1 + u^2)</c> and <c>(a + i a u)(a - i a u) = a^2 (1 + u^2)</c>, so the sum's power is
/// lowered by one over its conjugate: a root of a linear over a linear. Rubi's 4.3.2.1. The
/// integrands are complex for a real <c>x</c> and are compared as complex numbers.
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class ImaginaryTangentSumBesideARootOfALinearInTheTangentIntegralTest
{
[Theory]
[InlineData("sqrt(a + i*a*tan(x))/(c + d*tan(x))^(3/2)")]
[InlineData("(a + i*a*tan(x))^(3/2)*(c + d*tan(x))^(5/2)")]
[InlineData("sqrt(a + i*a*tan(x))/sqrt(c + d*tan(x))")]
[InlineData("1/(sqrt(a + i*a*tan(x))*(c + d*tan(x))^(3/2))")]
[InlineData("1/((a + i*a*tan(x))^(3/2)*(c + d*tan(x))^(5/2))")]
public void OverTheSumsConjugate(string integrand)
{
var integral = integrand.ToEntity().Integrate("x");
var text = integral.Stringize();
Assert.DoesNotContain("integral(", text);
Assert.True(text.Length < 5000, $"{text.Length} characters of answer for {integrand}");
Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("c", 0.7).Substitute("d", 1.1);
var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
var original = Pinned(integrand.ToEntity());
var compared = 0;
foreach (var at in new[] { -1.2, -0.7, 0.3, 0.8, 1.3, 2.9 })
{
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}");
}
}
}
Loading