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