ring_math/
polynomial.rs

1use std::fmt::Debug;
2
3use scalarff::FieldElement;
4
5use crate::Vector;
6
7/// A univariate polynomial with coefficients in a field
8///
9/// The base field may be finite or infinite depending
10/// on T
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12#[derive(Clone, Debug, Eq)]
13pub struct Polynomial<T>
14where
15    T: FieldElement,
16{
17    pub coefficients: Vec<T>, // len() <= degree, non-existent elements assumed to be zero
18}
19
20impl<T: FieldElement> Polynomial<T> {
21    pub fn new(coefficients: Vec<T>) -> Self {
22        Self { coefficients }
23    }
24
25    /// Return the zero polynomial
26    pub fn zero() -> Self {
27        Self {
28            coefficients: vec![],
29        }
30    }
31
32    /// Return the identity polynomial
33    pub fn identity() -> Self {
34        Self {
35            coefficients: vec![T::one()],
36        }
37    }
38
39    /// Returns true if `self` is the zero polynomial
40    pub fn is_zero(&self) -> bool {
41        if self.coefficients.is_empty() {
42            return true;
43        }
44        for v in &self.coefficients {
45            if *v != T::zero() {
46                return false;
47            }
48        }
49        true
50    }
51
52    /// Do a scalar multiplication in place
53    pub fn mul_scalar(&mut self, v: &T) {
54        for i in 0..self.coefficients.len() {
55            self.coefficients[i] *= v.clone();
56        }
57    }
58
59    /// Return the constant term of the polynomial.
60    /// This is the coefficient of the x^0 term.
61    pub fn constant_term(&self) -> T {
62        if self.coefficients.is_empty() {
63            T::zero()
64        } else {
65            self.coefficients[0].clone()
66        }
67    }
68
69    /// Return a coefficient vector with trailing zero
70    /// coefficients removed.
71    ///
72    /// out.len() == self.degree() + 1
73    pub fn coef_vec(&self) -> Vector<T> {
74        let mut out = Vector::new();
75        for i in 0..(self.degree() + 1) {
76            out.push(self.coefficients[i].clone());
77        }
78        out
79    }
80
81    /// Add a term to the polynomial
82    pub fn term(&mut self, coef: &T, exp: usize) {
83        if self.coefficients.len() < exp + 1 {
84            self.coefficients.resize(exp + 1, T::zero());
85        }
86        self.coefficients[exp] += coef.clone();
87    }
88
89    /// Remove the highest degree term from the polynomial and return it
90    pub fn pop_term(&mut self) -> (T, usize) {
91        for i in 0..self.coefficients.len() {
92            let index = self.coefficients.len() - (i + 1);
93            let v = self.coefficients[index].clone();
94            if v != T::zero() {
95                self.coefficients[index] = T::zero();
96                return (v, index);
97            }
98        }
99        (T::zero(), 0)
100    }
101
102    /// Return the degree of the polynomial. e.g. the degree of the largest
103    /// non-zero term
104    pub fn degree(&self) -> usize {
105        for i in 0..self.coefficients.len() {
106            let index = self.coefficients.len() - (i + 1);
107            if self.coefficients[index] != T::zero() {
108                return index;
109            }
110        }
111        0
112    }
113
114    /// a fast method for multiplying by a single term polynomial
115    /// with a coefficient of 1
116    /// e.g. multiplying by x^5
117    pub fn shift_and_clone(&self, degree: usize) -> Self {
118        let mut shifted_coefs = vec![T::zero(); degree];
119        shifted_coefs.extend(self.coefficients.clone());
120        Self {
121            coefficients: shifted_coefs,
122        }
123    }
124
125    /// return q, r such that self = q * divisor + r
126    /// divisor must not be the zero polynomial
127    pub fn div(&self, divisor: &Self) -> (Self, Self) {
128        if divisor.is_zero() {
129            panic!("divide by zero");
130        }
131        if self.degree() < divisor.degree() {
132            return (Self::zero(), self.clone());
133        }
134        let mut dclone = divisor.clone();
135        let mut quotient = Self::zero();
136        let (divisor_term, divisor_term_exp) = dclone.pop_term();
137        let divisor_term_inv = T::one() / divisor_term.clone();
138        let mut remainder = self.clone();
139        while !remainder.is_zero() && remainder.degree() >= divisor.degree() {
140            let (largest_term, largest_term_exp) = remainder.clone().pop_term();
141            let new_coef = largest_term * divisor_term_inv.clone();
142            let new_exp = largest_term_exp - divisor_term_exp;
143            quotient.term(&new_coef, new_exp);
144            let mut t = divisor.shift_and_clone(new_exp);
145            t.mul_scalar(&new_coef);
146            remainder = remainder - t;
147        }
148        (quotient, remainder)
149    }
150}
151
152impl<T: FieldElement> std::fmt::Display for Polynomial<T> {
153    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154        if self.is_zero() {
155            write!(f, "0")
156        } else {
157            write!(
158                f,
159                "{}",
160                self.coefficients
161                    .iter()
162                    .enumerate()
163                    .map(|(i, v)| {
164                        if *v == T::zero() && i > 0 {
165                            return "".to_string();
166                        }
167                        if i > 0 {
168                            format!("{}x^{i}", v.to_string())
169                        } else {
170                            v.to_string()
171                        }
172                    })
173                    .filter(|x| x.len() > 0)
174                    .collect::<Vec<_>>()
175                    .join(" + ")
176            )
177        }
178    }
179}
180
181impl<T: FieldElement> std::ops::Add for Polynomial<T> {
182    type Output = Self;
183
184    fn add(self, other: Self) -> Self {
185        let max_len = if self.coefficients.len() > other.coefficients.len() {
186            self.coefficients.len()
187        } else {
188            other.coefficients.len()
189        };
190        let mut coefficients = vec![T::zero(); max_len];
191        for x in 0..max_len {
192            if x < self.coefficients.len() {
193                coefficients[x] += self.coefficients[x].clone();
194            }
195            if x < other.coefficients.len() {
196                coefficients[x] += other.coefficients[x].clone();
197            }
198        }
199        Polynomial { coefficients }
200    }
201}
202
203impl<T: FieldElement> std::ops::Sub for Polynomial<T> {
204    type Output = Self;
205
206    fn sub(self, other: Self) -> Self {
207        let max_len = if self.coefficients.len() > other.coefficients.len() {
208            self.coefficients.len()
209        } else {
210            other.coefficients.len()
211        };
212        let mut coefficients = vec![T::zero(); max_len];
213        for x in 0..max_len {
214            if x < self.coefficients.len() {
215                coefficients[x] += self.coefficients[x].clone();
216            }
217            if x < other.coefficients.len() {
218                coefficients[x] -= other.coefficients[x].clone();
219            }
220        }
221        Polynomial { coefficients }
222    }
223}
224
225impl<T: FieldElement> std::ops::Mul for Polynomial<T> {
226    type Output = Self;
227
228    fn mul(self, other: Self) -> Self {
229        let mut coefficients = vec![T::zero(); self.coefficients.len() + other.coefficients.len()];
230        for i in 0..other.coefficients.len() {
231            for j in 0..self.coefficients.len() {
232                // combine the exponents
233                let e = j + i;
234                // combine with existing coefficients
235                coefficients[e] += self.coefficients[j].clone() * other.coefficients[i].clone();
236            }
237        }
238        // TODO: remove this trimming, it's only necessary
239        // because of hacky compatiblity between ashlang serialization
240        // and PolynomialRingElement serialization
241        let mut max = coefficients.len();
242        for i in (0..coefficients.len()).rev() {
243            if coefficients[i] != T::zero() {
244                break;
245            }
246            max = i;
247        }
248        Polynomial {
249            coefficients: coefficients[0..max].to_vec(),
250        }
251    }
252}
253
254impl<T: FieldElement> std::ops::Neg for Polynomial<T> {
255    type Output = Self;
256
257    fn neg(self) -> Self {
258        Polynomial {
259            coefficients: self.coefficients.iter().map(|v| -v.clone()).collect(),
260        }
261    }
262}
263
264impl<T: FieldElement> std::cmp::PartialEq for Polynomial<T> {
265    fn eq(&self, other: &Self) -> bool {
266        if self.degree() != other.degree() {
267            return false;
268        }
269        for i in 0..self.degree() {
270            if self.coefficients[i] != other.coefficients[i] {
271                return false;
272            }
273        }
274        true
275    }
276}
277
278impl<T: FieldElement> std::hash::Hash for Polynomial<T> {
279    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
280        // hash only the non-zero coefficients
281        for i in 0..self.degree() {
282            self.coefficients[i].hash(state);
283        }
284    }
285}
286
287#[cfg(test)]
288mod test {
289    use super::Polynomial;
290    use scalarff::FieldElement;
291    use scalarff::OxfoiFieldElement;
292
293    #[test]
294    #[cfg(feature = "rand")]
295    fn mul_div() {
296        for _ in 0..100 {
297            let mut r = rand::thread_rng();
298            let p1 = Polynomial {
299                coefficients: vec![
300                    OxfoiFieldElement::sample_uniform(&mut r),
301                    OxfoiFieldElement::sample_uniform(&mut r),
302                    OxfoiFieldElement::sample_uniform(&mut r),
303                    OxfoiFieldElement::sample_uniform(&mut r),
304                    OxfoiFieldElement::sample_uniform(&mut r),
305                ],
306            };
307            let p2 = Polynomial {
308                coefficients: vec![
309                    OxfoiFieldElement::sample_uniform(&mut r),
310                    OxfoiFieldElement::sample_uniform(&mut r),
311                    OxfoiFieldElement::sample_uniform(&mut r),
312                ],
313            };
314            let (q, r) = p1.div(&p2);
315            assert!(!q.is_zero());
316            assert!(!p1.is_zero());
317            assert!(!p2.is_zero());
318            assert_eq!(q * p2 + r, p1);
319        }
320    }
321}