ark-r1cs-std/src/uint/xor.rs
src/uint/xor.rsRaw
1use ark_ff::Field;
2use ark_relations::r1cs::SynthesisError;
3use ark_std::{ops::BitXor, ops::BitXorAssign};
4
5use super::*;
6
7impl<const N: usize, T: PrimUInt, F: Field> UInt<N, T, F> {
8 fn _xor(&self, other: &Self) -> Result<Self, SynthesisError> {
9 let mut result = self.clone();
10 for (a, b) in result.bits.iter_mut().zip(&other.bits) {
11 *a ^= b;
12 }
13 result.value = self.value.and_then(|a| Some(a ^ other.value?));
14 Ok(result)
15 }
16}
17
18impl<'a, const N: usize, T: PrimUInt, F: Field> BitXor<Self> for &'a UInt<N, T, F> {
19 type Output = UInt<N, T, F>;
20 /// Outputs `self ^ other`.
21 ///
22 /// If at least one of `self` and `other` are constants, then this method
23 /// *does not* create any constraints or variables.
24 ///
25 /// ```
26 /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> {
27 /// // We'll use the BLS12-381 scalar field for our constraints.
28 /// use ark_test_curves::bls12_381::Fr;
29 /// use ark_relations::r1cs::*;
30 /// use ark_r1cs_std::prelude::*;
31 ///
32 /// let cs = ConstraintSystem::<Fr>::new_ref();
33 /// let a = UInt8::new_witness(cs.clone(), || Ok(16))?;
34 /// let b = UInt8::new_witness(cs.clone(), || Ok(17))?;
35 /// let c = UInt8::new_witness(cs.clone(), || Ok(1))?;
36 ///
37 /// (a ^ &b).enforce_equal(&c)?;
38 /// assert!(cs.is_satisfied().unwrap());
39 /// # Ok(())
40 /// # }
41 /// ```
42 #[tracing::instrument(target = "r1cs", skip(self, other))]
43 fn bitxor(self, other: Self) -> Self::Output {
44 self._xor(other).unwrap()
45 }
46}
47
48impl<'a, const N: usize, T: PrimUInt, F: Field> BitXor<&'a Self> for UInt<N, T, F> {
49 type Output = UInt<N, T, F>;
50
51 #[tracing::instrument(target = "r1cs", skip(self, other))]
52 fn bitxor(self, other: &Self) -> Self::Output {
53 self._xor(&other).unwrap()
54 }
55}
56
57impl<'a, const N: usize, T: PrimUInt, F: Field> BitXor<UInt<N, T, F>> for &'a UInt<N, T, F> {
58 type Output = UInt<N, T, F>;
59
60 #[tracing::instrument(target = "r1cs", skip(self, other))]
61 fn bitxor(self, other: UInt<N, T, F>) -> Self::Output {
62 self._xor(&other).unwrap()
63 }
64}
65
66impl<const N: usize, T: PrimUInt, F: Field> BitXor<Self> for UInt<N, T, F> {
67 type Output = Self;
68
69 #[tracing::instrument(target = "r1cs", skip(self, other))]
70 fn bitxor(self, other: Self) -> Self::Output {
71 self._xor(&other).unwrap()
72 }
73}
74
75impl<const N: usize, T: PrimUInt, F: Field> BitXorAssign<Self> for UInt<N, T, F> {
76 /// Sets `self = self ^ other`.
77 ///
78 /// If at least one of `self` and `other` are constants, then this method
79 /// *does not* create any constraints or variables.
80 ///
81 /// ```
82 /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> {
83 /// // We'll use the BLS12-381 scalar field for our constraints.
84 /// use ark_test_curves::bls12_381::Fr;
85 /// use ark_relations::r1cs::*;
86 /// use ark_r1cs_std::prelude::*;
87 ///
88 /// let cs = ConstraintSystem::<Fr>::new_ref();
89 /// let mut a = UInt8::new_witness(cs.clone(), || Ok(16))?;
90 /// let b = UInt8::new_witness(cs.clone(), || Ok(17))?;
91 /// let c = UInt8::new_witness(cs.clone(), || Ok(1))?;
92 ///
93 /// a ^= b;
94 /// a.enforce_equal(&c)?;
95 /// assert!(cs.is_satisfied().unwrap());
96 /// # Ok(())
97 /// # }
98 /// ```
99 #[tracing::instrument(target = "r1cs", skip(self, other))]
100 fn bitxor_assign(&mut self, other: Self) {
101 let result = self._xor(&other).unwrap();
102 *self = result;
103 }
104}
105
106impl<'a, const N: usize, T: PrimUInt, F: Field> BitXorAssign<&'a Self> for UInt<N, T, F> {
107 #[tracing::instrument(target = "r1cs", skip(self, other))]
108 fn bitxor_assign(&mut self, other: &'a Self) {
109 let result = self._xor(other).unwrap();
110 *self = result;
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117 use crate::{
118 alloc::{AllocVar, AllocationMode},
119 prelude::EqGadget,
120 uint::test_utils::{run_binary_exhaustive, run_binary_random},
121 R1CSVar,
122 };
123 use ark_ff::PrimeField;
124 use ark_test_curves::bls12_381::Fr;
125
126 fn uint_xor<T: PrimUInt, const N: usize, F: PrimeField>(
127 a: UInt<N, T, F>,
128 b: UInt<N, T, F>,
129 ) -> Result<(), SynthesisError> {
130 let cs = a.cs().or(b.cs());
131 let both_constant = a.is_constant() && b.is_constant();
132 let computed = &a ^ &b;
133 let expected_mode = if both_constant {
134 AllocationMode::Constant
135 } else {
136 AllocationMode::Witness
137 };
138 let expected = UInt::<N, T, F>::new_variable(
139 cs.clone(),
140 || Ok(a.value()? ^ b.value()?),
141 expected_mode,
142 )?;
143 assert_eq!(expected.value(), computed.value());
144 expected.enforce_equal(&computed)?;
145 if !both_constant {
146 assert!(cs.is_satisfied().unwrap());
147 }
148 Ok(())
149 }
150
151 #[test]
152 fn u8_xor() {
153 run_binary_exhaustive(uint_xor::<u8, 8, Fr>).unwrap()
154 }
155
156 #[test]
157 fn u16_xor() {
158 run_binary_random::<1000, 16, _, _>(uint_xor::<u16, 16, Fr>).unwrap()
159 }
160
161 #[test]
162 fn u32_xor() {
163 run_binary_random::<1000, 32, _, _>(uint_xor::<u32, 32, Fr>).unwrap()
164 }
165
166 #[test]
167 fn u64_xor() {
168 run_binary_random::<1000, 64, _, _>(uint_xor::<u64, 64, Fr>).unwrap()
169 }
170
171 #[test]
172 fn u128_xor() {
173 run_binary_random::<1000, 128, _, _>(uint_xor::<u128, 128, Fr>).unwrap()
174 }
175}