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#[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 #[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)] impl<
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 #[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
201impl<
204 F: One + Zero + Clone + Neg<Output = F> + MulAssign + AddAssign + Sub<Output = F> + PartialEq,
205 > Polynomial<F>
206{
207 #[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
222impl<
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 #[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 #[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 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 #[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 #[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 #[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#[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 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 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}