Skip to main content

falcon_rust/
polynomial.rs

1use falcon_profiler::profiling;
2use num::{One, Zero};
3use shake::{ExtendableOutput, Shake256, Update, XofReader};
4use std::default::Default;
5use std::fmt::{Debug, Display};
6use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign};
7
8use itertools::Itertools;
9
10use crate::falcon_field::Felt;
11use crate::fixed_point::FixedPoint64;
12use crate::inverse::Inverse;
13
14/// Marked pub for benchmarking; not considered part of the public API.
15#[doc(hidden)]
16#[derive(Debug, Clone)]
17pub struct Polynomial<F> {
18    pub coefficients: Vec<F>,
19}
20
21impl<F> Polynomial<F>
22where
23    F: Clone,
24{
25    pub fn new(coefficients: Vec<F>) -> Self {
26        Self { coefficients }
27    }
28}
29
30impl<F> Polynomial<F>
31where
32    F: Clone + Neg<Output = F>,
33{
34    /// Compute the Hermitian adjoint of the polynomial f in the
35    /// cyclotomic ring $\mathbb{Q}[X] / \langle X^n + 1 \rangle$ where $n \geq \deg(f)+1$.
36    /// In this structure, the Hermitian adjoint is given by
37    ///
38    /// $$ f*(X) = f_{[0]} + \sum_{i=1}^{n-1} f_{[i]} * X({n-i}) . $$
39    #[allow(dead_code)]
40    pub fn hermitian_adjoint(&self) -> Polynomial<F> {
41        let coefficients = [
42            vec![self.coefficients[0].clone()],
43            self.coefficients
44                .iter()
45                .skip(1)
46                .cloned()
47                .map(|c| -c)
48                .rev()
49                .collect_vec(),
50        ]
51        .concat();
52        Polynomial { coefficients }
53    }
54}
55
56#[profiling]
57fn vector_karatsuba<
58    F: Zero + AddAssign + Mul<Output = F> + Sub<Output = F> + Div<Output = F> + Clone,
59>(
60    left: &[F],
61    right: &[F],
62) -> Vec<F> {
63    let n = left.len();
64    if n <= 8 {
65        let mut product = vec![F::zero(); left.len() + right.len() - 1];
66        for (i, l) in left.iter().enumerate() {
67            for (j, r) in right.iter().enumerate() {
68                product[i + j] += l.clone() * r.clone();
69            }
70        }
71        return product;
72    }
73    let n_over_2 = n / 2;
74    let mut product = vec![F::zero(); 2 * n - 1];
75    let left_lo = &left[0..n_over_2];
76    let right_lo = &right[0..n_over_2];
77    let left_hi = &left[n_over_2..];
78    let right_hi = &right[n_over_2..];
79    let left_sum = left_lo
80        .iter()
81        .zip(left_hi)
82        .map(|(a, b)| a.clone() + b.clone())
83        .collect_vec();
84    let right_sum = right_lo
85        .iter()
86        .zip(right_hi)
87        .map(|(a, b)| a.clone() + b.clone())
88        .collect_vec();
89
90    let prod_lo = vector_karatsuba(left_lo, right_lo);
91    let prod_hi = vector_karatsuba(left_hi, right_hi);
92    let prod_mid = vector_karatsuba(&left_sum, &right_sum)
93        .iter()
94        .zip(prod_lo.iter().zip(prod_hi.iter()))
95        .map(|(s, (l, h))| s.clone() - (l.clone() + h.clone()))
96        .collect_vec();
97
98    for (i, l) in prod_lo.into_iter().enumerate() {
99        product[i] = l;
100    }
101    for (i, m) in prod_mid.into_iter().enumerate() {
102        product[i + n_over_2] += m;
103    }
104    for (i, h) in prod_hi.into_iter().enumerate() {
105        product[i + n] += h
106    }
107    product
108}
109
110#[allow(private_bounds)] // The module is marked `pub` only for benchmarking.
111impl<
112        F: Mul<Output = F> + Sub<Output = F> + AddAssign + Zero + Div<Output = F> + Inverse + Clone,
113    > Polynomial<F>
114{
115    #[profiling]
116    pub fn hadamard_mul(&self, other: &Self) -> Self {
117        Polynomial::new(
118            self.coefficients
119                .iter()
120                .zip(other.coefficients.iter())
121                .map(|(a, b)| *a * *b)
122                .collect_vec(),
123        )
124    }
125    #[profiling]
126    pub fn hadamard_div(&self, other: &Self) -> Self {
127        let other_coefficients_inverse = F::batch_inverse_or_zero(&other.coefficients);
128        Polynomial::new(
129            self.coefficients
130                .iter()
131                .zip(other_coefficients_inverse.iter())
132                .map(|(a, b)| *a * *b)
133                .collect_vec(),
134        )
135    }
136    #[profiling]
137    pub fn hadamard_inv(&self) -> Self {
138        let coefficients_inverse = F::batch_inverse_or_zero(&self.coefficients);
139        Polynomial::new(coefficients_inverse)
140    }
141}
142
143impl<F: Mul<Output = F> + Sub<Output = F> + AddAssign + Zero + Div<Output = F> + Clone>
144    Polynomial<F>
145{
146    /// Multiply two polynomials using Karatsuba's divide-and-conquer algorithm.
147    #[profiling]
148    pub fn karatsuba(&self, other: &Self) -> Self {
149        Polynomial::new(vector_karatsuba(&self.coefficients, &other.coefficients))
150    }
151}
152
153impl<F: Zero + PartialEq + Clone> Polynomial<F> {
154    pub fn degree(&self) -> Option<usize> {
155        if self.coefficients.is_empty() {
156            return None;
157        }
158        let mut max_index = self.coefficients.len() - 1;
159        while self.coefficients[max_index] == F::zero() {
160            if let Some(new_index) = max_index.checked_sub(1) {
161                max_index = new_index;
162            } else {
163                return None;
164            }
165        }
166        Some(max_index)
167    }
168    pub fn lc(&self) -> F {
169        match self.degree() {
170            Some(non_negative_degree) => self.coefficients[non_negative_degree].clone(),
171            None => F::zero(),
172        }
173    }
174}
175
176impl<F: Zero + Clone> Polynomial<F> {
177    pub fn shift(&self, shamt: usize) -> Self {
178        Self {
179            coefficients: [vec![F::zero(); shamt], self.coefficients.clone()].concat(),
180        }
181    }
182
183    pub fn constant(f: F) -> Self {
184        Self {
185            coefficients: vec![f],
186        }
187    }
188
189    pub fn map<G: Clone, C: FnMut(&F) -> G>(&self, closure: C) -> Polynomial<G> {
190        Polynomial::<G>::new(self.coefficients.iter().map(closure).collect_vec())
191    }
192
193    pub fn fold<G, C: FnMut(G, &F) -> G + Clone>(&self, mut initial_value: G, closure: C) -> G {
194        for c in self.coefficients.iter() {
195            initial_value = (closure.clone())(initial_value, c);
196        }
197        initial_value
198    }
199}
200
201/// The following implementations are specific to cyclotomic polynomial rings,
202/// i.e., F[ X ] / <X^n + 1>, and are used extensively in Falcon.
203impl<
204        F: One + Zero + Clone + Neg<Output = F> + MulAssign + AddAssign + Sub<Output = F> + PartialEq,
205    > Polynomial<F>
206{
207    /// Reduce the polynomial by X^n + 1.
208    #[profiling]
209    pub fn reduce_by_cyclotomic(&self, n: usize) -> Self {
210        let mut coefficients = vec![F::zero(); n];
211        let mut sign = -F::one();
212        for (i, c) in self.coefficients.iter().cloned().enumerate() {
213            if i % n == 0 {
214                sign *= -F::one();
215            }
216            coefficients[i % n] += sign.clone() * c;
217        }
218        Polynomial::new(coefficients)
219    }
220}
221
222/// The following implementations are specific to cyclotomic polynomial rings,
223/// i.e., F[ X ] / <X^n + 1>, and are used extensively in Falcon.
224impl<
225        F: One
226            + Zero
227            + Clone
228            + Neg<Output = F>
229            + MulAssign
230            + AddAssign
231            + Div<Output = F>
232            + Sub<Output = F>
233            + PartialEq,
234    > Polynomial<F>
235{
236    /// Compute the multiplicative inverse of the polynomial in the ring
237    /// F[ X ] / <X^n + 1>
238    ///
239    /// This function assumes that F is a field; otherwise the gcd will never end.
240    #[allow(dead_code)]
241    pub(crate) fn cyclotomic_ring_inverse(&self, n: usize) -> Self {
242        let mut cyclotomic_coefficients = vec![F::zero(); n + 1];
243        cyclotomic_coefficients[0] = F::one();
244        cyclotomic_coefficients[n] = F::one();
245        let (_, a, _) = Polynomial::xgcd(self, &Polynomial::new(cyclotomic_coefficients));
246        a
247    }
248
249    /// Compute the field norm of the polynomial as an element of the cyclotomic
250    /// ring  F[ X ] / <X^n + 1 > relative to one of half the size, i.e.,
251    ///  F[ X ] / <X^(n/2) + 1> .
252    ///
253    /// Corresponds to formula 3.25 in the spec [1, p.30].
254    ///
255    /// [1]: https://falcon-sign.info/falcon.pdf
256    ///
257    #[profiling]
258    pub fn field_norm(&self) -> Self {
259        let n = self.coefficients.len();
260        let mut f0_coefficients = vec![F::zero(); n / 2];
261        let mut f1_coefficients = vec![F::zero(); n / 2];
262        for i in 0..n / 2 {
263            f0_coefficients[i] = self.coefficients[2 * i].clone();
264            f1_coefficients[i] = self.coefficients[2 * i + 1].clone();
265        }
266        let f0 = Polynomial::new(f0_coefficients);
267        let f1 = Polynomial::new(f1_coefficients);
268        // Use Karatsuba (O(n^1.58)) rather than the naive `*` operator's O(n^2)
269        // schoolbook: these two squarings are the dominant cost of the deep
270        // NTRU-solve recursion (at n=256 the schoolbook does 256^2 allocating
271        // BigInt multiplies per call).
272        let f0_squared = f0.karatsuba(&f0).reduce_by_cyclotomic(n / 2);
273        let f1_squared = f1.karatsuba(&f1).reduce_by_cyclotomic(n / 2);
274        let x = Polynomial::new(vec![F::zero(), F::one()]);
275        f0_squared - (x * f1_squared).reduce_by_cyclotomic(n / 2)
276    }
277
278    /// Lift an element from a cyclotomic polynomial ring to one of double the
279    /// size. Do this by interleaving zeros.
280    ///
281    #[profiling]
282    pub fn lift_next_cyclotomic(&self) -> Self {
283        let n = self.coefficients.len();
284        let mut coefficients = vec![F::zero(); n * 2];
285        for i in 0..n {
286            coefficients[2 * i] = self.coefficients[i].clone();
287        }
288        Self::new(coefficients)
289    }
290
291    /// Compute the galois adjoint of the polynomial in the cyclotomic ring
292    /// F[ X ] / < X^n + 1 > , which corresponds to flipping the sign of all
293    /// odd coefficients.
294    ///
295    #[profiling]
296    pub fn galois_adjoint(&self) -> Self {
297        Self::new(
298            self.coefficients
299                .iter()
300                .enumerate()
301                .map(|(i, c)| {
302                    if i % 2 == 0 {
303                        c.clone()
304                    } else {
305                        c.clone().neg()
306                    }
307                })
308                .collect_vec(),
309        )
310    }
311}
312
313impl<
314        F: One
315            + Zero
316            + Clone
317            + Neg<Output = F>
318            + MulAssign
319            + AddAssign
320            + Div<Output = F>
321            + Sub<Output = F>
322            + PartialEq,
323    > Polynomial<F>
324{
325    /// Extended Euclidean algorithm for polynomials. Uses the EEA to compute
326    /// the greatest common divisor g and Bezout coefficients u, v such that
327    ///
328    /// $$  u a + v b = 1 . $$
329    ///
330    /// Implementation adapted from Wikipedia [1].
331    ///
332    /// [1]: https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm#Pseudocode
333    ///
334    #[profiling]
335    pub(crate) fn xgcd(a: &Self, b: &Self) -> (Self, Self, Self) {
336        if a.is_zero() || b.is_zero() {
337            return (Self::zero(), Self::zero(), Self::zero());
338        }
339        let (mut old_r, mut r) = (a.clone(), b.clone());
340        let (mut old_s, mut s) = (Self::one(), Self::zero());
341        let (mut old_t, mut t) = (Self::zero(), Self::one());
342
343        while !r.is_zero() {
344            let quotient = old_r.clone() / r.clone();
345            (old_r, r) = (r.clone(), old_r.clone() - quotient.clone() * r.clone());
346            (old_s, s) = (s.clone(), old_s.clone() - quotient.clone() * s.clone());
347            (old_t, t) = (t.clone(), old_t.clone() - quotient.clone() * t.clone());
348        }
349
350        (old_r, old_s, old_t)
351    }
352}
353
354#[allow(private_bounds)]
355impl<F: Clone + Into<FixedPoint64>> Polynomial<F> {
356    #[allow(dead_code)]
357    #[profiling]
358    pub(crate) fn l2_norm(&self) -> FixedPoint64 {
359        self.l2_norm_squared().sqrt()
360    }
361    #[profiling]
362    pub(crate) fn l2_norm_squared(&self) -> FixedPoint64 {
363        self.coefficients
364            .iter()
365            .map(|i| Into::<FixedPoint64>::into(i.clone()))
366            .map(|i| i * i)
367            .sum::<FixedPoint64>()
368    }
369}
370
371impl<F> PartialEq for Polynomial<F>
372where
373    F: Zero + PartialEq + Clone + AddAssign,
374{
375    fn eq(&self, other: &Self) -> bool {
376        if self.is_zero() && other.is_zero() {
377            true
378        } else if self.is_zero() || other.is_zero() {
379            false
380        } else {
381            let self_degree = self.degree().unwrap();
382            let other_degree = other.degree().unwrap();
383            self.coefficients[0..=self_degree] == other.coefficients[0..=other_degree]
384        }
385    }
386}
387
388impl<F> Eq for Polynomial<F> where F: Zero + PartialEq + Clone + AddAssign {}
389
390impl<F> Add for &Polynomial<F>
391where
392    F: Add<Output = F> + AddAssign + Clone,
393{
394    type Output = Polynomial<F>;
395
396    fn add(self, rhs: Self) -> Self::Output {
397        let coefficients = if self.coefficients.len() >= rhs.coefficients.len() {
398            let mut coefficients = self.coefficients.clone();
399            for (i, c) in rhs.coefficients.iter().enumerate() {
400                coefficients[i] += c.clone();
401            }
402            coefficients
403        } else {
404            let mut coefficients = rhs.coefficients.clone();
405            for (i, c) in self.coefficients.iter().enumerate() {
406                coefficients[i] += c.clone();
407            }
408            coefficients
409        };
410        Self::Output { coefficients }
411    }
412}
413impl<F> Add for Polynomial<F>
414where
415    F: Add<Output = F> + AddAssign + Clone,
416{
417    type Output = Polynomial<F>;
418    fn add(self, rhs: Self) -> Self::Output {
419        let coefficients = if self.coefficients.len() >= rhs.coefficients.len() {
420            let mut coefficients = self.coefficients.clone();
421            for (i, c) in rhs.coefficients.into_iter().enumerate() {
422                coefficients[i] += c;
423            }
424            coefficients
425        } else {
426            let mut coefficients = rhs.coefficients.clone();
427            for (i, c) in self.coefficients.into_iter().enumerate() {
428                coefficients[i] += c;
429            }
430            coefficients
431        };
432        Self::Output { coefficients }
433    }
434}
435
436impl<F> AddAssign for Polynomial<F>
437where
438    F: Add<Output = F> + AddAssign + Clone,
439{
440    fn add_assign(&mut self, rhs: Self) {
441        if self.coefficients.len() >= rhs.coefficients.len() {
442            for (i, c) in rhs.coefficients.into_iter().enumerate() {
443                self.coefficients[i] += c;
444            }
445        } else {
446            let mut coefficients = rhs.coefficients.clone();
447            for (i, c) in self.coefficients.iter().enumerate() {
448                coefficients[i] += c.clone();
449            }
450            self.coefficients = coefficients;
451        }
452    }
453}
454
455impl<F> Sub for &Polynomial<F>
456where
457    F: Sub<Output = F> + Clone + Neg<Output = F> + Add<Output = F> + AddAssign,
458{
459    type Output = Polynomial<F>;
460
461    fn sub(self, rhs: Self) -> Self::Output {
462        self + &(-rhs)
463    }
464}
465impl<F> Sub for Polynomial<F>
466where
467    F: Sub<Output = F> + Clone + Neg<Output = F> + Add<Output = F> + AddAssign,
468{
469    type Output = Polynomial<F>;
470
471    fn sub(self, rhs: Self) -> Self::Output {
472        self + (-rhs)
473    }
474}
475
476impl<F> SubAssign for Polynomial<F>
477where
478    F: Add<Output = F> + Neg<Output = F> + AddAssign + Clone + Sub<Output = F>,
479{
480    fn sub_assign(&mut self, rhs: Self) {
481        self.coefficients = self.clone().sub(rhs).coefficients;
482    }
483}
484
485impl<F: Neg<Output = F> + Clone> Neg for &Polynomial<F> {
486    type Output = Polynomial<F>;
487
488    fn neg(self) -> Self::Output {
489        Self::Output {
490            coefficients: self.coefficients.iter().cloned().map(|a| -a).collect(),
491        }
492    }
493}
494impl<F: Neg<Output = F> + Clone> Neg for Polynomial<F> {
495    type Output = Self;
496
497    fn neg(self) -> Self::Output {
498        Self::Output {
499            coefficients: self.coefficients.iter().cloned().map(|a| -a).collect(),
500        }
501    }
502}
503
504impl<F> Mul for &Polynomial<F>
505where
506    F: Add + AddAssign + Mul<Output = F> + Sub<Output = F> + Zero + PartialEq + Clone,
507{
508    type Output = Polynomial<F>;
509
510    fn mul(self, other: Self) -> Self::Output {
511        if self.is_zero() || other.is_zero() {
512            return Polynomial::<F>::zero();
513        }
514        let mut coefficients =
515            vec![F::zero(); self.coefficients.len() + other.coefficients.len() - 1];
516        for i in 0..self.coefficients.len() {
517            for j in 0..other.coefficients.len() {
518                coefficients[i + j] += self.coefficients[i].clone() * other.coefficients[j].clone();
519            }
520        }
521        Polynomial { coefficients }
522    }
523}
524
525impl<F> Mul for Polynomial<F>
526where
527    F: Add + AddAssign + Mul<Output = F> + Zero + PartialEq + Clone,
528{
529    type Output = Self;
530
531    fn mul(self, other: Self) -> Self::Output {
532        if self.is_zero() || other.is_zero() {
533            return Self::zero();
534        }
535        let mut coefficients =
536            vec![F::zero(); self.coefficients.len() + other.coefficients.len() - 1];
537        for i in 0..self.coefficients.len() {
538            for j in 0..other.coefficients.len() {
539                coefficients[i + j] += self.coefficients[i].clone() * other.coefficients[j].clone();
540            }
541        }
542        Self { coefficients }
543    }
544}
545
546impl<F: Add + Mul<Output = F> + Zero + Clone> Mul<F> for &Polynomial<F> {
547    type Output = Polynomial<F>;
548
549    fn mul(self, other: F) -> Self::Output {
550        Polynomial {
551            coefficients: self
552                .coefficients
553                .iter()
554                .cloned()
555                .map(|i| i * other.clone())
556                .collect_vec(),
557        }
558    }
559}
560impl<F: Add + Mul<Output = F> + Zero + Clone> Mul<F> for Polynomial<F> {
561    type Output = Polynomial<F>;
562
563    fn mul(self, other: F) -> Self::Output {
564        Polynomial {
565            coefficients: self
566                .coefficients
567                .iter()
568                .cloned()
569                .map(|i| i * other.clone())
570                .collect_vec(),
571        }
572    }
573}
574impl<F> One for Polynomial<F>
575where
576    F: Clone + One + PartialEq + Zero + AddAssign,
577{
578    fn one() -> Self {
579        Self {
580            coefficients: vec![F::one()],
581        }
582    }
583}
584
585impl<F> Zero for Polynomial<F>
586where
587    F: Zero + PartialEq + Clone + AddAssign,
588{
589    fn zero() -> Self {
590        Self {
591            coefficients: vec![],
592        }
593    }
594
595    fn is_zero(&self) -> bool {
596        self.degree().is_none()
597    }
598}
599
600impl<F> Div<Polynomial<F>> for Polynomial<F>
601where
602    F: Zero
603        + One
604        + PartialEq
605        + AddAssign
606        + Clone
607        + Mul<Output = F>
608        + MulAssign
609        + Div<Output = F>
610        + Neg<Output = F>
611        + Sub<Output = F>,
612{
613    type Output = Polynomial<F>;
614
615    fn div(self, denominator: Self) -> Self::Output {
616        if denominator.is_zero() {
617            panic!();
618        }
619        if self.is_zero() {
620            Self::zero();
621        }
622        let mut remainder = self.clone();
623        let mut quotient = Polynomial::<F>::zero();
624        while remainder.degree().unwrap() >= denominator.degree().unwrap() {
625            let shift = remainder.degree().unwrap() - denominator.degree().unwrap();
626            let quotient_coefficient = remainder.lc() / denominator.lc();
627            let monomial = Self::constant(quotient_coefficient).shift(shift);
628            quotient += monomial.clone();
629            remainder -= monomial * denominator.clone();
630            if remainder.is_zero() {
631                break;
632            }
633        }
634        quotient
635    }
636}
637
638/// Hash a string to a random polynomial in ZZ[ X ] mod <Phi(X), q>.
639/// Algorithm 3, "HashToPoint" in the spec (page 31).
640#[profiling]
641pub(crate) fn hash_to_point(string: &[u8], n: usize) -> Polynomial<Felt> {
642    const K: u32 = (1u32 << 16) / (Felt::Q as u32);
643
644    let mut hasher = Shake256::default();
645    hasher.update(string);
646    let mut reader = hasher.finalize_xof();
647
648    let mut coefficients: Vec<Felt> = Vec::with_capacity(n);
649    while coefficients.len() != n {
650        let mut randomness = [0u8; 2];
651        reader.read(&mut randomness);
652        // Arabic endianness but so be it
653        let t = ((randomness[0] as u32) << 8) | (randomness[1] as u32);
654        if t < K * (Felt::Q as u32) {
655            coefficients.push(Felt::new((t % (Felt::Q as u32)) as u16));
656        }
657    }
658
659    Polynomial { coefficients }
660}
661
662impl<T: Display> Display for Polynomial<T> {
663    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
664        write!(f, "[{}]", self.coefficients.iter().join(", "))
665    }
666}
667
668#[cfg(test)]
669mod test {
670    use crate::falcon_field::Felt;
671    use crate::polynomial::hash_to_point;
672    use crate::polynomial::Polynomial;
673    use itertools::Itertools;
674    use rand::rng;
675    use rand::RngExt;
676
677    #[test]
678    fn test_hash_to_point_sanity() {
679        // KAT obtained from python script, run in the same directory
680        // as a copy of the official python implementation.
681        // ```
682        // from falcon import SecretKey
683        // sk = SecretKey(512)
684        // sk.hash_to_point(b"", b"")
685        // ```
686        let hash_of_empty = Polynomial {
687            coefficients: [
688                5816, 7463, 2984, 11537, 9019, 4074, 5180, 11040, 4044, 8937, 694, 7042, 9481,
689                10084, 3795, 5677, 5977, 1241, 6332, 2817, 413, 1971, 755, 7241, 6041, 9347, 4136,
690                11948, 9140, 1210, 5150, 1630, 4015, 2390, 2346, 2025, 4272, 10978, 7171, 8764,
691                11920, 888, 12160, 11275, 7043, 10323, 1181, 1873, 5876, 12213, 627, 11319, 5675,
692                8207, 6210, 385, 333, 4581, 1359, 10859, 3346, 3970, 8720, 3640, 8157, 1080, 2794,
693                5769, 11618, 6780, 1734, 6484, 1575, 9433, 10353, 2004, 5921, 5013, 4753, 9865,
694                10931, 6621, 1417, 9804, 12027, 9437, 10657, 3260, 9541, 4967, 12124, 6827, 333,
695                6404, 3498, 6920, 3979, 14, 440, 1293, 8011, 7567, 3899, 3252, 4023, 10727, 11938,
696                957, 2412, 9552, 10409, 8063, 9131, 9835, 10305, 3124, 6303, 12241, 6354, 2540,
697                10113, 10777, 6803, 4879, 10952, 10503, 1728, 5067, 3339, 7045, 11333, 5469, 11062,
698                11666, 5235, 2314, 3345, 2224, 2274, 8060, 4304, 6716, 11595, 1541, 996, 6983, 36,
699                449, 7401, 4987, 9177, 810, 1908, 8650, 7646, 6893, 4919, 1971, 4930, 11763, 201,
700                12223, 9234, 4081, 6199, 12047, 7646, 9639, 6814, 6739, 5279, 4012, 2101, 10707,
701                4241, 12146, 3779, 3999, 3176, 1699, 10294, 5168, 5590, 457, 9709, 6450, 442, 8884,
702                6755, 10995, 10923, 3935, 8499, 3508, 12088, 1115, 11336, 1379, 7557, 4954, 7639,
703                2514, 8672, 6686, 98, 5676, 8800, 5712, 4724, 7724, 3202, 12128, 10940, 10177,
704                9421, 11013, 7372, 8546, 441, 6261, 8779, 2453, 12082, 7922, 5307, 6920, 7726, 823,
705                10561, 1251, 10358, 8383, 10905, 8145, 1733, 1718, 3105, 10756, 6798, 10209, 7976,
706                11148, 9353, 4746, 1089, 11444, 6571, 409, 8381, 10325, 7649, 10042, 5587, 3625,
707                10182, 10494, 228, 4687, 5949, 7995, 12092, 3312, 5339, 5920, 8145, 6796, 1992,
708                3205, 2761, 12199, 11342, 9695, 390, 252, 989, 1385, 12148, 8324, 10694, 3690,
709                3440, 8888, 12238, 9018, 3354, 5859, 6298, 8098, 4388, 3788, 3045, 11095, 2372,
710                10036, 9233, 168, 8500, 3604, 2494, 9854, 5679, 2182, 3350, 7798, 8310, 3544, 948,
711                7646, 7235, 2650, 6008, 4610, 2159, 6884, 10545, 688, 4115, 10312, 4408, 4951,
712                2891, 9791, 1377, 5909, 11147, 11139, 4969, 5158, 350, 1067, 4242, 10820, 1818,
713                6473, 105, 2919, 10892, 7116, 850, 11409, 2652, 6392, 2540, 6892, 8372, 11975,
714                4994, 2621, 2763, 11837, 6132, 11293, 9138, 8769, 10964, 9826, 601, 7007, 9078,
715                10340, 9410, 8746, 10835, 9053, 11010, 5308, 8851, 1976, 11016, 599, 8348, 9876,
716                7100, 1333, 4550, 1735, 4598, 9970, 525, 8320, 1609, 9213, 4178, 484, 10814, 1760,
717                9667, 8369, 2286, 10384, 12139, 24, 1178, 5682, 7074, 3676, 3661, 3322, 1831, 5562,
718                734, 8059, 8750, 6951, 4760, 10395, 1019, 9404, 2923, 6715, 123, 10157, 4892, 7667,
719                1677, 4175, 3455, 12123, 10730, 2000, 8212, 2665, 7088, 8741, 10936, 3172, 225,
720                3867, 5140, 2310, 6453, 2898, 3637, 4580, 113, 5991, 3532, 3363, 11457, 11601,
721                7280, 6792, 11872, 8127, 2192, 10761, 9019, 8197, 8965, 6061, 10799, 988, 10522,
722                1281, 1965, 2716, 9841, 7496, 8456, 5192, 825, 3727, 4664, 7906, 8521, 5901, 10200,
723                5167, 9451, 10825, 12011, 2272, 8698, 8174, 11973, 5155, 6890, 9999, 4391, 12044,
724                1620, 8310, 111, 4481, 9650, 2077, 7691, 7531, 1956, 494, 3297, 1623, 3266, 7018,
725                2031, 6317, 4657, 5206, 2581, 11227, 10508, 4567, 8892, 1363, 6790, 6180, 1588,
726                9776, 11998, 10689, 492, 331,
727            ]
728            .map(Felt::new)
729            .to_vec(),
730        };
731        assert_eq!(hash_of_empty, hash_to_point(&[], 512));
732    }
733
734    #[test]
735    fn test_div() {
736        let mut rng = rng();
737        let n = rng.random_range(1..100);
738        let m = rng.random_range(1..100);
739        let expected_coefficients = (0..n).map(|_| rng.random_range(-5..5)).collect_vec();
740        let cofactor_coefficients = (0..m).map(|_| rng.random_range(-5..5)).collect_vec();
741        let cofactor_polynomial = Polynomial::new(cofactor_coefficients);
742        let product = Polynomial::new(expected_coefficients.clone()) * cofactor_polynomial.clone();
743        let quotient = product / cofactor_polynomial;
744        assert_eq!(Polynomial::new(expected_coefficients), quotient);
745    }
746
747    #[test]
748    fn test_karatsuba() {
749        let mut rng = rng();
750        let n = 64;
751        let coefficients_lhs = (0..n).map(|_| rng.random_range(-5..5) as f64).collect_vec();
752        let lhs = Polynomial::new(coefficients_lhs);
753        let coefficients_rhs = (0..n).map(|_| rng.random_range(-5..5) as f64).collect_vec();
754        let rhs = Polynomial::new(coefficients_rhs);
755
756        let schoolbook = lhs.clone() * rhs.clone();
757        let karatsuba = lhs.karatsuba(&rhs);
758        let difference = schoolbook - karatsuba;
759        assert!(f64::from(difference.l2_norm()) < f64::EPSILON * 100.0);
760    }
761}