polynomint/
math.rs

1use crate::Polynomial;
2
3impl Polynomial {
4    /// Gives a new polynomial equal to the old one times x.
5    ///
6    /// # Examples
7    /// ```
8    /// use polynomint::{Polynomial, poly};
9    ///
10    /// let first = poly![1, 2, 3];
11    /// let second = first.times_x();
12    ///
13    /// assert_eq!(second, poly![0, 1, 2, 3]);
14    /// ```
15    pub fn times_x(&self) -> Self {
16        let mut coeffs = vec![0];
17        coeffs.append(&mut self.coeffs.clone());
18        Self { coeffs }
19    }
20
21    /// Gives a new polynomial equal to the remainder of the old one when taken
22    /// modulo `n`.
23    ///
24    /// # Examples
25    /// ```
26    /// use polynomint::{Polynomial, poly};
27    ///
28    /// let poly = poly![6, -5, 3, -7, 4];
29    /// assert_eq!(poly.rem_euclid(2), poly![0, 1, 1, 1]);
30    /// assert_eq!(poly.rem_euclid(4), poly![2, 3, 3, 1]);
31    /// assert_eq!(poly.rem_euclid(5), poly![1, 0, 3, 3, 4]);
32    /// ```
33    pub fn rem_euclid(&self, n: isize) -> Self {
34        if self.is_zero() {
35            Polynomial::zero()
36        } else {
37            let mut coeffs = self.coeffs.clone();
38            for i in 0..=self.degree() {
39                coeffs[i as usize] = coeffs[i as usize].rem_euclid(n);
40            }
41            let mut output = Polynomial { coeffs };
42            output.reduce();
43            output
44        }
45    }
46
47    /// Creates a new polynomial which is the derivative of the old one.
48    ///
49    /// # Examples
50    /// ```
51    /// use polynomint::{Polynomial, poly};
52    ///
53    /// let poly1 = poly![1, -2, 5, 4]; // 4x^3 + 5x^2 - 2x + 1
54    /// assert_eq!(poly1.derivative(), poly![-2, 10, 12]); // deriv. is 12x^2 + 10x - 2
55    /// let poly2 = poly![192, 3, -4, -9, 0, 38]; // 38x^5 - 9x^3 - 4x^2 + 3x + 192
56    /// assert_eq!(poly2.derivative(), poly![3, -8, -27, 0, 190]); // deriv. is 190x^4 - 27x^2 - 8x + 3
57    /// ```
58    pub fn derivative(&self) -> Self {
59        if self.degree() <= 0 {
60            Self::zero()
61        } else {
62            let mut coeffs = Vec::new();
63            for i in 0..self.degree() {
64                coeffs.push((i + 1) * self.coeffs[i as usize + 1]);
65            }
66            let mut output = Self { coeffs };
67            output.reduce();
68            output
69        }
70    }
71
72    /// Plugs in a specific `isize` value `x` to the polynomial.
73    ///
74    /// # Examples
75    /// ```
76    /// use polynomint::{poly, Polynomial};
77    ///
78    /// let poly1 = poly![5,2,1];
79    /// let poly2 = poly![-5,4,-3,-1];
80    ///
81    /// assert_eq!(poly1.eval(1), 8);
82    /// assert_eq!(poly2.eval(1), -5);
83    ///
84    /// assert_eq!(poly1.eval(-2), 5);
85    /// assert_eq!(poly2.eval(-2), -17);
86    /// ```
87    pub fn eval(&self, x: isize) -> isize {
88        let mut acc = 0;
89        // take a polynomial like 5x^2 + 2x + 3: we can get this by: 0 *= x -> 0
90        //                                                             += 5 -> 5
91        //                                                             *= x -> 5x
92        //                                                             += 2 -> 5x + 2
93        //                                                             *= x -> 5x^2 + 2x
94        //                                                             += 3 -> 5x^2 + 2x + 3
95        // this motivates the loop
96        for &i in self.coeffs.iter().rev() {
97            acc *= x;
98            acc += i;
99        }
100        acc
101    }
102
103    /// Returns `true` if `x` is a root of the polynomial; otherwise returns `false`.
104    ///
105    /// # Examples
106    /// ```
107    /// use polynomint::{Polynomial, poly};
108    /// let poly = poly![-2, 1] * poly![-4, 1] * poly![3, 1];
109    ///
110    /// assert_eq!(poly, poly![24, -10, -3, 1]);
111    /// assert!(poly.has_root(2));
112    /// assert!(poly.has_root(4));
113    /// assert!(poly.has_root(-3));
114    /// assert!(!poly.has_root(1));
115    /// ```
116    pub fn has_root(&self, x: isize) -> bool {
117        self.eval(x) == 0
118    }
119
120    /// Returns `true` if `x` is a root of the polynomial taken modulo `div`; otherwise returns false.
121    ///
122    /// # Examples
123    /// ```
124    /// use polynomint::{Polynomial, poly};
125    ///
126    /// let poly = poly![-2, 1] * poly![-6, 1];
127    ///
128    /// assert_eq!(poly, poly![12, -8, 1]);
129    /// assert!(poly.has_root_mod(2, 5));
130    /// assert!(poly.has_root_mod(1, 5));
131    /// assert!(poly.has_root_mod(2, 3));
132    /// assert!(poly.has_root_mod(0, 3));
133    /// assert!(!poly.has_root_mod(4, 5));
134    /// ```
135    pub fn has_root_mod(&self, x: isize, div: isize) -> bool {
136        self.eval(x).rem_euclid(div) == 0
137    }
138
139    /// If `a` is a root of `self`, returns `Some(p)` where `self = p * (x - a)`.
140    /// (That is, if `a` is a root of `self`, this returns the result of factoring
141    /// `x - a` out of `self`.) Otherwise returns `None`.
142    ///
143    /// # Examples
144    /// ```
145    /// use polynomint::{Polynomial, poly};
146    ///
147    /// let poly = poly![12, -8, 1]; // x^2 - 8x + 12 = (x - 2)(x - 6)
148    /// assert_eq!(poly.factor_root(2), Some(poly![-6, 1]));
149    /// assert_eq!(poly.factor_root(6), Some(poly![-2, 1]));
150    /// assert_eq!(poly.factor_root(5), None);
151    /// ```
152    pub fn factor_root(&self, a: isize) -> Option<Self> {
153        // if not a root, we're done
154        if !self.has_root(a) {
155            None
156        // if polynomial is zero, everything's a root, and the factoring gives zero again
157        } else if self.is_zero() {
158            Some(Self::zero())
159        // if zero is a root, then we can just skip the constant and be done
160        } else if a == 0 {
161            Some(Self {
162                coeffs: self.iter().skip(1).copied().collect(),
163            })
164        // otherwise, we know that the last coefficient b[0] of the output
165        // will be -c[0]/a where c is self's coeff vec, and b[n] = (b[n-1] - c[n])/a
166        // in general; thus the loop below does what we want---
167        } else {
168            let mut coeffs = Vec::new();
169            // keep an accumulator,
170            let mut acc = 0;
171            for &coeff in self.iter().take(self.degree() as usize) {
172                // and at each step, subtract c[n] and divide by a
173                acc -= coeff;
174                acc /= a;
175                coeffs.push(acc);
176            }
177            Some(Self { coeffs })
178        }
179    }
180
181    /// If `a` is a root of `self` and if `p` is a prime, this returns the
182    /// result of factoring `x - a` out of `self`, if everything is considered
183    /// a polynomial with coefficients modulo `p`. Otherwise returns `None`.
184    ///
185    /// The API demands that `p` be prime because factoring gets more complicated
186    /// when the modulus is composite, like the integers mod 4---the example below,
187    /// `x^2 - 8x + 12`, just becomes `x^2`, but `x^2 = x^2 + 4x + 4 = (x + 2)^2`,
188    /// and unique factorization is lost.
189    ///
190    /// # Examples
191    /// ```
192    /// use polynomint::{Polynomial, poly};
193    ///
194    /// let poly = poly![12, -8, 1]; // x^2 - 8x + 12 = (x - 2)(x - 6)
195    ///                              // = x^2 + x (mod 3) = x(x - 2) or x(x + 1) mod 3
196    ///                              // = x^2 + 2x + 2 = (x - 2)(x + 4) or (x - 1)(x + 3) mod 5
197    /// assert_eq!(poly.factor_root_mod(2, 3), Some(poly![0, 1]));
198    /// assert_eq!(poly.factor_root_mod(0, 3), Some(poly![1, 1]));
199    /// assert_eq!(poly.factor_root_mod(1, 3), None);
200    /// assert_eq!(poly.factor_root_mod(2, 5), Some(poly![4, 1]));
201    /// assert_eq!(poly.factor_root_mod(1, 5), Some(poly![3, 1]));
202    /// assert_eq!(poly.factor_root_mod(0, 5), None);
203    ///
204    /// let poly2 = poly![1, 0, 1]; // x^2 + 1 = (x + 1)^2 mod 2
205    ///
206    /// assert_eq!(poly2.factor_root_mod(1, 2), Some(poly![1, 1]));
207    /// assert_eq!(poly2.factor_root_mod(0, 2), None);
208    /// ```
209    pub fn factor_root_mod(&self, a: isize, p: isize) -> Option<Self> {
210        // if not a root or p isn't prime, we're done
211        if !self.has_root_mod(a, p) || !Self::is_prime(p as usize) {
212            None
213        // if polynomial is zero, everything's a root, and the factoring gives zero again
214        } else if self.is_zero() {
215            Some(Self::zero())
216        // if zero is a root, then we can just skip the constant, reduce mod div, and be done
217        } else if a == 0 {
218            let mut output = Self {
219                coeffs: self.rem_euclid(p).iter().skip(1).copied().collect(),
220            };
221            output.reduce();
222            Some(output)
223        // otherwise, we know that the last coefficient b[0] of the output
224        // will be -c[0]/a where c is self's coeff vec, and b[n] = (b[n-1] - c[n])/a
225        // in general; thus the loop below does what we want---
226        } else {
227            let mut coeffs = Vec::new();
228            // keep an accumulator,
229            let mut acc = 0;
230            for &coeff in self.rem_euclid(p).iter().take(self.degree() as usize) {
231                // and at each step, subtract c[n] and divide by a
232                acc -= coeff;
233                acc *= Self::inv_mod_p(a.rem_euclid(p), p);
234                coeffs.push(acc.rem_euclid(p));
235            }
236            Some(Self { coeffs })
237        }
238    }
239
240    fn is_prime(p: usize) -> bool {
241        if p == 2 || p == 3 {
242            true
243        } else if p == 1 || p % 2 == 0 || p % 3 == 0 {
244            false
245        } else {
246            // we need only search for prime factors up to the sqrt of n;
247            // every prime past 3 is either 1 or 5 mod 6, so we can quickly
248            // reduce our search space to a size of approx sqrt(n)/3
249            for i in (5..((p as f64).sqrt().floor() as usize)).filter(|&x| x % 6 == 1 || x % 6 == 5)
250            {
251                if p % i == 0 {
252                    return false;
253                }
254            }
255            true
256        }
257    }
258
259    fn inv_mod_p(a: isize, p: isize) -> isize {
260        let mut r_pair = (a, p);
261        let mut s_pair = (1, 0);
262        while r_pair.1 != 0 {
263            let quot = r_pair.0 / r_pair.1;
264            r_pair = (r_pair.1, r_pair.0 - quot * r_pair.1);
265            s_pair = (s_pair.1, s_pair.0 - quot * s_pair.1);
266        }
267        s_pair.0.rem_euclid(p)
268    }
269}