1use core::borrow::Borrow;
2
3use ark_ff::{Field, PrimeField};
4use ark_relations::gr1cs::{ConstraintSystemRef, Namespace, SynthesisError, Variable};
5
6use crate::{
7 alloc::{AllocVar, AllocationMode},
8 select::CondSelectGadget,
9};
10
11use super::Boolean;
12
13#[derive(Clone, Debug, Eq, PartialEq)]
20#[must_use]
21pub struct AllocatedBool<F: Field> {
22 pub(super) variable: Variable,
23 pub(super) cs: ConstraintSystemRef<F>,
24 pub(super) value: Option<bool>,
25}
26
27impl<F: Field> AllocatedBool<F> {
28 pub fn value(&self) -> Result<bool, SynthesisError> {
30 self.value.ok_or(SynthesisError::AssignmentMissing)
31 }
32
33 pub fn variable(&self) -> Variable {
35 self.variable
36 }
37
38 #[doc(hidden)]
40 pub fn new_witness_without_booleanity_check<T: Borrow<bool>>(
41 cs: ConstraintSystemRef<F>,
42 f: impl FnOnce() -> Result<T, SynthesisError>,
43 ) -> Result<Self, SynthesisError> {
44 let mut value = None;
45 let f = || {
46 value = Some(*f()?.borrow());
47 value.ok_or(SynthesisError::AssignmentMissing)
48 };
49 let variable = cs.new_witness_variable(|| f().map(F::from))?;
50 Ok(Self {
51 variable,
52 cs,
53 value,
54 })
55 }
56
57 #[tracing::instrument(target = "gr1cs")]
60 pub fn not(&self) -> Result<Self, SynthesisError> {
61 let variable = self.cs.new_lc(|| lc_diff![Variable::One, self.variable])?;
62 Ok(Self {
63 variable,
64 cs: self.cs.clone(),
65 value: self.value.map(|v| !v),
66 })
67 }
68
69 #[tracing::instrument(target = "gr1cs")]
72 pub fn xor(&self, b: &Self) -> Result<Self, SynthesisError> {
73 let result = Self::new_witness_without_booleanity_check(self.cs.clone(), || {
74 Ok(self.value()? ^ b.value()?)
75 })?;
76
77 self.cs.enforce_r1cs_constraint(
93 || (F::ONE.double(), self.variable).into(),
94 || b.variable.into(),
95 || {
96 lc![
97 (F::ONE, self.variable),
98 (F::ONE, b.variable),
99 (-F::ONE, result.variable),
100 ]
101 },
102 )?;
103
104 Ok(result)
105 }
106
107 #[tracing::instrument(target = "gr1cs")]
110 pub fn and(&self, b: &Self) -> Result<Self, SynthesisError> {
111 let result = Self::new_witness_without_booleanity_check(self.cs.clone(), || {
112 Ok(self.value()? & b.value()?)
113 })?;
114
115 self.cs.enforce_r1cs_constraint(
118 || self.variable.into(),
119 || b.variable.into(),
120 || result.variable.into(),
121 )?;
122
123 Ok(result)
124 }
125
126 #[tracing::instrument(target = "gr1cs")]
129 pub fn or(&self, b: &Self) -> Result<Self, SynthesisError> {
130 let result = Self::new_witness_without_booleanity_check(self.cs.clone(), || {
131 Ok(self.value()? | b.value()?)
132 })?;
133
134 self.cs.enforce_r1cs_constraint(
137 || lc_diff![Variable::One, self.variable],
138 || lc_diff![Variable::One, b.variable],
139 || lc_diff![Variable::One, result.variable],
140 )?;
141
142 Ok(result)
143 }
144
145 #[tracing::instrument(target = "gr1cs")]
147 pub fn and_not(&self, b: &Self) -> Result<Self, SynthesisError> {
148 let result = Self::new_witness_without_booleanity_check(self.cs.clone(), || {
149 Ok(self.value()? & !b.value()?)
150 })?;
151
152 self.cs.enforce_r1cs_constraint(
155 || lc!() + self.variable,
156 || lc_diff![Variable::One, b.variable],
157 || lc!() + result.variable,
158 )?;
159
160 Ok(result)
161 }
162
163 #[tracing::instrument(target = "gr1cs")]
165 pub fn nor(&self, b: &Self) -> Result<Self, SynthesisError> {
166 let result = Self::new_witness_without_booleanity_check(self.cs.clone(), || {
167 Ok(!(self.value()? | b.value()?))
168 })?;
169
170 self.cs.enforce_r1cs_constraint(
173 || lc_diff![Variable::One, self.variable],
174 || lc_diff![Variable::One, b.variable],
175 || result.variable.into(),
176 )?;
177
178 Ok(result)
179 }
180}
181
182impl<F: Field> AllocVar<bool, F> for AllocatedBool<F> {
183 fn new_variable<T: Borrow<bool>>(
190 cs: impl Into<Namespace<F>>,
191 f: impl FnOnce() -> Result<T, SynthesisError>,
192 mode: AllocationMode,
193 ) -> Result<Self, SynthesisError> {
194 let ns = cs.into();
195 let cs = ns.cs();
196 if mode == AllocationMode::Constant {
197 let value = *f()?.borrow();
198 let variable = if value { Variable::One } else { Variable::Zero };
199 Ok(Self {
200 variable,
201 cs,
202 value: Some(value),
203 })
204 } else {
205 let mut value = None;
206 let f = || {
207 value = Some(*f()?.borrow());
208 value.ok_or(SynthesisError::AssignmentMissing)
209 };
210 let variable = if mode == AllocationMode::Input {
211 cs.new_input_variable(|| f().map(F::from))?
212 } else {
213 cs.new_witness_variable(|| f().map(F::from))?
214 };
215
216 cs.enforce_r1cs_constraint(
220 || lc_diff![Variable::One, variable],
221 || variable.into(),
222 || lc!(),
223 )?;
224
225 Ok(Self {
226 variable,
227 cs,
228 value,
229 })
230 }
231 }
232}
233
234impl<F: PrimeField> CondSelectGadget<F> for AllocatedBool<F> {
235 #[tracing::instrument(target = "gr1cs")]
236 fn conditionally_select(
237 cond: &Boolean<F>,
238 true_val: &Self,
239 false_val: &Self,
240 ) -> Result<Self, SynthesisError> {
241 let res = Boolean::conditionally_select(
242 cond,
243 &true_val.clone().into(),
244 &false_val.clone().into(),
245 )?;
246 match res {
247 Boolean::Var(a) => Ok(a),
248 _ => unreachable!("Impossible"),
249 }
250 }
251}
252
253#[cfg(test)]
254mod test {
255 use super::*;
256
257 use ark_relations::gr1cs::ConstraintSystem;
258 use ark_test_curves::bls12_381::Fr;
259 #[test]
260 fn allocated_xor() -> Result<(), SynthesisError> {
261 for a_val in [false, true].iter().copied() {
262 for b_val in [false, true].iter().copied() {
263 let cs = ConstraintSystem::<Fr>::new_ref();
264 let a = AllocatedBool::new_witness(cs.clone(), || Ok(a_val))?;
265 let b = AllocatedBool::new_witness(cs.clone(), || Ok(b_val))?;
266 let c = AllocatedBool::xor(&a, &b)?;
267 assert_eq!(c.value()?, a_val ^ b_val);
268
269 assert!(cs.is_satisfied().unwrap());
270 assert_eq!(a.value()?, (a_val));
271 assert_eq!(b.value()?, (b_val));
272 assert_eq!(c.value()?, (a_val ^ b_val));
273 }
274 }
275 Ok(())
276 }
277
278 #[test]
279 fn allocated_or() -> Result<(), SynthesisError> {
280 for a_val in [false, true].iter().copied() {
281 for b_val in [false, true].iter().copied() {
282 let cs = ConstraintSystem::<Fr>::new_ref();
283 let a = AllocatedBool::new_witness(cs.clone(), || Ok(a_val))?;
284 let b = AllocatedBool::new_witness(cs.clone(), || Ok(b_val))?;
285 let c = AllocatedBool::or(&a, &b)?;
286 assert_eq!(c.value()?, a_val | b_val);
287
288 assert!(cs.is_satisfied().unwrap());
289 assert_eq!(a.value()?, (a_val));
290 assert_eq!(b.value()?, (b_val));
291 assert_eq!(c.value()?, (a_val | b_val));
292 }
293 }
294 Ok(())
295 }
296
297 #[test]
298 fn allocated_and() -> Result<(), SynthesisError> {
299 for a_val in [false, true].iter().copied() {
300 for b_val in [false, true].iter().copied() {
301 let cs = ConstraintSystem::<Fr>::new_ref();
302 let a = AllocatedBool::new_witness(cs.clone(), || Ok(a_val))?;
303 let b = AllocatedBool::new_witness(cs.clone(), || Ok(b_val))?;
304 let c = AllocatedBool::and(&a, &b)?;
305 assert_eq!(c.value()?, a_val & b_val);
306
307 assert!(cs.is_satisfied().unwrap());
308 assert_eq!(a.value()?, (a_val));
309 assert_eq!(b.value()?, (b_val));
310 assert_eq!(c.value()?, (a_val & b_val));
311 }
312 }
313 Ok(())
314 }
315
316 #[test]
317 fn allocated_and_not() -> Result<(), SynthesisError> {
318 for a_val in [false, true].iter().copied() {
319 for b_val in [false, true].iter().copied() {
320 let cs = ConstraintSystem::<Fr>::new_ref();
321 let a = AllocatedBool::new_witness(cs.clone(), || Ok(a_val))?;
322 let b = AllocatedBool::new_witness(cs.clone(), || Ok(b_val))?;
323 let c = AllocatedBool::and_not(&a, &b)?;
324 assert_eq!(c.value()?, a_val & !b_val);
325
326 assert!(cs.is_satisfied().unwrap());
327 assert_eq!(a.value()?, (a_val));
328 assert_eq!(b.value()?, (b_val));
329 assert_eq!(c.value()?, (a_val & !b_val));
330 }
331 }
332 Ok(())
333 }
334
335 #[test]
336 fn allocated_nor() -> Result<(), SynthesisError> {
337 for a_val in [false, true].iter().copied() {
338 for b_val in [false, true].iter().copied() {
339 let cs = ConstraintSystem::<Fr>::new_ref();
340 let a = AllocatedBool::new_witness(cs.clone(), || Ok(a_val))?;
341 let b = AllocatedBool::new_witness(cs.clone(), || Ok(b_val))?;
342 let c = AllocatedBool::nor(&a, &b)?;
343 assert_eq!(c.value()?, !a_val & !b_val);
344
345 assert!(cs.is_satisfied().unwrap());
346 assert_eq!(a.value()?, (a_val));
347 assert_eq!(b.value()?, (b_val));
348 assert_eq!(c.value()?, (!a_val & !b_val));
349 }
350 }
351 Ok(())
352 }
353}