Polynomial factorization and roots calculator
This JavaScript/WebAssembly application can evaluate and factor polynomial expressions modulo a prime number or a power of a prime number. It can also evaluate, factor and find exact roots of integer polynomials by entering zero in the Modulus input box.
You can enter polynomials quickly by using dot notation. For example, to enter 2x^{4} + 3x^{2} + 1, you can type: 2.4 + 3.2 + 1
Integer polynomials in one variable are expressions that include a variable named x, integer numbers and the addition, subtraction and multiplication operators.
They can be expressed in the form: f(x) = f_{0} + f_{1}x + f_{2}x^{2} + ... + f_{n}x^{n}.
The number n is the degree of the polynomial. The coefficient f_{n} is the leading coefficient and the coefficient f_{0} is the trailing coefficient. They can be written as deg(f), lc(f) and tc(f) respectively.
Let f(x) and g(x) be two polynomials such that deg(f) ≥ deg(g). We can define addition, subtraction and multiplication as follows:
f(x) + g(x) = h(x) means h_{i} = f_{i} + g_{i} if i ≤ deg(g) and h_{i} = f_{i} otherwise.
f(x) − g(x) = h(x) means h_{i} = f_{i} − g_{i} if i ≤ deg(g) and h_{i} = f_{i} otherwise.
g(x) − f(x) = h(x) means h_{i} = g_{i} − f_{i} if i ≤ deg(g) and h_{i} = −f_{i} otherwise.
f(x) g(x) = h(x) means h_{i} = f_{0} g_{i} + f_{1} g_{i − 1} + ... + f_{i} g_{0} if i ≤ deg(g), h_{i} = f_{i − deg(g)} g_{deg(g)} + f_{i + 1 − deg(g)} g_{deg(g) − 1} + ... + f_{i} g_{0} otherwise.
The factorization of integer polynomials is a process to find one or more irreducible polynomials whose product is the original polynomial. An irreducible polynomial cannot be expressed as a product of two or more integer polynomials.
For example: x^{4} − 1 = (x^{2} + 1) (x + 1) (x − 1)
It can be shown that any integer polynomial can be factored in only one way in irreducible polynomials.
Multiplication of polynomials modulo a prime number can be performed in the usual way by multiplying the different terms of the polynomial and then adding the coefficients of the same degree. Finally the coefficients are reduced modulo that prime.
For example, the product of 3x^{2} + 5x + 1 and 6x^{2} + 4x + 3 modulo 7 is 18x^{4} + (30+12)x^{3} + (9+20+6)x^{2} + (15+4)x + 3 modulo 7 which equals 4x^{4} + 5x + 3
It can be shown that the polynomials modulo a prime can be factored into the leading coefficient and monic prime polynomials in only one way (monic polynomials have the leading coefficient equal to one.)
It can also be shown that if there are no repeated factors, the polynomial can be factored modulo a power of that prime in only one way.
Use the upper input box to enter the polynomial expression and the lower input box to enter a numerical expression for the modulus, which must be either zero or an integer number greater than 1 of the form prime number raised to an exponent. You can just evaluate the polynomial or evaluate and factor it, by pressing the corresponding button.
Example for integer polynomial factorization: to factor x^{30} − 1, type xcaret 30minus 1 in the upper input box and 0 in the lower input box. Then press the factor button.
Example for polynomial modulo a prime factorization: copy any of the expressions below in the upper input box and type the number 211 (a prime number) in the lower input box. Then press the button "Factor expression".
- 6x caret 8+x caret 5+3
- 2*left parenthesis x+6right parenthesis *left parenthesis x-5right parenthesis +xx caret 4+23x
The exponentiation symbol is not present in some mobile devices, so two asterisks ** can by typed as the exponentiation operator. So, equivalent expressions would be:
- 6x**8+x**5+3
- 2*left parenthesis x+6right parenthesis *left parenthesis x-5right parenthesis +xx**4+23x
You can type a dot (.) and the application will replace it by "x caret ". This saves a lot of time in mobile devices because there is no need to switch between alphabetical and numerical keyboards for simple polynomials.
For the first example you would type:
- 6.8+.5+3
Notice that factoring large degree polynomials will take a lot of time. That's why the applet accepts polynomials of degree up to 1000.
The Pretty print checkbox can be used to see the exponents in the usual superscript notation (when the checkbox is checked) or preceded by the caret character (when it is unchecked). It can be also be used to see the radicands in the traditional notation. The first option is used to see the factorization on the display or print it. The second option is used to copy the data to another application.
After the applet finishes factoring, it multiplies the factors in order to validate if they are equal to the input polynomial.
When the modulus box is zero, the application finds the roots of the polynomial factors using radical expressions. The program supports degrees up to 5. By Abel-Ruffini theorem, some polynomials of degree 5 cannot be expressed by radical expressions.
You can also enter expressions that use the following operators and parentheses:
- plus sign+ for addition
- minus sign- for subtraction
- asterisk* for multiplication
- slash/ for integer division
- percent symbol% for remainder
- caret or two asterisks for exponentiation
Operators and functions for the polynomial (upper box) only:
- Gcd(f,g): Greatest common divisor of these two polynomials.
- Der(f): Derivative of this polynomial.
Operators and functions for the modulus (lower box) only:
- <, ==, >; <=, >=, != for comparisons. The operators return zero for false and -1 for true.
- AND, OR, XOR, NOT for binary logic. The operations are done in binary (base 2). Positive (negative) numbers are prepended with an infinite number of bits set to zero (one).
- SHL: When b ≥ 0, a SHL b shifts a left the number of bits specified by b. This is equivalent to a × 2^{b}. Otherwise, a SHL b shifts a right the number of bits specified by −b. This is equivalent to floor(a / 2^{−b}). Example: 5 SHL 3 = 40.
- SHR: When b ≥ 0, a SHR b shifts a right the number of bits specified by b. This is equivalent to floor(a / 2^{b}). Otherwise, a SHR b shifts a left the number of bits specified by −b. This is equivalent to a × 2^{−b}. Example: -19 SHR 2 = -5.
- n!: factorial (n must be greater than or equal to zero). Example: 6! = 6 × 5 × 4 × 3 × 2 = 720.
- p#: primorial (product of all primes less or equal than p). Example: 12# = 11 × 7 × 5 × 3 × 2 = 2310.
- B(n): Previous probable prime before n. Example: B(24) = 23.
- F(n): Fibonacci number F_{n} from the sequence 0, 1, 1, 2, 3, 5, 8, 13, 21, etc. where each element equals the sum of the previous two members of the sequence. Example: F(7) = 13.
- L(n): Lucas number L_{n} = F_{n-1} + F_{n+1}
- N(n): Next probable prime after n. Example: N(24) = 29.
- P(n): Unrestricted Partition Number (number of decompositions of n into sums of integers without regard to order). Example: P(4) = 5 because the number 4 can be partitioned in 5 different ways: 4 = 3+1 = 2+2 = 2+1+1 = 1+1+1+1.
- Gcd(m,n): Greatest common divisor of these two integers. Example: GCD(12, 16) = 4.
- Modinv(m,n): inverse of m modulo n, only valid when m and n are coprime, menaning that they do not have common factors. Example: Modinv(3,7) = 5 because 3 × 5 ≡ 1 (mod 7)
- Modpow(m,n,r): finds m^{n} modulo r. Example: Modpow(3, 4, 7) = 4, because 3^{4} ≡ 4 (mod 7).
- IsPrime(n): returns zero if n is not probable prime, -1 if it is. Example: IsPrime(5) = -1.
- NumDigits(n,r): Number of digits of n in base r. Example: NumDigits(13, 2) = 4 because 13 in binary (base 2) is expressed as 1101.
- SumDigits(n,r): Sum of digits of n in base r. Example: SumDigits(213, 10) = 6 because the sum of the digits expressed in decimal is 2+1+3 = 6.
- RevDigits(n,r): finds the value obtained by writing backwards the digits of n in base r. Example: RevDigits(213, 10) = 312.
You can use the prefix 0x for hexadecimal numbers, for example 0x38 is equal to 56.
Polynomials modulo a prime number
The polynomial division requires multiple modular divisions where the divisor is the leading coefficient of the divisor polynomial.
To compute the modular division a / b (mod p), first the modular multiplicative inverse c is found. This number has the property bc ≡ 1 (mod p) and it can be found using the extended Euclidean algorithm as follows:
function modInv(value, modulus)
{
V1 ← 1; V3 ← value;
U1 ← 0; U3 ← modulus;
while V3 ≠ 0
{
QQ ← U3 / V3;
T1 ← U1 − V1 * QQ;
T3 ← U3 − V3 * QQ;
U1 ← V1; U3 ← V3;
V1 ← T1; V3 ← T3;
}
return U1;
}
Then the division is equal to the product ac (mod p).
To minimize the number of modular divisions (which are expensive), we can multiply all coefficients of both polynomials (dividend and divisor) by the multiplicative inverse of the leading coefficient of the divisor polynomial. In this way, we will divide by a monic polynomial, that is, a polynomial whose leading coefficient equals 1. This will not change the quotient, but the remainder will need to be multiplied by the leading coefficient of the divisor polynomial.
Example: perform the division (3x^{3} + 7x^{2} + 5x + 6) / (4x^{2} + 3x + 10) (mod 11):
First we have to find the multiplicative inverse of 4 (mod 11), which equals 3, because 4 × 3 = 12 ≡ 1 (mod 11). Multiplying all coefficients by 3 we get:
(9x^{3} + 10x^{2} + 4x + 7) / (x^{2} + 9x + 8) (mod 11)
Dividing the leading coefficient of the dividend polynomial over the leading coefficient of the divisor polynomial: 9x^{3} / x^{2} ≡ 9x (mod 11).
Now we subtract the product of what we have just found by the divisor polynomial from the dividend polynomial. We obtain:
9x^{3} + 10x^{2} + 4x + 7 - 9x(x^{2} + 9x + 8) ≡ 6x^{2} + 9x + 7 (mod 11). Notice that 10 − 9 × 9 ≡ 6 (mod 11) and 4 − 9 × 8 (mod 11) ≡ 9 (mod 11).
Dividing the leading coefficient of the remainder polynomial we just found over the leading coefficient of the divisor polynomial: 6x^{2} / x^{2} ≡ 6 (mod 11).
Now we subtract the product of what we have just found by the divisor polynomial from the remainder polynomial. We obtain:
(6x^{2} + 9x + 7) − 6(x^{2} + 9x + 8) ≡ 10x + 3 (mod 11). Notice that 9 − 6 × 9 ≡ 10 (mod 11) and 7 − 6 × 8 ≡ 3 (mod 11).
The quotient polynomial equals 9x + 6 and the remainder polynomial equals 4(10x + 3) ≡ 7 x + 1 (mod 11).
Integer polynomials
The division proceeds in the same way as in the previous subsection, with the difference that there is no multiplicative inverse of the leading coefficient of the divisor polynomial. So for each iteration of the algorithm, a division is required. If the divisor polynomial is not monic, sometimes the division cannot be performed. This occurs when the leading coefficient of the remainder is not multiple of the leading coefficient of the divisor.
The greatest common divisor of two polynomials is the polynomial of the highest possible degree, that divides both polynomials.
For example: gcd(x^{2} + 6x + 5, 2x^{2} + 13x + 15) = x + 5
Polynomials modulo a prime number
The greatest common divisor can be found using the Euclidean algorithm as follows:
function gcdm(poly1, poly2, p)
{
a ← poly1;
b ← poly2;
while b ≠ 0
t ← b;
b ← remainder(a, b) (mod p);
a ← t;
return a;
}
Integer polynomials
While the same algorithm could be used for finding the gcd of two integer polynomials, the coefficients of the intermediate polynomials increase very quickly, so this is not useful.
There are two methods to find the gcd: the subresultant algorithm and the modular algorithm. The latter, invented by William Brown, uses gcd of polynomials modulo a prime, so this is better for our purposes.
function gcdp(poly1, poly2)
{
c_{1} ← cont(poly1);
c_{2} ← cont(poly2);
c ← gcd(c_{1}, c_{2});
a ← poly1 / c_{1};
b ← poly2 / c_{2};
h ← gcd(lc(poly1), lc(poly2));
d ← min(deg(poly1), deg(poly2));
m ← 1;
g_{m} ← 0;
repeat
{
do
{
do
{
p ← nextPrime();
} while remainder(m*h, p) = 0;
r ← poly1 (mod p);
s ← poly2 (mod p);
g_{p} ← gcdm(r, s, p);
if deg(g_{p}) = 0
{
output c; stop;
}
} while deg(g_{p}) > d;
g_{p} ← (h mod p)/lc(g_{p}) * g_{p};
if deg(g_{p}) < d
{
m ← 1;
g_{m} ← 0;
d ← deg(g_{p});
}
h ← CRA([g_{p}, p], [g_{m}, m]);
if h = g_{m}
{
h ← h / cont(h);
if remainder(a, h) = 0 and remainder(b, h) = 0
{
output c*h; stop;
}
}
m ← p * m;
g_{m} ← h;
}
}
The content of a polynomial is the greatest common divisor of all coefficients of that polynomial. The sign of the content matches the sign of the leading coefficient. It is expressed as cont(f) in the previous algorithm.
The coefficients of the result of the Chinese Remainder Algorithm (function CRA) computed modulo mp, must be in the range −mp/2 to mp/2.
The idea behind this algorithm is to compute several gcd of the input polynomials modulo different primes. We can see that their degrees are greater or equal than the degree of the polynomial gcd.
There are three cases:
- The degree of the modular gcd is greater than the degree of previous modular gcd: the new result is discarded because it has incorrect degree.
- The degree of the modular gcd is less than the degree of previous modular gcd: the old result is discarded because it has incorrect degree. The new result is used instead.
- Both degrees are equal: the coefficients of the both gcds are merged using the Chinese Remainder Theorem. This algorithm computes g (mod mp) given g_{m} (mod m) and g_{p} (mod p).
The algorithm continues until the computed polynomial divides both input polynomials.
The factoring algorithm can be divided in four steps: square-free factorization, distinct degree factorization, equal degree factorization and factor lift. All steps require monic polynomials, so before starting, we will have to multiply all coefficients by the modular multiplicative inverse of the leading coefficient of the polynomial.
Square-free factorization
The next steps do not work if there are repeated factors, so the first step is to factor the polynomial in such a way that there are no repeated factors.
The derivative of the polynomial f(x) = f_{0} + f_{1}x + f_{2}x^{2} + ... + f_{n}x^{n} is f'(x) = f_{1} + 2f_{2}x + ... + nf_{n}x^{n − 1}
The recursive algorithm is:
function SFF(f)
{
i ← 1; g ← f';
if g ≠ 0
{
c ← gcd(f, g);
w ← f / c;
while w ≠ 1
{
y ← gcd(w, c); z ← w / y;
Output(z^{i}); i ← i + 1;
w ← y; c ← c / y;
}
if c ≠ 1
{
c ← c^{1/p};
Output(SFF(c)^{p});
}
}
else
{
f ← f^{1/p};
Output(SFF(f)^{p});
}
}
For each polynomial on the output of this algorithm we have to perform the next steps.
Distinct degree factorization
This is a method that exploits the fact that the product of all monic irreducible polynomials of degrees that divide d (mod p) equals x^{e} − x where e = p^{d}.
function DDF(f, p)
{
i ← 1;
h ← f;
j ← x;
q ← 0;
while deg(h) ≥ 2i
{
j ← j^{p} (mod h);
g ← gcdm(h, j − x);
if g ≠ 1
{
Output(g, i);
q ← 1;
h ← h / g;
}
}
if h ≠ 1
{
Output(h, deg(h));
q ← 1;
}
if q = 0
{
Output(f, 1);
}
}
The pairs that form the output of this function are the input for the next step. The first element of the pair is a factor of f which is the product of all factors of the degree specified in the second element of the pair.
Equal degree factorization
This is a probabilistic method due to David Cantor and Hans Zassenhaus that finds all factors of the same degree from the output of the previous algorithm:
function EDF(f, d, p)
{
n ← deg(f);
set list of factors to f;
while size(list of factors) < n/d
{
h ← random polynomial with deg(h) < n;
e ← (q^{d} − 1) / 2;
g ← h^{e} − 1 (mod f);
for each element u of list of factors
{
if deg(u) > d
{
j ← gcdm(g, u);
if j ≠ 1 and j ≠ u
{
discard u from list of factors;
add j and u/j to list of factors;
}
}
}
}
Output list of factors
}
Polynomial lift
Once all irreducible factors of the polynomial modulo p are found, we can find the factor of the polynomial modulo p^{n} if there are no repeated factors. The following algorithm can lift from modulo m to m^{2}, so we can execute it several times until the complete lifting is done.
On input: f = g*h (mod m), s*g + t*h = 1 (mod m)
On output: f = g'*h' (mod m^{2}), s'*g' + t'*h' = 1 (mod m^{2}) with deg(s') < deg(h'), deg(t') < deg(g')
All calculations below are performed mod m^{2}.
e ← f - g*h
Compute q, r such that s*e = q*h + r
g' ← g + t*e + q*g
h' ← h + r
e ← s*g' + t*h' − 1
Compute q, r such that s*e = q*h + r
s' ← s − r
t' ← t − t*e − q*g'
The initial values of s and t are obtained from g and h using Extended Euclidean algorithm.
We can use the output of the previous section to factor integer polynomials.
First of all we need to divide the polynomial by its content (the greatest common divisor of all coefficients with the sign of the leading coefficient). The result is the principal part, named pp(f).
To perform the square-free factorization, we can factor recursively gcd(f, f') and f/gcd(f, f').
At this moment we know that there are no repeated factors. We have to find a small prime p for which the polynomial has no repeated factors. This can be easily found by checking that gcd(f, f') ≡ 1 (mod p).
We need to find a bound of the coefficient of factors. The Knuth-Cohen bound for coefficients of polynomial factors can be computed as follows:
If polynomial G divides F we have for all j:
|G_{j}| ≤ binomial(n − 1, j)*(Σ_{i} |F_{i}|^{2})^{1/2} + binomial(n − 1, j −1) * |F_{m}|
where m is the degree of F and n is the degree of G. The maximum degree to be considered is n = ceil(m/2).
Once the bound B is found, we have to compute the least value of e such that 2B < p^{e}.
Now we factor the polynomial mod p^{e} which has r different factors. If r > 10, we can try a different value of p, so we minimize the number of factors found. The application tries up to 5 different primes.
We will now combine the factors found modulo p^{e} to get integer polynomial factors. There are 2^{r} possible combinations, but most of them can be easily discarded.
Let the combination of factors found are f_{0}, f_{1}, ..., f_{s}. Compute a ≡ lc(f) tc(f_{0}) tc(f_{1}) ... tc(f_{s}) (mod p^{e}) and b ≡ lc(f) tc(f) (mod p^{e}). In both cases the products must be in the range −p^{e}/2 to p^{e}/2. If a does not divide b, we can discard that combination.
For the very few combinations that remain, compute a ≡ lc (f) f_{0} f_{1} ... f_{s} (mod p^{e}). Again, this time the coefficients of this polynomial must be in the range −p^{e}/2 to p^{e}/2. Compute b = gcdp(a, lc(f) f). If the degree of b is zero, discard this combination. Otherwise, the polynomial b is a proper factor of f.
Even though the steps are very fast, for some polynomials the number of combinations is very huge. So the program uses Van Hoeij algorithm which uses LLL algorithm to determine quickly the combinations to use.
The program finds the roots from the irreducible polynomials obtained in the previous section.
For polynomials of degrees up to 4, the standard formulas are used. No cube roots of complex numbers are used. In these cases (named casus irreducibilis), the program uses trigonometric functions.
For polynomials of degree 5, the application uses the results of D. S. Dummit's paper Solving Solvable Quintic, Mathematics of Computation volume 57, number 195, July 1991, pp. 387-401.
The program can determine whether an irreducible polynomial is cyclotomic, i. e., a divisor of x^{n} − 1. In this case the program shows the complex roots of 1:
cosine of m times pin + i times sine of m times pin
where m and n are coprime.
If n = 2^{q} × 3^{r} × 5^{s} × 17^{t} where r < 2, s < 2 and t < 2, the program can find the roots as radical expressions.
For irreducible polynomials of the form a x^{n} + b, the roots can be computed as the product of a real number by a complex root of 1, so the method used in the previous paragraph is used.
For a x^{2n} + b x^{n} + c there are two cases. If the quadratic polynomial (discarding the variable n) has real roots, again we can use the methods of the previous paragraph. Otherwise, only trigonometric functions are shown.
For almost all polynomials with degree 5 or greater, the roots cannot be expressed as radical expressions. To detect these cases with high probability, the program uses the results of two papers about Galois theory:
- J.H. Davenport, G.C. Smith, Fast recognition of alternating and symmetric Galois groups, Journal of Pure and Applied Algebra 153 (2000) 17-25.
- K. Conrad, Recognizing Galois groups S_{n} and A_{n}
You can download the source of the current program and the old sum polynomial factorization applet from GitHub. Notice that the source code is in C language and you need the Emscripten environment in order to generate Javascript.
Written by Dario Alpern. Last updated 12 October 2020.