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