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#include <concepts>
15
16namespace hi::inline v1 {
17
22template<std::unsigned_integral DigitType, std::size_t NumDigits, bool IsSigned>
23struct bigint {
24 using digit_type = DigitType;
25 using signed_digit_type = std::make_signed_t<digit_type>;
26 static constexpr auto num_digits = NumDigits;
27 static constexpr auto is_signed = IsSigned;
28 static constexpr auto bits_per_digit = sizeof(digit_type) * CHAR_BIT;
29
30 static constexpr digit_type zero_digit = 0;
31 static constexpr digit_type min1_digit = static_cast<digit_type>(signed_digit_type{-1});
32
35 digit_type digits[num_digits];
36
39 constexpr bigint() noexcept
40 {
41 for (std::size_t i = 0; i != num_digits; ++i) {
42 digits[i] = zero_digit;
43 }
44 }
45
46 constexpr bigint(bigint const &) noexcept = default;
47 constexpr bigint &operator=(bigint const &) noexcept = default;
48 constexpr bigint(bigint &&) noexcept = default;
49 constexpr bigint &operator=(bigint &&) noexcept = default;
50
53 template<std::size_t N, bool S>
54 constexpr bigint(bigint<digit_type, N, S> const &rhs) noexcept requires(N < num_digits)
55 {
56 std::size_t i = 0;
57
58 // Copy the data from a smaller bigint.
59 for (; i != N; ++i) {
60 digits[i] = rhs.digits[i];
61 }
62
63 // Sign extent the most-siginificant-digit.
64 hilet sign = rhs.is_negative() ? min1_digit : zero_digit;
65 for (; i != num_digits; ++i) {
66 digits[i] = sign;
67 }
68 }
69
72 template<std::size_t N, bool S>
73 constexpr bigint &operator=(bigint<digit_type, N, S> const &rhs) noexcept requires(N < num_digits)
74 {
75 std::size_t i = 0;
76
77 // Copy the data from a smaller bigint.
78 for (; i != N; ++i) {
79 digits[i] = rhs.digits[i];
80 }
81
82 // Sign extent the most-siginificant-digit.
83 hilet sign = rhs.is_negative() ? min1_digit : zero_digit;
84 for (; i != num_digits; ++i) {
85 digits[i] = sign;
86 }
87 return *this;
88 }
89
90 constexpr bigint(std::integral auto value) noexcept
91 {
92 static_assert(sizeof(value) <= sizeof(digit_type));
93 static_assert(num_digits > 0);
94
95 if constexpr (std::is_signed_v<decltype(value)>) {
96 // Sign extent the value to the size of the first digit.
97 digits[0] = static_cast<digit_type>(static_cast<signed_digit_type>(value));
98 } else {
99 digits[0] = static_cast<digit_type>(value);
100 }
101
102 // Sign extent to the rest of the digits.
103 hilet sign = value < 0 ? min1_digit : zero_digit;
104 for (std::size_t i = 1; i != num_digits; ++i) {
105 digits[i] = sign;
106 }
107 }
108
109 constexpr bigint &operator=(std::integral auto value) noexcept
110 {
111 static_assert(sizeof(value) <= sizeof(digit_type));
112 static_assert(num_digits > 0);
113
114 if constexpr (std::is_signed_v<decltype(value)>) {
115 // Sign extent the value to the size of the first digit.
116 digits[0] = static_cast<digit_type>(static_cast<signed_digit_type>(value));
117 } else {
118 digits[0] = static_cast<digit_type>(value);
119 }
120
121 // Sign extent to the rest of the digits.
122 hilet sign = value < 0 ? min1_digit : zero_digit;
123 for (std::size_t i = 1; i != num_digits; ++i) {
124 digits[i] = sign;
125 }
126 return *this;
127 }
128
129 constexpr explicit bigint(std::string_view str, int base = 10) noexcept : bigint()
130 {
131 std::size_t i = 0;
132 for (; i < str.size(); ++i) {
133 (*this) *= base;
134 (*this) += base16::int_from_char<int>(str[i]);
135 }
136 }
137
138 constexpr explicit operator unsigned long long() const noexcept
139 {
140 return static_cast<unsigned long long>(digits[0]);
141 }
142 constexpr explicit operator signed long long() const noexcept
143 {
144 return static_cast<signed long long>(digits[0]);
145 }
146 constexpr explicit operator unsigned long() const noexcept
147 {
148 return static_cast<unsigned long>(digits[0]);
149 }
150 constexpr explicit operator signed long() const noexcept
151 {
152 return static_cast<signed long>(digits[0]);
153 }
154 constexpr explicit operator unsigned int() const noexcept
155 {
156 return static_cast<unsigned int>(digits[0]);
157 }
158 constexpr explicit operator signed int() const noexcept
159 {
160 return static_cast<signed int>(digits[0]);
161 }
162 constexpr explicit operator unsigned short() const noexcept
163 {
164 return static_cast<unsigned short>(digits[0]);
165 }
166 constexpr explicit operator signed short() const noexcept
167 {
168 return static_cast<signed short>(digits[0]);
169 }
170 constexpr explicit operator unsigned char() const noexcept
171 {
172 return static_cast<unsigned char>(digits[0]);
173 }
174 constexpr explicit operator signed char() const noexcept
175 {
176 return static_cast<signed char>(digits[0]);
177 }
178
179 constexpr explicit operator bool() const noexcept
180 {
181 for (std::size_t i = 0; i != num_digits; ++i) {
182 if (digits[i] != 0) {
183 return true;
184 }
185 }
186 return false;
187 }
188
189 [[nodiscard]] constexpr bool is_negative() const noexcept
190 {
191 if constexpr (is_signed and num_digits > 0) {
192 return static_cast<signed_digit_type>(digits[num_digits - 1]) < 0;
193 } else {
194 return false;
195 }
196 }
197
198 template<std::size_t N, bool S>
199 constexpr explicit operator bigint<digit_type, N, S>() const noexcept
200 {
201 auto r = bigint<digit_type, N, S>{};
202
203 hilet sign = is_negative() ? min1_digit : zero_digit;
204 for (auto i = 0; i != N; ++i) {
205 r.digits[i] = i < num_digits ? digits[i] : sign;
206 }
207 return r;
208 }
209
210 std::string string() const noexcept
211 {
212 constexpr auto oneOver10 = reciprocal(bigint<digit_type, num_digits * 2, is_signed>{10});
213
214 auto tmp = *this;
215
216 std::string r;
217
218 if (tmp == 0) {
219 r = "0";
220 } else {
221 while (tmp > 0) {
222 bigint remainder;
223 std::tie(tmp, remainder) = div(tmp, bigint{10}, oneOver10);
224 r += (static_cast<unsigned char>(remainder) + '0');
225 }
226 }
227
228 std::reverse(r.begin(), r.end());
229 return r;
230 }
231
232 std::string uuid_string() const noexcept
233 {
234 static_assert(
235 std::is_same_v<digit_type, uint64_t> && num_digits == 2,
236 "uuid_string should only be called on a uuid compatible type");
237 return std::format(
238 "{:08x}-{:04x}-{:04x}-{:04x}-{:012x}",
239 static_cast<uint32_t>(digits[1] >> 32),
240 static_cast<uint16_t>(digits[1] >> 16),
241 static_cast<uint16_t>(digits[1]),
242 static_cast<uint16_t>(digits[0] >> 48),
243 digits[0] & 0x0000ffff'ffffffffULL);
244 }
245
246 [[nodiscard]] constexpr bigint operator-() const noexcept
247 {
248 bigint r;
249 neg_carry_chain(r.digits, digits, num_digits);
250 return r;
251 }
252
253 constexpr bigint &operator<<=(std::size_t rhs) noexcept
254 {
255 sll_carry_chain(digits, digits, rhs, num_digits);
256 return *this;
257 }
258
259 constexpr bigint &operator>>=(std::size_t rhs) noexcept
260 {
261 if constexpr (is_signed) {
262 sra_carry_chain(digits, digits, rhs, num_digits);
263 } else {
264 srl_carry_chain(digits, digits, rhs, num_digits);
265 }
266 return *this;
267 }
268
269 constexpr bigint &operator*=(bigint const &rhs) noexcept
270 {
271 auto r = bigint{0};
272 mul_carry_chain(r.digits, digits, rhs.digits, num_digits);
273 *this = r;
274 return *this;
275 }
276
277 constexpr bigint &operator+=(bigint const &rhs) noexcept
278 {
279 add_carry_chain(digits, digits, rhs.digits, num_digits);
280 return *this;
281 }
282
283 constexpr bigint &operator-=(bigint const &rhs) noexcept
284 {
285 sub_carry_chain(digits, digits, rhs.digits, num_digits);
286 return *this;
287 }
288
289 constexpr bigint &operator&=(bigint const &rhs) noexcept
290 {
291 and_carry_chain(digits, digits, rhs.digits, num_digits);
292 return *this;
293 }
294
295 constexpr bigint &operator|=(bigint const &rhs) noexcept
296 {
297 or_carry_chain(digits, digits, rhs.digits, num_digits);
298 return *this;
299 }
300
301 constexpr bigint &operator^=(bigint const &rhs) noexcept
302 {
303 xor_carry_chain(digits, digits, rhs.digits, num_digits);
304 return *this;
305 }
306
307 static bigint from_big_endian(uint8_t const *data) noexcept
308 {
309 auto r = bigint{};
310 for (ssize_t i = static_cast<ssize_t>(num_digits) - 1; i >= 0; i--) {
311 digit_type d = 0;
312 for (std::size_t j = 0; j < sizeof(digit_type); j++) {
313 d <<= 8;
314 d |= *(data++);
315 }
316 r.digits[i] = d;
317 }
318 return r;
319 }
320
321 static bigint from_little_endian(uint8_t const *data) noexcept
322 {
323 auto r = bigint{};
324 for (int i = 0; i < num_digits; ++i) {
325 digit_type d = 0;
326 for (std::size_t j = 0; j < sizeof(digit_type); j++) {
327 d |= static_cast<digit_type>(*(data++)) << (j * 8);
328 }
329 r.digits[i] = d;
330 }
331 return r;
332 }
333
334 static bigint from_big_endian(void const *data) noexcept
335 {
336 return from_big_endian(static_cast<uint8_t const *>(data));
337 }
338
339 static bigint from_little_endian(void const *data) noexcept
340 {
341 return from_little_endian(static_cast<uint8_t const *>(data));
342 }
343
349 [[nodiscard]] constexpr friend bigint crc(bigint const &lhs, bigint const &rhs) noexcept requires(not is_signed)
350 {
351 hilet polynomialOrder = bsr_carry_chain(rhs.digits, rhs.num_digits);
352 hi_assert(polynomialOrder >= 0);
353
354 auto tmp = static_cast<bigint<digit_type, 2 * num_digits>>(lhs) << polynomialOrder;
355 auto rhs_ = static_cast<bigint<digit_type, 2 * num_digits>>(rhs);
356
357 auto tmp_highest_bit = bsr_carry_chain(tmp.digits, tmp.num_digits);
358 while (tmp_highest_bit >= polynomialOrder) {
359 hilet divident = rhs_ << (tmp_highest_bit - polynomialOrder);
360
361 tmp ^= divident;
362 tmp_highest_bit = bsr_carry_chain(tmp.digits, tmp.num_digits);
363 }
364
365 return static_cast<bigint>(tmp);
366 }
367
375 [[nodiscard]] constexpr friend bigint reciprocal(bigint const &rhs)
376 {
378 r.digits[num_digits] = 1;
379 return static_cast<bigint>(r / rhs);
380 }
381
382 [[nodiscard]] constexpr friend bool operator==(bigint const &lhs, bigint const &rhs) noexcept
383 {
384 return eq_carry_chain(lhs.digits, rhs.digits, lhs.num_digits);
385 }
386
387 [[nodiscard]] constexpr friend std::strong_ordering operator<=>(bigint const &lhs, bigint const &rhs) noexcept
388 {
389 if constexpr (lhs.is_signed or rhs.is_signed) {
390 return cmp_signed_carry_chain(lhs.digits, rhs.digits, lhs.num_digits);
391 } else {
392 return cmp_unsigned_carry_chain(lhs.digits, rhs.digits, lhs.num_digits);
393 }
394 }
395
396 [[nodiscard]] constexpr friend bigint operator<<(bigint const &lhs, std::size_t rhs) noexcept
397 {
398 auto r = bigint{};
399 sll_carry_chain(r.digits, lhs.digits, rhs, lhs.num_digits);
400 return r;
401 }
402
403 [[nodiscard]] constexpr friend bigint operator>>(bigint const &lhs, std::size_t rhs) noexcept
404 {
405 auto r = bigint{};
406 if constexpr (lhs.is_signed) {
407 sra_carry_chain(r.digits, lhs.digits, rhs, lhs.num_digits);
408 } else {
409 srl_carry_chain(r.digits, lhs.digits, rhs, lhs.num_digits);
410 }
411 return r;
412 }
413
414 [[nodiscard]] constexpr friend bigint operator*(bigint const &lhs, bigint const &rhs) noexcept
415 {
416 auto r = bigint{};
417 mul_carry_chain(r.digits, lhs.digits, rhs.digits, lhs.num_digits);
418 return r;
419 }
420
421 [[nodiscard]] constexpr friend bigint operator+(bigint const &lhs, bigint const &rhs) noexcept
422 {
423 auto r = bigint{};
424 add_carry_chain(r.digits, lhs.digits, rhs.digits, lhs.num_digits);
425 return r;
426 }
427
428 [[nodiscard]] constexpr friend bigint operator-(bigint const &lhs, bigint const &rhs) noexcept
429 {
430 auto r = bigint{};
431 sub_carry_chain(r.digits, lhs.digits, rhs.digits, lhs.num_digits);
432 return r;
433 }
434
435 [[nodiscard]] constexpr friend bigint operator~(bigint const &rhs) noexcept
436 {
437 auto r = bigint{};
438 invert_carry_chain(r.digits, rhs.digits, rhs.num_digits);
439 return r;
440 }
441
442 [[nodiscard]] constexpr friend bigint operator|(bigint const &lhs, bigint const &rhs) noexcept
443 {
444 auto r = bigint{};
445 or_carry_chain(r.digits, lhs.digits, rhs.digits, lhs.num_digits);
446 return r;
447 }
448
449 [[nodiscard]] constexpr friend bigint operator&(bigint const &lhs, bigint const &rhs) noexcept
450 {
451 auto r = bigint{};
452 and_carry_chain(r.digits, lhs.digits, rhs.digits, lhs.num_digits);
453 return r;
454 }
455
456 [[nodiscard]] constexpr friend bigint operator^(bigint const &lhs, bigint const &rhs) noexcept
457 {
458 auto r = bigint{};
459 xor_carry_chain(r.digits, lhs.digits, rhs.digits, lhs.num_digits);
460 return r;
461 }
462
463 [[nodiscard]] constexpr friend std::pair<bigint, bigint> div(bigint const &lhs, bigint const &rhs) noexcept
464 {
465 auto quotient = bigint{};
466 auto remainder = bigint{};
467
468 if constexpr (is_signed) {
469 signed_div_carry_chain(quotient.digits, remainder.digits, lhs.digits, rhs.digits, lhs.num_digits);
470 } else {
471 div_carry_chain(quotient.digits, remainder.digits, lhs.digits, rhs.digits, lhs.num_digits);
472 }
473 return std::pair{quotient, remainder};
474 }
475
476 [[nodiscard]] constexpr friend std::pair<bigint, bigint>
477 div(bigint const &lhs, bigint const &rhs, bigint<digit_type, 2 * num_digits, is_signed> const &rhs_reciprocal) noexcept
478 requires(not is_signed)
479 {
480 constexpr auto nr_bits = num_digits * bits_per_digit;
481
482 using bigint_x3_type = bigint<digit_type, 3 * num_digits, is_signed>;
483
484 auto quotient = bigint_x3_type{lhs} * bigint_x3_type{rhs_reciprocal};
485 quotient >>= (2 * nr_bits);
486
487 auto product = bigint_x3_type{quotient} * bigint_x3_type{rhs};
488
489 hi_axiom(product <= lhs);
490 auto remainder = lhs - product;
491
492 int retry = 0;
493 while (remainder >= rhs) {
494 if (retry++ > 3) {
495 return div(lhs, rhs);
496 }
497
498 remainder -= rhs;
499 quotient += 1;
500 }
501 return std::pair{static_cast<bigint>(quotient), static_cast<bigint>(remainder)};
502 }
503
504 [[nodiscard]] constexpr friend bigint operator/(bigint const &lhs, bigint const &rhs) noexcept
505 {
506 auto quotient = bigint{};
507 auto remainder = bigint{};
508
509 if constexpr (is_signed) {
510 signed_div_carry_chain(quotient.digits, remainder.digits, lhs.digits, rhs.digits, lhs.num_digits);
511 } else {
512 div_carry_chain(quotient.digits, remainder.digits, lhs.digits, rhs.digits, lhs.num_digits);
513 }
514 return quotient;
515 }
516
517 [[nodiscard]] constexpr friend bigint operator%(bigint const &lhs, bigint const &rhs) noexcept
518 {
519 auto quotient = bigint{};
520 auto remainder = bigint{};
521
522 if constexpr (is_signed) {
523 signed_div_carry_chain(quotient.digits, remainder.digits, lhs.digits, rhs.digits, lhs.num_digits);
524 } else {
525 div_carry_chain(quotient.digits, remainder.digits, lhs.digits, rhs.digits, lhs.num_digits);
526 }
527 return remainder;
528 }
529
530 friend std::ostream &operator<<(std::ostream &lhs, bigint const &rhs)
531 {
532 return lhs << rhs.string();
533 }
534};
535
536template<std::unsigned_integral T, std::size_t N>
539
540template<std::unsigned_integral T, std::size_t N>
543
544template<std::unsigned_integral T, std::size_t N, bool S>
546};
547
551
552} // namespace hi::inline v1
553
554
555template<std::unsigned_integral DigitType, std::size_t NumDigits, bool IsSigned>
556struct std::numeric_limits<hi::bigint<DigitType, NumDigits, IsSigned>> {
557 using value_type = hi::bigint<DigitType, NumDigits, IsSigned>;
558
559 static constexpr bool is_specialized = true;
560 static constexpr bool is_signed = IsSigned;
561 static constexpr bool is_integer = true;
562 static constexpr bool is_exact = true;
563 static constexpr bool has_infinity = false;
564 static constexpr bool has_quiet_NaN = false;
565 static constexpr bool has_signaling_NaN = false;
566 static constexpr float_denorm_style has_denorm = std::denorm_absent;
567 static constexpr bool has_denorm_loss = false;
568 static constexpr float_round_style round_style = std::round_toward_zero;
569 static constexpr bool is_iec559 = false;
570 static constexpr bool is_bounded = true;
571 static constexpr bool is_modulo = true;
572 static constexpr int digits = std::numeric_limits<DigitType>::digits * NumDigits;
573 static constexpr int digits10 = std::numeric_limits<DigitType>::digits10 * NumDigits;
574 static constexpr int max_digits10 = 0;
575 static constexpr int min_exponent = 0;
576 static constexpr int min_exponent10 = 0;
577 static constexpr int max_exponent = 0;
578 static constexpr int max_exponent10 = 0;
579 static constexpr bool traps = std::numeric_limits<DigitType>::traps;
580 static constexpr bool tinyness_before = false;
581
582 static constexpr value_type min() noexcept
583 {
584 auto r = value_type{};
587
588 for (std::size_t i = 0; i != value_type::num_digits; ++i) {
589 r.digits[i] = umin;
590 }
591
592 if constexpr (value_type::is_signed and value_type::num_digits > 0) {
593 r.digits[value_type::num_digits - 1] = static_cast<value_type::digit_type>(smin);
594 }
595
596 return r;
597 }
598
599 static constexpr value_type lowest() noexcept
600 {
601 return min();
602 }
603
604 static constexpr value_type max() noexcept
605 {
606 auto r = value_type{};
609
610 for (std::size_t i = 0; i != value_type::num_digits; ++i) {
611 r.digits[i] = umax;
612 }
613
614 if constexpr (value_type::is_signed and value_type::num_digits > 0) {
615 r.digits[value_type::num_digits - 1] = smax;
616 }
617
618 return r;
619 }
620
621 static constexpr value_type epsilon() noexcept
622 {
623 return value_type{0};
624 }
625
626 static constexpr value_type round_error() noexcept
627 {
628 return value_type{0};
629 }
630
631 static constexpr value_type infinity() noexcept
632 {
633 return value_type{0};
634 }
635
636 static constexpr value_type quiet_NaN() noexcept
637 {
638 return value_type{0};
639 }
640
641 static constexpr value_type signaling_NaN() noexcept
642 {
643 return value_type{0};
644 }
645
646 static constexpr value_type denorm_min() noexcept
647 {
648 return value_type{0};
649 }
650};
651
std::ptrdiff_t ssize_t
Signed size/index into an array.
Definition required.hpp:37
#define hilet
Invariant should be the default for variables.
Definition required.hpp:23
constexpr alignment operator|(horizontal_alignment lhs, vertical_alignment rhs) noexcept
Combine vertical and horizontal alignment.
Definition alignment.hpp:200
STL namespace.
High performance big integer implementation.
Definition bigint.hpp:23
constexpr bigint() noexcept
Construct and clear an bigint.
Definition bigint.hpp:39
constexpr friend bigint reciprocal(bigint const &rhs)
Definition bigint.hpp:375
constexpr bigint & operator=(bigint< digit_type, N, S > const &rhs) noexcept
Assign from a small bigint.
Definition bigint.hpp:73
constexpr friend bigint crc(bigint const &lhs, bigint const &rhs) noexcept
Definition bigint.hpp:349
Is a numeric signed integer.
Definition type_traits.hpp:28
Is a numeric unsigned integer.
Definition type_traits.hpp:47
Is a numeric integer.
Definition type_traits.hpp:67
T begin(T... args)
T denorm_min(T... args)
T div(T... args)
T end(T... args)
T epsilon(T... args)
T infinity(T... args)
T lowest(T... args)
T max(T... args)
T min(T... args)
T quiet_NaN(T... args)
T remainder(T... args)
T reverse(T... args)
T round_error(T... args)
T signaling_NaN(T... args)
T tie(T... args)