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

Generated with cgit - Back to sebastiano.tronto.net