Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
bigfield.test.cpp
Go to the documentation of this file.
3
6
7#include "../bool/bool.hpp"
8#include "../byte_array/byte_array.hpp"
9#include "../field/field.hpp"
10#include "./bigfield.hpp"
18#include <gtest/gtest.h>
19#include <memory>
20#include <utility>
21
22using namespace bb;
23
24namespace {
26}
27
28enum struct InputType {
29 WITNESS,
31};
32
37
38// Helper to extract Builder and Params from bigfield<Builder, Params>
39template <typename T> struct extract_builder;
40template <typename T> struct extract_fq_params;
41
42template <template <typename, typename> class BigField, typename Builder, typename Params>
43struct extract_builder<BigField<Builder, Params>> {
44 using type = Builder;
45};
46
47template <template <typename, typename> class BigField, typename Builder, typename Params>
48struct extract_fq_params<BigField<Builder, Params>> {
49 using type = Params;
50};
51
52template <typename BigField> using builder_t = typename extract_builder<BigField>::type;
53template <typename BigField> using params_t = typename extract_fq_params<BigField>::type;
54
56template <typename BigField> class stdlib_bigfield : public testing::Test {
57
58 using Builder = builder_t<BigField>; // extract builder from BigField
59 using fr_ct = typename bb::stdlib::bn254<Builder>::ScalarField; // native circuit field
60 using fq_native = bb::field<params_t<BigField>>; // native bigfield type
61 using fq_ct = BigField; // non-native field (circuit type)
62 using witness_ct = stdlib::witness_t<Builder>; // circuit witness type
63 using bool_ct = stdlib::bool_t<Builder>; // circuit boolean type
64 using byte_array_ct = stdlib::byte_array<Builder>; // circuit byte array type
65
66 public:
68 {
69 auto builder = Builder();
70 fq_ct constant = fq_ct(1);
71 fq_ct var = fq_ct::create_from_u512_as_witness(&builder, 1);
72 fr_ct small_var = witness_ct(&builder, fr(1));
73 fq_ct mixed = fq_ct(1).add_to_lower_limb(small_var, 1);
74 fq_ct r;
75
76 r = mixed + mixed;
77 r = mixed - mixed;
78 r = mixed + var;
79 r = mixed + constant;
80 r = mixed - var;
81 r = mixed - constant;
82 r = var - mixed;
83
84 r = var * constant;
85 r = constant / var;
86 r = constant * constant;
87 r = constant / constant;
88
89 r = mixed * var;
90 r = mixed / var;
91 r = mixed * mixed;
92 r = mixed * constant;
93 bool result = CircuitChecker::check(builder);
94 EXPECT_EQ(result, true);
95 }
96
97 // The bug happens when we are applying the CRT formula to a*b < r, which can happen when using the division
98 // operator
100 {
101 auto builder = Builder();
102 uint256_t value(2);
103 fq_ct tval = fq_ct::create_from_u512_as_witness(&builder, value);
104 fq_ct tval1 = tval - tval;
105 fq_ct tval2 = tval1 / tval;
106 (void)tval2;
107 bool result = CircuitChecker::check(builder);
108 EXPECT_EQ(result, true);
109 }
110
111 static void test_bad_mul()
112 {
113 auto builder = Builder();
114 uint256_t value(2);
115 fq_ct tval = fq_ct::create_from_u512_as_witness(&builder, value);
116 fq_ct tval1 = tval - tval;
117 fq_ct tval2 = tval1 / tval;
118 (void)tval2;
119 bool result = CircuitChecker::check(builder);
120 EXPECT_EQ(result, true);
121 }
122
123 // Gets a random bigfield element that is a circuit-witness
125 {
127 if (reduce_input) {
128 elt_native = elt_native.reduce_once().reduce_once();
129 }
130 fr elt_native_lo = fr(uint256_t(elt_native).slice(0, fq_ct::NUM_LIMB_BITS * 2));
131 fr elt_native_hi = fr(uint256_t(elt_native).slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 4));
132 fq_ct elt_ct(witness_ct(builder, elt_native_lo), witness_ct(builder, elt_native_hi));
133 // UNset free witness tag so we don't have to unset it in every test
134 elt_ct.unset_free_witness_tag();
135 return std::make_pair(elt_native, elt_ct);
136 }
137
138 // Gets a random bigfield element that is a circuit-constant
140 {
142 if (reduce_input) {
143 elt_native = elt_native.reduce_once().reduce_once();
144 }
145 fq_ct elt_ct(builder, uint256_t(elt_native));
146 return std::make_pair(elt_native, elt_ct);
147 }
148
149 // Gets a random bigfield element that may be either circuit-witness or cirucit-constant
151 {
152 return (engine.get_random_uint8() & 1) == 1 ? get_random_witness(builder, reduce_input)
153 : get_random_constant(builder, reduce_input);
154 }
155
157 {
158 if (type == InputType::WITNESS) {
159 return get_random_witness(builder, reduce_input);
160 }
161 return get_random_constant(builder, reduce_input);
162 }
163
165 size_t num,
166 bool reduce_input = false)
167 {
168 std::vector<fq_native> elts(num);
169 std::vector<fq_ct> big_elts(num);
170 for (size_t i = 0; i < num; ++i) {
171 auto [elt, big_elt] = get_random_witness(builder, reduce_input);
172 elts[i] = elt;
173 big_elts[i] = big_elt;
174 }
175 return std::make_pair(elts, big_elts);
176 }
177
179 size_t num,
180 bool reduce_input = false)
181 {
182 std::vector<fq_native> elts(num);
183 std::vector<fq_ct> big_elts(num);
184 for (size_t i = 0; i < num; ++i) {
185 auto [elt, big_elt] = get_random_constant(builder, reduce_input);
186 elts[i] = elt;
187 big_elts[i] = big_elt;
188 }
189 return std::make_pair(elts, big_elts);
190 }
191
194 size_t num,
195 bool reduce_input = false)
196 {
197 std::vector<fq_native> elts(num);
198 std::vector<fq_ct> big_elts(num);
199 for (size_t i = 0; i < num; ++i) {
200 auto [elt, big_elt] = get_random_element(builder, type, reduce_input);
201 elts[i] = elt;
202 big_elts[i] = big_elt;
203 }
204 return std::make_pair(elts, big_elts);
205 }
206
208 {
209 auto builder = Builder();
210
211 // Test 1: Limb tag merging - getting tag merges from internal limbs
212 {
213 auto [a_native, a_ct] = get_random_witness(&builder);
214 a_ct.binary_basis_limbs[0].element.set_origin_tag(submitted_value_origin_tag);
215 a_ct.binary_basis_limbs[1].element.set_origin_tag(challenge_origin_tag);
216 a_ct.prime_basis_limb.set_origin_tag(next_challenge_tag);
217 EXPECT_EQ(a_ct.get_origin_tag(), first_second_third_merged_tag);
218 }
219
220 // Test 2: Setting tag propagates to all limbs
221 {
222 auto [a_native, a_ct] = get_random_witness(&builder);
223 a_ct.set_origin_tag(submitted_value_origin_tag);
224 EXPECT_EQ(a_ct.binary_basis_limbs[0].element.get_origin_tag(), submitted_value_origin_tag);
225 EXPECT_EQ(a_ct.binary_basis_limbs[1].element.get_origin_tag(), submitted_value_origin_tag);
226 EXPECT_EQ(a_ct.binary_basis_limbs[2].element.get_origin_tag(), submitted_value_origin_tag);
227 EXPECT_EQ(a_ct.binary_basis_limbs[3].element.get_origin_tag(), submitted_value_origin_tag);
228 EXPECT_EQ(a_ct.prime_basis_limb.get_origin_tag(), submitted_value_origin_tag);
229 }
230
231 // Setup for operation tests
232 auto [a_native, a] = get_random_witness(&builder);
233 auto [b_native, b] = get_random_witness(&builder);
234 auto [c_native, c] = get_random_witness(&builder);
235 a.set_origin_tag(submitted_value_origin_tag);
236 b.set_origin_tag(challenge_origin_tag);
237 c.set_origin_tag(next_challenge_tag);
238
239 // Binary operators merge tags
240 EXPECT_EQ((a + b).get_origin_tag(), first_two_merged_tag);
241 EXPECT_EQ((a - b).get_origin_tag(), first_two_merged_tag);
242 EXPECT_EQ((a * b).get_origin_tag(), first_two_merged_tag);
243
244 // Assignment operators merge tags
245 {
246 auto [tmp_native, tmp] = get_random_witness(&builder);
247 tmp.set_origin_tag(submitted_value_origin_tag);
248 auto [other_native, other] = get_random_witness(&builder);
249 other.set_origin_tag(challenge_origin_tag);
250 tmp += other;
251 EXPECT_EQ(tmp.get_origin_tag(), first_two_merged_tag);
252 }
253
254 // add_two merges tags
255 EXPECT_EQ(a.add_two(b, c).get_origin_tag(), first_second_third_merged_tag);
256
257 // sum merges tags
258 {
259 std::vector<fq_ct> to_sum;
260 auto [s1_native, s1] = get_random_witness(&builder);
261 auto [s2_native, s2] = get_random_witness(&builder);
262 s1.set_origin_tag(submitted_value_origin_tag);
263 s2.set_origin_tag(challenge_origin_tag);
264 to_sum.push_back(s1);
265 to_sum.push_back(s2);
266 EXPECT_EQ(fq_ct::sum(to_sum).get_origin_tag(), first_two_merged_tag);
267 }
268
269 // madd merges tags
270 EXPECT_EQ(a.madd(b, { c }).get_origin_tag(), first_second_third_merged_tag);
271
272 // sqradd merges tags
273 EXPECT_EQ(a.sqradd({ b }).get_origin_tag(), first_two_merged_tag);
274
275 // mult_madd merges tags
276 {
277 std::vector<fq_ct> left = { a };
278 std::vector<fq_ct> right = { b };
279 std::vector<fq_ct> to_add = { c };
280 EXPECT_EQ(fq_ct::mult_madd(left, right, to_add).get_origin_tag(), first_second_third_merged_tag);
281 }
282
283 // dual_madd merges tags
284 {
285 auto [d_native, d] = get_random_witness(&builder);
286 auto [e_native, e] = get_random_witness(&builder);
287 d.set_origin_tag(submitted_value_origin_tag);
288 e.set_origin_tag(challenge_origin_tag);
289 EXPECT_EQ(fq_ct::dual_madd(a, b, a, b, { c }).get_origin_tag(), first_second_third_merged_tag);
290 }
291
292 // div_without_denominator_check merges tags
293 EXPECT_EQ(a.div_without_denominator_check(b).get_origin_tag(), first_two_merged_tag);
294
295 // conditional_select merges tags
296 {
297 auto predicate = bool_ct(witness_ct(&builder, true));
298 predicate.set_origin_tag(next_challenge_tag);
299 EXPECT_EQ(a.conditional_select(b, predicate).get_origin_tag(), first_second_third_merged_tag);
300 }
301
302 // conditional_negate merges tags
303 {
304 auto predicate = bool_ct(witness_ct(&builder, false));
305 predicate.set_origin_tag(challenge_origin_tag);
306 EXPECT_EQ(a.conditional_negate(predicate).get_origin_tag(), first_two_merged_tag);
307 }
308
309 // self_reduce preserves tag
310 {
311 auto [tmp_native, tmp] = get_random_witness(&builder);
312 tmp.set_origin_tag(submitted_value_origin_tag);
313 tmp.self_reduce();
314 EXPECT_EQ(tmp.get_origin_tag(), submitted_value_origin_tag);
315 }
316
317 // assert_is_in_field preserves tag
318 {
319 auto [tmp_native, tmp] = get_random_witness(&builder, /*reduce=*/true);
320 tmp.set_origin_tag(submitted_value_origin_tag);
321 tmp.assert_is_in_field();
322 EXPECT_EQ(tmp.get_origin_tag(), submitted_value_origin_tag);
323 }
324
325 // byte_array construction propagates tag
326 {
328 std::vector<uint8_t> input_bytes(sizeof(fq_native));
329 fq_native::serialize_to_buffer(val, &input_bytes[0]);
330 byte_array_ct input_arr(&builder, input_bytes);
331 input_arr.set_origin_tag(submitted_value_origin_tag);
332 fq_ct from_bytes(input_arr);
333 EXPECT_EQ(from_bytes.get_origin_tag(), submitted_value_origin_tag);
334 }
335
336 // pow preserves tag (exponent is a constant, not a circuit variable)
337 EXPECT_EQ(a.pow(5).get_origin_tag(), submitted_value_origin_tag);
338
339#ifndef NDEBUG
340 // Instant death tag causes exception
341 {
342 auto [death_native, death] = get_random_witness(&builder);
343 death.set_origin_tag(instant_death_tag);
344 EXPECT_THROW(death + death, std::runtime_error);
345 }
346#endif
347 }
348
350 {
351 auto builder = Builder();
352 {
353 fr elt_native_lo = fr(uint256_t(fr::random_element()).slice(0, fq_ct::NUM_LIMB_BITS * 2)); // 136 bits
354 fr elt_native_hi = fr(uint256_t(fr::random_element()).slice(0, fq_ct::NUM_LIMB_BITS * 2)); // 136 bits
355 fq_ct elt_witness_ct =
356 fq_ct(witness_ct(&builder, elt_native_lo), witness_ct(&builder, elt_native_hi), true);
357 fq_ct elt_constant_ct = fq_ct(fr_ct(&builder, elt_native_lo), fr_ct(&builder, elt_native_hi), true);
358 }
359 {
360 fr elt_native_lo = fr(uint256_t(fr::random_element()).slice(0, fq_ct::NUM_LIMB_BITS * 2)); // 136 bits
361 fr elt_native_hi = fr(uint256_t(fr::random_element()).slice(0, fq_ct::NUM_LIMB_BITS * 2 - 3)); // 133 bits
362 fq_ct elt_witness_ct = fq_ct(witness_ct(&builder, elt_native_lo),
363 witness_ct(&builder, elt_native_hi),
364 false, // can_overflow must be false as max_bitlength is provided
365 4 * fq_ct::NUM_LIMB_BITS - 3);
366 fq_ct elt_constant_ct = fq_ct(fr_ct(&builder, elt_native_lo),
367 fr_ct(&builder, elt_native_hi),
368 false, // can_overflow must be false as max_bitlength is provided
369 4 * fq_ct::NUM_LIMB_BITS - 3);
370 }
371 bool result = CircuitChecker::check(builder);
372 EXPECT_EQ(result, true);
373 }
374
376 {
377 auto builder = Builder();
378 fr limb_1_native = fr(uint256_t(fr::random_element()).slice(0, fq_ct::NUM_LIMB_BITS + 10)); // 78 bits
379 fr limb_2_native = fr(uint256_t(fr::random_element()).slice(0, fq_ct::NUM_LIMB_BITS + 10)); // 78 bits
380 fr limb_3_native = fr(uint256_t(fr::random_element()).slice(0, fq_ct::NUM_LIMB_BITS + 10)); // 78 bits
381 fr limb_4_native = fr(uint256_t(fr::random_element()).slice(0, fq_ct::NUM_LIMB_BITS + 12)); // 80 bits
382
383 fr_ct limb_1_ct = fr_ct(witness_ct(&builder, limb_1_native));
384 fr_ct limb_2_ct = fr_ct(witness_ct(&builder, limb_2_native));
385 fr_ct limb_3_ct = fr_ct(witness_ct(&builder, limb_3_native));
386 fr_ct limb_4_ct = fr_ct(witness_ct(&builder, limb_4_native));
387
388 // This does not add any range constraints on the limbs, so virtually any limb values are valid.
389 // It does however correctly compute the prime basis limb (from the supplied limbs).
390 fq_ct result = fq_ct::unsafe_construct_from_limbs(limb_1_ct, limb_2_ct, limb_3_ct, limb_4_ct);
391
392 fr expected_prime_limb = limb_1_native;
393 expected_prime_limb += (limb_2_native * fq_ct::shift_1);
394 expected_prime_limb += (limb_3_native * fq_ct::shift_2);
395 expected_prime_limb += (limb_4_native * fq_ct::shift_3);
396 EXPECT_EQ(expected_prime_limb, result.prime_basis_limb.get_value());
397
398 // The other constructor takes in the prime limb as well (without any checks).
399 fq_ct result_1 = fq_ct::unsafe_construct_from_limbs(
400 limb_1_ct, limb_2_ct, limb_3_ct, limb_4_ct, witness_ct(&builder, fr::random_element()));
401 EXPECT_EQ(result.binary_basis_limbs[0].element.get_value(), result_1.binary_basis_limbs[0].element.get_value());
402
403 bool result_check = CircuitChecker::check(builder);
404 EXPECT_EQ(result_check, true);
405 }
406
408 {
409 auto builder = Builder();
410 fr limb_1_native = fr(uint256_t(fr::random_element()).slice(0, fq_ct::NUM_LIMB_BITS)); // 68 bits
411 fr limb_2_native = fr(uint256_t(fr::random_element()).slice(0, fq_ct::NUM_LIMB_BITS)); // 68 bits
412 fr limb_3_native = fr(uint256_t(fr::random_element()).slice(0, fq_ct::NUM_LIMB_BITS)); // 68 bits
413 fr limb_4_native = fr(uint256_t(fr::random_element()).slice(0, fq_ct::NUM_LAST_LIMB_BITS)); // |p|-3*68 bits
414
415 fr_ct limb_1_ct = fr_ct(witness_ct(&builder, limb_1_native));
416 fr_ct limb_2_ct = fr_ct(witness_ct(&builder, limb_2_native));
417 fr_ct limb_3_ct = fr_ct(witness_ct(&builder, limb_3_native));
418 fr_ct limb_4_ct = fr_ct(witness_ct(&builder, limb_4_native));
419
420 // This does add range constraints on the limbs, so the limbs must be in range.
421 // It also correctly computes the prime basis limb (from the supplied limbs).
422 fq_ct result = fq_ct::construct_from_limbs(limb_1_ct, limb_2_ct, limb_3_ct, limb_4_ct);
423
424 fr expected_prime_limb = limb_1_native;
425 expected_prime_limb += (limb_2_native * fq_ct::shift_1);
426 expected_prime_limb += (limb_3_native * fq_ct::shift_2);
427 expected_prime_limb += (limb_4_native * fq_ct::shift_3);
428 EXPECT_EQ(expected_prime_limb, result.prime_basis_limb.get_value());
429
430 // All four limbs as 68-bit range constrained (fourth limb is set equal to limb_3)
431 fq_ct result_1 = fq_ct::construct_from_limbs(limb_1_ct, limb_2_ct, limb_3_ct, limb_3_ct, /*can_overflow=*/true);
432 EXPECT_EQ(result.binary_basis_limbs[0].element.get_value(), result_1.binary_basis_limbs[0].element.get_value());
433
434 bool result_check = CircuitChecker::check(builder);
435 EXPECT_EQ(result_check, true);
436 }
437
439 {
440 auto builder = Builder();
441 fr limb_1_native = fr(uint256_t(fr::random_element()).slice(0, fq_ct::NUM_LIMB_BITS)); // 68 bits
442 fr limb_2_native = fr(uint256_t(fr::random_element()).slice(0, fq_ct::NUM_LIMB_BITS)); // 68 bits
443 fr limb_3_native = fr(uint256_t(fr::random_element()).slice(0, fq_ct::NUM_LIMB_BITS)); // 68 bits
444 fr limb_4_native = fr(uint256_t(fr::random_element()).slice(0, fq_ct::NUM_LAST_LIMB_BITS)); // |p|-3*68 bits
445
446 // Make limb_1 out of range
447 limb_1_native = uint256_t(limb_1_native) + (uint256_t(1) << fq_ct::NUM_LIMB_BITS);
448
449 fr_ct limb_1_ct = fr_ct(witness_ct(&builder, limb_1_native));
450 fr_ct limb_2_ct = fr_ct(witness_ct(&builder, limb_2_native));
451 fr_ct limb_3_ct = fr_ct(witness_ct(&builder, limb_3_native));
452 fr_ct limb_4_ct = fr_ct(witness_ct(&builder, limb_4_native));
453
454 // This will fail because limb_1 is out of range
455 fq_ct result = fq_ct::construct_from_limbs(limb_1_ct, limb_2_ct, limb_3_ct, limb_4_ct);
456 fr expected_prime_limb = limb_1_native;
457 expected_prime_limb += (limb_2_native * fq_ct::shift_1);
458 expected_prime_limb += (limb_3_native * fq_ct::shift_2);
459 expected_prime_limb += (limb_4_native * fq_ct::shift_3);
460 EXPECT_EQ(expected_prime_limb, result.prime_basis_limb.get_value());
461
462 bool result_check = CircuitChecker::check(builder);
463 EXPECT_EQ(result_check, false);
464 EXPECT_EQ(builder.err(), "bigfield::construct_from_limbs: limb 0 or 1 too large: lo limb.");
465 }
466
467 static void test_add_two(InputType a_type, InputType b_type, InputType c_type)
468 {
469 auto builder = Builder();
470 size_t num_repetitions = 10;
471 for (size_t i = 0; i < num_repetitions; ++i) {
472 auto [a_native, a_ct] = get_random_element(&builder, a_type); // fq, fq_ct
473 auto [b_native, b_ct] = get_random_element(&builder, b_type); // fq, fq_ct
474 auto [c_native, c_ct] = get_random_element(&builder, c_type); // fq, fq_ct
475
476 fq_ct d_ct;
477 if (i == num_repetitions - 1) {
479 d_ct = a_ct.add_two(b_ct, c_ct);
480 BENCH_GATE_COUNT_END(builder, "ADD_TWO");
481 } else {
482 d_ct = a_ct.add_two(b_ct, c_ct);
483 }
484 d_ct.self_reduce();
485
486 fq_native expected = (a_native + b_native + c_native).reduce_once().reduce_once();
487 expected = expected.from_montgomery_form_reduced();
488 uint512_t result = d_ct.get_value();
489
490 EXPECT_EQ(result.lo.data[0], expected.data[0]);
491 EXPECT_EQ(result.lo.data[1], expected.data[1]);
492 EXPECT_EQ(result.lo.data[2], expected.data[2]);
493 EXPECT_EQ(result.lo.data[3], expected.data[3]);
494 EXPECT_EQ(result.hi.data[0], 0ULL);
495 EXPECT_EQ(result.hi.data[1], 0ULL);
496 EXPECT_EQ(result.hi.data[2], 0ULL);
497 EXPECT_EQ(result.hi.data[3], 0ULL);
498 }
499 bool result = CircuitChecker::check(builder);
500 EXPECT_EQ(result, true);
501 }
502
503 static void test_sum(InputType a_type, bool mixed_inputs = false)
504 {
505 auto builder = Builder();
506 std::vector<size_t> num_elements_to_sum = { 1, 2, 10, 20 };
507
508 for (size_t num_elements : num_elements_to_sum) {
509 auto [a_native, a_ct] = get_random_elements(&builder, a_type, num_elements); // fq, fq_ct
510 auto [b_native, b_ct] = get_random_elements(&builder, !a_type, num_elements); // fq, fq_ct
511
512 std::vector<fq_ct> to_sum;
513 for (size_t j = 0; j < num_elements; ++j) {
514 to_sum.push_back(a_ct[j]);
515
516 if (mixed_inputs) {
517 to_sum.push_back(b_ct[j]);
518 }
519 }
520
521 fq_ct c_ct;
522 if (num_elements == 20) {
524 c_ct = fq_ct::sum(to_sum);
526 } else {
527 c_ct = fq_ct::sum(to_sum);
528 }
529
530 // Need to self-reduce as we are summing potentially many elements
531 c_ct.self_reduce();
532
533 fq_native expected = fq_native::zero();
534 for (size_t j = 0; j < num_elements; ++j) {
535 expected += a_native[j];
536
537 if (mixed_inputs) {
538 expected += b_native[j];
539 }
540 }
541 expected = expected.from_montgomery_form_reduced();
542 uint512_t result = c_ct.get_value();
543
544 EXPECT_EQ(result.lo.data[0], expected.data[0]);
545 EXPECT_EQ(result.lo.data[1], expected.data[1]);
546 EXPECT_EQ(result.lo.data[2], expected.data[2]);
547 EXPECT_EQ(result.lo.data[3], expected.data[3]);
548 EXPECT_EQ(result.hi.data[0], 0ULL);
549 EXPECT_EQ(result.hi.data[1], 0ULL);
550 EXPECT_EQ(result.hi.data[2], 0ULL);
551 EXPECT_EQ(result.hi.data[3], 0ULL);
552 }
553
554 bool result = CircuitChecker::check(builder);
555 EXPECT_EQ(result, true);
556 }
557
558 // Generic binary operator test function
559 template <typename CircuitOpFunc, typename NativeOpFunc>
561 InputType b_type,
562 CircuitOpFunc circuit_op,
563 NativeOpFunc native_op,
564 const char* op_name,
565 size_t num_repetitions = 10,
566 bool need_reduced_inputs = false,
567 bool need_reduction_after = false)
568 {
569 auto builder = Builder();
570 for (size_t i = 0; i < num_repetitions; ++i) {
571 auto [a_native, a_ct] = get_random_element(&builder, a_type, need_reduced_inputs); // fq_native, fq_ct
572 auto [b_native, b_ct] = get_random_element(&builder, b_type, need_reduced_inputs); // fq_native, fq_ct
573
574 fq_ct c_ct;
575 if (i == num_repetitions - 1) {
576 std::string bench_name = std::string(op_name);
577 BENCH_GATE_COUNT_START(builder, bench_name.c_str());
578 c_ct = circuit_op(a_ct, b_ct);
579 BENCH_GATE_COUNT_END(builder, bench_name.c_str());
580 } else {
581 c_ct = circuit_op(a_ct, b_ct);
582 }
583
584 // Some operations (add, sub, div) may need a self-reduction to get back into the field range
585 if (need_reduction_after) {
586 c_ct.self_reduce();
587 }
588
589 fq_native expected = native_op(a_native, b_native);
590 if (need_reduction_after) {
591 expected = expected.reduce_once().reduce_once();
592 }
593 expected = expected.from_montgomery_form_reduced();
594 uint512_t result = c_ct.get_value();
595
596 EXPECT_EQ(result.lo.data[0], expected.data[0]);
597 EXPECT_EQ(result.lo.data[1], expected.data[1]);
598 EXPECT_EQ(result.lo.data[2], expected.data[2]);
599 EXPECT_EQ(result.lo.data[3], expected.data[3]);
600 EXPECT_EQ(result.hi.data[0], 0ULL);
601 EXPECT_EQ(result.hi.data[1], 0ULL);
602 EXPECT_EQ(result.hi.data[2], 0ULL);
603 EXPECT_EQ(result.hi.data[3], 0ULL);
604 }
605 bool result = CircuitChecker::check(builder);
606 EXPECT_EQ(result, true);
607 }
608
609#define BINARY_OP_TEST(op_name, bench_name, op_symbol, repetitions, reduced_inputs, reduction_after) \
610 static void test_##op_name(InputType a_type, InputType b_type) \
611 { \
612 test_binary_operator_generic( \
613 a_type, \
614 b_type, \
615 [](const fq_ct& a, const fq_ct& b) { return a op_symbol b; }, \
616 [](const fq_native& a, const fq_native& b) { return a op_symbol b; }, \
617 #bench_name, \
618 repetitions, \
619 reduced_inputs, \
620 reduction_after); \
621 }
622
623 BINARY_OP_TEST(mul, MUL, *, 10, false, false)
624 BINARY_OP_TEST(add, ADD, +, 10, false, true)
625 BINARY_OP_TEST(sub, SUB, -, 10, false, true)
626 BINARY_OP_TEST(div, DIV, /, 10, true, true)
627
628 static void test_negate(InputType a_type)
629 {
631 a_type,
632 InputType::CONSTANT, // b is unused
633 [](const fq_ct& a, const fq_ct&) { return -a; },
634 [](const fq_native& a, const fq_native&) { return -a; },
635 "NEGATE",
636 10,
637 false, // need_reduced_inputs
638 true // need_reduction_after
639 );
640 }
641
642 static void test_sqr(InputType a_type)
643 {
645 a_type,
646 InputType::CONSTANT, // b is unused
647 [](const fq_ct& a, const fq_ct&) { return a.sqr(); },
648 [](const fq_native& a, const fq_native&) { return a.sqr(); },
649 "SQR",
650 10,
651 false,
652 false);
653 }
654
655 // Generic assignment operator test function
656 template <typename CircuitOpFunc, typename NativeOpFunc>
658 InputType b_type,
659 CircuitOpFunc circuit_op,
660 NativeOpFunc native_op,
661 const char* op_name,
662 size_t num_repetitions = 4,
663 bool need_reduced_inputs = false,
664 bool need_reduction_after = false)
665 {
666 auto builder = Builder();
667 for (size_t i = 0; i < num_repetitions; ++i) {
668 auto [a_native, a_ct] = get_random_element(&builder, a_type, need_reduced_inputs); // fq, fq_ct
669 auto [b_native, b_ct] = get_random_element(&builder, b_type, need_reduced_inputs); // fq, fq_ct
670
671 if (i == num_repetitions - 1) {
672 std::string bench_name = std::string(op_name);
673 BENCH_GATE_COUNT_START(builder, bench_name.c_str());
674 circuit_op(a_ct, b_ct);
675 BENCH_GATE_COUNT_END(builder, bench_name.c_str());
676 } else {
677 circuit_op(a_ct, b_ct);
678 }
679
680 // Need to self-reduce as assignment operators do not automatically reduce
681 a_ct.self_reduce();
682
683 fq_native expected = native_op(a_native, b_native);
684 if (need_reduction_after) {
685 expected = expected.reduce_once().reduce_once();
686 }
687 expected = expected.from_montgomery_form_reduced();
688 uint512_t result = a_ct.get_value();
689
690 EXPECT_EQ(result.lo.data[0], expected.data[0]);
691 EXPECT_EQ(result.lo.data[1], expected.data[1]);
692 EXPECT_EQ(result.lo.data[2], expected.data[2]);
693 EXPECT_EQ(result.lo.data[3], expected.data[3]);
694 EXPECT_EQ(result.hi.data[0], 0ULL);
695 EXPECT_EQ(result.hi.data[1], 0ULL);
696 EXPECT_EQ(result.hi.data[2], 0ULL);
697 EXPECT_EQ(result.hi.data[3], 0ULL);
698 }
699 bool result = CircuitChecker::check(builder);
700 EXPECT_EQ(result, true);
701 }
702
703#define ASSIGNMENT_OP_TEST(op_name, bench_name, op_symbol, repetitions, reduced_inputs, reduction_after) \
704 static void test_##op_name(InputType a_type, InputType b_type) \
705 { \
706 test_assign_operator_generic( \
707 a_type, \
708 b_type, \
709 [](fq_ct& a, const fq_ct& b) { a op_symbol## = b; }, \
710 [](const fq_native& a, const fq_native& b) { return a op_symbol b; }, \
711 #bench_name, \
712 repetitions, \
713 reduced_inputs, \
714 reduction_after); \
715 }
716
717 // Generate assignment operator tests using the macro
718 ASSIGNMENT_OP_TEST(mul_assign, MUL_ASSIGN, *, 10, false, false)
719 ASSIGNMENT_OP_TEST(add_assign, ADD_ASSIGN, +, 10, false, true)
720 ASSIGNMENT_OP_TEST(sub_assign, SUB_ASSIGN, -, 10, false, true)
721 ASSIGNMENT_OP_TEST(div_assign, DIV_ASSIGN, /, 10, true, true)
722
723 static void test_madd(InputType a_type, InputType b_type, InputType c_type)
724 {
725 auto builder = Builder();
726 size_t num_repetitions = 4;
727 for (size_t i = 0; i < num_repetitions; ++i) {
728 auto [a_native, a_ct] = get_random_element(&builder, a_type); // fq_native, fq_ct
729 auto [b_native, b_ct] = get_random_element(&builder, b_type); // fq_native, fq_ct
730 auto [c_native, c_ct] = get_random_element(&builder, c_type); // fq_native, fq_ct
731
732 fq_ct d_ct;
733 if (i == num_repetitions - 1) {
735 d_ct = a_ct.madd(b_ct, { c_ct });
737 } else {
738 d_ct = a_ct.madd(b_ct, { c_ct });
739 }
740
741 fq_native expected = (a_native * b_native) + c_native;
742 expected = expected.from_montgomery_form_reduced();
743 uint512_t result = d_ct.get_value();
744
745 EXPECT_EQ(result.lo.data[0], expected.data[0]);
746 EXPECT_EQ(result.lo.data[1], expected.data[1]);
747 EXPECT_EQ(result.lo.data[2], expected.data[2]);
748 EXPECT_EQ(result.lo.data[3], expected.data[3]);
749 EXPECT_EQ(result.hi.data[0], 0ULL);
750 EXPECT_EQ(result.hi.data[1], 0ULL);
751 EXPECT_EQ(result.hi.data[2], 0ULL);
752 EXPECT_EQ(result.hi.data[3], 0ULL);
753 }
754 bool result = CircuitChecker::check(builder);
755 EXPECT_EQ(result, true);
756 }
757
758 static void test_sqradd(InputType a_type, InputType b_type)
759 {
760 auto builder = Builder();
761 size_t num_repetitions = 4;
762 for (size_t i = 0; i < num_repetitions; ++i) {
763 auto [a_native, a_ct] = get_random_element(&builder, a_type); // fq_native, fq_ct
764 auto [b_native, b_ct] = get_random_element(&builder, b_type); // fq_native, fq_ct
765
766 fq_ct c_ct;
767 if (i == num_repetitions - 1) {
769 c_ct = a_ct.sqradd({ b_ct });
770 BENCH_GATE_COUNT_END(builder, "SQRADD");
771 } else {
772 c_ct = a_ct.sqradd({ b_ct });
773 }
774 c_ct.self_reduce();
775
776 fq_native expected = (a_native.sqr()) + b_native;
777 expected = expected.from_montgomery_form_reduced();
778 uint512_t result = c_ct.get_value();
779
780 EXPECT_EQ(result.lo.data[0], expected.data[0]);
781 EXPECT_EQ(result.lo.data[1], expected.data[1]);
782 EXPECT_EQ(result.lo.data[2], expected.data[2]);
783 EXPECT_EQ(result.lo.data[3], expected.data[3]);
784 EXPECT_EQ(result.hi.data[0], 0ULL);
785 EXPECT_EQ(result.hi.data[1], 0ULL);
786 EXPECT_EQ(result.hi.data[2], 0ULL);
787 EXPECT_EQ(result.hi.data[3], 0ULL);
788 }
789 bool result = CircuitChecker::check(builder);
790 EXPECT_EQ(result, true);
791 }
792
793 static void test_mult_madd(InputType left_type, InputType right_type, InputType to_add_type, bool edge_case = false)
794 {
795 auto builder = Builder();
796 size_t num_repetitions = 1;
797 const size_t number_of_madds = 16;
798 for (size_t i = 0; i < num_repetitions; ++i) {
799 // Get random witnesses for the multiplicands and the to_add values
800 auto [mul_left_native, mul_left_ct] =
801 get_random_elements(&builder, left_type, number_of_madds); // std::vector<fq_native>, std::vector<fq_ct>
802 auto [mul_right_native, mul_right_ct] = get_random_elements(
803 &builder, right_type, number_of_madds); // std::vector<fq_native>, std::vector<fq_ct>
804 auto [to_add_native, to_add_ct] = get_random_elements(
805 &builder, to_add_type, number_of_madds); // std::vector<fq_native>, std::vector<fq_ct>
806
807 if (edge_case) {
808 // Replace last element in the multiplicands and summand with element of the opposite type
809 // This is to test the edge case where we have a mix of witness and constant types
810 auto [extra_left_native, extra_left_ct] = get_random_element(&builder, !left_type); // fq, fq_ct
811 auto [extra_right_native, extra_right_ct] = get_random_element(&builder, !right_type); // fq, fq_ct
812 auto [extra_to_add_native, extra_to_add_ct] = get_random_element(&builder, !to_add_type); // fq, fq_ct
813 mul_right_native[number_of_madds - 1] = extra_right_native;
814 mul_left_native[number_of_madds - 1] = extra_left_native;
815 to_add_native[number_of_madds - 1] = extra_to_add_native;
816 mul_left_ct[number_of_madds - 1] = extra_left_ct;
817 mul_right_ct[number_of_madds - 1] = extra_right_ct;
818 to_add_ct[number_of_madds - 1] = extra_to_add_ct;
819 }
820
821 fq_ct f_ct;
822 if (i == num_repetitions - 1) {
823 BENCH_GATE_COUNT_START(builder, "MULT_MADD");
824 f_ct = fq_ct::mult_madd(mul_left_ct, mul_right_ct, to_add_ct);
825 BENCH_GATE_COUNT_END(builder, "MULT_MADD");
826 } else {
827 f_ct = fq_ct::mult_madd(mul_left_ct, mul_right_ct, to_add_ct);
828 }
829
830 // Compute expected value
831 fq_native expected(0);
832 for (size_t j = 0; j < number_of_madds; j++) {
833 expected += mul_left_native[j] * mul_right_native[j];
834 expected += to_add_native[j];
835 }
836 expected = expected.from_montgomery_form_reduced();
837 uint512_t result = f_ct.get_value();
838
839 EXPECT_EQ(result.lo.data[0], expected.data[0]);
840 EXPECT_EQ(result.lo.data[1], expected.data[1]);
841 EXPECT_EQ(result.lo.data[2], expected.data[2]);
842 EXPECT_EQ(result.lo.data[3], expected.data[3]);
843 EXPECT_EQ(result.hi.data[0], 0ULL);
844 EXPECT_EQ(result.hi.data[1], 0ULL);
845 EXPECT_EQ(result.hi.data[2], 0ULL);
846 EXPECT_EQ(result.hi.data[3], 0ULL);
847 }
848 if (builder.failed()) {
849 info("Builder failed with error: ", builder.err());
850 };
851 bool result = CircuitChecker::check(builder);
852 EXPECT_EQ(result, true);
853 }
854
855 static void test_dual_madd()
856 {
857 auto builder = Builder();
858 size_t num_repetitions = 1;
859 for (size_t i = 0; i < num_repetitions; ++i) {
860 auto [a_native, a_ct] = get_random_witness(&builder); // fq_native, fq_ct
861 auto [b_native, b_ct] = get_random_witness(&builder); // fq_native, fq_ct
862 auto [c_native, c_ct] = get_random_witness(&builder); // fq_native, fq_ct
863 auto [d_native, d_ct] = get_random_witness(&builder); // fq_native, fq_ct
864 auto [e_native, e_ct] = get_random_witness(&builder); // fq_native, fq_ct
865
866 fq_ct f_ct;
867 if (i == num_repetitions - 1) {
868 BENCH_GATE_COUNT_START(builder, "DUAL_MADD");
869 f_ct = fq_ct::dual_madd(a_ct, b_ct, c_ct, d_ct, { e_ct });
870 BENCH_GATE_COUNT_END(builder, "DUAL_MADD");
871 } else {
872 f_ct = fq_ct::dual_madd(a_ct, b_ct, c_ct, d_ct, { e_ct });
873 }
874
875 fq_native expected = (a_native * b_native) + (c_native * d_native) + e_native;
876 expected = expected.from_montgomery_form_reduced();
877 uint512_t result = f_ct.get_value();
878
879 EXPECT_EQ(result.lo.data[0], expected.data[0]);
880 EXPECT_EQ(result.lo.data[1], expected.data[1]);
881 EXPECT_EQ(result.lo.data[2], expected.data[2]);
882 EXPECT_EQ(result.lo.data[3], expected.data[3]);
883 EXPECT_EQ(result.hi.data[0], 0ULL);
884 EXPECT_EQ(result.hi.data[1], 0ULL);
885 EXPECT_EQ(result.hi.data[2], 0ULL);
886 EXPECT_EQ(result.hi.data[3], 0ULL);
887 }
888 if (builder.failed()) {
889 info("Builder failed with error: ", builder.err());
890 };
891 bool result = CircuitChecker::check(builder);
892 EXPECT_EQ(result, true);
893 }
894
896 {
897 auto builder = Builder();
898 size_t num_repetitions = 10;
899 for (size_t i = 0; i < num_repetitions; ++i) {
900 // We need reduced inputs for division.
901 auto [a_native, a_ct] = get_random_element(&builder, a_type, true); // reduced fq_native, fq_ct
902 auto [b_native, b_ct] = get_random_element(&builder, b_type, true); // reduced fq_native, fq_ct
903
904 fq_ct c_ct;
905 if (i == num_repetitions - 1) {
906 BENCH_GATE_COUNT_START(builder, "DIV_DENOM_NO_CHECK");
907 c_ct = a_ct.div_without_denominator_check(b_ct);
908 BENCH_GATE_COUNT_END(builder, "DIV_DENOM_NO_CHECK");
909 } else {
910 c_ct = a_ct.div_without_denominator_check(b_ct);
911 }
912
913 fq_native expected = (a_native / b_native);
914 expected = expected.reduce_once().reduce_once();
915 expected = expected.from_montgomery_form_reduced();
916 uint512_t result = c_ct.get_value();
917
918 EXPECT_EQ(result.lo.data[0], expected.data[0]);
919 EXPECT_EQ(result.lo.data[1], expected.data[1]);
920 EXPECT_EQ(result.lo.data[2], expected.data[2]);
921 EXPECT_EQ(result.lo.data[3], expected.data[3]);
922 EXPECT_EQ(result.hi.data[0], 0ULL);
923 EXPECT_EQ(result.hi.data[1], 0ULL);
924 EXPECT_EQ(result.hi.data[2], 0ULL);
925 EXPECT_EQ(result.hi.data[3], 0ULL);
926 }
927 bool result = CircuitChecker::check(builder);
928 EXPECT_EQ(result, true);
929 }
930
931 static void test_add_and_div()
932 {
933 auto builder = Builder();
934 size_t num_repetitions = 1;
935 for (size_t i = 0; i < num_repetitions; ++i) {
936
937 auto [a_native, a_ct] = get_random_witness(&builder); // fq_native, fq_ct
938 auto [b_native, b_ct] = get_random_witness(&builder); // fq_native, fq_ct
939 auto [c_native, c_ct] = get_random_witness(&builder); // fq_native, fq_ct
940 auto [d_native, d_ct] = get_random_witness(&builder); // fq_native, fq_ct
941
942 fq_ct e = (a_ct + b_ct) / (c_ct + d_ct);
943
944 fq_native expected = (a_native + b_native) / (c_native + d_native);
945 expected = expected.reduce_once().reduce_once();
946 expected = expected.from_montgomery_form_reduced();
947 uint512_t result = e.get_value();
948
949 EXPECT_EQ(result.lo.data[0], expected.data[0]);
950 EXPECT_EQ(result.lo.data[1], expected.data[1]);
951 EXPECT_EQ(result.lo.data[2], expected.data[2]);
952 EXPECT_EQ(result.lo.data[3], expected.data[3]);
953 EXPECT_EQ(result.hi.data[0], 0ULL);
954 EXPECT_EQ(result.hi.data[1], 0ULL);
955 EXPECT_EQ(result.hi.data[2], 0ULL);
956 EXPECT_EQ(result.hi.data[3], 0ULL);
957 }
958 bool result = CircuitChecker::check(builder);
959 EXPECT_EQ(result, true);
960 }
961
962 static void test_add_and_mul(InputType summand_type)
963 {
964 auto builder = Builder();
965 size_t num_repetitions = 10;
966 for (size_t i = 0; i < num_repetitions; ++i) {
967
968 auto [a_native, a_ct] = get_random_witness(&builder); // fq_native, fq_ct
969 auto [b_native, b_ct] = get_random_element(&builder, summand_type); // fq_native, fq_ct
970 auto [c_native, c_ct] = get_random_witness(&builder); // fq_native, fq_ct
971 auto [d_native, d_ct] = get_random_element(&builder, summand_type); // fq_native, fq_ct
972
973 fq_ct e = (a_ct + b_ct) * (c_ct + d_ct);
974
975 fq_native expected = (a_native + b_native) * (c_native + d_native);
976 expected = expected.from_montgomery_form_reduced();
977 uint512_t result = e.get_value();
978
979 EXPECT_EQ(result.lo.data[0], expected.data[0]);
980 EXPECT_EQ(result.lo.data[1], expected.data[1]);
981 EXPECT_EQ(result.lo.data[2], expected.data[2]);
982 EXPECT_EQ(result.lo.data[3], expected.data[3]);
983 EXPECT_EQ(result.hi.data[0], 0ULL);
984 EXPECT_EQ(result.hi.data[1], 0ULL);
985 EXPECT_EQ(result.hi.data[2], 0ULL);
986 EXPECT_EQ(result.hi.data[3], 0ULL);
987 }
988 bool result = CircuitChecker::check(builder);
989 EXPECT_EQ(result, true);
990 }
991
992 static void test_sub_and_mul(InputType subtrahend_type)
993 {
994 auto builder = Builder();
995 size_t num_repetitions = 10;
996 for (size_t i = 0; i < num_repetitions; ++i) {
997 auto [a_native, a_ct] = get_random_witness(&builder); // fq_native, fq_ct
998 auto [b_native, b_ct] = get_random_element(&builder, subtrahend_type); // fq_native, fq_ct
999 auto [c_native, c_ct] = get_random_witness(&builder); // fq_native, fq_ct
1000 auto [d_native, d_ct] = get_random_element(&builder, subtrahend_type); // fq_native, fq_ct
1001
1002 fq_ct e = (a_ct - b_ct) * (c_ct - d_ct);
1003
1004 fq_native expected = (a_native - b_native) * (c_native - d_native);
1005
1006 expected = expected.from_montgomery_form_reduced();
1007 uint512_t result = e.get_value();
1008
1009 EXPECT_EQ(result.lo.data[0], expected.data[0]);
1010 EXPECT_EQ(result.lo.data[1], expected.data[1]);
1011 EXPECT_EQ(result.lo.data[2], expected.data[2]);
1012 EXPECT_EQ(result.lo.data[3], expected.data[3]);
1013 EXPECT_EQ(result.hi.data[0], 0ULL);
1014 EXPECT_EQ(result.hi.data[1], 0ULL);
1015 EXPECT_EQ(result.hi.data[2], 0ULL);
1016 EXPECT_EQ(result.hi.data[3], 0ULL);
1017 }
1018 bool result = CircuitChecker::check(builder);
1019 EXPECT_EQ(result, true);
1020 }
1021
1022 static void test_msub_div(InputType multiplicand_type, InputType to_sub_type, InputType divisor_type)
1023 {
1024 size_t num_repetitions = 8;
1025 for (size_t i = 0; i < num_repetitions; ++i) {
1026 auto builder = Builder();
1027 auto [mul_l, mul_l_ct] = get_random_element(&builder, multiplicand_type);
1028 auto [mul_r1, mul_r1_ct] = get_random_element(&builder, multiplicand_type);
1029 auto [mul_r2, mul_r2_ct] = get_random_element(&builder, multiplicand_type);
1030 auto [divisor1, divisor1_ct] = get_random_element(&builder, divisor_type);
1031 auto [divisor2, divisor2_ct] = get_random_element(&builder, divisor_type);
1032 auto [to_sub1, to_sub1_ct] = get_random_element(&builder, to_sub_type);
1033 auto [to_sub2, to_sub2_ct] = get_random_element(&builder, to_sub_type);
1034
1035 fq_ct result_ct;
1036 if (i == num_repetitions - 1) {
1037 BENCH_GATE_COUNT_START(builder, "MSUB_DIV");
1038 result_ct = fq_ct::msub_div(
1039 { mul_l_ct }, { mul_r1_ct - mul_r2_ct }, divisor1_ct - divisor2_ct, { to_sub1_ct, to_sub2_ct });
1040 BENCH_GATE_COUNT_END(builder, "MSUB_DIV");
1041 } else {
1042 result_ct = fq_ct::msub_div(
1043 { mul_l_ct }, { mul_r1_ct - mul_r2_ct }, divisor1_ct - divisor2_ct, { to_sub1_ct, to_sub2_ct });
1044 }
1045
1046 fq_native expected = (-(mul_l * (mul_r1 - mul_r2) + to_sub1 + to_sub2)) / (divisor1 - divisor2);
1047 EXPECT_EQ(result_ct.get_value().lo, uint256_t(expected));
1048 EXPECT_EQ(result_ct.get_value().hi, uint256_t(0));
1049
1050 bool result = CircuitChecker::check(builder);
1051 EXPECT_EQ(result, true);
1052 }
1053 }
1054
1055 static void test_conditional_assign(InputType a_type, InputType b_type, InputType predicate_type)
1056 {
1057 auto builder = Builder();
1058 size_t num_repetitions = 1;
1059 for (size_t i = 0; i < num_repetitions; ++i) {
1060
1061 auto [a_native, a_ct] = get_random_element(&builder, a_type); // fq_native, fq_ct
1062 auto [b_native, b_ct] = get_random_element(&builder, b_type); // fq_native, fq_ct
1063
1064 bool_ct predicate_a;
1065 if (predicate_type == InputType::WITNESS) {
1066 predicate_a = bool_ct(witness_ct(&builder, true));
1067 } else {
1068 predicate_a = bool_ct(&builder, true);
1069 }
1070
1071 fq_ct c = fq_ct::conditional_assign(predicate_a, a_ct, b_ct);
1072 fq_ct d = fq_ct::conditional_assign(!predicate_a, a_ct, b_ct);
1073
1074 fq_ct e = c + d;
1075 e.self_reduce();
1076 uint512_t c_out = c.get_value();
1077 uint512_t d_out = d.get_value();
1078 uint512_t e_out = e.get_value();
1079
1080 fq_native result_c(c_out.lo);
1081 fq_native result_d(d_out.lo);
1082 fq_native result_e(e_out.lo);
1083
1084 EXPECT_EQ(result_c, a_native);
1085 EXPECT_EQ(result_d, b_native);
1086 EXPECT_EQ(result_e, fq_native(a_native + b_native));
1087 }
1088 bool result = CircuitChecker::check(builder);
1089 EXPECT_EQ(result, true);
1090 }
1091
1092 static void test_conditional_select(InputType a_type, InputType b_type, InputType predicate_type)
1093 {
1094 auto builder = Builder();
1095 size_t num_repetitions = 1;
1096 for (size_t i = 0; i < num_repetitions; ++i) {
1097
1098 auto [a_native, a_ct] = get_random_element(&builder, a_type); // fq_native, fq_ct
1099 auto [b_native, b_ct] = get_random_element(&builder, b_type); // fq_native, fq_ct
1100
1101 bool_ct predicate_a;
1102 if (predicate_type == InputType::WITNESS) {
1103 predicate_a = bool_ct(witness_ct(&builder, true));
1104 } else {
1105 predicate_a = bool_ct(&builder, true);
1106 }
1107
1108 fq_ct c = a_ct.conditional_select(b_ct, predicate_a);
1109 fq_ct d = a_ct.conditional_select(b_ct, !predicate_a);
1110
1111 fq_ct e = c + d;
1112 e.self_reduce();
1113 uint512_t c_out = c.get_value();
1114 uint512_t d_out = d.get_value();
1115 uint512_t e_out = e.get_value();
1116
1117 fq_native result_c(c_out.lo);
1118 fq_native result_d(d_out.lo);
1119 fq_native result_e(e_out.lo);
1120
1121 EXPECT_EQ(result_c, b_native);
1122 EXPECT_EQ(result_d, a_native);
1123 EXPECT_EQ(result_e, fq_native(a_native + b_native));
1124 }
1125 bool result = CircuitChecker::check(builder);
1126 EXPECT_EQ(result, true);
1127 }
1128
1129 static void test_conditional_negate(InputType a_type, InputType predicate_type)
1130 {
1131 auto builder = Builder();
1132 size_t num_repetitions = 1;
1133 for (size_t i = 0; i < num_repetitions; ++i) {
1134
1135 auto [a_native, a_ct] = get_random_element(&builder, a_type); // fq_native, fq_ct
1136
1137 bool_ct predicate_a;
1138 if (predicate_type == InputType::WITNESS) {
1139 predicate_a = bool_ct(witness_ct(&builder, true));
1140 } else {
1141 predicate_a = bool_ct(&builder, true);
1142 }
1143
1144 fq_ct c = a_ct.conditional_negate(predicate_a);
1145 fq_ct d = a_ct.conditional_negate(!predicate_a);
1146
1147 fq_ct e = c + d;
1148 c.self_reduce();
1149 d.self_reduce();
1150 e.self_reduce();
1151 uint512_t c_out = c.get_value();
1152 uint512_t d_out = d.get_value();
1153 uint512_t e_out = e.get_value();
1154
1155 fq_native result_c(c_out.lo);
1156 fq_native result_d(d_out.lo);
1157 fq_native result_e(e_out.lo);
1158
1159 fq_native expected_c = (-a_native);
1160 fq_native expected_d = a_native;
1161
1162 EXPECT_EQ(result_c, expected_c);
1163 EXPECT_EQ(result_d, expected_d);
1164 EXPECT_EQ(result_e, fq_native(0));
1165 }
1166 bool result = CircuitChecker::check(builder);
1167 EXPECT_EQ(result, true);
1168 }
1169
1171 {
1172 auto builder = Builder();
1173 size_t num_repetitions = 1;
1174 for (size_t i = 0; i < num_repetitions; ++i) {
1175 // Note: we're using g1 = bn254 here. not tested for other curves.
1176 g1::affine_element P1(g1::element::random_element());
1177 g1::affine_element P2(g1::element::random_element());
1178
1179 fq_ct x1(
1180 witness_ct(&builder, fr(uint256_t(P1.x).slice(0, fq_ct::NUM_LIMB_BITS * 2))),
1181 witness_ct(&builder, fr(uint256_t(P1.x).slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 4))));
1182 fq_ct y1(
1183 witness_ct(&builder, fr(uint256_t(P1.y).slice(0, fq_ct::NUM_LIMB_BITS * 2))),
1184 witness_ct(&builder, fr(uint256_t(P1.y).slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 4))));
1185 fq_ct x2(
1186 witness_ct(&builder, fr(uint256_t(P2.x).slice(0, fq_ct::NUM_LIMB_BITS * 2))),
1187 witness_ct(&builder, fr(uint256_t(P2.x).slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 4))));
1188 fq_ct y2(
1189 witness_ct(&builder, fr(uint256_t(P2.y).slice(0, fq_ct::NUM_LIMB_BITS * 2))),
1190 witness_ct(&builder, fr(uint256_t(P2.y).slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 4))));
1191
1192 uint64_t before = builder.get_num_finalized_gates_inefficient();
1193 fq_ct lambda = (y2 - y1) / (x2 - x1);
1194 fq_ct x3 = lambda.sqr() - (x2 + x1);
1195 fq_ct y3 = (x1 - x3) * lambda - y1;
1196 uint64_t after = builder.get_num_finalized_gates_inefficient();
1197 std::cerr << "added gates = " << after - before << std::endl;
1198
1199 // Check the result against the native group addition
1201 fq expected_x = P3.x;
1202 fq expected_y = P3.y;
1203 expected_x = expected_x.from_montgomery_form_reduced();
1204 expected_y = expected_y.from_montgomery_form_reduced();
1205 uint512_t result_x = x3.get_value() % fq_ct::modulus_u512;
1206 uint512_t result_y = y3.get_value() % fq_ct::modulus_u512;
1207 EXPECT_EQ(result_x.lo.data[0], expected_x.data[0]);
1208 EXPECT_EQ(result_x.lo.data[1], expected_x.data[1]);
1209 EXPECT_EQ(result_x.lo.data[2], expected_x.data[2]);
1210 EXPECT_EQ(result_x.lo.data[3], expected_x.data[3]);
1211 EXPECT_EQ(result_y.lo.data[0], expected_y.data[0]);
1212 EXPECT_EQ(result_y.lo.data[1], expected_y.data[1]);
1213 EXPECT_EQ(result_y.lo.data[2], expected_y.data[2]);
1214 EXPECT_EQ(result_y.lo.data[3], expected_y.data[3]);
1215 }
1216 bool result = CircuitChecker::check(builder);
1217 EXPECT_EQ(result, true);
1218 }
1219
1220 static void test_reduce()
1221 {
1222 auto builder = Builder();
1223 size_t num_repetitions = 10;
1224 for (size_t i = 0; i < num_repetitions; ++i) {
1225 auto [a_native, a_ct] = get_random_witness(&builder); // fq_native, fq_ct
1226 auto [b_native, b_ct] = get_random_witness(&builder); // fq_native, fq_ct
1227
1228 fq_ct c_ct = a_ct;
1229 fq_native expected = a_native;
1230 for (size_t i = 0; i < 16; ++i) {
1231 c_ct = b_ct * b_ct + c_ct;
1232 expected = b_native * b_native + expected;
1233 }
1234
1235 c_ct.self_reduce();
1236
1237 fq_native result = fq_native(c_ct.get_value().lo);
1238 EXPECT_EQ(result, expected);
1239 EXPECT_EQ(c_ct.get_value().get_msb() < (fq_ct::modulus.get_msb() + 1), true);
1240 }
1241 bool result = CircuitChecker::check(builder);
1242 EXPECT_EQ(result, true);
1243 }
1244
1245 static void test_equality_operator(InputType a_type, InputType b_type)
1246 {
1247 auto builder = Builder();
1248 size_t num_repetitions = 10;
1249 for (size_t i = 0; i < num_repetitions; ++i) {
1250
1251 auto [a_native, a_ct] = get_random_element(&builder, a_type); // fq_native, fq_ct
1252 auto [b_native, b_ct] = get_random_element(&builder, b_type); // fq_native, fq_ct
1253
1254 // Construct witness from a_native
1255 fq_ct another_a_ct = fq_ct::create_from_u512_as_witness(&builder, uint512_t(a_native), true);
1256 bool_ct equality_with_self = (a_ct == another_a_ct);
1257 EXPECT_EQ(equality_with_self.get_value(), true);
1258
1259 // Check against b
1260 bool expected = (a_native == b_native);
1261 bool_ct result = (a_ct == b_ct);
1262 EXPECT_EQ(result.get_value(), expected);
1263 }
1264 bool result = CircuitChecker::check(builder);
1265 EXPECT_EQ(result, true);
1266 }
1267
1269 {
1270 auto builder = Builder();
1271 size_t num_repetitions = 10;
1272 for (size_t i = 0; i < num_repetitions; ++i) {
1273
1274 // Get unreduced inputs
1275 auto [a_native, a_ct] = get_random_witness(&builder); // fq_native, fq_ct
1276 auto [b_native, b_ct] = get_random_witness(&builder); // fq_native, fq_ct
1277
1278 // Get a reduced input
1279 auto [d_native, d_ct] = get_random_witness(&builder, true); // fq_native, fq_ct
1280
1281 // c_ct will be unreduced while performing operations
1282 fq_ct c_ct = a_ct;
1283 fq_native expected = a_native;
1284 for (size_t i = 0; i < 16; ++i) {
1285 c_ct = b_ct * b_ct + c_ct;
1286 expected = b_native * b_native + expected;
1287 }
1288
1289 // We need to reduce before calling assert_is_in_field
1290 c_ct.self_reduce();
1291 c_ct.assert_is_in_field();
1292
1293 // We can directly call assert_is_in_field on a reduced element
1294 d_ct.assert_is_in_field();
1295
1296 uint256_t result = (c_ct.get_value().lo);
1297 EXPECT_EQ(result, uint256_t(expected));
1298 EXPECT_EQ(c_ct.get_value().get_msb() < (fq_ct::modulus.get_msb() + 1), true);
1299 }
1300 bool result = CircuitChecker::check(builder);
1301 EXPECT_EQ(result, true);
1302 }
1303
1305 {
1306 auto builder = Builder();
1307 size_t num_repetitions = 1000;
1308 fq_ct c_ct = fq_ct::zero();
1309 fq_native expected = fq_native::zero();
1310 for (size_t i = 0; i < num_repetitions; ++i) {
1311
1312 auto [a_native, a_ct] = get_random_witness(&builder); // fq_native, fq_ct
1313 auto [b_native, b_ct] = get_random_witness(&builder); // fq_native, fq_ct
1314
1315 for (size_t i = 0; i < 16; ++i) {
1316 c_ct += a_ct * b_ct;
1317 expected += a_native * b_native;
1318 }
1319
1320 // Break out of the loop if c has exceeded the modulus
1321 if (c_ct.get_value() >= fq_ct::modulus) {
1322 break;
1323 }
1324 }
1325
1326 // this will fail because mult and add have been performed without reduction
1327 c_ct.assert_is_in_field();
1328
1329 // results must match (reduction called after assert_is_in_field)
1330 c_ct.self_reduce();
1331 uint256_t result_val = c_ct.get_value().lo;
1332 EXPECT_EQ(result_val, uint256_t(expected));
1333
1334 bool result = CircuitChecker::check(builder);
1335 EXPECT_EQ(result, false);
1336 }
1337
1339 {
1340 auto builder = Builder();
1341 size_t num_repetitions = 10;
1342 constexpr size_t num_bits = 200;
1343 constexpr uint256_t bit_mask = (uint256_t(1) << num_bits) - 1;
1344 for (size_t i = 0; i < num_repetitions; ++i) {
1345
1346 uint256_t a_u256 = uint256_t(fq_native::random_element()) & bit_mask;
1347 uint256_t b_u256 = uint256_t(fq_native::random_element()) & bit_mask;
1348
1349 // Construct 200-bit bigfield elements
1350 fq_ct a_ct(witness_ct(&builder, fr(a_u256.slice(0, fq_ct::NUM_LIMB_BITS * 2))),
1351 witness_ct(&builder, fr(a_u256.slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 4))));
1352 fq_ct b_ct(witness_ct(&builder, fr(b_u256.slice(0, fq_ct::NUM_LIMB_BITS * 2))),
1353 witness_ct(&builder, fr(b_u256.slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 4))));
1354
1355 // Assert a, b < 2^200
1356 a_ct.assert_less_than(bit_mask + 1);
1357 b_ct.assert_less_than(bit_mask + 1);
1358 EXPECT_EQ(a_ct.get_value().get_msb() < num_bits, true);
1359 EXPECT_EQ(b_ct.get_value().get_msb() < num_bits, true);
1360 }
1361 bool result = CircuitChecker::check(builder);
1362 EXPECT_EQ(result, true);
1363 }
1364
1366 {
1367 auto builder = Builder();
1368 constexpr size_t num_bits = 200;
1369 constexpr uint256_t bit_mask = (uint256_t(1) << num_bits) - 1;
1370
1371 size_t num_repetitions = 1000;
1372 fq_ct c_ct = fq_ct::zero();
1373 fq_native expected = fq_native::zero();
1374 for (size_t i = 0; i < num_repetitions; ++i) {
1375
1376 uint256_t a_u256 = uint256_t(fq_native::random_element()) & bit_mask;
1377 uint256_t b_u256 = uint256_t(fq_native::random_element()) & bit_mask;
1378
1379 // Construct 200-bit bigfield elements
1380 fq_ct a_ct(witness_ct(&builder, fr(a_u256.slice(0, fq_ct::NUM_LIMB_BITS * 2))),
1381 witness_ct(&builder, fr(a_u256.slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 4))));
1382 fq_ct b_ct(witness_ct(&builder, fr(b_u256.slice(0, fq_ct::NUM_LIMB_BITS * 2))),
1383 witness_ct(&builder, fr(b_u256.slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 4))));
1384
1385 // Mul and add without reduction to exceed 200 bits
1386 for (size_t i = 0; i < 16; ++i) {
1387 c_ct += a_ct * b_ct;
1388 expected += fq_native(a_u256) * fq_native(b_u256);
1389 }
1390
1391 // Break out of the loop if c has exceeded 200 bits
1392 if (c_ct.get_value().get_msb() >= num_bits) {
1393 break;
1394 }
1395 }
1396
1397 // check that assert_less_than fails
1398 c_ct.assert_less_than(bit_mask + 1);
1399
1400 // results must match (reduction called after assert_is_in_field)
1401 c_ct.self_reduce();
1402 uint256_t result_val = c_ct.get_value().lo;
1403 EXPECT_EQ(result_val, uint256_t(expected));
1404
1405 bool result = CircuitChecker::check(builder);
1406 EXPECT_EQ(result, false);
1407 }
1408
1410 {
1411 auto builder = Builder();
1412 size_t num_repetitions = 10;
1413 for (size_t i = 0; i < num_repetitions; ++i) {
1414
1415 // Get unreduced inputs
1416 auto [a_native, a_ct] = get_random_witness(&builder); // fq_native, fq_ct
1417 auto [b_native, b_ct] = get_random_witness(&builder); // fq_native, fq_ct
1418
1419 // c_ct will be unreduced while performing operations
1420 fq_ct c_ct = a_ct;
1421 fq_native expected = a_native;
1422 for (size_t i = 0; i < 16; ++i) {
1423 c_ct = b_ct * b_ct + c_ct;
1424 expected = b_native * b_native + expected;
1425 }
1426
1427 // reduce c to [0, p)
1428 // count gates for the last iteration only
1429 if (i == num_repetitions - 1) {
1430 BENCH_GATE_COUNT_START(builder, "REDUCE_MOD_P");
1431 c_ct.reduce_mod_target_modulus();
1432 BENCH_GATE_COUNT_END(builder, "REDUCE_MOD_P");
1433 } else {
1434 c_ct.reduce_mod_target_modulus();
1435 }
1436
1437 uint256_t result = (c_ct.get_value().lo);
1438 EXPECT_EQ(result, uint256_t(expected));
1439 EXPECT_EQ(c_ct.get_value() < fq_ct::modulus, true);
1440 }
1441 bool result = CircuitChecker::check(builder);
1442 EXPECT_EQ(result, true);
1443 }
1444
1446 {
1447 auto builder = Builder();
1448 size_t num_repetitions = 10;
1449 for (size_t i = 0; i < num_repetitions; ++i) {
1450
1453
1454 std::vector<uint8_t> input_a(sizeof(fq_native));
1455 fq_native::serialize_to_buffer(a_native, &input_a[0]);
1456 std::vector<uint8_t> input_b(sizeof(fq_native));
1457 fq_native::serialize_to_buffer(b_native, &input_b[0]);
1458
1459 stdlib::byte_array<Builder> input_arr_a(&builder, input_a);
1460 stdlib::byte_array<Builder> input_arr_b(&builder, input_b);
1461
1462 fq_ct a_ct(input_arr_a);
1463 fq_ct b_ct(input_arr_b);
1464
1465 fq_ct c_ct = a_ct * b_ct;
1466
1467 fq_native expected = a_native * b_native;
1468 uint256_t result = (c_ct.get_value().lo);
1469 EXPECT_EQ(result, uint256_t(expected));
1470 }
1471 bool result = CircuitChecker::check(builder);
1472 EXPECT_EQ(result, true);
1473 }
1474
1475 // Test byte_array constructor with boundary values: 0, 1, p-1, and values >= p.
1477 {
1478 // Helper: convert a uint256_t to a 32-byte big-endian vector (same layout as fq_native::serialize_to_buffer)
1479 const auto to_bytes = [](uint256_t v) {
1480 std::vector<uint8_t> buf(32, 0);
1481 for (int i = 31; i >= 0; --i) {
1482 buf[static_cast<size_t>(i)] = static_cast<uint8_t>(v.data[0] & 0xff);
1483 v >>= 8;
1484 }
1485 return buf;
1486 };
1487
1488 const uint256_t p = fq_ct::modulus;
1489
1490 struct TestCase {
1492 bool expect_less_than_p; // whether value < p
1493 };
1494
1495 // Boundary values: 0, 1, p-1 (all valid); p (invalid — equals modulus)
1496 // Nibble-boundary values: 0x0f...f (all low nibbles set), 0x10...0 (carry into high nibble)
1497 const std::vector<TestCase> cases = {
1498 { uint256_t(0), true },
1499 { uint256_t(1), true },
1500 { p - 1, true },
1501 // Nibble boundary: value whose split byte is 0x0f (lo nibble=0xf, hi nibble=0)
1502 { uint256_t(0x0f) << (7 * 8), true }, // byte 7 from the right = overlap byte for limb0/limb1
1503 // Nibble boundary: value whose split byte is 0xf0 (lo nibble=0, hi nibble=0xf)
1504 { uint256_t(0xf0) << (7 * 8), true },
1505 // p itself — a bigfield constructed from p bytes will have value == p, which is >= p
1506 { p, false },
1507 };
1508
1509 for (const auto& tc : cases) {
1510 auto builder = Builder();
1511 byte_array_ct arr(&builder, to_bytes(tc.value));
1512 fq_ct el(arr);
1513
1514 // Check that the reconstructed value matches
1515 EXPECT_EQ(el.get_value().lo, tc.value);
1516
1517 // Verify is_less_than agrees with our expectation
1518 auto cmp = el.is_less_than(p);
1519 cmp.assert_equal(stdlib::bool_t<Builder>(tc.expect_less_than_p));
1520
1521 EXPECT_TRUE(CircuitChecker::check(builder));
1522 EXPECT_FALSE(builder.failed());
1523 }
1524 }
1525
1526 // This check tests if elements are reduced to fit quotient into range proof
1528 {
1529 auto builder = Builder();
1530 const uint256_t input =
1531 uint256_t(0xfffffffffffffffe, 0xffffffffffffffff, 0xffffffffffffffff, 0x3fffffffffffffff);
1532
1533 fq_ct a(witness_ct(&builder, fr(uint256_t(input).slice(0, fq_ct::NUM_LIMB_BITS * 2))),
1534 witness_ct(&builder, fr(uint256_t(input).slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 4))),
1535 false);
1536 auto a1 = a;
1537 auto a2 = a;
1538 auto a3 = a;
1539 auto a4 = a;
1540
1541 for (auto i = 0; i < 8; i++) {
1542 a = a + a;
1543 a1 = a1 + a1;
1544 a2 = a2 + a2;
1545 a3 = a3 + a3;
1546 a4 = a4 + a4;
1547 }
1548
1549 auto b = a * a;
1550 (void)b;
1551
1552 auto c = a1.sqr();
1553 (void)c;
1554
1555 auto d = a2.sqradd({});
1556 (void)d;
1557
1558 auto e = a3.madd(a3, {});
1559 (void)e;
1560
1561 auto f = fq_ct::mult_madd({ a4 }, { a4 }, {}, false);
1562 (void)f;
1563
1564 bool result = CircuitChecker::check(builder);
1565 EXPECT_EQ(result, true);
1566 }
1567
1569 {
1570 auto builder = Builder();
1571 fq_native a(0);
1572 fq_native b(1);
1573 fq_ct a_ct(&builder, a);
1574 fq_ct b_ct(&builder, b);
1575 fq_ct selected = a_ct.conditional_select(b_ct, bool_ct(&builder, true));
1576 EXPECT_EQ(fq_native((selected.get_value() % uint512_t(fq_native::modulus)).lo), b);
1577 }
1578
1580 {
1581 auto builder = Builder();
1582 fq_native a(1);
1583 fq_ct a_ct(&builder, a);
1584 fq_ct ret = fq_ct::div_check_denominator_nonzero({}, a_ct);
1585 EXPECT_NE(ret.get_context(), nullptr);
1586 }
1587
1588 static void test_inversion()
1589 {
1590 fq_ct a = fq_ct(-7);
1591 fq_ct a_inverse = a.invert();
1592 fq_ct a_inverse_division = fq_ct(1) / a;
1593
1594 fq_native a_native = fq_native(-7);
1595 fq_native a_native_inverse = a_native.invert();
1596 EXPECT_EQ(fq_native((a.get_value() % uint512_t(fq_native::modulus)).lo), a_native);
1597 EXPECT_EQ(fq_native((a_inverse.get_value() % uint512_t(fq_native::modulus)).lo), a_native_inverse);
1598 EXPECT_EQ(fq_native((a_inverse_division.get_value() % uint512_t(fq_native::modulus)).lo), a_native_inverse);
1599 }
1600
1602 {
1603 auto builder = Builder();
1604 size_t num_repetitions = 10;
1605 for (size_t i = 0; i < num_repetitions; ++i) {
1606 auto [a_native, a_ct] = get_random_witness(&builder); // fq_native, fq_ct
1607 auto [c_native, c_ct] = get_random_witness(&builder); // fq_native, fq_ct
1608 auto [d_native, d_ct] = get_random_witness(&builder); // fq_native, fq_ct
1609
1610 fq_ct two_ct = fq_ct::unsafe_construct_from_limbs(witness_ct(&builder, fr(2)),
1611 witness_ct(&builder, fr(0)),
1612 witness_ct(&builder, fr(0)),
1613 witness_ct(&builder, fr(0)));
1614 fq_ct t0 = a_ct + a_ct;
1615 fq_ct t1 = a_ct * two_ct;
1616
1617 t0.assert_equal(t1);
1618 t0.assert_is_not_equal(c_ct);
1619 t0.assert_is_not_equal(d_ct);
1620 stdlib::bool_t<Builder> is_equal_a = t0 == t1;
1621 stdlib::bool_t<Builder> is_equal_b = t0 == c_ct;
1622 EXPECT_TRUE(is_equal_a.get_value());
1623 EXPECT_FALSE(is_equal_b.get_value());
1624 }
1625 bool result = CircuitChecker::check(builder);
1626 EXPECT_EQ(result, true);
1627 }
1628
1629 // Test assert_zero_if with a zero bigfield - should always pass regardless of predicate
1630 static void test_assert_zero_if_zero_value(bool predicate_value, InputType predicate_type)
1631 {
1632 auto builder = Builder();
1633
1634 // Create a zero bigfield as witness
1635 fq_ct zero_ct = fq_ct::create_from_u512_as_witness(&builder, uint256_t(0));
1636
1637 // Create predicate
1638 bool_ct predicate;
1639 if (predicate_type == InputType::WITNESS) {
1640 predicate = bool_ct(witness_ct(&builder, predicate_value));
1641 } else {
1642 predicate = bool_ct(&builder, predicate_value);
1643 }
1644
1645 // Should always pass: zero bigfield satisfies the constraint regardless of predicate
1646 zero_ct.assert_zero_if(predicate);
1647
1648 bool result = CircuitChecker::check(builder);
1649 EXPECT_EQ(result, true);
1650 }
1651
1652 // Test assert_zero_if with a non-zero bigfield
1653 // - predicate=false: should pass (check is skipped)
1654 // - predicate=true: should fail (non-zero value violates constraint)
1655 static void test_assert_zero_if_nonzero_value(bool predicate_value,
1656 InputType value_type,
1657 InputType predicate_type,
1658 bool expect_circuit_check_pass)
1659 {
1660 auto builder = Builder();
1661
1662 // Create a non-zero bigfield
1663 fq_ct nonzero_ct;
1664 if (value_type == InputType::WITNESS) {
1665 nonzero_ct = fq_ct::create_from_u512_as_witness(&builder, uint256_t(42));
1666 } else {
1667 nonzero_ct = fq_ct(&builder, uint256_t(42));
1668 }
1669
1670 // Create predicate
1671 bool_ct predicate;
1672 if (predicate_type == InputType::WITNESS) {
1673 predicate = bool_ct(witness_ct(&builder, predicate_value));
1674 } else {
1675 predicate = bool_ct(&builder, predicate_value);
1676 }
1677
1678 nonzero_ct.assert_zero_if(predicate);
1679
1680 bool result = CircuitChecker::check(builder);
1681 EXPECT_EQ(result, expect_circuit_check_pass);
1682 }
1683
1684 // Test assert_zero_if with computed zero (result of subtraction) - should pass
1686 {
1687 auto builder = Builder();
1688
1689 // Create two equal bigfields and subtract to get zero
1690 auto [a_native, a_ct] = get_random_witness(&builder);
1691 fq_ct b_ct = fq_ct::create_from_u512_as_witness(&builder, uint256_t(a_native));
1692
1693 // Compute a - a which should be zero
1694 fq_ct zero_ct = a_ct - b_ct;
1695
1696 // We need reduction so that the value is actually zero (k * p is not allowed)
1697 zero_ct.self_reduce();
1698
1699 // Create true predicate as witness
1700 bool_ct predicate = bool_ct(witness_ct(&builder, true));
1701
1702 // This should pass: computed zero with true predicate
1703 zero_ct.assert_zero_if(predicate);
1704
1705 bool result = CircuitChecker::check(builder);
1706 EXPECT_EQ(result, true);
1707 }
1708
1709 static void test_pow()
1710 {
1712
1714 uint32_t exponent_val = engine.get_random_uint32();
1715 // Set the high bit
1716 exponent_val |= static_cast<uint32_t>(1) << 31;
1717 fq_ct base_constant(&builder, static_cast<uint256_t>(base_val));
1718 fq_ct base_witness_ct = fq_ct::from_witness(&builder, typename fq_ct::native(base_val));
1719 // This also tests for the case where the exponent is zero
1720 for (size_t i = 0; i <= 32; i += 4) {
1721 uint32_t current_exponent_val = exponent_val >> i;
1722 fq_native expected = base_val.pow(current_exponent_val);
1723
1724 // Check for constant bigfield element with constant exponent
1725 fq_ct result_constant_base = base_constant.pow(current_exponent_val);
1726 EXPECT_EQ(fq_native(result_constant_base.get_value()), expected);
1727
1728 // Check for witness base with constant exponent
1729 fq_ct result_witness_base = base_witness_ct.pow(current_exponent_val);
1730 EXPECT_EQ(fq_native(result_witness_base.get_value()), expected);
1731 }
1732
1733 bool check_result = CircuitChecker::check(builder);
1734 EXPECT_EQ(check_result, true);
1735 }
1736
1737 static void test_pow_one()
1738 {
1740
1742
1743 uint32_t current_exponent_val = 1;
1744 fq_ct base_constant_ct(&builder, static_cast<uint256_t>(base_val));
1745 fq_ct base_witness_ct = fq_ct::from_witness(&builder, typename fq_ct::native(base_val));
1746 fq_native expected = base_val.pow(current_exponent_val);
1747
1748 // Check for constant bigfield element with constant exponent
1749 fq_ct result_constant_base = base_constant_ct.pow(current_exponent_val);
1750 EXPECT_EQ(fq_native(result_constant_base.get_value()), expected);
1751
1752 // Check for witness base with constant exponent
1753 fq_ct result_witness_base = base_witness_ct.pow(current_exponent_val);
1754 EXPECT_EQ(fq_native(result_witness_base.get_value()), expected);
1755
1756 bool check_result = CircuitChecker::check(builder);
1757 EXPECT_EQ(check_result, true);
1758 }
1759
1761 {
1762 auto builder = Builder();
1763 size_t num_repetitions = 10;
1764 constexpr size_t num_bits = 200;
1765 constexpr uint256_t bit_mask = (uint256_t(1) << num_bits) - 1;
1766 for (size_t i = 0; i < num_repetitions; ++i) {
1767
1768 uint256_t a_u256 = uint256_t(fq_native::random_element()) & bit_mask;
1769 uint256_t b_u256 = uint256_t(fq_native::random_element()) & bit_mask;
1770
1771 // Construct 200-bit bigfield elements
1772 fq_ct a_ct(witness_ct(&builder, fr(a_u256.slice(0, fq_ct::NUM_LIMB_BITS * 2))),
1773 witness_ct(&builder, fr(a_u256.slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 4))));
1774 fq_ct b_ct(witness_ct(&builder, fr(b_u256.slice(0, fq_ct::NUM_LIMB_BITS * 2))),
1775 witness_ct(&builder, fr(b_u256.slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 4))));
1776
1777 // Assert a, b < 2^200
1780 EXPECT_EQ(a_ct.get_value().get_msb() < num_bits, true);
1781 EXPECT_EQ(b_ct.get_value().get_msb() < num_bits, true);
1782 }
1783
1784 // Also test when: p < a < bound
1785 // define a = p + small_random_value
1786 uint256_t small_mask = (uint256_t(1) << 16) - 1;
1787 uint256_t a_u256 = uint256_t(fq_native::random_element()) & small_mask;
1788 a_u256 += uint256_t(fq_native::modulus);
1789
1790 // upper bound must be greater than p + 2^16: we set it to p + 2^30
1791 uint256_t upper_bound = (uint256_t(1) << 30) + uint256_t(fq_native::modulus);
1792
1793 // Construct bigfield element
1794 fq_ct a_ct(witness_ct(&builder, fr(a_u256.slice(0, fq_ct::NUM_LIMB_BITS * 2))),
1795 witness_ct(&builder, fr(a_u256.slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 4))),
1796 /*can_overflow*/ true);
1797
1798 // Assert a < bound
1800 EXPECT_EQ(a_ct.get_value() > uint512_t(fq_native::modulus), true);
1801
1802 // Combined circuit should pass
1803 bool result = CircuitChecker::check(builder);
1804 EXPECT_EQ(result, true);
1805 }
1806
1808 {
1809 {
1810 // Test a case when the value is exactly equal to the limit
1811 auto builder = Builder();
1812 constexpr size_t num_bits = 200;
1813 constexpr uint256_t bit_mask = (uint256_t(1) << num_bits) - 1;
1814 fq_ct a_ct(witness_ct(&builder, fr(bit_mask.slice(0, fq_ct::NUM_LIMB_BITS * 2))),
1815 witness_ct(&builder, fr(bit_mask.slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 4))));
1816
1817 // check that unsafe_assert_less_than fails when we try to check a < a.
1819
1820 bool result = CircuitChecker::check(builder);
1821 EXPECT_EQ(result, false);
1822 }
1823 {
1824 // Test a case when the value is (B + 2) but the bound is B.
1825 auto builder = Builder();
1826 constexpr size_t num_bits = 200;
1827 constexpr uint256_t bit_mask = (uint256_t(1) << num_bits) - 1;
1828 const uint256_t upper_bound = uint256_t(fq_native::random_element()) & bit_mask;
1829 const uint256_t a_value = upper_bound + uint256_t(2);
1830 fq_ct a_ct(witness_ct(&builder, fr(a_value.slice(0, fq_ct::NUM_LIMB_BITS * 2))),
1831 witness_ct(&builder, fr(a_value.slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 4))));
1832
1833 // check that unsafe_assert_less_than fails when we try to check (B + 2) < B.
1835
1836 bool result = CircuitChecker::check(builder);
1837 EXPECT_EQ(result, false);
1838 }
1839 {
1840 // Test a case when p < bound < a
1841 auto builder = Builder();
1842 uint256_t small_mask = (uint256_t(1) << 32) - 1;
1843 uint256_t a_u256 = uint256_t(fq_native::random_element()) & small_mask;
1844 a_u256 += uint256_t(fq_native::modulus);
1845
1846 // upper bound must be greater than p but smaller than a
1847 uint256_t upper_bound = uint256_t(fq_native::modulus) + uint256_t(1);
1848
1849 // Construct bigfield element
1850 fq_ct a_ct(witness_ct(&builder, fr(a_u256.slice(0, fq_ct::NUM_LIMB_BITS * 2))),
1851 witness_ct(&builder, fr(a_u256.slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 4))),
1852 /*can_overflow*/ true);
1853
1854 // check that unsafe_assert_less_than fails when we try to check a < bound.
1856
1857 bool result = CircuitChecker::check(builder);
1858 EXPECT_EQ(result, false);
1859 }
1860 }
1861
1863 {
1865
1866 // The circuit enforces:
1867 // a * b + (c0 + c1 + ...) = q * p + (r0 + r1 + ...) mod 2^T
1868 // a * b + (c0 + c1 + ...) = q * p + (r0 + r1 + ...) mod n
1869
1870 // Single addend and remainder
1871 auto [a_native, a_ct] = get_random_witness(&builder);
1872 auto [b_native, b_ct] = get_random_witness(&builder);
1873 auto [c_native, c_ct] = get_random_witness(&builder);
1874
1875 // Get quotient and remainder for (a * b + c) from native values
1876 uint1024_t native_sum = uint1024_t(a_native) * uint1024_t(b_native) + uint1024_t(c_native);
1877 auto [q_native_1024, r_native_1024] = native_sum.divmod(uint1024_t(fq_ct::modulus));
1878 const uint512_t q_native_512 = q_native_1024.lo;
1879 const uint512_t r_native_512 = r_native_1024.lo;
1880 fq_ct q_ct = fq_ct::create_from_u512_as_witness(&builder, q_native_512, true);
1881 fq_ct r_ct = fq_ct::create_from_u512_as_witness(&builder, r_native_512, true);
1882
1883 // Call unsafe_evaluate_multiply_add (via friendly class)
1884 stdlib::bigfield_test_access::unsafe_evaluate_multiply_add(a_ct, b_ct, { c_ct }, q_ct, { r_ct });
1885
1886 // The above function does not protect against CRT overflows, i.e., check if lhs and rhs are less than
1887 // M = (2^T * n). Verify that adding a multiple of M to both sides does not result in an unsatisfiable circuit.
1888 uint512_t big_M = uint512_t(fr::modulus) * fq_ct::binary_basis.modulus;
1889 uint512_t modified_c_native = uint512_t(c_native) + big_M;
1890 uint512_t modified_r_native = uint512_t(r_native_512) + big_M;
1891 fq_ct modified_c_ct = fq_ct::create_from_u512_as_witness(&builder, modified_c_native, true);
1892 fq_ct modified_r_ct = fq_ct::create_from_u512_as_witness(&builder, modified_r_native, true);
1893
1894 // Call unsafe_evaluate_multiply_add (via friendly class)
1896 a_ct, b_ct, { modified_c_ct }, q_ct, { modified_r_ct });
1897
1898 // Native verification mod p
1899 fq_native expected_lhs = a_native * b_native + c_native;
1900 fq_native expected_rhs = fq_native(q_native_512) * fq_ct::modulus + fq_native(r_native_512);
1901 EXPECT_EQ(expected_lhs, expected_rhs);
1902
1903 // Native verification mod 2^T
1904 uint1024_t lhs_1024 = uint512_t(a_native) * uint512_t(b_native) + uint512_t(c_native);
1905 uint1024_t rhs_1024 = q_native_512 * fq_ct::modulus + r_native_512;
1906 auto [quotient_lhs, remainder_lhs] = lhs_1024.divmod(fq_ct::binary_basis.modulus);
1907 auto [quotient_rhs, remainder_rhs] = rhs_1024.divmod(fq_ct::binary_basis.modulus);
1908 EXPECT_EQ(remainder_lhs, remainder_rhs);
1909
1910 // Native verification mod n
1911 fr expected_lhs_fr = fr(a_native) * fr(b_native) + fr(c_native);
1912 fr expected_rhs_fr = fr(q_native_512) * fr(fq_ct::modulus) + fr(r_native_512);
1913 EXPECT_EQ(expected_lhs_fr, expected_rhs_fr);
1914
1915 // Check circuit correctness
1916 bool result = CircuitChecker::check(builder);
1917 EXPECT_EQ(result, true);
1918 }
1919
1921 {
1922 auto builder = Builder();
1923
1924 // The circuit enforces:
1925 // a * b + (c0 + c1 + ...) = q * p + (r0 + r1 + ...) mod 2^T
1926 // a * b + (c0 + c1 + ...) = q * p + (r0 + r1 + ...) mod n
1927
1928 // Single addend and remainder
1929 auto [a_native, a_ct] = get_random_witness(&builder);
1930 auto [b_native, b_ct] = get_random_witness(&builder);
1931 auto [c_native, c_ct] = get_random_witness(&builder);
1932
1933 // Get quotient and remainder for (a * b + c) from native values
1934 uint512_t native_sum = uint512_t(a_native) * uint512_t(b_native) + uint512_t(c_native);
1935 auto [q_native_uint512_t, r_native_uint512_t] = native_sum.divmod(uint512_t(fq_ct::modulus));
1936 fq_ct q_ct = fq_ct::create_from_u512_as_witness(
1937 &builder, q_native_uint512_t + uint512_t(1), true); // Intentionally poisoned
1938 fq_ct r_ct = fq_ct::create_from_u512_as_witness(&builder, r_native_uint512_t, true);
1939
1940 // Call unsafe_evaluate_multiply_add (via friendly class)
1941 stdlib::bigfield_test_access::unsafe_evaluate_multiply_add(a_ct, b_ct, { c_ct }, q_ct, { r_ct });
1942
1943 // Check circuit correctness
1944 bool result = CircuitChecker::check(builder);
1945 EXPECT_EQ(result, false);
1946 EXPECT_EQ(builder.err(), "bigfield: prime limb identity failed");
1947 }
1948
1950 {
1952
1953 // The circuit enforces:
1954 // a1 * b1 + a2 * b2 + ... + (c0 + c1 + ...) = q * p + (r0 + r1 + ...) mod 2^T
1955 // a1 * b1 + a2 * b2 + ... + (c0 + c1 + ...) = q * p + (r0 + r1 + ...) mod n
1956 size_t num_terms = 3;
1957 std::vector<fq_native> a_natives;
1958 std::vector<fq_native> b_natives;
1959 std::vector<fq_ct> a_cts;
1960 std::vector<fq_ct> b_cts;
1961
1962 for (size_t i = 0; i < num_terms; ++i) {
1963 auto [a_native, a_ct] = get_random_witness(&builder);
1964 auto [b_native, b_ct] = get_random_witness(&builder);
1965 a_natives.push_back(a_native);
1966 b_natives.push_back(b_native);
1967 a_cts.push_back(a_ct);
1968 b_cts.push_back(b_ct);
1969 }
1970
1971 auto [c_native, c_ct] = get_random_witness(&builder);
1972
1973 // Get quotient and remainder for (sum of ai * bi + c) from native values
1974 uint1024_t native_sum = uint1024_t(c_native);
1975 for (size_t i = 0; i < num_terms; ++i) {
1976 native_sum += uint1024_t(a_natives[i]) * uint1024_t(b_natives[i]);
1977 }
1978 auto [q_native_1024, r_native_1024] = native_sum.divmod(uint512_t(fq_ct::modulus));
1979 const uint512_t q_native_512 = q_native_1024.lo;
1980 const uint512_t r_native_512 = r_native_1024.lo;
1981 fq_ct q_ct = fq_ct::create_from_u512_as_witness(&builder, q_native_512, true);
1982 fq_ct r_ct = fq_ct::create_from_u512_as_witness(&builder, r_native_512, true);
1983
1984 // Call unsafe_evaluate_multiply_add (via friendly class)
1985 stdlib::bigfield_test_access::unsafe_evaluate_multiple_multiply_add(a_cts, b_cts, { c_ct }, q_ct, { r_ct });
1986
1987 // Native verification mod p
1988 fq_native expected_lhs = fq_native(c_native);
1989 for (size_t i = 0; i < num_terms; ++i) {
1990 expected_lhs += fq_native(a_natives[i]) * fq_native(b_natives[i]);
1991 }
1992 fq_native expected_rhs = fq_native(q_native_512) * fq_ct::modulus + fq_native(r_native_512);
1993 EXPECT_EQ(expected_lhs, expected_rhs);
1994
1995 // Native verification mod 2^T
1996 uint1024_t lhs_1024 = uint1024_t(c_native);
1997 for (size_t i = 0; i < num_terms; ++i) {
1998 lhs_1024 += uint1024_t(a_natives[i]) * uint1024_t(b_natives[i]);
1999 }
2000 uint1024_t rhs_1024 = q_native_512 * fq_ct::modulus + r_native_512;
2001 auto [quotient_lhs, remainder_lhs] = lhs_1024.divmod(fq_ct::binary_basis.modulus);
2002 auto [quotient_rhs, remainder_rhs] = rhs_1024.divmod(fq_ct::binary_basis.modulus);
2003 EXPECT_EQ(remainder_lhs, remainder_rhs);
2004
2005 // Native verification mod n
2006 fr expected_lhs_fr = fr(c_native);
2007 for (size_t i = 0; i < num_terms; ++i) {
2008 expected_lhs_fr += fr(a_natives[i]) * fr(b_natives[i]);
2009 }
2010 fr expected_rhs_fr = fr(q_native_512) * fr(fq_ct::modulus) + fr(r_native_512);
2011 EXPECT_EQ(expected_lhs_fr, expected_rhs_fr);
2012
2013 // Check circuit correctness
2014 bool result = CircuitChecker::check(builder);
2015 EXPECT_EQ(result, true);
2016 }
2017
2019 {
2021
2022 // The circuit enforces:
2023 // a1 * b1 + a2 * b2 + ... + (c0 + c1 + ...) = q * p + (r0 + r1 + ...) mod 2^T
2024 // a1 * b1 + a2 * b2 + ... + (c0 + c1 + ...) = q * p + (r0 + r1 + ...) mod n
2025 size_t num_terms = 3;
2026 std::vector<fq_native> a_natives;
2027 std::vector<fq_native> b_natives;
2028 std::vector<fq_ct> a_cts;
2029 std::vector<fq_ct> b_cts;
2030
2031 for (size_t i = 0; i < num_terms; ++i) {
2032 auto [a_native, a_ct] = get_random_witness(&builder);
2033 auto [b_native, b_ct] = get_random_witness(&builder);
2034 a_natives.push_back(a_native);
2035 b_natives.push_back(b_native);
2036 a_cts.push_back(a_ct);
2037 b_cts.push_back(b_ct);
2038 }
2039
2040 auto [c_native, c_ct] = get_random_witness(&builder);
2041
2042 // Get quotient and remainder for (sum of ai * bi + c) from native values
2043 uint1024_t native_sum = uint1024_t(c_native);
2044 for (size_t i = 0; i < num_terms; ++i) {
2045 native_sum += uint1024_t(a_natives[i]) * uint1024_t(b_natives[i]);
2046 }
2047 auto [q_native_1024, r_native_1024] = native_sum.divmod(uint1024_t(fq_ct::modulus));
2048 fq_ct q_ct = fq_ct::create_from_u512_as_witness(
2049 &builder, q_native_1024.lo + uint512_t(1), true); // Intentionally poisoned
2050 fq_ct r_ct = fq_ct::create_from_u512_as_witness(&builder, r_native_1024.lo, true);
2051
2052 // Call unsafe_evaluate_multiply_add (via friendly class)
2053 stdlib::bigfield_test_access::unsafe_evaluate_multiple_multiply_add(a_cts, b_cts, { c_ct }, q_ct, { r_ct });
2054
2055 // Check circuit correctness
2056 bool result = CircuitChecker::check(builder);
2057 EXPECT_EQ(result, false);
2058 EXPECT_EQ(builder.err(), "bigfield: prime limb identity failed");
2059 }
2060
2062 {
2063 auto builder = Builder();
2065 uint256_t two_to_68 = uint256_t(1) << fq_ct::NUM_LIMB_BITS;
2066 // construct bigfield where the low limb has a non-trivial `additive_constant`
2067 fq_ct z(zero + two_to_68, zero);
2068 // assert invariant for every limb: actual value <= maximum value
2069 // Failed in the past for for StandardCircuitBuilder
2070 for (auto zi : z.binary_basis_limbs) {
2071 EXPECT_LE(uint256_t(zi.element.get_value()), zi.maximum_value);
2072 }
2073 }
2074
2076 {
2077 auto builder = Builder();
2078 fq_ct witness_one = fq_ct::create_from_u512_as_witness(&builder, uint256_t(1));
2079 fq_ct constant_one(1);
2080 fq_ct::msub_div({ witness_one }, { witness_one }, constant_one, { witness_one }, true);
2081 bool result = CircuitChecker::check(builder);
2082 EXPECT_EQ(result, true);
2083 }
2084
2086 {
2087 typedef stdlib::bool_t<Builder> bool_t;
2088 auto builder = Builder();
2089
2090 fq_ct w0 = fq_ct::from_witness(&builder, typename fq_ct::native(1));
2091 w0 = w0.conditional_negate(bool_t(&builder, true));
2092 w0 = w0.conditional_negate(bool_t(&builder, false));
2093 w0 = w0.conditional_negate(bool_t(&builder, true));
2094 w0 = w0.conditional_negate(bool_t(&builder, true));
2095 fq_ct w4 = w0.conditional_negate(bool_t(&builder, false));
2096 w4 = w4.conditional_negate(bool_t(&builder, true));
2097 w4 = w4.conditional_negate(bool_t(&builder, true));
2098 fq_ct w5 = w4 - w0;
2099 fq_ct w6 = w5 / 1;
2100 (void)(w6);
2101 EXPECT_TRUE(CircuitChecker::check(builder));
2102 }
2103
2105 {
2106 auto builder = Builder();
2107
2108 fq_ct numerator = fq_ct::create_from_u512_as_witness(&builder, uint256_t(1) << (68 + 67));
2109 numerator.binary_basis_limbs[0].maximum_value = 0;
2110 numerator.binary_basis_limbs[1].maximum_value = uint256_t(1) << 67;
2111 numerator.binary_basis_limbs[2].maximum_value = 0;
2112 numerator.binary_basis_limbs[3].maximum_value = 0;
2113
2114 for (size_t i = 0; i < 9; i++) {
2115 numerator = numerator + numerator;
2116 }
2117 fq_ct denominator = fq_ct::create_from_u512_as_witness(&builder, uint256_t(1));
2118 fq_ct result = numerator / denominator;
2119 (void)(result);
2120 EXPECT_TRUE(CircuitChecker::check(builder));
2121 }
2122
2124 {
2126 uint256_t dlimb0_value = uint256_t("0x00000000000000000000000000000000000000000000000bef7fa109038857fc");
2127 uint256_t dlimb0_max = uint256_t("0x00000000000000000000000000000000000000000000000fffffffffffffffff");
2128 uint256_t dlimb1_value = uint256_t("0x0000000000000000000000000000000000000000000000056f10535779f56339");
2129 uint256_t dlimb1_max = uint256_t("0x00000000000000000000000000000000000000000000000fffffffffffffffff");
2130 uint256_t dlimb2_value = uint256_t("0x00000000000000000000000000000000000000000000000c741f60a1ec4e114e");
2131 uint256_t dlimb2_max = uint256_t("0x00000000000000000000000000000000000000000000000fffffffffffffffff");
2132 uint256_t dlimb3_value = uint256_t("0x000000000000000000000000000000000000000000000000000286b3cd344d8b");
2133 uint256_t dlimb3_max = uint256_t("0x0000000000000000000000000000000000000000000000000003ffffffffffff");
2134 uint256_t dlimb_prime = uint256_t("0x286b3cd344d8bc741f60a1ec4e114e56f10535779f56339bef7fa109038857fc");
2135
2136 uint256_t nlimb0_value = uint256_t("0x00000000000000000000000000000000000000000000080a84d9bea2b012417c");
2137 uint256_t nlimb0_max = uint256_t("0x000000000000000000000000000000000000000000000ff7c7469df4081b61fc");
2138 uint256_t nlimb1_value = uint256_t("0x00000000000000000000000000000000000000000000080f50ee84526e8e5ba7");
2139 uint256_t nlimb1_max = uint256_t("0x000000000000000000000000000000000000000000000ffef965c67ba5d5893c");
2140 uint256_t nlimb2_value = uint256_t("0x00000000000000000000000000000000000000000000080aba136ca8eaf6dc1b");
2141 uint256_t nlimb2_max = uint256_t("0x000000000000000000000000000000000000000000000ff8171d22fd607249ea");
2142 uint256_t nlimb3_value = uint256_t("0x00000000000000000000000000000000000000000000000001f0042419843c29");
2143 uint256_t nlimb3_max = uint256_t("0x00000000000000000000000000000000000000000000000003e00636264659ff");
2144 uint256_t nlimb_prime = uint256_t("0x000000000000000000000000000000474da776b8ee19a56b08186bdcf01240d8");
2145
2146 fq_ct w0 = fq_ct::from_witness(&builder, fq_native(0));
2147 w0.binary_basis_limbs[0].element = witness_ct(&builder, dlimb0_value);
2148 w0.binary_basis_limbs[1].element = witness_ct(&builder, dlimb1_value);
2149 w0.binary_basis_limbs[2].element = witness_ct(&builder, dlimb2_value);
2150 w0.binary_basis_limbs[3].element = witness_ct(&builder, dlimb3_value);
2151 w0.binary_basis_limbs[0].maximum_value = dlimb0_max;
2152 w0.binary_basis_limbs[1].maximum_value = dlimb1_max;
2153 w0.binary_basis_limbs[2].maximum_value = dlimb2_max;
2154 w0.binary_basis_limbs[3].maximum_value = dlimb3_max;
2155 w0.prime_basis_limb = witness_ct(&builder, dlimb_prime);
2156
2157 fq_ct w1 = fq_ct::from_witness(&builder, fq_native(0));
2158 w1.binary_basis_limbs[0].element = witness_ct(&builder, nlimb0_value);
2159 w1.binary_basis_limbs[1].element = witness_ct(&builder, nlimb1_value);
2160 w1.binary_basis_limbs[2].element = witness_ct(&builder, nlimb2_value);
2161 w1.binary_basis_limbs[3].element = witness_ct(&builder, nlimb3_value);
2162 w1.binary_basis_limbs[0].maximum_value = nlimb0_max;
2163 w1.binary_basis_limbs[1].maximum_value = nlimb1_max;
2164 w1.binary_basis_limbs[2].maximum_value = nlimb2_max;
2165 w1.binary_basis_limbs[3].maximum_value = nlimb3_max;
2166 w1.prime_basis_limb = witness_ct(&builder, nlimb_prime);
2167
2168 fq_ct w2 = w1 / w0;
2169 (void)w2;
2170 EXPECT_TRUE(CircuitChecker::check(builder));
2171 }
2172
2174 {
2175 auto builder = Builder();
2176 fq_ct zero = fq_ct::create_from_u512_as_witness(&builder, uint256_t(0));
2177 fq_ct alsozero = fq_ct::create_from_u512_as_witness(&builder, fq_ct::modulus_u512);
2178 for (size_t i = 0; i < 4; i++) {
2179 zero.binary_basis_limbs[i].maximum_value = zero.binary_basis_limbs[i].element.get_value();
2180 alsozero.binary_basis_limbs[i].maximum_value = alsozero.binary_basis_limbs[i].element.get_value();
2181 }
2182 zero.assert_is_not_equal(alsozero);
2183 bool result = CircuitChecker::check(builder);
2184 EXPECT_EQ(result, false);
2185 }
2186};
2187
2188// Define types for which the above tests will be constructed.
2189using CircuitTypes = testing::Types<typename bb::stdlib::bn254<UltraCircuitBuilder>::BaseField,
2194// Define the suite of tests.
2196
2197TYPED_TEST(stdlib_bigfield, assert_not_equal_regression)
2198{
2199 TestFixture::test_assert_not_equal_regression();
2200}
2201
2202TYPED_TEST(stdlib_bigfield, add_to_lower_limb_regression)
2203{
2204 TestFixture::test_add_to_lower_limb_regression();
2205}
2207{
2208 TestFixture::test_bad_mul();
2209}
2210
2211TYPED_TEST(stdlib_bigfield, division_formula_regression)
2212{
2213 TestFixture::test_division_formula_bug();
2214}
2216{
2217 TestFixture::test_basic_tag_logic();
2218}
2219TYPED_TEST(stdlib_bigfield, test_constructor)
2220{
2221 TestFixture::test_constructor_from_two_elements();
2222}
2223TYPED_TEST(stdlib_bigfield, test_unsafe_construct_from_limbs)
2224{
2225 TestFixture::test_unsafe_construct_from_limbs();
2226}
2227TYPED_TEST(stdlib_bigfield, test_construct_from_limbs)
2228{
2229 TestFixture::test_construct_from_limbs();
2230}
2231TYPED_TEST(stdlib_bigfield, test_construct_from_limbs_fails)
2232{
2233 TestFixture::test_construct_from_limbs_fails();
2234}
2236{
2237 TestFixture::test_add_two(InputType::WITNESS, InputType::WITNESS, InputType::WITNESS);
2238}
2239TYPED_TEST(stdlib_bigfield, add_two_with_constants)
2240{
2241 TestFixture::test_add_two(InputType::WITNESS, InputType::WITNESS, InputType::CONSTANT);
2242 TestFixture::test_add_two(InputType::WITNESS, InputType::CONSTANT, InputType::WITNESS);
2243 TestFixture::test_add_two(InputType::WITNESS, InputType::CONSTANT, InputType::CONSTANT);
2244 TestFixture::test_add_two(InputType::CONSTANT, InputType::WITNESS, InputType::WITNESS);
2245 TestFixture::test_add_two(InputType::CONSTANT, InputType::WITNESS, InputType::CONSTANT);
2246 TestFixture::test_add_two(InputType::CONSTANT, InputType::CONSTANT, InputType::WITNESS);
2247 TestFixture::test_add_two(InputType::CONSTANT, InputType::CONSTANT, InputType::CONSTANT);
2248}
2250{
2251 TestFixture::test_sum(InputType::WITNESS);
2252}
2253TYPED_TEST(stdlib_bigfield, sum_with_mixed_inputs)
2254{
2255 TestFixture::test_sum(InputType::WITNESS, true);
2256}
2257TYPED_TEST(stdlib_bigfield, sum_with_constant)
2258{
2259 TestFixture::test_sum(InputType::CONSTANT);
2260}
2262{
2263 TestFixture::test_mul(InputType::WITNESS, InputType::WITNESS);
2264}
2265TYPED_TEST(stdlib_bigfield, mul_with_constant)
2266{
2267 TestFixture::test_mul(InputType::WITNESS, InputType::CONSTANT);
2268 TestFixture::test_mul(InputType::CONSTANT, InputType::WITNESS);
2269 TestFixture::test_mul(InputType::CONSTANT, InputType::CONSTANT);
2270}
2272{
2273 TestFixture::test_sub(InputType::WITNESS, InputType::WITNESS);
2274}
2275TYPED_TEST(stdlib_bigfield, sub_with_constant)
2276{
2277 TestFixture::test_sub(InputType::WITNESS, InputType::CONSTANT);
2278 TestFixture::test_sub(InputType::CONSTANT, InputType::WITNESS);
2279 TestFixture::test_sub(InputType::CONSTANT, InputType::CONSTANT);
2280}
2282{
2283 TestFixture::test_add(InputType::WITNESS, InputType::WITNESS);
2284}
2285TYPED_TEST(stdlib_bigfield, add_with_constant)
2286{
2287 TestFixture::test_add(InputType::WITNESS, InputType::CONSTANT);
2288 TestFixture::test_add(InputType::CONSTANT, InputType::WITNESS);
2289 TestFixture::test_add(InputType::CONSTANT, InputType::CONSTANT);
2290}
2292{
2293 TestFixture::test_div(InputType::WITNESS, InputType::WITNESS); // w / w
2294}
2295TYPED_TEST(stdlib_bigfield, div_with_constant)
2296{
2297 TestFixture::test_div(InputType::WITNESS, InputType::CONSTANT); // w / c
2298 TestFixture::test_div(InputType::CONSTANT, InputType::WITNESS); // c / w
2299 TestFixture::test_div(InputType::CONSTANT, InputType::CONSTANT); // c / c
2300}
2302{
2303 TestFixture::test_sqr(InputType::WITNESS);
2304}
2305TYPED_TEST(stdlib_bigfield, sqr_with_constant)
2306{
2307 TestFixture::test_sqr(InputType::CONSTANT);
2308}
2310{
2311 TestFixture::test_negate(InputType::WITNESS);
2312}
2314{
2315 TestFixture::test_mul_assign(InputType::WITNESS, InputType::WITNESS);
2316}
2317TYPED_TEST(stdlib_bigfield, mul_assignment_with_constant)
2318{
2319 TestFixture::test_mul_assign(InputType::WITNESS, InputType::CONSTANT);
2320 TestFixture::test_mul_assign(InputType::CONSTANT, InputType::WITNESS);
2321 TestFixture::test_mul_assign(InputType::CONSTANT, InputType::CONSTANT);
2322}
2324{
2325 TestFixture::test_add_assign(InputType::WITNESS, InputType::WITNESS);
2326}
2327TYPED_TEST(stdlib_bigfield, add_assignment_with_constant)
2328{
2329 TestFixture::test_add_assign(InputType::WITNESS, InputType::CONSTANT);
2330 TestFixture::test_add_assign(InputType::CONSTANT, InputType::WITNESS);
2331 TestFixture::test_add_assign(InputType::CONSTANT, InputType::CONSTANT);
2332}
2334{
2335 TestFixture::test_sub_assign(InputType::WITNESS, InputType::WITNESS);
2336}
2337TYPED_TEST(stdlib_bigfield, sub_assignment_with_constant)
2338{
2339 TestFixture::test_sub_assign(InputType::WITNESS, InputType::CONSTANT);
2340 TestFixture::test_sub_assign(InputType::CONSTANT, InputType::WITNESS);
2341 TestFixture::test_sub_assign(InputType::CONSTANT, InputType::CONSTANT);
2342}
2344{
2345 TestFixture::test_div_assign(InputType::WITNESS, InputType::WITNESS); // w / w
2346}
2347TYPED_TEST(stdlib_bigfield, div_assignment_with_constant)
2348{
2349 TestFixture::test_div_assign(InputType::WITNESS, InputType::CONSTANT); // w / c
2350 TestFixture::test_div_assign(InputType::CONSTANT, InputType::WITNESS); // c / w
2351 TestFixture::test_div_assign(InputType::CONSTANT, InputType::CONSTANT); // c / c
2352}
2354{
2355 TestFixture::test_madd(InputType::WITNESS, InputType::WITNESS, InputType::CONSTANT); // w * w + w
2356}
2357TYPED_TEST(stdlib_bigfield, madd_with_constants)
2358{
2359 TestFixture::test_madd(InputType::WITNESS, InputType::WITNESS, InputType::CONSTANT); // w * w + c
2360 TestFixture::test_madd(InputType::WITNESS, InputType::CONSTANT, InputType::WITNESS); // w * c + w
2361 TestFixture::test_madd(InputType::WITNESS, InputType::CONSTANT, InputType::CONSTANT); // w * c + c
2362 TestFixture::test_madd(InputType::CONSTANT, InputType::WITNESS, InputType::WITNESS); // c * w + w
2363 TestFixture::test_madd(InputType::CONSTANT, InputType::WITNESS, InputType::CONSTANT); // c * w + c
2364 TestFixture::test_madd(InputType::WITNESS, InputType::WITNESS, InputType::CONSTANT); // w * w + c
2365 TestFixture::test_madd(InputType::WITNESS, InputType::CONSTANT, InputType::WITNESS); // w * c + w
2366 TestFixture::test_madd(InputType::CONSTANT, InputType::WITNESS, InputType::CONSTANT); // c * w + c
2367}
2369{
2370 TestFixture::test_sqradd(InputType::WITNESS, InputType::WITNESS); // w^2 + w
2371}
2372TYPED_TEST(stdlib_bigfield, sqradd_with_constant)
2373{
2374 TestFixture::test_sqradd(InputType::WITNESS, InputType::CONSTANT); // w^2 + c
2375 TestFixture::test_sqradd(InputType::CONSTANT, InputType::WITNESS); // c^2 + w
2376 TestFixture::test_sqradd(InputType::CONSTANT, InputType::CONSTANT); // c^2 + c
2377}
2379{
2380 TestFixture::test_mult_madd(InputType::WITNESS, InputType::WITNESS, InputType::WITNESS); // ∑ (w * w + w)
2381}
2382TYPED_TEST(stdlib_bigfield, mult_madd_with_constants)
2383{
2384 TestFixture::test_mult_madd(InputType::WITNESS, InputType::WITNESS, InputType::CONSTANT); // ∑ (w * w + c)
2385 TestFixture::test_mult_madd(InputType::WITNESS, InputType::CONSTANT, InputType::WITNESS); // ∑ (w * c + w)
2386 TestFixture::test_mult_madd(InputType::WITNESS, InputType::CONSTANT, InputType::CONSTANT); // ∑ (w * c + c)
2387 TestFixture::test_mult_madd(InputType::CONSTANT, InputType::CONSTANT, InputType::CONSTANT); // ∑ (c * c + c)
2388}
2389TYPED_TEST(stdlib_bigfield, mult_madd_edge_cases)
2390{
2391 // all witness except the last one
2392 TestFixture::test_mult_madd(InputType::WITNESS, InputType::WITNESS, InputType::WITNESS, true);
2393 // all constant except the last one
2394 TestFixture::test_mult_madd(InputType::CONSTANT, InputType::CONSTANT, InputType::CONSTANT, true);
2395}
2397{
2398 TestFixture::test_dual_madd();
2399}
2400TYPED_TEST(stdlib_bigfield, div_without_denominator_check)
2401{
2402 TestFixture::test_div_without_denominator_check(InputType::WITNESS, InputType::WITNESS); // w / w
2403}
2404TYPED_TEST(stdlib_bigfield, div_without_denominator_check_with_constant)
2405{
2406 TestFixture::test_div_without_denominator_check(InputType::WITNESS, InputType::CONSTANT); // w / c
2407 TestFixture::test_div_without_denominator_check(InputType::CONSTANT, InputType::WITNESS); // c / w
2408 TestFixture::test_div_without_denominator_check(InputType::CONSTANT, InputType::CONSTANT); // c / c
2409}
2411{
2412 TestFixture::test_add_and_div();
2413}
2415{
2416 TestFixture::test_add_and_mul(InputType::WITNESS); // (w + w) * (w + w)
2417}
2418TYPED_TEST(stdlib_bigfield, add_and_mul_with_constants)
2419{
2420 TestFixture::test_add_and_mul(InputType::CONSTANT); // (w + c) * (w + c)
2421}
2423{
2424 TestFixture::test_sub_and_mul(InputType::WITNESS); // (w - w) * (w - w)
2425}
2426TYPED_TEST(stdlib_bigfield, sub_and_mul_with_constants)
2427{
2428 TestFixture::test_sub_and_mul(InputType::CONSTANT); // (w - c) * (w - c)
2429}
2431{
2432 TestFixture::test_msub_div(
2433 InputType::WITNESS, InputType::WITNESS, InputType::WITNESS); // (-w * w - w - w) / (w - w)
2434}
2435TYPED_TEST(stdlib_bigfield, msub_div_with_constants)
2436{
2437 TestFixture::test_msub_div(
2438 InputType::WITNESS, InputType::WITNESS, InputType::CONSTANT); // (-w * w - w - c) / (w - w)
2439 TestFixture::test_msub_div(
2440 InputType::WITNESS, InputType::CONSTANT, InputType::WITNESS); // (-w * c - w - w) / (w - w)
2441 TestFixture::test_msub_div(
2442 InputType::WITNESS, InputType::CONSTANT, InputType::CONSTANT); // (-w * c - w - c) / (w - w)
2443 TestFixture::test_msub_div(
2444 InputType::CONSTANT, InputType::WITNESS, InputType::WITNESS); // (-c * w - c - w) / (w - w)
2445 TestFixture::test_msub_div(
2446 InputType::CONSTANT, InputType::WITNESS, InputType::CONSTANT); // (-c * w - c - c) / (w - w)
2447 TestFixture::test_msub_div(
2448 InputType::CONSTANT, InputType::CONSTANT, InputType::CONSTANT); // (-c * c - c - c) / (w - w)
2449}
2450TYPED_TEST(stdlib_bigfield, conditional_assign)
2451{
2452 TestFixture::test_conditional_assign(InputType::WITNESS, InputType::WITNESS, InputType::WITNESS); // w ? w : w
2453}
2454TYPED_TEST(stdlib_bigfield, conditional_assign_with_constants)
2455{
2456 TestFixture::test_conditional_assign(InputType::WITNESS, InputType::WITNESS, InputType::CONSTANT); // w ? w : c
2457 TestFixture::test_conditional_assign(InputType::WITNESS, InputType::CONSTANT, InputType::WITNESS); // w ? c : w
2458 TestFixture::test_conditional_assign(InputType::WITNESS, InputType::CONSTANT, InputType::CONSTANT); // w ? c : c
2459 TestFixture::test_conditional_assign(InputType::CONSTANT, InputType::WITNESS, InputType::WITNESS); // c ? w : w
2460 TestFixture::test_conditional_assign(InputType::CONSTANT, InputType::WITNESS, InputType::CONSTANT); // c ? w : c
2461 TestFixture::test_conditional_assign(InputType::CONSTANT, InputType::CONSTANT, InputType::CONSTANT); // c ? c : c
2462}
2463TYPED_TEST(stdlib_bigfield, conditional_select)
2464{
2465 TestFixture::test_conditional_select(InputType::WITNESS, InputType::WITNESS, InputType::WITNESS); // w ? w : w
2466}
2467TYPED_TEST(stdlib_bigfield, conditional_select_with_constants)
2468{
2469 TestFixture::test_conditional_select(InputType::WITNESS, InputType::WITNESS, InputType::CONSTANT); // w ? w : c
2470 TestFixture::test_conditional_select(InputType::WITNESS, InputType::CONSTANT, InputType::WITNESS); // w ? c : w
2471 TestFixture::test_conditional_select(InputType::WITNESS, InputType::CONSTANT, InputType::CONSTANT); // w ? c : c
2472 TestFixture::test_conditional_select(InputType::CONSTANT, InputType::WITNESS, InputType::WITNESS); // c ? w : w
2473 TestFixture::test_conditional_select(InputType::CONSTANT, InputType::WITNESS, InputType::CONSTANT); // c ? w : c
2474 TestFixture::test_conditional_select(InputType::CONSTANT, InputType::CONSTANT, InputType::CONSTANT); // c ? c : c
2475}
2476TYPED_TEST(stdlib_bigfield, msb_div_ctx_crash_regression)
2477{
2478 TestFixture::test_msub_div_ctx_crash_regression();
2479}
2480TYPED_TEST(stdlib_bigfield, conditional_negate)
2481{
2482 TestFixture::test_conditional_negate(InputType::WITNESS, InputType::WITNESS); // w ? -w : w
2483}
2484TYPED_TEST(stdlib_bigfield, conditional_negate_with_constants)
2485{
2486 TestFixture::test_conditional_negate(InputType::WITNESS, InputType::CONSTANT); // w ? -c : w
2487 TestFixture::test_conditional_negate(InputType::CONSTANT, InputType::WITNESS); // c ? -w : w
2488 TestFixture::test_conditional_negate(InputType::CONSTANT, InputType::CONSTANT); // c ? -c : c
2489}
2490TYPED_TEST(stdlib_bigfield, group_operations)
2491{
2492 // skip this test if the field is not bn254 base field
2494 GTEST_SKIP() << "skipping group operations test for non-bn254 base field";
2495 }
2496 TestFixture::test_group_operations();
2497}
2499{
2500 TestFixture::test_reduce();
2501}
2503{
2504 TestFixture::test_equality_operator(InputType::WITNESS, InputType::WITNESS); // w == w
2505}
2506TYPED_TEST(stdlib_bigfield, equality_with_constants)
2507{
2508 TestFixture::test_equality_operator(InputType::WITNESS, InputType::CONSTANT); // w == c
2509 TestFixture::test_equality_operator(InputType::CONSTANT, InputType::WITNESS); // c == w
2510 TestFixture::test_equality_operator(InputType::CONSTANT, InputType::CONSTANT); // c == c
2511}
2512
2513TYPED_TEST(stdlib_bigfield, unsafe_assert_less_than)
2514{
2515 TestFixture::test_unsafe_assert_less_than();
2516}
2517TYPED_TEST(stdlib_bigfield, unsafe_assert_less_than_fails)
2518{
2519 TestFixture::test_unsafe_assert_less_than_fails();
2520}
2521TYPED_TEST(stdlib_bigfield, unsafe_evaluate_multiply_add)
2522{
2523 TestFixture::test_unsafe_evaluate_multiply_add();
2524}
2525TYPED_TEST(stdlib_bigfield, unsafe_evaluate_multiply_add_fails)
2526{
2527 TestFixture::test_unsafe_evaluate_multiply_add_fails();
2528}
2529TYPED_TEST(stdlib_bigfield, unsafe_evaluate_multiple_multiply_add)
2530{
2531 TestFixture::test_unsafe_multiple_multiply_add();
2532}
2533TYPED_TEST(stdlib_bigfield, unsafe_evaluate_multiple_multiply_add_fails)
2534{
2535 TestFixture::test_unsafe_multiple_multiply_add_fails();
2536}
2537
2538TYPED_TEST(stdlib_bigfield, assert_is_in_field_success)
2539{
2540 TestFixture::test_assert_is_in_field_success();
2541}
2542TYPED_TEST(stdlib_bigfield, assert_is_in_field_fails)
2543{
2544 TestFixture::test_assert_is_in_field_fails();
2545}
2546TYPED_TEST(stdlib_bigfield, assert_less_than_success)
2547{
2548 TestFixture::test_assert_less_than_success();
2549}
2550TYPED_TEST(stdlib_bigfield, assert_less_than_fails)
2551{
2552 TestFixture::test_assert_less_than_fails();
2553}
2554TYPED_TEST(stdlib_bigfield, reduce_mod_target_modulus)
2555{
2556 TestFixture::test_reduce_mod_target_modulus();
2557}
2558TYPED_TEST(stdlib_bigfield, byte_array_constructors)
2559{
2560 TestFixture::test_byte_array_constructors();
2561}
2562TYPED_TEST(stdlib_bigfield, byte_array_constructor_boundary_values)
2563{
2564 TestFixture::test_byte_array_constructor_boundary_values();
2565}
2566TYPED_TEST(stdlib_bigfield, quotient_completeness_regression)
2567{
2568 TestFixture::test_quotient_completeness();
2569}
2570
2571TYPED_TEST(stdlib_bigfield, conditional_select_regression)
2572{
2573 TestFixture::test_conditional_select_regression();
2574}
2575
2576TYPED_TEST(stdlib_bigfield, division_context)
2577{
2578 TestFixture::test_division_context();
2579}
2580
2582{
2583 TestFixture::test_inversion();
2584}
2585
2586TYPED_TEST(stdlib_bigfield, assert_equal_not_equal)
2587{
2588 TestFixture::test_assert_equal_not_equal();
2589}
2590
2592{
2593 TestFixture::test_pow();
2594}
2595
2597{
2598 TestFixture::test_pow_one();
2599}
2600TYPED_TEST(stdlib_bigfield, nonnormalized_field_bug_regression)
2601{
2602 TestFixture::test_nonnormalized_field_bug_regression();
2603}
2604
2605TYPED_TEST(stdlib_bigfield, internal_div_bug_regression)
2606{
2607 TestFixture::test_internal_div_regression();
2608 TestFixture::test_internal_div_regression2();
2609 TestFixture::test_internal_div_regression3();
2610}
2611
2612// Zero value should always pass, regardless of predicate value
2613TYPED_TEST(stdlib_bigfield, assert_zero_if_zero_value)
2614{
2615 // predicate=true, witness predicate
2616 TestFixture::test_assert_zero_if_zero_value(true, InputType::WITNESS);
2617 // predicate=true, constant predicate
2618 TestFixture::test_assert_zero_if_zero_value(true, InputType::CONSTANT);
2619 // predicate=false, witness predicate
2620 TestFixture::test_assert_zero_if_zero_value(false, InputType::WITNESS);
2621 // predicate=false, constant predicate
2622 TestFixture::test_assert_zero_if_zero_value(false, InputType::CONSTANT);
2623}
2624
2625// Non-zero value with false predicate should pass (check is skipped)
2626TYPED_TEST(stdlib_bigfield, assert_zero_if_nonzero_value_false_predicate)
2627{
2628 TestFixture::test_assert_zero_if_nonzero_value(false, InputType::WITNESS, InputType::WITNESS, true);
2629 TestFixture::test_assert_zero_if_nonzero_value(false, InputType::WITNESS, InputType::CONSTANT, true);
2630 TestFixture::test_assert_zero_if_nonzero_value(false, InputType::CONSTANT, InputType::WITNESS, true);
2631 TestFixture::test_assert_zero_if_nonzero_value(false, InputType::CONSTANT, InputType::CONSTANT, true);
2632}
2633
2634// Non-zero value with true predicate should fail
2635TYPED_TEST(stdlib_bigfield, assert_zero_if_nonzero_value_true_predicate_fails)
2636{
2637 TestFixture::test_assert_zero_if_nonzero_value(true, InputType::WITNESS, InputType::WITNESS, false);
2638 TestFixture::test_assert_zero_if_nonzero_value(true, InputType::WITNESS, InputType::CONSTANT, false);
2639 TestFixture::test_assert_zero_if_nonzero_value(true, InputType::CONSTANT, InputType::WITNESS, false);
2640 // Note: We don't test (CONSTANT, CONSTANT) because when both value and predicate are constants,
2641 // the assertion is evaluated at circuit construction time, not at verification time.
2642}
2643
2644// Computed zero (from subtraction) should pass with true predicate
2645TYPED_TEST(stdlib_bigfield, assert_zero_if_computed_zero)
2646{
2647 TestFixture::test_assert_zero_if_computed_zero();
2648}
2649
2651{
2652 using Builder = TypeParam::field_ct::Builder;
2653 using fq_ct = TypeParam;
2655
2657
2658 auto [c_native, c_ct] = TestFixture::get_random_witness(&builder);
2659 BB_ASSERT_LT(static_cast<uint256_t>(c_native), fq_ct::modulus);
2660
2661 // c_ct < modulus
2662 auto is_ok = c_ct.is_less_than(fq_ct::modulus);
2663 is_ok.assert_equal(stdlib::bool_t<Builder>(true));
2664
2665 // c_ct not smaller than itself
2666 auto is_not_ok = c_ct.is_less_than(c_native);
2667 is_not_ok.assert_equal(stdlib::bool_t<Builder>(false));
2668
2669 // c_ct < c_native + 1 (edge case)
2670 auto is_ok_edge_case = c_ct.is_less_than(c_native + 1);
2671 is_ok_edge_case.assert_equal(stdlib::bool_t<Builder>(true));
2672
2673 // c_ct > modulus fails comparison but doesn't make the circuit fail
2674 std::vector<uint8_t> c_bytes(32, 0xff);
2676 // For bn254, NUM_LAST_LIMB_BITS = 50, so we need to set the first byte to something bigger than 0x30 (the first
2677 // byte of the modulus) that still fits in 50 bits
2678 c_bytes[0] = 0x31;
2679 }
2680 byte_array_ct c_byte_array = byte_array_ct(&builder, c_bytes);
2681 fq_ct reconstructed_from_bytes(c_byte_array);
2682 auto is_not_ok_larger_than_modulus = reconstructed_from_bytes.is_less_than(fq_ct::modulus);
2683 is_not_ok_larger_than_modulus.assert_equal(stdlib::bool_t<Builder>(false));
2684
2685 EXPECT_TRUE(CircuitChecker::check(builder));
2686 EXPECT_FALSE(builder.failed());
2687}
#define BB_ASSERT_LT(left, right,...)
Definition assert.hpp:143
#define BINARY_OP_TEST(op_name, bench_name, op_symbol, repetitions, reduced_inputs, reduction_after)
typename extract_builder< BigField >::type builder_t
typename extract_fq_params< BigField >::type params_t
InputType
constexpr InputType operator!(InputType type)
#define ASSIGNMENT_OP_TEST(op_name, bench_name, op_symbol, repetitions, reduced_inputs, reduction_after)
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
element class. Implements ecc group arithmetic using Jacobian coordinates See https://hyperelliptic....
Definition element.hpp:35
virtual uint8_t get_random_uint8()=0
virtual uint32_t get_random_uint32()=0
virtual uint256_t get_random_uint256()=0
constexpr uint256_t slice(uint64_t start, uint64_t end) const
std::pair< uintx, uintx > divmod(const uintx &b) const
static void unsafe_evaluate_multiple_multiply_add(const std::vector< bigfield > &input_left, const std::vector< bigfield > &input_right, const std::vector< bigfield > &to_add, const bigfield &input_quotient, const std::vector< bigfield > &input_remainders)
static void unsafe_evaluate_multiply_add(const bigfield &input_left, const bigfield &input_to_mul, const std::vector< bigfield > &to_add, const bigfield &input_quotient, const std::vector< bigfield > &input_remainders)
static void unsafe_assert_less_than(const bigfield &input, const uint256_t &upper_limit)
Implements boolean logic in-circuit.
Definition bool.hpp:60
bool get_value() const
Definition bool.hpp:125
Represents a dynamic array of bytes in-circuit.
void set_origin_tag(bb::OriginTag tag)
static witness_t create_constant_witness(Builder *parent_context, const bb::fr &in)
Definition witness.hpp:45
typename bb::stdlib::bn254< Builder >::ScalarField fr_ct
static void test_nonnormalized_field_bug_regression()
static void test_internal_div_regression3()
static void test_conditional_assign(InputType a_type, InputType b_type, InputType predicate_type)
static void test_reduce_mod_target_modulus()
static void test_group_operations()
static void test_assert_zero_if_zero_value(bool predicate_value, InputType predicate_type)
static void test_sum(InputType a_type, bool mixed_inputs=false)
static void test_assert_is_in_field_fails()
static void test_construct_from_limbs()
static void test_bad_mul()
static void test_add_to_lower_limb_regression()
static void test_assert_less_than_fails()
static void test_equality_operator(InputType a_type, InputType b_type)
static void test_conditional_select(InputType a_type, InputType b_type, InputType predicate_type)
static void test_pow()
static void test_add_and_mul(InputType summand_type)
static std::pair< std::vector< fq_native >, std::vector< fq_ct > > get_random_witnesses(Builder *builder, size_t num, bool reduce_input=false)
static void test_unsafe_multiple_multiply_add()
static void test_quotient_completeness()
static void test_unsafe_evaluate_multiply_add()
static void test_msub_div_ctx_crash_regression()
static void test_unsafe_multiple_multiply_add_fails()
static void test_constructor_from_two_elements()
static void test_reduce()
static void test_assert_less_than_success()
static std::pair< fq_native, fq_ct > get_random_constant(Builder *builder, bool reduce_input=false)
static void test_sqradd(InputType a_type, InputType b_type)
builder_t< BigField > Builder
static void test_assert_is_in_field_success()
static void test_add_two(InputType a_type, InputType b_type, InputType c_type)
static void test_inversion()
static void test_add_and_div()
static void test_byte_array_constructors()
static void test_negate(InputType a_type)
static void test_byte_array_constructor_boundary_values()
static void test_msub_div(InputType multiplicand_type, InputType to_sub_type, InputType divisor_type)
static void test_binary_operator_generic(InputType a_type, InputType b_type, CircuitOpFunc circuit_op, NativeOpFunc native_op, const char *op_name, size_t num_repetitions=10, bool need_reduced_inputs=false, bool need_reduction_after=false)
static std::pair< fq_native, fq_ct > get_random_element(Builder *builder, bool reduce_input=false)
static std::pair< fq_native, fq_ct > get_random_witness(Builder *builder, bool reduce_input=false)
static std::pair< std::vector< fq_native >, std::vector< fq_ct > > get_random_elements(Builder *builder, InputType type, size_t num, bool reduce_input=false)
static void test_sqr(InputType a_type)
static std::pair< fq_native, fq_ct > get_random_element(Builder *builder, InputType type, bool reduce_input=false)
static void test_div_without_denominator_check(InputType a_type, InputType b_type)
static void test_sub_and_mul(InputType subtrahend_type)
static void test_assert_not_equal_regression()
static void test_internal_div_regression2()
static void test_basic_tag_logic()
static void test_madd(InputType a_type, InputType b_type, InputType c_type)
static void test_conditional_negate(InputType a_type, InputType predicate_type)
static void test_unsafe_construct_from_limbs()
stdlib::bool_t< Builder > bool_ct
static void test_assert_zero_if_nonzero_value(bool predicate_value, InputType value_type, InputType predicate_type, bool expect_circuit_check_pass)
static std::pair< std::vector< fq_native >, std::vector< fq_ct > > get_random_constants(Builder *builder, size_t num, bool reduce_input=false)
static void test_assign_operator_generic(InputType a_type, InputType b_type, CircuitOpFunc circuit_op, NativeOpFunc native_op, const char *op_name, size_t num_repetitions=4, bool need_reduced_inputs=false, bool need_reduction_after=false)
static void test_internal_div_regression()
static void test_assert_zero_if_computed_zero()
static void test_dual_madd()
static void test_mult_madd(InputType left_type, InputType right_type, InputType to_add_type, bool edge_case=false)
stdlib::witness_t< Builder > witness_ct
static void test_unsafe_assert_less_than_fails()
static void test_unsafe_assert_less_than()
static void test_construct_from_limbs_fails()
static void test_assert_equal_not_equal()
static void test_unsafe_evaluate_multiply_add_fails()
static void test_conditional_select_regression()
static void test_division_context()
static void test_division_formula_bug()
bb::field< params_t< BigField > > fq_native
static void test_pow_one()
#define BENCH_GATE_COUNT_END(builder, op_name)
Definition log.hpp:18
#define BENCH_GATE_COUNT_START(builder, op_name)
Definition log.hpp:15
#define info(...)
Definition log.hpp:93
AluTraceBuilder builder
Definition alu.test.cpp:124
FF a
FF b
ECCVMCircuitBuilder Builder
numeric::RNG & engine
bn254::BaseField fq_ct
stdlib::byte_array< Builder > byte_array_ct
crypto::Poseidon2Bn254ScalarFieldParams Params
uintx< uint256_t > uint512_t
Definition uintx.hpp:309
RNG & get_debug_randomness(bool reset, std::uint_fast64_t seed)
Definition engine.cpp:245
uintx< uint512_t > uint1024_t
Definition uintx.hpp:311
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
TYPED_TEST_SUITE(CommitmentKeyTest, Curves)
@ SUB
Subtract two field elements.
@ DIV
Divide two field elements.
@ MUL
Multiply two field elements.
@ ADD_ASSIGN
Add-assign operation.
@ DIV_ASSIGN
Divide-assign operation.
@ MUL_ASSIGN
Multiply-assign operation.
@ ADD
Add two field elements.
@ SUB_ASSIGN
Subtract-assign operation.
field< Bn254FrParams > fr
Definition fr.hpp:155
C slice(C const &container, size_t start)
Definition container.hpp:9
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
This file contains part of the logic for the Origin Tag mechanism that tracks the use of in-circuit p...
#define STANDARD_TESTING_TAGS
testing::Types< bb::MegaCircuitBuilder, bb::UltraCircuitBuilder > CircuitTypes
General class for prime fields see Prime field documentation["field documentation"] for general imple...
BB_INLINE constexpr field from_montgomery_form_reduced() const noexcept
static constexpr uint256_t modulus
BB_INLINE constexpr field pow(const uint256_t &exponent) const noexcept
constexpr field invert() const noexcept
static field random_element(numeric::RNG *engine=nullptr) noexcept
BB_INLINE constexpr field sqr() const noexcept
static void serialize_to_buffer(const field &value, uint8_t *buffer)
BB_INLINE constexpr field reduce_once() const noexcept
static constexpr field zero()