use ark_ff::PrimeField; use ark_relations::r1cs::SynthesisError; use ark_std::{ops::BitOr, ops::BitOrAssign}; use crate::{ eq::EqGadget, fields::{fp::FpVar, FieldVar}, }; use super::Boolean; impl Boolean { fn _or(&self, other: &Self) -> Result { use Boolean::*; match (self, other) { (&Constant(false), x) | (x, &Constant(false)) => Ok(x.clone()), (&Constant(true), _) | (_, &Constant(true)) => Ok(Constant(true)), (Var(ref x), Var(ref y)) => Ok(Var(x.or(y)?)), } } /// Outputs `bits[0] | bits[1] | ... | bits.last().unwrap()`. /// /// ``` /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> { /// // We'll use the BLS12-381 scalar field for our constraints. /// use ark_test_curves::bls12_381::Fr; /// use ark_relations::r1cs::*; /// use ark_r1cs_std::prelude::*; /// /// let cs = ConstraintSystem::::new_ref(); /// /// let a = Boolean::new_witness(cs.clone(), || Ok(true))?; /// let b = Boolean::new_witness(cs.clone(), || Ok(false))?; /// let c = Boolean::new_witness(cs.clone(), || Ok(false))?; /// /// Boolean::kary_or(&[a.clone(), b.clone(), c.clone()])?.enforce_equal(&Boolean::TRUE)?; /// Boolean::kary_or(&[a.clone(), c.clone()])?.enforce_equal(&Boolean::TRUE)?; /// Boolean::kary_or(&[b.clone(), c.clone()])?.enforce_equal(&Boolean::FALSE)?; /// /// assert!(cs.is_satisfied().unwrap()); /// # Ok(()) /// # } /// ``` #[tracing::instrument(target = "r1cs")] pub fn kary_or(bits: &[Self]) -> Result { assert!(!bits.is_empty()); if bits.len() <= 3 { let mut cur: Option = None; for next in bits { cur = if let Some(b) = cur { Some(b | next) } else { Some(next.clone()) }; } Ok(cur.expect("should not be 0")) } else { // b0 | b1 | ... | bN == 1 if and only if not all of b0, b1, ..., bN are 0. // We can enforce this by requiring that the sum of b0, b1, ..., bN is not 0. let sum_bits: FpVar<_> = bits.iter().map(|b| FpVar::from(b.clone())).sum(); sum_bits.is_neq(&FpVar::zero()) } } } impl<'a, F: PrimeField> BitOr for &'a Boolean { type Output = Boolean; /// Outputs `self | other`. /// /// If at least one of `self` and `other` are constants, then this method /// *does not* create any constraints or variables. /// /// ``` /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> { /// // We'll use the BLS12-381 scalar field for our constraints. /// use ark_test_curves::bls12_381::Fr; /// use ark_relations::r1cs::*; /// use ark_r1cs_std::prelude::*; /// /// let cs = ConstraintSystem::::new_ref(); /// /// let a = Boolean::new_witness(cs.clone(), || Ok(true))?; /// let b = Boolean::new_witness(cs.clone(), || Ok(false))?; /// /// (&a | &b).enforce_equal(&Boolean::TRUE)?; /// (&b | &a).enforce_equal(&Boolean::TRUE)?; /// /// (&a | &a).enforce_equal(&Boolean::TRUE)?; /// (&b | &b).enforce_equal(&Boolean::FALSE)?; /// /// assert!(cs.is_satisfied().unwrap()); /// # Ok(()) /// # } /// ``` #[tracing::instrument(target = "r1cs", skip(self, other))] fn bitor(self, other: Self) -> Self::Output { self._or(other).unwrap() } } impl<'a, F: PrimeField> BitOr<&'a Self> for Boolean { type Output = Boolean; #[tracing::instrument(target = "r1cs", skip(self, other))] fn bitor(self, other: &Self) -> Self::Output { self._or(&other).unwrap() } } impl<'a, F: PrimeField> BitOr> for &'a Boolean { type Output = Boolean; #[tracing::instrument(target = "r1cs", skip(self, other))] fn bitor(self, other: Boolean) -> Self::Output { self._or(&other).unwrap() } } impl BitOr for Boolean { type Output = Self; #[tracing::instrument(target = "r1cs", skip(self, other))] fn bitor(self, other: Self) -> Self::Output { self._or(&other).unwrap() } } impl BitOrAssign for Boolean { /// Sets `self = self | other`. #[tracing::instrument(target = "r1cs", skip(self, other))] fn bitor_assign(&mut self, other: Self) { let result = self._or(&other).unwrap(); *self = result; } } impl<'a, F: PrimeField> BitOrAssign<&'a Self> for Boolean { /// Sets `self = self | other`. #[tracing::instrument(target = "r1cs", skip(self, other))] fn bitor_assign(&mut self, other: &'a Self) { let result = self._or(other).unwrap(); *self = result; } } #[cfg(test)] mod tests { use super::*; use crate::{ alloc::{AllocVar, AllocationMode}, boolean::test_utils::run_binary_exhaustive, prelude::EqGadget, R1CSVar, }; use ark_test_curves::bls12_381::Fr; #[test] fn or() { run_binary_exhaustive::(|a, b| { let cs = a.cs().or(b.cs()); let both_constant = a.is_constant() && b.is_constant(); let computed = &a | &b; let expected_mode = if both_constant { AllocationMode::Constant } else { AllocationMode::Witness }; let expected = Boolean::new_variable(cs.clone(), || Ok(a.value()? | b.value()?), expected_mode)?; assert_eq!(expected.value(), computed.value()); expected.enforce_equal(&computed)?; if !both_constant { assert!(cs.is_satisfied().unwrap()); } Ok(()) }) .unwrap() } }