diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index 83a62c6da..aad6eccdf 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -42,6 +42,26 @@ A not-equal written `<>` is read as `not (a = b)`, and printed that way.
## Unreleased — since 2.6.0
+### A system with more or fewer equations than unknowns is solved or declined
+
+**Not silent: what was an exception is an answer or `null`.** `EquationSystem.Solve` threw
+`WrongNumberOfArgumentsException` for a system with more or fewer equations than unknowns, unless it was polynomial
+over the rationals with rational solutions or had fewer equations and was linear with rational coefficients. Such a
+system is now eliminated one unknown at a time, and an unknown that nothing is left to constrain becomes a parameter,
+`t_1`, as in the linear case. Every row is substituted back into every equation and kept only where each simplifies to
+zero. Where one does not, or where a branch cannot be decided, the system is declined as `null`, which `Solve`
+documents as no solution found ([#212](https://github.com/asc-community/AngouriMath/issues/212)). Code that caught the
+exception around `Solve` to detect a non-square system no longer sees it.
+
+| Input | Was (2.6.0) | Now |
+|---|---|---|
+| `MathS.Equations("x^2 + y^2 - 1").Solve("x", "y")` | `WrongNumberOfArgumentsException` | `[[t_1, sqrt(-(t_1 ^ 2 + -1))], [t_1, -sqrt(-(t_1 ^ 2 + -1))]]` |
+| `MathS.Equations("a*x + y - 1").Solve("x", "y")` | `WrongNumberOfArgumentsException` | `[[t_1, -(a * t_1 + -1)]]` |
+| `MathS.Equations("sin(x)", "x").Solve("x")` | `WrongNumberOfArgumentsException` | `[0]` |
+| `MathS.Equations("sin(x) - y", "cos(x) - y", "x - pi/4").Solve("x", "y")` | `WrongNumberOfArgumentsException` | `[[pi / 4, 1/2 * sqrt(2)]]` |
+| `MathS.Equations("sqrt(y) - x").Solve("x", "y")` | `WrongNumberOfArgumentsException` | `null`: `y = x^2` is no solution where `x` is negative |
+| `MathS.Equations("x + y - a", "x - y - b", "x - c").Solve("x", "y")` | `WrongNumberOfArgumentsException` | `null`: there are solutions only where `a + b = 2c` |
+
### `am_classify` is a preliminary tool, served by `amcli mcp --preliminary`
**Not silent: what answered is a failure naming the flag.** `am_classify` guesses which field a formula comes from by
diff --git a/Sources/.editorconfig b/Sources/.editorconfig
index 45b05543c..b1324ec83 100644
--- a/Sources/.editorconfig
+++ b/Sources/.editorconfig
@@ -252,9 +252,15 @@ file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed
[AngouriMath/Functions/Continuous/Solvers/EquationSolver/LinearSystemSolver.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
+[AngouriMath/Functions/Continuous/Solvers/EquationSolver/NonSquareSystemSolver.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
+
[AngouriMath/Functions/Continuous/Solvers/EquationSolver/ModulusSolver.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/NonSquareSystemTest.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/ParametricSystemTest.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/Core/EquationSystem.cs b/Sources/AngouriMath/Core/EquationSystem.cs
index 65175390e..1dfe06237 100644
--- a/Sources/AngouriMath/Core/EquationSystem.cs
+++ b/Sources/AngouriMath/Core/EquationSystem.cs
@@ -67,7 +67,10 @@ private static Entity AsAnExpressionEqualToZero(Entity equation)
/// Solves a system of equations, if possible.
///
///
- /// The umber of variables must match number of equations
+ /// The unknowns, as many as there are equations or not. Where there are more, or where a
+ /// system leaves one free, it becomes a parameter in the answer: x^2 + y^2 = 1 for
+ /// x and y is two rows, x = t_1 with y the positive and the
+ /// negative square root of 1 - t_1^2.
///
///
/// If there exists at least one solution, returns a solution matrix
diff --git a/Sources/AngouriMath/Docs/Usage/Exceptions.md b/Sources/AngouriMath/Docs/Usage/Exceptions.md
index fb1045c7b..d493560d4 100644
--- a/Sources/AngouriMath/Docs/Usage/Exceptions.md
+++ b/Sources/AngouriMath/Docs/Usage/Exceptions.md
@@ -216,9 +216,9 @@ largest base there are digits for, and the base that was asked for.
### `WrongNumberOfArgumentsException`
An API call whose arity does not match: a compiled function called with the wrong number of values,
-a truth table asked for over a different number of variables than the expression has, a system of
-equations with more or fewer equations than unknowns. Where the expression is in scope the message
-names it and its variables.
+or a truth table asked for over a different number of variables than the expression has. Where the
+expression is in scope the message names it and its variables. A system of equations with more or
+fewer equations than unknowns is not one: it is solved, or declined as `null`.
Compare `FunctionArgumentCountException`, which is the same complaint about a function call written
inside a string. **Use `FunctionArgumentCountException` from the parser and this one from
diff --git a/Sources/AngouriMath/Functions/Continuous/Solvers/EquationSolver.cs b/Sources/AngouriMath/Functions/Continuous/Solvers/EquationSolver.cs
index de4091918..902ebc064 100644
--- a/Sources/AngouriMath/Functions/Continuous/Solvers/EquationSolver.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Solvers/EquationSolver.cs
@@ -127,8 +127,8 @@ internal static Set Solve(Entity equation, Variable x)
if (Groebner.GroebnerSystemSolver.TrySolve(equations, variables, out var triangularised))
return triangularised;
- // Fewer equations than unknowns is not a mistake by the caller, which is what
- // the exception below says it is. A linear system of that shape has infinitely
+ // Fewer equations than unknowns is not a mistake by the caller, which is what an
+ // exception here used to say it was. A linear system of that shape has infinitely
// many solutions and one family that describes them all, so it is answered as
// that family -- the unknowns a row reduction leaves free become parameters.
// Only the short count is taken here: a square system that is rank-deficient
@@ -139,8 +139,14 @@ internal static Set Solve(Entity equation, Variable x)
equations, variables, out var family))
return family;
+ // Nor is any other count: `x^2 + y^2 = 1` has a family of solutions in a
+ // parameter, and `sin(x) = 0` with `x = 0` has one solution. Those are eliminated,
+ // and a system whose answer cannot be shown to hold is declined, as `Solve`
+ // documents. https://github.com/asc-community/AngouriMath/issues/212
if (equations.Count != vars.Length)
- throw new WrongNumberOfArgumentsException("Number of equations must be equal to that of vars");
+ return NonSquareSystemSolver.Solve(equations, variables, ledger) is { } families
+ ? new MatrixBuilder(families, vars.Length).ToMatrix()
+ : null;
int initVarCount = vars.Length;
var res = InSolveSystem(equations, vars, Sumf.Sum(equations), ledger);
diff --git a/Sources/AngouriMath/Functions/Continuous/Solvers/EquationSolver/NonSquareSystemSolver.cs b/Sources/AngouriMath/Functions/Continuous/Solvers/EquationSolver/NonSquareSystemSolver.cs
new file mode 100644
index 000000000..4f8b648e6
--- /dev/null
+++ b/Sources/AngouriMath/Functions/Continuous/Solvers/EquationSolver/NonSquareSystemSolver.cs
@@ -0,0 +1,231 @@
+//
+// 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.Core.Budgets;
+using AngouriMath.Core.Exceptions;
+using System;
+using static AngouriMath.Entity;
+using static AngouriMath.Entity.Number;
+using static AngouriMath.Entity.Set;
+
+namespace AngouriMath.Functions.Algebra.AnalyticalSolving
+{
+ ///
+ /// A system with more or fewer equations than unknowns that neither the triangularisation nor
+ /// takes, such as x^2 + y^2 = 1 for x and
+ /// y, or sin(x) = 0 and x = 0 for x. Each unknown is eliminated in
+ /// turn, an unknown that nothing is left to constrain becomes a parameter, and every family
+ /// found is substituted back into every equation.
+ /// #212
+ ///
+ ///
+ ///
+ /// An answer is given only where it is shown to hold. The elimination trusts each solve
+ /// it makes, and a solve can answer for a case its equation does not cover: sqrt(y) = x
+ /// is solved for y as x^2, which is no solution where x is negative. So
+ /// each family is kept only where every equation, with the family substituted, simplifies to
+ /// 0. sqrt(t^2) - t does not, and that system is declined.
+ ///
+ ///
+ /// A decline declines the whole system. An equation left with no unknown in it is zero,
+ /// a number away from zero, or neither as far as can be told, like (a + b)/2 - c. A
+ /// branch where one is away from zero has no solutions and is dropped. A branch where one
+ /// cannot be told gives no answer for the system, rather than leaving the other branches to
+ /// read as all of its solutions.
+ ///
+ ///
+ /// A square system does not come here. It is the eliminator's in
+ /// , whose answers this would change.
+ ///
+ ///
+ internal static class NonSquareSystemSolver
+ {
+ ///
+ /// The solutions of in , a list of
+ /// values in their order for each family, or where none were found.
+ ///
+ internal static List>? Solve(List equations, Variable[] variables, BudgetLedger ledger)
+ {
+ // One parameter per unknown that is left free, the same in every branch. Each is
+ // minted against the equations and the parameters already minted, since CreateUnique
+ // compares whole names in use and would otherwise hand back the same name twice.
+ var parameters = new Variable?[variables.Length];
+ Entity nameSource = equations.Count > 0 ? Sumf.Sum(equations) : Integer.Zero;
+ Variable ParameterFor(int index)
+ {
+ if (parameters[index] is { } minted)
+ return minted;
+ var parameter = Variable.CreateUnique(nameSource, "t");
+ nameSource += parameter;
+ return parameters[index] = parameter;
+ }
+
+ if (Eliminate(equations, variables, variables.Length, ParameterFor, ledger) is not { Count: > 0 } families)
+ return null;
+ foreach (var family in families)
+ foreach (var equation in equations)
+ {
+ var substituted = equation;
+ for (var index = 0; index < variables.Length; index++)
+ substituted = substituted.Substitute(variables[index], family[index]);
+ if (!Vanishes(substituted))
+ return null;
+ }
+ return families;
+ }
+
+ ///
+ /// The solutions in the first of , each
+ /// a list of their values in order. Empty where there are none, and
+ /// where that could not be told.
+ ///
+ private static List>? Eliminate(List equations, Variable[] variables, int count,
+ Func parameterFor, BudgetLedger ledger)
+ {
+ // Charged as InSolveSystem charges it: one unit for each candidate explored.
+ if (!ledger.Spend())
+ throw new NotSufficientlySupportedException(
+ "this system of equations is not solvable within the budget. Raise "
+ + "MathS.Settings.Budget to allow more, or pass a cancellation token with "
+ + "MathS.Multithreading.SetLocalCancellationToken");
+
+ // No unknown is left, so each equation holds as it stands or does not.
+ if (count == 0)
+ {
+ var told = true;
+ foreach (var equation in equations)
+ switch (Holds(equation))
+ {
+ case false:
+ return new();
+ case null:
+ told = false;
+ break;
+ }
+ return told ? new() { new() } : null;
+ }
+
+ // No equation is left, so every unknown left is free.
+ if (equations.Count == 0)
+ {
+ var free = new List();
+ for (var index = 0; index < count; index++)
+ free.Add(parameterFor(index));
+ return new() { free };
+ }
+
+ var variable = variables[count - 1];
+ // The equation with the fewest occurrences of the unknown first, which is solved by
+ // inverting it and has the plainest roots: of `cos(x) = sin(x)` and `x = pi/4`, the
+ // first is solved as ln(sqrt(i))/i and the second as pi/4.
+ var candidates = Enumerable.Range(0, equations.Count)
+ .Where(i => equations[i].ContainsNode(variable))
+ .OrderBy(i => equations[i].Nodes.Count(node => node == variable))
+ .ThenBy(i => equations[i].Complexity)
+ .ToList();
+ foreach (var i in candidates)
+ {
+ // An empty set is passed over as InSolveSystem passes it over: substituting an
+ // earlier unknown can leave an occurrence that cancels, and then the solve has
+ // nothing to say about this one.
+ if (equations[i].SolveEquation(variable).InnerSimplified is not FiniteSet roots || roots.Count == 0)
+ continue;
+ var rest = new List(equations);
+ rest.RemoveAt(i);
+ if (EachRoot(roots, rest, variables, count, parameterFor, ledger) is { } solutions)
+ return solutions;
+ // This equation's roots left a question that could not be told. Another
+ // equation's may not: `sin(x) = 0` gives x = 2 pi n_1, where `x = 0` gives 0.
+ }
+
+ if (candidates.Count > 0)
+ return null;
+ // Nothing constrains this unknown, so it is a parameter in every solution of the rest.
+ var others = Eliminate(equations, variables, count - 1, parameterFor, ledger);
+ others?.ForEach(family => family.Add(parameterFor(count - 1)));
+ return others;
+ }
+
+ ///
+ /// The solutions where the last of the unknowns is each of in
+ /// turn, or where that could not be told for one of them.
+ ///
+ private static List>? EachRoot(FiniteSet roots, List rest, Variable[] variables, int count,
+ Func parameterFor, BudgetLedger ledger)
+ {
+ var variable = variables[count - 1];
+ var solutions = new List>();
+ foreach (var root in roots)
+ {
+ var substituted = rest.Select(equation => equation.Substitute(variable, root)).ToList();
+ if (Eliminate(substituted, variables, count - 1, parameterFor, ledger) is not { } families)
+ return null;
+ foreach (var family in families)
+ {
+ var value = root;
+ for (var index = 0; index < count - 1; index++)
+ value = value.Substitute(variables[index], family[index]);
+ family.Add(value.InnerSimplified);
+ solutions.Add(family);
+ }
+ }
+ return solutions;
+ }
+
+ ///
+ /// Whether , with no unknown left in it, holds:
+ /// where it simplifies to zero, where it is a
+ /// number away from zero, and where neither can be told.
+ ///
+ private static bool? Holds(Entity equation)
+ {
+ if (Vanishes(equation))
+ return true;
+ if (equation.Vars.Any() || equation.Evaled is not Complex value || value.IsNaN)
+ return null;
+ // An exact number that is not zero is not zero. A decimal is taken as away from zero
+ // only well past the digits its evaluation may have lost.
+ if (value is Rational)
+ return false;
+ return ((Real)value.Abs()).EDecimal.ToDouble() > 1e-9 ? false : null;
+ }
+
+ ///
+ /// Whether is zero for every value of its symbols: zero as it
+ /// is evaluated, or simplified to zero. A simplification to 0 provided C counts only
+ /// where C holds with the symbols pinned, since t - c (t/c) simplifies to
+ /// 0 provided not c = 0 for a c that is zero without being written
+ /// 0, such as (a + 1)^2 - a^2 - 2a - 1.
+ ///
+ private static bool Vanishes(Entity expression)
+ {
+ if (expression.InnerSimplified.Evaled is Complex { IsZero: true })
+ return true;
+ // Away from zero at a point is not zero everywhere, which settles most without
+ // simplifying.
+ if (Pinned(expression).Evaled is Complex { IsNaN: false } atAPoint
+ && ((Real)atAPoint.Abs()).EDecimal.ToDouble() > 1e-9)
+ return false;
+ return expression.Simplify() switch
+ {
+ Providedf(var simplified, var condition) =>
+ simplified.Evaled is Complex { IsZero: true } && Pinned(condition).Evaled is Entity.Boolean { Value: true },
+ var simplified => simplified.Evaled is Complex { IsZero: true },
+ };
+ }
+
+ /// with each of its symbols at a fixed value.
+ private static Entity Pinned(Entity expression)
+ {
+ var values = new[] { 1.37, 0.61, 2.23, 1.91, 0.83, 1.13 };
+ var index = 0;
+ foreach (var symbol in expression.Vars.ToList())
+ expression = expression.Substitute(symbol, values[index++ % values.Length]);
+ return expression;
+ }
+ }
+}
diff --git a/Sources/Tests/UnitTests/Algebra/SolveTest/NonSquareSystemTest.cs b/Sources/Tests/UnitTests/Algebra/SolveTest/NonSquareSystemTest.cs
new file mode 100644
index 000000000..311a57b01
--- /dev/null
+++ b/Sources/Tests/UnitTests/Algebra/SolveTest/NonSquareSystemTest.cs
@@ -0,0 +1,109 @@
+//
+// 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;
+
+namespace AngouriMath.Tests.Algebra
+{
+ ///
+ /// Systems with more or fewer equations than unknowns that are not linear over the
+ /// rationals, which answers. Each of these raised
+ /// WrongNumberOfArgumentsException, and is now answered or declined as
+ /// .
+ /// #212
+ ///
+ [Trait("Area", "Algebra")]
+ public sealed class NonSquareSystemTest
+ {
+ private static Entity.Matrix? Solve(string[] equations, string[] variables)
+ => MathS.Equations(equations.Select(e => e.ToEntity()).ToArray())
+ .Solve(variables.Select(v => (Entity.Variable)v).ToArray());
+
+ ///
+ /// Each row, substituted into each equation, is zero at several values of the parameters
+ /// and symbols left in it, negative ones among them. That is checked numerically rather
+ /// than by simplifying, which is how the solver checks it: sqrt(t^2) - t is zero at
+ /// every positive t and at no negative one.
+ ///
+ private static void HoldsAtSampledValues(string[] equations, string[] variables, Entity.Matrix solutions)
+ {
+ var samples = new[] { 1.37, -1.7, 2.3, -0.61 };
+ for (var row = 0; row < solutions.RowCount; row++)
+ foreach (var text in equations)
+ {
+ Entity residual = text.ToEntity();
+ for (var column = 0; column < variables.Length; column++)
+ residual = residual.Substitute(variables[column], solutions[row, column]);
+ var symbols = residual.Vars.ToList();
+ for (var shift = 0; shift < samples.Length; shift++)
+ {
+ var pinned = residual;
+ for (var i = 0; i < symbols.Count; i++)
+ pinned = pinned.Substitute(symbols[i], samples[(shift + i) % samples.Length]);
+ var value = Assert.IsAssignableFrom(pinned.Evaled);
+ Assert.False(value.IsNaN, $"{residual} at {pinned}");
+ Assert.True(((Entity.Number.Real)value.Abs()).EDecimal.ToDouble() < 1e-9, $"{residual} at {pinned} is {value}");
+ }
+ }
+ }
+
+ ///
+ /// Fewer equations than unknowns, not linear in them or with a symbol for a coefficient:
+ /// an unknown that is left free is a parameter, and a root with two values is two rows.
+ ///
+ [Theory]
+ [InlineData(new[] { "x^2 + y^2 - 1" }, new[] { "x", "y" }, 2)]
+ [InlineData(new[] { "x*y - 1" }, new[] { "x", "y" }, 1)]
+ [InlineData(new[] { "a*x + y - 1" }, new[] { "x", "y" }, 1)]
+ [InlineData(new[] { "sin(x) + y" }, new[] { "x", "y" }, 1)]
+ [InlineData(new[] { "e^x - y", "y - z" }, new[] { "x", "y", "z" }, 1)]
+ [InlineData(new[] { "x^2 + y - 1" }, new[] { "x", "y", "z" }, 1)]
+ public void AShortSystemIsAFamily(string[] equations, string[] variables, int rows)
+ {
+ var solutions = Solve(equations, variables);
+ Assert.NotNull(solutions);
+ Assert.Equal(rows, solutions!.RowCount);
+ Assert.Equal(variables.Length, solutions.ColumnCount);
+ HoldsAtSampledValues(equations, variables, solutions);
+ }
+
+ ///
+ /// More equations than unknowns, which the triangularisation does not take because they
+ /// are not polynomial. The last drops x = -1, where sin(-pi/2) - 1 is -2.
+ ///
+ [Theory]
+ [InlineData(new[] { "sin(x)", "x" }, new[] { "x" }, new[] { "0" })]
+ [InlineData(new[] { "e^x - 1", "x + y", "y" }, new[] { "x", "y" }, new[] { "0", "0" })]
+ [InlineData(new[] { "sin(x) - y", "cos(x) - y", "x - pi/4" }, new[] { "x", "y" }, new[] { "pi/4", "sqrt(2)/2" })]
+ [InlineData(new[] { "x^2 - 1", "sin(pi*x/2) - 1" }, new[] { "x" }, new[] { "1" })]
+ public void AnOverdeterminedSystemIsSolved(string[] equations, string[] variables, string[] solution)
+ {
+ var solutions = Solve(equations, variables);
+ Assert.NotNull(solutions);
+ Assert.Equal(1, solutions!.RowCount);
+ for (var column = 0; column < variables.Length; column++)
+ Assert.Equal(Entity.Number.Integer.Create(0), (solutions[0, column] - solution[column]).Simplify());
+ }
+
+ ///
+ /// What the elimination finds and cannot show to hold. sqrt(y) = x is solved for
+ /// y as x^2, no solution where x is negative. The coefficient of
+ /// y in the second is zero without being written so, and dividing by it gives a
+ /// family that Simplify calls zero provided it is not zero. The third has solutions
+ /// only where a + b = 2c.
+ ///
+ [Theory]
+ [InlineData(new[] { "sqrt(y) - x" }, new[] { "x", "y" })]
+ [InlineData(new[] { "x^2 - ((a + 1)^2 - a^2 - 2*a - 1)*y" }, new[] { "x", "y" })]
+ [InlineData(new[] { "x + y - a", "x - y - b", "x - c" }, new[] { "x", "y" })]
+ public void WhatCannotBeShownToHoldIsDeclined(string[] equations, string[] variables)
+ => Assert.Null(Solve(equations, variables));
+ }
+}
diff --git a/Sources/Tests/UnitTests/Algebra/SolveTest/ParametricSystemTest.cs b/Sources/Tests/UnitTests/Algebra/SolveTest/ParametricSystemTest.cs
index 7c0281561..0de312f73 100644
--- a/Sources/Tests/UnitTests/Algebra/SolveTest/ParametricSystemTest.cs
+++ b/Sources/Tests/UnitTests/Algebra/SolveTest/ParametricSystemTest.cs
@@ -6,7 +6,6 @@
//
using AngouriMath;
-using AngouriMath.Core.Exceptions;
using AngouriMath.Extensions;
using System.Linq;
using Xunit;
@@ -124,22 +123,6 @@ public void AContradictoryShortSystemHasNoSolutions()
=> Assert.Null(MathS.Equations(new Entity[] { "x + y - 1", "2*x + 2*y - 3" })
.Solve("x", "y", "z"));
- ///
- /// What is not taken here, and still reaches the old refusal. Linearity is checked rather
- /// than assumed, so a product or a power of the unknowns fails the check; and a
- /// coefficient that is not rational is refused because a row reduction has to decide
- /// whether a pivot is zero, which is not decidable for a symbol.
- ///
- [Theory]
- [InlineData(new[] { "x^2 + y^2 - 1" }, new[] { "x", "y" })]
- [InlineData(new[] { "x*y - 1" }, new[] { "x", "y" })]
- [InlineData(new[] { "a*x + y - 1" }, new[] { "x", "y" })]
- [InlineData(new[] { "sin(x) + y" }, new[] { "x", "y" })]
- public void WhatIsNotLinearInTheUnknownsIsRefused(string[] equations, string[] variables)
- => Assert.Throws(
- () => MathS.Equations(equations.Select(e => e.ToEntity()).ToArray())
- .Solve(variables.Select(v => (Entity.Variable)v).ToArray()));
-
///
/// The neighbours must answer exactly as they did: a determined system, an
/// overdetermined but consistent one, and a contradictory one. Only the short count