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}
352
353template <class T> constexpr field<T> field<T>::pow(const uint256_t& exponent) const noexcept
354{
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;
372
373template <class T> constexpr field<T> field<T>::pow(const uint64_t exponent) const noexcept
374{
375 return pow({ exponent, 0, 0, 0 });
376}
377
378template <class T> constexpr field<T> field<T>::invert() const noexcept
379{
380 if (*this == zero()) {
381 throw_or_abort("Trying to invert zero in the field");
382 }
383 return pow(modulus_minus_two);
384}
385
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
391// TODO(https://github.com/AztecProtocol/barretenberg/issues/1166)
392template <class T> void field<T>::batch_invert(std::span<field> coeffs) noexcept
393{
394 const size_t n = coeffs.size();
395
396 auto temporaries_ptr = std::static_pointer_cast<field[]>(get_mem_slab(n * sizeof(field)));
397 auto skipped_ptr = std::static_pointer_cast<bool[]>(get_mem_slab(n));
398 auto temporaries = temporaries_ptr.get();
399 auto* skipped = skipped_ptr.get();
400
401 field accumulator = one();
402 for (size_t i = 0; i < n; ++i) {
403 temporaries[i] = accumulator;
404 if (coeffs[i].is_zero()) {
405 skipped[i] = true;
406 } else {
407 skipped[i] = false;
408 accumulator *= coeffs[i];
409 }
410 }
411
412 // std::vector<field> temporaries;
413 // std::vector<bool> skipped;
414 // temporaries.reserve(n);
415 // skipped.reserve(n);
416
417 // field accumulator = one();
418 // for (size_t i = 0; i < n; ++i) {
419 // temporaries.emplace_back(accumulator);
420 // if (coeffs[i].is_zero()) {
421 // skipped.emplace_back(true);
422 // } else {
423 // skipped.emplace_back(false);
424 // accumulator *= coeffs[i];
425 // }
426 // }
427
428 accumulator = accumulator.invert();
429
430 field T0;
431 for (size_t i = n - 1; i < n; --i) {
432 if (!skipped[i]) {
433 T0 = accumulator * temporaries[i];
434 accumulator *= coeffs[i];
435 coeffs[i] = T0;
436 }
437 }
438}
439
448template <class T> constexpr field<T> field<T>::tonelli_shanks_sqrt() const noexcept
449{
450 // Tonelli-shanks algorithm begins by finding a field element Q and integer S,
451 // such that (p - 1) = Q.2^{s}
452 // We can determine s by counting the least significant set bit of `p - 1`
453 // We pick elements `r, g` such that g = r^Q and r is not a square.
454 // (the coset generators are all nonresidues and satisfy this condition)
455 //
456 // To find the square root of `u`, consider `v = u^(Q - 1 / 2)`
457 // There exists an integer `e` where uv^2 = g^e (see Theorem 3.1 in paper).
458 // If `u` is a square, `e` is even and (uvg^{−e/2})^2 = u^2v^2g^e = u^{Q+1}g^{-e} = u
459 //
460 // The goal of the algorithm is two fold:
461 // 1. find `e` given `u`
462 // 2. compute `sqrt(u) = uvg^{−e/2}`
463 constexpr uint256_t Q = (modulus - 1) >> static_cast<uint64_t>(primitive_root_log_size());
464 constexpr uint256_t Q_minus_one_over_two = (Q - 1) >> 1;
465 field v = pow(Q_minus_one_over_two);
466 field uv = operator*(v); // uv = u^{(Q + 1) / 2}
467 // uvv = g^e for some unknown e. Goal is to find e.
468 field uvv = uv * v; // uvv = u^{(Q - 1) / 2 + (Q + 1) / 2} = u^{Q}
469
470 // check if t is a square with euler's criterion
471 // if not, we don't have a quadratic residue and a has no square root!
472 field check = uvv;
473 for (size_t i = 0; i < primitive_root_log_size() - 1; ++i) {
474 check.self_sqr();
475 }
476 if (check != 1) {
477 return 0;
478 }
479
480 constexpr field g = coset_generator<0>().pow(Q);
481 constexpr field g_inv = coset_generator<0>().pow(modulus - 1 - Q);
482 constexpr size_t root_bits = primitive_root_log_size();
483 constexpr size_t table_bits = 6;
484 constexpr size_t num_tables = root_bits / table_bits + (root_bits % table_bits != 0 ? 1 : 0);
485 constexpr size_t num_offset_tables = num_tables - 1;
486 constexpr size_t table_size = static_cast<size_t>(1UL) << table_bits;
487
488 using GTable = std::array<field, table_size>;
489 constexpr auto get_g_table = [&](const field& h) {
490 GTable result;
491 result[0] = 1;
492 for (size_t i = 1; i < table_size; ++i) {
493 result[i] = result[i - 1] * h;
494 }
495 return result;
496 };
497 constexpr std::array<GTable, num_tables> g_tables = [&]() {
498 field working_base = g_inv;
500 for (size_t i = 0; i < num_tables; ++i) {
501 result[i] = get_g_table(working_base);
502 for (size_t j = 0; j < table_bits; ++j) {
503 working_base.self_sqr();
504 }
505 }
506 return result;
507 }();
508 constexpr std::array<GTable, num_offset_tables> offset_g_tables = [&]() {
509 field working_base = g_inv;
510 for (size_t i = 0; i < root_bits % table_bits; ++i) {
511 working_base.self_sqr();
512 }
514 for (size_t i = 0; i < num_offset_tables; ++i) {
515 result[i] = get_g_table(working_base);
516 for (size_t j = 0; j < table_bits; ++j) {
517 working_base.self_sqr();
518 }
519 }
520 return result;
521 }();
522
523 constexpr GTable root_table_a = get_g_table(g.pow(1UL << ((num_tables - 1) * table_bits)));
524 constexpr GTable root_table_b = get_g_table(g.pow(1UL << (root_bits - table_bits)));
525 // compute uvv^{2^table_bits}, uvv^{2^{table_bits*2}}, ..., uvv^{2^{table_bits*num_tables}}
527 field base = uvv;
528 for (size_t i = 0; i < num_tables - 1; ++i) {
529 uvv_powers[i] = base;
530 for (size_t j = 0; j < table_bits; ++j) {
531 base.self_sqr();
532 }
533 }
534 uvv_powers[num_tables - 1] = base;
536 for (size_t i = 0; i < num_tables; ++i) {
537 size_t table_index = num_tables - 1 - i;
538 field target = uvv_powers[table_index];
539 for (size_t j = 0; j < i; ++j) {
540 size_t e_idx = num_tables - 1 - (i - 1) + j;
541 size_t g_idx = num_tables - 2 - j;
542
543 field g_lookup;
544 if (j != i - 1) {
545 g_lookup = offset_g_tables[g_idx - 1][e_slices[e_idx]]; // e1
546 } else {
547 g_lookup = g_tables[g_idx][e_slices[e_idx]];
548 }
549 target *= g_lookup;
550 }
551 size_t count = 0;
552
553 if (i == 0) {
554 for (auto& x : root_table_a) {
555 if (x == target) {
556 break;
557 }
558 count += 1;
559 }
560 } else {
561 for (auto& x : root_table_b) {
562 if (x == target) {
563 break;
564 }
565 count += 1;
567 }
569 ASSERT_IN_CONSTEXPR(count != table_size);
570 e_slices[table_index] = count;
573 // We want to compute g^{-e/2} which requires computing `e/2` via our slice representation
574 for (size_t i = 0; i < num_tables; ++i) {
575 auto& e_slice = e_slices[num_tables - 1 - i];
576 // e_slices[num_tables - 1] is always even.
577 // From theorem 3.1 (https://cr.yp.to/papers/sqroot-20011123-retypeset20220327.pdf)
578 // if slice is odd, propagate the downshifted bit into previous slice value
579 if ((e_slice & 1UL) == 1UL) {
580 size_t borrow_value = (i == 1) ? 1UL << ((root_bits % table_bits) - 1) : (1UL << (table_bits - 1));
581 e_slices[num_tables - i] += borrow_value;
582 }
583 e_slice >>= 1;
584 }
585
586 field g_pow_minus_e_over_2 = 1;
587 for (size_t i = 0; i < num_tables; ++i) {
588 if (i == 0) {
589 g_pow_minus_e_over_2 *= g_tables[i][e_slices[num_tables - 1 - i]];
590 } else {
591 g_pow_minus_e_over_2 *= offset_g_tables[i - 1][e_slices[num_tables - 1 - i]];
592 }
593 }
594 return uv * g_pow_minus_e_over_2;
595}
596
597template <class T>
598constexpr std::pair<bool, field<T>> field<T>::sqrt() const noexcept
599 requires((T::modulus_0 & 0x3UL) == 0x3UL)
600{
601 constexpr uint256_t sqrt_exponent = (modulus + uint256_t(1)) >> 2;
602 field root = pow(sqrt_exponent);
603 if ((root * root) == (*this)) {
604 return std::pair<bool, field>(true, root);
605 }
606 return std::pair<bool, field>(false, field::zero());
607}
608
609template <class T>
610constexpr std::pair<bool, field<T>> field<T>::sqrt() const noexcept
611 requires((T::modulus_0 & 0x3UL) != 0x3UL)
612{
613 field root = tonelli_shanks_sqrt();
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> constexpr field<T> field<T>::operator/(const field& other) const noexcept
621{
622 return operator*(other.invert());
623}
624
625template <class T> constexpr field<T>& field<T>::operator/=(const field& other) & noexcept
626{
627 *this = operator/(other);
628 return *this;
629}
630
631template <class T> constexpr void field<T>::self_set_msb() & noexcept
632{
633 data[3] = 0ULL | (1ULL << 63ULL);
634}
635
636template <class T> constexpr bool field<T>::is_msb_set() const noexcept
637{
638 return (data[3] >> 63ULL) == 1ULL;
639}
640
641template <class T> constexpr uint64_t field<T>::is_msb_set_word() const noexcept
642{
643 return (data[3] >> 63ULL);
644}
645
646template <class T> constexpr bool field<T>::is_zero() const noexcept
647{
648 return ((data[0] | data[1] | data[2] | data[3]) == 0) ||
649 (data[0] == T::modulus_0 && data[1] == T::modulus_1 && data[2] == T::modulus_2 && data[3] == T::modulus_3);
650}
651
652template <class T> constexpr field<T> field<T>::get_root_of_unity(size_t subgroup_size) noexcept
653{
654#if defined(__SIZEOF_INT128__) && !defined(__wasm__)
655 field r{ T::primitive_root_0, T::primitive_root_1, T::primitive_root_2, T::primitive_root_3 };
656#else
657 field r{ T::primitive_root_wasm_0, T::primitive_root_wasm_1, T::primitive_root_wasm_2, T::primitive_root_wasm_3 };
658#endif
659 for (size_t i = primitive_root_log_size(); i > subgroup_size; --i) {
660 r.self_sqr();
661 }
662 return r;
663}
664
666{
667 if (engine == nullptr) {
669 }
670 constexpr field pow_2_256 = field(uint256_t(1) << 128).sqr();
671 field lo;
672 field hi;
675 return lo + (pow_2_256 * hi);
676}
677
678template <class T> constexpr size_t field<T>::primitive_root_log_size() noexcept
679{
680 uint256_t target = modulus - 1;
681 size_t result = 0;
682 while (!target.get_bit(result)) {
683 ++result;
684 }
685 return result;
686}
687
688template <class T>
690{
691 constexpr size_t n = COSET_GENERATOR_SIZE;
692 constexpr uint64_t subgroup_size = 1 << 30;
693
694 std::array<field, COSET_GENERATOR_SIZE> result{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
695 if (n > 0) {
696 result[0] = (multiplicative_generator());
697 }
698 field work_variable = multiplicative_generator() + field(1);
699
700 size_t count = 1;
701 while (count < n) {
702 // work_variable contains a new field element, and we need to test that, for all previous vector
703 // elements, result[i] / work_variable is not a member of our subgroup
704 field work_inverse = work_variable.invert();
705 bool valid = true;
706 for (size_t j = 0; j < count; ++j) {
707 field subgroup_check = (work_inverse * result[j]).pow(subgroup_size);
708 if (subgroup_check == field(1)) {
709 valid = false;
710 break;
711 }
713 if (valid) {
714 result[count] = (work_variable);
715 ++count;
716 }
717 work_variable += field(1);
718 }
719 return result;
720}
721
722template <class T> constexpr field<T> field<T>::multiplicative_generator() noexcept
723{
724 field target(1);
725 uint256_t p_minus_one_over_two = (modulus - 1) >> 1;
726 bool found = false;
727 while (!found) {
728 target += field(1);
729 found = (target.pow(p_minus_one_over_two) == -field(1));
730 }
731 return target;
732}
733
734// This function is used to serialize a field. It matches the old serialization format by first
735// converting the field from Montgomery form, which is a special representation used for efficient
736// modular arithmetic.
737template <class Params> void field<Params>::msgpack_pack(auto& packer) const
738{
739 // The field is first converted from Montgomery form, similar to how the old format did it.
740 auto adjusted = from_montgomery_form();
741
742 // The data is then converted to big endian format using htonll, which stands for "host to network long
743 // long". This is necessary because the data will be written to a raw msgpack buffer, which requires big
744 // endian format.
745 uint64_t bin_data[4] = {
746 htonll(adjusted.data[3]), htonll(adjusted.data[2]), htonll(adjusted.data[1]), htonll(adjusted.data[0])
747 };
748
749 // The packer is then used to write the binary data to the buffer, just like in the old format.
750 packer.pack_bin(sizeof(bin_data));
751 packer.pack_bin_body((const char*)bin_data, sizeof(bin_data)); // NOLINT
752}
753
754// This function is used to deserialize a field. It also matches the old deserialization format by
755// reading the binary data as big endian uint64_t's, correcting them to the host endianness, and
756// then converting the field back to Montgomery form.
757template <class Params> void field<Params>::msgpack_unpack(auto o)
758{
759 // The binary data is first extracted from the msgpack object.
760 std::array<uint8_t, sizeof(data)> raw_data = o;
761
762 // The binary data is then read as big endian uint64_t's. This is done by casting the raw data to uint64_t*
763 // and then using ntohll ("network to host long long") to correct the endianness to the host's endianness.
764 uint64_t* cast_data = (uint64_t*)&raw_data[0]; // NOLINT
765 uint64_t reversed[] = { ntohll(cast_data[3]), ntohll(cast_data[2]), ntohll(cast_data[1]), ntohll(cast_data[0]) };
766
767 // The corrected data is then copied back into the field's data array.
768 for (int i = 0; i < 4; i++) {
769 data[i] = reversed[i];
770 }
771
772 // Finally, the field is converted back to Montgomery form, just like in the old format.
773 *this = to_montgomery_form();
774}
775
776} // namespace bb
777
778// clang-format off
779// NOLINTEND(cppcoreguidelines-avoid-c-arrays)
780// clang-format on
#define ASSERT_IN_CONSTEXPR(expression,...)
Definition assert.hpp:40
virtual uint256_t get_random_uint256()=0
constexpr bool get_bit(uint64_t bit_index) const
const std::vector< FF > data
numeric::RNG & engine
#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)
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(std::span< field > 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
void throw_or_abort(std::string const &err)