Polynomial.js is a TypeScript library for parsing and computing univariate polynomials. It supports real, rational, complex, quaternion, modular, and custom coefficient fields.
The library provides immutable arithmetic operations, derivatives, integrals, Euclidean division, greatest common divisors, reciprocal polynomials, and Horner evaluation. It does not calculate roots, but it can construct a polynomial from known roots.
npm install polynomialPolynomial.js requires Node.js 20 or newer when used in Node.js.
import Polynomial from 'polynomial';
Polynomial.setField('R');
const polynomial = Polynomial.fromRoots([1, 2, 3]);
console.log(polynomial.toString()); // x^3-6x^2+11x-6
console.log(polynomial.eval(2)); // 0
console.log(polynomial.derive().toString()); // 3x^2-12x+11Every polynomial is an instance of the Polynomial class and must be constructed with new. Arithmetic methods return new instances and do not mutate the receiver.
const left = new Polynomial('3x^2');
const sum = left.add('-x^2');
console.log(left.toString()); // 3x^2
console.log(sum.toString()); // 2x^2CommonJS is supported as well:
const Polynomial = require('polynomial');The constructor and arithmetic methods accept polynomial strings, numbers, coefficient arrays, coefficient maps, and other Polynomial instances.
new Polynomial('23x^4+98x^2+4');
new Polynomial(55);
new Polynomial([1, 2, 3]); // 3x^2+2x+1
new Polynomial({ 3: 4, 5: 9 }); // 9x^5+4x^3String exponents must be non-negative integers. Whitespace is ignored. Coefficients may contain multiplication and division, while nested polynomial expressions and parentheses are not parsed.
new Polynomial('2 * 3x^2 + 4/5x - 1');The selected field parses each coefficient, enabling field-specific forms such as fractions, repeating decimals, and complex numbers.
Polynomial.setField('Q');
console.log(new Polynomial('5/3x^3+4/3x').toString());
Polynomial.setField('C');
console.log(new Polynomial('23ix^4+98x^2+i').toString());The built-in fields are:
| Name | Coefficients | Implementation |
|---|---|---|
R |
Real numbers | JavaScript numbers |
Q |
Rational numbers | Fraction.js |
C |
Complex numbers | Complex.js |
H |
Quaternions | Quaternion.js |
Zp |
Integers modulo p |
Built in, for example Z7 |
Polynomial.setField() selects the default field for future instances. Each instance captures that field when it is constructed, so changing the default does not alter existing polynomials or their subsequent operations.
Polynomial.setField('Q');
const rational = new Polynomial('1/3x');
Polynomial.setField('R');
console.log(rational.add('1/3x').toString()); // 0.(6)xDivision in Zp requires the divisor to have a multiplicative inverse. Use a prime modulus when all non-zero coefficients must be invertible.
A custom field can be installed as the default with setField() or passed directly to the constructor. Passing it to the constructor avoids shared configuration and keeps the field local to that polynomial and all results derived from it.
import Polynomial, { type PolynomialField } from 'polynomial';
const field: PolynomialField<number> = {
add: (left, right) => left + Number(right),
sub: (left, right) => left - Number(right),
mul: (left, right) => left * Number(right),
div: (left, right) => left / Number(right),
parse: value => Number(value),
empty: value => value === undefined || value === 0,
pow: (base, exponent) => base ** exponent,
equals: (left, right) => left === Number(right),
characteristic: 0,
magnitude: value => Math.abs(value),
sqrt: value => value < 0 ? null : Math.sqrt(value),
};
const polynomial = new Polynomial('x^2+2x+1', field);new Polynomial(input?, field?)creates a polynomial.Polynomial.fromRoots(roots, field?)creates a monic polynomial with the given roots.Polynomial.quotient(modulus, field?)creates the quotient ring defined by a non-constant modulus polynomial.Polynomial.enumerate(maxDegree, maxElements?)orPolynomial.enumerate(maxDegree, field, maxElements?)enumerates polynomials over a finite prime field.Polynomial.cyclicCodeGenerators(length, dimension, maxGenerators?)orPolynomial.cyclicCodeGenerators(length, dimension, field, maxGenerators?)finds all generator polynomials for a cyclic code.Polynomial.setField(field)selectsR,Q,C,H,Zp, or a custom field as the default for future instances.
-
add(value)returns the sum. -
sub(value)returns the difference. -
mul(value)returns the product. -
addmul(left, right)adds the product of two values. -
div(value)returns the quotient of polynomial long division. -
mod(value)returns the remainder of polynomial long division. -
gcd(value)returns the monic greatest common divisor. -
lcm(value)returns the monic least common multiple. -
isDivisibleBy(value)reports whether polynomial division has zero remainder. -
factor()returns the leading unit followed by monic irreducible factors with multiplicity. -
isIrreducible()reports whether a positive-degree polynomial is irreducible. -
cyclicCodeBasis(length)returns the shifted basis generated by a divisor of$x^n-1$ . -
compare(value)compares finite-field polynomials in coefficient order. -
successor()returns the next polynomial in that order. -
pow(exponent)raises the polynomial to a non-negative integer power. -
neg()negates every coefficient. -
reciprocal()reverses the coefficient order relative to the degree. -
monic()divides every coefficient by the leading coefficient. -
dispersion(maxShift?)returns the greatest non-negative integerkfor whichgcd(p(x), p(x+k))is non-constant. -
sqrt()returns a polynomialgsatisfyingg² = p, ornullwhen no polynomial square root exists.
For R, Q, and C, dispersion() derives a finite search bound from the coefficients. A custom field can provide magnitude() and an optional characteristic to enable the same behavior. Otherwise, pass an explicit non-negative maxShift. Non-constant polynomials over a field with positive characteristic have infinite dispersion because shifts by multiples of the characteristic leave the polynomial unchanged.
Polynomial.setField('Q');
const polynomial = new Polynomial('x^3-7x^2+10x'); // roots: 0, 2, 5
console.log(polynomial.dispersion()); // 5
console.log(polynomial.dispersion(4)); // 3sqrt() uses the root with the principal leading coefficient. Its negation is the second square root. Exact roots are supported over Q and prime fields, while R also permits irrational coefficients and C uses the principal complex coefficient root. Custom fields must provide sqrt(value). Quaternion coefficients are not supported because the coefficient recurrence assumes commutative multiplication.
const square = new Polynomial('9x^4+6x^3-11x^2-4x+4');
const root = square.sqrt();
console.log(root?.toString()); // 3x^2+x-2
console.log(root?.mul(root).toString()); // 9x^4+6x^3-11x^2-4x+4derive(order = 1)returns the requested derivative.integrate(order = 1)returns the requested antiderivative with zero integration constants.eval(value)evaluates the polynomial using Horner's method.compose(value)substitutes a polynomial for the indeterminate using Horner's method.result(value)is a deprecated alias foreval(value).
const polynomial = new Polynomial('2x^3-3x+5');
console.log(polynomial.compose('x^2+1').toString()); // 2x^6+6x^4+3x^2+4Polynomial.quotient(modulus, field?) constructs modulus.degree(). A ring created from an existing polynomial retains that polynomial's coefficient field.
Polynomial.setField('Z2');
const ring = Polynomial.quotient('x^3+x+1');
const alpha = ring.create('x');
console.log(alpha.pow(3).toString()); // x+1
console.log(alpha.pow(7).toString()); // 1
console.log(alpha.inverse().toString()); // x^2+1The ring provides create(value), zero(), one(), and its monic modulus. evaluate(polynomial, value) evaluates a polynomial at a residue class using Horner's method, while isRoot(polynomial, value) tests whether that value is a root. Residue classes provide add, sub, mul, div, neg, pow, inverse, equals, and isZero. Division and inversion throw RangeError when the divisor is not a unit. Residues from different quotient-ring instances cannot be combined.
Polynomial.setField('Z2');
const ring = Polynomial.quotient('x^3+x+1');
const alpha = ring.create('x');
console.log(ring.evaluate('x^2+x+1', alpha).toString()); // x^2+x+1
console.log(ring.isRoot('x^3+x+1', alpha)); // trueUnlike Polynomial.eval(), which returns a coefficient-field value, ring.evaluate() returns a residue class. Evaluation is defined for every quotient ring and does not require the modulus to be irreducible. Values already belonging to another quotient-ring instance are rejected.
When the coefficient field is Zp and the modulus is irreducible, the quotient is the finite field isField() checks this condition, and order() returns elements(), primitiveElements(), and primitivePolynomials(). A residue class provides additiveOrder(), additiveSubgroup(), multiplicativeOrder(), multiplicativeSubgroup(), isPrimitive(), and minimalPolynomial().
Polynomial.setField('Z2');
const field = Polynomial.quotient('x^4+x+1');
const alpha = field.create('x');
const beta = alpha.pow(5);
console.log(field.order()); // 16
console.log(alpha.multiplicativeOrder()); // 15
console.log(alpha.isPrimitive()); // true
console.log(beta.multiplicativeSubgroup().map(String));
// ['1', 'x^2+x', 'x^2+x+1']
console.log(beta.minimalPolynomial().toString()); // x^2+x+1
console.log(field.primitivePolynomials().map(String));
// ['x^4+x+1', 'x^4+x^3+1']primitivePolynomials() returns the distinct minimal polynomials of the primitive elements, ordered by finite-field coefficient order. Frobenius conjugates are deduplicated, so each primitive polynomial occurs exactly once.
The subgroup methods list the identity first, followed by successive multiples or powers of the residue. Their optional limit defaults to one million elements. elements(maxElements?), primitiveElements(maxElements?), and primitivePolynomials(maxElements?) use the same default and throw RangeError before enumeration if the field is larger. Multiplicative operations and minimal polynomials require an irreducible modulus; ordinary quotient-ring arithmetic does not.
factor() and isIrreducible() operate over prime coefficient fields Zp. Factorization uses square-free decomposition, Frobenius roots for inseparable polynomials, and Berlekamp's algorithm. Repeated factors occur repeatedly in the returned array.
Polynomial.setField('Z2');
const polynomial = new Polynomial('x^5+x^2+x+1');
console.log(polynomial.factor().map(String));
// ['x+1', 'x+1', 'x^3+x+1']For a non-monic polynomial, the leading unit is the first factor, so multiplying all returned factors reconstructs the input. The factorization of 1 is empty, while factoring the zero polynomial throws RangeError. Fields of characteristic zero and composite modular rings are rejected.
If g.cyclicCodeBasis(n) returns the standard basis
Polynomial.setField('Z2');
const generator = new Polynomial('x^4+x^3+x^2+1');
console.log(generator.cyclicCodeBasis(7).map(String));
// ['x^4+x^3+x^2+1', 'x^5+x^4+x^3+x', 'x^6+x^5+x^4+x^2']The generator is normalized to monic form. Invalid lengths, the zero polynomial, and generators that do not divide
Polynomial.cyclicCodeGenerators(n, k) returns every monic divisor of cyclicCodeBasis(n).
Polynomial.setField('Z2');
const generators = Polynomial.cyclicCodeGenerators(7, 4);
console.log(generators.map(String));
// ['x^3+x^2+1', 'x^3+x+1']
console.log(generators[0].cyclicCodeBasis(7).length); // 4Repeated irreducible factors are retained when the characteristic divides the code length. Dimensions from zero through
compare() and successor() use base-$p$ coefficient order with the constant coefficient as the least significant digit. Polynomial.enumerate(d) returns all
Polynomial.setField('Z2');
console.log(Polynomial.enumerate(1).map(String));
// ['0', '1', 'x', 'x+1']
console.log(new Polynomial('x+1').successor().toString()); // x^2Enumeration defaults to at most one million results. Pass the limit as the second argument when using the active field, or as the third argument when providing a custom field.
degree()returns the polynomial degree, or-Infinityfor the zero polynomial.lc()returns the leading coefficient, orundefinedfor the zero polynomial.lm()returns the leading monomial.equals(value)compares normalized coefficients in the current field.isZero()reports whether the polynomial is zero.isMonic()reports whether the leading coefficient is one.toArray(width?)returns coefficients in ascending exponent order, optionally padded with zeros.clone()returns an independent polynomial with the same coefficients and field.toString()returns the plain-text representation.toLatex()returns a LaTeX representation.toHorner()returns a Horner-scheme representation.coeffexposes the coefficient map indexed by exponent.
const polynomial = new Polynomial('23x^4+98x^2+4');
console.log(polynomial.coeff[4]); // 23
console.log(polynomial.degree()); // 4
console.log(polynomial.toArray()); // [4, 0, 98, 0, 23]Set Polynomial.trace to true before division to retain the subtracted terms and final remainder. The property is replaced with an array of polynomial instances.
Polynomial.trace = true;
new Polynomial('x^4+3x^3+2x^2+6x').div('x+3');
console.log(Polynomial.trace.map(step => step.toString()));
// ['x^4+3x^3', '2x^2+6x', '0']
Polynomial.trace = null;The browser builds include all built-in coefficient implementations. No separate Fraction.js, Complex.js, or Quaternion.js scripts are required.
Use the global IIFE build directly:
<script src="https://unpkg.com/polynomial@2.0.0/dist/polynomial.min.js"></script>
<script>
Polynomial.setField('C');
console.log(new Polynomial('x-i').mul('x+i').toString());
</script>Or import the standalone browser ES module:
import Polynomial from 'https://unpkg.com/polynomial@2.0.0/dist/polynomial.min.mjs';AMD loaders receive the class as an anonymous module from polynomial.min.js.
The package ships declarations for ESM and CommonJS. ESM exposes the class as both the default and named Polynomial export, together with BuiltInField, PolynomialField, CoefficientMap, PolynomialInput, PolynomialQuotientRing, PolynomialResidue, and PolynomialResidueInput types.
CommonJS namespace aliases are available as Polynomial.FieldName, Polynomial.Field<C>, Polynomial.Coefficients<C>, Polynomial.Input<C>, Polynomial.QuotientRing<C>, Polynomial.Residue<C>, and Polynomial.ResidueInput<C>.
Malformed polynomial syntax throws SyntaxError. Invalid fields and exponents, division by zero, and non-invertible modular coefficients throw RangeError or TypeError as appropriate.
npm install
npm testnpm test performs a strict TypeScript build, checks ESM and CommonJS declaration consumers, and runs the Node.js and standalone browser tests.
Copyright (c) 2026 Robert Eisele
Licensed under the MIT license.