@@ -16148,12 +16148,40 @@ node is Powf(var @base, var power) && @base.ContainsNode(u)
1614816148 if (inU.ContainsNode(x))
1614916149 return null;
1615016150 // dx = du/d.
16151- if (Integration.ComputeAsAQuestionOfItsOwn((inU / slope).InnerSimplified, u, integrateByParts) is not { } answer)
16151+ var question = (inU / slope).InnerSimplified;
16152+ // A zero over a constant among partial fractions over the linear -- what a pole whose
16153+ // residue vanishes leaves -- is written `0 provided c != 0` by the inner simplification,
16154+ // and the condition rises to the top of the question in u, where only the substitution
16155+ // search reads a question: it spent seven seconds declining the remainder by parts of
16156+ // Rubi's `(A + B ln(e (a + b x)/(c + d x)))/(f + g x)^5` for it, and the sum was then
16157+ // integrated term by term in x, in a longer answer. The condition says only that a
16158+ // constant is not zero, which the generic case takes, so the question is asked without
16159+ // it. Not beside a polynomial term, which in u integrates to a multiple of the linear
16160+ // where in x it is one of x, the longer spelling.
16161+ if (question is Providedf(var unconditioned, var condition) && IsANonzeroCondition(condition, u)
16162+ && expr is Sumf or Minusf && Sumf.LinearChildren(expr).Any(IsAZeroOverAConstant)
16163+ && Sumf.LinearChildren(expr).All(term => IsAZeroOverAConstant(term) || term.ContainsNode(x) && !TreeAnalyzer.TryGetPolynomial(term, x, out _)))
16164+ question = unconditioned;
16165+ if (Integration.ComputeAsAQuestionOfItsOwn(question, u, integrateByParts) is not { } answer)
1615216166 return null;
1615316167 var back = answer.Substitute(u, linear);
1615416168 return back.Nodes.Any(node => node == MathS.NaN) ? null : back;
16169+
16170+ bool IsAZeroOverAConstant(Entity term) => term is Divf(var zero, var constant) && zero == Number.Integer.Zero && !constant.ContainsNode(x);
1615516171 }
1615616172
16173+ /// <summary>
16174+ /// Whether <paramref name="condition"/> says only that constants are not zero:
16175+ /// <c>not c = 0</c> with <c>c</c> free of <paramref name="u"/>, or a conjunction of such.
16176+ /// </summary>
16177+ private static bool IsANonzeroCondition(Entity condition, Entity.Variable u) => condition switch
16178+ {
16179+ Andf(var left, var right) => IsANonzeroCondition(left, u) && IsANonzeroCondition(right, u),
16180+ Notf(Equalsf(var left, var right)) => !left.ContainsNode(u) && !right.ContainsNode(u)
16181+ && (right.Evaled is Number.Complex { IsZero: true } || left.Evaled is Number.Complex { IsZero: true }),
16182+ _ => false,
16183+ };
16184+
1615716185 /// <summary>
1615816186 /// A polynomial times powers of error functions of one shifted linear, written in
1615916187 /// <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
@@ -28508,7 +28536,25 @@ private static Entity WithLogarithmArgumentsOverOneBar(Entity expr, Entity.Varia
2850828536 {
2850928537 if (!Integration.AnsweringTheQuestionAskedOrOneBelow)
2851028538 return null;
28511- expr = WithNegativePowersOfProductsApart(expr);
28539+ // The reciprocal of a product read as the quotient it is, where written apart it hides
28540+ // a linear. The constant-over-a-product branch of SolveAsPolynomialTerm hands
28541+ // `1/((a/b + x)^3 (A + B ln(...)))` on as its power -1, and that power written apart
28542+ // over one bar is `((a + b x)^3)^(-1) b^3`, a power of a power, from which no linear is
28543+ // read: declined, the substitution search spent five to forty seconds on it and
28544+ // declined it too, and the quotient one level up was then answered here at once. Read
28545+ // as that quotient it is answered as that quotient is, in the same words. A reciprocal
28546+ // that written apart is read, `((a + b x)(c + d x) L^2)^(-1)`, is read so, as before.
28547+ var readFromTheReciprocal = false;
28548+ var apart = WithNegativePowersOfProductsApart(expr);
28549+ if (expr is Powf(Mulf reciprocal, Number.Integer { EInteger: var minusOne }) && minusOne.Equals(EInteger.FromInt32(-1))
28550+ && FactorsOfTheIntegrand(apart).Any(pair => pair.Factor is Powf(Powf(var nested, _), _) && nested.ContainsNode(x)
28551+ && !nested.Nodes.Any(node => node is Logf)))
28552+ {
28553+ expr = Number.Integer.One / reciprocal;
28554+ readFromTheReciprocal = true;
28555+ apart = WithNegativePowersOfProductsApart(expr);
28556+ }
28557+ expr = apart;
2851228558 // The logarithm's argument: a product of constants and powers of two linears with
2851328559 // opposite exponents.
2851428560 Entity? logarithm = null;
@@ -28622,6 +28668,14 @@ private static Entity WithLogarithmArgumentsOverOneBar(Entity expr, Entity.Varia
2862228668 }
2862328669 if (logarithmsFactor is null || aPolynomialBesideTheLogarithm)
2862428670 return null;
28671+ // Read from the reciprocal, a power of t beside the logarithm's alone is left where it
28672+ // went before: the substitution search after this rule answers it through the
28673+ // logarithm, writing `e^((A + B ln(k t))/B)/t` as the `k e^(A/B)` it is -- Rubi's
28674+ // `1/((a g + b g x)^2 (A + B ln(e (a + b x)/(c + d x))))` in 129 characters, where
28675+ // this writes 231.
28676+ if (readFromTheReciprocal && powerOfDenominator.Evaled is Number.Complex { IsZero: true }
28677+ && powerOfT.Evaled is not Number.Complex { IsZero: true })
28678+ return null;
2862528679 Entity above = constantInFront * logarithmsFactor, below = Number.Integer.One;
2862628680 foreach (var (@base, power) in new[] { (t, Functions.PartialFractions.Bare(powerOfT.Simplify())), (denominatorInT, Functions.PartialFractions.Bare(powerOfDenominator.Simplify())) })
2862728681 {
@@ -28761,11 +28815,14 @@ static bool AreEqualAsPolynomials(Entity left, Entity right)
2876128815 Entity? differentiated = null;
2876228816 Entity writtenPower = Number.Integer.One;
2876328817 Entity rational = Number.Integer.One;
28818+ var aPolynomialBeside = true;
2876428819 foreach (var (factor, underneath) in FactorsOfTheIntegrand(expr))
2876528820 {
2876628821 if (!factor.ContainsNode(x) || IsARationalFunction(factor, x))
2876728822 {
2876828823 rational = underneath ? rational / factor : rational * factor;
28824+ if (factor.ContainsNode(x) && (underneath || factor is Powf(_, var exponentOfFactor) && exponentOfFactor.Evaled is not Number.Integer { EInteger.Sign: >= 0 }))
28825+ aPolynomialBeside = false;
2876928826 continue;
2877028827 }
2877128828 if (differentiated is not null)
@@ -28793,8 +28850,16 @@ static bool AreEqualAsPolynomials(Entity left, Entity right)
2879328850 // And term by term, each term of the sum differentiated on its own: simplified
2879428851 // whole, `b/(a + b x) - d/(c + d x)` is brought over one bar, and the pieces below
2879528852 // then hold a quadratic with symbols in it.
28853+ // And beside a polynomial, a power of a quotient with x on both sides written apart as
28854+ // well: `ln(e ((a + b x)/(c + d x))^n)` as written differentiates to two terms each
28855+ // over `(c + d x)^2 (a + b x)`, and the long division of every remainder by that
28856+ // cubic, its symbolic coefficients simplified, took Rubi's
28857+ // `(a g + b g x)^4 (A + B ln(e ((a + b x)/(c + d x))^n))` seventeen seconds and an
28858+ // answer of 4,063 characters, where the spelling `e (a + b x)^n/(c + d x)^n` takes a
28859+ // third of a second. Beside a polynomial only: over `(f + g x)^5` the remainders
28860+ // written so took one second to six.
2879628861 var derivativeTerms = new List<Entity>();
28797- foreach (var term in TermsDistributed(WithLogarithmsOfProductsApart(differentiated, x)))
28862+ foreach (var term in TermsDistributed(WithLogarithmsOfProductsApart(differentiated, x, powersOfQuotientsApart: aPolynomialBeside )))
2879828863 {
2879928864 var termDerivative = Functions.PartialFractions.Bare(term.Differentiate(x).InnerSimplified);
2880028865 if (termDerivative == Number.Integer.Zero || termDerivative.Evaled is Number.Complex { IsZero: true })
@@ -29067,22 +29132,38 @@ private static List<Entity> TermsDistributed(Entity expr)
2906729132 /// as the sum of the logarithms of the factors holding <paramref name="x"/>, each
2906829133 /// times its power, and the constant factors dropped. Equal to the original up to a
2906929134 /// constant on each interval where both are defined, which is all a derivative sees.
29135+ /// With <paramref name="powersOfQuotientsApart"/>, a power of a quotient of two factors
29136+ /// holding <paramref name="x"/> is written apart too, each side's logarithm times the
29137+ /// power: <c>ln(e ((a + b x)/(c + d x))^n)</c> is <c>n ln(a + b x) - n ln(c + d x)</c>,
29138+ /// whose derivative is the same, since <c>(Q^n)'/Q^n = n Q'/Q</c>.
2907029139 /// </summary>
29071- private static Entity WithLogarithmsOfProductsApart(Entity expr, Entity.Variable x)
29140+ private static Entity WithLogarithmsOfProductsApart(Entity expr, Entity.Variable x, bool powersOfQuotientsApart )
2907229141 => expr.Replace(node =>
2907329142 {
2907429143 if (node is not Logf(var @base, var argument) || @base != MathS.e || !argument.ContainsNode(x) || argument is not (Mulf or Divf or Powf))
2907529144 return node;
2907629145 Entity sum = Number.Integer.Zero;
2907729146 foreach (var (factor, underneath) in FactorsOfTheIntegrand(argument))
29147+ Add(factor, underneath, Number.Integer.One);
29148+ return sum;
29149+
29150+ void Add(Entity factor, bool underneath, Entity outer)
2907829151 {
2907929152 if (!factor.ContainsNode(x))
29080- continue ;
29153+ return ;
2908129154 var (inner, power) = factor is Powf(var b, var p) && !p.ContainsNode(x) ? (b, p) : (factor, (Entity)Number.Integer.One);
29155+ if (outer != Number.Integer.One)
29156+ power = power == Number.Integer.One ? outer : outer * power;
29157+ if (powersOfQuotientsApart && power != Number.Integer.One
29158+ && inner is Divf(var numerator, var denominator) && numerator.ContainsNode(x) && denominator.ContainsNode(x))
29159+ {
29160+ foreach (var (innerFactor, innerUnderneath) in FactorsOfTheIntegrand(inner))
29161+ Add(innerFactor, underneath != innerUnderneath, power);
29162+ return;
29163+ }
2908229164 var term = power == Number.Integer.One ? MathS.Ln(inner) : power * MathS.Ln(inner);
2908329165 sum = underneath ? sum - term : sum + term;
2908429166 }
29085- return sum;
2908629167 });
2908729168
2908829169 /// <summary>
0 commit comments