VALUE
rb_int_powm(int const argc, VALUE * const argv, VALUE const num)
{
rb_check_arity(argc, 1, 2);
if (argc == 1) {
return rb_funcall(num, rb_intern("**"), 1, argv[0]);
}
else {
VALUE const a = num;
VALUE const b = argv[0];
VALUE m = argv[1];
int nega_flg = 0;
if ( ! RB_INTEGER_TYPE_P(b)) {
rb_raise(rb_eTypeError, "Integer#pow() 2nd argument not allowed unless a 1st argument is integer");
}
if (rb_num_negative_int_p(b)) {
rb_raise(rb_eRangeError, "Integer#pow() 1st argument cannot be negative when 2nd argument specified");
}
if (!RB_INTEGER_TYPE_P(m)) {
rb_raise(rb_eTypeError, "Integer#pow() 2nd argument not allowed unless all arguments are integers");
}
if (rb_num_negative_int_p(m)) {
m = rb_funcall(m, idUMinus, 0);
nega_flg = 1;
}
if (!rb_num_positive_int_p(m)) {
rb_num_zerodiv();
}
if (FIXNUM_P(m)) {
long const half_val = (long)HALF_LONG_MSB;
long const mm = FIX2LONG(m);
if (mm <= half_val) {
return int_pow_tmp1(rb_int_modulo(a, m), b, mm, nega_flg);
} else {
return int_pow_tmp2(rb_int_modulo(a, m), b, mm, nega_flg);
}
} else if (RB_TYPE_P(m, T_BIGNUM)) {
return int_pow_tmp3(rb_int_modulo(a, m), b, m, nega_flg);
}
}
UNREACHABLE;
}