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