bigint.h (5989B)
1 // Taken from https://git.tronto.net/zmodn 2 3 #ifndef BIGUNSIGNED_H 4 #define BIGUNSIGNED_H 5 6 #include <cstdint> 7 #include <iostream> 8 #include <string_view> 9 10 constexpr uint64_t abs64(int64_t); 11 constexpr uint64_t pow10(uint64_t); 12 13 // Big integer class for numbers of at most N decimal digits. 14 // The number E is used to tune the size of each digit, mostly for 15 // testing purposes. 16 17 template<uint64_t N = 50, uint64_t E = 9> 18 requires (E < 10) 19 class BigInt { 20 public: 21 // The member variables sign and digits are declared public so that 22 // BigInt becomes a structural type and can be used in templates. 23 24 static constexpr uint64_t M = pow10(E); 25 static constexpr uint64_t D = (N / E) + 1; 26 27 bool sign; 28 uint64_t digits[D]; 29 30 constexpr BigInt() : sign{true} { 31 std::fill(digits, digits+D, 0); 32 } 33 34 constexpr BigInt(int64_t n) : sign{n >= 0} { 35 std::fill(digits, digits+D, 0); 36 digits[0] = abs64(n); 37 carryover(); 38 } 39 40 constexpr BigInt(const std::string_view s) : sign{true} { 41 std::fill(digits, digits+D, 0); 42 if (s.size() == 0) 43 return; 44 for (int i = s.size()-1, j = 0; i >= 0; i--, j++) { 45 if (s[i] == '\'') 46 continue; 47 if (i == 0 && s[i] == '-') { 48 sign = false; 49 break; 50 } 51 digits[j/E] += (pow10(j % E)) 52 * static_cast<uint64_t>(s[i] - '0'); 53 } 54 } 55 56 constexpr auto operator<=>(const BigInt& other) const { 57 if (sign != other.sign) 58 return sign <=> other.sign; 59 60 for (int i = D-1; i >= 0; i--) 61 if (digits[i] != other.digits[i]) 62 return sign ? 63 digits[i] <=> other.digits[i] : 64 other.digits[i] <=> digits[i]; 65 66 return 0 <=> 0; 67 } 68 69 constexpr bool operator==(const BigInt& other) const = default; 70 71 constexpr BigInt abs() const { 72 BigInt ret = *this; 73 ret.sign = true; 74 return ret; 75 } 76 77 constexpr BigInt operator-() const { 78 if (*this == 0) 79 return 0; 80 BigInt ret = *this; 81 ret.sign = !ret.sign; 82 return ret; 83 } 84 85 constexpr BigInt operator+(const BigInt& z) const { 86 if (sign && z.sign) 87 return positive_sum(*this, z); 88 else if (sign && !z.sign) 89 return positive_diff(*this, -z); 90 else if (!sign && z.sign) 91 return positive_diff(z, -*this); 92 else 93 return -positive_sum(-*this, -z); 94 } 95 96 constexpr BigInt operator-(const BigInt& z) const { 97 return *this + (-z); 98 } 99 100 constexpr BigInt operator*(const BigInt& z) const { 101 BigInt ret; 102 ret.sign = !(sign ^ z.sign); 103 for (int i = 0; i < D; i++) 104 for (int j = 0; i+j < D; j++) 105 ret.digits[i+j] += digits[i] * z.digits[j]; 106 ret.carryover(); 107 return ret; 108 } 109 110 constexpr BigInt operator/(const BigInt& z) const { 111 auto [q, r] = euclidean_division(*this, z); 112 return q; 113 } 114 115 constexpr BigInt operator%(const BigInt& z) const { 116 auto [q, r] = euclidean_division(*this, z); 117 return r; 118 } 119 120 constexpr BigInt operator+=(const BigInt& z) { return *this = *this + z; } 121 constexpr BigInt operator++() { return *this += 1; } 122 constexpr BigInt operator-=(const BigInt& z) { return *this = *this - z; } 123 constexpr BigInt operator--() { return *this -= 1; } 124 constexpr BigInt operator*=(const BigInt& z) { return *this = *this * z; } 125 constexpr BigInt operator/=(const BigInt& z) { return *this = *this / z; } 126 constexpr BigInt operator%=(const BigInt& z) { return *this = *this % z; } 127 128 friend std::ostream& operator<<(std::ostream& os, const BigInt<N, E>& z) { 129 if (z == 0) { 130 os << "0"; 131 return os; 132 } 133 134 if (!z.sign) 135 os << "-"; 136 137 int j; 138 for (j = z.D-1; z.digits[j] == 0; j--) ; 139 os << z.digits[j]; // Top digit is not padded 140 141 for (int i = j-1; i >= 0; i--) { 142 std::string num = std::to_string(z.digits[i]); 143 os << std::string(E - num.length(), '0') << num; 144 } 145 return os; 146 } 147 148 private: 149 constexpr void carryover() { 150 for (int i = 1; i < D; i++) { 151 auto c = digits[i-1] / M; 152 digits[i-1] -= c * M; 153 digits[i] += c; 154 } 155 } 156 157 constexpr BigInt half() const { 158 BigInt ret; 159 uint64_t carry = 0; 160 for (int i = D-1; i >= 0; i--) { 161 ret.digits[i] += (digits[i] + M * carry) / 2; 162 carry = digits[i] % 2; 163 } 164 return ret; 165 } 166 167 static constexpr BigInt powM(uint64_t e) { 168 BigInt ret; 169 ret.digits[e] = 1; 170 return ret; 171 } 172 173 // Sum of non-negative integers 174 static constexpr BigInt positive_sum(const BigInt& x, const BigInt& y) { 175 BigInt ret; 176 for (int i = 0; i < D; i++) 177 ret.digits[i] = x.digits[i] + y.digits[i]; 178 ret.carryover(); 179 return ret; 180 } 181 182 // Difference of non-negative integers (result may be negative) 183 static constexpr BigInt positive_diff(const BigInt& x, const BigInt& y) { 184 if (y > x) 185 return -positive_diff(y, x); 186 187 BigInt ret; 188 uint64_t carry = 0; 189 for (int i = 0; i < D; i++) { 190 uint64_t oldcarry = carry; 191 if (x.digits[i] < y.digits[i] + oldcarry) { 192 ret.digits[i] = M; 193 carry = 1; 194 } else { 195 carry = 0; 196 } 197 ret.digits[i] += x.digits[i]; 198 ret.digits[i] -= y.digits[i] + oldcarry; 199 } 200 ret.carryover(); 201 return ret; 202 } 203 204 // Division with remainder, UB if y == 0 205 static constexpr std::pair<BigInt, BigInt> 206 euclidean_division(const BigInt& x, const BigInt& y) { 207 auto [q, r] = positive_div(x.abs(), y.abs()); 208 if (x.sign && y.sign) 209 return std::pair(q, r); 210 else if (x.sign && !y.sign) 211 return r == 0 ? std::pair(-q, 0) : std::pair(-q-1, y+r); 212 else if (!x.sign && y.sign) 213 return r == 0 ? std::pair(-q, r) : std::pair(-q-1, y-r); 214 else 215 return std::pair(q, -r); 216 } 217 218 // Division with remainder of non-negative integers, UB if y == 0 219 // This method is inefficient (O(log(x/y)) BigInt multiplications) 220 static constexpr std::pair<BigInt, BigInt> 221 positive_div(const BigInt& x, const BigInt& y) { 222 BigInt q = 0; 223 BigInt r = x; 224 225 if (y > x) 226 return std::pair(q, r); 227 228 BigInt lb = 0; 229 BigInt ub = x; 230 while (true) { 231 BigInt q = (ub + lb).half(); 232 BigInt r = x - y*q; 233 234 if (r < 0) 235 ub = q; 236 else if (r >= y) 237 lb = q+1; 238 else 239 return std::pair(q, r); 240 } 241 } 242 }; 243 244 constexpr uint64_t abs64(int64_t x) { 245 return static_cast<uint64_t>(x > 0 ? x : -x); 246 } 247 248 constexpr uint64_t pow10(uint64_t e) { 249 if (e == 0) 250 return 1; 251 else 252 return 10 * pow10(e-1); 253 } 254 255 #endif