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: Completed, auditors: [Raju], commit: }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
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 >= MODULUS_TOP_LIMB_LARGE_THRESHOLD) ||
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 field result = asm_mul_with_coarse_reduction(*this, other);
45 result.assert_coarse_form();
46 return result;
47 }
48}
49
50template <class T> constexpr field<T>& field<T>::operator*=(const field& other) & noexcept
51{
52 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= MODULUS_TOP_LIMB_LARGE_THRESHOLD) ||
53 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
54 // >= 255-bits or <= 64-bits.
55 *this = operator*(other);
56 } else {
58 *this = operator*(other);
59 } else {
60 asm_self_mul_with_coarse_reduction(*this, other);
61 assert_coarse_form();
62 }
63 }
64 return *this;
65}
66
72template <class T> constexpr field<T> field<T>::sqr() const noexcept
73{
74 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= MODULUS_TOP_LIMB_LARGE_THRESHOLD) ||
75 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
76 return montgomery_square();
77 } else {
79 return montgomery_square();
80 }
81 field result = asm_sqr_with_coarse_reduction(*this);
82 result.assert_coarse_form();
83 return result;
84 }
85}
86
87template <class T> constexpr void field<T>::self_sqr() & noexcept
88{
89 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= MODULUS_TOP_LIMB_LARGE_THRESHOLD) ||
90 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
91 *this = montgomery_square();
92 } else {
94 *this = montgomery_square();
95 } else {
96 asm_self_sqr_with_coarse_reduction(*this);
97 assert_coarse_form();
98 }
99 }
100}
101
107template <class T> constexpr field<T> field<T>::operator+(const field& other) const noexcept
108{
109 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= MODULUS_TOP_LIMB_LARGE_THRESHOLD) ||
110 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
111 return add(other);
112 } else {
114 return add(other);
115 }
116 field result = asm_add_with_coarse_reduction(*this, other);
117 result.assert_coarse_form();
118 return result;
119 }
120}
121
122template <class T> constexpr field<T>& field<T>::operator+=(const field& other) & noexcept
123{
124 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= MODULUS_TOP_LIMB_LARGE_THRESHOLD) ||
125 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
126 (*this) = operator+(other);
127 } else {
129 (*this) = operator+(other);
130 } else {
131 asm_self_add_with_coarse_reduction(*this, other);
132 assert_coarse_form();
133 }
134 }
135 return *this;
136}
137
138template <class T> constexpr field<T> field<T>::operator++() noexcept
139{
140 return *this += 1;
141}
142
143// NOLINTNEXTLINE(cert-dcl21-cpp) circular linting errors. If const is added, linter suggests removing
144template <class T> constexpr field<T> field<T>::operator++(int) noexcept
145{
146 field<T> value_before_incrementing = *this;
147 *this += 1;
148 return value_before_incrementing;
149}
150
156template <class T> constexpr field<T> field<T>::operator-(const field& other) const noexcept
157{
158 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= MODULUS_TOP_LIMB_LARGE_THRESHOLD) ||
159 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
160 return subtract(other);
161 } else {
163 return subtract(other);
164 }
165 field result = asm_sub_with_coarse_reduction(*this, other);
166 result.assert_coarse_form();
167 return result;
168 }
169}
170
171template <class T> constexpr field<T> field<T>::operator-() const noexcept
172{
173 // Negate via (p - x). For small moduli, subtract() handles the coarse-form correction:
174 // if x > p, it adds 2p, yielding 3p - x ∈ (p, 2p). Result is always in [0, 2p) strict.
175 // Using modulus (not twice_modulus) avoids producing exactly 2p when x = 0, which would
176 // violate the strict [0, 2p) coarse-form invariant inside subtract/asm_sub.
177 constexpr field p{ modulus.data[0], modulus.data[1], modulus.data[2], modulus.data[3] };
178 return p - *this;
179}
180
181template <class T> constexpr field<T>& field<T>::operator-=(const field& other) & noexcept
182{
183 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= MODULUS_TOP_LIMB_LARGE_THRESHOLD) ||
184 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
185 *this = subtract(other);
186 } else {
188 *this = subtract(other);
189 } else {
190 asm_self_sub_with_coarse_reduction(*this, other);
191 assert_coarse_form();
192 }
193 }
194 return *this;
195}
196
197template <class T> constexpr void field<T>::self_neg() & noexcept
198{
199 // See operator-() for explanation: use modulus (not twice_modulus) to avoid 2p intermediate.
200 constexpr field p{ modulus.data[0], modulus.data[1], modulus.data[2], modulus.data[3] };
201 *this = p - *this;
202}
203
204template <class T> constexpr void field<T>::self_conditional_negate(const uint64_t predicate) & noexcept
205{
206 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= MODULUS_TOP_LIMB_LARGE_THRESHOLD) ||
207 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
208 *this = predicate ? -(*this) : *this; // NOLINT
209 } else {
211 *this = predicate ? -(*this) : *this; // NOLINT
212 } else {
213 asm_conditional_negate(*this, predicate);
214 }
215 }
216}
217
229template <class T> constexpr bool field<T>::operator>(const field& other) const noexcept
230{
231 const field left = reduce_once();
232 const field right = other.reduce_once();
233 const bool t0 = left.data[3] > right.data[3];
234 const bool t1 = (left.data[3] == right.data[3]) && (left.data[2] > right.data[2]);
235 const bool t2 =
236 (left.data[3] == right.data[3]) && (left.data[2] == right.data[2]) && (left.data[1] > right.data[1]);
237 const bool t3 = (left.data[3] == right.data[3]) && (left.data[2] == right.data[2]) &&
238 (left.data[1] == right.data[1]) && (left.data[0] > right.data[0]);
239 return (t0 || t1 || t2 || t3);
240}
241
253template <class T> constexpr bool field<T>::operator<(const field& other) const noexcept
254{
255 return (other > *this);
256}
257
258template <class T> constexpr bool field<T>::operator==(const field& other) const noexcept
259{
260 // for both 254-bit fields and 256-bit fields, there are at most two representatives for each element of the prime
261 // field. This is because for 254-bit fields, the internal representation is in [0, 2*p) and for 256-bit feilds, the
262 // internal representation is an arbitrary `uint256_t`.
263 const field left = reduce_once();
264 const field right = other.reduce_once();
265 return (left.data[0] == right.data[0]) && (left.data[1] == right.data[1]) && (left.data[2] == right.data[2]) &&
266 (left.data[3] == right.data[3]);
267}
268
269template <class T> constexpr bool field<T>::operator!=(const field& other) const noexcept
270{
271 return (!operator==(other));
272}
273// to/from montgomery form methods.
274// We note that we do not need to perform extra reductions to run the from/to montgomery form algorithms for the
275// non-WASM builds. In the case of 254-bit fields, one way of saying this is: by the analysis in the field
276// documentation, as the constant we are multiplying by (aR) is less than p (r_squared, one_raw), for any 256-bit
277// number, the Montgomery multiplication algorithm will yield something in the range [0, 2p), i.e., in coarse form, as
278// desired. For 256-bit fields, that this is true again follows from the fact that the constant we are multiplying by is
279// less than p; hence the output of Montgomery multiplication of aR with a field element whose internal representation
280// is 256-bits will again be 256-bits. For more details, plesae see the field documentation.
281//
282// Note: For WASM builds, we do need the extra reduce_once() to ensure correctness with the 29-bit limb Montgomery
283// multiplication implementation.
284
285template <class T> constexpr field<T> field<T>::to_montgomery_form() const noexcept
286{
287 constexpr field r_squared =
288 field{ r_squared_uint.data[0], r_squared_uint.data[1], r_squared_uint.data[2], r_squared_uint.data[3] };
289 return *this * r_squared;
290}
291
292template <class T> constexpr field<T> field<T>::from_montgomery_form() const noexcept
293{
294 constexpr field one_raw{ 1, 0, 0, 0 };
295 return operator*(one_raw);
296}
297
298template <class T> constexpr void field<T>::self_to_montgomery_form() & noexcept
299{
300 constexpr field r_squared =
301 field{ r_squared_uint.data[0], r_squared_uint.data[1], r_squared_uint.data[2], r_squared_uint.data[3] };
302 *this *= r_squared;
305template <class T> constexpr void field<T>::self_from_montgomery_form() & noexcept
307 constexpr field one_raw{ 1, 0, 0, 0 };
308 *this *= one_raw;
310
311// Reduced versions - guarantee canonical form [0, p)
312template <class T> constexpr field<T> field<T>::to_montgomery_form_reduced() const noexcept
313{
314 return to_montgomery_form().reduce_once();
317template <class T> constexpr field<T> field<T>::from_montgomery_form_reduced() const noexcept
318{
319 return from_montgomery_form().reduce_once();
320}
321
322template <class T> constexpr void field<T>::self_to_montgomery_form_reduced() & noexcept
324 self_to_montgomery_form();
325 self_reduce_once();
326}
328template <class T> constexpr void field<T>::self_from_montgomery_form_reduced() & noexcept
329{
330 self_from_montgomery_form();
331 self_reduce_once();
332}
334template <class T> constexpr field<T> field<T>::reduce_once() const noexcept
335{
336 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= MODULUS_TOP_LIMB_LARGE_THRESHOLD) ||
337 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
338 return reduce();
339 } else {
341 return reduce();
343 return asm_reduce_once(*this);
344 }
345}
346
347template <class T> constexpr void field<T>::self_reduce_once() & noexcept
348{
349 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= MODULUS_TOP_LIMB_LARGE_THRESHOLD) ||
350 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
351 *this = reduce();
352 } else {
354 *this = reduce();
355 } else {
356 asm_self_reduce_once(*this);
358 }
359}
360
361template <class T> constexpr field<T> field<T>::pow(const uint256_t& exponent) const noexcept
362{
363 field accumulator{ data[0], data[1], data[2], data[3] };
364 field to_mul{ data[0], data[1], data[2], data[3] };
365 const uint64_t maximum_set_bit = exponent.get_msb();
367 for (int i = static_cast<int>(maximum_set_bit) - 1; i >= 0; --i) {
368 accumulator.self_sqr();
369 if (exponent.get_bit(static_cast<uint64_t>(i))) {
370 accumulator *= to_mul;
373 if (exponent == uint256_t(0)) {
374 accumulator = one();
375 } else if (*this == zero()) {
376 accumulator = zero();
377 }
378 return accumulator;
379}
381template <class T> constexpr field<T> field<T>::pow(const uint64_t exponent) const noexcept
382{
383 return pow({ exponent, 0, 0, 0 });
384}
385
386template <class T> constexpr field<T> field<T>::invert() const noexcept
387{
388 if (*this == zero()) {
389 bb::assert_failure("Trying to invert zero in the field");
390 }
391 return pow(modulus_minus_two);
392}
393
394template <class T> void field<T>::batch_invert(field* coeffs, const size_t n) noexcept
395{
396 batch_invert(std::span{ coeffs, n });
397}
398
399template <class T> void field<T>::batch_invert(std::span<field> coeffs) noexcept
400{
401 batch_invert<decltype(coeffs)>(coeffs);
402}
403
412template <class T>
413template <typename C>
414 requires requires(C& c) {
415 { c.size() } -> std::convertible_to<size_t>;
416 { c[0] };
417 }
418void field<T>::batch_invert(C& coeffs) noexcept
419{
420 const size_t n = coeffs.size();
421
422 std::vector<field> temporaries;
423 std::vector<bool> skipped;
424 temporaries.resize(n);
425 skipped.resize(n);
426
427 field accumulator = one();
428 for (size_t i = 0; i < n; ++i) {
429 temporaries[i] = accumulator;
430 if (coeffs[i].is_zero()) {
431 skipped[i] = true;
432 } else {
433 skipped[i] = false;
434 accumulator *= coeffs[i];
435 }
436 }
437 accumulator = accumulator.invert();
438
439 field T0;
440 for (size_t i = n - 1; i < n; --i) {
441 if (!skipped[i]) {
442 T0 = accumulator * temporaries[i];
443 accumulator *= coeffs[i];
444 coeffs[i] = T0;
445 }
446 }
447}
448
460template <class T> constexpr field<T> field<T>::tonelli_shanks_sqrt() const noexcept
461{
462 if (is_zero()) {
463 return field::zero();
464 }
465 if (*this == field::one()) {
466 return field::one();
467 }
468 // Tonelli-shanks algorithm begins by finding integers Q, S, with Q odd, such that (p - 1) = Q.2^{S}.
469 // We can determine s by counting the least significant set bit of `p - 1`. We pick elements `r, g` such that g =
470 // r^Q and r is not a square. (the coset generators are all nonresidues and satisfy this condition). This forces g
471 // to have order exactly 2^{S}.
472 //
473 // To find the square root of `u`, consider `v = u^((Q - 1)/2)`
474 // There exists an integer `e` where uv^2 = g^e (see Theorem 3.1 in paper -- the point is that uv^2 has 2-primary
475 // order). If `u` is a square, `e` is even and (uvg^{−e/2})^2 = u^2v^2g^e = u^{Q+1}g^{-e} = u
476 //
477 // The goal of the algorithm is two fold:
478 // 1. find `e` given `u`. (Discrete log is easy for 2-primary groups; we used an optimized chunking strategy.)
479 // 2. compute `sqrt(u) = uvg^{−e/2}`
480
481 // -----------------------------------------------------------------------------------------
482 // STEP 1: Compute the initial values v, uv, and uvv
483 // -----------------------------------------------------------------------------------------
484 // Q is the odd part of (p - 1), i.e., (p - 1) = Q * 2^S where S = primitive_root_log_size().
485 constexpr uint256_t Q = (modulus - 1) >> static_cast<uint64_t>(primitive_root_log_size());
486 constexpr uint256_t Q_minus_one_over_two = (Q - 1) >> 1;
487
488 field v = pow(Q_minus_one_over_two);
489 field uv = operator*(v);
490 // uvv = uv * v = u^{(Q+1)/2} * u^{(Q-1)/2} = u^Q
491 // By Theorem 3.1, uvv lies in the 2-primary subgroup generated by g, so uvv = g^e for some integer e.
492 field uvv = uv * v;
493
494 // -----------------------------------------------------------------------------------------
495 // STEP 2: Check if u is a quadratic residue
496 // -----------------------------------------------------------------------------------------
497 // u is a quadratic residue iff u^{(p-1)/2} = 1.
498 // Since uv^2 = u^Q and (p-1)/2 = Q * 2^{S-1}, we have u^{(p-1)/2} = (uv^2)^{2^{S-1}}.
499 // So we square uv^2 exactly (S-1) times and check if the result is 1.
500 field check = uvv;
501 for (size_t i = 0; i < primitive_root_log_size() - 1; ++i) {
502 check.self_sqr();
503 }
504 if (check != field::one()) {
505 // u is not a quadratic residue; return 0 to indicate no square root exists.
506 return field::zero();
507 }
508
509 // -----------------------------------------------------------------------------------------
510 // STEP 3: Set up precomputed lookup tables for the discrete log computation
511 // -----------------------------------------------------------------------------------------
512 // g = r^Q where r is a quadratic non-residue (coset_generator).
513 // Since r has order (p-1) and Q is the odd part, g has order exactly 2^S.
514 constexpr field g = coset_generator().pow(Q);
515
516 // g_inv = g^{-1} = r^{-Q} = r^{p-1-Q}
517 constexpr field g_inv = coset_generator().pow(modulus - 1 - Q);
518
519 // S = primitive_root_log_size() is the 2-adic valuation of (p-1), i.e., the largest power of 2 dividing (p-1).
520 constexpr size_t root_bits = primitive_root_log_size();
521
522 // table_bits (called 'w' in Bernstein's paper) determines the chunk size for the discrete log.
523 // We process the exponent e in chunks of table_bits bits at a time.
524 // Using 6 bits means tables of size 64, balancing memory usage vs. number of iterations.
525 constexpr size_t table_bits = 6;
526
527 // num_tables = ceil(S / table_bits)
528 // WARNING: this will have to be slightly changed if root_bits is exactly divisible by table_bits.
529 constexpr size_t num_tables = root_bits / table_bits + (root_bits % table_bits != 0 ? 1 : 0);
530 constexpr size_t num_offset_tables = num_tables - 1;
531
532 // table_size = 2^table_bits = 64 entries per table.
533 constexpr size_t table_size = static_cast<size_t>(1UL) << table_bits;
535 using GTable = std::array<field, table_size>;
536
537 // get_g_table(h) returns [h^0, h^1, h^2, ..., h^{table_size-1}].
538 // This allows O(1) lookup of h^k for any k in [0, table_size).
539 constexpr auto get_g_table = [&](const field& h) {
540 GTable result;
541 result[0] = 1;
542 for (size_t i = 1; i < table_size; ++i) {
543 result[i] = result[i - 1] * h;
544 }
545 return result;
546 };
547
548 // g_tables[i] contains powers of g_inv^{2^{table_bits * i}}.
549 // This allows us to compute g_inv^{e} efficiently by decomposing e into table_bits-sized chunks.
550 // g_tables[i][k] = g_inv^{k * 2^{table_bits * i}}
551 constexpr std::array<GTable, num_tables> g_tables = [&]() {
552 field working_base = g_inv;
554 for (size_t i = 0; i < num_tables; ++i) {
555 result[i] = get_g_table(working_base);
556 // Square table_bits times to get g_inv^{2^{table_bits * (i+1)}}
557 for (size_t j = 0; j < table_bits; ++j) {
558 working_base.self_sqr();
559 }
560 }
561 return result;
562 }();
563
564 // offset_g_tables handle the case where root_bits is not a multiple of table_bits.
565 // The first chunk may have fewer than table_bits bits, so we need offset tables
566 // that start from g_inv^{2^{root_bits % table_bits}} instead of g_inv.
567 constexpr std::array<GTable, num_offset_tables> offset_g_tables = [&]() {
568 field working_base = g_inv;
569 // Skip ahead by (root_bits % table_bits) squarings to align with the chunk boundaries.
570 for (size_t i = 0; i < root_bits % table_bits; ++i) {
571 working_base.self_sqr();
572 }
574 for (size_t i = 0; i < num_offset_tables; ++i) {
575 result[i] = get_g_table(working_base);
576 for (size_t j = 0; j < table_bits; ++j) {
577 working_base.self_sqr();
578 }
579 }
580 return result;
581 }();
582
583 // root_table_a and root_table_b are used to find the discrete log in each chunk.
584 // They contain powers of g (not g_inv) so we can match against uvv raised to appropriate powers.
585 // root_table_a: powers of g^{2^{(num_tables-1) * table_bits}} - used for the first (most significant) chunk.
586 constexpr GTable root_table_a = get_g_table(g.pow(1UL << ((num_tables - 1) * table_bits)));
587 // root_table_b: powers of g^{2^{root_bits - table_bits}} - used for subsequent chunks.
588 constexpr GTable root_table_b = get_g_table(g.pow(1UL << (root_bits - table_bits)));
589
590 // -----------------------------------------------------------------------------------------
591 // STEP 4: Compute powers of uvv for the chunked discrete log
592 // -----------------------------------------------------------------------------------------
593 // uvv_powers[i] = (uv^2)^{2^{table_bits * i}}
594 // These are the values we'll use to extract each chunk of the exponent e.
596 field base = uvv;
597 for (size_t i = 0; i < num_tables - 1; ++i) {
598 uvv_powers[i] = base;
599 for (size_t j = 0; j < table_bits; ++j) {
600 base.self_sqr();
601 }
602 }
603 uvv_powers[num_tables - 1] = base;
604
605 // -----------------------------------------------------------------------------------------
606 // STEP 5: Extract the chunks of e
607 // -----------------------------------------------------------------------------------------
608 // We find e such that uv^2 = g^e by determining e chunk by chunk, from most significant to least.
609 // e_slices[i] will hold the i-th chunk of e (each chunk is table_bits bits).
611 for (size_t i = 0; i < num_tables; ++i) {
612 // Process chunks from most significant (table_index = num_tables - 1) to least significant.
613 size_t table_index = num_tables - 1 - i;
614
615 // Start with (uv^2)^{2^{table_bits * table_index}}.
616 field target = uvv_powers[table_index];
617
618 // Correct target using previously discovered chunks.
619 // This removes the contribution of higher-order chunks so we can isolate this chunk.
620 for (size_t j = 0; j < i; ++j) {
621 size_t e_idx = num_tables - 1 - (i - 1) + j;
622 size_t g_idx = num_tables - 2 - j;
623
624 field g_lookup;
625 if (j != i - 1) {
626 g_lookup = offset_g_tables[g_idx - 1][e_slices[e_idx]];
627 } else {
628 g_lookup = g_tables[g_idx][e_slices[e_idx]];
629 }
630 target *= g_lookup;
631 }
632
633 // Search for target in the appropriate root table.
634 // target should equal g^{e_slice * 2^{...}} for some e_slice in [0, table_size).
635 size_t count = 0;
636
637 if (i == 0) {
638 // First iteration: use root_table_a for the most significant chunk.
639 for (auto& x : root_table_a) {
640 if (x == target) {
641 break;
643 count += 1;
644 }
645 } else {
646 // Subsequent iterations: use root_table_b.
647 for (auto& x : root_table_b) {
648 if (x == target) {
649 break;
650 }
651 count += 1;
652 }
653 }
654
655 if (count == table_size) {
656 // This should never happen if u is a valid quadratic residue.
657 bb::assert_failure("Tonelli-Shanks: count == table_size");
658 }
659 e_slices[table_index] = count;
660 }
661
662 // -----------------------------------------------------------------------------------------
663 // STEP 6: Compute e/2 from the slice representation
664 // -----------------------------------------------------------------------------------------
665 // We need g^{-e/2}, so we must divide e by 2.
666 // Since e is even (guaranteed by Theorem 3.1 for quadratic residues), this is exact.
667 // We perform the division on the slice representation by right-shifting each slice
668 // and propagating any "borrow" (the shifted-out bit) to the next slice.
669 for (size_t i = 0; i < num_tables; ++i) {
670 auto& e_slice = e_slices[num_tables - 1 - i];
671 // e_slices[num_tables - 1] (the most significant slice) is always even by Theorem 3.1.
672 // If a slice is odd, the bit that gets shifted out must be added to the previous slice
673 // (which represents higher powers of 2).
674 if ((e_slice & 1UL) == 1UL) {
675 // borrow_value is 2^{table_bits - 1} normally, but for the boundary between
676 // the first chunk (which may be smaller) and second chunk, we use the remainder size.
677 size_t borrow_value = (i == 1) ? 1UL << ((root_bits % table_bits) - 1) : (1UL << (table_bits - 1));
678 e_slices[num_tables - i] += borrow_value;
679 }
680 e_slice >>= 1;
681 }
682
683 // -----------------------------------------------------------------------------------------
684 // STEP 7: Compute g^{-e/2} from the slices and return the final square root
685 // -----------------------------------------------------------------------------------------
686 // g^{-e/2} = product of g_inv^{slice[i] * 2^{table_bits * i}} for all chunks i.
687 // We look up each term in the appropriate precomputed table.
688 field g_pow_minus_e_over_2 = 1;
689 for (size_t i = 0; i < num_tables; ++i) {
690 if (i == 0) {
691 g_pow_minus_e_over_2 *= g_tables[i][e_slices[num_tables - 1 - i]];
692 } else {
693 g_pow_minus_e_over_2 *= offset_g_tables[i - 1][e_slices[num_tables - 1 - i]];
694 }
695 }
696 // Final result: sqrt(u) = uv * g^{-e/2} = u^{(Q+1)/2} * g^{-e/2}
697 auto result = uv * g_pow_minus_e_over_2;
698 if (result * result != *this) {
699 bb::assert_failure("Tonelli-Shanks sqrt verification failed");
700 }
701 return result;
702}
703
704template <class T>
705constexpr std::pair<bool, field<T>> field<T>::sqrt() const noexcept
706 requires((T::modulus_0 & 0x3UL) == 0x3UL)
707{
708 constexpr uint256_t sqrt_exponent = (modulus + uint256_t(1)) >> 2;
709 field root = pow(sqrt_exponent);
710 if ((root * root) == (*this)) {
711 return std::pair<bool, field>(true, root);
712 }
713 return std::pair<bool, field>(false, field::zero());
714}
715
716template <class T>
717constexpr std::pair<bool, field<T>> field<T>::sqrt() const noexcept
718 requires((T::modulus_0 & 0x3UL) != 0x3UL)
719{
720 field root = tonelli_shanks_sqrt();
721 if ((root * root) == (*this)) {
722 return std::pair<bool, field>(true, root);
723 }
724 return std::pair<bool, field>(false, field::zero());
725}
726
727template <class T> constexpr field<T> field<T>::operator/(const field& other) const noexcept
728{
729 return operator*(other.invert());
730}
731
732template <class T> constexpr field<T>& field<T>::operator/=(const field& other) & noexcept
733{
734 *this = operator/(other);
735 return *this;
736}
737
738template <class T> constexpr void field<T>::self_set_msb() & noexcept
739{
740 data[3] = 0ULL | (1ULL << 63ULL);
741}
742
743template <class T> constexpr bool field<T>::is_msb_set() const noexcept
744{
745 return (data[3] >> 63ULL) == 1ULL;
746}
747
748template <class T> constexpr uint64_t field<T>::is_msb_set_word() const noexcept
749{
750 return (data[3] >> 63ULL);
751}
752
753template <class T> constexpr bool field<T>::is_zero() const noexcept
754{
755 // Use bitwise OR (not || or && operator) so neither chain short-circuits: the running time must not depend on
756 // whether the value is zero, on which limb of the modulus first matches/diverges, or on which form
757 // (raw 0 vs the modulus) is being tested.
758 const uint64_t raw_zero = data[0] | data[1] | data[2] | data[3];
759 const uint64_t mod_zero =
760 (data[0] ^ T::modulus_0) | (data[1] ^ T::modulus_1) | (data[2] ^ T::modulus_2) | (data[3] ^ T::modulus_3);
761 return static_cast<bool>(static_cast<uint64_t>(raw_zero == 0) | static_cast<uint64_t>(mod_zero == 0));
762}
763
764template <class T> constexpr field<T> field<T>::get_root_of_unity(size_t subgroup_size) noexcept
765{
766#if defined(__SIZEOF_INT128__) && !defined(__wasm__)
767 field r{ T::primitive_root_0, T::primitive_root_1, T::primitive_root_2, T::primitive_root_3 };
768#else
769 field r{ T::primitive_root_wasm_0, T::primitive_root_wasm_1, T::primitive_root_wasm_2, T::primitive_root_wasm_3 };
770#endif
771 for (size_t i = primitive_root_log_size(); i > subgroup_size; --i) {
772 r.self_sqr();
773 }
774 return r;
775}
776
778{
779 if (engine == nullptr) {
781 }
782 constexpr field pow_2_256 = field(uint256_t(1) << 128).sqr();
783 field lo;
784 field hi;
787 return lo + (pow_2_256 * hi);
788}
789
790template <class T> constexpr size_t field<T>::primitive_root_log_size() noexcept
791{
792 uint256_t target = modulus - 1;
793 size_t result = 0;
794 while (!target.get_bit(result)) {
795 ++result;
796 }
797 return result;
798}
799
800// This function is used to serialize a field. It matches the old serialization format by first
801// converting the field from Montgomery form, which is a special representation used for efficient
802// modular arithmetic.
803template <class Params> void field<Params>::msgpack_pack(auto& packer) const
804{
805 // The field is first converted from Montgomery form to canonical [0, p) representation.
806 auto adjusted = from_montgomery_form_reduced();
807
808 // The data is then converted to big endian format using htonll, which stands for "host to network long
809 // long". This is necessary because the data will be written to a raw msgpack buffer, which requires big
810 // endian format.
811 uint64_t bin_data[4] = {
812 htonll(adjusted.data[3]), htonll(adjusted.data[2]), htonll(adjusted.data[1]), htonll(adjusted.data[0])
813 };
814
815 // The packer is then used to write the binary data to the buffer, just like in the old format.
816 packer.pack_bin(sizeof(bin_data));
817 packer.pack_bin_body((const char*)bin_data, sizeof(bin_data)); // NOLINT
818}
819
820// This function is used to deserialize a field. It also matches the old deserialization format by
821// reading the binary data as big endian uint64_t's, correcting them to the host endianness, and
822// then converting the field back to Montgomery form.
823template <class Params> void field<Params>::msgpack_unpack(auto o)
824{
825 // The binary data is first extracted from the msgpack object.
826 std::array<uint8_t, sizeof(data)> raw_data = o;
827
828 // The binary data is then read as big endian uint64_t's. This is done by casting the raw data to uint64_t*
829 // and then using ntohll ("network to host long long") to correct the endianness to the host's endianness.
830 uint64_t* cast_data = (uint64_t*)&raw_data[0]; // NOLINT
831 uint64_t reversed[] = { ntohll(cast_data[3]), ntohll(cast_data[2]), ntohll(cast_data[1]), ntohll(cast_data[0]) };
832
833 // The corrected data is then copied back into the field's data array.
834 for (int i = 0; i < 4; i++) {
835 data[i] = reversed[i];
836 }
837
838 // Reject non-canonical field encodings (values >= modulus) to ensure strict parsing.
839 if (uint256_t{ data[0], data[1], data[2], data[3] } >= modulus) {
840 throw_or_abort("msgpack field deserialization: non-canonical encoding (value >= modulus)");
841 }
842
843 // Finally, the field is converted back to Montgomery form, just like in the old format.
844 *this = to_montgomery_form();
845}
846
847} // namespace bb
848
849// clang-format off
850// NOLINTEND(cppcoreguidelines-avoid-c-arrays)
851// clang-format on
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:258
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 void g(state_array &state, size_t a, size_t b, size_t c, size_t d, uint32_t x, uint32_t y)
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...
void assert_coarse_form() const noexcept
BB_INLINE constexpr field from_montgomery_form_reduced() const noexcept
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_to_montgomery_form_reduced() &noexcept
static constexpr field one()
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 void self_from_montgomery_form_reduced() &noexcept
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
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 to_montgomery_form_reduced() const noexcept
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
BB_INLINE constexpr field & operator*=(const field &other) &noexcept
BB_INLINE constexpr bool is_zero() const noexcept
static void batch_invert(C &coeffs) noexcept
Batch invert a collection of field elements using Montgomery's trick.
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)