zmodn

A simple C++ library for integers modulo N
git clone https://git.tronto.net/zmodn
Download | Log | Files | Refs | README

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