@@ -26347,6 +26347,104 @@ when two.Equals(EInteger.FromInt32(2)) && e1 == MathS.e && e2 == MathS.e
2634726347 return answer.Nodes.Any(node => node == MathS.NaN) ? null : answer;
2634826348 }
2634926349
26350+ /// <summary>
26351+ /// A product of whole powers of a sine and a cosine of one linear times something else in x
26352+ /// that is no polynomial, <c>sin(u)^3 cos(u)^6/(a + b u)</c>, linearised: the product is a sum
26353+ /// of <c>cos(k u)</c> and <c>sin(k u)</c> by the product-to-sum formulas, and each term beside
26354+ /// the rest its own question.
26355+ /// </summary>
26356+ /// <remarks>
26357+ /// <c>sin(3 u)/(a + b u)</c> is answered in sines and cosine integrals, and the product of
26358+ /// powers was not: Rubi's 5.1.4 <c>x^3 (1 - c^2 x^2)^(5/2)/(a + b asin(c x))</c>, which under
26359+ /// <c>u = asin(c x)</c> is that product, was declined. Last but one, so that everything
26360+ /// answering the product as written goes first.
26361+ /// https://github.com/asc-community/AngouriMath/issues/718
26362+ /// </remarks>
26363+ internal static Entity? SolveAProductOfPowersOfASineAndACosineByLinearising(Entity expr, Entity.Variable x)
26364+ {
26365+ if (!Integration.AnsweringTheQuestionAsked)
26366+ return null;
26367+ static IEnumerable<(Entity Factor, bool Below)> Factors(Entity e, bool below) => e switch
26368+ {
26369+ Mulf(var l, var r) => Factors(l, below).Concat(Factors(r, below)),
26370+ Divf(var l, var r) => Factors(l, below).Concat(Factors(r, !below)),
26371+ _ => new[] { (e, below) }
26372+ };
26373+ Entity? argument = null;
26374+ int sines = 0, cosines = 0;
26375+ Entity rest = Number.Integer.One;
26376+ foreach (var (factor, below) in Factors(expr, false))
26377+ {
26378+ var (function, power) = factor is Powf(var b0, Number.Integer { EInteger: var p0 }) && b0 is Sinf or Cosf && p0.Sign > 0 && p0.CanFitInInt32()
26379+ ? (b0, p0.ToInt32Unchecked()) : (factor, 1);
26380+ if (!below && function is Sinf or Cosf && function.ContainsNode(x))
26381+ {
26382+ var y0 = function is Sinf(var ys) ? ys : ((Cosf)function).Argument;
26383+ if (argument is not null && argument != y0)
26384+ return null;
26385+ argument = y0;
26386+ if (function is Sinf) sines += power; else cosines += power;
26387+ }
26388+ else
26389+ rest = below ? rest / factor : rest * factor;
26390+ }
26391+ if (argument is null || sines + cosines < 2 || sines + cosines > 24
26392+ || !TreeAnalyzer.TryGetPolyLinear(argument, x, out _, out _)
26393+ || !rest.ContainsNode(x) || rest.Nodes.Any(node => node is Sinf or Cosf or Tanf or Cotanf or Secantf or Cosecantf)
26394+ || TreeAnalyzer.TryGetPolynomial(rest, x, out var asPolynomial) && asPolynomial.Keys.All(key => key.Sign >= 0))
26395+ return null;
26396+ // cos(k y) and sin(k y) coefficients, multiplied by sin y and cos y one at a time.
26397+ var cosine = new Dictionary<int, ERational> { [0] = ERational.One };
26398+ var sine = new Dictionary<int, ERational>();
26399+ static void Add(Dictionary<int, ERational> into, int k, ERational value)
26400+ => into[k] = (into.TryGetValue(k, out var old) ? old + value : value).ToLowestTerms();
26401+ var half = ERational.Create(1, 2);
26402+ for (var step = 0; step < sines + cosines; step++)
26403+ {
26404+ var bySine = step < sines;
26405+ var (nextCosine, nextSine) = (new Dictionary<int, ERational>(), new Dictionary<int, ERational>());
26406+ foreach (var (k, value) in cosine.Select(pair => (pair.Key, pair.Value)))
26407+ if (bySine)
26408+ { // cos(k y) sin y = (sin((k + 1) y) - sin((k - 1) y))/2
26409+ Add(nextSine, k + 1, value * half);
26410+ if (k - 1 > 0) Add(nextSine, k - 1, -value * half);
26411+ else if (k - 1 < 0) Add(nextSine, 1 - k, value * half);
26412+ }
26413+ else
26414+ { // cos(k y) cos y = (cos((k + 1) y) + cos((k - 1) y))/2
26415+ Add(nextCosine, k + 1, value * half);
26416+ Add(nextCosine, System.Math.Abs(k - 1), value * half);
26417+ }
26418+ foreach (var (k, value) in sine.Select(pair => (pair.Key, pair.Value)))
26419+ if (bySine)
26420+ { // sin(k y) sin y = (cos((k - 1) y) - cos((k + 1) y))/2
26421+ Add(nextCosine, System.Math.Abs(k - 1), value * half);
26422+ Add(nextCosine, k + 1, -value * half);
26423+ }
26424+ else
26425+ { // sin(k y) cos y = (sin((k + 1) y) + sin((k - 1) y))/2
26426+ Add(nextSine, k + 1, value * half);
26427+ if (k - 1 > 0) Add(nextSine, k - 1, value * half);
26428+ else if (k - 1 < 0) Add(nextSine, 1 - k, -value * half);
26429+ }
26430+ (cosine, sine) = (nextCosine, nextSine);
26431+ }
26432+ Entity answer = Number.Integer.Zero;
26433+ foreach (var (terms, isSine) in new[] { (cosine, false), (sine, true) })
26434+ foreach (var (k, value) in terms.OrderBy(term => term.Key).Select(pair => (pair.Key, pair.Value)))
26435+ {
26436+ if (value.IsZero)
26437+ continue;
26438+ var multiple = k == 1 ? argument : Number.Integer.Create(k) * argument;
26439+ Entity piece = k == 0 ? rest : (isSine ? MathS.Sin(multiple) : MathS.Cos(multiple)) * rest;
26440+ if (Integration.ComputeAsAQuestionOfItsOwn(piece, x, integrateByParts: true) is not { } integrated)
26441+ return null;
26442+ answer += Number.Rational.Create(value.Numerator, value.Denominator) * integrated;
26443+ }
26444+ answer = answer.InnerSimplified;
26445+ return answer.Nodes.Any(node => node == MathS.NaN) ? null : answer;
26446+ }
26447+
2635026448 /// <summary>
2635126449 /// A rational function of a sine, or of a cosine, over a quadratic in it with a symbol
2635226450 /// among the coefficients: <c>sin(x)/(a + b sin(x) + c sin(x)^2)</c>, and the same with
0 commit comments