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
36template <typename Builder>
39{
40 ASSERT((value.witness == bb::fr::zero()) || (value.witness == bb::fr::one()),
41 "bool_t: witness value is not 0 or 1");
42 witness_index = value.witness_index;
43 // Constrain x := other.witness by the relation x^2 = x
44 context->create_bool_gate(witness_index);
45 witness_bool = (value.witness == bb::fr::one());
46 witness_inverted = false;
48}
52template <typename Builder>
53bool_t<Builder>::bool_t(Builder* parent_context, const bool value)
54 : context(parent_context)
55 , witness_bool(value)
56{}
57
61template <typename Builder>
64 , witness_bool(other.witness_bool)
65 , witness_inverted(other.witness_inverted)
66 , witness_index(other.witness_index)
67 , tag(other.tag)
68{}
73template <typename Builder>
76 , witness_bool(other.witness_bool)
77 , witness_inverted(other.witness_inverted)
78 , witness_index(other.witness_index)
79 , tag(other.tag)
80{}
81
85template <typename Builder> bool_t<Builder>& bool_t<Builder>::operator=(const bool other)
86{
87 context = nullptr;
88 witness_index = IS_CONSTANT;
89 witness_bool = other;
90 witness_inverted = false;
91 return *this;
92}
97template <typename Builder> bool_t<Builder>& bool_t<Builder>::operator=(const bool_t& other)
98{
99 context = other.context;
100 witness_index = other.witness_index;
101 witness_bool = other.witness_bool;
102 witness_inverted = other.witness_inverted;
103 tag = other.tag;
104 return *this;
106
110template <typename Builder> bool_t<Builder>& bool_t<Builder>::operator=(bool_t&& other)
111{
112 context = other.context;
113 witness_index = other.witness_index;
114 witness_bool = other.witness_bool;
115 witness_inverted = other.witness_inverted;
116 tag = other.tag;
117 return *this;
118}
123template <typename Builder> bool_t<Builder>& bool_t<Builder>::operator=(const witness_t<Builder>& other)
124{
125 ASSERT((other.witness == bb::fr::one()) || (other.witness == bb::fr::zero()));
126 context = other.context;
127 witness_bool = other.witness == bb::fr::one();
128 witness_index = other.witness_index;
129 witness_inverted = false;
130 // Constrain x := other.witness by the relation x^2 = x
131 context->create_bool_gate(witness_index);
132 set_free_witness_tag();
133 return *this;
134}
135
139template <typename Builder> bool_t<Builder> bool_t<Builder>::operator&(const bool_t& other) const
140{
141 bool_t<Builder> result(context ? context : other.context);
142 bool left = witness_inverted ^ witness_bool;
143 bool right = other.witness_inverted ^ other.witness_bool;
144 result.witness_bool = left && right;
145
146 ASSERT(result.context || (is_constant() && other.is_constant()));
147 if (!is_constant() && !other.is_constant()) {
149 result.witness_index = context->add_variable(value);
150
179 int i_a(static_cast<int>(witness_inverted));
180 int i_b(static_cast<int>(other.witness_inverted));
181
182 fr q_m{ 1 - 2 * i_b - 2 * i_a + 4 * i_a * i_b };
183 fr q_l{ i_b * (1 - 2 * i_a) };
184 fr q_r{ i_a * (1 - 2 * i_b) };
185 fr q_o{ -1 };
186 fr q_c{ i_a * i_b };
187
188 context->create_poly_gate(
189 { witness_index, other.witness_index, result.witness_index, q_m, q_l, q_r, q_o, q_c });
190 } else if (!is_constant() && other.is_constant()) {
191 ASSERT(!other.witness_inverted);
192 // If rhs is a constant true, the output is determined by the lhs. Otherwise the output is a constant
193 // `false`.
194 result = other.witness_bool ? *this : other;
195
196 } else if (is_constant() && !other.is_constant()) {
197 ASSERT(!witness_inverted);
198 // If lhs is a constant true, the output is determined by the rhs. Otherwise the output is a constant
199 // `false`.
200 result = witness_bool ? other : *this;
201 }
202
203 result.tag = OriginTag(tag, other.tag);
204 return result;
205}
206
210template <typename Builder> bool_t<Builder> bool_t<Builder>::operator|(const bool_t& other) const
211{
212 bool_t<Builder> result(context ? context : other.context);
213
214 ASSERT(result.context || (is_constant() && other.is_constant()));
215
216 result.witness_bool = (witness_bool ^ witness_inverted) | (other.witness_bool ^ other.witness_inverted);
218 if (!is_constant() && !other.is_constant()) {
219 result.witness_index = context->add_variable(value);
220 // Let
221 // a := lhs = *this;
222 // b := rhs = other;
223 // The result is given by
224 // a + b - a * b = [-(1 - 2*i_a) * (1 - 2*i_b)] * w_a w_b +
225 // [(1 - 2 * i_a) * (1 - i_b)] * w_a
226 // [(1 - 2 * i_b) * (1 - i_a)] * w_b
227 // [i_a + i_b - i_a * i_b] * 1
228 const int rhs_inverted = static_cast<int>(other.witness_inverted);
229 const int lhs_inverted = static_cast<int>(witness_inverted);
230
231 bb::fr q_m{ -(1 - 2 * rhs_inverted) * (1 - 2 * lhs_inverted) };
232 bb::fr q_l{ (1 - 2 * lhs_inverted) * (1 - rhs_inverted) };
233 bb::fr q_r{ (1 - lhs_inverted) * (1 - 2 * rhs_inverted) };
234 bb::fr q_o{ bb::fr::neg_one() };
235 bb::fr q_c{ rhs_inverted + lhs_inverted - rhs_inverted * lhs_inverted };
236
237 // Let r := a | b;
238 // Constrain
239 // q_m * w_a * w_b + q_l * w_a + q_r * w_b + q_o * r + q_c = 0
240 context->create_poly_gate(
241 { witness_index, other.witness_index, result.witness_index, q_m, q_l, q_r, q_o, q_c });
242 } else if (!is_constant() && other.is_constant()) {
243 BB_ASSERT_EQ(other.witness_inverted, false);
244
245 // If we are computing a | b and b is a constant `true`, the result is a constant `true` that does not
246 // depend on `a`.
247 result = other.witness_bool ? other : *this;
248
249 } else if (is_constant() && !other.is_constant()) {
250 // If we are computing a | b and `a` is a constant `true`, the result is a constant `true` that does not
251 // depend on `b`.
252 BB_ASSERT_EQ(witness_inverted, false);
253 result = witness_bool ? *this : other;
254 }
255 result.tag = OriginTag(tag, other.tag);
256 return result;
257}
258
262template <typename Builder> bool_t<Builder> bool_t<Builder>::operator^(const bool_t& other) const
263{
264 bool_t<Builder> result(context == nullptr ? other.context : context);
265
266 ASSERT(result.context || (is_constant() && other.is_constant()));
267
268 result.witness_bool = (witness_bool ^ witness_inverted) ^ (other.witness_bool ^ other.witness_inverted);
269 bb::fr value = result.witness_bool ? bb::fr::one() : bb::fr::zero();
270
271 if (!is_constant() && !other.is_constant()) {
272 result.witness_index = context->add_variable(value);
273 // Let
274 // a := lhs = *this;
275 // b := rhs = other;
276 // The result is given by
277 // a + b - 2 * a * b = [-2 *(1 - 2*i_a) * (1 - 2*i_b)] * w_a w_b +
278 // [(1 - 2 * i_a) * (1 - 2 * i_b)] * w_a
279 // [(1 - 2 * i_b) * (1 - 2 * i_a)] * w_b
280 // [i_a + i_b - 2 * i_a * i_b] * 1]
281 const int rhs_inverted = static_cast<int>(other.witness_inverted);
282 const int lhs_inverted = static_cast<int>(witness_inverted);
283 // Compute the value that's being used in several selectors
284 const int aux_prod = (1 - 2 * rhs_inverted) * (1 - 2 * lhs_inverted);
285
286 bb::fr q_m{ -2 * aux_prod };
287 bb::fr q_l{ aux_prod };
288 bb::fr q_r{ aux_prod };
289 bb::fr q_o{ bb::fr::neg_one() };
290 bb::fr q_c{ lhs_inverted + rhs_inverted - 2 * rhs_inverted * lhs_inverted };
291
292 // Let r := a ^ b;
293 // Constrain
294 // q_m * w_a * w_b + q_l * w_a + q_r * w_b + q_o * r + q_c = 0
295 context->create_poly_gate(
296 { witness_index, other.witness_index, result.witness_index, q_m, q_l, q_r, q_o, q_c });
297 } else if (!is_constant() && other.is_constant()) {
298 // witness ^ 1 = !witness
299 BB_ASSERT_EQ(other.witness_inverted, false);
300 result = other.witness_bool ? !*this : *this;
301
302 } else if (is_constant() && !other.is_constant()) {
303 BB_ASSERT_EQ(witness_inverted, false);
304 result = witness_bool ? !other : other;
305 }
306 result.tag = OriginTag(tag, other.tag);
307 return result;
308}
312template <typename Builder> bool_t<Builder> bool_t<Builder>::operator!() const
313{
314 bool_t<Builder> result(*this);
315 if (result.is_constant()) {
316 ASSERT(!witness_inverted);
317 // Negate the value of a constant bool_t element.
318 result.witness_bool = !result.witness_bool;
319 } else {
320 // Negate the `inverted` flag of a witnees bool_t element.
321 result.witness_inverted = !result.witness_inverted;
322 }
323 return result;
324}
325
329template <typename Builder> bool_t<Builder> bool_t<Builder>::operator==(const bool_t& other) const
330{
331 ASSERT(context || other.context || (is_constant() && other.is_constant()));
332 bool_t<Builder> result(context ? context : other.context);
333
334 result.witness_bool = (witness_bool ^ witness_inverted) == (other.witness_bool ^ other.witness_inverted);
335 if (!is_constant() && !other.is_constant()) {
336 const bb::fr value = result.witness_bool ? bb::fr::one() : bb::fr::zero();
337
338 result.witness_index = context->add_variable(value);
339
340 // Let
341 // a := lhs = *this;
342 // b := rhs = other;
343 // The result is given by
344 // 1 - a - b + 2 a * b = [2 *(1 - 2*i_a) * (1 - 2*i_b)] * w_a w_b +
345 // [-(1 - 2 * i_a) * (1 - 2 * i_b)] * w_a +
346 // [-(1 - 2 * i_b) * (1 - 2 * i_a)] * w_b +
347 // [1 - i_a - i_b + 2 * i_a * i_b] * 1
348 const int rhs_inverted = static_cast<int>(other.witness_inverted);
349 const int lhs_inverted = static_cast<int>(witness_inverted);
350 // Compute the value that's being used in several selectors
351 const int aux_prod = (1 - 2 * rhs_inverted) * (1 - 2 * lhs_inverted);
352 bb::fr q_m{ 2 * aux_prod };
353 bb::fr q_l{ -aux_prod };
354 bb::fr q_r{ -aux_prod };
355 bb::fr q_o{ bb::fr::neg_one() };
356 bb::fr q_c{ 1 - lhs_inverted - rhs_inverted + 2 * rhs_inverted * lhs_inverted };
357
358 context->create_poly_gate(
359 { witness_index, other.witness_index, result.witness_index, q_m, q_r, q_l, q_o, q_c });
360
361 } else if (!is_constant() && (other.is_constant())) {
362 // Compare *this with a constant other. If other == true, then we're checking *this == true. In this case we
363 // propagate *this without adding extra constraints, otherwise (if other = false), we propagate !*this.
364 BB_ASSERT_EQ(other.witness_inverted, false);
365 result = other.witness_bool ? *this : !(*this);
366 } else if (is_constant() && !other.is_constant()) {
367 // Completely analogous to the previous case.
368 BB_ASSERT_EQ(witness_inverted, false);
369 result = witness_bool ? other : !other;
370 }
371
372 result.tag = OriginTag(tag, other.tag);
373 return result;
374}
378template <typename Builder> bool_t<Builder> bool_t<Builder>::operator!=(const bool_t<Builder>& other) const
379{
380 return operator^(other);
381}
382
383template <typename Builder> bool_t<Builder> bool_t<Builder>::operator&&(const bool_t<Builder>& other) const
384{
385 return operator&(other);
386}
387
388template <typename Builder> bool_t<Builder> bool_t<Builder>::operator||(const bool_t<Builder>& other) const
389{
390 return operator|(other);
391}
392
396template <typename Builder> void bool_t<Builder>::assert_equal(const bool_t& rhs, std::string const& msg) const
397{
398 const bool_t lhs = *this;
399 Builder* ctx = lhs.get_context() ? lhs.get_context() : rhs.get_context();
400 (void)OriginTag(get_origin_tag(), rhs.get_origin_tag());
401 if (lhs.is_constant() && rhs.is_constant()) {
402 BB_ASSERT_EQ(lhs.get_value(), rhs.get_value());
403 } else if (lhs.is_constant()) {
405 // if rhs is inverted, flip the value of the lhs constant
406 const bool lhs_value = rhs.witness_inverted ? !lhs.witness_bool : lhs.witness_bool;
407 ctx->assert_equal_constant(rhs.witness_index, lhs_value, msg);
408 } else if (rhs.is_constant()) {
410 // if lhs is inverted, flip the value of the rhs constant
411 const bool rhs_value = lhs.witness_inverted ? !rhs.witness_bool : rhs.witness_bool;
412 ctx->assert_equal_constant(lhs.witness_index, rhs_value, msg);
413 } else {
414 bool_t left = lhs;
415 bool_t right = rhs;
416 // we need to normalize iff lhs or rhs has an inverted witness (but not both)
417 if (lhs.witness_inverted ^ rhs.witness_inverted) {
418 left = left.normalize();
419 right = right.normalize();
420 }
421 ctx->assert_equal(left.witness_index, right.witness_index, msg);
422 }
423}
424
428template <typename Builder>
430 const bool_t& lhs,
431 const bool_t& rhs)
432{
433 if (predicate.is_constant()) {
434 auto result = bool_t(predicate.get_value() ? lhs : rhs);
435 result.set_origin_tag(OriginTag(predicate.get_origin_tag(), lhs.get_origin_tag(), rhs.get_origin_tag()));
436 return result;
437 }
438
439 bool same = lhs.witness_index == rhs.witness_index;
440 bool witness_same = same && !lhs.is_constant() && (lhs.witness_inverted == rhs.witness_inverted);
441 bool const_same = same && lhs.is_constant() && (lhs.witness_bool == rhs.witness_bool);
442 if (witness_same || const_same) {
443 return lhs;
444 }
445 return (predicate && lhs) || (!predicate && rhs);
446}
447
451template <typename Builder> bool_t<Builder> bool_t<Builder>::implies(const bool_t<Builder>& other) const
452{
453 // Thanks to negation operator being free, this computation requires at most 1 gate.
454 return (!(*this) || other); // P => Q is equiv. to !P || Q (not(P) or Q).
455}
456
460template <typename Builder> void bool_t<Builder>::must_imply(const bool_t& other, std::string const& msg) const
461{
462 implies(other).assert_equal(true, msg);
463}
464
468template <typename Builder> bool_t<Builder> bool_t<Builder>::implies_both_ways(const bool_t<Builder>& other) const
469{
470 return !((*this) ^ other); // P <=> Q is equiv. to !(P ^ Q) (not(P xor Q)).
471}
472
478template <typename Builder> bool_t<Builder> bool_t<Builder>::normalize() const
479{
480 if (is_constant()) {
481 ASSERT(!witness_inverted);
482 return *this;
483 }
484
485 if (!witness_inverted) {
486 return *this;
487 }
488 // Let a := *this, need to constrain a = a_norm
489 // [1 - 2 i_a] w_a + [-1] w_a_norm + [i_a] = 0
490 // ^ ^ ^
491 // q_l q_o q_c
492 const bool value = witness_bool ^ witness_inverted;
493
494 uint32_t new_witness = context->add_variable(bb::fr{ static_cast<int>(value) });
495
496 const int inverted = static_cast<int>(witness_inverted);
497 bb::fr q_l{ 1 - 2 * inverted };
498 bb::fr q_c{ inverted };
499 bb::fr q_o = bb::fr::neg_one();
500 bb::fr q_m = bb::fr::zero();
501 bb::fr q_r = bb::fr::zero();
502 context->create_poly_gate({ witness_index, witness_index, new_witness, q_m, q_l, q_r, q_o, q_c });
503
504 witness_index = new_witness;
505 witness_bool = value;
506 witness_inverted = false;
507 return *this;
508}
509
511template class bool_t<bb::MegaCircuitBuilder>;
512
513} // namespace bb::stdlib
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:59
#define ASSERT(expression,...)
Definition assert.hpp:49
Implements boolean logic in-circuit.
Definition bool.hpp:59
bool get_value() const
Definition bool.hpp:109
bool is_constant() const
Definition bool.hpp:111
void set_origin_tag(const OriginTag &new_tag) const
Definition bool.hpp:119
bool_t implies(const bool_t &other) const
Implements implication operator in circuit.
Definition bool.cpp:451
bool_t normalize() const
A bool_t element is normalized if witness_inverted == false. For a given *this, output its normalized...
Definition bool.cpp:478
bool_t operator&(const bool_t &other) const
Implements AND in circuit.
Definition bool.cpp:139
void set_free_witness_tag()
Definition bool.hpp:121
bool_t operator!() const
Implements negation in circuit.
Definition bool.cpp:312
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:429
bool_t operator!=(const bool_t &other) const
Implements the not equal operator in circuit.
Definition bool.cpp:378
Builder * get_context() const
Definition bool.hpp:117
Builder * context
Definition bool.hpp:130
uint32_t witness_index
Definition bool.hpp:133
bool_t operator&&(const bool_t &other) const
Definition bool.cpp:383
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:388
void must_imply(const bool_t &other, std::string const &msg="bool_t::must_imply") const
Constrains the (a => b) == true.
Definition bool.cpp:460
bool_t & operator=(const bool other)
Assigns a native bool to bool_t object.
Definition bool.cpp:85
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:396
bool witness_inverted
Definition bool.hpp:132
bool_t operator|(const bool_t &other) const
Implements OR in circuit.
Definition bool.cpp:210
OriginTag tag
Definition bool.hpp:134
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:468
OriginTag get_origin_tag() const
Definition bool.hpp:120
bool_t operator^(const bool_t &other) const
Implements XOR in circuit.
Definition bool.cpp:262
bool_t operator==(const bool_t &other) const
Implements equality operator in circuit.
Definition bool.cpp:329
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()