Equations and solutions
Equation holds an equation or an inequality, and Solution is the exact value of a root. The first one produces the second.
import {Equation, Polynom, Solution} from 'pimath'Equation
An Equation is left <sign> right, where both members are Polynom objects and the sign is =, <, >, <= or >=.
Build an equation
new Equation('2x+3=7').display // 2x+3=7
new Equation('x^2-5x+6=0').tex // x^{ 2 }-5x+6=0
new Equation('x+1>=3').tex // x+1\geq3
new Equation(new Polynom('x^2-4')).display // x^(2)-4=0
new Equation('2x+3', '7').display // 2x+3=7
new Equation('2x+3', '7', '<').display // 2x+3<7A string needs a sign, otherwise it throws a ParseError. A Polynom given alone is read as polynom = 0. Signs can also be written \leq, geq, =>...
const E = new Equation('2x+3=7')
E.left.display // 2x+3
E.right.display // 7
E.sign // =
E.variables // [ 'x' ]
E.degree().display // 1left and right give the polynoms themselves, split() gives copies.
Solve
solve() returns the roots of left - right = 0 as Solution objects, sorted by value. It ignores the sign: an inequality has the roots of its equality.
new Equation('2x+3=7').solve().map(s => s.display) // [ '2' ]
new Equation('x^2-5x+6=0').solve().map(s => s.display) // [ '2', '3' ]
new Equation('x^2-2=0').solve().map(s => s.display) // [ '-sqrt(2)', 'sqrt(2)' ]
new Equation('x^2+1=0').solve().length // 0Roots are exact when they are rational or quadratic radicals, and radicals are simplified (sqrt(2), not sqrt(8)/2). An equation with no real solution gives an empty array.
Transform
Operations modify the equation and return it. A failing operation leaves it unchanged. Adding or subtracting a value applies to both members, and multiplying by a negative number reverses an inequality.
new Equation('2x+3=7').subtract(3).display // 2x=4
new Equation('2x=4').divide(2).display // x=2
new Equation('x+1<3').multiply(-1).display // -x-1>-3
new Equation('x+1<3').opposite().display // -x-1>-3
new Equation('x^2=2x+3').reduce().display // x^(2)-2x-3=0
new Equation('3x+2=5').reorder().display // 3x=3moveLeft() moves everything to the left, reorder() keeps the terms with a variable on the left and the constants on the right, and reduce() gives integer coefficients without common divisor and a positive leading term.
Isolate a variable
new Equation('2x+3=7').canIsolate('x') // true
new Equation('2x+3=7').isolate('x').display // x=2
new Equation('2x+3y=6').isolate('y').display // y=-2/3x+2
new Equation('x^2=4').canIsolate('x') // falseisolate works on an equation of the first degree without a term such as xy, and throws a MathError otherwise. Test with canIsolate first.
Check a value and compare
const E = new Equation('2x+3=7')
E.evaluate({x: 2}) // true
E.evaluate({x: 3}) // false
new Equation('x+1<3').evaluate({x: 1}) // true
E.test({x: new Fraction(2)}) // trueevaluate honours the sign, test only checks that both members have the same value.
Two notions of equality exist.
new Equation('3x=5').isEqual(new Equation('3x=5')) // true
new Equation('3x=5').isEqual(new Equation('5=3x')) // false
new Equation('3x=5').isEquivalentTo(new Equation('6x=10')) // true
new Equation('3x=5').isEquivalentTo(new Equation('3x-5=0')) // trueisEqual compares the equations as written. isEquivalentTo checks that they have the same solutions, up to a factor, and it takes the direction of an inequality into account.
Solution
A Solution is an exact value: a rational part plus a radical part. It comes from solve(), or you can build one.
const [a, b] = new Equation('x^2-2x-1=0').solve()
a.display // 1-sqrt(2)
b.tex // 1 + \sqrt{ 2 }
b.value.toFixed(3) // 2.414
b.exact // true
b.fraction.display // 1
b.root.display // sqrt(2)The exact flag is true for a rational value or a quadratic radical, and false for a numerical approximation (an irreducible cubic or a polynom of degree 4 or more). count is the multiplicity of the root.
const [s] = new Equation('x^2-2x+1=0').solve()
s.display // 1
s.count // 2Build a solution
new Solution('2/5').display // 2/5
new Solution('(-1+sqrt(5))/2').tex // \frac{ -1 + \sqrt{ 5 } }{ 2 }
new Solution('2sqrt(3)/5').display // (2sqrt(3))/5
Solution.fromQuadratic(1, -2, -1).map(s => s.display) // [ '1-sqrt(2)', '1+sqrt(2)' ]A string accepts a rational, a radical (sqrt(5), 2sqrt(3)/5) or the combined form (a±bsqrt(c))/d. Anything else throws a ParseError.
Display
withDigits(n) shows the decimal value, whatever the exact form.
const x = new Solution('(1+sqrt(2))/2')
x.withDigits(2).display // 1.21
x.withDigits(null).display // (1+sqrt(2))/2
x.variable // xvariable names the unknown the solution belongs to (x by default).
Members
Equation
Constructors
new Equation()new Equation(equation: InputAlgebra<Polynom> \| Equation)new Equation(left: InputAlgebra<Polynom>, right: InputAlgebra<Polynom>, sign?: EquationSign)
Static members
| Member | Description |
|---|---|
fromString(equationString: string): Equation | Build the equation from a complete equation string (containing a sign). Throws: {ParseError} when the string carries no equation sign. |
fromPolynoms(left: InputAlgebra<Polynom>, right: InputAlgebra<Polynom>, sign?: EquationSign): Equation | Build the equation from its two members and an optional sign (defaults to =). |
isEquationString(equationString: string): boolean | True when the string contains an equation sign (=, < or >). |
makeSolutionsUnique(solutions: Solution[], sorted?: boolean): Solution[] | Remove the duplicated solutions (same tex), keeping the first one. With sorted, sort them by value. |
Properties and getters
| Member | Description |
|---|---|
tex: string | LaTeX rendering of the current state (terminal output, not chainable). |
display: string | ASCIIMath rendering of the current state (terminal output, not chainable). |
left: Polynom | Left member. This is the polynom itself, not a copy: use split for copies. |
numberOfVars: number | Number of distinct variables in both members. |
right: Polynom | Right member. This is the polynom itself, not a copy: use split for copies. |
sign: string | Sign of the equation: =, <, >, <= or >=. Setting it accepts the other notations (\leq, geq, =>...). |
signAsTex: string | The sign written for LaTeX: \leq and \geq for the non-strict inequalities. |
variables: string[] | The variables of both members, without duplicates. |
Methods
| Member | Description |
|---|---|
parse(value: InputAlgebra<Polynom> | Equation): Equation | Router: dispatch an input to the matching from*** method. A same-type input goes through copy, a string is a full equation (strict: fromString throws if it carries no sign), and a bare algebraic expression is read as <expr> = 0. |
copy(value: Equation): Equation | Copy the mathematical state (left, right and sign) of another equation into this one. Entry point used by the constructor for an Equation input. |
clone(): Equation | Full duplicate of this instance, including display state (selected form and options set via as*** / with***). Distinct from copy. |
withDigits(digits: number | null): Equation | Show every numeric value as a decimal with digits digits after the point (exact values included: 3/2 becomes 1.50 with 2 digits). |
add(...values: InputValue<Equation | Polynom>[]): Equation | Add one or more values to the equation. An Equation operand is added member by member; any other value (Polynom, Monom, Fraction, number, or a sign-less string) is added to both members. Atomic: a failing operand leaves the receiver intact. |
degree(variable?: string): Fraction | Get the degree of the equation |
divide(...values: InputValue<Fraction>[]): Equation | Divide both members of the equation by a value (coerced to a Fraction). A zero divisor is a no-op (the equation is returned unchanged). For an inequality, a negative divisor reverses the sign. 8x+10=6x | 2 -> 4x+5=3x |
evaluate(values: InputValue<Fraction> | LiteralType<number | Fraction>, asNumeric?: boolean): boolean | Evaluate the equation as a predicate: substitute the given variable values into both members and return whether the relation left <sign> right holds (the equation sign is honoured, including inequalities). Distinct from the value-producing evaluate of Polynom/Monom (which returns a number). |
hasVariable(variable: string): boolean | Determine if the equation contains a variable. |
isEqual(value: InputValue<Equation>): boolean | Strict equality: both members are identical as written and the sign matches. Sensitive to how terms are split and to the side order (3x=5 is not isEqual to 5=3x). For "same solutions", use isEquivalentTo. |
isEquivalentTo(equ: Equation): boolean | Equivalence: the two (in)equations share the same solution set. All terms are moved left and the resulting polynoms are compared up to a scalar factor (3x=5, 3x-5=0 and 6x=10 are all equivalent). For inequalities a negative factor mirrors the equation, so the sign must reverse accordingly. Looser than isEqual. |
hasCrossTerm(): boolean | Whether the equation contains a cross-term monom (a single monom carrying more than one variable, such as xy), which prevents isolating a variable linearly. A linear equation in several distinct variables (e.g. ax+by+c=0) has no cross-term and stays isolable. |
canIsolate(variable?: string): boolean | Whether the equation can be linearly isolated for the given variable: it must be of the first degree and free of any cross-term monom. Pure query (no mutation); use it to guard isolate. |
isolate(variable?: string): Equation | Reorder the equation to keep a single variable on the left and the rest on the right, with a coefficient of 1. For an inequality, a negative coefficient reverses the sign. Atomic: on failure the receiver is left unchanged. Throws: {MathError} when the equation cannot be isolated (guard with canIsolate). |
moveLeft(): Equation | Reorder will move all monoms containing a variable on the left, all the other on the right. |
multiply(...values: InputValue<Fraction>[]): Equation | Multiply both members of the equation by one or more fraction values. A negative factor reverses the inequality sign. Atomic: a failing operand leaves the receiver intact. |
opposite(): Equation | Multiply both members by -1. For an inequality the direction is reversed (x < 3 becomes -x > -3), so the equation keeps the same solutions. |
pow(value: number): Equation | Raise both members to the power value. The sign is left as it is, so squaring an inequality does not keep its solutions. Throws: {InvalidArgumentError} if value is not a non-negative integer. |
reduce(): Equation | Put the equation in a canonical form: every term on the left (the right member is zero), integer coefficients without common divisor, and a positive leading coefficient. A negative factor reverses the sign of an inequality. |
reorder(allLeft?: boolean): Equation | Move the terms with a variable to the left and the constants to the right. With allLeft, move everything to the left instead. The sign does not change. |
replaceBy(variable: string, P: Polynom): Equation | Replace a variable by a polynom in both members (in place). Throws: {MathError} if the variable appears with a negative power. |
simplify(): Equation | Multiply by the lcm denominator and divide by the gcm numerators. |
solve(): Solution[] | Solve left - right = 0, whatever the sign, and return the roots as Solutions sorted by value. An inequality has the roots of its equality: use TableOfSigns to study its sign. Throws: {MathError} if a degree is not a natural number. |
split(): [Polynom, Polynom] | Copies of the left and right members, as [left, right]. |
subtract(...values: InputValue<Equation | Polynom>[]): Equation | Subtract one or more values from the equation (member by member for an Equation operand, from both members otherwise). Atomic: a failing operand leaves the receiver intact. |
test(values: LiteralType<Fraction>): boolean | True when both members have the same value for the given values of the variables. The sign is ignored: use evaluate to test an inequality. Throws: {MathError} if a variable has no value. |
Solution
Constructors
new Solution(value?: string \| Fraction \| Root \| PolynomRoot \| Solution)
Static members
| Member | Description |
|---|---|
fromFraction(value: InputValue<Fraction>): Solution | Build an exact solution from a rational value. |
fromRoot(value: InputValue<Root>): Solution | Build an exact solution from a radical value. |
fromPolynomRoot(root: PolynomRoot): Solution | Wrap a raw PolynomRoot (fraction + radical parts) into a Solution, mapping its multiplicity to count. |
fromQuadratic(A: InputValue<Fraction>, B: InputValue<Fraction>, C: InputValue<Fraction>): Solution[] | Solve A·x² + B·x + C = 0. Returns [] when the discriminant is negative, a single solution (with count === 2) for a double root, otherwise the two solutions. Irrational roots are kept exact (index-2 Root). |
fromString(value: string): Solution | Parse a string representation of a solution. Three strict forms: - Pure rational : 2/5, -3, 1.23 - Pure radical : sqrt(5), -2sqrt(3), sqrt(5)/3, 2sqrt(3)/5 - Combined : (-1+sqrt(5))/2, (3-2sqrt(7))/4 Throws: {ParseError} if the string matches none of the supported forms. |
Properties and getters
| Member | Description |
|---|---|
tex: string | LaTeX rendering. An explicit override (set tex) takes precedence; otherwise the value is composed from its parts: the fraction or the radical alone in the simple cases, else (numerator ± radical)/denominator. With withDigits, the decimal value. |
display: string | ASCII rendering. Mirrors tex (override precedence, then #compose). Returns null only when explicitly overridden to null via set display. |
count: number | Multiplicity of the root (always >= 1; defaults to 1). A double root reports 2, etc. |
exact: boolean | Whether the value is faithfully exact: true for an exact rational or a quadratic irrational carried by an index-2 Root, false for a floating (Cardan / bisection) approximation. Never mark a float value exact. |
fraction: Fraction | Rational part of the value. |
root: Root | Radical part of the value: zero for a rational solution. |
value: number | Decimal value: the rational part plus the radical part. |
variable: string | Variable the solution belongs to (x by default). The setter accepts a single lowercase letter. Throws: {InvalidArgumentError} when setting anything else. |
Methods
| Member | Description |
|---|---|
withDigits(digits: number | null): Solution | Show the numeric value as a decimal with digits digits, whatever the exact form (a radical included: (1+sqrt(2))/2 becomes 1.21). null goes back to the exact form. Takes precedence over a tex / display override. Throws: {InvalidArgumentError} if digits is neither null nor an integer from 0 to 100. |
parse(value: string | Fraction | Root | PolynomRoot | Solution): Solution | Router that builds a Solution from a supported input by delegating to the matching factory: string → fromString, Fraction → fromFraction, Root → fromRoot, PolynomRoot → fromPolynomRoot, Solution → copy. |
copy(value: Solution): Solution | Copy the math state of another Solution into this (parts are cloned). Display overrides are not transferred, and any stale #display/#tex on this is dropped so the getters recompute from the new state. |
clone(): Solution | Full duplicate of this Solution, including the display state (#display/#tex). |
isZero(): boolean | True when both the rational part and the radical part are zero. |
reduce(): Solution | Reduce the radical and rational parts to their canonical form, in place, and return this. Deliberate exception to the clone-and-commit atomicity convention: Root.reduce / Fraction.reduce are total (never throw), so the receiver cannot be left half-mutated. |
setExact(value?: boolean): Solution | Flag the solution as exact (default) or approximate. |