forked from t3nsor/codebook
-
Notifications
You must be signed in to change notification settings - Fork 0
/
bignum.cpp
205 lines (175 loc) · 5.49 KB
/
bignum.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
// Depending on your application it's pretty unlikely that you'll have to type out the entirety of this struct. For example, in most cases you won't need division, and you can leave out most of the operators too.
// NB: Thse are fairly terrible implementations. Multiplication is about twice as slow as Python, and division is about 20 times slower. Use only as a last resort when for some reason you can't use Java's native bignums.
struct bignum {
typedef unsigned int uint;
vector<uint> digits;
static const uint RADIX = 1000000000;
bignum(): digits(1, 0) {}
bignum(const bignum& x): digits(x.digits) {}
bignum(unsigned long long x) {
*this = x;
}
bignum(const char* x) {
*this = x;
}
bignum(const string& s) {
*this = s;
}
bignum& operator=(const bignum& y) {
digits = y.digits; return *this;
}
bignum& operator=(unsigned long long x) {
digits.assign(1, x%RADIX);
if (x >= RADIX) {
digits.push_back(x/RADIX);
}
return *this;
}
bignum& operator=(const char* s) {
int slen=strlen(s),i,l;
digits.resize((slen+8)/9);
for (l=0; slen>0; l++,slen-=9) {
digits[l]=0;
for (i=slen>9?slen-9:0; i<slen; i++) {
digits[l]=10*digits[l]+s[i]-'0';
}
}
while (digits.size() > 1 && !digits.back()) digits.pop_back();
return *this;
}
bignum& operator=(const string& s) {
return *this = s.c_str();
}
void add(const bignum& x) {
int l = max(digits.size(), x.digits.size());
digits.resize(l+1);
for (int d=0, carry=0; d<=l; d++) {
uint sum=carry;
if (d<digits.size()) sum+=digits[d];
if (d<x.digits.size()) sum+=x.digits[d];
digits[d]=sum;
if (digits[d]>=RADIX) {
digits[d]-=RADIX; carry=1;
} else {
carry=0;
}
}
if (!digits.back()) digits.pop_back();
}
void sub(const bignum& x) {
// if ((*this)<x) throw; //negative numbers not yet supported
for (int d=0, borrow=0; d<digits.size(); d++) {
digits[d]-=borrow;
if (d<x.digits.size()) digits[d]-=x.digits[d];
if (digits[d]>>31) { digits[d]+=RADIX; borrow=1; } else borrow=0;
}
while (digits.size() > 1 && !digits.back()) digits.pop_back();
}
void mult(const bignum& x) {
vector<uint> res(digits.size() + x.digits.size());
unsigned long long y,z;
for (int i=0; i<digits.size(); i++) {
for (int j=0; j<x.digits.size(); j++) {
unsigned long long y=digits[i]; y*=x.digits[j];
unsigned long long z=y/RADIX;
res[i+j+1]+=z; res[i+j]+=y-RADIX*z; //mod is slow
if (res[i+j] >= RADIX) { res[i+j] -= RADIX; res[i+j+1]++; }
for (int k = i+j+1; res[k] >= RADIX; res[k] -= RADIX, res[++k]++);
}
}
digits = res;
while (digits.size() > 1 && !digits.back()) digits.pop_back();
}
// returns the remainder
bignum div(const bignum& x) {
bignum dividend(*this);
bignum divisor(x);
fill(digits.begin(), digits.end(), 0);
// shift divisor up
int pwr = dividend.digits.size() - divisor.digits.size();
if (pwr > 0) {
divisor.digits.insert(divisor.digits.begin(), pwr, 0);
}
while (pwr >= 0) {
if (dividend.digits.size() > divisor.digits.size()) {
unsigned long long q = dividend.digits.back();
q *= RADIX; q += dividend.digits[dividend.digits.size()-2];
q /= 1+divisor.digits.back();
dividend -= divisor*q; digits[pwr] = q;
if (dividend >= divisor) { digits[pwr]++; dividend -= divisor; }
assert(dividend.digits.size() <= divisor.digits.size()); continue;
}
while (dividend.digits.size() == divisor.digits.size()) {
uint q = dividend.digits.back() / (1+divisor.digits.back());
if (q == 0) break;
digits[pwr] += q; dividend -= divisor*q;
}
if (dividend >= divisor) { dividend -= divisor; digits[pwr]++; }
pwr--; divisor.digits.erase(divisor.digits.begin());
}
while (digits.size() > 1 && !digits.back()) digits.pop_back();
return dividend;
}
string to_string() const {
ostringstream oss;
oss << digits.back();
for (int i = digits.size() - 2; i >= 0; i--) {
oss << setfill('0') << setw(9) << digits[i];
}
return oss.str();
}
bignum operator+(const bignum& y) const {
bignum res(*this); res.add(y); return res;
}
bignum operator-(const bignum& y) const {
bignum res(*this); res.sub(y); return res;
}
bignum operator*(const bignum& y) const {
bignum res(*this); res.mult(y); return res;
}
bignum operator/(const bignum& y) const {
bignum res(*this); res.div(y); return res;
}
bignum operator%(const bignum& y) const {
bignum res(*this); return res.div(y);
}
bignum& operator+=(const bignum& y) {
add(y); return *this;
}
bignum& operator-=(const bignum& y) {
sub(y); return *this;
}
bignum& operator*=(const bignum& y) {
mult(y); return *this;
}
bignum& operator/=(const bignum& y) {
div(y); return *this;
}
bignum& operator%=(const bignum& y) {
*this = div(y);
}
bool operator==(const bignum& y) const {
return digits == y.digits;
}
bool operator<(const bignum& y) const {
if (digits.size() < y.digits.size()) return true;
if (digits.size() > y.digits.size()) return false;
for (int i = digits.size()-1; i >= 0; i--) {
if (digits[i] < y.digits[i]) {
return true;
} else if (digits[i] > y.digits[i]) {
return false;
}
}
return false;
}
bool operator>(const bignum& y) const {
return y<*this;
}
bool operator<=(const bignum& y) const {
return !(y<*this);
}
bool operator>=(const bignum& y) const {
return !(*this<y);
}
};