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