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
18 changes: 18 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,24 @@ rules' already
| `"asin(sqrt(1 + x) - sqrt(x))".ToEntity().Integrate("x")` | `integral(...)` | an antiderivative in `arcsin(sqrt(1 + x) - sqrt(x))` |
| `"x^3*atan(-sqrt(x)+sqrt(1+x))".ToEntity().Integrate("x")` | `integral(...)` | an antiderivative in the arctangent |

### A sum of two squares below the bar, beside a repeated factor, is split over its conjugates

**Answers where there were none.** `(c + d tan(x))^(3/2)/(a + b tan(x))^2` was declined in 2.5.0 and ran
past two minutes since. Under `u = tan(x)` and `t = sqrt(c + d u)` the `1 + u^2` the tangent leaves is
`(t^2 - c)^2 + d^2`, a quartic beside the square of `a d + b (t^2 - c)`, and the Hermite reduction
solved for the numerators over it with the symbols in every entry. A written sum of two squares
`P^2 + Q^2`, the greater of their degrees two, beside a repeated factor, is `(P - i Q)(P + i Q)` below
the bar now, and over those every factor is one the split by residues reads -- under a root of the
quotient of two such linears the sum is `(b - d t^2)^2 + (c t^2 - a)^2`. Rubi's 4.3.2.1
([#718](https://github.com/asc-community/AngouriMath/issues/718)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"(c + d*tan(x))^(3/2)/(a + b*tan(x))^2".ToEntity().Integrate("x")` | `integral(...)`; past two minutes on the unreleased master | arctangents and logarithms of `sqrt(c + d tan(x))`, in two seconds |
| `"(c + d*tan(x))^(5/2)/(a + b*tan(x))^2".ToEntity().Integrate("x")` | `integral(...)` | the same |
| `"(c + d*tan(x))^(3/2)/(a + b*tan(x))^3".ToEntity().Integrate("x")` | `integral(...)` | the same |
| `"1/((a + b*tan(x))^(3/2)*(c + d*tan(x))^(3/2))".ToEntity().Integrate("x")` | `integral(...)`; past a minute on the unreleased master | in the root of the quotient of the two, in a second |

### A function of `x^n` for a symbolic `n` beside a power of `x` is integrated in a power of `x`

**Answers where there were none.** `x^(-1 + 4n)/(a + b x^n + c x^(2n))` and the rest of Rubi's
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,19 @@ is var (multiple, leftover)
?? Integration.ComputeIndefiniteIntegral(inY, y, integrateByParts)) is { } inTheShiftedVariable)
return inTheShiftedVariable.Substitute(y, (x + shift).InnerSimplified);

// A written factor that is a sum of two squares, `P^2 + Q^2` with the greater of their
// degrees two, beside a repeated factor: it is `(P - i Q)(P + i Q)`, and over those two
// the split by residues above reads every factor. `1 + u^2` beside a root of a
// linear is `(t^2 - c)^2 + d^2` under `t = sqrt(c + d u)`, a quartic nothing splits, and
// the Hermite reduction below solved for its numerators with the symbols in every entry:
// `(c + d tan(x))^(3/2)/(a + b tan(x))^2` ran past two minutes. Once: what this writes
// has no such factor left.
if (Mulf.LinearChildren(denominator).Any(f => f.ContainsNode(x) && f is Powf(_, Number.Integer { EInteger.Sign: > 0 } e) && e != Number.Integer.One)
&& OverTheConjugatesOfASumOfTwoSquares(denominator, x) is { } overTheConjugates
&& (SolveByPartialFractions(numerator / overTheConjugates, x, integrateByParts)
?? Integration.ComputeIndefiniteIntegral(numerator / overTheConjugates, x, integrateByParts)) is { } overConjugates)
return overConjugates;

// A denominator with a written repeated factor takes the Hermite reduction first:
// the rational part of the answer in one linear solve, and what is left is a proper
// fraction over a squarefree denominator for the splits below. `(1 + x^2)/(x (1 + x^3)^2)`
Expand Down Expand Up @@ -14363,6 +14376,43 @@ private static bool IsASumOfMonomials(Entity expr, Entity.Variable x)
return changed ? product : null;
}

/// <summary>
/// <paramref name="denominator"/> with every written factor <c>P^2 + Q^2</c>, <c>P</c> and
/// <c>Q</c> polynomials in <paramref name="x"/> the greater of whose degrees is two, written
/// as <c>(P - i Q)(P + i Q)</c>, with its power; <see langword="null"/> where there is none,
/// or where nothing in the quotient is a symbol: with numbers alone the split over the reals
/// reads a biquadratic as it is. Not of the first degree: <c>(a + b x)^2 + k^2</c> is a
/// quadratic every rule reads, by an arctangent.
/// </summary>
private static Entity? OverTheConjugatesOfASumOfTwoSquares(Entity denominator, Entity.Variable x)
{
static int? DegreeOfAPolynomial(Entity expr, Entity.Variable x)
=> !expr.ContainsNode(x) ? 0
: TreeAnalyzer.TryGetPolynomial(expr, x, out var read) && read.Count > 0 && read.Values.All(coefficient => !coefficient.ContainsNode(x))
&& read.Keys.All(power => power.Sign >= 0 && power.CanFitInInt32())
? read.Keys.Max()!.ToInt32Unchecked() : null;
if (!denominator.Vars.Any(symbol => symbol != x))
return null;
var changed = false;
Entity product = Number.Integer.One;
foreach (var factor in Mulf.LinearChildren(denominator))
{
var (@base, power) = factor is Powf(var b, Number.Integer { EInteger.Sign: > 0 } p) ? (b, p) : (factor, Number.Integer.One);
if (@base is Sumf(Powf(var first, Number.Integer(2)), Powf(var second, Number.Integer(2)))
&& (first.ContainsNode(x) || second.ContainsNode(x))
&& DegreeOfAPolynomial(first, x) is { } firstDegree && DegreeOfAPolynomial(second, x) is { } secondDegree
&& System.Math.Max(firstDegree, secondDegree) == 2)
{
var (minus, plus) = (first - MathS.i * second, first + MathS.i * second);
product *= power == Number.Integer.One ? minus * plus : MathS.Pow(minus, power) * MathS.Pow(plus, power);
changed = true;
continue;
}
product *= factor;
}
return changed ? product : null;
}

/// <summary>
/// <paramref name="denominator"/> with every quadratic factor that has a root off the real line
/// in common with a linear factor beside it written as its leading coefficient times the
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//
// 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 sum of two squares below the bar beside a repeated factor, written over its two
/// conjugates. Under <c>u = tan(x)</c> and <c>t = sqrt(c + d u)</c>, the <c>1 + u^2</c> of
/// Rubi's 4.3.2.1 <c>(c + d tan(x))^(3/2)/(a + b tan(x))^2</c> is <c>(t^2 - c)^2 + d^2</c>,
/// a quartic beside the square of <c>a d + b (t^2 - c)</c>; the Hermite reduction solved for
/// its numerators with the symbols in every entry and ran past two minutes. Written as
/// <c>(t^2 - c - i d)(t^2 - c + i d)</c>, every factor is one the split by residues reads.
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
/// </summary>
/// <remarks>
/// Under a root of the quotient of two such linears the sum is <c>(b - d t^2)^2 + (c t^2 - a)^2</c>,
/// both parts in <c>t</c>, and its conjugates are quadratics as well. The integrands are real
/// where <c>a + b tan(x)</c> and <c>c + d tan(x)</c> are positive, which the points are.
/// </remarks>
[Trait("Area", "Calculus")]
public sealed class SumOfTwoSquaresBelowTheBarIntegralTest
{
[Theory]
[InlineData("(c + d*tan(x))^(3/2)/(a + b*tan(x))^2")]
[InlineData("(c + d*tan(x))^(5/2)/(a + b*tan(x))^2")]
[InlineData("(c + d*tan(x))^(3/2)/(a + b*tan(x))^3")]
[InlineData("x^4/((a*d + (x^2 - c)*b)^2*((x^2 - c)^2 + d^2))")]
[InlineData("1/((a + b*tan(x))^(3/2)*(c + d*tan(x))^(3/2))")]
[InlineData("1/((a + b*tan(x))^(5/2)*(c + d*tan(x))^(3/2))")]
public void OverItsConjugates(string integrand)
{
var integral = integrand.ToEntity().Integrate("x");
Assert.DoesNotContain("integral(", integral.Stringize());
Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("b", 0.7).Substitute("c", 1.1).Substitute("d", 0.6);
var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
var original = Pinned(integrand.ToEntity());
foreach (var at in new[] { 0.2, 0.5, 0.9, 1.2 })
{
var want = original.Substitute("x", at).EvalNumerical();
var got = derivative.Substitute("x", at).EvalNumerical();
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}");
}
}
}
}
Loading