Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
bool.cpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: not started, auditors: [], date: YYYY-MM-DD }
3// external_1: { status: not started, auditors: [], date: YYYY-MM-DD }
4// external_2: { status: not started, auditors: [], date: YYYY-MM-DD }
5// =====================
6
7#include "bool.hpp"
8#include "../circuit_builders/circuit_builders.hpp"
11
12using namespace bb;
13
14namespace bb::stdlib {
15
19template <typename Builder>
21 : witness_bool(value)
22{}
23
27template <typename Builder>
29 : context(parent_context)
30{}
31
39template <typename Builder>
40bool_t<Builder>::bool_t(const witness_t<Builder>& value, const bool& use_range_constraint)
42{
43 ASSERT((value.witness == bb::fr::zero()) || (value.witness == bb::fr::one()),
44 "bool_t: witness value is not 0 or 1");
45 witness_index = value.witness_index;
46
47 if (use_range_constraint) {
48 // Create a range constraint gate
49 context->create_new_range_constraint(witness_index, 1, "bool_t: witness value is not 0 or 1");
50 } else {
51 // Create an arithmetic gate to enforce the relation x^2 = x
52 context->create_bool_gate(witness_index);
53 }
54 witness_bool = (value.witness == bb::fr::one());
55 witness_inverted = false;
57}
61template <typename Builder>
62bool_t<Builder>::bool_t(Builder* parent_context, const bool value)
63 : context(parent_context)
64 , witness_bool(value)
65{}
66
70template <typename Builder>
72 : context(other.context)
73 , witness_bool(other.witness_bool)
74 , witness_inverted(other.witness_inverted)
75 , witness_index(other.witness_index)
76 , tag(other.tag)
77{}
82template <typename Builder>
85 , witness_bool(other.witness_bool)
86 , witness_inverted(other.witness_inverted)
87 , witness_index(other.witness_index)
88 , tag(other.tag)
89{}
90
95template <typename Builder>
97{
98 ASSERT(witness_index != IS_CONSTANT);
99 bool_t<Builder> result(ctx);
100 result.witness_index = witness_index;
101 const bb::fr value = ctx->get_variable(witness_index);
102 // It does not create a constraint.
103 BB_ASSERT_EQ(value * value - value, 0, "bool_t: creating a witness bool from a non-boolean value");
104 result.witness_bool = (value == 1);
105 result.witness_inverted = false;
106 return result;
108
112template <typename Builder> bool_t<Builder>& bool_t<Builder>::operator=(const bool other)
113{
114 context = nullptr;
115 witness_index = IS_CONSTANT;
116 witness_bool = other;
117 witness_inverted = false;
118 return *this;
119}
120
124template <typename Builder> bool_t<Builder>& bool_t<Builder>::operator=(const bool_t& other)
125{
126 context = other.context;
127 witness_index = other.witness_index;
128 witness_bool = other.witness_bool;
129 witness_inverted = other.witness_inverted;
130 tag = other.tag;
131 return *this;
132}
133
137template <typename Builder> bool_t<Builder>& bool_t<Builder>::operator=(bool_t&& other)
138{
139 context = other.context;
140 witness_index = other.witness_index;
141 witness_bool = other.witness_bool;
142 witness_inverted = other.witness_inverted;
143 tag = other.tag;
144 return *this;
145}
150template <typename Builder> bool_t<Builder>& bool_t<Builder>::operator=(const witness_t<Builder>& other)
151{
152 ASSERT((other.witness == bb::fr::one()) || (other.witness == bb::fr::zero()));
153 context = other.context;
154 witness_bool = other.witness == bb::fr::one();
155 witness_index = other.witness_index;
156 witness_inverted = false;
157 // Constrain x := other.witness by the relation x^2 = x
158 context->create_bool_gate(witness_index);
159 set_free_witness_tag();
160 return *this;
161}
162
166template <typename Builder> bool_t<Builder> bool_t<Builder>::operator&(const bool_t& other) const
167{
168 bool_t<Builder> result(context ? context : other.context);
169 bool left = witness_inverted ^ witness_bool;
170 bool right = other.witness_inverted ^ other.witness_bool;
171 result.witness_bool = left && right;
172
173 ASSERT(result.context || (is_constant() && other.is_constant()));
174 if (!is_constant() && !other.is_constant()) {
176 result.witness_index = context->add_variable(value);
177
206 int i_a(static_cast<int>(witness_inverted));
207 int i_b(static_cast<int>(other.witness_inverted));
208
209 fr q_m{ 1 - 2 * i_b - 2 * i_a + 4 * i_a * i_b };
210 fr q_l{ i_b * (1 - 2 * i_a) };
211 fr q_r{ i_a * (1 - 2 * i_b) };
212 fr q_o{ -1 };
213 fr q_c{ i_a * i_b };
214
215 context->create_poly_gate(
216 { witness_index, other.witness_index, result.witness_index, q_m, q_l, q_r, q_o, q_c });
217 } else if (!is_constant() && other.is_constant()) {
218 ASSERT(!other.witness_inverted);
219 // If rhs is a constant true, the output is determined by the lhs. Otherwise the output is a constant
220 // `false`.
221 result = other.witness_bool ? *this : other;
222
223 } else if (is_constant() && !other.is_constant()) {
224 ASSERT(!witness_inverted);
225 // If lhs is a constant true, the output is determined by the rhs. Otherwise the output is a constant
226 // `false`.
227 result = witness_bool ? other : *this;
228 }
229
230 result.tag = OriginTag(tag, other.tag);
231 return result;
232}
233
237template <typename Builder> bool_t<Builder> bool_t<Builder>::operator|(const bool_t& other) const
238{
239 bool_t<Builder> result(context ? context : other.context);
240
241 ASSERT(result.context || (is_constant() && other.is_constant()));
242
243 result.witness_bool = (witness_bool ^ witness_inverted) | (other.witness_bool ^ other.witness_inverted);
245 if (!is_constant() && !other.is_constant()) {
246 result.witness_index = context->add_variable(value);
247 // Let
248 // a := lhs = *this;
249 // b := rhs = other;
250 // The result is given by
251 // a + b - a * b = [-(1 - 2*i_a) * (1 - 2*i_b)] * w_a w_b +
252 // [(1 - 2 * i_a) * (1 - i_b)] * w_a
253 // [(1 - 2 * i_b) * (1 - i_a)] * w_b
254 // [i_a + i_b - i_a * i_b] * 1
255 const int rhs_inverted = static_cast<int>(other.witness_inverted);
256 const int lhs_inverted = static_cast<int>(witness_inverted);
257
258 bb::fr q_m{ -(1 - 2 * rhs_inverted) * (1 - 2 * lhs_inverted) };
259 bb::fr q_l{ (1 - 2 * lhs_inverted) * (1 - rhs_inverted) };
260 bb::fr q_r{ (1 - lhs_inverted) * (1 - 2 * rhs_inverted) };
261 bb::fr q_o{ bb::fr::neg_one() };
262 bb::fr q_c{ rhs_inverted + lhs_inverted - rhs_inverted * lhs_inverted };
263
264 // Let r := a | b;
265 // Constrain
266 // q_m * w_a * w_b + q_l * w_a + q_r * w_b + q_o * r + q_c = 0
267 context->create_poly_gate(
268 { witness_index, other.witness_index, result.witness_index, q_m, q_l, q_r, q_o, q_c });
269 } else if (!is_constant() && other.is_constant()) {
270 BB_ASSERT_EQ(other.witness_inverted, false);
271
272 // If we are computing a | b and b is a constant `true`, the result is a constant `true` that does not
273 // depend on `a`.
274 result = other.witness_bool ? other : *this;
275
276 } else if (is_constant() && !other.is_constant()) {
277 // If we are computing a | b and `a` is a constant `true`, the result is a constant `true` that does not
278 // depend on `b`.
279 BB_ASSERT_EQ(witness_inverted, false);
280 result = witness_bool ? *this : other;
281 }
282 result.tag = OriginTag(tag, other.tag);
283 return result;
284}
285
289template <typename Builder> bool_t<Builder> bool_t<Builder>::operator^(const bool_t& other) const
290{
291 bool_t<Builder> result(context == nullptr ? other.context : context);
292
293 ASSERT(result.context || (is_constant() && other.is_constant()));
294
295 result.witness_bool = (witness_bool ^ witness_inverted) ^ (other.witness_bool ^ other.witness_inverted);
296 bb::fr value = result.witness_bool ? bb::fr::one() : bb::fr::zero();
297
298 if (!is_constant() && !other.is_constant()) {
299 result.witness_index = context->add_variable(value);
300 // Let
301 // a := lhs = *this;
302 // b := rhs = other;
303 // The result is given by
304 // a + b - 2 * a * b = [-2 *(1 - 2*i_a) * (1 - 2*i_b)] * w_a w_b +
305 // [(1 - 2 * i_a) * (1 - 2 * i_b)] * w_a
306 // [(1 - 2 * i_b) * (1 - 2 * i_a)] * w_b
307 // [i_a + i_b - 2 * i_a * i_b] * 1]
308 const int rhs_inverted = static_cast<int>(other.witness_inverted);
309 const int lhs_inverted = static_cast<int>(witness_inverted);
310 // Compute the value that's being used in several selectors
311 const int aux_prod = (1 - 2 * rhs_inverted) * (1 - 2 * lhs_inverted);
312
313 bb::fr q_m{ -2 * aux_prod };
314 bb::fr q_l{ aux_prod };
315 bb::fr q_r{ aux_prod };
316 bb::fr q_o{ bb::fr::neg_one() };
317 bb::fr q_c{ lhs_inverted + rhs_inverted - 2 * rhs_inverted * lhs_inverted };
318
319 // Let r := a ^ b;
320 // Constrain
321 // q_m * w_a * w_b + q_l * w_a + q_r * w_b + q_o * r + q_c = 0
322 context->create_poly_gate(
323 { witness_index, other.witness_index, result.witness_index, q_m, q_l, q_r, q_o, q_c });
324 } else if (!is_constant() && other.is_constant()) {
325 // witness ^ 1 = !witness
326 BB_ASSERT_EQ(other.witness_inverted, false);
327 result = other.witness_bool ? !*this : *this;
328
329 } else if (is_constant() && !other.is_constant()) {
330 BB_ASSERT_EQ(witness_inverted, false);
331 result = witness_bool ? !other : other;
332 }
333 result.tag = OriginTag(tag, other.tag);
334 return result;
335}
339template <typename Builder> bool_t<Builder> bool_t<Builder>::operator!() const
340{
341 bool_t<Builder> result(*this);
342 if (result.is_constant()) {
343 ASSERT(!witness_inverted);
344 // Negate the value of a constant bool_t element.
345 result.witness_bool = !result.witness_bool;
346 } else {
347 // Negate the `inverted` flag of a witnees bool_t element.
348 result.witness_inverted = !result.witness_inverted;
349 }
350 return result;
351}
352
356template <typename Builder> bool_t<Builder> bool_t<Builder>::operator==(const bool_t& other) const
357{
358 ASSERT(context || other.context || (is_constant() && other.is_constant()));
359 bool_t<Builder> result(context ? context : other.context);
360
361 result.witness_bool = (witness_bool ^ witness_inverted) == (other.witness_bool ^ other.witness_inverted);
362 if (!is_constant() && !other.is_constant()) {
363 const bb::fr value = result.witness_bool ? bb::fr::one() : bb::fr::zero();
364
365 result.witness_index = context->add_variable(value);
366
367 // Let
368 // a := lhs = *this;
369 // b := rhs = other;
370 // The result is given by
371 // 1 - a - b + 2 a * b = [2 *(1 - 2*i_a) * (1 - 2*i_b)] * w_a w_b +
372 // [-(1 - 2 * i_a) * (1 - 2 * i_b)] * w_a +
373 // [-(1 - 2 * i_b) * (1 - 2 * i_a)] * w_b +
374 // [1 - i_a - i_b + 2 * i_a * i_b] * 1
375 const int rhs_inverted = static_cast<int>(other.witness_inverted);
376 const int lhs_inverted = static_cast<int>(witness_inverted);
377 // Compute the value that's being used in several selectors
378 const int aux_prod = (1 - 2 * rhs_inverted) * (1 - 2 * lhs_inverted);
379 bb::fr q_m{ 2 * aux_prod };
380 bb::fr q_l{ -aux_prod };
381 bb::fr q_r{ -aux_prod };
382 bb::fr q_o{ bb::fr::neg_one() };
383 bb::fr q_c{ 1 - lhs_inverted - rhs_inverted + 2 * rhs_inverted * lhs_inverted };
384
385 context->create_poly_gate(
386 { witness_index, other.witness_index, result.witness_index, q_m, q_r, q_l, q_o, q_c });
387
388 } else if (!is_constant() && (other.is_constant())) {
389 // Compare *this with a constant other. If other == true, then we're checking *this == true. In this case we
390 // propagate *this without adding extra constraints, otherwise (if other = false), we propagate !*this.
391 BB_ASSERT_EQ(other.witness_inverted, false);
392 result = other.witness_bool ? *this : !(*this);
393 } else if (is_constant() && !other.is_constant()) {
394 // Completely analogous to the previous case.
395 BB_ASSERT_EQ(witness_inverted, false);
396 result = witness_bool ? other : !other;
397 }
398
399 result.tag = OriginTag(tag, other.tag);
400 return result;
401}
405template <typename Builder> bool_t<Builder> bool_t<Builder>::operator!=(const bool_t<Builder>& other) const
406{
407 return operator^(other);
408}
409
410template <typename Builder> bool_t<Builder> bool_t<Builder>::operator&&(const bool_t<Builder>& other) const
411{
412 return operator&(other);
413}
414
415template <typename Builder> bool_t<Builder> bool_t<Builder>::operator||(const bool_t<Builder>& other) const
416{
417 return operator|(other);
418}
419
423template <typename Builder> void bool_t<Builder>::assert_equal(const bool_t& rhs, std::string const& msg) const
424{
425 const bool_t lhs = *this;
426 Builder* ctx = lhs.get_context() ? lhs.get_context() : rhs.get_context();
427 (void)OriginTag(get_origin_tag(), rhs.get_origin_tag());
428 if (lhs.is_constant() && rhs.is_constant()) {
429 BB_ASSERT_EQ(lhs.get_value(), rhs.get_value());
430 } else if (lhs.is_constant()) {
432 // if rhs is inverted, flip the value of the lhs constant
433 const bool lhs_value = rhs.witness_inverted ? !lhs.witness_bool : lhs.witness_bool;
434 ctx->assert_equal_constant(rhs.witness_index, lhs_value, msg);
435 } else if (rhs.is_constant()) {
437 // if lhs is inverted, flip the value of the rhs constant
438 const bool rhs_value = lhs.witness_inverted ? !rhs.witness_bool : rhs.witness_bool;
439 ctx->assert_equal_constant(lhs.witness_index, rhs_value, msg);
440 } else {
441 bool_t left = lhs;
442 bool_t right = rhs;
443 // we need to normalize iff lhs or rhs has an inverted witness (but not both)
444 if (lhs.witness_inverted ^ rhs.witness_inverted) {
445 left = left.normalize();
446 right = right.normalize();
447 }
448 ctx->assert_equal(left.witness_index, right.witness_index, msg);
449 }
450}
451
455template <typename Builder>
457 const bool_t& lhs,
458 const bool_t& rhs)
459{
460 if (predicate.is_constant()) {
461 auto result = bool_t(predicate.get_value() ? lhs : rhs);
462 result.set_origin_tag(OriginTag(predicate.get_origin_tag(), lhs.get_origin_tag(), rhs.get_origin_tag()));
463 return result;
464 }
465
466 bool same = lhs.witness_index == rhs.witness_index;
467 bool witness_same = same && !lhs.is_constant() && (lhs.witness_inverted == rhs.witness_inverted);
468 bool const_same = same && lhs.is_constant() && (lhs.witness_bool == rhs.witness_bool);
469 if (witness_same || const_same) {
470 return lhs;
471 }
472 return (predicate && lhs) || (!predicate && rhs);
473}
474
478template <typename Builder> bool_t<Builder> bool_t<Builder>::implies(const bool_t<Builder>& other) const
479{
480 // Thanks to negation operator being free, this computation requires at most 1 gate.
481 return (!(*this) || other); // P => Q is equiv. to !P || Q (not(P) or Q).
482}
483
487template <typename Builder> void bool_t<Builder>::must_imply(const bool_t& other, std::string const& msg) const
488{
489 implies(other).assert_equal(true, msg);
490}
491
495template <typename Builder> bool_t<Builder> bool_t<Builder>::implies_both_ways(const bool_t<Builder>& other) const
496{
497 return !((*this) ^ other); // P <=> Q is equiv. to !(P ^ Q) (not(P xor Q)).
498}
499
505template <typename Builder> bool_t<Builder> bool_t<Builder>::normalize() const
506{
507 if (is_constant()) {
508 ASSERT(!witness_inverted);
509 return *this;
510 }
511
512 if (!witness_inverted) {
513 return *this;
514 }
515 // Let a := *this, need to constrain a = a_norm
516 // [1 - 2 i_a] w_a + [-1] w_a_norm + [i_a] = 0
517 // ^ ^ ^
518 // q_l q_o q_c
519 const bool value = witness_bool ^ witness_inverted;
520
521 uint32_t new_witness = context->add_variable(bb::fr{ static_cast<int>(value) });
522
523 const int inverted = static_cast<int>(witness_inverted);
524 bb::fr q_l{ 1 - 2 * inverted };
525 bb::fr q_c{ inverted };
526 bb::fr q_o = bb::fr::neg_one();
527 bb::fr q_m = bb::fr::zero();
528 bb::fr q_r = bb::fr::zero();
529 context->create_poly_gate({ witness_index, witness_index, new_witness, q_m, q_l, q_r, q_o, q_c });
530
531 witness_index = new_witness;
532 witness_bool = value;
533 witness_inverted = false;
534 return *this;
535}
536
538template class bool_t<bb::MegaCircuitBuilder>;
539
540} // namespace bb::stdlib
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:88
#define ASSERT(expression,...)
Definition assert.hpp:77
Implements boolean logic in-circuit.
Definition bool.hpp:59
bool get_value() const
Definition bool.hpp:111
bool is_constant() const
Definition bool.hpp:113
void set_origin_tag(const OriginTag &new_tag) const
Definition bool.hpp:128
bool_t implies(const bool_t &other) const
Implements implication operator in circuit.
Definition bool.cpp:478
bool_t normalize() const
A bool_t element is normalized if witness_inverted == false. For a given *this, output its normalized...
Definition bool.cpp:505
bool_t operator&(const bool_t &other) const
Implements AND in circuit.
Definition bool.cpp:166
void set_free_witness_tag()
Definition bool.hpp:130
bool_t operator!() const
Implements negation in circuit.
Definition bool.cpp:339
static bool_t conditional_assign(const bool_t< Builder > &predicate, const bool_t &lhs, const bool_t &rhs)
Implements the ternary operator - if predicate == true then return lhs, else return rhs.
Definition bool.cpp:456
bool_t operator!=(const bool_t &other) const
Implements the not equal operator in circuit.
Definition bool.cpp:405
Builder * get_context() const
Definition bool.hpp:126
Builder * context
Definition bool.hpp:141
uint32_t witness_index
Definition bool.hpp:144
bool_t operator&&(const bool_t &other) const
Definition bool.cpp:410
bool_t(const bool value=false)
Construct a constant bool_t object from a bool value.
Definition bool.cpp:20
bool_t operator||(const bool_t &other) const
Definition bool.cpp:415
void must_imply(const bool_t &other, std::string const &msg="bool_t::must_imply") const
Constrains the (a => b) == true.
Definition bool.cpp:487
bool_t & operator=(const bool other)
Assigns a native bool to bool_t object.
Definition bool.cpp:112
void assert_equal(const bool_t &rhs, std::string const &msg="bool_t::assert_equal") const
Implements copy constraint for bool_t elements.
Definition bool.cpp:423
bool witness_inverted
Definition bool.hpp:143
bool_t operator|(const bool_t &other) const
Implements OR in circuit.
Definition bool.cpp:237
OriginTag tag
Definition bool.hpp:145
static bool_t from_witness_index_unsafe(Builder *ctx, uint32_t witness_index)
Create a bool_t from a witness index that is known to contain a constrained bool value.
Definition bool.cpp:96
bool_t implies_both_ways(const bool_t &other) const
Implements a "double-implication" (<=>), a.k.a "iff", a.k.a. "biconditional".
Definition bool.cpp:495
OriginTag get_origin_tag() const
Definition bool.hpp:129
bool_t operator^(const bool_t &other) const
Implements XOR in circuit.
Definition bool.cpp:289
bool_t operator==(const bool_t &other) const
Implements equality operator in circuit.
Definition bool.cpp:356
StrictMock< MockContext > context
Entry point for Barretenberg command-line interface.
This file contains part of the logic for the Origin Tag mechanism that tracks the use of in-circuit p...
static constexpr field neg_one()
static constexpr field one()
static constexpr field zero()