subproductdomain_nucypher/
lib.rs1#![allow(non_snake_case)]
2#![allow(dead_code)]
3
4use std::mem;
5
6use ark_ec::{
7 pairing::Pairing, scalar_mul::fixed_base::FixedBase, AffineRepr, CurveGroup,
8};
9use ark_ff::{FftField, Field, Zero};
10use ark_poly::{
11 univariate::DensePolynomial, DenseUVPolynomial, EvaluationDomain,
12 GeneralEvaluationDomain, Polynomial,
13};
14
15pub fn fast_multiexp<Group: CurveGroup>(
19 scalars: &[Group::ScalarField],
20 base: Group,
21) -> Vec<Group::Affine> {
22 let window_size = FixedBase::get_mul_window_size(scalars.len());
23
24 let scalar_bits: usize = mem::size_of::<Group::ScalarField>() * 8 - 1;
25 let base_table =
26 FixedBase::get_window_table(scalar_bits, window_size, base);
27
28 let exp =
29 FixedBase::msm::<Group>(scalar_bits, window_size, &base_table, scalars);
30 Group::normalize_batch(&exp)
31}
32
33#[allow(dead_code)]
34pub fn poly_from_scalar<F: FftField>(s: &F) -> DensePolynomial<F> {
35 DensePolynomial::<F> { coeffs: vec![*s] }
36}
37
38#[allow(dead_code)]
39pub fn moduli_from_scalar<F: FftField>(s: &F) -> DensePolynomial<F> {
40 DensePolynomial::<F> {
41 coeffs: vec![-*s, F::one()],
42 }
43}
44
45pub fn inverse_mod_xl<F: FftField>(
52 func: &DensePolynomial<F>,
53 length: usize,
54) -> Option<DensePolynomial<F>> {
55 let log = ark_std::log2(length); if let Some(f_0_inv) = func.coeffs[0].inverse() {
57 let mut func_inv = DensePolynomial::<F> {
59 coeffs: vec![f_0_inv], };
61
62 let mut acc = 2usize;
63 for _ in 0..log {
65 func_inv =
66 &(&func_inv + &func_inv) - &(func * &(&func_inv * &func_inv)); func_inv.coeffs.resize(
68 ark_std::cmp::min(func_inv.coeffs.len(), acc),
69 F::zero(),
70 ); acc *= 2;
72 }
73 Some(func_inv)
74 } else {
75 None
77 }
78}
79
80pub fn rev<F: FftField>(f: &mut DensePolynomial<F>, m: usize) {
84 assert!(f.coeffs.len() - 1 <= m);
85 for _ in 0..(m - (f.coeffs.len() - 1)) {
86 f.coeffs.push(F::zero());
87 }
88 f.reverse();
89}
90
91pub fn fast_divide_monic<F: FftField>(
94 func: &DensePolynomial<F>,
95 divisor: &DensePolynomial<F>,
96) -> (DensePolynomial<F>, DensePolynomial<F>) {
97 if func.coeffs().len() < divisor.coeffs().len() {
100 return (
101 DensePolynomial::<F> {
102 coeffs: vec![F::zero()],
103 },
104 func.clone(),
105 );
106 }
107 let deg_diff = func.coeffs().len() - divisor.coeffs().len();
108
109 let mut rev_f = func.clone();
110 let mut rev_g = divisor.clone();
111 rev_f.reverse();
112 rev_g.reverse();
113
114 let mut quotient =
115 &rev_f * &inverse_mod_xl::<F>(&rev_g, deg_diff + 1).unwrap();
116 quotient.coeffs.resize(deg_diff + 1, F::zero());
117 rev::<F>(&mut quotient, deg_diff);
118 let remainder = func - &(divisor * "ient);
119 (quotient, remainder)
120}
121
122#[derive(Debug, Clone)]
131pub struct SubproductDomain<F: FftField> {
132 pub u: Vec<F>,
134 pub t: SubproductTree<F>,
136 pub prime: DensePolynomial<F>, }
139
140impl<F: FftField> SubproductDomain<F> {
141 pub fn new(u: Vec<F>) -> SubproductDomain<F> {
143 let t = SubproductTree::new(&u);
144 let prime = derivative::<F>(&t.m);
145 SubproductDomain { u, t, prime }
146 }
147
148 #[allow(dead_code)]
149 pub fn evaluate(&self, f: &DensePolynomial<F>) -> Vec<F> {
151 let mut evals = vec![F::zero(); self.u.len()];
152 self.t.evaluate(f, &self.u, &mut evals);
153 evals
154 }
155
156 #[allow(dead_code)]
157 pub fn interpolate(&self, v: &[F]) -> DensePolynomial<F> {
159 self.t.interpolate(&self.u, v)
160 }
161
162 pub fn inverse_lagrange_coefficients(&self) -> Vec<F> {
164 self.t.inverse_lagrange_coefficients(&self.u)
165 }
166
167 #[allow(dead_code)]
168 pub fn linear_combine(&self, c: &[F]) -> DensePolynomial<F> {
170 self.t.linear_combine(&self.u, c)
171 }
172}
173
174#[derive(Debug, Clone)]
180pub struct SubproductTree<F: FftField> {
181 pub left: Option<Box<SubproductTree<F>>>,
183 pub right: Option<Box<SubproductTree<F>>>,
185 pub m: DensePolynomial<F>,
187}
188
189impl<F: FftField> SubproductTree<F> {
190 pub fn new(u: &[F]) -> SubproductTree<F> {
196 if u.len() == 1 {
198 SubproductTree {
199 left: None,
200 right: None,
201 m: DensePolynomial::<F> {
202 coeffs: vec![-u[0], F::one()], },
204 }
205 } else {
206 let n = u.len() / 2;
208 let (u_0, u_1) = u.split_at(n);
209 let left = Box::new(SubproductTree::new(u_0));
210 let right = Box::new(SubproductTree::new(u_1));
211 let m = &left.m * &right.m;
212 SubproductTree {
213 left: Some(left),
214 right: Some(right),
215 m,
216 }
217 }
218 }
219 pub fn evaluate(&self, f: &DensePolynomial<F>, u: &[F], t: &mut [F]) {
227 assert!(f.degree() < u.len());
228
229 if u.len() == 1 {
230 t[0] = f.coeffs[0];
232 return;
233 }
234
235 let left = self.left.as_ref().unwrap();
237 let right = self.right.as_ref().unwrap();
238
239 let (_, r_0) = fast_divide_monic::<F>(f, &left.m);
241 let (_, r_1) = fast_divide_monic::<F>(f, &right.m);
242
243 let n = u.len() / 2;
245 let (u_0, u_1) = u.split_at(n);
246 let (t_0, t_1) = t.split_at_mut(n);
247
248 left.evaluate(&r_0, u_0, t_0);
249 right.evaluate(&r_1, u_1, t_1);
250 }
251
252 #[allow(dead_code)]
253 pub fn interpolate(&self, u: &[F], v: &[F]) -> DensePolynomial<F> {
255 let mut lagrange_coeff = self.inverse_lagrange_coefficients(u);
256
257 for (s_i, v_i) in lagrange_coeff.iter_mut().zip(v.iter()) {
258 *s_i = s_i.inverse().unwrap() * *v_i;
259 }
260
261 self.linear_combine(u, &lagrange_coeff)
262 }
263
264 pub fn inverse_lagrange_coefficients(&self, u: &[F]) -> Vec<F> {
266 if u.len() == 1 {
268 return vec![F::one()];
269 }
270 let mut evals = vec![F::zero(); u.len()];
271 let m_prime = derivative::<F>(&self.m);
272 self.evaluate(&m_prime, u, &mut evals);
273 evals
274 }
275
276 #[allow(dead_code)]
277 pub fn linear_combine(&self, u: &[F], c: &[F]) -> DensePolynomial<F> {
283 if u.len() == 1 {
284 return DensePolynomial::<F> { coeffs: vec![c[0]] };
286 }
287 let n = u.len() / 2;
288 let (u_0, u_1) = u.split_at(n);
289 let (c_0, c_1) = c.split_at(n);
290
291 let left = self.left.as_ref().unwrap();
293 let right = self.right.as_ref().unwrap();
294 let r_0 = left.linear_combine(u_0, c_0);
295 let r_1 = right.linear_combine(u_1, c_1);
296
297 &(&right.m * &r_0) + &(&left.m * &r_1)
300 }
301}
302
303pub fn derivative<F: FftField>(f: &DensePolynomial<F>) -> DensePolynomial<F> {
305 let mut coeffs = Vec::with_capacity(f.coeffs().len() - 1);
306 for (i, c) in f.coeffs.iter().enumerate().skip(1) {
307 coeffs.push(F::from(i as u128) * c);
308 }
309 DensePolynomial::<F> { coeffs }
310}
311
312pub fn build_circulant<F: FftField>(
316 f: &DensePolynomial<F>,
317 n: usize, ) -> Vec<F> {
319 assert!(n >= f.degree());
320 let mut circulant = vec![F::zero(); 2 * n];
321 let coeffs = f.coeffs();
322 if n == coeffs.len() - 1 {
323 circulant[0] = *coeffs.last().unwrap();
324 circulant[n] = *coeffs.last().unwrap();
325 circulant[n + 1..n + 1 + coeffs.len() - 2]
326 .copy_from_slice(&coeffs[1..coeffs.len() - 1]);
327 } else {
328 circulant[n + 1..n + 1 + coeffs.len() - 1]
329 .copy_from_slice(&coeffs[1..]);
330 }
331 circulant
332}
333
334#[allow(dead_code)]
335pub fn toeplitz_mul<E: Pairing, const NORMALIZE: bool>(
337 polynomial: &DensePolynomial<E::ScalarField>,
338 v: &[E::G1Affine],
339 size: usize,
340) -> Result<(Vec<E::G1>, E::ScalarField), anyhow::Error> {
341 let m = polynomial.coeffs.len() - 1;
342 let size = ark_std::cmp::max(size, m);
343
344 let domain = GeneralEvaluationDomain::<E::ScalarField>::new(2 * size)
345 .ok_or_else(|| {
346 anyhow::anyhow!("toeplitz multiplication on too large a domain")
347 })?;
348
349 let circulant_size = domain.size();
350 let toeplitz_size = circulant_size / 2;
351 let mut circulant =
352 build_circulant::<E::ScalarField>(polynomial, toeplitz_size);
353
354 let mut tmp: Vec<E::G1> = Vec::with_capacity(circulant_size);
355
356 for _ in 0..(toeplitz_size - v.len()) {
357 tmp.push(E::G1::zero());
358 }
359
360 for i in v.iter().rev() {
361 tmp.push(i.into_group());
362 }
363
364 tmp.resize(circulant_size, E::G1::zero());
365 domain.fft_in_place(&mut tmp);
366 domain.fft_in_place(&mut circulant);
367
368 for (i, j) in tmp.iter_mut().zip(circulant.iter()) {
369 *i *= *j;
370 }
371
372 domain.ifft_in_place(&mut tmp);
374
375 Ok((
376 tmp[..toeplitz_size].to_vec(),
377 E::ScalarField::from(domain.size() as u128)
378 .inverse()
379 .unwrap(),
380 ))
381}
382
383#[cfg(test)]
384mod tests {
385 use ark_ec::pairing::Pairing;
386 use ark_ff::{One, Zero};
387 use ark_poly::{polynomial::univariate::DensePolynomial, Polynomial};
388 use ark_std::UniformRand;
389
390 use super::*;
391
392 type ScalarField = <ark_bls12_381::Bls12_381 as Pairing>::ScalarField;
393 #[test]
394 fn test_inverse() {
395 let rng = &mut ark_std::test_rng();
396
397 for l in [1, 2, 3, 5, 19, 25, 101].iter() {
398 for degree in 0..(l + 4) {
399 for _ in 0..10 {
400 let p = DensePolynomial::<ScalarField>::rand(degree, rng);
401 let p_inv = inverse_mod_xl::<ScalarField>(&p, *l).unwrap();
402 let mut t = &p * &p_inv; t.coeffs.resize(*l, ScalarField::zero()); assert_eq!(t.coeffs[0], ScalarField::one()); for i in t.iter().skip(1) {
406 assert_eq!(*i, ScalarField::zero()); }
408 }
409 }
410 }
411 }
412
413 #[test]
414 fn test_divide() {
415 let rng = &mut ark_std::test_rng();
416
417 let degree = 100;
418 for g_deg in 1..100 {
419 let f = DensePolynomial::<ScalarField>::rand(degree, rng);
420 let mut g = DensePolynomial::<ScalarField>::rand(g_deg, rng);
421 *g.last_mut().unwrap() = ScalarField::one(); let (q, r) = fast_divide_monic::<ScalarField>(&f, &g);
424
425 let t = &(&q * &g) + &r;
426
427 for (i, j) in t.coeffs.iter().zip(f.coeffs.iter()) {
428 assert_eq!(*i, *j);
429 }
430 }
431 }
432
433 #[test]
434 fn test_interpolate() {
435 let rng = &mut ark_std::test_rng();
436 for d in 1..100 {
437 let mut points = vec![];
438 let mut evals = vec![];
439 for _ in 0..d {
440 points.push(ScalarField::rand(rng));
441 evals.push(ScalarField::rand(rng));
442 }
443
444 let s = SubproductDomain::<ScalarField>::new(points);
445 let p = s.interpolate(&evals);
446
447 for (x, y) in s.u.iter().zip(evals.iter()) {
448 assert_eq!(p.evaluate(x), *y)
449 }
450 }
451 }
452
453 #[test]
454 fn test_linear_combine() {
455 let rng = &mut ark_std::test_rng();
456 for d in 1..100 {
457 let mut u = vec![];
458 let mut c = vec![];
459 for _ in 0..d {
460 u.push(ScalarField::rand(rng));
461 c.push(ScalarField::rand(rng));
462 }
463 let s = SubproductDomain::<ScalarField>::new(u);
464 let f = s.linear_combine(&c);
465
466 let r = ScalarField::rand(rng);
467 let m = s.t.m.evaluate(&r);
468 let mut total = ScalarField::zero();
469 for (u_i, c_i) in s.u.iter().zip(c.iter()) {
470 total += m * *c_i / (r - u_i);
471 }
472 assert_eq!(f.evaluate(&r), total);
473 }
474 }
475
476 #[test]
477 fn test_inv_lagrange() {
478 let rng = &mut ark_std::test_rng();
479 for d in 1..100 {
480 let mut u = vec![];
481 for _ in 0..d {
482 u.push(ScalarField::rand(rng));
483 }
484 let s = SubproductDomain::<ScalarField>::new(u);
485 let f = s.inverse_lagrange_coefficients();
486
487 for (i, j) in s.u.iter().zip(f.iter()) {
488 assert_eq!(s.prime.evaluate(i), *j);
489 }
490 }
491 }
492}