diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index f4c8a7de7..666a34afa 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -76,6 +76,29 @@ the square root the solver takes after solving for `x^2`, where an equation has | `"x^4 - (a^2 + b^2)*x^2 + a^2*b^2 = 0".Solve("x")` | `{ sqrt((a ^ 2 + b ^ 2 - sqrt((-(a ^ 2 + b ^ 2)) ^ 2 - 4 * a ^ 2 * b ^ 2)) / 2), … }` | `{ -b, b, -a, a }` | | `"sin(x)^2 = (a + 1)^2".Solve("x")` | `{ arcsin(sqrt((a + 1) ^ 2)) + 2 * pi * n_1, … }` | `{ arcsin(-a - 1) + 2 * pi * n_1, … }` | +### A fractional power is inverted where its values lie + +**Silent: a root gains the condition under which it is one.** The principal power `z^p` has argument `p arg(z)`, so +for `0 < p < 1` its values lie in the sector `-p pi < arg <= p pi`, with `0`, and for `-1 < p < 0` in +`p pi <= arg < -p pi`, without `0`. `x^p = a` was solved to `a^(1/p)` with no condition, which is no root where `a` is +outside the sector: `sqrt(x) = a` was `{ a ^ 2 }`, and at `a = -1` that is `1`, whose square root is `1` and not `-1`. The root now carries +the sector, written with `arg`, as `abs`'s inversion carries its own condition. A number on the other side is decided +as before, and `|p| > 1` needs no condition, since every value is then a power. What the equation is provided is known +while it is solved, as a provided expression is differentiated: a value known to be real, or declared real, has the +half-line for its sector, one known to lie on it needs no condition, and one known to lie off it has no root. A condition that is already part of +another is no longer stated twice: a system's rows carried the same condition once a substitution +([#212](https://github.com/asc-community/AngouriMath/issues/212)). + +| Input | Was (2.6.0) | Now | +|---|---|---| +| `"sqrt(x) = a".ToEntity().Solve("x")` | `{ a ^ 2 }` | `{ a ^ 2 provided a = 0 or -1/2 * pi < arg(a) and arg(a) <= 1/2 * pi }` | +| `"x^(1/3) = a".ToEntity().Solve("x")` | `{ a ^ 3 }` | `{ a ^ 3 provided a = 0 or -1/3 * pi < arg(a) and arg(a) <= 1/3 * pi }` | +| `"x^(-1/2) = a".ToEntity().Solve("x")` | `{ a ^ (-2) }` | `{ a ^ (-2) provided not a = 0 and -1/2 * pi <= arg(a) and arg(a) < 1/2 * pi }` | +| `"(sqrt(x) = a) provided a in RR".ToEntity().Solve("x")` | `{ a ^ 2 provided a in RR }` | `{ a ^ 2 provided a in RR and a >= 0 }` | +| `"(sqrt(x) = a) provided a > 0".ToEntity().Solve("x")` | `{ a ^ 2 provided a > 0 }` | `{ a ^ 2 provided a > 0 }` | +| `"(sqrt(x) = a) provided a < 0".ToEntity().Solve("x")` | `{ a ^ 2 provided a < 0 }` | `{ }` | +| `MathS.Equations("sqrt(y) - x", "y - a").Solve("x", "y")` | `[[sqrt(a), (sqrt(a)) ^ 2], [-sqrt(a), (-sqrt(a)) ^ 2]]` | `[[sqrt(a) provided C(sqrt(a)), (sqrt(a)) ^ 2 provided C(sqrt(a))], [-sqrt(a) provided C(-sqrt(a)), (-sqrt(a)) ^ 2 provided C(-sqrt(a))]]`, `C(v)` being `v = 0 or -1/2 * pi < arg(v) and arg(v) <= 1/2 * pi` | + ### A system with a product equation is solved branch by branch **Silent: solutions that were missing are answered.** `EquationSystem.Solve` eliminated `x * y = 0` for `y` as `0`, @@ -229,7 +252,7 @@ is `null`. A row with symbols in it is tried at one value of them, and where it | `MathS.Equations("sqrt(y) - x", "x + 1").Solve("x", "y")` | `[[-1, 1]]` | `null`: `sqrt(1) - (-1)` is 2 | | `MathS.Equations("sqrt(y) - x", "y - 4").Solve("x", "y")` | `[[2, 4], [-2, 4]]` | `[[2, 4]]` | | `MathS.Equations("ln(x) + ln(y)", "x - y").Solve("x", "y")` | `[[-1, -1], [1, 1]]` | `[[1, 1]]`: `ln(-1) + ln(-1)` is `2 pi i` | -| `MathS.Equations("sqrt(y) - x", "y - a").Solve("x", "y")` | `[[sqrt(a), (sqrt(a)) ^ 2], [-sqrt(a), (-sqrt(a)) ^ 2]]` | `null`: the second row is a solution only at `a = 0` | +| `MathS.Equations("sqrt(y) - x", "y - a").Solve("x", "y")` | `[[sqrt(a), (sqrt(a)) ^ 2], [-sqrt(a), (-sqrt(a)) ^ 2]]` | both rows, each with where it holds: see *A fractional power is inverted where its values lie* | ### The power reduction of a squared cosine is proved, as the squared sine's is diff --git a/Sources/.editorconfig b/Sources/.editorconfig index 2447db278..c7ede9965 100644 --- a/Sources/.editorconfig +++ b/Sources/.editorconfig @@ -240,6 +240,9 @@ file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed [Tests/UnitTests/Convenience/ConjugateAndArgumentTest.cs] file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n +[Tests/UnitTests/Algebra/SolveTest/FractionalPowerRangeTest.cs] +file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n + [Tests/UnitTests/Convenience/IversonTest.cs] file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n diff --git a/Sources/AngouriMath/Functions/Continuous/Solvers/EquationSolver/AnalyticalEquationSolver.cs b/Sources/AngouriMath/Functions/Continuous/Solvers/EquationSolver/AnalyticalEquationSolver.cs index acc8eeb5d..c285ba0d5 100644 --- a/Sources/AngouriMath/Functions/Continuous/Solvers/EquationSolver/AnalyticalEquationSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Solvers/EquationSolver/AnalyticalEquationSolver.cs @@ -199,8 +199,11 @@ when @base.ContainsNode(x) && !power.ContainsNode(x) // reached at all. https://github.com/asc-community/AngouriMath/issues/744 case Function when expr.Nodes.Count(node => node == x) == 1: return expr.Invert(0, x)?.Select(ent => TryDowncast(expr, x, ent)).ToSet(); + // Solved knowing the condition, as a provided expression is differentiated: the + // roots are filtered by it anyway, and what holds under it can be stated by it. + // https://github.com/asc-community/AngouriMath/issues/212 case Providedf(var expression, var predicate): - return Solve(expression, x, compensateSolving).Filter(predicate, x); + return TreeAnalyzer.Assuming(predicate, () => Solve(expression, x, compensateSolving)).Filter(predicate, x); case Piecewise p: return EquationSolver.SolvePiecewise(p, x, (e, x) => Solve(e, x, compensateSolving)); // A product is zero exactly where one of its factors is. Without this the diff --git a/Sources/AngouriMath/Functions/Continuous/Solvers/EquationSolver/InvertNode.Classes.cs b/Sources/AngouriMath/Functions/Continuous/Solvers/EquationSolver/InvertNode.Classes.cs index 4b30cf4db..276792180 100644 --- a/Sources/AngouriMath/Functions/Continuous/Solvers/EquationSolver/InvertNode.Classes.cs +++ b/Sources/AngouriMath/Functions/Continuous/Solvers/EquationSolver/InvertNode.Classes.cs @@ -117,13 +117,46 @@ partial record Powf return Together(Number.GetAllRootsOf1(pow.Abs()).Select(root => Base.Invert(root * MathS.Pow(value, 1 / Exponent), x))); } else - return Base.Invert(MathS.Pow(value, 1 / Exponent), x); + return Base.Invert(MathS.Pow(value, 1 / Exponent), x) + ?.Select(root => root.Provided(InTheRange(value))); } // a ^ x = value => x = log(a, value) // TODO: determine how we should return periodic roots as // (e ^ x) ^ a != (e ^ a) ^ x for logs return Exponent.Invert(MathS.Log(Base, value), x); } + + // The principal power z^p is e^(p ln z), so its argument is p arg(z): in (-p pi, p pi] + // for 0 < p < 1, and in [p pi, -p pi) for -1 < p < 0, where it is never 0. A value + // outside that sector is no power of anything, and v^(1/p) is then a number whose + // power is not v: sqrt(y) = x was answered { x ^ 2 }, and sqrt((-1) ^ 2) is 1. For + // |p| > 1 the sector is the whole plane, and v^(1/p) is a preimage of every v. A + // symbolic exponent has no sector to state. + // https://github.com/asc-community/AngouriMath/issues/212 + private Entity InTheRange(Entity value) + { + if (Exponent.Evaled is not Real { EDecimal.IsFinite: true } p + || p.EDecimal.Abs().CompareTo(PeterO.Numbers.EDecimal.One) >= 0) + return Boolean.True; + // Where the value is known to be real -- by its codomain, or by what the equation + // it came from was provided -- arg(value) is 0 or pi, so the sector is the half-line + // value >= 0 for p > 0 and value > 0 for p < 0, and nothing is stated where that is + // known as well: sqrt(x) = a provided a > 0 is a ^ 2 provided a > 0. + Entity halfLine = p.IsPositive ? new GreaterOrEqualf(value, Integer.Zero) : new Greaterf(value, Integer.Zero); + Entity outside = p.IsPositive ? new Lessf(value, Integer.Zero) : new LessOrEqualf(value, Integer.Zero); + if (TreeAnalyzer.IsKnown(halfLine)) + return Boolean.True; + if (TreeAnalyzer.IsKnown(outside)) + return Boolean.False; + if (Conjugatef.IsKnownReal(value) || TreeAnalyzer.IsKnownToBeReal(value)) + return halfLine; + var bound = (Exponent * MathS.pi).InnerSimplified; + var opposite = ((-Exponent).InnerSimplified * MathS.pi).InnerSimplified; + var angle = MathS.Arg(value); + return p.IsPositive + ? value.EqualTo(Integer.Zero) | (new Lessf(opposite, angle) & new LessOrEqualf(angle, bound)) + : !value.EqualTo(Integer.Zero) & new LessOrEqualf(bound, angle) & new Lessf(angle, opposite); + } } // TODO: Consider case when sin(sin(x)) where double-mention of n occures diff --git a/Sources/AngouriMath/Functions/Continuous/Solvers/EquationSolver/SolveStatement.cs b/Sources/AngouriMath/Functions/Continuous/Solvers/EquationSolver/SolveStatement.cs index ac48d0af9..f743c0caa 100644 --- a/Sources/AngouriMath/Functions/Continuous/Solvers/EquationSolver/SolveStatement.cs +++ b/Sources/AngouriMath/Functions/Continuous/Solvers/EquationSolver/SolveStatement.cs @@ -419,7 +419,7 @@ private static Entity Equivalent(Entity left, Entity right) // enough to list them. https://github.com/asc-community/AngouriMath/issues/1409 Congruentf(var left, var right, Integer modulus) when Congruence(left - right, modulus, x) is { } classes => classes, - Providedf(var e, var predicate) => Solve(e, x)?.Filter(predicate, x), + Providedf(var e, var predicate) => TreeAnalyzer.Assuming(predicate, () => Solve(e, x))?.Filter(predicate, x), Piecewise p => EquationSolver.SolvePiecewiseOrNull(p, x, Solve), // A statement the solver has no arm for is the set of x with the property, left diff --git a/Sources/AngouriMath/Functions/Evaluation/Evaluation.Continuous/Evaluation.Continuous.Arithmetics.Classes.cs b/Sources/AngouriMath/Functions/Evaluation/Evaluation.Continuous/Evaluation.Continuous.Arithmetics.Classes.cs index 2c287891d..11784cdb5 100644 --- a/Sources/AngouriMath/Functions/Evaluation/Evaluation.Continuous/Evaluation.Continuous.Arithmetics.Classes.cs +++ b/Sources/AngouriMath/Functions/Evaluation/Evaluation.Continuous/Evaluation.Continuous.Arithmetics.Classes.cs @@ -584,7 +584,7 @@ public partial record Conjugatef // Real where the node's own codomain says so: a modulus, or a symbol declared with // domain(x, RR). A bare Variable is Domain.Any, so conjugate(x) stays. Not the codomain // setting, since what this returns is cached on the node and the setting is not. - private static bool IsKnownReal(Entity entity) + internal static bool IsKnownReal(Entity entity) => entity.Codomain is Domain.Prime or Domain.PositiveInteger or Domain.NonNegativeInteger or Domain.Integer or Domain.Rational or Domain.Real; diff --git a/Sources/AngouriMath/Functions/Evaluation/Evaluation.Omni/Evaluation.Omni.Classes.cs b/Sources/AngouriMath/Functions/Evaluation/Evaluation.Omni/Evaluation.Omni.Classes.cs index 628fdcced..329ef5a66 100644 --- a/Sources/AngouriMath/Functions/Evaluation/Evaluation.Omni/Evaluation.Omni.Classes.cs +++ b/Sources/AngouriMath/Functions/Evaluation/Evaluation.Omni/Evaluation.Omni.Classes.cs @@ -455,12 +455,38 @@ protected override Entity InnerSimplify(bool isExact) => (a, b) => (a, b) switch { (Providedf exprProvided, Providedf predProvided) => - Decide(exprProvided.Expression, exprProvided.Predicate & predProvided.Predicate & predProvided.Expression), - (var expr, Providedf predProvided) => Decide(expr, predProvided.Predicate & predProvided.Expression), - (Providedf exprProvided, var pred) => Decide(exprProvided.Expression, pred & exprProvided.Predicate), + Decide(exprProvided.Expression, Both(Both(exprProvided.Predicate, predProvided.Predicate), predProvided.Expression)), + (var expr, Providedf predProvided) => Decide(expr, Both(predProvided.Predicate, predProvided.Expression)), + (Providedf exprProvided, var pred) => Decide(exprProvided.Expression, Both(pred, exprProvided.Predicate)), (var expr, var pred) => Decide(expr, pred), }, (@this, a, b) => ((Providedf)@this).New(a, b), isExact); + + // Two conditions joined, with a conjunct already stated not stated again. A row of a + // system substituted into the expressions that carry its condition had it conjoined + // once a substitution, five times over for sqrt(y) - x, y - a. The conjunction is + // rebuilt only where something repeats, so every other condition keeps its shape. + // https://github.com/asc-community/AngouriMath/issues/212 + private static Entity Both(Entity first, Entity second) + { + var conjuncts = new List(); + var repeated = false; + void Gather(Entity condition) + { + if (condition is Andf(var left, var right)) + { + Gather(left); + Gather(right); + } + else if (conjuncts.Contains(condition)) + repeated = true; + else + conjuncts.Add(condition); + } + Gather(first); + Gather(second); + return repeated ? conjuncts.Aggregate((joined, next) => joined & next) : first & second; + } } partial record Piecewise diff --git a/Sources/AngouriMath/Functions/TreeAnalyzer/RealValued.cs b/Sources/AngouriMath/Functions/TreeAnalyzer/RealValued.cs index 76c7fd401..bc77dc0fb 100644 --- a/Sources/AngouriMath/Functions/TreeAnalyzer/RealValued.cs +++ b/Sources/AngouriMath/Functions/TreeAnalyzer/RealValued.cs @@ -172,6 +172,32 @@ or Arctanf or Arccotanf or Signumf or Erff or Erfcf or Erfif or Eif or Sif or Sh /// [ThreadStatic] private static Entity? assumed; + /// + /// Whether what says is known forces . + /// + internal static bool IsKnown(Entity predicate) + => assumed is { } known && PredicateEntailment.Entails(known, predicate); + + /// + /// Whether what says is known makes real: it is + /// in RR, or it is ordered against something, which an order comparison has a truth + /// value only for. + /// + internal static bool IsKnownToBeReal(Entity expr) + { + if (assumed is not { } known) + return false; + if (PredicateEntailment.Entails(known, expr.In(AngouriMath.Core.Domain.Real))) + return true; + static bool Orders(Entity condition, Entity expr) => condition switch + { + Andf(var left, var right) => Orders(left, expr) || Orders(right, expr), + ComparisonSign sign => sign.DirectChildren[0] == expr || sign.DirectChildren[1] == expr, + _ => false + }; + return Orders(known, expr); + } + /// /// Runs with known to hold, as well /// as whatever was known already. diff --git a/Sources/Tests/UnitTests/Algebra/SolveTest/FractionalPowerRangeTest.cs b/Sources/Tests/UnitTests/Algebra/SolveTest/FractionalPowerRangeTest.cs new file mode 100644 index 000000000..49ad456fa --- /dev/null +++ b/Sources/Tests/UnitTests/Algebra/SolveTest/FractionalPowerRangeTest.cs @@ -0,0 +1,99 @@ +// +// 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 AngouriMath; +using AngouriMath.Extensions; +using System.Linq; +using Xunit; +using static AngouriMath.Entity; + +namespace AngouriMath.Tests.Algebra +{ + /// + /// The principal power z^p has argument p arg(z), so for |p| < 1 its + /// values lie in a sector, and an equation with its other side outside the sector has no + /// solution. sqrt(x) = a was answered { a ^ 2 }, which is wrong at + /// a = -1: sqrt((-1)^2) is 1. + /// #212 + /// + [Trait("Area", "Algebra")] + public sealed class FractionalPowerRangeTest + { + private static Entity OnlyRoot(string equation) + { + var roots = Assert.IsType(equation.ToEntity().Solve("x")); + return Assert.Single(roots); + } + + /// + /// The root holds exactly where its condition does, at a value inside the sector, one on + /// its included edge, and one outside. + /// + [Theory] + [InlineData("sqrt(x) = a", "2", true)] + [InlineData("sqrt(x) = a", "0", true)] + [InlineData("sqrt(x) = a", "i", true)] + [InlineData("sqrt(x) = a", "-i", false)] + [InlineData("sqrt(x) = a", "-1", false)] + [InlineData("x^(1/3) = a", "1 + i", true)] + [InlineData("x^(1/3) = a", "-1", false)] + [InlineData("x^(-1/2) = a", "-i", true)] + [InlineData("x^(-1/2) = a", "i", false)] + [InlineData("x^(-1/2) = a", "0", false)] + public void TheRootHoldsWhereItsConditionDoes(string equation, string value, bool holds) + { + var root = Assert.IsType(OnlyRoot(equation)); + var at = value.ToEntity(); + Assert.Equal(Boolean.Create(holds), root.Predicate.Substitute("a", at).EvalBoolean()); + if (!holds) + return; + var lhs = equation.Split('=')[0].ToEntity(); + var residual = (lhs.Substitute("x", root.Expression.Substitute("a", at)) - at).EvalNumerical(); + Assert.True(Number.Abs(residual).EDecimal.CompareTo(PeterO.Numbers.EDecimal.FromString("1e-30")) < 0, + $"{equation} at a = {value}: the residual is {residual}"); + } + + /// A number on the other side is decided, as it was. + [Theory] + [InlineData("sqrt(x) = 2", "{ 4 }")] + [InlineData("sqrt(x) = -1", "{ }")] + [InlineData("sqrt(x) = i", "{ -1 }")] + [InlineData("sqrt(x) = -i", "{ }")] + public void ANumberIsDecided(string equation, string expected) + => Assert.Equal(expected.ToEntity(), equation.ToEntity().Solve("x")); + + /// + /// What the equation is provided is known while it is solved. A real value has argument 0 + /// or pi, so the sector is the half-line; a value known to lie on it needs nothing, + /// and one known to lie off it has no root. + /// + [Theory] + [InlineData("(sqrt(x) = a) provided a in RR", "{ a ^ 2 provided a in RR and a >= 0 }")] + [InlineData("(sqrt(x) = a) provided a > 0", "{ a ^ 2 provided a > 0 }")] + [InlineData("(sqrt(x) = a) provided a >= 1", "{ a ^ 2 provided a >= 1 }")] + [InlineData("(sqrt(x) = a) provided a > -5", "{ a ^ 2 provided a > -5 and a >= 0 }")] + [InlineData("(x^(-1/2) = a) provided a in RR", "{ a ^ (-2) provided a in RR and a > 0 }")] + [InlineData("(sqrt(x) = a) provided a < 0", "{ }")] + [InlineData("(x^(-1/2) = a) provided a <= 0", "{ }")] + public void WhatTheEquationIsProvidedIsKnown(string equation, string expected) + => Assert.Equal(expected.ToEntity(), equation.ToEntity().Solve("x")); + + /// A symbol declared real is the same case: domain(a, RR). + [Fact] + public void ASymbolDeclaredRealHasTheHalfLine() + => Assert.Equal("{ domain(a, RR) ^ 2 provided domain(a, RR) >= 0 }".ToEntity(), + "sqrt(x) = domain(a, RR)".ToEntity().Solve("x")); + + /// + /// For |p| > 1 the sector is the whole plane, and v^(1/p) is a preimage + /// of every v, so there is nothing to state. + /// + [Fact] + public void APowerAboveOneNeedsNoCondition() + => Assert.IsNotType(OnlyRoot("x^(3/2) = a")); + } +} diff --git a/Sources/Tests/UnitTests/Algebra/SolveTest/SpuriousSystemRowTest.cs b/Sources/Tests/UnitTests/Algebra/SolveTest/SpuriousSystemRowTest.cs index c9805da31..023fcf342 100644 --- a/Sources/Tests/UnitTests/Algebra/SolveTest/SpuriousSystemRowTest.cs +++ b/Sources/Tests/UnitTests/Algebra/SolveTest/SpuriousSystemRowTest.cs @@ -52,13 +52,40 @@ public void ASystemWhoseOnlyRowIsNoSolutionHasNone() /// /// [-sqrt(a), a] fails at a = 137/100 and holds at a = 0, so it - /// cannot be dropped as no solution, and the system is declined instead of being - /// answered by the other row alone. A row like it that holds for some values only, as - /// sqrt(t^2) = t does for positive t, would be lost by dropping it. + /// cannot be dropped as no solution. It was declined, until the inversion of + /// sqrt(y) = x came to carry where it holds, x = 0 or + /// -pi/2 < arg(x) <= pi/2: now each row says where it is one. So at every value + /// of a tried, each row whose condition holds solves both equations, and one row's + /// condition holds. + /// #212 /// - [Fact] - public void ARowThatFailsForSomeValueDeclinesTheSystem() - => Assert.Null(Solve("sqrt(y) - x", "y - a")); + [Theory] + [InlineData("137/100")] + [InlineData("-2")] + [InlineData("3i")] + [InlineData("-1 - i")] + public void ARowThatHoldsForSomeValuesSaysWhere(string value) + { + var solutions = Solve("sqrt(y) - x", "y - a"); + Assert.NotNull(solutions); + var applying = 0; + for (var row = 0; row < solutions!.RowCount; row++) + { + var x = solutions[row, 0].Substitute("a", value.ToEntity()); + var y = solutions[row, 1].Substitute("a", value.ToEntity()); + if (x is not Entity.Providedf(var xValue, var condition)) + (xValue, condition) = (x, Entity.Boolean.True); + if (condition.EvalBoolean() is not Entity.Boolean(true)) + continue; + applying++; + while (y is Entity.Providedf(var inner, _)) + y = inner; + var residual = "sqrt(y) - x".ToEntity().Substitute("y", y).Substitute("x", xValue).EvalNumerical(); + Assert.True(Entity.Number.Abs(residual).EDecimal.CompareTo(PeterO.Numbers.EDecimal.FromString("1e-30")) < 0, + $"row {row} at a = {value}: sqrt(y) - x is {residual}"); + } + Assert.True(applying > 0, $"no row applies at a = {value}"); + } /// /// What was answered rightly still is: rows in a symbol of the system's own, and rows in