1use std::{
17 cmp::{max, min},
18 marker::PhantomData,
19 ops::Rem,
20};
21
22use ff::PrimeField;
23use midnight_proofs::{
24 circuit::{Layouter, Value},
25 plonk::Error,
26};
27use num_bigint::BigUint;
28use num_integer::Integer;
29use num_traits::One;
30#[cfg(any(test, feature = "testing"))]
31use {
32 crate::testing_utils::FromScratch,
33 midnight_proofs::plonk::{Column, ConstraintSystem, Instance},
34};
35
36use super::{bound_of_addition, AssignedBigUint};
37#[cfg(test)]
38use crate::biguint::types::TEST_NB_BITS;
39#[cfg(test)]
40use crate::instructions::AssignmentInstructions;
41use crate::{
42 biguint::{biguint_to_limbs, LOG2_BASE},
43 field::{foreign::util::big_to_limbs, AssignedBounded},
44 instructions::{
45 AssertionInstructions, ControlFlowInstructions, EqualityInstructions, NativeInstructions,
46 ZeroInstructions,
47 },
48 types::{AssignedBit, AssignedByte, AssignedNative},
49 utils::{
50 types::InnerValue,
51 util::{big_to_fe, fe_to_big},
52 },
53};
54
55#[derive(Clone, Debug)]
56pub struct BigUintGadget<F, N>
60where
61 F: PrimeField,
62 N: NativeInstructions<F>,
63{
64 native_gadget: N,
65 _marker: PhantomData<F>,
66}
67
68impl<F, N> BigUintGadget<F, N>
69where
70 F: PrimeField,
71 N: NativeInstructions<F>,
72{
73 pub fn new(native_gadget: &N) -> Self {
75 Self {
76 native_gadget: native_gadget.clone(),
77 _marker: PhantomData,
78 }
79 }
80}
81
82impl<F, N> AssertionInstructions<F, AssignedBigUint<F>> for BigUintGadget<F, N>
83where
84 F: PrimeField,
85 N: NativeInstructions<F>,
86{
87 fn assert_equal(
88 &self,
89 layouter: &mut impl Layouter<F>,
90 x: &AssignedBigUint<F>,
91 y: &AssignedBigUint<F>,
92 ) -> Result<(), Error> {
93 assert!(x.is_normalized());
94 assert!(y.is_normalized());
95
96 let n = max(x.limbs.len(), y.limbs.len());
97 let mut x = x.clone();
98 let mut y = y.clone();
99 self.resize(layouter, n, &mut x)?;
100 self.resize(layouter, n, &mut y)?;
101
102 for i in 0..n {
103 self.native_gadget.assert_equal(layouter, &x.limbs[i], &y.limbs[i])?;
104 }
105 Ok(())
106 }
107
108 fn assert_not_equal(
109 &self,
110 layouter: &mut impl Layouter<F>,
111 x: &AssignedBigUint<F>,
112 y: &AssignedBigUint<F>,
113 ) -> Result<(), Error> {
114 let x_eq_y = self.is_equal(layouter, x, y)?;
115 self.native_gadget.assert_equal_to_fixed(layouter, &x_eq_y, false)
116 }
117
118 fn assert_equal_to_fixed(
119 &self,
120 layouter: &mut impl Layouter<F>,
121 x: &AssignedBigUint<F>,
122 constant: BigUint,
123 ) -> Result<(), Error> {
124 assert!(x.is_normalized());
125
126 let mut constant_limbs = biguint_to_limbs::<F>(&constant, None);
127 if x.limbs.len() < constant_limbs.len() {
128 panic!(
129 "An AssignedBigUint with {} limbs in base 2^{} cannot be equal to {}",
130 x.limbs.len(),
131 LOG2_BASE,
132 constant
133 )
134 }
135
136 constant_limbs.resize(x.limbs.len(), F::ZERO);
137
138 for (i, ci) in constant_limbs.iter().enumerate() {
139 self.native_gadget.assert_equal_to_fixed(layouter, &x.limbs[i], *ci)?;
140 }
141
142 Ok(())
143 }
144
145 fn assert_not_equal_to_fixed(
146 &self,
147 layouter: &mut impl Layouter<F>,
148 x: &AssignedBigUint<F>,
149 constant: BigUint,
150 ) -> Result<(), Error> {
151 let x_eq_constant = self.is_equal_to_fixed(layouter, x, constant)?;
152 self.native_gadget.assert_equal_to_fixed(layouter, &x_eq_constant, false)
153 }
154}
155
156impl<F, N> EqualityInstructions<F, AssignedBigUint<F>> for BigUintGadget<F, N>
157where
158 F: PrimeField,
159 N: NativeInstructions<F>,
160{
161 fn is_equal(
162 &self,
163 layouter: &mut impl Layouter<F>,
164 x: &AssignedBigUint<F>,
165 y: &AssignedBigUint<F>,
166 ) -> Result<AssignedBit<F>, Error> {
167 assert!(x.is_normalized());
168 assert!(y.is_normalized());
169
170 let n = max(x.limbs.len(), y.limbs.len());
171 let mut x = x.clone();
172 let mut y = y.clone();
173 self.resize(layouter, n, &mut x)?;
174 self.resize(layouter, n, &mut y)?;
175
176 let xi_eq_yi_bits = (x.limbs.iter())
177 .zip(y.limbs.iter())
178 .map(|(xi, yi)| self.native_gadget.is_equal(layouter, xi, yi))
179 .collect::<Result<Vec<_>, Error>>()?;
180
181 self.native_gadget.and(layouter, &xi_eq_yi_bits)
182 }
183
184 fn is_not_equal(
185 &self,
186 layouter: &mut impl Layouter<F>,
187 x: &AssignedBigUint<F>,
188 y: &AssignedBigUint<F>,
189 ) -> Result<AssignedBit<F>, Error> {
190 let b = self.is_equal(layouter, x, y)?;
191 self.native_gadget.not(layouter, &b)
192 }
193
194 fn is_equal_to_fixed(
195 &self,
196 layouter: &mut impl Layouter<F>,
197 x: &AssignedBigUint<F>,
198 constant: BigUint,
199 ) -> Result<AssignedBit<F>, Error> {
200 assert!(x.is_normalized());
201
202 let mut constant_limbs = biguint_to_limbs::<F>(&constant, None);
203 if x.limbs.len() < constant_limbs.len() {
204 return self.native_gadget.assign_fixed(layouter, false);
207 }
208
209 constant_limbs.resize(x.limbs.len(), F::ZERO);
210
211 let xi_eq_yi_bits = (x.limbs.iter())
212 .zip(constant_limbs.iter())
213 .map(|(xi, ci)| self.native_gadget.is_equal_to_fixed(layouter, xi, *ci))
214 .collect::<Result<Vec<_>, Error>>()?;
215
216 self.native_gadget.and(layouter, &xi_eq_yi_bits)
217 }
218
219 fn is_not_equal_to_fixed(
220 &self,
221 layouter: &mut impl Layouter<F>,
222 x: &AssignedBigUint<F>,
223 constant: BigUint,
224 ) -> Result<AssignedBit<F>, Error> {
225 let b = self.is_equal_to_fixed(layouter, x, constant)?;
226 self.native_gadget.not(layouter, &b)
227 }
228}
229
230impl<F, N> ZeroInstructions<F, AssignedBigUint<F>> for BigUintGadget<F, N>
231where
232 F: PrimeField,
233 N: NativeInstructions<F>,
234{
235}
236
237impl<F, N> ControlFlowInstructions<F, AssignedBigUint<F>> for BigUintGadget<F, N>
238where
239 F: PrimeField,
240 N: NativeInstructions<F>,
241{
242 fn select(
243 &self,
244 layouter: &mut impl Layouter<F>,
245 cond: &AssignedBit<F>,
246 x: &AssignedBigUint<F>,
247 y: &AssignedBigUint<F>,
248 ) -> Result<AssignedBigUint<F>, Error> {
249 let n = max(x.limbs.len(), y.limbs.len());
250 let mut x = x.clone();
251 let mut y = y.clone();
252 self.resize(layouter, n, &mut x)?;
253 self.resize(layouter, n, &mut y)?;
254
255 let limbs = (x.limbs.iter())
256 .zip(y.limbs.iter())
257 .map(|(xi, yi)| self.native_gadget.select(layouter, cond, xi, yi))
258 .collect::<Result<Vec<_>, _>>()?;
259
260 let limb_size_bounds = (x.limb_size_bounds.iter())
261 .zip(y.limb_size_bounds.iter())
262 .map(|(xi_bound, yi_bound)| max(xi_bound, yi_bound))
263 .copied()
264 .collect::<Vec<_>>();
265
266 Ok(AssignedBigUint {
267 limbs,
268 limb_size_bounds,
269 })
270 }
271}
272
273impl<F, N> BigUintGadget<F, N>
274where
275 F: PrimeField,
276 N: NativeInstructions<F>,
277{
278 pub fn assign_biguint(
280 &self,
281 layouter: &mut impl Layouter<F>,
282 value: Value<BigUint>,
283 nb_bits: u32,
284 ) -> Result<AssignedBigUint<F>, Error> {
285 self.assign_bounded(layouter, value, nb_bits)
286 }
287
288 pub fn assign_fixed_biguint(
290 &self,
291 layouter: &mut impl Layouter<F>,
292 constant: BigUint,
293 ) -> Result<AssignedBigUint<F>, Error> {
294 let nb_bits = max(constant.bits(), 1) as u32;
295 let nb_limbs = nb_bits.div_ceil(LOG2_BASE) as usize;
296 let base = BigUint::one() << LOG2_BASE;
297 let limbs = big_to_limbs(nb_limbs as u32, &base, &constant)
298 .into_iter()
299 .map(|l| self.native_gadget.assign_fixed(layouter, big_to_fe::<F>(l)))
300 .collect::<Result<Vec<_>, Error>>()?;
301
302 let mut limb_size_bounds = vec![LOG2_BASE; nb_limbs];
306 *limb_size_bounds.last_mut().unwrap() = (nb_bits - 1).rem(LOG2_BASE) + 1; Ok(AssignedBigUint {
309 limbs,
310 limb_size_bounds,
311 })
312 }
313
314 pub fn constrain_as_public_input(
326 &self,
327 layouter: &mut impl Layouter<F>,
328 assigned: &AssignedBigUint<F>,
329 nb_bits: u32,
330 ) -> Result<(), Error> {
331 if nb_bits != assigned.nb_bits() {
332 return Err(Error::Synthesis(format!(
333 "constrain_as_public_input: {nb_bits} != {} (the derived `nb_bits` bound)",
334 assigned.nb_bits()
335 )));
336 }
337 self.normalize(layouter, assigned)?
338 .limbs
339 .iter()
340 .try_for_each(|l| self.native_gadget.constrain_as_public_input(layouter, l))
341 }
342
343 pub fn add(
345 &self,
346 layouter: &mut impl Layouter<F>,
347 x: &AssignedBigUint<F>,
348 y: &AssignedBigUint<F>,
349 ) -> Result<AssignedBigUint<F>, Error> {
350 let mut limbs = Vec::with_capacity(max(x.limbs.len(), y.limbs.len()));
351 let mut limb_size_bounds = vec![];
352
353 let n = min(x.limbs.len(), y.limbs.len());
354 for i in 0..n {
355 limbs.push(self.native_gadget.add(layouter, &x.limbs[i], &y.limbs[i])?);
356 limb_size_bounds.push(bound_of_addition(
357 x.limb_size_bounds[i],
358 y.limb_size_bounds[i],
359 ));
360 }
361
362 if x.limbs.len() > y.limbs.len() {
363 limbs.extend(x.limbs[n..].to_vec());
364 limb_size_bounds.extend(x.limb_size_bounds[n..].to_vec());
365 }
366
367 if y.limbs.len() > x.limbs.len() {
368 limbs.extend(y.limbs[n..].to_vec());
369 limb_size_bounds.extend(y.limb_size_bounds[n..].to_vec());
370 }
371
372 let z = AssignedBigUint {
373 limbs,
374 limb_size_bounds,
375 };
376
377 self.normalize(layouter, &z)
378 }
379
380 pub fn sub(
386 &self,
387 layouter: &mut impl Layouter<F>,
388 x: &AssignedBigUint<F>,
389 y: &AssignedBigUint<F>,
390 ) -> Result<AssignedBigUint<F>, Error> {
391 let res_value = (x.value())
392 .zip(y.value())
393 .map(|(x, y)| if x >= y { x - y } else { BigUint::ZERO });
397 let res = self.assign_bounded(layouter, res_value, x.nb_bits())?;
398 let z = self.add(layouter, &res, y)?;
399 self.assert_equal(layouter, x, &z)?;
400 Ok(res)
401 }
402
403 pub fn mul(
405 &self,
406 layouter: &mut impl Layouter<F>,
407 x: &AssignedBigUint<F>,
408 y: &AssignedBigUint<F>,
409 ) -> Result<AssignedBigUint<F>, Error> {
410 let x = self.normalize(layouter, x)?;
411 let y = self.normalize(layouter, y)?;
412
413 let native_gadget = &self.native_gadget;
414 let zero = native_gadget.assign_fixed(layouter, F::ZERO)?;
415 let nb_prod_limbs = x.limbs.len() + y.limbs.len() - 1;
416 let mut limbs = vec![zero; nb_prod_limbs];
417 let mut limb_size_bounds = vec![0; nb_prod_limbs];
418
419 for i in 0..x.limbs.len() {
420 for j in 0..y.limbs.len() {
421 let p = native_gadget.mul(layouter, &x.limbs[i], &y.limbs[j], None)?;
422 let p_bound = x.limb_size_bounds[i] + y.limb_size_bounds[j];
423 limbs[i + j] = native_gadget.add(layouter, &limbs[i + j], &p)?;
424 limb_size_bounds[i + j] = bound_of_addition(limb_size_bounds[i + j], p_bound);
425 }
426 }
427
428 let z = AssignedBigUint {
429 limbs,
430 limb_size_bounds,
431 };
432
433 self.normalize(layouter, &z)
434 }
435
436 pub fn div_rem(
441 &self,
442 layouter: &mut impl Layouter<F>,
443 x: &AssignedBigUint<F>,
444 y: &AssignedBigUint<F>,
445 ) -> Result<(AssignedBigUint<F>, AssignedBigUint<F>), Error> {
446 let (q_value, r_value) = x.value().zip(y.value()).map(|(x, y)| x.div_rem(&y)).unzip();
447
448 let q = self.assign_bounded(layouter, q_value, x.nb_bits())?;
449 let r = self.assign_bounded(layouter, r_value, y.nb_bits())?;
450
451 let q_times_y = self.mul(layouter, &q, y)?;
452 let q_times_y_plus_r = self.add(layouter, &q_times_y, &r)?;
453 self.assert_equal(layouter, x, &q_times_y_plus_r)?;
454
455 self.assert_lower_than(layouter, &r, y)?;
456
457 Ok((q, r))
458 }
459
460 pub fn mod_exp(
462 &self,
463 layouter: &mut impl Layouter<F>,
464 x: &AssignedBigUint<F>,
465 n: u64,
466 m: &AssignedBigUint<F>,
467 ) -> Result<AssignedBigUint<F>, Error> {
468 if n == 0 {
469 return self.assign_fixed_biguint(layouter, BigUint::one());
470 }
471
472 let mut n = n;
473 let mut tmp = x.clone();
474 let mut res = None;
475
476 while n > 0 {
478 if n & 1 != 0 {
479 res = match res {
480 None => Some(tmp.clone()),
481 Some(acc) => Some(self.mod_mul(layouter, &acc, &tmp, m)?),
482 };
483 }
484
485 n >>= 1;
486
487 if n > 0 {
488 tmp = self.mod_mul(layouter, &tmp, &tmp, m)?;
489 }
490 }
491
492 Ok(res.unwrap())
493 }
494
495 pub fn to_le_bits(
498 &self,
499 layouter: &mut impl Layouter<F>,
500 x: &AssignedBigUint<F>,
501 ) -> Result<Vec<AssignedBit<F>>, Error> {
502 assert!(x.is_normalized());
503
504 let bits = x
505 .limbs
506 .iter()
507 .map(|limb| {
508 self.native_gadget.assigned_to_le_bits(
509 layouter,
510 limb,
511 Some(LOG2_BASE as usize),
512 true,
513 )
514 })
515 .collect::<Result<Vec<_>, Error>>()?
516 .into_iter()
517 .flatten()
518 .collect();
519
520 Ok(bits)
521 }
522
523 #[allow(clippy::assertions_on_constants)]
526 pub fn to_le_bytes(
527 &self,
528 layouter: &mut impl Layouter<F>,
529 x: &AssignedBigUint<F>,
530 ) -> Result<Vec<AssignedByte<F>>, Error> {
531 assert!(x.is_normalized());
532 assert!(LOG2_BASE % 8 == 0);
533 let nb_bytes_per_limb = LOG2_BASE as usize / 8;
534
535 let bytes = x
536 .limbs
537 .iter()
538 .map(|limb| {
539 self.native_gadget.assigned_to_le_bytes(layouter, limb, Some(nb_bytes_per_limb))
540 })
541 .collect::<Result<Vec<_>, Error>>()?
542 .into_iter()
543 .flatten()
544 .collect();
545
546 Ok(bytes)
547 }
548
549 pub fn from_le_bits(
552 &self,
553 layouter: &mut impl Layouter<F>,
554 bits: &[AssignedBit<F>],
555 ) -> Result<AssignedBigUint<F>, Error> {
556 let limbs = bits
557 .chunks(LOG2_BASE as usize)
558 .map(|chunk_bits| self.native_gadget.assigned_from_le_bits(layouter, chunk_bits))
559 .collect::<Result<Vec<_>, Error>>()?;
560
561 let limb_size_bounds = bits
562 .chunks(LOG2_BASE as usize)
563 .map(|chunk_bits| chunk_bits.len() as u32)
564 .collect();
565
566 Ok(AssignedBigUint {
567 limbs,
568 limb_size_bounds,
569 })
570 }
571
572 #[allow(clippy::assertions_on_constants)]
575 pub fn from_le_bytes(
576 &self,
577 layouter: &mut impl Layouter<F>,
578 bytes: &[AssignedByte<F>],
579 ) -> Result<AssignedBigUint<F>, Error> {
580 assert!(LOG2_BASE % 8 == 0);
581 let nb_bytes_per_limb = LOG2_BASE as usize / 8;
582
583 let limbs = bytes
584 .chunks(nb_bytes_per_limb)
585 .map(|chunk_bytes| self.native_gadget.assigned_from_le_bytes(layouter, chunk_bytes))
586 .collect::<Result<Vec<_>, Error>>()?;
587
588 let limb_size_bounds = bytes
589 .chunks(nb_bytes_per_limb)
590 .map(|chunk_bytes| 8 * chunk_bytes.len() as u32)
591 .collect();
592
593 Ok(AssignedBigUint {
594 limbs,
595 limb_size_bounds,
596 })
597 }
598
599 pub fn lower_than(
601 &self,
602 layouter: &mut impl Layouter<F>,
603 x: &AssignedBigUint<F>,
604 y: &AssignedBigUint<F>,
605 ) -> Result<AssignedBit<F>, Error> {
606 let geq = self.geq(layouter, x, y)?;
607 self.native_gadget.not(layouter, &geq)
608 }
609}
610
611impl<F, N> BigUintGadget<F, N>
613where
614 F: PrimeField,
615 N: NativeInstructions<F>,
616{
617 fn assign_bounded(
620 &self,
621 layouter: &mut impl Layouter<F>,
622 value: Value<BigUint>,
623 nb_bits: u32,
624 ) -> Result<AssignedBigUint<F>, Error> {
625 let nb_limbs = max(nb_bits, 1).div_ceil(LOG2_BASE) as usize;
626 let mut limb_size_bounds = vec![LOG2_BASE; nb_limbs];
629 *limb_size_bounds.last_mut().unwrap() = (nb_bits - 1).rem(LOG2_BASE) + 1; let limbs = value
632 .map(|x| big_to_limbs(nb_limbs as u32, &(BigUint::one() << LOG2_BASE), &x))
633 .transpose_vec(nb_limbs)
634 .into_iter()
635 .zip(limb_size_bounds.iter())
636 .map(|(limb_value, size_bound)| {
637 self.native_gadget.assign_lower_than_fixed(
638 layouter,
639 limb_value.map(big_to_fe::<F>),
640 &(BigUint::one() << *size_bound),
641 )
642 })
643 .collect::<Result<Vec<_>, Error>>()?;
644
645 Ok(AssignedBigUint::<F> {
646 limbs,
647 limb_size_bounds,
648 })
649 }
650
651 fn normalize(
654 &self,
655 layouter: &mut impl Layouter<F>,
656 x: &AssignedBigUint<F>,
657 ) -> Result<AssignedBigUint<F>, Error> {
658 if x.is_normalized() {
659 return Ok(x.clone());
660 }
661
662 let native_gadget = &self.native_gadget;
663 let nb_limbs_output = x.nb_bits().div_ceil(LOG2_BASE) as usize;
664
665 let mut x = x.clone();
667 self.resize(layouter, nb_limbs_output, &mut x)?;
668
669 let mut carry: AssignedNative<F> = native_gadget.assign_fixed(layouter, F::ZERO)?;
670 let mut carry_size_bound = 0;
671 let mut limbs = Vec::with_capacity(nb_limbs_output);
672
673 for i in 0..nb_limbs_output {
674 let payload = native_gadget.add(layouter, &carry, &x.limbs[i])?;
675 let payload_bound = bound_of_addition(carry_size_bound, x.limb_size_bounds[i]);
676
677 if payload_bound >= F::NUM_BITS {
679 panic!("normalize: overflow over native modulus; decrease LOG2_BASE to avoid this")
680 }
681
682 let (q, limb) = self.div_rem_native_by_base(layouter, &payload, payload_bound)?;
683
684 carry_size_bound = max(payload_bound, LOG2_BASE) - LOG2_BASE;
686 carry = q;
687
688 limbs.push(limb);
689 }
690
691 native_gadget.assert_equal_to_fixed(layouter, &carry, F::ZERO)?;
693
694 Ok(AssignedBigUint {
695 limbs,
696 limb_size_bounds: vec![LOG2_BASE; nb_limbs_output],
697 })
698 }
699
700 fn resize(
707 &self,
708 layouter: &mut impl Layouter<F>,
709 n: usize,
710 x: &mut AssignedBigUint<F>,
711 ) -> Result<(), Error> {
712 if x.limbs.len() > n {
713 panic!("resize: the number of limbs is greater than the desired size");
714 }
715 let zero: AssignedNative<F> = self.native_gadget.assign_fixed(layouter, F::ZERO)?;
716 x.limbs.resize(n, zero);
717 x.limb_size_bounds.resize(n, 0);
718
719 Ok(())
720 }
721
722 fn mod_mul(
724 &self,
725 layouter: &mut impl Layouter<F>,
726 x: &AssignedBigUint<F>,
727 y: &AssignedBigUint<F>,
728 m: &AssignedBigUint<F>,
729 ) -> Result<AssignedBigUint<F>, Error> {
730 let p = self.mul(layouter, x, y)?;
731 let (_, r) = self.div_rem(layouter, &p, m)?;
732 Ok(r)
733 }
734
735 fn geq(
737 &self,
738 layouter: &mut impl Layouter<F>,
739 x: &AssignedBigUint<F>,
740 y: &AssignedBigUint<F>,
741 ) -> Result<AssignedBit<F>, Error> {
742 assert!(x.is_normalized());
743 assert!(y.is_normalized());
744
745 let n = max(x.limbs.len(), y.limbs.len());
746 let mut x = x.clone();
747 let mut y = y.clone();
748 self.resize(layouter, n, &mut x)?;
749 self.resize(layouter, n, &mut y)?;
750
751 let init = self.native_gadget.assign_fixed(layouter, true)?;
752 x.limbs.iter().zip(y.limbs.iter()).try_fold(init, |acc, (xi, yi)| {
753 let xi_eq_yi = self.native_gadget.is_equal(layouter, xi, yi)?;
754 let xi = AssignedBounded::<F>::to_assigned_bounded_unsafe(xi, LOG2_BASE);
755 let yi = AssignedBounded::<F>::to_assigned_bounded_unsafe(yi, LOG2_BASE);
756 let xi_greater_than_yi = self.native_gadget.greater_than(layouter, &xi, &yi)?;
757
758 let acc = self.native_gadget.and(layouter, &[xi_eq_yi, acc])?;
759
760 self.native_gadget.or(layouter, &[xi_greater_than_yi, acc])
761 })
762 }
763
764 fn assert_lower_than(
766 &self,
767 layouter: &mut impl Layouter<F>,
768 x: &AssignedBigUint<F>,
769 y: &AssignedBigUint<F>,
770 ) -> Result<(), Error> {
771 let b = self.geq(layouter, x, y)?;
772 self.native_gadget.assert_equal_to_fixed(layouter, &b, false)
773 }
774
775 fn div_rem_native_by_base(
785 &self,
786 layouter: &mut impl Layouter<F>,
787 x: &AssignedNative<F>,
788 x_size_bound: u32,
789 ) -> Result<(AssignedNative<F>, AssignedNative<F>), Error> {
790 assert!(x_size_bound < F::NUM_BITS);
791 let native_gadget = &self.native_gadget;
792 let base = BigUint::one() << LOG2_BASE;
793 let (q_value, r_value) = x
794 .value()
795 .map(|v| {
796 let (q, r) = fe_to_big(*v).div_rem(&base);
797 (big_to_fe(q), big_to_fe(r))
798 })
799 .unzip();
800 let shifted_x_size_bound = max(x_size_bound, LOG2_BASE) - LOG2_BASE;
801 let q_bound = BigUint::one() << shifted_x_size_bound;
802
803 let q = native_gadget.assign_lower_than_fixed(layouter, q_value, &q_bound)?;
804 let r = native_gadget.assign_lower_than_fixed(layouter, r_value, &base)?;
805
806 let q_times_base_plus_r = native_gadget.linear_combination(
807 layouter,
808 &[
809 (F::from_u128(1 << LOG2_BASE), q.clone()),
810 (F::ONE, r.clone()),
811 ],
812 F::ZERO,
813 )?;
814 native_gadget.assert_equal(layouter, x, &q_times_base_plus_r)?;
815
816 Ok((q, r))
817 }
818}
819
820#[cfg(test)]
823impl<F, N> AssignmentInstructions<F, AssignedBigUint<F>> for BigUintGadget<F, N>
824where
825 F: PrimeField,
826 N: NativeInstructions<F>,
827{
828 fn assign(
829 &self,
830 layouter: &mut impl Layouter<F>,
831 value: Value<BigUint>,
832 ) -> Result<AssignedBigUint<F>, Error> {
833 self.assign_biguint(layouter, value, TEST_NB_BITS)
834 }
835
836 fn assign_fixed(
837 &self,
838 layouter: &mut impl Layouter<F>,
839 constant: BigUint,
840 ) -> Result<AssignedBigUint<F>, Error> {
841 self.assign_fixed_biguint(layouter, constant)
842 }
843}
844
845#[cfg(any(test, feature = "testing"))]
846impl<F, N> FromScratch<F> for BigUintGadget<F, N>
847where
848 F: PrimeField,
849 N: NativeInstructions<F> + FromScratch<F>,
850{
851 type Config = <N as FromScratch<F>>::Config;
852
853 fn new_from_scratch(config: &Self::Config) -> Self {
854 let native_gadget = <N as FromScratch<F>>::new_from_scratch(config);
855 BigUintGadget::<F, N>::new(&native_gadget)
856 }
857
858 fn configure_from_scratch(
859 meta: &mut ConstraintSystem<F>,
860 instance_columns: &[Column<Instance>; 2],
861 ) -> Self::Config {
862 <N as FromScratch<F>>::configure_from_scratch(meta, instance_columns)
863 }
864
865 fn load_from_scratch(&self, layouter: &mut impl Layouter<F>) -> Result<(), Error> {
866 self.native_gadget.load_from_scratch(layouter)
867 }
868}
869
870#[cfg(test)]
871mod tests {
872
873 use ff::FromUniformBytes;
874 use midnight_curves::Fq as BlsScalar;
875 use midnight_proofs::{
876 circuit::SimpleFloorPlanner,
877 dev::MockProver,
878 plonk::{Circuit, ConstraintSystem},
879 };
880 use num_bigint::RandBigInt;
881 use num_traits::Zero;
882
883 use super::*;
884 use crate::{
885 field::{decomposition::chip::P2RDecompositionChip, NativeChip, NativeGadget},
886 instructions::{assertions, control_flow, equality, zero},
887 testing_utils::FromScratch,
888 };
889
890 type NG<F> = NativeGadget<F, P2RDecompositionChip<F>, NativeChip<F>>;
892 type BG<F> = BigUintGadget<F, NG<F>>;
893
894 macro_rules! test_field {
895 ($mod:ident, $op:ident, $field:ident, $name:expr) => {
896 $mod::tests::$op::<$field, AssignedBigUint<$field>, BG<$field>>($name);
897 };
898 }
899
900 macro_rules! test {
901 ($mod:ident, $op:ident) => {
902 #[test]
903 fn $op() {
904 test_field!($mod, $op, BlsScalar, "biguint_gadget");
905 }
906 };
907 }
908
909 test!(assertions, test_assertions);
910
911 test!(equality, test_is_equal);
912
913 test!(zero, test_zero_assertions);
914 test!(zero, test_is_zero);
915
916 test!(control_flow, test_select);
917 test!(control_flow, test_cond_assert_equal);
918 test!(control_flow, test_cond_swap);
919
920 #[derive(Clone, Debug)]
921 enum Operation {
922 Add,
923 Sub,
924 Mul,
925 Div,
926 Rem,
927 ModExp,
928 Bits,
929 Bytes,
930 Lower,
931 }
932
933 #[derive(Clone, Debug)]
934 struct TestCircuit<F, N> {
935 x: Value<BigUint>,
936 y: Value<BigUint>,
937 expected: BigUint,
938 operation: Operation,
939 _marker: PhantomData<(F, N)>,
940 }
941
942 impl<F, N> Circuit<F> for TestCircuit<F, N>
943 where
944 F: PrimeField,
945 N: NativeInstructions<F> + FromScratch<F>,
946 {
947 type Config = <N as FromScratch<F>>::Config;
948 type FloorPlanner = SimpleFloorPlanner;
949 type Params = ();
950
951 fn without_witnesses(&self) -> Self {
952 unreachable!()
953 }
954
955 fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
956 let committed_instance_column = meta.instance_column();
957 let instance_column = meta.instance_column();
958 <N as FromScratch<F>>::configure_from_scratch(
959 meta,
960 &[committed_instance_column, instance_column],
961 )
962 }
963
964 fn synthesize(
965 &self,
966 config: Self::Config,
967 mut layouter: impl Layouter<F>,
968 ) -> Result<(), Error> {
969 let native_gadget = <N as FromScratch<F>>::new_from_scratch(&config);
970 let biguint_gadget = BigUintGadget::<F, N>::new(&native_gadget);
971
972 let x = biguint_gadget.assign_biguint(&mut layouter, self.x.clone(), 1024)?;
973 let y = biguint_gadget.assign_biguint(&mut layouter, self.y.clone(), 1024)?;
974
975 let res = match self.operation {
976 Operation::Add => biguint_gadget.add(&mut layouter, &x, &y)?,
977 Operation::Sub => biguint_gadget.sub(&mut layouter, &x, &y)?,
978 Operation::Mul => biguint_gadget.mul(&mut layouter, &x, &y)?,
979 Operation::Div => biguint_gadget.div_rem(&mut layouter, &x, &y)?.0,
980 Operation::Rem => biguint_gadget.div_rem(&mut layouter, &x, &y)?.1,
981 Operation::ModExp => biguint_gadget.mod_exp(&mut layouter, &x, 3, &y)?,
982 Operation::Bits => {
983 let bits = biguint_gadget.to_le_bits(&mut layouter, &x)?;
984 biguint_gadget.from_le_bits(&mut layouter, &bits)?
985 }
986 Operation::Bytes => {
987 let bytes = biguint_gadget.to_le_bytes(&mut layouter, &x)?;
988 biguint_gadget.from_le_bytes(&mut layouter, &bytes)?
989 }
990 Operation::Lower => {
991 let b = biguint_gadget.lower_than(&mut layouter, &x, &y)?;
992 biguint_gadget.from_le_bits(&mut layouter, &[b])?
993 }
994 };
995
996 let expected = biguint_gadget.assign_fixed(&mut layouter, self.expected.clone())?;
997
998 biguint_gadget.assert_equal(&mut layouter, &expected, &res)?;
999
1000 native_gadget.load_from_scratch(&mut layouter)
1001 }
1002 }
1003
1004 fn run<F>(x: &BigUint, y: &BigUint, expected: &BigUint, operation: Operation, must_pass: bool)
1005 where
1006 F: PrimeField + FromUniformBytes<64> + Ord,
1007 {
1008 let circuit = TestCircuit::<F, NativeGadget<F, P2RDecompositionChip<F>, NativeChip<F>>> {
1009 x: Value::known(x.clone()),
1010 y: Value::known(y.clone()),
1011 expected: expected.clone(),
1012 operation,
1013 _marker: PhantomData,
1014 };
1015 let log2_nb_rows = 12;
1016 let public_inputs = vec![vec![], vec![]];
1017 match MockProver::run(log2_nb_rows, &circuit, public_inputs) {
1018 Ok(prover) => match prover.verify() {
1019 Ok(()) => assert!(must_pass),
1020 Err(e) => assert!(!must_pass, "Failed verifier with error {e:?}"),
1021 },
1022 Err(e) => assert!(!must_pass, "Failed prover with error {e:?}"),
1023 }
1024 }
1025
1026 fn random_biguint(nb_bits: u64) -> BigUint {
1027 rand::thread_rng().gen_biguint(nb_bits)
1028 }
1029
1030 #[test]
1031 fn test_add_biguint() {
1032 type F = midnight_curves::Fq;
1033 let zero = BigUint::ZERO;
1034 for _ in 0..10 {
1035 let x: BigUint = random_biguint(1024);
1036 let y: BigUint = random_biguint(1024);
1037 run::<F>(&x, &y, &(&x + &y), Operation::Add, true);
1038 run::<F>(&x, &zero, &x, Operation::Add, true);
1039 run::<F>(&x, &y, &zero, Operation::Add, false)
1040 }
1041 }
1042
1043 #[test]
1044 fn test_sub_biguint() {
1045 type F = midnight_curves::Fq;
1046 let zero = BigUint::ZERO;
1047 let one = BigUint::one();
1048 for _ in 0..10 {
1049 let x: BigUint = random_biguint(1024);
1050 let y: BigUint = random_biguint(1024);
1051 let (x, y) = if x >= y { (x, y) } else { (y, x) };
1052 run::<F>(&x, &y, &(&x - &y), Operation::Sub, true);
1053 run::<F>(&y, &x, &zero, Operation::Sub, false);
1054 run::<F>(&x, &zero, &x, Operation::Sub, true);
1055 run::<F>(&x, &x, &zero, Operation::Sub, true);
1056 run::<F>(&zero, &zero, &zero, Operation::Sub, true);
1057 run::<F>(&(&x + &one), &x, &one, Operation::Sub, true);
1058 run::<F>(&x, &y, &zero, Operation::Sub, false)
1059 }
1060 }
1061
1062 #[test]
1063 fn test_mul_biguint() {
1064 type F = midnight_curves::Fq;
1065 let zero = BigUint::ZERO;
1066 let one = BigUint::one();
1067 for _ in 0..10 {
1068 let x: BigUint = random_biguint(1024);
1069 let y: BigUint = random_biguint(1024);
1070 run::<F>(&x, &y, &(&x * &y), Operation::Mul, true);
1071 run::<F>(&x, &zero, &zero, Operation::Mul, true);
1072 run::<F>(&zero, &x, &zero, Operation::Mul, true);
1073 run::<F>(&x, &one, &x, Operation::Mul, true);
1074 run::<F>(&one, &x, &x, Operation::Mul, true);
1075 run::<F>(&x, &y, &zero, Operation::Add, false)
1076 }
1077 }
1078
1079 #[test]
1080 fn test_div_rem_biguint() {
1081 type F = midnight_curves::Fq;
1082 let zero = BigUint::ZERO;
1083 let one = BigUint::one();
1084 for _ in 0..10 {
1085 let x: BigUint = random_biguint(1024);
1086 let y: BigUint = random_biguint(1000);
1087 let (q, r) = x.div_rem(&y);
1088 let x_plus_one = &x + BigUint::one();
1089 run::<F>(&x, &y, &q, Operation::Div, true);
1090 run::<F>(&x, &one, &x, Operation::Div, true);
1091 run::<F>(&x, &x, &one, Operation::Div, true);
1092 run::<F>(&x, &x_plus_one, &zero, Operation::Div, true);
1093 run::<F>(&x, &y, &random_biguint(1024), Operation::Div, false);
1094
1095 run::<F>(&x, &y, &r, Operation::Rem, true);
1096 run::<F>(&x, &one, &zero, Operation::Rem, true);
1097 run::<F>(&x, &x, &zero, Operation::Rem, true);
1098 run::<F>(&x, &x_plus_one, &x, Operation::Rem, true);
1099 run::<F>(&x, &y, &random_biguint(1024), Operation::Rem, false)
1100 }
1101 }
1102
1103 #[test]
1104 fn test_mod_exp_biguint() {
1105 type F = midnight_curves::Fq;
1106 let zero = BigUint::ZERO;
1107 let one = BigUint::one();
1108 for _ in 0..10 {
1109 let x: BigUint = random_biguint(1024);
1110 let m: BigUint = random_biguint(1024);
1111 let res = (&x * &x * &x).div_rem(&m).1;
1112 run::<F>(&x, &m, &res, Operation::ModExp, true);
1113 run::<F>(&zero, &m, &zero, Operation::ModExp, true);
1114 run::<F>(&one, &m, &one, Operation::ModExp, true);
1115 run::<F>(&x, &m, &BigUint::ZERO, Operation::ModExp, false)
1116 }
1117 }
1118
1119 #[test]
1120 fn test_biguint_to_and_from_bits() {
1121 type F = midnight_curves::Fq;
1122 let zero = BigUint::ZERO;
1123 let one = BigUint::one();
1124 for _ in 0..10 {
1125 let x: BigUint = random_biguint(1024);
1126 run::<F>(&x, &BigUint::default(), &x, Operation::Bits, true);
1127 run::<F>(&x, &BigUint::default(), &zero, Operation::Bits, false);
1128 }
1129 run::<F>(&zero, &BigUint::default(), &zero, Operation::Bits, true);
1130 run::<F>(&one, &BigUint::default(), &one, Operation::Bits, true);
1131 }
1132
1133 #[test]
1134 fn test_biguint_to_and_from_bytes() {
1135 type F = midnight_curves::Fq;
1136 let zero = BigUint::ZERO;
1137 let one = BigUint::one();
1138 for _ in 0..10 {
1139 let x: BigUint = random_biguint(1024);
1140 run::<F>(&x, &BigUint::default(), &x, Operation::Bytes, true);
1141 run::<F>(&x, &BigUint::default(), &zero, Operation::Bytes, false);
1142 }
1143 run::<F>(&zero, &BigUint::default(), &zero, Operation::Bytes, true);
1144 run::<F>(&one, &BigUint::default(), &one, Operation::Bytes, true);
1145 }
1146
1147 #[test]
1148 fn test_lower_than_biguint() {
1149 type F = midnight_curves::Fq;
1150 let zero = BigUint::ZERO;
1151 let one = BigUint::one();
1152 for _ in 0..10 {
1153 let x: BigUint = random_biguint(1024);
1154 let y: BigUint = random_biguint(1024);
1155 let res = if x < y {
1156 BigUint::one()
1157 } else {
1158 BigUint::zero()
1159 };
1160 run::<F>(&x, &y, &res, Operation::Lower, true);
1161 run::<F>(&x, &x, &zero, Operation::Lower, true);
1162 run::<F>(&x, &x, &one, Operation::Lower, false);
1163 run::<F>(&x, &(&x + BigUint::one()), &one, Operation::Lower, true);
1164 }
1165 run::<F>(&zero, &zero, &zero, Operation::Lower, true);
1166 run::<F>(&zero, &one, &one, Operation::Lower, true);
1167 run::<F>(&one, &zero, &zero, Operation::Lower, true);
1168 run::<F>(&one, &one, &zero, Operation::Lower, true);
1169 }
1170}