Coverage Report

Created: 2017-04-15 07:07

/home/travis/build/MoarVM/MoarVM/src/math/bigintops.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
#include <math.h>
3
4
#ifndef MAX
5
3
    #define MAX(x,y) ((x)>(y)?(x):(y))
6
#endif
7
8
#ifndef MIN
9
352
    #define MIN(x,y) ((x)<(y)?(x):(y))
10
#endif
11
12
577
MVM_STATIC_INLINE void adjust_nursery(MVMThreadContext *tc, MVMP6bigintBody *body) {
13
577
    if (MVM_BIGINT_IS_BIG(body)) {
14
352
        int used = USED(body->u.bigint);
15
352
        int adjustment = MIN(used, 32768) & ~0x7;
16
352
        if (adjustment && (char *)tc->nursery_alloc_limit - adjustment > (char *)tc->nursery_alloc) {
17
2
            tc->nursery_alloc_limit = (char *)(tc->nursery_alloc_limit) - adjustment;
18
2
        }
19
352
    }
20
577
}
21
22
/* Taken from mp_set_long, but portably accepts a 64-bit number. */
23
2
int MVM_bigint_mp_set_uint64(mp_int * a, MVMuint64 b) {
24
2
  int     x, res;
25
2
26
2
  mp_zero (a);
27
2
28
2
  /* set four bits at a time */
29
34
  for (x = 0; x < sizeof(MVMuint64) * 2; x++) {
30
32
    /* shift the number up four bits */
31
32
    if ((res = mp_mul_2d (a, 4, a)) != MP_OKAY) {
32
0
      return res;
33
0
    }
34
32
35
32
    /* OR in the top four bits of the source */
36
32
    a->dp[0] |= (b >> ((sizeof(MVMuint64)) * 8 - 4)) & 15;
37
32
38
32
    /* shift the source up to the next four bits */
39
32
    b <<= 4;
40
32
41
32
    /* ensure that digits are not clamped off */
42
32
    a->used += 1;
43
32
  }
44
2
  mp_clamp(a);
45
2
  return MP_OKAY;
46
2
}
47
48
12
static MVMnum64 mp_get_double(mp_int *a) {
49
12
    MVMnum64 d    = 0.0;
50
12
    MVMnum64 sign = SIGN(a) == MP_NEG ? -1.0 : 1.0;
51
12
    int i;
52
12
    if (USED(a) == 0)
53
0
        return d;
54
12
    if (USED(a) == 1)
55
5
        return sign * (MVMnum64) DIGIT(a, 0);
56
12
57
7
    mp_clamp(a);
58
7
    i = USED(a) - 1;
59
7
    d = (MVMnum64) DIGIT(a, i);
60
7
    i--;
61
7
    if (i == -1) {
62
0
        return sign * d;
63
0
    }
64
7
    d *= pow(2.0, DIGIT_BIT);
65
7
    d += (MVMnum64) DIGIT(a, i);
66
7
67
7
    if (USED(a) > 2) {
68
5
        i--;
69
5
        d *= pow(2.0, DIGIT_BIT);
70
5
        d += (MVMnum64) DIGIT(a, i);
71
5
    }
72
7
73
7
    d *= pow(2.0, DIGIT_BIT * i);
74
7
    return sign * d;
75
7
}
76
77
320
static void from_num(MVMnum64 d, mp_int *a) {
78
320
    MVMnum64 d_digit = pow(2, DIGIT_BIT);
79
320
    MVMnum64 da      = fabs(d);
80
320
    MVMnum64 upper;
81
320
    MVMnum64 lower;
82
320
    MVMnum64 lowest;
83
320
    MVMnum64 rest;
84
320
    int      digits  = 0;
85
320
86
320
    mp_zero(a);
87
320
88
342
    while (da > d_digit * d_digit * d_digit) {;
89
22
        da /= d_digit;
90
22
        digits++;
91
22
    }
92
320
    mp_grow(a, digits + 3);
93
320
94
320
    /* populate the top 3 digits */
95
320
    upper = da / (d_digit*d_digit);
96
320
    rest = fmod(da, d_digit*d_digit);
97
320
    lower = rest / d_digit;
98
320
    lowest = fmod(rest,d_digit );
99
320
    if (upper >= 1) {
100
2
        MVM_bigint_mp_set_uint64(a, (MVMuint64) upper);
101
2
        mp_mul_2d(a, DIGIT_BIT , a);
102
2
        DIGIT(a, 0) = (mp_digit) lower;
103
2
        mp_mul_2d(a, DIGIT_BIT , a);
104
318
    } else {
105
318
        if (lower >= 1) {
106
0
            MVM_bigint_mp_set_uint64(a, (MVMuint64) lower);
107
0
            mp_mul_2d(a, DIGIT_BIT , a);
108
0
            a->used = 2;
109
318
        } else {
110
318
            a->used = 1;
111
318
        }
112
318
    }
113
320
    DIGIT(a, 0) = (mp_digit) lowest;
114
320
115
320
    /* shift the rest */
116
320
    mp_mul_2d(a, DIGIT_BIT * digits, a);
117
320
    if (d < 0)
118
2
        mp_neg(a, a);
119
320
    mp_clamp(a);
120
320
    mp_shrink(a);
121
320
}
122
123
/* Returns the body of a P6bigint, containing the bigint/smallint union, for
124
 * operations that want to explicitly handle the two. */
125
2.70k
static MVMP6bigintBody * get_bigint_body(MVMThreadContext *tc, MVMObject *obj) {
126
2.70k
    if (IS_CONCRETE(obj))
127
2.70k
        return (MVMP6bigintBody *)REPR(obj)->box_funcs.get_boxed_ref(tc,
128
2.70k
            STABLE(obj), obj, OBJECT_BODY(obj), MVM_REPR_ID_P6bigint);
129
2.70k
    else
130
0
        MVM_exception_throw_adhoc(tc,
131
0
            "Can only perform big integer operations on concrete objects");
132
2.70k
}
133
134
/* Checks if a bigint can be stored small. */
135
897
static int can_be_smallint(const mp_int *i) {
136
897
    if (USED(i) != 1)
137
102
        return 0;
138
795
    return MVM_IS_32BIT_INT(DIGIT(i, 0));
139
897
}
140
141
/* Forces a bigint, even if we only have a smallint. Takes a parameter that
142
 * indicates where to allocate a temporary mp_int if needed. */
143
719
static mp_int * force_bigint(const MVMP6bigintBody *body, mp_int **tmp) {
144
719
    if (MVM_BIGINT_IS_BIG(body)) {
145
634
        return body->u.bigint;
146
634
    }
147
85
    else {
148
85
        MVMint32 value = body->u.smallint.value;
149
85
        mp_int *i = MVM_malloc(sizeof(mp_int));
150
85
        mp_init(i);
151
85
        if (value >= 0) {
152
68
            mp_set_long(i, value);
153
68
        }
154
17
        else {
155
17
            mp_set_long(i, -value);
156
17
            mp_neg(i, i);
157
17
        }
158
99
        while (*tmp)
159
14
            tmp++;
160
85
        *tmp = i;
161
85
        return i;
162
85
    }
163
719
}
164
165
/* Clears an array that may contain tempory big ints. */
166
362
static void clear_temp_bigints(mp_int *const *ints, MVMint32 n) {
167
362
    MVMint32 i;
168
1.08k
    for (i = 0; i < n; i++)
169
719
        if (ints[i]) {
170
85
            mp_clear(ints[i]);
171
85
            MVM_free(ints[i]);
172
85
        }
173
362
}
174
175
/* Stores an int64 in a bigint result body, either as a 32-bit smallint if it
176
 * is in range, or a big integer if not. */
177
40
static void store_int64_result(MVMP6bigintBody *body, MVMint64 result) {
178
40
    if (MVM_IS_32BIT_INT(result)) {
179
40
        body->u.smallint.flag = MVM_BIGINT_32_FLAG;
180
40
        body->u.smallint.value = (MVMint32)result;
181
40
    }
182
0
    else {
183
0
        mp_int *i = MVM_malloc(sizeof(mp_int));
184
0
        mp_init(i);
185
0
        if (result >= 0) {
186
0
            MVM_bigint_mp_set_uint64(i, (MVMuint64)result);
187
0
        }
188
0
        else {
189
0
            MVM_bigint_mp_set_uint64(i, (MVMuint64)-result);
190
0
            mp_neg(i, i);
191
0
        }
192
0
        body->u.bigint = i;
193
0
    }
194
40
}
195
196
/* Stores an bigint in a bigint result body, either as a 32-bit smallint if it
197
 * is in range, or a big integer if not. Clears and frees the passed bigint if
198
 * it is not being used. */
199
672
static void store_bigint_result(MVMP6bigintBody *body, mp_int *i) {
200
672
    if (can_be_smallint(i)) {
201
0
        body->u.smallint.flag = MVM_BIGINT_32_FLAG;
202
0
        body->u.smallint.value = SIGN(i) == MP_NEG ? -DIGIT(i, 0) : DIGIT(i, 0);
203
0
        mp_clear(i);
204
0
        MVM_free(i);
205
0
    }
206
672
    else {
207
672
        body->u.bigint = i;
208
672
    }
209
672
}
210
211
/* Bitops on libtomath (no 2s compliment API) are horrendously inefficient and
212
 * really should be hand-coded to work DIGIT-by-DIGIT with in-loop carry
213
 * handling.  For now we have these fixups.
214
 *
215
 * The following inverts the bits of a negative bigint, adds 1 to that, and
216
 * appends sign-bit extension DIGITs to it to give us a 2s compliment
217
 * representation in memory.  Do not call it on positive bigints.
218
 */
219
1
static void grow_and_negate(const mp_int *a, int size, mp_int *b) {
220
1
    int i;
221
1
    /* Always add an extra DIGIT so we can tell positive values
222
1
     * with a one in the highest bit apart from negative values.
223
1
     */
224
1
    int actual_size = MAX(size, USED(a)) + 1;
225
1
226
1
    SIGN(b) = MP_ZPOS;
227
1
    mp_grow(b, actual_size);
228
1
    USED(b) = actual_size;
229
2
    for (i = 0; i < USED(a); i++) {
230
1
        DIGIT(b, i) = (~DIGIT(a, i)) & MP_MASK;
231
1
    }
232
2
    for (; i < actual_size; i++) {
233
1
        DIGIT(b, i) = MP_MASK;
234
1
    }
235
1
    /* Note: This add cannot cause another grow assuming nobody ever
236
1
     * tries to use tommath -0 for anything, and nobody tries to use
237
1
     * this on positive bigints.
238
1
     */
239
1
    mp_add_d(b, 1, b);
240
1
}
241
242
static void two_complement_bitop(mp_int *a, mp_int *b, mp_int *c,
243
1
                                 int (*mp_bitop)(mp_int *, mp_int *, mp_int *)) {
244
1
245
1
    mp_int d;
246
1
    mp_int e;
247
1
    mp_int *f;
248
1
    mp_int *g;
249
1
250
1
    f = a;
251
1
    g = b;
252
1
    if (MP_NEG == SIGN(a)) {
253
1
        mp_init(&d);
254
1
        grow_and_negate(a, USED(b), &d);
255
1
        f = &d;
256
1
    }
257
1
    if (MP_NEG == SIGN(b)) {
258
0
        mp_init(&e);
259
0
        grow_and_negate(b, USED(a), &e);
260
0
        g = &e;
261
0
    }
262
1
    /* f and g now guaranteed to each point to positive bigints containing
263
1
     * a 2s compliment representation of the values in a and b.  If either
264
1
     * a or b was negative, the representation is one tomath "digit" longer
265
1
     * than it need be and sign extended.
266
1
     */
267
1
    mp_bitop(f, g, c);
268
1
    if (f == &d) mp_clear(&d);
269
1
    if (g == &e) mp_clear(&e);
270
1
    /* Use the fact that tomath clamps to detect results that should be
271
1
     * signed.  If we created extra tomath "digits" and they resulted in
272
1
     * sign bits of 0, they have been clamped away.  If the resulting sign
273
1
     * bits were 1, they remain, and c will have more digits than either of
274
1
     * original operands.  Note this only works because we do not
275
1
     * support NOR/NAND/NXOR, and so two zero sign bits can never create 1s.
276
1
     */
277
1
    if (USED(c) > MAX(USED(a),USED(b))) {
278
0
        int i;
279
0
        for (i = 0; i < USED(c); i++) {
280
0
            DIGIT(c, i) = (~DIGIT(c, i)) & MP_MASK;
281
0
        }
282
0
        mp_add_d(c, 1, c);
283
0
        mp_neg(c, c);
284
0
    }
285
1
}
286
287
0
static void two_complement_shl(mp_int *result, mp_int *value, MVMint64 count) {
288
0
    if (count >= 0) {
289
0
        mp_mul_2d(value, count, result);
290
0
    }
291
0
    else if (MP_NEG == SIGN(value)) {
292
0
        /* fake two's complement semantics on top of sign-magnitude
293
0
         * algorithm appears to work [citation needed]
294
0
         */
295
0
        mp_add_d(value, 1, result);
296
0
        mp_div_2d(result, -count, result, NULL);
297
0
        mp_sub_d(result, 1, result);
298
0
    }
299
0
    else {
300
0
        mp_div_2d(value, -count, result, NULL);
301
0
    }
302
0
}
303
304
#define MVM_BIGINT_UNARY_OP(opname, SMALLINT_OP) \
305
29
void MVM_bigint_##opname(MVMThreadContext *tc, MVMObject *result, MVMObject *source) { \
306
29
    MVMP6bigintBody *bb = get_bigint_body(tc, result); \
307
29
    if (!IS_CONCRETE(source)) { \
308
0
        store_int64_result(bb, 0); \
309
0
    } \
310
29
    else { \
311
29
        MVMP6bigintBody *ba = get_bigint_body(tc, source); \
312
29
        if (MVM_BIGINT_IS_BIG(ba)) { \
313
4
            mp_int *ia = ba->u.bigint; \
314
4
            mp_int *ib = MVM_malloc(sizeof(mp_int)); \
315
4
            mp_init(ib); \
316
4
            mp_##opname(ia, ib); \
317
4
            store_bigint_result(bb, ib); \
318
4
            adjust_nursery(tc, bb); \
319
4
        } \
320
25
        else { \
321
25
            MVMint64 sb; \
322
25
            MVMint64 sa = ba->u.smallint.value; \
323
25
            SMALLINT_OP; \
324
25
            store_int64_result(bb, sb); \
325
25
        } \
326
29
    } \
327
29
}
MVM_bigint_abs
Line
Count
Source
305
27
void MVM_bigint_##opname(MVMThreadContext *tc, MVMObject *result, MVMObject *source) { \
306
27
    MVMP6bigintBody *bb = get_bigint_body(tc, result); \
307
27
    if (!IS_CONCRETE(source)) { \
308
0
        store_int64_result(bb, 0); \
309
0
    } \
310
27
    else { \
311
27
        MVMP6bigintBody *ba = get_bigint_body(tc, source); \
312
27
        if (MVM_BIGINT_IS_BIG(ba)) { \
313
4
            mp_int *ia = ba->u.bigint; \
314
4
            mp_int *ib = MVM_malloc(sizeof(mp_int)); \
315
4
            mp_init(ib); \
316
4
            mp_##opname(ia, ib); \
317
4
            store_bigint_result(bb, ib); \
318
4
            adjust_nursery(tc, bb); \
319
4
        } \
320
23
        else { \
321
23
            MVMint64 sb; \
322
23
            MVMint64 sa = ba->u.smallint.value; \
323
23
            SMALLINT_OP; \
324
23
            store_int64_result(bb, sb); \
325
23
        } \
326
27
    } \
327
27
}
MVM_bigint_neg
Line
Count
Source
305
2
void MVM_bigint_##opname(MVMThreadContext *tc, MVMObject *result, MVMObject *source) { \
306
2
    MVMP6bigintBody *bb = get_bigint_body(tc, result); \
307
2
    if (!IS_CONCRETE(source)) { \
308
0
        store_int64_result(bb, 0); \
309
0
    } \
310
2
    else { \
311
2
        MVMP6bigintBody *ba = get_bigint_body(tc, source); \
312
2
        if (MVM_BIGINT_IS_BIG(ba)) { \
313
0
            mp_int *ia = ba->u.bigint; \
314
0
            mp_int *ib = MVM_malloc(sizeof(mp_int)); \
315
0
            mp_init(ib); \
316
0
            mp_##opname(ia, ib); \
317
0
            store_bigint_result(bb, ib); \
318
0
            adjust_nursery(tc, bb); \
319
0
        } \
320
2
        else { \
321
2
            MVMint64 sb; \
322
2
            MVMint64 sa = ba->u.smallint.value; \
323
2
            SMALLINT_OP; \
324
2
            store_int64_result(bb, sb); \
325
2
        } \
326
2
    } \
327
2
}
328
329
#define MVM_BIGINT_BINARY_OP(opname) \
330
1
MVMObject * MVM_bigint_##opname(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a, MVMObject *b) { \
331
1
    MVMP6bigintBody *ba, *bb, *bc; \
332
1
    MVMObject *result; \
333
1
    mp_int *tmp[2] = { NULL, NULL }; \
334
1
    mp_int *ia, *ib, *ic; \
335
1
    MVMROOT(tc, a, { \
336
1
    MVMROOT(tc, b, { \
337
1
        result = MVM_repr_alloc_init(tc, result_type);\
338
1
    }); \
339
1
    }); \
340
1
    ba = get_bigint_body(tc, a); \
341
1
    bb = get_bigint_body(tc, b); \
342
1
    bc = get_bigint_body(tc, result); \
343
1
    ia = force_bigint(ba, tmp); \
344
1
    ib = force_bigint(bb, tmp); \
345
1
    ic = MVM_malloc(sizeof(mp_int)); \
346
1
    mp_init(ic); \
347
1
    mp_##opname(ia, ib, ic); \
348
1
    store_bigint_result(bc, ic); \
349
1
    clear_temp_bigints(tmp, 2); \
350
1
    adjust_nursery(tc, bc); \
351
1
    return result; \
352
1
}
353
354
#define MVM_BIGINT_BINARY_OP_SIMPLE(opname, SMALLINT_OP) \
355
205
MVMObject * MVM_bigint_##opname(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a, MVMObject *b) { \
356
205
    MVMP6bigintBody *ba, *bb, *bc; \
357
205
    MVMObject *result; \
358
205
    ba = get_bigint_body(tc, a); \
359
205
    bb = get_bigint_body(tc, b); \
360
205
    if (MVM_BIGINT_IS_BIG(ba) || MVM_BIGINT_IS_BIG(bb)) { \
361
205
        mp_int *tmp[2] = { NULL, NULL }; \
362
205
        mp_int *ia, *ib, *ic; \
363
205
        MVMROOT(tc, a, { \
364
205
        MVMROOT(tc, b, { \
365
205
            result = MVM_repr_alloc_init(tc, result_type);\
366
205
        }); \
367
205
        }); \
368
205
        ba = get_bigint_body(tc, a); \
369
205
        bb = get_bigint_body(tc, b); \
370
205
        bc = get_bigint_body(tc, result); \
371
205
        ia = force_bigint(ba, tmp); \
372
205
        ib = force_bigint(bb, tmp); \
373
205
        ic = MVM_malloc(sizeof(mp_int)); \
374
205
        mp_init(ic); \
375
205
        mp_##opname(ia, ib, ic); \
376
205
        store_bigint_result(bc, ic); \
377
205
        clear_temp_bigints(tmp, 2); \
378
205
        adjust_nursery(tc, bc); \
379
205
    } \
380
0
    else { \
381
0
        MVMint64 sc; \
382
0
        MVMint64 sa = ba->u.smallint.value; \
383
0
        MVMint64 sb = bb->u.smallint.value; \
384
0
        SMALLINT_OP; \
385
0
        result = MVM_intcache_get(tc, result_type, sc); \
386
0
        if (result) \
387
0
            return result; \
388
0
        result = MVM_repr_alloc_init(tc, result_type);\
389
0
        bc = get_bigint_body(tc, result); \
390
0
        store_int64_result(bc, sc); \
391
0
    } \
392
205
    return result; \
393
205
}
MVM_bigint_add
Line
Count
Source
355
103
MVMObject * MVM_bigint_##opname(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a, MVMObject *b) { \
356
103
    MVMP6bigintBody *ba, *bb, *bc; \
357
103
    MVMObject *result; \
358
103
    ba = get_bigint_body(tc, a); \
359
103
    bb = get_bigint_body(tc, b); \
360
103
    if (MVM_BIGINT_IS_BIG(ba) || MVM_BIGINT_IS_BIG(bb)) { \
361
103
        mp_int *tmp[2] = { NULL, NULL }; \
362
103
        mp_int *ia, *ib, *ic; \
363
103
        MVMROOT(tc, a, { \
364
103
        MVMROOT(tc, b, { \
365
103
            result = MVM_repr_alloc_init(tc, result_type);\
366
103
        }); \
367
103
        }); \
368
103
        ba = get_bigint_body(tc, a); \
369
103
        bb = get_bigint_body(tc, b); \
370
103
        bc = get_bigint_body(tc, result); \
371
103
        ia = force_bigint(ba, tmp); \
372
103
        ib = force_bigint(bb, tmp); \
373
103
        ic = MVM_malloc(sizeof(mp_int)); \
374
103
        mp_init(ic); \
375
103
        mp_##opname(ia, ib, ic); \
376
103
        store_bigint_result(bc, ic); \
377
103
        clear_temp_bigints(tmp, 2); \
378
103
        adjust_nursery(tc, bc); \
379
103
    } \
380
0
    else { \
381
0
        MVMint64 sc; \
382
0
        MVMint64 sa = ba->u.smallint.value; \
383
0
        MVMint64 sb = bb->u.smallint.value; \
384
0
        SMALLINT_OP; \
385
0
        result = MVM_intcache_get(tc, result_type, sc); \
386
0
        if (result) \
387
0
            return result; \
388
0
        result = MVM_repr_alloc_init(tc, result_type);\
389
0
        bc = get_bigint_body(tc, result); \
390
0
        store_int64_result(bc, sc); \
391
0
    } \
392
103
    return result; \
393
103
}
MVM_bigint_sub
Line
Count
Source
355
1
MVMObject * MVM_bigint_##opname(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a, MVMObject *b) { \
356
1
    MVMP6bigintBody *ba, *bb, *bc; \
357
1
    MVMObject *result; \
358
1
    ba = get_bigint_body(tc, a); \
359
1
    bb = get_bigint_body(tc, b); \
360
1
    if (MVM_BIGINT_IS_BIG(ba) || MVM_BIGINT_IS_BIG(bb)) { \
361
1
        mp_int *tmp[2] = { NULL, NULL }; \
362
1
        mp_int *ia, *ib, *ic; \
363
1
        MVMROOT(tc, a, { \
364
1
        MVMROOT(tc, b, { \
365
1
            result = MVM_repr_alloc_init(tc, result_type);\
366
1
        }); \
367
1
        }); \
368
1
        ba = get_bigint_body(tc, a); \
369
1
        bb = get_bigint_body(tc, b); \
370
1
        bc = get_bigint_body(tc, result); \
371
1
        ia = force_bigint(ba, tmp); \
372
1
        ib = force_bigint(bb, tmp); \
373
1
        ic = MVM_malloc(sizeof(mp_int)); \
374
1
        mp_init(ic); \
375
1
        mp_##opname(ia, ib, ic); \
376
1
        store_bigint_result(bc, ic); \
377
1
        clear_temp_bigints(tmp, 2); \
378
1
        adjust_nursery(tc, bc); \
379
1
    } \
380
0
    else { \
381
0
        MVMint64 sc; \
382
0
        MVMint64 sa = ba->u.smallint.value; \
383
0
        MVMint64 sb = bb->u.smallint.value; \
384
0
        SMALLINT_OP; \
385
0
        result = MVM_intcache_get(tc, result_type, sc); \
386
0
        if (result) \
387
0
            return result; \
388
0
        result = MVM_repr_alloc_init(tc, result_type);\
389
0
        bc = get_bigint_body(tc, result); \
390
0
        store_int64_result(bc, sc); \
391
0
    } \
392
1
    return result; \
393
1
}
MVM_bigint_mul
Line
Count
Source
355
101
MVMObject * MVM_bigint_##opname(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a, MVMObject *b) { \
356
101
    MVMP6bigintBody *ba, *bb, *bc; \
357
101
    MVMObject *result; \
358
101
    ba = get_bigint_body(tc, a); \
359
101
    bb = get_bigint_body(tc, b); \
360
101
    if (MVM_BIGINT_IS_BIG(ba) || MVM_BIGINT_IS_BIG(bb)) { \
361
101
        mp_int *tmp[2] = { NULL, NULL }; \
362
101
        mp_int *ia, *ib, *ic; \
363
101
        MVMROOT(tc, a, { \
364
101
        MVMROOT(tc, b, { \
365
101
            result = MVM_repr_alloc_init(tc, result_type);\
366
101
        }); \
367
101
        }); \
368
101
        ba = get_bigint_body(tc, a); \
369
101
        bb = get_bigint_body(tc, b); \
370
101
        bc = get_bigint_body(tc, result); \
371
101
        ia = force_bigint(ba, tmp); \
372
101
        ib = force_bigint(bb, tmp); \
373
101
        ic = MVM_malloc(sizeof(mp_int)); \
374
101
        mp_init(ic); \
375
101
        mp_##opname(ia, ib, ic); \
376
101
        store_bigint_result(bc, ic); \
377
101
        clear_temp_bigints(tmp, 2); \
378
101
        adjust_nursery(tc, bc); \
379
101
    } \
380
0
    else { \
381
0
        MVMint64 sc; \
382
0
        MVMint64 sa = ba->u.smallint.value; \
383
0
        MVMint64 sb = bb->u.smallint.value; \
384
0
        SMALLINT_OP; \
385
0
        result = MVM_intcache_get(tc, result_type, sc); \
386
0
        if (result) \
387
0
            return result; \
388
0
        result = MVM_repr_alloc_init(tc, result_type);\
389
0
        bc = get_bigint_body(tc, result); \
390
0
        store_int64_result(bc, sc); \
391
0
    } \
392
101
    return result; \
393
101
}
394
395
#define MVM_BIGINT_BINARY_OP_2(opname, SMALLINT_OP) \
396
4
MVMObject * MVM_bigint_##opname(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a, MVMObject *b) { \
397
4
    MVMP6bigintBody *ba = get_bigint_body(tc, a); \
398
4
    MVMP6bigintBody *bb = get_bigint_body(tc, b); \
399
4
    MVMP6bigintBody *bc; \
400
4
    MVMObject *result; \
401
4
    MVMROOT(tc, a, { \
402
4
    MVMROOT(tc, b, { \
403
4
        result = MVM_repr_alloc_init(tc, result_type);\
404
4
    }); \
405
4
    }); \
406
4
    bc = get_bigint_body(tc, result); \
407
4
    if (MVM_BIGINT_IS_BIG(ba) || MVM_BIGINT_IS_BIG(bb)) { \
408
1
        mp_int *tmp[2] = { NULL, NULL }; \
409
1
        mp_int *ia = force_bigint(ba, tmp); \
410
1
        mp_int *ib = force_bigint(bb, tmp); \
411
1
        mp_int *ic = MVM_malloc(sizeof(mp_int)); \
412
1
        mp_init(ic); \
413
1
        two_complement_bitop(ia, ib, ic, mp_##opname); \
414
1
        store_bigint_result(bc, ic); \
415
1
        clear_temp_bigints(tmp, 2); \
416
1
        adjust_nursery(tc, bc); \
417
1
    } \
418
3
    else { \
419
3
        MVMint64 sc; \
420
3
        MVMint64 sa = ba->u.smallint.value; \
421
3
        MVMint64 sb = bb->u.smallint.value; \
422
3
        SMALLINT_OP; \
423
3
        store_int64_result(bc, sc); \
424
3
    } \
425
4
    return result; \
426
4
}
MVM_bigint_or
Line
Count
Source
396
1
MVMObject * MVM_bigint_##opname(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a, MVMObject *b) { \
397
1
    MVMP6bigintBody *ba = get_bigint_body(tc, a); \
398
1
    MVMP6bigintBody *bb = get_bigint_body(tc, b); \
399
1
    MVMP6bigintBody *bc; \
400
1
    MVMObject *result; \
401
1
    MVMROOT(tc, a, { \
402
1
    MVMROOT(tc, b, { \
403
1
        result = MVM_repr_alloc_init(tc, result_type);\
404
1
    }); \
405
1
    }); \
406
1
    bc = get_bigint_body(tc, result); \
407
1
    if (MVM_BIGINT_IS_BIG(ba) || MVM_BIGINT_IS_BIG(bb)) { \
408
0
        mp_int *tmp[2] = { NULL, NULL }; \
409
0
        mp_int *ia = force_bigint(ba, tmp); \
410
0
        mp_int *ib = force_bigint(bb, tmp); \
411
0
        mp_int *ic = MVM_malloc(sizeof(mp_int)); \
412
0
        mp_init(ic); \
413
0
        two_complement_bitop(ia, ib, ic, mp_##opname); \
414
0
        store_bigint_result(bc, ic); \
415
0
        clear_temp_bigints(tmp, 2); \
416
0
        adjust_nursery(tc, bc); \
417
0
    } \
418
1
    else { \
419
1
        MVMint64 sc; \
420
1
        MVMint64 sa = ba->u.smallint.value; \
421
1
        MVMint64 sb = bb->u.smallint.value; \
422
1
        SMALLINT_OP; \
423
1
        store_int64_result(bc, sc); \
424
1
    } \
425
1
    return result; \
426
1
}
MVM_bigint_xor
Line
Count
Source
396
1
MVMObject * MVM_bigint_##opname(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a, MVMObject *b) { \
397
1
    MVMP6bigintBody *ba = get_bigint_body(tc, a); \
398
1
    MVMP6bigintBody *bb = get_bigint_body(tc, b); \
399
1
    MVMP6bigintBody *bc; \
400
1
    MVMObject *result; \
401
1
    MVMROOT(tc, a, { \
402
1
    MVMROOT(tc, b, { \
403
1
        result = MVM_repr_alloc_init(tc, result_type);\
404
1
    }); \
405
1
    }); \
406
1
    bc = get_bigint_body(tc, result); \
407
1
    if (MVM_BIGINT_IS_BIG(ba) || MVM_BIGINT_IS_BIG(bb)) { \
408
0
        mp_int *tmp[2] = { NULL, NULL }; \
409
0
        mp_int *ia = force_bigint(ba, tmp); \
410
0
        mp_int *ib = force_bigint(bb, tmp); \
411
0
        mp_int *ic = MVM_malloc(sizeof(mp_int)); \
412
0
        mp_init(ic); \
413
0
        two_complement_bitop(ia, ib, ic, mp_##opname); \
414
0
        store_bigint_result(bc, ic); \
415
0
        clear_temp_bigints(tmp, 2); \
416
0
        adjust_nursery(tc, bc); \
417
0
    } \
418
1
    else { \
419
1
        MVMint64 sc; \
420
1
        MVMint64 sa = ba->u.smallint.value; \
421
1
        MVMint64 sb = bb->u.smallint.value; \
422
1
        SMALLINT_OP; \
423
1
        store_int64_result(bc, sc); \
424
1
    } \
425
1
    return result; \
426
1
}
MVM_bigint_and
Line
Count
Source
396
2
MVMObject * MVM_bigint_##opname(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a, MVMObject *b) { \
397
2
    MVMP6bigintBody *ba = get_bigint_body(tc, a); \
398
2
    MVMP6bigintBody *bb = get_bigint_body(tc, b); \
399
2
    MVMP6bigintBody *bc; \
400
2
    MVMObject *result; \
401
2
    MVMROOT(tc, a, { \
402
2
    MVMROOT(tc, b, { \
403
2
        result = MVM_repr_alloc_init(tc, result_type);\
404
2
    }); \
405
2
    }); \
406
2
    bc = get_bigint_body(tc, result); \
407
2
    if (MVM_BIGINT_IS_BIG(ba) || MVM_BIGINT_IS_BIG(bb)) { \
408
1
        mp_int *tmp[2] = { NULL, NULL }; \
409
1
        mp_int *ia = force_bigint(ba, tmp); \
410
1
        mp_int *ib = force_bigint(bb, tmp); \
411
1
        mp_int *ic = MVM_malloc(sizeof(mp_int)); \
412
1
        mp_init(ic); \
413
1
        two_complement_bitop(ia, ib, ic, mp_##opname); \
414
1
        store_bigint_result(bc, ic); \
415
1
        clear_temp_bigints(tmp, 2); \
416
1
        adjust_nursery(tc, bc); \
417
1
    } \
418
1
    else { \
419
1
        MVMint64 sc; \
420
1
        MVMint64 sa = ba->u.smallint.value; \
421
1
        MVMint64 sb = bb->u.smallint.value; \
422
1
        SMALLINT_OP; \
423
1
        store_int64_result(bc, sc); \
424
1
    } \
425
2
    return result; \
426
2
}
427
428
MVM_BIGINT_UNARY_OP(abs, { sb = labs(sa); })
429
MVM_BIGINT_UNARY_OP(neg, { sb = -sa; })
430
431
/* unused */
432
/* MVM_BIGINT_UNARY_OP(sqrt) */
433
434
MVM_BIGINT_BINARY_OP_SIMPLE(add, { sc = sa + sb; })
435
MVM_BIGINT_BINARY_OP_SIMPLE(sub, { sc = sa - sb; })
436
MVM_BIGINT_BINARY_OP_SIMPLE(mul, { sc = sa * sb; })
437
MVM_BIGINT_BINARY_OP(lcm)
438
439
1
MVMObject *MVM_bigint_gcd(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a, MVMObject *b) {
440
1
    MVMP6bigintBody *ba = get_bigint_body(tc, a);
441
1
    MVMP6bigintBody *bb = get_bigint_body(tc, b);
442
1
    MVMP6bigintBody *bc;
443
1
    MVMObject       *result;
444
1
445
1
    MVMROOT(tc, a, {
446
1
    MVMROOT(tc, b, {
447
1
        result = MVM_repr_alloc_init(tc, result_type);
448
1
    });
449
1
    });
450
1
451
1
    bc = get_bigint_body(tc, result);
452
1
453
1
    if (MVM_BIGINT_IS_BIG(ba) || MVM_BIGINT_IS_BIG(bb)) {
454
0
        mp_int *tmp[2] = { NULL, NULL };
455
0
        mp_int *ia = force_bigint(ba, tmp);
456
0
        mp_int *ib = force_bigint(bb, tmp);
457
0
        mp_int *ic = MVM_malloc(sizeof(mp_int));
458
0
        mp_init(ic);
459
0
        mp_gcd(ia, ib, ic);
460
0
        store_bigint_result(bc, ic);
461
0
        clear_temp_bigints(tmp, 2);
462
0
        adjust_nursery(tc, bc);
463
1
    } else {
464
1
        MVMint32 sa = ba->u.smallint.value;
465
1
        MVMint32 sb = bb->u.smallint.value;
466
1
        MVMint32 t;
467
1
        sa = abs(sa);
468
1
        sb = abs(sb);
469
3
        while (sb != 0) {
470
2
            t  = sb;
471
2
            sb = sa % sb;
472
2
            sa = t;
473
2
        }
474
1
        store_int64_result(bc, sa);
475
1
    }
476
1
477
1
    return result;
478
1
}
479
480
MVM_BIGINT_BINARY_OP_2(or , { sc = sa | sb; })
481
MVM_BIGINT_BINARY_OP_2(xor, { sc = sa ^ sb; })
482
MVM_BIGINT_BINARY_OP_2(and, { sc = sa & sb; })
483
484
81
MVMint64 MVM_bigint_cmp(MVMThreadContext *tc, MVMObject *a, MVMObject *b) {
485
81
    MVMP6bigintBody *ba = get_bigint_body(tc, a);
486
81
    MVMP6bigintBody *bb = get_bigint_body(tc, b);
487
81
    if (MVM_BIGINT_IS_BIG(ba) || MVM_BIGINT_IS_BIG(bb)) {
488
24
        mp_int *tmp[2] = { NULL, NULL };
489
24
        mp_int *ia = force_bigint(ba, tmp);
490
24
        mp_int *ib = force_bigint(bb, tmp);
491
24
        MVMint64 r = (MVMint64)mp_cmp(ia, ib);
492
24
        clear_temp_bigints(tmp, 2);
493
24
        return r;
494
24
    }
495
57
    else {
496
57
        MVMint64 sa = ba->u.smallint.value;
497
57
        MVMint64 sb = bb->u.smallint.value;
498
30
        return sa == sb ? 0 : sa <  sb ? -1 : 1;
499
57
    }
500
81
}
501
502
6
MVMObject * MVM_bigint_mod(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a, MVMObject *b) {
503
6
    MVMP6bigintBody *ba = get_bigint_body(tc, a);
504
6
    MVMP6bigintBody *bb = get_bigint_body(tc, b);
505
6
    MVMP6bigintBody *bc;
506
6
507
6
    MVMObject *result;
508
6
509
6
    MVMROOT(tc, a, {
510
6
    MVMROOT(tc, b, {
511
6
        result = MVM_repr_alloc_init(tc, result_type);
512
6
    });
513
6
    });
514
6
515
6
    bc = get_bigint_body(tc, result);
516
6
517
6
    /* XXX the behavior of C's mod operator is not correct
518
6
     * for our purposes. So we rely on mp_mod for all our modulus
519
6
     * calculations for now. */
520
6
    if (1 || MVM_BIGINT_IS_BIG(ba) || MVM_BIGINT_IS_BIG(bb)) {
521
6
        mp_int *tmp[2] = { NULL, NULL };
522
6
        mp_int *ia = force_bigint(ba, tmp);
523
6
        mp_int *ib = force_bigint(bb, tmp);
524
6
        mp_int *ic = MVM_malloc(sizeof(mp_int));
525
6
        int mp_result;
526
6
527
6
        mp_init(ic);
528
6
529
6
        mp_result = mp_mod(ia, ib, ic);
530
6
        clear_temp_bigints(tmp, 2);
531
6
532
6
        if (mp_result == MP_VAL) {
533
0
            MVM_exception_throw_adhoc(tc, "Division by zero");
534
0
        }
535
6
        store_bigint_result(bc, ic);
536
6
        adjust_nursery(tc, bc);
537
0
    } else {
538
0
        store_int64_result(bc, ba->u.smallint.value % bb->u.smallint.value);
539
0
    }
540
6
541
6
    return result;
542
6
}
543
544
4
MVMObject *MVM_bigint_div(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a, MVMObject *b) {
545
4
    MVMP6bigintBody *ba = get_bigint_body(tc, a);
546
4
    MVMP6bigintBody *bb = get_bigint_body(tc, b);
547
4
    MVMP6bigintBody *bc;
548
4
    mp_int *ia, *ib, *ic;
549
4
    int cmp_a;
550
4
    int cmp_b;
551
4
    mp_int remainder;
552
4
    mp_int intermediate;
553
4
    MVMObject *result;
554
4
555
4
    int mp_result;
556
4
557
4
    MVMROOT(tc, a, {
558
4
    MVMROOT(tc, b, {
559
4
        result = MVM_repr_alloc_init(tc, result_type);
560
4
    });
561
4
    });
562
4
563
4
    bc = get_bigint_body(tc, result);
564
4
565
4
    /* we only care about MP_LT or !MP_LT, so we give MP_GT even for 0. */
566
4
    if (MVM_BIGINT_IS_BIG(ba)) {
567
2
        cmp_a = !mp_iszero(ba->u.bigint) && SIGN(ba->u.bigint) == MP_NEG ? MP_LT : MP_GT;
568
2
    } else {
569
1
        cmp_a = ba->u.smallint.value < 0 ? MP_LT : MP_GT;
570
2
    }
571
4
    if (MVM_BIGINT_IS_BIG(bb)) {
572
1
        cmp_b = !mp_iszero(bb->u.bigint) && SIGN(bb->u.bigint) == MP_NEG ? MP_LT : MP_GT;
573
3
    } else {
574
3
        cmp_b = bb->u.smallint.value < 0 ? MP_LT : MP_GT;
575
3
    }
576
4
577
4
    if (MVM_BIGINT_IS_BIG(ba) || MVM_BIGINT_IS_BIG(bb)) {
578
2
        mp_int *tmp[2] = { NULL, NULL };
579
2
        ia = force_bigint(ba, tmp);
580
2
        ib = force_bigint(bb, tmp);
581
2
582
2
        ic = MVM_malloc(sizeof(mp_int));
583
2
        mp_init(ic);
584
2
585
2
        /* if we do a div with a negative, we need to make sure
586
2
         * the result is floored rather than rounded towards
587
2
         * zero, like C and libtommath would do. */
588
2
        if ((cmp_a == MP_LT) ^ (cmp_b == MP_LT)) {
589
1
            mp_init(&remainder);
590
1
            mp_init(&intermediate);
591
1
            mp_result = mp_div(ia, ib, &intermediate, &remainder);
592
1
            if (mp_result == MP_VAL) {
593
0
                mp_clear(&remainder);
594
0
                mp_clear(&intermediate);
595
0
                clear_temp_bigints(tmp, 2);
596
0
                MVM_exception_throw_adhoc(tc, "Division by zero");
597
0
            }
598
1
            if (mp_iszero(&remainder) == 0) {
599
1
                mp_sub_d(&intermediate, 1, ic);
600
0
            } else {
601
0
                mp_copy(&intermediate, ic);
602
0
            }
603
1
            mp_clear(&remainder);
604
1
            mp_clear(&intermediate);
605
1
        } else {
606
1
            mp_result = mp_div(ia, ib, ic, NULL);
607
1
            if (mp_result == MP_VAL) {
608
0
                clear_temp_bigints(tmp, 2);
609
0
                MVM_exception_throw_adhoc(tc, "Division by zero");
610
0
            }
611
1
        }
612
2
        store_bigint_result(bc, ic);
613
2
        clear_temp_bigints(tmp, 2);
614
2
        adjust_nursery(tc, bc);
615
2
    } else {
616
2
        MVMint32 num   = ba->u.smallint.value;
617
2
        MVMint32 denom = bb->u.smallint.value;
618
2
        MVMint32 value;
619
2
        if ((cmp_a == MP_LT) ^ (cmp_b == MP_LT)) {
620
1
            if (denom == 0) {
621
0
                MVM_exception_throw_adhoc(tc, "Division by zero");
622
0
            }
623
1
            if ((num % denom) != 0) {
624
1
                value = num / denom - 1;
625
0
            } else {
626
0
                value = num / denom;
627
0
            }
628
1
        } else {
629
1
            value = num / denom;
630
1
        }
631
2
        store_int64_result(bc, value);
632
2
    }
633
4
634
4
    return result;
635
4
}
636
637
MVMObject * MVM_bigint_pow(MVMThreadContext *tc, MVMObject *a, MVMObject *b,
638
115
        MVMObject *num_type, MVMObject *int_type) {
639
115
    MVMP6bigintBody *ba = get_bigint_body(tc, a);
640
115
    MVMP6bigintBody *bb = get_bigint_body(tc, b);
641
115
    MVMObject       *r  = NULL;
642
115
643
115
    mp_int *tmp[2] = { NULL, NULL };
644
115
    mp_int *base        = force_bigint(ba, tmp);
645
115
    mp_int *exponent    = force_bigint(bb, tmp);
646
115
    mp_digit exponent_d = 0;
647
115
648
115
    if (mp_iszero(exponent) || (MP_EQ == mp_cmp_d(base, 1))) {
649
18
        r = MVM_repr_box_int(tc, int_type, 1);
650
18
    }
651
97
    else if (SIGN(exponent) == MP_ZPOS) {
652
96
        exponent_d = mp_get_int(exponent);
653
96
        if ((MP_GT == mp_cmp_d(exponent, exponent_d))) {
654
7
            if (mp_iszero(base)) {
655
1
                r = MVM_repr_box_int(tc, int_type, 0);
656
1
            }
657
6
            else if (mp_get_int(base) == 1) {
658
2
                r = MVM_repr_box_int(tc, int_type, MP_ZPOS == SIGN(base) || mp_iseven(exponent) ? 1 : -1);
659
2
            }
660
4
            else {
661
4
                MVMnum64 inf;
662
4
                if (MP_ZPOS == SIGN(base) || mp_iseven(exponent)) {
663
3
                    inf = MVM_num_posinf(tc);
664
3
                }
665
1
                else {
666
1
                    inf = MVM_num_neginf(tc);
667
1
                }
668
4
                r = MVM_repr_box_num(tc, num_type, inf);
669
4
            }
670
7
        }
671
89
        else {
672
89
            mp_int *ic = MVM_malloc(sizeof(mp_int));
673
89
            MVMP6bigintBody *resbody;
674
89
            mp_init(ic);
675
89
            mp_expt_d(base, exponent_d, ic);
676
89
            r = MVM_repr_alloc_init(tc, int_type);
677
89
            resbody = get_bigint_body(tc, r);
678
89
            store_bigint_result(resbody, ic);
679
89
            adjust_nursery(tc, resbody);
680
89
        }
681
96
    }
682
1
    else {
683
1
        MVMnum64 f_base = mp_get_double(base);
684
1
        MVMnum64 f_exp = mp_get_double(exponent);
685
1
        r = MVM_repr_box_num(tc, num_type, pow(f_base, f_exp));
686
1
    }
687
115
    clear_temp_bigints(tmp, 2);
688
115
    return r;
689
115
}
690
691
4
MVMObject *MVM_bigint_shl(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a, MVMint64 n) {
692
4
    MVMP6bigintBody *ba = get_bigint_body(tc, a);
693
4
    MVMP6bigintBody *bb;
694
4
    MVMObject       *result;
695
4
696
4
    MVMROOT(tc, a, {
697
4
        result = MVM_repr_alloc_init(tc, result_type);
698
4
    });
699
4
700
4
    bb = get_bigint_body(tc, result);
701
4
702
4
    if (MVM_BIGINT_IS_BIG(ba) || n >= 31) {
703
0
        mp_int *tmp[1] = { NULL };
704
0
        mp_int *ia = force_bigint(ba, tmp);
705
0
        mp_int *ib = MVM_malloc(sizeof(mp_int));
706
0
        mp_init(ib);
707
0
        two_complement_shl(ib, ia, n);
708
0
        store_bigint_result(bb, ib);
709
0
        clear_temp_bigints(tmp, 1);
710
0
        adjust_nursery(tc, bb);
711
4
    } else {
712
4
        MVMint64 value;
713
4
        if (n < 0)
714
3
            value = ((MVMint64)ba->u.smallint.value) >> -n;
715
4
        else
716
1
            value = ((MVMint64)ba->u.smallint.value) << n;
717
4
        store_int64_result(bb, value);
718
4
    }
719
4
720
4
    return result;
721
4
}
722
723
4
MVMObject *MVM_bigint_shr(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a, MVMint64 n) {
724
4
    MVMP6bigintBody *ba = get_bigint_body(tc, a);
725
4
    MVMP6bigintBody *bb;
726
4
    MVMObject       *result;
727
4
728
4
    MVMROOT(tc, a, {
729
4
        result = MVM_repr_alloc_init(tc, result_type);
730
4
    });
731
4
732
4
    bb = get_bigint_body(tc, result);
733
4
734
4
    if (MVM_BIGINT_IS_BIG(ba) || n < 0) {
735
0
        mp_int *tmp[1] = { NULL };
736
0
        mp_int *ia = force_bigint(ba, tmp);
737
0
        mp_int *ib = MVM_malloc(sizeof(mp_int));
738
0
        mp_init(ib);
739
0
        two_complement_shl(ib, ia, -n);
740
0
        store_bigint_result(bb, ib);
741
0
        clear_temp_bigints(tmp, 1);
742
0
        adjust_nursery(tc, bb);
743
4
    } else if (n >= 32) {
744
0
        store_int64_result(bb, 0);
745
4
    } else {
746
4
        MVMint32 value = ba->u.smallint.value;
747
4
        value = value >> n;
748
4
        store_int64_result(bb, value);
749
4
    }
750
4
751
4
    return result;
752
4
}
753
754
1
MVMObject *MVM_bigint_not(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a) {
755
1
    MVMP6bigintBody *ba = get_bigint_body(tc, a);
756
1
    MVMP6bigintBody *bb;
757
1
    MVMObject       *result;
758
1
759
1
    MVMROOT(tc, a, {
760
1
        result = MVM_repr_alloc_init(tc, result_type);
761
1
    });
762
1
763
1
    bb = get_bigint_body(tc, result);
764
1
765
1
    if (MVM_BIGINT_IS_BIG(ba)) {
766
0
        mp_int *ia = ba->u.bigint;
767
0
        mp_int *ib = MVM_malloc(sizeof(mp_int));
768
0
        mp_init(ib);
769
0
        /* two's complement not: add 1 and negate */
770
0
        mp_add_d(ia, 1, ib);
771
0
        mp_neg(ib, ib);
772
0
        store_bigint_result(bb, ib);
773
0
        adjust_nursery(tc, bb);
774
1
    } else {
775
1
        MVMint32 value = ba->u.smallint.value;
776
1
        value = ~value;
777
1
        store_int64_result(bb, value);
778
1
    }
779
1
780
1
    return result;
781
1
}
782
783
1
void MVM_bigint_expmod(MVMThreadContext *tc, MVMObject *result, MVMObject *a, MVMObject *b, MVMObject *c) {
784
1
    MVMP6bigintBody *ba = get_bigint_body(tc, a);
785
1
    MVMP6bigintBody *bb = get_bigint_body(tc, b);
786
1
    MVMP6bigintBody *bc = get_bigint_body(tc, c);
787
1
    MVMP6bigintBody *bd = get_bigint_body(tc, result);
788
1
789
1
    mp_int *tmp[3] = { NULL, NULL, NULL };
790
1
791
1
    mp_int *ia = force_bigint(ba, tmp);
792
1
    mp_int *ib = force_bigint(bb, tmp);
793
1
    mp_int *ic = force_bigint(bc, tmp);
794
1
    mp_int *id = MVM_malloc(sizeof(mp_int));
795
1
    mp_init(id);
796
1
797
1
    mp_exptmod(ia, ib, ic, id);
798
1
    store_bigint_result(bd, id);
799
1
    clear_temp_bigints(tmp, 3);
800
1
    adjust_nursery(tc, bd);
801
1
}
802
803
225
void MVM_bigint_from_str(MVMThreadContext *tc, MVMObject *a, const char *buf) {
804
225
    MVMP6bigintBody *body = get_bigint_body(tc, a);
805
225
    mp_int *i = MVM_malloc(sizeof(mp_int));
806
225
    mp_init(i);
807
225
    mp_read_radix(i, buf, 10);
808
225
    adjust_nursery(tc, body);
809
225
    if (can_be_smallint(i)) {
810
0
        body->u.smallint.flag = MVM_BIGINT_32_FLAG;
811
0
        body->u.smallint.value = SIGN(i) == MP_NEG ? -DIGIT(i, 0) : DIGIT(i, 0);
812
0
        mp_clear(i);
813
0
        MVM_free(i);
814
0
    }
815
225
    else {
816
225
        body->u.bigint = i;
817
225
    }
818
225
}
819
820
442
MVMString * MVM_bigint_to_str(MVMThreadContext *tc, MVMObject *a, int base) {
821
442
    MVMP6bigintBody *body = get_bigint_body(tc, a);
822
442
    if (MVM_BIGINT_IS_BIG(body)) {
823
361
        mp_int *i = body->u.bigint;
824
361
        int len;
825
361
        char *buf;
826
361
        MVMString *result;
827
361
        mp_radix_size(i, base, &len);
828
361
        buf = (char *) MVM_malloc(len);
829
361
        mp_toradix_n(i, buf, base, len);
830
361
        result = MVM_string_ascii_decode(tc, tc->instance->VMString, buf, len - 1);
831
361
        MVM_free(buf);
832
361
        return result;
833
361
    }
834
81
    else {
835
81
        if (base == 10) {
836
32
            return MVM_coerce_i_s(tc, body->u.smallint.value);
837
32
        }
838
49
        else {
839
49
            /* It's small, but shove it through bigint lib, as it knows how to
840
49
             * get other bases right. */
841
49
            mp_int i;
842
49
            int len;
843
49
            char *buf;
844
49
            MVMString *result;
845
49
846
49
            MVMint32 value = body->u.smallint.value;
847
49
            mp_init(&i);
848
49
            if (value >= 0) {
849
45
                mp_set_long(&i, value);
850
45
            }
851
4
            else {
852
4
                mp_set_long(&i, -value);
853
4
                mp_neg(&i, &i);
854
4
            }
855
49
856
49
            mp_radix_size(&i, base, &len);
857
49
            buf = (char *) MVM_malloc(len);
858
49
            mp_toradix_n(&i, buf, base, len);
859
49
            result = MVM_string_ascii_decode(tc, tc->instance->VMString, buf, len - 1);
860
49
            MVM_free(buf);
861
49
            mp_clear(&i);
862
49
863
49
            return result;
864
49
        }
865
81
    }
866
442
}
867
868
8
MVMnum64 MVM_bigint_to_num(MVMThreadContext *tc, MVMObject *a) {
869
8
    MVMP6bigintBody *ba = get_bigint_body(tc, a);
870
8
871
8
    if (MVM_BIGINT_IS_BIG(ba)) {
872
8
        mp_int *ia = ba->u.bigint;
873
8
        return mp_get_double(ia);
874
0
    } else {
875
0
        return (double)ba->u.smallint.value;
876
0
    }
877
8
}
878
879
320
MVMObject *MVM_bigint_from_num(MVMThreadContext *tc, MVMObject *result_type, MVMnum64 n) {
880
320
    MVMObject * const result = MVM_repr_alloc_init(tc, result_type);
881
320
    MVMP6bigintBody *ba = get_bigint_body(tc, result);
882
320
    mp_int *ia = MVM_malloc(sizeof(mp_int));
883
320
    mp_init(ia);
884
320
    from_num(n, ia);
885
320
    store_bigint_result(ba, ia);
886
320
    return result;
887
320
}
888
889
7
MVMnum64 MVM_bigint_div_num(MVMThreadContext *tc, MVMObject *a, MVMObject *b) {
890
7
    MVMP6bigintBody *ba = get_bigint_body(tc, a);
891
7
    MVMP6bigintBody *bb = get_bigint_body(tc, b);
892
7
    MVMnum64 c;
893
7
894
7
    if (MVM_BIGINT_IS_BIG(ba) || MVM_BIGINT_IS_BIG(bb)) {
895
1
        mp_int *tmp[2] = { NULL, NULL };
896
1
        mp_int *ia = force_bigint(ba, tmp);
897
1
        mp_int *ib = force_bigint(bb, tmp);
898
1
899
1
        int max_size = DIGIT_BIT * MAX(USED(ia), USED(ib));
900
1
        if (max_size > 1023) {
901
1
            mp_int reduced_a, reduced_b;
902
1
            mp_init(&reduced_a);
903
1
            mp_init(&reduced_b);
904
1
            mp_div_2d(ia, max_size - 1023, &reduced_a, NULL);
905
1
            mp_div_2d(ib, max_size - 1023, &reduced_b, NULL);
906
1
            c = mp_get_double(&reduced_a) / mp_get_double(&reduced_b);
907
1
            mp_clear(&reduced_a);
908
1
            mp_clear(&reduced_b);
909
0
        } else {
910
0
            c = mp_get_double(ia) / mp_get_double(ib);
911
0
        }
912
1
        clear_temp_bigints(tmp, 2);
913
6
    } else {
914
6
        c = (double)ba->u.smallint.value / (double)bb->u.smallint.value;
915
6
    }
916
7
    return c;
917
7
}
918
919
1
void MVM_bigint_rand(MVMThreadContext *tc, MVMObject *a, MVMObject *b) {
920
1
    MVMP6bigintBody *ba = get_bigint_body(tc, a);
921
1
    MVMP6bigintBody *bb = get_bigint_body(tc, b);
922
1
923
1
    mp_int *tmp[1] = { NULL };
924
1
    mp_int *rnd = MVM_malloc(sizeof(mp_int));
925
1
    mp_int *max = force_bigint(bb, tmp);
926
1
927
1
    /* Workaround tommath issue #56 */
928
1
    mp_int workaround;
929
1
    mp_init (&workaround);
930
1
    mp_rand(&workaround, USED(max) + 1);
931
1
    mp_mul_2d(&workaround, 29, &workaround);
932
1
933
1
    mp_init(rnd);
934
1
    mp_rand(rnd, USED(max) + 1);
935
1
936
1
    mp_xor(rnd, &workaround, rnd);
937
1
    mp_clear(&workaround);
938
1
939
1
    mp_mod(rnd, max, rnd);
940
1
    store_bigint_result(ba, rnd);
941
1
    clear_temp_bigints(tmp, 1);
942
1
    adjust_nursery(tc, ba);
943
1
}
944
945
6
MVMint64 MVM_bigint_is_prime(MVMThreadContext *tc, MVMObject *a, MVMint64 b) {
946
6
    /* mp_prime_is_prime returns True for 1, and I think
947
6
     * it's worth special-casing this particular number :-)
948
6
     */
949
6
    MVMP6bigintBody *ba = get_bigint_body(tc, a);
950
6
951
6
    if (MVM_BIGINT_IS_BIG(ba) || ba->u.smallint.value != 1) {
952
5
        mp_int *tmp[1] = { NULL };
953
5
        mp_int *ia = force_bigint(ba, tmp);
954
5
        if (mp_cmp_d(ia, 1) == MP_EQ) {
955
0
            clear_temp_bigints(tmp, 1);
956
0
            return 0;
957
0
        }
958
5
        else {
959
5
            int result;
960
5
            mp_prime_is_prime(ia, b, &result);
961
5
            clear_temp_bigints(tmp, 1);
962
5
            return result;
963
5
        }
964
1
    } else {
965
1
        /* we only reach this if we have a smallint that's equal to 1.
966
1
         * which we define as not-prime. */
967
1
        return 0;
968
1
    }
969
6
}
970
971
/* concatenating with "" ensures that only literal strings are accepted as argument. */
972
#define STR_WITH_LEN(str)  ("" str ""), (sizeof(str) - 1)
973
974
21
MVMObject * MVM_bigint_radix(MVMThreadContext *tc, MVMint64 radix, MVMString *str, MVMint64 offset, MVMint64 flag, MVMObject *type) {
975
21
    MVMObject *result;
976
21
    MVMint64 chars  = MVM_string_graphs(tc, str);
977
21
    MVMuint16  neg  = 0;
978
21
    MVMint64   ch;
979
21
980
21
    mp_int zvalue;
981
21
    mp_int zbase;
982
21
983
21
    MVMObject *value_obj;
984
21
    mp_int *value;
985
21
    MVMP6bigintBody *bvalue;
986
21
987
21
    MVMObject *base_obj;
988
21
    mp_int *base;
989
21
    MVMP6bigintBody *bbase;
990
21
991
21
    MVMObject *pos_obj;
992
21
    MVMint64   pos  = -1;
993
21
994
21
    if (radix > 36) {
995
0
        MVM_exception_throw_adhoc(tc, "Cannot convert radix of %"PRId64" (max 36)", radix);
996
0
    }
997
21
998
21
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&str);
999
21
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&type);
1000
21
1001
21
    /* initialize the object */
1002
21
    result = MVM_repr_alloc_init(tc, MVM_hll_current(tc)->slurpy_array_type);
1003
21
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&result);
1004
21
1005
21
    mp_init(&zvalue);
1006
21
    mp_init(&zbase);
1007
21
    mp_set_int(&zbase, 1);
1008
21
1009
21
    value_obj = MVM_repr_alloc_init(tc, type);
1010
21
    MVM_repr_push_o(tc, result, value_obj);
1011
21
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&value_obj);
1012
21
1013
21
    base_obj = MVM_repr_alloc_init(tc, type);
1014
21
    MVM_repr_push_o(tc, result, base_obj);
1015
21
1016
21
    bvalue = get_bigint_body(tc, value_obj);
1017
21
    bbase  = get_bigint_body(tc, base_obj);
1018
21
1019
21
    value = MVM_malloc(sizeof(mp_int));
1020
21
    base  = MVM_malloc(sizeof(mp_int));
1021
21
1022
21
    mp_init(value);
1023
21
    mp_init(base);
1024
21
1025
21
    mp_set_int(base, 1);
1026
21
1027
21
    ch = (offset < chars) ? MVM_string_get_grapheme_at_nocheck(tc, str, offset) : 0;
1028
21
    if ((flag & 0x02) && (ch == '+' || ch == '-')) {
1029
4
        neg = (ch == '-');
1030
4
        offset++;
1031
4
        ch = (offset < chars) ? MVM_string_get_grapheme_at_nocheck(tc, str, offset) : 0;
1032
4
    }
1033
21
1034
126
    while (offset < chars) {
1035
126
        if (ch >= '0' && ch <= '9') ch = ch - '0'; /* fast-path for ASCII 0..9 */
1036
8
        else if (ch >= 'a' && ch <= 'z') ch = ch - 'a' + 10;
1037
5
        else if (ch >= 'A' && ch <= 'Z') ch = ch - 'A' + 10;
1038
2
        else if (ch >= 0xFF21 && ch <= 0xFF3A) ch = ch - 0xFF21 + 10; /* uppercase fullwidth */
1039
2
        else if (ch >= 0xFF41 && ch <= 0xFF5A) ch = ch - 0xFF41 + 10; /* lowercase fullwidth */
1040
2
        else if (ch > 0 && MVM_unicode_codepoint_get_property_int(tc, ch, MVM_UNICODE_PROPERTY_NUMERIC_TYPE)
1041
2
         == MVM_UNICODE_PVALUE_Numeric_Type_DECIMAL) {
1042
0
            /* as of Unicode 9.0.0, characters with the 'de' Numeric Type (and are
1043
0
             * thus also of General Category Nd, since 4.0.0) are contiguous
1044
0
             * sequences of 10 chars whose Numeric Values ascend from 0 through 9.
1045
0
             */
1046
0
1047
0
            /* the string returned for NUMERIC_VALUE_NUMERATOR contains an integer
1048
0
             * value. We can use numerator because they all are from 0-9 and have
1049
0
             * denominator of 1 */
1050
0
            ch = fast_atoi(MVM_unicode_codepoint_get_property_cstr(tc, ch, MVM_UNICODE_PROPERTY_NUMERIC_VALUE_NUMERATOR));
1051
0
        }
1052
2
        else break;
1053
124
        if (ch >= radix) break;
1054
123
        mp_mul_d(&zvalue, radix, &zvalue);
1055
123
        mp_add_d(&zvalue, ch, &zvalue);
1056
123
        mp_mul_d(&zbase, radix, &zbase);
1057
123
        offset++; pos = offset;
1058
123
        if (ch != 0 || !(flag & 0x04)) { mp_copy(&zvalue, value); mp_copy(&zbase, base); }
1059
123
        if (offset >= chars) break;
1060
105
        ch = MVM_string_get_grapheme_at_nocheck(tc, str, offset);
1061
105
        if (ch != '_') continue;
1062
4
        offset++;
1063
4
        if (offset >= chars) break;
1064
4
        ch = MVM_string_get_grapheme_at_nocheck(tc, str, offset);
1065
4
    }
1066
21
1067
21
    mp_clear(&zvalue);
1068
21
    mp_clear(&zbase);
1069
21
1070
21
    if (neg || flag & 0x01) {
1071
5
        mp_neg(value, value);
1072
5
    }
1073
21
1074
21
    store_bigint_result(bvalue, value);
1075
21
    store_bigint_result(bbase, base);
1076
21
1077
21
    adjust_nursery(tc, bvalue);
1078
21
    adjust_nursery(tc, bbase);
1079
21
1080
21
    pos_obj = MVM_repr_box_int(tc, type, pos);
1081
21
    MVM_repr_push_o(tc, result, pos_obj);
1082
21
1083
21
    MVM_gc_root_temp_pop_n(tc, 4);
1084
21
1085
21
    return result;
1086
21
}
1087
1088
/* returns 1 if a is too large to fit into an INTVAL without loss of
1089
   information */
1090
6
MVMint64 MVM_bigint_is_big(MVMThreadContext *tc, MVMObject *a) {
1091
6
    MVMP6bigintBody *ba = get_bigint_body(tc, a);
1092
6
1093
6
    if (MVM_BIGINT_IS_BIG(ba)) {
1094
1
        mp_int *b = ba->u.bigint;
1095
1
        MVMint64 is_big = b->used > 1;
1096
1
        /* XXX somebody please check that on a 32 bit platform */
1097
1
        if ( sizeof(MVMint64) * 8 > DIGIT_BIT && is_big == 0 && DIGIT(b, 0) & ~0x7FFFFFFFUL)
1098
0
            is_big = 1;
1099
1
        return is_big;
1100
5
    } else {
1101
5
        /* if it's in a smallint, it's 32 bits big at most and fits into an INTVAL easily. */
1102
5
        return 0;
1103
5
    }
1104
6
}
1105
1106
4
MVMint64 MVM_bigint_bool(MVMThreadContext *tc, MVMObject *a) {
1107
4
    MVMP6bigintBody *body = get_bigint_body(tc, a);
1108
4
    if (MVM_BIGINT_IS_BIG(body))
1109
2
        return !mp_iszero(body->u.bigint);
1110
4
    else
1111
2
        return body->u.smallint.value != 0;
1112
4
}