diff --git a/Sources/AngouriMath/Convenience/AngouriMathExtensions.cs b/Sources/AngouriMath/Convenience/AngouriMathExtensions.cs index fdfe1d497..dee989172 100644 --- a/Sources/AngouriMath/Convenience/AngouriMathExtensions.cs +++ b/Sources/AngouriMath/Convenience/AngouriMathExtensions.cs @@ -406,6 +406,22 @@ public static Interval ToEntity(this (Entity left, bool leftClosed, Entity right /// public static Entity Factorize(this string expr) => expr.ToEntity().Factorize(); + /// + /// Parses the given expression and writes it as a single fraction: one numerator over one + /// denominator, nothing cancelled. See . + /// + /// The expression as one fraction, or unchanged where it has no division in it + /// + /// + /// Console.WriteLine("a + b/c".AsSingleFraction()); + /// + /// Prints + /// + /// (a * c + b) / c + /// + /// + public static Entity AsSingleFraction(this string expr) => expr.ToEntity().AsSingleFraction(); + /// /// Subsitutes a variable by replacing all its occurances with the given value /// diff --git a/Sources/AngouriMath/Core/Transformations/Transformation.Catalogue.cs b/Sources/AngouriMath/Core/Transformations/Transformation.Catalogue.cs index 9fe022b27..e3fbc7626 100644 --- a/Sources/AngouriMath/Core/Transformations/Transformation.Catalogue.cs +++ b/Sources/AngouriMath/Core/Transformations/Transformation.Catalogue.cs @@ -164,6 +164,96 @@ private sealed class NumericContentTransformation : Transformation => Functions.NumericContent.Extracted(input); } + /// + /// Writes an expression as a single fraction, as + /// does: one numerator over one denominator, nothing divided inside either, and nothing + /// cancelled or multiplied out. Where there is no division the input comes back, and where + /// dividing by a fraction moves its denominator into the numerator the answer says that + /// denominator is nonzero. + /// + /// + /// Held in a nested class for the reason is. + /// + public static Transformation AsSingleFraction => SingleFractionHolder.Instance; + + private static class SingleFractionHolder + { + [ConstantField] + internal static readonly Transformation Instance = new SingleFractionTransformation(); + } + + private sealed class SingleFractionTransformation : Transformation + { + public override string Name => "single-fraction"; + public override TransformationRelation Relation => TransformationRelation.Equivalence; + + // Sound: a sum or a product of quotients is defined exactly where each quotient is, + // and so is the fraction it is gathered into, since nothing is cancelled. Turning a + // quotient over is the one step that moves a denominator out of the way: + // (a/b)/(c/d) is (a d)/(b c), which has a value where d is zero and the expression + // has none, so the answer carries `provided not d = 0`. + public override Soundness Soundness => Soundness.Sound; + + // The two halves are tidied each on its own -- the operands put in order and like + // terms collected, which is Simplify's own tidying pass without its search, so + // `t + 1 + t^2 + 1` is `2 + t + t^2` -- and the quotient is not, so that no factor of + // the numerator meets one of the denominator and cancels. + protected override Entity? ApplyCore(Entity input) + { + // A number is already what it is; there is nothing in it to gather. + if (input is Entity.Number) + return input; + // A rational number is a fraction too, as it is written: 2/3 + x/2 is (3 x + 4)/6, + // not (x + 4/3)/2. Inside a function's argument it folds back when its half is + // tidied, since the argument is not gathered. + var written = input.Replace(static node => node is Entity.Number.Rational { ERational: var value } and not Entity.Number.Integer + ? new Entity.Divf(Entity.Number.Integer.Create(value.Numerator), Entity.Number.Integer.Create(value.Denominator)) + : node); + var carried = new List(); + var (numerator, denominator) = Functions.SingleQuotient.OverLeastCommonDenominator(written, carried); + if (denominator == Entity.Number.Integer.One && carried.Count == 0) + return input; + var top = Tidied(numerator); + var bottom = Tidied(denominator); + var fraction = bottom == Entity.Number.Integer.One ? top : new Entity.Divf(top, bottom); + + // Only what the new denominator does not already exclude: in (1/x)/(1/x), which + // is x/x, the x carried up is still in the denominator. + var nonzero = new List(); + foreach (var factor in carried) + { + var tidied = Tidied(factor); + if (!AlreadyNonzero(tidied, bottom) && !nonzero.Contains(tidied)) + nonzero.Add(tidied); + } + if (nonzero.Count == 0) + return fraction; + var condition = !nonzero[0].EqualTo(0); + for (var i = 1; i < nonzero.Count; i++) + condition &= !nonzero[i].EqualTo(0); + return new Entity.Providedf(fraction, condition); + } + + // Simplify's tidying pass, and the neatening it ends on, which writes `x^2 + -1` as + // `x^2 - 1`. + private static Entity Tidied(Entity half) + => Functions.Simplificator.SimplifyChildren(half).Rewrite(RewriteRules.NumericNeat); + + // Whether `denominator` being nonzero already says `factor` is: a nonzero number + // always is, and otherwise each factor of `factor` has to be one of the + // denominator's, up to a whole positive power, since a product vanishes exactly where + // one of its factors does and a power exactly where its base does. + private static bool AlreadyNonzero(Entity factor, Entity denominator) + { + var excluded = Entity.Mulf.LinearChildren(denominator).Select(Base).ToList(); + return Entity.Mulf.LinearChildren(factor).All(piece => + piece is Entity.Number.Complex { IsZero: false } || excluded.Contains(Base(piece))); + + static Entity Base(Entity piece) + => piece is Entity.Powf(var @base, Entity.Number.Integer power) && power.EInteger.Sign > 0 ? @base : piece; + } + } + /// /// The rule-based half of , without the polynomial /// layer. diff --git a/Sources/AngouriMath/Docs/Contributing/Transformations.md b/Sources/AngouriMath/Docs/Contributing/Transformations.md index 0cb49b7f6..711cb8743 100644 --- a/Sources/AngouriMath/Docs/Contributing/Transformations.md +++ b/Sources/AngouriMath/Docs/Contributing/Transformations.md @@ -55,6 +55,7 @@ on hashing or on which type loaded first. Entity.Simplify(level) -> Transformation.SimplificationAtLevel(level) -> Simplificator.Simplify Entity.Expand(level) -> Transformation.ExpansionAtLevel(level) -> Entity.ExpandOverSum Entity.Factorize(level) -> Transformation.FactorizationAtLevel(level) -> RewriteRules, composed +Entity.AsSingleFraction() -> Transformation.AsSingleFraction -> SingleQuotient.OverLeastCommonDenominator, each half tidied Entity.Differentiate(x) -> Transformation.Differentiation(x) -> Entity.DifferentiateOnce Entity.Integrate(x) -> Transformation.Integration(x) -> Integration.ComputeIndefiniteIntegral Entity.Limit(x, to, side) -> Transformation.LimitAt(x, to, side) -> LimitFunctional.ComputeLimit diff --git a/Sources/AngouriMath/Functions/Evaluation/Evaluation.Definition.cs b/Sources/AngouriMath/Functions/Evaluation/Evaluation.Definition.cs index 7d111ef5d..6db8d75a0 100644 --- a/Sources/AngouriMath/Functions/Evaluation/Evaluation.Definition.cs +++ b/Sources/AngouriMath/Functions/Evaluation/Evaluation.Definition.cs @@ -747,6 +747,39 @@ private static Entity CollectLikeTerms(Entity expanded) public Entity Factorize(int level = 2) => Transformation.FactorizationAtLevel(level).ApplyOrKeep(this); + /// + /// This expression written as a single fraction: one numerator over one denominator, with + /// nothing divided inside either, as a + b/c is written (a c + b)/c. Any + /// expression, functions included, and nothing cancelled or multiplied out, so the factors + /// of the denominator stay as they were: 1/(t^2 + 1) + 1/(t + 1) is + /// (2 + t + t^2) / ((t^2 + 1)(t + 1)), and x/x stays x/x. The common + /// denominator is the least common multiple of the denominators as they are written, so + /// 1/x + 1/x^2 is (x + 1)/x^2; nothing is factorised to find it. A rational number + /// counts as the fraction it is written as, so 2/3 + x/2 is (4 + 3x)/6. An + /// expression with no division in it comes back as it was. Dividing by a fraction moves + /// its denominator into the numerator, and the expression has no value where that + /// denominator is zero, so the answer says so: 1/(1/x) is x provided not x = 0. + /// + /// + /// never does this, since one fraction is not always the + /// simpler form: 1/x + 1/y reads more easily than (x + y)/(x y). For a + /// rational function in lowest terms, the denominator multiplied out and monic, use + /// , which is a canonical form and + /// declines anything with a function in it. + /// #1239 + /// + /// + /// + /// Console.WriteLine("sin(x) + 1/cos(x)".ToEntity().AsSingleFraction()); + /// + /// Prints + /// + /// (1 + cos(x) * sin(x)) / cos(x) + /// + /// + public Entity AsSingleFraction() + => Transformation.AsSingleFraction.ApplyOrKeep(this); + /// /// Simplifies an equation ( e.g. (x - y) * (x + y) -> x^2 - y^2, but 3 * x + y * x = (3 + y) * x ) /// diff --git a/Sources/AngouriMath/Functions/SingleQuotient.cs b/Sources/AngouriMath/Functions/SingleQuotient.cs index af3a81369..3788e2978 100644 --- a/Sources/AngouriMath/Functions/SingleQuotient.cs +++ b/Sources/AngouriMath/Functions/SingleQuotient.cs @@ -17,7 +17,7 @@ namespace AngouriMath.Functions /// /// /// - /// This is what other systems call together or ratsimp, and it is deliberately + /// This is writing an expression as a single fraction, and it is deliberately /// not part of . Putting a sum over a common /// denominator makes some expressions worse — 1/x + 1/y is easier to read than /// (x + y)/(x*y) — which is why every system that has it keeps it as an operation you @@ -34,6 +34,12 @@ namespace AngouriMath.Functions /// distribution and is integrated at once, and was declined for want of it. /// /// + /// The public entry is , through + /// , which + /// gathers through , reads a rational number as the + /// fraction it is written as, and tidies each half. + /// + /// /// No cancellation. The two halves are returned as built, with no common factor taken /// out: x/x comes back as (x, x) and not as (1, 1). Cancelling needs a /// gcd, which needs to know what the expression is a polynomial in, and this runs @@ -44,7 +50,7 @@ namespace AngouriMath.Functions /// It always terminates and never grows without bound, because it recurses only into /// the operands of the node it is given and each recursion is on a strictly smaller tree. What /// it can do is make the tree bigger — combining a sum of n quotients multiplies the - /// denominators — so is a transformation to ask for rather than one to apply + /// denominators — so is a transformation to ask for rather than one to apply /// on the way past. /// /// @@ -55,36 +61,60 @@ internal static class SingleQuotient /// expression that had no division in it, which is the signal that nothing was combined. /// internal static (Entity Numerator, Entity Denominator) Of(Entity expr) + => Of(expr, carried: null, leastCommon: false); + + /// + /// The expression as one fraction, written the way it is by hand: a sum goes over the + /// least common multiple of its terms' denominators as they stand, each factor to the + /// highest power any of them has it and the whole numbers by their least common multiple, + /// so 1/x + 1/x^2 is (x + 1, x^2) and not (x^2 + x, x^3). Nothing is + /// factorised to find it: x^2 - 1 and x + 1 share no factor here. Each + /// denominator that turning a quotient over moves into the numerator is added to + /// , since the expression is undefined where one of those is + /// zero and the fraction need not be: 1/(1/x) is (x, 1), which has a value + /// at zero. + /// + /// + /// keeps the product of the denominators, for the callers above, + /// which divide out afterwards. + /// + internal static (Entity Numerator, Entity Denominator) OverLeastCommonDenominator(Entity expr, List carried) + => Of(expr, carried, leastCommon: true); + + private static (Entity Numerator, Entity Denominator) Of(Entity expr, List? carried, bool leastCommon) { switch (expr) { case Divf(var dividend, var divisor): { - var (an, ad) = Of(dividend); - var (bn, bd) = Of(divisor); + var (an, ad) = Of(dividend, carried, leastCommon); + var (bn, bd) = Of(divisor, carried, leastCommon); // (an/ad) / (bn/bd) = (an * bd) / (ad * bn) + Carry(bd, carried); return (Times(an, bd), Times(ad, bn)); } case Mulf(var left, var right): { - var (an, ad) = Of(left); - var (bn, bd) = Of(right); + var (an, ad) = Of(left, carried, leastCommon); + var (bn, bd) = Of(right, carried, leastCommon); return (Times(an, bn), Times(ad, bd)); } case Sumf(var augend, var addend): { - var (an, ad) = Of(augend); - var (bn, bd) = Of(addend); - return (Plus(Times(an, bd), Times(bn, ad)), Times(ad, bd)); + var (an, ad) = Of(augend, carried, leastCommon); + var (bn, bd) = Of(addend, carried, leastCommon); + var (common, forA, forB) = Over(ad, bd, leastCommon); + return (Plus(Times(an, forA), Times(bn, forB)), common); } case Minusf(var minuend, var subtrahend): { - var (an, ad) = Of(minuend); - var (bn, bd) = Of(subtrahend); - return (Minus(Times(an, bd), Times(bn, ad)), Times(ad, bd)); + var (an, ad) = Of(minuend, carried, leastCommon); + var (bn, bd) = Of(subtrahend, carried, leastCommon); + var (common, forA, forB) = Over(ad, bd, leastCommon); + return (Minus(Times(an, forA), Times(bn, forB)), common); } // A whole power distributes over the quotient, and a negative one turns it over. @@ -92,9 +122,11 @@ internal static (Entity Numerator, Entity Denominator) Of(Entity expr) // (a/b)^(1/2) is not sqrt(a)/sqrt(b) on the branch cut. case Powf(var @base, Integer power): { - var (bn, bd) = Of(@base); + var (bn, bd) = Of(@base, carried, leastCommon); if (bd == Integer.One && power.EInteger.Sign >= 0) return (expr, Integer.One); + if (power.EInteger.Sign < 0) + Carry(bd, carried); // The first power is the base itself: `A^(-1)` is `1/A`, not `1^1/A^1`, // which nothing reading the factors of the denominator took for `A`. if (power.EInteger.Equals(EInteger.FromInt32(-1))) @@ -109,6 +141,102 @@ internal static (Entity Numerator, Entity Denominator) Of(Entity expr) } } + private static void Carry(Entity denominator, List? carried) + { + if (carried is not null && denominator != Integer.One) + carried.Add(denominator); + } + + /// + /// A common denominator of and , with what each + /// is multiplied by to reach it: their product, or with + /// their least common multiple as written. + /// + private static (Entity Common, Entity ForA, Entity ForB) Over(Entity a, Entity b, bool leastCommon) + { + if (!leastCommon) + return (Times(a, b), b, a); + if (a == Integer.One) + return (b, b, Integer.One); + if (b == Integer.One || a == b) + return (a, Integer.One, b == Integer.One ? a : Integer.One); + var (aWhole, aFactors) = Factors(a); + var (bWhole, bFactors) = Factors(b); + if (aWhole.IsZero || bWhole.IsZero) + return (Times(a, b), b, a); + var whole = aWhole.Abs().Divide(aWhole.Abs().Gcd(bWhole.Abs())).Multiply(bWhole.Abs()); + var common = new List<(Entity Base, EInteger Power)>(aFactors); + foreach (var (@base, power) in bFactors) + { + var at = common.FindIndex(factor => factor.Base == @base); + if (at < 0) + common.Add((@base, power)); + else if (power.CompareTo(common[at].Power) > 0) + common[at] = (@base, power); + } + return (Product(whole, common, null), + Product(whole.Divide(aWhole), common, aFactors), + Product(whole.Divide(bWhole), common, bFactors)); + } + + /// + /// A denominator as a whole number times its other factors, each with the whole power it + /// is raised to: a factor written twice is one factor with the powers added, and a power + /// of a whole number is part of the whole number. + /// + private static (EInteger Whole, List<(Entity Base, EInteger Power)> Factors) Factors(Entity denominator) + { + var whole = EInteger.One; + var factors = new List<(Entity Base, EInteger Power)>(); + foreach (var factor in Mulf.LinearChildren(denominator)) + { + switch (factor) + { + case Integer integer: + whole = whole.Multiply(integer.EInteger); + continue; + case Powf(Integer number, Integer power) when power.EInteger.Sign > 0 + && power.EInteger.CompareTo(EInteger.FromInt32(MaxFoldedPower)) <= 0: + whole = whole.Multiply(number.EInteger.Pow(power.EInteger)); + continue; + } + var (@base, exponent) = factor is Powf(var raised, Integer raisedTo) && raisedTo.EInteger.Sign > 0 + ? (raised, raisedTo.EInteger) + : (factor, EInteger.One); + var at = factors.FindIndex(known => known.Base == @base); + if (at < 0) + factors.Add((@base, exponent)); + else + factors[at] = (@base, factors[at].Power.Add(exponent)); + } + return (whole, factors); + } + + /// + /// A power of a whole number is multiplied out into the whole number only up to this + /// exponent; past it the power stays a factor like any other, which is still correct. + /// + private const int MaxFoldedPower = 64; + + /// + /// times each factor of to the power + /// that is short of it, or to its whole power where + /// is . + /// + private static Entity Product(EInteger whole, List<(Entity Base, EInteger Power)> common, + List<(Entity Base, EInteger Power)>? own) + { + Entity product = Integer.Create(whole); + foreach (var (@base, power) in common) + { + var ownPower = own?.Find(factor => factor.Base == @base).Power ?? EInteger.Zero; + var missing = power.Subtract(ownPower); + if (missing.Sign > 0) + product = Times(product, missing.Equals(EInteger.One) ? @base : MathS.Pow(@base, Integer.Create(missing))); + } + return product; + } + /// /// rewritten as a single quotient, or unchanged where it had no /// division in it to combine. diff --git a/Sources/Tests/UnitTests/Algebra/AsSingleFractionTest.cs b/Sources/Tests/UnitTests/Algebra/AsSingleFractionTest.cs new file mode 100644 index 000000000..4f102fae8 --- /dev/null +++ b/Sources/Tests/UnitTests/Algebra/AsSingleFractionTest.cs @@ -0,0 +1,136 @@ +// +// 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.Linq; +using AngouriMath; +using AngouriMath.Core.Transformations; +using AngouriMath.Extensions; +using Xunit; + +namespace AngouriMath.Tests.Algebra +{ + /// + /// An expression written as a single fraction: one numerator over one denominator, nothing + /// divided inside either, nothing cancelled or multiplied out. + /// https://github.com/asc-community/AngouriMath/issues/1239 + /// + [Trait("Area", "Algebra")] + public sealed class AsSingleFractionTest + { + /// The printed form is the point of the operation, so it is what these assert. + [Theory] + [InlineData("a + b/c", "(a * c + b) / c")] + [InlineData("1 + 2/(1+t^2)", "(t ^ 2 + 3) / (t ^ 2 + 1)")] + [InlineData("1/(t^2+1) + 1/(t+1)", "(2 + t + t ^ 2) / ((t ^ 2 + 1) * (t + 1))")] + [InlineData("sin(x) + 1/cos(x)", "(1 + cos(x) * sin(x)) / cos(x)")] + [InlineData("2/3 + x/2", "(4 + 3 * x) / 6")] + [InlineData("x^(-2) + 1", "(x ^ 2 + 1) / x ^ 2")] + public void WrittenAsOneFraction(string written, string fraction) + => Assert.Equal(fraction, written.AsSingleFraction().Stringize()); + + /// + /// Over the least common multiple of the denominators as they are written, as it is done + /// by hand: a factor two terms share is not multiplied in twice. Nothing is factorised to + /// find it, so x^2 - 1 and x + 1 share nothing. + /// + [Theory] + [InlineData("1/x + 1/x^2", "(x + 1) / x ^ 2")] + [InlineData("x/2 + x/4", "x * 3 / 4")] + [InlineData("1/6 + 1/4", "5 / 12")] + [InlineData("1/(x + 1) + 1/(x + 1)^2", "(x + 2) / (x + 1) ^ 2")] + [InlineData("a/(b*c) + d/(b*e)", "(a * e + c * d) / (b * c * e)")] + [InlineData("1/(x^2 - 1) + 1/(x + 1)", "(x + x ^ 2) / ((x ^ 2 - 1) * (x + 1))")] + public void OverTheLeastCommonDenominator(string written, string fraction) + => Assert.Equal(fraction, written.AsSingleFraction().Stringize()); + + /// + /// Dividing by a fraction moves its denominator into the numerator, where it no longer + /// stops the answer having a value, so the answer says it is nonzero -- unless the new + /// denominator still says so itself. + /// + [Theory] + [InlineData("1/(1/x)", "x provided not x = 0")] + [InlineData("(a/b)/(c/d)", "a * d / (b * c) provided not d = 0")] + [InlineData("(x/y)^(-2)", "y ^ 2 / x ^ 2 provided not y = 0")] + [InlineData("(1/x)/(1/x)", "x / x")] + public void TurningAFractionOverSaysItsDenominatorIsNonzero(string written, string fraction) + => Assert.Equal(fraction, written.AsSingleFraction().Stringize()); + + /// + /// Undefined where the expression is: at zero for the variable named, and one for every + /// other, each of these has no value, and neither has its single fraction. + /// + [Theory] + [InlineData("1/(1/x)", "x")] + [InlineData("(a/b)/(c/d)", "d")] + [InlineData("(1/x)^(-1)", "x")] + [InlineData("(x/y)^(-2)", "y")] + [InlineData("x/(y/x)", "x")] + [InlineData("(1/x)/(1/x)", "x")] + [InlineData("1/x + 1/x^2", "x")] + public void TheDomainIsKept(string written, string atZero) + { + var original = written.ToEntity(); + var fraction = original.AsSingleFraction(); + foreach (var variable in original.Vars) + { + Entity value = variable.Name == atZero ? 0 : 1; + original = original.Substitute(variable, value); + fraction = fraction.Substitute(variable, value); + } + Assert.Equal(MathS.NaN, original.Evaled); + Assert.Equal(MathS.NaN, fraction.Evaled); + } + + /// Nothing cancels, and a function's argument is not gathered. + [Theory] + [InlineData("x/x", "x / x")] + [InlineData("sin(x/2) + 1/x", "(1 + sin(x / 2) * x) / x")] + public void NothingIsCancelledAndArgumentsAreLeft(string written, string fraction) + => Assert.Equal(fraction, written.AsSingleFraction().Stringize()); + + /// With no division in it, or a number, the expression comes back as it was. + [Theory] + [InlineData("x")] + [InlineData("x^2 + 1")] + [InlineData("1/2")] + public void WithNothingToGatherItComesBack(string written) + => Assert.Equal(written.ToEntity(), written.ToEntity().AsSingleFraction()); + + /// The same value everywhere both are defined. + [Theory] + [InlineData("a + b/c")] + [InlineData("1/(t^2+1) + 1/(t+1)")] + [InlineData("sin(x) + 1/cos(x)")] + [InlineData("2/3 + x/2")] + [InlineData("(a/b)/(c/d)")] + [InlineData("x/(y/x)")] + [InlineData("1/x + 1/x^2")] + [InlineData("a/(b*c) + d/(b*e)")] + [InlineData("1/(x + 1) + 1/(x + 1)^2")] + public void TheValueIsKept(string written) + { + var original = written.ToEntity(); + var fraction = original.AsSingleFraction(); + foreach (var (name, value) in new[] { ("a", 2), ("b", -3), ("c", 5), ("d", 11), ("e", 17), ("t", 3), ("x", 7), ("y", 13) }) + { + original = original.Substitute(name, value); + fraction = fraction.Substitute(name, value); + } + Assert.Equal(original.EvalNumerical().RealPart.EDecimal.RoundToPrecision(PeterO.Numbers.EContext.ForPrecision(30)), + fraction.EvalNumerical().RealPart.EDecimal.RoundToPrecision(PeterO.Numbers.EContext.ForPrecision(30))); + } + + [Fact] + public void TheTransformationIsTheMethod() + { + Entity written = "1 + 2/(1+t^2)"; + Assert.Equal(written.AsSingleFraction(), Transformation.AsSingleFraction.ApplyOrKeep(written)); + Assert.Equal("single-fraction", Transformation.AsSingleFraction.Name); + } + } +} diff --git a/Sources/Tests/UnitTests/Common/PublicApi.txt b/Sources/Tests/UnitTests/Common/PublicApi.txt index 8e82a22ab..b48d96118 100644 --- a/Sources/Tests/UnitTests/Common/PublicApi.txt +++ b/Sources/Tests/UnitTests/Common/PublicApi.txt @@ -343,6 +343,7 @@ AngouriMath.Core.Transformations.Soundness.value__ : System.Int32 AngouriMath.Core.Transformations.Transformation.Apply(AngouriMath.Entity) : AngouriMath.Core.Transformations.TransformationResult AngouriMath.Core.Transformations.Transformation.ApplyCore(AngouriMath.Entity) : AngouriMath.Entity AngouriMath.Core.Transformations.Transformation.ApplyOrKeep(AngouriMath.Entity) : AngouriMath.Entity +AngouriMath.Core.Transformations.Transformation.AsSingleFraction { } : AngouriMath.Core.Transformations.Transformation AngouriMath.Core.Transformations.Transformation.Canonicalization { } : AngouriMath.Core.Transformations.Transformation AngouriMath.Core.Transformations.Transformation.CanonicalizationOverGraph(AngouriMath.Core.Budgets.WorkBudget) : AngouriMath.Core.Transformations.Transformation AngouriMath.Core.Transformations.Transformation.CanonicalizationOverGraph(AngouriMath.Core.Budgets.WorkBudget, AngouriMath.Core.Transformations.RewriteRuleGrowth) : AngouriMath.Core.Transformations.Transformation @@ -2786,6 +2787,7 @@ AngouriMath.Entity.Arccotan() : AngouriMath.Entity AngouriMath.Entity.Arcsec() : AngouriMath.Entity AngouriMath.Entity.Arcsin() : AngouriMath.Entity AngouriMath.Entity.Arctan() : AngouriMath.Entity +AngouriMath.Entity.AsSingleFraction() : AngouriMath.Entity AngouriMath.Entity.Canonicalize() : AngouriMath.Entity AngouriMath.Entity.CanonicalizeAsRationalFunction() : AngouriMath.Entity AngouriMath.Entity.Ceil() : AngouriMath.Entity @@ -2934,6 +2936,7 @@ AngouriMath.Entity.op_Multiply(AngouriMath.Entity, AngouriMath.Entity) : Angouri AngouriMath.Entity.op_Subtraction(AngouriMath.Entity, AngouriMath.Entity) : AngouriMath.Entity AngouriMath.Entity.op_UnaryNegation(AngouriMath.Entity) : AngouriMath.Entity AngouriMath.Entity.op_UnaryPlus(AngouriMath.Entity) : AngouriMath.Entity +AngouriMath.Extensions.AngouriMathExtensions.AsSingleFraction(System.String) : AngouriMath.Entity AngouriMath.Extensions.AngouriMathExtensions.Compile(System.String, AngouriMath.Core.Compilation.IntoLinq.CompilationProtocol, System.Type, System.Collections.Generic.IEnumerable>) : TDelegate AngouriMath.Extensions.AngouriMathExtensions.Compile(System.String, AngouriMath.Entity+Variable) : Func AngouriMath.Extensions.AngouriMathExtensions.Compile(System.String, AngouriMath.Entity+Variable, AngouriMath.Entity+Variable) : Func