jaulib v1.4.1
Jau Support Library (C++, Java, ..)
Loading...
Searching...
No Matches
big_int.hpp
Go to the documentation of this file.
1/**
2 * big_int_t (this file)
3 * (c) 2024 Gothel Software e.K.
4 *
5 * Includes code from: Botan BigInt (bigint.h, ..)
6 * (C) 1999-2011,2012,2014,2019 Jack Lloyd
7 * 2007 FlexSecure
8 *
9 * Includes code from: Botan Division Algorithms (divide.h)
10 * (C) 1999-2007,2012,2018,2021 Jack Lloyd
11 *
12 * jaulib including this code is released under the MIT License (see COPYING)
13 * Botan itself is released under the Simplified BSD License (see COPYING)
14 *
15 * SPDX-License-Identifier: MIT
16 *
17 * This Source Code Form is subject to the terms of the MIT License
18 * If a copy of the MIT was not distributed with this file,
19 * you can obtain one at https://opensource.org/license/mit/.
20 */
21
22#ifndef JAU_BIG_INT_HPP_
23#define JAU_BIG_INT_HPP_
24
25#include <cassert>
26#include <cstdint>
27
28#include <jau/byte_util.hpp>
29#include <jau/cpp_lang_util.hpp>
31#include <jau/string_util.hpp>
32
33namespace jau::mp {
34
35 /** \addtogroup Integer
36 *
37 * @{
38 */
39
40 /**
41 * Arbitrary precision integer type
42 *
43 * @anchor bigint_storage_format
44 * ### Local storage format
45 * Internally the big integer is stored in an array of mp_word_t ordered little-endian alike,
46 * with the least significant word at the array-bottom and most significant word at the array-top.
47 *
48 * The mp_word_t itself is stored in jau::endian::native!
49 */
50 class BigInt {
51 public:
52 /**
53 * Sign symbol definitions for positive and negative numbers
54 */
55 enum sign_t { negative = 0,
56 positive = 1 };
57
58 public:
59 BigInt() noexcept = default;
60
61 BigInt(const BigInt& o) noexcept = default;
62
63 ~BigInt() noexcept = default;
64
65 /**
66 * Create a 0-value big_int
67 */
68 static BigInt zero() { return BigInt(); }
69
70 /**
71 * Create a 1-value big_int
72 */
73 static BigInt one() { return BigInt::from_word(1); }
74
75 /**
76 * Create big_int from an unsigned 64 bit integer
77 * @param n initial value of this big_int
78 */
79 static BigInt from_u64(uint64_t n) {
80 BigInt bn;
81 if ( 64 == mp_word_bits ) {
82 bn.set_word_at(0, n);
83 } else {
84 bn.set_word_at(1, static_cast<mp_word_t>(n >> 32));
85 bn.set_word_at(0, static_cast<mp_word_t>(n));
86 }
87 return bn;
88 }
89
90 /**
91 * Create big_int from a mp_word_t (limb)
92 * @param n initial value of this big_int
93 */
95 BigInt bn;
96 bn.set_word_at(0, n);
97 return bn;
98 }
99
100 /**
101 * Create big_int from a signed 32 bit integer
102 * @param n initial value of this big_int
103 */
104 static BigInt from_s32(int32_t n) {
105 if ( n >= 0 ) {
106 return BigInt::from_u64(static_cast<uint64_t>(n));
107 } else {
108 return -BigInt::from_u64(static_cast<uint64_t>(-n));
109 }
110 }
111
112 /**
113 * Create big_int of specified size, all zeros
114 * @param n size of the internal register in words
115 */
116 static BigInt with_capacity(size_t n) {
117 BigInt bn;
118 bn.grow_to(n);
119 return bn;
120 }
121
122 /**
123 * Create a power of two
124 * @param n the power of two to create
125 * @return big_int_t representing 2^n
126 */
127 static BigInt power_of_2(size_t n) {
128 BigInt b;
129 b.set_bit(n);
130 return b;
131 }
132
133 /**
134 * Create big_int_t from an unsigned 64 bit integer
135 * @param n initial value of this big_int_t
136 */
137 BigInt(uint64_t n) {
138 if ( 64 == mp_word_bits ) {
139 m_data.set_word_at(0, n);
140 } else {
141 m_data.set_word_at(1, static_cast<mp_word_t>(n >> 32));
142 m_data.set_word_at(0, static_cast<mp_word_t>(n));
143 }
144 }
145
146 /**
147 * Construct a big_int_t from a string encoded as hexadecimal or decimal.
148 *
149 * Both number bases may lead a `-`, denoting a negative number.
150 *
151 * Hexadecimal is detected by a leading `0x`.
152 */
153 BigInt(const std::string& str) {
154 size_t markers = 0;
155 bool is_negative = false;
156
157 if ( str.length() > 0 && str[0] == '-' ) {
158 markers += 1;
159 is_negative = true;
160 }
161
162 if ( str.length() > markers + 2 && str[markers] == '0' &&
163 str[markers + 1] == 'x' ) {
164 markers += 2;
165 *this = hex_decode(cast_char_ptr_to_uint8(str.data()) + markers, str.length() - markers, lb_endian_t::big);
166 } else {
167 *this = dec_decode(cast_char_ptr_to_uint8(str.data()) + markers, str.length() - markers);
168 }
169
170 if ( is_negative )
172 else
174 }
175
176 /**
177 * Create a big_int_t from an integer in a byte array with given byte_len,
178 * considering the given byte_order.
179 *
180 * The value is stored in the local storage format, see \ref bigint_storage_format
181 *
182 * @param buf the byte array holding the value
183 * @param byte_len size of buf in bytes
184 * @param littleEndian
185 */
186 BigInt(const uint8_t buf[], size_t byte_len, const lb_endian_t byte_order) {
187 binary_decode(buf, byte_len, byte_order);
188 }
189
190 BigInt(std::vector<mp_word_t>&& other_reg) noexcept {
191 this->swap_reg(other_reg);
192 }
193
194 BigInt(BigInt&& other) noexcept {
195 this->swap(other);
196 }
197
198 BigInt& operator=(const BigInt& r) = default;
199
200 BigInt& operator=(BigInt&& other) noexcept {
201 if ( this != &other ) {
202 this->swap(other);
203 }
204 return *this;
205 }
206
207 /**
208 * Swap this value with another
209 * @param other big_int to swap values with
210 */
211 void swap(BigInt& other) noexcept {
212 m_data.swap(other.m_data);
213 std::swap(m_signedness, other.m_signedness);
214 }
215
216 private:
217 void swap_reg(std::vector<mp_word_t>& reg) noexcept {
218 m_data.swap(reg);
219 // sign left unchanged
220 }
221
222 public:
223 /** Unary negation operator, returns new negative instance of this. */
224 BigInt operator-() const noexcept { return BigInt(*this).flip_sign(); }
225
226 /**
227 * @param n the offset to get a byte from
228 * @result byte at offset n
229 */
230 uint8_t byte_at(size_t n) const noexcept {
231 return get_byte_var_be(sizeof(mp_word_t) - (n % sizeof(mp_word_t)) - 1,
232 word_at(n / sizeof(mp_word_t)));
233 }
234
235 /**
236 * Return the mp_word_t at a specified position of the internal register
237 * @param n position in the register
238 * @return value at position n
239 */
240 mp_word_t word_at(size_t n) const noexcept {
241 return m_data.get_word_at(n);
242 }
243
244 /**
245 * Return a const pointer to the register
246 * @result a pointer to the start of the internal register
247 */
248 const mp_word_t* data() const { return m_data.const_data(); }
249
250 /**
251 * Tests if the sign of the integer is negative
252 * @result true, iff the integer has a negative sign
253 */
254 bool is_negative() const noexcept { return sign() == negative; }
255
256 /**
257 * Tests if the sign of the integer is positive
258 * @result true, iff the integer has a positive sign
259 */
260 bool is_positive() const noexcept { return sign() == positive; }
261
262 /**
263 * Return the sign of the integer
264 * @result the sign of the integer
265 */
266 sign_t sign() const noexcept { return m_signedness; }
267
268 /**
269 * @result the opposite sign of the represented integer value
270 */
271 sign_t reverse_sign() const noexcept {
272 if ( sign() == positive ) {
273 return negative;
274 }
275 return positive;
276 }
277
278 /**
279 * Flip the sign of this big_int
280 */
281 BigInt& flip_sign() noexcept {
282 return set_sign(reverse_sign());
283 }
284
285 /**
286 * Set sign of the integer
287 * @param sign new Sign to set
288 */
290 if ( sign == negative && is_zero() ) {
291 sign = positive;
292 }
293 m_signedness = sign;
294 return *this;
295 }
296
297 /** Returns absolute (positive) value of this instance */
298 BigInt abs() const noexcept {
299 return BigInt(*this).set_sign(positive);
300 }
301
302 /**
303 * Give size of internal register
304 * @result size of internal register in words
305 */
306 size_t size() const noexcept { return m_data.size(); }
307
308 /**
309 * Return how many words we need to hold this value
310 * @result significant words of the represented integer value
311 */
312 size_t sig_words() const noexcept { return m_data.sig_words(); }
313
314 /** Returns byte length of this integer */
315 size_t bytes() const { return jau::round_up(bits(), 8U) / 8U; }
316
317 /** Returns bit length of this integer */
318 size_t bits() const noexcept {
319 const size_t words = sig_words();
320 if ( words == 0 ) {
321 return 0;
322 }
323 const size_t full_words = (words - 1) * mp_word_bits;
324 const size_t top_bits = mp_word_bits - top_bits_free();
325 return full_words + top_bits;
326 }
327
328 /**
329 * Zeroize the big_int. The size of the underlying register is not
330 * modified.
331 */
332 void clear() {
333 m_data.set_to_zero();
334 m_signedness = positive;
335 }
336
337 /**
338 * Compares this instance against other considering both sign() value
339 * Returns
340 * - -1 if this < other
341 * - 0 if this == other
342 * - 1 if this > other
343 */
344 int compare(const BigInt& b) const noexcept {
345 return cmp(b, true);
346 }
347
348 /**
349 * Test if the integer has an even value
350 * @result true if the integer is even, false otherwise
351 */
352 bool is_even() const noexcept { return get_bit(0) == 0; }
353
354 /**
355 * Test if the integer has an odd value
356 * @result true if the integer is odd, false otherwise
357 */
358 bool is_odd() const noexcept { return get_bit(0) == 1; }
359
360 /**
361 * Test if the integer is not zero
362 * @result true if the integer is non-zero, false otherwise
363 */
364 bool is_nonzero() const noexcept { return !is_zero(); }
365
366 /**
367 * Test if the integer is zero
368 * @result true if the integer is zero, false otherwise
369 */
370 bool is_zero() const noexcept {
371 return sig_words() == 0;
372 }
373
374 /**
375 * Return bit value at specified position
376 * @param n the bit offset to test
377 * @result true, if the bit at position n is set, false otherwise
378 */
379 bool get_bit(size_t n) const noexcept {
380 return (word_at(n / mp_word_bits) >> (n % mp_word_bits)) & 1;
381 }
382
383 /**
384 * Set bit at specified position
385 * @param n bit position to set
386 */
387 void set_bit(size_t n) noexcept {
388 conditionally_set_bit(n, true);
389 }
390
391 /**
392 * Conditionally set bit at specified position. Note if set_it is
393 * false, nothing happens, and if the bit is already set, it
394 * remains set.
395 *
396 * @param n bit position to set
397 * @param set_it if the bit should be set
398 */
399 void conditionally_set_bit(size_t n, bool set_it) noexcept {
400 const size_t which = n / mp_word_bits;
401 const mp_word_t mask = static_cast<mp_word_t>(set_it) << (n % mp_word_bits);
402 m_data.set_word_at(which, word_at(which) | mask);
403 }
404
405 /** ! operator, returns `true` iff this is zero, otherwise false. */
406 bool operator!() const noexcept { return is_zero(); }
407
408 bool operator==(const BigInt& b) const noexcept { return is_equal(b); }
409 bool operator!=(const BigInt& b) const noexcept { return !is_equal(b); }
410 bool operator<=(const BigInt& b) const noexcept { return cmp(b) <= 0; }
411 bool operator>=(const BigInt& b) const noexcept { return cmp(b) >= 0; }
412 bool operator<(const BigInt& b) const noexcept { return is_less_than(b); }
413 bool operator>(const BigInt& b) const noexcept { return b.is_less_than(*this); }
414
415 /**
416 std::strong_ordering operator<=>(const BigInt& b) const noexcept {
417 const int r = cmp(b);
418 return 0 == r ? std::strong_ordering::equal : ( 0 > r ? std::strong_ordering::less : std::strong_ordering::greater);
419 } */
420
421 BigInt& operator++() noexcept { return *this += 1; }
422
423 BigInt& operator--() noexcept { return *this -= 1; }
424
425 BigInt operator++(int) noexcept {
426 BigInt x = (*this);
427 ++(*this);
428 return x;
429 }
430
431 BigInt operator--(int) noexcept {
432 BigInt x = (*this);
433 --(*this);
434 return x;
435 }
436
437 BigInt& operator+=(const BigInt& y) noexcept {
438 return add(y.data(), y.sig_words(), y.sign());
439 }
440
441 BigInt& operator-=(const BigInt& y) noexcept {
442 return add(y.data(), y.sig_words(), y.sign() == positive ? negative : positive);
443 }
444
445 BigInt operator+(const BigInt& y) const noexcept {
446 return add2(*this, y.data(), y.sig_words(), y.sign());
447 }
448
449 BigInt operator-(const BigInt& y) const noexcept {
450 return add2(*this, y.data(), y.sig_words(), y.reverse_sign());
451 }
452
453 BigInt& operator<<=(size_t shift) noexcept {
454 const size_t shift_words = shift / mp_word_bits;
455 const size_t shift_bits = shift % mp_word_bits;
456 const size_t size = sig_words();
457
458 const size_t bits_free = top_bits_free();
459
460 const size_t new_size = size + shift_words + (bits_free < shift_bits);
461
462 m_data.grow_to(new_size);
463
464 ops::bigint_shl1(m_data.mutable_data(), new_size, size, shift_words, shift_bits);
465
466 return *this;
467 }
468
469 BigInt& operator>>=(size_t shift) noexcept {
470 const size_t shift_words = shift / mp_word_bits;
471 const size_t shift_bits = shift % mp_word_bits;
472
473 ops::bigint_shr1(m_data.mutable_data(), m_data.size(), shift_words, shift_bits);
474
475 if ( is_negative() && is_zero() ) {
477 }
478 return *this;
479 }
480
481 BigInt operator<<(size_t shift) const {
482 const size_t shift_words = shift / mp_word_bits;
483 const size_t shift_bits = shift % mp_word_bits;
484 const size_t x_sw = sig_words();
485
486 BigInt y = BigInt::with_capacity(x_sw + shift_words + (shift_bits ? 1 : 0));
487 ops::bigint_shl2(y.mutable_data(), data(), x_sw, shift_words, shift_bits);
488 y.set_sign(sign());
489 return y;
490 }
491
492 BigInt operator>>(size_t shift) const {
493 const size_t shift_words = shift / mp_word_bits;
494 const size_t shift_bits = shift % mp_word_bits;
495 const size_t x_sw = sig_words();
496
497 if ( shift_words >= x_sw ) {
498 return BigInt::zero();
499 }
500
501 BigInt y = BigInt::with_capacity(x_sw - shift_words);
502 ops::bigint_shr2(y.mutable_data(), data(), x_sw, shift_words, shift_bits);
503
504 if ( is_negative() && y.is_zero() ) {
506 } else {
507 y.set_sign(sign());
508 }
509 return y;
510 }
511
512 BigInt& operator*=(const BigInt& y) noexcept {
513 std::vector<mp_word_t> ws;
514 return this->mul(y, ws);
515 }
516
517 BigInt operator*(const BigInt& y) noexcept {
518 const size_t x_sw = sig_words();
519 const size_t y_sw = y.sig_words();
520
521 BigInt z;
522 z.resize(size() + y.size());
523
524 if ( x_sw == 1 && y_sw ) {
525 ops::bigint_linmul3(z.mutable_data(), y.data(), y_sw, word_at(0));
526 } else if ( y_sw == 1 && x_sw ) {
527 ops::bigint_linmul3(z.mutable_data(), data(), x_sw, y.word_at(0));
528 } else if ( x_sw && y_sw ) {
529 ops::basecase_mul(z.mutable_data(), z.size(), data(), x_sw, y.data(), y_sw);
530 }
531 z.cond_flip_sign(x_sw > 0 && y_sw > 0 && sign() != y.sign());
532 return z;
533 }
534
536 if ( y.sig_words() == 1 && jau::is_power_of_2(y.word_at(0)) ) {
537 (*this) >>= (y.bits() - 1);
538 } else {
539 (*this) = (*this) / y;
540 }
541 return (*this);
542 }
543
544 BigInt operator/(const BigInt& y) const {
545 if ( y.sig_words() == 1 ) {
546 return *this / y.word_at(0);
547 }
548 BigInt q, r;
549 vartime_divide(*this, y, q, r);
550 return q;
551 }
552 /**
553 * Modulo operator
554 * @param y the modulus to reduce this by
555 */
556 BigInt& operator%=(const BigInt& mod) {
557 return (*this = (*this) % mod);
558 }
559
560 BigInt operator%(const BigInt& mod) {
561 if ( mod.is_zero() ) {
563 }
564 if ( mod.is_negative() ) {
565 throw jau::math::MathDomainError("mod < 0", E_FILE_LINE);
566 }
567 if ( is_positive() && mod.is_positive() && *this < mod ) {
568 return *this;
569 }
570 if ( mod.sig_words() == 1 ) {
571 return from_word(*this % mod.word_at(0));
572 }
573 BigInt q, r;
574 vartime_divide(*this, mod, q, r);
575 return r;
576 }
577
578 /**
579 * Returns (*this)^e, or pow(*this, e)
580 *
581 * Implementation is not optimized and naive, i.e. O(n)
582 *
583 * @param e the exponent
584 */
586 const BigInt& b = *this;
587 if ( b.is_zero() ) {
588 return BigInt::zero();
589 }
590 const BigInt one_v = BigInt::one();
591 BigInt r = one_v;
592 bool is_negative;
593 if ( e.is_negative() ) {
594 is_negative = true;
595 e.flip_sign();
596 } else {
597 is_negative = false;
598 }
599
600 while ( e.is_nonzero() ) {
601 r *= b;
602 --e;
603 }
604
605 if ( is_negative ) {
606 return one_v / r;
607 } else {
608 return r;
609 }
610 }
611
612 /**
613 * Returns (*this)^e % m, or pow(*this, e) % m
614 *
615 * Implementation is not optimized and naive, i.e. O(n)
616 *
617 * @param e the exponent
618 */
620 const BigInt& b = *this;
621 if ( b.is_zero() ) {
622 return BigInt::zero();
623 }
624 const BigInt one_v = BigInt::one();
625 BigInt r = one_v;
626 bool is_negative;
627 if ( e.is_negative() ) {
628 is_negative = true;
629 e.flip_sign();
630 } else {
631 is_negative = false;
632 }
633
634 while ( e.is_nonzero() ) {
635 r *= b;
636 r %= m;
637 --e;
638 }
639
640 if ( is_negative ) {
641 return one_v / r;
642 } else {
643 return r;
644 }
645 }
646
647 /**
648 * Square value of *this
649 * @param ws a temp workspace
650 */
651 BigInt& square(std::vector<mp_word_t>& ws); // TODO
652
653 /**
654 * Set *this to y - *this
655 * @param y the big_int_t to subtract from
656 * @param ws a temp workspace
657 */
658 BigInt& rev_sub(const BigInt& y, std::vector<mp_word_t>& ws); // TODO
659
660 /**
661 * Set *this to (*this + y) % mod
662 * This function assumes *this is >= 0 && < mod
663 * @param y the big_int_t to add - assumed y >= 0 and y < mod
664 * @param mod the positive modulus
665 * @param ws a temp workspace
666 */
667 BigInt& mod_add(const BigInt& y, const BigInt& mod, std::vector<mp_word_t>& ws); // TODO
668
669 /**
670 * Set *this to (*this - y) % mod
671 * This function assumes *this is >= 0 && < mod
672 * @param y the big_int_t to subtract - assumed y >= 0 and y < mod
673 * @param mod the positive modulus
674 * @param ws a temp workspace
675 */
676 BigInt& mod_sub(const BigInt& y, const BigInt& mod, std::vector<mp_word_t>& ws); // TODO
677
678 /**
679 * Set *this to (*this * y) % mod
680 * This function assumes *this is >= 0 && < mod
681 * y should be small, less than 16
682 * @param y the small integer to multiply by
683 * @param mod the positive modulus
684 * @param ws a temp workspace
685 */
686 BigInt& mod_mul(uint8_t y, const BigInt& mod, std::vector<mp_word_t>& ws); // TODO
687
688 /**
689 * @param rng a random number generator
690 * @param min the minimum value (must be non-negative)
691 * @param max the maximum value (must be non-negative and > min)
692 * @return random integer in [min,max)
693 static big_int_t random_integer(RandomNumberGenerator& rng,
694 const big_int_t& min,
695 const big_int_t& max); // TODO
696 */
697
698 std::string to_dec_string(bool add_details = false) const {
699 // Use the largest power of 10 that fits in a mp_word_t
700 mp_word_t conversion_radix, radix_digits;
701 if constexpr ( 64 == mp_word_bits ) {
702 conversion_radix = 10000000000000000000U;
703 radix_digits = 19;
704 } else {
705 conversion_radix = 1000000000U;
706 radix_digits = 9;
707 }
708
709 // (over-)estimate of the number of digits needed; log2(10) ~ 3.3219
710 const size_t digit_estimate = static_cast<size_t>(1 + (static_cast<double>(this->bits()) / 3.32));
711
712 // (over-)estimate of db such that conversion_radix^db > *this
713 const size_t digit_blocks = (digit_estimate + radix_digits - 1) / radix_digits;
714
715 BigInt value = *this;
716 value.set_sign(positive);
717
718 // Extract groups of digits into words
719 std::vector<mp_word_t> digit_groups(digit_blocks);
720
721 for ( size_t i = 0; i != digit_blocks; ++i ) {
722 mp_word_t remainder = 0;
723 ct_divide_word(value, conversion_radix, value, remainder);
724 digit_groups[i] = remainder;
725 }
726 assert(value.is_zero());
727
728 // Extract digits from the groups
729 std::vector<uint8_t> digits(digit_blocks * radix_digits);
730
731 for ( size_t i = 0; i != digit_blocks; ++i ) {
732 mp_word_t remainder = digit_groups[i];
733 for ( size_t j = 0; j != radix_digits; ++j ) {
734 // Compiler should convert div/mod by 10 into mul by magic constant
735 const mp_word_t digit = remainder % 10;
736 remainder /= 10;
737 digits[radix_digits * i + j] = static_cast<uint8_t>(digit);
738 }
739 }
740
741 // remove leading zeros
742 while ( !digits.empty() && digits.back() == 0 ) {
743 digits.pop_back();
744 }
745
746 assert(digit_estimate >= digits.size());
747
748 // Reverse the digits to big-endian and format to text
749 std::string s;
750 s.reserve(1 + digits.size());
751
752 if ( is_negative() ) {
753 s += "-";
754 }
755
756 // Reverse and convert to textual digits
757 for ( uint8_t d : digits ) {
758 s.push_back(static_cast<char>(d + '0')); // assumes ASCII
759 }
760
761 if ( s.empty() ) {
762 s += "0";
763 }
764 if ( add_details ) {
765 append_detail(s);
766 }
767 return s;
768 }
769
770 std::string to_hex_string(bool add_details = false) const noexcept {
771 std::vector<uint8_t> bits;
772 const uint8_t* data;
773 size_t data_len;
774
775 if ( is_zero() ) {
776 bits.push_back(0);
777 data = bits.data();
778 data_len = bits.size();
779 } else {
780 data = reinterpret_cast<const uint8_t*>(m_data.const_data());
781 data_len = bytes();
782 }
783
784 std::string s;
785 if ( is_negative() ) {
786 s += "-";
787 }
789 if ( add_details ) {
790 append_detail(s);
791 }
792 return s;
793 }
794
795 private:
796 class data_t {
797 public:
798 data_t() noexcept
799 : m_reg(), m_sig_words(sig_words_npos) { }
800
801 data_t(const data_t& o) noexcept = default;
802
803 data_t(data_t&& o) noexcept {
804 swap(o);
805 }
806 data_t(std::vector<mp_word_t>&& reg) noexcept {
807 swap(reg);
808 }
809
810 ~data_t() noexcept = default;
811
812 data_t& operator=(const data_t& r) noexcept = default;
813
814 data_t& operator=(data_t&& other) noexcept {
815 if ( this != &other ) {
816 this->swap(other);
817 }
818 return *this;
819 }
820 data_t& operator=(std::vector<mp_word_t>&& other_reg) noexcept {
821 if ( &m_reg != &other_reg ) {
822 this->swap(other_reg);
823 }
824 return *this;
825 }
826
827 mp_word_t* mutable_data() noexcept {
828 invalidate_sig_words();
829 return m_reg.data();
830 }
831
832 const mp_word_t* const_data() const noexcept {
833 return m_reg.data();
834 }
835
836 std::vector<mp_word_t>& mutable_vector() noexcept {
837 invalidate_sig_words();
838 return m_reg;
839 }
840
841 const std::vector<mp_word_t>& const_vector() const noexcept {
842 return m_reg;
843 }
844
845 mp_word_t get_word_at(size_t n) const noexcept {
846 if ( n < m_reg.size() ) {
847 return m_reg[n];
848 }
849 return 0;
850 }
851
852 void set_word_at(size_t i, mp_word_t w) {
853 invalidate_sig_words();
854 if ( i >= m_reg.size() ) {
855 if ( w == 0 ) {
856 return;
857 }
858 grow_to(i + 1);
859 }
860 m_reg[i] = w;
861 }
862
863 void set_words(const mp_word_t w[], size_t len) {
864 invalidate_sig_words();
865 m_reg.assign(w, w + len);
866 }
867
868 void set_to_zero() {
869 m_reg.resize(m_reg.capacity());
870 clear_mem(m_reg.data(), m_reg.size());
871 m_sig_words = 0;
872 }
873
874 void set_size(size_t s) {
875 invalidate_sig_words();
876 clear_mem(m_reg.data(), m_reg.size());
877 m_reg.resize(s + (8 - (s % 8)));
878 }
879
880 void mask_bits(size_t n) noexcept {
881 if ( n == 0 ) { return set_to_zero(); }
882
883 const size_t top_word = n / mp_word_bits;
884
885 // if(top_word < sig_words()) ?
886 if ( top_word < size() ) {
887 const mp_word_t mask = (static_cast<mp_word_t>(1) << (n % mp_word_bits)) - 1;
888 const size_t len = size() - (top_word + 1);
889 if ( len > 0 ) {
890 clear_mem(&m_reg[top_word + 1], len);
891 }
892 m_reg[top_word] &= mask;
893 invalidate_sig_words();
894 }
895 }
896
897 void grow_to(size_t n) const {
898 if ( n > size() ) {
899 if ( n <= m_reg.capacity() ) {
900 m_reg.resize(n);
901 } else {
902 m_reg.resize(n + (8 - (n % 8)));
903 }
904 }
905 }
906
907 size_t size() const noexcept { return m_reg.size(); }
908
909 void shrink_to_fit(size_t min_size = 0) {
910 const size_t words = std::max(min_size, sig_words());
911 m_reg.resize(words);
912 }
913
914 void resize(size_t s) {
915 m_reg.resize(s);
916 }
917
918 void swap(data_t& other) noexcept {
919 m_reg.swap(other.m_reg);
920 std::swap(m_sig_words, other.m_sig_words);
921 }
922
923 void swap(std::vector<mp_word_t>& reg) noexcept {
924 m_reg.swap(reg);
925 invalidate_sig_words();
926 }
927
928 void invalidate_sig_words() const noexcept {
929 m_sig_words = sig_words_npos;
930 }
931
932 size_t sig_words() const noexcept {
933 if ( m_sig_words == sig_words_npos ) {
934 m_sig_words = calc_sig_words();
935 } else {
936 assert(m_sig_words == calc_sig_words());
937 }
938 return m_sig_words;
939 }
940
941 private:
942 static const size_t sig_words_npos = static_cast<size_t>(-1);
943
944 size_t calc_sig_words() const noexcept {
945 const size_t sz = m_reg.size();
946 size_t sig = sz;
947
948 mp_word_t sub = 1;
949
950 for ( size_t i = 0; i != sz; ++i ) {
951 const mp_word_t w = m_reg[sz - i - 1];
952 sub &= ct_is_zero(w);
953 sig -= sub;
954 }
955
956 /*
957 * This depends on the data so is poisoned, but unpoison it here as
958 * later conditionals are made on the size.
959 */
961
962 return sig;
963 }
964
965 mutable std::vector<mp_word_t> m_reg;
966 mutable size_t m_sig_words = sig_words_npos;
967 };
968 data_t m_data;
969 sign_t m_signedness = positive;
970
971 /**
972 * Byte extraction of big-endian value
973 * @param byte_num which byte to extract, 0 == highest byte
974 * @param input the value to extract from
975 * @return byte byte_num of input
976 */
977 template<typename T>
978 static constexpr uint8_t get_byte_var_be(size_t byte_num, T input) noexcept {
979 return static_cast<uint8_t>(input >> (((~byte_num) & (sizeof(T) - 1)) << 3));
980 }
981 /**
982 * Byte extraction of little-endian value
983 * @param byte_num which byte to extract, 0 == lowest byte
984 * @param input the value to extract from
985 * @return byte byte_num of input
986 */
987 template<typename T>
988 static constexpr uint8_t get_byte_var_le(size_t byte_num, T input) noexcept {
989 return static_cast<uint8_t>(input >> (byte_num << 3));
990 }
991
992 /**
993 * Zero out some bytes. Warning: use secure_scrub_memory instead if the
994 * memory is about to be freed or otherwise the compiler thinks it can
995 * elide the writes.
996 *
997 * @param ptr a pointer to memory to zero
998 * @param bytes the number of bytes to zero in ptr
999 */
1000 static constexpr void clear_bytes(void* ptr, size_t bytes) noexcept {
1001 if ( bytes > 0 ) {
1002 std::memset(ptr, 0, bytes);
1003 }
1004 }
1005
1006 /**
1007 * Zero memory before use. This simply calls memset and should not be
1008 * used in cases where the compiler cannot see the call as a
1009 * side-effecting operation (for example, if calling clear_mem before
1010 * deallocating memory, the compiler would be allowed to omit the call
1011 * to memset entirely under the as-if rule.)
1012 *
1013 * @param ptr a pointer to an array of Ts to zero
1014 * @param n the number of Ts pointed to by ptr
1015 */
1016 template<typename T>
1017 static constexpr void clear_mem(T* ptr, size_t n) noexcept {
1018 clear_bytes(ptr, sizeof(T) * n);
1019 }
1020
1021 /**
1022 * Increase internal register buffer to at least n words
1023 * @param n new size of register
1024 */
1025 void grow_to(size_t n) const { m_data.grow_to(n); }
1026
1027 void resize(size_t s) { m_data.resize(s); }
1028
1029 void set_word_at(size_t i, mp_word_t w) {
1030 m_data.set_word_at(i, w);
1031 }
1032
1033 void set_words(const mp_word_t w[], size_t len) {
1034 m_data.set_words(w, len);
1035 }
1036 /**
1037 * Return a mutable pointer to the register
1038 * @result a pointer to the start of the internal register
1039 */
1040 mp_word_t* mutable_data() { return m_data.mutable_data(); }
1041
1042 /**
1043 * Set this number to the value in buf with given byte_len,
1044 * considering the given byte_order.
1045 *
1046 * The value is stored in the local storage format, see \ref bigint_storage_format
1047 */
1048 void binary_decode(const uint8_t buf[], size_t byte_len, const lb_endian_t byte_order) {
1049 const size_t full_words = byte_len / sizeof(mp_word_t);
1050 const size_t extra_bytes = byte_len % sizeof(mp_word_t);
1051
1052 // clear() + setting size
1053 m_signedness = positive;
1054 m_data.set_size(jau::round_up(full_words + (extra_bytes > 0 ? 1U : 0U), 8U));
1055
1056 mp_word_t* sink = m_data.mutable_data();
1057 if ( is_little_endian(byte_order) ) {
1058 // little-endian to local (words arranged as little-endian w/ word itself in native-endian)
1059 for ( size_t i = 0; i < full_words; ++i ) {
1060 sink[i] = jau::get_value<mp_word_t>(buf + sizeof(mp_word_t) * i, byte_order);
1061 }
1062 } else {
1063 // big-endian to local (words arranged as little-endian w/ word itself in native-endian)
1064 for ( size_t i = 0; i < full_words; ++i ) {
1065 sink[i] = jau::get_value<mp_word_t>(buf + byte_len - sizeof(mp_word_t) * (i + 1), byte_order);
1066 }
1067 }
1068 mp_word_t le_w = 0;
1069 if ( is_little_endian(byte_order) ) {
1070 for ( size_t i = 0; i < extra_bytes; ++i ) {
1071 le_w |= mp_word_t(buf[full_words * sizeof(mp_word_t) + i]) << (i * 8); // next lowest byte
1072 }
1073 } else {
1074 for ( size_t i = 0; i < extra_bytes; ++i ) {
1075 le_w = (le_w << 8) | mp_word_t(buf[i]); // buf[0] highest byte
1076 }
1077 }
1078 sink[full_words] = jau::le_to_cpu(le_w);
1079 }
1080
1081 /**
1082 * Set this number to the decoded hex-string value of buf with given str_len,
1083 * considering the given byte_order.
1084 *
1085 * The value is stored in the local storage format, see \ref bigint_storage_format
1086 */
1087 static BigInt hex_decode(const uint8_t buf[], size_t str_len, const lb_endian_t byte_order) {
1088 BigInt r;
1089
1090 std::vector<uint8_t> bin_out;
1091 const auto [str_len2, str_ok] = jau::fromHexString(bin_out, buf, str_len, byte_order, jau::False() /* checkPrefix */);
1092 if ( !str_ok ) {
1093 throw jau::math::MathDomainError("invalid hexadecimal char @ " + std::to_string(str_len2) + "/" + std::to_string(str_len) + " of '" +
1094 std::string(cast_uint8_ptr_to_char(buf), str_len) + "'",
1095 E_FILE_LINE);
1096 }
1097 r.binary_decode(bin_out.data(), bin_out.size(), lb_endian_t::little);
1098 return r;
1099 }
1100
1101 static BigInt dec_decode(const uint8_t buf[], size_t str_len) {
1102 BigInt r;
1103
1104 // This could be made faster using the same trick as to_dec_string
1105 for ( size_t i = 0; i < str_len; ++i ) {
1106 const char c = static_cast<char>(buf[i]);
1107
1108 if ( c < '0' || c > '9' ) {
1109 throw jau::math::MathDomainError("invalid decimal char", E_FILE_LINE);
1110 }
1111 const uint8_t x = c - '0';
1112 assert(x < 10);
1113
1114 r *= 10;
1115 r += x;
1116 }
1117 return r;
1118 }
1119
1120 public:
1121 /**
1122 * Stores this number to the value in buf with given byte_len,
1123 * considering the given byte_order.
1124 *
1125 * The value is read from the local storage in its format, see \ref bigint_storage_format
1126 *
1127 * If byte_len is less than the byt-esize of this integer, i.e. bytes(),
1128 * then it will be truncated.
1129 *
1130 * If byte_len is greater than the byte-size of this integer, i.e. bytes(), it will be zero-padded.
1131 *
1132 * @return actual number of bytes copied, i.e. min(byte_len, bytes());
1133 */
1134 size_t binary_encode(uint8_t output[], size_t byte_len, const lb_endian_t byte_order) const noexcept {
1135 const size_t full_words = byte_len / sizeof(mp_word_t);
1136 const size_t extra_bytes = byte_len % sizeof(mp_word_t);
1137
1138 if ( is_little_endian(byte_order) ) {
1139 // to little-endian from local (words arranged as little-endian w/ word itself in native-endian)
1140 for ( size_t i = 0; i < full_words; ++i ) {
1141 jau::put_value<mp_word_t>(output + i * sizeof(mp_word_t), word_at(i), byte_order);
1142 }
1143 } else {
1144 // to big-endian from local (words arranged as little-endian w/ word itself in native-endian)
1145 for ( size_t i = 0; i < full_words; ++i ) {
1146 jau::put_value<mp_word_t>(output + byte_len - (i + 1) * sizeof(mp_word_t), word_at(i), byte_order);
1147 }
1148 }
1149 if ( extra_bytes > 0 ) {
1150 const mp_word_t le_w = jau::cpu_to_le(word_at(full_words));
1151 if ( is_little_endian(byte_order) ) {
1152 for ( size_t i = 0; i < extra_bytes; ++i ) {
1153 output[full_words * sizeof(mp_word_t) + i] = get_byte_var_le(i, le_w); // next lowest byte
1154 }
1155 } else {
1156 for ( size_t i = 0; i < extra_bytes; ++i ) {
1157 output[extra_bytes - 1 - i] = get_byte_var_le(i, le_w); // output[0] highest byte
1158 }
1159 }
1160 }
1161 return extra_bytes + full_words * sizeof(mp_word_t);
1162 }
1163
1164 private:
1165 size_t top_bits_free() const noexcept {
1166 const size_t words = sig_words();
1167
1168 const mp_word_t top_word = word_at(words - 1);
1169 const size_t bits_used = jau::high_bit(top_word);
1170 CT::unpoison(bits_used);
1171 return mp_word_bits - bits_used;
1172 }
1173
1174 /**
1175 * Compares this instance against other, considering sign depending on check_signs
1176 * Returns
1177 * - -1 if this < other
1178 * - 0 if this == other
1179 * - 1 if this > other
1180 */
1181 int cmp(const BigInt& other, bool check_signs = true) const noexcept {
1182 if ( check_signs ) {
1183 if ( other.is_positive() && this->is_negative() ) {
1184 return -1;
1185 }
1186 if ( other.is_negative() && this->is_positive() ) {
1187 return 1;
1188 }
1189 if ( other.is_negative() && this->is_negative() ) {
1190 return (-ops::bigint_cmp(this->data(), this->size(),
1191 other.data(), other.size()));
1192 }
1193 }
1194 return ops::bigint_cmp(this->data(), this->size(),
1195 other.data(), other.size());
1196 }
1197
1198 bool is_equal(const BigInt& other) const noexcept {
1199 if ( this->sign() != other.sign() ) {
1200 return false;
1201 }
1202 return ops::bigint_ct_is_eq(this->data(), this->sig_words(),
1203 other.data(), other.sig_words())
1204 .is_set();
1205 }
1206
1207 bool is_less_than(const BigInt& other) const noexcept {
1208 if ( this->is_negative() && other.is_positive() ) {
1209 return true;
1210 }
1211 if ( this->is_positive() && other.is_negative() ) {
1212 return false;
1213 }
1214 if ( other.is_negative() && this->is_negative() ) {
1215 return ops::bigint_ct_is_lt(other.data(), other.sig_words(),
1216 this->data(), this->sig_words())
1217 .is_set();
1218 }
1219 return ops::bigint_ct_is_lt(this->data(), this->sig_words(),
1220 other.data(), other.sig_words())
1221 .is_set();
1222 }
1223
1224 BigInt& add(const mp_word_t y[], size_t y_words, sign_t y_sign) {
1225 const size_t x_sw = sig_words();
1226 grow_to(std::max(x_sw, y_words) + 1);
1227
1228 if ( sign() == y_sign ) {
1229 ops::bigint_add2(mutable_data(), size() - 1, y, y_words);
1230 } else {
1231 const int32_t relative_size = ops::bigint_cmp(data(), x_sw, y, y_words);
1232
1233 if ( relative_size >= 0 ) {
1234 // *this >= y
1235 ops::bigint_sub2(mutable_data(), x_sw, y, y_words);
1236 } else {
1237 // *this < y
1238 ops::bigint_sub2_rev(mutable_data(), y, y_words);
1239 }
1240 // this->sign_fixup(relative_size, y_sign);
1241 if ( relative_size < 0 ) {
1242 set_sign(y_sign);
1243 } else if ( relative_size == 0 ) {
1245 }
1246 }
1247 return (*this);
1248 }
1249
1250 BigInt& operator+=(mp_word_t y) noexcept {
1251 return add(&y, 1, sign_t::positive);
1252 }
1253 BigInt& operator-=(mp_word_t y) noexcept {
1254 return add(&y, 1, sign_t::negative);
1255 }
1256
1257 static BigInt add2(const BigInt& x, const mp_word_t y[], size_t y_words, sign_t y_sign) {
1258 const size_t x_sw = x.sig_words();
1259
1260 BigInt z = BigInt::with_capacity(std::max(x_sw, y_words) + 1);
1261
1262 if ( x.sign() == y_sign ) {
1263 ops::bigint_add3(z.mutable_data(), x.data(), x_sw, y, y_words);
1264 z.set_sign(x.sign());
1265 } else {
1266 const int32_t relative_size = ops::bigint_sub_abs(z.mutable_data(), x.data(), x_sw, y, y_words);
1267
1268 // z.sign_fixup(relative_size, y_sign);
1269 if ( relative_size < 0 ) {
1270 z.set_sign(y_sign);
1271 } else if ( relative_size == 0 ) {
1272 z.set_sign(positive);
1273 } else {
1274 z.set_sign(x.sign());
1275 }
1276 }
1277 return z;
1278 }
1279
1280 BigInt& mul(const BigInt& y, std::vector<mp_word_t>& ws) noexcept {
1281 const size_t x_sw = sig_words();
1282 const size_t y_sw = y.sig_words();
1283 set_sign((sign() == y.sign()) ? positive : negative);
1284
1285 if ( x_sw == 0 || y_sw == 0 ) {
1286 clear();
1288 } else if ( x_sw == 1 && y_sw ) {
1289 grow_to(y_sw + 1);
1290 ops::bigint_linmul3(mutable_data(), y.data(), y_sw, word_at(0));
1291 } else if ( y_sw == 1 && x_sw ) {
1292 mp_word_t carry = ops::bigint_linmul2(mutable_data(), x_sw, y.word_at(0));
1293 set_word_at(x_sw, carry);
1294 } else {
1295 const size_t new_size = x_sw + y_sw + 1;
1296 // ws.resize(new_size);
1297 (void)ws;
1298 std::vector<mp_word_t> z_reg(new_size);
1299
1300 ops::basecase_mul(z_reg.data(), z_reg.size(), data(), x_sw, y.data(), y_sw);
1301
1302 this->swap_reg(z_reg);
1303 }
1304
1305 return (*this);
1306 }
1307
1309 const size_t x_sw = sig_words();
1310 BigInt z = BigInt::with_capacity(x_sw + 1);
1311
1312 if ( x_sw && y ) {
1313 ops::bigint_linmul3(z.mutable_data(), data(), x_sw, y);
1314 z.set_sign(sign());
1315 }
1316 return z;
1317 }
1318
1319 void cond_flip_sign(bool predicate) noexcept {
1320 // This code is assuming Negative == 0, Positive == 1
1321 const auto mask = CT::Mask<uint8_t>::expand(predicate);
1322 const uint8_t current_sign = static_cast<uint8_t>(sign());
1323 const uint8_t new_sign = mask.select(current_sign ^ 1, current_sign);
1324 set_sign(static_cast<sign_t>(new_sign));
1325 }
1326
1327 /**
1328 * Return *this % mod
1329 *
1330 * Assumes that *this is (if anything) only slightly larger than
1331 * mod and performs repeated subtractions. It should not be used if
1332 * *this is much larger than mod, instead use modulo operator.
1333 */
1334 inline size_t reduce_below(const BigInt& p, std::vector<mp_word_t>& ws) {
1335 if ( p.is_negative() || this->is_negative() ) {
1336 std::string msg;
1337 if ( p.is_negative() ) {
1338 msg.append("p < 0");
1339 }
1340 if ( this->is_negative() ) {
1341 if ( msg.length() > 0 ) {
1342 msg.append(" and ");
1343 }
1344 msg.append("*this < 0");
1345 }
1346 throw jau::math::MathDomainError(msg, E_FILE_LINE);
1347 }
1348 const size_t p_words = p.sig_words();
1349
1350 if ( size() < p_words + 1 ) {
1351 grow_to(p_words + 1);
1352 }
1353 if ( ws.size() < p_words + 1 ) {
1354 ws.resize(p_words + 1);
1355 }
1356 clear_mem(ws.data(), ws.size());
1357
1358 size_t reductions = 0;
1359
1360 for ( ;; ) {
1361 mp_word_t borrow = ops::bigint_sub3(ws.data(), data(), p_words + 1, p.data(), p_words);
1362 if ( borrow ) {
1363 break;
1364 }
1365 ++reductions;
1366 swap_reg(ws);
1367 }
1368 return reductions;
1369 }
1370
1371 static void sign_fixup(const BigInt& x, const BigInt& y, BigInt& q, BigInt& r) {
1372 q.cond_flip_sign(x.sign() != y.sign());
1373
1374 if ( x.is_negative() && r.is_nonzero() ) {
1375 q -= 1;
1376 r = y.abs() - r;
1377 }
1378 }
1379
1380 static bool division_check(mp_word_t q, mp_word_t y2, mp_word_t y1,
1381 mp_word_t x3, mp_word_t x2, mp_word_t x1) noexcept {
1382 /*
1383 Compute (y3,y2,y1) = (y2,y1) * q
1384 and return true if (y3,y2,y1) > (x3,x2,x1)
1385 */
1386
1387 mp_word_t y3 = 0;
1388 y1 = ops::word_madd2(q, y1, y3);
1389 y2 = ops::word_madd2(q, y2, y3);
1390
1391 const mp_word_t x[3] = { x1, x2, x3 };
1392 const mp_word_t y[3] = { y1, y2, y3 };
1393
1394 return ops::bigint_ct_is_lt(x, 3, y, 3).is_set();
1395 }
1396
1397 /*
1398 * Solve x = q * y + r
1399 *
1400 * See Handbook of Applied Cryptography section 14.2.5
1401 */
1402 static void vartime_divide(const BigInt& x, const BigInt& y_arg, BigInt& q_out, BigInt& r_out) {
1403 if ( y_arg.is_zero() ) {
1404 throw jau::math::MathDivByZeroError("y_arg == 0", E_FILE_LINE);
1405 }
1406 const size_t y_words = y_arg.sig_words();
1407
1408 assert(y_words > 0);
1409
1410 BigInt y = y_arg;
1411
1412 BigInt r = x;
1413 BigInt q = BigInt::zero();
1414 std::vector<mp_word_t> ws;
1415
1416 r.set_sign(BigInt::positive);
1417 y.set_sign(BigInt::positive);
1418
1419 // Calculate shifts needed to normalize y with high bit set
1420 const size_t shifts = y.top_bits_free();
1421
1422 y <<= shifts;
1423 r <<= shifts;
1424
1425 // we know y has not changed size, since we only shifted up to set high bit
1426 const size_t t = y_words - 1;
1427 const size_t n = std::max(y_words, r.sig_words()) - 1; // r may have changed size however
1428 assert(n >= t);
1429
1430 q.grow_to(n - t + 1);
1431
1432 mp_word_t* q_words = q.mutable_data();
1433
1434 BigInt shifted_y = y << (mp_word_bits * (n - t));
1435
1436 // Set q_{n-t} to number of times r > shifted_y
1437 q_words[n - t] = r.reduce_below(shifted_y, ws);
1438
1439 const mp_word_t y_t0 = y.word_at(t);
1440 const mp_word_t y_t1 = y.word_at(t - 1);
1441 assert((y_t0 >> (mp_word_bits - 1)) == 1);
1442
1443 for ( size_t j = n; j != t; --j ) {
1444 const mp_word_t x_j0 = r.word_at(j);
1445 const mp_word_t x_j1 = r.word_at(j - 1);
1446 const mp_word_t x_j2 = r.word_at(j - 2);
1447
1448 mp_word_t qjt = ops::bigint_divop(x_j0, x_j1, y_t0);
1449
1450 qjt = CT::Mask<mp_word_t>::is_equal(x_j0, y_t0).select(mp_word_max, qjt);
1451
1452 // Per HAC 14.23, this operation is required at most twice
1453 qjt -= division_check(qjt, y_t0, y_t1, x_j0, x_j1, x_j2);
1454 qjt -= division_check(qjt, y_t0, y_t1, x_j0, x_j1, x_j2);
1455 assert(division_check(qjt, y_t0, y_t1, x_j0, x_j1, x_j2) == false);
1456
1457 shifted_y >>= mp_word_bits;
1458 // Now shifted_y == y << (BOTAN_MP_WORD_BITS * (j-t-1))
1459
1460 // TODO this sequence could be better
1461 r -= shifted_y * qjt;
1462 qjt -= r.is_negative();
1463 r += shifted_y * static_cast<mp_word_t>(r.is_negative());
1464
1465 q_words[j - t - 1] = qjt;
1466 }
1467
1468 r >>= shifts;
1469
1470 sign_fixup(x, y_arg, q, r);
1471
1472 r_out = r;
1473 q_out = q;
1474 }
1475
1476 BigInt operator/(const mp_word_t& y) const {
1477 if ( y == 0 ) {
1478 throw jau::math::MathDivByZeroError("y == 0", E_FILE_LINE);
1479 }
1480 BigInt q;
1481 mp_word_t r;
1482 ct_divide_word(*this, y, q, r);
1483 return q;
1484 }
1485
1486 static void ct_divide_word(const BigInt& x, mp_word_t y, BigInt& q_out, mp_word_t& r_out) {
1487 if ( y == 0 ) {
1488 throw jau::math::MathDivByZeroError("y == 0", E_FILE_LINE);
1489 }
1490 const size_t x_words = x.sig_words();
1491 const size_t x_bits = x.bits();
1492
1493 BigInt q = BigInt::with_capacity(x_words);
1494 mp_word_t r = 0;
1495
1496 for ( size_t i = 0; i != x_bits; ++i ) {
1497 const size_t b = x_bits - 1 - i;
1498 const bool x_b = x.get_bit(b);
1499
1500 const auto r_carry = CT::Mask<mp_word_t>::expand(r >> (mp_word_bits - 1));
1501
1502 r *= 2;
1503 r += x_b;
1504
1505 const auto r_gte_y = CT::Mask<mp_word_t>::is_gte(r, y) | r_carry;
1506 q.conditionally_set_bit(b, r_gte_y.is_set());
1507 r = r_gte_y.select(r - y, r);
1508 }
1509
1510 if ( x.is_negative() ) {
1511 q.flip_sign();
1512 if ( r != 0 ) {
1513 --q;
1514 r = y - r;
1515 }
1516 }
1517
1518 r_out = r;
1519 q_out = q;
1520 }
1521
1523 if ( mod == 0 ) {
1524 throw jau::math::MathDivByZeroError("mod == 0", E_FILE_LINE);
1525 }
1526 if ( mod == 1 ) {
1527 return 0;
1528 }
1529 mp_word_t remainder = 0;
1530
1531 if ( jau::is_power_of_2(mod) ) {
1532 remainder = (word_at(0) & (mod - 1));
1533 } else {
1534 const size_t sw = sig_words();
1535 for ( size_t i = sw; i > 0; --i ) {
1536 remainder = ops::bigint_modop(remainder, word_at(i - 1), mod);
1537 }
1538 }
1539 if ( remainder && sign() == BigInt::negative ) {
1540 return mod - remainder;
1541 }
1542 return remainder;
1543 }
1544
1545 void append_detail(std::string& s) const noexcept {
1546 s.append(", bits ").append(std::to_string(bits())).append(", ").append(std::to_string(sig_words())).append(" word(s): ");
1547 for ( size_t i = 0; i < sig_words(); ++i ) {
1548 const mp_word_t w = word_at(i);
1550 .append(", ");
1551 }
1552 }
1553 };
1554
1555 /**@}*/
1556} // namespace jau::mp
1557
1558namespace jau {
1559
1560 /** \addtogroup Integer
1561 *
1562 * @{
1563 */
1564
1565 inline mp::BigInt abs(const mp::BigInt& x) noexcept { return x.abs(); }
1566 inline mp::BigInt pow(mp::BigInt b, const mp::BigInt& e) { return b.pow(e); }
1567
1568 inline const mp::BigInt& min(const mp::BigInt& x, const mp::BigInt& y) noexcept {
1569 return x < y ? x : y;
1570 }
1571 inline const mp::BigInt& max(const mp::BigInt& x, const mp::BigInt& y) noexcept {
1572 return x > y ? x : y;
1573 }
1574 inline const mp::BigInt& clamp(const mp::BigInt& x, const mp::BigInt& min_val, const mp::BigInt& max_val) noexcept {
1575 return min(max(x, min_val), max_val);
1576 }
1577
1578 inline mp::BigInt& min(mp::BigInt& x, mp::BigInt& y) noexcept {
1579 return x < y ? x : y;
1580 }
1581 inline mp::BigInt& max(mp::BigInt& x, mp::BigInt& y) noexcept {
1582 return x > y ? x : y;
1583 }
1584 inline mp::BigInt& clamp(mp::BigInt& x, mp::BigInt& min_val, mp::BigInt& max_val) noexcept {
1585 return min(max(x, min_val), max_val);
1586 }
1587
1588 inline mp::BigInt gcd(const mp::BigInt& a, const mp::BigInt& b) noexcept {
1589 mp::BigInt a_ = abs(a);
1590 mp::BigInt b_ = abs(b);
1591 while ( b_.is_nonzero() ) {
1592 const mp::BigInt t = b_;
1593 b_ = a_ % b_;
1594 a_ = t;
1595 }
1596 return a_;
1597 }
1598
1599 /**@}*/
1600} // namespace jau
1601
1602namespace std {
1603 inline std::ostream& operator<<(std::ostream& out, const jau::mp::BigInt& v) {
1604 return out << v.to_dec_string();
1605 }
1606} // namespace std
1607
1608#endif /** JAU_BIG_INT_HPP_ */
#define E_FILE_LINE
static Mask< T > is_gte(T x, T y) noexcept
Return a Mask<T> which is set if x >= y.
Definition ct_utils.hpp:177
static Mask< T > is_equal(T x, T y) noexcept
Return a Mask<T> which is set if x == y.
Definition ct_utils.hpp:145
static Mask< T > expand(T v) noexcept
Return a Mask<T> which is set if v is != 0.
Definition ct_utils.hpp:119
math_error_t::div_by_zero, i.e.
math_error_t::invalid
Arbitrary precision integer type.
Definition big_int.hpp:50
BigInt & set_sign(sign_t sign) noexcept
Set sign of the integer.
Definition big_int.hpp:289
BigInt(uint64_t n)
Create big_int_t from an unsigned 64 bit integer.
Definition big_int.hpp:137
bool is_even() const noexcept
Test if the integer has an even value.
Definition big_int.hpp:352
size_t bits() const noexcept
Returns bit length of this integer.
Definition big_int.hpp:318
std::string to_dec_string(bool add_details=false) const
Definition big_int.hpp:698
bool operator==(const BigInt &b) const noexcept
Definition big_int.hpp:408
bool is_odd() const noexcept
Test if the integer has an odd value.
Definition big_int.hpp:358
bool get_bit(size_t n) const noexcept
Return bit value at specified position.
Definition big_int.hpp:379
BigInt & operator-=(const BigInt &y) noexcept
Definition big_int.hpp:441
BigInt abs() const noexcept
Returns absolute (positive) value of this instance.
Definition big_int.hpp:298
BigInt & operator++() noexcept
std::strong_ordering operator<=>(const BigInt& b) const noexcept { const int r = cmp(b); return 0 == ...
Definition big_int.hpp:421
BigInt operator-() const noexcept
Unary negation operator, returns new negative instance of this.
Definition big_int.hpp:224
BigInt & mod_add(const BigInt &y, const BigInt &mod, std::vector< mp_word_t > &ws)
Set *this to (*this + y) % mod This function assumes *this is >= 0 && < mod.
BigInt pow(BigInt e)
Returns (*this)^e, or pow(*this, e)
Definition big_int.hpp:585
BigInt & operator--() noexcept
Definition big_int.hpp:423
size_t bytes() const
Returns byte length of this integer.
Definition big_int.hpp:315
BigInt & operator<<=(size_t shift) noexcept
Definition big_int.hpp:453
BigInt(const uint8_t buf[], size_t byte_len, const lb_endian_t byte_order)
Create a big_int_t from an integer in a byte array with given byte_len, considering the given byte_or...
Definition big_int.hpp:186
BigInt & operator=(const BigInt &r)=default
BigInt operator>>(size_t shift) const
Definition big_int.hpp:492
std::string to_hex_string(bool add_details=false) const noexcept
Definition big_int.hpp:770
BigInt operator--(int) noexcept
Definition big_int.hpp:431
sign_t reverse_sign() const noexcept
Definition big_int.hpp:271
bool operator<=(const BigInt &b) const noexcept
Definition big_int.hpp:410
BigInt operator+(const BigInt &y) const noexcept
Definition big_int.hpp:445
sign_t
Sign symbol definitions for positive and negative numbers.
Definition big_int.hpp:55
BigInt operator%(const BigInt &mod)
Definition big_int.hpp:560
bool operator>(const BigInt &b) const noexcept
Definition big_int.hpp:413
const mp_word_t * data() const
Return a const pointer to the register.
Definition big_int.hpp:248
bool is_nonzero() const noexcept
Test if the integer is not zero.
Definition big_int.hpp:364
size_t size() const noexcept
Give size of internal register.
Definition big_int.hpp:306
static BigInt one()
Create a 1-value big_int.
Definition big_int.hpp:73
sign_t sign() const noexcept
Return the sign of the integer.
Definition big_int.hpp:266
bool is_negative() const noexcept
Tests if the sign of the integer is negative.
Definition big_int.hpp:254
BigInt & operator>>=(size_t shift) noexcept
Definition big_int.hpp:469
BigInt & rev_sub(const BigInt &y, std::vector< mp_word_t > &ws)
Set *this to y - *this.
BigInt & operator=(BigInt &&other) noexcept
Definition big_int.hpp:200
static BigInt zero()
Create a 0-value big_int.
Definition big_int.hpp:68
void conditionally_set_bit(size_t n, bool set_it) noexcept
Conditionally set bit at specified position.
Definition big_int.hpp:399
size_t sig_words() const noexcept
Return how many words we need to hold this value.
Definition big_int.hpp:312
void clear()
Zeroize the big_int.
Definition big_int.hpp:332
BigInt(std::vector< mp_word_t > &&other_reg) noexcept
Definition big_int.hpp:190
BigInt() noexcept=default
BigInt & operator%=(const BigInt &mod)
Modulo operator.
Definition big_int.hpp:556
bool operator!() const noexcept
!
Definition big_int.hpp:406
BigInt & mod_sub(const BigInt &y, const BigInt &mod, std::vector< mp_word_t > &ws)
Set *this to (*this - y) % mod This function assumes *this is >= 0 && < mod.
bool is_positive() const noexcept
Tests if the sign of the integer is positive.
Definition big_int.hpp:260
bool operator<(const BigInt &b) const noexcept
Definition big_int.hpp:412
bool operator!=(const BigInt &b) const noexcept
Definition big_int.hpp:409
bool operator>=(const BigInt &b) const noexcept
Definition big_int.hpp:411
size_t binary_encode(uint8_t output[], size_t byte_len, const lb_endian_t byte_order) const noexcept
Stores this number to the value in buf with given byte_len, considering the given byte_order.
Definition big_int.hpp:1134
void swap(BigInt &other) noexcept
Swap this value with another.
Definition big_int.hpp:211
int compare(const BigInt &b) const noexcept
Compares this instance against other considering both sign() value Returns.
Definition big_int.hpp:344
static BigInt from_s32(int32_t n)
Create big_int from a signed 32 bit integer.
Definition big_int.hpp:104
BigInt & square(std::vector< mp_word_t > &ws)
Square value of *this.
BigInt & operator/=(const BigInt &y)
Definition big_int.hpp:535
static BigInt from_word(mp_word_t n)
Create big_int from a mp_word_t (limb)
Definition big_int.hpp:94
static BigInt with_capacity(size_t n)
Create big_int of specified size, all zeros.
Definition big_int.hpp:116
bool is_zero() const noexcept
Test if the integer is zero.
Definition big_int.hpp:370
mp_word_t word_at(size_t n) const noexcept
Return the mp_word_t at a specified position of the internal register.
Definition big_int.hpp:240
BigInt operator/(const BigInt &y) const
Definition big_int.hpp:544
BigInt operator-(const BigInt &y) const noexcept
Definition big_int.hpp:449
BigInt & operator*=(const BigInt &y) noexcept
Definition big_int.hpp:512
BigInt & mod_mul(uint8_t y, const BigInt &mod, std::vector< mp_word_t > &ws)
Set *this to (*this * y) % mod This function assumes *this is >= 0 && < mod y should be small,...
uint8_t byte_at(size_t n) const noexcept
Definition big_int.hpp:230
BigInt mod_pow(BigInt e, const BigInt &m)
Returns (*this)^e % m, or pow(*this, e) % m.
Definition big_int.hpp:619
BigInt & flip_sign() noexcept
Flip the sign of this big_int.
Definition big_int.hpp:281
BigInt & operator+=(const BigInt &y) noexcept
Definition big_int.hpp:437
void set_bit(size_t n) noexcept
Set bit at specified position.
Definition big_int.hpp:387
BigInt operator*(const BigInt &y) noexcept
Definition big_int.hpp:517
BigInt operator<<(size_t shift) const
Definition big_int.hpp:481
BigInt(const std::string &str)
Construct a big_int_t from a string encoded as hexadecimal or decimal.
Definition big_int.hpp:153
static BigInt from_u64(uint64_t n)
Create big_int from an unsigned 64 bit integer.
Definition big_int.hpp:79
BigInt operator++(int) noexcept
Definition big_int.hpp:425
BigInt(BigInt &&other) noexcept
Definition big_int.hpp:194
static BigInt power_of_2(size_t n)
Create a power of two.
Definition big_int.hpp:127
constexpr bool is_little_endian() noexcept
Evaluates true if platform is running in little endian mode, i.e.
constexpr std::enable_if_t< std::is_standard_layout_v< T >, T > get_value(uint8_t const *buffer) noexcept
Returns a T value from the given byte address using packed_t to resolve a potential memory alignment ...
char * cast_uint8_ptr_to_char(uint8_t *b) noexcept
constexpr uint16_t le_to_cpu(uint16_t const l) noexcept
lb_endian_t
Simplified reduced endian type only covering little- and big-endian.
constexpr std::enable_if_t< std::is_standard_layout_v< T >, void > put_value(uint8_t *buffer, const T &v) noexcept
Put the given T value into the given byte address using packed_t to resolve a potential memory alignm...
constexpr uint16_t cpu_to_le(uint16_t const h) noexcept
const uint8_t * cast_char_ptr_to_uint8(const char *s) noexcept
@ little
Identifier for little endian, equivalent to endian::little.
@ big
Identifier for big endian, equivalent to endian::big.
constexpr T ct_is_zero(T x)
Returns ~0 (2-complement) if arg is zero, otherwise 0 (w/o branching) in O(1) and constant time (CT).
constexpr bool value(const Bool rhs) noexcept
constexpr Bool False() noexcept
void swap(cow_darray< Value_type, Size_type, Alloc_type > &rhs, cow_darray< Value_type, Size_type, Alloc_type > &lhs) noexcept
constexpr T clamp(const T x, const T min_val, const T max_val) noexcept
Returns constrained integral value to lie between given min- and maximum value (w/ branching) in O(1)...
constexpr T round_up(const T n, const U align_to)
Round up w/ branching in O(1)
Definition int_math.hpp:96
constexpr nsize_t high_bit(T x)
Return the index of the highest set bit w/ branching (loop) in O(n/2).
Definition int_math.hpp:203
constexpr size_t digits(const T x, const nsize_t radix) noexcept
Returns the number of digits of the given unsigned integral value number and the given radix.
Definition int_math.hpp:476
constexpr T gcd(T a, T b) noexcept
Returns the greatest common divisor (GCD) of the two given integer values following Euclid's algorith...
Definition int_math.hpp:370
mp::BigInt pow(mp::BigInt b, const mp::BigInt &e)
Definition big_int.hpp:1566
constexpr bool is_power_of_2(const T x) noexcept
Power of 2 test in O(1), i.e.
Definition int_math.hpp:134
constexpr T min(const T x, const T y) noexcept
Returns the minimum of two integrals (w/ branching) in O(1)
constexpr T max(const T x, const T y) noexcept
Returns the maximum of two integrals (w/ branching) in O(1)
constexpr T abs(const T x) noexcept
Returns the absolute value of an arithmetic number (w/ branching) in O(1)
SizeBoolPair fromHexString(std::vector< uint8_t > &out, const uint8_t hexstr[], const size_t hexstr_len, const lb_endian_t byteOrder=lb_endian_t::big, const Bool checkPrefix=Bool::True) noexcept
Converts a given hexadecimal string representation into a byte vector, lsb-first.
std::string toHexString(const void *data, const nsize_t length, const lb_endian_t byteOrder=lb_endian_t::big, const LoUpCase capitalization=LoUpCase::lower, const PrefixOpt prefix=PrefixOpt::prefix) noexcept
Produce a hexadecimal string representation of the given lsb-first byte values.
void unpoison(const T *p, size_t n)
Definition ct_utils.hpp:54
CT::Mask< mp_word_t > bigint_ct_is_lt(const mp_word_t x[], size_t x_size, const mp_word_t y[], size_t y_size, bool lt_or_equal=false) noexcept
Compare x and y Return ~0 if x[0:x_size] < y[0:y_size] or 0 otherwise If lt_or_equal is true,...
CT::Mask< mp_word_t > bigint_ct_is_eq(const mp_word_t x[], size_t x_size, const mp_word_t y[], size_t y_size) noexcept
int bigint_cmp(const mp_word_t x[], size_t x_size, const mp_word_t y[], size_t y_size) noexcept
Compare unsigned x and y mp_word_t.
mp_word_t bigint_divop(mp_word_t n1, mp_word_t n0, mp_word_t d)
Computes ((n1<<bits) + n0) / d.
int32_t bigint_sub_abs(mp_word_t z[], const mp_word_t x[], size_t x_size, const mp_word_t y[], size_t y_size) noexcept
Set z to abs(x-y), ie if x >= y, then compute z = x - y Otherwise compute z = y - x No borrow is poss...
mp_word_t bigint_add2(mp_word_t x[], size_t x_size, const mp_word_t y[], size_t y_size) noexcept
Two operand addition with carry out.
void bigint_shr1(mp_word_t x[], size_t x_size, size_t word_shift, size_t bit_shift) noexcept
mp_word_t bigint_linmul2(mp_word_t x[], size_t x_size, mp_word_t y) noexcept
void bigint_linmul3(mp_word_t z[], const mp_word_t x[], size_t x_size, mp_word_t y) noexcept
mp_word_t bigint_sub3(mp_word_t z[], const mp_word_t x[], size_t x_size, const mp_word_t y[], size_t y_size) noexcept
Three operand subtraction.
void bigint_shl2(mp_word_t y[], const mp_word_t x[], size_t x_size, size_t word_shift, size_t bit_shift) noexcept
void bigint_add3(mp_word_t z[], const mp_word_t x[], size_t x_size, const mp_word_t y[], size_t y_size) noexcept
Three operand addition.
mp_word_t bigint_sub2(mp_word_t x[], size_t x_size, const mp_word_t y[], size_t y_size) noexcept
Two operand subtraction.
mp_word_t bigint_modop(mp_word_t n1, mp_word_t n0, mp_word_t d)
Compute ((n1<<bits) + n0) % d.
mp_word_t word_madd2(mp_word_t a, mp_word_t b, mp_word_t &c) noexcept
Word Multiply/Add.
void bigint_shl1(mp_word_t x[], size_t x_size, size_t x_words, size_t word_shift, size_t bit_shift) noexcept
void basecase_mul(mp_word_t z[], size_t z_size, const mp_word_t x[], size_t x_size, const mp_word_t y[], size_t y_size) noexcept
void bigint_shr2(mp_word_t y[], const mp_word_t x[], size_t x_size, size_t word_shift, size_t bit_shift) noexcept
void bigint_sub2_rev(mp_word_t x[], const mp_word_t y[], size_t y_size) noexcept
Two operand subtraction, x = y - x; assumes y >= x.
big_int_t (this file) (c) 2024 Gothel Software e.K.
Definition big_int.hpp:33
jau::uint_bytes< impl::best_word_byte_size()>::type mp_word_t
constexpr const size_t mp_word_bits
constexpr const mp_word_t mp_word_max
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition backtrace.hpp:32
STL namespace.
std::ostream & operator<<(std::ostream &os, const jau::fraction_timespec &v) noexcept
Output stream operator for jau::fraction_timespec.