ark_r1cs_std/uint/add/
wrapping.rs

1use ark_ff::PrimeField;
2use ark_relations::r1cs::SynthesisError;
3
4use crate::uint::*;
5use crate::R1CSVar;
6
7impl<const N: usize, T: PrimUInt, F: PrimeField> UInt<N, T, F> {
8    /// Compute `*self = self.wrapping_add(other)`.
9    pub fn wrapping_add_in_place(&mut self, other: &Self) {
10        let result = Self::wrapping_add_many(&[self.clone(), other.clone()]).unwrap();
11        *self = result;
12    }
13
14    /// Compute `self.wrapping_add(other)`.
15    pub fn wrapping_add(&self, other: &Self) -> Self {
16        let mut result = self.clone();
17        result.wrapping_add_in_place(other);
18        result
19    }
20
21    /// Perform wrapping addition of `operands`.
22    /// Computes `operands[0].wrapping_add(operands[1]).wrapping_add(operands[2])...`.
23    ///
24    /// The user must ensure that overflow does not occur.
25    #[tracing::instrument(target = "r1cs", skip(operands))]
26    pub fn wrapping_add_many(operands: &[Self]) -> Result<Self, SynthesisError>
27    where
28        F: PrimeField,
29    {
30        let (mut sum_bits, value) = Self::add_many_helper(operands, |a, b| a.wrapping_add(&b))?;
31        if operands.is_constant() {
32            // If all operands are constant, then the result is also constant.
33            // In this case, we can return early.
34            Ok(UInt::constant(value.unwrap()))
35        } else {
36            sum_bits.truncate(N);
37            Ok(UInt {
38                bits: sum_bits.try_into().unwrap(),
39                value,
40            })
41        }
42    }
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48    use crate::{
49        alloc::{AllocVar, AllocationMode},
50        prelude::EqGadget,
51        uint::test_utils::{run_binary_exhaustive, run_binary_random},
52        R1CSVar,
53    };
54    use ark_ff::PrimeField;
55    use ark_test_curves::bls12_381::Fr;
56
57    fn uint_wrapping_add<T: PrimUInt, const N: usize, F: PrimeField>(
58        a: UInt<N, T, F>,
59        b: UInt<N, T, F>,
60    ) -> Result<(), SynthesisError> {
61        let cs = a.cs().or(b.cs());
62        let both_constant = a.is_constant() && b.is_constant();
63        let computed = a.wrapping_add(&b);
64        let expected_mode = if both_constant {
65            AllocationMode::Constant
66        } else {
67            AllocationMode::Witness
68        };
69        let expected = UInt::new_variable(
70            cs.clone(),
71            || Ok(a.value()?.wrapping_add(&b.value()?)),
72            expected_mode,
73        )?;
74        assert_eq!(expected.value(), computed.value());
75        expected.enforce_equal(&computed)?;
76        if !both_constant {
77            assert!(cs.is_satisfied().unwrap());
78        }
79        Ok(())
80    }
81
82    #[test]
83    fn u8_wrapping_add() {
84        run_binary_exhaustive(uint_wrapping_add::<u8, 8, Fr>).unwrap()
85    }
86
87    #[test]
88    fn u16_wrapping_add() {
89        run_binary_random::<1000, 16, _, _>(uint_wrapping_add::<u16, 16, Fr>).unwrap()
90    }
91
92    #[test]
93    fn u32_wrapping_add() {
94        run_binary_random::<1000, 32, _, _>(uint_wrapping_add::<u32, 32, Fr>).unwrap()
95    }
96
97    #[test]
98    fn u64_wrapping_add() {
99        run_binary_random::<1000, 64, _, _>(uint_wrapping_add::<u64, 64, Fr>).unwrap()
100    }
101
102    #[test]
103    fn u128_wrapping_add() {
104        run_binary_random::<1000, 128, _, _>(uint_wrapping_add::<u128, 128, Fr>).unwrap()
105    }
106}