1use std::{
24 fmt::Debug,
25 ops::{Add, Neg},
26};
27
28use midnight_proofs::{circuit::Layouter, plonk::Error};
29
30use crate::{
31 instructions::{AssertionInstructions, AssignmentInstructions},
32 types::InnerValue,
33 CircuitField,
34};
35
36pub trait ArithInstructions<F, Assigned>:
38 Clone + Debug + AssignmentInstructions<F, Assigned> + AssertionInstructions<F, Assigned>
39where
40 F: CircuitField,
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, cost_measure_end, cost_measure_start},
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: CircuitField,
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(
453 meta,
454 &mut vec![],
455 &mut vec![],
456 &[committed_instance_column, instance_column],
457 )
458 }
459
460 fn synthesize(
461 &self,
462 config: Self::Config,
463 mut layouter: impl Layouter<F>,
464 ) -> Result<(), Error> {
465 let chip = ArithChip::new_from_scratch(&config);
466
467 let y_idx = min(self.inputs.len() - 1, 1);
469 let x = chip.assign(&mut layouter, Value::known(self.inputs[0].clone()))?;
470 let y = chip.assign_fixed(&mut layouter, self.inputs[y_idx].clone())?;
471 let k = self.inputs[y_idx].clone();
472
473 cost_measure_start(&mut layouter);
474 let res = match self.operation {
475 Operation::Add => chip.add(&mut layouter, &x, &y),
476 Operation::Sub => chip.sub(&mut layouter, &x, &y),
477 Operation::Mul => chip.mul(&mut layouter, &x, &y, None),
478 Operation::Div => chip.div(&mut layouter, &x, &y),
479 Operation::Neg => chip.neg(&mut layouter, &x),
480 Operation::Inv => chip.inv(&mut layouter, &x),
481 Operation::Pow(n) => chip.pow(&mut layouter, &x, n),
482 Operation::AddConst => chip.add_constant(&mut layouter, &x, k),
483 Operation::MulByConst => chip.mul_by_constant(&mut layouter, &x, k),
484 Operation::LinearComb => {
485 let mut terms = vec![];
486 for i in 0..(self.inputs.len() / 2) {
487 let coeff = self.inputs[2 * i].clone();
488 let x_val = self.inputs[2 * i + 1].clone();
489 let x = chip.assign(&mut layouter, Value::known(x_val))?;
490 terms.push((coeff, x));
491 }
492 let constant = self.inputs.last().unwrap().clone();
493 chip.linear_combination(&mut layouter, &terms, constant)
494 }
495 Operation::AddAndMul => chip.add_and_mul(
496 &mut layouter,
497 (Assigned::Element::from(1), &x),
498 (Assigned::Element::from(1), &y),
499 (Assigned::Element::from(0), &y),
500 Assigned::Element::from(0),
501 Assigned::Element::from(1),
502 ),
503 }?;
504 cost_measure_end(&mut layouter);
505
506 let expected = chip.assign_fixed(&mut layouter, self.expected.clone())?;
507 chip.assert_equal(&mut layouter, &expected, &res)?;
508
509 chip.load_from_scratch(&mut layouter)
510 }
511 }
512
513 fn run<F, Assigned, ArithChip>(
514 inputs: &[Assigned::Element],
515 expected: Assigned::Element,
516 operation: Operation,
517 must_pass: bool,
518 cost_model: bool,
519 chip_name: &str,
520 op_name: &str,
521 ) where
522 F: CircuitField + FromUniformBytes<64> + Ord,
523 Assigned: InnerValue,
524 Assigned::Element: Default
525 + PartialEq
526 + From<u64>
527 + Add<Output = Assigned::Element>
528 + Neg<Output = Assigned::Element>,
529 ArithChip: ArithInstructions<F, Assigned> + FromScratch<F>,
530 {
531 let circuit = TestCircuit::<F, Assigned, ArithChip> {
532 inputs: inputs.to_vec(),
533 expected,
534 operation: operation.clone(),
535 _marker: PhantomData,
536 };
537 let public_inputs = vec![vec![], vec![]];
538 match MockProver::run(&circuit, public_inputs) {
539 Ok(prover) => match prover.verify() {
540 Ok(()) => assert!(must_pass),
541 Err(e) => assert!(!must_pass, "Failed verifier with error {e:?}"),
542 },
543 Err(e) => assert!(!must_pass, "Failed prover with error {e:?}"),
544 }
545
546 if cost_model {
547 circuit_to_json(chip_name, op_name, circuit);
548 }
549 }
550
551 fn i64_to_element<Element>(x: &i64) -> Element
552 where
553 Element: From<u64> + Neg<Output = Element>,
554 {
555 let mut res = Element::from(x.unsigned_abs());
556 if *x < 0 {
557 res = -res
558 }
559 res
560 }
561
562 pub fn test_add<F, Assigned, ArithChip>(chip_name: &str)
563 where
564 F: CircuitField + FromUniformBytes<64> + Ord,
565 Assigned: InnerValue,
566 Assigned::Element: Default
567 + PartialEq
568 + From<u64>
569 + Add<Output = Assigned::Element>
570 + Neg<Output = Assigned::Element>,
571 ArithChip: ArithInstructions<F, Assigned> + FromScratch<F>,
572 {
573 let mut rng = ChaCha8Rng::seed_from_u64(0xc0ffee);
574 let r = rng.next_u32() as i64;
575 let s = rng.next_u32() as i64;
576 let mut cost_model = true;
577 [
578 (r, r, 2 * r, true),
579 (r, s, r + s, true),
580 (0, 0, 0, true),
581 (0, 1, 1, true),
582 (1, 0, 1, true),
583 (1, 1, 2, true),
584 (3, 5, 8, true),
585 (1, 1, 3, false),
586 ]
587 .iter()
588 .for_each(|(x, y, expected, must_pass)| {
589 let inputs = [i64_to_element(x), i64_to_element(y)];
590 let expected: Assigned::Element = i64_to_element(expected);
591 run::<F, Assigned, ArithChip>(
592 &inputs,
593 expected.clone(),
594 Operation::Add,
595 *must_pass,
596 cost_model,
597 chip_name,
598 "add",
599 );
600 run::<F, Assigned, ArithChip>(
601 &inputs,
602 expected,
603 Operation::AddConst,
604 *must_pass,
605 cost_model,
606 chip_name,
607 "add_constant",
608 );
609 cost_model = false;
610 });
611 }
612
613 pub fn test_sub<F, Assigned, ArithChip>(cost_model_name: &str)
614 where
615 F: CircuitField + FromUniformBytes<64> + Ord,
616 Assigned: InnerValue,
617 Assigned::Element: Default
618 + PartialEq
619 + From<u64>
620 + Add<Output = Assigned::Element>
621 + Neg<Output = Assigned::Element>,
622 ArithChip: ArithInstructions<F, Assigned> + FromScratch<F>,
623 {
624 let mut rng = ChaCha8Rng::seed_from_u64(0xc0ffee);
625 let r = rng.next_u32() as i64;
626 let s = rng.next_u32() as i64;
627 let mut cost_model = true;
628 [
629 (r, r, 0, true),
630 (r, s, r - s, true),
631 (0, 0, 0, true),
632 (1, 0, 1, true),
633 (2, 1, 1, true),
634 (8, 5, 3, true),
635 (3, -5, 8, true),
636 (3, -3, 0, false),
637 ]
638 .iter()
639 .for_each(|(x, y, expected, must_pass)| {
640 let inputs = [i64_to_element(x), i64_to_element(y)];
641 let expected = i64_to_element(expected);
642 run::<F, Assigned, ArithChip>(
643 &inputs,
644 expected,
645 Operation::Sub,
646 *must_pass,
647 cost_model,
648 cost_model_name,
649 "sub",
650 );
651 cost_model = false;
652 });
653 }
654
655 pub fn test_mul<F, Assigned, ArithChip>(cost_model_name: &str)
656 where
657 F: CircuitField + FromUniformBytes<64> + Ord,
658 Assigned: InnerValue,
659 Assigned::Element: Default
660 + PartialEq
661 + From<u64>
662 + Add<Output = Assigned::Element>
663 + Neg<Output = Assigned::Element>,
664 ArithChip: ArithInstructions<F, Assigned> + FromScratch<F>,
665 {
666 let mut rng = ChaCha8Rng::seed_from_u64(0xc0ffee);
667 let r = rng.next_u32() as i64;
668 let s = rng.next_u32() as i64;
669 let mut cost_model = true;
670 [
671 (2, r, 2 * r, true),
672 (s, 0, 0, true),
673 (0, 0, 0, true),
674 (1, 0, 0, true),
675 (-2, -1, 2, true),
676 (8, 5, 40, true),
677 (3, -5, -15, true),
678 (0, 1, 1, false),
679 (2, 1, 0, false),
680 ]
681 .iter()
682 .for_each(|(x, y, expected, must_pass)| {
683 let inputs = [i64_to_element(x), i64_to_element(y)];
684 let expected: Assigned::Element = i64_to_element(expected);
685 run::<F, Assigned, ArithChip>(
686 &inputs,
687 expected.clone(),
688 Operation::Mul,
689 *must_pass,
690 cost_model,
691 cost_model_name,
692 "mul",
693 );
694 run::<F, Assigned, ArithChip>(
695 &inputs,
696 expected,
697 Operation::MulByConst,
698 *must_pass,
699 cost_model,
700 cost_model_name,
701 "mul_by_const",
702 );
703 cost_model = false;
704 });
705 }
706
707 pub fn test_div<F, Assigned, ArithChip>(cost_model_name: &str)
708 where
709 F: CircuitField + FromUniformBytes<64> + Ord,
710 Assigned: InnerValue,
711 Assigned::Element: Default
712 + PartialEq
713 + From<u64>
714 + Add<Output = Assigned::Element>
715 + Neg<Output = Assigned::Element>,
716 ArithChip: ArithInstructions<F, Assigned> + FromScratch<F>,
717 {
718 let mut rng = ChaCha8Rng::seed_from_u64(0xc0ffee);
719 let r = rng.next_u32() as i64;
720 let s = rng.next_u32() as i64;
721 let mut cost_model = true;
722 [
723 (r, r, 1, true),
724 (s, 1, s, true),
725 (0, 1, 0, true),
726 (1, 1, 1, true),
727 (2, -1, -2, true),
728 (8, 4, 2, true),
729 (91, 13, 7, true),
730 (0, 0, 0, false),
731 (3, 2, 1, false),
732 (s, s, -1, false),
733 ]
734 .iter()
735 .for_each(|(x, y, expected, must_pass)| {
736 let inputs = [i64_to_element(x), i64_to_element(y)];
737 let expected = i64_to_element(expected);
738 run::<F, Assigned, ArithChip>(
739 &inputs,
740 expected,
741 Operation::Div,
742 *must_pass,
743 cost_model,
744 cost_model_name,
745 "div",
746 );
747 cost_model = false;
748 });
749 }
750
751 pub fn test_neg<F, Assigned, ArithChip>(cost_model_name: &str)
752 where
753 F: CircuitField + FromUniformBytes<64> + Ord,
754 Assigned: InnerValue,
755 Assigned::Element: Default
756 + PartialEq
757 + From<u64>
758 + Add<Output = Assigned::Element>
759 + Neg<Output = Assigned::Element>,
760 ArithChip: ArithInstructions<F, Assigned> + FromScratch<F>,
761 {
762 let mut rng = ChaCha8Rng::seed_from_u64(0xc0ffee);
763 let r = rng.next_u32() as i64;
764 let mut cost_model = true;
765 [
766 (r, -r, true),
767 (0, 0, true),
768 (1, -1, true),
769 (-1, 1, true),
770 (2, -2, true),
771 (1, 1, false),
772 ]
773 .iter()
774 .for_each(|(x, expected, must_pass)| {
775 let inputs = [i64_to_element(x)];
776 let expected = i64_to_element(expected);
777 run::<F, Assigned, ArithChip>(
778 &inputs,
779 expected,
780 Operation::Neg,
781 *must_pass,
782 cost_model,
783 cost_model_name,
784 "neg",
785 );
786 cost_model = false;
787 });
788 }
789
790 pub fn test_inv<F, Assigned, ArithChip>(cost_model_name: &str)
791 where
792 F: CircuitField + FromUniformBytes<64> + Ord,
793 Assigned: InnerValue,
794 Assigned::Element: Default
795 + PartialEq
796 + From<u64>
797 + Add<Output = Assigned::Element>
798 + Neg<Output = Assigned::Element>
799 + Invertible,
800 ArithChip: ArithInstructions<F, Assigned> + FromScratch<F>,
801 {
802 let mut rng = ChaCha8Rng::seed_from_u64(0xc0ffee);
803 let zero = Assigned::Element::from(0);
804 let one = Assigned::Element::from(1);
805 let r = i64_to_element::<Assigned::Element>(&(rng.next_u32() as i64));
806 let mut cost_model = true;
807 [
808 (r.invert(), r.clone(), true),
809 (one.clone(), one.clone(), true),
810 (-one.clone(), -one.clone(), true),
811 (-one.clone(), one, false),
812 (r.clone(), r, false),
813 (zero.clone(), zero, false),
814 ]
815 .into_iter()
816 .for_each(|(x, expected, must_pass)| {
817 run::<F, Assigned, ArithChip>(
818 &[x],
819 expected,
820 Operation::Inv,
821 must_pass,
822 cost_model,
823 cost_model_name,
824 "inv",
825 );
826 cost_model = false;
827 });
828 }
829
830 pub fn test_pow<F, Assigned, ArithChip>(cost_model_name: &str)
831 where
832 F: CircuitField + FromUniformBytes<64> + Ord,
833 Assigned: InnerValue,
834 Assigned::Element: Default
835 + PartialEq
836 + From<u64>
837 + Add<Output = Assigned::Element>
838 + Neg<Output = Assigned::Element>,
839 ArithChip: ArithInstructions<F, Assigned> + FromScratch<F>,
840 {
841 let mut rng = ChaCha8Rng::seed_from_u64(0xc0ffee);
842 let r = (rng.next_u32() >> 1) as i64;
843 let mut cost_model = true;
844 [
845 (r, 2, r * r, true),
846 (0, 0, 1, true),
847 (1, 0, 1, true),
848 (r, 0, 1, true),
849 (1, 1, 1, true),
850 (1, 2, 1, true),
851 (2, 3, 8, true),
852 (4, 5, 1024, true),
853 (-3, 2, 9, true),
854 (-7, 3, -343, true),
855 (2, 62, 1 << 62, true),
856 (r, 0, 0, false),
857 (2, 2, 3, false),
858 ]
859 .iter()
860 .for_each(|(x, n, expected, must_pass)| {
861 let inputs = [i64_to_element(x)];
862 let expected: Assigned::Element = i64_to_element(expected);
863 run::<F, Assigned, ArithChip>(
864 &inputs,
865 expected.clone(),
866 Operation::Pow(*n),
867 *must_pass,
868 cost_model,
869 cost_model_name,
870 "pow",
871 );
872 cost_model = false;
873 });
874 }
875
876 pub fn test_linear_combination<F, Assigned, ArithChip>(cost_model_name: &str)
877 where
878 F: CircuitField + FromUniformBytes<64> + Ord,
879 Assigned: InnerValue,
880 Assigned::Element: Default
881 + PartialEq
882 + From<u64>
883 + Add<Output = Assigned::Element>
884 + Neg<Output = Assigned::Element>,
885 ArithChip: ArithInstructions<F, Assigned> + FromScratch<F>,
886 {
887 let mut rng = ChaCha8Rng::seed_from_u64(0xc0ffee);
888 let r = rng.next_u32() as i64;
889 let s = rng.next_u32() as i64;
890 let mut cost_model = true;
891 [
892 (vec![(17, r), (-4, s)], 2, 17 * r - 4 * s + 2, true),
893 (vec![], r, r, true),
894 (vec![(7, 13)], -1, 90, true),
895 (vec![(-10, 5), (5, 10)], 0, 0, true),
896 (vec![(0, 0), (0, 0)], 0, 0, true),
897 (vec![(2, 3), (4, 7), (-1, 2)], 5, 37, true),
898 (vec![(1, 1), (2, 1), (4, 1), (8, 1)], 0, 15, true),
899 (vec![(1, 3), (2, 3), (4, 3), (8, 3), (16, 3)], 7, 100, true),
900 (
901 vec![
902 (1, 3),
903 (2, 3),
904 (4, 3),
905 (8, 3),
906 (16, 3),
907 (1, 1),
908 (2, 1),
909 (4, 1),
910 (8, 1),
911 ],
912 7,
913 115,
914 true,
915 ),
916 ]
917 .iter()
918 .for_each(|(terms, constant, expected, must_pass)| {
919 let mut inputs = vec![];
920 for (coeff, x_val) in terms {
921 inputs.push(i64_to_element(coeff));
922 inputs.push(i64_to_element(x_val));
923 }
924 inputs.push(i64_to_element(constant));
925 let expected = i64_to_element(expected);
926 run::<F, Assigned, ArithChip>(
927 &inputs,
928 expected,
929 Operation::LinearComb,
930 *must_pass,
931 cost_model,
932 cost_model_name,
933 "linear_comb",
934 );
935 cost_model = false;
936 });
937 }
938
939 pub fn test_add_and_mul<F, Assigned, ArithChip>(chip_name: &str)
940 where
941 F: CircuitField + FromUniformBytes<64> + Ord,
942 Assigned: Clone + Debug + InnerValue,
943 Assigned::Element: Clone
944 + Debug
945 + Default
946 + PartialEq
947 + From<u64>
948 + Add<Output = Assigned::Element>
949 + Neg<Output = Assigned::Element>,
950 ArithChip: ArithInstructions<F, Assigned> + FromScratch<F>,
951 {
952 let mut rng = ChaCha8Rng::seed_from_u64(0xc0ffee);
953 let r = (rng.next_u32() as i64) / 2;
955 let s = (rng.next_u32() as i64) / 2;
956 let mut cost_model = true;
957 [
958 (r, s, r + s + r * s, true),
959 (0, 0, 0, true),
960 (1, 1, 2, false),
961 (1, r, 2 * r + 1, true),
962 ]
963 .iter()
964 .for_each(|(x, y, expected, must_pass)| {
965 let inputs = [i64_to_element(x), i64_to_element(y)];
966 let expected: Assigned::Element = i64_to_element(expected);
967 run::<F, Assigned, ArithChip>(
968 &inputs,
969 expected.clone(),
970 Operation::AddAndMul,
971 *must_pass,
972 cost_model,
973 chip_name,
974 "add_and_mul",
975 );
976 cost_model = false;
977 });
978 }
979}