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 @@ -1173,6 +1173,22 @@ it in the answer, and what the loop asks is whether the degree has fallen to not
| `"F^(c*(a+b*x))*sin(d+pe*x)^3".Integrate("x")` | left unevaluated | the antiderivative |
| `"F^(c*(a+b*x))*sin(d+pe*x)*cos(d+pe*x)".Integrate("x")` | left unevaluated | the antiderivative |

### A polynomial may stand beside a root of `a ± a cos(y)`

`x^3 sqrt(a + a cos(c + d x))` was left as written. The half angle at which `a ± a cos(y)` is a
square answers the root alone, and it substitutes the angle -- so anything else that mentioned
the variable stopped it, a polynomial among them. What is left of the variable is the variable
itself: with `u` half the angle (a quarter turn further for the sine's identity), `x` is
`(2u - offset)/rate`, so the polynomial is a polynomial in `u`, and what is handed on is one
times a cosine of `u`, which the closed rules answer. Rubi's 4.2.10
([#718](https://github.com/asc-community/AngouriMath/issues/718)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"x^3*sqrt(a+a*cos(c+d*x))".Integrate("x")` | left unevaluated | the antiderivative, with `sgn(cos((c + d x)/2))` in front |
| `"x^3*sqrt(a-a*cos(x))".Integrate("x")` | left unevaluated | the antiderivative |
| `"x^2*sqrt(a+a*sin(x))".Integrate("x")` | left unevaluated | the antiderivative |

### `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 @@ -2426,7 +2426,7 @@ Entity Step(ERational power)
else if (argument != thisArgument)
return null;
}
if (argument is null || !TreeAnalyzer.TryGetPolyLinear(argument, x, out var rate, out _)
if (argument is null || !TreeAnalyzer.TryGetPolyLinear(argument, x, out var rate, out var offsetOfArgument)
|| rate.ContainsNode(x) || TreeAnalyzer.IsZero(rate))
return null;
if (!expr.Nodes.All(node => !node.ContainsNode(x)
Expand Down Expand Up @@ -2494,8 +2494,19 @@ Entity Step(ERational power)
return constant * MathS.Pow(square, Number.Integer.Create(twoP));
});
var inU = rewritten.Substitute(sine, sineInU).Substitute(cosine, cosineInU);
// What is left of the variable is the variable itself: the angle is `rate x + offset`
// and `u` is half of it (a quarter turn on for the sine), so `x` is
// `(2u - pi/2 - offset)/rate` there and `(2u - offset)/rate` here -- and
// `x^3 sqrt(a + a cos(c + d x))` is a polynomial in `u` times a cosine of it, which
// the closed rules answer, where the rule used to decline anything beside the root.
if (inU.ContainsNode(x))
return null;
{
var argumentInU = sineKind ? (2 * u - MathS.pi / 2).InnerSimplified : (2 * u).InnerSimplified;
var xInU = ((argumentInU - offsetOfArgument) / rate).InnerSimplified;
inU = inU.Substitute(x, xInU);
if (inU.ContainsNode(x))
return null;
}
// dx = 2 du / rate.
var integrand = (inU * 2 / rate).InnerSimplified;
if (Integration.ComputeAsAQuestionOfItsOwn(integrand, u, integrateByParts) is not { } result)
Expand Down Expand Up @@ -12416,7 +12427,7 @@ bool IsAConstantTimesAPowerOfOneMinusUSquared(Entity polynomial, out Entity cons
else if (argument != thisArgument)
return null;
}
if (argument is null || !TreeAnalyzer.TryGetPolyLinear(argument, x, out var rate, out _)
if (argument is null || !TreeAnalyzer.TryGetPolyLinear(argument, x, out var rate, out var offsetOfArgument)
|| rate.ContainsNode(x) || TreeAnalyzer.IsZero(rate))
return null;
if (!expr.Nodes.All(node => !node.ContainsNode(x)
Expand Down Expand Up @@ -14846,7 +14857,7 @@ bool IsAPolynomialInBoth(Entity polynomial)
else if (argument != thisArgument)
return null;
}
if (argument is null || !TreeAnalyzer.TryGetPolyLinear(argument, x, out var rate, out _)
if (argument is null || !TreeAnalyzer.TryGetPolyLinear(argument, x, out var rate, out var offsetOfArgument)
|| rate.ContainsNode(x) || TreeAnalyzer.IsZero(rate))
return null;
var sine = MathS.Sin(argument);
Expand Down
20 changes: 20 additions & 0 deletions Sources/Tests/UnitTests/Calculus/HalfAngleSquareTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,26 @@ public void ThePairOfPowersTwoApartIsElementary(string integrand)
Assert.True(compared >= 4, $"only {compared} of five points were comparable for {integrand}");
}

/// <summary>
/// A polynomial beside the root: the rule substitutes the half angle, and what is left
/// of the variable is the variable itself -- <c>x</c> is <c>(2u - offset)/rate</c> for
/// the cosine and a quarter turn further for the sine -- so
/// <c>x^3 sqrt(a + a cos(c + d x))</c> is a polynomial in <c>u</c> times a cosine of it,
/// which the closed rules answer. It used to decline anything standing beside the root.
/// Rubi's 4.2.1. <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
/// </summary>
[Theory]
[InlineData("x^3*sqrt(a + a*cos(c + d*x))")]
[InlineData("x*sqrt(a + a*cos(x))")]
[InlineData("x^3*sqrt(a - a*cos(x))")]
[InlineData("x^2*sqrt(a + a*sin(x))")]
[InlineData("x*(a + a*cos(x))^(3/2)")]
public void APolynomialMayStandBesideTheRoot(string integrand)
{
DifferentiatesBack(integrand, 2);
DifferentiatesBack(integrand, -3);
}

/// <summary>A combination that does not kill the residual is not this rule's, and is left alone.</summary>
[Fact]
public void AnotherCoefficientIsLeftAsWritten()
Expand Down
Loading