snippets 0.1.0
Loading...
Searching...
No Matches
big_integer.hpp
Go to the documentation of this file.
1#ifndef BIG_INTEGER_H
2#define BIG_INTEGER_H
3
4#include <ranges>
5#include <vector>
6#include <string>
7#include <algorithm>
8#include <cstdint>
9#include <concepts>
10#include <optional>
11#include <iostream>
12#include <numeric>
13
14namespace hsc_snippets {
22 class BigInteger {
23 private:
24 std::vector<std::uint8_t> digits;
25 bool isNegative = false;
26
27 // Helper function to remove leading zeros
28 void removeLeadingZeros() {
29 while (digits.size() > 1 && digits.back() == 0) {
30 digits.pop_back();
31 }
32 if (digits.size() == 1 && digits[0] == 0) {
33 isNegative = false; // Ensure 0 is always positive
34 }
35 }
36
37 [[nodiscard]] bool _isAbsoluteGreaterOrEqual(const BigInteger &other) const {
38 if (digits.size() != other.digits.size()) {
39 return digits.size() > other.digits.size();
40 }
41
42 for (int i = static_cast<int>(digits.size()) - 1; i >= 0; --i) {
43 if (digits[i] != other.digits[i]) {
44 return digits[i] > other.digits[i];
45 }
46 }
47
48 return true; // Equal in absolute value
49 }
50
51#pragma region private constructors
52
53 BigInteger() = default;
54
55 BigInteger(bool isNegative, const std::vector<std::uint8_t> &digits)
56 : digits(digits), isNegative(isNegative) {
57 }
58
59#pragma endregion
60
61#pragma region add&subtract
62
63 [[nodiscard]] BigInteger _add(const BigInteger &other) const {
64 BigInteger result;
65 result.digits.clear(); // Ensure the result starts with an empty digit vector
66
67 size_t maxLength = std::max(digits.size(), other.digits.size());
68 int carry = 0;
69
70 for (size_t i = 0; i < maxLength || carry; ++i) {
71 int digitSum = carry;
72
73 if (i < digits.size()) {
74 digitSum += digits[i];
75 }
76 if (i < other.digits.size()) {
77 digitSum += other.digits[i];
78 }
79
80 result.digits.push_back(digitSum % 10); // Store the single digit
81 carry = digitSum / 10; // Calculate carry for the next digit
82 }
83
84 return result;
85 }
86
87 [[nodiscard]] BigInteger _subtract(const BigInteger &other) const {
88 BigInteger result;
89 result.digits.clear();
90
91 int borrow = 0;
92 for (size_t i = 0; i < digits.size(); ++i) {
93 int digitSub = digits[i] - borrow - (i < other.digits.size() ? other.digits[i] : 0);
94 borrow = 0;
95
96 if (digitSub < 0) {
97 digitSub += 10;
98 borrow = 1;
99 }
100
101 result.digits.push_back(digitSub);
102 }
103
104 result.removeLeadingZeros(); // Defined previously to remove leading zeros
105
106 return result;
107 }
108
109#pragma endregion
110
111 public:
112#pragma region conversion
113
120 static BigInteger parse(const std::string &number) {
121 BigInteger result;
122 size_t start = 0;
123
124 if (!number.empty() && number[0] == '-') {
125 result.isNegative = true;
126 start = 1;
127 }
128
129 for (size_t i = number.size(); i > start; --i) {
130 char c = number[i - 1];
131 if (c < '0' || c > '9') {
132 throw std::invalid_argument("Invalid character in number string.");
133 }
134 result.digits.push_back(c - '0');
135 }
136 result.removeLeadingZeros();
137 return result;
138 }
139
146 template<std::integral T>
147 static BigInteger from_integer(T number) {
148 if (number == std::numeric_limits<T>::min()) {
150 }
151
152 BigInteger result;
153 if (number < 0) {
154 result.isNegative = true;
155 number = -number; // Make number positive for processing
156 }
157
158 do {
159 result.digits.push_back(number % 10);
160 number /= 10;
161 } while (number > 0);
162
163 return result;
164 }
165
170 [[nodiscard]] std::string to_string() const {
171 if (digits.empty()) {
172 return "0";
173 }
174
175 std::string str;
176 if (isNegative) {
177 str += '-';
178 }
179
180 str.reserve(digits.size() + str.size());
181 for (unsigned char digit: std::ranges::reverse_view(digits)) {
182 str += static_cast<char>('0' + digit);
183 }
184 return str;
185 }
186
197 template<std::integral T>
198 std::optional<T> to() const {
199 T result = 0;
200 T multiplier = 1;
201
202 if constexpr (!std::is_signed_v<T>) {
203 if (isNegative) {
204 return std::nullopt;
205 }
206 }
207
208 for (size_t i = 0; i < digits.size(); ++i) {
209 T digitValue = static_cast<T>(digits[i]);
210
211 // Check for potential overflow when adding the digit value
212 T tempResult = result + digitValue * multiplier;
213 if (tempResult < result) {
214 return std::nullopt; // Overflow occurred during addition
215 }
216 result = tempResult;
217
218 // Check for potential overflow in the next multiplier
219 if (i < digits.size() - 1) {
220 if (std::numeric_limits<T>::max() / 10 < multiplier) {
221 return std::nullopt; // Next multiplier would overflow
222 }
223 multiplier *= 10;
224 }
225 }
226
227 if constexpr (std::is_signed_v<T>) {
228 if (isNegative) {
229 if (result == 0 || result > std::numeric_limits<T>::max()) {
230 return std::nullopt; // Can't represent this negative value in T
231 }
232 result = -result; // Negate the result
233 }
234 }
235
236 return result;
237 }
238
239#pragma endregion
240
241#pragma region singletons
242
248 static const BigInteger &zero() {
249 static BigInteger zeroInstance(false, {0}); // Zero is not negative and has a single digit '0'
250 return zeroInstance;
251 }
252
257 static const BigInteger &one() {
258 static BigInteger oneInstance(false, {1}); // One is not negative and has a single digit '1'
259 return oneInstance;
260 }
261
266 static const BigInteger &two() {
267 static BigInteger twoInstance(false, {2}); // Two is not negative and has a single digit '2'
268 return twoInstance;
269 }
270
276 template<std::integral T>
278 static BigInteger minValue = []() -> BigInteger {
279 if constexpr (std::is_signed_v<T>) {
280 T number = std::numeric_limits<T>::min();
281 BigInteger result;
282 result.isNegative = true;
283
284 // Special handling for the most negative number, which cannot be directly negated in signed types
285 if (number == std::numeric_limits<T>::min()) {
286 result.digits.push_back(-(number % 10));
287 number /= 10;
288 number = -number;
289 }
290
291 while (number > 0) {
292 result.digits.push_back(number % 10);
293 number /= 10;
294 }
295
296 return result;
297 } else {
298 // For unsigned types, the minimum value is always 0
299 return BigInteger::zero();
300 }
301 }();
302
303 return minValue;
304 }
305
311 template<std::integral T>
313 static BigInteger maxValue = []() -> BigInteger {
314 T number = std::numeric_limits<T>::max();
315 BigInteger result;
316 result.isNegative = false;
317
318 while (number > 0) {
319 result.digits.push_back(number % 10);
320 number /= 10;
321 }
322
323 return result;
324 }();
325
326 return maxValue; // Return a const reference to the singleton instance
327 }
328
329#pragma endregion
330
331 // Negates the BigInteger instance.
332 void negate() {
333 if (digits.size() == 1 && digits[0] == 0) {
334 // Zero remains non-negative
335 isNegative = false;
336 } else {
337 isNegative = !isNegative;
338 }
339 }
340
345 [[nodiscard]] BigInteger abs() const {
346 BigInteger result = *this;
347 result.isNegative = false; // Remove sign
348 return result;
349 }
350
351
352 // Unary minus operator to return the negated value of the BigInteger instance.
354 BigInteger result = *this;
355 result.negate();
356 return result;
357 }
358
359
360#pragma region arithmetic
361
367 BigInteger operator+(const BigInteger &other) const {
368 BigInteger result;
369
370 if (isNegative == other.isNegative) {
371 result = _add(other);
372 result.isNegative = isNegative; // Result will have the same sign
373 } else {
374 // Determine which number is greater in absolute value
375 if (_isAbsoluteGreaterOrEqual(other)) {
376 result = _subtract(other);
377 result.isNegative = isNegative;
378 } else {
379 result = other._subtract(*this);
380 result.isNegative = other.isNegative;
381 }
382 }
383
384 result.removeLeadingZeros(); // Ensure there are no leading zeros
385 if (result.digits.size() == 1 && result.digits[0] == 0) {
386 result.isNegative = false; // Explicitly set the sign to non-negative for zero
387 }
388
389 return result;
390 }
391
397 BigInteger operator-(const BigInteger &other) const {
398 BigInteger negatedOther = other;
399 negatedOther.isNegative = !other.isNegative; // Negate the second operand
400 return *this + negatedOther; // Use the + operator
401 }
402
418 BigInteger operator*(const BigInteger &other) const {
419 // Check if either operand is zero
420 if (*this == BigInteger::zero() || other == BigInteger::zero()) {
421 return BigInteger::zero();
422 }
423
424 size_t totalLength = digits.size() + other.digits.size();
425 BigInteger result;
426 result.digits.resize(totalLength, 0); // Prepare the result vector with the appropriate size
427
428 // Perform the multiplication algorithm
429 for (size_t i = 0; i < digits.size(); ++i) {
430 int carry = 0;
431 for (size_t j = 0; j < other.digits.size() || carry; ++j) {
432 int product = carry + result.digits[i + j];
433 if (j < other.digits.size()) {
434 product += digits[i] * other.digits[j];
435 }
436 result.digits[i + j] = product % 10;
437 carry = product / 10;
438 }
439 }
440
441 result.removeLeadingZeros(); // Ensure the result is correctly processed for leading zeros
442 result.isNegative = isNegative != other.isNegative; // Determine the sign of the result
443
444 return result;
445 }
446
464 BigInteger operator/(const BigInteger &other) const {
465 if (other == BigInteger::zero()) {
466 throw std::runtime_error("Division by zero");
467 }
468
469 BigInteger dividend = this->abs();
470 BigInteger divisor = other.abs();
471 BigInteger quotient;
472 BigInteger current;
473 quotient.digits.resize(dividend.digits.size(), 0);
474
475 for (int i = static_cast<int>(dividend.digits.size()) - 1; i >= 0; --i) {
476 current = current * BigInteger::from_integer(10) + BigInteger::from_integer(dividend.digits[i]);
477 int count = 0;
478 while (current >= divisor) {
479 current = current - divisor;
480 ++count;
481 }
482 quotient.digits[i] = count;
483 }
484
485 quotient.removeLeadingZeros(); // Remove any leading zeros
486
487 // After calculating the quotient, if it is zero, its sign should be positive
488 if (quotient == BigInteger::zero()) {
489 quotient.isNegative = false;
490 } else {
491 // Otherwise, the sign of the quotient is determined by the signs of the operands
492 quotient.isNegative = isNegative != other.isNegative;
493 }
494
495 return quotient;
496 }
497
498
511 // Use the existing addition logic (_add method, or however addition is implemented in your class)
512 *this = *this + other; // This line might change depending on how you've implemented addition internally
513 return *this; // Return the current instance after modification
514 }
515
528 // Use the existing subtraction logic (_subtract method, or however subtraction is implemented in your class)
529 *this = *this - other; // This line might change depending on how you've implemented subtraction internally
530 return *this; // Return the current instance after modification
531 }
532
533
541 *this = *this * other;
542 return *this;
543 }
544
552 *this = *this / other;
553 return *this;
554 }
555
556
568 BigInteger operator%(const BigInteger &other) const {
569 if (other == BigInteger::zero()) {
570 throw std::runtime_error("Modulo by zero");
571 }
572
573 BigInteger quotient = *this / other;
574 BigInteger product = quotient * other;
575 BigInteger remainder = *this - product;
576
577 return remainder;
578 }
579
580#pragma endregion
581
597 [[nodiscard]] std::pair<BigInteger, BigInteger> divmod(const BigInteger &other) const {
598 if (other == BigInteger::zero()) {
599 throw std::runtime_error("Modulo by zero");
600 }
601
602 BigInteger quotient = *this / other;
603 BigInteger product = quotient * other;
604 BigInteger remainder = *this - product;
605
606 return std::make_pair(quotient, remainder);
607 }
608
615 static BigInteger max(const BigInteger &a, const BigInteger &b) {
616 if (a.isNegative && !b.isNegative) return b; // a is negative, b is positive
617 if (!a.isNegative && b.isNegative) return a; // a is positive, b is negative
618
619 // Both are positive or negative
620 if (a._isAbsoluteGreaterOrEqual(b))
621 return a.isNegative ? b : a; // If both have same sign, return based on absolute value
622 return a.isNegative ? a : b;
623 }
624
631 static BigInteger min(const BigInteger &a, const BigInteger &b) {
632 if (a.isNegative && !b.isNegative) return a; // a is negative, b is positive
633 if (!a.isNegative && b.isNegative) return b; // a is positive, b is negative
634
635 // Both are positive or negative
636 if (a._isAbsoluteGreaterOrEqual(b))
637 return a.isNegative ? a : b; // If both have same sign, return based on absolute value
638 return a.isNegative ? b : a;
639 }
640
641#pragma region increment & decrement
642
643 // Prefix increment
645 *this = *this + BigInteger::one();
646 return *this;
647 }
648
649 // Postfix increment
651 BigInteger temp = *this;
652 ++(*this); // Use prefix increment
653 return temp;
654 }
655
656 // Prefix decrement
658 *this = *this - BigInteger::one();
659 return *this;
660 }
661
662 // Postfix decrement
664 BigInteger temp = *this;
665 --(*this); // Use prefix decrement
666 return temp;
667 }
668
669#pragma endregion
670
671
672#pragma region comparators
673
674 // Equality comparator
675 bool operator==(const BigInteger &other) const {
676 return isNegative == other.isNegative && digits == other.digits;
677 }
678
679 // Inequality comparator
680 bool operator!=(const BigInteger &other) const {
681 return !(*this == other);
682 }
683
684 // Less than comparator
685 bool operator<(const BigInteger &other) const {
686 if (isNegative != other.isNegative) {
687 return isNegative; // If *this is negative and other is positive, *this is smaller
688 }
689
690 if (digits.size() != other.digits.size()) {
691 return isNegative ? digits.size() > other.digits.size() : digits.size() < other.digits.size();
692 }
693
694 for (int i = static_cast<int>(digits.size()) - 1; i >= 0; --i) {
695 if (digits[i] != other.digits[i]) {
696 return isNegative ? digits[i] > other.digits[i] : digits[i] < other.digits[i];
697 }
698 }
699
700 return false; // Numbers are equal, so *this is not less than other
701 }
702
703 // Less than or equal comparator
704 bool operator<=(const BigInteger &other) const {
705 return *this == other || *this < other;
706 }
707
708 // Greater than comparator
709 bool operator>(const BigInteger &other) const {
710 return !(*this <= other);
711 }
712
713 // Greater than or equal comparator
714 bool operator>=(const BigInteger &other) const {
715 return !(*this < other);
716 }
717
718#pragma endregion
719
731 static BigInteger gcd(const BigInteger &a, const BigInteger &b) {
733 BigInteger x = a.abs(); // Work with absolute values to ensure non-negativity
734 BigInteger y = b.abs();
735
736 while (y != zero) {
737 BigInteger temp = y;
738 y = x % y; // Replace y with the remainder of x divided by y
739 x = temp; // Replace x with the old value of y
740 }
741
742 return x; // x contains the gcd when y becomes zero
743 }
744
745
757 static BigInteger lcm(const BigInteger &a, const BigInteger &b) {
758 if (a == BigInteger::zero() && b == BigInteger::zero()) {
759 return BigInteger::zero(); // LCM of zero and zero is defined as zero
760 }
761
762 BigInteger product = a * b; // Calculate the product of a and b
763 BigInteger gcdValue = gcd(a, b); // Calculate the gcd of a and b
764 BigInteger lcmValue = product / gcdValue; // Calculate the lcm using the product and gcd
765
766 return lcmValue.abs(); // Ensure the lcm is non-negative
767 }
768
775 static BigInteger factorial(unsigned int n) {
776 BigInteger result = BigInteger::from_integer(1); // Start with 1 as 0! = 1 and 1! = 1
777
778 for (unsigned int i = 2; i <= n; ++i) {
779 result = result * BigInteger::from_integer(i); // Multiply result by i for each step from 2 to n
780 }
781
782 return result;
783 }
784
792 static BigInteger pow(const BigInteger &a, unsigned int n) {
793 if (n == 0) {
794 return BigInteger::from_integer(1); // a^0 = 1 for any a
795 }
796
798 BigInteger base = a;
799 while (n > 0) {
800 if (n % 2 == 1) {
801 // If n is odd
802 result = result * base; // Multiply result by current base
803 }
804 base = base * base; // Square the base
805 n /= 2; // Halve the exponent
806 }
807
808 return result;
809 }
810
818 static BigInteger sqrt(const BigInteger &number) {
819 if (number.isNegative) {
820 throw std::runtime_error("Square root of a negative number is not defined.");
821 }
822
823 if (number == zero() || number == one()) {
824 return number;
825 }
826
827 BigInteger low = one();
828 BigInteger high = number;
829 BigInteger prev;
830
831 while (low <= high) {
832 BigInteger mid = (low + high) / two();
833 BigInteger square = mid * mid;
834
835 if (square == number) {
836 return mid;
837 } else if (square < number) {
838 low = mid + one();
839 prev = mid;
840 } else {
841 high = mid - one();
842 }
843 }
844
845 return prev;
846 }
847
848
856 static BigInteger log2(const BigInteger &number) {
857 if (number == BigInteger::zero()) {
858 throw std::out_of_range("log2(0) is undefined");
859 }
860 if (number.isNegative) {
861 throw std::out_of_range("log2(negative) is not allowed");
862 }
863
865 BigInteger s = number;
866 while (s != BigInteger::zero()) {
867 logVal = logVal + BigInteger::one();
868 s = s / BigInteger::two();
869 }
870 return logVal;
871 }
872
880 static BigInteger log10(const BigInteger &number) {
881 if (number == BigInteger::zero()) {
882 throw std::out_of_range("log10(0) is undefined");
883 }
884 if (number.isNegative) {
885 throw std::out_of_range("log10(negative) is not allowed");
886 }
887
888 // log10 of a number is roughly the number of its digits minus 1
889 return BigInteger::from_integer(number.digits.size() - 1);
890 }
891
900 void multiplyByPowerOfTen(std::size_t power) {
901 if (*this == zero()) return; // 0 * 10^n = 0, no need to change anything
902 digits.insert(digits.begin(), power, 0); // Insert 'power' number of zeros at the beginning
903 removeLeadingZeros(); // Just in case there were leading zeros before
904 }
905
914 void divideByPowerOfTen(std::size_t power) {
915 if (power >= digits.size()) {
916 // If power is greater than or equal to the number of digits, the result is 0
917 digits.clear();
918 digits.push_back(0);
919 isNegative = false; // Zero is not negative
920 } else {
921 // Erase 'power' number of digits from the beginning
922 digits.erase(digits.begin(), digits.begin() + static_cast<int>(power));
923 }
924 removeLeadingZeros(); // Just in case leading zeros are created
925 }
926 };
927}
928
929#endif // BIG_INTEGER_H
Implements a class for representing and manipulating large integers beyond the native integer range.
Definition big_integer.hpp:22
bool operator>=(const BigInteger &other) const
Definition big_integer.hpp:714
std::optional< T > to() const
Definition big_integer.hpp:198
static BigInteger lcm(const BigInteger &a, const BigInteger &b)
Definition big_integer.hpp:757
BigInteger & operator-=(const BigInteger &other)
Definition big_integer.hpp:527
bool operator>(const BigInteger &other) const
Definition big_integer.hpp:709
BigInteger operator%(const BigInteger &other) const
Definition big_integer.hpp:568
bool operator<(const BigInteger &other) const
Definition big_integer.hpp:685
bool operator==(const BigInteger &other) const
Definition big_integer.hpp:675
static const BigInteger & getMaxValueInstance()
Definition big_integer.hpp:312
BigInteger & operator+=(const BigInteger &other)
Definition big_integer.hpp:510
static BigInteger factorial(unsigned int n)
Definition big_integer.hpp:775
std::string to_string() const
Definition big_integer.hpp:170
BigInteger operator+(const BigInteger &other) const
Definition big_integer.hpp:367
BigInteger operator--(int)
Definition big_integer.hpp:663
BigInteger operator++(int)
Definition big_integer.hpp:650
BigInteger operator/(const BigInteger &other) const
Definition big_integer.hpp:464
static const BigInteger & getMinValueInstance()
Definition big_integer.hpp:277
BigInteger & operator/=(const BigInteger &other)
Definition big_integer.hpp:551
static BigInteger log2(const BigInteger &number)
Definition big_integer.hpp:856
static BigInteger min(const BigInteger &a, const BigInteger &b)
Definition big_integer.hpp:631
bool operator<=(const BigInteger &other) const
Definition big_integer.hpp:704
static BigInteger log10(const BigInteger &number)
Definition big_integer.hpp:880
BigInteger operator*(const BigInteger &other) const
Definition big_integer.hpp:418
void negate()
Definition big_integer.hpp:332
BigInteger operator-(const BigInteger &other) const
Definition big_integer.hpp:397
BigInteger & operator++()
Definition big_integer.hpp:644
static BigInteger gcd(const BigInteger &a, const BigInteger &b)
Definition big_integer.hpp:731
void divideByPowerOfTen(std::size_t power)
Definition big_integer.hpp:914
static BigInteger sqrt(const BigInteger &number)
Definition big_integer.hpp:818
static const BigInteger & one()
Definition big_integer.hpp:257
static BigInteger parse(const std::string &number)
Definition big_integer.hpp:120
BigInteger operator-() const
Definition big_integer.hpp:353
bool operator!=(const BigInteger &other) const
Definition big_integer.hpp:680
BigInteger & operator*=(const BigInteger &other)
Definition big_integer.hpp:540
static BigInteger max(const BigInteger &a, const BigInteger &b)
Definition big_integer.hpp:615
BigInteger abs() const
Definition big_integer.hpp:345
static const BigInteger & two()
Definition big_integer.hpp:266
static BigInteger pow(const BigInteger &a, unsigned int n)
Definition big_integer.hpp:792
std::pair< BigInteger, BigInteger > divmod(const BigInteger &other) const
Definition big_integer.hpp:597
BigInteger & operator--()
Definition big_integer.hpp:657
void multiplyByPowerOfTen(std::size_t power)
Definition big_integer.hpp:900
static BigInteger from_integer(T number)
Definition big_integer.hpp:147
static const BigInteger & zero()
Definition big_integer.hpp:248
Definition big_integer.hpp:14