1use std::{
24 fmt::Debug,
25 ops::{Add, Neg},
26};
27
28use ff::PrimeField;
29use midnight_proofs::{circuit::Layouter, plonk::Error};
30
31use crate::{
32 instructions::{AssertionInstructions, AssignmentInstructions},
33 types::InnerValue,
34};
35
36pub trait ArithInstructions<F, Assigned>:
38 Clone + Debug + AssignmentInstructions<F, Assigned> + AssertionInstructions<F, Assigned>
39where
40 F: PrimeField,
41 Assigned::Element:
42 PartialEq + From<u64> + Add<Output = Assigned::Element> + Neg<Output = Assigned::Element>,
43 Assigned: InnerValue,
44{
45 fn linear_combination(
68 &self,
69 layouter: &mut impl Layouter<F>,
70 terms: &[(Assigned::Element, Assigned)],
71 constant: Assigned::Element,
72 ) -> Result<Assigned, Error>;
73
74 fn add(
86 &self,
87 layouter: &mut impl Layouter<F>,
88 x: &Assigned,
89 y: &Assigned,
90 ) -> Result<Assigned, Error> {
91 self.linear_combination(
92 layouter,
93 &[
94 (Assigned::Element::from(1), x.clone()),
95 (Assigned::Element::from(1), y.clone()),
96 ],
97 Assigned::Element::from(0),
98 )
99 }
100
101 fn sub(
113 &self,
114 layouter: &mut impl Layouter<F>,
115 x: &Assigned,
116 y: &Assigned,
117 ) -> Result<Assigned, Error> {
118 self.linear_combination(
119 layouter,
120 &[
121 (Assigned::Element::from(1), x.clone()),
122 (-Assigned::Element::from(1), y.clone()),
123 ],
124 Assigned::Element::from(0),
125 )
126 }
127
128 fn mul(
140 &self,
141 layouter: &mut impl Layouter<F>,
142 x: &Assigned,
143 y: &Assigned,
144 multiplying_constant: Option<Assigned::Element>,
145 ) -> Result<Assigned, Error>;
146
147 fn div(
168 &self,
169 layouter: &mut impl Layouter<F>,
170 x: &Assigned,
171 y: &Assigned,
172 ) -> Result<Assigned, Error>;
173
174 fn neg(&self, layouter: &mut impl Layouter<F>, x: &Assigned) -> Result<Assigned, Error> {
185 self.linear_combination(
186 layouter,
187 &[(-Assigned::Element::from(1), x.clone())],
188 Assigned::Element::from(0),
189 )
190 }
191
192 fn inv(&self, layouter: &mut impl Layouter<F>, x: &Assigned) -> Result<Assigned, Error>;
212
213 fn inv0(&self, layouter: &mut impl Layouter<F>, x: &Assigned) -> Result<Assigned, Error>;
230
231 fn add_constant(
246 &self,
247 layouter: &mut impl Layouter<F>,
248 x: &Assigned,
249 constant: Assigned::Element,
250 ) -> Result<Assigned, Error> {
251 if constant == Assigned::Element::from(0) {
252 return Ok(x.clone());
253 }
254 self.linear_combination(
255 layouter,
256 &[(Assigned::Element::from(1), x.clone())],
257 constant,
258 )
259 }
260
261 fn add_constants(
281 &self,
282 layouter: &mut impl Layouter<F>,
283 xs: &[Assigned],
284 constants: &[Assigned::Element],
285 ) -> Result<Vec<Assigned>, Error> {
286 assert_eq!(xs.len(), constants.len());
287
288 (xs.iter().zip(constants.iter()))
289 .map(|(x, c)| self.add_constant(layouter, x, c.clone()))
290 .collect()
291 }
292
293 fn mul_by_constant(
307 &self,
308 layouter: &mut impl Layouter<F>,
309 x: &Assigned,
310 constant: Assigned::Element,
311 ) -> Result<Assigned, Error> {
312 if constant == Assigned::Element::from(0) {
313 return self.assign_fixed(layouter, Assigned::Element::from(0));
314 }
315 if constant == Assigned::Element::from(1) {
316 return Ok(x.clone());
317 }
318 self.linear_combination(
319 layouter,
320 &[(constant, x.clone())],
321 Assigned::Element::from(0),
322 )
323 }
324
325 fn square(&self, layouter: &mut impl Layouter<F>, x: &Assigned) -> Result<Assigned, Error> {
327 self.mul(layouter, x, x, None)
328 }
329
330 fn pow(
333 &self,
334 layouter: &mut impl Layouter<F>,
335 x: &Assigned,
336 n: u64,
337 ) -> Result<Assigned, Error> {
338 if n == 0 {
339 return self.assign_fixed(layouter, Assigned::Element::from(1));
340 }
341
342 let mut n = n;
343 let mut tmp = x.clone();
344 let mut res = None;
345
346 while n > 0 {
349 if n & 1 != 0 {
350 res = match res {
351 None => Some(tmp.clone()),
352 Some(acc) => Some(self.mul(layouter, &acc, &tmp, None)?),
353 };
354 }
355
356 n >>= 1;
357
358 if n > 0 {
359 tmp = self.square(layouter, &tmp)?;
360 }
361 }
362
363 Ok(res.unwrap())
364 }
365
366 fn add_and_mul(
368 &self,
369 layouter: &mut impl Layouter<F>,
370 (a, x): (Assigned::Element, &Assigned),
371 (b, y): (Assigned::Element, &Assigned),
372 (c, z): (Assigned::Element, &Assigned),
373 k: Assigned::Element,
374 m: Assigned::Element,
375 ) -> Result<Assigned, Error> {
376 let p = self.mul(layouter, x, y, None)?;
377 self.linear_combination(
378 layouter,
379 &[(a, x.clone()), (b, y.clone()), (c, z.clone()), (m, p)],
380 k,
381 )
382 }
383}
384
385#[cfg(test)]
386pub(crate) mod tests {
387 use std::{cmp::min, marker::PhantomData};
388
389 use ff::FromUniformBytes;
390 use midnight_proofs::{
391 circuit::{Layouter, SimpleFloorPlanner, Value},
392 dev::MockProver,
393 plonk::{Circuit, ConstraintSystem},
394 };
395 use rand::{RngCore, SeedableRng};
396 use rand_chacha::ChaCha8Rng;
397
398 use super::*;
399 use crate::{
400 testing_utils::{FromScratch, Invertible},
401 utils::circuit_modeling::circuit_to_json,
402 };
403
404 #[derive(Clone, Debug)]
405 enum Operation {
406 Add,
407 Sub,
408 Mul,
409 Div,
410 Neg,
411 Inv,
412 Pow(u64),
413 AddConst,
414 MulByConst,
415 LinearComb,
416 AddAndMul,
417 }
418
419 #[derive(Clone, Debug)]
420 struct TestCircuit<F, Assigned, ArithChip>
421 where
422 Assigned: InnerValue,
423 {
424 inputs: Vec<Assigned::Element>,
425 expected: Assigned::Element,
426 operation: Operation,
427 _marker: PhantomData<(F, Assigned, ArithChip)>,
428 }
429
430 impl<F, Assigned, ArithChip> Circuit<F> for TestCircuit<F, Assigned, ArithChip>
431 where
432 F: PrimeField,
433 Assigned: InnerValue,
434 Assigned::Element: Default
435 + PartialEq
436 + From<u64>
437 + Add<Output = Assigned::Element>
438 + Neg<Output = Assigned::Element>,
439 ArithChip: ArithInstructions<F, Assigned> + FromScratch<F>,
440 {
441 type Config = <ArithChip as FromScratch<F>>::Config;
442 type FloorPlanner = SimpleFloorPlanner;
443 type Params = ();
444
445 fn without_witnesses(&self) -> Self {
446 unreachable!()
447 }
448
449 fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
450 let committed_instance_column = meta.instance_column();
451 let instance_column = meta.instance_column();
452 ArithChip::configure_from_scratch(meta, &[committed_instance_column, instance_column])
453 }
454
455 fn synthesize(
456 &self,
457 config: Self::Config,
458 mut layouter: impl Layouter<F>,
459 ) -> Result<(), Error> {
460 let chip = ArithChip::new_from_scratch(&config);
461
462 let y_idx = min(self.inputs.len() - 1, 1);
464 let x = chip.assign(&mut layouter, Value::known(self.inputs[0].clone()))?;
465 let y = chip.assign_fixed(&mut layouter, self.inputs[y_idx].clone())?;
466 let k = self.inputs[y_idx].clone();
467
468 let res = match self.operation {
469 Operation::Add => chip.add(&mut layouter, &x, &y),
470 Operation::Sub => chip.sub(&mut layouter, &x, &y),
471 Operation::Mul => chip.mul(&mut layouter, &x, &y, None),
472 Operation::Div => chip.div(&mut layouter, &x, &y),
473 Operation::Neg => chip.neg(&mut layouter, &x),
474 Operation::Inv => chip.inv(&mut layouter, &x),
475 Operation::Pow(n) => chip.pow(&mut layouter, &x, n),
476 Operation::AddConst => chip.add_constant(&mut layouter, &x, k),
477 Operation::MulByConst => chip.mul_by_constant(&mut layouter, &x, k),
478 Operation::LinearComb => {
479 let mut terms = vec![];
480 for i in 0..(self.inputs.len() / 2) {
481 let coeff = self.inputs[2 * i].clone();
482 let x_val = self.inputs[2 * i + 1].clone();
483 let x = chip.assign(&mut layouter, Value::known(x_val))?;
484 terms.push((coeff, x));
485 }
486 let constant = self.inputs.last().unwrap().clone();
487 chip.linear_combination(&mut layouter, &terms, constant)
488 }
489 Operation::AddAndMul => chip.add_and_mul(
490 &mut layouter,
491 (Assigned::Element::from(1), &x),
492 (Assigned::Element::from(1), &y),
493 (Assigned::Element::from(0), &y),
494 Assigned::Element::from(0),
495 Assigned::Element::from(1),
496 ),
497 }?;
498
499 let expected = chip.assign_fixed(&mut layouter, self.expected.clone())?;
500 chip.assert_equal(&mut layouter, &expected, &res)?;
501
502 chip.load_from_scratch(&mut layouter)
503 }
504 }
505
506 fn run<F, Assigned, ArithChip>(
507 inputs: &[Assigned::Element],
508 expected: Assigned::Element,
509 operation: Operation,
510 must_pass: bool,
511 cost_model: bool,
512 chip_name: &str,
513 op_name: &str,
514 ) where
515 F: PrimeField + FromUniformBytes<64> + Ord,
516 Assigned: InnerValue,
517 Assigned::Element: Default
518 + PartialEq
519 + From<u64>
520 + Add<Output = Assigned::Element>
521 + Neg<Output = Assigned::Element>,
522 ArithChip: ArithInstructions<F, Assigned> + FromScratch<F>,
523 {
524 let circuit = TestCircuit::<F, Assigned, ArithChip> {
525 inputs: inputs.to_vec(),
526 expected,
527 operation: operation.clone(),
528 _marker: PhantomData,
529 };
530 let log2_nb_rows = 10;
531 let public_inputs = vec![vec![], vec![]];
532 match MockProver::run(log2_nb_rows, &circuit, public_inputs) {
533 Ok(prover) => match prover.verify() {
534 Ok(()) => assert!(must_pass),
535 Err(e) => assert!(!must_pass, "Failed verifier with error {e:?}"),
536 },
537 Err(e) => assert!(!must_pass, "Failed prover with error {e:?}"),
538 }
539
540 if cost_model {
541 circuit_to_json(chip_name, op_name, circuit);
542 }
543 }
544
545 fn i64_to_element<Element>(x: &i64) -> Element
546 where
547 Element: From<u64> + Neg<Output = Element>,
548 {
549 let mut res = Element::from(x.unsigned_abs());
550 if *x < 0 {
551 res = -res
552 }
553 res
554 }
555
556 pub fn test_add<F, Assigned, ArithChip>(chip_name: &str)
557 where
558 F: PrimeField + FromUniformBytes<64> + Ord,
559 Assigned: InnerValue,
560 Assigned::Element: Default
561 + PartialEq
562 + From<u64>
563 + Add<Output = Assigned::Element>
564 + Neg<Output = Assigned::Element>,
565 ArithChip: ArithInstructions<F, Assigned> + FromScratch<F>,
566 {
567 let mut rng = ChaCha8Rng::seed_from_u64(0xc0ffee);
568 let r = rng.next_u32() as i64;
569 let s = rng.next_u32() as i64;
570 let mut cost_model = true;
571 [
572 (r, r, 2 * r, true),
573 (r, s, r + s, true),
574 (0, 0, 0, true),
575 (0, 1, 1, true),
576 (1, 0, 1, true),
577 (1, 1, 2, true),
578 (3, 5, 8, true),
579 (1, 1, 3, false),
580 ]
581 .iter()
582 .for_each(|(x, y, expected, must_pass)| {
583 let inputs = [i64_to_element(x), i64_to_element(y)];
584 let expected: Assigned::Element = i64_to_element(expected);
585 run::<F, Assigned, ArithChip>(
586 &inputs,
587 expected.clone(),
588 Operation::Add,
589 *must_pass,
590 cost_model,
591 chip_name,
592 "add",
593 );
594 run::<F, Assigned, ArithChip>(
595 &inputs,
596 expected,
597 Operation::AddConst,
598 *must_pass,
599 cost_model,
600 chip_name,
601 "add_constant",
602 );
603 cost_model = false;
604 });
605 }
606
607 pub fn test_sub<F, Assigned, ArithChip>(cost_model_name: &str)
608 where
609 F: PrimeField + FromUniformBytes<64> + Ord,
610 Assigned: InnerValue,
611 Assigned::Element: Default
612 + PartialEq
613 + From<u64>
614 + Add<Output = Assigned::Element>
615 + Neg<Output = Assigned::Element>,
616 ArithChip: ArithInstructions<F, Assigned> + FromScratch<F>,
617 {
618 let mut rng = ChaCha8Rng::seed_from_u64(0xc0ffee);
619 let r = rng.next_u32() as i64;
620 let s = rng.next_u32() as i64;
621 let mut cost_model = true;
622 [
623 (r, r, 0, true),
624 (r, s, r - s, true),
625 (0, 0, 0, true),
626 (1, 0, 1, true),
627 (2, 1, 1, true),
628 (8, 5, 3, true),
629 (3, -5, 8, true),
630 (3, -3, 0, false),
631 ]
632 .iter()
633 .for_each(|(x, y, expected, must_pass)| {
634 let inputs = [i64_to_element(x), i64_to_element(y)];
635 let expected = i64_to_element(expected);
636 run::<F, Assigned, ArithChip>(
637 &inputs,
638 expected,
639 Operation::Sub,
640 *must_pass,
641 cost_model,
642 cost_model_name,
643 "sub",
644 );
645 cost_model = false;
646 });
647 }
648
649 pub fn test_mul<F, Assigned, ArithChip>(cost_model_name: &str)
650 where
651 F: PrimeField + FromUniformBytes<64> + Ord,
652 Assigned: InnerValue,
653 Assigned::Element: Default
654 + PartialEq
655 + From<u64>
656 + Add<Output = Assigned::Element>
657 + Neg<Output = Assigned::Element>,
658 ArithChip: ArithInstructions<F, Assigned> + FromScratch<F>,
659 {
660 let mut rng = ChaCha8Rng::seed_from_u64(0xc0ffee);
661 let r = rng.next_u32() as i64;
662 let s = rng.next_u32() as i64;
663 let mut cost_model = true;
664 [
665 (2, r, 2 * r, true),
666 (s, 0, 0, true),
667 (0, 0, 0, true),
668 (1, 0, 0, true),
669 (-2, -1, 2, true),
670 (8, 5, 40, true),
671 (3, -5, -15, true),
672 (0, 1, 1, false),
673 (2, 1, 0, false),
674 ]
675 .iter()
676 .for_each(|(x, y, expected, must_pass)| {
677 let inputs = [i64_to_element(x), i64_to_element(y)];
678 let expected: Assigned::Element = i64_to_element(expected);
679 run::<F, Assigned, ArithChip>(
680 &inputs,
681 expected.clone(),
682 Operation::Mul,
683 *must_pass,
684 cost_model,
685 cost_model_name,
686 "mul",
687 );
688 run::<F, Assigned, ArithChip>(
689 &inputs,
690 expected,
691 Operation::MulByConst,
692 *must_pass,
693 cost_model,
694 cost_model_name,
695 "mul_by_const",
696 );
697 cost_model = false;
698 });
699 }
700
701 pub fn test_div<F, Assigned, ArithChip>(cost_model_name: &str)
702 where
703 F: PrimeField + FromUniformBytes<64> + Ord,
704 Assigned: InnerValue,
705 Assigned::Element: Default
706 + PartialEq
707 + From<u64>
708 + Add<Output = Assigned::Element>
709 + Neg<Output = Assigned::Element>,
710 ArithChip: ArithInstructions<F, Assigned> + FromScratch<F>,
711 {
712 let mut rng = ChaCha8Rng::seed_from_u64(0xc0ffee);
713 let r = rng.next_u32() as i64;
714 let s = rng.next_u32() as i64;
715 let mut cost_model = true;
716 [
717 (r, r, 1, true),
718 (s, 1, s, true),
719 (0, 1, 0, true),
720 (1, 1, 1, true),
721 (2, -1, -2, true),
722 (8, 4, 2, true),
723 (91, 13, 7, true),
724 (0, 0, 0, false),
725 (3, 2, 1, false),
726 (s, s, -1, false),
727 ]
728 .iter()
729 .for_each(|(x, y, expected, must_pass)| {
730 let inputs = [i64_to_element(x), i64_to_element(y)];
731 let expected = i64_to_element(expected);
732 run::<F, Assigned, ArithChip>(
733 &inputs,
734 expected,
735 Operation::Div,
736 *must_pass,
737 cost_model,
738 cost_model_name,
739 "div",
740 );
741 cost_model = false;
742 });
743 }
744
745 pub fn test_neg<F, Assigned, ArithChip>(cost_model_name: &str)
746 where
747 F: PrimeField + FromUniformBytes<64> + Ord,
748 Assigned: InnerValue,
749 Assigned::Element: Default
750 + PartialEq
751 + From<u64>
752 + Add<Output = Assigned::Element>
753 + Neg<Output = Assigned::Element>,
754 ArithChip: ArithInstructions<F, Assigned> + FromScratch<F>,
755 {
756 let mut rng = ChaCha8Rng::seed_from_u64(0xc0ffee);
757 let r = rng.next_u32() as i64;
758 let mut cost_model = true;
759 [
760 (r, -r, true),
761 (0, 0, true),
762 (1, -1, true),
763 (-1, 1, true),
764 (2, -2, true),
765 (1, 1, false),
766 ]
767 .iter()
768 .for_each(|(x, expected, must_pass)| {
769 let inputs = [i64_to_element(x)];
770 let expected = i64_to_element(expected);
771 run::<F, Assigned, ArithChip>(
772 &inputs,
773 expected,
774 Operation::Neg,
775 *must_pass,
776 cost_model,
777 cost_model_name,
778 "neg",
779 );
780 cost_model = false;
781 });
782 }
783
784 pub fn test_inv<F, Assigned, ArithChip>(cost_model_name: &str)
785 where
786 F: PrimeField + FromUniformBytes<64> + Ord,
787 Assigned: InnerValue,
788 Assigned::Element: Default
789 + PartialEq
790 + From<u64>
791 + Add<Output = Assigned::Element>
792 + Neg<Output = Assigned::Element>
793 + Invertible,
794 ArithChip: ArithInstructions<F, Assigned> + FromScratch<F>,
795 {
796 let mut rng = ChaCha8Rng::seed_from_u64(0xc0ffee);
797 let zero = Assigned::Element::from(0);
798 let one = Assigned::Element::from(1);
799 let r = i64_to_element::<Assigned::Element>(&(rng.next_u32() as i64));
800 let mut cost_model = true;
801 [
802 (r.invert(), r.clone(), true),
803 (one.clone(), one.clone(), true),
804 (-one.clone(), -one.clone(), true),
805 (-one.clone(), one, false),
806 (r.clone(), r, false),
807 (zero.clone(), zero, false),
808 ]
809 .into_iter()
810 .for_each(|(x, expected, must_pass)| {
811 run::<F, Assigned, ArithChip>(
812 &[x],
813 expected,
814 Operation::Inv,
815 must_pass,
816 cost_model,
817 cost_model_name,
818 "inv",
819 );
820 cost_model = false;
821 });
822 }
823
824 pub fn test_pow<F, Assigned, ArithChip>(cost_model_name: &str)
825 where
826 F: PrimeField + FromUniformBytes<64> + Ord,
827 Assigned: InnerValue,
828 Assigned::Element: Default
829 + PartialEq
830 + From<u64>
831 + Add<Output = Assigned::Element>
832 + Neg<Output = Assigned::Element>,
833 ArithChip: ArithInstructions<F, Assigned> + FromScratch<F>,
834 {
835 let mut rng = ChaCha8Rng::seed_from_u64(0xc0ffee);
836 let r = (rng.next_u32() >> 1) as i64;
837 let mut cost_model = true;
838 [
839 (r, 2, r * r, true),
840 (0, 0, 1, true),
841 (1, 0, 1, true),
842 (r, 0, 1, true),
843 (1, 1, 1, true),
844 (1, 2, 1, true),
845 (2, 3, 8, true),
846 (4, 5, 1024, true),
847 (-3, 2, 9, true),
848 (-7, 3, -343, true),
849 (2, 62, 1 << 62, true),
850 (r, 0, 0, false),
851 (2, 2, 3, false),
852 ]
853 .iter()
854 .for_each(|(x, n, expected, must_pass)| {
855 let inputs = [i64_to_element(x)];
856 let expected: Assigned::Element = i64_to_element(expected);
857 run::<F, Assigned, ArithChip>(
858 &inputs,
859 expected.clone(),
860 Operation::Pow(*n),
861 *must_pass,
862 cost_model,
863 cost_model_name,
864 "pow",
865 );
866 cost_model = false;
867 });
868 }
869
870 pub fn test_linear_combination<F, Assigned, ArithChip>(cost_model_name: &str)
871 where
872 F: PrimeField + FromUniformBytes<64> + Ord,
873 Assigned: InnerValue,
874 Assigned::Element: Default
875 + PartialEq
876 + From<u64>
877 + Add<Output = Assigned::Element>
878 + Neg<Output = Assigned::Element>,
879 ArithChip: ArithInstructions<F, Assigned> + FromScratch<F>,
880 {
881 let mut rng = ChaCha8Rng::seed_from_u64(0xc0ffee);
882 let r = rng.next_u32() as i64;
883 let s = rng.next_u32() as i64;
884 let mut cost_model = true;
885 [
886 (vec![(17, r), (-4, s)], 2, 17 * r - 4 * s + 2, true),
887 (vec![], r, r, true),
888 (vec![(7, 13)], -1, 90, true),
889 (vec![(-10, 5), (5, 10)], 0, 0, true),
890 (vec![(0, 0), (0, 0)], 0, 0, true),
891 (vec![(2, 3), (4, 7), (-1, 2)], 5, 37, true),
892 (vec![(1, 1), (2, 1), (4, 1), (8, 1)], 0, 15, true),
893 (vec![(1, 3), (2, 3), (4, 3), (8, 3), (16, 3)], 7, 100, true),
894 (
895 vec![
896 (1, 3),
897 (2, 3),
898 (4, 3),
899 (8, 3),
900 (16, 3),
901 (1, 1),
902 (2, 1),
903 (4, 1),
904 (8, 1),
905 ],
906 7,
907 115,
908 true,
909 ),
910 ]
911 .iter()
912 .for_each(|(terms, constant, expected, must_pass)| {
913 let mut inputs = vec![];
914 for (coeff, x_val) in terms {
915 inputs.push(i64_to_element(coeff));
916 inputs.push(i64_to_element(x_val));
917 }
918 inputs.push(i64_to_element(constant));
919 let expected = i64_to_element(expected);
920 run::<F, Assigned, ArithChip>(
921 &inputs,
922 expected,
923 Operation::LinearComb,
924 *must_pass,
925 cost_model,
926 cost_model_name,
927 "linear_comb",
928 );
929 cost_model = false;
930 });
931 }
932
933 pub fn test_add_and_mul<F, Assigned, ArithChip>(chip_name: &str)
934 where
935 F: PrimeField + FromUniformBytes<64> + Ord,
936 Assigned: Clone + Debug + InnerValue,
937 Assigned::Element: Clone
938 + Debug
939 + Default
940 + PartialEq
941 + From<u64>
942 + Add<Output = Assigned::Element>
943 + Neg<Output = Assigned::Element>,
944 ArithChip: ArithInstructions<F, Assigned> + FromScratch<F>,
945 {
946 let mut rng = ChaCha8Rng::seed_from_u64(0xc0ffee);
947 let r = (rng.next_u32() as i64) / 2;
949 let s = (rng.next_u32() as i64) / 2;
950 let mut cost_model = true;
951 [
952 (r, s, r + s + r * s, true),
953 (0, 0, 0, true),
954 (1, 1, 2, false),
955 (1, r, 2 * r + 1, true),
956 ]
957 .iter()
958 .for_each(|(x, y, expected, must_pass)| {
959 let inputs = [i64_to_element(x), i64_to_element(y)];
960 let expected: Assigned::Element = i64_to_element(expected);
961 run::<F, Assigned, ArithChip>(
962 &inputs,
963 expected.clone(),
964 Operation::AddAndMul,
965 *must_pass,
966 cost_model,
967 chip_name,
968 "add_and_mul",
969 );
970 cost_model = false;
971 });
972 }
973}