diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index 5e39bfc78..90c8c587b 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -2335,6 +2335,24 @@ beside a power of `x`, the answer is written with the same constant: `x Ei(a + b
| `"x*Shi(a+b*x)^2".Integrate("x")` | `a ^ 2 * Shi * x ^ 2 / 2 + a * b * 2 * Shi * x ^ 3 / 3 + b ^ 2 * Shi * x ^ 4 / 4 + C`, with `Shi` a variable | an antiderivative in `Shi(a + b x)` and `Ei(2 (a + b x))`, `Ei(-2 (a + b x))` |
| `"x*Ei(a+b*x)^2".Integrate("x")` | `a ^ 2 * Ei * x ^ 2 / 2 + a * b * 2 * Ei * x ^ 3 / 3 + b ^ 2 * Ei * x ^ 4 / 4 + C`, with `Ei` a variable | an antiderivative in `Ei(a + b x)` and `Ei(2 (a + b x))` |
+### A square of a special function of a shifted argument is integrated by parts
+
+The remainder of a special function's square was asked term by term only for an argument `b x`.
+For `a + b x`, the remainder divided by the linear until the constant of integration was matched to
+it; with that matched, the terms are the case without the offset, and the asking is offered for any
+linear argument ([#1501](https://github.com/asc-community/AngouriMath/issues/1501)). The exception
+is an error function beside anything but itself, whose derivative is a Gaussian of the shifted
+argument, so a polynomial beside it stays in every term: `(c + d x) erf(a + b x)^2` is still
+declined. An integrand holding one of these functions had no reading in 2.5.0, which the entries for
+the functions themselves record, and no integrand without one changes: Rubi's independent suites and
+a sample of its families 1 to 7 are answered alone as they were.
+
+| Input | Was (2.5.0) | Now |
+|---|---|---|
+| `"Shi(a+b*x)^2".Integrate("x")` | `Shi * (a + b * x) ^ 3 / 3 / b + C`, with `Shi` a variable | an antiderivative in `Shi(a + b x)`, `Ei(2 (a + b x))` and `Ei(-2 (a + b x))` |
+| `"x^2*Ei(a+b*x)^2".Integrate("x")` | `a ^ 2 * Ei * x ^ 3 / 3 + a * b * 2 * Ei * x ^ 4 / 4 + b ^ 2 * Ei * x ^ 5 / 5 + C`, with `Ei` a variable | an antiderivative in `Ei(a + b x)` and `Ei(2 (a + b x))` |
+| `"erf(a+b*x)^2".Integrate("x")` | `UnrecognizedFunctionParseException`: there is no function `erf` | an antiderivative in `erf` |
+
### An exponential or a hyperbolic function over several linears is split into partial fractions over them
`e^x/(x (x + 1))` was left unevaluated, where `e^x/x` and `e^x/(x + 1)` were each answered with
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
index 8694cc5d6..c4354b9a5 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
@@ -1842,12 +1842,10 @@ static Entity WithTheConstantMatchedTo(Entity antiderivative, Entity derivativeO
// So its terms are asked, each as a question of its own. From the top only,
// which is where that asking reaches, and once: a term asked so is at the top
// itself, and its own remainder asked the same way would lift the search again,
- // each level with the depth reset. And of a multiple of x only: of `a + b x`, the
- // remainder is over the linear, and its terms are the exponentials a hyperbolic
- // function is written as, each a harder question than the whole -- the decline of
- // `x Shi(a + b x)^2` went from a second to past twenty asking them.
+ // each level with the depth reset. And not for every argument: see
+ // TheRemainderIsAskedTermByTerm.
// https://github.com/asc-community/AngouriMath/issues/1501
- if (remainingIntegral is null && besideASpecialFunction && OfAMultipleOfTheVariable(v, x)
+ if (remainingIntegral is null && besideASpecialFunction && TheRemainderIsAskedTermByTerm(v, u, x)
&& Integration.AnsweringTheQuestionAsked && !askingTheTermsOfARemainder)
{
var terms = remaining is Divf(var remainingAbove, var remainingBelow)
@@ -1872,7 +1870,7 @@ static Entity WithTheConstantMatchedTo(Entity antiderivative, Entity derivativeO
askingTheTermsOfARemainder = false;
}
}
- else if (remainingIntegral is null && besideASpecialFunction && OfAMultipleOfTheVariable(v, x)
+ else if (remainingIntegral is null && besideASpecialFunction && TheRemainderIsAskedTermByTerm(v, u, x)
&& Integration.AnsweringTheQuestionAsked)
Integration.DeclinedForItsScope();
if (remainingIntegral is null) return null;
@@ -20345,12 +20343,29 @@ private static bool IsASpecialFunctionOfALinearOrAPowerOfOne(Entity factor, Vari
|| factor is Powf(var @base, Number.Integer power) && power.EInteger.Sign > 0 && IsASpecialFunctionOfALinear(@base, x);
///
- /// Whether the special function in , or in the base of its power,
- /// is of a multiple of , b x, with no offset.
+ /// Whether the remainder after differentiating , a special function of
+ /// a linear argument or a power of one, against is asked term by term
+ /// when nothing answers it whole. Of a multiple of , always. Of
+ /// a + b x, where the function's derivative divides by its argument -- Ei,
+ /// Si, Ci, Shi, Chi -- since the constant of the factor
+ /// integrated beside it is then matched to the linear and the terms are the case without
+ /// the offset; and where the function is integrated against itself, the square alone.
+ /// Not an error function of a + b x beside anything else: its derivative is a
+ /// Gaussian of the shifted argument rather than a quotient by it, a polynomial beside it
+ /// stays in every term, and asking them took the decline of (c + d x) erf(a + b x)^2
+ /// from six seconds to twenty-four, answering nothing.
+ /// https://github.com/asc-community/AngouriMath/issues/1501
///
- private static bool OfAMultipleOfTheVariable(Entity factor, Variable x)
- => (factor is Powf(var @base, _) ? @base : factor).DirectChildren.FirstOrDefault() is { } argument
- && TreeAnalyzer.TryGetPolyLinear(argument, x, out _, out var offset) && TreeAnalyzer.IsZero(offset);
+ private static bool TheRemainderIsAskedTermByTerm(Entity v, Entity u, Variable x)
+ {
+ var special = v is Powf(var @base, _) ? @base : v;
+ if (special.DirectChildren.FirstOrDefault() is not { } argument
+ || !TreeAnalyzer.TryGetPolyLinear(argument, x, out _, out var offset))
+ return false;
+ return TreeAnalyzer.IsZero(offset)
+ || special is not (Entity.Erff or Entity.Erfcf or Entity.Erfif)
+ || u == special;
+ }
///
/// The factors of that are polynomials in of
diff --git a/Sources/Tests/UnitTests/Calculus/SpecialFunctionsByPartsTest.cs b/Sources/Tests/UnitTests/Calculus/SpecialFunctionsByPartsTest.cs
index 6df826679..c83e8806d 100644
--- a/Sources/Tests/UnitTests/Calculus/SpecialFunctionsByPartsTest.cs
+++ b/Sources/Tests/UnitTests/Calculus/SpecialFunctionsByPartsTest.cs
@@ -142,6 +142,10 @@ public void ASquareIsTwoRoundsOfParts(string integrand)
[Theory]
[InlineData("x*Shi(a + b*x)^2")]
[InlineData("x*Ei(a + b*x)^2")]
+ [InlineData("Shi(a + b*x)^2")]
+ [InlineData("x*Si(a + b*x)^2")]
+ [InlineData("x^2*Ei(a + b*x)^2")]
+ [InlineData("erf(a + b*x)^2")]
public void ASquareOfAShiftedArgumentIsTwoRoundsOfParts(string integrand)
=> DifferentiatesBack(integrand, Parameters);
}