Numbers
Three building blocks sit under every other class: Fraction (an exact rational number), Root (a number such as 3√5) and Numeric (small functions on JavaScript numbers).
import {Fraction, Root, Numeric} from 'pimath'Fraction
A Fraction stores an integer numerator and an integer denominator. It is exact: 1/3 + 1/6 is 1/2, not 0.4999999.
Build a fraction
new Fraction('3/4').display // 3/4
new Fraction(3, 4).display // 3/4
new Fraction(0.75).display // 3/4
new Fraction('2.31(25)').display // 11447/4950
new Fraction().display // 0A string can be an integer, a decimal, a/b or a periodic decimal where the repeating digits are in parentheses. The static Fraction.isFraction(value) tells whether a value is understood.
Fraction.isFraction('2/3') // true
Fraction.isFraction('2/') // falseOperations change the object
add, subtract, multiply, divide, pow, root, sqrt, abs, opposite, inverse and reduce modify the fraction and return it. Chain them, and use clone() to keep the original.
const a = new Fraction('1/2')
const b = a.clone().add('1/3')
a.display // 1/2
b.display // 5/6
new Fraction('2/3').multiply('3/4').display // 1/2
new Fraction('-3/4').abs().display // 3/4
new Fraction(9, 4).sqrt().display // 3/2
new Fraction(6, -4).reduce().display // -3/2add, subtract and divide reduce their result, and so does multiply unless it is called with false as second argument. Fraction.xMultiply(...values) multiplies without reducing.
If an operation throws, the fraction is left unchanged.
Display
const f = new Fraction('-3/4')
f.tex // -\frac{ 3 }{ 4 }
f.display // -3/4
f.asDfrac.tex // -\dfrac{ 3 }{ 4 }
f.value // -0.75asFrac, asDfrac and asTfrac choose the LaTeX command, withSign shows a + before a positive value, and withDigits(n) shows a decimal with n digits, exact fractions included. These options are chainable and kept by clone().
new Fraction('3/2').withSign.display // +3/2
new Fraction('3/2').withDigits(2).display // 1.50
new Fraction('3/2').withDigits(null).display // 3/2Compare and test
new Fraction('2/4').isEqual(new Fraction('1/2')) // true
new Fraction('2/4').isReduced() // false
new Fraction('3/2').isGreater(1) // true
new Fraction('4/2').isRelative() // true
new Fraction('3/2').hasFractionalPart() // true
new Fraction('1/3').isPeriodic() // true
new Fraction('9/4').isSquare() // trueVocabulary used by the library:
- relative: an integer of any sign (
isRelative()); natural: a non-negative integer (isNatural()). isRational()is true for every exact finite value, integers included.hasFractionalPart()is true only for an exact value that is not an integer.isPositive()is true for zero,isStrictlyPositive()is not.
Collections
Fraction.max('1/2', '2/3', '1/3').display // 2/3
Fraction.min('1/2', '2/3', '1/3').display // 1/3
Fraction.sort(['1/2', '2/3', '1/3']).map(f => f.display) // [ '1/3', '1/2', '2/3' ]
Fraction.unique(['1/2', '2/4', '1/3']).map(f => f.display) // [ '1/2', '1/3' ]
Fraction.toSameDenominateur('1/2', '1/3').map(f => f.display) // [ '3/6', '2/6' ]
Fraction.average(1, 2).display // 3/2Invalid values and infinity
Fraction never throws on an undefined result, like a JavaScript number. Test the result instead.
new Fraction('1/0').display // +oo
new Fraction('1/0').isFinite() // false
new Fraction('0/0').isNaN() // true
new Fraction('abc').isNaN() // true
new Fraction(1).divide(0).display // +ooAn operation that is mathematically impossible, such as an even root of a negative value, throws a MathError.
new Fraction(-4).sqrt() // throws MathErrorRoot
A Root is factor · ⁿ√radical: the factor and the radical are fractions, and the index n is 2 for a square root. It is built from a string, a number or a fraction.
new Root('sqrt(12)').display // sqrt(12)
new Root('3sqrt(5)').tex // 3 \sqrt{ 5 }
new Root('root(3)(10)').display // root(3)(10)
new Root('5/2').display // 5/2sqrt(2/7) is the root of a fraction. A bare /7 after the radical divides the whole root: sqrt2/7 is (√2)/7.
new Root('sqrt(2)/7').value // 0.202
new Root('sqrt(2/7)').value // 0.535Unlike Fraction, Root is strict: a malformed input throws a ParseError.
new Root('2sqrt') // throws ParseErrorSimplify
reduce() removes the root from the denominator and extracts the greatest perfect power.
new Root('sqrt(12)').reduce().tex // 2 \sqrt{ 3 }
new Root('sqrt(1/2)').reduce().display // sqrt(2)/2
new Root('root(3)(-40)').reduce().display // -2root(3)(5)For an odd index, the sign always moves to the factor. An even root of a negative radical is left as it is, and its value is NaN.
new Root('sqrt(-4)').value // NaN
new Root('sqrt(9)').isRational() // true
new Root('sqrt(2)').isRational() // falseOperations
Like fractions, operations modify the root and return it.
new Root('2sqrt(3)').add('5sqrt(3)').display // 7sqrt(3)
new Root('sqrt(2)').multiply('sqrt(3)').display // sqrt(6)
new Root('sqrt(8)').isEqual(new Root('2sqrt(2)')) // trueadd and subtract need the same index and the same radical once both roots are reduced; otherwise they throw a MathError. multiply needs the same index or the same radical. A failed operation leaves the root unchanged.
Numeric
Numeric is a set of functions on JavaScript numbers. It is useful to build a generator from integers.
Numeric.gcd(12, 18) // 6
Numeric.lcm(4, 6) // 12
Numeric.dividers(12) // [ 1, 2, 3, 4, 6, 12 ]
Numeric.primes(5) // [ 2, 3, 5, 7, 11 ]
Numeric.pythagoreanTripletsWithTarget(5).filter(t => t[0] > 0 && t[0] < t[1]) // [ [ 3, 4, 5 ] ]A function that checks an integer contract throws an InvalidArgumentError, and a function defined over a mathematical domain returns a value for an input outside of it: Numeric.nthRoot(-4, 2) is NaN, and Numeric.greatestPower gives 1 when nothing can be extracted.
Members
Fraction
Constructors
new Fraction()new Fraction(value: InputValue<Fraction>)new Fraction(numerator: number, denominator: number)
Static members
| Member | Description |
|---|---|
areEquals(...F: Fraction[]): boolean | True when every given fraction has the same value as the first one (2/4 equals 1/2). |
average(...fractions: (InputValue<Fraction>)[]): Fraction | Arithmetic mean of the values, as a new fraction. Without any value the result is +∞. |
isFraction(value: InputValue<Fraction>): boolean | True when new Fraction(value) understands the value: a Fraction, a number, "a/b", a decimal string, a periodic notation such as "2.31(25)", or an empty string (zero). |
max(...fractions: InputValue<Fraction>[]): Fraction | Largest of the values, as a new fraction. |
min(...fractions: (InputValue<Fraction>)[]): Fraction | Smallest of the values, as a new fraction. |
sort(fractions: (InputValue<Fraction>)[], reverse?: boolean): Fraction[] | Sort by value, ascending (descending when reverse is true). Returns a new array; the Fraction instances of the input are reused, not cloned. |
toSameDenominateur(...fractions: InputValue<Fraction>[]): Fraction[] | New fractions sharing the least common denominator, without reducing them. |
unique(fractions: (InputValue<Fraction>)[]): Fraction[] | Remove the duplicates by value (2/4 and 1/2 are the same). Keeps the first occurrence, as a clone. |
xMultiply(...values: (InputValue<Fraction>)[]): Fraction | Product of the values, as a new fraction that is not reduced (call reduce to simplify it). |
fromNumber(value: number): Fraction | Build a new fraction from a single number (integer or decimal). Throws: {InvalidArgumentError} for exponential notation (e.g. 1e-7), which would break the integer numerator/denominator invariant. |
fromNumbers(numerator: number, denominator: number): Fraction | Build a new fraction from a numerator and a denominator, without reducing it. A zero denominator gives a signed infinity (3/0), or an invalid fraction for 0/0. Values that are not safe integers are converted through fromNumber and become approximate. |
fromPeriodic(value: string | number, length: number): Fraction | Build a new fraction from a decimal whose last length digits repeat (e.g. fromPeriodic(2.3125, 2) → 11447/4950). Throws: {InvalidArgumentError} if length is not a positive integer, or exceeds the number of decimal digits. |
fromString(value: string): Fraction | Build a new fraction from a string: an integer, a decimal, "a/b" or a periodic decimal "2.31(25)". A malformed string does not throw: the result is an invalid fraction (see isNaN). |
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). |
denominator: number | Denominator, as stored (it is not reduced automatically). Setting it to zero gives a signed infinity, or an invalid fraction when the numerator is zero too. |
asDfrac: Fraction | Display option: .tex uses \dfrac instead of \frac. Chainable, persistent, kept by clone. |
exact: boolean | False when the value is an approximation (inexact root, very long decimal, integer part above 2^53). An approximate fraction is displayed as a decimal with 3 digits. |
asFrac: Fraction | Display option: .tex uses \frac (default). Chainable, persistent, kept by clone. |
numerator: number | Numerator, as stored (it carries the sign unless the denominator is negative). |
asTfrac: Fraction | Display option: .tex uses \tfrac instead of \frac. Chainable, persistent, kept by clone. |
value: number | Decimal value (numerator / denominator). Negative zero is returned as 0. |
withSign: Fraction | Display option: show a + before a positive value (zero included). Chainable, persistent, kept by clone. Cancel it with withoutSign. |
withoutSign: Fraction | Display option: cancel withSign. Chainable. |
Methods
| Member | Description |
|---|---|
parse(value: InputValue<Fraction>, denominator?: number): Fraction | Router: inspect the input type/format and delegate to the matching from*** builder (fromString / fromNumber / fromNumbers), or to copy() for a Fraction input. Does not build the fraction inline. |
clone(): Fraction | Full duplicate of this instance, including display state (selected form and options set via as*** / with***). Distinct from copy. |
copy(value: Fraction): Fraction | Copy the mathematical value of another fraction into this one. NOTE: unlike clone, copy intentionally transfers only the mathematical state (#numerator, #denominator, #exact) and NOT the display state (#type, #digits, #withSign). This divergence is by design: parse() routes a Fraction input to copy(), so the receiving fraction keeps its own formatting configuration. |
abs(): Fraction | Replace the fraction by its absolute value. |
add(F: InputValue<Fraction>): Fraction | Add a value (fraction, number or string) and reduce the result. Modifies and returns the receiver. The result is flagged approximate (exact is false) when an operand is approximate or an integer part exceeds 2^53. |
amplify(k: number): Fraction | Scale numerator and denominator by the same non-zero integer factor. Throws: {InvalidArgumentError} if k is not a safe integer, or is zero. |
compare(F: InputValue<Fraction>, sign?: CompareSign): boolean | Compare this fraction with another value using the given relational sign. |
withDigits(digits: number | null): Fraction | Show the value as a decimal with digits digits (exact fractions included), or null for the default display: an exact fraction as a/b, an approximate value with 3 digits. Throws: {InvalidArgumentError} if digits is neither null nor an integer from 0 to 100. |
divide(F: InputValue<Fraction>): Fraction | Divide by another value. Total operation (see the "IExpression totality" convention): dividing by zero does not throw but yields a signed infinity, preserving the dividend's sign (e.g. (-3) / 0 → -∞). |
infinite(): Fraction | Turn the receiver into +∞. Use opposite afterwards for -∞. |
invalid(): Fraction | Turn the receiver into an invalid fraction (NaN). Test it with isNaN. |
inverse(): Fraction | Swap numerator and denominator, keeping the sign. Total operation, like divide: the inverse of zero is +∞ (1 / 0), not an error. |
isEqual(than: Fraction | number): boolean | True when both values are equal. Fractions are compared by cross product: 2/4 equals 1/2. A NaN is equal to nothing. |
isEven(): boolean | True for an even integer. A fraction that is not an integer is neither even nor odd. |
isFinite(): boolean | True unless the fraction is infinite or invalid. |
isGeq(than: Fraction | number): boolean | True when the receiver is greater than or equal to than. |
isGreater(than: Fraction | number): boolean | True when the receiver is strictly greater than than. |
isInfinity(): boolean | True for +∞ and -∞. |
isLeq(than: Fraction | number): boolean | True when the receiver is less than or equal to than. |
isLesser(than: Fraction | number): boolean | True when the receiver is strictly less than than. |
isNaN(): boolean | True for an invalid fraction (0/0, a malformed string, a failed parse). |
isNatural(): boolean | True for a non-negative integer (zero included). |
isNegative(): boolean | True when the value is strictly negative. Zero and NaN are not negative (see sign). |
isNotEqual(than: Fraction | number): boolean | True when the values differ. Unlike a negated isEqual, an invalid fraction is also considered different. |
isNotZero(): boolean | True when the numerator is not zero. |
isOdd(): boolean | True for an odd integer, negative ones included. |
isOne(): boolean | True for the literal 1/1. A non-reduced 2/2 is not "one": call reduce first. |
isPeriodic(): boolean | True when the decimal expansion of the exact fraction never stops (1/3, 1/7), that is when the reduced denominator has a prime factor other than 2 and 5. False for an approximate or non finite value. |
isPositive(): boolean | True when the value is positive or zero (see sign). |
isRational(): boolean | True when the fraction is an exact rational number: a finite, exact numerator/denominator. Integers are included (4 = 4/1 is rational). Approximate (non-exact) fractions and ±Infinity / NaN are not rational. |
hasFractionalPart(): boolean | True when the fraction is exact and NOT an integer, i.e. it has a non-zero fractional part (e.g. 3/2). Use isRelative to test for an integer. (This was the former behaviour of isRational.) |
isReduced(): boolean | True when numerator and denominator have no common divisor other than 1. |
isRelative(): boolean | True when the fraction represents a relative integer, i.e. its reduced denominator equals 1 (e.g. 6/3 = 2). See hasFractionalPart for the exact non-integer case. |
isSquare(): boolean | True when the reduced fraction is the square of a rational (4/9). A negative value is never a square. |
isStrictlyNegative(): boolean | True when the value is below zero. |
isStrictlyPositive(): boolean | True when the value is above zero. |
isUnit(): boolean | True for 1 and -1, as integers. |
isZero(): boolean | True when the numerator is zero. |
multiply(F: InputValue<Fraction>, reduce?: boolean): Fraction | Multiply by a value (fraction, number or string). Modifies and returns the receiver. |
one(): Fraction | Set the value to 1. The display options are kept. |
opposite(): Fraction | Change the sign of the fraction (- times the numerator). |
pow(p: number | Fraction): Fraction | Raise to the power p. The fraction is reduced first; a negative p inverts it. An integer exponent gives an exact result. A fractional exponent is exact only when numerator and denominator are perfect powers ((9/4)^0.5 = 3/2); otherwise the result is approximate. A rejected call leaves the receiver intact. Throws: {MathError} if the base is negative and the exponent is not an integer. |
reduce(): Fraction | Divide numerator and denominator by their greatest common divisor, and keep the sign on the numerator. |
root(p: number): Fraction | p-th root of the fraction, exact when it is a perfect power, otherwise an approximation (exact becomes false). A rejected root leaves the receiver intact (atomicity). Throws: {InvalidArgumentError} if p is not a safe integer.; {MathError} if the radicand is negative and p is even (not real). |
sign(): 1 | -1 | Sign of the fraction, as +1 or -1. NOTE (assumed): zero AND NaN are classified as positive (sign() returns 1). Consequently isPositive() and isNatural() are true for zero, while isNegative() is false. A NaN has no real sign; +1 is returned so an invalid fraction is not misclassified as negative. |
sqrt(): Fraction | Square root, same as root(2). Exact for a perfect square, approximate otherwise. Throws: {MathError} if the fraction is negative. |
subtract(F: InputValue<Fraction>): Fraction | Subtract a value (fraction, number or string) and reduce the result. Modifies and returns the receiver. |
toRepeatingDecimal(): { sign: 1 | -1; integerPart: string; nonPeriodic: string; periodic: string; periodLength: number; } | Decompose the fraction into its repeating-decimal parts (sign, integer part, non-periodic digits, periodic digits and period length). Throws: {InvalidArgumentError} if the fraction is non-exact or infinite. |
zero(): Fraction | Set the value to 0. The display options are kept. |
Root
Constructors
new Root(value?: InputValue<Root \| Fraction>)
Static members
| Member | Description |
|---|---|
fromFraction(value: Fraction): Root | Build a new root of index 2 equal to the fraction (factor = value, radical = 1). |
fromString(value: string): Root | Build a new Root by delegating a string to the sqrt / root parser, or to the scalar builder for a plain fraction string. Throws: {ParseError} if the string is a malformed radical or fraction. |
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). |
factor: Fraction | Fraction multiplying the radical. Never an invalid (NaN) fraction. Throws: {InvalidArgumentError} if value is an invalid (NaN) fraction. |
index: number | Index of the root: 2 for a square root, 3 for a cube root... Throws: {InvalidArgumentError} if value is not a strictly positive integer. |
indexAsPow: Fraction | The index as a fractional exponent: 1 / index (e.g. index 3 → 1/3). |
radical: Fraction | Fraction under the root sign. Never an invalid (NaN) fraction. Throws: {InvalidArgumentError} if value is an invalid (NaN) fraction. |
value: number | Numeric value of the root. Returns NaN for an even root of a negative radical (not real); an odd root of a negative is real and negative. |
withSign: Root | Display option (chainable, returns this): force a leading '+' on a positive factor. |
withoutSign: Root | Display option (chainable, returns this): remove the forced leading '+'. |
Methods
| Member | Description |
|---|---|
parse(value: InputValue<Root | Fraction>): Root | Router: inspect the input type and delegate to the matching builder. A same-type (Root) input goes through copy(); every other form has its own from*** / #fromScalar. Construction logic is never inlined here. Throws: {ParseError} if a string/number input is malformed. |
copy(value: Root): Root | Copy the mathematical state of another instance into this (display state is NOT transferred — the receiver keeps its own formatting). Entry point used when constructing/parsing from a same-type input. Distinct from clone. Classes without a real implementation throw NotImplementedError. |
clone(): Root | Full duplicate of this instance, including display state (selected form and options set via as*** / with***). Distinct from copy. |
add(value: InputValue<Root>): Root | Add another root. Both operands are reduced to a common radical first. Throws: {MathError} if the roots do not share the same index and radical. |
divide(value: InputValue<Root>): Root | Divide by another root (multiply by its inverse). Throws: {MathError} if the divisor is zero, or the roots are incompatible for multiplication. |
from(index: number, radical: InputValue<Fraction>, factor?: InputValue<Fraction>): Root | Explicit builder: index, radical and optional factor (defaults to 1). Throws: {InvalidArgumentError} if index is not a strictly positive integer.; {ParseError} if radical or factor is an invalid (NaN) primitive. |
group(): Root | convert to root(index)(radical), without factor |
inverse(): Root | Multiplicative inverse (1 / this): inverse both factor and radical. Throws: {MathError} if the root is zero (0 has no inverse). |
isEqual(root: Root): boolean | True when both roots have the same real value, compared exactly (no floating point): sqrt(8) equals 2sqrt(2). An even root of a negative radical is not real and equals nothing. |
isOne(): boolean | True when factor and radical are both 1. Reduce the root first to detect sqrt(4). |
isRational(): boolean | True when the value is a rational number once reduced (sqrt(9), root(3)(-8)), false for sqrt(2). |
isZero(): boolean | True when the factor or the radical is zero. |
multiply(value: InputValue<Root>): Root | Multiply by another root. Computed on a clone and committed on success (atomicity). Throws: {MathError} unless the two roots share their radical or their index. |
one(): Root | Set the value to 1. |
opposite(): Root | Change the sign of the root (the sign is carried by the factor). |
pow(value: number): Root | Raise the root to a power. Computed on a clone and committed only on success, so a rejected pow leaves the receiver intact (atomicity). Throws: {MathError} (via Fraction.pow) for a fractional power of a negative factor or radical. |
reduce(): Root | Put the root in simplified form: no root in the denominator, and the greatest perfect power extracted from the radical (sqrt(12) becomes 2sqrt(3)). For an odd index, the sign always ends up on the factor and the radical becomes positive (root(3)(-40) becomes -2root(3)(5)). For an even index, a negative radical is left unchanged. |
root(value: number): Root | Take the value-th root of this root (e.g. sqrt of sqrt). Computed on a clone and committed only on success (atomicity). Throws: {InvalidArgumentError} if value is not a strictly positive integer. |
sqrt(): Root | Square root of the root, same as root(2). |
subtract(value: InputValue<Root>): Root | Subtract another root (see add for the shared-radical requirement). Throws: {MathError} if the roots do not share the same index and radical. |
zero(): Root | Set the value to 0. |
Numeric
| Function | Description |
|---|---|
decompose(value: number): number[][] | Split a value into all its factor pairs [u, v] such that u * v = value. Pairs the smallest remaining divider with the largest; a perfect-square middle divider is paired with itself (e.g. 25 -> [[1, 25], [5, 5]]). Throws: {InvalidArgumentError} if value is not a finite integer (propagated from dividers). |
dividers(value: number): number[] | Get the sorted list of all (unique) dividers of an integer. Returns [] for 0 (every integer divides 0; there is no finite list to return). Throws: {InvalidArgumentError} if value is not a finite integer. |
divideNumbersByGCD(...values: number[]): number[] | Divide every value by their greatest common divisor (simplify a ratio). |
gcd(...values: number[]): number | Greatest common divisor of the given integers. Always positive; the gcd of an empty list, or of a single 0, is 1. |
lcm(...values: number[]): number | Least common multiple of the given integers. Computed as a / gcd * b to avoid overflowing MAX_SAFE_INTEGER; returns 0 when any value is 0. |
numberCorrection(value: number, number_of_digits?: number): number | Round away floating point representation noise (e.g. 0.1 + 0.2 -> 0.3). Contrast with round, which rescales via 1e{n} string exponents to support arbitrary (including negative) decimals; this one trims noise through toFixed on a fixed [0, 100] digit range. |
primes(nb?: number): number[] | Get the list of the nth first prime numbers. Throws: {InvalidArgumentError} if nb is negative or not an integer. |
pythagoreanTripletsWithTarget(target: number, targetIsSquare?: boolean): [number, number, number][] | Find every pythagorean triplet [u, v, hypotenuse] with u^2 + v^2 = hypotenuse^2. Both (u, v) and (v, u) orderings and zero legs are returned; callers filter them. target 0 yields the single triplet [0, 0, 0]. Throws: {InvalidArgumentError} if target is negative or not a finite integer. |
round(value: number, decimals?: number): number | Round a value to a given number of decimals. Negative decimals round to tens, hundreds, etc. Contrast with numberCorrection, which trims floating point noise through toFixed rather than rescaling. |
greatestPower(value: number, index: number): number | Get the greatest perfect power of the given index that divides value (e.g. greatestPower(50, 2) = 25). Returns 1 when none exists, when index <= 0, or for value 0 (nothing to extract). A negative value with an odd index propagates the sign so callers can pull it out of a radical (e.g. greatestPower(-8, 3) = -8, greatestPower(-40, 3) = -8). A negative value with an even index returns 1 (an even root of a negative is not real, nothing to extract). |
nthRoot(value: number, index: number): number | Exact integer nth root of a perfect index-th power (e.g. nthRoot(8, 3) = 2), avoiding Math.pow floating-point noise. Falls back to the real root when value is not a perfect power. Handles negative values for odd indices; nthRoot(0, index) is 0. Returns NaN when index <= 0 or for an even root of a negative value. |