/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 | } 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 | } |
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 | } 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 | } |
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 | } |
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 | } 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 | } |
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 | } |
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 | } |