taming-cpp

Experiments with C++ and comparisons with C
git clone https://git.tronto.net/taming-cpp
Download | Log | Files | Refs | README

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