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