diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
index 4f92adc78..31ac6efca 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
@@ -13801,6 +13801,58 @@ node is Powf(var @base, var power) && @base.ContainsNode(u)
return back.Nodes.Any(node => node == MathS.NaN) ? null : back;
}
+ ///
+ /// A polynomial times powers of error functions of one shifted linear, written in
+ /// u = a + b x: x erf(a + b x)^2 is (u - a) erf(u)^2/b^2, which by parts
+ /// answers, since there the Gaussian the error function differentiates to is of the
+ /// variable itself.
+ ///
+ ///
+ /// In x, by parts leaves x^n e^(-(a + b x)^2) erf(a + b x), which nothing reads, and
+ /// the remainder is declined term by term rather than asked (see
+ /// TheRemainderIsAskedTermByTerm): (c + d x)^2 erf(a + b x)^2 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
+ ///
+ 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;
+ }
+
///
/// A sine or a cosecant of twice an argument beside functions of the argument, written as the
/// product it is: sin(2A) = 2 sin(A) cos(A), so that every trigonometric function is of
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
index 7204719a2..4f34b7cdc 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
@@ -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
diff --git a/Sources/Tests/UnitTests/Calculus/APolynomialBesideErrorFunctionsOfAShiftedLinearIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/APolynomialBesideErrorFunctionsOfAShiftedLinearIntegralTest.cs
new file mode 100644
index 000000000..ca1a79c1d
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/APolynomialBesideErrorFunctionsOfAShiftedLinearIntegralTest.cs
@@ -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
+{
+ ///
+ /// A polynomial times powers of error functions of one shifted linear, written in
+ /// u = a + b x. In x, by parts leaves a Gaussian of a + b x beside a polynomial,
+ /// which nothing read, and (c + d x)^2 erf(a + b x)^2 ran past twenty seconds. Rubi's 8.1.
+ /// #1945
+ ///
+ [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}");
+ }
+ }
+}