Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
uint128_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#ifdef __i386__
8#pragma once
9#include "../bitop/get_msb.hpp"
10#include "./uint128.hpp"
12namespace bb::numeric {
13
14constexpr std::pair<uint32_t, uint32_t> uint128_t::mul_wide(const uint32_t a, const uint32_t b)
15{
16 const uint32_t a_lo = a & 0xffffULL;
17 const uint32_t a_hi = a >> 16ULL;
18 const uint32_t b_lo = b & 0xffffULL;
19 const uint32_t b_hi = b >> 16ULL;
20
21 const uint32_t lo_lo = a_lo * b_lo;
22 const uint32_t hi_lo = a_hi * b_lo;
23 const uint32_t lo_hi = a_lo * b_hi;
24 const uint32_t hi_hi = a_hi * b_hi;
25
26 const uint32_t cross = (lo_lo >> 16) + (hi_lo & 0xffffULL) + lo_hi;
27
28 return { (cross << 16ULL) | (lo_lo & 0xffffULL), (hi_lo >> 16ULL) + (cross >> 16ULL) + hi_hi };
29}
30
31// compute a + b + carry, returning the carry
32constexpr std::pair<uint32_t, uint32_t> uint128_t::addc(const uint32_t a, const uint32_t b, const uint32_t carry_in)
33{
34 const uint32_t sum = a + b;
35 const auto carry_temp = static_cast<uint32_t>(sum < a);
36 const uint32_t r = sum + carry_in;
37 const uint32_t carry_out = carry_temp + static_cast<unsigned int>(r < carry_in);
38 return { r, carry_out };
39}
40
41constexpr uint32_t uint128_t::addc_discard_hi(const uint32_t a, const uint32_t b, const uint32_t carry_in)
42{
43 return a + b + carry_in;
44}
45
46constexpr std::pair<uint32_t, uint32_t> uint128_t::sbb(const uint32_t a, const uint32_t b, const uint32_t borrow_in)
47{
48 const uint32_t t_1 = a - (borrow_in >> 31ULL);
49 const auto borrow_temp_1 = static_cast<uint32_t>(t_1 > a);
50 const uint32_t t_2 = t_1 - b;
51 const auto borrow_temp_2 = static_cast<uint32_t>(t_2 > t_1);
52
53 return { t_2, 0ULL - (borrow_temp_1 | borrow_temp_2) };
54}
55
56constexpr uint32_t uint128_t::sbb_discard_hi(const uint32_t a, const uint32_t b, const uint32_t borrow_in)
57{
58 return a - b - (borrow_in >> 31ULL);
59}
60
61// {r, carry_out} = a + carry_in + b * c
62constexpr std::pair<uint32_t, uint32_t> uint128_t::mac(const uint32_t a,
63 const uint32_t b,
64 const uint32_t c,
65 const uint32_t carry_in)
66{
67 std::pair<uint32_t, uint32_t> result = mul_wide(b, c);
68 result.first += a;
69 const auto overflow_c = static_cast<uint32_t>(result.first < a);
70 result.first += carry_in;
71 const auto overflow_carry = static_cast<uint32_t>(result.first < carry_in);
72 result.second += (overflow_c + overflow_carry);
73 return result;
74}
75
76constexpr uint32_t uint128_t::mac_discard_hi(const uint32_t a,
77 const uint32_t b,
78 const uint32_t c,
79 const uint32_t carry_in)
80{
81 return (b * c + a + carry_in);
82}
83
84constexpr std::pair<uint128_t, uint128_t> uint128_t::divmod(const uint128_t& b) const
85{
86 if (*this == 0 || b == 0) {
87 return { 0, 0 };
88 }
89 if (b == 1) {
90 return { *this, 0 };
91 }
92 if (*this == b) {
93 return { 1, 0 };
94 }
95 if (b > *this) {
96 return { 0, *this };
97 }
98
99 uint128_t quotient = 0;
100 uint128_t remainder = *this;
101
102 uint64_t bit_difference = get_msb() - b.get_msb();
103
104 uint128_t divisor = b << bit_difference;
105 uint128_t accumulator = uint128_t(1) << bit_difference;
106
107 // if the divisor is bigger than the remainder, a and b have the same bit length
108 if (divisor > remainder) {
109 divisor >>= 1;
110 accumulator >>= 1;
111 }
112
113 // while the remainder is bigger than our original divisor, we can subtract multiples of b from the remainder,
114 // and add to the quotient
115 while (remainder >= b) {
116
117 // we've shunted 'divisor' up to have the same bit length as our remainder.
118 // If remainder >= divisor, then a is at least '1 << bit_difference' multiples of b
119 if (remainder >= divisor) {
120 remainder -= divisor;
121 // we can use OR here instead of +, as
122 // accumulator is always a nice power of two
123 quotient |= accumulator;
124 }
125 divisor >>= 1;
126 accumulator >>= 1;
127 }
128
129 return { quotient, remainder };
130}
131
132constexpr std::pair<uint128_t, uint128_t> uint128_t::mul_extended(const uint128_t& other) const
133{
134 const auto [r0, t0] = mul_wide(data[0], other.data[0]);
135 const auto [q0, t1] = mac(t0, data[0], other.data[1], 0);
136 const auto [q1, t2] = mac(t1, data[0], other.data[2], 0);
137 const auto [q2, z0] = mac(t2, data[0], other.data[3], 0);
138
139 const auto [r1, t3] = mac(q0, data[1], other.data[0], 0);
140 const auto [q3, t4] = mac(q1, data[1], other.data[1], t3);
141 const auto [q4, t5] = mac(q2, data[1], other.data[2], t4);
142 const auto [q5, z1] = mac(z0, data[1], other.data[3], t5);
143
144 const auto [r2, t6] = mac(q3, data[2], other.data[0], 0);
145 const auto [q6, t7] = mac(q4, data[2], other.data[1], t6);
146 const auto [q7, t8] = mac(q5, data[2], other.data[2], t7);
147 const auto [q8, z2] = mac(z1, data[2], other.data[3], t8);
148
149 const auto [r3, t9] = mac(q6, data[3], other.data[0], 0);
150 const auto [r4, t10] = mac(q7, data[3], other.data[1], t9);
151 const auto [r5, t11] = mac(q8, data[3], other.data[2], t10);
152 const auto [r6, r7] = mac(z2, data[3], other.data[3], t11);
153
154 uint128_t lo(r0, r1, r2, r3);
155 uint128_t hi(r4, r5, r6, r7);
156 return { lo, hi };
157}
158
164constexpr uint128_t uint128_t::slice(const uint64_t start, const uint64_t end) const
165{
166 const uint64_t range = end - start;
167 const uint128_t mask = (range == 128) ? -uint128_t(1) : (uint128_t(1) << range) - 1;
168 return ((*this) >> start) & mask;
169}
170
171constexpr uint128_t uint128_t::pow(const uint128_t& exponent) const
172{
173 uint128_t accumulator{ data[0], data[1], data[2], data[3] };
174 uint128_t to_mul{ data[0], data[1], data[2], data[3] };
175 const uint64_t maximum_set_bit = exponent.get_msb();
176
177 for (int i = static_cast<int>(maximum_set_bit) - 1; i >= 0; --i) {
178 accumulator *= accumulator;
179 if (exponent.get_bit(static_cast<uint64_t>(i))) {
180 accumulator *= to_mul;
181 }
182 }
183 if (exponent == uint128_t(0)) {
184 accumulator = uint128_t(1);
185 } else if (*this == uint128_t(0)) {
186 accumulator = uint128_t(0);
187 }
188 return accumulator;
189}
190
191constexpr bool uint128_t::get_bit(const uint64_t bit_index) const
192{
193 ASSERT_IN_CONSTEXPR(bit_index < 128);
194 if (bit_index > 127) {
195 return false;
196 }
197 const auto idx = static_cast<size_t>(bit_index >> 5);
198 const size_t shift = bit_index & 31;
199 return static_cast<bool>((data[idx] >> shift) & 1);
200}
201
202constexpr uint64_t uint128_t::get_msb() const
203{
204 uint64_t idx = numeric::get_msb64(data[3]);
205 idx = (idx == 0 && data[3] == 0) ? numeric::get_msb64(data[2]) : idx + 32;
206 idx = (idx == 0 && data[2] == 0) ? numeric::get_msb64(data[1]) : idx + 32;
207 idx = (idx == 0 && data[1] == 0) ? numeric::get_msb64(data[0]) : idx + 32;
208 return idx;
209}
210
211constexpr uint128_t uint128_t::operator+(const uint128_t& other) const
212{
213 const auto [r0, t0] = addc(data[0], other.data[0], 0);
214 const auto [r1, t1] = addc(data[1], other.data[1], t0);
215 const auto [r2, t2] = addc(data[2], other.data[2], t1);
216 const auto r3 = addc_discard_hi(data[3], other.data[3], t2);
217 return { r0, r1, r2, r3 };
218};
219
220constexpr uint128_t uint128_t::operator-(const uint128_t& other) const
221{
222
223 const auto [r0, t0] = sbb(data[0], other.data[0], 0);
224 const auto [r1, t1] = sbb(data[1], other.data[1], t0);
225 const auto [r2, t2] = sbb(data[2], other.data[2], t1);
226 const auto r3 = sbb_discard_hi(data[3], other.data[3], t2);
227 return { r0, r1, r2, r3 };
228}
229
230constexpr uint128_t uint128_t::operator-() const
231{
232 return uint128_t(0) - *this;
233}
234
235constexpr uint128_t uint128_t::operator*(const uint128_t& other) const
236{
237 const auto [r0, t0] = mac(0, data[0], other.data[0], 0ULL);
238 const auto [q0, t1] = mac(0, data[0], other.data[1], t0);
239 const auto [q1, t2] = mac(0, data[0], other.data[2], t1);
240 const auto q2 = mac_discard_hi(0, data[0], other.data[3], t2);
241
242 const auto [r1, t3] = mac(q0, data[1], other.data[0], 0ULL);
243 const auto [q3, t4] = mac(q1, data[1], other.data[1], t3);
244 const auto q4 = mac_discard_hi(q2, data[1], other.data[2], t4);
245
246 const auto [r2, t5] = mac(q3, data[2], other.data[0], 0ULL);
247 const auto q5 = mac_discard_hi(q4, data[2], other.data[1], t5);
248
249 const auto r3 = mac_discard_hi(q5, data[3], other.data[0], 0ULL);
250
251 return { r0, r1, r2, r3 };
252}
253
254constexpr uint128_t uint128_t::operator/(const uint128_t& other) const
255{
256 return divmod(other).first;
257}
258
259constexpr uint128_t uint128_t::operator%(const uint128_t& other) const
260{
261 return divmod(other).second;
262}
263
264constexpr uint128_t uint128_t::operator&(const uint128_t& other) const
265{
266 return { data[0] & other.data[0], data[1] & other.data[1], data[2] & other.data[2], data[3] & other.data[3] };
267}
268
269constexpr uint128_t uint128_t::operator^(const uint128_t& other) const
270{
271 return { data[0] ^ other.data[0], data[1] ^ other.data[1], data[2] ^ other.data[2], data[3] ^ other.data[3] };
272}
273
274constexpr uint128_t uint128_t::operator|(const uint128_t& other) const
275{
276 return { data[0] | other.data[0], data[1] | other.data[1], data[2] | other.data[2], data[3] | other.data[3] };
277}
278
279constexpr uint128_t uint128_t::operator~() const
280{
281 return { ~data[0], ~data[1], ~data[2], ~data[3] };
282}
283
284constexpr bool uint128_t::operator==(const uint128_t& other) const
285{
286 return data[0] == other.data[0] && data[1] == other.data[1] && data[2] == other.data[2] && data[3] == other.data[3];
287}
288
289constexpr bool uint128_t::operator!=(const uint128_t& other) const
290{
291 return !(*this == other);
292}
293
294constexpr bool uint128_t::operator!() const
295{
296 return *this == uint128_t(0ULL);
297}
298
299constexpr bool uint128_t::operator>(const uint128_t& other) const
300{
301 bool t0 = data[3] > other.data[3];
302 bool t1 = data[3] == other.data[3] && data[2] > other.data[2];
303 bool t2 = data[3] == other.data[3] && data[2] == other.data[2] && data[1] > other.data[1];
304 bool t3 =
305 data[3] == other.data[3] && data[2] == other.data[2] && data[1] == other.data[1] && data[0] > other.data[0];
306 return t0 || t1 || t2 || t3;
307}
308
309constexpr bool uint128_t::operator>=(const uint128_t& other) const
310{
311 return (*this > other) || (*this == other);
312}
313
314constexpr bool uint128_t::operator<(const uint128_t& other) const
315{
316 return other > *this;
317}
318
319constexpr bool uint128_t::operator<=(const uint128_t& other) const
320{
321 return (*this < other) || (*this == other);
322}
323
324constexpr uint128_t uint128_t::operator>>(const uint128_t& other) const
325{
326 uint32_t total_shift = other.data[0];
327
328 if (total_shift >= 128 || (other.data[1] != 0U) || (other.data[2] != 0U) || (other.data[3] != 0U)) {
329 return 0;
330 }
331
332 if (total_shift == 0) {
333 return *this;
334 }
335
336 uint32_t num_shifted_limbs = total_shift >> 5ULL;
337 uint32_t limb_shift = total_shift & 31ULL;
338
339 std::array<uint32_t, 4> shifted_limbs = { 0, 0, 0, 0 };
340
341 if (limb_shift == 0) {
342 shifted_limbs[0] = data[0];
343 shifted_limbs[1] = data[1];
344 shifted_limbs[2] = data[2];
345 shifted_limbs[3] = data[3];
346 } else {
347 uint32_t remainder_shift = 32ULL - limb_shift;
348
349 shifted_limbs[3] = data[3] >> limb_shift;
350
351 uint32_t remainder = (data[3]) << remainder_shift;
352
353 shifted_limbs[2] = (data[2] >> limb_shift) + remainder;
354
355 remainder = (data[2]) << remainder_shift;
356
357 shifted_limbs[1] = (data[1] >> limb_shift) + remainder;
358
359 remainder = (data[1]) << remainder_shift;
360
361 shifted_limbs[0] = (data[0] >> limb_shift) + remainder;
362 }
363 uint128_t result(0);
364
365 for (size_t i = 0; i < 4 - num_shifted_limbs; ++i) {
366 result.data[i] = shifted_limbs[static_cast<size_t>(i + num_shifted_limbs)];
367 }
368
369 return result;
370}
371
372constexpr uint128_t uint128_t::operator<<(const uint128_t& other) const
373{
374 uint32_t total_shift = other.data[0];
375
376 if (total_shift >= 128 || (other.data[1] != 0U) || (other.data[2] != 0U) || (other.data[3] != 0U)) {
377 return 0;
378 }
379
380 if (total_shift == 0) {
381 return *this;
382 }
383 uint32_t num_shifted_limbs = total_shift >> 5ULL;
384 uint32_t limb_shift = total_shift & 31ULL;
385
386 std::array<uint32_t, 4> shifted_limbs{ 0, 0, 0, 0 };
387
388 if (limb_shift == 0) {
389 shifted_limbs[0] = data[0];
390 shifted_limbs[1] = data[1];
391 shifted_limbs[2] = data[2];
392 shifted_limbs[3] = data[3];
393 } else {
394 uint32_t remainder_shift = 32ULL - limb_shift;
395
396 shifted_limbs[0] = data[0] << limb_shift;
397
398 uint32_t remainder = data[0] >> remainder_shift;
399
400 shifted_limbs[1] = (data[1] << limb_shift) + remainder;
401
402 remainder = data[1] >> remainder_shift;
403
404 shifted_limbs[2] = (data[2] << limb_shift) + remainder;
405
406 remainder = data[2] >> remainder_shift;
407
408 shifted_limbs[3] = (data[3] << limb_shift) + remainder;
409 }
410 uint128_t result(0);
411
412 for (size_t i = 0; i < 4 - num_shifted_limbs; ++i) {
413 result.data[static_cast<size_t>(i + num_shifted_limbs)] = shifted_limbs[i];
414 }
415
416 return result;
417}
418
419} // namespace bb::numeric
420#endif
#define ASSERT_IN_CONSTEXPR(expression,...)
Definition assert.hpp:40
const std::vector< FF > data
FF a
FF b
constexpr uint64_t get_msb64(const uint64_t in)
Definition get_msb.hpp:30
constexpr T get_msb(const T in)
Definition get_msb.hpp:47
Inner sum(Cont< Inner, Args... > const &in)
Definition container.hpp:70
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
unsigned __int128 uint128_t
Definition serialize.hpp:44