arnaucube
arnaucube/ark-r1cs-stdPublic Self-hosted repository
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}