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
Original file line number Diff line number Diff line change
Expand Up @@ -13801,6 +13801,58 @@ node is Powf(var @base, var power) && @base.ContainsNode(u)
return back.Nodes.Any(node => node == MathS.NaN) ? null : back;
}

/// <summary>
/// A polynomial times powers of error functions of one shifted linear, written in
/// <c>u = a + b x</c>: <c>x erf(a + b x)^2</c> is <c>(u - a) erf(u)^2/b^2</c>, which by parts
/// answers, since there the Gaussian the error function differentiates to is of the
/// variable itself.
/// </summary>
/// <remarks>
/// In x, by parts leaves <c>x^n e^(-(a + b x)^2) erf(a + b x)</c>, which nothing reads, and
/// the remainder is declined term by term rather than asked (see
/// <c>TheRemainderIsAskedTermByTerm</c>): <c>(c + d x)^2 erf(a + b x)^2</c> ran past twenty
/// seconds. Only where the offset is not zero, since without it there is nothing to move,
/// and only error functions, whose derivatives are Gaussians of their argument rather than
/// quotients by it. Rubi's 8.1.
/// https://github.com/asc-community/AngouriMath/issues/1945
/// </remarks>
internal static Entity? SolveAPolynomialBesideErrorFunctionsOfAShiftedLinear(Entity expr, Entity.Variable x, bool integrateByParts)
{
if (!Integration.AnsweringTheQuestionAskedOrOneBelow)
return null;
Entity? argument = null;
Entity? polynomial = null;
foreach (var factor in Mulf.LinearChildren(expr))
{
var special = factor is Powf(var @base, Number.Integer power) && power.EInteger.Sign > 0 ? @base : factor;
if (special is Entity.Erff or Entity.Erfcf or Entity.Erfif && special.DirectChildren[0] is var inner)
{
if (argument is null)
argument = inner;
else if (inner != argument)
return null;
}
else if (!factor.ContainsNode(x))
continue;
else if (ThePolynomialIn(factor, x) is not null)
polynomial = polynomial is null ? factor : polynomial * factor;
else
return null;
}
if (argument is null || polynomial is null
|| !TreeAnalyzer.TryGetPolyLinear(argument, x, out var slope, out var offset)
|| slope.ContainsNode(x) || offset.ContainsNode(x) || TreeAnalyzer.IsZero(slope) || VanishesIdentically(offset))
return null;
// x = (u - a)/b, so that each error function is of u, and dx = du/b.
var u = Variable.CreateUnique(expr, "u_linear");
var inU = expr.Replace(node => node == argument ? u : node).Substitute(x, (u - offset) / slope);
if (inU.ContainsNode(x)
|| Integration.ComputeAsAQuestionOfItsOwn((inU / slope).InnerSimplified, u, integrateByParts) is not { } answer)
return null;
var back = answer.Substitute(u, argument);
return back.Nodes.Any(node => node == MathS.NaN) ? null : back;
}

/// <summary>
/// A sine or a cosecant of twice an argument beside functions of the argument, written as the
/// product it is: <c>sin(2A) = 2 sin(A) cos(A)</c>, so that every trigonometric function is of
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,9 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
// the sum and writes what is left out in powers of x, where the linear is gone; and
// before the substitution search, which reads one function at a time.
if ((answer = IndefiniteIntegralSolver.SolveByWritingAFunctionOfOneShiftedLinear(expr, x, integrateByParts)) is { }) return answer;
// A polynomial beside error functions of one shifted linear, written in it: by parts in
// x leaves a Gaussian of the shifted linear beside a polynomial, which nothing reads.
if ((answer = IndefiniteIntegralSolver.SolveAPolynomialBesideErrorFunctionsOfAShiftedLinear(expr, x, integrateByParts)) is { }) return answer;
if ((answer = IndefiniteIntegralSolver.SolveAsPolynomialTerm(expr, x, integrateByParts)) is { }) return answer;
// A product of powers of the variable with a power of a constant multiple of it
// among them, `(c x)^m x^n`, by the power rule with the written power kept as
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//
// 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 polynomial times powers of error functions of one shifted linear, written in
/// <c>u = a + b x</c>. In x, by parts leaves a Gaussian of <c>a + b x</c> beside a polynomial,
/// which nothing read, and <c>(c + d x)^2 erf(a + b x)^2</c> ran past twenty seconds. Rubi's 8.1.
/// <a href="https://github.com/asc-community/AngouriMath/issues/1945">#1945</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class APolynomialBesideErrorFunctionsOfAShiftedLinearIntegralTest
{
[Theory]
[InlineData("x*erf(a + b*x)^2")]
[InlineData("x^2*erf(a + b*x)^2")]
[InlineData("(c + d*x)^2*erf(a + b*x)^2")]
[InlineData("(c + d*x)^2*erfc(a + b*x)^2")]
[InlineData("(c + d*x)^2*erfi(a + b*x)^2")]
[InlineData("(c + d*x)*erf(a + b*x)")]
public void WrittenInTheShiftedLinear(string integrand)
{
var integral = integrand.ToEntity().Integrate("x");
var text = integral.Stringize();
Assert.DoesNotContain("integral(", text);
Assert.True(text.Length < 5000, $"{text.Length} characters of answer for {integrand}");
Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("b", 0.7).Substitute("c", 0.4).Substitute("d", 0.3);
var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
var original = Pinned(integrand.ToEntity());
var compared = 0;
foreach (var at in new[] { -1.2, -0.7, 0.3, 0.8, 1.3, 2.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