Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
field_impl.hpp
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#pragma once
13#include <memory>
14#include <span>
15#include <type_traits>
16#include <vector>
17
20
21namespace bb {
22
23// clang-format off
24// disable the following style guides:
25// cppcoreguidelines-avoid-c-arrays : we make heavy use of c-style arrays here to prevent default-initialization of memory when constructing `field` objects.
26// The intention is for field to act like a primitive numeric type with the performance/complexity trade-offs expected from this.
27// NOLINTBEGIN(cppcoreguidelines-avoid-c-arrays)
28// clang-format on
34template <class T> constexpr field<T> field<T>::operator*(const field& other) const noexcept
35{
36 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= 0x4000000000000000ULL) ||
37 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
38 // >= 255-bits or <= 64-bits.
39 return montgomery_mul(other);
40 } else {
42 return montgomery_mul(other);
43 }
44 return asm_mul_with_coarse_reduction(*this, other);
45 }
46}
47
48template <class T> constexpr field<T>& field<T>::operator*=(const field& other) & noexcept
49{
50 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= 0x4000000000000000ULL) ||
51 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
52 // >= 255-bits or <= 64-bits.
53 *this = operator*(other);
54 } else {
56 *this = operator*(other);
57 } else {
58 asm_self_mul_with_coarse_reduction(*this, other); // asm_self_mul(*this, other);
59 }
60 }
61 return *this;
62}
63
69template <class T> constexpr field<T> field<T>::sqr() const noexcept
70{
71 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= 0x4000000000000000ULL) ||
72 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
73 return montgomery_square();
74 } else {
76 return montgomery_square();
77 }
78 return asm_sqr_with_coarse_reduction(*this); // asm_sqr(*this);
79 }
80}
81
82template <class T> constexpr void field<T>::self_sqr() & noexcept
83{
84 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= 0x4000000000000000ULL) ||
85 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
86 *this = montgomery_square();
87 } else {
89 *this = montgomery_square();
90 } else {
91 asm_self_sqr_with_coarse_reduction(*this);
92 }
93 }
94}
95
101template <class T> constexpr field<T> field<T>::operator+(const field& other) const noexcept
102{
103 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= 0x4000000000000000ULL) ||
104 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
105 return add(other);
106 } else {
108 return add(other);
109 }
110 return asm_add_with_coarse_reduction(*this, other); // asm_add_without_reduction(*this, other);
111 }
112}
113
114template <class T> constexpr field<T>& field<T>::operator+=(const field& other) & noexcept
115{
116 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= 0x4000000000000000ULL) ||
117 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
118 (*this) = operator+(other);
119 } else {
121 (*this) = operator+(other);
122 } else {
123 asm_self_add_with_coarse_reduction(*this, other); // asm_self_add(*this, other);
124 }
125 }
126 return *this;
127}
128
129template <class T> constexpr field<T> field<T>::operator++() noexcept
130{
131 return *this += 1;
132}
133
134// NOLINTNEXTLINE(cert-dcl21-cpp) circular linting errors. If const is added, linter suggests removing
135template <class T> constexpr field<T> field<T>::operator++(int) noexcept
136{
137 field<T> value_before_incrementing = *this;
138 *this += 1;
139 return value_before_incrementing;
140}
141
147template <class T> constexpr field<T> field<T>::operator-(const field& other) const noexcept
148{
149 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= 0x4000000000000000ULL) ||
150 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
151 return subtract_coarse(other); // modulus - *this;
152 } else {
154 return subtract_coarse(other); // subtract(other);
155 }
156 return asm_sub_with_coarse_reduction(*this, other); // asm_sub(*this, other);
157 }
158}
159
160template <class T> constexpr field<T> field<T>::operator-() const noexcept
161{
162 if constexpr ((T::modulus_3 >= 0x4000000000000000ULL) ||
163 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
164 constexpr field p{ modulus.data[0], modulus.data[1], modulus.data[2], modulus.data[3] };
165 return p - *this; // modulus - *this;
166 }
167
168 // TODO(@zac-williamson): there are 3 ways we can make this more efficient
169 // 1: we subtract `p` from `*this` instead of `2p`
170 // 2: instead of `p - *this`, we use an asm block that does `p - *this` without the assembly reduction step
171 // 3: we replace `(p - *this).reduce_once()` with an assembly block that is equivalent to `p - *this`,
172 // but we call `REDUCE_FIELD_ELEMENT` with `not_twice_modulus` instead of `twice_modulus`
173 // not sure which is faster and whether any of the above might break something!
174 //
175 // More context below:
176 // the operator-(a, b) method's asm implementation has a sneaky was to check underflow.
177 // if `a - b` underflows we need to add in `2p`. Instead of conditional branching which would cause pipeline
178 // flushes, we add `2p` into the result of `a - b`. If the result triggers the overflow flag, then we know we are
179 // correcting an *underflow* produced from computing `a - b`. Finally...we use the overflow flag to conditionally
180 // move data into registers such that we end up with either `a - b` or `2p + (a - b)` (this is branchless). OK! So
181 // what's the problem? Well we assume that every field element lies between 0 and 2p - 1. But we are computing `2p -
182 // *this`! If *this = 0 then we exceed this bound hence the need for the extra reduction step. HOWEVER, we also know
183 // that 2p - *this won't underflow so we could skip the underflow check present in the assembly code
184 constexpr field p{ twice_modulus.data[0], twice_modulus.data[1], twice_modulus.data[2], twice_modulus.data[3] };
185 return (p - *this).reduce_once(); // modulus - *this;
186}
187
188template <class T> constexpr field<T>& field<T>::operator-=(const field& other) & noexcept
189{
190 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= 0x4000000000000000ULL) ||
191 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
192 *this = subtract_coarse(other); // subtract(other);
193 } else {
195 *this = subtract_coarse(other); // subtract(other);
196 } else {
197 asm_self_sub_with_coarse_reduction(*this, other); // asm_self_sub(*this, other);
198 }
199 }
200 return *this;
201}
202
203template <class T> constexpr void field<T>::self_neg() & noexcept
204{
205 if constexpr ((T::modulus_3 >= 0x4000000000000000ULL) ||
206 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
207 constexpr field p{ modulus.data[0], modulus.data[1], modulus.data[2], modulus.data[3] };
208 *this = p - *this;
209 } else {
210 constexpr field p{ twice_modulus.data[0], twice_modulus.data[1], twice_modulus.data[2], twice_modulus.data[3] };
211 *this = (p - *this).reduce_once();
212 }
213}
214
215template <class T> constexpr void field<T>::self_conditional_negate(const uint64_t predicate) & noexcept
216{
217 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= 0x4000000000000000ULL) ||
218 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
219 *this = predicate ? -(*this) : *this; // NOLINT
220 } else {
222 *this = predicate ? -(*this) : *this; // NOLINT
223 } else {
224 asm_conditional_negate(*this, predicate);
225 }
226 }
227}
228
240template <class T> constexpr bool field<T>::operator>(const field& other) const noexcept
241{
242 const field left = reduce_once();
243 const field right = other.reduce_once();
244 const bool t0 = left.data[3] > right.data[3];
245 const bool t1 = (left.data[3] == right.data[3]) && (left.data[2] > right.data[2]);
246 const bool t2 =
247 (left.data[3] == right.data[3]) && (left.data[2] == right.data[2]) && (left.data[1] > right.data[1]);
248 const bool t3 = (left.data[3] == right.data[3]) && (left.data[2] == right.data[2]) &&
249 (left.data[1] == right.data[1]) && (left.data[0] > right.data[0]);
250 return (t0 || t1 || t2 || t3);
251}
252
264template <class T> constexpr bool field<T>::operator<(const field& other) const noexcept
265{
266 return (other > *this);
267}
268
269template <class T> constexpr bool field<T>::operator==(const field& other) const noexcept
270{
271 const field left = reduce_once();
272 const field right = other.reduce_once();
273 return (left.data[0] == right.data[0]) && (left.data[1] == right.data[1]) && (left.data[2] == right.data[2]) &&
274 (left.data[3] == right.data[3]);
275}
276
277template <class T> constexpr bool field<T>::operator!=(const field& other) const noexcept
278{
279 return (!operator==(other));
280}
281
282template <class T> constexpr field<T> field<T>::to_montgomery_form() const noexcept
283{
284 constexpr field r_squared =
285 field{ r_squared_uint.data[0], r_squared_uint.data[1], r_squared_uint.data[2], r_squared_uint.data[3] };
286
287 field result = *this;
288 // TODO(@zac-williamson): are these reductions needed?
289 // Rationale: We want to take any 256-bit input and be able to convert into montgomery form.
290 // A basic heuristic we use is that any input into the `*` operator must be between [0, 2p - 1]
291 // to prevent overflows in the asm algorithm.
292 // However... r_squared is already reduced so perhaps we can relax this requirement?
293 // (would be good to identify a failure case where not calling self_reduce triggers an error)
294 result.self_reduce_once();
295 result.self_reduce_once();
296 result.self_reduce_once();
297 return (result * r_squared).reduce_once();
298}
299
300template <class T> constexpr field<T> field<T>::from_montgomery_form() const noexcept
301{
302 constexpr field one_raw{ 1, 0, 0, 0 };
303 return operator*(one_raw).reduce_once();
304}
305
306template <class T> constexpr void field<T>::self_to_montgomery_form() & noexcept
307{
308 constexpr field r_squared =
309 field{ r_squared_uint.data[0], r_squared_uint.data[1], r_squared_uint.data[2], r_squared_uint.data[3] };
311 self_reduce_once();
312 self_reduce_once();
313 self_reduce_once();
314 *this *= r_squared;
315 self_reduce_once();
316}
317
318template <class T> constexpr void field<T>::self_from_montgomery_form() & noexcept
319{
320 constexpr field one_raw{ 1, 0, 0, 0 };
321 *this *= one_raw;
322 self_reduce_once();
324
325template <class T> constexpr field<T> field<T>::reduce_once() const noexcept
326{
327 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= 0x4000000000000000ULL) ||
328 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
329 return reduce();
330 } else {
332 return reduce();
334 return asm_reduce_once(*this);
335 }
338template <class T> constexpr void field<T>::self_reduce_once() & noexcept
340 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= 0x4000000000000000ULL) ||
341 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
342 *this = reduce();
343 } else {
345 *this = reduce();
346 } else {
347 asm_self_reduce_once(*this);
348 }
349 }
350}
351
352template <class T> constexpr field<T> field<T>::pow(const uint256_t& exponent) const noexcept
354 field accumulator{ data[0], data[1], data[2], data[3] };
355 field to_mul{ data[0], data[1], data[2], data[3] };
356 const uint64_t maximum_set_bit = exponent.get_msb();
357
358 for (int i = static_cast<int>(maximum_set_bit) - 1; i >= 0; --i) {
359 accumulator.self_sqr();
360 if (exponent.get_bit(static_cast<uint64_t>(i))) {
361 accumulator *= to_mul;
362 }
363 }
364 if (exponent == uint256_t(0)) {
365 accumulator = one();
366 } else if (*this == zero()) {
367 accumulator = zero();
368 }
369 return accumulator;
370}
372template <class T> constexpr field<T> field<T>::pow(const uint64_t exponent) const noexcept
373{
374 return pow({ exponent, 0, 0, 0 });
377template <class T> constexpr field<T> field<T>::invert() const noexcept
379 if (*this == zero()) {
380 bb::assert_failure("Trying to invert zero in the field");
381 }
382 return pow(modulus_minus_two);
383}
384
385// TODO(https://github.com/AztecProtocol/barretenberg/issues/1166)
386template <class T> void field<T>::batch_invert(field* coeffs, const size_t n) noexcept
387{
388 batch_invert(std::span{ coeffs, n });
389}
390
391template <class T> void field<T>::batch_invert(std::span<field> coeffs) noexcept
392{
393 batch_invert<decltype(coeffs)>(coeffs);
394}
395
396template <class T>
397template <typename C>
398 requires requires(C& c) {
399 { c.size() } -> std::convertible_to<size_t>;
400 { c[0] };
401 }
402void field<T>::batch_invert(C& coeffs) noexcept
403{
404 const size_t n = coeffs.size();
405
406 std::vector<field> temporaries;
407 std::vector<bool> skipped;
408 temporaries.reserve(n);
409 skipped.reserve(n);
410
411 field accumulator = one();
412 for (size_t i = 0; i < n; ++i) {
413 temporaries[i] = accumulator;
414 if (coeffs[i].is_zero()) {
415 skipped[i] = true;
416 } else {
417 skipped[i] = false;
418 accumulator *= coeffs[i];
419 }
420 }
421
422 // std::vector<field> temporaries;
423 // std::vector<bool> skipped;
424 // temporaries.reserve(n);
425 // skipped.reserve(n);
426
427 // field accumulator = one();
428 // for (size_t i = 0; i < n; ++i) {
429 // temporaries.emplace_back(accumulator);
430 // if (coeffs[i].is_zero()) {
431 // skipped.emplace_back(true);
432 // } else {
433 // skipped.emplace_back(false);
434 // accumulator *= coeffs[i];
435 // }
436 // }
437
438 accumulator = accumulator.invert();
439
440 field T0;
441 for (size_t i = n - 1; i < n; --i) {
442 if (!skipped[i]) {
443 T0 = accumulator * temporaries[i];
444 accumulator *= coeffs[i];
445 coeffs[i] = T0;
446 }
447 }
448}
449
458template <class T> constexpr field<T> field<T>::tonelli_shanks_sqrt() const noexcept
459{
460 // Tonelli-shanks algorithm begins by finding a field element Q and integer S,
461 // such that (p - 1) = Q.2^{s}
462 // We can determine s by counting the least significant set bit of `p - 1`
463 // We pick elements `r, g` such that g = r^Q and r is not a square.
464 // (the coset generators are all nonresidues and satisfy this condition)
465 //
466 // To find the square root of `u`, consider `v = u^(Q - 1 / 2)`
467 // There exists an integer `e` where uv^2 = g^e (see Theorem 3.1 in paper).
468 // If `u` is a square, `e` is even and (uvg^{−e/2})^2 = u^2v^2g^e = u^{Q+1}g^{-e} = u
469 //
470 // The goal of the algorithm is two fold:
471 // 1. find `e` given `u`
472 // 2. compute `sqrt(u) = uvg^{−e/2}`
473 constexpr uint256_t Q = (modulus - 1) >> static_cast<uint64_t>(primitive_root_log_size());
474 constexpr uint256_t Q_minus_one_over_two = (Q - 1) >> 1;
475 field v = pow(Q_minus_one_over_two);
476 field uv = operator*(v); // uv = u^{(Q + 1) / 2}
477 // uvv = g^e for some unknown e. Goal is to find e.
478 field uvv = uv * v; // uvv = u^{(Q - 1) / 2 + (Q + 1) / 2} = u^{Q}
479
480 // check if t is a square with euler's criterion
481 // if not, we don't have a quadratic residue and a has no square root!
482 field check = uvv;
483 for (size_t i = 0; i < primitive_root_log_size() - 1; ++i) {
484 check.self_sqr();
485 }
486 if (check != 1) {
487 return 0;
488 }
489
490 constexpr field g = coset_generator<0>().pow(Q);
491 constexpr field g_inv = coset_generator<0>().pow(modulus - 1 - Q);
492 constexpr size_t root_bits = primitive_root_log_size();
493 constexpr size_t table_bits = 6;
494 constexpr size_t num_tables = root_bits / table_bits + (root_bits % table_bits != 0 ? 1 : 0);
495 constexpr size_t num_offset_tables = num_tables - 1;
496 constexpr size_t table_size = static_cast<size_t>(1UL) << table_bits;
497
498 using GTable = std::array<field, table_size>;
499 constexpr auto get_g_table = [&](const field& h) {
500 GTable result;
501 result[0] = 1;
502 for (size_t i = 1; i < table_size; ++i) {
503 result[i] = result[i - 1] * h;
504 }
505 return result;
506 };
507 constexpr std::array<GTable, num_tables> g_tables = [&]() {
508 field working_base = g_inv;
510 for (size_t i = 0; i < num_tables; ++i) {
511 result[i] = get_g_table(working_base);
512 for (size_t j = 0; j < table_bits; ++j) {
513 working_base.self_sqr();
514 }
515 }
516 return result;
517 }();
518 constexpr std::array<GTable, num_offset_tables> offset_g_tables = [&]() {
519 field working_base = g_inv;
520 for (size_t i = 0; i < root_bits % table_bits; ++i) {
521 working_base.self_sqr();
522 }
524 for (size_t i = 0; i < num_offset_tables; ++i) {
525 result[i] = get_g_table(working_base);
526 for (size_t j = 0; j < table_bits; ++j) {
527 working_base.self_sqr();
528 }
529 }
530 return result;
531 }();
532
533 constexpr GTable root_table_a = get_g_table(g.pow(1UL << ((num_tables - 1) * table_bits)));
534 constexpr GTable root_table_b = get_g_table(g.pow(1UL << (root_bits - table_bits)));
535 // compute uvv^{2^table_bits}, uvv^{2^{table_bits*2}}, ..., uvv^{2^{table_bits*num_tables}}
537 field base = uvv;
538 for (size_t i = 0; i < num_tables - 1; ++i) {
539 uvv_powers[i] = base;
540 for (size_t j = 0; j < table_bits; ++j) {
541 base.self_sqr();
542 }
543 }
544 uvv_powers[num_tables - 1] = base;
546 for (size_t i = 0; i < num_tables; ++i) {
547 size_t table_index = num_tables - 1 - i;
548 field target = uvv_powers[table_index];
549 for (size_t j = 0; j < i; ++j) {
550 size_t e_idx = num_tables - 1 - (i - 1) + j;
551 size_t g_idx = num_tables - 2 - j;
552
553 field g_lookup;
554 if (j != i - 1) {
555 g_lookup = offset_g_tables[g_idx - 1][e_slices[e_idx]]; // e1
556 } else {
557 g_lookup = g_tables[g_idx][e_slices[e_idx]];
558 }
559 target *= g_lookup;
560 }
561 size_t count = 0;
562
563 if (i == 0) {
564 for (auto& x : root_table_a) {
565 if (x == target) {
566 break;
567 }
568 count += 1;
569 }
570 } else {
571 for (auto& x : root_table_b) {
572 if (x == target) {
573 break;
574 }
575 count += 1;
576 }
577 }
579 BB_ASSERT(count != table_size);
580 e_slices[table_index] = count;
581 }
582
583 // We want to compute g^{-e/2} which requires computing `e/2` via our slice representation
584 for (size_t i = 0; i < num_tables; ++i) {
585 auto& e_slice = e_slices[num_tables - 1 - i];
586 // e_slices[num_tables - 1] is always even.
587 // From theorem 3.1 (https://cr.yp.to/papers/sqroot-20011123-retypeset20220327.pdf)
588 // if slice is odd, propagate the downshifted bit into previous slice value
589 if ((e_slice & 1UL) == 1UL) {
590 size_t borrow_value = (i == 1) ? 1UL << ((root_bits % table_bits) - 1) : (1UL << (table_bits - 1));
591 e_slices[num_tables - i] += borrow_value;
592 }
593 e_slice >>= 1;
594 }
595
596 field g_pow_minus_e_over_2 = 1;
597 for (size_t i = 0; i < num_tables; ++i) {
598 if (i == 0) {
599 g_pow_minus_e_over_2 *= g_tables[i][e_slices[num_tables - 1 - i]];
600 } else {
601 g_pow_minus_e_over_2 *= offset_g_tables[i - 1][e_slices[num_tables - 1 - i]];
602 }
603 }
604 return uv * g_pow_minus_e_over_2;
605}
606
607template <class T>
608constexpr std::pair<bool, field<T>> field<T>::sqrt() const noexcept
609 requires((T::modulus_0 & 0x3UL) == 0x3UL)
610{
611 constexpr uint256_t sqrt_exponent = (modulus + uint256_t(1)) >> 2;
612 field root = pow(sqrt_exponent);
613 if ((root * root) == (*this)) {
614 return std::pair<bool, field>(true, root);
615 }
616 return std::pair<bool, field>(false, field::zero());
617}
618
619template <class T>
620constexpr std::pair<bool, field<T>> field<T>::sqrt() const noexcept
621 requires((T::modulus_0 & 0x3UL) != 0x3UL)
622{
623 field root = tonelli_shanks_sqrt();
624 if ((root * root) == (*this)) {
625 return std::pair<bool, field>(true, root);
626 }
627 return std::pair<bool, field>(false, field::zero());
628}
629
630template <class T> constexpr field<T> field<T>::operator/(const field& other) const noexcept
631{
632 return operator*(other.invert());
633}
634
635template <class T> constexpr field<T>& field<T>::operator/=(const field& other) & noexcept
636{
637 *this = operator/(other);
638 return *this;
639}
640
641template <class T> constexpr void field<T>::self_set_msb() & noexcept
642{
643 data[3] = 0ULL | (1ULL << 63ULL);
644}
645
646template <class T> constexpr bool field<T>::is_msb_set() const noexcept
647{
648 return (data[3] >> 63ULL) == 1ULL;
649}
650
651template <class T> constexpr uint64_t field<T>::is_msb_set_word() const noexcept
652{
653 return (data[3] >> 63ULL);
654}
655
656template <class T> constexpr bool field<T>::is_zero() const noexcept
657{
658 return ((data[0] | data[1] | data[2] | data[3]) == 0) ||
659 (data[0] == T::modulus_0 && data[1] == T::modulus_1 && data[2] == T::modulus_2 && data[3] == T::modulus_3);
660}
661
662template <class T> constexpr field<T> field<T>::get_root_of_unity(size_t subgroup_size) noexcept
663{
664#if defined(__SIZEOF_INT128__) && !defined(__wasm__)
665 field r{ T::primitive_root_0, T::primitive_root_1, T::primitive_root_2, T::primitive_root_3 };
666#else
667 field r{ T::primitive_root_wasm_0, T::primitive_root_wasm_1, T::primitive_root_wasm_2, T::primitive_root_wasm_3 };
668#endif
669 for (size_t i = primitive_root_log_size(); i > subgroup_size; --i) {
670 r.self_sqr();
671 }
672 return r;
673}
674
676{
677 if (engine == nullptr) {
679 }
680 constexpr field pow_2_256 = field(uint256_t(1) << 128).sqr();
681 field lo;
682 field hi;
685 return lo + (pow_2_256 * hi);
686}
687
688template <class T> constexpr size_t field<T>::primitive_root_log_size() noexcept
689{
690 uint256_t target = modulus - 1;
691 size_t result = 0;
692 while (!target.get_bit(result)) {
693 ++result;
694 }
695 return result;
696}
697
698template <class T>
700{
701 constexpr size_t n = COSET_GENERATOR_SIZE;
702 constexpr uint64_t subgroup_size = 1 << 30;
703
704 std::array<field, COSET_GENERATOR_SIZE> result{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
705 if (n > 0) {
706 result[0] = (multiplicative_generator());
707 }
708 field work_variable = multiplicative_generator() + field(1);
709
710 size_t count = 1;
711 while (count < n) {
712 // work_variable contains a new field element, and we need to test that, for all previous vector
713 // elements, result[i] / work_variable is not a member of our subgroup
714 field work_inverse = work_variable.invert();
715 bool valid = true;
716 for (size_t j = 0; j < count; ++j) {
717 field subgroup_check = (work_inverse * result[j]).pow(subgroup_size);
718 if (subgroup_check == field(1)) {
719 valid = false;
720 break;
722 }
723 if (valid) {
724 result[count] = (work_variable);
725 ++count;
726 }
727 work_variable += field(1);
728 }
729 return result;
730}
731
732template <class T> constexpr field<T> field<T>::multiplicative_generator() noexcept
733{
734 field target(1);
735 uint256_t p_minus_one_over_two = (modulus - 1) >> 1;
736 bool found = false;
737 while (!found) {
738 target += field(1);
739 found = (target.pow(p_minus_one_over_two) == -field(1));
740 }
741 return target;
742}
743
744// This function is used to serialize a field. It matches the old serialization format by first
745// converting the field from Montgomery form, which is a special representation used for efficient
746// modular arithmetic.
747template <class Params> void field<Params>::msgpack_pack(auto& packer) const
748{
749 // The field is first converted from Montgomery form, similar to how the old format did it.
750 auto adjusted = from_montgomery_form();
751
752 // The data is then converted to big endian format using htonll, which stands for "host to network long
753 // long". This is necessary because the data will be written to a raw msgpack buffer, which requires big
754 // endian format.
755 uint64_t bin_data[4] = {
756 htonll(adjusted.data[3]), htonll(adjusted.data[2]), htonll(adjusted.data[1]), htonll(adjusted.data[0])
757 };
758
759 // The packer is then used to write the binary data to the buffer, just like in the old format.
760 packer.pack_bin(sizeof(bin_data));
761 packer.pack_bin_body((const char*)bin_data, sizeof(bin_data)); // NOLINT
762}
763
764// This function is used to deserialize a field. It also matches the old deserialization format by
765// reading the binary data as big endian uint64_t's, correcting them to the host endianness, and
766// then converting the field back to Montgomery form.
767template <class Params> void field<Params>::msgpack_unpack(auto o)
768{
769 // The binary data is first extracted from the msgpack object.
770 std::array<uint8_t, sizeof(data)> raw_data = o;
771
772 // The binary data is then read as big endian uint64_t's. This is done by casting the raw data to uint64_t*
773 // and then using ntohll ("network to host long long") to correct the endianness to the host's endianness.
774 uint64_t* cast_data = (uint64_t*)&raw_data[0]; // NOLINT
775 uint64_t reversed[] = { ntohll(cast_data[3]), ntohll(cast_data[2]), ntohll(cast_data[1]), ntohll(cast_data[0]) };
776
777 // The corrected data is then copied back into the field's data array.
778 for (int i = 0; i < 4; i++) {
779 data[i] = reversed[i];
780 }
781
782 // Finally, the field is converted back to Montgomery form, just like in the old format.
783 *this = to_montgomery_form();
784}
785
786} // namespace bb
787
788// clang-format off
789// NOLINTEND(cppcoreguidelines-avoid-c-arrays)
790// clang-format on
#define BB_ASSERT(expression,...)
Definition assert.hpp:67
virtual uint256_t get_random_uint256()=0
constexpr bool get_bit(uint64_t bit_index) const
const std::vector< MemoryValue > data
numeric::RNG & engine
#define BBERG_NO_ASM
RNG & get_randomness()
Definition engine.cpp:203
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
Univariate< Fr, domain_end > operator+(const Fr &ff, const Univariate< Fr, domain_end > &uv)
void assert_failure(std::string const &err)
Definition assert.cpp:11
Univariate< Fr, domain_end > operator*(const Fr &ff, const Univariate< Fr, domain_end > &uv)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
General class for prime fields see Prime field documentation["field documentation"] for general imple...
static constexpr field get_root_of_unity(size_t subgroup_size) noexcept
BB_INLINE constexpr field & operator+=(const field &other) &noexcept
BB_INLINE constexpr void self_reduce_once() &noexcept
BB_INLINE constexpr bool operator!=(const field &other) const noexcept
BB_INLINE constexpr field operator*(const field &other) const noexcept
BB_INLINE constexpr field operator+(const field &other) const noexcept
constexpr field tonelli_shanks_sqrt() const noexcept
Implements an optimized variant of Tonelli-Shanks via lookup tables. Algorithm taken from https://cr....
BB_INLINE constexpr field to_montgomery_form() const noexcept
BB_INLINE constexpr void self_conditional_negate(uint64_t predicate) &noexcept
BB_INLINE constexpr field pow(const uint256_t &exponent) const noexcept
static constexpr std::array< field, COSET_GENERATOR_SIZE > compute_coset_generators() noexcept
BB_INLINE constexpr field operator++() noexcept
constexpr field operator/(const field &other) const noexcept
BB_INLINE constexpr void self_sqr() &noexcept
constexpr field invert() const noexcept
BB_INLINE constexpr void self_neg() &noexcept
BB_INLINE constexpr bool operator==(const field &other) const noexcept
BB_INLINE constexpr bool is_msb_set() const noexcept
static field random_element(numeric::RNG *engine=nullptr) noexcept
BB_INLINE constexpr field sqr() const noexcept
BB_INLINE constexpr bool operator>(const field &other) const noexcept
Greater-than operator.
BB_INLINE constexpr field & operator-=(const field &other) &noexcept
void msgpack_pack(auto &packer) const
constexpr std::pair< bool, field > sqrt() const noexcept
Compute square root of the field element.
BB_INLINE constexpr void self_from_montgomery_form() &noexcept
static constexpr field multiplicative_generator() noexcept
BB_INLINE constexpr field & operator*=(const field &other) &noexcept
BB_INLINE constexpr bool is_zero() const noexcept
static void batch_invert(C &coeffs) noexcept
BB_INLINE constexpr void self_to_montgomery_form() &noexcept
BB_INLINE constexpr field from_montgomery_form() const noexcept
BB_INLINE constexpr field operator-() const noexcept
BB_INLINE constexpr bool operator<(const field &other) const noexcept
Less-than operator.
BB_INLINE constexpr void self_set_msb() &noexcept
void msgpack_unpack(auto o)
constexpr field & operator/=(const field &other) &noexcept
static constexpr size_t primitive_root_log_size() noexcept
BB_INLINE constexpr field reduce_once() const noexcept
static constexpr field zero()
BB_INLINE constexpr uint64_t is_msb_set_word() const noexcept