sonobe/folding-schemes/src/folding/protogalaxy/utils.rs
folding-schemes/src/folding/protogalaxy/utils.rsRaw
1use ark_ff::PrimeField;
2use ark_r1cs_std::fields::{fp::FpVar, FieldVar};
3use num_integer::Integer;
4
5/// Returns (b, b^2, b^4, ..., b^{2^{t-1}})
6pub fn exponential_powers<F: PrimeField>(b: F, t: usize) -> Vec<F> {
7 let mut r = vec![F::zero(); t];
8 r[0] = b;
9 for i in 1..t {
10 r[i] = r[i - 1].square();
11 }
12 r
13}
14
15/// The in-circuit version of `exponential_powers`
16pub fn exponential_powers_var<F: PrimeField>(b: FpVar<F>, t: usize) -> Vec<FpVar<F>> {
17 let mut r = vec![FpVar::zero(); t];
18 r[0] = b;
19 for i in 1..t {
20 r[i] = &r[i - 1] * &r[i - 1];
21 }
22 r
23}
24
25/// Returns (a, a^2, a^3, ..., a^{n-1})
26pub fn all_powers<F: PrimeField>(a: F, n: usize) -> Vec<F> {
27 let mut r = vec![F::zero(); n];
28 for (i, r_i) in r.iter_mut().enumerate() {
29 *r_i = a.pow([i as u64]);
30 }
31 r
32}
33
34/// The in-circuit version of `all_powers`
35pub fn all_powers_var<F: PrimeField>(a: FpVar<F>, n: usize) -> Vec<FpVar<F>> {
36 if n == 0 {
37 return vec![];
38 }
39 let mut r = vec![FpVar::zero(); n];
40 r[0] = FpVar::one();
41 for i in 1..n {
42 r[i] = &r[i - 1] * &a;
43 }
44 r
45}
46
47/// returns a vector containing βᵢ* = βᵢ + α ⋅ δᵢ
48pub fn betas_star<F: PrimeField>(betas: &[F], deltas: &[F], alpha: F) -> Vec<F> {
49 betas
50 .iter()
51 .zip(
52 deltas
53 .iter()
54 .map(|delta_i| alpha * delta_i)
55 .collect::<Vec<F>>(),
56 )
57 .map(|(beta_i, delta_i_alpha)| *beta_i + delta_i_alpha)
58 .collect()
59}
60
61/// The in-circuit version of `betas_star`
62pub fn betas_star_var<F: PrimeField>(
63 betas: &[FpVar<F>],
64 deltas: &[FpVar<F>],
65 alpha: &FpVar<F>,
66) -> Vec<FpVar<F>> {
67 betas
68 .iter()
69 .zip(deltas)
70 .map(|(beta_i, delta_i)| beta_i + alpha * delta_i)
71 .collect::<Vec<FpVar<F>>>()
72}
73
74/// Returns the product of selected elements in `betas`.
75/// For every index `j`, whether `betas[j]` is selected is determined by the
76/// `j`-th bit in the binary (little endian) representation of `i`.
77///
78/// If `betas = (β, β^2, β^4, ..., β^{2^{t-1}})`, then the result is equal to
79/// `β^i`.
80pub fn pow_i<F: PrimeField>(mut i: usize, betas: &[F]) -> F {
81 let mut j = 0;
82 let mut r = F::one();
83 while i > 0 {
84 if i.is_odd() {
85 r *= betas[j];
86 }
87 i >>= 1;
88 j += 1;
89 }
90 r
91}
92
93/// The in-circuit version of `pow_i`
94#[allow(dead_code)] // Will remove this once we have the decider circuit for Protogalaxy
95pub fn pow_i_var<F: PrimeField>(mut i: usize, betas: &[FpVar<F>]) -> FpVar<F> {
96 let mut j = 0;
97 let mut r = FieldVar::one();
98 while i > 0 {
99 if i.is_odd() {
100 r *= &betas[j];
101 }
102 i >>= 1;
103 j += 1;
104 }
105 r
106}
107
108#[cfg(test)]
109mod tests {
110 use std::error::Error;
111
112 use ark_bn254::Fr;
113 use ark_r1cs_std::{alloc::AllocVar, R1CSVar};
114 use ark_relations::r1cs::ConstraintSystem;
115 use ark_std::{test_rng, UniformRand};
116 use rand::Rng;
117
118 use super::*;
119
120 #[test]
121 fn test_exponential_powers() -> Result<(), Box<dyn Error>> {
122 let rng = &mut test_rng();
123
124 for t in 1..10 {
125 let cs = ConstraintSystem::<Fr>::new_ref();
126
127 let b = Fr::rand(rng);
128 let b_var = FpVar::new_witness(cs.clone(), || Ok(b))?;
129
130 let r = exponential_powers(b, t);
131 let r_var = exponential_powers_var(b_var, t);
132
133 assert_eq!(r, r_var.value()?);
134 assert!(cs.is_satisfied()?);
135 }
136
137 Ok(())
138 }
139
140 #[test]
141 fn test_all_powers() -> Result<(), Box<dyn Error>> {
142 let rng = &mut test_rng();
143
144 for n in 1..10 {
145 let cs = ConstraintSystem::<Fr>::new_ref();
146
147 let a = Fr::rand(rng);
148 let a_var = FpVar::new_witness(cs.clone(), || Ok(a))?;
149
150 let r = all_powers(a, n);
151 let r_var = all_powers_var(a_var, n);
152
153 assert_eq!(r, r_var.value()?);
154 assert!(cs.is_satisfied()?);
155 }
156
157 Ok(())
158 }
159
160 #[test]
161 fn test_betas_star() -> Result<(), Box<dyn Error>> {
162 let rng = &mut test_rng();
163
164 for t in 1..10 {
165 let cs = ConstraintSystem::<Fr>::new_ref();
166
167 let betas = (0..t).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
168 let deltas = (0..t).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
169 let alpha = Fr::rand(rng);
170
171 let betas_var = Vec::new_witness(cs.clone(), || Ok(betas.clone()))?;
172 let deltas_var = Vec::new_witness(cs.clone(), || Ok(deltas.clone()))?;
173 let alpha_var = FpVar::new_witness(cs.clone(), || Ok(alpha))?;
174
175 let r = betas_star(&betas, &deltas, alpha);
176 let r_var = betas_star_var(&betas_var, &deltas_var, &alpha_var);
177 assert_eq!(r, r_var.value()?);
178 assert!(cs.is_satisfied()?);
179 }
180
181 Ok(())
182 }
183
184 #[test]
185 fn test_pow_i() -> Result<(), Box<dyn Error>> {
186 let rng = &mut test_rng();
187
188 for t in 1..10 {
189 let cs = ConstraintSystem::<Fr>::new_ref();
190
191 let betas = (0..t).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
192 let i = rng.gen_range(0..(1 << t));
193
194 let betas_var = Vec::new_witness(cs.clone(), || Ok(betas.clone()))?;
195
196 let r = pow_i(i, &betas);
197 let r_var = pow_i_var(i, &betas_var);
198 assert_eq!(r, r_var.value()?);
199 assert!(cs.is_satisfied()?);
200 }
201
202 Ok(())
203 }
204}