Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
cycle_group.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 "../field/field.hpp"
12
13#include "./cycle_group.hpp"
18namespace bb::stdlib {
19
27// WORKTODO: is this fuzzer only logic? if so lets mark it accordingly e.g. with sig (Builder* _context, FUZZER_ONLY)
28template <typename Builder>
30 : x(0)
31 , y(0)
32 , _is_infinity(true)
33 , _is_constant(true)
34 , _is_standard(true)
35 , context(_context)
36{}
37
45template <typename Builder>
47 : x(_x.normalize())
48 , y(_y.normalize())
49 , _is_infinity(is_infinity)
50 , _is_constant(_x.is_constant() && _y.is_constant() && is_infinity.is_constant())
51 , _is_standard(is_infinity.is_constant())
52{
53 if (_x.get_context() != nullptr) {
54 context = _x.get_context();
55 } else if (_y.get_context() != nullptr) {
56 context = _y.get_context();
57 } else {
58 context = is_infinity.get_context();
59 }
60
61 if (is_infinity.is_constant() && is_infinity.get_value()) {
62 this->x = 0;
63 this->y = 0;
64 this->_is_infinity = true;
65 this->_is_constant = true;
66 }
67
68 // TODO(https://github.com/AztecProtocol/barretenberg/issues/1067): This ASSERT is missing in the constructor but
69 // causes schnorr acir test to fail due to a bad input (a public key that has x and y coordinate set to 0).
70 // Investigate this to be able to enable the test.
71 // ASSERT(get_value().on_curve());
72}
73
87template <typename Builder>
88cycle_group<Builder>::cycle_group(const FF& _x, const FF& _y, bool is_infinity)
89 : x(is_infinity ? 0 : _x)
90 , y(is_infinity ? 0 : _y)
91 , _is_infinity(is_infinity)
92 , _is_constant(true)
93 , _is_standard(true)
94 , context(nullptr)
96 ASSERT(get_value().on_curve());
97}
109template <typename Builder>
111 : x(_in.is_point_at_infinity() ? 0 : _in.x)
112 , y(_in.is_point_at_infinity() ? 0 : _in.y)
113 , _is_infinity(_in.is_point_at_infinity())
114 , _is_constant(true)
115 , _is_standard(true)
116 , context(nullptr)
117{}
118
126template <typename Builder> cycle_group<Builder> cycle_group<Builder>::one(Builder* _context)
128 field_t x(_context, Group::one.x);
129 field_t y(_context, Group::one.y);
130 return cycle_group<Builder>(x, y, /*is_infinity=*/false);
131}
132
145template <typename Builder>
147{
148 cycle_group result(_context);
149
150 // Point at infinity's coordinates break our arithmetic
151 // Since we are not using these coordinates anyway
152 // We can set them both to be zero
153 if (_in.is_point_at_infinity()) {
154 result.x = field_t(witness_t(_context, FF::zero()));
155 result.y = field_t(witness_t(_context, FF::zero()));
156 } else {
157 result.x = field_t(witness_t(_context, _in.x));
158 result.y = field_t(witness_t(_context, _in.y));
159 }
160 result._is_infinity = bool_t(witness_t(_context, _in.is_point_at_infinity()));
161 result._is_constant = false;
162 result._is_standard = true;
163 result.validate_is_on_curve();
164 result.set_free_witness_tag();
165 return result;
166}
167
180template <typename Builder>
182{
183 cycle_group result(_context);
184
185 // Point at infinity's coordinates break our arithmetic
186 // Since we are not using these coordinates anyway
187 // We can set them both to be zero
188 if (_in.is_point_at_infinity()) {
189 result.x = FF::zero();
190 result.y = FF::zero();
191 result._is_constant = true;
192 } else {
193 result.x = field_t(witness_t(_context, _in.x));
194 result.y = field_t(witness_t(_context, _in.y));
195 result.x.assert_equal(result.x.get_value());
196 result.y.assert_equal(result.y.get_value());
197 result._is_constant = false;
198 }
199 // point at infinity is circuit constant
200 result._is_infinity = _in.is_point_at_infinity();
201 result._is_standard = true;
202 result.unset_free_witness_tag();
203 return result;
204}
205
206template <typename Builder> Builder* cycle_group<Builder>::get_context(const cycle_group& other) const
207{
208 if (get_context() != nullptr) {
209 return get_context();
210 }
211 return other.get_context();
212}
213
215{
216 AffineElement result(x.get_value(), y.get_value());
217 if (is_point_at_infinity().get_value()) {
218 result.self_set_infinity();
219 }
220 return result;
221}
222
228template <typename Builder> void cycle_group<Builder>::validate_is_on_curve() const
229{
230 // This class is for short Weierstrass curves only!
231 static_assert(Group::curve_a == 0);
232 auto xx = x * x;
233 auto xxx = xx * x;
234 auto res = y.madd(y, -xxx - Group::curve_b);
235 // if the point is marked as the point at infinity, then res should be changed to 0, but otherwise, we leave res
236 // unchanged from the original value
237 res *= !is_point_at_infinity();
238 res.assert_is_zero();
239}
245{
246 this->standardize();
247 return *this;
248}
249
255template <typename Builder> void cycle_group<Builder>::set_point_at_infinity(const bool_t& is_infinity)
256{
257 BB_ASSERT_EQ(this->x.is_constant() && this->y.is_constant() && this->_is_infinity.is_constant(),
258 this->_is_constant);
259
260 this->_is_standard = true;
261
262 if (is_infinity.is_constant() && this->_is_infinity.is_constant()) {
263 // Check that it's not possible to enter the case when
264 // The point is already infinity, but `is_infinity` = false
265 ASSERT((this->_is_infinity.get_value() == is_infinity.get_value()) || is_infinity.get_value());
266
267 if (is_infinity.get_value()) {
268 this->x = 0;
269 this->y = 0;
270 this->_is_infinity = true;
271 this->_is_constant = true;
272 }
273 return;
274 }
275
276 if (is_infinity.is_constant() && !this->_is_infinity.is_constant()) {
277 if (is_infinity.get_value()) {
278 this->x = 0;
279 this->y = 0;
280 this->_is_infinity = true;
281 this->_is_constant = true;
282 } else {
283 this->_is_infinity.assert_equal(false);
284 this->_is_infinity = false;
285 }
286 return;
287 }
288
289 if (this->_is_infinity.is_constant() && this->_is_infinity.get_value()) {
290 // I can't imagine this case happening, but still
291 is_infinity.assert_equal(true);
292
293 this->x = 0;
294 this->y = 0;
295 this->_is_constant = true;
296 return;
297 }
298
299 this->x = field_t::conditional_assign(is_infinity, 0, this->x).normalize();
300 this->y = field_t::conditional_assign(is_infinity, 0, this->y).normalize();
301
302 // We won't bump into the case where we end up with non constant coordinates
303 ASSERT(!this->x.is_constant());
304 ASSERT(!this->y.is_constant());
305 this->_is_constant = false;
306
307 // We have to check this to avoid the situation, where we change the infinity
308 bool_t set_allowed = (this->_is_infinity == is_infinity) || is_infinity;
309 set_allowed.assert_equal(true);
310 this->_is_infinity = is_infinity;
311
312 // In case we set point at infinity on a constant without an existing context
313 if (this->context == nullptr) {
314 this->context = is_infinity.get_context();
315 }
316}
317
323template <typename Builder> void cycle_group<Builder>::standardize()
324{
325 BB_ASSERT_EQ(this->x.is_constant() && this->y.is_constant() && this->_is_infinity.is_constant(),
326 this->_is_constant);
327 if (this->_is_infinity.is_constant() && this->_is_infinity.get_value()) {
328 ASSERT(this->_is_constant);
329 ASSERT(this->_is_standard);
330 }
331
332 if (this->_is_standard) {
333 return;
334 }
335 this->_is_standard = true;
336
337 this->x = field_t::conditional_assign(this->_is_infinity, 0, this->x).normalize();
338 this->y = field_t::conditional_assign(this->_is_infinity, 0, this->y).normalize();
339}
340
348template <typename Builder>
351{
352 // ensure we use a value of y that is not zero. (only happens if point at infinity)
353 // this costs 0 gates if `is_infinity` is a circuit constant
354 auto modified_y = field_t::conditional_assign(is_point_at_infinity(), 1, y).normalize();
355
356 // We have to return the point at infinity immediately
357 // Cause in that very case the `modified_y` is a constant value, with witness_index = -1
358 // Hence the following `create_ecc_dbl_gate` will throw an ASSERTION error
359 if (this->is_point_at_infinity().is_constant() && this->is_point_at_infinity().get_value()) {
360 return *this;
361 }
362
363 cycle_group result;
364 if (hint.has_value()) {
365 auto x3 = hint.value().x;
366 auto y3 = hint.value().y;
367 if (is_constant()) {
368 result = cycle_group(x3, y3, is_point_at_infinity());
369 // We need to manually propagate the origin tag
370 result.set_origin_tag(get_origin_tag());
371
372 return result;
373 }
374
375 result = cycle_group(witness_t(context, x3), witness_t(context, y3), is_point_at_infinity());
376 } else {
377 auto x1 = x.get_value();
378 auto y1 = modified_y.get_value();
379
380 // N.B. the formula to derive the witness value for x3 mirrors the formula in elliptic_relation.hpp
381 // Specifically, we derive x^4 via the Short Weierstrass curve formula `y^2 = x^3 + b`
382 // i.e. x^4 = x * (y^2 - b)
383 // We must follow this pattern exactly to support the edge-case where the input is the point at infinity.
384 auto y_pow_2 = y1.sqr();
385 auto x_pow_4 = x1 * (y_pow_2 - Group::curve_b);
386 auto lambda_squared = (x_pow_4 * 9) / (y_pow_2 * 4);
387 auto lambda = (x1 * x1 * 3) / (y1 + y1);
388 auto x3 = lambda_squared - x1 - x1;
389 auto y3 = lambda * (x1 - x3) - y1;
390 if (is_constant()) {
391 auto result = cycle_group(x3, y3, is_point_at_infinity().get_value());
392 // We need to manually propagate the origin tag
393 result.set_origin_tag(get_origin_tag());
394 return result;
395 }
396
397 result = cycle_group(witness_t(context, x3), witness_t(context, y3), is_point_at_infinity());
398 }
399
400 context->create_ecc_dbl_gate(bb::ecc_dbl_gate_<FF>{
401 .x1 = x.get_witness_index(),
402 .y1 = modified_y.get_witness_index(),
403 .x3 = result.x.get_witness_index(),
404 .y3 = result.y.get_witness_index(),
405 });
406
407 // We need to manually propagate the origin tag
408 result.x.set_origin_tag(OriginTag(x.get_origin_tag(), y.get_origin_tag()));
409 result.y.set_origin_tag(OriginTag(x.get_origin_tag(), y.get_origin_tag()));
410 return result;
411}
412
425template <typename Builder>
427 const std::optional<AffineElement> hint) const
429{
430 auto context = get_context(other);
431
432 const bool lhs_constant = is_constant();
433 const bool rhs_constant = other.is_constant();
434 if (lhs_constant && !rhs_constant) {
435 auto lhs = cycle_group::from_constant_witness(context, get_value());
436 // We need to manually propagate the origin tag
437 lhs.set_origin_tag(get_origin_tag());
438 return lhs.unconditional_add(other, hint);
439 }
440 if (!lhs_constant && rhs_constant) {
441 auto rhs = cycle_group::from_constant_witness(context, other.get_value());
442 // We need to manually propagate the origin tag
443 rhs.set_origin_tag(other.get_origin_tag());
444 return unconditional_add(rhs, hint);
445 }
446 cycle_group result;
447 if (hint.has_value()) {
448 auto x3 = hint.value().x;
449 auto y3 = hint.value().y;
450 if (lhs_constant && rhs_constant) {
451 return cycle_group(x3, y3, /*is_infinity=*/false);
452 }
453 result = cycle_group(witness_t(context, x3), witness_t(context, y3), /*is_infinity=*/false);
454 } else {
455 const auto p1 = get_value();
456 const auto p2 = other.get_value();
457 AffineElement p3(Element(p1) + Element(p2));
458 if (lhs_constant && rhs_constant) {
459 auto result = cycle_group(p3);
460 // We need to manually propagate the origin tag
461 result.set_origin_tag(OriginTag(get_origin_tag(), other.get_origin_tag()));
462 return result;
463 }
464 field_t r_x(witness_t(context, p3.x));
465 field_t r_y(witness_t(context, p3.y));
466 result = cycle_group(r_x, r_y, /*is_infinity=*/false);
467 }
468 bb::ecc_add_gate_<FF> add_gate{
469 .x1 = x.get_witness_index(),
470 .y1 = y.get_witness_index(),
471 .x2 = other.x.get_witness_index(),
472 .y2 = other.y.get_witness_index(),
473 .x3 = result.x.get_witness_index(),
474 .y3 = result.y.get_witness_index(),
475 .sign_coefficient = 1,
476 };
477 context->create_ecc_add_gate(add_gate);
478
479 // We need to manually propagate the origin tag (merging the tag of two inputs)
480 result.set_origin_tag(OriginTag(get_origin_tag(), other.get_origin_tag()));
481 return result;
482}
483
495template <typename Builder>
497 const std::optional<AffineElement> hint) const
498{
499 if constexpr (!IS_ULTRA) {
500 return unconditional_add(-other, hint);
501 } else {
502 auto context = get_context(other);
503
504 const bool lhs_constant = is_constant();
505 const bool rhs_constant = other.is_constant();
506
507 if (lhs_constant && !rhs_constant) {
509 // We need to manually propagate the origin tag
510 lhs.set_origin_tag(get_origin_tag());
511 return lhs.unconditional_subtract(other, hint);
512 }
513 if (!lhs_constant && rhs_constant) {
515 // We need to manually propagate the origin tag
516 rhs.set_origin_tag(other.get_origin_tag());
517 return unconditional_subtract(rhs);
518 }
519 cycle_group result;
520 if (hint.has_value()) {
521 auto x3 = hint.value().x;
522 auto y3 = hint.value().y;
523 if (lhs_constant && rhs_constant) {
524 return cycle_group(x3, y3, /*is_infinity=*/false);
525 }
526 result = cycle_group(witness_t(context, x3), witness_t(context, y3), /*is_infinity=*/false);
527 } else {
528 auto p1 = get_value();
529 auto p2 = other.get_value();
530 AffineElement p3(Element(p1) - Element(p2));
531 if (lhs_constant && rhs_constant) {
532 auto result = cycle_group(p3);
533 // We need to manually propagate the origin tag
534 result.set_origin_tag(OriginTag(get_origin_tag(), other.get_origin_tag()));
535 return result;
536 }
537 field_t r_x(witness_t(context, p3.x));
538 field_t r_y(witness_t(context, p3.y));
539 result = cycle_group(r_x, r_y, /*is_infinity=*/false);
540 }
541 bb::ecc_add_gate_<FF> add_gate{
542 .x1 = x.get_witness_index(),
543 .y1 = y.get_witness_index(),
544 .x2 = other.x.get_witness_index(),
545 .y2 = other.y.get_witness_index(),
546 .x3 = result.x.get_witness_index(),
547 .y3 = result.y.get_witness_index(),
548 .sign_coefficient = -1,
549 };
550 context->create_ecc_add_gate(add_gate);
551
552 // We need to manually propagate the origin tag (merging the tag of two inputs)
553 result.set_origin_tag(OriginTag(get_origin_tag(), other.get_origin_tag()));
554 return result;
555 }
556}
557
571template <typename Builder>
573 const std::optional<AffineElement> hint) const
574{
575 field_t x_delta = this->x - other.x;
576 if (x_delta.is_constant()) {
577 ASSERT(x_delta.get_value() != 0);
578 } else {
579 x_delta.assert_is_not_zero("cycle_group::checked_unconditional_add, x-coordinate collision");
580 }
581 return unconditional_add(other, hint);
582}
583
597template <typename Builder>
599 const std::optional<AffineElement> hint) const
600{
601 field_t x_delta = this->x - other.x;
602 if (x_delta.is_constant()) {
603 ASSERT(x_delta.get_value() != 0);
604 } else {
605 x_delta.assert_is_not_zero("cycle_group::checked_unconditional_subtract, x-coordinate collision");
606 }
607 return unconditional_subtract(other, hint);
608}
609
620template <typename Builder> cycle_group<Builder> cycle_group<Builder>::operator+(const cycle_group& other) const
621{
622 if (this->_is_infinity.is_constant() && this->_is_infinity.get_value()) {
623 return other;
624 }
625 if (other._is_infinity.is_constant() && other._is_infinity.get_value()) {
626 return *this;
627 }
628
629 const bool_t x_coordinates_match = (x == other.x);
630 const bool_t y_coordinates_match = (y == other.y);
631 const bool_t double_predicate = (x_coordinates_match && y_coordinates_match);
632 const bool_t infinity_predicate = (x_coordinates_match && !y_coordinates_match);
633
634 auto x1 = x;
635 auto y1 = y;
636 auto x2 = other.x;
637 auto y2 = other.y;
638 // if x_coordinates match, lambda triggers a divide by zero error.
639 // Adding in `x_coordinates_match` ensures that lambda will always be well-formed
640 auto x_diff = x2.add_two(-x1, x_coordinates_match);
641 // Computes lambda = (y2-y1)/x_diff, using the fact that x_diff is never 0
642 field_t lambda;
643 if ((y1.is_constant() && y2.is_constant()) || x_diff.is_constant()) {
644 lambda = (y2 - y1).divide_no_zero_check(x_diff);
645 } else {
646 lambda =
647 field_t::from_witness(this->get_context(other), (y2.get_value() - y1.get_value()) / x_diff.get_value());
648 // We need to manually propagate the origin tag
649 lambda.set_origin_tag(OriginTag(x_diff.get_origin_tag(), y1.get_origin_tag(), y2.get_origin_tag()));
650 field_t::evaluate_polynomial_identity(x_diff, lambda, -y2, y1);
651 }
652
653 auto x3 = lambda.madd(lambda, -(x2 + x1));
654 auto y3 = lambda.madd(x1 - x3, -y1);
655 cycle_group add_result(x3, y3, x_coordinates_match);
656
657 auto dbl_result = dbl();
658
659 // dbl if x_match, y_match
660 // infinity if x_match, !y_match
661 auto result_x = field_t::conditional_assign(double_predicate, dbl_result.x, add_result.x);
662 auto result_y = field_t::conditional_assign(double_predicate, dbl_result.y, add_result.y);
663
664 const bool_t lhs_infinity = is_point_at_infinity();
665 const bool_t rhs_infinity = other.is_point_at_infinity();
666 // if lhs infinity, return rhs
667 result_x = field_t::conditional_assign(lhs_infinity, other.x, result_x);
668 result_y = field_t::conditional_assign(lhs_infinity, other.y, result_y);
669
670 // if rhs infinity, return lhs
671 result_x = field_t::conditional_assign(rhs_infinity, x, result_x).normalize();
672 result_y = field_t::conditional_assign(rhs_infinity, y, result_y).normalize();
673
674 // is result point at infinity?
675 // yes = infinity_predicate && !lhs_infinity && !rhs_infinity
676 // yes = lhs_infinity && rhs_infinity
677 bool_t result_is_infinity = infinity_predicate && (!lhs_infinity && !rhs_infinity);
678 result_is_infinity = result_is_infinity || (lhs_infinity && rhs_infinity);
679
680 return cycle_group(result_x, result_y, result_is_infinity);
681}
682
693template <typename Builder> cycle_group<Builder> cycle_group<Builder>::operator-(const cycle_group& other) const
694{
695 if (other._is_infinity.is_constant() && other._is_infinity.get_value()) {
696 return *this;
697 }
698 if (this->_is_infinity.is_constant() && this->_is_infinity.get_value()) {
699 return -other;
700 }
701
702 const bool_t x_coordinates_match = (x == other.x);
703 const bool_t y_coordinates_match = (y == other.y);
704 const bool_t double_predicate = (x_coordinates_match && !y_coordinates_match).normalize();
705 const bool_t infinity_predicate = (x_coordinates_match && y_coordinates_match).normalize();
706 if constexpr (IsUltraBuilder<Builder>) {
707 if (!infinity_predicate.is_constant()) {
708 infinity_predicate.get_context()->update_used_witnesses(infinity_predicate.witness_index);
709 }
710 }
711 auto x1 = x;
712 auto y1 = y;
713 auto x2 = other.x;
714 auto y2 = other.y;
715 auto x_diff = x2.add_two(-x1, x_coordinates_match);
716 // Computes lambda = (-y2-y1)/x_diff, using the fact that x_diff is never 0
717 field_t lambda;
718 if ((y1.is_constant() && y2.is_constant()) || x_diff.is_constant()) {
719 lambda = (-y2 - y1).divide_no_zero_check(x_diff);
720 } else {
721 lambda =
722 field_t::from_witness(this->get_context(other), (-y2.get_value() - y1.get_value()) / x_diff.get_value());
723 // We need to manually propagate the origin tag
724 lambda.set_origin_tag(OriginTag(x_diff.get_origin_tag(), y1.get_origin_tag(), y2.get_origin_tag()));
725 field_t::evaluate_polynomial_identity(x_diff, lambda, y2, y1);
726 }
727
728 auto x3 = lambda.madd(lambda, -(x2 + x1));
729 auto y3 = lambda.madd(x1 - x3, -y1);
730 cycle_group add_result(x3, y3, x_coordinates_match);
731
732 auto dbl_result = dbl();
733
734 // dbl if x_match, !y_match
735 // infinity if x_match, y_match
736 auto result_x = field_t::conditional_assign(double_predicate, dbl_result.x, add_result.x);
737 auto result_y = field_t::conditional_assign(double_predicate, dbl_result.y, add_result.y);
738
739 if constexpr (IsUltraBuilder<Builder>) {
740 if (result_x.get_context()) {
741 result_x.get_context()->update_used_witnesses(result_x.witness_index);
742 }
743 if (result_y.get_context()) {
744 result_y.get_context()->update_used_witnesses(result_y.witness_index);
745 }
746 }
747
748 const bool_t lhs_infinity = is_point_at_infinity();
749 const bool_t rhs_infinity = other.is_point_at_infinity();
750 // if lhs infinity, return -rhs
751 result_x = field_t::conditional_assign(lhs_infinity, other.x, result_x);
752 result_y = field_t::conditional_assign(lhs_infinity, (-other.y).normalize(), result_y);
753
754 // if rhs infinity, return lhs
755 result_x = field_t::conditional_assign(rhs_infinity, x, result_x).normalize();
756 result_y = field_t::conditional_assign(rhs_infinity, y, result_y).normalize();
757
758 // is result point at infinity?
759 // yes = infinity_predicate && !lhs_infinity && !rhs_infinity
760 // yes = lhs_infinity && rhs_infinity
761 // n.b. can likely optimize this
762 bool_t result_is_infinity = infinity_predicate && (!lhs_infinity && !rhs_infinity);
763 result_is_infinity = result_is_infinity || (lhs_infinity && rhs_infinity);
764
765 return cycle_group(result_x, result_y, result_is_infinity);
766}
767
775template <typename Builder> cycle_group<Builder> cycle_group<Builder>::operator-() const
776{
777 cycle_group result(*this);
778 // We have to normalize immediately. All the methods, related to
779 // elliptic curve operations, assume that the coordinates are in normalized form and
780 // don't perform any extra normalizations
781 result.y = (-y).normalize();
782 return result;
783}
784
785template <typename Builder> cycle_group<Builder>& cycle_group<Builder>::operator+=(const cycle_group& other)
786{
787 *this = *this + other;
788 return *this;
789}
790
791template <typename Builder> cycle_group<Builder>& cycle_group<Builder>::operator-=(const cycle_group& other)
792{
793 *this = *this - other;
794 return *this;
795}
796
823template <typename Builder>
825 const std::span<cycle_scalar> scalars,
826 const std::span<cycle_group> base_points,
827 const std::span<AffineElement const> offset_generators,
828 const bool unconditional_add)
829{
830 BB_ASSERT_EQ(scalars.size(), base_points.size());
831
832 Builder* context = nullptr;
833 for (auto& scalar : scalars) {
834 if (scalar.lo.get_context() != nullptr) {
835 context = scalar.get_context();
836 break;
837 }
838 }
839 for (auto& point : base_points) {
840 if (point.get_context() != nullptr) {
841 context = point.get_context();
842 break;
843 }
844 }
845
846 size_t num_bits = 0;
847 for (auto& s : scalars) {
848 num_bits = std::max(num_bits, s.num_bits());
849 }
850 size_t num_rounds = (num_bits + TABLE_BITS - 1) / TABLE_BITS;
851
852 const size_t num_points = scalars.size();
853
855
862 std::vector<Element> operation_transcript;
863 std::vector<std::vector<Element>> native_straus_tables;
864 Element offset_generator_accumulator = offset_generators[0];
865 {
866 for (size_t i = 0; i < num_points; ++i) {
867 std::vector<Element> native_straus_table;
868 native_straus_table.emplace_back(offset_generators[i + 1]);
869 size_t table_size = 1ULL << TABLE_BITS;
870 for (size_t j = 1; j < table_size; ++j) {
871 native_straus_table.emplace_back(native_straus_table[j - 1] + base_points[i].get_value());
872 }
873 native_straus_tables.emplace_back(native_straus_table);
874 }
875 for (size_t i = 0; i < num_points; ++i) {
876 scalar_slices.emplace_back(straus_scalar_slice(context, scalars[i], TABLE_BITS));
877
879 base_points[i].get_value(), offset_generators[i + 1], TABLE_BITS);
880 std::copy(table_transcript.begin() + 1, table_transcript.end(), std::back_inserter(operation_transcript));
881 }
882 Element accumulator = offset_generators[0];
883
884 for (size_t i = 0; i < num_rounds; ++i) {
885 if (i != 0) {
886 for (size_t j = 0; j < TABLE_BITS; ++j) {
887 // offset_generator_accuulator is a regular Element, so dbl() won't add constraints
888 accumulator = accumulator.dbl();
889 operation_transcript.emplace_back(accumulator);
890 offset_generator_accumulator = offset_generator_accumulator.dbl();
891 }
892 }
893 for (size_t j = 0; j < num_points; ++j) {
894
895 const Element point =
896 native_straus_tables[j][static_cast<size_t>(scalar_slices[j].slices_native[num_rounds - i - 1])];
897
898 accumulator += point;
899
900 operation_transcript.emplace_back(accumulator);
901 offset_generator_accumulator = offset_generator_accumulator + Element(offset_generators[j + 1]);
902 }
903 }
904 }
905
906 // Normalize the computed witness points and convert into AffineElement type
907 Element::batch_normalize(&operation_transcript[0], operation_transcript.size());
908
909 std::vector<AffineElement> operation_hints;
910 operation_hints.reserve(operation_transcript.size());
911 for (auto& element : operation_transcript) {
912 operation_hints.emplace_back(AffineElement(element.x, element.y));
913 }
914
916 const size_t hints_per_table = (1ULL << TABLE_BITS) - 1;
917 OriginTag tag{};
918 for (size_t i = 0; i < num_points; ++i) {
919 std::span<AffineElement> table_hints(&operation_hints[i * hints_per_table], hints_per_table);
920 // Merge tags
921 tag = OriginTag(tag, scalars[i].get_origin_tag(), base_points[i].get_origin_tag());
922 scalar_slices.emplace_back(straus_scalar_slice(context, scalars[i], TABLE_BITS));
923 point_tables.emplace_back(straus_lookup_table(context, base_points[i], offset_generators[i + 1], TABLE_BITS));
924 }
925
926 AffineElement* hint_ptr = &operation_hints[num_points * hints_per_table];
927 cycle_group accumulator = offset_generators[0];
928
929 // populate the set of points we are going to add into our accumulator, *before* we do any ECC operations
930 // this way we are able to fuse mutliple ecc add / ecc double operations and reduce total gate count.
931 // (ecc add/ecc double gates normally cost 2 Ultra gates. However if we chain add->add, add->double,
932 // double->add, double->double, they only cost one)
933 std::vector<cycle_group> points_to_add;
934 for (size_t i = 0; i < num_rounds; ++i) {
935 for (size_t j = 0; j < num_points; ++j) {
936 const std::optional<field_t> scalar_slice = scalar_slices[j].read(num_rounds - i - 1);
937 // if we are doing a batch mul over scalars of different bit-lengths, we may not have any scalar bits for a
938 // given round and a given scalar
939 if (scalar_slice.has_value()) {
940 const cycle_group point = point_tables[j].read(scalar_slice.value());
941 points_to_add.emplace_back(point);
942 }
943 }
944 }
945
946 std::vector<std::tuple<field_t, field_t>> x_coordinate_checks;
947 size_t point_counter = 0;
948 for (size_t i = 0; i < num_rounds; ++i) {
949 if (i != 0) {
950 for (size_t j = 0; j < TABLE_BITS; ++j) {
951 accumulator = accumulator.dbl(*hint_ptr);
952 hint_ptr++;
953 }
954 }
955
956 for (size_t j = 0; j < num_points; ++j) {
957 const std::optional<field_t> scalar_slice = scalar_slices[j].read(num_rounds - i - 1);
958 // if we are doing a batch mul over scalars of different bit-lengths, we may not have a bit slice
959 // for a given round and a given scalar
960 BB_ASSERT_EQ(scalar_slice.value().get_value(), scalar_slices[j].slices_native[num_rounds - i - 1]);
961 if (scalar_slice.has_value()) {
962 const auto& point = points_to_add[point_counter++];
963 if (!unconditional_add) {
964 x_coordinate_checks.push_back({ accumulator.x, point.x });
965 }
966 accumulator = accumulator.unconditional_add(point, *hint_ptr);
967 hint_ptr++;
968 }
969 }
970 }
971
972 // validate that none of the x-coordinate differences are zero
973 // we batch the x-coordinate checks together
974 // because `assert_is_not_zero` witness generation needs a modular inversion (expensive)
975 field_t coordinate_check_product = 1;
976 for (auto& [x1, x2] : x_coordinate_checks) {
977 auto x_diff = x2 - x1;
978 coordinate_check_product *= x_diff;
979 }
980 coordinate_check_product.assert_is_not_zero("_variable_base_batch_mul_internal x-coordinate collision");
981
982 // Set the final accumulator's tag to the union of all points' and scalars' tags
983 accumulator.set_origin_tag(tag);
989 return { accumulator, AffineElement(offset_generator_accumulator) };
990}
991
1004template <typename Builder>
1006 const std::span<cycle_scalar> scalars,
1007 const std::span<AffineElement> base_points,
1008 const std::span<AffineElement const> /*unused*/)
1010{
1011 BB_ASSERT_EQ(scalars.size(), base_points.size());
1012
1013 const size_t num_points = base_points.size();
1016
1017 std::vector<MultiTableId> plookup_table_ids;
1018 std::vector<AffineElement> plookup_base_points;
1019 std::vector<field_t> plookup_scalars;
1020
1021 OriginTag tag{};
1022 for (size_t i = 0; i < num_points; ++i) {
1023 // Merge all tags of scalars
1024 tag = OriginTag(tag, scalars[i].get_origin_tag());
1027 ASSERT(table_id.has_value());
1028 plookup_table_ids.emplace_back(table_id.value()[0]);
1029 plookup_table_ids.emplace_back(table_id.value()[1]);
1030 plookup_base_points.emplace_back(base_points[i]);
1031 plookup_base_points.emplace_back(Element(base_points[i]) * (uint256_t(1) << cycle_scalar::LO_BITS));
1032 plookup_scalars.emplace_back(scalars[i].lo);
1033 plookup_scalars.emplace_back(scalars[i].hi);
1034 }
1035
1036 std::vector<cycle_group> lookup_points;
1037 Element offset_generator_accumulator = Group::point_at_infinity;
1038 for (size_t i = 0; i < plookup_scalars.size(); ++i) {
1039 plookup::ReadData<field_t> lookup_data =
1040 plookup_read<Builder>::get_lookup_accumulators(plookup_table_ids[i], plookup_scalars[i]);
1041 for (size_t j = 0; j < lookup_data[ColumnIdx::C2].size(); ++j) {
1042 const auto x = lookup_data[ColumnIdx::C2][j];
1043 const auto y = lookup_data[ColumnIdx::C3][j];
1044 lookup_points.emplace_back(cycle_group(x, y, /*is_infinity=*/false));
1045 }
1046
1049
1050 ASSERT(offset_1.has_value());
1051 offset_generator_accumulator += offset_1.value();
1052 }
1059 std::vector<Element> operation_transcript;
1060 {
1061 Element accumulator = lookup_points[0].get_value();
1062 for (size_t i = 1; i < lookup_points.size(); ++i) {
1063 accumulator = accumulator + (lookup_points[i].get_value());
1064 operation_transcript.emplace_back(accumulator);
1065 }
1066 }
1067 Element::batch_normalize(&operation_transcript[0], operation_transcript.size());
1068 std::vector<AffineElement> operation_hints;
1069 operation_hints.reserve(operation_transcript.size());
1070 for (auto& element : operation_transcript) {
1071 operation_hints.emplace_back(AffineElement(element.x, element.y));
1072 }
1073
1074 cycle_group accumulator = lookup_points[0];
1075 // Perform all point additions sequentially. The Ultra ecc_addition relation costs 1 gate iff additions are chained
1076 // and output point of previous addition = input point of current addition.
1077 // If this condition is not met, the addition relation costs 2 gates. So it's good to do these sequentially!
1078 for (size_t i = 1; i < lookup_points.size(); ++i) {
1079 accumulator = accumulator.unconditional_add(lookup_points[i], operation_hints[i - 1]);
1080 }
1086 // Set accumulator's origin tag to the union of all scalars' tags
1087 accumulator.set_origin_tag(tag);
1088 return { accumulator, offset_generator_accumulator };
1089}
1090
1124template <typename Builder>
1126 const std::vector<cycle_scalar>& scalars,
1128{
1129 BB_ASSERT_EQ(scalars.size(), base_points.size());
1130
1131 std::vector<cycle_scalar> variable_base_scalars;
1132 std::vector<cycle_group> variable_base_points;
1133 std::vector<cycle_scalar> fixed_base_scalars;
1134 std::vector<AffineElement> fixed_base_points;
1135
1136 // Merge all tags
1137 OriginTag result_tag;
1138 for (auto [point, scalar] : zip_view(base_points, scalars)) {
1139 result_tag = OriginTag(result_tag, OriginTag(point.get_origin_tag(), scalar.get_origin_tag()));
1140 }
1141 size_t num_bits = 0;
1142 for (auto& s : scalars) {
1143 num_bits = std::max(num_bits, s.num_bits());
1144
1145 // Note: is this the best place to put `validate_is_in_field`? Should it not be part of the constructor?
1146 // Note note: validate_scalar_is_in_field does not apply range checks to the hi/lo slices, this is performed
1147 // implicitly via the scalar mul algorithm
1148 s.validate_scalar_is_in_field();
1149 }
1150
1151 // if num_bits != NUM_BITS, skip lookup-version of fixed-base scalar mul. too much complexity
1152 bool num_bits_not_full_field_size = num_bits != NUM_BITS;
1153
1154 // When calling `_variable_base_batch_mul_internal`, we can unconditionally add iff all of the input points
1155 // are fixed-base points
1156 // (i.e. we are ULTRA Builder and we are doing fixed-base mul over points not present in our plookup tables)
1157 bool can_unconditional_add = true;
1158 bool has_non_constant_component = false;
1159 Element constant_acc = Group::point_at_infinity;
1160 for (size_t i = 0; i < scalars.size(); ++i) {
1161 bool scalar_constant = scalars[i].is_constant();
1162 bool point_constant = base_points[i].is_constant();
1163 if (scalar_constant && point_constant) {
1164 constant_acc += (base_points[i].get_value()) * (scalars[i].get_value());
1165 } else if (!scalar_constant && point_constant) {
1166 if (base_points[i].get_value().is_point_at_infinity()) {
1167 // oi mate, why are you creating a circuit that multiplies a known point at infinity?
1168 continue;
1169 }
1170 if constexpr (IS_ULTRA) {
1171 if (!num_bits_not_full_field_size &&
1173 fixed_base_scalars.push_back(scalars[i]);
1174 fixed_base_points.push_back(base_points[i].get_value());
1175 } else {
1176 // womp womp. We have lookup tables at home. ROM tables.
1177 variable_base_scalars.push_back(scalars[i]);
1178 variable_base_points.push_back(base_points[i]);
1179 }
1180 } else {
1181 fixed_base_scalars.push_back(scalars[i]);
1182 fixed_base_points.push_back(base_points[i].get_value());
1183 }
1184 has_non_constant_component = true;
1185 } else {
1186 variable_base_scalars.push_back(scalars[i]);
1187 variable_base_points.push_back(base_points[i]);
1188 can_unconditional_add = false;
1189 has_non_constant_component = true;
1190 // variable base
1191 }
1192 }
1193
1194 // If all inputs are constant, return the computed constant component and call it a day.
1195 if (!has_non_constant_component) {
1196 auto result = cycle_group(constant_acc);
1197 result.set_origin_tag(result_tag);
1198 return result;
1199 }
1200
1201 // add the constant component into our offset accumulator
1202 // (we'll subtract `offset_accumulator` from the MSM output i.e. we negate here to counter the future negation)
1203 Element offset_accumulator = -constant_acc;
1204 const bool has_variable_points = !variable_base_points.empty();
1205 const bool has_fixed_points = !fixed_base_points.empty();
1206
1207 // Compute all required offset generators.
1208 const size_t num_offset_generators =
1209 variable_base_points.size() + fixed_base_points.size() + has_variable_points + has_fixed_points;
1210 const std::span<AffineElement const> offset_generators =
1211 context.generators->get(num_offset_generators, 0, OFFSET_GENERATOR_DOMAIN_SEPARATOR);
1212
1213 cycle_group result;
1214 if (has_fixed_points) {
1215 const auto [fixed_accumulator, offset_generator_delta] =
1216 _fixed_base_batch_mul_internal(fixed_base_scalars, fixed_base_points, offset_generators);
1217 offset_accumulator += offset_generator_delta;
1218 result = fixed_accumulator;
1219 }
1220
1221 if (has_variable_points) {
1222 std::span<AffineElement const> offset_generators_for_variable_base_batch_mul{
1223 offset_generators.data() + fixed_base_points.size(), offset_generators.size() - fixed_base_points.size()
1224 };
1225 const auto [variable_accumulator, offset_generator_delta] =
1226 _variable_base_batch_mul_internal(variable_base_scalars,
1227 variable_base_points,
1228 offset_generators_for_variable_base_batch_mul,
1229 can_unconditional_add);
1230 offset_accumulator += offset_generator_delta;
1231 if (has_fixed_points) {
1232 result = can_unconditional_add ? result.unconditional_add(variable_accumulator)
1233 : result.checked_unconditional_add(variable_accumulator);
1234 } else {
1235 result = variable_accumulator;
1236 }
1237 }
1238
1239 // Update `result` to remove the offset generator terms, and add in any constant terms from `constant_acc`.
1240 // We have two potential modes here:
1241 // 1. All inputs are fixed-base and constant_acc is not the point at infinity
1242 // 2. Everything else.
1243 // Case 1 is a special case, as we *know* we cannot hit incomplete addition edge cases,
1244 // under the assumption that all input points are linearly independent of one another.
1245 // Because constant_acc is not the point at infnity we know that at least 1 input scalar was not zero,
1246 // i.e. the output will not be the point at infinity. We also know under case 1, we won't trigger the
1247 // doubling formula either, as every point is lienarly independent of every other point (including offset
1248 // generators).
1249 if (!constant_acc.is_point_at_infinity() && can_unconditional_add) {
1250 result = result.unconditional_add(AffineElement(-offset_accumulator));
1251 } else {
1252 // For case 2, we must use a full subtraction operation that handles all possible edge cases, as the output
1253 // point may be the point at infinity.
1254 // TODO(@zac-williamson) We can probably optimize this a bit actually. We might hit the point at infinity,
1255 // but an honest prover won't trigger the doubling edge case.
1256 // (doubling edge case implies input points are also the offset generator points,
1257 // which we can assume an honest Prover will not do if we make this case produce unsatisfiable constraints)
1258 // We could do the following:
1259 // 1. If x-coords match, assert y-coords do not match
1260 // 2. If x-coords match, return point at infinity, else return result - offset_accumulator.
1261 // This would be slightly cheaper than operator- as we do not have to evaluate the double edge case.
1262 result = result - AffineElement(offset_accumulator);
1263 }
1264 // Ensure the tag of the result is a union of all inputs
1265 result.set_origin_tag(result_tag);
1266 return result;
1267}
1268
1269template <typename Builder> cycle_group<Builder> cycle_group<Builder>::operator*(const cycle_scalar& scalar) const
1270{
1271 return batch_mul({ *this }, { scalar });
1272}
1273
1274template <typename Builder> cycle_group<Builder>& cycle_group<Builder>::operator*=(const cycle_scalar& scalar)
1275{
1276 *this = operator*(scalar);
1277 return *this;
1278}
1279
1280template <typename Builder> cycle_group<Builder> cycle_group<Builder>::operator*(const BigScalarField& scalar) const
1281{
1282 return batch_mul({ *this }, { scalar });
1283}
1284
1286{
1287 *this = operator*(scalar);
1288 return *this;
1289}
1290
1292{
1293 this->standardize();
1294 other.standardize();
1295 const auto equal = (x == other.x) && (y == other.y) && (this->_is_infinity == other._is_infinity);
1296 return equal;
1297}
1298
1299template <typename Builder> void cycle_group<Builder>::assert_equal(cycle_group& other, std::string const& msg)
1300{
1301 this->standardize();
1302 other.standardize();
1303 x.assert_equal(other.x, msg);
1304 y.assert_equal(other.y, msg);
1305 this->_is_infinity.assert_equal(other._is_infinity);
1306}
1307
1308template <typename Builder>
1310 const cycle_group& lhs,
1311 const cycle_group& rhs)
1312{
1313 auto x_res = field_t::conditional_assign(predicate, lhs.x, rhs.x).normalize();
1314 auto y_res = field_t::conditional_assign(predicate, lhs.y, rhs.y).normalize();
1315 auto _is_infinity_res =
1317
1318 bool _is_standard_res = lhs._is_standard && rhs._is_standard;
1319 if (predicate.is_constant()) {
1320 _is_standard_res = predicate.get_value() ? lhs._is_standard : rhs._is_standard;
1321 }
1322
1323 // Rare case when we bump into two constants, s.t. lhs = -rhs
1324 if (x_res.is_constant() && !y_res.is_constant()) {
1325 auto ctx = predicate.get_context();
1326 x_res = field_t::from_witness_index(ctx, ctx->put_constant_variable(x_res.get_value()));
1327 }
1328
1329 cycle_group<Builder> result(x_res, y_res, _is_infinity_res);
1330 result._is_standard = _is_standard_res;
1331 return result;
1332};
1333
1334template <typename Builder> cycle_group<Builder> cycle_group<Builder>::operator/(const cycle_group& /*unused*/) const
1335{
1336 // TODO(@kevaundray solve the discrete logarithm problem)
1337 throw_or_abort("Implementation under construction...");
1338}
1339
1342
1343} // namespace bb::stdlib
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:59
#define ASSERT(expression,...)
Definition assert.hpp:49
Container type for lookup table reads.
Definition types.hpp:418
static std::optional< std::array< MultiTableId, 2 > > get_lookup_table_ids_for_point(const affine_element &input)
Given a point, return (if it exists) the 2 MultiTableId's that correspond to the LO_SCALAR,...
static bool lookup_table_exists_for_point(const affine_element &input)
Given a point, do we have a precomputed lookup table for this point?
static std::optional< affine_element > get_generator_offset_for_table_id(MultiTableId table_id)
Given a table id, return the offset generator term that will be present in the final scalar mul outpu...
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
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
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
Builder * get_context() const
Definition bool.hpp:117
uint32_t witness_index
Definition bool.hpp:133
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
cycle_group represents a group Element of the proving system's embedded curve i.e....
static cycle_group from_constant_witness(Builder *_context, const AffineElement &_in)
Converts a native AffineElement into a witness, but constrains the witness values to be known constan...
cycle_group & operator*=(const cycle_scalar &scalar)
void standardize()
Get the point to the standard form. If the point is a point at infinity, ensure the coordinates are (...
static batch_mul_internal_output _fixed_base_batch_mul_internal(std::span< cycle_scalar > scalars, std::span< AffineElement > base_points, std::span< AffineElement const > offset_generators)
Internal algorithm to perform a fixed-base batch mul for ULTRA Builder.
cycle_group get_standard_form()
Get point in standard form. If the point is a point at infinity, ensure the coordinates are (0,...
typename Curve::Element Element
bool_t operator==(cycle_group &other)
Builder * get_context(const cycle_group &other) const
cycle_group & operator-=(const cycle_group &other)
static cycle_group conditional_assign(const bool_t &predicate, const cycle_group &lhs, const cycle_group &rhs)
void unset_free_witness_tag()
Unset the free witness flag for the cycle_group's tags.
cycle_group checked_unconditional_subtract(const cycle_group &other, const std::optional< AffineElement > hint=std::nullopt) const
Will evaluate ECC point subtraction over *this and other. Uses incomplete addition formula If incompl...
static cycle_group from_witness(Builder *_context, const AffineElement &_in)
Converts an AffineElement into a circuit witness.
cycle_group operator-() const
Negates a point.
static cycle_group one(Builder *_context)
Construct a cycle_group representation of Group::one.
void set_free_witness_tag()
Set the free witness flag for the cycle_group's tags.
void set_origin_tag(OriginTag tag) const
Set the origin tag for x, y and _is_infinity members of cycle_group.
cycle_group operator/(const cycle_group &other) const
cycle_group & operator+=(const cycle_group &other)
void validate_is_on_curve() const
On-curve check.
bool_t is_point_at_infinity() const
static batch_mul_internal_output _variable_base_batch_mul_internal(std::span< cycle_scalar > scalars, std::span< cycle_group > base_points, std::span< AffineElement const > offset_generators, bool unconditional_add)
Internal algorithm to perform a variable-base batch mul.
typename Builder::FF FF
cycle_group(Builder *_context=nullptr)
Construct a new cycle group<Builder>::cycle group object defaults to a constant point at infinity.
AffineElement get_value() const
OriginTag get_origin_tag() const
Get the origin tag of cycle_group (a merege of origin tags of x, y and _is_infinity members)
cycle_group operator*(const cycle_scalar &scalar) const
void assert_equal(cycle_group &other, std::string const &msg="cycle_group::assert_equal")
void set_point_at_infinity(const bool_t &is_infinity)
Set the point to the point at infinity. Depending on constant'ness of the predicate put the coordinat...
cycle_group dbl(const std::optional< AffineElement > hint=std::nullopt) const
Evaluates a doubling. Uses Ultra double gate.
cycle_group operator+(const cycle_group &other) const
Will evaluate ECC point addition over *this and other. This method uses complete addition i....
typename Curve::AffineElement AffineElement
cycle_group unconditional_subtract(const cycle_group &other, const std::optional< AffineElement > hint=std::nullopt) const
will evaluate ECC point subtraction over *this and other. Incomplete addition formula edge cases are ...
Builder * get_context() const
cycle_group checked_unconditional_add(const cycle_group &other, const std::optional< AffineElement > hint=std::nullopt) const
Will evaluate ECC point addition over *this and other. Uses incomplete addition formula If incomplete...
static cycle_group batch_mul(const std::vector< cycle_group > &base_points, const std::vector< BigScalarField > &scalars, GeneratorContext context={})
cycle_group unconditional_add(const cycle_group &other, const std::optional< AffineElement > hint=std::nullopt) const
Will evaluate ECC point addition over *this and other. Incomplete addition formula edge cases are NOT...
cycle_scalar represents a member of the cycle curve SCALAR FIELD. This is NOT the native circuit fiel...
static constexpr size_t LO_BITS
void assert_equal(const field_t &rhs, std::string const &msg="field_t::assert_equal") const
Copy constraint: constrain that *this field is equal to rhs element.
Definition field.cpp:929
field_t madd(const field_t &to_mul, const field_t &to_add) const
Definition field.cpp:507
static field_t from_witness_index(Builder *ctx, uint32_t witness_index)
Definition field.cpp:59
static void evaluate_polynomial_identity(const field_t &a, const field_t &b, const field_t &c, const field_t &d)
Given a, b, c, d, constrain a * b + c + d = 0 by creating a big_mul_gate.
Definition field.cpp:1107
static field_t conditional_assign(const bool_t< Builder > &predicate, const field_t &lhs, const field_t &rhs)
If predicate == true then return lhs, else return rhs.
Definition field.cpp:884
Builder * get_context() const
Definition field.hpp:389
bb::fr get_value() const
Given a := *this, compute its value given by a.v * a.mul + a.add.
Definition field.cpp:827
field_t normalize() const
Return a new element, where the in-circuit witness contains the actual represented value (multiplicat...
Definition field.cpp:635
static field_t from_witness(Builder *ctx, const bb::fr &input)
Definition field.hpp:424
bool is_constant() const
Definition field.hpp:399
void set_origin_tag(const OriginTag &new_tag) const
Definition field.hpp:332
field_t add_two(const field_t &add_b, const field_t &add_c) const
Efficiently compute (this + a + b) using big_mul gate.
Definition field.cpp:572
void assert_is_not_zero(std::string const &msg="field_t::assert_is_not_zero") const
Constrain *this to be non-zero by establishing that it has an inverse.
Definition field.cpp:707
uint32_t get_witness_index() const
Get the witness index of the current field element.
Definition field.hpp:461
static plookup::ReadData< field_pt > get_lookup_accumulators(const plookup::MultiTableId id, const field_pt &key_a, const field_pt &key_b=0, const bool is_2_to_1_lookup=false)
Definition plookup.cpp:19
straus_lookup_table computes a lookup table of size 1 << table_bits
static std::vector< Element > compute_straus_lookup_table_hints(const Element &base_point, const Element &offset_generator, size_t table_bits)
Compute the output points generated when computing the Straus lookup table.
straus_scalar_slice decomposes an input scalar into table_bits bit-slices. Used in batch_mul,...
StrictMock< MockContext > context
std::conditional_t< IsGoblinBigGroup< C, Fq, Fr, G >, element_goblin::goblin_element< C, goblin_field< C >, Fr, G >, element_default::element< C, Fq, Fr, G > > element
element wraps either element_default::element or element_goblin::goblin_element depending on parametr...
Univariate< Fr, domain_end, domain_start, skip_count > operator*(const Fr &ff, const Univariate< Fr, domain_end, domain_start, skip_count > &uv)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
This file contains part of the logic for the Origin Tag mechanism that tracks the use of in-circuit p...
Curve::Element Element
static constexpr field zero()
Stores temporary variables produced by internal multiplication algorithms.
void throw_or_abort(std::string const &err)