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
12 changes: 12 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,18 @@ It is written out again in the precision asked for now
| `Rational.Create(128, 12167)`'s `EDecimal`, read at the default precision and then under 300 digits | 100 digits | 300 digits |
| `sqrt(46) * 128/12167` evaluated under 300 digits, with that rational | relative error `4.7e-101` | correct to 300 digits |

### A power of x times the logarithm of a polynomial plus a root is integrated by parts over the conjugate

**Answers where there were none.** `x^3 ln(-1 + 4 x + 4 sqrt((x - 1) x))` was declined, and so were three more of Rubi's 3.5,
with `x^(3/2)`, `x^(-3/2)` and `x^(-5/2)` beside the same logarithm. With `D = A^2 - B^2 Q`, the
derivative of `ln(A + B sqrt(Q))` is `D'/(2 D) + (Q (A B' - A' B) + A B Q'/2)/(D sqrt(Q))` -- here `D = 8 x + 1` -- so by
parts what is left is a rational function and one over the root
([#233](https://github.com/asc-community/AngouriMath/issues/233)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"x^3*ln(-1+4*x+4*sqrt((x-1)*x))".ToEntity().Integrate("x")` | `integral(...)` | `x^4 ln(...)/4` minus a polynomial, `ln(8 x + 1)` and terms over `sqrt(x^2 - x)` |

### The last digits of an evaluated logarithm, exponential, real power or complex power

**Silent, and in the hundredth digit.** `ln`, `log`, `e^x`, a real base to a real power and the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,97 @@ private static bool IsAnInverseFunctionTimesARationalFunction(Entity expr, Entit
return inverse == 1;
}

/// <summary>
/// A power of <c>x</c> times the logarithm of <c>A + B sqrt(Q)</c>, for polynomials <c>A</c>,
/// <c>B</c> and <c>Q</c>, by parts, with the logarithm's derivative written over the conjugate:
/// with <c>D = A^2 - B^2 Q</c>, it is
/// <c>D'/(2 D) + (Q (A B' - A' B) + A B Q'/2)/(D sqrt(Q))</c>, a rational function and one over
/// the root, which the rules for those answer. Rubi's 3.5,
/// <c>x^3 ln(-1 + 4 x + 4 sqrt((x - 1) x))</c>, where <c>D = 8 x + 1</c>.
/// https://github.com/asc-community/AngouriMath/issues/233
/// </summary>
/// <remarks>
/// The search differentiates the logarithm as written, a quotient with the root in both
/// parts, and ran past the budget on what was left. Only for a <c>D</c> that is not a
/// constant: for one that is, <c>ln(x + sqrt(x^2 + 1))</c> and the other inverse hyperbolic
/// functions, the derivative is the root's alone, and the rules for those answer them.
/// The derivative is an identity of the algebra, <c>sqrt(Q)^2 = Q</c>, with no branch in it.
/// </remarks>
internal static Entity? SolveByPartsAgainstALogarithmOfARadicalSum(Entity expr, Entity.Variable x)
{
Entity constant = Number.Integer.One;
Logf? logarithm = null;
Entity power = Number.Integer.Zero;
foreach (var (factor, underneath) in FactorsOfTheIntegrand(expr))
{
if (!factor.ContainsNode(x))
{
constant = underneath ? constant / factor : constant * factor;
continue;
}
if (!underneath && logarithm is null && factor is Logf ln && ln.Base == MathS.e)
{
logarithm = ln;
continue;
}
Entity? k = factor == x ? Number.Integer.One : factor is Powf(var raisedX, Number.Rational r) && raisedX == x ? r : null;
if (k is null)
return null;
power = underneath ? power - k : power + k;
}
if (logarithm is null || logarithm.Antilogarithm is not (Sumf or Minusf) || power.InnerSimplified.Evaled is not Number.Rational m
|| m == Number.Integer.MinusOne)
return null;
// The argument as A + B sqrt(Q), term by term.
Entity a = Number.Integer.Zero, b = Number.Integer.Zero;
Entity? radicand = null;
foreach (var term in Sumf.LinearChildren(logarithm.Antilogarithm))
{
Entity? root = null;
Entity rest = Number.Integer.One;
foreach (var factor in Mulf.LinearChildren(term))
{
if (root is null && factor is Powf(var q, Number.Rational half) && half == Number.Rational.Create(1, 2) && q.ContainsNode(x))
root = q;
else
rest *= factor;
}
if (root is null)
{
a += term;
continue;
}
if (radicand is not null && radicand != root)
return null;
radicand = root;
b += rest;
}
if (radicand is null
|| !TreeAnalyzer.TryGetPolynomial(a, x, out var aRead) || aRead.Keys.Any(k => k.Sign < 0)
|| !TreeAnalyzer.TryGetPolynomial(b, x, out var bRead) || bRead.Keys.Any(k => k.Sign < 0)
|| !TreeAnalyzer.TryGetPolynomial(radicand, x, out var qRead) || qRead.Keys.Any(k => k.Sign < 0) || qRead.Keys.All(k => k.IsZero))
return null;
// D = A^2 - B^2 Q, and the numerator over the root, as polynomials.
Entity Written(Entity polynomial) => TreeAnalyzer.TryGetPolynomial(polynomial, x, out var read)
? read.Where(monomial => monomial.Value.InnerSimplified.Evaled is not Number.Complex { IsZero: true })
.Aggregate((Entity)Number.Integer.Zero, (sum, monomial) => sum + monomial.Value.InnerSimplified * (monomial.Key.IsZero ? Number.Integer.One : monomial.Key.Equals(EInteger.One) ? x : MathS.Pow(x, Number.Integer.Create(monomial.Key))))
: polynomial;
var d = Written(a * a - b * b * radicand);
if (!TreeAnalyzer.TryGetPolynomial(d, x, out var dRead) || dRead.Keys.All(k => k.IsZero) || d == Number.Integer.Zero)
return null;
var (da, db, dq) = (a.Differentiate(x), b.Differentiate(x), radicand.Differentiate(x));
var overTheRoot = Written(radicand * (a * db - da * b) + a * b * dq / 2);
var raised = (m + 1).InnerSimplified;
Entity xRaised = raised == Number.Integer.One ? x : MathS.Pow(x, raised);
var rational = xRaised * Written(d.Differentiate(x)) / (2 * d);
var radical = xRaised * overTheRoot / (d * MathS.Sqrt(radicand));
if (Integration.ComputeAsAQuestionOfItsOwn(rational, x, integrateByParts: true) is not { } first
|| Integration.ComputeAsAQuestionOfItsOwn(radical, x, integrateByParts: true) is not { } second
|| first.Nodes.Concat(second.Nodes).Any(node => node is Integralf || node == MathS.NaN))
return null;
return (constant * (xRaised * logarithm - first - second) / raised).InnerSimplified;
}

/// <summary>
/// An integrand that is a constant multiple of <c>D'/D</c> for a sum <c>D</c> below the
/// bar holding a root, decided at sampled points and answered as that multiple of
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,8 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
// A power of a quotient of linears beside whole powers of them, which is how e^(n acoth(a x))
// over a power of c - a^2 c x^2 arrives folded, under t = the quotient.
if ((answer = IndefiniteIntegralSolver.SolveByTheMoebiusSubstitutionOfAQuotientOfLinears(expr, x)) is { }) return answer;
// And a power of x times the logarithm of A + B sqrt(Q), by parts, its derivative over the conjugate.
if ((answer = IndefiniteIntegralSolver.SolveByPartsAgainstALogarithmOfARadicalSum(expr, x)) is { }) return answer;
// A power of an exponential with a positive base is the exponential of the product,
// exactly, and only that spelling is one the exponential rules read.
if ((answer = IndefiniteIntegralSolver.SolveByFlatteningAPowerOfAnExponential(expr, x, integrateByParts)) is { }) return answer;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//
// 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 x times <c>ln(A + B sqrt(Q))</c>, by parts, with the logarithm's derivative written
/// over the conjugate, <c>D = A^2 - B^2 Q</c>: a rational function plus one over the root. The
/// search differentiated it as written and ran past its budget. Rubi's 3.5.
/// <a href="https://github.com/asc-community/AngouriMath/issues/233">#233</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class APowerTimesALogarithmOfARadicalSumIntegralTest
{
[Theory]
[InlineData("ln(-1 + 4*x + 4*sqrt((x - 1)*x))")]
[InlineData("x^3*ln(-1 + 4*x + 4*sqrt((x - 1)*x))")]
[InlineData("x*ln(2*x + sqrt(x^2 + 3))")]
public void ByPartsOverTheConjugate(string integrand)
{
var integral = integrand.ToEntity().Integrate("x");
var text = integral.Stringize();
Assert.DoesNotContain("integral(", text);
var derivative = integral.Substitute("C", 0).Differentiate("x");
var original = integrand.ToEntity();
var compared = 0;
// Where the root is real and the logarithm's argument positive.
foreach (var at in new[] { 1.2, 1.7, 2.3, 3.1, 4.4, 6.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