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