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
15 changes: 15 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,21 @@ 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…` |

### 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
on the unreleased master with `(1/b a b c - a c)^(-2)` in it, which is zero to a negative power: the
answer had no value anywhere. The square is written as a power of its root, `(x + a/b)^(2 p)`, beside
`a c + b c x`, which is `b c (x + a/b)`, and the rule for two linear powers whose exponents sum to a whole
number divides by the two linears' cross term, declining where it is zero -- tested as a number, which
with symbols in it it is not. It is tested as a value now, and the integral is answered by the rules
after it
([#1793](https://github.com/asc-community/AngouriMath/issues/1793)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"(a*c + b*c*x)^(-3-2*p)*(f + g*x)*(a^2 + 2*a*b*x + b^2*x^2)^p".ToEntity().Integrate("x")` | `integral(...)`; an answer with no value on the unreleased master | the antiderivative |

### A power of a constant below `1e-50` is no longer simplified to zero

**Answers that were wrong.** Evaluation rounds a value within `1e-50` of an integer onto it, and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13059,8 +13059,11 @@ static bool IsRationalInBoth(Entity e, Entity.Variable x, Entity.Variable L)
var k = -sum.ToInt32Checked();
if (!TreeAnalyzer.TryGetPolyLinear(first, x, out var b1, out var a1) || !TreeAnalyzer.TryGetPolyLinear(second, x, out var b2, out var a2))
return null;
// Zero as a value, not only as a number: `x + a/b` beside `a c + b c x` is one linear
// twice, their cross term `a/b (b c) - a c` has symbols in it and is zero, and the
// formula below divided by it -- an answer with no value anywhere.
var d = (a1 * b2 - a2 * b1).InnerSimplified;
if (d.Evaled is Number.Complex { IsZero: true })
if (Functions.PartialFractions.IsZeroAsAValue(d))
return null;
Entity product = Number.Integer.One;
foreach (var (factor, _) in polynomial)
Expand Down
27 changes: 27 additions & 0 deletions Sources/Tests/UnitTests/Calculus/TwoLinearPowersIntegralTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,32 @@ Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("b", 0.9).Substitut
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
}
}

/// <summary>
/// One linear twice is not two: `x + a/b` beside `a c + b c x` has a cross term
/// `a/b (b c) - a c` that is zero with symbols in it, and the formula divided by it, for an
/// answer with no value anywhere. Rubi's 1.2.1.3:2459.
/// https://github.com/asc-community/AngouriMath/issues/1793
/// </summary>
[Theory]
[InlineData("(a*c + b*c*x)^(-3 - 2*p)*(f + g*x)*(a^2 + 2*a*b*x + b^2*x^2)^p")]
[InlineData("(x + a/b)^(2*p)*(a*c + b*c*x)^(-3 - 2*p)*(f + g*x)")]
public void OneLinearTwiceHasAValue(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("f", 0.3).Substitute("g", 1.7).Substitute("p", 0.29);
var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
var original = Pinned(integrand.ToEntity());
foreach (var at in new[] { 0.3, 0.8, 1.5, 2.4 })
{
var got = derivative.Substitute("x", at).EvalNumerical();
var want = original.Substitute("x", at).EvalNumerical();
Assert.False(got.IsNaN, $"the antiderivative of {integrand} has no derivative at x = {at}: {integral}");
Assert.True(Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart) < 1e-9 * Math.Max(1.0, Math.Abs((double)want.RealPart)),
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
}
}
}
}
Loading