ark_r1cs_std/uint/
and.rs

1use ark_ff::Field;
2use ark_relations::r1cs::SynthesisError;
3use ark_std::{ops::BitAnd, ops::BitAndAssign};
4
5use super::*;
6
7impl<const N: usize, T: PrimUInt, F: Field> UInt<N, T, F> {
8    fn _and(&self, other: &Self) -> Result<Self, SynthesisError> {
9        let mut result = self.clone();
10        result._and_in_place(other)?;
11        Ok(result)
12    }
13
14    fn _and_in_place(&mut self, other: &Self) -> Result<(), SynthesisError> {
15        for (a, b) in self.bits.iter_mut().zip(&other.bits) {
16            *a &= b;
17        }
18        self.value = self.value.and_then(|a| Some(a & other.value?));
19        Ok(())
20    }
21}
22
23impl<'a, const N: usize, T: PrimUInt, F: Field> BitAnd<Self> for &'a UInt<N, T, F> {
24    type Output = UInt<N, T, F>;
25    /// Outputs `self & other`.
26    ///
27    /// If at least one of `self` and `other` are constants, then this method
28    /// *does not* create any constraints or variables.
29    ///
30    /// ```
31    /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> {
32    /// // We'll use the BLS12-381 scalar field for our constraints.
33    /// use ark_test_curves::bls12_381::Fr;
34    /// use ark_relations::r1cs::*;
35    /// use ark_r1cs_std::prelude::*;
36    ///
37    /// let cs = ConstraintSystem::<Fr>::new_ref();
38    /// let a = UInt8::new_witness(cs.clone(), || Ok(16))?;
39    /// let b = UInt8::new_witness(cs.clone(), || Ok(17))?;
40    /// let c = UInt8::new_witness(cs.clone(), || Ok(16 & 17))?;
41    ///
42    /// (a & &b).enforce_equal(&c)?;
43    /// assert!(cs.is_satisfied().unwrap());
44    /// # Ok(())
45    /// # }
46    /// ```
47    #[tracing::instrument(target = "r1cs", skip(self, other))]
48    fn bitand(self, other: Self) -> Self::Output {
49        self._and(other).unwrap()
50    }
51}
52
53impl<'a, const N: usize, T: PrimUInt, F: Field> BitAnd<&'a Self> for UInt<N, T, F> {
54    type Output = UInt<N, T, F>;
55    /// Outputs `self & other`.
56    ///
57    /// If at least one of `self` and `other` are constants, then this method
58    /// *does not* create any constraints or variables.
59    ///
60    /// ```
61    /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> {
62    /// // We'll use the BLS12-381 scalar field for our constraints.
63    /// use ark_test_curves::bls12_381::Fr;
64    /// use ark_relations::r1cs::*;
65    /// use ark_r1cs_std::prelude::*;
66    ///
67    /// let cs = ConstraintSystem::<Fr>::new_ref();
68    /// let a = UInt8::new_witness(cs.clone(), || Ok(16))?;
69    /// let b = UInt8::new_witness(cs.clone(), || Ok(17))?;
70    /// let c = UInt8::new_witness(cs.clone(), || Ok(16 & 17))?;
71    ///
72    /// (a & &b).enforce_equal(&c)?;
73    /// assert!(cs.is_satisfied().unwrap());
74    /// # Ok(())
75    /// # }
76    /// ```
77    #[tracing::instrument(target = "r1cs", skip(self, other))]
78    fn bitand(mut self, other: &Self) -> Self::Output {
79        self._and_in_place(other).unwrap();
80        self
81    }
82}
83
84impl<'a, const N: usize, T: PrimUInt, F: Field> BitAnd<UInt<N, T, F>> for &'a UInt<N, T, F> {
85    type Output = UInt<N, T, F>;
86
87    /// Outputs `self & other`.
88    ///
89    /// If at least one of `self` and `other` are constants, then this method
90    /// *does not* create any constraints or variables.
91    ///
92    /// ```
93    /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> {
94    /// // We'll use the BLS12-381 scalar field for our constraints.
95    /// use ark_test_curves::bls12_381::Fr;
96    /// use ark_relations::r1cs::*;
97    /// use ark_r1cs_std::prelude::*;
98    ///
99    /// let cs = ConstraintSystem::<Fr>::new_ref();
100    /// let a = UInt8::new_witness(cs.clone(), || Ok(16))?;
101    /// let b = UInt8::new_witness(cs.clone(), || Ok(17))?;
102    /// let c = UInt8::new_witness(cs.clone(), || Ok(16 & 17))?;
103    ///
104    /// (a & &b).enforce_equal(&c)?;
105    /// assert!(cs.is_satisfied().unwrap());
106    /// # Ok(())
107    /// # }
108    /// ```
109    #[tracing::instrument(target = "r1cs", skip(self, other))]
110    fn bitand(self, other: UInt<N, T, F>) -> Self::Output {
111        other & self
112    }
113}
114
115impl<const N: usize, T: PrimUInt, F: Field> BitAnd<Self> for UInt<N, T, F> {
116    type Output = Self;
117
118    /// Outputs `self & other`.
119    ///
120    /// If at least one of `self` and `other` are constants, then this method
121    /// *does not* create any constraints or variables.
122    ///
123    /// ```
124    /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> {
125    /// // We'll use the BLS12-381 scalar field for our constraints.
126    /// use ark_test_curves::bls12_381::Fr;
127    /// use ark_relations::r1cs::*;
128    /// use ark_r1cs_std::prelude::*;
129    ///
130    /// let cs = ConstraintSystem::<Fr>::new_ref();
131    /// let a = UInt8::new_witness(cs.clone(), || Ok(16))?;
132    /// let b = UInt8::new_witness(cs.clone(), || Ok(17))?;
133    /// let c = UInt8::new_witness(cs.clone(), || Ok(16 & 17))?;
134    ///
135    /// (a & &b).enforce_equal(&c)?;
136    /// assert!(cs.is_satisfied().unwrap());
137    /// # Ok(())
138    /// # }
139    /// ```
140    #[tracing::instrument(target = "r1cs", skip(self, other))]
141    fn bitand(self, other: Self) -> Self::Output {
142        self & &other
143    }
144}
145
146impl<'a, const N: usize, T: PrimUInt, F: Field> BitAnd<T> for UInt<N, T, F> {
147    type Output = UInt<N, T, F>;
148
149    #[tracing::instrument(target = "r1cs", skip(self, other))]
150    fn bitand(self, other: T) -> Self::Output {
151        self & UInt::constant(other)
152    }
153}
154
155impl<'a, const N: usize, T: PrimUInt, F: Field> BitAnd<&'a T> for UInt<N, T, F> {
156    type Output = UInt<N, T, F>;
157
158    #[tracing::instrument(target = "r1cs", skip(self, other))]
159    fn bitand(self, other: &'a T) -> Self::Output {
160        self & UInt::constant(*other)
161    }
162}
163
164impl<'a, const N: usize, T: PrimUInt, F: Field> BitAnd<&'a T> for &'a UInt<N, T, F> {
165    type Output = UInt<N, T, F>;
166
167    #[tracing::instrument(target = "r1cs", skip(self, other))]
168    fn bitand(self, other: &'a T) -> Self::Output {
169        self & UInt::constant(*other)
170    }
171}
172
173impl<'a, const N: usize, T: PrimUInt, F: Field> BitAnd<T> for &'a UInt<N, T, F> {
174    type Output = UInt<N, T, F>;
175
176    #[tracing::instrument(target = "r1cs", skip(self, other))]
177    fn bitand(self, other: T) -> Self::Output {
178        self & UInt::constant(other)
179    }
180}
181
182impl<const N: usize, T: PrimUInt, F: Field> BitAndAssign<Self> for UInt<N, T, F> {
183    /// Sets `self = self & other`.
184    ///
185    /// If at least one of `self` and `other` are constants, then this method
186    /// *does not* create any constraints or variables.
187    ///
188    /// ```
189    /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> {
190    /// // We'll use the BLS12-381 scalar field for our constraints.
191    /// use ark_test_curves::bls12_381::Fr;
192    /// use ark_relations::r1cs::*;
193    /// use ark_r1cs_std::prelude::*;
194    ///
195    /// let cs = ConstraintSystem::<Fr>::new_ref();
196    /// let mut a = UInt8::new_witness(cs.clone(), || Ok(16))?;
197    /// let b = UInt8::new_witness(cs.clone(), || Ok(17))?;
198    /// let c = UInt8::new_witness(cs.clone(), || Ok(16 & 17))?;
199    ///
200    /// a &= &b;
201    /// a.enforce_equal(&c)?;
202    /// assert!(cs.is_satisfied().unwrap());
203    /// # Ok(())
204    /// # }
205    /// ```
206    #[tracing::instrument(target = "r1cs", skip(self, other))]
207    fn bitand_assign(&mut self, other: Self) {
208        self._and_in_place(&other).unwrap();
209    }
210}
211
212impl<'a, const N: usize, T: PrimUInt, F: Field> BitAndAssign<&'a Self> for UInt<N, T, F> {
213    /// Sets `self = self & other`.
214    ///
215    /// If at least one of `self` and `other` are constants, then this method
216    /// *does not* create any constraints or variables.
217    ///
218    /// ```
219    /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> {
220    /// // We'll use the BLS12-381 scalar field for our constraints.
221    /// use ark_test_curves::bls12_381::Fr;
222    /// use ark_relations::r1cs::*;
223    /// use ark_r1cs_std::prelude::*;
224    ///
225    /// let cs = ConstraintSystem::<Fr>::new_ref();
226    /// let mut a = UInt8::new_witness(cs.clone(), || Ok(16))?;
227    /// let b = UInt8::new_witness(cs.clone(), || Ok(17))?;
228    /// let c = UInt8::new_witness(cs.clone(), || Ok(16 & 17))?;
229    ///
230    /// a &= &b;
231    /// a.enforce_equal(&c)?;
232    /// assert!(cs.is_satisfied().unwrap());
233    /// # Ok(())
234    /// # }
235    /// ```
236    #[tracing::instrument(target = "r1cs", skip(self, other))]
237    fn bitand_assign(&mut self, other: &'a Self) {
238        self._and_in_place(&other).unwrap();
239    }
240}
241
242impl<const N: usize, T: PrimUInt, F: Field> BitAndAssign<T> for UInt<N, T, F> {
243    #[tracing::instrument(target = "r1cs", skip(self, other))]
244    fn bitand_assign(&mut self, other: T) {
245        *self &= &Self::constant(other);
246    }
247}
248
249impl<'a, const N: usize, T: PrimUInt, F: Field> BitAndAssign<&'a T> for UInt<N, T, F> {
250    #[tracing::instrument(target = "r1cs", skip(self, other))]
251    fn bitand_assign(&mut self, other: &'a T) {
252        *self &= &Self::constant(*other);
253    }
254}
255
256#[cfg(test)]
257mod tests {
258    use super::*;
259    use crate::{
260        alloc::{AllocVar, AllocationMode},
261        prelude::EqGadget,
262        uint::test_utils::{run_binary_exhaustive_both, run_binary_random_both},
263        R1CSVar,
264    };
265    use ark_ff::PrimeField;
266    use ark_test_curves::bls12_381::Fr;
267
268    fn uint_and<T: PrimUInt, const N: usize, F: PrimeField>(
269        a: UInt<N, T, F>,
270        b: UInt<N, T, F>,
271    ) -> Result<(), SynthesisError> {
272        let cs = a.cs().or(b.cs());
273        let both_constant = a.is_constant() && b.is_constant();
274        let computed = &a & &b;
275        let expected_mode = if both_constant {
276            AllocationMode::Constant
277        } else {
278            AllocationMode::Witness
279        };
280        let expected = UInt::<N, T, F>::new_variable(
281            cs.clone(),
282            || Ok(a.value()? & b.value()?),
283            expected_mode,
284        )?;
285        assert_eq!(expected.value(), computed.value());
286        expected.enforce_equal(&computed)?;
287        if !both_constant {
288            assert!(cs.is_satisfied().unwrap());
289        }
290        Ok(())
291    }
292
293    fn uint_and_native<T: PrimUInt, const N: usize, F: PrimeField>(
294        a: UInt<N, T, F>,
295        b: T,
296    ) -> Result<(), SynthesisError> {
297        let cs = a.cs();
298        let computed = &a & b;
299        let expected_mode = if a.is_constant() {
300            AllocationMode::Constant
301        } else {
302            AllocationMode::Witness
303        };
304        let expected =
305            UInt::<N, T, F>::new_variable(cs.clone(), || Ok(a.value()? & b), expected_mode)?;
306        assert_eq!(expected.value(), computed.value());
307        expected.enforce_equal(&computed)?;
308        if !a.is_constant() {
309            assert!(cs.is_satisfied().unwrap());
310        }
311        Ok(())
312    }
313
314    #[test]
315    fn u8_and() {
316        run_binary_exhaustive_both(uint_and::<u8, 8, Fr>, uint_and_native::<u8, 8, Fr>).unwrap()
317    }
318
319    #[test]
320    fn u16_and() {
321        run_binary_random_both::<1000, 16, _, _>(
322            uint_and::<u16, 16, Fr>,
323            uint_and_native::<u16, 16, Fr>,
324        )
325        .unwrap()
326    }
327
328    #[test]
329    fn u32_and() {
330        run_binary_random_both::<1000, 32, _, _>(
331            uint_and::<u32, 32, Fr>,
332            uint_and_native::<u32, 32, Fr>,
333        )
334        .unwrap()
335    }
336
337    #[test]
338    fn u64_and() {
339        run_binary_random_both::<1000, 64, _, _>(
340            uint_and::<u64, 64, Fr>,
341            uint_and_native::<u64, 64, Fr>,
342        )
343        .unwrap()
344    }
345
346    #[test]
347    fn u128_and() {
348        run_binary_random_both::<1000, 128, _, _>(
349            uint_and::<u128, 128, Fr>,
350            uint_and_native::<u128, 128, Fr>,
351        )
352        .unwrap()
353    }
354}