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 // This check tests if elements are reduced to fit quotient into range proof
1477 {
1478 auto builder = Builder();
1479 const uint256_t input =
1480 uint256_t(0xfffffffffffffffe, 0xffffffffffffffff, 0xffffffffffffffff, 0x3fffffffffffffff);
1481
1482 fq_ct a(witness_ct(&builder, fr(uint256_t(input).slice(0, fq_ct::NUM_LIMB_BITS * 2))),
1483 witness_ct(&builder, fr(uint256_t(input).slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 4))),
1484 false);
1485 auto a1 = a;
1486 auto a2 = a;
1487 auto a3 = a;
1488 auto a4 = a;
1489
1490 for (auto i = 0; i < 8; i++) {
1491 a = a + a;
1492 a1 = a1 + a1;
1493 a2 = a2 + a2;
1494 a3 = a3 + a3;
1495 a4 = a4 + a4;
1496 }
1497
1498 auto b = a * a;
1499 (void)b;
1500
1501 auto c = a1.sqr();
1502 (void)c;
1503
1504 auto d = a2.sqradd({});
1505 (void)d;
1506
1507 auto e = a3.madd(a3, {});
1508 (void)e;
1509
1510 auto f = fq_ct::mult_madd({ a4 }, { a4 }, {}, false);
1511 (void)f;
1512
1513 bool result = CircuitChecker::check(builder);
1514 EXPECT_EQ(result, true);
1515 }
1516
1518 {
1519 auto builder = Builder();
1520 fq_native a(0);
1521 fq_native b(1);
1522 fq_ct a_ct(&builder, a);
1523 fq_ct b_ct(&builder, b);
1524 fq_ct selected = a_ct.conditional_select(b_ct, bool_ct(&builder, true));
1525 EXPECT_EQ(fq_native((selected.get_value() % uint512_t(fq_native::modulus)).lo), b);
1526 }
1527
1529 {
1530 auto builder = Builder();
1531 fq_native a(1);
1532 fq_ct a_ct(&builder, a);
1533 fq_ct ret = fq_ct::div_check_denominator_nonzero({}, a_ct);
1534 EXPECT_NE(ret.get_context(), nullptr);
1535 }
1536
1537 static void test_inversion()
1538 {
1539 fq_ct a = fq_ct(-7);
1540 fq_ct a_inverse = a.invert();
1541 fq_ct a_inverse_division = fq_ct(1) / a;
1542
1543 fq_native a_native = fq_native(-7);
1544 fq_native a_native_inverse = a_native.invert();
1545 EXPECT_EQ(fq_native((a.get_value() % uint512_t(fq_native::modulus)).lo), a_native);
1546 EXPECT_EQ(fq_native((a_inverse.get_value() % uint512_t(fq_native::modulus)).lo), a_native_inverse);
1547 EXPECT_EQ(fq_native((a_inverse_division.get_value() % uint512_t(fq_native::modulus)).lo), a_native_inverse);
1548 }
1549
1551 {
1552 auto builder = Builder();
1553 size_t num_repetitions = 10;
1554 for (size_t i = 0; i < num_repetitions; ++i) {
1555 auto [a_native, a_ct] = get_random_witness(&builder); // fq_native, fq_ct
1556 auto [c_native, c_ct] = get_random_witness(&builder); // fq_native, fq_ct
1557 auto [d_native, d_ct] = get_random_witness(&builder); // fq_native, fq_ct
1558
1559 fq_ct two_ct = fq_ct::unsafe_construct_from_limbs(witness_ct(&builder, fr(2)),
1560 witness_ct(&builder, fr(0)),
1561 witness_ct(&builder, fr(0)),
1562 witness_ct(&builder, fr(0)));
1563 fq_ct t0 = a_ct + a_ct;
1564 fq_ct t1 = a_ct * two_ct;
1565
1566 t0.assert_equal(t1);
1567 t0.assert_is_not_equal(c_ct);
1568 t0.assert_is_not_equal(d_ct);
1569 stdlib::bool_t<Builder> is_equal_a = t0 == t1;
1570 stdlib::bool_t<Builder> is_equal_b = t0 == c_ct;
1571 EXPECT_TRUE(is_equal_a.get_value());
1572 EXPECT_FALSE(is_equal_b.get_value());
1573 }
1574 bool result = CircuitChecker::check(builder);
1575 EXPECT_EQ(result, true);
1576 }
1577
1578 // Test assert_zero_if with a zero bigfield - should always pass regardless of predicate
1579 static void test_assert_zero_if_zero_value(bool predicate_value, InputType predicate_type)
1580 {
1581 auto builder = Builder();
1582
1583 // Create a zero bigfield as witness
1584 fq_ct zero_ct = fq_ct::create_from_u512_as_witness(&builder, uint256_t(0));
1585
1586 // Create predicate
1587 bool_ct predicate;
1588 if (predicate_type == InputType::WITNESS) {
1589 predicate = bool_ct(witness_ct(&builder, predicate_value));
1590 } else {
1591 predicate = bool_ct(&builder, predicate_value);
1592 }
1593
1594 // Should always pass: zero bigfield satisfies the constraint regardless of predicate
1595 zero_ct.assert_zero_if(predicate);
1596
1597 bool result = CircuitChecker::check(builder);
1598 EXPECT_EQ(result, true);
1599 }
1600
1601 // Test assert_zero_if with a non-zero bigfield
1602 // - predicate=false: should pass (check is skipped)
1603 // - predicate=true: should fail (non-zero value violates constraint)
1604 static void test_assert_zero_if_nonzero_value(bool predicate_value,
1605 InputType value_type,
1606 InputType predicate_type,
1607 bool expect_circuit_check_pass)
1608 {
1609 auto builder = Builder();
1610
1611 // Create a non-zero bigfield
1612 fq_ct nonzero_ct;
1613 if (value_type == InputType::WITNESS) {
1614 nonzero_ct = fq_ct::create_from_u512_as_witness(&builder, uint256_t(42));
1615 } else {
1616 nonzero_ct = fq_ct(&builder, uint256_t(42));
1617 }
1618
1619 // Create predicate
1620 bool_ct predicate;
1621 if (predicate_type == InputType::WITNESS) {
1622 predicate = bool_ct(witness_ct(&builder, predicate_value));
1623 } else {
1624 predicate = bool_ct(&builder, predicate_value);
1625 }
1626
1627 nonzero_ct.assert_zero_if(predicate);
1628
1629 bool result = CircuitChecker::check(builder);
1630 EXPECT_EQ(result, expect_circuit_check_pass);
1631 }
1632
1633 // Test assert_zero_if with computed zero (result of subtraction) - should pass
1635 {
1636 auto builder = Builder();
1637
1638 // Create two equal bigfields and subtract to get zero
1639 auto [a_native, a_ct] = get_random_witness(&builder);
1640 fq_ct b_ct = fq_ct::create_from_u512_as_witness(&builder, uint256_t(a_native));
1641
1642 // Compute a - a which should be zero
1643 fq_ct zero_ct = a_ct - b_ct;
1644
1645 // We need reduction so that the value is actually zero (k * p is not allowed)
1646 zero_ct.self_reduce();
1647
1648 // Create true predicate as witness
1649 bool_ct predicate = bool_ct(witness_ct(&builder, true));
1650
1651 // This should pass: computed zero with true predicate
1652 zero_ct.assert_zero_if(predicate);
1653
1654 bool result = CircuitChecker::check(builder);
1655 EXPECT_EQ(result, true);
1656 }
1657
1658 static void test_pow()
1659 {
1661
1663 uint32_t exponent_val = engine.get_random_uint32();
1664 // Set the high bit
1665 exponent_val |= static_cast<uint32_t>(1) << 31;
1666 fq_ct base_constant(&builder, static_cast<uint256_t>(base_val));
1667 fq_ct base_witness_ct = fq_ct::from_witness(&builder, typename fq_ct::native(base_val));
1668 // This also tests for the case where the exponent is zero
1669 for (size_t i = 0; i <= 32; i += 4) {
1670 uint32_t current_exponent_val = exponent_val >> i;
1671 fq_native expected = base_val.pow(current_exponent_val);
1672
1673 // Check for constant bigfield element with constant exponent
1674 fq_ct result_constant_base = base_constant.pow(current_exponent_val);
1675 EXPECT_EQ(fq_native(result_constant_base.get_value()), expected);
1676
1677 // Check for witness base with constant exponent
1678 fq_ct result_witness_base = base_witness_ct.pow(current_exponent_val);
1679 EXPECT_EQ(fq_native(result_witness_base.get_value()), expected);
1680 }
1681
1682 bool check_result = CircuitChecker::check(builder);
1683 EXPECT_EQ(check_result, true);
1684 }
1685
1686 static void test_pow_one()
1687 {
1689
1691
1692 uint32_t current_exponent_val = 1;
1693 fq_ct base_constant_ct(&builder, static_cast<uint256_t>(base_val));
1694 fq_ct base_witness_ct = fq_ct::from_witness(&builder, typename fq_ct::native(base_val));
1695 fq_native expected = base_val.pow(current_exponent_val);
1696
1697 // Check for constant bigfield element with constant exponent
1698 fq_ct result_constant_base = base_constant_ct.pow(current_exponent_val);
1699 EXPECT_EQ(fq_native(result_constant_base.get_value()), expected);
1700
1701 // Check for witness base with constant exponent
1702 fq_ct result_witness_base = base_witness_ct.pow(current_exponent_val);
1703 EXPECT_EQ(fq_native(result_witness_base.get_value()), expected);
1704
1705 bool check_result = CircuitChecker::check(builder);
1706 EXPECT_EQ(check_result, true);
1707 }
1708
1710 {
1711 auto builder = Builder();
1712 size_t num_repetitions = 10;
1713 constexpr size_t num_bits = 200;
1714 constexpr uint256_t bit_mask = (uint256_t(1) << num_bits) - 1;
1715 for (size_t i = 0; i < num_repetitions; ++i) {
1716
1717 uint256_t a_u256 = uint256_t(fq_native::random_element()) & bit_mask;
1718 uint256_t b_u256 = uint256_t(fq_native::random_element()) & bit_mask;
1719
1720 // Construct 200-bit bigfield elements
1721 fq_ct a_ct(witness_ct(&builder, fr(a_u256.slice(0, fq_ct::NUM_LIMB_BITS * 2))),
1722 witness_ct(&builder, fr(a_u256.slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 4))));
1723 fq_ct b_ct(witness_ct(&builder, fr(b_u256.slice(0, fq_ct::NUM_LIMB_BITS * 2))),
1724 witness_ct(&builder, fr(b_u256.slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 4))));
1725
1726 // Assert a, b < 2^200
1729 EXPECT_EQ(a_ct.get_value().get_msb() < num_bits, true);
1730 EXPECT_EQ(b_ct.get_value().get_msb() < num_bits, true);
1731 }
1732
1733 // Also test when: p < a < bound
1734 // define a = p + small_random_value
1735 uint256_t small_mask = (uint256_t(1) << 16) - 1;
1736 uint256_t a_u256 = uint256_t(fq_native::random_element()) & small_mask;
1737 a_u256 += uint256_t(fq_native::modulus);
1738
1739 // upper bound must be greater than p + 2^16: we set it to p + 2^30
1740 uint256_t upper_bound = (uint256_t(1) << 30) + uint256_t(fq_native::modulus);
1741
1742 // Construct bigfield element
1743 fq_ct a_ct(witness_ct(&builder, fr(a_u256.slice(0, fq_ct::NUM_LIMB_BITS * 2))),
1744 witness_ct(&builder, fr(a_u256.slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 4))),
1745 /*can_overflow*/ true);
1746
1747 // Assert a < bound
1749 EXPECT_EQ(a_ct.get_value() > uint512_t(fq_native::modulus), true);
1750
1751 // Combined circuit should pass
1752 bool result = CircuitChecker::check(builder);
1753 EXPECT_EQ(result, true);
1754 }
1755
1757 {
1758 {
1759 // Test a case when the value is exactly equal to the limit
1760 auto builder = Builder();
1761 constexpr size_t num_bits = 200;
1762 constexpr uint256_t bit_mask = (uint256_t(1) << num_bits) - 1;
1763 fq_ct a_ct(witness_ct(&builder, fr(bit_mask.slice(0, fq_ct::NUM_LIMB_BITS * 2))),
1764 witness_ct(&builder, fr(bit_mask.slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 4))));
1765
1766 // check that unsafe_assert_less_than fails when we try to check a < a.
1768
1769 bool result = CircuitChecker::check(builder);
1770 EXPECT_EQ(result, false);
1771 }
1772 {
1773 // Test a case when the value is (B + 2) but the bound is B.
1774 auto builder = Builder();
1775 constexpr size_t num_bits = 200;
1776 constexpr uint256_t bit_mask = (uint256_t(1) << num_bits) - 1;
1777 const uint256_t upper_bound = uint256_t(fq_native::random_element()) & bit_mask;
1778 const uint256_t a_value = upper_bound + uint256_t(2);
1779 fq_ct a_ct(witness_ct(&builder, fr(a_value.slice(0, fq_ct::NUM_LIMB_BITS * 2))),
1780 witness_ct(&builder, fr(a_value.slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 4))));
1781
1782 // check that unsafe_assert_less_than fails when we try to check (B + 2) < B.
1784
1785 bool result = CircuitChecker::check(builder);
1786 EXPECT_EQ(result, false);
1787 }
1788 {
1789 // Test a case when p < bound < a
1790 auto builder = Builder();
1791 uint256_t small_mask = (uint256_t(1) << 32) - 1;
1792 uint256_t a_u256 = uint256_t(fq_native::random_element()) & small_mask;
1793 a_u256 += uint256_t(fq_native::modulus);
1794
1795 // upper bound must be greater than p but smaller than a
1796 uint256_t upper_bound = uint256_t(fq_native::modulus) + uint256_t(1);
1797
1798 // Construct bigfield element
1799 fq_ct a_ct(witness_ct(&builder, fr(a_u256.slice(0, fq_ct::NUM_LIMB_BITS * 2))),
1800 witness_ct(&builder, fr(a_u256.slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 4))),
1801 /*can_overflow*/ true);
1802
1803 // check that unsafe_assert_less_than fails when we try to check a < bound.
1805
1806 bool result = CircuitChecker::check(builder);
1807 EXPECT_EQ(result, false);
1808 }
1809 }
1810
1812 {
1814
1815 // The circuit enforces:
1816 // a * b + (c0 + c1 + ...) = q * p + (r0 + r1 + ...) mod 2^T
1817 // a * b + (c0 + c1 + ...) = q * p + (r0 + r1 + ...) mod n
1818
1819 // Single addend and remainder
1820 auto [a_native, a_ct] = get_random_witness(&builder);
1821 auto [b_native, b_ct] = get_random_witness(&builder);
1822 auto [c_native, c_ct] = get_random_witness(&builder);
1823
1824 // Get quotient and remainder for (a * b + c) from native values
1825 uint1024_t native_sum = uint1024_t(a_native) * uint1024_t(b_native) + uint1024_t(c_native);
1826 auto [q_native_1024, r_native_1024] = native_sum.divmod(uint1024_t(fq_ct::modulus));
1827 const uint512_t q_native_512 = q_native_1024.lo;
1828 const uint512_t r_native_512 = r_native_1024.lo;
1829 fq_ct q_ct = fq_ct::create_from_u512_as_witness(&builder, q_native_512, true);
1830 fq_ct r_ct = fq_ct::create_from_u512_as_witness(&builder, r_native_512, true);
1831
1832 // Call unsafe_evaluate_multiply_add (via friendly class)
1833 stdlib::bigfield_test_access::unsafe_evaluate_multiply_add(a_ct, b_ct, { c_ct }, q_ct, { r_ct });
1834
1835 // The above function does not protect against CRT overflows, i.e., check if lhs and rhs are less than
1836 // M = (2^T * n). Verify that adding a multiple of M to both sides does not result in an unsatisfiable circuit.
1837 uint512_t big_M = uint512_t(fr::modulus) * fq_ct::binary_basis.modulus;
1838 uint512_t modified_c_native = uint512_t(c_native) + big_M;
1839 uint512_t modified_r_native = uint512_t(r_native_512) + big_M;
1840 fq_ct modified_c_ct = fq_ct::create_from_u512_as_witness(&builder, modified_c_native, true);
1841 fq_ct modified_r_ct = fq_ct::create_from_u512_as_witness(&builder, modified_r_native, true);
1842
1843 // Call unsafe_evaluate_multiply_add (via friendly class)
1845 a_ct, b_ct, { modified_c_ct }, q_ct, { modified_r_ct });
1846
1847 // Native verification mod p
1848 fq_native expected_lhs = a_native * b_native + c_native;
1849 fq_native expected_rhs = fq_native(q_native_512) * fq_ct::modulus + fq_native(r_native_512);
1850 EXPECT_EQ(expected_lhs, expected_rhs);
1851
1852 // Native verification mod 2^T
1853 uint1024_t lhs_1024 = uint512_t(a_native) * uint512_t(b_native) + uint512_t(c_native);
1854 uint1024_t rhs_1024 = q_native_512 * fq_ct::modulus + r_native_512;
1855 auto [quotient_lhs, remainder_lhs] = lhs_1024.divmod(fq_ct::binary_basis.modulus);
1856 auto [quotient_rhs, remainder_rhs] = rhs_1024.divmod(fq_ct::binary_basis.modulus);
1857 EXPECT_EQ(remainder_lhs, remainder_rhs);
1858
1859 // Native verification mod n
1860 fr expected_lhs_fr = fr(a_native) * fr(b_native) + fr(c_native);
1861 fr expected_rhs_fr = fr(q_native_512) * fr(fq_ct::modulus) + fr(r_native_512);
1862 EXPECT_EQ(expected_lhs_fr, expected_rhs_fr);
1863
1864 // Check circuit correctness
1865 bool result = CircuitChecker::check(builder);
1866 EXPECT_EQ(result, true);
1867 }
1868
1870 {
1871 auto builder = Builder();
1872
1873 // The circuit enforces:
1874 // a * b + (c0 + c1 + ...) = q * p + (r0 + r1 + ...) mod 2^T
1875 // a * b + (c0 + c1 + ...) = q * p + (r0 + r1 + ...) mod n
1876
1877 // Single addend and remainder
1878 auto [a_native, a_ct] = get_random_witness(&builder);
1879 auto [b_native, b_ct] = get_random_witness(&builder);
1880 auto [c_native, c_ct] = get_random_witness(&builder);
1881
1882 // Get quotient and remainder for (a * b + c) from native values
1883 uint512_t native_sum = uint512_t(a_native) * uint512_t(b_native) + uint512_t(c_native);
1884 auto [q_native_uint512_t, r_native_uint512_t] = native_sum.divmod(uint512_t(fq_ct::modulus));
1885 fq_ct q_ct = fq_ct::create_from_u512_as_witness(
1886 &builder, q_native_uint512_t + uint512_t(1), true); // Intentionally poisoned
1887 fq_ct r_ct = fq_ct::create_from_u512_as_witness(&builder, r_native_uint512_t, true);
1888
1889 // Call unsafe_evaluate_multiply_add (via friendly class)
1890 stdlib::bigfield_test_access::unsafe_evaluate_multiply_add(a_ct, b_ct, { c_ct }, q_ct, { r_ct });
1891
1892 // Check circuit correctness
1893 bool result = CircuitChecker::check(builder);
1894 EXPECT_EQ(result, false);
1895 EXPECT_EQ(builder.err(), "bigfield: prime limb identity failed");
1896 }
1897
1899 {
1901
1902 // The circuit enforces:
1903 // a1 * b1 + a2 * b2 + ... + (c0 + c1 + ...) = q * p + (r0 + r1 + ...) mod 2^T
1904 // a1 * b1 + a2 * b2 + ... + (c0 + c1 + ...) = q * p + (r0 + r1 + ...) mod n
1905 size_t num_terms = 3;
1906 std::vector<fq_native> a_natives;
1907 std::vector<fq_native> b_natives;
1908 std::vector<fq_ct> a_cts;
1909 std::vector<fq_ct> b_cts;
1910
1911 for (size_t i = 0; i < num_terms; ++i) {
1912 auto [a_native, a_ct] = get_random_witness(&builder);
1913 auto [b_native, b_ct] = get_random_witness(&builder);
1914 a_natives.push_back(a_native);
1915 b_natives.push_back(b_native);
1916 a_cts.push_back(a_ct);
1917 b_cts.push_back(b_ct);
1918 }
1919
1920 auto [c_native, c_ct] = get_random_witness(&builder);
1921
1922 // Get quotient and remainder for (sum of ai * bi + c) from native values
1923 uint1024_t native_sum = uint1024_t(c_native);
1924 for (size_t i = 0; i < num_terms; ++i) {
1925 native_sum += uint1024_t(a_natives[i]) * uint1024_t(b_natives[i]);
1926 }
1927 auto [q_native_1024, r_native_1024] = native_sum.divmod(uint512_t(fq_ct::modulus));
1928 const uint512_t q_native_512 = q_native_1024.lo;
1929 const uint512_t r_native_512 = r_native_1024.lo;
1930 fq_ct q_ct = fq_ct::create_from_u512_as_witness(&builder, q_native_512, true);
1931 fq_ct r_ct = fq_ct::create_from_u512_as_witness(&builder, r_native_512, true);
1932
1933 // Call unsafe_evaluate_multiply_add (via friendly class)
1934 stdlib::bigfield_test_access::unsafe_evaluate_multiple_multiply_add(a_cts, b_cts, { c_ct }, q_ct, { r_ct });
1935
1936 // Native verification mod p
1937 fq_native expected_lhs = fq_native(c_native);
1938 for (size_t i = 0; i < num_terms; ++i) {
1939 expected_lhs += fq_native(a_natives[i]) * fq_native(b_natives[i]);
1940 }
1941 fq_native expected_rhs = fq_native(q_native_512) * fq_ct::modulus + fq_native(r_native_512);
1942 EXPECT_EQ(expected_lhs, expected_rhs);
1943
1944 // Native verification mod 2^T
1945 uint1024_t lhs_1024 = uint1024_t(c_native);
1946 for (size_t i = 0; i < num_terms; ++i) {
1947 lhs_1024 += uint1024_t(a_natives[i]) * uint1024_t(b_natives[i]);
1948 }
1949 uint1024_t rhs_1024 = q_native_512 * fq_ct::modulus + r_native_512;
1950 auto [quotient_lhs, remainder_lhs] = lhs_1024.divmod(fq_ct::binary_basis.modulus);
1951 auto [quotient_rhs, remainder_rhs] = rhs_1024.divmod(fq_ct::binary_basis.modulus);
1952 EXPECT_EQ(remainder_lhs, remainder_rhs);
1953
1954 // Native verification mod n
1955 fr expected_lhs_fr = fr(c_native);
1956 for (size_t i = 0; i < num_terms; ++i) {
1957 expected_lhs_fr += fr(a_natives[i]) * fr(b_natives[i]);
1958 }
1959 fr expected_rhs_fr = fr(q_native_512) * fr(fq_ct::modulus) + fr(r_native_512);
1960 EXPECT_EQ(expected_lhs_fr, expected_rhs_fr);
1961
1962 // Check circuit correctness
1963 bool result = CircuitChecker::check(builder);
1964 EXPECT_EQ(result, true);
1965 }
1966
1968 {
1970
1971 // The circuit enforces:
1972 // a1 * b1 + a2 * b2 + ... + (c0 + c1 + ...) = q * p + (r0 + r1 + ...) mod 2^T
1973 // a1 * b1 + a2 * b2 + ... + (c0 + c1 + ...) = q * p + (r0 + r1 + ...) mod n
1974 size_t num_terms = 3;
1975 std::vector<fq_native> a_natives;
1976 std::vector<fq_native> b_natives;
1977 std::vector<fq_ct> a_cts;
1978 std::vector<fq_ct> b_cts;
1979
1980 for (size_t i = 0; i < num_terms; ++i) {
1981 auto [a_native, a_ct] = get_random_witness(&builder);
1982 auto [b_native, b_ct] = get_random_witness(&builder);
1983 a_natives.push_back(a_native);
1984 b_natives.push_back(b_native);
1985 a_cts.push_back(a_ct);
1986 b_cts.push_back(b_ct);
1987 }
1988
1989 auto [c_native, c_ct] = get_random_witness(&builder);
1990
1991 // Get quotient and remainder for (sum of ai * bi + c) from native values
1992 uint1024_t native_sum = uint1024_t(c_native);
1993 for (size_t i = 0; i < num_terms; ++i) {
1994 native_sum += uint1024_t(a_natives[i]) * uint1024_t(b_natives[i]);
1995 }
1996 auto [q_native_1024, r_native_1024] = native_sum.divmod(uint1024_t(fq_ct::modulus));
1997 fq_ct q_ct = fq_ct::create_from_u512_as_witness(
1998 &builder, q_native_1024.lo + uint512_t(1), true); // Intentionally poisoned
1999 fq_ct r_ct = fq_ct::create_from_u512_as_witness(&builder, r_native_1024.lo, true);
2000
2001 // Call unsafe_evaluate_multiply_add (via friendly class)
2002 stdlib::bigfield_test_access::unsafe_evaluate_multiple_multiply_add(a_cts, b_cts, { c_ct }, q_ct, { r_ct });
2003
2004 // Check circuit correctness
2005 bool result = CircuitChecker::check(builder);
2006 EXPECT_EQ(result, false);
2007 EXPECT_EQ(builder.err(), "bigfield: prime limb identity failed");
2008 }
2009
2011 {
2012 auto builder = Builder();
2014 uint256_t two_to_68 = uint256_t(1) << fq_ct::NUM_LIMB_BITS;
2015 // construct bigfield where the low limb has a non-trivial `additive_constant`
2016 fq_ct z(zero + two_to_68, zero);
2017 // assert invariant for every limb: actual value <= maximum value
2018 // Failed in the past for for StandardCircuitBuilder
2019 for (auto zi : z.binary_basis_limbs) {
2020 EXPECT_LE(uint256_t(zi.element.get_value()), zi.maximum_value);
2021 }
2022 }
2023
2025 {
2026 auto builder = Builder();
2027 fq_ct witness_one = fq_ct::create_from_u512_as_witness(&builder, uint256_t(1));
2028 fq_ct constant_one(1);
2029 fq_ct::msub_div({ witness_one }, { witness_one }, constant_one, { witness_one }, true);
2030 bool result = CircuitChecker::check(builder);
2031 EXPECT_EQ(result, true);
2032 }
2033
2035 {
2036 typedef stdlib::bool_t<Builder> bool_t;
2037 auto builder = Builder();
2038
2039 fq_ct w0 = fq_ct::from_witness(&builder, typename fq_ct::native(1));
2040 w0 = w0.conditional_negate(bool_t(&builder, true));
2041 w0 = w0.conditional_negate(bool_t(&builder, false));
2042 w0 = w0.conditional_negate(bool_t(&builder, true));
2043 w0 = w0.conditional_negate(bool_t(&builder, true));
2044 fq_ct w4 = w0.conditional_negate(bool_t(&builder, false));
2045 w4 = w4.conditional_negate(bool_t(&builder, true));
2046 w4 = w4.conditional_negate(bool_t(&builder, true));
2047 fq_ct w5 = w4 - w0;
2048 fq_ct w6 = w5 / 1;
2049 (void)(w6);
2050 EXPECT_TRUE(CircuitChecker::check(builder));
2051 }
2052
2054 {
2055 auto builder = Builder();
2056
2057 fq_ct numerator = fq_ct::create_from_u512_as_witness(&builder, uint256_t(1) << (68 + 67));
2058 numerator.binary_basis_limbs[0].maximum_value = 0;
2059 numerator.binary_basis_limbs[1].maximum_value = uint256_t(1) << 67;
2060 numerator.binary_basis_limbs[2].maximum_value = 0;
2061 numerator.binary_basis_limbs[3].maximum_value = 0;
2062
2063 for (size_t i = 0; i < 9; i++) {
2064 numerator = numerator + numerator;
2065 }
2066 fq_ct denominator = fq_ct::create_from_u512_as_witness(&builder, uint256_t(1));
2067 fq_ct result = numerator / denominator;
2068 (void)(result);
2069 EXPECT_TRUE(CircuitChecker::check(builder));
2070 }
2071
2073 {
2075 uint256_t dlimb0_value = uint256_t("0x00000000000000000000000000000000000000000000000bef7fa109038857fc");
2076 uint256_t dlimb0_max = uint256_t("0x00000000000000000000000000000000000000000000000fffffffffffffffff");
2077 uint256_t dlimb1_value = uint256_t("0x0000000000000000000000000000000000000000000000056f10535779f56339");
2078 uint256_t dlimb1_max = uint256_t("0x00000000000000000000000000000000000000000000000fffffffffffffffff");
2079 uint256_t dlimb2_value = uint256_t("0x00000000000000000000000000000000000000000000000c741f60a1ec4e114e");
2080 uint256_t dlimb2_max = uint256_t("0x00000000000000000000000000000000000000000000000fffffffffffffffff");
2081 uint256_t dlimb3_value = uint256_t("0x000000000000000000000000000000000000000000000000000286b3cd344d8b");
2082 uint256_t dlimb3_max = uint256_t("0x0000000000000000000000000000000000000000000000000003ffffffffffff");
2083 uint256_t dlimb_prime = uint256_t("0x286b3cd344d8bc741f60a1ec4e114e56f10535779f56339bef7fa109038857fc");
2084
2085 uint256_t nlimb0_value = uint256_t("0x00000000000000000000000000000000000000000000080a84d9bea2b012417c");
2086 uint256_t nlimb0_max = uint256_t("0x000000000000000000000000000000000000000000000ff7c7469df4081b61fc");
2087 uint256_t nlimb1_value = uint256_t("0x00000000000000000000000000000000000000000000080f50ee84526e8e5ba7");
2088 uint256_t nlimb1_max = uint256_t("0x000000000000000000000000000000000000000000000ffef965c67ba5d5893c");
2089 uint256_t nlimb2_value = uint256_t("0x00000000000000000000000000000000000000000000080aba136ca8eaf6dc1b");
2090 uint256_t nlimb2_max = uint256_t("0x000000000000000000000000000000000000000000000ff8171d22fd607249ea");
2091 uint256_t nlimb3_value = uint256_t("0x00000000000000000000000000000000000000000000000001f0042419843c29");
2092 uint256_t nlimb3_max = uint256_t("0x00000000000000000000000000000000000000000000000003e00636264659ff");
2093 uint256_t nlimb_prime = uint256_t("0x000000000000000000000000000000474da776b8ee19a56b08186bdcf01240d8");
2094
2095 fq_ct w0 = fq_ct::from_witness(&builder, fq_native(0));
2096 w0.binary_basis_limbs[0].element = witness_ct(&builder, dlimb0_value);
2097 w0.binary_basis_limbs[1].element = witness_ct(&builder, dlimb1_value);
2098 w0.binary_basis_limbs[2].element = witness_ct(&builder, dlimb2_value);
2099 w0.binary_basis_limbs[3].element = witness_ct(&builder, dlimb3_value);
2100 w0.binary_basis_limbs[0].maximum_value = dlimb0_max;
2101 w0.binary_basis_limbs[1].maximum_value = dlimb1_max;
2102 w0.binary_basis_limbs[2].maximum_value = dlimb2_max;
2103 w0.binary_basis_limbs[3].maximum_value = dlimb3_max;
2104 w0.prime_basis_limb = witness_ct(&builder, dlimb_prime);
2105
2106 fq_ct w1 = fq_ct::from_witness(&builder, fq_native(0));
2107 w1.binary_basis_limbs[0].element = witness_ct(&builder, nlimb0_value);
2108 w1.binary_basis_limbs[1].element = witness_ct(&builder, nlimb1_value);
2109 w1.binary_basis_limbs[2].element = witness_ct(&builder, nlimb2_value);
2110 w1.binary_basis_limbs[3].element = witness_ct(&builder, nlimb3_value);
2111 w1.binary_basis_limbs[0].maximum_value = nlimb0_max;
2112 w1.binary_basis_limbs[1].maximum_value = nlimb1_max;
2113 w1.binary_basis_limbs[2].maximum_value = nlimb2_max;
2114 w1.binary_basis_limbs[3].maximum_value = nlimb3_max;
2115 w1.prime_basis_limb = witness_ct(&builder, nlimb_prime);
2116
2117 fq_ct w2 = w1 / w0;
2118 (void)w2;
2119 EXPECT_TRUE(CircuitChecker::check(builder));
2120 }
2121
2123 {
2124 auto builder = Builder();
2125 fq_ct zero = fq_ct::create_from_u512_as_witness(&builder, uint256_t(0));
2126 fq_ct alsozero = fq_ct::create_from_u512_as_witness(&builder, fq_ct::modulus_u512);
2127 for (size_t i = 0; i < 4; i++) {
2128 zero.binary_basis_limbs[i].maximum_value = zero.binary_basis_limbs[i].element.get_value();
2129 alsozero.binary_basis_limbs[i].maximum_value = alsozero.binary_basis_limbs[i].element.get_value();
2130 }
2131 zero.assert_is_not_equal(alsozero);
2132 bool result = CircuitChecker::check(builder);
2133 EXPECT_EQ(result, false);
2134 }
2135};
2136
2137// Define types for which the above tests will be constructed.
2138using CircuitTypes = testing::Types<typename bb::stdlib::bn254<UltraCircuitBuilder>::BaseField,
2143// Define the suite of tests.
2145
2146TYPED_TEST(stdlib_bigfield, assert_not_equal_regression)
2147{
2148 TestFixture::test_assert_not_equal_regression();
2149}
2150
2151TYPED_TEST(stdlib_bigfield, add_to_lower_limb_regression)
2152{
2153 TestFixture::test_add_to_lower_limb_regression();
2154}
2156{
2157 TestFixture::test_bad_mul();
2158}
2159
2160TYPED_TEST(stdlib_bigfield, division_formula_regression)
2161{
2162 TestFixture::test_division_formula_bug();
2163}
2165{
2166 TestFixture::test_basic_tag_logic();
2167}
2168TYPED_TEST(stdlib_bigfield, test_constructor)
2169{
2170 TestFixture::test_constructor_from_two_elements();
2171}
2172TYPED_TEST(stdlib_bigfield, test_unsafe_construct_from_limbs)
2173{
2174 TestFixture::test_unsafe_construct_from_limbs();
2175}
2176TYPED_TEST(stdlib_bigfield, test_construct_from_limbs)
2177{
2178 TestFixture::test_construct_from_limbs();
2179}
2180TYPED_TEST(stdlib_bigfield, test_construct_from_limbs_fails)
2181{
2182 TestFixture::test_construct_from_limbs_fails();
2183}
2185{
2186 TestFixture::test_add_two(InputType::WITNESS, InputType::WITNESS, InputType::WITNESS);
2187}
2188TYPED_TEST(stdlib_bigfield, add_two_with_constants)
2189{
2190 TestFixture::test_add_two(InputType::WITNESS, InputType::WITNESS, InputType::CONSTANT);
2191 TestFixture::test_add_two(InputType::WITNESS, InputType::CONSTANT, InputType::WITNESS);
2192 TestFixture::test_add_two(InputType::WITNESS, InputType::CONSTANT, InputType::CONSTANT);
2193 TestFixture::test_add_two(InputType::CONSTANT, InputType::WITNESS, InputType::WITNESS);
2194 TestFixture::test_add_two(InputType::CONSTANT, InputType::WITNESS, InputType::CONSTANT);
2195 TestFixture::test_add_two(InputType::CONSTANT, InputType::CONSTANT, InputType::WITNESS);
2196 TestFixture::test_add_two(InputType::CONSTANT, InputType::CONSTANT, InputType::CONSTANT);
2197}
2199{
2200 TestFixture::test_sum(InputType::WITNESS);
2201}
2202TYPED_TEST(stdlib_bigfield, sum_with_mixed_inputs)
2203{
2204 TestFixture::test_sum(InputType::WITNESS, true);
2205}
2206TYPED_TEST(stdlib_bigfield, sum_with_constant)
2207{
2208 TestFixture::test_sum(InputType::CONSTANT);
2209}
2211{
2212 TestFixture::test_mul(InputType::WITNESS, InputType::WITNESS);
2213}
2214TYPED_TEST(stdlib_bigfield, mul_with_constant)
2215{
2216 TestFixture::test_mul(InputType::WITNESS, InputType::CONSTANT);
2217 TestFixture::test_mul(InputType::CONSTANT, InputType::WITNESS);
2218 TestFixture::test_mul(InputType::CONSTANT, InputType::CONSTANT);
2219}
2221{
2222 TestFixture::test_sub(InputType::WITNESS, InputType::WITNESS);
2223}
2224TYPED_TEST(stdlib_bigfield, sub_with_constant)
2225{
2226 TestFixture::test_sub(InputType::WITNESS, InputType::CONSTANT);
2227 TestFixture::test_sub(InputType::CONSTANT, InputType::WITNESS);
2228 TestFixture::test_sub(InputType::CONSTANT, InputType::CONSTANT);
2229}
2231{
2232 TestFixture::test_add(InputType::WITNESS, InputType::WITNESS);
2233}
2234TYPED_TEST(stdlib_bigfield, add_with_constant)
2235{
2236 TestFixture::test_add(InputType::WITNESS, InputType::CONSTANT);
2237 TestFixture::test_add(InputType::CONSTANT, InputType::WITNESS);
2238 TestFixture::test_add(InputType::CONSTANT, InputType::CONSTANT);
2239}
2241{
2242 TestFixture::test_div(InputType::WITNESS, InputType::WITNESS); // w / w
2243}
2244TYPED_TEST(stdlib_bigfield, div_with_constant)
2245{
2246 TestFixture::test_div(InputType::WITNESS, InputType::CONSTANT); // w / c
2247 TestFixture::test_div(InputType::CONSTANT, InputType::WITNESS); // c / w
2248 TestFixture::test_div(InputType::CONSTANT, InputType::CONSTANT); // c / c
2249}
2251{
2252 TestFixture::test_sqr(InputType::WITNESS);
2253}
2254TYPED_TEST(stdlib_bigfield, sqr_with_constant)
2255{
2256 TestFixture::test_sqr(InputType::CONSTANT);
2257}
2259{
2260 TestFixture::test_negate(InputType::WITNESS);
2261}
2263{
2264 TestFixture::test_mul_assign(InputType::WITNESS, InputType::WITNESS);
2265}
2266TYPED_TEST(stdlib_bigfield, mul_assignment_with_constant)
2267{
2268 TestFixture::test_mul_assign(InputType::WITNESS, InputType::CONSTANT);
2269 TestFixture::test_mul_assign(InputType::CONSTANT, InputType::WITNESS);
2270 TestFixture::test_mul_assign(InputType::CONSTANT, InputType::CONSTANT);
2271}
2273{
2274 TestFixture::test_add_assign(InputType::WITNESS, InputType::WITNESS);
2275}
2276TYPED_TEST(stdlib_bigfield, add_assignment_with_constant)
2277{
2278 TestFixture::test_add_assign(InputType::WITNESS, InputType::CONSTANT);
2279 TestFixture::test_add_assign(InputType::CONSTANT, InputType::WITNESS);
2280 TestFixture::test_add_assign(InputType::CONSTANT, InputType::CONSTANT);
2281}
2283{
2284 TestFixture::test_sub_assign(InputType::WITNESS, InputType::WITNESS);
2285}
2286TYPED_TEST(stdlib_bigfield, sub_assignment_with_constant)
2287{
2288 TestFixture::test_sub_assign(InputType::WITNESS, InputType::CONSTANT);
2289 TestFixture::test_sub_assign(InputType::CONSTANT, InputType::WITNESS);
2290 TestFixture::test_sub_assign(InputType::CONSTANT, InputType::CONSTANT);
2291}
2293{
2294 TestFixture::test_div_assign(InputType::WITNESS, InputType::WITNESS); // w / w
2295}
2296TYPED_TEST(stdlib_bigfield, div_assignment_with_constant)
2297{
2298 TestFixture::test_div_assign(InputType::WITNESS, InputType::CONSTANT); // w / c
2299 TestFixture::test_div_assign(InputType::CONSTANT, InputType::WITNESS); // c / w
2300 TestFixture::test_div_assign(InputType::CONSTANT, InputType::CONSTANT); // c / c
2301}
2303{
2304 TestFixture::test_madd(InputType::WITNESS, InputType::WITNESS, InputType::CONSTANT); // w * w + w
2305}
2306TYPED_TEST(stdlib_bigfield, madd_with_constants)
2307{
2308 TestFixture::test_madd(InputType::WITNESS, InputType::WITNESS, InputType::CONSTANT); // w * w + c
2309 TestFixture::test_madd(InputType::WITNESS, InputType::CONSTANT, InputType::WITNESS); // w * c + w
2310 TestFixture::test_madd(InputType::WITNESS, InputType::CONSTANT, InputType::CONSTANT); // w * c + c
2311 TestFixture::test_madd(InputType::CONSTANT, InputType::WITNESS, InputType::WITNESS); // c * w + w
2312 TestFixture::test_madd(InputType::CONSTANT, InputType::WITNESS, InputType::CONSTANT); // c * w + c
2313 TestFixture::test_madd(InputType::WITNESS, InputType::WITNESS, InputType::CONSTANT); // w * w + c
2314 TestFixture::test_madd(InputType::WITNESS, InputType::CONSTANT, InputType::WITNESS); // w * c + w
2315 TestFixture::test_madd(InputType::CONSTANT, InputType::WITNESS, InputType::CONSTANT); // c * w + c
2316}
2318{
2319 TestFixture::test_sqradd(InputType::WITNESS, InputType::WITNESS); // w^2 + w
2320}
2321TYPED_TEST(stdlib_bigfield, sqradd_with_constant)
2322{
2323 TestFixture::test_sqradd(InputType::WITNESS, InputType::CONSTANT); // w^2 + c
2324 TestFixture::test_sqradd(InputType::CONSTANT, InputType::WITNESS); // c^2 + w
2325 TestFixture::test_sqradd(InputType::CONSTANT, InputType::CONSTANT); // c^2 + c
2326}
2328{
2329 TestFixture::test_mult_madd(InputType::WITNESS, InputType::WITNESS, InputType::WITNESS); // ∑ (w * w + w)
2330}
2331TYPED_TEST(stdlib_bigfield, mult_madd_with_constants)
2332{
2333 TestFixture::test_mult_madd(InputType::WITNESS, InputType::WITNESS, InputType::CONSTANT); // ∑ (w * w + c)
2334 TestFixture::test_mult_madd(InputType::WITNESS, InputType::CONSTANT, InputType::WITNESS); // ∑ (w * c + w)
2335 TestFixture::test_mult_madd(InputType::WITNESS, InputType::CONSTANT, InputType::CONSTANT); // ∑ (w * c + c)
2336 TestFixture::test_mult_madd(InputType::CONSTANT, InputType::CONSTANT, InputType::CONSTANT); // ∑ (c * c + c)
2337}
2338TYPED_TEST(stdlib_bigfield, mult_madd_edge_cases)
2339{
2340 // all witness except the last one
2341 TestFixture::test_mult_madd(InputType::WITNESS, InputType::WITNESS, InputType::WITNESS, true);
2342 // all constant except the last one
2343 TestFixture::test_mult_madd(InputType::CONSTANT, InputType::CONSTANT, InputType::CONSTANT, true);
2344}
2346{
2347 TestFixture::test_dual_madd();
2348}
2349TYPED_TEST(stdlib_bigfield, div_without_denominator_check)
2350{
2351 TestFixture::test_div_without_denominator_check(InputType::WITNESS, InputType::WITNESS); // w / w
2352}
2353TYPED_TEST(stdlib_bigfield, div_without_denominator_check_with_constant)
2354{
2355 TestFixture::test_div_without_denominator_check(InputType::WITNESS, InputType::CONSTANT); // w / c
2356 TestFixture::test_div_without_denominator_check(InputType::CONSTANT, InputType::WITNESS); // c / w
2357 TestFixture::test_div_without_denominator_check(InputType::CONSTANT, InputType::CONSTANT); // c / c
2358}
2360{
2361 TestFixture::test_add_and_div();
2362}
2364{
2365 TestFixture::test_add_and_mul(InputType::WITNESS); // (w + w) * (w + w)
2366}
2367TYPED_TEST(stdlib_bigfield, add_and_mul_with_constants)
2368{
2369 TestFixture::test_add_and_mul(InputType::CONSTANT); // (w + c) * (w + c)
2370}
2372{
2373 TestFixture::test_sub_and_mul(InputType::WITNESS); // (w - w) * (w - w)
2374}
2375TYPED_TEST(stdlib_bigfield, sub_and_mul_with_constants)
2376{
2377 TestFixture::test_sub_and_mul(InputType::CONSTANT); // (w - c) * (w - c)
2378}
2380{
2381 TestFixture::test_msub_div(
2382 InputType::WITNESS, InputType::WITNESS, InputType::WITNESS); // (-w * w - w - w) / (w - w)
2383}
2384TYPED_TEST(stdlib_bigfield, msub_div_with_constants)
2385{
2386 TestFixture::test_msub_div(
2387 InputType::WITNESS, InputType::WITNESS, InputType::CONSTANT); // (-w * w - w - c) / (w - w)
2388 TestFixture::test_msub_div(
2389 InputType::WITNESS, InputType::CONSTANT, InputType::WITNESS); // (-w * c - w - w) / (w - w)
2390 TestFixture::test_msub_div(
2391 InputType::WITNESS, InputType::CONSTANT, InputType::CONSTANT); // (-w * c - w - c) / (w - w)
2392 TestFixture::test_msub_div(
2393 InputType::CONSTANT, InputType::WITNESS, InputType::WITNESS); // (-c * w - c - w) / (w - w)
2394 TestFixture::test_msub_div(
2395 InputType::CONSTANT, InputType::WITNESS, InputType::CONSTANT); // (-c * w - c - c) / (w - w)
2396 TestFixture::test_msub_div(
2397 InputType::CONSTANT, InputType::CONSTANT, InputType::CONSTANT); // (-c * c - c - c) / (w - w)
2398}
2399TYPED_TEST(stdlib_bigfield, conditional_assign)
2400{
2401 TestFixture::test_conditional_assign(InputType::WITNESS, InputType::WITNESS, InputType::WITNESS); // w ? w : w
2402}
2403TYPED_TEST(stdlib_bigfield, conditional_assign_with_constants)
2404{
2405 TestFixture::test_conditional_assign(InputType::WITNESS, InputType::WITNESS, InputType::CONSTANT); // w ? w : c
2406 TestFixture::test_conditional_assign(InputType::WITNESS, InputType::CONSTANT, InputType::WITNESS); // w ? c : w
2407 TestFixture::test_conditional_assign(InputType::WITNESS, InputType::CONSTANT, InputType::CONSTANT); // w ? c : c
2408 TestFixture::test_conditional_assign(InputType::CONSTANT, InputType::WITNESS, InputType::WITNESS); // c ? w : w
2409 TestFixture::test_conditional_assign(InputType::CONSTANT, InputType::WITNESS, InputType::CONSTANT); // c ? w : c
2410 TestFixture::test_conditional_assign(InputType::CONSTANT, InputType::CONSTANT, InputType::CONSTANT); // c ? c : c
2411}
2412TYPED_TEST(stdlib_bigfield, conditional_select)
2413{
2414 TestFixture::test_conditional_select(InputType::WITNESS, InputType::WITNESS, InputType::WITNESS); // w ? w : w
2415}
2416TYPED_TEST(stdlib_bigfield, conditional_select_with_constants)
2417{
2418 TestFixture::test_conditional_select(InputType::WITNESS, InputType::WITNESS, InputType::CONSTANT); // w ? w : c
2419 TestFixture::test_conditional_select(InputType::WITNESS, InputType::CONSTANT, InputType::WITNESS); // w ? c : w
2420 TestFixture::test_conditional_select(InputType::WITNESS, InputType::CONSTANT, InputType::CONSTANT); // w ? c : c
2421 TestFixture::test_conditional_select(InputType::CONSTANT, InputType::WITNESS, InputType::WITNESS); // c ? w : w
2422 TestFixture::test_conditional_select(InputType::CONSTANT, InputType::WITNESS, InputType::CONSTANT); // c ? w : c
2423 TestFixture::test_conditional_select(InputType::CONSTANT, InputType::CONSTANT, InputType::CONSTANT); // c ? c : c
2424}
2425TYPED_TEST(stdlib_bigfield, msb_div_ctx_crash_regression)
2426{
2427 TestFixture::test_msub_div_ctx_crash_regression();
2428}
2429TYPED_TEST(stdlib_bigfield, conditional_negate)
2430{
2431 TestFixture::test_conditional_negate(InputType::WITNESS, InputType::WITNESS); // w ? -w : w
2432}
2433TYPED_TEST(stdlib_bigfield, conditional_negate_with_constants)
2434{
2435 TestFixture::test_conditional_negate(InputType::WITNESS, InputType::CONSTANT); // w ? -c : w
2436 TestFixture::test_conditional_negate(InputType::CONSTANT, InputType::WITNESS); // c ? -w : w
2437 TestFixture::test_conditional_negate(InputType::CONSTANT, InputType::CONSTANT); // c ? -c : c
2438}
2439TYPED_TEST(stdlib_bigfield, group_operations)
2440{
2441 // skip this test if the field is not bn254 base field
2443 GTEST_SKIP() << "skipping group operations test for non-bn254 base field";
2444 }
2445 TestFixture::test_group_operations();
2446}
2448{
2449 TestFixture::test_reduce();
2450}
2452{
2453 TestFixture::test_equality_operator(InputType::WITNESS, InputType::WITNESS); // w == w
2454}
2455TYPED_TEST(stdlib_bigfield, equality_with_constants)
2456{
2457 TestFixture::test_equality_operator(InputType::WITNESS, InputType::CONSTANT); // w == c
2458 TestFixture::test_equality_operator(InputType::CONSTANT, InputType::WITNESS); // c == w
2459 TestFixture::test_equality_operator(InputType::CONSTANT, InputType::CONSTANT); // c == c
2460}
2461
2462TYPED_TEST(stdlib_bigfield, unsafe_assert_less_than)
2463{
2464 TestFixture::test_unsafe_assert_less_than();
2465}
2466TYPED_TEST(stdlib_bigfield, unsafe_assert_less_than_fails)
2467{
2468 TestFixture::test_unsafe_assert_less_than_fails();
2469}
2470TYPED_TEST(stdlib_bigfield, unsafe_evaluate_multiply_add)
2471{
2472 TestFixture::test_unsafe_evaluate_multiply_add();
2473}
2474TYPED_TEST(stdlib_bigfield, unsafe_evaluate_multiply_add_fails)
2475{
2476 TestFixture::test_unsafe_evaluate_multiply_add_fails();
2477}
2478TYPED_TEST(stdlib_bigfield, unsafe_evaluate_multiple_multiply_add)
2479{
2480 TestFixture::test_unsafe_multiple_multiply_add();
2481}
2482TYPED_TEST(stdlib_bigfield, unsafe_evaluate_multiple_multiply_add_fails)
2483{
2484 TestFixture::test_unsafe_multiple_multiply_add_fails();
2485}
2486
2487TYPED_TEST(stdlib_bigfield, assert_is_in_field_success)
2488{
2489 TestFixture::test_assert_is_in_field_success();
2490}
2491TYPED_TEST(stdlib_bigfield, assert_is_in_field_fails)
2492{
2493 TestFixture::test_assert_is_in_field_fails();
2494}
2495TYPED_TEST(stdlib_bigfield, assert_less_than_success)
2496{
2497 TestFixture::test_assert_less_than_success();
2498}
2499TYPED_TEST(stdlib_bigfield, assert_less_than_fails)
2500{
2501 TestFixture::test_assert_less_than_fails();
2502}
2503TYPED_TEST(stdlib_bigfield, reduce_mod_target_modulus)
2504{
2505 TestFixture::test_reduce_mod_target_modulus();
2506}
2507TYPED_TEST(stdlib_bigfield, byte_array_constructors)
2508{
2509 TestFixture::test_byte_array_constructors();
2510}
2511TYPED_TEST(stdlib_bigfield, quotient_completeness_regression)
2512{
2513 TestFixture::test_quotient_completeness();
2514}
2515
2516TYPED_TEST(stdlib_bigfield, conditional_select_regression)
2517{
2518 TestFixture::test_conditional_select_regression();
2519}
2520
2521TYPED_TEST(stdlib_bigfield, division_context)
2522{
2523 TestFixture::test_division_context();
2524}
2525
2527{
2528 TestFixture::test_inversion();
2529}
2530
2531TYPED_TEST(stdlib_bigfield, assert_equal_not_equal)
2532{
2533 TestFixture::test_assert_equal_not_equal();
2534}
2535
2537{
2538 TestFixture::test_pow();
2539}
2540
2542{
2543 TestFixture::test_pow_one();
2544}
2545TYPED_TEST(stdlib_bigfield, nonnormalized_field_bug_regression)
2546{
2547 TestFixture::test_nonnormalized_field_bug_regression();
2548}
2549
2550TYPED_TEST(stdlib_bigfield, internal_div_bug_regression)
2551{
2552 TestFixture::test_internal_div_regression();
2553 TestFixture::test_internal_div_regression2();
2554 TestFixture::test_internal_div_regression3();
2555}
2556
2557// Zero value should always pass, regardless of predicate value
2558TYPED_TEST(stdlib_bigfield, assert_zero_if_zero_value)
2559{
2560 // predicate=true, witness predicate
2561 TestFixture::test_assert_zero_if_zero_value(true, InputType::WITNESS);
2562 // predicate=true, constant predicate
2563 TestFixture::test_assert_zero_if_zero_value(true, InputType::CONSTANT);
2564 // predicate=false, witness predicate
2565 TestFixture::test_assert_zero_if_zero_value(false, InputType::WITNESS);
2566 // predicate=false, constant predicate
2567 TestFixture::test_assert_zero_if_zero_value(false, InputType::CONSTANT);
2568}
2569
2570// Non-zero value with false predicate should pass (check is skipped)
2571TYPED_TEST(stdlib_bigfield, assert_zero_if_nonzero_value_false_predicate)
2572{
2573 TestFixture::test_assert_zero_if_nonzero_value(false, InputType::WITNESS, InputType::WITNESS, true);
2574 TestFixture::test_assert_zero_if_nonzero_value(false, InputType::WITNESS, InputType::CONSTANT, true);
2575 TestFixture::test_assert_zero_if_nonzero_value(false, InputType::CONSTANT, InputType::WITNESS, true);
2576 TestFixture::test_assert_zero_if_nonzero_value(false, InputType::CONSTANT, InputType::CONSTANT, true);
2577}
2578
2579// Non-zero value with true predicate should fail
2580TYPED_TEST(stdlib_bigfield, assert_zero_if_nonzero_value_true_predicate_fails)
2581{
2582 TestFixture::test_assert_zero_if_nonzero_value(true, InputType::WITNESS, InputType::WITNESS, false);
2583 TestFixture::test_assert_zero_if_nonzero_value(true, InputType::WITNESS, InputType::CONSTANT, false);
2584 TestFixture::test_assert_zero_if_nonzero_value(true, InputType::CONSTANT, InputType::WITNESS, false);
2585 // Note: We don't test (CONSTANT, CONSTANT) because when both value and predicate are constants,
2586 // the assertion is evaluated at circuit construction time, not at verification time.
2587}
2588
2589// Computed zero (from subtraction) should pass with true predicate
2590TYPED_TEST(stdlib_bigfield, assert_zero_if_computed_zero)
2591{
2592 TestFixture::test_assert_zero_if_computed_zero();
2593}
2594
2596{
2597 using Builder = TypeParam::field_ct::Builder;
2598 using fq_ct = TypeParam;
2600
2602
2603 auto [c_native, c_ct] = TestFixture::get_random_witness(&builder);
2604 BB_ASSERT_LT(static_cast<uint256_t>(c_native), fq_ct::modulus);
2605
2606 // c_ct < modulus
2607 auto is_ok = c_ct.is_less_than(fq_ct::modulus);
2608 is_ok.assert_equal(stdlib::bool_t<Builder>(true));
2609
2610 // c_ct not smaller than itself
2611 auto is_not_ok = c_ct.is_less_than(c_native);
2612 is_not_ok.assert_equal(stdlib::bool_t<Builder>(false));
2613
2614 // c_ct < c_native + 1 (edge case)
2615 auto is_ok_edge_case = c_ct.is_less_than(c_native + 1);
2616 is_ok_edge_case.assert_equal(stdlib::bool_t<Builder>(true));
2617
2618 // c_ct > modulus fails comparison but doesn't make the circuit fail
2619 std::vector<uint8_t> c_bytes(32, 0xff);
2620 byte_array_ct c_byte_array = byte_array_ct(&builder, c_bytes);
2621 fq_ct reconstructed_from_bytes(c_byte_array);
2622 auto is_not_ok_larger_than_modulus = reconstructed_from_bytes.is_less_than(fq_ct::modulus);
2623 is_not_ok_larger_than_modulus.assert_equal(stdlib::bool_t<Builder>(false));
2624
2625 EXPECT_TRUE(CircuitChecker::check(builder));
2626 EXPECT_FALSE(builder.failed());
2627}
#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:33
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_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:306
RNG & get_debug_randomness(bool reset, std::uint_fast64_t seed)
Definition engine.cpp:217
uintx< uint512_t > uint1024_t
Definition uintx.hpp:308
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()