HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
bigint.hpp
1// Copyright 2019 Pokitec
2// All rights reserved.
3
4#pragma once
5
6#include "TTauri/Foundation/strings.hpp"
7#include "TTauri/Foundation/math.hpp"
8#include "TTauri/Foundation/int_carry.hpp"
9#include <fmt/format.h>
10#include <type_traits>
11#include <ostream>
12
13namespace tt {
14
15//template<typename T, int N, bool SIGNED=false>
16//struct bigint;
17
18//template<typename T, int N, int O>
19//tt_force_inline bigint<T,N> bigint_reciprocal(bigint<T,O> const &divider);
20
21//template<typename T, int N, typename U>
22//constexpr bigint<T,N> bigint_reciprocal(U const &divider);
23
28template<typename T, int N>
29struct bigint {
30 static_assert(N >= 0, "bigint must have zero or more digits.");
31 static_assert(std::is_integral_v<T> && std::is_unsigned_v<T>, "bigint's digit must be unsigned integers.");
32
33 using digit_type = T;
34 static constexpr int nr_digits = N;
35 static constexpr int bits_per_digit = sizeof(digit_type) * 8;
36 static constexpr int nr_bits = nr_digits * bits_per_digit;
37
41
42 tt_force_inline bigint() noexcept = default;
43 tt_force_inline ~bigint() = default;
44 tt_force_inline bigint(bigint const &) noexcept = default;
45 tt_force_inline bigint &operator=(bigint const &) noexcept = default;
46 tt_force_inline bigint(bigint &&) noexcept = default;
47 tt_force_inline bigint &operator=(bigint &&) noexcept = default;
48
49 template<int R, std::enable_if_t<R < N, int> = 0>
50 tt_force_inline bigint(bigint<T,R> const &rhs) noexcept {
51 int i = 0;
52 for (; i != R; ++i) {
53 digits[i] = rhs.digits[i];
54 }
55 for (; i != N; ++i) {
56 digits[i] = 0;
57 }
58 }
59
60 template<int R, std::enable_if_t<R < N, int> = 0>
61 tt_force_inline bigint &operator=(bigint<T,R> const &rhs) noexcept {
62 int i = 0;
63 for (; i != R; ++i) {
64 digits[i] = rhs.digits[i];
65 }
66 for (; i != N; ++i) {
67 digits[i] = 0;
68 }
69 return *this;
70 }
71
72 template<typename R, std::enable_if_t<std::is_integral_v<R>, int> = 0>
73 tt_force_inline bigint(R value) noexcept {
74 digits[0] = static_cast<digit_type>(value);
75 for (auto i = 1; i < nr_digits; i++) {
76 digits[i] = 0;
77 }
78 }
79
80 template<typename R, std::enable_if_t<std::is_integral_v<R>, int> = 0>
81 tt_force_inline bigint &operator=(R value) noexcept {
82 digits[0] = value;
83 for (auto i = 1; i < nr_digits; i++) {
84 digits[i] = 0;
85 }
86 return *this;
87 }
88
89 tt_force_inline explicit bigint(std::string_view str, int base=10) noexcept :
90 bigint(0)
91 {
92 for (auto i = 0; i < str.size(); i++) {
93 auto nibble = char_to_nibble(str[i]);
94 (*this) *= base;
95 (*this) += nibble;
96 }
97 }
98
99 tt_force_inline explicit operator unsigned long long () const noexcept { return static_cast<unsigned long long>(digits[0]); }
100 tt_force_inline explicit operator signed long long () const noexcept { return static_cast<signed long long>(digits[0]); }
101 tt_force_inline explicit operator unsigned long () const noexcept { return static_cast<unsigned long>(digits[0]); }
102 tt_force_inline explicit operator signed long () const noexcept { return static_cast<signed long>(digits[0]); }
103 tt_force_inline explicit operator unsigned int () const noexcept { return static_cast<unsigned int>(digits[0]); }
104 tt_force_inline explicit operator signed int () const noexcept { return static_cast<signed int>(digits[0]); }
105 tt_force_inline explicit operator unsigned short () const noexcept { return static_cast<unsigned short>(digits[0]); }
106 tt_force_inline explicit operator signed short () const noexcept { return static_cast<signed short>(digits[0]); }
107 tt_force_inline explicit operator unsigned char () const noexcept { return static_cast<unsigned char>(digits[0]); }
108 tt_force_inline explicit operator signed char () const noexcept { return static_cast<signed char>(digits[0]); }
109 tt_force_inline explicit operator char () const noexcept { return static_cast<char>(digits[0]); }
110
111 tt_force_inline explicit operator bool () const noexcept {
112 for (ssize_t i = 0; i != N; ++i) {
113 if (digits[i] != 0) {
114 return true;
115 }
116 }
117 return false;
118 }
119
120 template<int O>
121 tt_force_inline explicit operator bigint<T,O> () const noexcept {
122 bigint<T,O> r;
123
124 for (auto i = 0; i < O; i++) {
125 r.digits[i] = i < N ? digits[i] : 0;
126 }
127 return r;
128 }
129
130 std::string string() const noexcept {
131 static auto oneOver10 = bigint_reciprocal(bigint<T,N*2>{10});
132
133 auto tmp = *this;
134
135 std::string r;
136
137 if (tmp == 0) {
138 r = "0";
139 } else {
140 while (tmp > 0) {
141 bigint remainder;
142 std::tie(tmp, remainder) = div(tmp, 10, oneOver10);
143 r += (static_cast<char>(remainder) + '0');
144 }
145 }
146
147 std::reverse(r.begin(), r.end());
148 return r;
149 }
150
151 std::string UUIDString() const noexcept {
152 static_assert(std::is_same_v<T,uint64_t> && N == 2, "UUIDString should only be called on a uuid compatible type");
153 return fmt::format("{:08x}-{:04x}-{:04x}-{:04x}-{:012x}",
154 static_cast<uint32_t>(digits[1] >> 32),
155 static_cast<uint16_t>(digits[1] >> 16),
156 static_cast<uint16_t>(digits[1]),
157 static_cast<uint16_t>(digits[0] >> 48),
158 digits[0] & 0x0000ffff'ffffffffULL
159 );
160 }
161
162 tt_force_inline digit_type get_bit(unsigned int count) const noexcept {
163 ttlet digit_count = count / bits_per_digit;
164 ttlet bit_count = count % bits_per_digit;
165
166 return (digits[digit_count] >> bit_count) & 1;
167 }
168
169 tt_force_inline void set_bit(unsigned int count, digit_type value = 1) noexcept {
170 ttlet digit_count = count / bits_per_digit;
171 ttlet bit_count = count % bits_per_digit;
172
173 digits[digit_count] |= (value << bit_count);
174 }
175
176 template<typename R, std::enable_if_t<std::is_integral_v<R>, int> = 0>
177 tt_force_inline bigint &operator<<=(R rhs) noexcept {
178 bigint_shift_left(*this, *this, static_cast<int>(rhs));
179 return *this;
180 }
181
182 template<typename R, std::enable_if_t<std::is_integral_v<R>, int> = 0>
183 tt_force_inline constexpr bigint &operator>>=(R rhs) noexcept {
184 bigint_shift_right(*this, *this, static_cast<int>(rhs));
185 return *this;
186 }
187
188 tt_force_inline bigint &operator*=(bigint const &rhs) noexcept {
189 auto r = bigint<T,N>{0};
190 bigint_multiply(r, *this, rhs);
191 *this = r;
192 return *this;
193 }
194
195 tt_force_inline bigint &operator+=(bigint const &rhs) noexcept {
196 bigint_add(*this, *this, rhs);
197 return *this;
198 }
199
200 tt_force_inline bigint &operator-=(bigint const &rhs) noexcept {
201 bigint_subtract(*this, *this, rhs);
202 return *this;
203 }
204
205 tt_force_inline bigint &operator&=(bigint const &rhs) noexcept {
206 bigint_and(*this, *this, rhs);
207 return *this;
208 }
209
210 tt_force_inline bigint &operator|=(bigint const &rhs) noexcept {
211 bigint_or(*this, *this, rhs);
212 return *this;
213 }
214
215 tt_force_inline bigint &operator^=(bigint const &rhs) noexcept {
216 bigint_xor(*this, *this, rhs);
217 return *this;
218 }
219
220 tt_force_inline bigint crc(bigint const &rhs) noexcept {
221 return bigint_crc(*this, rhs);
222 }
223
224 static tt_force_inline bigint fromBigEndian(uint8_t const *data) noexcept {
225 auto r = bigint{};
226 for (int i = N - 1; i >= 0; i--) {
227 digit_type d = 0;
228 for (ssize_t j = 0; j < sizeof(digit_type); j++) {
229 d <<= 8;
230 d |= *(data++);
231 }
232 r.digits[i] = d;
233 }
234 return r;
235 }
236 static tt_force_inline bigint fromLittleEndian(uint8_t const *data) noexcept {
237 auto r = bigint{};
238 for (int i = 0; i < N; i++) {
239 digit_type d = 0;
240 for (ssize_t j = 0; j < sizeof(digit_type); j++) {
241 d |= static_cast<digit_type>(*(data++)) << (j*8);
242 }
243 r.digits[i] = d;
244 }
245 return r;
246 }
247
248 static tt_force_inline bigint fromBigEndian(void const *data) noexcept {
249 return fromBigEndian(static_cast<uint8_t const *>(data));
250 }
251
252 static tt_force_inline bigint fromLittleEndian(void const *data) noexcept {
253 return fromLittleEndian(static_cast<uint8_t const *>(data));
254 }
255
256 [[nodiscard]] tt_force_inline friend int bigint_compare(bigint const &lhs, bigint const &rhs) noexcept
257 {
258 for (int i = N - 1; i >= 0; --i) {
259 auto lhs_digit = lhs.digits[i];
260 auto rhs_digit = rhs.digits[i];
261 if (lhs_digit != rhs_digit) {
262 return lhs_digit < rhs_digit ? -1 : 1;
263 }
264 }
265 return 0;
266 }
267
268 tt_force_inline friend void bigint_invert(bigint &o, bigint const &rhs) noexcept
269 {
270 for (auto i = 0; i < N; i++) {
271 o.digits[i] = ~rhs.digits[i];
272 }
273 }
274
275 tt_force_inline friend void bigint_add(bigint &o, bigint const &lhs, bigint const &rhs, T carry=0) noexcept
276 {
277 for (auto i = 0; i < N; i++) {
278 std::tie(o.digits[i], carry) = add_carry(lhs.digits[i], rhs.digits[i], carry);
279 }
280 }
281
282 tt_force_inline friend void bigint_multiply(bigint &o, bigint const &lhs, bigint const &rhs) noexcept
283 {
284 for (auto rhs_index = 0; rhs_index < N; rhs_index++) {
285 ttlet rhs_digit = rhs.digits[rhs_index];
286
287 T carry = 0;
288 for (auto lhs_index = 0; (lhs_index + rhs_index) < N; lhs_index++) {
289 ttlet lhs_digit = lhs.digits[lhs_index];
290
291 T result;
292 T accumulator = o.digits[rhs_index + lhs_index];
293 std::tie(result, carry) = multiply_carry(lhs_digit, rhs_digit, carry, accumulator);
294 o.digits[rhs_index + lhs_index] = result;
295 }
296 }
297 }
298
299 tt_force_inline friend void bigint_div(bigint &quotient, bigint &remainder, bigint const &lhs, bigint const &rhs) noexcept
300 {
301 for (auto i = nr_bits - 1; i >= 0; i--) {
302 remainder <<= 1;
303 remainder |= lhs.get_bit(i);
304 if (remainder >= rhs) {
305 remainder -= rhs;
306 quotient.set_bit(i);
307 }
308 }
309 }
310
311 tt_force_inline friend void bigint_div(bigint &r_quotient, bigint &r_remainder, bigint const &lhs, bigint const &rhs, bigint<T,2*N> const &rhs_reciprocal) noexcept
312 {
313 auto quotient = bigint<T,3*N>{0};
314 bigint_multiply(quotient, lhs, rhs_reciprocal);
315
316 quotient >>= (2*nr_bits);
317
318 auto product = bigint<T,3*N>{0};
319 bigint_multiply(product, quotient, rhs);
320
321 tt_assume(product <= lhs);
322 auto remainder = lhs - product;
323
324 int retry = 0;
325 while (remainder >= rhs) {
326 if (retry++ > 3) {
327 tt_assert(false);
328 bigint_div(r_quotient, r_remainder, lhs, rhs);
329 }
330
331 remainder -= rhs;
332 quotient += 1;
333 }
334 r_quotient = static_cast<bigint>(quotient);
335 r_remainder = static_cast<bigint>(remainder);
336 }
337
341 [[nodiscard]] tt_force_inline friend int bigint_bsr(bigint const &rhs) noexcept
342 {
343 for (auto i = N - 1; i >= 0; i--) {
344 auto tmp = bsr(rhs.digits[i]);
345 if (tmp >= 0) {
346 return (i * bits_per_digit) + tmp;
347 }
348 }
349 return -1;
350 }
351
357 [[nodiscard]] tt_force_inline friend bigint bigint_crc(bigint const &lhs, bigint const &rhs) noexcept
358 {
359 ttlet polynomialOrder = bigint_bsr(rhs);
360 tt_assert(polynomialOrder >= 0);
361
362 auto tmp = static_cast<bigint<T,2*N>>(lhs) << polynomialOrder;
363 auto rhs_ = static_cast<bigint<T,2*N>>(rhs);
364
365 auto tmp_highest_bit = bigint_bsr(tmp);
366 while (tmp_highest_bit >= polynomialOrder) {
367 ttlet divident = rhs_ << (tmp_highest_bit - polynomialOrder);
368
369 tmp ^= divident;
370 tmp_highest_bit = bigint_bsr(tmp);
371 }
372
373 return static_cast<bigint>(tmp);
374 }
375
383 [[nodiscard]] tt_force_inline friend bigint bigint_reciprocal(bigint const &rhs) {
384 auto r = bigint<T,N+1>(0);
385 r.digits[N] = 1;
386 return static_cast<bigint>(r / rhs);
387 }
388
389
390 tt_force_inline friend void bigint_and(bigint &o, bigint const &lhs, bigint const &rhs) noexcept
391 {
392 for (auto i = 0; i != N; ++i) {
393 o.digits[i] = lhs.digits[i] & rhs.digits[i];
394 }
395 }
396
397 tt_force_inline friend void bigint_or(bigint &o, bigint const &lhs, bigint const &rhs) noexcept
398 {
399 for (auto i = 0; i != N; ++i) {
400 o.digits[i] = lhs.digits[i] | rhs.digits[i];
401 }
402 }
403
404 tt_force_inline friend void bigint_xor(bigint &o, bigint const &lhs, bigint const &rhs) noexcept
405 {
406 for (auto i = 0; i != N; ++i) {
407 o.digits[i] = lhs.digits[i] ^ rhs.digits[i];
408 }
409 }
410
411 tt_force_inline friend void bigint_subtract(bigint &o, bigint const &lhs, bigint const &rhs) noexcept
412 {
413 bigint rhs_;
414 bigint_invert(rhs_, rhs);
415 bigint_add(o, lhs, rhs_, T{1});
416 }
417
418 tt_force_inline friend void bigint_shift_left(bigint &o, bigint const &lhs, int count) noexcept
419 {
420 ttlet digit_count = count / bits_per_digit;
421 ttlet bit_count = count % bits_per_digit;
422
423 if (&o != &lhs || digit_count > 0) {
424 for (int i = N - 1; i >= digit_count; --i) {
425 o.digits[i] = lhs.digits[i - digit_count];
426 }
427 for (int i = digit_count - 1; i >= 0; --i) {
428 o.digits[i] = 0;
429 }
430 }
431
432 if (bit_count > 0) {
433 T carry = 0;
434 for (auto i = 0; i != N; ++i) {
435 std::tie(o.digits[i], carry) = shift_left_carry(o.digits[i], bit_count, carry);
436 }
437 }
438 }
439
440 tt_force_inline friend void bigint_shift_right(bigint &o, bigint const &lhs, int count) noexcept
441 {
442 ttlet digit_count = count / bits_per_digit;
443 ttlet bit_count = count % bits_per_digit;
444
445 if (&o != &lhs || digit_count > 0) {
446 auto i = 0;
447 for (; i != (N - digit_count); ++i) {
448 o.digits[i] = lhs.digits[i + digit_count];
449 }
450 for (; i != N; ++i) {
451 o.digits[i] = 0;
452 }
453 }
454
455 if (bit_count > 0) {
456 T carry = 0;
457 for (auto i = N - 1; i >= 0; --i) {
458 std::tie(o.digits[i], carry) = shift_right_carry(o.digits[i], bit_count, carry);
459 }
460 }
461 }
462
463 [[nodiscard]] tt_force_inline friend bool operator==(bigint const &lhs, bigint const &rhs) noexcept
464 {
465 return bigint_compare(lhs, rhs) == 0;
466 }
467
468 [[nodiscard]] tt_force_inline friend bool operator<(bigint const &lhs, bigint const &rhs) noexcept
469 {
470 return bigint_compare(lhs, rhs) < 0;
471 }
472
473 [[nodiscard]] tt_force_inline friend bool operator>(bigint const &lhs, bigint const &rhs) noexcept
474 {
475 return bigint_compare(lhs, rhs) > 0;
476 }
477
478 [[nodiscard]] tt_force_inline friend bool operator!=(bigint const &lhs, bigint const &rhs) noexcept
479 {
480 return bigint_compare(lhs, rhs) != 0;
481 }
482
483 [[nodiscard]] tt_force_inline friend bool operator>=(bigint const &lhs, bigint const &rhs) noexcept
484 {
485 return bigint_compare(lhs, rhs) >= 0;
486 }
487
488 [[nodiscard]] tt_force_inline friend bool operator<=(bigint const &lhs, bigint const &rhs) noexcept
489 {
490 return bigint_compare(lhs, rhs) <= 0;
491 }
492
493 template<typename R, std::enable_if_t<std::is_integral_v<R>,int> = 0>
494 [[nodiscard]] tt_force_inline friend auto operator<<(bigint const &lhs, R const &rhs) noexcept {
495 bigint o;
496 bigint_shift_left(o, lhs, static_cast<int>(rhs));
497 return o;
498 }
499
500 template<typename R, std::enable_if_t<std::is_integral_v<R>,int> = 0>
501 [[nodiscard]] tt_force_inline friend auto operator>>(bigint const &lhs, R const &rhs) noexcept {
502 bigint o;
503 bigint_shift_right(o, lhs, static_cast<int>(rhs));
504 return o;
505 }
506
507 [[nodiscard]] tt_force_inline friend auto operator*(bigint const &lhs, bigint const &rhs) noexcept
508 {
509 auto o = bigint{0};
510 bigint_multiply(o, lhs, rhs);
511 return o;
512 }
513
514 [[nodiscard]] tt_force_inline friend auto operator+(bigint const &lhs, bigint const &rhs) noexcept
515 {
516 auto o = bigint{0};
517 bigint_add(o, lhs, rhs);
518 return o;
519 }
520
521 [[nodiscard]] tt_force_inline friend auto operator-(bigint const &lhs, bigint const &rhs) noexcept
522 {
523 auto o = bigint{0};
524 bigint_subtract(o, lhs, rhs);
525 return o;
526 }
527
528 [[nodiscard]] tt_force_inline friend auto operator~(bigint const &rhs) noexcept
529 {
530 bigint o;
531 bigint_invert(o, rhs);
532 return o;
533 }
534
535 [[nodiscard]] tt_force_inline friend auto operator|(bigint const &lhs, bigint const &rhs) noexcept
536 {
537 auto o = bigint{0};
538 bigint_or(o, lhs, rhs);
539 return o;
540 }
541
542 [[nodiscard]] tt_force_inline friend auto operator&(bigint const &lhs, bigint const &rhs) noexcept
543 {
544 auto o = bigint{0};
545 bigint_and(o, lhs, rhs);
546 return o;
547 }
548
549 [[nodiscard]] tt_force_inline friend auto operator^(bigint const &lhs, bigint const &rhs) noexcept
550 {
551 auto o = bigint{0};
552 bigint_xor(o, lhs, rhs);
553 return o;
554 }
555
556 [[nodiscard]] tt_force_inline friend auto div(bigint const &lhs, bigint const &rhs) noexcept
557 {
558 auto quotient = bigint{0};
559 auto remainder = bigint{0};
560
561 bigint_div(quotient, remainder, lhs, rhs);
562 return std::pair{ quotient, remainder };
563 }
564
565 [[nodiscard]] tt_force_inline friend auto div(bigint const &lhs, bigint const &rhs, bigint<T,2*N> const &rhs_reciprocal) noexcept
566 {
567 auto quotient = bigint{0};
568 auto remainder = bigint{0};
569
570 bigint_div(quotient, remainder, lhs, rhs, rhs_reciprocal);
571 return std::pair{ quotient, remainder };
572 }
573
574
575 [[nodiscard]] tt_force_inline friend auto operator/(bigint const &lhs, bigint const &rhs) noexcept
576 {
577 auto quotient = bigint{0};
578 auto remainder = bigint{0};
579
580 bigint_div(quotient, remainder, lhs, rhs);
581 return quotient;
582 }
583
584 [[nodiscard]] tt_force_inline friend auto operator%(bigint const &lhs, bigint const &rhs) noexcept
585 {
586 auto quotient = bigint{0};
587 auto remainder = bigint{0};
588
589 bigint_div(quotient, remainder, lhs, rhs);
590 return remainder;
591 }
592
593 friend std::ostream &operator<<(std::ostream &lhs, bigint const &rhs) {
594 return lhs << rhs.string();
595 }
596};
597
598using ubig128 = bigint<uint64_t,2>;
599using uuid = bigint<uint64_t,2>;
600
601}
STL namespace.
Definition bigint.hpp:29
std::array< digit_type, nr_digits > digits
Definition bigint.hpp:40
T begin(T... args)
T count(T... args)
T div(T... args)
T end(T... args)
T operator>(T... args)
T remainder(T... args)
T reverse(T... args)
T tie(T... args)