Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
bigfield_impl.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: Complete, auditors: [Suyash], commit: }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
7#pragma once
8
13#include <cstdint>
14#include <tuple>
15
16#include "../circuit_builders/circuit_builders.hpp"
17#include "bigfield.hpp"
18
19#include "../field/field.hpp"
21
22namespace bb::stdlib {
23
24template <typename Builder, typename T>
26 : context(parent_context)
27 , binary_basis_limbs{ Limb(field_t<Builder>(parent_context, bb::fr(0))),
28 Limb(field_t<Builder>(parent_context, bb::fr(0))),
29 Limb(field_t<Builder>(parent_context, bb::fr(0))),
30 Limb(field_t<Builder>(parent_context, bb::fr(0))) }
31 , prime_basis_limb(context, 0)
32{}
33
34template <typename Builder, typename T>
36 : context(parent_context)
37 , binary_basis_limbs{ Limb(field_t<Builder>(parent_context, bb::fr(value.slice(0, NUM_LIMB_BITS)))),
38 Limb(field_t<Builder>(parent_context, bb::fr(value.slice(NUM_LIMB_BITS, NUM_LIMB_BITS * 2)))),
39 Limb(field_t<Builder>(parent_context,
40 bb::fr(value.slice(NUM_LIMB_BITS * 2, NUM_LIMB_BITS * 3)))),
41 Limb(field_t<Builder>(parent_context,
42 bb::fr(value.slice(NUM_LIMB_BITS * 3, NUM_LIMB_BITS * 4)))) }
43 , prime_basis_limb(context, value)
44{
46}
47
48template <typename Builder, typename T>
50 const field_t<Builder>& high_bits_in,
51 const bool can_overflow,
52 const size_t maximum_bitlength)
53{
54 BB_ASSERT_EQ(low_bits_in.is_constant(), high_bits_in.is_constant());
55 BB_ASSERT((can_overflow == true && maximum_bitlength == 0) ||
56 (can_overflow == false && (maximum_bitlength == 0 || maximum_bitlength > (3 * NUM_LIMB_BITS))));
57
58 // Check that the values of two parts are within specified bounds
59 BB_ASSERT_LT(uint256_t(low_bits_in.get_value()), uint256_t(1) << (NUM_LIMB_BITS * 2));
60 BB_ASSERT_LT(uint256_t(high_bits_in.get_value()), uint256_t(1) << (NUM_LIMB_BITS * 2));
61
62 context = low_bits_in.context == nullptr ? high_bits_in.context : low_bits_in.context;
67 if (!low_bits_in.is_constant()) {
68 // Decompose the low bits into 2 limbs and range constrain them.
69 const auto limb_witnesses =
70 decompose_non_native_field_double_width_limb(context, low_bits_in.get_witness_index());
71 limb_0.witness_index = limb_witnesses[0];
72 limb_1.witness_index = limb_witnesses[1];
73 } else {
74 uint256_t slice_0 = uint256_t(low_bits_in.additive_constant).slice(0, NUM_LIMB_BITS);
75 uint256_t slice_1 = uint256_t(low_bits_in.additive_constant).slice(NUM_LIMB_BITS, 2 * NUM_LIMB_BITS);
76 limb_0 = field_t(context, bb::fr(slice_0));
77 limb_1 = field_t(context, bb::fr(slice_1));
78 }
79
80 // If we wish to continue working with this element with lazy reductions - i.e. not moding out again after each
81 // addition we apply a more limited range - 2^s for smallest s such that p<2^s (this is the case can_overflow ==
82 // false)
83 uint64_t num_last_limb_bits = (can_overflow) ? NUM_LIMB_BITS : NUM_LAST_LIMB_BITS;
84
85 // if maximum_bitlength is set, this supercedes can_overflow
86 if (maximum_bitlength > 0) {
87 BB_ASSERT_GT(maximum_bitlength, 3 * NUM_LIMB_BITS);
88 BB_ASSERT_LTE(maximum_bitlength, 4 * NUM_LIMB_BITS);
89 num_last_limb_bits = maximum_bitlength - (3 * NUM_LIMB_BITS);
90 }
91 // We create the high limb values similar to the low limb ones above
92 const uint64_t num_high_limb_bits = NUM_LIMB_BITS + num_last_limb_bits;
93 if (!high_bits_in.is_constant()) {
94 // Decompose the high bits into 2 limbs and range constrain them.
95 const auto limb_witnesses = decompose_non_native_field_double_width_limb(
96 context, high_bits_in.get_witness_index(), static_cast<size_t>(num_high_limb_bits));
97 limb_2.witness_index = limb_witnesses[0];
98 limb_3.witness_index = limb_witnesses[1];
99 } else {
100 uint256_t slice_2 = uint256_t(high_bits_in.additive_constant).slice(0, NUM_LIMB_BITS);
101 uint256_t slice_3 = uint256_t(high_bits_in.additive_constant).slice(NUM_LIMB_BITS, num_high_limb_bits);
102 limb_2 = field_t(context, bb::fr(slice_2));
103 limb_3 = field_t(context, bb::fr(slice_3));
104 }
105 binary_basis_limbs[0] = Limb(limb_0, DEFAULT_MAXIMUM_LIMB);
106 binary_basis_limbs[1] = Limb(limb_1, DEFAULT_MAXIMUM_LIMB);
107 binary_basis_limbs[2] = Limb(limb_2, DEFAULT_MAXIMUM_LIMB);
108 if (maximum_bitlength > 0) {
109 uint256_t max_limb_value = (uint256_t(1) << (maximum_bitlength - (3 * NUM_LIMB_BITS))) - 1;
110 binary_basis_limbs[3] = Limb(limb_3, max_limb_value);
111 } else {
112 binary_basis_limbs[3] =
113 Limb(limb_3, can_overflow ? DEFAULT_MAXIMUM_LIMB : DEFAULT_MAXIMUM_MOST_SIGNIFICANT_LIMB);
114 }
115 prime_basis_limb = low_bits_in + (high_bits_in * shift_2);
116 auto new_tag = OriginTag(low_bits_in.tag, high_bits_in.tag);
117 set_origin_tag(new_tag);
118}
119
120template <typename Builder, typename T>
122 : context(other.context)
123 , binary_basis_limbs{ other.binary_basis_limbs[0],
124 other.binary_basis_limbs[1],
125 other.binary_basis_limbs[2],
126 other.binary_basis_limbs[3] }
127 , prime_basis_limb(other.prime_basis_limb)
128{}
129
130template <typename Builder, typename T>
132 : context(other.context)
133 , binary_basis_limbs{ other.binary_basis_limbs[0],
134 other.binary_basis_limbs[1],
135 other.binary_basis_limbs[2],
136 other.binary_basis_limbs[3] }
137 , prime_basis_limb(other.prime_basis_limb)
138{}
139
140template <typename Builder, typename T>
142 const uint512_t& value,
143 const bool can_overflow,
144 const size_t maximum_bitlength)
145{
146 BB_ASSERT((can_overflow == true && maximum_bitlength == 0) ||
147 (can_overflow == false && (maximum_bitlength == 0 || maximum_bitlength > (3 * NUM_LIMB_BITS))));
149 limbs[0] = value.slice(0, NUM_LIMB_BITS).lo;
150 limbs[1] = value.slice(NUM_LIMB_BITS, NUM_LIMB_BITS * 2).lo;
151 limbs[2] = value.slice(NUM_LIMB_BITS * 2, NUM_LIMB_BITS * 3).lo;
152 limbs[3] = value.slice(NUM_LIMB_BITS * 3, NUM_LIMB_BITS * 4).lo;
153
154 field_t<Builder> limb_0(ctx);
155 field_t<Builder> limb_1(ctx);
156 field_t<Builder> limb_2(ctx);
157 field_t<Builder> limb_3(ctx);
158 field_t<Builder> prime_limb(ctx);
159 limb_0.witness_index = ctx->add_variable(bb::fr(limbs[0]));
160 limb_1.witness_index = ctx->add_variable(bb::fr(limbs[1]));
161 limb_2.witness_index = ctx->add_variable(bb::fr(limbs[2]));
162 limb_3.witness_index = ctx->add_variable(bb::fr(limbs[3]));
163 prime_limb.witness_index = ctx->add_variable(limb_0.get_value() + limb_1.get_value() * shift_1 +
164 limb_2.get_value() * shift_2 + limb_3.get_value() * shift_3);
165 // evaluate prime basis limb with addition gate that taps into the 4th wire in the next gate
166 ctx->create_big_add_gate({ limb_1.get_witness_index(),
167 limb_2.get_witness_index(),
168 limb_3.get_witness_index(),
169 prime_limb.get_witness_index(),
170 shift_1,
171 shift_2,
172 shift_3,
173 -1,
174 0 },
175 true);
176 // NOTE(https://github.com/AztecProtocol/barretenberg/issues/879): Optimisation opportunity to use a single gate
177 // (and remove dummy gate). Currently, dummy gate is necessary for preceeding big add gate as these gates fall in
178 // the arithmetic block. More details on the linked Github issue.
179 ctx->create_unconstrained_gate(
180 ctx->blocks.arithmetic, ctx->zero_idx(), ctx->zero_idx(), ctx->zero_idx(), limb_0.get_witness_index());
181
182 uint64_t num_last_limb_bits = (can_overflow) ? NUM_LIMB_BITS : NUM_LAST_LIMB_BITS;
183
184 bigfield result(ctx);
185 result.binary_basis_limbs[0] = Limb(limb_0, DEFAULT_MAXIMUM_LIMB);
186 result.binary_basis_limbs[1] = Limb(limb_1, DEFAULT_MAXIMUM_LIMB);
187 result.binary_basis_limbs[2] = Limb(limb_2, DEFAULT_MAXIMUM_LIMB);
188 result.binary_basis_limbs[3] =
189 Limb(limb_3, can_overflow ? DEFAULT_MAXIMUM_LIMB : DEFAULT_MAXIMUM_MOST_SIGNIFICANT_LIMB);
190
191 // if maximum_bitlength is set, this supercedes can_overflow
192 if (maximum_bitlength > 0) {
193 BB_ASSERT_GT(maximum_bitlength, 3 * NUM_LIMB_BITS);
194 num_last_limb_bits = maximum_bitlength - (3 * NUM_LIMB_BITS);
195 uint256_t max_limb_value = (uint256_t(1) << num_last_limb_bits) - 1;
196 result.binary_basis_limbs[3].maximum_value = max_limb_value;
197 }
198 result.prime_basis_limb = prime_limb;
199 ctx->range_constrain_two_limbs(limb_0.get_witness_index(),
200 limb_1.get_witness_index(),
201 static_cast<size_t>(NUM_LIMB_BITS),
202 static_cast<size_t>(NUM_LIMB_BITS),
203 "bigfield::create_from_u512_as_witness: limb 0 or 1 too large");
204 ctx->range_constrain_two_limbs(limb_2.get_witness_index(),
205 limb_3.get_witness_index(),
206 static_cast<size_t>(NUM_LIMB_BITS),
207 static_cast<size_t>(num_last_limb_bits),
208 "bigfield::create_from_u512_as_witness: limb 2 or 3 too large");
209
210 // Mark the element as coming out of nowhere
211 result.set_free_witness_tag();
212
213 return result;
214}
215
216template <typename Builder, typename T> bigfield<Builder, T>::bigfield(const byte_array<Builder>& bytes)
217{
218 BB_ASSERT_EQ(bytes.size(), 32U); // we treat input as a 256-bit big integer
219 const auto split_byte_into_nibbles = [](Builder* ctx, const field_t<Builder>& split_byte) {
220 const uint64_t byte_val = uint256_t(split_byte.get_value()).data[0];
221 const uint64_t lo_nibble_val = byte_val & 15ULL;
222 const uint64_t hi_nibble_val = byte_val >> 4;
223
224 const field_t<Builder> lo_nibble(witness_t<Builder>(ctx, lo_nibble_val));
225 const field_t<Builder> hi_nibble(witness_t<Builder>(ctx, hi_nibble_val));
226 lo_nibble.create_range_constraint(4, "bigfield: lo_nibble too large");
227 hi_nibble.create_range_constraint(4, "bigfield: hi_nibble too large");
228
229 const uint256_t hi_nibble_shift = uint256_t(1) << 4;
230 const field_t<Builder> sum = lo_nibble + (hi_nibble * hi_nibble_shift);
231 sum.assert_equal(split_byte);
232 lo_nibble.set_origin_tag(split_byte.tag);
233 hi_nibble.set_origin_tag(split_byte.tag);
234 return std::make_pair(lo_nibble, hi_nibble);
235 };
236
237 const auto reconstruct_two_limbs = [&split_byte_into_nibbles](Builder* ctx,
238 const field_t<Builder>& hi_bytes,
239 const field_t<Builder>& lo_bytes,
240 const field_t<Builder>& split_byte) {
241 const auto [lo_nibble, hi_nibble] = split_byte_into_nibbles(ctx, split_byte);
242
243 const uint256_t hi_bytes_shift = uint256_t(1) << 4;
244 const uint256_t lo_nibble_shift = uint256_t(1) << 64;
245 field_t<Builder> hi_limb = hi_nibble + hi_bytes * hi_bytes_shift;
246 field_t<Builder> lo_limb = lo_bytes + lo_nibble * lo_nibble_shift;
247 return std::make_pair(lo_limb, hi_limb);
248 };
249 Builder* ctx = bytes.get_context();
250
251 // The input bytes are interpreted as a 256-bit integer, which is split into 4 limbs as follows:
252 //
253 // overlap byte overlap byte
254 // ↓ ↓
255 // [ b31 b30 ... b25 b24 | b23 | b22 b21 ... b16 b15 | b14 b13 ... b8 b7 | b06 | b5 b4 ... b1 b0 ]
256 // |--------------------------|--------------------------|-----------------------|----------------------|
257 // ↑ 68 bits ↑ 68 bits ↑ 68 bits ↑ 52 bits ↑
258 // [ limb l0 | limb l1 | limb l2 | limb l3 ]
259 //
260 const field_t<Builder> hi_8_bytes(bytes.slice(0, 6));
261 const field_t<Builder> mid_split_byte(bytes.slice(6, 1));
262 const field_t<Builder> mid_8_bytes(bytes.slice(7, 8));
263
264 const field_t<Builder> lo_8_bytes(bytes.slice(15, 8));
265 const field_t<Builder> lo_split_byte(bytes.slice(23, 1));
266 const field_t<Builder> lolo_8_bytes(bytes.slice(24, 8));
267
268 const auto [limb0, limb1] = reconstruct_two_limbs(ctx, lo_8_bytes, lolo_8_bytes, lo_split_byte);
269 const auto [limb2, limb3] = reconstruct_two_limbs(ctx, hi_8_bytes, mid_8_bytes, mid_split_byte);
270
271 const auto res = bigfield::unsafe_construct_from_limbs(limb0, limb1, limb2, limb3, true);
272
273 const auto num_last_limb_bits = 256 - (NUM_LIMB_BITS * 3);
274 res.binary_basis_limbs[3].maximum_value = (uint64_t(1) << num_last_limb_bits) - 1;
275 *this = res;
276 set_origin_tag(bytes.get_origin_tag());
277}
278
279template <typename Builder, typename T> bigfield<Builder, T>& bigfield<Builder, T>::operator=(const bigfield& other)
280{
281 if (this == &other) {
282 return *this;
283 }
284 context = other.context;
285 binary_basis_limbs[0] = other.binary_basis_limbs[0];
286 binary_basis_limbs[1] = other.binary_basis_limbs[1];
287 binary_basis_limbs[2] = other.binary_basis_limbs[2];
288 binary_basis_limbs[3] = other.binary_basis_limbs[3];
289 prime_basis_limb = other.prime_basis_limb;
290 return *this;
291}
292
293template <typename Builder, typename T> bigfield<Builder, T>& bigfield<Builder, T>::operator=(bigfield&& other) noexcept
294{
295 context = other.context;
296 binary_basis_limbs[0] = other.binary_basis_limbs[0];
297 binary_basis_limbs[1] = other.binary_basis_limbs[1];
298 binary_basis_limbs[2] = other.binary_basis_limbs[2];
299 binary_basis_limbs[3] = other.binary_basis_limbs[3];
300 prime_basis_limb = other.prime_basis_limb;
301 return *this;
302}
303
304template <typename Builder, typename T> uint512_t bigfield<Builder, T>::get_value() const
305{
306 uint512_t t0 = uint256_t(binary_basis_limbs[0].element.get_value());
307 uint512_t t1 = uint256_t(binary_basis_limbs[1].element.get_value());
308 uint512_t t2 = uint256_t(binary_basis_limbs[2].element.get_value());
309 uint512_t t3 = uint256_t(binary_basis_limbs[3].element.get_value());
310 return t0 + (t1 << (NUM_LIMB_BITS)) + (t2 << (2 * NUM_LIMB_BITS)) + (t3 << (3 * NUM_LIMB_BITS));
311}
312
313template <typename Builder, typename T> uint512_t bigfield<Builder, T>::get_maximum_value() const
314{
315 uint512_t t0 = uint512_t(binary_basis_limbs[0].maximum_value);
316 uint512_t t1 = uint512_t(binary_basis_limbs[1].maximum_value) << NUM_LIMB_BITS;
317 uint512_t t2 = uint512_t(binary_basis_limbs[2].maximum_value) << (NUM_LIMB_BITS * 2);
318 uint512_t t3 = uint512_t(binary_basis_limbs[3].maximum_value) << (NUM_LIMB_BITS * 3);
319 return t0 + t1 + t2 + t3;
320}
321
322template <typename Builder, typename T>
324 const uint256_t& other_maximum_value) const
325{
326 reduction_check();
327 BB_ASSERT_LTE(uint512_t(other_maximum_value) + uint512_t(binary_basis_limbs[0].maximum_value),
328 uint512_t(get_maximum_unreduced_limb_value()));
329 // needed cause a constant doesn't have a valid context
330 Builder* ctx = context ? context : other.context;
331
332 if (is_constant() && other.is_constant()) {
333 return bigfield(ctx, uint256_t((get_value() + uint256_t(other.get_value())) % modulus_u512));
334 }
335
337 // If the original value is constant, we have to reinitialize the higher limbs to be witnesses when adding a witness
338 if (is_constant()) {
339 auto context = other.context;
340 for (size_t i = 1; i < NUM_LIMBS; i++) {
341 // Construct a witness element from the original constant limb
342 result.binary_basis_limbs[i] =
343 Limb(field_t<Builder>::from_witness(context, binary_basis_limbs[i].element.get_value()),
344 binary_basis_limbs[i].maximum_value);
345 // Ensure it is fixed
346 result.binary_basis_limbs[i].element.fix_witness();
347 result.context = ctx;
348 }
349 } else {
350
351 // if this element is a witness, then all limbs will be witnesses
352 result = *this;
353 }
354 result.binary_basis_limbs[0].maximum_value = binary_basis_limbs[0].maximum_value + other_maximum_value;
355
356 result.binary_basis_limbs[0].element = binary_basis_limbs[0].element + other;
357 result.prime_basis_limb = prime_basis_limb + other;
358 result.set_origin_tag(OriginTag(get_origin_tag(), other.tag));
359 return result;
360}
361
362template <typename Builder, typename T>
364{
365 reduction_check();
366 other.reduction_check();
367 // needed cause a constant doesn't have a valid context
368 Builder* ctx = context ? context : other.context;
369
370 if (is_constant() && other.is_constant()) {
371 auto result = bigfield(ctx, uint256_t((get_value() + other.get_value()) % modulus_u512));
372 result.set_origin_tag(OriginTag(get_origin_tag(), other.get_origin_tag()));
373 return result;
374 }
375 bigfield result(ctx);
376 result.binary_basis_limbs[0].maximum_value =
377 binary_basis_limbs[0].maximum_value + other.binary_basis_limbs[0].maximum_value;
378 result.binary_basis_limbs[1].maximum_value =
379 binary_basis_limbs[1].maximum_value + other.binary_basis_limbs[1].maximum_value;
380 result.binary_basis_limbs[2].maximum_value =
381 binary_basis_limbs[2].maximum_value + other.binary_basis_limbs[2].maximum_value;
382 result.binary_basis_limbs[3].maximum_value =
383 binary_basis_limbs[3].maximum_value + other.binary_basis_limbs[3].maximum_value;
384
385 // If both the elements are witnesses, we use an optimized addition trick that uses 4 gates instead of 5.
386 //
387 // Naively, we would need 5 gates to add two bigfield elements: 4 gates to add the binary basis limbs and
388 // 1 gate to add the prime basis limbs.
389 //
390 // In the optimized version, we fit 15 witnesses into 4 gates (4 + 4 + 4 + 3 = 15), and we add the prime basis limbs
391 // and one of the binary basis limbs in the first gate.
392 // gate 1: z.limb_0 = x.limb_0 + y.limb_0 && z.prime_limb = x.prime_limb + y.prime_limb
393 // gate 2: z.limb_1 = x.limb_1 + y.limb_1
394 // gate 3: z.limb_2 = x.limb_2 + y.limb_2
395 // gate 4: z.limb_3 = x.limb_3 + y.limb_3
396 //
397 bool both_witness = !is_constant() && !other.is_constant();
398 bool both_prime_limb_multiplicative_constant_one =
399 (prime_basis_limb.multiplicative_constant == 1 && other.prime_basis_limb.multiplicative_constant == 1);
400 if (both_prime_limb_multiplicative_constant_one && both_witness) {
401 bool limbconst = is_constant() || other.is_constant() ||
402 field_ct::witness_indices_match(prime_basis_limb, other.prime_basis_limb);
403 if (!limbconst) {
404 // Extract witness indices and multiplicative constants for binary basis limbs
405 std::array<std::pair<uint32_t, bb::fr>, NUM_LIMBS> x_scaled;
406 std::array<std::pair<uint32_t, bb::fr>, NUM_LIMBS> y_scaled;
408
409 for (size_t i = 0; i < NUM_LIMBS; ++i) {
410 const auto& x_limb = binary_basis_limbs[i].element;
411 const auto& y_limb = other.binary_basis_limbs[i].element;
412
413 x_scaled[i] = { x_limb.witness_index, x_limb.multiplicative_constant };
414 y_scaled[i] = { y_limb.witness_index, y_limb.multiplicative_constant };
415 c_adds[i] = bb::fr(x_limb.additive_constant + y_limb.additive_constant);
416 }
417
418 // Extract witness indices for prime basis limb
419 uint32_t x_prime(prime_basis_limb.witness_index);
420 uint32_t y_prime(other.prime_basis_limb.witness_index);
421 bb::fr c_prime(prime_basis_limb.additive_constant + other.prime_basis_limb.additive_constant);
422
423 const auto output_witnesses =
424 ctx->evaluate_non_native_field_addition({ x_scaled[0], y_scaled[0], c_adds[0] },
425 { x_scaled[1], y_scaled[1], c_adds[1] },
426 { x_scaled[2], y_scaled[2], c_adds[2] },
427 { x_scaled[3], y_scaled[3], c_adds[3] },
428 { x_prime, y_prime, c_prime });
429
430 result.binary_basis_limbs[0].element = field_t<Builder>::from_witness_index(ctx, output_witnesses[0]);
431 result.binary_basis_limbs[1].element = field_t<Builder>::from_witness_index(ctx, output_witnesses[1]);
432 result.binary_basis_limbs[2].element = field_t<Builder>::from_witness_index(ctx, output_witnesses[2]);
433 result.binary_basis_limbs[3].element = field_t<Builder>::from_witness_index(ctx, output_witnesses[3]);
434 result.prime_basis_limb = field_t<Builder>::from_witness_index(ctx, output_witnesses[4]);
435 result.set_origin_tag(OriginTag(get_origin_tag(), other.get_origin_tag()));
436 return result;
437 }
438 }
439
440 // If one of the elements is a constant or its prime limb does not have a multiplicative constant of 1, we
441 // use the standard addition method. This will not use additional gates because field addition with one constant
442 // does not require any additional gates.
443 result.binary_basis_limbs[0].element = binary_basis_limbs[0].element + other.binary_basis_limbs[0].element;
444 result.binary_basis_limbs[1].element = binary_basis_limbs[1].element + other.binary_basis_limbs[1].element;
445 result.binary_basis_limbs[2].element = binary_basis_limbs[2].element + other.binary_basis_limbs[2].element;
446 result.binary_basis_limbs[3].element = binary_basis_limbs[3].element + other.binary_basis_limbs[3].element;
447 result.prime_basis_limb = prime_basis_limb + other.prime_basis_limb;
449 result.set_origin_tag(OriginTag(get_origin_tag(), other.get_origin_tag()));
450 return result;
451}
452
453template <typename Builder, typename T>
455{
456 reduction_check();
457 add_a.reduction_check();
458 add_b.reduction_check();
459
460 Builder* ctx = (context == nullptr) ? (add_a.context == nullptr ? add_b.context : add_a.context) : context;
461
462 if (is_constant() && add_a.is_constant() && add_b.is_constant()) {
463 auto result = bigfield(ctx, uint256_t((get_value() + add_a.get_value() + add_b.get_value()) % modulus_u512));
464 result.set_origin_tag(OriginTag(this->get_origin_tag(), add_a.get_origin_tag(), add_b.get_origin_tag()));
465 return result;
466 }
467
468 bigfield result(ctx);
469 result.binary_basis_limbs[0].maximum_value = binary_basis_limbs[0].maximum_value +
470 add_a.binary_basis_limbs[0].maximum_value +
471 add_b.binary_basis_limbs[0].maximum_value;
472 result.binary_basis_limbs[1].maximum_value = binary_basis_limbs[1].maximum_value +
473 add_a.binary_basis_limbs[1].maximum_value +
474 add_b.binary_basis_limbs[1].maximum_value;
475 result.binary_basis_limbs[2].maximum_value = binary_basis_limbs[2].maximum_value +
476 add_a.binary_basis_limbs[2].maximum_value +
477 add_b.binary_basis_limbs[2].maximum_value;
478 result.binary_basis_limbs[3].maximum_value = binary_basis_limbs[3].maximum_value +
479 add_a.binary_basis_limbs[3].maximum_value +
480 add_b.binary_basis_limbs[3].maximum_value;
481
482 result.binary_basis_limbs[0].element =
483 binary_basis_limbs[0].element.add_two(add_a.binary_basis_limbs[0].element, add_b.binary_basis_limbs[0].element);
484 result.binary_basis_limbs[1].element =
485 binary_basis_limbs[1].element.add_two(add_a.binary_basis_limbs[1].element, add_b.binary_basis_limbs[1].element);
486 result.binary_basis_limbs[2].element =
487 binary_basis_limbs[2].element.add_two(add_a.binary_basis_limbs[2].element, add_b.binary_basis_limbs[2].element);
488 result.binary_basis_limbs[3].element =
489 binary_basis_limbs[3].element.add_two(add_a.binary_basis_limbs[3].element, add_b.binary_basis_limbs[3].element);
490 result.prime_basis_limb = prime_basis_limb.add_two(add_a.prime_basis_limb, add_b.prime_basis_limb);
491 result.set_origin_tag(OriginTag(this->get_origin_tag(), add_a.get_origin_tag(), add_b.get_origin_tag()));
492 return result;
493}
494
495template <typename Builder, typename T>
497{
498 Builder* ctx = context ? context : other.context;
499 reduction_check();
500 other.reduction_check();
501
502 if (is_constant() && other.is_constant()) {
503 uint512_t left = get_value() % modulus_u512;
504 uint512_t right = other.get_value() % modulus_u512;
505 uint512_t out = (left + modulus_u512 - right) % modulus_u512;
506
507 auto result = bigfield(ctx, uint256_t(out.lo));
508 result.set_origin_tag(OriginTag(get_origin_tag(), other.get_origin_tag()));
509 return result;
510 }
511
512 if (other.is_constant()) {
513 uint512_t right = other.get_value() % modulus_u512;
514 uint512_t neg_right = (modulus_u512 - right) % modulus_u512;
515 bigfield summand = bigfield(ctx, uint256_t(neg_right.lo));
516 summand.set_origin_tag(OriginTag(other.get_origin_tag()));
517 return operator+(summand);
518 }
519
538
547 uint256_t limb_0_maximum_value = other.binary_basis_limbs[0].maximum_value;
548
549 // Compute maximum shift factor for limb_0
550 uint64_t limb_0_borrow_shift = std::max(limb_0_maximum_value.get_msb() + 1, NUM_LIMB_BITS);
551
552 // Compute the maximum negative value of limb_1, including the bits limb_0 may need to borrow
553 uint256_t limb_1_maximum_value =
554 other.binary_basis_limbs[1].maximum_value + (uint256_t(1) << (limb_0_borrow_shift - NUM_LIMB_BITS));
555
556 // repeat the above for the remaining limbs
557 uint64_t limb_1_borrow_shift = std::max(limb_1_maximum_value.get_msb() + 1, NUM_LIMB_BITS);
558 uint256_t limb_2_maximum_value =
559 other.binary_basis_limbs[2].maximum_value + (uint256_t(1) << (limb_1_borrow_shift - NUM_LIMB_BITS));
560 uint64_t limb_2_borrow_shift = std::max(limb_2_maximum_value.get_msb() + 1, NUM_LIMB_BITS);
561
562 uint256_t limb_3_maximum_value =
563 other.binary_basis_limbs[3].maximum_value + (uint256_t(1) << (limb_2_borrow_shift - NUM_LIMB_BITS));
564
573 uint1024_t constant_to_add_factor =
574 (uint1024_t(limb_3_maximum_value) << (NUM_LIMB_BITS * 3)) / uint1024_t(modulus_u512) + uint1024_t(1);
575 uint512_t constant_to_add = constant_to_add_factor.lo * modulus_u512;
576
601 uint256_t t0(uint256_t(1) << limb_0_borrow_shift);
602 uint256_t t1((uint256_t(1) << limb_1_borrow_shift) - (uint256_t(1) << (limb_0_borrow_shift - NUM_LIMB_BITS)));
603 uint256_t t2((uint256_t(1) << limb_2_borrow_shift) - (uint256_t(1) << (limb_1_borrow_shift - NUM_LIMB_BITS)));
604 uint256_t t3(uint256_t(1) << (limb_2_borrow_shift - NUM_LIMB_BITS));
610 uint256_t to_add_0 = uint256_t(constant_to_add.slice(0, NUM_LIMB_BITS)) + t0;
611 uint256_t to_add_1 = uint256_t(constant_to_add.slice(NUM_LIMB_BITS, NUM_LIMB_BITS * 2)) + t1;
612 uint256_t to_add_2 = uint256_t(constant_to_add.slice(NUM_LIMB_BITS * 2, NUM_LIMB_BITS * 3)) + t2;
613 uint256_t to_add_3 = uint256_t(constant_to_add.slice(NUM_LIMB_BITS * 3, NUM_LIMB_BITS * 4)) - t3;
614
618 result.binary_basis_limbs[0].maximum_value = binary_basis_limbs[0].maximum_value + to_add_0;
619 result.binary_basis_limbs[1].maximum_value = binary_basis_limbs[1].maximum_value + to_add_1;
620 result.binary_basis_limbs[2].maximum_value = binary_basis_limbs[2].maximum_value + to_add_2;
621 result.binary_basis_limbs[3].maximum_value = binary_basis_limbs[3].maximum_value + to_add_3;
622
626 result.binary_basis_limbs[0].element = binary_basis_limbs[0].element + bb::fr(to_add_0);
627 result.binary_basis_limbs[1].element = binary_basis_limbs[1].element + bb::fr(to_add_1);
628 result.binary_basis_limbs[2].element = binary_basis_limbs[2].element + bb::fr(to_add_2);
629 result.binary_basis_limbs[3].element = binary_basis_limbs[3].element + bb::fr(to_add_3);
630
631 bool both_witness = !is_constant() && !other.is_constant();
632 bool both_prime_limb_multiplicative_constant_one =
633 (prime_basis_limb.multiplicative_constant == 1 && other.prime_basis_limb.multiplicative_constant == 1);
634 if (both_prime_limb_multiplicative_constant_one && both_witness) {
635 bool limbconst = is_constant() || other.is_constant() ||
636 field_ct::witness_indices_match(prime_basis_limb, other.prime_basis_limb);
637
638 if (!limbconst) {
639 // Extract witness indices and multiplicative constants for binary basis limbs
640 std::array<std::pair<uint32_t, bb::fr>, NUM_LIMBS> x_scaled;
641 std::array<std::pair<uint32_t, bb::fr>, NUM_LIMBS> y_scaled;
643
644 for (size_t i = 0; i < NUM_LIMBS; ++i) {
645 const auto& x_limb = result.binary_basis_limbs[i].element;
646 const auto& y_limb = other.binary_basis_limbs[i].element;
647
648 x_scaled[i] = { x_limb.witness_index, x_limb.multiplicative_constant };
649 y_scaled[i] = { y_limb.witness_index, y_limb.multiplicative_constant };
650 c_diffs[i] = bb::fr(x_limb.additive_constant - y_limb.additive_constant);
651 }
652
653 // Extract witness indices for prime basis limb
654 uint32_t x_prime(prime_basis_limb.witness_index);
655 uint32_t y_prime(other.prime_basis_limb.witness_index);
656 bb::fr c_prime(prime_basis_limb.additive_constant - other.prime_basis_limb.additive_constant);
657 uint512_t constant_to_add_mod_native = (constant_to_add) % prime_basis.modulus;
658 c_prime += bb::fr(constant_to_add_mod_native.lo);
659
660 const auto output_witnesses =
661 ctx->evaluate_non_native_field_subtraction({ x_scaled[0], y_scaled[0], c_diffs[0] },
662 { x_scaled[1], y_scaled[1], c_diffs[1] },
663 { x_scaled[2], y_scaled[2], c_diffs[2] },
664 { x_scaled[3], y_scaled[3], c_diffs[3] },
665 { x_prime, y_prime, c_prime });
666
667 result.binary_basis_limbs[0].element = field_t<Builder>::from_witness_index(ctx, output_witnesses[0]);
668 result.binary_basis_limbs[1].element = field_t<Builder>::from_witness_index(ctx, output_witnesses[1]);
669 result.binary_basis_limbs[2].element = field_t<Builder>::from_witness_index(ctx, output_witnesses[2]);
670 result.binary_basis_limbs[3].element = field_t<Builder>::from_witness_index(ctx, output_witnesses[3]);
671 result.prime_basis_limb = field_t<Builder>::from_witness_index(ctx, output_witnesses[4]);
672
673 result.set_origin_tag(OriginTag(get_origin_tag(), other.get_origin_tag()));
674 return result;
675 }
676 }
677
678 result.binary_basis_limbs[0].element -= other.binary_basis_limbs[0].element;
679 result.binary_basis_limbs[1].element -= other.binary_basis_limbs[1].element;
680 result.binary_basis_limbs[2].element -= other.binary_basis_limbs[2].element;
681 result.binary_basis_limbs[3].element -= other.binary_basis_limbs[3].element;
682
686 uint512_t constant_to_add_mod_native = (constant_to_add) % prime_basis.modulus;
687 field_t prime_basis_to_add(ctx, bb::fr(constant_to_add_mod_native.lo));
688 result.prime_basis_limb = prime_basis_limb + prime_basis_to_add;
689 result.prime_basis_limb -= other.prime_basis_limb;
690 return result;
691}
692
693template <typename Builder, typename T>
695{
696 // First we do basic reduction checks of individual elements
697 reduction_check();
698 other.reduction_check();
699 Builder* ctx = context ? context : other.context;
700 // Now we can actually compute the quotient and remainder values
701 const auto [quotient_value, remainder_value] = compute_quotient_remainder_values(*this, other, {});
702 bigfield remainder;
703 bigfield quotient;
704 // If operands are constant, define result as a constant value and return
705 if (is_constant() && other.is_constant()) {
706 remainder = bigfield(ctx, uint256_t(remainder_value.lo));
707 remainder.set_origin_tag(OriginTag(get_origin_tag(), other.get_origin_tag()));
708 return remainder;
709 } else {
710 // when writing a*b = q*p + r we wish to enforce r<2^s for smallest s such that p<2^s
711 // hence the second constructor call is with can_overflow=false. This will allow using r in more additions
712 // mod 2^t without needing to apply the mod, where t=4*NUM_LIMB_BITS
713
714 // Check if the product overflows CRT or the quotient can't be contained in a range proof and reduce
715 // accordingly
716 auto [reduction_required, num_quotient_bits] =
717 get_quotient_reduction_info({ get_maximum_value() }, { other.get_maximum_value() }, {});
718 if (reduction_required) {
719 if (get_maximum_value() > other.get_maximum_value()) {
720 self_reduce();
721 } else {
722 other.self_reduce();
723 }
724 return (*this).operator*(other);
725 }
726 quotient = create_from_u512_as_witness(ctx, quotient_value, false, num_quotient_bits);
727 remainder = create_from_u512_as_witness(ctx, remainder_value);
728 };
729
730 // Call `evaluate_multiply_add` to validate the correctness of our computed quotient and remainder
731 unsafe_evaluate_multiply_add(*this, other, {}, quotient, { remainder });
732
733 remainder.set_origin_tag(OriginTag(get_origin_tag(), other.get_origin_tag()));
734 return remainder;
735}
736
737template <typename Builder, typename T>
739{
740
741 return internal_div({ *this }, other, true);
742}
751template <typename Builder, typename T>
753{
754 BB_ASSERT_GT(terms.size(), 0U);
755
756 if (terms.size() == 1) {
757 return terms[0];
758 }
759
760 bigfield acc = terms[0];
761 for (size_t i = 1; i < (terms.size() + 1) / 2; i++) {
762 acc = acc.add_two(terms[2 * i - 1], terms[2 * i]);
763 }
764 if ((terms.size() & 1) == 0) {
765 acc += terms[terms.size() - 1];
766 }
767 return acc;
768}
769
779template <typename Builder, typename T>
781 const bigfield& denominator,
782 bool check_for_zero)
783{
784 BB_ASSERT_LT(numerators.size(), MAXIMUM_SUMMAND_COUNT);
785 if (numerators.empty()) {
786 if (check_for_zero) {
787 // We do not want to trigger division by zero in the empty sum case
788 denominator.assert_is_not_equal(zero());
789 }
790 return bigfield<Builder, T>(denominator.get_context(), uint256_t(0));
791 }
792
793 denominator.reduction_check();
794 Builder* ctx = denominator.context;
795 uint512_t numerator_values(0);
796 bool numerator_constant = true;
797 OriginTag tag = denominator.get_origin_tag();
798 for (const auto& numerator_element : numerators) {
799 ctx = (ctx == nullptr) ? numerator_element.get_context() : ctx;
800 numerator_element.reduction_check();
801 numerator_values += numerator_element.get_value();
802 numerator_constant = numerator_constant && (numerator_element.is_constant());
803 tag = OriginTag(tag, numerator_element.get_origin_tag());
804 }
805
806 // a / b = c
807 // => c * b = a mod p
808 const uint1024_t left = uint1024_t(numerator_values);
809 const uint1024_t right = uint1024_t(denominator.get_value());
810 const uint1024_t modulus(target_basis.modulus);
811 // Compute the division result natively in the target field: reduce both operands mod p, then multiply
812 // the numerator by the denominator's inverse (Montgomery mul + safegcd). This is far cheaper than the
813 // uint512 extended-Euclid invmod plus uint1024 modular reduction it replaces. A denominator that is a
814 // nonzero multiple of p reduces to zero and yields 0, matching the previous invmod behavior.
815 uint512_t inverse_value(0);
816 const uint256_t denominator_reduced = (right % modulus).lo.lo;
817 if (denominator_reduced != 0) {
818 using NativeField = bb::field<T>;
819 const NativeField numerator_native(uint256_t((left % modulus).lo.lo));
820 inverse_value = uint512_t(uint256_t(numerator_native * NativeField(denominator_reduced).invert()));
821 }
822
823 const uint1024_t quotient_1024 =
824 (uint1024_t(inverse_value) * right + unreduced_zero().get_value() - left) / modulus;
825 const uint512_t quotient_value = quotient_1024.lo;
826
827 bigfield inverse;
828 bigfield quotient;
829 if (numerator_constant && denominator.is_constant()) {
830 if (check_for_zero) {
831 // We want to avoid division by zero in the constant case
832 BB_ASSERT(denominator.get_value() != uint512_t(0), "bigfield: division by zero in constant division");
833 }
834 inverse = bigfield(ctx, uint256_t(inverse_value));
835 inverse.set_origin_tag(tag);
836 return inverse;
837 } else {
838 // NOTE(https://github.com/AztecProtocol/aztec-packages/issues/15385): We can do a simplification when the
839 // denominator is constant. We can compute its inverse out-of-circuit and then multiply it with the numerator.
840 // We only add the check if the result is non-constant
841 std::vector<uint1024_t> numerator_max;
842 for (const auto& n : numerators) {
843 numerator_max.push_back(n.get_maximum_value());
844 }
845
846 auto [reduction_required, num_quotient_bits] =
847 get_quotient_reduction_info({ static_cast<uint512_t>(DEFAULT_MAXIMUM_REMAINDER) },
848 { denominator.get_maximum_value() },
849 { unreduced_zero() },
850 numerator_max);
851 if (reduction_required) {
852
853 denominator.self_reduce();
854 return internal_div(numerators, denominator, check_for_zero);
855 }
856 // We do this after the quotient check, since this creates gates and we don't want to do this twice
857 if (check_for_zero) {
858 denominator.assert_is_not_equal(zero());
859 }
860
861 quotient = create_from_u512_as_witness(ctx, quotient_value, false, num_quotient_bits);
862 inverse = create_from_u512_as_witness(ctx, inverse_value);
863 }
864
865 inverse.set_origin_tag(tag);
866 unsafe_evaluate_multiply_add(denominator, inverse, { unreduced_zero() }, quotient, numerators);
867 return inverse;
868}
869
876template <typename Builder, typename T>
878 const bigfield& denominator)
879{
880 return internal_div(numerators, denominator, false);
881}
882
883template <typename Builder, typename T>
885{
886 return internal_div({ *this }, denominator, false);
887}
888
894template <typename Builder, typename T>
896 const bigfield& denominator)
897{
898 return internal_div(numerators, denominator, true);
899}
900
901template <typename Builder, typename T> bigfield<Builder, T> bigfield<Builder, T>::sqr() const
902{
903 reduction_check();
904 Builder* ctx = context;
905
906 const auto [quotient_value, remainder_value] = compute_quotient_remainder_values(*this, *this, {});
907
908 bigfield remainder;
909 bigfield quotient;
910 if (is_constant()) {
911 remainder = bigfield(ctx, uint256_t(remainder_value.lo));
912 return remainder;
913 } else {
914
915 auto [reduction_required, num_quotient_bits] = get_quotient_reduction_info(
916 { get_maximum_value() }, { get_maximum_value() }, {}, { DEFAULT_MAXIMUM_REMAINDER });
917 if (reduction_required) {
918 self_reduce();
919 return sqr();
920 }
921
922 quotient = create_from_u512_as_witness(ctx, quotient_value, false, num_quotient_bits);
923 remainder = create_from_u512_as_witness(ctx, remainder_value);
924 };
925
926 unsafe_evaluate_square_add(*this, {}, quotient, remainder);
927 remainder.set_origin_tag(get_origin_tag());
928 return remainder;
929}
930
931template <typename Builder, typename T>
933{
934 BB_ASSERT_LTE(to_add.size(), MAXIMUM_SUMMAND_COUNT);
935 reduction_check();
936
937 Builder* ctx = context;
938
940 bool add_constant = true;
941 for (const auto& add_element : to_add) {
942 add_element.reduction_check();
943 add_values += add_element.get_value();
944 add_constant = add_constant && (add_element.is_constant());
945 }
946
947 const uint1024_t left(get_value());
948 const uint1024_t right(get_value());
949 const uint1024_t add_right(add_values);
950 const uint1024_t modulus(target_basis.modulus);
951
952 bigfield remainder;
953 bigfield quotient;
954 if (is_constant()) {
955 if (add_constant) {
956
957 const auto [quotient_1024, remainder_1024] = (left * right + add_right).divmod(modulus);
958 remainder = bigfield(ctx, uint256_t(remainder_1024.lo.lo));
959 // Merge tags
960 OriginTag new_tag = get_origin_tag();
961 for (auto& element : to_add) {
962 new_tag = OriginTag(new_tag, element.get_origin_tag());
963 }
964 remainder.set_origin_tag(new_tag);
965 return remainder;
966 } else {
967
968 const auto [quotient_1024, remainder_1024] = (left * right).divmod(modulus);
969 std::vector<bigfield> new_to_add;
970 for (auto& add_element : to_add) {
971 new_to_add.push_back(add_element);
972 }
973
974 new_to_add.push_back(bigfield(ctx, remainder_1024.lo.lo));
975 return sum(new_to_add);
976 }
977 } else {
978
979 // Check the quotient fits the range proof
980 auto [reduction_required, num_quotient_bits] = get_quotient_reduction_info(
981 { get_maximum_value() }, { get_maximum_value() }, to_add, { DEFAULT_MAXIMUM_REMAINDER });
982
983 if (reduction_required) {
984 self_reduce();
985 return sqradd(to_add);
986 }
987 const auto [quotient_1024, remainder_1024] = (left * right + add_right).divmod(modulus);
988 uint512_t quotient_value = quotient_1024.lo;
989 uint256_t remainder_value = remainder_1024.lo.lo;
990
991 quotient = create_from_u512_as_witness(ctx, quotient_value, false, num_quotient_bits);
992 remainder = create_from_u512_as_witness(ctx, remainder_value);
993 };
994 OriginTag new_tag = get_origin_tag();
995 for (auto& element : to_add) {
996 new_tag = OriginTag(new_tag, element.get_origin_tag());
997 }
998 remainder.set_origin_tag(new_tag);
999 unsafe_evaluate_square_add(*this, to_add, quotient, remainder);
1000 return remainder;
1001}
1002
1003template <typename Builder, typename T> bigfield<Builder, T> bigfield<Builder, T>::pow(const uint32_t exponent) const
1004{
1005 // Just return one immediately
1006 if (exponent == 0) {
1007 return bigfield(uint256_t(1));
1008 }
1009
1010 // If this is a constant, compute result directly
1011 if (is_constant()) {
1012 auto base_val = get_value();
1013 uint512_t result_val = 1;
1014 uint512_t base = base_val % modulus_u512;
1015 uint32_t shifted_exponent = exponent;
1016
1017 // Fast modular exponentiation
1018 while (shifted_exponent > 0) {
1019 if (shifted_exponent & 1) {
1020 result_val = (uint1024_t(result_val) * uint1024_t(base) % uint1024_t(modulus_u512)).lo;
1021 }
1022 base = (uint1024_t(base) * uint1024_t(base) % uint1024_t(modulus_u512)).lo;
1023 shifted_exponent >>= 1;
1024 }
1025 return bigfield(this->context, uint256_t(result_val.lo));
1026 }
1027
1028 bool accumulator_initialized = false;
1029 bigfield accumulator;
1030 bigfield running_power = *this;
1031 uint32_t shifted_exponent = exponent;
1032
1033 // Square and multiply
1034 while (shifted_exponent != 0) {
1035 if (shifted_exponent & 1) {
1036 if (!accumulator_initialized) {
1037 accumulator = running_power;
1038 accumulator_initialized = true;
1039 } else {
1040 accumulator *= running_power;
1041 }
1042 }
1043 shifted_exponent >>= 1;
1044
1045 // Only square if there are more bits to process.
1046 // It is important to avoid squaring in the final iteration as it otherwise results in
1047 // unwanted gates and variables in the circuit.
1048 if (shifted_exponent != 0) {
1049 running_power = running_power.sqr();
1051 }
1052 return accumulator;
1053}
1054
1055template <typename Builder, typename T>
1057{
1058 BB_ASSERT_LTE(to_add.size(), MAXIMUM_SUMMAND_COUNT);
1059 Builder* ctx = context ? context : to_mul.context;
1060 reduction_check();
1061 to_mul.reduction_check();
1062
1064 bool add_constant = true;
1065
1066 for (const auto& add_element : to_add) {
1067 add_element.reduction_check();
1068 add_values += add_element.get_value();
1069 add_constant = add_constant && (add_element.is_constant());
1070 }
1071
1072 const uint1024_t left(get_value());
1073 const uint1024_t mul_right(to_mul.get_value());
1074 const uint1024_t add_right(add_values);
1075 const uint1024_t modulus(target_basis.modulus);
1076
1077 const auto [quotient_1024, remainder_1024] = (left * mul_right + add_right).divmod(modulus);
1078
1079 const uint512_t quotient_value = quotient_1024.lo;
1080 const uint512_t remainder_value = remainder_1024.lo;
1081
1082 bigfield remainder;
1083 bigfield quotient;
1084 if (is_constant() && to_mul.is_constant() && add_constant) {
1085 remainder = bigfield(ctx, uint256_t(remainder_value.lo));
1086 return remainder;
1087 } else if (is_constant() && to_mul.is_constant()) {
1088 const auto [_, mul_remainder_1024] = (left * mul_right).divmod(modulus);
1089 std::vector<bigfield> to_add_copy(to_add);
1090 to_add_copy.push_back(bigfield(ctx, uint256_t(mul_remainder_1024.lo.lo)));
1091 return bigfield::sum(to_add_copy);
1092 } else {
1093 auto [reduction_required, num_quotient_bits] = get_quotient_reduction_info(
1094 { get_maximum_value() }, { to_mul.get_maximum_value() }, to_add, { DEFAULT_MAXIMUM_REMAINDER });
1095 if (reduction_required) {
1096 if (get_maximum_value() > to_mul.get_maximum_value()) {
1097 self_reduce();
1098 } else {
1099 to_mul.self_reduce();
1100 }
1101 return (*this).madd(to_mul, to_add);
1102 }
1103 quotient = create_from_u512_as_witness(ctx, quotient_value, false, num_quotient_bits);
1104 remainder = create_from_u512_as_witness(ctx, remainder_value);
1105 };
1106
1107 // We need to manually propagate the origin tag
1108 OriginTag new_tag = OriginTag(get_origin_tag(), to_mul.get_origin_tag());
1109 for (auto& element : to_add) {
1110 new_tag = OriginTag(new_tag, element.get_origin_tag());
1111 }
1112 remainder.set_origin_tag(new_tag);
1113 quotient.set_origin_tag(new_tag);
1114 unsafe_evaluate_multiply_add(*this, to_mul, to_add, quotient, { remainder });
1115
1116 return remainder;
1117}
1118
1130template <typename Builder, typename T>
1132 std::vector<bigfield>& mul_right,
1133 const std::vector<bigfield>& to_add)
1135 BB_ASSERT_EQ(mul_left.size(), mul_right.size());
1136 BB_ASSERT_LTE(to_add.size(), MAXIMUM_SUMMAND_COUNT);
1137 BB_ASSERT_LTE(mul_left.size(), MAXIMUM_SUMMAND_COUNT);
1138
1139 const size_t number_of_products = mul_left.size();
1140 // Get the maximum values of elements
1141 std::vector<uint512_t> max_values_left;
1142 std::vector<uint512_t> max_values_right;
1143
1144 max_values_left.reserve(number_of_products);
1145 max_values_right.reserve(number_of_products);
1146 // Do regular reduction checks for all elements
1147 for (auto& left_element : mul_left) {
1148 left_element.reduction_check();
1149 max_values_left.emplace_back(left_element.get_maximum_value());
1150 }
1151
1152 for (auto& right_element : mul_right) {
1153 right_element.reduction_check();
1154 max_values_right.emplace_back(right_element.get_maximum_value());
1155 }
1156
1157 // Perform CRT checks for the whole evaluation
1158 // 1. Check if we can overflow CRT modulus
1159 // 2. Check if the quotient actually fits in our range proof.
1160 // 3. If we haven't passed one of the checks, reduce accordingly, starting with the largest product
1161
1162 // We only get the bitlength of range proof if there is no reduction
1163 bool reduction_required = std::get<0>(
1164 get_quotient_reduction_info(max_values_left, max_values_right, to_add, { DEFAULT_MAXIMUM_REMAINDER }));
1165
1166 if (reduction_required) {
1168 // We are out of luck and have to reduce the elements to keep the intermediate result below CRT modulus
1169 // For that we need to compute the maximum update - how much reducing each element is going to update the
1170 // quotient.
1171 // Contents of the tuple: | Qmax_before-Qmax_after | product number | argument number |
1173
1174 // We use this lambda function before the loop and in the loop itself
1175 // It computes the maximum value update from reduction of each element
1176 auto compute_updates = [](std::vector<std::tuple<uint1024_t, size_t, size_t>>& maxval_updates,
1177 std::vector<bigfield>& m_left,
1178 std::vector<bigfield>& m_right,
1179 size_t number_of_products) {
1180 maxval_updates.resize(0);
1181 maxval_updates.reserve(number_of_products * 2);
1182 // Compute all reduction differences
1183 for (size_t i = 0; i < number_of_products; i++) {
1184 uint1024_t original_left = static_cast<uint1024_t>(m_left[i].get_maximum_value());
1185 uint1024_t original_right = static_cast<uint1024_t>(m_right[i].get_maximum_value());
1186 uint1024_t original_product = original_left * original_right;
1187 if (m_left[i].is_constant()) {
1188 // If the multiplicand is constant, we can't reduce it, so the update is 0.
1189 maxval_updates.emplace_back(std::tuple<uint1024_t, size_t, size_t>(0, i, 0));
1190 } else {
1191 uint1024_t new_product = DEFAULT_MAXIMUM_REMAINDER * original_right;
1192 if (new_product > original_product) {
1193 throw_or_abort("bigfield: This should never happen");
1194 }
1195 maxval_updates.emplace_back(
1196 std::tuple<uint1024_t, size_t, size_t>(original_product - new_product, i, 0));
1197 }
1198 if (m_right[i].is_constant()) {
1199 // If the multiplicand is constant, we can't reduce it, so the update is 0.
1200 maxval_updates.emplace_back(std::tuple<uint1024_t, size_t, size_t>(0, i, 1));
1201 } else {
1202 uint1024_t new_product = DEFAULT_MAXIMUM_REMAINDER * original_left;
1203 if (new_product > original_product) {
1204 throw_or_abort("bigfield: This should never happen");
1205 }
1206 maxval_updates.emplace_back(
1207 std::tuple<uint1024_t, size_t, size_t>(original_product - new_product, i, 1));
1208 }
1209 }
1210 };
1211
1212 auto compare_update_tuples = [](std::tuple<uint1024_t, size_t, size_t>& left_element,
1214 return std::get<0>(left_element) > std::get<0>(right_element);
1215 };
1216
1217 // Now we loop through, reducing 1 element each time. This is costly in code, but allows us to use fewer
1218 // gates
1219
1220 while (reduction_required) {
1221 // Compute the possible reduction updates
1222 compute_updates(maximum_value_updates, mul_left, mul_right, number_of_products);
1223
1224 // Sort the vector, larger values first
1225 std::sort(maximum_value_updates.begin(), maximum_value_updates.end(), compare_update_tuples);
1226
1227 // We choose the largest update
1228 auto [update_size, largest_update_product_index, multiplicand_index] = maximum_value_updates[0];
1229 if (!update_size) {
1230 throw_or_abort("bigfield: Can't reduce further");
1231 }
1232 // Reduce the larger of the multiplicands that compose the product
1233 if (multiplicand_index == 0) {
1234 mul_left[largest_update_product_index].self_reduce();
1235 } else {
1236 mul_right[largest_update_product_index].self_reduce();
1237 }
1238
1239 for (size_t i = 0; i < number_of_products; i++) {
1240 max_values_left[i] = mul_left[i].get_maximum_value();
1241 max_values_right[i] = mul_right[i].get_maximum_value();
1242 }
1243 reduction_required = std::get<0>(
1244 get_quotient_reduction_info(max_values_left, max_values_right, to_add, { DEFAULT_MAXIMUM_REMAINDER }));
1245 }
1246
1247 // Now we have reduced everything exactly to the point of no overflow. There is probably a way to use even
1248 // fewer reductions, but for now this will suffice.
1249 }
1250}
1251
1261template <typename Builder, typename T>
1263 const std::vector<bigfield>& mul_right,
1264 const std::vector<bigfield>& to_add,
1265 bool fix_remainder_to_zero)
1266{
1267 BB_ASSERT_EQ(mul_left.size(), mul_right.size());
1268 BB_ASSERT_LTE(mul_left.size(), MAXIMUM_SUMMAND_COUNT);
1269 BB_ASSERT_LTE(to_add.size(), MAXIMUM_SUMMAND_COUNT);
1270
1271 std::vector<bigfield> mutable_mul_left(mul_left);
1272 std::vector<bigfield> mutable_mul_right(mul_right);
1273
1274 const size_t number_of_products = mul_left.size();
1275
1276 const uint1024_t modulus(target_basis.modulus);
1277 uint1024_t worst_case_product_sum(0);
1278 uint1024_t add_right_constant_sum(0);
1279
1280 // First we do all constant optimizations
1281 bool add_constant = true;
1282 std::vector<bigfield> new_to_add;
1283
1284 OriginTag new_tag = OriginTag::constant(); // Initialize as CONSTANT so merging with input tags works correctly
1285 // Merge all tags. Do it in pairs (logically a submitted value can be masked by a challenge)
1286 for (auto [left_element, right_element] : zip_view(mul_left, mul_right)) {
1287 new_tag = OriginTag(new_tag, OriginTag(left_element.get_origin_tag(), right_element.get_origin_tag()));
1288 }
1289 for (auto& element : to_add) {
1290 new_tag = OriginTag(new_tag, element.get_origin_tag());
1291 }
1292
1293 for (const auto& add_element : to_add) {
1294 add_element.reduction_check();
1295 if (add_element.is_constant()) {
1296 add_right_constant_sum += uint1024_t(add_element.get_value());
1297 } else {
1298 add_constant = false;
1299 new_to_add.push_back(add_element);
1300 }
1301 }
1302
1303 // Compute the product sum
1304 // Optimize constant use
1305 uint1024_t sum_of_constant_products(0);
1306 std::vector<bigfield> new_input_left;
1307 std::vector<bigfield> new_input_right;
1308 bool product_sum_constant = true;
1309 for (size_t i = 0; i < number_of_products; i++) {
1310 if (mutable_mul_left[i].is_constant() && mutable_mul_right[i].is_constant()) {
1311 // If constant, just add to the sum
1312 sum_of_constant_products +=
1313 uint1024_t(mutable_mul_left[i].get_value()) * uint1024_t(mutable_mul_right[i].get_value());
1314 } else {
1315 // If not, add to nonconstant sum and remember the elements
1316 new_input_left.push_back(mutable_mul_left[i]);
1317 new_input_right.push_back(mutable_mul_right[i]);
1318 product_sum_constant = false;
1319 }
1320 }
1321
1322 Builder* ctx = nullptr;
1323 // Search through all multiplicands on the left
1324 for (auto& el : mutable_mul_left) {
1325 if (el.context) {
1326 ctx = el.context;
1327 break;
1328 }
1329 }
1330 // And on the right
1331 if (!ctx) {
1332 for (auto& el : mutable_mul_right) {
1333 if (el.context) {
1334 ctx = el.context;
1335 break;
1336 }
1337 }
1338 }
1339 if (product_sum_constant) {
1340 if (add_constant) {
1341 // Simply return the constant, no need unsafe_multiply_add
1342 const auto [quotient_1024, remainder_1024] =
1343 (sum_of_constant_products + add_right_constant_sum).divmod(modulus);
1344 BB_ASSERT(!fix_remainder_to_zero || remainder_1024 == 0);
1345 auto result = bigfield(ctx, uint256_t(remainder_1024.lo.lo));
1346 result.set_origin_tag(new_tag);
1347 return result;
1348 } else {
1349 const auto [quotient_1024, remainder_1024] =
1350 (sum_of_constant_products + add_right_constant_sum).divmod(modulus);
1351 uint256_t remainder_value = remainder_1024.lo.lo;
1353 if (remainder_value == uint256_t(0)) {
1354 // No need to add extra term to new_to_add
1355 result = sum(new_to_add);
1356 } else {
1357 // Add the constant term
1358 new_to_add.push_back(bigfield(ctx, uint256_t(remainder_value)));
1359 result = sum(new_to_add);
1360 }
1361 if (fix_remainder_to_zero) {
1362 result.self_reduce();
1363 result.assert_equal(zero());
1364 }
1365 result.set_origin_tag(new_tag);
1366 return result;
1367 }
1368 }
1369
1370 // Now that we know that there is at least 1 non-constant multiplication, we can start estimating reductions.
1371 BB_ASSERT(ctx != nullptr);
1372
1373 // Compute the constant term we're adding
1374 const auto [_, constant_part_remainder_1024] = (sum_of_constant_products + add_right_constant_sum).divmod(modulus);
1375 const uint256_t constant_part_remainder_256 = constant_part_remainder_1024.lo.lo;
1376
1377 if (constant_part_remainder_256 != uint256_t(0)) {
1378 new_to_add.push_back(bigfield(ctx, constant_part_remainder_256));
1379 }
1380 // Compute added sum
1381 uint1024_t add_right_final_sum(0);
1382 uint1024_t add_right_maximum(0);
1383 for (const auto& add_element : new_to_add) {
1384 // Technically not needed, but better to leave just in case
1385 add_element.reduction_check();
1386 add_right_final_sum += uint1024_t(add_element.get_value());
1387
1388 add_right_maximum += uint1024_t(add_element.get_maximum_value());
1389 }
1390 const size_t final_number_of_products = new_input_left.size();
1391
1392 // We need to check if it is possible to reduce the products enough
1393 worst_case_product_sum = uint1024_t(final_number_of_products) * uint1024_t(DEFAULT_MAXIMUM_REMAINDER) *
1394 uint1024_t(DEFAULT_MAXIMUM_REMAINDER);
1395
1396 // Check that we can actually reduce the products enough, this assert will probably never get triggered
1397 BB_ASSERT_LT(worst_case_product_sum + add_right_maximum, get_maximum_crt_product());
1398
1399 // We've collapsed all constants, checked if we can compute the sum of products in the worst case, time to check
1400 // if we need to reduce something
1401 perform_reductions_for_mult_madd(new_input_left, new_input_right, new_to_add);
1402 uint1024_t sum_of_products_final(0);
1403 for (size_t i = 0; i < final_number_of_products; i++) {
1404 sum_of_products_final += uint1024_t(new_input_left[i].get_value()) * uint1024_t(new_input_right[i].get_value());
1405 }
1406
1407 // Get the number of range proof bits for the quotient
1408 const size_t num_quotient_bits = get_quotient_max_bits({ DEFAULT_MAXIMUM_REMAINDER });
1409
1410 // Compute the quotient and remainder
1411 const auto [quotient_1024, remainder_1024] = (sum_of_products_final + add_right_final_sum).divmod(modulus);
1412
1413 // If we are establishing an identity and the remainder has to be zero, we need to check, that it actually is
1414
1415 if (fix_remainder_to_zero) {
1416 // This is not the only check. Circuit check is coming later :)
1417 BB_ASSERT_EQ(remainder_1024.lo, uint512_t(0));
1418 }
1419 const uint512_t quotient_value = quotient_1024.lo;
1420 const uint512_t remainder_value = remainder_1024.lo;
1421
1422 bigfield remainder;
1423 bigfield quotient;
1424 // Constrain quotient to mitigate CRT overflow attacks
1425 quotient = create_from_u512_as_witness(ctx, quotient_value, false, num_quotient_bits);
1426
1427 if (fix_remainder_to_zero) {
1428 remainder = zero();
1429 // remainder needs to be defined as wire value and not selector values to satisfy
1430 // Ultra's bigfield custom gates
1431 remainder.convert_constant_to_fixed_witness(ctx);
1432 } else {
1433 remainder = create_from_u512_as_witness(ctx, remainder_value);
1434 }
1435
1436 // We need to manually propagate the origin tag
1437 quotient.set_origin_tag(new_tag);
1438 remainder.set_origin_tag(new_tag);
1439
1440 unsafe_evaluate_multiple_multiply_add(new_input_left, new_input_right, new_to_add, quotient, { remainder });
1441
1442 return remainder;
1443}
1444
1450template <typename Builder, typename T>
1452 const bigfield& right_a,
1453 const bigfield& left_b,
1454 const bigfield& right_b,
1455 const std::vector<bigfield>& to_add)
1456{
1457 BB_ASSERT_LTE(to_add.size(), MAXIMUM_SUMMAND_COUNT);
1458 left_a.reduction_check();
1459 right_a.reduction_check();
1460 left_b.reduction_check();
1461 right_b.reduction_check();
1462
1463 std::vector<bigfield> mul_left = { left_a, left_b };
1464 std::vector<bigfield> mul_right = { right_a, right_b };
1465
1466 return mult_madd(mul_left, mul_right, to_add);
1467}
1468
1487template <typename Builder, typename T>
1489 const std::vector<bigfield>& mul_right,
1490 const bigfield& divisor,
1491 const std::vector<bigfield>& to_sub,
1492 bool enable_divisor_nz_check)
1493{
1494 // Check the basics
1495 BB_ASSERT_EQ(mul_left.size(), mul_right.size());
1496 BB_ASSERT((divisor.get_value() % modulus_u512) != 0, "bigfield: Division by zero in msub_div");
1497
1498 OriginTag new_tag = divisor.get_origin_tag();
1499 for (auto [left_element, right_element] : zip_view(mul_left, mul_right)) {
1500 new_tag = OriginTag(new_tag, OriginTag(left_element.get_origin_tag(), right_element.get_origin_tag()));
1501 }
1502 for (auto& element : to_sub) {
1503 new_tag = OriginTag(new_tag, element.get_origin_tag());
1504 }
1505 // Get the context
1506 Builder* ctx = divisor.context;
1507 if (ctx == NULL) {
1508 for (auto& el : mul_left) {
1509 if (el.context != NULL) {
1510 ctx = el.context;
1511 break;
1512 }
1513 }
1514 }
1515 if (ctx == NULL) {
1516 for (auto& el : mul_right) {
1517 if (el.context != NULL) {
1518 ctx = el.context;
1519 break;
1520 }
1521 }
1522 }
1523 if (ctx == NULL) {
1524 for (auto& el : to_sub) {
1525 if (el.context != NULL) {
1526 ctx = el.context;
1527 break;
1528 }
1529 }
1530 }
1531 const size_t num_multiplications = mul_left.size();
1532 native product_native = 0;
1533 bool products_constant = true;
1534
1535 // This check is optional, because it is heavy and often we don't need it at all
1536 if (enable_divisor_nz_check) {
1537 divisor.assert_is_not_equal(zero());
1538 }
1539
1540 // Compute the sum of products
1541 for (size_t i = 0; i < num_multiplications; ++i) {
1542 const native mul_left_native(uint512_t(mul_left[i].get_value() % modulus_u512).lo);
1543 const native mul_right_native(uint512_t(mul_right[i].get_value() % modulus_u512).lo);
1544 product_native += (mul_left_native * -mul_right_native);
1545 products_constant = products_constant && mul_left[i].is_constant() && mul_right[i].is_constant();
1546 }
1547
1548 // Compute the sum of to_sub
1549 native sub_native(0);
1550 bool sub_constant = true;
1551 for (const auto& sub : to_sub) {
1552 sub_native += (uint512_t(sub.get_value() % modulus_u512).lo);
1553 sub_constant = sub_constant && sub.is_constant();
1554 }
1555
1556 native divisor_native(uint512_t(divisor.get_value() % modulus_u512).lo);
1557
1558 // Compute the result
1559 const native result_native = (product_native - sub_native) / divisor_native;
1560
1561 const uint1024_t result_value = uint1024_t(uint512_t(static_cast<uint256_t>(result_native)));
1562
1563 // If everything is constant, then we just return the constant
1564 if (sub_constant && products_constant && divisor.is_constant()) {
1565 auto result = bigfield(ctx, uint256_t(result_value.lo.lo));
1566 result.set_origin_tag(new_tag);
1567 return result;
1568 }
1569
1570 BB_ASSERT(ctx != NULL);
1571 // Create the result witness
1572 bigfield result = create_from_u512_as_witness(ctx, result_value.lo);
1573
1574 // We need to manually propagate the origin tag
1575 result.set_origin_tag(new_tag);
1576
1577 std::vector<bigfield> eval_left{ result };
1578 std::vector<bigfield> eval_right{ divisor };
1579 for (const auto& in : mul_left) {
1580 eval_left.emplace_back(in);
1581 }
1582 for (const auto& in : mul_right) {
1583 eval_right.emplace_back(in);
1584 }
1585
1586 mult_madd(eval_left, eval_right, to_sub, true);
1587
1588 return result;
1589}
1590
1591template <typename Builder, typename T>
1593{
1594 Builder* ctx = context ? context : predicate.context;
1595
1596 if (is_constant() && predicate.is_constant()) {
1597 auto result = *this;
1598 if (predicate.get_value()) {
1599 BB_ASSERT_LT(get_value(), modulus_u512);
1600 uint512_t out_val = (modulus_u512 - get_value()) % modulus_u512;
1601 result = bigfield(ctx, out_val.lo);
1602 }
1603 result.set_origin_tag(OriginTag(get_origin_tag(), predicate.get_origin_tag()));
1604 return result;
1605 }
1606 reduction_check();
1607
1608 // We want to check:
1609 // predicate = 1 ==> (0 - *this)
1610 // predicate = 0 ==> *this
1611 //
1612 // We just use the conditional_assign method to do this as it costs the same number of gates as computing
1613 // p * (0 - *this) + (1 - p) * (*this)
1614 //
1615 bigfield<Builder, T> negative_this = zero() - *this;
1616 bigfield<Builder, T> result = bigfield<Builder, T>::conditional_assign(predicate, negative_this, *this);
1617
1618 return result;
1619}
1620
1621template <typename Builder, typename T>
1623 const bool_t<Builder>& predicate) const
1624{
1625 // If the predicate is constant, the conditional selection can be done out of circuit
1626 if (predicate.is_constant()) {
1627 bigfield result = predicate.get_value() ? other : *this;
1628 result.set_origin_tag(OriginTag(get_origin_tag(), other.get_origin_tag(), predicate.get_origin_tag()));
1629 return result;
1630 }
1631
1632 // If both elements are the same, we can just return one of them
1633 auto is_limb_same = [](const field_ct& a, const field_ct& b) {
1634 const bool is_witness_index_same = field_ct::witness_indices_match(a, b);
1635 const bool is_add_constant_same = a.additive_constant == b.additive_constant;
1636 const bool is_mul_constant_same = a.multiplicative_constant == b.multiplicative_constant;
1637 return is_witness_index_same && is_add_constant_same && is_mul_constant_same;
1638 };
1639
1640 bool is_limb_0_same = is_limb_same(binary_basis_limbs[0].element, other.binary_basis_limbs[0].element);
1641 bool is_limb_1_same = is_limb_same(binary_basis_limbs[1].element, other.binary_basis_limbs[1].element);
1642 bool is_limb_2_same = is_limb_same(binary_basis_limbs[2].element, other.binary_basis_limbs[2].element);
1643 bool is_limb_3_same = is_limb_same(binary_basis_limbs[3].element, other.binary_basis_limbs[3].element);
1644 bool is_prime_limb_same = is_limb_same(prime_basis_limb, other.prime_basis_limb);
1645 if (is_limb_0_same && is_limb_1_same && is_limb_2_same && is_limb_3_same && is_prime_limb_same) {
1646 return *this;
1647 }
1648
1649 Builder* ctx = context ? context : (other.context ? other.context : predicate.context);
1650
1651 // For each limb, we must select:
1652 // `this` if predicate == 0
1653 // `other` if predicate == 1
1654 //
1655 // Thus, we compute the resulting limb as follows:
1656 // result.limb := predicate * (other.limb - this.limb) + this.limb.
1657 //
1658 // Note that each call to `madd` will add a gate as predicate is a witness at this point.
1659 // There can be edge cases where `this` and `other` are both constants and only differ in one limb.
1660 // In such a case, the `madd` for the differing limb will be a no-op (i.e., redundant gate), as the
1661 // difference will be zero. For example,
1662 // binary limbs prime limb
1663 // this: (0x5, 0x1, 0x0, 0x0) (0x100000000000000005)
1664 // other: (0x7, 0x1, 0x0, 0x0) (0x100000000000000007)
1665 // Here, the `madd` for the second, third and fourth binary limbs will be a no-op, as the difference
1666 // between `this` and `other` is zero for those limbs.
1667 //
1668 // We allow this to happen because we want to maintain limb consistency (i.e., all limbs either witness or
1669 // constant).
1670 field_ct binary_limb_0 = field_ct(predicate).madd(
1671 other.binary_basis_limbs[0].element - binary_basis_limbs[0].element, binary_basis_limbs[0].element);
1672 field_ct binary_limb_1 = field_ct(predicate).madd(
1673 other.binary_basis_limbs[1].element - binary_basis_limbs[1].element, binary_basis_limbs[1].element);
1674 field_ct binary_limb_2 = field_ct(predicate).madd(
1675 other.binary_basis_limbs[2].element - binary_basis_limbs[2].element, binary_basis_limbs[2].element);
1676 field_ct binary_limb_3 = field_ct(predicate).madd(
1677 other.binary_basis_limbs[3].element - binary_basis_limbs[3].element, binary_basis_limbs[3].element);
1678 field_ct prime_limb = field_ct(predicate).madd(other.prime_basis_limb - prime_basis_limb, prime_basis_limb);
1679
1680 bigfield result(ctx);
1681 // the maximum of the maximal values of elements is large enough
1682 result.binary_basis_limbs[0] =
1683 Limb(binary_limb_0, std::max(binary_basis_limbs[0].maximum_value, other.binary_basis_limbs[0].maximum_value));
1684 result.binary_basis_limbs[1] =
1685 Limb(binary_limb_1, std::max(binary_basis_limbs[1].maximum_value, other.binary_basis_limbs[1].maximum_value));
1686 result.binary_basis_limbs[2] =
1687 Limb(binary_limb_2, std::max(binary_basis_limbs[2].maximum_value, other.binary_basis_limbs[2].maximum_value));
1688 result.binary_basis_limbs[3] =
1689 Limb(binary_limb_3, std::max(binary_basis_limbs[3].maximum_value, other.binary_basis_limbs[3].maximum_value));
1690 result.prime_basis_limb = prime_limb;
1691 result.set_origin_tag(OriginTag(get_origin_tag(), other.get_origin_tag(), predicate.tag));
1692 return result;
1693}
1694
1715template <typename Builder, typename T> bool_t<Builder> bigfield<Builder, T>::operator==(const bigfield& other) const
1716{
1717 Builder* ctx = context ? context : other.get_context();
1718 auto lhs = get_value() % modulus_u512;
1719 auto rhs = other.get_value() % modulus_u512;
1720 bool is_equal_raw = (lhs == rhs);
1721 if (is_constant() && other.is_constant()) {
1722 return is_equal_raw;
1723 }
1724
1725 // The context should not be null at this point.
1726 BB_ASSERT(ctx != NULL);
1727 bool_t<Builder> is_equal = witness_t<Builder>(ctx, is_equal_raw);
1728
1729 // We need to manually propagate the origin tag
1730 is_equal.set_origin_tag(OriginTag(get_origin_tag(), other.get_origin_tag()));
1731
1732 bigfield diff = (*this) - other;
1733 native diff_native = native((diff.get_value() % modulus_u512).lo);
1734 native inverse_native = is_equal_raw ? 0 : diff_native.invert();
1735
1736 bigfield inverse = bigfield::from_witness(ctx, inverse_native);
1737
1738 // We need to manually propagate the origin tag
1739 inverse.set_origin_tag(OriginTag(get_origin_tag(), other.get_origin_tag()));
1740
1741 bigfield multiplicand = bigfield::conditional_assign(is_equal, one(), inverse);
1742
1743 bigfield product = diff * multiplicand;
1744
1746
1748 product.binary_basis_limbs[0].element.assert_equal(result);
1749 product.binary_basis_limbs[1].element.assert_equal(0);
1750 product.binary_basis_limbs[2].element.assert_equal(0);
1751 product.binary_basis_limbs[3].element.assert_equal(0);
1752 is_equal.set_origin_tag(OriginTag(get_origin_tag(), other.get_origin_tag()));
1753 return is_equal;
1754}
1755
1756template <typename Builder, typename T> void bigfield<Builder, T>::reduction_check() const
1757{
1758 if (is_constant()) {
1759 uint256_t reduced_value = (get_value() % modulus_u512).lo;
1760 bigfield reduced(context, uint256_t(reduced_value));
1761 // Save tags
1762 const auto origin_tags = std::vector({ binary_basis_limbs[0].element.get_origin_tag(),
1763 binary_basis_limbs[1].element.get_origin_tag(),
1764 binary_basis_limbs[2].element.get_origin_tag(),
1765 binary_basis_limbs[3].element.get_origin_tag(),
1766 prime_basis_limb.get_origin_tag() });
1767
1768 // Directly assign to mutable members (avoiding assignment operator)
1769 binary_basis_limbs[0] = reduced.binary_basis_limbs[0];
1770 binary_basis_limbs[1] = reduced.binary_basis_limbs[1];
1771 binary_basis_limbs[2] = reduced.binary_basis_limbs[2];
1772 binary_basis_limbs[3] = reduced.binary_basis_limbs[3];
1773 prime_basis_limb = reduced.prime_basis_limb;
1774
1775 // Preserve origin tags (useful in simulator)
1776 binary_basis_limbs[0].element.set_origin_tag(origin_tags[0]);
1777 binary_basis_limbs[1].element.set_origin_tag(origin_tags[1]);
1778 binary_basis_limbs[2].element.set_origin_tag(origin_tags[2]);
1779 binary_basis_limbs[3].element.set_origin_tag(origin_tags[3]);
1780 prime_basis_limb.set_origin_tag(origin_tags[4]);
1781 return;
1782 }
1783
1784 uint256_t maximum_unreduced_limb_value = get_maximum_unreduced_limb_value();
1785 bool limb_overflow_test_0 = binary_basis_limbs[0].maximum_value > maximum_unreduced_limb_value;
1786 bool limb_overflow_test_1 = binary_basis_limbs[1].maximum_value > maximum_unreduced_limb_value;
1787 bool limb_overflow_test_2 = binary_basis_limbs[2].maximum_value > maximum_unreduced_limb_value;
1788 bool limb_overflow_test_3 = binary_basis_limbs[3].maximum_value > maximum_unreduced_limb_value;
1789 if (get_maximum_value() > get_maximum_unreduced_value() || limb_overflow_test_0 || limb_overflow_test_1 ||
1790 limb_overflow_test_2 || limb_overflow_test_3) {
1791 self_reduce();
1792 }
1793}
1794
1795template <typename Builder, typename T> void bigfield<Builder, T>::sanity_check() const
1796{
1797
1798 uint256_t prohibited_limb_value = get_prohibited_limb_value();
1799 bool limb_overflow_test_0 = binary_basis_limbs[0].maximum_value > prohibited_limb_value;
1800 bool limb_overflow_test_1 = binary_basis_limbs[1].maximum_value > prohibited_limb_value;
1801 bool limb_overflow_test_2 = binary_basis_limbs[2].maximum_value > prohibited_limb_value;
1802 bool limb_overflow_test_3 = binary_basis_limbs[3].maximum_value > prohibited_limb_value;
1803 // max_val < sqrt(2^T * n)
1804 // Note this is a static assertion, so it is not checked at runtime
1805 BB_ASSERT(!(get_maximum_value() > get_prohibited_value() || limb_overflow_test_0 || limb_overflow_test_1 ||
1806 limb_overflow_test_2 || limb_overflow_test_3));
1807}
1808
1809template <typename Builder, typename T>
1810void bigfield<Builder, T>::assert_zero_if(const bool_t<Builder>& predicate, std::string const& msg) const
1811{
1812 // Assert that all limbs are zero when predicate is true
1813 const field_ct predicate_field = field_ct(predicate);
1814 (binary_basis_limbs[0].element * predicate_field).assert_is_zero(msg + ": binary limb 0 not zero");
1815 (binary_basis_limbs[1].element * predicate_field).assert_is_zero(msg + ": binary limb 1 not zero");
1816 (binary_basis_limbs[2].element * predicate_field).assert_is_zero(msg + ": binary limb 2 not zero");
1817 (binary_basis_limbs[3].element * predicate_field).assert_is_zero(msg + ": binary limb 3 not zero");
1818 (prime_basis_limb * predicate_field).assert_is_zero(msg + ": prime limb not zero");
1819}
1820
1821// Underneath performs unsafe_assert_less_than(modulus)
1822// create a version with mod 2^t element part in [0,p-1]
1823// After range-constraining to size 2^s, we check (p-1)-a is non-negative as integer.
1824// We perform subtraction using carries on blocks of size 2^b. The operations inside the blocks are done mod r
1825// Including the effect of carries the operation inside each limb is in the range [-2^b-1,2^{b+1}]
1826// Assuming this values are all distinct mod r, which happens e.g. if r/2>2^{b+1}, then if all limb values are
1827// non-negative at the end of subtraction, we know the subtraction result is positive as integers and a<p
1828template <typename Builder, typename T> void bigfield<Builder, T>::assert_is_in_field(std::string const& msg) const
1829{
1830 assert_less_than(modulus, msg == "bigfield::assert_is_in_field" ? "bigfield::assert_less_than" : msg);
1831}
1832
1833// Asserts that the element is < upper_limit. We first range constrain the limbs and then calls
1834// unsafe_assert_less_than(upper_limit).
1835template <typename Builder, typename T>
1836void bigfield<Builder, T>::assert_less_than(const uint256_t& upper_limit, std::string const& msg) const
1837{
1838 // For constant bigfields, just verify the value is in range (no circuit constraints needed)
1839 if (is_constant()) {
1840 BB_ASSERT((get_value() % modulus_u512).lo < upper_limit, msg);
1841 return;
1842 }
1843
1844 bool is_default_msg = msg == "bigfield::assert_less_than";
1845
1846 // Range constrain the binary basis limbs of the element to respective limb sizes.
1847 // This is required because the comparison is done using subtractions, which can result in overflows.
1848 // Range constrain the first two limbs each to NUM_LIMB_BITS
1849 auto ctx = get_context();
1850 ctx->range_constrain_two_limbs(binary_basis_limbs[0].element.get_witness_index(),
1851 binary_basis_limbs[1].element.get_witness_index(),
1852 static_cast<size_t>(NUM_LIMB_BITS),
1853 static_cast<size_t>(NUM_LIMB_BITS),
1854 is_default_msg ? "bigfield::assert_less_than: limb 0 or 1 too large" : msg);
1855
1856 // Range constrain the last two limbs to NUM_LIMB_BITS and NUM_LAST_LIMB_BITS
1857 ctx->range_constrain_two_limbs(binary_basis_limbs[2].element.get_witness_index(),
1858 binary_basis_limbs[3].element.get_witness_index(),
1859 static_cast<size_t>(NUM_LIMB_BITS),
1860 static_cast<size_t>(NUM_LAST_LIMB_BITS),
1861 is_default_msg ? "bigfield::assert_less_than: limb 2 or 3 too large" : msg);
1862
1863 // Now we can check that the element is < upper_limit.
1864 unsafe_assert_less_than(upper_limit, is_default_msg ? "bigfield::unsafe_assert_less_than" : msg);
1865}
1866
1867// Return (a < b) as bool circuit type.
1868template <typename Builder, typename T>
1869bool_t<Builder> bigfield<Builder, T>::is_less_than(const uint256_t& upper_limit, std::string const& msg) const
1870{
1871 bool is_default_msg = msg == "bigfield::is_less_than";
1872
1873 Builder* ctx = get_context();
1874
1875 // Range constraint the limbs, this is required by the ranged_less_than function
1876 ctx->range_constrain_two_limbs(binary_basis_limbs[0].element.get_witness_index(),
1877 binary_basis_limbs[1].element.get_witness_index(),
1878 static_cast<size_t>(NUM_LIMB_BITS),
1879 static_cast<size_t>(NUM_LIMB_BITS),
1880 is_default_msg ? "bigfield::is_less_than: limb 0 or 1 too large" : msg);
1881
1882 ctx->range_constrain_two_limbs(binary_basis_limbs[2].element.get_witness_index(),
1883 binary_basis_limbs[3].element.get_witness_index(),
1884 static_cast<size_t>(NUM_LIMB_BITS),
1885 static_cast<size_t>(NUM_LAST_LIMB_BITS),
1886 is_default_msg ? "bigfield::is_less_than: limb 2 or 3 too large" : msg);
1887
1888 const uint256_t upper_limit_value_0 = upper_limit.slice(0, NUM_LIMB_BITS);
1889 const uint256_t upper_limit_value_1 = upper_limit.slice(NUM_LIMB_BITS, NUM_LIMB_BITS * 2);
1890 const uint256_t upper_limit_value_2 = upper_limit.slice(NUM_LIMB_BITS * 2, NUM_LIMB_BITS * 3);
1891 const uint256_t upper_limit_value_3 =
1892 upper_limit.slice(NUM_LIMB_BITS * 3, (NUM_LIMB_BITS * 3) + NUM_LAST_LIMB_BITS);
1893
1894 bool_t<Builder> third_limb_is_smaller = binary_basis_limbs[3].element.template ranged_less_than<NUM_LAST_LIMB_BITS>(
1895 field_t<Builder>(upper_limit_value_3));
1896 bool_t<Builder> third_limb_is_equal = binary_basis_limbs[3].element == field_t<Builder>(upper_limit_value_3);
1897
1898 bool_t<Builder> second_limb_is_smaller =
1899 binary_basis_limbs[2].element.template ranged_less_than<NUM_LIMB_BITS>(field_t<Builder>(upper_limit_value_2));
1900 bool_t<Builder> second_limb_is_equal = binary_basis_limbs[2].element == field_t<Builder>(upper_limit_value_2);
1901
1902 bool_t<Builder> first_limb_is_smaller =
1903 binary_basis_limbs[1].element.template ranged_less_than<NUM_LIMB_BITS>(field_t<Builder>(upper_limit_value_1));
1904 bool_t<Builder> first_limb_is_equal = binary_basis_limbs[1].element == field_t<Builder>(upper_limit_value_1);
1905
1906 bool_t<Builder> zeroth_limb_is_smaller =
1907 binary_basis_limbs[0].element.template ranged_less_than<NUM_LIMB_BITS>(field_t<Builder>(upper_limit_value_0));
1908
1909 // Limb comparison: we start from the most-significant limb and proceed to the least-significant limb
1911 third_limb_is_smaller || (third_limb_is_equal && second_limb_is_smaller) ||
1912 (third_limb_is_equal && second_limb_is_equal && first_limb_is_smaller) ||
1913 (third_limb_is_equal && second_limb_is_equal && first_limb_is_equal && zeroth_limb_is_smaller);
1914
1915 return result;
1916}
1917
1918// Reduces the element mod p. This is a strict reduction mod p, so the output is guaranteed to be < p.
1919template <typename Builder, typename T> void bigfield<Builder, T>::reduce_mod_target_modulus() const
1920{
1921 // First we lazy-reduce the element mod p, and constrain the output/remainder to be < 2^s where s = ceil(log2(p)).
1922 // This brings the element into the range [0, 2^s) such that the limbs of the reduced element are all range
1923 // constrained to < 2^b (last limb < 2^(s - 3b)).
1924 self_reduce();
1925
1926 // Then we constrain the element to be < target modulus using strict comparison.
1927 unsafe_assert_less_than(modulus);
1928}
1929
1930// Asserts that the element is < upper_limit. We mark this as unsafe because it assumes that the element is already
1931// range constrained to < 2^s where s = ceil(log2(p)).
1932template <typename Builder, typename T>
1933void bigfield<Builder, T>::unsafe_assert_less_than(const uint256_t& upper_limit, std::string const& msg) const
1934{
1935 // Warning: this assumes we have run circuit construction at least once in debug mode where large non reduced
1936 // constants are NOT allowed via ASSERT
1937 if (is_constant()) {
1938 BB_ASSERT_LT(get_value(), static_cast<uint512_t>(upper_limit));
1939 return;
1940 }
1941
1942 BB_ASSERT(upper_limit != 0);
1943 // The circuit checks that limit - this >= 0, so if we are doing a less_than comparison, we need to subtract 1
1944 // from the limit
1945 uint256_t strict_upper_limit = upper_limit - uint256_t(1);
1946 uint256_t value = get_value().lo;
1947
1948 const uint256_t upper_limit_value_0 = strict_upper_limit.slice(0, NUM_LIMB_BITS);
1949 const uint256_t upper_limit_value_1 = strict_upper_limit.slice(NUM_LIMB_BITS, NUM_LIMB_BITS * 2);
1950 const uint256_t upper_limit_value_2 = strict_upper_limit.slice(NUM_LIMB_BITS * 2, NUM_LIMB_BITS * 3);
1951 const uint256_t upper_limit_value_3 = strict_upper_limit.slice(NUM_LIMB_BITS * 3, NUM_LIMB_BITS * 4);
1952
1953 const uint256_t val_0 = value.slice(0, NUM_LIMB_BITS);
1954 const uint256_t val_1 = value.slice(NUM_LIMB_BITS, NUM_LIMB_BITS * 2);
1955 const uint256_t val_2 = value.slice(NUM_LIMB_BITS * 2, NUM_LIMB_BITS * 3);
1956
1957 bool borrow_0_value = val_0 > upper_limit_value_0;
1958 bool borrow_1_value = (val_1 + uint256_t(borrow_0_value)) > (upper_limit_value_1);
1959 bool borrow_2_value = (val_2 + uint256_t(borrow_1_value)) > (upper_limit_value_2);
1960
1961 field_t<Builder> upper_limit_0(context, upper_limit_value_0);
1962 field_t<Builder> upper_limit_1(context, upper_limit_value_1);
1963 field_t<Builder> upper_limit_2(context, upper_limit_value_2);
1964 field_t<Builder> upper_limit_3(context, upper_limit_value_3);
1965 bool_t<Builder> borrow_0(witness_t<Builder>(context, borrow_0_value));
1966 bool_t<Builder> borrow_1(witness_t<Builder>(context, borrow_1_value));
1967 bool_t<Builder> borrow_2(witness_t<Builder>(context, borrow_2_value));
1968 // Unset free witness tag because these are auxiliary witnesses
1969 borrow_0.unset_free_witness_tag();
1970 borrow_1.unset_free_witness_tag();
1971 borrow_2.unset_free_witness_tag();
1972
1973 // The way we use borrows here ensures that we are checking that upper_limit - binary_basis > 0.
1974 // We check that the result in each limb is > 0.
1975 // If the modulus part in this limb is smaller, we simply borrow the value from the higher limb.
1976 // The prover can rearrange the borrows the way they like. The important thing is that the borrows are
1977 // constrained.
1978 field_t<Builder> r0 =
1979 upper_limit_0 - binary_basis_limbs[0].element + (static_cast<field_t<Builder>>(borrow_0) * shift_1);
1980 field_t<Builder> r1 = upper_limit_1 - binary_basis_limbs[1].element +
1981 (static_cast<field_t<Builder>>(borrow_1) * shift_1) - static_cast<field_t<Builder>>(borrow_0);
1982 field_t<Builder> r2 = upper_limit_2 - binary_basis_limbs[2].element +
1983 (static_cast<field_t<Builder>>(borrow_2) * shift_1) - static_cast<field_t<Builder>>(borrow_1);
1984 field_t<Builder> r3 = upper_limit_3 - binary_basis_limbs[3].element - static_cast<field_t<Builder>>(borrow_2);
1985
1986 // We need to range constrain the r0,r1,r2,r3 values to ensure they are "small enough".
1987 get_context()->range_constrain_two_limbs(
1988 r0.get_witness_index(),
1989 r1.get_witness_index(),
1990 static_cast<size_t>(NUM_LIMB_BITS),
1991 static_cast<size_t>(NUM_LIMB_BITS),
1992 msg == "bigfield::unsafe_assert_less_than" ? "bigfield::unsafe_assert_less_than: r0 or r1 too large" : msg);
1993 get_context()->range_constrain_two_limbs(
1994 r2.get_witness_index(),
1995 r3.get_witness_index(),
1996 static_cast<size_t>(NUM_LIMB_BITS),
1997 static_cast<size_t>(NUM_LAST_LIMB_BITS),
1998 msg == "bigfield::unsafe_assert_less_than" ? "bigfield::unsafe_assert_less_than: r2 or r3 too large" : msg);
1999}
2000
2001// check elements are equal mod p by proving their integer difference is a multiple of p.
2002// This relies on the minus operator for a-b increasing a by a multiple of p large enough so diff is non-negative
2003// When one of the elements is a constant and another is a witness we check equality of limbs, so if the witness
2004// bigfield element is in an unreduced form, it needs to be reduced first. We don't have automatic reduced form
2005// detection for now, so it is up to the circuit writer to detect this
2006template <typename Builder, typename T>
2007void bigfield<Builder, T>::assert_equal(const bigfield& other, std::string const& msg) const
2008{
2009 Builder* ctx = this->context ? this->context : other.context;
2010 if (is_constant() && other.is_constant()) {
2011 std::cerr << "bigfield: calling assert equal on 2 CONSTANT bigfield elements...is this intended?" << std::endl;
2012 BB_ASSERT_EQ(get_value(), other.get_value(), "We expect constants to be less than the target modulus");
2013 return;
2014 } else if (other.is_constant()) {
2015 // NOTE(https://github.com/AztecProtocol/barretenberg/issues/998): This does a limb-wise integer
2016 // comparison, so `this` must already be in reduced form (value in [0, p)) before calling this method.
2017 // If `this = kp + r` and `other = r`, the limbs differ and an honest prover cannot satisfy the
2018 // constraints. Callers are responsible for calling self_reduce() first when necessary; we omit it
2019 // here to avoid adding spurious gates in the common case where `this` is already reduced.
2020 // `other` should never exceed the modulus by design; we assert this as a precaution.
2021 BB_ASSERT_LT(get_value(),
2022 modulus_u512,
2023 "bigfield::assert_equal: 'this' is not reduced (value >= p). Call self_reduce() before comparing "
2024 "against a constant.");
2025 BB_ASSERT_LT(other.get_value(), modulus_u512);
2026 field_t<Builder> t0 = (binary_basis_limbs[0].element - other.binary_basis_limbs[0].element);
2027 field_t<Builder> t1 = (binary_basis_limbs[1].element - other.binary_basis_limbs[1].element);
2028 field_t<Builder> t2 = (binary_basis_limbs[2].element - other.binary_basis_limbs[2].element);
2029 field_t<Builder> t3 = (binary_basis_limbs[3].element - other.binary_basis_limbs[3].element);
2030 field_t<Builder> t4 = (prime_basis_limb - other.prime_basis_limb);
2031 t0.assert_is_zero();
2032 t1.assert_is_zero();
2033 t2.assert_is_zero();
2034 t3.assert_is_zero();
2035 t4.assert_is_zero();
2036 return;
2037 } else if (is_constant()) {
2038 other.assert_equal(*this, msg);
2039 return;
2040 } else {
2041 // Catch the error if the reduced value of the two elements are not equal
2042 uint512_t lhs_reduced_value = get_value() % modulus_u512;
2043 uint512_t rhs_reduced_value = other.get_value() % modulus_u512;
2044 if ((lhs_reduced_value != rhs_reduced_value) && !get_context()->failed()) {
2045 get_context()->failure(msg);
2046 }
2047
2048 // Remove tags, we don't want to cause violations on assert_equal
2049 const auto original_tag = get_origin_tag();
2050 const auto other_original_tag = other.get_origin_tag();
2051 auto empty_tag = OriginTag::constant(); // Disable origin checking during intermediate operations
2052 set_origin_tag(empty_tag);
2053 other.set_origin_tag(empty_tag);
2054
2055 bigfield diff = *this - other;
2056 const uint512_t diff_val = diff.get_value();
2057 const uint512_t modulus(target_basis.modulus);
2058
2059 const auto [quotient_512, remainder_512] = (diff_val).divmod(modulus);
2060 if (remainder_512 != 0) {
2061 std::cerr << "bigfield: remainder not zero!" << std::endl;
2062 }
2063 bigfield quotient;
2064
2065 const size_t num_quotient_bits = get_quotient_max_bits({ 0 });
2066 quotient = bigfield(witness_t(ctx, fr(quotient_512.slice(0, NUM_LIMB_BITS * 2).lo)),
2067 witness_t(ctx, fr(quotient_512.slice(NUM_LIMB_BITS * 2, NUM_LIMB_BITS * 4).lo)),
2068 false,
2069 num_quotient_bits);
2070 unsafe_evaluate_multiply_add(diff, { one() }, {}, quotient, { zero() });
2071
2072 // Restore tags
2073 set_origin_tag(original_tag);
2074 other.set_origin_tag(other_original_tag);
2075 }
2076}
2077
2078// construct a proof that points are different mod p, when they are different mod r
2079// WARNING: This method doesn't have perfect completeness - for points equal mod r (or with certain difference kp
2080// mod r) but different mod p, you can't construct a proof. The failure probability is at most
2081// (L + R + 1) / r where L = floor(a.max / p), R = floor(b.max / p), r = native field size (~2^254).
2082// With max bounded by 2^256 - 1 and p >= 2^249, we get L,R <= 127, so probability < 2^{-246}.
2083// Note also that the number of constraints depends on how
2084// much the values have overflown beyond p e.g. due to an addition chain The function is based on the following.
2085// Suppose a-b = 0 mod p. Then a-b = k*p for k in a range [-R,L] for largest L and R such that L*p>= a, R*p>=b.
2086// And also a-b = k*p mod r for such k. Thus we can verify a-b is non-zero mod p by taking the product of such values
2087// (a-b-kp) and showing it's non-zero mod r
2088template <typename Builder, typename T>
2089void bigfield<Builder, T>::assert_is_not_equal(const bigfield& other, std::string const& msg) const
2090{
2091 // Why would we use this for 2 constants? Turns out, in biggroup
2092 const auto get_overload_count = [target_modulus = modulus_u512](const uint512_t& maximum_value) {
2093 uint512_t target = target_modulus;
2094 size_t overload_count = 0;
2095 while (target <= maximum_value) {
2096 ++overload_count;
2097 target += target_modulus;
2098 }
2099 return overload_count;
2100 };
2101 const size_t lhs_overload_count = get_overload_count(get_maximum_value());
2102 const size_t rhs_overload_count = get_overload_count(other.get_maximum_value());
2103
2104 // if (a == b) then (a == b mod n)
2105 // to save gates, we only check that (a == b mod n)
2106
2107 // if numeric val of a = a' + p.q
2108 // we want to check (a' + p.q == b mod n)
2109 const field_t<Builder> base_diff = prime_basis_limb - other.prime_basis_limb;
2110 auto diff = base_diff;
2111 field_t<Builder> prime_basis(get_context(), modulus);
2112 field_t<Builder> prime_basis_accumulator = prime_basis;
2113 // Each loop iteration adds 1 gate
2114 // (prime_basis and prime_basis accumulator are constant so only the * operator adds a gate)
2115 for (size_t i = 0; i < lhs_overload_count; ++i) {
2116 diff = diff * (base_diff - prime_basis_accumulator);
2117 prime_basis_accumulator += prime_basis;
2118 }
2119 prime_basis_accumulator = prime_basis;
2120 for (size_t i = 0; i < rhs_overload_count; ++i) {
2121 diff = diff * (base_diff + prime_basis_accumulator);
2122 prime_basis_accumulator += prime_basis;
2123 }
2124 diff.assert_is_not_zero(msg);
2125}
2126
2127// We reduce an element's mod 2^t representation (t=4*NUM_LIMB_BITS) to size 2^s for smallest s with 2^s>p
2128// This is much cheaper than actually reducing mod p and suffices for addition chains (where we just need not to
2129// overflow 2^t) We also reduce any "spillage" inside the first 3 limbs, so that their range is NUM_LIMB_BITS and
2130// not larger
2131template <typename Builder, typename T> void bigfield<Builder, T>::self_reduce() const
2132{
2133 // Warning: this assumes we have run circuit construction at least once in debug mode where large non reduced
2134 // constants are disallowed via ASSERT
2135 if (is_constant()) {
2136 return;
2137 }
2138 OriginTag new_tag = get_origin_tag();
2139 const auto [quotient_value, remainder_value] = get_value().divmod(target_basis.modulus);
2140
2141 bigfield quotient(context);
2142
2143 uint512_t maximum_quotient_size = get_maximum_value() / target_basis.modulus;
2144 uint64_t maximum_quotient_bits = maximum_quotient_size.get_msb() + 1;
2145 if ((maximum_quotient_bits & 1ULL) == 1ULL) {
2146 ++maximum_quotient_bits;
2147 }
2148
2149 BB_ASSERT_LTE(maximum_quotient_bits, NUM_LIMB_BITS);
2150 uint32_t quotient_limb_index = context->add_variable(bb::fr(quotient_value.lo));
2151 field_t<Builder> quotient_limb = field_t<Builder>::from_witness_index(context, quotient_limb_index);
2152 context->create_limbed_range_constraint(quotient_limb.get_witness_index(),
2153 static_cast<size_t>(maximum_quotient_bits));
2154
2155 BB_ASSERT_LT((uint1024_t(1) << maximum_quotient_bits) * uint1024_t(modulus_u512) + DEFAULT_MAXIMUM_REMAINDER,
2156 get_maximum_crt_product());
2157 quotient.binary_basis_limbs[0] = Limb(quotient_limb, (uint256_t(1) << maximum_quotient_bits) - 1);
2161 quotient.prime_basis_limb = quotient_limb;
2162 // this constructor with can_overflow=false will enforce remainder of size<2^s
2163 bigfield remainder = bigfield(
2164 witness_t(context, fr(remainder_value.slice(0, NUM_LIMB_BITS * 2).lo)),
2165 witness_t(context, fr(remainder_value.slice(NUM_LIMB_BITS * 2, NUM_LIMB_BITS * 3 + NUM_LAST_LIMB_BITS).lo)));
2166
2167 unsafe_evaluate_multiply_add(*this, one(), {}, quotient, { remainder });
2168 binary_basis_limbs[0] =
2169 remainder.binary_basis_limbs[0]; // Combination of const method and mutable variables is good practice?
2170 binary_basis_limbs[1] = remainder.binary_basis_limbs[1];
2171 binary_basis_limbs[2] = remainder.binary_basis_limbs[2];
2172 binary_basis_limbs[3] = remainder.binary_basis_limbs[3];
2173 prime_basis_limb = remainder.prime_basis_limb;
2174 set_origin_tag(new_tag);
2175} // namespace stdlib
2176
2177template <typename Builder, typename T>
2179 const bigfield& input_to_mul,
2180 const std::vector<bigfield>& to_add,
2181 const bigfield& input_quotient,
2182 const std::vector<bigfield>& input_remainders)
2183{
2184
2185 BB_ASSERT_LTE(to_add.size(), MAXIMUM_SUMMAND_COUNT);
2186 BB_ASSERT_LTE(input_remainders.size(), MAXIMUM_SUMMAND_COUNT);
2187 // Sanity checks
2188 input_left.sanity_check();
2189 input_to_mul.sanity_check();
2190 input_quotient.sanity_check();
2191 for (auto& el : to_add) {
2192 el.sanity_check();
2193 }
2194 for (auto& el : input_remainders) {
2195 el.sanity_check();
2196 }
2197
2198 std::vector<bigfield> remainders(input_remainders);
2199
2200 bigfield left = input_left;
2201 bigfield to_mul = input_to_mul;
2202 bigfield quotient = input_quotient;
2203
2204 // Either of the multiplicand must be a witness.
2205 BB_ASSERT(!left.is_constant() || !to_mul.is_constant());
2206 Builder* ctx = left.context ? left.context : to_mul.context;
2207
2208 // Compute the maximum value of the product of the two inputs: max(a * b)
2209 uint512_t max_ab_lo(0);
2210 uint512_t max_ab_hi(0);
2211 std::tie(max_ab_lo, max_ab_hi) = compute_partial_schoolbook_multiplication(left.get_binary_basis_limb_maximums(),
2213
2214 // Compute the maximum value of the product of the quotient and neg_modulus: max(q * p')
2215 uint512_t max_q_neg_p_lo(0);
2216 uint512_t max_q_neg_p_hi(0);
2217 std::tie(max_q_neg_p_lo, max_q_neg_p_hi) = compute_partial_schoolbook_multiplication(
2218 neg_modulus_mod_binary_basis_limbs_u256, quotient.get_binary_basis_limb_maximums());
2219
2220 // Compute the maximum value that needs to be borrowed from the hi limbs to the lo limb.
2221 // Check the README for the explanation of the borrow.
2222 uint256_t max_remainders_lo(0);
2223 for (const auto& remainder : input_remainders) {
2224 max_remainders_lo += remainder.binary_basis_limbs[0].maximum_value +
2225 (remainder.binary_basis_limbs[1].maximum_value << NUM_LIMB_BITS);
2226 }
2227
2228 // While performing the subtraction of remainder r as:
2229 //
2230 // (a * b + q * p') - (r)
2231 //
2232 // we want to ensure that the lower limbs do not underflow. So we add a borrow value
2233 // to the lower limbs and subtract it from the higher limbs. Naturally, such a borrow value
2234 // must be a multiple of 2^2L (where L = NUM_LIMB_BITS). Let borrow_lo_value be the value
2235 // borrowed from the hi limbs, then we must have:
2236 //
2237 // borrow_lo_value * 2^(2L) >= max_remainders_lo
2238 //
2239 // Thus, we can compute the minimum borrow_lo_value as:
2240 //
2241 // borrow_lo_value = ⌈ max_remainders_lo / 2^(2L) ⌉
2242 //
2243 uint256_t borrow_lo_value =
2244 (max_remainders_lo + ((uint256_t(1) << (2 * NUM_LIMB_BITS)) - 1)) >> (2 * NUM_LIMB_BITS);
2245 field_t<Builder> borrow_lo(ctx, bb::fr(borrow_lo_value));
2246
2247 uint512_t max_a0(0);
2248 uint512_t max_a1(0);
2249 for (size_t i = 0; i < to_add.size(); ++i) {
2250 max_a0 += to_add[i].binary_basis_limbs[0].maximum_value +
2251 (to_add[i].binary_basis_limbs[1].maximum_value << NUM_LIMB_BITS);
2252 max_a1 += to_add[i].binary_basis_limbs[2].maximum_value +
2253 (to_add[i].binary_basis_limbs[3].maximum_value << NUM_LIMB_BITS);
2254 }
2255 const uint512_t max_lo = max_ab_lo + max_q_neg_p_lo + max_remainders_lo + max_a0;
2256 const uint512_t max_lo_carry = max_lo >> (2 * NUM_LIMB_BITS);
2257 const uint512_t max_hi = max_ab_hi + max_q_neg_p_hi + max_a1 + max_lo_carry;
2258
2259 uint64_t max_lo_bits = (max_lo.get_msb() + 1);
2260 uint64_t max_hi_bits = max_hi.get_msb() + 1;
2261
2262 BB_ASSERT(max_lo_bits > (2 * NUM_LIMB_BITS));
2263 BB_ASSERT(max_hi_bits > (2 * NUM_LIMB_BITS));
2264
2265 uint64_t carry_lo_msb = max_lo_bits - (2 * NUM_LIMB_BITS);
2266 uint64_t carry_hi_msb = max_hi_bits - (2 * NUM_LIMB_BITS);
2267
2268 // The custom bigfield multiplication gate requires inputs are witnesses.
2269 // If we're using constant values, instantiate them as circuit variables
2270 //
2271 // Explanation:
2272 // The bigfield multiplication gate expects witnesses and disallows circuit constants
2273 // because allowing circuit constants would lead to complex circuit logic to support
2274 // different combinations of constant and witness inputs. Particularly, bigfield multiplication
2275 // gate enforces constraints of the form: a * b - q * p + r = 0, where:
2276 //
2277 // input left a = (a3 || a2 || a1 || a0)
2278 // input right b = (b3 || b2 || b1 || b0)
2279 // quotient q = (q3 || q2 || q1 || q0)
2280 // remainder r = (r3 || r2 || r1 || r0)
2281 //
2282 // | a1 | b1 | r0 | lo_0 | <-- product gate 1: check lo_0
2283 // | a0 | b0 | a3 | b3 |
2284 // | a2 | b2 | r3 | hi_0 |
2285 // | a1 | b1 | r2 | hi_1 |
2286 //
2287 // Example constaint: lo_0 = (a1 * b0 + a0 * b1) * 2^b + (a0 * b0) - r0
2288 // ==> w4 = (w1 * w'2 + w'1 * w2) * 2^b + (w'1 * w'2) - w3
2289 //
2290 // If a, b both are witnesses, this special gate performs 3 field multiplications per gate.
2291 // If b was a constant, then we would need to no field multiplications, but instead update the
2292 // the limbs of a with multiplicative and additive constants. This just makes the circuit logic
2293 // more complex, so we disallow constants. If there are constants, we convert them to fixed witnesses (at the
2294 // expense of 1 extra gate per constant).
2295 //
2296 const auto convert_constant_to_fixed_witness = [ctx](const bigfield& input) {
2297 bigfield output(input);
2298 // Save the original tag before converting to witnesses
2299 auto original_tag = input.get_origin_tag();
2300 output.prime_basis_limb =
2301 field_t<Builder>::from_witness_index(ctx, ctx->put_constant_variable(input.prime_basis_limb.get_value()));
2303 ctx, ctx->put_constant_variable(input.binary_basis_limbs[0].element.get_value()));
2305 ctx, ctx->put_constant_variable(input.binary_basis_limbs[1].element.get_value()));
2307 ctx, ctx->put_constant_variable(input.binary_basis_limbs[2].element.get_value()));
2309 ctx, ctx->put_constant_variable(input.binary_basis_limbs[3].element.get_value()));
2310 output.context = ctx;
2311 // Restore the original tag after converting to witnesses
2312 output.set_origin_tag(original_tag);
2313 return output;
2314 };
2315 if (left.is_constant()) {
2316 left = convert_constant_to_fixed_witness(left);
2317 }
2318 if (to_mul.is_constant()) {
2319 to_mul = convert_constant_to_fixed_witness(to_mul);
2320 }
2321 if (quotient.is_constant()) {
2322 quotient = convert_constant_to_fixed_witness(quotient);
2323 }
2324 if (remainders[0].is_constant()) {
2325 remainders[0] = convert_constant_to_fixed_witness(remainders[0]);
2326 }
2327
2328 std::vector<field_t<Builder>> limb_0_accumulator{ remainders[0].binary_basis_limbs[0].element };
2329 std::vector<field_t<Builder>> limb_2_accumulator{ remainders[0].binary_basis_limbs[2].element };
2330 std::vector<field_t<Builder>> prime_limb_accumulator{ remainders[0].prime_basis_limb };
2331 for (size_t i = 1; i < remainders.size(); ++i) {
2332 limb_0_accumulator.emplace_back(remainders[i].binary_basis_limbs[0].element);
2333 limb_0_accumulator.emplace_back(remainders[i].binary_basis_limbs[1].element * shift_1);
2334 limb_2_accumulator.emplace_back(remainders[i].binary_basis_limbs[2].element);
2335 limb_2_accumulator.emplace_back(remainders[i].binary_basis_limbs[3].element * shift_1);
2336 prime_limb_accumulator.emplace_back(remainders[i].prime_basis_limb);
2337 }
2338 for (const auto& add : to_add) {
2339 limb_0_accumulator.emplace_back(-add.binary_basis_limbs[0].element);
2340 limb_0_accumulator.emplace_back(-add.binary_basis_limbs[1].element * shift_1);
2341 limb_2_accumulator.emplace_back(-add.binary_basis_limbs[2].element);
2342 limb_2_accumulator.emplace_back(-add.binary_basis_limbs[3].element * shift_1);
2343 prime_limb_accumulator.emplace_back(-add.prime_basis_limb);
2344 }
2345
2346 const auto& t0 = remainders[0].binary_basis_limbs[1].element;
2347 const auto& t1 = remainders[0].binary_basis_limbs[3].element;
2348 bool needs_normalize = (t0.additive_constant != 0 || t0.multiplicative_constant != 1);
2349 needs_normalize = needs_normalize || (t1.additive_constant != 0 || t1.multiplicative_constant != 1);
2350
2351 if (needs_normalize) {
2352 limb_0_accumulator.emplace_back(remainders[0].binary_basis_limbs[1].element * shift_1);
2353 limb_2_accumulator.emplace_back(remainders[0].binary_basis_limbs[3].element * shift_1);
2354 }
2355
2356 std::array<field_t<Builder>, NUM_LIMBS> remainder_limbs{
2357 field_t<Builder>::accumulate(limb_0_accumulator),
2358 needs_normalize ? field_t<Builder>::from_witness_index(ctx, ctx->zero_idx())
2359 : remainders[0].binary_basis_limbs[1].element,
2360 field_t<Builder>::accumulate(limb_2_accumulator),
2361 needs_normalize ? field_t<Builder>::from_witness_index(ctx, ctx->zero_idx())
2362 : remainders[0].binary_basis_limbs[3].element,
2363 };
2364 field_t<Builder> remainder_prime_limb = field_t<Builder>::accumulate(prime_limb_accumulator);
2365
2370 {
2371 remainder_limbs[0].get_witness_index(),
2372 remainder_limbs[1].get_witness_index(),
2373 remainder_limbs[2].get_witness_index(),
2374 remainder_limbs[3].get_witness_index(),
2375 },
2376 { neg_modulus_mod_binary_basis_limbs[0],
2377 neg_modulus_mod_binary_basis_limbs[1],
2378 neg_modulus_mod_binary_basis_limbs[2],
2379 neg_modulus_mod_binary_basis_limbs[3] },
2380 };
2381
2382 // N.B. this method DOES NOT evaluate the prime field component of the non-native field mul
2383 const auto [lo_idx, hi_idx] = ctx->evaluate_non_native_field_multiplication(witnesses);
2384
2386 to_mul.prime_basis_limb,
2387 quotient.prime_basis_limb * negative_prime_modulus_mod_native_basis,
2388 -remainder_prime_limb,
2389 "bigfield: prime limb identity failed");
2390
2391 field_t lo = field_t<Builder>::from_witness_index(ctx, lo_idx) + borrow_lo;
2393
2394 // if both the hi and lo output limbs have less than 70 bits, we can use our custom
2395 // limb accumulation gate (accumulates 2 field elements, each composed of 5 14-bit limbs, in 3 gates)
2396 if (carry_lo_msb <= 70 && carry_hi_msb <= 70) {
2397 ctx->range_constrain_two_limbs(hi.get_witness_index(),
2398 lo.get_witness_index(),
2399 static_cast<size_t>(carry_hi_msb),
2400 static_cast<size_t>(carry_lo_msb),
2401 "bigfield::unsafe_evaluate_multiply_add: carries too large");
2402 } else {
2403 hi.create_range_constraint(static_cast<size_t>(carry_hi_msb), "bigfield: carry_hi too large");
2404 lo.create_range_constraint(static_cast<size_t>(carry_lo_msb), "bigfield: carry_lo too large");
2405 }
2406}
2407
2408template <typename Builder, typename T>
2410 const std::vector<bigfield>& input_right,
2411 const std::vector<bigfield>& to_add,
2412 const bigfield& input_quotient,
2413 const std::vector<bigfield>& input_remainders)
2414{
2415 BB_ASSERT_EQ(input_left.size(), input_right.size());
2416 BB_ASSERT_LTE(input_left.size(), MAXIMUM_SUMMAND_COUNT);
2417 BB_ASSERT_LTE(to_add.size(), MAXIMUM_SUMMAND_COUNT);
2418 BB_ASSERT_LTE(input_remainders.size(), MAXIMUM_SUMMAND_COUNT);
2419
2420 // Sanity checks
2421 bool is_left_constant = true;
2422 for (auto& el : input_left) {
2423 el.sanity_check();
2424 is_left_constant &= el.is_constant();
2425 }
2426 bool is_right_constant = true;
2427 for (auto& el : input_right) {
2428 el.sanity_check();
2429 is_right_constant &= el.is_constant();
2430 }
2431 for (auto& el : to_add) {
2432 el.sanity_check();
2433 }
2434 input_quotient.sanity_check();
2435 for (auto& el : input_remainders) {
2436 el.sanity_check();
2437 }
2438
2439 // We must have at least one left or right multiplicand as witnesses.
2440 BB_ASSERT(!is_left_constant || !is_right_constant);
2441
2442 std::vector<bigfield> remainders(input_remainders);
2443 std::vector<bigfield> left(input_left);
2444 std::vector<bigfield> right(input_right);
2445 bigfield quotient = input_quotient;
2446 const size_t num_multiplications = input_left.size();
2447
2448 // Fetch the context
2449 Builder* ctx = nullptr;
2450 for (const auto& el : input_left) {
2451 if (el.context) {
2452 ctx = el.context;
2453 break;
2454 }
2455 }
2456 if (ctx == nullptr) {
2457 for (const auto& el : input_right) {
2458 if (el.context) {
2459 ctx = el.context;
2460 break;
2461 }
2462 }
2463 }
2464 BB_ASSERT(ctx != nullptr);
2465
2472 uint512_t max_lo = 0;
2473 uint512_t max_hi = 0;
2474
2475 // Compute the maximum value that needs to be borrowed from the hi limbs to the lo limb.
2476 // Check the README for the explanation of the borrow.
2477 uint256_t max_remainders_lo(0);
2478 for (const auto& remainder : input_remainders) {
2479 max_remainders_lo += remainder.binary_basis_limbs[0].maximum_value +
2480 (remainder.binary_basis_limbs[1].maximum_value << NUM_LIMB_BITS);
2481 }
2482
2483 // While performing the subtraction of (sum of) remainder(s) as:
2484 //
2485 // (Σi ai * bi + q * p') - (Σj rj)
2486 //
2487 // we want to ensure that the lower limbs do not underflow. So we add a borrow value
2488 // to the lower limbs and subtract it from the higher limbs. Naturally, such a borrow value
2489 // must be a multiple of 2^2L (where L = NUM_LIMB_BITS). Let borrow_lo_value be the value
2490 // borrowed from the hi limbs, then we must have:
2491 //
2492 // borrow_lo_value * 2^(2L) >= max_remainders_lo
2493 //
2494 // Thus, we can compute the minimum borrow_lo_value as:
2495 //
2496 // borrow_lo_value = ⌈ max_remainders_lo / 2^(2L) ⌉
2497 //
2498 uint256_t borrow_lo_value =
2499 (max_remainders_lo + ((uint256_t(1) << (2 * NUM_LIMB_BITS)) - 1)) >> (2 * NUM_LIMB_BITS);
2500 field_t<Builder> borrow_lo(ctx, bb::fr(borrow_lo_value));
2501
2502 // Compute the maximum value of the quotient times modulus.
2503 const auto [max_q_neg_p_lo, max_q_neg_p_hi] = compute_partial_schoolbook_multiplication(
2504 neg_modulus_mod_binary_basis_limbs_u256, quotient.get_binary_basis_limb_maximums());
2505
2506 // update max_lo, max_hi with quotient limb product terms.
2507 max_lo += max_q_neg_p_lo + max_remainders_lo;
2508 max_hi += max_q_neg_p_hi;
2509
2510 // Compute maximum value of addition terms in `to_add` and add to max_lo, max_hi
2511 uint512_t max_a0(0);
2512 uint512_t max_a1(0);
2513 for (size_t i = 0; i < to_add.size(); ++i) {
2514 max_a0 += to_add[i].binary_basis_limbs[0].maximum_value +
2515 (to_add[i].binary_basis_limbs[1].maximum_value << NUM_LIMB_BITS);
2516 max_a1 += to_add[i].binary_basis_limbs[2].maximum_value +
2517 (to_add[i].binary_basis_limbs[3].maximum_value << NUM_LIMB_BITS);
2518 }
2519 max_lo += max_a0;
2520 max_hi += max_a1;
2521
2522 // Compute the maximum value of our multiplication products and add to max_lo, max_hi
2523 for (size_t i = 0; i < num_multiplications; ++i) {
2524 const auto [product_lo, product_hi] = compute_partial_schoolbook_multiplication(
2525 left[i].get_binary_basis_limb_maximums(), right[i].get_binary_basis_limb_maximums());
2526 max_lo += product_lo;
2527 max_hi += product_hi;
2528 }
2529
2530 const uint512_t max_lo_carry = max_lo >> (2 * NUM_LIMB_BITS);
2531 max_hi += max_lo_carry;
2532 // Compute the maximum number of bits in `max_lo` and `max_hi` - this defines the range constraint values we
2533 // will need to apply to validate our product
2534 uint64_t max_lo_bits = (max_lo.get_msb() + 1);
2535 uint64_t max_hi_bits = max_hi.get_msb() + 1;
2536
2537 // The custom bigfield multiplication gate requires inputs are witnesses.
2538 // If we're using constant values, instantiate them as circuit variables
2539 //
2540 // Explanation:
2541 // The bigfield multiplication gate expects witnesses and disallows circuit constants
2542 // because allowing circuit constants would lead to complex circuit logic to support
2543 // different combinations of constant and witness inputs. Particularly, bigfield multiplication
2544 // gate enforces constraints of the form: a * b - q * p + r = 0, where:
2545 //
2546 // input left a = (a3 || a2 || a1 || a0)
2547 // input right b = (b3 || b2 || b1 || b0)
2548 // quotient q = (q3 || q2 || q1 || q0)
2549 // remainder r = (r3 || r2 || r1 || r0)
2550 //
2551 // | a1 | b1 | r0 | lo_0 | <-- product gate 1: check lo_0
2552 // | a0 | b0 | a3 | b3 |
2553 // | a2 | b2 | r3 | hi_0 |
2554 // | a1 | b1 | r2 | hi_1 |
2555 //
2556 // Example constaint: lo_0 = (a1 * b0 + a0 * b1) * 2^b + (a0 * b0) - r0
2557 // ==> w4 = (w1 * w'2 + w'1 * w2) * 2^b + (w'1 * w'2) - w3
2558 //
2559 // If a, b both are witnesses, this special gate performs 3 field multiplications per gate.
2560 // If b was a constant, then we would need to no field multiplications, but instead update the
2561 // the limbs of a with multiplicative and additive constants. This just makes the circuit logic
2562 // more complex, so we disallow constants. If there are constants, we convert them to fixed witnesses (at the
2563 // expense of 1 extra gate per constant).
2564 //
2565 const auto convert_constant_to_fixed_witness = [ctx](const bigfield& input) {
2566 BB_ASSERT(input.is_constant());
2567 bigfield output(input);
2568 // Save the original tag before converting to witnesses
2569 auto original_tag = input.get_origin_tag();
2570 output.prime_basis_limb =
2571 field_t<Builder>::from_witness_index(ctx, ctx->put_constant_variable(input.prime_basis_limb.get_value()));
2573 ctx, ctx->put_constant_variable(input.binary_basis_limbs[0].element.get_value()));
2575 ctx, ctx->put_constant_variable(input.binary_basis_limbs[1].element.get_value()));
2577 ctx, ctx->put_constant_variable(input.binary_basis_limbs[2].element.get_value()));
2579 ctx, ctx->put_constant_variable(input.binary_basis_limbs[3].element.get_value()));
2580 output.context = ctx;
2581 // Restore the original tag after converting to witnesses
2582 output.set_origin_tag(original_tag);
2583 return output;
2584 };
2585
2586 // evalaute a nnf mul and add into existing lohi output for our extra product terms
2587 // we need to add the result of (left_b * right_b) into lo_1_idx and hi_1_idx
2588 // our custom gate evaluates: ((a * b) + (q * neg_modulus) - r) / 2^{136} = lo + hi * 2^{136}
2589 // where q is a 'quotient' bigfield and neg_modulus is defined by selector polynomial values
2590 // The custom gate costs 7 constraints, which is cheaper than computing `a * b` using multiplication +
2591 // addition gates But....we want to obtain `left_a * right_b + lo_1 + hi_1 * 2^{136} = lo + hi * 2^{136}` If
2592 // we set `neg_modulus = [2^{136}, 0, 0, 0]` and `q = [lo_1, 0, hi_1, 0]`, then we will add `lo_1` into
2593 // `lo`, and `lo_1/2^{136} + hi_1` into `hi`. we can then subtract off `lo_1/2^{136}` from `hi`, by setting
2594 // `r = [0, 0, lo_1, 0]` This saves us 2 addition gates as we don't have to add together the outputs of two
2595 // calls to `evaluate_non_native_field_multiplication`
2596 std::vector<field_t<Builder>> limb_0_accumulator;
2597 std::vector<field_t<Builder>> limb_2_accumulator;
2598 std::vector<field_t<Builder>> prime_limb_accumulator;
2599
2600 for (size_t i = 0; i < num_multiplications; ++i) {
2601 if (left[i].is_constant()) {
2602 left[i] = convert_constant_to_fixed_witness(left[i]);
2603 }
2604 if (right[i].is_constant()) {
2605 right[i] = convert_constant_to_fixed_witness(right[i]);
2606 }
2607
2608 if (i > 0) {
2610 left[i].get_binary_basis_limb_witness_indices(),
2611 right[i].get_binary_basis_limb_witness_indices(),
2612 };
2613
2614 const auto [lo_2_idx, hi_2_idx] = ctx->queue_partial_non_native_field_multiplication(mul_witnesses);
2615
2618 // Set CONSTANT tags so these intermediate results are absorbed during tag merging
2619 // The final tag will come from the remainders which have properly merged tags from mult_madd
2620 auto const_tag = OriginTag::constant();
2621 lo_2.set_origin_tag(const_tag);
2622 hi_2.set_origin_tag(const_tag);
2623
2624 limb_0_accumulator.emplace_back(-lo_2);
2625 limb_2_accumulator.emplace_back(-hi_2);
2626 prime_limb_accumulator.emplace_back(-(left[i].prime_basis_limb * right[i].prime_basis_limb));
2627 }
2628 }
2629 if (quotient.is_constant()) {
2630 quotient = convert_constant_to_fixed_witness(quotient);
2631 }
2632
2633 bool no_remainders = remainders.empty();
2634 if (!no_remainders) {
2635 limb_0_accumulator.emplace_back(remainders[0].binary_basis_limbs[0].element);
2636 limb_2_accumulator.emplace_back(remainders[0].binary_basis_limbs[2].element);
2637 prime_limb_accumulator.emplace_back(remainders[0].prime_basis_limb);
2638 }
2639 for (size_t i = 1; i < remainders.size(); ++i) {
2640 limb_0_accumulator.emplace_back(remainders[i].binary_basis_limbs[0].element);
2641 limb_0_accumulator.emplace_back(remainders[i].binary_basis_limbs[1].element * shift_1);
2642 limb_2_accumulator.emplace_back(remainders[i].binary_basis_limbs[2].element);
2643 limb_2_accumulator.emplace_back(remainders[i].binary_basis_limbs[3].element * shift_1);
2644 prime_limb_accumulator.emplace_back(remainders[i].prime_basis_limb);
2645 }
2646 for (const auto& add : to_add) {
2647 limb_0_accumulator.emplace_back(-add.binary_basis_limbs[0].element);
2648 limb_0_accumulator.emplace_back(-add.binary_basis_limbs[1].element * shift_1);
2649 limb_2_accumulator.emplace_back(-add.binary_basis_limbs[2].element);
2650 limb_2_accumulator.emplace_back(-add.binary_basis_limbs[3].element * shift_1);
2651 prime_limb_accumulator.emplace_back(-add.prime_basis_limb);
2652 }
2653
2654 field_t<Builder> accumulated_lo = field_t<Builder>::accumulate(limb_0_accumulator);
2655 field_t<Builder> accumulated_hi = field_t<Builder>::accumulate(limb_2_accumulator);
2656 if (accumulated_lo.is_constant()) {
2657 accumulated_lo =
2658 field_t<Builder>::from_witness_index(ctx, ctx->put_constant_variable(accumulated_lo.get_value()));
2659 }
2660 if (accumulated_hi.is_constant()) {
2661 accumulated_hi =
2662 field_t<Builder>::from_witness_index(ctx, ctx->put_constant_variable(accumulated_hi.get_value()));
2663 }
2664 field_t<Builder> remainder1 = no_remainders ? field_t<Builder>::from_witness_index(ctx, ctx->zero_idx())
2665 : remainders[0].binary_basis_limbs[1].element;
2666 if (remainder1.is_constant()) {
2667 remainder1 = field_t<Builder>::from_witness_index(ctx, ctx->put_constant_variable(remainder1.get_value()));
2668 }
2669 field_t<Builder> remainder3 = no_remainders ? field_t<Builder>::from_witness_index(ctx, ctx->zero_idx())
2670 : remainders[0].binary_basis_limbs[3].element;
2671 if (remainder3.is_constant()) {
2672 remainder3 = field_t<Builder>::from_witness_index(ctx, ctx->put_constant_variable(remainder3.get_value()));
2673 }
2674 std::array<field_t<Builder>, NUM_LIMBS> remainder_limbs{
2675 accumulated_lo,
2676 remainder1,
2677 accumulated_hi,
2678 remainder3,
2679 };
2680 field_t<Builder> remainder_prime_limb = field_t<Builder>::accumulate(prime_limb_accumulator);
2681
2683 left[0].get_binary_basis_limb_witness_indices(),
2684 right[0].get_binary_basis_limb_witness_indices(),
2686 {
2687 remainder_limbs[0].get_witness_index(),
2688 remainder_limbs[1].get_witness_index(),
2689 remainder_limbs[2].get_witness_index(),
2690 remainder_limbs[3].get_witness_index(),
2691 },
2692 { neg_modulus_mod_binary_basis_limbs[0],
2693 neg_modulus_mod_binary_basis_limbs[1],
2694 neg_modulus_mod_binary_basis_limbs[2],
2695 neg_modulus_mod_binary_basis_limbs[3] },
2696 };
2697
2698 const auto [lo_1_idx, hi_1_idx] = ctx->evaluate_non_native_field_multiplication(witnesses);
2699
2700 field_t<Builder>::evaluate_polynomial_identity(left[0].prime_basis_limb,
2701 right[0].prime_basis_limb,
2702 quotient.prime_basis_limb * negative_prime_modulus_mod_native_basis,
2703 -remainder_prime_limb,
2704 "bigfield: prime limb identity failed");
2705
2706 field_t lo = field_t<Builder>::from_witness_index(ctx, lo_1_idx) + borrow_lo;
2708
2709 BB_ASSERT(max_lo_bits > (2 * NUM_LIMB_BITS));
2710 BB_ASSERT(max_hi_bits > (2 * NUM_LIMB_BITS));
2711
2712 uint64_t carry_lo_msb = max_lo_bits - (2 * NUM_LIMB_BITS);
2713 uint64_t carry_hi_msb = max_hi_bits - (2 * NUM_LIMB_BITS);
2714
2715 // if both the hi and lo output limbs have less than 70 bits, we can use our custom
2716 // limb accumulation gate (accumulates 2 field elements, each composed of 5 14-bit limbs, in 3 gates)
2717 if (carry_lo_msb <= 70 && carry_hi_msb <= 70) {
2718 ctx->range_constrain_two_limbs(hi.get_witness_index(),
2719 lo.get_witness_index(),
2720 static_cast<size_t>(carry_hi_msb),
2721 static_cast<size_t>(carry_lo_msb),
2722 "bigfield::unsafe_evaluate_multiply_add: carries too large");
2723 } else {
2724 hi.create_range_constraint(static_cast<size_t>(carry_hi_msb), "bigfield: carry_hi too large");
2725 lo.create_range_constraint(static_cast<size_t>(carry_lo_msb), "bigfield: carry_lo too large");
2726 }
2727}
2728
2729template <typename Builder, typename T>
2731 const std::vector<bigfield>& to_add,
2732 const bigfield& quotient,
2733 const bigfield& remainder)
2734{
2735 BB_ASSERT_LTE(to_add.size(), MAXIMUM_SUMMAND_COUNT);
2736
2737 // Suppose input is:
2738 // x = (x3 || x2 || x1 || x0)
2739 //
2740 // x * x = (x0 * x0) +
2741 // (2 • x0 * x1) • 2^b +
2742 // (2 • x0 * x2 + x1 * x1) • 2^{2b} +
2743 // (2 • x0 * x3 + 2 • x1 * x2) • 2^{3b}.
2744 //
2745 // We need 6 multiplications to compute the above, which can be computed using two custom multiplication gates.
2746 // Since each custom bigfield gate can compute 3, we can compute the above using 2 custom multiplication gates
2747 // (as against 3 gates if we used the current bigfield multiplication gate).
2748 // We however avoid this optimization for now and end up using the existing bigfield multiplication gate.
2749 //
2750 unsafe_evaluate_multiply_add(left, left, to_add, quotient, { remainder });
2751}
2752
2753template <typename Builder, typename T>
2755 const bigfield& a, const bigfield& b, const std::vector<bigfield>& to_add)
2756{
2757 BB_ASSERT_LTE(to_add.size(), MAXIMUM_SUMMAND_COUNT);
2758
2760 for (const auto& add_element : to_add) {
2761 add_element.reduction_check();
2762 add_values += add_element.get_value();
2763 }
2764
2765 const uint1024_t left(a.get_value());
2766 const uint1024_t right(b.get_value());
2767 const uint1024_t add_right(add_values);
2768 const uint1024_t modulus(target_basis.modulus);
2769
2770 const auto [quotient_1024, remainder_1024] = (left * right + add_right).divmod(modulus);
2771
2772 return { quotient_1024.lo, remainder_1024.lo };
2773}
2774
2775template <typename Builder, typename T>
2777 const std::vector<uint512_t>& bs,
2778 const std::vector<uint512_t>& to_add)
2779{
2780 BB_ASSERT_EQ(as.size(), bs.size());
2781 BB_ASSERT_LTE(to_add.size(), MAXIMUM_SUMMAND_COUNT);
2782
2784 for (const auto& add_element : to_add) {
2785 add_values += add_element;
2786 }
2787 uint1024_t product_sum(0);
2788 for (size_t i = 0; i < as.size(); i++) {
2789 product_sum += uint1024_t(as[i]) * uint1024_t(bs[i]);
2790 }
2791 const uint1024_t add_right(add_values);
2792 const uint1024_t modulus(target_basis.modulus);
2793
2794 const auto [quotient_1024, remainder_1024] = (product_sum + add_right).divmod(modulus);
2795
2796 return quotient_1024.lo;
2797}
2798template <typename Builder, typename T>
2800 const std::vector<uint512_t>& bs_max,
2801 const std::vector<bigfield>& to_add,
2802 const std::vector<uint1024_t>& remainders_max)
2803{
2804 BB_ASSERT_EQ(as_max.size(), bs_max.size());
2805
2806 BB_ASSERT_LTE(to_add.size(), MAXIMUM_SUMMAND_COUNT);
2807 BB_ASSERT_LTE(as_max.size(), MAXIMUM_SUMMAND_COUNT);
2808 BB_ASSERT_LTE(remainders_max.size(), MAXIMUM_SUMMAND_COUNT);
2809
2810 // Check if the product sum can overflow CRT modulus
2811 if (mul_product_overflows_crt_modulus(as_max, bs_max, to_add)) {
2812 return std::pair<bool, size_t>(true, 0);
2813 }
2814 const size_t num_quotient_bits = get_quotient_max_bits(remainders_max);
2815 std::vector<uint512_t> to_add_max;
2816 for (auto& added_element : to_add) {
2817 to_add_max.push_back(added_element.get_maximum_value());
2818 }
2819 // Get maximum value of quotient
2820 const uint512_t maximum_quotient = compute_maximum_quotient_value(as_max, bs_max, to_add_max);
2821
2822 // Check if the quotient can fit into the range proof
2823 if (maximum_quotient >= (uint512_t(1) << num_quotient_bits)) {
2824 return std::pair<bool, size_t>(true, 0);
2825 }
2826 return std::pair<bool, size_t>(false, num_quotient_bits);
2827}
2828
2829template <typename Builder, typename T>
2832{
2833 const uint512_t b0_inner = (a_limbs[1] * b_limbs[0]);
2834 const uint512_t b1_inner = (a_limbs[0] * b_limbs[1]);
2835 const uint512_t c0_inner = (a_limbs[1] * b_limbs[1]);
2836 const uint512_t c1_inner = (a_limbs[2] * b_limbs[0]);
2837 const uint512_t c2_inner = (a_limbs[0] * b_limbs[2]);
2838 const uint512_t d0_inner = (a_limbs[3] * b_limbs[0]);
2839 const uint512_t d1_inner = (a_limbs[2] * b_limbs[1]);
2840 const uint512_t d2_inner = (a_limbs[1] * b_limbs[2]);
2841 const uint512_t d3_inner = (a_limbs[0] * b_limbs[3]);
2842
2843 const uint512_t r0_inner = (a_limbs[0] * b_limbs[0]); // c0 := a0 * b0
2844 const uint512_t r1_inner = b0_inner + b1_inner; // c1 := a1 * b0 + a0 * b1
2845 const uint512_t r2_inner = c0_inner + c1_inner + c2_inner; // c2 := a2 * b0 + a1 * b1 + a0 * b2
2846 const uint512_t r3_inner = d0_inner + d1_inner + d2_inner + d3_inner; // c3 := a3 * b0 + a2 * b1 + a1 * b2 + a0 * b3
2847 const uint512_t lo_val = r0_inner + (r1_inner << NUM_LIMB_BITS); // lo := c0 + c1 * 2^b
2848 const uint512_t hi_val = r2_inner + (r3_inner << NUM_LIMB_BITS); // hi := c2 + c3 * 2^b
2849 return std::pair<uint512_t, uint512_t>(lo_val, hi_val);
2850}
2851
2861template <typename Builder, typename T>
2863 const uint32_t limb_idx,
2864 const size_t num_limb_bits)
2865{
2866 BB_ASSERT_LT(uint256_t(ctx->get_variable(limb_idx)), (uint256_t(1) << num_limb_bits));
2867 constexpr bb::fr LIMB_MASK = (uint256_t(1) << NUM_LIMB_BITS) - 1;
2868 const uint256_t value = ctx->get_variable(limb_idx);
2869 const uint256_t low = value & LIMB_MASK;
2870 const uint256_t hi = value >> NUM_LIMB_BITS;
2871 BB_ASSERT_EQ(low + (hi << NUM_LIMB_BITS), value);
2872
2873 const uint32_t low_idx = ctx->add_variable(bb::fr(low));
2874 const uint32_t hi_idx = ctx->add_variable(bb::fr(hi));
2875
2876 BB_ASSERT_GT(num_limb_bits, NUM_LIMB_BITS);
2877 const size_t lo_bits = NUM_LIMB_BITS;
2878 const size_t hi_bits = num_limb_bits - NUM_LIMB_BITS;
2879 ctx->range_constrain_two_limbs(
2880 low_idx, hi_idx, lo_bits, hi_bits, "decompose_non_native_field_double_width_limb: limbs too large");
2881
2882 // Constrain: original == low + hi * 2^NUM_LIMB_BITS
2886 field_t<Builder>::evaluate_linear_identity(original, -lo_field, -hi_field * shift_1, field_t<Builder>(0));
2887
2888 return std::array<uint32_t, 2>{ low_idx, hi_idx };
2889}
2890
2891} // namespace bb::stdlib
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
#define BB_ASSERT_GT(left, right,...)
Definition assert.hpp:113
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:83
#define BB_ASSERT_LTE(left, right,...)
Definition assert.hpp:158
#define BB_ASSERT_LT(left, right,...)
Definition assert.hpp:143
constexpr uint256_t slice(uint64_t start, uint64_t end) const
constexpr uint64_t get_msb() const
constexpr uintx slice(const uint64_t start, const uint64_t end) const
Definition uintx.hpp:81
constexpr uint64_t get_msb() const
Definition uintx.hpp:68
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)
Evaluate a relation involving multiple multiplications and additions.
static bigfield conditional_assign(const bool_t< Builder > &predicate, const bigfield &lhs, const bigfield &rhs)
Definition bigfield.hpp:593
static bigfield msub_div(const std::vector< bigfield > &mul_left, const std::vector< bigfield > &mul_right, const bigfield &divisor, const std::vector< bigfield > &to_sub, bool enable_divisor_nz_check=true)
Builder * get_context() const
Definition bigfield.hpp:714
bigfield operator*(const bigfield &other) const
Evaluate a non-native field multiplication: (a * b = c mod p) where p == target_basis....
bigfield conditional_select(const bigfield &other, const bool_t< Builder > &predicate) const
Create an element which is equal to either this or other based on the predicate.
static bigfield div_check_denominator_nonzero(const std::vector< bigfield > &numerators, const bigfield &denominator)
static bigfield sum(const std::vector< bigfield > &terms)
Create constraints for summing these terms.
bigfield(const field_t< Builder > &low_bits, const field_t< Builder > &high_bits, const bool can_overflow=false, const size_t maximum_bitlength=0)
Constructs a new bigfield object from two field elements representing the low and high bits.
static void unsafe_evaluate_square_add(const bigfield &left, const std::vector< bigfield > &to_add, const bigfield &quotient, const bigfield &remainder)
Evaluate a square with several additions.
bigfield madd(const bigfield &to_mul, const std::vector< bigfield > &to_add) const
Compute a * b + ...to_add = c mod p.
bigfield conditional_negate(const bool_t< Builder > &predicate) const
static bigfield mult_madd(const std::vector< bigfield > &mul_left, const std::vector< bigfield > &mul_right, const std::vector< bigfield > &to_add, bool fix_remainder_to_zero=false)
void set_origin_tag(const bb::OriginTag &tag) const
Definition bigfield.hpp:716
uint512_t get_value() const
void assert_is_in_field(std::string const &msg="bigfield::assert_is_in_field") const
static bigfield internal_div(const std::vector< bigfield > &numerators, const bigfield &denominator, bool check_for_zero)
void assert_less_than(const uint256_t &upper_limit, std::string const &msg="bigfield::assert_less_than") const
bigfield add_to_lower_limb(const field_t< Builder > &other, const uint256_t &other_maximum_value) const
Add a field element to the lower limb. CAUTION (the element has to be constrained before using this f...
bigfield & operator=(const bigfield &other)
void convert_constant_to_fixed_witness(Builder *builder)
Definition bigfield.hpp:690
uint512_t get_maximum_value() const
std::array< uint256_t, NUM_LIMBS > get_binary_basis_limb_maximums()
Get the maximum values of the binary basis limbs.
static uint512_t compute_maximum_quotient_value(const std::vector< uint512_t > &as, const std::vector< uint512_t > &bs, const std::vector< uint512_t > &to_add)
Compute the maximum possible value of quotient of a*b+\sum(to_add)
bigfield sqradd(const std::vector< bigfield > &to_add) const
Square and add operator, computes a * a + ...to_add = c mod p.
bigfield add_two(const bigfield &add_a, const bigfield &add_b) const
Create constraints for summing three bigfield elements efficiently.
std::array< uint32_t, NUM_LIMBS > get_binary_basis_limb_witness_indices() const
Get the witness indices of the (normalized) binary basis limbs.
bb::OriginTag get_origin_tag() const
Definition bigfield.hpp:731
static bigfield from_witness(Builder *ctx, const bb::field< T > &input)
Definition bigfield.hpp:322
void reduction_check() const
Check if the bigfield element needs to be reduced.
void assert_zero_if(const bool_t< Builder > &predicate, std::string const &msg="bigfield::assert_zero_if failed") const
static constexpr uint256_t modulus
Definition bigfield.hpp:340
bool_t< Builder > is_less_than(const uint256_t &upper_limit, std::string const &msg="bigfield::is_less_than") const
static bigfield dual_madd(const bigfield &left_a, const bigfield &right_a, const bigfield &left_b, const bigfield &right_b, const std::vector< bigfield > &to_add)
bigfield sqr() const
Square operator, computes a * a = c mod p.
static void perform_reductions_for_mult_madd(std::vector< bigfield > &mul_left, std::vector< bigfield > &mul_right, const std::vector< bigfield > &to_add)
Performs individual reductions on the supplied elements as well as more complex reductions to prevent...
bool is_constant() const
Check if the bigfield is constant, i.e. its prime limb is constant.
Definition bigfield.hpp:628
static std::array< uint32_t, 2 > decompose_non_native_field_double_width_limb(Builder *ctx, const uint32_t limb_idx, const size_t num_limb_bits=(2 *NUM_LIMB_BITS))
Decompose a single witness into two limbs, range constrained to NUM_LIMB_BITS (68) and num_limb_bits ...
void reduce_mod_target_modulus() const
static std::pair< uint512_t, uint512_t > compute_quotient_remainder_values(const bigfield &a, const bigfield &b, const std::vector< bigfield > &to_add)
Compute the quotient and remainder values for dividing (a * b + (to_add[0] + ... + to_add[-1])) with ...
void unsafe_assert_less_than(const uint256_t &upper_limit, std::string const &msg="bigfield::unsafe_assert_less_than") const
Assert that the current bigfield is less than the given upper limit.
bigfield operator+(const bigfield &other) const
Adds two bigfield elements. Inputs are reduced to the modulus if necessary. Requires 4 gates if both ...
void assert_equal(const bigfield &other, std::string const &msg="bigfield::assert_equal") const
static bigfield create_from_u512_as_witness(Builder *ctx, const uint512_t &value, const bool can_overflow=false, const size_t maximum_bitlength=0)
Creates a bigfield element from a uint512_t. Bigfield element is constructed as a witness and not a c...
bigfield pow(const uint32_t exponent) const
Raise the bigfield element to the power of (out-of-circuit) exponent.
static std::pair< bool, size_t > get_quotient_reduction_info(const std::vector< uint512_t > &as_max, const std::vector< uint512_t > &bs_max, const std::vector< bigfield > &to_add, const std::vector< uint1024_t > &remainders_max={ DEFAULT_MAXIMUM_REMAINDER })
Check for 2 conditions (CRT modulus is overflown or the maximum quotient doesn't fit into range proof...
static bigfield unsafe_construct_from_limbs(const field_t< Builder > &a, const field_t< Builder > &b, const field_t< Builder > &c, const field_t< Builder > &d, const bool can_overflow=false)
Construct a bigfield element from binary limbs that are already reduced.
Definition bigfield.hpp:174
void sanity_check() const
Perform a sanity check on a value that is about to interact with another value.
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)
Evaluate a multiply add identity with several added elements and several remainders.
field_t< Builder > prime_basis_limb
Represents a bigfield element in the prime basis: (a mod n) where n is the native modulus.
Definition bigfield.hpp:992
static bigfield div_without_denominator_check(const std::vector< bigfield > &numerators, const bigfield &denominator)
std::array< Limb, NUM_LIMBS > binary_basis_limbs
Represents a bigfield element in the binary basis. A bigfield element is represented as a combination...
Definition bigfield.hpp:983
bool_t< Builder > operator==(const bigfield &other) const
Validate whether two bigfield elements are equal to each other.
bigfield operator-() const
Negation operator, works by subtracting this from zero.
Definition bigfield.hpp:482
static std::pair< uint512_t, uint512_t > compute_partial_schoolbook_multiplication(const std::array< uint256_t, NUM_LIMBS > &a_limbs, const std::array< uint256_t, NUM_LIMBS > &b_limbs)
Compute the partial multiplication of two uint256_t arrays using schoolbook multiplication.
void self_reduce() const
Reduce the bigfield element modulo the target modulus.
void assert_is_not_equal(const bigfield &other, std::string const &msg="bigfield: prime limb diff is zero, but expected non-zero") const
bigfield operator/(const bigfield &other) const
Implements boolean logic in-circuit.
Definition bool.hpp:60
bool get_value() const
Definition bool.hpp:125
bool is_constant() const
Definition bool.hpp:127
void set_origin_tag(const OriginTag &new_tag) const
Definition bool.hpp:154
void unset_free_witness_tag()
Definition bool.hpp:157
Builder * context
Definition bool.hpp:168
OriginTag tag
Definition bool.hpp:179
OriginTag get_origin_tag() const
Definition bool.hpp:155
Represents a dynamic array of bytes in-circuit.
byte_array slice(size_t offset) const
Slice bytes from the byte array starting at offset. Does not add any constraints.
size_t size() const
Builder * get_context() const
bb::OriginTag get_origin_tag() const
void assert_is_zero(std::string const &msg="field_t::assert_is_zero") const
Enforce a copy constraint between *this and 0 stored at zero_idx of the Builder.
Definition field.cpp:689
void assert_equal(const field_t &rhs, std::string const &msg="field_t::assert_equal") const
Copy constraint: constrain that *this field is equal to rhs element.
Definition field.cpp:942
field_t madd(const field_t &to_mul, const field_t &to_add) const
Definition field.cpp:520
static field_t from_witness_index(Builder *ctx, uint32_t witness_index)
Definition field.cpp:67
bb::fr additive_constant
Definition field.hpp:94
static void evaluate_polynomial_identity(const field_t &a, const field_t &b, const field_t &c, const field_t &d, const std::string &msg="field_t::evaluate_polynomial_identity")
Given a, b, c, d, constrain a * b + c + d = 0 by creating a big_mul_gate.
Definition field.cpp:1137
static field_t accumulate(const std::vector< field_t > &input)
Efficiently compute the sum of vector entries. Using big_add_gate we reduce the number of gates neede...
Definition field.cpp:1180
static bool witness_indices_match(const field_t &a, const field_t &b)
Check if two field elements have the same witness index (for identity checks).
Definition field.hpp:545
void create_range_constraint(size_t num_bits, std::string const &msg="field_t::range_constraint") const
Let x = *this.normalize(), constrain x.v < 2^{num_bits}.
Definition field.cpp:921
Builder * context
Definition field.hpp:57
bb::fr multiplicative_constant
Definition field.hpp:95
static field_t conditional_assign_internal(const bool_t< Builder > &predicate, const field_t &lhs, const field_t &rhs)
If predicate == true then return lhs, else return rhs.
Definition field.cpp:895
bb::fr get_value() const
Given a := *this, compute its value given by a.v * a.mul + a.add.
Definition field.cpp:838
static void evaluate_linear_identity(const field_t &a, const field_t &b, const field_t &c, const field_t &d, const std::string &msg="field_t::evaluate_linear_identity")
Constrain a + b + c + d to be equal to 0.
Definition field.cpp:1101
bool is_constant() const
Definition field.hpp:455
void set_origin_tag(const OriginTag &new_tag) const
Definition field.hpp:358
uint32_t witness_index
Definition field.hpp:145
void assert_is_not_zero(std::string const &msg="field_t::assert_is_not_zero") const
Constrain *this to be non-zero by establishing that it has an inverse.
Definition field.cpp:720
uint32_t get_witness_index() const
Get the witness index of the current field element.
Definition field.hpp:532
StrictMock< MockContext > context
FF a
FF b
stdlib::field_t< Builder > field_ct
void add_values(TreeType &tree, const std::vector< NullifierLeafValue > &values)
uintx< uint256_t > uint512_t
Definition uintx.hpp:309
uintx< uint512_t > uint1024_t
Definition uintx.hpp:311
std::conditional_t< IsGoblinBigGroup< C, Fq, Fr, G >, element_goblin::goblin_element< C, goblin_field< C >, Fr, G >, element_default::element< C, Fq, Fr, G > > element
element wraps either element_default::element or element_goblin::goblin_element depending on parametr...
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
Univariate< Fr, domain_end > operator+(const Fr &ff, const Univariate< Fr, domain_end > &uv)
field< Bn254FrParams > fr
Definition fr.hpp:155
C slice(C const &container, size_t start)
Definition container.hpp:9
Inner sum(Cont< Inner, Args... > const &in)
Definition container.hpp:70
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::vector< Instruction > target
This file contains part of the logic for the Origin Tag mechanism that tracks the use of in-circuit p...
bb::VectorAffineElementPushSpan< BaseParams > lhs
bb::VectorAffineElementPushSpan< BaseParams > out
bb::VectorAffineElementPushSpan< BaseParams > rhs
static OriginTag constant()
constexpr field invert() const noexcept
Represents a single limb of a bigfield element, with its value and maximum value.
Definition bigfield.hpp:45
void throw_or_abort(std::string const &err)
VectorField result