diff --git a/plonky2/src/fri/recursive_verifier.rs b/plonky2/src/fri/recursive_verifier.rs index 9b619ea8..f56e19ca 100644 --- a/plonky2/src/fri/recursive_verifier.rs +++ b/plonky2/src/fri/recursive_verifier.rs @@ -332,7 +332,8 @@ impl, const D: usize> CircuitBuilder { let x_index_within_coset = self.le_sum(x_index_within_coset_bits.iter()); // Check consistency with our old evaluation from the previous round. - self.random_access_extension(x_index_within_coset, old_eval, evals.clone()); + let new_eval = self.random_access_extension(x_index_within_coset, evals.clone()); + self.connect_extension(new_eval, old_eval); // Infer P(y) from {P(x)}_{x^arity=y}. old_eval = with_context!( diff --git a/plonky2/src/gadgets/curve.rs b/plonky2/src/gadgets/curve.rs index 9183a07a..c2af0104 100644 --- a/plonky2/src/gadgets/curve.rs +++ b/plonky2/src/gadgets/curve.rs @@ -1,11 +1,18 @@ +use std::marker::PhantomData; + use plonky2_field::extension_field::Extendable; use plonky2_field::field_types::Field; use crate::curve::curve_types::{AffinePoint, Curve, CurveScalar}; +use crate::gadgets::arithmetic_u32::U32Target; +use crate::gadgets::biguint::BigUintTarget; use crate::gadgets::nonnative::NonNativeTarget; use crate::hash::hash_types::RichField; +use crate::iop::target::Target; use crate::plonk::circuit_builder::CircuitBuilder; +const WINDOW_SIZE: usize = 4; + /// A Target representing an affine point on the curve `C`. We use incomplete arithmetic for efficiency, /// so we assume these points are not zero. #[derive(Clone, Debug)] @@ -157,6 +164,82 @@ impl, const D: usize> CircuitBuilder { result } + + pub fn precompute_window( + &mut self, + p: &AffinePointTarget, + ) -> Vec> { + let mut multiples = Vec::new(); + multiples.push(self.constant_affine_point(C::GENERATOR_AFFINE)); + let mut cur = p.clone(); + for _pow in 1..WINDOW_SIZE { + for existing in multiples.clone() { + multiples.push(self.curve_add(&cur, &existing)); + } + cur = self.curve_double(&cur); + } + + multiples + } + + pub fn random_access_curve_points( + &mut self, + access_index: Target, + v: Vec>, + ) -> AffinePointTarget { + let num_limbs = v[0].x.value.num_limbs(); + let x_limbs: Vec> = (0..num_limbs) + .map(|i| v.iter().map(|p| p.x.value.limbs[i].0).collect()) + .collect(); + let y_limbs: Vec> = (0..num_limbs) + .map(|i| v.iter().map(|p| p.y.value.limbs[i].0).collect()) + .collect(); + + let selected_x_limbs: Vec<_> = x_limbs + .iter() + .map(|limbs| U32Target(self.random_access(access_index, limbs.clone()))) + .collect(); + let selected_y_limbs: Vec<_> = y_limbs + .iter() + .map(|limbs| U32Target(self.random_access(access_index, limbs.clone()))) + .collect(); + + let x = NonNativeTarget { + value: BigUintTarget { + limbs: selected_x_limbs, + }, + _phantom: PhantomData, + }; + let y = NonNativeTarget { + value: BigUintTarget { + limbs: selected_y_limbs, + }, + _phantom: PhantomData, + }; + AffinePointTarget { x, y } + } + + pub fn curve_scalar_mul_windowed( + &mut self, + p: &AffinePointTarget, + n: &NonNativeTarget, + ) -> AffinePointTarget { + let mut result = self.constant_affine_point(C::GENERATOR_AFFINE); + + let precomputation = self.precompute_window(p); + + let windows = self.split_nonnative_to_4_bit_limbs(n); + let m = C::ScalarField::BITS / WINDOW_SIZE; + for i in m..0 { + result = self.curve_double(&result); + let window = windows[i]; + + let to_add = self.random_access_curve_points(window, precomputation.clone()); + result = self.curve_add(&result, &to_add); + } + + result + } } #[cfg(test)] diff --git a/plonky2/src/gadgets/mod.rs b/plonky2/src/gadgets/mod.rs index 9d6f7686..95e46b42 100644 --- a/plonky2/src/gadgets/mod.rs +++ b/plonky2/src/gadgets/mod.rs @@ -16,3 +16,4 @@ pub mod range_check; pub mod select; pub mod split_base; pub(crate) mod split_join; +pub mod split_nonnative; diff --git a/plonky2/src/gadgets/random_access.rs b/plonky2/src/gadgets/random_access.rs index 9518e9fa..ec3c889a 100644 --- a/plonky2/src/gadgets/random_access.rs +++ b/plonky2/src/gadgets/random_access.rs @@ -10,13 +10,15 @@ use crate::plonk::circuit_builder::CircuitBuilder; impl, const D: usize> CircuitBuilder { /// Checks that a `Target` matches a vector at a non-deterministic index. /// Note: `access_index` is not range-checked. - pub fn random_access(&mut self, access_index: Target, claimed_element: Target, v: Vec) { + pub fn random_access(&mut self, access_index: Target, v: Vec) -> Target { let vec_size = v.len(); let bits = log2_strict(vec_size); debug_assert!(vec_size > 0); if vec_size == 1 { - return self.connect(claimed_element, v[0]); + return v[0]; } + let claimed_element = self.add_virtual_target(); + let dummy_gate = RandomAccessGate::::new_from_config(&self.config, bits); let (gate_index, copy) = self.find_slot(dummy_gate, &[], &[]); @@ -34,6 +36,8 @@ impl, const D: usize> CircuitBuilder { claimed_element, Target::wire(gate_index, dummy_gate.wire_claimed_element(copy)), ); + + claimed_element } /// Checks that an `ExtensionTarget` matches a vector at a non-deterministic index. @@ -41,16 +45,13 @@ impl, const D: usize> CircuitBuilder { pub fn random_access_extension( &mut self, access_index: Target, - claimed_element: ExtensionTarget, v: Vec>, - ) { - for i in 0..D { - self.random_access( - access_index, - claimed_element.0[i], - v.iter().map(|et| et.0[i]).collect(), - ); - } + ) -> ExtensionTarget { + let v: Vec<_> = (0..D) + .map(|i| self.random_access(access_index, v.iter().map(|et| et.0[i]).collect())) + .collect(); + + ExtensionTarget(v.try_into().unwrap()) } } @@ -80,7 +81,8 @@ mod tests { for i in 0..len { let it = builder.constant(F::from_canonical_usize(i)); let elem = builder.constant_extension(vec[i]); - builder.random_access_extension(it, elem, v.clone()); + let res = builder.random_access_extension(it, v.clone()); + builder.connect_extension(elem, res); } let data = builder.build::(); diff --git a/plonky2/src/gadgets/split_nonnative.rs b/plonky2/src/gadgets/split_nonnative.rs new file mode 100644 index 00000000..d1f16b65 --- /dev/null +++ b/plonky2/src/gadgets/split_nonnative.rs @@ -0,0 +1,34 @@ +use itertools::Itertools; +use plonky2_field::extension_field::Extendable; +use plonky2_field::field_types::Field; + +use crate::gadgets::arithmetic_u32::U32Target; +use crate::gadgets::nonnative::NonNativeTarget; +use crate::hash::hash_types::RichField; +use crate::iop::target::Target; +use crate::plonk::circuit_builder::CircuitBuilder; + +impl, const D: usize> CircuitBuilder { + pub fn split_u32_to_4_bit_limbs(&mut self, val: U32Target) -> Vec { + let two_bit_limbs = self.split_le_base::<2>(val.0, 16); + let four = self.constant(F::from_canonical_usize(4)); + let combined_limbs = two_bit_limbs + .iter() + .tuples() + .map(|(&a, &b)| self.mul_add(b, four, a)) + .collect(); + + combined_limbs + } + + pub fn split_nonnative_to_4_bit_limbs( + &mut self, + val: &NonNativeTarget, + ) -> Vec { + val.value + .limbs + .iter() + .flat_map(|&l| self.split_u32_to_4_bit_limbs(l)) + .collect() + } +} diff --git a/plonky2/src/hash/merkle_proofs.rs b/plonky2/src/hash/merkle_proofs.rs index c3ebf406..c4188271 100644 --- a/plonky2/src/hash/merkle_proofs.rs +++ b/plonky2/src/hash/merkle_proofs.rs @@ -78,11 +78,9 @@ impl, const D: usize> CircuitBuilder { let index = self.le_sum(leaf_index_bits[proof.siblings.len()..].iter().copied()); for i in 0..4 { - self.random_access( - index, - state.elements[i], - merkle_cap.0.iter().map(|h| h.elements[i]).collect(), - ); + let result = + self.random_access(index, merkle_cap.0.iter().map(|h| h.elements[i]).collect()); + self.connect(result, state.elements[i]); } } @@ -110,11 +108,11 @@ impl, const D: usize> CircuitBuilder { } for i in 0..4 { - self.random_access( + let result = self.random_access( cap_index, - state.elements[i], merkle_cap.0.iter().map(|h| h.elements[i]).collect(), ); + self.connect(result, state.elements[i]); } } diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index bd216389..ff975659 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -846,3 +846,443 @@ impl, const D: usize> CircuitBuilder { } } } +<<<<<<< HEAD +======= + +/// Various gate types can contain multiple copies in a single Gate. This helper struct lets a +/// CircuitBuilder track such gates that are currently being "filled up." +pub struct BatchedGates, const D: usize> { + /// A map `(c0, c1) -> (g, i)` from constants `(c0,c1)` to an available arithmetic gate using + /// these constants with gate index `g` and already using `i` arithmetic operations. + pub(crate) free_arithmetic: HashMap<(F, F), (usize, usize)>, + pub(crate) free_base_arithmetic: HashMap<(F, F), (usize, usize)>, + + pub(crate) free_mul: HashMap, + + /// A map `b -> (g, i)` from `b` bits to an available random access gate of that size with gate + /// index `g` and already using `i` random accesses. + pub(crate) free_random_access: HashMap, + + /// `current_switch_gates[chunk_size - 1]` contains None if we have no switch gates with the value + /// chunk_size, and contains `(g, i, c)`, if the gate `g`, at index `i`, already contains `c` copies + /// of switches + pub(crate) current_switch_gates: Vec, usize, usize)>>, + + /// A map `n -> (g, i)` from `n` number of addends to an available `U32AddManyGate` of that size with gate + /// index `g` and already using `i` random accesses. + pub(crate) free_u32_add_many: HashMap, + + /// The `U32ArithmeticGate` currently being filled (so new u32 arithmetic operations will be added to this gate before creating a new one) + pub(crate) current_u32_arithmetic_gate: Option<(usize, usize)>, + /// The `U32SubtractionGate` currently being filled (so new u32 subtraction operations will be added to this gate before creating a new one) + pub(crate) current_u32_subtraction_gate: Option<(usize, usize)>, + + /// An available `ConstantGate` instance, if any. + pub(crate) free_constant: Option<(usize, usize)>, +} + +impl, const D: usize> BatchedGates { + pub fn new() -> Self { + Self { + free_arithmetic: HashMap::new(), + free_base_arithmetic: HashMap::new(), + free_mul: HashMap::new(), + free_random_access: HashMap::new(), + current_switch_gates: Vec::new(), + free_u32_add_many: HashMap::new(), + current_u32_arithmetic_gate: None, + current_u32_subtraction_gate: None, + free_constant: None, + } + } +} + +impl, const D: usize> CircuitBuilder { + /// Finds the last available arithmetic gate with the given constants or add one if there aren't any. + /// Returns `(g,i)` such that there is an arithmetic gate with the given constants at index + /// `g` and the gate's `i`-th operation is available. + pub(crate) fn find_base_arithmetic_gate(&mut self, const_0: F, const_1: F) -> (usize, usize) { + let (gate, i) = self + .batched_gates + .free_base_arithmetic + .get(&(const_0, const_1)) + .copied() + .unwrap_or_else(|| { + let gate = self.add_gate( + ArithmeticGate::new_from_config(&self.config), + vec![const_0, const_1], + ); + (gate, 0) + }); + + // Update `free_arithmetic` with new values. + if i < ArithmeticGate::num_ops(&self.config) - 1 { + self.batched_gates + .free_base_arithmetic + .insert((const_0, const_1), (gate, i + 1)); + } else { + self.batched_gates + .free_base_arithmetic + .remove(&(const_0, const_1)); + } + + (gate, i) + } + + /// Finds the last available arithmetic gate with the given constants or add one if there aren't any. + /// Returns `(g,i)` such that there is an arithmetic gate with the given constants at index + /// `g` and the gate's `i`-th operation is available. + pub(crate) fn find_arithmetic_gate(&mut self, const_0: F, const_1: F) -> (usize, usize) { + let (gate, i) = self + .batched_gates + .free_arithmetic + .get(&(const_0, const_1)) + .copied() + .unwrap_or_else(|| { + let gate = self.add_gate( + ArithmeticExtensionGate::new_from_config(&self.config), + vec![const_0, const_1], + ); + (gate, 0) + }); + + // Update `free_arithmetic` with new values. + if i < ArithmeticExtensionGate::::num_ops(&self.config) - 1 { + self.batched_gates + .free_arithmetic + .insert((const_0, const_1), (gate, i + 1)); + } else { + self.batched_gates + .free_arithmetic + .remove(&(const_0, const_1)); + } + + (gate, i) + } + + /// Finds the last available arithmetic gate with the given constants or add one if there aren't any. + /// Returns `(g,i)` such that there is an arithmetic gate with the given constants at index + /// `g` and the gate's `i`-th operation is available. + pub(crate) fn find_mul_gate(&mut self, const_0: F) -> (usize, usize) { + let (gate, i) = self + .batched_gates + .free_mul + .get(&const_0) + .copied() + .unwrap_or_else(|| { + let gate = self.add_gate( + MulExtensionGate::new_from_config(&self.config), + vec![const_0], + ); + (gate, 0) + }); + + // Update `free_arithmetic` with new values. + if i < MulExtensionGate::::num_ops(&self.config) - 1 { + self.batched_gates.free_mul.insert(const_0, (gate, i + 1)); + } else { + self.batched_gates.free_mul.remove(&const_0); + } + + (gate, i) + } + + /// Finds the last available random access gate with the given `bits` or adds one if there aren't any. + /// Returns `(g,i)` such that there is a random access gate for the given `bits` at index + /// `g` and the gate's `i`-th random access is available. + pub(crate) fn find_random_access_gate(&mut self, bits: usize) -> (usize, usize) { + let (gate, i) = self + .batched_gates + .free_random_access + .get(&bits) + .copied() + .unwrap_or_else(|| { + let gate = self.add_gate( + RandomAccessGate::new_from_config(&self.config, bits), + vec![], + ); + (gate, 0) + }); + + // Update `free_random_access` with new values. + if i + 1 < RandomAccessGate::::new_from_config(&self.config, bits).num_copies { + self.batched_gates + .free_random_access + .insert(bits, (gate, i + 1)); + } else { + self.batched_gates.free_random_access.remove(&bits); + } + + (gate, i) + } + + pub fn find_switch_gate(&mut self, chunk_size: usize) -> (SwitchGate, usize, usize) { + if self.batched_gates.current_switch_gates.len() < chunk_size { + self.batched_gates.current_switch_gates.extend(vec![ + None; + chunk_size + - self + .batched_gates + .current_switch_gates + .len() + ]); + } + + let (gate, gate_index, next_copy) = + match self.batched_gates.current_switch_gates[chunk_size - 1].clone() { + None => { + let gate = SwitchGate::::new_from_config(&self.config, chunk_size); + let gate_index = self.add_gate(gate.clone(), vec![]); + (gate, gate_index, 0) + } + Some((gate, idx, next_copy)) => (gate, idx, next_copy), + }; + + let num_copies = gate.num_copies; + + if next_copy == num_copies - 1 { + self.batched_gates.current_switch_gates[chunk_size - 1] = None; + } else { + self.batched_gates.current_switch_gates[chunk_size - 1] = + Some((gate.clone(), gate_index, next_copy + 1)); + } + + (gate, gate_index, next_copy) + } + + /// Finds the last available U32 add-many gate with the given `num_addends` or adds one if there aren't any. + /// Returns `(g,i)` such that there is a `U32AddManyGate` for the given `num_addends` at index + /// `g` and the gate's `i`-th copy is available. + pub(crate) fn find_u32_add_many_gate(&mut self, num_addends: usize) -> (usize, usize) { + let (gate, i) = self + .batched_gates + .free_u32_add_many + .get(&num_addends) + .copied() + .unwrap_or_else(|| { + let gate = self.add_gate( + U32AddManyGate::new_from_config(&self.config, num_addends), + vec![], + ); + (gate, 0) + }); + + // Update `free_u32_add_many` with new values. + if i + 1 < U32AddManyGate::::new_from_config(&self.config, num_addends).num_ops { + self.batched_gates + .free_u32_add_many + .insert(num_addends, (gate, i + 1)); + } else { + self.batched_gates.free_u32_add_many.remove(&num_addends); + } + + (gate, i) + } + + pub(crate) fn find_u32_arithmetic_gate(&mut self) -> (usize, usize) { + let (gate_index, copy) = match self.batched_gates.current_u32_arithmetic_gate { + None => { + let gate = U32ArithmeticGate::new_from_config(&self.config); + let gate_index = self.add_gate(gate, vec![]); + (gate_index, 0) + } + Some((gate_index, copy)) => (gate_index, copy), + }; + + if copy == U32ArithmeticGate::::num_ops(&self.config) - 1 { + self.batched_gates.current_u32_arithmetic_gate = None; + } else { + self.batched_gates.current_u32_arithmetic_gate = Some((gate_index, copy + 1)); + } + + (gate_index, copy) + } + + pub(crate) fn find_u32_subtraction_gate(&mut self) -> (usize, usize) { + let (gate_index, copy) = match self.batched_gates.current_u32_subtraction_gate { + None => { + let gate = U32SubtractionGate::new_from_config(&self.config); + let gate_index = self.add_gate(gate, vec![]); + (gate_index, 0) + } + Some((gate_index, copy)) => (gate_index, copy), + }; + + if copy == U32SubtractionGate::::num_ops(&self.config) - 1 { + self.batched_gates.current_u32_subtraction_gate = None; + } else { + self.batched_gates.current_u32_subtraction_gate = Some((gate_index, copy + 1)); + } + + (gate_index, copy) + } + + /// Returns the gate index and copy index of a free `ConstantGate` slot, potentially adding a + /// new `ConstantGate` if needed. + fn constant_gate_instance(&mut self) -> (usize, usize) { + if self.batched_gates.free_constant.is_none() { + let num_consts = self.config.constant_gate_size; + // We will fill this `ConstantGate` with zero constants initially. + // These will be overwritten by `constant` as the gate instances are filled. + let gate = self.add_gate(ConstantGate { num_consts }, vec![F::ZERO; num_consts]); + self.batched_gates.free_constant = Some((gate, 0)); + } + + let (gate, instance) = self.batched_gates.free_constant.unwrap(); + if instance + 1 < self.config.constant_gate_size { + self.batched_gates.free_constant = Some((gate, instance + 1)); + } else { + self.batched_gates.free_constant = None; + } + (gate, instance) + } + + /// Fill the remaining unused arithmetic operations with zeros, so that all + /// `ArithmeticGate` are run. + fn fill_base_arithmetic_gates(&mut self) { + let zero = self.zero(); + for ((c0, c1), (_gate, i)) in self.batched_gates.free_base_arithmetic.clone() { + for _ in i..ArithmeticGate::num_ops(&self.config) { + // If we directly wire in zero, an optimization will skip doing anything and return + // zero. So we pass in a virtual target and connect it to zero afterward. + let dummy = self.add_virtual_target(); + self.arithmetic(c0, c1, dummy, dummy, dummy); + self.connect(dummy, zero); + } + } + assert!(self.batched_gates.free_base_arithmetic.is_empty()); + } + + /// Fill the remaining unused arithmetic operations with zeros, so that all + /// `ArithmeticExtensionGenerator`s are run. + fn fill_arithmetic_gates(&mut self) { + let zero = self.zero_extension(); + for ((c0, c1), (_gate, i)) in self.batched_gates.free_arithmetic.clone() { + for _ in i..ArithmeticExtensionGate::::num_ops(&self.config) { + // If we directly wire in zero, an optimization will skip doing anything and return + // zero. So we pass in a virtual target and connect it to zero afterward. + let dummy = self.add_virtual_extension_target(); + self.arithmetic_extension(c0, c1, dummy, dummy, dummy); + self.connect_extension(dummy, zero); + } + } + assert!(self.batched_gates.free_arithmetic.is_empty()); + } + + /// Fill the remaining unused arithmetic operations with zeros, so that all + /// `ArithmeticExtensionGenerator`s are run. + fn fill_mul_gates(&mut self) { + let zero = self.zero_extension(); + for (c0, (_gate, i)) in self.batched_gates.free_mul.clone() { + for _ in i..MulExtensionGate::::num_ops(&self.config) { + // If we directly wire in zero, an optimization will skip doing anything and return + // zero. So we pass in a virtual target and connect it to zero afterward. + let dummy = self.add_virtual_extension_target(); + self.arithmetic_extension(c0, F::ZERO, dummy, dummy, zero); + self.connect_extension(dummy, zero); + } + } + assert!(self.batched_gates.free_mul.is_empty()); + } + + /// Fill the remaining unused random access operations with zeros, so that all + /// `RandomAccessGenerator`s are run. + fn fill_random_access_gates(&mut self) { + let zero = self.zero(); + for (bits, (_, i)) in self.batched_gates.free_random_access.clone() { + let max_copies = + RandomAccessGate::::new_from_config(&self.config, bits).num_copies; + for _ in i..max_copies { + let result = self.random_access(zero, vec![zero; 1 << bits]); + self.connect(result, zero); + } + } + } + + /// Fill the remaining unused switch gates with dummy values, so that all + /// `SwitchGenerator`s are run. + fn fill_switch_gates(&mut self) { + let zero = self.zero(); + + for chunk_size in 1..=self.batched_gates.current_switch_gates.len() { + if let Some((gate, gate_index, mut copy)) = + self.batched_gates.current_switch_gates[chunk_size - 1].clone() + { + while copy < gate.num_copies { + for element in 0..chunk_size { + let wire_first_input = + Target::wire(gate_index, gate.wire_first_input(copy, element)); + let wire_second_input = + Target::wire(gate_index, gate.wire_second_input(copy, element)); + let wire_switch_bool = + Target::wire(gate_index, gate.wire_switch_bool(copy)); + self.connect(zero, wire_first_input); + self.connect(zero, wire_second_input); + self.connect(zero, wire_switch_bool); + } + copy += 1; + } + } + } + } + + /// Fill the remaining unused u32 add-many operations with zeros, so that all + /// `U32AddManyGenerator`s are run. + fn fill_u32_add_many_gates(&mut self) { + let zero = self.zero_u32(); + for (num_addends, (_, i)) in self.batched_gates.free_u32_add_many.clone() { + let max_copies = + U32AddManyGate::::new_from_config(&self.config, num_addends).num_ops; + for _ in i..max_copies { + let gate = U32AddManyGate::::new_from_config(&self.config, num_addends); + let (gate_index, copy) = self.find_u32_add_many_gate(num_addends); + + for j in 0..num_addends { + self.connect( + Target::wire(gate_index, gate.wire_ith_op_jth_addend(copy, j)), + zero.0, + ); + } + self.connect(Target::wire(gate_index, gate.wire_ith_carry(copy)), zero.0); + } + } + } + + /// Fill the remaining unused U32 arithmetic operations with zeros, so that all + /// `U32ArithmeticGenerator`s are run. + fn fill_u32_arithmetic_gates(&mut self) { + let zero = self.zero_u32(); + if let Some((_gate_index, copy)) = self.batched_gates.current_u32_arithmetic_gate { + for _ in copy..U32ArithmeticGate::::num_ops(&self.config) { + let dummy = self.add_virtual_u32_target(); + self.mul_add_u32(dummy, dummy, dummy); + self.connect_u32(dummy, zero); + } + } + } + + /// Fill the remaining unused U32 subtraction operations with zeros, so that all + /// `U32SubtractionGenerator`s are run. + fn fill_u32_subtraction_gates(&mut self) { + let zero = self.zero_u32(); + if let Some((_gate_index, copy)) = self.batched_gates.current_u32_subtraction_gate { + for _i in copy..U32SubtractionGate::::num_ops(&self.config) { + let dummy = self.add_virtual_u32_target(); + self.sub_u32(dummy, dummy, dummy); + self.connect_u32(dummy, zero); + } + } + } + + fn fill_batched_gates(&mut self) { + self.fill_arithmetic_gates(); + self.fill_base_arithmetic_gates(); + self.fill_mul_gates(); + self.fill_random_access_gates(); + self.fill_switch_gates(); + self.fill_u32_add_many_gates(); + self.fill_u32_arithmetic_gates(); + self.fill_u32_subtraction_gates(); + } +} +>>>>>>> aa48021 (windowed multiplication in circuit)