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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
|
#include "BigInt.h"
#include "MemoryManager.h"
#include "Types.h"
#include <limits.h>
#include <string.h>
typedef uint_fast32_t UWord;
#define UWORD_MAX UINT_FAST32_MAX
#define UWORD_BIT (sizeof(UWord) * CHAR_BIT)
/** Metadata **/
struct BigIntMeta {
MemoryManager *mm;
usize words;
};
static inline struct BigIntMeta*
B_metadata(BigInt s) {
return (struct BigIntMeta*)(s - sizeof(struct BigIntMeta));
}
/** Helpers **/
static inline void*
B_start(MemoryManager *mm, usize words) {
if (NULL == mm)
return NULL;
struct BigIntMeta bim = {
.mm = mm,
.words = words,
};
void* start = NULL;
if (R_Ok != mm->alloc(*mm, &start, sizeof(bim) + bim.words * sizeof(UWord)) ||
NULL == start)
{
return NULL;
}
memcpy(start, &bim, sizeof(bim));
return start + sizeof(bim);
}
static inline Result
B_resize(BigInt *x) {
struct BigIntMeta *bim = B_metadata(*x);
void *start = bim;
RETURN_NOTOK(
bim->mm->realloc(*bim->mm, &start, sizeof(*bim) + (bim->words << 1) * sizeof(UWord)));
bim = start;
*x = start + sizeof(struct BigIntMeta);
bim->words <<= 1;
return R_Ok;
}
/** Create **/
BigInt
B_new(MemoryManager *mm, int value) {
UWord *start = B_start(mm, 1);
if (NULL != start)
memcpy(start, &value, sizeof(value));
return start;
}
BigInt
B_newstr(MemoryManager *mm, const char* str) {
return NULL;
}
BigInt
B_newbytes(MemoryManager *mm, usize bytes) {
return B_start(mm, 1 + (bytes - 1) / sizeof(UWord));
}
BigInt
B_dup(MemoryManager *mm, BigInt bigint) {
if (NULL == bigint)
return NULL;
struct BigIntMeta *bim = B_metadata(bigint);
void *start = B_start(mm, bim->words);
if (NULL != start)
memcpy(start, bigint, bim->words * sizeof(UWord));
return start;
}
BigInt
B_newsum(MemoryManager *mm, BigInt a, BigInt b) {
BigInt total = B_dup(mm, a);
if (NULL == total ||
R_Ok != B_sum(&total, b))
{
return NULL;
}
return total;
}
BigInt
B_newsub(MemoryManager *mm, BigInt a, BigInt b) {
BigInt total = B_dup(mm, a);
if (NULL == total ||
R_Ok != B_sub(&total, b))
{
return NULL;
}
return total;
}
/* Free */
Result
B_free(BigInt *bigint) {
if (NULL == bigint || NULL == *bigint)
return R_NullArgument;
struct BigIntMeta *start = B_metadata(*bigint);
*bigint = (BigInt*)start;
return start->mm->free(*start->mm, (void*)bigint);
}
/* Modify */
Result
B_sum(BigInt *inta, BigInt intb) {
if (NULL == inta || NULL == intb || NULL == *inta)
return R_NullArgument;
struct BigIntMeta *am = B_metadata(*inta), *bm = B_metadata(intb);
UWord *a = *inta, *b = intb;
int overflow = 0;
usize w;
for (w = 0; w < bm->words; ++w) {
if (w >= am->words)
B_resize(inta);
if (!(overflow && a[w] > UWORD_MAX - 1)) {
if (overflow)
++a[w];
overflow = a[w] > UWORD_MAX - b[w];
}
a[w] += b[w];
}
if (overflow) {
if (w >= am->words) {
RETURN_NOTOK(B_resize(inta));
a = *inta;
}
++a[w];
}
return R_Ok;
}
Result
B_sub(BigInt *inta, BigInt intb) {
if (NULL == inta || NULL == intb || NULL == *inta)
return R_NullArgument;
struct BigIntMeta *am = B_metadata(*inta), *bm = B_metadata(intb);
UWord *a = *inta, *b = intb;
int underflow = 0;
usize w;
for (w = 0; w < bm->words; ++w) {
if (w >= am->words)
B_resize(inta);
if (!(underflow && a[w] < 0 + 1)) {
if (underflow)
--a[w];
underflow = a[w] < 0 + b[w];
}
a[w] -= b[w];
}
if (underflow) {
if (w >= am->words) {
RETURN_NOTOK(B_resize(inta));
a = *inta;
}
--a[w];
}
return R_Ok;
}
Result
B_rshift(BigInt *intx, usize shift) {
if (NULL == intx || NULL == *intx)
return R_NullArgument;
if (shift == 0)
return R_Ok;
struct BigIntMeta *bim = B_metadata(*intx);
UWord *x = *intx;
usize word_moves = shift / (sizeof(UWord) * CHAR_BIT);
if (word_moves) {
usize w = 0;
for (; w < bim->words - word_moves; ++w)
x[w] = x[w + word_moves];
for (; w < bim->words; ++w)
x[w] = 0;
}
usize bit_moves = shift % UWORD_BIT;
if (bit_moves == 1) {
x[0] >>= 1;
for (usize w = 1; w < bim->words; ++w) {
x[w - 1] |= (x[w] & 1) << (UWORD_BIT - 1);
x[w] >>= 1;
}
}
else if (bit_moves > 1) {
UWord mask = ((UWord)1 << bit_moves) - 1;
x[0] >>= bit_moves;
for (usize w = 1; w < bim->words; ++w) {
x[w - 1] |= (x[w] & mask) << (UWORD_BIT - bit_moves);
x[w] >>= bit_moves;
}
}
return R_Ok;
}
Result
B_lshift(BigInt *intx, usize shift) {
if (NULL == intx || NULL == *intx)
return R_NullArgument;
if (shift == 0)
return R_Ok;
struct BigIntMeta *bim = B_metadata(*intx);
UWord *x = *intx;
usize word_moves = shift / UWORD_BIT;
if (word_moves) {
usize w = bim->words - 1;
for (; w >= word_moves; --w)
x[w] = x[w - word_moves];
for (; w < bim->words; --w)
x[w] = 0;
}
usize bit_moves = shift % UWORD_BIT;
if (bit_moves == 1) {
UWord mask = (UWord)1 << (UWORD_BIT - 1);
x[bim->words - 1] <<= 1;
for (usize w = bim->words - 2; w < bim->words; --w) {
x[w + 1] |= (x[w] & mask) >> (UWORD_BIT - 1);
x[w] <<= 1;
}
}
else if (bit_moves > 1) {
UWord mask = (((UWord)1 << bit_moves) - 1) << (UWORD_BIT - bit_moves);
x[bim->words - 1] <<= bit_moves;
for (usize w = bim->words - 2; w < bim->words; --w) {
x[w + 1] |= (x[w] & mask) >> (UWORD_BIT - bit_moves);
x[w] <<= bit_moves;
}
}
return R_Ok;
}
/* Get */
usize
B_bytes(BigInt x) {
if (NULL == x)
return 0;
return B_metadata(x)->words * sizeof(UWord);
}
usize
B_bitwidth(BigInt intx) {
if (NULL == intx)
return 0;
struct BigIntMeta *bim = B_metadata(intx);
UWord *x = intx;
for (usize w = bim->words - 1; w < bim->words; --w) {
if (x[w] == 0) continue;
// TODO: use faster search than linear
usize width = UWORD_BIT * (w + 1);
for (UWord mask = (UWord)1 << (UWORD_BIT - 1); mask > 0; mask >>= 1, --width) {
if (x[w] & mask)
return width;
}
}
return 0;
}
char*
B_tostr(MemoryManager *mm, BigInt intx) {
if (NULL == intx)
return NULL;
struct BigIntMeta *bim = B_metadata(intx);
UWord *x = intx;
usize n = B_bitwidth(intx);
if (0 == n) {
char *str = NULL;
if (R_Ok != mm->alloc(*mm, (void**)&str, sizeof(char) * 2))
return NULL;
str[0] = '0';
str[1] = '\0';
return str;
}
// TODO: these calculations are based on Wikipedia's statement that
// "4 x ceil(n / 3)" is enough bits of memory.
// This is true, but it grows faster than needed, so from 31 bits onward,
// we use too much memory (adding unnecessary leading 0s).
// The exact value is "ceil(log10(2^x - 1))"
usize bcd_bytes = 1 + (n - 1) / 6;
int odd_nibbles = (1 + (n - 1) / 3) % 2;
char *str;
if (R_Ok != mm->alloc(*mm, (void**)&str, sizeof(char) * (bcd_bytes * 2 - odd_nibbles + 1)))
return NULL;
u8 *bcd;
if (R_Ok != mm->alloc(*mm, (void**)&bcd, sizeof(u8) * bcd_bytes)) {
mm->free(*mm, (void**)&str);
return NULL;
}
memset(bcd, 0, sizeof(u8) * bcd_bytes);
UWord mask = (UWord)1 << (sizeof(UWord) * 8 - 1);
// Double-dable, binary to binary-coded decimal
for (usize w = bim->words - 1; w < bim->words; --w) {
for (UWord m = mask; m != 0; m >>= 1) {
// Incrementing values >=5 by 3
for (usize b = 0; b < bcd_bytes; ++b) {
// Upper nibble
if ((bcd[b] & 0b11110000) >= 0b01010000)
bcd[b] += 0b00110000;
// Lower nibble
if ((bcd[b] & 0b00001111) >= 5)
bcd[b] += 3;
}
// Left shift all bits in bcd
u8 carry = 0, next_carry = 0;
for (usize b = 0; b < bcd_bytes; ++b) {
next_carry = !!(bcd[b] & 0b10000000);
bcd[b] <<= 1;
bcd[b] |= carry;
carry = next_carry;
}
// Place leading bit in x into bcd
bcd[0] |= !!(x[w] & m);
}
}
// BCD to char string
char *s = str;
if (!odd_nibbles)
*(s++) = ((bcd[bcd_bytes - 1] & 0b11110000) >> 4) + '0';
*(s++) = (bcd[bcd_bytes - 1] & 0b00001111) + '0';
for (usize b = bcd_bytes - 2; b < bcd_bytes; --b) {
*(s++) = ((bcd[b] & 0b11110000) >> 4) + '0';
*(s++) = (bcd[b] & 0b00001111) + '0';
}
*s = '\0';
mm->free(*mm, (void**)&bcd);
return str;
}
|