Coverage Report

Created: 2023-09-25 06:17

/src/mruby/mrbgems/mruby-numeric-ext/src/numeric_ext.c
Line
Count
Source (jump to first uncovered line)
1
#include <mruby.h>
2
#include <mruby/numeric.h>
3
#include <mruby/array.h>
4
#include <mruby/internal.h>
5
#include <mruby/presym.h>
6
7
#ifndef MRB_NO_FLOAT
8
static mrb_value flo_remainder(mrb_state *mrb, mrb_value self);
9
#endif
10
11
/*
12
 *  call-seq:
13
 *     num.remainder(numeric)  ->  real
14
 *
15
 *  <code>x.remainder(y)</code> means <code>x-y*(x/y).truncate</code>.
16
 *
17
 *  See Numeric#divmod.
18
 */
19
static mrb_value
20
int_remainder(mrb_state *mrb, mrb_value x)
21
0
{
22
0
  mrb_value y = mrb_get_arg1(mrb);
23
0
  mrb_int a, b;
24
25
0
#ifdef MRB_USE_BIGINT
26
0
  if (mrb_bigint_p(x)) {
27
0
    if (mrb_integer_p(y) || mrb_bigint_p(y)) {
28
0
      return mrb_bint_rem(mrb, x, y);
29
0
    }
30
0
    return flo_remainder(mrb, mrb_float_value(mrb, mrb_as_float(mrb, x)));
31
0
  }
32
0
#endif
33
0
  a = mrb_integer(x);
34
0
  if (mrb_integer_p(y)) {
35
0
    b = mrb_integer(y);
36
0
    if (b == 0) mrb_int_zerodiv(mrb);
37
0
    if (a == MRB_INT_MIN && b == -1) return mrb_fixnum_value(0);
38
0
    return mrb_int_value(mrb, a % b);
39
0
  }
40
#ifdef MRB_NO_FLOAT
41
  mrb_raise(mrb, E_TYPE_ERROR, "non integer remainder");
42
#else
43
0
  return flo_remainder(mrb, mrb_float_value(mrb, mrb_as_float(mrb, x)));
44
0
#endif
45
0
}
46
47
mrb_value mrb_int_pow(mrb_state *mrb, mrb_value x, mrb_value y);
48
49
/*
50
 * call-seq:
51
 *    integer.pow(numeric)           ->  numeric
52
 *    integer.pow(integer, integer)  ->  integer
53
 *
54
 * Returns (modular) exponentiation as:
55
 *
56
 *   a.pow(b)     #=> same as a**b
57
 *   a.pow(b, m)  #=> same as (a**b) % m, but avoids huge temporary values
58
 */
59
static mrb_value
60
int_powm(mrb_state *mrb, mrb_value x)
61
0
{
62
0
  mrb_value m, e;
63
0
  mrb_int exp, mod, result = 1;
64
65
0
  if (mrb_get_argc(mrb) == 1) {
66
0
    return mrb_int_pow(mrb, x, mrb_get_arg1(mrb));
67
0
  }
68
0
  mrb_get_args(mrb, "oo", &e, &m);
69
0
  if (!mrb_integer_p(e)
70
0
#ifdef MRB_USE_BIGINT
71
0
      && !mrb_bigint_p(e)
72
0
#endif
73
0
      ) {
74
0
    mrb_raise(mrb, E_TYPE_ERROR, "int.pow(n,m): 2nd argument not allowed unless 1st argument is an integer");
75
0
  }
76
0
#ifdef MRB_USE_BIGINT
77
0
  if (mrb_bigint_p(x)) {
78
0
    return mrb_bint_powm(mrb, x, e, m);
79
0
  }
80
0
  if (mrb_bigint_p(e) || mrb_bigint_p(m)) {
81
0
    return mrb_bint_powm(mrb, mrb_bint_new_int(mrb, mrb_integer(x)), e, m);
82
0
  }
83
0
#endif
84
0
  exp = mrb_integer(e);
85
0
  if (exp < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "int.pow(n,m): n must be positive");
86
0
  if (!mrb_integer_p(m)) mrb_raise(mrb, E_TYPE_ERROR, "int.pow(n,m): m must be integer");
87
0
  mod = mrb_integer(m);
88
0
  if (mod < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "int.pow(n,m): m must be positive when 2nd argument specified");
89
0
  if (mod == 0) mrb_int_zerodiv(mrb);
90
0
  if (mod == 1) return mrb_fixnum_value(0);
91
0
  mrb_int base = mrb_integer(x);
92
0
  for (;;) {
93
0
    mrb_int tmp;
94
0
    if (exp & 1) {
95
0
      if (mrb_int_mul_overflow(result, base, &tmp)) {
96
0
        result %= mod; base %= mod;
97
0
        if (mrb_int_mul_overflow(result, base, &tmp)) {
98
0
#ifdef MRB_USE_BIGINT
99
0
          return mrb_bint_powm(mrb, mrb_bint_new_int(mrb, mrb_integer(x)), e, m);
100
#else
101
          mrb_int_overflow(mrb, "pow");
102
#endif
103
0
        }
104
0
      }
105
0
      result = tmp % mod;
106
0
    }
107
0
    exp >>= 1;
108
0
    if (exp == 0) break;
109
0
    if (mrb_int_mul_overflow(base, base, &tmp)) {
110
0
      base %= mod;
111
0
      if (mrb_int_mul_overflow(base, base, &tmp)) {
112
0
#ifdef MRB_USE_BIGINT
113
0
        return mrb_bint_powm(mrb, mrb_bint_new_int(mrb, mrb_integer(x)), e, m);
114
#else
115
        mrb_int_overflow(mrb, "pow");
116
#endif
117
0
      }
118
0
    }
119
0
    base = tmp % mod;
120
0
  }
121
0
  return mrb_int_value(mrb, result);
122
0
}
123
124
/*
125
 *  call-seq:
126
 *    digits(base = 10) -> array_of_integers
127
 *
128
 *  Returns an array of integers representing the +base+-radix
129
 *  digits of +self+;
130
 *  the first element of the array represents the least significant digit:
131
 *
132
 *    12345.digits      # => [5, 4, 3, 2, 1]
133
 *    12345.digits(7)   # => [4, 6, 6, 0, 5]
134
 *    12345.digits(100) # => [45, 23, 1]
135
 *
136
 *  Raises an exception if +self+ is negative or +base+ is less than 2.
137
 *
138
 */
139
140
static mrb_value
141
int_digits(mrb_state *mrb, mrb_value self)
142
0
{
143
0
  mrb_int base = 10;
144
145
0
  mrb_get_args(mrb, "|i", &base);
146
0
  if (base < 0) {
147
0
    mrb_raise(mrb, E_ARGUMENT_ERROR, "negative radix");
148
0
  }
149
0
  else if (base < 2) {
150
0
    mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %i", base);
151
0
  }
152
0
#ifdef MRB_USE_BIGINT
153
0
  if (mrb_bigint_p(self)) {
154
0
    mrb_value x = self;
155
0
    mrb_value zero = mrb_fixnum_value(0);
156
0
    mrb_value bv = mrb_int_value(mrb, base);
157
0
    if (mrb_bint_cmp(mrb, x, zero) < 0) {
158
0
      mrb_raise(mrb, E_ARGUMENT_ERROR, "number should be positive");
159
0
    }
160
0
    mrb_value digits = mrb_ary_new(mrb);
161
162
0
    while (mrb_bint_cmp(mrb, x, zero) == 0) {
163
0
      mrb_ary_push(mrb, digits, zero);
164
0
      return digits;
165
0
    }
166
167
0
    while (mrb_bint_cmp(mrb, x, zero) > 0) {
168
0
      mrb_value q = mrb_bint_mod(mrb, x, bv);
169
0
      mrb_ary_push(mrb, digits, q);
170
0
      x = mrb_bint_div(mrb, x, bv);
171
0
      if (!mrb_bigint_p(x)) {
172
0
        mrb_int n = mrb_integer(x);
173
0
        while (n > 0) {
174
0
          mrb_int q = n % base;
175
0
          mrb_ary_push(mrb, digits, mrb_int_value(mrb, q));
176
0
          n /= base;
177
0
        }
178
0
        break;
179
0
      }
180
0
    }
181
0
    return digits;
182
0
  }
183
0
#endif
184
0
  mrb_int n = mrb_integer(self);
185
0
  if (n < 0) {
186
0
    mrb_raise(mrb, E_ARGUMENT_ERROR, "number should be positive");
187
0
  }
188
189
0
  mrb_value digits = mrb_ary_new(mrb);
190
191
0
  if (n == 0) {
192
0
    mrb_ary_push(mrb, digits, mrb_fixnum_value(0));
193
0
    return digits;
194
0
  }
195
196
0
  while (n > 0) {
197
0
    mrb_int q = n % base;
198
0
    mrb_ary_push(mrb, digits, mrb_int_value(mrb, q));
199
0
    n /= base;
200
0
  }
201
0
  return digits;
202
0
}
203
204
#ifndef MRB_NO_FLOAT
205
static mrb_value
206
flo_remainder(mrb_state *mrb, mrb_value self)
207
0
{
208
0
  mrb_float a, b;
209
210
0
  a = mrb_float(self);
211
0
  mrb_get_args(mrb, "f", &b);
212
0
  if (b == 0) mrb_int_zerodiv(mrb);
213
0
  if (isinf(b)) return mrb_float_value(mrb, a);
214
0
  return mrb_float_value(mrb, a-b*trunc(a/b));
215
0
}
216
#endif
217
218
void
219
mrb_mruby_numeric_ext_gem_init(mrb_state* mrb)
220
476
{
221
476
  struct RClass *ic = mrb->integer_class;
222
223
476
  mrb_define_alias(mrb, ic, "modulo", "%");
224
476
  mrb_define_method(mrb, ic, "remainder", int_remainder, MRB_ARGS_REQ(1));
225
226
476
  mrb_define_method_id(mrb, ic, MRB_SYM(pow), int_powm, MRB_ARGS_ARG(1,1));
227
476
  mrb_define_method_id(mrb, ic, MRB_SYM(digits), int_digits, MRB_ARGS_OPT(1));
228
229
476
#ifndef MRB_NO_FLOAT
230
476
  struct RClass *fc = mrb->float_class;
231
232
476
  mrb_define_alias(mrb, fc, "modulo", "%");
233
476
  mrb_define_method(mrb, fc, "remainder", flo_remainder, MRB_ARGS_REQ(1));
234
235
476
  mrb_define_const_id(mrb, fc, MRB_SYM(RADIX),        mrb_fixnum_value(MRB_FLT_RADIX));
236
476
  mrb_define_const_id(mrb, fc, MRB_SYM(MANT_DIG),     mrb_fixnum_value(MRB_FLT_MANT_DIG));
237
476
  mrb_define_const_id(mrb, fc, MRB_SYM(EPSILON),      mrb_float_value(mrb, MRB_FLT_EPSILON));
238
476
  mrb_define_const_id(mrb, fc, MRB_SYM(DIG),          mrb_fixnum_value(MRB_FLT_DIG));
239
476
  mrb_define_const_id(mrb, fc, MRB_SYM(MIN_EXP),      mrb_fixnum_value(MRB_FLT_MIN_EXP));
240
476
  mrb_define_const_id(mrb, fc, MRB_SYM(MIN),          mrb_float_value(mrb, MRB_FLT_MIN));
241
476
  mrb_define_const_id(mrb, fc, MRB_SYM(MIN_10_EXP),   mrb_fixnum_value(MRB_FLT_MIN_10_EXP));
242
476
  mrb_define_const_id(mrb, fc, MRB_SYM(MAX_EXP),      mrb_fixnum_value(MRB_FLT_MAX_EXP));
243
476
  mrb_define_const_id(mrb, fc, MRB_SYM(MAX),          mrb_float_value(mrb, MRB_FLT_MAX));
244
476
  mrb_define_const_id(mrb, fc, MRB_SYM(MAX_10_EXP),   mrb_fixnum_value(MRB_FLT_MAX_10_EXP));
245
476
#endif /* MRB_NO_FLOAT */
246
476
}
247
248
void
249
mrb_mruby_numeric_ext_gem_final(mrb_state* mrb)
250
476
{
251
476
}