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}