HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
bigint.hpp
1// Copyright Take Vos 2019-2022.
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 "utility/module.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] = truncate<digit_type>(narrow_cast<signed_digit_type>(value));
98 } else {
99 digits[0] = narrow_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 truncate<unsigned long long>(digits[0]);
141 }
142 constexpr explicit operator signed long long() const noexcept
143 {
144 return truncate<signed long long>(digits[0]);
145 }
146 constexpr explicit operator unsigned long() const noexcept
147 {
148 return truncate<unsigned long>(digits[0]);
149 }
150 constexpr explicit operator signed long() const noexcept
151 {
152 return truncate<signed long>(digits[0]);
153 }
154 constexpr explicit operator unsigned int() const noexcept
155 {
156 return truncate<unsigned int>(digits[0]);
157 }
158 constexpr explicit operator signed int() const noexcept
159 {
160 return truncate<signed int>(digits[0]);
161 }
162 constexpr explicit operator unsigned short() const noexcept
163 {
164 return truncate<unsigned short>(digits[0]);
165 }
166 constexpr explicit operator signed short() const noexcept
167 {
168 return truncate<signed short>(digits[0]);
169 }
170 constexpr explicit operator unsigned char() const noexcept
171 {
172 return truncate<unsigned char>(digits[0]);
173 }
174 constexpr explicit operator signed char() const noexcept
175 {
176 return truncate<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 hi_axiom_not_null(data);
310
311 auto r = bigint{};
312 for (ssize_t i = narrow_cast<ssize_t>(num_digits) - 1; i >= 0; i--) {
313 digit_type d = 0;
314 for (std::size_t j = 0; j < sizeof(digit_type); j++) {
315 d <<= 8;
316 d |= *(data++);
317 }
318 r.digits[i] = d;
319 }
320 return r;
321 }
322
323 static bigint from_little_endian(uint8_t const *data) noexcept
324 {
325 auto r = bigint{};
326 for (int i = 0; i < num_digits; ++i) {
327 digit_type d = 0;
328 for (std::size_t j = 0; j < sizeof(digit_type); j++) {
329 d |= static_cast<digit_type>(*(data++)) << (j * 8);
330 }
331 r.digits[i] = d;
332 }
333 return r;
334 }
335
336 static bigint from_big_endian(void const *data) noexcept
337 {
338 return from_big_endian(static_cast<uint8_t const *>(data));
339 }
340
341 static bigint from_little_endian(void const *data) noexcept
342 {
343 return from_little_endian(static_cast<uint8_t const *>(data));
344 }
345
351 [[nodiscard]] constexpr friend bigint crc(bigint const &lhs, bigint const &rhs) noexcept requires(not is_signed)
352 {
353 hilet polynomialOrder = bsr_carry_chain(rhs.digits, rhs.num_digits);
354 hi_assert(polynomialOrder >= 0);
355
356 auto tmp = static_cast<bigint<digit_type, 2 * num_digits, is_signed>>(lhs) << polynomialOrder;
357 auto rhs_ = static_cast<bigint<digit_type, 2 * num_digits, is_signed>>(rhs);
358
359 auto tmp_highest_bit = bsr_carry_chain(tmp.digits, tmp.num_digits);
360 while (tmp_highest_bit >= polynomialOrder) {
361 hilet divident = rhs_ << (tmp_highest_bit - polynomialOrder);
362
363 tmp ^= divident;
364 tmp_highest_bit = bsr_carry_chain(tmp.digits, tmp.num_digits);
365 }
366
367 return static_cast<bigint>(tmp);
368 }
369
377 [[nodiscard]] constexpr friend bigint reciprocal(bigint const &rhs)
378 {
380 r.digits[num_digits] = 1;
381 return static_cast<bigint>(r / rhs);
382 }
383
384 [[nodiscard]] constexpr friend bool operator==(bigint const &lhs, bigint const &rhs) noexcept
385 {
386 return eq_carry_chain(lhs.digits, rhs.digits, lhs.num_digits);
387 }
388
389 [[nodiscard]] constexpr friend std::strong_ordering operator<=>(bigint const &lhs, bigint const &rhs) noexcept
390 {
391 if constexpr (lhs.is_signed or rhs.is_signed) {
392 return cmp_signed_carry_chain(lhs.digits, rhs.digits, lhs.num_digits);
393 } else {
394 return cmp_unsigned_carry_chain(lhs.digits, rhs.digits, lhs.num_digits);
395 }
396 }
397
398 [[nodiscard]] constexpr friend bigint operator<<(bigint const &lhs, std::size_t rhs) noexcept
399 {
400 auto r = bigint{};
401 sll_carry_chain(r.digits, lhs.digits, rhs, lhs.num_digits);
402 return r;
403 }
404
405 [[nodiscard]] constexpr friend bigint operator>>(bigint const &lhs, std::size_t rhs) noexcept
406 {
407 auto r = bigint{};
408 if constexpr (lhs.is_signed) {
409 sra_carry_chain(r.digits, lhs.digits, rhs, lhs.num_digits);
410 } else {
411 srl_carry_chain(r.digits, lhs.digits, rhs, lhs.num_digits);
412 }
413 return r;
414 }
415
416 [[nodiscard]] constexpr friend bigint operator*(bigint const &lhs, bigint const &rhs) noexcept
417 {
418 auto r = bigint{};
419 mul_carry_chain(r.digits, lhs.digits, rhs.digits, lhs.num_digits);
420 return r;
421 }
422
423 [[nodiscard]] constexpr friend bigint operator+(bigint const &lhs, bigint const &rhs) noexcept
424 {
425 auto r = bigint{};
426 add_carry_chain(r.digits, lhs.digits, rhs.digits, lhs.num_digits);
427 return r;
428 }
429
430 [[nodiscard]] constexpr friend bigint operator-(bigint const &lhs, bigint const &rhs) noexcept
431 {
432 auto r = bigint{};
433 sub_carry_chain(r.digits, lhs.digits, rhs.digits, lhs.num_digits);
434 return r;
435 }
436
437 [[nodiscard]] constexpr friend bigint operator~(bigint const &rhs) noexcept
438 {
439 auto r = bigint{};
440 invert_carry_chain(r.digits, rhs.digits, rhs.num_digits);
441 return r;
442 }
443
444 [[nodiscard]] constexpr friend bigint operator|(bigint const &lhs, bigint const &rhs) noexcept
445 {
446 auto r = bigint{};
447 or_carry_chain(r.digits, lhs.digits, rhs.digits, lhs.num_digits);
448 return r;
449 }
450
451 [[nodiscard]] constexpr friend bigint operator&(bigint const &lhs, bigint const &rhs) noexcept
452 {
453 auto r = bigint{};
454 and_carry_chain(r.digits, lhs.digits, rhs.digits, lhs.num_digits);
455 return r;
456 }
457
458 [[nodiscard]] constexpr friend bigint operator^(bigint const &lhs, bigint const &rhs) noexcept
459 {
460 auto r = bigint{};
461 xor_carry_chain(r.digits, lhs.digits, rhs.digits, lhs.num_digits);
462 return r;
463 }
464
465 [[nodiscard]] constexpr friend std::pair<bigint, bigint> div(bigint const &lhs, bigint const &rhs) noexcept
466 {
467 auto quotient = bigint{};
468 auto remainder = bigint{};
469
470 if constexpr (is_signed) {
471 signed_div_carry_chain(quotient.digits, remainder.digits, lhs.digits, rhs.digits, lhs.num_digits);
472 } else {
473 div_carry_chain(quotient.digits, remainder.digits, lhs.digits, rhs.digits, lhs.num_digits);
474 }
475 return std::pair{quotient, remainder};
476 }
477
478 [[nodiscard]] constexpr friend std::pair<bigint, bigint>
479 div(bigint const &lhs, bigint const &rhs, bigint<digit_type, 2 * num_digits, is_signed> const &rhs_reciprocal) noexcept
480 requires(not is_signed)
481 {
482 constexpr auto nr_bits = num_digits * bits_per_digit;
483
484 using bigint_x3_type = bigint<digit_type, 3 * num_digits, is_signed>;
485
486 auto quotient = bigint_x3_type{lhs} * bigint_x3_type{rhs_reciprocal};
487 quotient >>= (2 * nr_bits);
488
489 auto product = bigint_x3_type{quotient} * bigint_x3_type{rhs};
490
491 hi_axiom(product <= lhs);
492 auto remainder = lhs - product;
493
494 int retry = 0;
495 while (remainder >= rhs) {
496 if (retry++ > 3) {
497 return div(lhs, rhs);
498 }
499
500 remainder -= rhs;
501 quotient += 1;
502 }
503 return std::pair{static_cast<bigint>(quotient), static_cast<bigint>(remainder)};
504 }
505
506 [[nodiscard]] constexpr friend bigint operator/(bigint const &lhs, bigint const &rhs) noexcept
507 {
508 auto quotient = bigint{};
509 auto remainder = bigint{};
510
511 if constexpr (is_signed) {
512 signed_div_carry_chain(quotient.digits, remainder.digits, lhs.digits, rhs.digits, lhs.num_digits);
513 } else {
514 div_carry_chain(quotient.digits, remainder.digits, lhs.digits, rhs.digits, lhs.num_digits);
515 }
516 return quotient;
517 }
518
519 [[nodiscard]] constexpr friend bigint operator%(bigint const &lhs, bigint const &rhs) noexcept
520 {
521 auto quotient = bigint{};
522 auto remainder = bigint{};
523
524 if constexpr (is_signed) {
525 signed_div_carry_chain(quotient.digits, remainder.digits, lhs.digits, rhs.digits, lhs.num_digits);
526 } else {
527 div_carry_chain(quotient.digits, remainder.digits, lhs.digits, rhs.digits, lhs.num_digits);
528 }
529 return remainder;
530 }
531
532 friend std::ostream &operator<<(std::ostream &lhs, bigint const &rhs)
533 {
534 return lhs << rhs.string();
535 }
536};
537
538template<std::unsigned_integral T, std::size_t N>
541
542template<std::unsigned_integral T, std::size_t N>
545
546template<std::unsigned_integral T, std::size_t N, bool S>
548};
549
553
554} // namespace hi::inline v1
555
556
557template<std::unsigned_integral DigitType, std::size_t NumDigits, bool IsSigned>
558struct std::numeric_limits<hi::bigint<DigitType, NumDigits, IsSigned>> {
559 using value_type = hi::bigint<DigitType, NumDigits, IsSigned>;
560
561 static constexpr bool is_specialized = true;
562 static constexpr bool is_signed = IsSigned;
563 static constexpr bool is_integer = true;
564 static constexpr bool is_exact = true;
565 static constexpr bool has_infinity = false;
566 static constexpr bool has_quiet_NaN = false;
567 static constexpr bool has_signaling_NaN = false;
568 static constexpr float_denorm_style has_denorm = std::denorm_absent;
569 static constexpr bool has_denorm_loss = false;
570 static constexpr float_round_style round_style = std::round_toward_zero;
571 static constexpr bool is_iec559 = false;
572 static constexpr bool is_bounded = true;
573 static constexpr bool is_modulo = true;
574 static constexpr int digits = std::numeric_limits<DigitType>::digits * NumDigits;
575 static constexpr int digits10 = std::numeric_limits<DigitType>::digits10 * NumDigits;
576 static constexpr int max_digits10 = 0;
577 static constexpr int min_exponent = 0;
578 static constexpr int min_exponent10 = 0;
579 static constexpr int max_exponent = 0;
580 static constexpr int max_exponent10 = 0;
581 static constexpr bool traps = std::numeric_limits<DigitType>::traps;
582 static constexpr bool tinyness_before = false;
583
584 static constexpr value_type min() noexcept
585 {
586 auto r = value_type{};
589
590 for (std::size_t i = 0; i != value_type::num_digits; ++i) {
591 r.digits[i] = umin;
592 }
593
594 if constexpr (value_type::is_signed and value_type::num_digits > 0) {
595 r.digits[value_type::num_digits - 1] = truncate<typename value_type::digit_type>(smin);
596 }
597
598 return r;
599 }
600
601 static constexpr value_type lowest() noexcept
602 {
603 return min();
604 }
605
606 static constexpr value_type max() noexcept
607 {
608 auto r = value_type{};
611
612 for (std::size_t i = 0; i != value_type::num_digits; ++i) {
613 r.digits[i] = umax;
614 }
615
616 if constexpr (value_type::is_signed and value_type::num_digits > 0) {
617 r.digits[value_type::num_digits - 1] = smax;
618 }
619
620 return r;
621 }
622
623 static constexpr value_type epsilon() noexcept
624 {
625 return value_type{0};
626 }
627
628 static constexpr value_type round_error() noexcept
629 {
630 return value_type{0};
631 }
632
633 static constexpr value_type infinity() noexcept
634 {
635 return value_type{0};
636 }
637
638 static constexpr value_type quiet_NaN() noexcept
639 {
640 return value_type{0};
641 }
642
643 static constexpr value_type signaling_NaN() noexcept
644 {
645 return value_type{0};
646 }
647
648 static constexpr value_type denorm_min() noexcept
649 {
650 return value_type{0};
651 }
652};
653
#define hi_assert(expression,...)
Assert if expression is true.
Definition assert.hpp:184
#define hi_axiom(expression,...)
Specify an axiom; an expression that is true.
Definition assert.hpp:238
#define hi_axiom_not_null(expression,...)
Assert if an expression is not nullptr.
Definition assert.hpp:257
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
STL namespace.
DOXYGEN BUG.
Definition algorithm.hpp:13
geometry/margins.hpp
Definition cache.hpp:11
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)
Calculate the reciprocal at a certain precision.
Definition bigint.hpp:377
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
Calculate the remainder of a CRC check.
Definition bigint.hpp:351
Is a numeric signed integer.
Definition type_traits.hpp:29
Is a numeric unsigned integer.
Definition type_traits.hpp:48
Is a numeric integer.
Definition type_traits.hpp:68
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)