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
17 changes: 17 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,23 @@ split. It is written `b (s + r)(s^2 - r s + r^2)` now, with `r = (a/b)^(1/3)`, w
| `"sec(c + d*x)^2/(a + b*sin(c + d*x)^3)".ToEntity().Integrate("x")` | `integral(...)` | 7,010 characters |
| `"sec(c + d*x)^4/(a + b*sin(c + d*x)^3)".ToEntity().Integrate("x")` | `integral(...)` | 11,646 characters |

### A linear factor common to a sum below the bar is taken out of it

**Answers where there were none.** `1/(a x + b x/log(c x^n))` was left unevaluated. With x taken out of the sum below the
bar it is `1/(x (a + b/log(c x^n)))`, a function of the logarithm beside its differential, which is answered. The same
holds for the higher powers of the logarithm and for Rubi's 3.5:37
([#718](https://github.com/asc-community/AngouriMath/issues/718)). `tanh(a + 2 log(x))^2/x^3`, which 2.5.0 left
unevaluated, is answered by this route as well.

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"1/(a*x+b*x/ln(c*x^n))".ToEntity().Integrate("x")` | `integral(...)` | 72 characters |
| `"1/(a*x+b*x/ln(c*x^n)^2)".ToEntity().Integrate("x")` | `integral(...)` | 503 characters |
| `"1/(a*x+b*x/ln(c*x^n)^3)".ToEntity().Integrate("x")` | `integral(...)` | 514 characters |
| `"1/(a*x+b*x/ln(c*x^n)^4)".ToEntity().Integrate("x")` | `integral(...)` | 337 characters |
| `"(a*x+2*b*n*ln(c*x^n))/(a*x^2+b*x*ln(c*x^n)^2)".ToEntity().Integrate("x")` | `integral(...)` | 37 characters |
| `"tanh(a+2*ln(x))^2/x^3".ToEntity().Integrate("x")` | `integral(...)` | 7,796 characters |

### A symbol shared by a numerator sum's coefficients is taken out before the partial fractions

**Answers where there were none.** `(b d + 2c d x)^9/(a + b x + c x^2)^3` ran past a minute, where
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29334,6 +29334,37 @@ static bool IsZero(Entity value) => value.InnerSimplified.Evaled is Number.Compl
return written == expr ? null : Integration.ComputeAsTheSameQuestion(written, x, integrateByParts);
}

/// <summary>
/// A quotient whose denominator is a sum every term of which carries the same power of one
/// linear form in <paramref name="x"/>, with that power taken out below the bar:
/// <c>1/(a x + b x/ln(c x^n))</c> is <c>1/(x (a + b/ln(c x^n)))</c>, a function of the
/// logarithm times its differential.
/// </summary>
/// <remarks>
/// Rubi's 3.5 <c>1/(a x + b x/log(c x^n)^k)</c> were left unevaluated, and the same with
/// <c>x</c> taken out is answered in a tenth of a second.
/// https://github.com/asc-community/AngouriMath/issues/718
/// </remarks>
internal static Entity? SolveByGatheringACommonLinearFactorOfASumBelowTheBar(Entity expr, Entity.Variable x, bool integrateByParts)
{
static bool IsASum(Entity e) => e is Sumf or Minusf;
Entity above, below;
var power = 1;
if (expr is Divf(var n, Powf(var d, Number.Integer k)) && IsASum(d) && k.EInteger.Sign > 0 && k.EInteger.CanFitInInt32())
(above, below, power) = (n, d, k.EInteger.ToInt32Unchecked());
else if (expr is Divf(var n1, var d1) && IsASum(d1))
(above, below) = (n1, d1);
else if (expr is Powf(var d2, Number.Integer k2) && IsASum(d2) && k2.EInteger.Sign < 0 && k2.EInteger.CanFitInInt32())
(above, below, power) = (Number.Integer.One, d2, -k2.EInteger.ToInt32Unchecked());
else
return null;
if (TryTakeACommonLinearFactorOfASum(below, x) is not var (factored, inside))
return null;
var taken = factored * inside;
var written = above / (power == 1 ? taken : MathS.Pow(factored, Number.Integer.Create(power)) * MathS.Pow(inside, Number.Integer.Create(power)));
return Integration.ComputeAsTheSameQuestion(written, x, integrateByParts);
}

/// <summary>
/// The sum with the highest power of one linear form in <paramref name="x"/> that every
/// term carries taken out of it, as that power and what is left; <see langword="null"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1223,6 +1223,11 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
if (expr is Entity.Sumf or Entity.Minusf
&& (answer = IndefiniteIntegralSolver.SolveByGatheringACommonPolynomialFactorOfASum(expr, x, integrateByParts)) is { })
return answer;
// The same factor taken out of a sum below the bar: `1/(a x + b x/ln(c x^n))` is
// `1/(x (a + b/ln(c x^n)))`, a function of the logarithm beside its differential.
if (expr is Entity.Divf or Entity.Powf
&& (answer = IndefiniteIntegralSolver.SolveByGatheringACommonLinearFactorOfASumBelowTheBar(expr, x, integrateByParts)) is { })
return answer;
// A power of the secant that is not whole beside the cosine, written as the cosine's
// with the constant that takes in front, which the substitutions below read.
if ((answer = IndefiniteIntegralSolver.SolveByWritingAPowerOfTheSecantInTheCosine(expr, x, integrateByParts)) is { }) return answer;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//
// 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 a linear common to every term of a sum below the bar, taken out: Rubi's 3.5
/// <c>1/(a x + b x/log(c x^n)^k)</c>, which was left unevaluated.
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class ACommonLinearFactorOfASumBelowTheBarIntegralTest
{
private static void DifferentiatesBack(string integrand, params (string, double)[] pins)
{
var integral = integrand.ToEntity().Integrate("x");
Assert.DoesNotContain("integral(", integral.Stringize());

var pinned = integral.Substitute("C", 0);
Entity original = integrand.ToEntity();
foreach (var (name, value) in pins)
{
pinned = pinned.Substitute(name, value);
original = original.Substitute(name, value);
}
var derivative = pinned.Differentiate("x");
// Where the logarithm is real and away from the zeros of the denominator.
foreach (var at in new[] { 0.4, 1.9, 2.7, 3.3 })
{
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);
var scale = Math.Max(1.0, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart));
Assert.True(difference / scale < 1e-8,
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
}
}

[Theory]
[InlineData("1/(a*x+b*x/ln(c*x^n))")]
[InlineData("1/(a*x+b*x/ln(c*x^n)^2)")]
[InlineData("1/(a*x+b*x/ln(c*x^n)^3)")]
public void IsAnsweredAndDifferentiatesBack(string integrand)
{
DifferentiatesBack(integrand, ("a", 1.3), ("b", 0.7), ("c", 2.5), ("n", 2));
DifferentiatesBack(integrand, ("a", 0.6), ("b", -0.2), ("c", 1.5), ("n", 3));
}
}
}
Loading