Skip to content

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).

js
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 ​

js
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 // 0

A 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.

js
Fraction.isFraction('2/3') // true
Fraction.isFraction('2/') // false

Operations 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.

js
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/2

add, 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 ​

js
const f = new Fraction('-3/4')

f.tex // -\frac{ 3 }{ 4 }
f.display // -3/4
f.asDfrac.tex // -\dfrac{ 3 }{ 4 }
f.value // -0.75

asFrac, 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().

js
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/2

Compare and test ​

js
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() // true

Vocabulary 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 ​

js
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/2

Invalid values and infinity ​

Fraction never throws on an undefined result, like a JavaScript number. Test the result instead.

js
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 // +oo

An operation that is mathematically impossible, such as an even root of a negative value, throws a MathError.

js
new Fraction(-4).sqrt() // throws MathError

Root ​

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.

js
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/2

sqrt(2/7) is the root of a fraction. A bare /7 after the radical divides the whole root: sqrt2/7 is (√2)/7.

js
new Root('sqrt(2)/7').value // 0.202
new Root('sqrt(2/7)').value // 0.535

Unlike Fraction, Root is strict: a malformed input throws a ParseError.

js
new Root('2sqrt') // throws ParseError

Simplify ​

reduce() removes the root from the denominator and extracts the greatest perfect power.

js
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.

js
new Root('sqrt(-4)').value // NaN
new Root('sqrt(9)').isRational() // true
new Root('sqrt(2)').isRational() // false

Operations ​

Like fractions, operations modify the root and return it.

js
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)')) // true

add 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.

js
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

MemberDescription
areEquals(...F: Fraction[]): booleanTrue when every given fraction has the same value as the first one (2/4 equals 1/2).
average(...fractions: (InputValue<Fraction>)[]): FractionArithmetic mean of the values, as a new fraction. Without any value the result is +∞.
isFraction(value: InputValue<Fraction>): booleanTrue 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>[]): FractionLargest of the values, as a new fraction.
min(...fractions: (InputValue<Fraction>)[]): FractionSmallest 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>)[]): FractionProduct of the values, as a new fraction that is not reduced (call reduce to simplify it).
fromNumber(value: number): FractionBuild 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): FractionBuild 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): FractionBuild 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): FractionBuild 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

MemberDescription
tex: stringLaTeX rendering of the current state (terminal output, not chainable).
display: stringASCIIMath rendering of the current state (terminal output, not chainable).
denominator: numberDenominator, 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: FractionDisplay option: .tex uses \dfrac instead of \frac. Chainable, persistent, kept by clone.
exact: booleanFalse 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: FractionDisplay option: .tex uses \frac (default). Chainable, persistent, kept by clone.
numerator: numberNumerator, as stored (it carries the sign unless the denominator is negative).
asTfrac: FractionDisplay option: .tex uses \tfrac instead of \frac. Chainable, persistent, kept by clone.
value: numberDecimal value (numerator / denominator). Negative zero is returned as 0.
withSign: FractionDisplay option: show a + before a positive value (zero included). Chainable, persistent, kept by clone. Cancel it with withoutSign.
withoutSign: FractionDisplay option: cancel withSign. Chainable.

Methods

MemberDescription
parse(value: InputValue<Fraction>, denominator?: number): FractionRouter: 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(): FractionFull duplicate of this instance, including display state (selected form and options set via as*** / with***). Distinct from copy.
copy(value: Fraction): FractionCopy 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(): FractionReplace the fraction by its absolute value.
add(F: InputValue<Fraction>): FractionAdd 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): FractionScale 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): booleanCompare this fraction with another value using the given relational sign.
withDigits(digits: number | null): FractionShow 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>): FractionDivide 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(): FractionTurn the receiver into +∞. Use opposite afterwards for -∞.
invalid(): FractionTurn the receiver into an invalid fraction (NaN). Test it with isNaN.
inverse(): FractionSwap numerator and denominator, keeping the sign. Total operation, like divide: the inverse of zero is +∞ (1 / 0), not an error.
isEqual(than: Fraction | number): booleanTrue when both values are equal. Fractions are compared by cross product: 2/4 equals 1/2. A NaN is equal to nothing.
isEven(): booleanTrue for an even integer. A fraction that is not an integer is neither even nor odd.
isFinite(): booleanTrue unless the fraction is infinite or invalid.
isGeq(than: Fraction | number): booleanTrue when the receiver is greater than or equal to than.
isGreater(than: Fraction | number): booleanTrue when the receiver is strictly greater than than.
isInfinity(): booleanTrue for +∞ and -∞.
isLeq(than: Fraction | number): booleanTrue when the receiver is less than or equal to than.
isLesser(than: Fraction | number): booleanTrue when the receiver is strictly less than than.
isNaN(): booleanTrue for an invalid fraction (0/0, a malformed string, a failed parse).
isNatural(): booleanTrue for a non-negative integer (zero included).
isNegative(): booleanTrue when the value is strictly negative. Zero and NaN are not negative (see sign).
isNotEqual(than: Fraction | number): booleanTrue when the values differ. Unlike a negated isEqual, an invalid fraction is also considered different.
isNotZero(): booleanTrue when the numerator is not zero.
isOdd(): booleanTrue for an odd integer, negative ones included.
isOne(): booleanTrue for the literal 1/1. A non-reduced 2/2 is not "one": call reduce first.
isPeriodic(): booleanTrue 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(): booleanTrue when the value is positive or zero (see sign).
isRational(): booleanTrue 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(): booleanTrue 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(): booleanTrue when numerator and denominator have no common divisor other than 1.
isRelative(): booleanTrue 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(): booleanTrue when the reduced fraction is the square of a rational (4/9). A negative value is never a square.
isStrictlyNegative(): booleanTrue when the value is below zero.
isStrictlyPositive(): booleanTrue when the value is above zero.
isUnit(): booleanTrue for 1 and -1, as integers.
isZero(): booleanTrue when the numerator is zero.
multiply(F: InputValue<Fraction>, reduce?: boolean): FractionMultiply by a value (fraction, number or string). Modifies and returns the receiver.
one(): FractionSet the value to 1. The display options are kept.
opposite(): FractionChange the sign of the fraction (- times the numerator).
pow(p: number | Fraction): FractionRaise 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(): FractionDivide numerator and denominator by their greatest common divisor, and keep the sign on the numerator.
root(p: number): Fractionp-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 | -1Sign 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(): FractionSquare root, same as root(2). Exact for a perfect square, approximate otherwise. Throws: {MathError} if the fraction is negative.
subtract(F: InputValue<Fraction>): FractionSubtract 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(): FractionSet the value to 0. The display options are kept.

Root ​

Constructors

  • new Root(value?: InputValue<Root \| Fraction>)

Static members

MemberDescription
fromFraction(value: Fraction): RootBuild a new root of index 2 equal to the fraction (factor = value, radical = 1).
fromString(value: string): RootBuild 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

MemberDescription
tex: stringLaTeX rendering of the current state (terminal output, not chainable).
display: stringASCIIMath rendering of the current state (terminal output, not chainable).
factor: FractionFraction multiplying the radical. Never an invalid (NaN) fraction. Throws: {InvalidArgumentError} if value is an invalid (NaN) fraction.
index: numberIndex of the root: 2 for a square root, 3 for a cube root... Throws: {InvalidArgumentError} if value is not a strictly positive integer.
indexAsPow: FractionThe index as a fractional exponent: 1 / index (e.g. index 3 → 1/3).
radical: FractionFraction under the root sign. Never an invalid (NaN) fraction. Throws: {InvalidArgumentError} if value is an invalid (NaN) fraction.
value: numberNumeric 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: RootDisplay option (chainable, returns this): force a leading '+' on a positive factor.
withoutSign: RootDisplay option (chainable, returns this): remove the forced leading '+'.

Methods

MemberDescription
parse(value: InputValue<Root | Fraction>): RootRouter: 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): RootCopy 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(): RootFull duplicate of this instance, including display state (selected form and options set via as*** / with***). Distinct from copy.
add(value: InputValue<Root>): RootAdd 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>): RootDivide 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>): RootExplicit 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(): Rootconvert to root(index)(radical), without factor
inverse(): RootMultiplicative inverse (1 / this): inverse both factor and radical. Throws: {MathError} if the root is zero (0 has no inverse).
isEqual(root: Root): booleanTrue 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(): booleanTrue when factor and radical are both 1. Reduce the root first to detect sqrt(4).
isRational(): booleanTrue when the value is a rational number once reduced (sqrt(9), root(3)(-8)), false for sqrt(2).
isZero(): booleanTrue when the factor or the radical is zero.
multiply(value: InputValue<Root>): RootMultiply 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(): RootSet the value to 1.
opposite(): RootChange the sign of the root (the sign is carried by the factor).
pow(value: number): RootRaise 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(): RootPut 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): RootTake 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(): RootSquare root of the root, same as root(2).
subtract(value: InputValue<Root>): RootSubtract another root (see add for the shared-radical requirement). Throws: {MathError} if the roots do not share the same index and radical.
zero(): RootSet the value to 0.

Numeric ​

FunctionDescription
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[]): numberGreatest common divisor of the given integers. Always positive; the gcd of an empty list, or of a single 0, is 1.
lcm(...values: number[]): numberLeast 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): numberRound 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): numberRound 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): numberGet 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): numberExact 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.

Released under the MIT License.