Skip to content

Commit caacd56

Browse files
committed
First shot at a "smart" nqp::pow_I opcode
The idea is to return either a bigint or a float (for the +-Inf case, and when the exponent is negative). The float part isn't tested yet, so likely wrong. The part that returns bigints has sparse test coverage, so it might be not too far off.
1 parent cf9144d commit caacd56

File tree

3 files changed

+75
-4
lines changed

3 files changed

+75
-4
lines changed

src/PAST/NQP.pir

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,7 @@ entry to produce the node to be returned.
455455
maphash['mod_I'] = 'nqp_bigint_mod__PPP'
456456
maphash['mod_n'] = 'mod__Nnn'
457457
maphash['pow_n'] = 'pow__Nnn'
458+
maphash['pow_I'] = 'nqp_bigint_pow__PPPP'
458459
maphash['neg_i'] = 'neg__Ii'
459460
maphash['neg_I'] = 'nqp_bigint_neg__PP'
460461
maphash['neg_n'] = 'neg__Nn'

src/ops/nqp_bigint.ops

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,3 +318,64 @@ inline op nqp_bigint_radix(out PMC, in INT, in STR, in INT, in INT, in PMC) :bas
318318
VTABLE_set_pmc_keyed_int(interp, out, 2, pos_obj);
319319
$1 = out;
320320
}
321+
322+
/* calculates $1 = $2 ** $3
323+
* if it either overflows ($3 being too big), or $2 is negative,
324+
* a float is returned. $4 should contain the type object to box the
325+
* float into.
326+
*/
327+
inline op nqp_bigint_pow(out PMC, in PMC, in PMC, in PMC) :base_core {
328+
mp_digit exponent_d = 0;
329+
mp_int *exponent = get_bigint(interp, $3);
330+
mp_int *base = get_bigint(interp, $2);
331+
int cmp = mp_cmp_d(exponent, 0);
332+
if (cmp == MP_EQ || MP_EQ == mp_cmp_d(base, 1)) {
333+
/* $x ** 0 or 1 ** $x */
334+
$1 = REPR($2)->allocate(interp, STABLE($2));
335+
REPR($1)->initialize(interp, STABLE($1), OBJECT_BODY($1));
336+
mp_set_int(get_bigint(interp, $1), 1);
337+
}
338+
else if (cmp == MP_GT) {
339+
exponent_d = mp_get_int(exponent);
340+
if (MP_GT == mp_cmp_d(exponent, exponent_d)) {
341+
/* the exponent is larger than what fits into an int register...
342+
* that's scary, and should be treated with care */
343+
344+
345+
/* XXX a bit ugly that it reuses cmp, but safe for now */
346+
cmp = mp_cmp_d(base, 0)
347+
if (MP_EQ == cmp || MP_EQ == mp_cmp_d(base, 1)) {
348+
/* 0 ** $big_number and 1 ** big_number are easy to do: */
349+
$1 = REPR($2)->allocate(interp, STABLE($2));
350+
REPR($1)->initialize(interp, STABLE($1), OBJECT_BODY($1));
351+
mp_copy(base, get_bigint(interp, $1));
352+
}
353+
else {
354+
$1 = REPR($4)->allocate(interp, STABLE($4));
355+
REPR($1)->initialize(interp, STABLE($1), OBJECT_BODY($1));
356+
/* TODO: better ways to create +- Inf */
357+
if (MP_GT == cmp) {
358+
REPR($1)->set_num(interp, STABLE($1), OBJECT_BODY($1), (FLOATVAL) 1.0/0.0);
359+
}
360+
else {
361+
REPR($1)->set_num(interp, STABLE($1), OBJECT_BODY($1), (FLOATVAL) -1.0/0.0);
362+
}
363+
}
364+
}
365+
else {
366+
/* since the exponent fits into a digit, mp_expt_d is fine */
367+
$1 = REPR($2)->allocate(interp, STABLE($2));
368+
REPR($1)->initialize(interp, STABLE($1), OBJECT_BODY($1));
369+
mp_expt_d(get_bigint(interp, $2), exponent_d, get_bigint(interp, $1));
370+
}
371+
}
372+
else {
373+
/* TODO: better way to get floats out of mp_int */
374+
FLOATVAL f_base = (FLOATVAL) REPR($2)->get_int(interp, STABLE($2), OBJECT_BODY($2));
375+
FLOATVAL f_exp = (FLOATVAL) REPR($3)->get_int(interp, STABLE($3), OBJECT_BODY($3));
376+
$1 = REPR($4)->allocate(interp, STABLE($4));
377+
REPR($1)->initialize(interp, STABLE($1), OBJECT_BODY($1));
378+
REPR($1)->set_num(interp, STABLE($1), OBJECT_BODY($1), pow(f_base, f_exp));
379+
}
380+
}
381+

t/nqp/60-bigint.t

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
#! nqp
22
use nqpmo;
33

4-
plan(19);
4+
plan(22);
55

66
pir::nqp_bigint_setup__v();
77

88
my $knowhow := pir::get_knowhow__P();
99
my $bi_type := $knowhow.new_type(:name('TestBigInt'), :repr('P6bigint'));
1010
$bi_type.HOW.compose($bi_type);
11-
sub s($x) { pir::nqp_bigint_to_str__SP($x) };
11+
sub str($x) { pir::nqp_bigint_to_str__SP($x) };
1212
sub iseq($x, $y) { nqp::iseq_I($x, nqp::box_i($y, $bi_type)) }
1313
sub box($x) { nqp::box_i($x, $bi_type) }
1414

@@ -17,8 +17,8 @@ my $one := box(1);
1717
my $b := pir::nqp_bigint_from_str__PPS($one, '-123');
1818
my $c := box(-123);
1919

20-
ok(s($b) eq '-123', 'can round-trip negative number (string)');
21-
ok(s($c) eq '-123', 'can round-trip negative number (string) by boxing');
20+
ok(str($b) eq '-123', 'can round-trip negative number (string)');
21+
ok(str($c) eq '-123', 'can round-trip negative number (string) by boxing');
2222
ok(nqp::unbox_i($b) == -123, 'can round-trip negative number by unboxing');
2323
ok(!nqp::iseq_I($one, $b), 'nqp::iseq_I can return false');
2424
ok(nqp::iseq_I($one, $one), 'nqp::iseq_I can return true');
@@ -51,3 +51,12 @@ ok(iseq($box_val_1, 4), 'can box to a complex type with a P6bigint target');
5151
my $box_val_2 := pir::nqp_bigint_from_str__PPS($bi_boxer, '38');
5252
ok(iseq($box_val_2, 38), 'can get a bigint from a string with boxing type');
5353
ok(iseq(nqp::add_I($box_val_1, $box_val_2), 42), 'addition works on boxing type');
54+
55+
56+
# Note that the last argument to pow_I should be capable of boxing a num,
57+
# so $bi_type is wrong here. But so far we only test the integer case,
58+
# so we can get away with it.
59+
my $big := nqp::pow_I($c, box(42), $bi_type);
60+
ok(str($big) eq '5970554685064519004265641008828923248442340700473500698131071806779372733915289638628729', 'pow (int, positive)');
61+
ok(iseq(nqp::pow_I(box(0), $big, $bi_type), 0), 'pow 0 ** large_number');
62+
ok(iseq(nqp::pow_I($one, $big, $bi_type), 1), 'pow 1 ** large_number');

0 commit comments

Comments
 (0)