aboutsummaryrefslogtreecommitdiff
path: root/bigint.h
diff options
context:
space:
mode:
authorSebastiano Tronto <sebastiano@tronto.net>2025-01-14 21:32:09 +0100
committerSebastiano Tronto <sebastiano@tronto.net>2025-01-14 21:32:09 +0100
commit38efc7d2df030d37e3f20efbce71fadad1294cef (patch)
treebbe8a7bbb6acbeb2b7ed54c189d1e719a7459427 /bigint.h
parent8ca27427af98bd3a82e8c5f4a199513295747930 (diff)
downloadzmodn-38efc7d2df030d37e3f20efbce71fadad1294cef.tar.gz
zmodn-38efc7d2df030d37e3f20efbce71fadad1294cef.zip
Added BigInt
Diffstat (limited to 'bigint.h')
-rw-r--r--bigint.h245
1 files changed, 245 insertions, 0 deletions
diff --git a/bigint.h b/bigint.h
new file mode 100644
index 0000000..a8199b9
--- /dev/null
+++ b/bigint.h
@@ -0,0 +1,245 @@
1#ifndef BIGUNSIGNED_H
2#define BIGUNSIGNED_H
3
4#include <cstdint>
5#include <iostream>
6#include <string_view>
7
8constexpr uint64_t abs64(int64_t);
9constexpr 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
15template<uint64_t N = 50, uint64_t E = 9>
16requires (E < 10)
17class BigInt {
18public:
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 bool fl = false;
128 if (!z.sign)
129 os << "-";
130 for (int i = z.D-1; i >= 0; i--)
131 if (fl = fl || z.digits[i] != 0; fl)
132 os << z.digits[i];
133 if (z == 0)
134 os << "0";
135 return os;
136 }
137
138private:
139 constexpr void carryover() {
140 for (int i = 1; i < D; i++) {
141 auto c = digits[i-1] / M;
142 digits[i-1] -= c * M;
143 digits[i] += c;
144 }
145 }
146
147 constexpr BigInt half() const {
148 BigInt ret;
149 uint64_t carry = 0;
150 for (int i = D-1; i >= 0; i--) {
151 ret.digits[i] += (digits[i] + M * carry) / 2;
152 carry = digits[i] % 2;
153 }
154 return ret;
155 }
156
157 static constexpr BigInt powM(uint64_t e) {
158 BigInt ret;
159 ret.digits[e] = 1;
160 return ret;
161 }
162
163 // Sum of non-negative integers
164 static constexpr BigInt positive_sum(const BigInt& x, const BigInt& y) {
165 BigInt ret;
166 for (int i = 0; i < D; i++)
167 ret.digits[i] = x.digits[i] + y.digits[i];
168 ret.carryover();
169 return ret;
170 }
171
172 // Difference of non-negative integers (result may be negative)
173 static constexpr BigInt positive_diff(const BigInt& x, const BigInt& y) {
174 if (y > x)
175 return -positive_diff(y, x);
176
177 BigInt ret;
178 uint64_t carry = 0;
179 for (int i = 0; i < D; i++) {
180 uint64_t oldcarry = carry;
181 if (x.digits[i] < y.digits[i] + oldcarry) {
182 ret.digits[i] = M;
183 carry = 1;
184 } else {
185 carry = 0;
186 }
187 ret.digits[i] += x.digits[i];
188 ret.digits[i] -= y.digits[i] + oldcarry;
189 }
190 ret.carryover();
191 return ret;
192 }
193
194 // Division with remainder, UB if y == 0
195 static constexpr std::pair<BigInt, BigInt>
196 euclidean_division(const BigInt& x, const BigInt& y) {
197 auto [q, r] = positive_div(x.abs(), y.abs());
198 if (x.sign && y.sign)
199 return std::pair(q, r);
200 else if (x.sign && !y.sign)
201 return r == 0 ? std::pair(-q, 0) : std::pair(-q-1, y+r);
202 else if (!x.sign && y.sign)
203 return r == 0 ? std::pair(-q, r) : std::pair(-q-1, y-r);
204 else
205 return std::pair(q, -r);
206 }
207
208 // Division with remainder of non-negative integers, UB if y == 0
209 // This method is inefficient (O(log(x/y)) BigInt multiplications)
210 static constexpr std::pair<BigInt, BigInt>
211 positive_div(const BigInt& x, const BigInt& y) {
212 BigInt q = 0;
213 BigInt r = x;
214
215 if (y > x)
216 return std::pair(q, r);
217
218 BigInt lb = 0;
219 BigInt ub = x;
220 while (true) {
221 BigInt q = (ub + lb).half();
222 BigInt r = x - y*q;
223
224 if (r < 0)
225 ub = q;
226 else if (r >= y)
227 lb = q+1;
228 else
229 return std::pair(q, r);
230 }
231 }
232};
233
234constexpr uint64_t abs64(int64_t x) {
235 return static_cast<uint64_t>(x > 0 ? x : -x);
236}
237
238constexpr uint64_t pow10(uint64_t e) {
239 if (e == 0)
240 return 1;
241 else
242 return 10 * pow10(e-1);
243}
244
245#endif