Coverage Report

Created: 2018-07-03 15:31

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