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