Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
prime_field.test.cpp
Go to the documentation of this file.
1
24#include <gtest/gtest.h>
25
26using namespace bb;
27
28namespace {
30
31// Helper to create a field element whose internal (Montgomery) representation has exactly
32// the given limbs. This is done by constructing from the value and then calling
33// from_montgomery_form() to make the limbs directly represent the desired pattern.
34// Also verifies that the internal representation matches the expected limbs (on non-WASM only,
35// since WASM uses different internal representation during computation).
36template <typename F> F create_element_with_limbs(uint64_t l0, uint64_t l1, uint64_t l2, uint64_t l3)
37{
38 uint256_t val{ l0, l1, l2, l3 };
39 F result(val);
40 result.self_from_montgomery_form_reduced();
41
42// On WASM, the internal Montgomery multiplication uses 29-bit limbs and may produce
43// different (but equivalent) representations. Skip limb verification on WASM.
44#if defined(__SIZEOF_INT128__) && !defined(__wasm__)
45 // Verify the internal representation matches expected limbs
46 EXPECT_EQ(result.data[0], l0);
47 EXPECT_EQ(result.data[1], l1);
48 EXPECT_EQ(result.data[2], l2);
49 EXPECT_EQ(result.data[3], l3);
50#endif
51
52 return result;
53}
54} // namespace
55
56// Type-parameterized test fixture for prime fields
57template <typename F> class PrimeFieldTest : public ::testing::Test {
58 public:
59 // Helper to get a random field element as uint256_t (for reference calculations)
61 {
63 while (res >= F::modulus) {
64 res -= F::modulus;
65 }
66 return res;
67 }
68};
69
70// Register all prime field types
71using PrimeFieldTypes = ::testing::Types<bb::fq, bb::fr, secp256k1::fq, secp256k1::fr, secp256r1::fq, secp256r1::fr>;
72
73// Fields where sqrt() works correctly
74using SqrtFieldTypes = ::testing::Types<bb::fq, bb::fr, secp256k1::fq, secp256r1::fq>;
75
76// Fields that have cube_root_of_unity() defined
77using CubeRootFieldTypes = ::testing::Types<bb::fq, bb::fr, secp256k1::fq, secp256k1::fr>;
78
79// Fields whose modulus is 256 bits.
80using TwoFiftySixBitFieldTypes = ::testing::Types<secp256k1::fq, secp256k1::fr>;
81
82// Fields whose modulus is 254 bits (BN254 fq and fr).
83using TwoFiftyFourBitFieldTypes = ::testing::Types<bb::fq, bb::fr>;
84
86
87template <typename> class PrimeFieldSqrtTest : public ::testing::Test {};
89
90template <typename> class PrimeFieldCubeRootTest : public ::testing::Test {};
92
93template <typename> class PrimeFieldTwoFiftySixTest : public ::testing::Test {};
95
96template <typename> class PrimeFieldTwoFiftyFourTest : public ::testing::Test {};
98// ================================
99// Compile-time Tests (Prime Field Specific)
100// ================================
101
102TYPED_TEST(PrimeFieldTest, CompileTimeEquality)
103{
104 using F = TypeParam;
105
106 constexpr F a{ 0x01, 0x02, 0x03, 0x04 };
107 constexpr F b{ 0x01, 0x02, 0x03, 0x04 };
108
109 constexpr F c{ 0x01, 0x02, 0x03, 0x05 };
110 constexpr F d{ 0x01, 0x02, 0x04, 0x04 };
111 constexpr F e{ 0x01, 0x03, 0x03, 0x04 };
112 constexpr F f{ 0x02, 0x02, 0x03, 0x04 };
113 static_assert(a == b);
114 static_assert(!(a == c));
115 static_assert(!(a == d));
116 static_assert(!(a == e));
117 static_assert(!(a == f));
118}
119
120TYPED_TEST(PrimeFieldTest, CompileTimeSmallAddSubMul)
121{
122 using F = TypeParam;
123
124 constexpr F a{ 0x01, 0x02, 0x03, 0x04 };
125 constexpr F b{ 0x05, 0x06, 0x07, 0x08 };
126
127 // Just verify these operations are constexpr and produce consistent results
128 constexpr F sum = a + b;
129 constexpr F diff = a - b;
130 constexpr F prod = a * b;
131 constexpr F sq = a.sqr();
132
133 // Verify at runtime that constexpr results match runtime results
134 EXPECT_EQ(sum, a + b);
135 EXPECT_EQ(diff, a - b);
136 EXPECT_EQ(prod, a * b);
137 EXPECT_EQ(sq, a.sqr());
138}
139
140TYPED_TEST(PrimeFieldTest, CompileTimeUint256Conversion)
141{
142 using F = TypeParam;
143
144 constexpr uint256_t a{ 0x1111, 0x2222, 0x3333, 0x4444 };
145 constexpr F b(a);
146 constexpr uint256_t c = b;
147 static_assert(a == c || a == c - F::modulus);
148}
149
150// ================================
151// uint256_t Arithmetic Verification
152// ================================
153
154TYPED_TEST(PrimeFieldTest, AdditionModular)
155{
156 using F = TypeParam;
157
158 uint256_t a_raw = TestFixture::get_random_element_raw();
159 uint256_t b_raw = TestFixture::get_random_element_raw();
160
161 F a(a_raw);
162 F b(b_raw);
163 F c = a + b;
164
165 uint512_t expected_512 = uint512_t(a_raw) + uint512_t(b_raw);
166 uint256_t expected = (expected_512 % uint512_t(F::modulus)).lo;
167
168 EXPECT_EQ(uint256_t(c), expected);
169}
170
171TYPED_TEST(PrimeFieldTest, SubtractionModular)
172{
173 using F = TypeParam;
174
175 uint256_t a_raw = TestFixture::get_random_element_raw();
176 uint256_t b_raw = TestFixture::get_random_element_raw();
177
178 F a(a_raw);
179 F b(b_raw);
180 F c = a - b;
181
182 uint512_t expected_512 = uint512_t(a_raw) + uint512_t(F::modulus) - uint512_t(b_raw);
183 uint256_t expected = (expected_512 % uint512_t(F::modulus)).lo;
184
185 EXPECT_EQ(uint256_t(c), expected);
186}
187
188TYPED_TEST(PrimeFieldTest, MultiplicationModular)
189{
190 using F = TypeParam;
191
192 uint256_t a_raw = TestFixture::get_random_element_raw();
193 uint256_t b_raw = TestFixture::get_random_element_raw();
194
195 F a(a_raw);
196 F b(b_raw);
197 F c = a * b;
198
199 uint512_t c_512 = uint512_t(a_raw) * uint512_t(b_raw);
200 uint256_t expected = (c_512 % uint512_t(F::modulus)).lo;
201
202 EXPECT_EQ(uint256_t(c), expected);
203}
204
205TYPED_TEST(PrimeFieldTest, SquaringModular)
206{
207 using F = TypeParam;
208
209 uint256_t a_raw = TestFixture::get_random_element_raw();
210
211 F a(a_raw);
212 F c = a.sqr();
213
214 uint512_t c_512 = uint512_t(a_raw) * uint512_t(a_raw);
215 uint256_t expected = (c_512 % uint512_t(F::modulus)).lo;
216
217 EXPECT_EQ(uint256_t(c), expected);
218}
219
220TYPED_TEST(PrimeFieldTest, Uint256Roundtrip)
221{
222 using F = TypeParam;
223
224 uint256_t original = TestFixture::get_random_element_raw();
225 F field_element(original);
226 uint256_t recovered(field_element);
227
228 EXPECT_EQ(original, recovered);
229}
230
231// ================================
232// Montgomery Form (Prime Field Specific)
233// ================================
234
235TYPED_TEST(PrimeFieldTest, MontgomeryRoundtrip)
236{
237 using F = TypeParam;
238
239 F a = F::random_element();
241 EXPECT_EQ(a, b);
242}
243
244// ================================
245// Square Root
246// ================================
247
249{
250 using F = TypeParam;
251
252 F one = F::one();
253 auto [is_sqr, root] = one.sqrt();
254
255 EXPECT_TRUE(is_sqr);
256 EXPECT_EQ(root.sqr(), one);
257}
258
260{
261 using F = TypeParam;
262
263 F a = F::random_element();
264 F a_sqr = a.sqr();
265 auto [is_sqr, root] = a_sqr.sqrt();
266
267 EXPECT_TRUE(is_sqr);
268 EXPECT_EQ(root.sqr(), a_sqr);
269 EXPECT_TRUE((root == a) || (root == -a));
270}
271
272// ================================
273// Cube Root of Unity
274// ================================
275
277{
278 using F = TypeParam;
279
280 // lambda^3 = 1, so (lambda * x)^3 = x^3
281 F x = F::random_element();
282 F lambda = F::cube_root_of_unity();
283 F lambda_x = x * lambda;
284
285 F x_cubed = x * x * x;
286 F lambda_x_cubed = lambda_x * lambda_x * lambda_x;
287
288 EXPECT_EQ(x_cubed, lambda_x_cubed);
289}
290
291// ================================
292// Exponentiation
293// ================================
294
295TYPED_TEST(PrimeFieldTest, PowZeroExponent)
296{
297 using F = TypeParam;
298
299 // a^0 = 1 for any non-zero a
300 F a = F::random_element();
301 EXPECT_EQ(a.pow(uint256_t(0)), F::one());
302}
303
305{
306 using F = TypeParam;
307
308 F a = F::random_element();
309 EXPECT_EQ(a.pow(uint256_t(1)), a);
310}
311
313{
314 using F = TypeParam;
315
316 F a = F::random_element();
317 EXPECT_EQ(a.pow(uint256_t(2)), a * a);
318}
319
321{
322 using F = TypeParam;
323
324 F a = F::random_element();
325 EXPECT_EQ(a.pow(uint256_t(3)), a * a * a);
326}
327
328// ================================
329// Batch Invert (only implemented for prime fields)
330// ================================
331
333{
334 using F = TypeParam;
335 constexpr size_t batch_size = 10;
336
337 std::vector<F> elements(batch_size);
338 std::vector<F> inverses(batch_size);
339
340 for (size_t i = 0; i < batch_size; ++i) {
341 elements[i] = F::random_element();
342 inverses[i] = elements[i];
343 }
344
345 F::batch_invert(&inverses[0], batch_size);
346
347 for (size_t i = 0; i < batch_size; ++i) {
348 F product = elements[i] * inverses[i];
349 product = product.reduce_once().reduce_once();
350 EXPECT_EQ(product, F::one());
351 }
352}
353
354// ================================
355// Increment Operators
356// ================================
357
359{
360 using F = TypeParam;
361
362 F a = F::random_element();
363 F a_copy = a;
364
365 a += 2;
366 F expected = a_copy + F(2);
367 EXPECT_EQ(a, expected);
368
369 a += 3;
370 expected = a_copy + F(5);
371 EXPECT_EQ(a, expected);
372}
373
374TYPED_TEST(PrimeFieldTest, PrefixIncrement)
375{
376 using F = TypeParam;
377
378 F a = F::random_element();
379 F a_before = a;
380 F b = ++a;
381
382 // Prefix increment returns the new value
383 EXPECT_EQ(b, a);
384 EXPECT_EQ(a, a_before + F(1));
385}
386
387TYPED_TEST(PrimeFieldTest, PostfixIncrement)
388{
389 using F = TypeParam;
390
391 F a = F::random_element();
392 F a_old = a;
393 F b = a++;
394
395 // Postfix increment returns the old value
396 EXPECT_EQ(b, a_old);
397 EXPECT_EQ(a, a_old + F(1));
398}
399
400// ================================
401// Internal Representation Tests for Big Fields
402// ================================
403
404// Shows that the raw limbs of an addition can have a result which is not automatically reduced, even when we are in the
405// 256-bit field range.
406TYPED_TEST(PrimeFieldTwoFiftySixTest, AddYieldsLimbsBiggerThanModulus)
407{
408 using F = TypeParam;
409
410 auto small_number = 10;
411 F small_field_elt = F(small_number).from_montgomery_form();
412 uint256_t small_number_from_limbs(
413 small_field_elt.data[0], small_field_elt.data[1], small_field_elt.data[2], small_field_elt.data[3]);
414 uint256_t big_number = uint256_t(F::modulus) - 1;
415 F big_field_elt = F(big_number).from_montgomery_form();
416 uint256_t big_number_from_limbs(
417 big_field_elt.data[0], big_field_elt.data[1], big_field_elt.data[2], big_field_elt.data[3]);
418
419 // make sure that the limbs combine to the number. (this is not immediate because we internall represent via
420 // Montgomery form. Here, it is guaranteed because we call `.from_montgomery_form()`).
421 EXPECT_EQ(small_number_from_limbs, small_number);
422 EXPECT_EQ(big_number, big_number_from_limbs);
423
424 F result = small_field_elt + big_field_elt;
425
426 // Extract the raw limbs from result
427 uint256_t result_from_limbs(result.data[0], result.data[1], result.data[2], result.data[3]);
428
429 // Check if the uint256_t from limbs is >= p
430 bool is_gte_modulus = result_from_limbs >= F::modulus;
431 EXPECT_EQ(is_gte_modulus, true);
432}
433
434// ================================
435// Single Non-Zero Limb Edge Cases
436// ================================
437// These tests verify correctness when the internal Montgomery representation has only one
438// non-zero limb, which can expose carry propagation bugs in Montgomery multiplication and
439// other operations.
440
441TYPED_TEST(PrimeFieldTest, SingleLimbArithmetic)
442{
443 using F = TypeParam;
444
445 // Test values for each limb position (only one limb non-zero at a time)
446 // For limb 3, use modulus.data[3] / 2 to stay below modulus
447 constexpr std::array<uint64_t, 4> limb_values = {
448 0xDEADBEEFCAFEBABEULL, 0xFEDCBA9876543210ULL, 0x123456789ABCDEF0ULL, F::modulus.data[3] / 2
449 };
450
451 for (size_t limb_idx = 0; limb_idx < 4; ++limb_idx) {
452 std::array<uint64_t, 4> limbs = { 0, 0, 0, 0 };
453 limbs[limb_idx] = limb_values[limb_idx];
454
455 F a = create_element_with_limbs<F>(limbs[0], limbs[1], limbs[2], limbs[3]);
456 F b = F::random_element();
457 uint256_t a_val = uint256_t(a);
458 uint256_t b_val = uint256_t(b);
459
460 // Test add
461 F sum = a + b;
462 uint512_t expected_sum = (uint512_t(a_val) + uint512_t(b_val)) % uint512_t(F::modulus);
463 EXPECT_EQ(uint256_t(sum), expected_sum.lo) << "Add failed for limb " << limb_idx;
464
465 // Test sub
466 F diff = a - b;
467 uint512_t expected_diff = (uint512_t(a_val) + uint512_t(F::modulus) - uint512_t(b_val)) % uint512_t(F::modulus);
468 EXPECT_EQ(uint256_t(diff), expected_diff.lo) << "Sub failed for limb " << limb_idx;
469
470 // Test mul
471 F prod = a * b;
472 uint512_t expected_prod = (uint512_t(a_val) * uint512_t(b_val)) % uint512_t(F::modulus);
473 EXPECT_EQ(uint256_t(prod), expected_prod.lo) << "Mul failed for limb " << limb_idx;
474
475 // Test sqr
476 F sq = a.sqr();
477 uint512_t expected_sq = (uint512_t(a_val) * uint512_t(a_val)) % uint512_t(F::modulus);
478 EXPECT_EQ(uint256_t(sq), expected_sq.lo) << "Sqr failed for limb " << limb_idx;
479 }
480}
481
482// Test multiplication of two single-limb values (different limbs)
483TYPED_TEST(PrimeFieldTest, CrossLimbMultiplication)
484{
485 using F = TypeParam;
486
487 // Create elements with single non-zero limbs at different positions
488 // For limb 3, use modulus.data[3] / 2 to stay below modulus
489 constexpr std::array<std::array<uint64_t, 4>, 4> limb_patterns = { { { { 0xABCDEF0123456789ULL, 0, 0, 0 } },
490 { { 0, 0x9876543210FEDCBAULL, 0, 0 } },
491 { { 0, 0, 0x1111222233334444ULL, 0 } },
492 { { 0, 0, 0, F::modulus.data[3] / 2 } } } };
493
494 // Build field elements and their uint256_t values
495 std::array<F, 4> elems;
497 for (size_t i = 0; i < 4; ++i) {
498 elems[i] = create_element_with_limbs<F>(
499 limb_patterns[i][0], limb_patterns[i][1], limb_patterns[i][2], limb_patterns[i][3]);
500 vals[i] = uint256_t(elems[i]);
501 }
502
503 // Test all pairwise multiplications
504 for (size_t i = 0; i < 4; ++i) {
505 for (size_t j = 0; j < 4; ++j) {
506 F prod = elems[i] * elems[j];
507 uint512_t expected_prod = (uint512_t(vals[i]) * uint512_t(vals[j])) % uint512_t(F::modulus);
508 EXPECT_EQ(uint256_t(prod), expected_prod.lo) << "Failed for limb " << i << " * limb " << j;
509 }
510 }
511}
512
513// ================================
514// Edge Cases Near Modulus Boundary
515// ================================
516
517// Test multiplication and squaring of values near the modulus
518TYPED_TEST(PrimeFieldTest, NearModulusMultiplication)
519{
520 using F = TypeParam;
521
522 for (uint64_t offset = 1; offset <= 10; ++offset) {
523 uint256_t val = F::modulus - offset;
524 F a(val);
525 uint256_t a_val = uint256_t(a);
526
527 // Test squaring
528 F sq = a.sqr();
529 uint512_t expected_sq = (uint512_t(a_val) * uint512_t(a_val)) % uint512_t(F::modulus);
530 EXPECT_EQ(uint256_t(sq), expected_sq.lo) << "Sqr failed for (p - " << offset << ")^2";
531
532 // Test a * (a + 1)
533 F a_plus_one = a + F(1);
534 uint256_t a_plus_one_val = uint256_t(a_plus_one);
535 F prod = a * a_plus_one;
536 uint512_t expected_prod = (uint512_t(a_val) * uint512_t(a_plus_one_val)) % uint512_t(F::modulus);
537 EXPECT_EQ(uint256_t(prod), expected_prod.lo)
538 << "Mul failed for (p - " << offset << ") * (p - " << offset << " + 1)";
539 }
540}
541
542// ================================
543// Boundary Tests
544// ================================
545// Test arithmetic with internal representations near the representation boundary.
546// - 254-bit fields (modulus.data[3] < MODULUS_TOP_LIMB_LARGE_THRESHOLD): boundary is 2p
547// - 256-bit fields (modulus.data[3] >= MODULUS_TOP_LIMB_LARGE_THRESHOLD): boundary is 2^256 - 1
548
549TYPED_TEST(PrimeFieldTest, BoundaryArithmetic)
550{
551 using F = TypeParam;
552 constexpr std::array<uint64_t, 3> offsets = { 1, 2, 3 };
553
554 for (uint64_t offset : offsets) {
555 F a;
556 if constexpr (F::modulus.data[3] >= MODULUS_TOP_LIMB_LARGE_THRESHOLD) {
557 // 256-bit fields: construct element with internal representation near 2^256 - offset.
558 // (p - offset) + (2^256 - p) = 2^256 - offset, which has field value -offset.
559 uint256_t two_256_minus_p = uint256_t(0) - F::modulus;
560 F two_256_minus_p_elt(two_256_minus_p);
561 two_256_minus_p_elt.self_from_montgomery_form_reduced();
562 F p_minus_offset(F::modulus - offset);
563 p_minus_offset.self_from_montgomery_form_reduced();
564 a = p_minus_offset + two_256_minus_p_elt;
565
566 // Verify internal representation is 2^256 - offset
567 if (offset == 1) {
568 for (size_t i = 0; i < 4; ++i) {
569 EXPECT_EQ(a.data[i], 0xFFFFFFFFFFFFFFFFULL);
570 }
571 }
572 } else {
573 // 254-bit fields: construct element with internal representation near 2p - (offset + 1).
574 // (p - 1) + (p - offset) = 2p - (offset + 1), which has field value -(offset + 1).
575 F p_minus_one(F::modulus - 1);
576 p_minus_one.self_from_montgomery_form_reduced();
577 F p_minus_offset(F::modulus - offset);
578 p_minus_offset.self_from_montgomery_form_reduced();
579 a = p_minus_one + p_minus_offset;
580 }
581
582 uint256_t a_val = uint256_t(a);
583 F b = F::random_element();
584
585 // Test all operations
586 F sum = a + b;
587 uint512_t expected_sum = (uint512_t(a_val) + uint512_t(uint256_t(b))) % uint512_t(F::modulus);
588 EXPECT_EQ(uint256_t(sum), expected_sum.lo) << "Add failed for offset " << offset;
589
590 F diff = a - b;
591 uint512_t expected_diff =
592 (uint512_t(a_val) + uint512_t(F::modulus) - uint512_t(uint256_t(b))) % uint512_t(F::modulus);
593 EXPECT_EQ(uint256_t(diff), expected_diff.lo) << "Sub failed for offset " << offset;
594
595 F prod = a * b;
596 uint512_t expected_prod = (uint512_t(a_val) * uint512_t(uint256_t(b))) % uint512_t(F::modulus);
597 EXPECT_EQ(uint256_t(prod), expected_prod.lo) << "Mul failed for offset " << offset;
598
599 F sq = a.sqr();
600 uint512_t expected_sq = (uint512_t(a_val) * uint512_t(a_val)) % uint512_t(F::modulus);
601 EXPECT_EQ(uint256_t(sq), expected_sq.lo) << "Sqr failed for offset " << offset;
602 }
603}
604
605// ================================
606// Serialization
607// ================================
608
610{
611 using F = TypeParam;
612
613 F a = F::random_element();
614 auto [actual, expected] = msgpack_roundtrip(a);
615 EXPECT_EQ(actual, expected);
616}
617
618// ================================
619// Montgomery Form Conversion Tests (254-bit fields)
620// ================================
621
625TYPED_TEST(PrimeFieldTwoFiftyFourTest, FromMontgomeryFormNoReductionNeeded)
626{
627 using F = TypeParam;
628 constexpr uint256_t two_p = F::modulus + F::modulus;
629
630 // Test 1: Random elements
631 for (size_t i = 0; i < 100; i++) {
632 F a = F::random_element();
633
634 // Get internal limbs before conversion
635 uint256_t a_internal(a.data[0], a.data[1], a.data[2], a.data[3]);
636
637 // Verify input is in [0, 2p) range (coarse representation)
638 ASSERT_LT(a_internal, two_p) << "Input not in coarse form";
639
640 F result = a.from_montgomery_form();
641
642 // Check result is already in [0, 2p) range
643 uint256_t result_internal(result.data[0], result.data[1], result.data[2], result.data[3]);
644 EXPECT_LT(result_internal, two_p) << "Result of from_montgomery_form exceeds [0, 2p) range";
645 }
646
647 // Test 2: Edge cases - elements near the boundary 2p
648 // Construct elements with internal representation near 2p - 1
649 for (uint64_t offset = 1; offset <= 10; offset++) {
650 // Create element with internal representation = (2p - offset)
651 // We do this by adding (p - 1) + (p - offset + 1) = 2p - offset
652 F p_minus_one(F::modulus - 1);
653 p_minus_one.self_from_montgomery_form();
654 F p_minus_offset_plus_one(F::modulus - offset + 1);
655 p_minus_offset_plus_one.self_from_montgomery_form();
656 F a = p_minus_one + p_minus_offset_plus_one;
657
658 // Verify we have internal representation near 2p
659 uint256_t a_internal(a.data[0], a.data[1], a.data[2], a.data[3]);
660 ASSERT_LT(a_internal, two_p) << "Test setup: element not in valid range";
661
662 F result = a.from_montgomery_form();
663
664 uint256_t result_internal(result.data[0], result.data[1], result.data[2], result.data[3]);
665 EXPECT_LT(result_internal, two_p)
666 << "Edge case: Result of from_montgomery_form exceeds [0, 2p) for offset " << offset;
667 }
668}
669
673TYPED_TEST(PrimeFieldTwoFiftyFourTest, ToMontgomeryFormNoReductionNeeded)
674{
675 using F = TypeParam;
676 constexpr uint256_t two_p = F::modulus + F::modulus;
677
678 // Test 1: Random elements
679 for (size_t i = 0; i < 100; i++) {
680 F a = F::random_element();
681
682 // Get internal limbs before conversion
683 uint256_t a_internal(a.data[0], a.data[1], a.data[2], a.data[3]);
684
685 // Verify input is in [0, 2p) range (coarse representation)
686 ASSERT_LT(a_internal, two_p) << "Input not in coarse form";
687
688 F result = a.to_montgomery_form();
689
690 // Check result is already in [0, 2p) range
691 uint256_t result_internal(result.data[0], result.data[1], result.data[2], result.data[3]);
692 EXPECT_LT(result_internal, two_p) << "Result of to_montgomery_form exceeds [0, 2p) range";
693 }
694
695 // Test 2: Edge cases - elements near the boundary 2p
696 // Construct elements with internal representation near 2p - 1
697 for (uint64_t offset = 1; offset <= 10; offset++) {
698 // Create element with internal representation = (2p - offset)
699 // We do this by adding (p - 1) + (p - offset + 1) = 2p - offset
700 F p_minus_one(F::modulus - 1);
701 p_minus_one.self_from_montgomery_form();
702 F p_minus_offset_plus_one(F::modulus - offset + 1);
703 p_minus_offset_plus_one.self_from_montgomery_form();
704 F a = p_minus_one + p_minus_offset_plus_one;
705
706 // Verify we have internal representation near 2p
707 uint256_t a_internal(a.data[0], a.data[1], a.data[2], a.data[3]);
708 ASSERT_LT(a_internal, two_p) << "Test setup: element not in valid range";
709
710 F result = a.to_montgomery_form();
711
712 uint256_t result_internal(result.data[0], result.data[1], result.data[2], result.data[3]);
713 EXPECT_LT(result_internal, two_p)
714 << "Edge case: Result of to_montgomery_form exceeds [0, 2p) for offset " << offset;
715 }
716}
static uint256_t get_random_element_raw()
virtual uint256_t get_random_uint256()=0
FF a
FF b
numeric::RNG & engine
ssize_t offset
Definition engine.cpp:52
uintx< uint256_t > uint512_t
Definition uintx.hpp:306
RNG & get_debug_randomness(bool reset, std::uint_fast64_t seed)
Definition engine.cpp:217
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
TYPED_TEST_SUITE(CommitmentKeyTest, Curves)
Inner sum(Cont< Inner, Args... > const &in)
Definition container.hpp:70
TYPED_TEST(CommitmentKeyTest, CommitToZeroPoly)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
::testing::Types< bb::fq, bb::fr > TwoFiftyFourBitFieldTypes
::testing::Types< bb::fq, bb::fr, secp256k1::fq, secp256r1::fq > SqrtFieldTypes
::testing::Types< secp256k1::fq, secp256k1::fr > TwoFiftySixBitFieldTypes
::testing::Types< bb::fq, bb::fr, secp256k1::fq, secp256k1::fr, secp256r1::fq, secp256r1::fr > PrimeFieldTypes
::testing::Types< bb::fq, bb::fr, secp256k1::fq, secp256k1::fr > CubeRootFieldTypes
BB_INLINE constexpr field to_montgomery_form() const noexcept
BB_INLINE constexpr field pow(const uint256_t &exponent) const noexcept
BB_INLINE constexpr field sqr() const noexcept
constexpr std::pair< bool, field > sqrt() const noexcept
Compute square root of the field element.
BB_INLINE constexpr field from_montgomery_form() const noexcept
std::pair< T, T > msgpack_roundtrip(const T &object)