ark_r1cs_std/boolean/
xor.rs

1use ark_ff::Field;
2use ark_relations::r1cs::SynthesisError;
3use ark_std::{ops::BitXor, ops::BitXorAssign};
4
5use super::Boolean;
6
7impl<F: Field> Boolean<F> {
8    fn _xor(&self, other: &Self) -> Result<Self, SynthesisError> {
9        use Boolean::*;
10        match (self, other) {
11            (&Constant(false), x) | (x, &Constant(false)) => Ok(x.clone()),
12            (&Constant(true), x) | (x, &Constant(true)) => Ok(!x),
13            (Var(ref x), Var(ref y)) => Ok(Var(x.xor(y)?)),
14        }
15    }
16}
17
18impl<'a, F: Field> BitXor<Self> for &'a Boolean<F> {
19    type Output = Boolean<F>;
20
21    /// Outputs `self ^ other`.
22    ///
23    /// If at least one of `self` and `other` are constants, then this method
24    /// *does not* create any constraints or variables.
25    ///
26    /// ```
27    /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> {
28    /// // We'll use the BLS12-381 scalar field for our constraints.
29    /// use ark_test_curves::bls12_381::Fr;
30    /// use ark_relations::r1cs::*;
31    /// use ark_r1cs_std::prelude::*;
32    ///
33    /// let cs = ConstraintSystem::<Fr>::new_ref();
34    ///
35    /// let a = Boolean::new_witness(cs.clone(), || Ok(true))?;
36    /// let b = Boolean::new_witness(cs.clone(), || Ok(false))?;
37    ///
38    /// (&a ^ &b).enforce_equal(&Boolean::TRUE)?;
39    /// (&b ^ &a).enforce_equal(&Boolean::TRUE)?;
40    ///
41    /// (&a ^ &a).enforce_equal(&Boolean::FALSE)?;
42    /// (&b ^ &b).enforce_equal(&Boolean::FALSE)?;
43    ///
44    /// assert!(cs.is_satisfied().unwrap());
45    /// # Ok(())
46    /// # }
47    /// ```
48    #[tracing::instrument(target = "r1cs", skip(self, other))]
49    fn bitxor(self, other: Self) -> Self::Output {
50        self._xor(other).unwrap()
51    }
52}
53
54impl<'a, F: Field> BitXor<&'a Self> for Boolean<F> {
55    type Output = Boolean<F>;
56
57    #[tracing::instrument(target = "r1cs", skip(self, other))]
58    fn bitxor(self, other: &Self) -> Self::Output {
59        self._xor(&other).unwrap()
60    }
61}
62
63impl<'a, F: Field> BitXor<Boolean<F>> for &'a Boolean<F> {
64    type Output = Boolean<F>;
65
66    #[tracing::instrument(target = "r1cs", skip(self, other))]
67    fn bitxor(self, other: Boolean<F>) -> Self::Output {
68        self._xor(&other).unwrap()
69    }
70}
71
72impl<F: Field> BitXor<Self> for Boolean<F> {
73    type Output = Self;
74
75    #[tracing::instrument(target = "r1cs", skip(self, other))]
76    fn bitxor(self, other: Self) -> Self::Output {
77        self._xor(&other).unwrap()
78    }
79}
80
81impl<F: Field> BitXorAssign<Self> for Boolean<F> {
82    /// Sets `self = self ^ other`.
83    #[tracing::instrument(target = "r1cs", skip(self, other))]
84    fn bitxor_assign(&mut self, other: Self) {
85        let result = self._xor(&other).unwrap();
86        *self = result;
87    }
88}
89
90impl<'a, F: Field> BitXorAssign<&'a Self> for Boolean<F> {
91    /// Sets `self = self ^ other`.
92    #[tracing::instrument(target = "r1cs", skip(self, other))]
93    fn bitxor_assign(&mut self, other: &'a Self) {
94        let result = self._xor(other).unwrap();
95        *self = result;
96    }
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102    use crate::{
103        alloc::{AllocVar, AllocationMode},
104        boolean::test_utils::run_binary_exhaustive,
105        prelude::EqGadget,
106        R1CSVar,
107    };
108    use ark_test_curves::bls12_381::Fr;
109
110    #[test]
111    fn xor() {
112        run_binary_exhaustive::<Fr>(|a, b| {
113            let cs = a.cs().or(b.cs());
114            let both_constant = a.is_constant() && b.is_constant();
115            let computed = &a ^ &b;
116            let expected_mode = if both_constant {
117                AllocationMode::Constant
118            } else {
119                AllocationMode::Witness
120            };
121            let expected =
122                Boolean::new_variable(cs.clone(), || Ok(a.value()? ^ b.value()?), expected_mode)?;
123            assert_eq!(expected.value(), computed.value());
124            expected.enforce_equal(&computed)?;
125            if !both_constant {
126                assert!(cs.is_satisfied().unwrap());
127            }
128            Ok(())
129        })
130        .unwrap()
131    }
132}