Skip to main content

nova_snark/frontend/gadgets/
boolean.rs

1//! Gadgets for allocating bits in the circuit and performing boolean logic.
2
3use ff::{PrimeField, PrimeFieldBits};
4
5use crate::frontend::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
6
7/// Represents a variable in the constraint system which is guaranteed
8/// to be either zero or one.
9#[derive(Debug, Clone)]
10pub struct AllocatedBit {
11  variable: Variable,
12  value: Option<bool>,
13}
14
15impl AllocatedBit {
16  /// Get inner value of [`AllocatedBit`]
17  pub fn get_value(&self) -> Option<bool> {
18    self.value
19  }
20
21  /// Get inner [`Variable`] of [`AllocatedBit`]
22  pub fn get_variable(&self) -> Variable {
23    self.variable
24  }
25
26  /// Construct an [`AllocatedBit`] from a variable and value without adding constraints.
27  ///
28  /// # Safety (Logical)
29  /// The caller MUST ensure that:
30  /// 1. The variable has already been constrained to be boolean (0 or 1) elsewhere
31  /// 2. The value correctly reflects the variable's assignment
32  ///
33  /// This is useful when a variable is known to be boolean due to constraints
34  /// added separately (e.g., via linear combinations that enforce 0/1).
35  pub fn from_parts_unchecked(variable: Variable, value: Option<bool>) -> Self {
36    AllocatedBit { variable, value }
37  }
38
39  /// Allocate a variable in the constraint system which can only be a
40  /// boolean value. Further, constrain that the boolean is false
41  /// unless the condition is false.
42  pub fn alloc_conditionally<Scalar, CS>(
43    mut cs: CS,
44    value: Option<bool>,
45    must_be_false: &AllocatedBit,
46  ) -> Result<Self, SynthesisError>
47  where
48    Scalar: PrimeField,
49    CS: ConstraintSystem<Scalar>,
50  {
51    let var = cs.alloc(
52      || "boolean",
53      || {
54        if value.ok_or(SynthesisError::AssignmentMissing)? {
55          Ok(Scalar::ONE)
56        } else {
57          Ok(Scalar::ZERO)
58        }
59      },
60    )?;
61
62    // Constrain: (1 - must_be_false - a) * a = 0
63    // if must_be_false is true, the equation
64    // reduces to -a * a = 0, which implies a = 0.
65    // if must_be_false is false, the equation
66    // reduces to (1 - a) * a = 0, which is a
67    // traditional boolean constraint.
68    cs.enforce(
69      || "boolean constraint",
70      |lc| lc + CS::one() - must_be_false.variable - var,
71      |lc| lc + var,
72      |lc| lc,
73    );
74
75    Ok(AllocatedBit {
76      variable: var,
77      value,
78    })
79  }
80
81  /// Allocate a variable in the constraint system which can only be a
82  /// boolean value.
83  pub fn alloc<Scalar, CS>(mut cs: CS, value: Option<bool>) -> Result<Self, SynthesisError>
84  where
85    Scalar: PrimeField,
86    CS: ConstraintSystem<Scalar>,
87  {
88    let var = cs.alloc(
89      || "boolean",
90      || {
91        if value.ok_or(SynthesisError::AssignmentMissing)? {
92          Ok(Scalar::ONE)
93        } else {
94          Ok(Scalar::ZERO)
95        }
96      },
97    )?;
98
99    // Constrain: (1 - a) * a = 0
100    // This constrains a to be either 0 or 1.
101    cs.enforce(
102      || "boolean constraint",
103      |lc| lc + CS::one() - var,
104      |lc| lc + var,
105      |lc| lc,
106    );
107
108    Ok(AllocatedBit {
109      variable: var,
110      value,
111    })
112  }
113
114  /// Performs an XOR operation over the two operands, returning
115  /// an `AllocatedBit`.
116  pub fn xor<Scalar, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError>
117  where
118    Scalar: PrimeField,
119    CS: ConstraintSystem<Scalar>,
120  {
121    let mut result_value = None;
122
123    let result_var = cs.alloc(
124      || "xor result",
125      || {
126        if a.value.ok_or(SynthesisError::AssignmentMissing)?
127          ^ b.value.ok_or(SynthesisError::AssignmentMissing)?
128        {
129          result_value = Some(true);
130
131          Ok(Scalar::ONE)
132        } else {
133          result_value = Some(false);
134
135          Ok(Scalar::ZERO)
136        }
137      },
138    )?;
139
140    // Constrain (a + a) * (b) = (a + b - c)
141    // Given that a and b are boolean constrained, if they
142    // are equal, the only solution for c is 0, and if they
143    // are different, the only solution for c is 1.
144    //
145    // ¬(a ∧ b) ∧ ¬(¬a ∧ ¬b) = c
146    // (1 - (a * b)) * (1 - ((1 - a) * (1 - b))) = c
147    // (1 - ab) * (1 - (1 - a - b + ab)) = c
148    // (1 - ab) * (a + b - ab) = c
149    // a + b - ab - (a^2)b - (b^2)a + (a^2)(b^2) = c
150    // a + b - ab - ab - ab + ab = c
151    // a + b - 2ab = c
152    // -2a * b = c - a - b
153    // 2a * b = a + b - c
154    // (a + a) * b = a + b - c
155    cs.enforce(
156      || "xor constraint",
157      |lc| lc + a.variable + a.variable,
158      |lc| lc + b.variable,
159      |lc| lc + a.variable + b.variable - result_var,
160    );
161
162    Ok(AllocatedBit {
163      variable: result_var,
164      value: result_value,
165    })
166  }
167
168  /// Performs an AND operation over the two operands, returning
169  /// an `AllocatedBit`.
170  pub fn and<Scalar, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError>
171  where
172    Scalar: PrimeField,
173    CS: ConstraintSystem<Scalar>,
174  {
175    let mut result_value = None;
176
177    let result_var = cs.alloc(
178      || "and result",
179      || {
180        if a.value.ok_or(SynthesisError::AssignmentMissing)?
181          & b.value.ok_or(SynthesisError::AssignmentMissing)?
182        {
183          result_value = Some(true);
184
185          Ok(Scalar::ONE)
186        } else {
187          result_value = Some(false);
188
189          Ok(Scalar::ZERO)
190        }
191      },
192    )?;
193
194    // Constrain (a) * (b) = (c), ensuring c is 1 iff
195    // a AND b are both 1.
196    cs.enforce(
197      || "and constraint",
198      |lc| lc + a.variable,
199      |lc| lc + b.variable,
200      |lc| lc + result_var,
201    );
202
203    Ok(AllocatedBit {
204      variable: result_var,
205      value: result_value,
206    })
207  }
208
209  /// Calculates `a AND (NOT b)`.
210  pub fn and_not<Scalar, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError>
211  where
212    Scalar: PrimeField,
213    CS: ConstraintSystem<Scalar>,
214  {
215    let mut result_value = None;
216
217    let result_var = cs.alloc(
218      || "and not result",
219      || {
220        if a.value.ok_or(SynthesisError::AssignmentMissing)?
221          & !b.value.ok_or(SynthesisError::AssignmentMissing)?
222        {
223          result_value = Some(true);
224
225          Ok(Scalar::ONE)
226        } else {
227          result_value = Some(false);
228
229          Ok(Scalar::ZERO)
230        }
231      },
232    )?;
233
234    // Constrain (a) * (1 - b) = (c), ensuring c is 1 iff
235    // a is true and b is false, and otherwise c is 0.
236    cs.enforce(
237      || "and not constraint",
238      |lc| lc + a.variable,
239      |lc| lc + CS::one() - b.variable,
240      |lc| lc + result_var,
241    );
242
243    Ok(AllocatedBit {
244      variable: result_var,
245      value: result_value,
246    })
247  }
248
249  /// Calculates `(NOT a) AND (NOT b)`.
250  pub fn nor<Scalar, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError>
251  where
252    Scalar: PrimeField,
253    CS: ConstraintSystem<Scalar>,
254  {
255    let mut result_value = None;
256
257    let result_var = cs.alloc(
258      || "nor result",
259      || {
260        if !a.value.ok_or(SynthesisError::AssignmentMissing)?
261          & !b.value.ok_or(SynthesisError::AssignmentMissing)?
262        {
263          result_value = Some(true);
264
265          Ok(Scalar::ONE)
266        } else {
267          result_value = Some(false);
268
269          Ok(Scalar::ZERO)
270        }
271      },
272    )?;
273
274    // Constrain (1 - a) * (1 - b) = (c), ensuring c is 1 iff
275    // a and b are both false, and otherwise c is 0.
276    cs.enforce(
277      || "nor constraint",
278      |lc| lc + CS::one() - a.variable,
279      |lc| lc + CS::one() - b.variable,
280      |lc| lc + result_var,
281    );
282
283    Ok(AllocatedBit {
284      variable: result_var,
285      value: result_value,
286    })
287  }
288}
289
290/// Convert a field element into a vector of [`AllocatedBit`]'s representing its bits.
291pub fn field_into_allocated_bits_le<Scalar, CS>(
292  mut cs: CS,
293  value: Option<Scalar>,
294) -> Result<Vec<AllocatedBit>, SynthesisError>
295where
296  Scalar: PrimeField,
297  Scalar: PrimeFieldBits,
298  CS: ConstraintSystem<Scalar>,
299{
300  // Deconstruct in big-endian bit order
301  let values = match value {
302    Some(ref value) => {
303      let field_char = Scalar::char_le_bits();
304      let mut field_char = field_char.into_iter().rev();
305
306      let mut tmp = Vec::with_capacity(Scalar::NUM_BITS as usize);
307
308      let mut found_one = false;
309      for b in value.to_le_bits().into_iter().rev() {
310        // Skip leading bits
311        found_one |= field_char.next().unwrap();
312        if !found_one {
313          continue;
314        }
315
316        tmp.push(Some(b));
317      }
318
319      assert_eq!(tmp.len(), Scalar::NUM_BITS as usize);
320
321      tmp
322    }
323    None => vec![None; Scalar::NUM_BITS as usize],
324  };
325
326  // Allocate in little-endian order
327  let bits = values
328    .into_iter()
329    .rev()
330    .enumerate()
331    .map(|(i, b)| AllocatedBit::alloc(cs.namespace(|| format!("bit {i}")), b))
332    .collect::<Result<Vec<_>, SynthesisError>>()?;
333
334  Ok(bits)
335}
336
337/// This is a boolean value which may be either a constant or
338/// an interpretation of an `AllocatedBit`.
339#[derive(Clone, Debug)]
340pub enum Boolean {
341  /// Existential view of the boolean variable
342  Is(AllocatedBit),
343  /// Negated view of the boolean variable
344  Not(AllocatedBit),
345  /// Constant (not an allocated variable)
346  Constant(bool),
347}
348
349impl Boolean {
350  /// Check if the boolean is a constant
351  pub fn is_constant(&self) -> bool {
352    matches!(*self, Boolean::Constant(_))
353  }
354
355  /// Constrain two booleans to be equal.
356  pub fn enforce_equal<Scalar, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<(), SynthesisError>
357  where
358    Scalar: PrimeField,
359    CS: ConstraintSystem<Scalar>,
360  {
361    match (a, b) {
362      (&Boolean::Constant(a), &Boolean::Constant(b)) => {
363        if a == b {
364          Ok(())
365        } else {
366          Err(SynthesisError::Unsatisfiable(
367            "Booleans are not equal".to_string(),
368          ))
369        }
370      }
371      (&Boolean::Constant(true), a) | (a, &Boolean::Constant(true)) => {
372        cs.enforce(
373          || "enforce equal to one",
374          |lc| lc,
375          |lc| lc,
376          |lc| lc + CS::one() - &a.lc(CS::one(), Scalar::ONE),
377        );
378
379        Ok(())
380      }
381      (&Boolean::Constant(false), a) | (a, &Boolean::Constant(false)) => {
382        cs.enforce(
383          || "enforce equal to zero",
384          |lc| lc,
385          |lc| lc,
386          |_| a.lc(CS::one(), Scalar::ONE),
387        );
388
389        Ok(())
390      }
391      (a, b) => {
392        cs.enforce(
393          || "enforce equal",
394          |lc| lc,
395          |lc| lc,
396          |_| a.lc(CS::one(), Scalar::ONE) - &b.lc(CS::one(), Scalar::ONE),
397        );
398
399        Ok(())
400      }
401    }
402  }
403
404  /// Get the inner value of the boolean.
405  pub fn get_value(&self) -> Option<bool> {
406    match *self {
407      Boolean::Constant(c) => Some(c),
408      Boolean::Is(ref v) => v.get_value(),
409      Boolean::Not(ref v) => v.get_value().map(|b| !b),
410    }
411  }
412
413  /// Return a linear combination representing the boolean.
414  pub fn lc<Scalar: PrimeField>(&self, one: Variable, coeff: Scalar) -> LinearCombination<Scalar> {
415    match *self {
416      Boolean::Constant(c) => {
417        if c {
418          LinearCombination::<Scalar>::zero() + (coeff, one)
419        } else {
420          LinearCombination::<Scalar>::zero()
421        }
422      }
423      Boolean::Is(ref v) => LinearCombination::<Scalar>::zero() + (coeff, v.get_variable()),
424      Boolean::Not(ref v) => {
425        LinearCombination::<Scalar>::zero() + (coeff, one) - (coeff, v.get_variable())
426      }
427    }
428  }
429
430  /// Construct a boolean from a known constant
431  pub fn constant(b: bool) -> Self {
432    Boolean::Constant(b)
433  }
434
435  /// Return a negated interpretation of this boolean.
436  pub fn not(&self) -> Self {
437    match *self {
438      Boolean::Constant(c) => Boolean::Constant(!c),
439      Boolean::Is(ref v) => Boolean::Not(v.clone()),
440      Boolean::Not(ref v) => Boolean::Is(v.clone()),
441    }
442  }
443
444  /// Perform XOR over two boolean operands
445  pub fn xor<'a, Scalar, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result<Self, SynthesisError>
446  where
447    Scalar: PrimeField,
448    CS: ConstraintSystem<Scalar>,
449  {
450    match (a, b) {
451      (&Boolean::Constant(false), x) | (x, &Boolean::Constant(false)) => Ok(x.clone()),
452      (&Boolean::Constant(true), x) | (x, &Boolean::Constant(true)) => Ok(x.not()),
453      // a XOR (NOT b) = NOT(a XOR b)
454      (is @ &Boolean::Is(_), not @ &Boolean::Not(_))
455      | (not @ &Boolean::Not(_), is @ &Boolean::Is(_)) => {
456        Ok(Boolean::xor(cs, is, &not.not())?.not())
457      }
458      // a XOR b = (NOT a) XOR (NOT b)
459      (&Boolean::Is(ref a), &Boolean::Is(ref b)) | (&Boolean::Not(ref a), &Boolean::Not(ref b)) => {
460        Ok(Boolean::Is(AllocatedBit::xor(cs, a, b)?))
461      }
462    }
463  }
464
465  /// Perform AND over two boolean operands
466  pub fn and<'a, Scalar, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result<Self, SynthesisError>
467  where
468    Scalar: PrimeField,
469    CS: ConstraintSystem<Scalar>,
470  {
471    match (a, b) {
472      // false AND x is always false
473      (&Boolean::Constant(false), _) | (_, &Boolean::Constant(false)) => {
474        Ok(Boolean::Constant(false))
475      }
476      // true AND x is always x
477      (&Boolean::Constant(true), x) | (x, &Boolean::Constant(true)) => Ok(x.clone()),
478      // a AND (NOT b)
479      (&Boolean::Is(ref is), &Boolean::Not(ref not))
480      | (&Boolean::Not(ref not), &Boolean::Is(ref is)) => {
481        Ok(Boolean::Is(AllocatedBit::and_not(cs, is, not)?))
482      }
483      // (NOT a) AND (NOT b) = a NOR b
484      (Boolean::Not(a), Boolean::Not(b)) => Ok(Boolean::Is(AllocatedBit::nor(cs, a, b)?)),
485      // a AND b
486      (Boolean::Is(a), Boolean::Is(b)) => Ok(Boolean::Is(AllocatedBit::and(cs, a, b)?)),
487    }
488  }
489
490  /// Perform OR over two boolean operands
491  pub fn or<'a, Scalar, CS>(
492    mut cs: CS,
493    a: &'a Boolean,
494    b: &'a Boolean,
495  ) -> Result<Boolean, SynthesisError>
496  where
497    Scalar: PrimeField,
498    CS: ConstraintSystem<Scalar>,
499  {
500    Ok(Boolean::not(&Boolean::and(
501      cs.namespace(|| "not and (not a) (not b)"),
502      &Boolean::not(a),
503      &Boolean::not(b),
504    )?))
505  }
506
507  /// Computes (a and b) xor ((not a) and c)
508  pub fn sha256_ch<'a, Scalar, CS>(
509    mut cs: CS,
510    a: &'a Self,
511    b: &'a Self,
512    c: &'a Self,
513  ) -> Result<Self, SynthesisError>
514  where
515    Scalar: PrimeField,
516    CS: ConstraintSystem<Scalar>,
517  {
518    let ch_value = match (a.get_value(), b.get_value(), c.get_value()) {
519      (Some(a), Some(b), Some(c)) => {
520        // (a and b) xor ((not a) and c)
521        Some((a & b) ^ ((!a) & c))
522      }
523      _ => None,
524    };
525
526    match (a, b, c) {
527      (&Boolean::Constant(_), &Boolean::Constant(_), &Boolean::Constant(_)) => {
528        // They're all constants, so we can just compute the value.
529
530        return Ok(Boolean::Constant(ch_value.expect("they're all constants")));
531      }
532      (&Boolean::Constant(false), _, c) => {
533        // If a is false
534        // (a and b) xor ((not a) and c)
535        // equals
536        // (false) xor (c)
537        // equals
538        // c
539        return Ok(c.clone());
540      }
541      (a, &Boolean::Constant(false), c) => {
542        // If b is false
543        // (a and b) xor ((not a) and c)
544        // equals
545        // ((not a) and c)
546        return Boolean::and(cs, &a.not(), c);
547      }
548      (a, b, &Boolean::Constant(false)) => {
549        // If c is false
550        // (a and b) xor ((not a) and c)
551        // equals
552        // (a and b)
553        return Boolean::and(cs, a, b);
554      }
555      (a, b, &Boolean::Constant(true)) => {
556        // If c is true
557        // (a and b) xor ((not a) and c)
558        // equals
559        // (a and b) xor (not a)
560        // equals
561        // not (a and (not b))
562        return Ok(Boolean::and(cs, a, &b.not())?.not());
563      }
564      (a, &Boolean::Constant(true), c) => {
565        // If b is true
566        // (a and b) xor ((not a) and c)
567        // equals
568        // a xor ((not a) and c)
569        // equals
570        // not ((not a) and (not c))
571        return Ok(Boolean::and(cs, &a.not(), &c.not())?.not());
572      }
573      (&Boolean::Constant(true), _, _) => {
574        // If a is true
575        // (a and b) xor ((not a) and c)
576        // equals
577        // b xor ((not a) and c)
578        // So we just continue!
579      }
580      (
581        &Boolean::Is(_) | &Boolean::Not(_),
582        &Boolean::Is(_) | &Boolean::Not(_),
583        &Boolean::Is(_) | &Boolean::Not(_),
584      ) => {}
585    }
586
587    let ch = cs.alloc(
588      || "ch",
589      || {
590        ch_value.ok_or(SynthesisError::AssignmentMissing).map(|v| {
591          if v {
592            Scalar::ONE
593          } else {
594            Scalar::ZERO
595          }
596        })
597      },
598    )?;
599
600    // a(b - c) = ch - c
601    cs.enforce(
602      || "ch computation",
603      |_| b.lc(CS::one(), Scalar::ONE) - &c.lc(CS::one(), Scalar::ONE),
604      |_| a.lc(CS::one(), Scalar::ONE),
605      |lc| lc + ch - &c.lc(CS::one(), Scalar::ONE),
606    );
607
608    Ok(
609      AllocatedBit {
610        value: ch_value,
611        variable: ch,
612      }
613      .into(),
614    )
615  }
616
617  /// Computes (a and b) xor (a and c) xor (b and c)
618  pub fn sha256_maj<'a, Scalar, CS>(
619    mut cs: CS,
620    a: &'a Self,
621    b: &'a Self,
622    c: &'a Self,
623  ) -> Result<Self, SynthesisError>
624  where
625    Scalar: PrimeField,
626    CS: ConstraintSystem<Scalar>,
627  {
628    let maj_value = match (a.get_value(), b.get_value(), c.get_value()) {
629      (Some(a), Some(b), Some(c)) => {
630        // (a and b) xor (a and c) xor (b and c)
631        Some((a & b) ^ (a & c) ^ (b & c))
632      }
633      _ => None,
634    };
635
636    match (a, b, c) {
637      (&Boolean::Constant(_), &Boolean::Constant(_), &Boolean::Constant(_)) => {
638        // They're all constants, so we can just compute the value.
639
640        return Ok(Boolean::Constant(maj_value.expect("they're all constants")));
641      }
642      (&Boolean::Constant(false), b, c) => {
643        // If a is false,
644        // (a and b) xor (a and c) xor (b and c)
645        // equals
646        // (b and c)
647        return Boolean::and(cs, b, c);
648      }
649      (a, &Boolean::Constant(false), c) => {
650        // If b is false,
651        // (a and b) xor (a and c) xor (b and c)
652        // equals
653        // (a and c)
654        return Boolean::and(cs, a, c);
655      }
656      (a, b, &Boolean::Constant(false)) => {
657        // If c is false,
658        // (a and b) xor (a and c) xor (b and c)
659        // equals
660        // (a and b)
661        return Boolean::and(cs, a, b);
662      }
663      (a, b, &Boolean::Constant(true)) => {
664        // If c is true,
665        // (a and b) xor (a and c) xor (b and c)
666        // equals
667        // (a and b) xor (a) xor (b)
668        // equals
669        // not ((not a) and (not b))
670        return Ok(Boolean::and(cs, &a.not(), &b.not())?.not());
671      }
672      (a, &Boolean::Constant(true), c) => {
673        // If b is true,
674        // (a and b) xor (a and c) xor (b and c)
675        // equals
676        // (a) xor (a and c) xor (c)
677        return Ok(Boolean::and(cs, &a.not(), &c.not())?.not());
678      }
679      (&Boolean::Constant(true), b, c) => {
680        // If a is true,
681        // (a and b) xor (a and c) xor (b and c)
682        // equals
683        // (b) xor (c) xor (b and c)
684        return Ok(Boolean::and(cs, &b.not(), &c.not())?.not());
685      }
686      (
687        &Boolean::Is(_) | &Boolean::Not(_),
688        &Boolean::Is(_) | &Boolean::Not(_),
689        &Boolean::Is(_) | &Boolean::Not(_),
690      ) => {}
691    }
692
693    let maj = cs.alloc(
694      || "maj",
695      || {
696        maj_value.ok_or(SynthesisError::AssignmentMissing).map(|v| {
697          if v {
698            Scalar::ONE
699          } else {
700            Scalar::ZERO
701          }
702        })
703      },
704    )?;
705
706    // ¬(¬a ∧ ¬b) ∧ ¬(¬a ∧ ¬c) ∧ ¬(¬b ∧ ¬c)
707    // (1 - ((1 - a) * (1 - b))) * (1 - ((1 - a) * (1 - c))) * (1 - ((1 - b) * (1 - c)))
708    // (a + b - ab) * (a + c - ac) * (b + c - bc)
709    // -2abc + ab + ac + bc
710    // a (-2bc + b + c) + bc
711    //
712    // (b) * (c) = (bc)
713    // (2bc - b - c) * (a) = bc - maj
714
715    let bc = Self::and(cs.namespace(|| "b and c"), b, c)?;
716
717    cs.enforce(
718      || "maj computation",
719      |_| {
720        bc.lc(CS::one(), Scalar::ONE) + &bc.lc(CS::one(), Scalar::ONE)
721          - &b.lc(CS::one(), Scalar::ONE)
722          - &c.lc(CS::one(), Scalar::ONE)
723      },
724      |_| a.lc(CS::one(), Scalar::ONE),
725      |_| bc.lc(CS::one(), Scalar::ONE) - maj,
726    );
727
728    Ok(
729      AllocatedBit {
730        value: maj_value,
731        variable: maj,
732      }
733      .into(),
734    )
735  }
736}
737
738impl From<AllocatedBit> for Boolean {
739  fn from(b: AllocatedBit) -> Boolean {
740    Boolean::Is(b)
741  }
742}