Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21546,6 +21546,26 @@ private static bool IsProperByDegree(Entity numerator, Entity denominator, Entit
&& TreeAnalyzer.TryGetPolynomial(denominator, x, out var below) && below.Count > 0
&& above.Keys.Max()!.CompareTo(below.Keys.Max()!) < 0;

/// <summary>Whether <paramref name="x"/> stands in <paramref name="expr"/> only under even whole powers.</summary>
private static bool IsEvenAsWritten(Entity expr, Entity.Variable x)
=> expr.Nodes.Count(node => node == x)
== expr.Nodes.Count(node => node is Powf(var @base, Number.Integer power) && @base == x && power.EInteger.IsEven);

/// <summary>
/// The degree in <paramref name="x"/> of a polynomial as it is written, without expanding
/// it: of a power its base's times the exponent, of a product the sum, of a sum the most.
/// </summary>
private static int DegreeAsWritten(Entity expr, Entity.Variable x) => expr switch
{
_ when !expr.ContainsNode(x) => 0,
Variable => 1,
Powf(var @base, Number.Integer power) when power.EInteger.CanFitInInt32() => DegreeAsWritten(@base, x) * power.EInteger.ToInt32Checked(),
Mulf(var left, var right) => DegreeAsWritten(left, x) + DegreeAsWritten(right, x),
Sumf(var left, var right) => System.Math.Max(DegreeAsWritten(left, x), DegreeAsWritten(right, x)),
Minusf(var left, var right) => System.Math.Max(DegreeAsWritten(left, x), DegreeAsWritten(right, x)),
_ => 0,
};

/// <summary>Whether every node of <paramref name="expr"/> holding <paramref name="x"/> is a sum, a product, a quotient or a whole power.</summary>
private static bool IsARationalFunction(Entity expr, Entity.Variable x)
=> expr.Nodes.All(node => !node.ContainsNode(x) || node is Variable or Sumf or Minusf or Mulf or Divf || node is Powf(_, Number.Integer));
Expand Down Expand Up @@ -28928,6 +28948,15 @@ private static bool IsAFunctionOfTheTangentAlone(Entity expr, Entity.Variable u)
// page of piecewise on the discriminant of the quadratic the other two make.
if (IsARationalFunction(expr, x) && TryReadAsQuotient(expr, out _, out var writtenBelow) && IsAProductOfSymbolicLinearFactors(writtenBelow, x))
return null;
// And an even one with symbols in it, past a sextic below the bar: the derivative of
// anything even is odd, so no polynomial candidate is the substitution for it, and
// the search spent thirty seconds simplifying the quotient by each to decline them all
// on `(1 + x^2)^2/((c + d + (c + 3d) x^2 + 2d x^4)^3 (1 + 2x^2))`, which the half-angle
// tangent makes of `1/((c + d sec(y))^3 sqrt(a + a sec(y)))`, where the partial
// fractions answer it in under one.
if (IsARationalFunction(expr, x) && IsEvenAsWritten(expr, x) && expr.Nodes.Any(node => node is Variable symbol && symbol != x)
&& TryReadAsQuotient(expr, out _, out var evenBelow) && DegreeAsWritten(evenBelow, x) >= 6)
return null;
// A rational function of exponentials of linears in x with a whole power of a
// sum of them in it is the exponential substitution's, exactly and at once, and
// this search is not the tool for it: `tanh(x)^5/sech(x)^4` arrives as a fifth
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//
// 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
{
/// <summary>
/// An even rational function with symbols in it and a denominator past a sextic, by the
/// partial fractions without the substitution search in front of them. Rubi's 1.2.2.6, and
/// what the half-angle tangent makes of 4.5.2.1's.
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class AnEvenRationalFunctionIntegralTest
{
[Theory]
[InlineData("(1 + x^2)^2/((c + d + (c + 3*d)*x^2 + 2*d*x^4)^3*(1 + 2*x^2))")]
[InlineData("(d + g*x^2 + f*x^4)/(x^4*(a + b*x^2 + c*x^4)^2)")]
public void ByThePartialFractions(string integrand)
{
var integral = integrand.ToEntity().Integrate("x");
var text = integral.Stringize();
Assert.DoesNotContain("integral(", text);
Assert.True(text.Length < 40000, $"{text.Length} characters of answer for {integrand}");
Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("b", 0.4).Substitute("c", 0.9).Substitute("d", 0.6).Substitute("f", 0.7).Substitute("g", 0.2);
var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
var original = Pinned(integrand.ToEntity());
var compared = 0;
foreach (var at in new[] { -2.1, -0.4, 0.3, 0.6, 1.6, 2.5 })
{
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}");
}
}
}
Loading