@@ -29308,7 +29308,7 @@ static bool IsOneOfTheFour(Entity node, Entity argument)
2930829308 {
2930929309 var (combinedAbove, combinedBelow) = Functions.SingleQuotient.Of(combined.InnerSimplified);
2931029310 integrand = (WithDegenerateBasesLowered(WithWholePowersDistributed(combinedAbove), t)
29311- / WithDegenerateBasesLowered(WithWholePowersDistributed(combinedBelow), t)).InnerSimplified;
29311+ / WithDegenerateBasesLowered(WithTheSharedFactorsOutOfEachSum( WithWholePowersDistributed(combinedBelow), t ), t)).InnerSimplified;
2931229312 }
2931329313 else
2931429314 integrand = combined.Simplify();
@@ -29369,6 +29369,96 @@ static bool NoRemainder(Entity rest)
2936929369 : null;
2937029370 }
2937129371
29372+ /// <summary>
29373+ /// <paramref name="product"/> with each factor that is a sum of products written as the
29374+ /// factors every term shares, with <paramref name="t"/> in them, times the sum of what
29375+ /// is left: <c>A (1 + t^2)(1 - t^2) + 2 b t (1 + t^2)(1 - t^2)</c> is
29376+ /// <c>(1 + t^2)(1 - t^2)(A + 2 b t)</c>.
29377+ /// </summary>
29378+ /// <remarks>
29379+ /// <c>1/(a + c sec(x) + b tan(x))</c> combines over <c>cos(x)</c> into a denominator
29380+ /// with both of the half angle's factors in each term, and read as a quartic with a
29381+ /// symbol in every coefficient it was declined after five seconds. Its numerator is
29382+ /// <c>2 (1 - t^2)^2</c>: with the factors out, the division below takes off the
29383+ /// <c>1 + t^2</c>, the partial fractions cancel the <c>1 - t^2</c>, and what is left
29384+ /// is a quadratic beside <c>1 + t^2</c>.
29385+ /// </remarks>
29386+ private static Entity WithTheSharedFactorsOutOfEachSum(Entity product, Entity.Variable t)
29387+ {
29388+ Entity written = Number.Integer.One;
29389+ foreach (var factor in Mulf.LinearChildren(product))
29390+ written *= factor is Sumf ? WithTheSharedFactorsOut(factor, t) : factor;
29391+ return written;
29392+ }
29393+
29394+ /// <summary>
29395+ /// <paramref name="sum"/> as the factors with <paramref name="t"/> in them that every
29396+ /// term has, each to the least power any term has it in, times the sum of what is left;
29397+ /// as written where no term shares one.
29398+ /// </summary>
29399+ private static Entity WithTheSharedFactorsOut(Entity sum, Entity.Variable t)
29400+ {
29401+ static Dictionary<Entity, EInteger> Powers(Entity term, Entity.Variable t)
29402+ {
29403+ var powers = new Dictionary<Entity, EInteger>();
29404+ foreach (var factor in Mulf.LinearChildren(term))
29405+ {
29406+ var (@base, power) = factor is Powf(var raised, Number.Integer { EInteger.Sign: > 0 } exponent)
29407+ ? (raised, exponent.EInteger) : (factor, EInteger.One);
29408+ if (@base.ContainsNode(t))
29409+ powers[@base] = powers.TryGetValue(@base, out var before) ? before.Add(power) : power;
29410+ }
29411+ return powers;
29412+ }
29413+ // Inner sums first: in `(a (1 + t^2) 2 t + b (1 - t^2)(1 + t^2)) 2 t + c (1 + t^2)^2 2 t`,
29414+ // which `1/(a + b cot(x) + c csc(x))` combines into, the first term has its
29415+ // `1 + t^2` inside a sum of its own.
29416+ var terms = Sumf.LinearChildren(sum).Select(term => WithTheSharedFactorsOutOfEachSum(term, t)).ToList();
29417+ if (terms.Count < 2)
29418+ return sum;
29419+ var shared = Powers(terms[0], t);
29420+ foreach (var term in terms.Skip(1))
29421+ {
29422+ var powers = Powers(term, t);
29423+ foreach (var @base in shared.Keys.ToList())
29424+ if (powers.TryGetValue(@base, out var power))
29425+ shared[@base] = EInteger.Min(shared[@base], power);
29426+ else
29427+ shared.Remove(@base);
29428+ }
29429+ // A bare `t` shared by every term is a root at zero, which the partial fractions
29430+ // find on their own; only a sum is worth taking out.
29431+ if (!shared.Keys.Any(@base => @base is Sumf or Minusf))
29432+ return terms.Aggregate((left, right) => left + right);
29433+ Entity common = Number.Integer.One;
29434+ foreach (var pair in shared)
29435+ common *= pair.Value.Equals(EInteger.One) ? pair.Key : MathS.Pow(pair.Key, Number.Integer.Create(pair.Value));
29436+ Entity rest = Number.Integer.Zero;
29437+ foreach (var term in terms)
29438+ {
29439+ var left = new Dictionary<Entity, EInteger>(shared);
29440+ Entity kept = Number.Integer.One;
29441+ foreach (var factor in Mulf.LinearChildren(term))
29442+ {
29443+ var (@base, power) = factor is Powf(var raised, Number.Integer { EInteger.Sign: > 0 } exponent)
29444+ ? (raised, exponent.EInteger) : (factor, EInteger.One);
29445+ if (left.TryGetValue(@base, out var owed) && owed.Sign > 0)
29446+ {
29447+ var taken = EInteger.Min(owed, power);
29448+ left[@base] = owed.Subtract(taken);
29449+ power = power.Subtract(taken);
29450+ if (power.IsZero)
29451+ continue;
29452+ kept *= power.Equals(EInteger.One) ? @base : MathS.Pow(@base, Number.Integer.Create(power));
29453+ }
29454+ else
29455+ kept *= factor;
29456+ }
29457+ rest += kept;
29458+ }
29459+ return common * rest;
29460+ }
29461+
2937229462 /// <summary>
2937329463 /// <paramref name="product"/> with each whole power of a product written as the product
2937429464 /// of the powers, the rest as written: <c>((1 + t^2)(1 - t^2))^6</c> is
0 commit comments