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