arnaucube
arnaucube/ark-r1cs-stdPublic Self-hosted repository
ark-r1cs-std/src/uint/and.rs
src/uint/and.rsRaw
  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}