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