barretenberg
Loading...
Searching...
No Matches
field_impl.hpp
1#pragma once
2#include "barretenberg/common/slab_allocator.hpp"
3#include "barretenberg/common/throw_or_abort.hpp"
4#include "barretenberg/numeric/bitop/get_msb.hpp"
5#include "barretenberg/numeric/random/engine.hpp"
6#include <memory>
7#include <span>
8#include <type_traits>
9#include <vector>
10
11#include "./field_declarations.hpp"
12
13namespace barretenberg {
14
15// clang-format off
16// disable the following style guides:
17// cppcoreguidelines-avoid-c-arrays : we make heavy use of c-style arrays here to prevent default-initialization of memory when constructing `field` objects.
18// The intention is for field to act like a primitive numeric type with the performance/complexity trade-offs expected from this.
19// NOLINTBEGIN(cppcoreguidelines-avoid-c-arrays)
20// clang-format on
26template <class T> constexpr field<T> field<T>::operator*(const field& other) const noexcept
27{
28 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= 0x4000000000000000ULL) ||
29 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
30 // >= 255-bits or <= 64-bits.
31 return montgomery_mul(other);
32 } else {
33 if (std::is_constant_evaluated()) {
34 return montgomery_mul(other);
35 }
36 return asm_mul_with_coarse_reduction(*this, other);
37 }
38}
39
40template <class T> constexpr field<T>& field<T>::operator*=(const field& other) noexcept
41{
42 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= 0x4000000000000000ULL) ||
43 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
44 // >= 255-bits or <= 64-bits.
45 *this = operator*(other);
46 } else {
47 if (std::is_constant_evaluated()) {
48 *this = operator*(other);
49 } else {
50 asm_self_mul_with_coarse_reduction(*this, other); // asm_self_mul(*this, other);
51 }
52 }
53 return *this;
54}
55
61template <class T> constexpr field<T> field<T>::sqr() const noexcept
62{
63 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= 0x4000000000000000ULL) ||
64 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
65 return montgomery_square();
66 } else {
67 if (std::is_constant_evaluated()) {
68 return montgomery_square();
69 }
70 return asm_sqr_with_coarse_reduction(*this); // asm_sqr(*this);
71 }
72}
73
74template <class T> constexpr void field<T>::self_sqr() noexcept
75{
76 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= 0x4000000000000000ULL) ||
77 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
78 *this = montgomery_square();
79 } else {
80 if (std::is_constant_evaluated()) {
81 *this = montgomery_square();
82 } else {
83 asm_self_sqr_with_coarse_reduction(*this);
84 }
85 }
86}
87
93template <class T> constexpr field<T> field<T>::operator+(const field& other) const noexcept
94{
95 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= 0x4000000000000000ULL) ||
96 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
97 return add(other);
98 } else {
99 if (std::is_constant_evaluated()) {
100 return add(other);
101 }
102 return asm_add_with_coarse_reduction(*this, other); // asm_add_without_reduction(*this, other);
103 }
104}
105
106template <class T> constexpr field<T>& field<T>::operator+=(const field& other) noexcept
107{
108 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= 0x4000000000000000ULL) ||
109 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
110 (*this) = operator+(other);
111 } else {
112 if (std::is_constant_evaluated()) {
113 (*this) = operator+(other);
114 } else {
115 asm_self_add_with_coarse_reduction(*this, other); // asm_self_add(*this, other);
116 }
117 }
118 return *this;
119}
120
121template <class T> constexpr field<T> field<T>::operator++() noexcept
122{
123 return *this += 1;
124}
125
126// NOLINTNEXTLINE(cert-dcl21-cpp) circular linting errors. If const is added, linter suggests removing
127template <class T> constexpr field<T> field<T>::operator++(int) noexcept
128{
129 field<T> value_before_incrementing = *this;
130 *this += 1;
131 return value_before_incrementing;
132}
133
139template <class T> constexpr field<T> field<T>::operator-(const field& other) const noexcept
140{
141 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= 0x4000000000000000ULL) ||
142 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
143 return subtract_coarse(other); // modulus - *this;
144 } else {
145 if (std::is_constant_evaluated()) {
146 return subtract_coarse(other); // subtract(other);
147 }
148 return asm_sub_with_coarse_reduction(*this, other); // asm_sub(*this, other);
149 }
150}
151
152template <class T> constexpr field<T> field<T>::operator-() const noexcept
153{
154 if constexpr ((T::modulus_3 >= 0x4000000000000000ULL) ||
155 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
156 constexpr field p{ modulus.data[0], modulus.data[1], modulus.data[2], modulus.data[3] };
157 return p - *this; // modulus - *this;
158 }
159
160 // TODO(@zac-williamson): there are 3 ways we can make this more efficient
161 // 1: we subtract `p` from `*this` instead of `2p`
162 // 2: instead of `p - *this`, we use an asm block that does `p - *this` without the assembly reduction step
163 // 3: we replace `(p - *this).reduce_once()` with an assembly block that is equivalent to `p - *this`,
164 // but we call `REDUCE_FIELD_ELEMENT` with `not_twice_modulus` instead of `twice_modulus`
165 // not sure which is faster and whether any of the above might break something!
166 //
167 // More context below:
168 // the operator-(a, b) method's asm implementation has a sneaky was to check underflow.
169 // if `a - b` underflows we need to add in `2p`. Instead of conditional branching which would cause pipeline
170 // flushes, we add `2p` into the result of `a - b`. If the result triggers the overflow flag, then we know we are
171 // correcting an *underflow* produced from computing `a - b`. Finally...we use the overflow flag to conditionally
172 // move data into registers such that we end up with either `a - b` or `2p + (a - b)` (this is branchless). OK! So
173 // what's the problem? Well we assume that every field element lies between 0 and 2p - 1. But we are computing `2p -
174 // *this`! If *this = 0 then we exceed this bound hence the need for the extra reduction step. HOWEVER, we also know
175 // that 2p - *this won't underflow so we could skip the underflow check present in the assembly code
176 constexpr field p{ twice_modulus.data[0], twice_modulus.data[1], twice_modulus.data[2], twice_modulus.data[3] };
177 return (p - *this).reduce_once(); // modulus - *this;
178}
179
180template <class T> constexpr field<T>& field<T>::operator-=(const field& other) noexcept
181{
182 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= 0x4000000000000000ULL) ||
183 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
184 *this = subtract_coarse(other); // subtract(other);
185 } else {
186 if (std::is_constant_evaluated()) {
187 *this = subtract_coarse(other); // subtract(other);
188 } else {
189 asm_self_sub_with_coarse_reduction(*this, other); // asm_self_sub(*this, other);
190 }
191 }
192 return *this;
193}
194
195template <class T> constexpr void field<T>::self_neg() noexcept
196{
197 if constexpr ((T::modulus_3 >= 0x4000000000000000ULL) ||
198 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
199 constexpr field p{ modulus.data[0], modulus.data[1], modulus.data[2], modulus.data[3] };
200 *this = p - *this;
201 } else {
202 constexpr field p{ twice_modulus.data[0], twice_modulus.data[1], twice_modulus.data[2], twice_modulus.data[3] };
203 *this = (p - *this).reduce_once();
204 }
205}
206
207template <class T> constexpr void field<T>::self_conditional_negate(const uint64_t predicate) noexcept
208{
209 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= 0x4000000000000000ULL) ||
210 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
211 *this = predicate ? -(*this) : *this; // NOLINT
212 } else {
213 if (std::is_constant_evaluated()) {
214 *this = predicate ? -(*this) : *this; // NOLINT
215 } else {
216 asm_conditional_negate(*this, predicate);
217 }
218 }
219}
220
232template <class T> constexpr bool field<T>::operator>(const field& other) const noexcept
233{
234 const field left = reduce_once();
235 const field right = other.reduce_once();
236 const bool t0 = left.data[3] > right.data[3];
237 const bool t1 = (left.data[3] == right.data[3]) && (left.data[2] > right.data[2]);
238 const bool t2 =
239 (left.data[3] == right.data[3]) && (left.data[2] == right.data[2]) && (left.data[1] > right.data[1]);
240 const bool t3 = (left.data[3] == right.data[3]) && (left.data[2] == right.data[2]) &&
241 (left.data[1] == right.data[1]) && (left.data[0] > right.data[0]);
242 return (t0 || t1 || t2 || t3);
243}
244
256template <class T> constexpr bool field<T>::operator<(const field& other) const noexcept
257{
258 return (other > *this);
259}
260
261template <class T> constexpr bool field<T>::operator==(const field& other) const noexcept
262{
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
274template <class T> constexpr field<T> field<T>::to_montgomery_form() const noexcept
275{
276 constexpr field r_squared{ T::r_squared_0, T::r_squared_1, T::r_squared_2, T::r_squared_3 };
277
278 field result = *this;
279 // TODO(@zac-williamson): are these reductions needed?
280 // Rationale: We want to take any 256-bit input and be able to convert into montgomery form.
281 // A basic heuristic we use is that any input into the `*` operator must be between [0, 2p - 1]
282 // to prevent overflows in the asm algorithm.
283 // However... r_squared is already reduced so perhaps we can relax this requirement?
284 // (would be good to identify a failure case where not calling self_reduce triggers an error)
285 result.self_reduce_once();
286 result.self_reduce_once();
287 result.self_reduce_once();
288 return (result * r_squared).reduce_once();
289}
290
291template <class T> constexpr field<T> field<T>::from_montgomery_form() const noexcept
292{
293 constexpr field one_raw{ 1, 0, 0, 0 };
294 return operator*(one_raw).reduce_once();
295}
296
297template <class T> constexpr void field<T>::self_to_montgomery_form() noexcept
298{
299 constexpr field r_squared{ T::r_squared_0, T::r_squared_1, T::r_squared_2, T::r_squared_3 };
300 self_reduce_once();
301 self_reduce_once();
302 self_reduce_once();
303 *this *= r_squared;
304 self_reduce_once();
305}
306
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 self_reduce_once();
312}
313
314template <class T> constexpr field<T> field<T>::reduce_once() const noexcept
315{
316 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= 0x4000000000000000ULL) ||
317 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
318 return reduce();
319 } else {
320 if (std::is_constant_evaluated()) {
321 return reduce();
322 }
323 return asm_reduce_once(*this);
324 }
325}
326
327template <class T> constexpr void field<T>::self_reduce_once() noexcept
328{
329 if constexpr (BBERG_NO_ASM || (T::modulus_3 >= 0x4000000000000000ULL) ||
330 (T::modulus_1 == 0 && T::modulus_2 == 0 && T::modulus_3 == 0)) {
331 *this = reduce();
332 } else {
333 if (std::is_constant_evaluated()) {
334 *this = reduce();
335 } else {
336 asm_self_reduce_once(*this);
337 }
338 }
339}
340
341template <class T> constexpr field<T> field<T>::pow(const uint256_t& exponent) const noexcept
342{
343
344 field accumulator{ data[0], data[1], data[2], data[3] };
345 field to_mul{ data[0], data[1], data[2], data[3] };
346 const uint64_t maximum_set_bit = exponent.get_msb();
347
348 for (int i = static_cast<int>(maximum_set_bit) - 1; i >= 0; --i) {
349 accumulator.self_sqr();
350 if (exponent.get_bit(static_cast<uint64_t>(i))) {
351 accumulator *= to_mul;
352 }
353 }
354 if (exponent == uint256_t(0)) {
355 accumulator = one();
356 } else if (*this == zero()) {
357 accumulator = zero();
358 }
359 return accumulator;
360}
361
362template <class T> constexpr field<T> field<T>::pow(const uint64_t exponent) const noexcept
363{
364 return pow({ exponent, 0, 0, 0 });
365}
366
367template <class T> constexpr field<T> field<T>::invert() const noexcept
368{
369 if (*this == zero()) {
370 throw_or_abort("Trying to invert zero in the field");
371 }
372 return pow(modulus_minus_two);
373}
374
375template <class T> void field<T>::batch_invert(field* coeffs, const size_t n) noexcept
376{
377 batch_invert(std::span{ coeffs, n });
378}
379
380template <class T> void field<T>::batch_invert(std::span<field> coeffs) noexcept
381{
382 const size_t n = coeffs.size();
383
384 auto temporaries_ptr = std::static_pointer_cast<field[]>(get_mem_slab(n * sizeof(field)));
385 auto skipped_ptr = std::static_pointer_cast<bool[]>(get_mem_slab(n));
386 auto temporaries = temporaries_ptr.get();
387 auto* skipped = skipped_ptr.get();
388
389 field accumulator = one();
390 for (size_t i = 0; i < n; ++i) {
391 temporaries[i] = accumulator;
392 if (coeffs[i].is_zero()) {
393 skipped[i] = true;
394 } else {
395 skipped[i] = false;
396 accumulator *= coeffs[i];
397 }
398 }
399
400 // std::vector<field> temporaries;
401 // std::vector<bool> skipped;
402 // temporaries.reserve(n);
403 // skipped.reserve(n);
404
405 // field accumulator = one();
406 // for (size_t i = 0; i < n; ++i) {
407 // temporaries.emplace_back(accumulator);
408 // if (coeffs[i].is_zero()) {
409 // skipped.emplace_back(true);
410 // } else {
411 // skipped.emplace_back(false);
412 // accumulator *= coeffs[i];
413 // }
414 // }
415
416 accumulator = accumulator.invert();
417
418 field T0;
419 for (size_t i = n - 1; i < n; --i) {
420 if (!skipped[i]) {
421 T0 = accumulator * temporaries[i];
422 accumulator *= coeffs[i];
423 coeffs[i] = T0;
424 }
425 }
426}
427
428template <class T> constexpr field<T> field<T>::tonelli_shanks_sqrt() const noexcept
429{
430 // Tonelli-shanks algorithm begins by finding a field element Q and integer S,
431 // such that (p - 1) = Q.2^{s}
432
433 // We can compute the square root of a, by considering a^{(Q + 1) / 2} = R
434 // Once we have found such an R, we have
435 // R^{2} = a^{Q + 1} = a^{Q}a
436 // If a^{Q} = 1, we have found our square root.
437 // Otherwise, we have a^{Q} = t, where t is a 2^{s-1}'th root of unity.
438 // This is because t^{2^{s-1}} = a^{Q.2^{s-1}}.
439 // We know that (p - 1) = Q.w^{s}, therefore t^{2^{s-1}} = a^{(p - 1) / 2}
440 // From Euler's criterion, if a is a quadratic residue, a^{(p - 1) / 2} = 1
441 // i.e. t^{2^{s-1}} = 1
442
443 // To proceed with computing our square root, we want to transform t into a smaller subgroup,
444 // specifically, the (s-2)'th roots of unity.
445 // We do this by finding some value b,such that
446 // (t.b^2)^{2^{s-2}} = 1 and R' = R.b
447 // Finding such a b is trivial, because from Euler's criterion, we know that,
448 // for any quadratic non-residue z, z^{(p - 1) / 2} = -1
449 // i.e. z^{Q.2^{s-1}} = -1
450 // => z^Q is a 2^{s-1}'th root of -1
451 // => z^{Q^2} is a 2^{s-2}'th root of -1
452 // Since t^{2^{s-1}} = 1, we know that t^{2^{s - 2}} = -1
453 // => t.z^{Q^2} is a 2^{s - 2}'th root of unity.
454
455 // We can iteratively transform t into ever smaller subgroups, until t = 1.
456 // At each iteration, we need to find a new value for b, which we can obtain
457 // by repeatedly squaring z^{Q}
458 constexpr uint256_t Q = (modulus - 1) >> static_cast<uint64_t>(primitive_root_log_size() - 1);
459 constexpr uint256_t Q_minus_one_over_two = (Q - 1) >> 2;
460
461 // __to_montgomery_form(Q_minus_one_over_two, Q_minus_one_over_two);
462 field z = coset_generator(0); // the generator is a non-residue
463 field b = pow(Q_minus_one_over_two);
464 field r = operator*(b); // r = a^{(Q + 1) / 2}
465 field t = r * b; // t = a^{(Q - 1) / 2 + (Q + 1) / 2} = a^{Q}
466
467 // check if t is a square with euler's criterion
468 // if not, we don't have a quadratic residue and a has no square root!
469 field check = t;
470 for (size_t i = 0; i < primitive_root_log_size() - 1; ++i) {
471 check.self_sqr();
472 }
473 if (check != one()) {
474 return zero();
475 }
476 field t1 = z.pow(Q_minus_one_over_two);
477 field t2 = t1 * z;
478 field c = t2 * t1; // z^Q
479
480 size_t m = primitive_root_log_size();
481
482 while (t != one()) {
483 size_t i = 0;
484 field t2m = t;
485
486 // find the smallest value of m, such that t^{2^m} = 1
487 while (t2m != one()) {
488 t2m.self_sqr();
489 i += 1;
490 }
491
492 size_t j = m - i - 1;
493 b = c;
494 while (j > 0) {
495 b.self_sqr();
496 --j;
497 } // b = z^2^(m-i-1)
498
499 c = b.sqr();
500 t = t * c;
501 r = r * b;
502 m = i;
503 }
504 return r;
505}
506
507template <class T> constexpr std::pair<bool, field<T>> field<T>::sqrt() const noexcept
508{
509 field root;
510 if constexpr ((T::modulus_0 & 0x3UL) == 0x3UL) {
511 constexpr uint256_t sqrt_exponent = (modulus + uint256_t(1)) >> 2;
512 root = pow(sqrt_exponent);
513 } else {
514 root = tonelli_shanks_sqrt();
515 }
516 if ((root * root) == (*this)) {
517 return std::pair<bool, field>(true, root);
518 }
519 return std::pair<bool, field>(false, field::zero());
520
521} // namespace barretenberg
522
523template <class T> constexpr field<T> field<T>::operator/(const field& other) const noexcept
524{
525 return operator*(other.invert());
526}
527
528template <class T> constexpr field<T>& field<T>::operator/=(const field& other) noexcept
529{
530 *this = operator/(other);
531 return *this;
532}
533
534template <class T> constexpr void field<T>::self_set_msb() noexcept
535{
536 data[3] = 0ULL | (1ULL << 63ULL);
537}
538
539template <class T> constexpr bool field<T>::is_msb_set() const noexcept
540{
541 return (data[3] >> 63ULL) == 1ULL;
542}
543
544template <class T> constexpr uint64_t field<T>::is_msb_set_word() const noexcept
545{
546 return (data[3] >> 63ULL);
547}
548
549template <class T> constexpr bool field<T>::is_zero() const noexcept
550{
551 return ((data[0] | data[1] | data[2] | data[3]) == 0) ||
552 (data[0] == T::modulus_0 && data[1] == T::modulus_1 && data[2] == T::modulus_2 && data[3] == T::modulus_3);
553}
554
555template <class T> constexpr field<T> field<T>::get_root_of_unity(size_t subgroup_size) noexcept
556{
557 field r{ T::primitive_root_0, T::primitive_root_1, T::primitive_root_2, T::primitive_root_3 };
558 for (size_t i = primitive_root_log_size(); i > subgroup_size; --i) {
559 r.self_sqr();
560 }
561 return r;
562}
563
564template <class T> field<T> field<T>::random_element(numeric::random::Engine* engine) noexcept
565{
566 if (engine == nullptr) {
567 engine = &numeric::random::get_engine();
568 }
569
570 uint512_t source = engine->get_random_uint512();
571 uint512_t q(modulus);
572 uint512_t reduced = source % q;
573 return field(reduced.lo);
574}
575
576template <class T> constexpr size_t field<T>::primitive_root_log_size() noexcept
577{
578 uint256_t target = modulus - 1;
579 size_t result = 0;
580 while (!target.get_bit(result)) {
581 ++result;
582 }
583 return result;
584}
585
586template <class T>
587constexpr std::array<field<T>, field<T>::COSET_GENERATOR_SIZE> field<T>::compute_coset_generators() noexcept
588{
589 constexpr size_t n = COSET_GENERATOR_SIZE;
590 constexpr uint64_t subgroup_size = 1 << 30;
591
592 std::array<field, COSET_GENERATOR_SIZE> result{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
593 if (n > 0) {
594 result[0] = (multiplicative_generator());
595 }
596 field work_variable = multiplicative_generator() + field(1);
597
598 size_t count = 1;
599 while (count < n) {
600 // work_variable contains a new field element, and we need to test that, for all previous vector elements,
601 // result[i] / work_variable is not a member of our subgroup
602 field work_inverse = work_variable.invert();
603 bool valid = true;
604 for (size_t j = 0; j < count; ++j) {
605 field subgroup_check = (work_inverse * result[j]).pow(subgroup_size);
606 if (subgroup_check == field(1)) {
607 valid = false;
608 break;
609 }
610 }
611 if (valid) {
612 result[count] = (work_variable);
613 ++count;
614 }
615 work_variable += field(1);
616 }
617 return result;
618}
619
620template <class T> constexpr field<T> field<T>::multiplicative_generator() noexcept
621{
622 field target(1);
623 uint256_t p_minus_one_over_two = (modulus - 1) >> 1;
624 bool found = false;
625 while (!found) {
626 target += field(1);
627 found = (target.pow(p_minus_one_over_two) == -field(1));
628 }
629 return target;
630}
631
632// This function is used to serialize a field. It matches the old serialization format by first
633// converting the field from Montgomery form, which is a special representation used for efficient
634// modular arithmetic.
635template <class Params> void field<Params>::msgpack_pack(auto& packer) const
636{
637 // The field is first converted from Montgomery form, similar to how the old format did it.
638 auto adjusted = from_montgomery_form();
639
640 // The data is then converted to big endian format using htonll, which stands for "host to network long long".
641 // This is necessary because the data will be written to a raw msgpack buffer, which requires big endian format.
642 uint64_t bin_data[4] = {
643 htonll(adjusted.data[3]), htonll(adjusted.data[2]), htonll(adjusted.data[1]), htonll(adjusted.data[0])
644 };
645
646 // The packer is then used to write the binary data to the buffer, just like in the old format.
647 packer.pack_bin(sizeof(bin_data));
648 packer.pack_bin_body((const char*)bin_data, sizeof(bin_data)); // NOLINT
649}
650
651// This function is used to deserialize a field. It also matches the old deserialization format by
652// reading the binary data as big endian uint64_t's, correcting them to the host endianness, and
653// then converting the field back to Montgomery form.
654template <class Params> void field<Params>::msgpack_unpack(auto o)
655{
656 // The binary data is first extracted from the msgpack object.
657 std::array<uint8_t, sizeof(data)> raw_data = o;
658
659 // The binary data is then read as big endian uint64_t's. This is done by casting the raw data to uint64_t* and then
660 // using ntohll ("network to host long long") to correct the endianness to the host's endianness.
661 uint64_t* cast_data = (uint64_t*)&raw_data[0]; // NOLINT
662 uint64_t reversed[] = { ntohll(cast_data[3]), ntohll(cast_data[2]), ntohll(cast_data[1]), ntohll(cast_data[0]) };
663
664 // The corrected data is then copied back into the field's data array.
665 for (int i = 0; i < 4; i++) {
666 data[i] = reversed[i];
667 }
668
669 // Finally, the field is converted back to Montgomery form, just like in the old format.
670 *this = to_montgomery_form();
671}
672
673} // namespace barretenberg
674
675// clang-format off
676// NOLINTEND(cppcoreguidelines-avoid-c-arrays)
677// clang-format on
Definition: engine.hpp:10
Definition: uint256.hpp:25
field_t pow(const field_t &exponent) const
raise a field_t to a power of an exponent (field_t). Note that the exponent must not exceed 32 bits a...
Definition: field.cpp:346
constexpr_utils defines some helper methods that perform some stl-equivalent operations but in a cons...
Definition: constexpr_utils.hpp:16
std::shared_ptr< void > get_mem_slab(size_t size)
Definition: slab_allocator.cpp:214
Definition: field_declarations.hpp:24
BBERG_INLINE constexpr bool operator<(const field &other) const noexcept
Less-than operator.
Definition: field_impl.hpp:256
BBERG_INLINE constexpr field sqr() const noexcept
Definition: field_impl.hpp:61
constexpr std::pair< bool, field > sqrt() const noexcept
Compute square root of the field element.
Definition: field_impl.hpp:507
BBERG_INLINE constexpr bool operator>(const field &other) const noexcept
Greater-than operator.
Definition: field_impl.hpp:232
BBERG_INLINE constexpr field operator+(const field &other) const noexcept
Definition: field_impl.hpp:93
BBERG_INLINE constexpr field operator*(const field &other) const noexcept
Definition: field_impl.hpp:26