Line | Count | Source |
1 | | /* |
2 | | rational.c: Coded by Tadayoshi Funaba 2008-2012 |
3 | | |
4 | | This implementation is based on Keiju Ishitsuka's Rational library |
5 | | which is written in ruby. |
6 | | */ |
7 | | |
8 | | #include "ruby/internal/config.h" |
9 | | |
10 | | #include <ctype.h> |
11 | | #include <float.h> |
12 | | #include <math.h> |
13 | | |
14 | | #ifdef HAVE_IEEEFP_H |
15 | | #include <ieeefp.h> |
16 | | #endif |
17 | | |
18 | | #if !defined(USE_GMP) |
19 | | #if defined(HAVE_LIBGMP) && defined(HAVE_GMP_H) |
20 | | # define USE_GMP 1 |
21 | | #else |
22 | | # define USE_GMP 0 |
23 | | #endif |
24 | | #endif |
25 | | |
26 | | #include "id.h" |
27 | | #include "internal.h" |
28 | | #include "internal/array.h" |
29 | | #include "internal/complex.h" |
30 | | #include "internal/error.h" |
31 | | #include "internal/gc.h" |
32 | | #include "internal/numeric.h" |
33 | | #include "internal/object.h" |
34 | | #include "internal/rational.h" |
35 | | #include "ruby_assert.h" |
36 | | |
37 | | #if USE_GMP |
38 | | RBIMPL_WARNING_PUSH() |
39 | | # ifdef _MSC_VER |
40 | | RBIMPL_WARNING_IGNORED(4146) /* for mpn_neg() */ |
41 | | # endif |
42 | | # include <gmp.h> |
43 | | RBIMPL_WARNING_POP() |
44 | | #endif |
45 | | |
46 | 0 | #define ZERO INT2FIX(0) |
47 | 0 | #define ONE INT2FIX(1) |
48 | 0 | #define TWO INT2FIX(2) |
49 | | |
50 | | #define GMP_GCD_DIGITS 1 |
51 | | |
52 | 0 | #define INT_ZERO_P(x) (FIXNUM_P(x) ? FIXNUM_ZERO_P(x) : rb_bigzero_p(x)) |
53 | | |
54 | | VALUE rb_cRational; |
55 | | |
56 | | static ID id_abs, id_integer_p, |
57 | | id_i_num, id_i_den; |
58 | | |
59 | 0 | #define id_idiv idDiv |
60 | 0 | #define id_to_i idTo_i |
61 | | |
62 | 1 | #define f_inspect rb_inspect |
63 | 0 | #define f_to_s rb_obj_as_string |
64 | | |
65 | | static VALUE nurat_to_f(VALUE self); |
66 | | static VALUE float_to_r(VALUE self); |
67 | | |
68 | | inline static VALUE |
69 | | f_add(VALUE x, VALUE y) |
70 | 0 | { |
71 | 0 | if (FIXNUM_ZERO_P(y)) |
72 | 0 | return x; |
73 | 0 | if (FIXNUM_ZERO_P(x)) |
74 | 0 | return y; |
75 | 0 | if (RB_INTEGER_TYPE_P(x)) |
76 | 0 | return rb_int_plus(x, y); |
77 | 0 | return rb_funcall(x, '+', 1, y); |
78 | 0 | } |
79 | | |
80 | | inline static VALUE |
81 | | f_div(VALUE x, VALUE y) |
82 | 0 | { |
83 | 0 | if (y == ONE) |
84 | 0 | return x; |
85 | 0 | if (RB_INTEGER_TYPE_P(x)) |
86 | 0 | return rb_int_div(x, y); |
87 | 0 | return rb_funcall(x, '/', 1, y); |
88 | 0 | } |
89 | | |
90 | | inline static int |
91 | | f_lt_p(VALUE x, VALUE y) |
92 | 0 | { |
93 | 0 | if (FIXNUM_P(x) && FIXNUM_P(y)) |
94 | 0 | return (SIGNED_VALUE)x < (SIGNED_VALUE)y; |
95 | 0 | if (RB_INTEGER_TYPE_P(x)) { |
96 | 0 | VALUE r = rb_int_cmp(x, y); |
97 | 0 | if (!NIL_P(r)) return rb_int_negative_p(r); |
98 | 0 | } |
99 | 0 | return RTEST(rb_funcall(x, '<', 1, y)); |
100 | 0 | } |
101 | | |
102 | | #ifndef NDEBUG |
103 | | /* f_mod is used only in f_gcd defined when NDEBUG is not defined */ |
104 | | inline static VALUE |
105 | | f_mod(VALUE x, VALUE y) |
106 | | { |
107 | | if (RB_INTEGER_TYPE_P(x)) |
108 | | return rb_int_modulo(x, y); |
109 | | return rb_funcall(x, '%', 1, y); |
110 | | } |
111 | | #endif |
112 | | |
113 | | inline static VALUE |
114 | | f_mul(VALUE x, VALUE y) |
115 | 0 | { |
116 | 0 | if (FIXNUM_ZERO_P(y) && RB_INTEGER_TYPE_P(x)) |
117 | 0 | return ZERO; |
118 | 0 | if (y == ONE) return x; |
119 | 0 | if (FIXNUM_ZERO_P(x) && RB_INTEGER_TYPE_P(y)) |
120 | 0 | return ZERO; |
121 | 0 | if (x == ONE) return y; |
122 | 0 | else if (RB_INTEGER_TYPE_P(x)) |
123 | 0 | return rb_int_mul(x, y); |
124 | 0 | return rb_funcall(x, '*', 1, y); |
125 | 0 | } |
126 | | |
127 | | inline static VALUE |
128 | | f_sub(VALUE x, VALUE y) |
129 | 0 | { |
130 | 0 | if (FIXNUM_P(y) && FIXNUM_ZERO_P(y)) |
131 | 0 | return x; |
132 | 0 | return rb_funcall(x, '-', 1, y); |
133 | 0 | } |
134 | | |
135 | | inline static VALUE |
136 | | f_abs(VALUE x) |
137 | 0 | { |
138 | 0 | if (RB_INTEGER_TYPE_P(x)) |
139 | 0 | return rb_int_abs(x); |
140 | 0 | return rb_funcall(x, id_abs, 0); |
141 | 0 | } |
142 | | |
143 | | |
144 | | inline static int |
145 | | f_integer_p(VALUE x) |
146 | 0 | { |
147 | 0 | return RB_INTEGER_TYPE_P(x); |
148 | 0 | } |
149 | | |
150 | | inline static VALUE |
151 | | f_to_i(VALUE x) |
152 | 0 | { |
153 | 0 | if (RB_TYPE_P(x, T_STRING)) |
154 | 0 | return rb_str_to_inum(x, 10, 0); |
155 | 0 | return rb_funcall(x, id_to_i, 0); |
156 | 0 | } |
157 | | |
158 | | inline static int |
159 | | f_eqeq_p(VALUE x, VALUE y) |
160 | 0 | { |
161 | 0 | if (FIXNUM_P(x) && FIXNUM_P(y)) |
162 | 0 | return x == y; |
163 | 0 | if (RB_INTEGER_TYPE_P(x)) |
164 | 0 | return RTEST(rb_int_equal(x, y)); |
165 | 0 | return (int)rb_equal(x, y); |
166 | 0 | } |
167 | | |
168 | | inline static VALUE |
169 | | f_idiv(VALUE x, VALUE y) |
170 | 0 | { |
171 | 0 | if (RB_INTEGER_TYPE_P(x)) |
172 | 0 | return rb_int_idiv(x, y); |
173 | 0 | return rb_funcall(x, id_idiv, 1, y); |
174 | 0 | } |
175 | | |
176 | 0 | #define f_expt10(x) rb_int_pow(INT2FIX(10), x) |
177 | | |
178 | | inline static int |
179 | | f_one_p(VALUE x) |
180 | 0 | { |
181 | 0 | if (RB_INTEGER_TYPE_P(x)) { |
182 | 0 | return x == LONG2FIX(1); |
183 | 0 | } |
184 | 0 | else if (RB_TYPE_P(x, T_RATIONAL)) { |
185 | 0 | VALUE num = RRATIONAL(x)->num; |
186 | 0 | VALUE den = RRATIONAL(x)->den; |
187 | |
|
188 | 0 | return num == LONG2FIX(1) && den == LONG2FIX(1); |
189 | 0 | } |
190 | 0 | return (int)rb_equal(x, ONE); |
191 | 0 | } |
192 | | |
193 | | inline static int |
194 | | f_minus_one_p(VALUE x) |
195 | 0 | { |
196 | 0 | if (RB_INTEGER_TYPE_P(x)) { |
197 | 0 | return x == LONG2FIX(-1); |
198 | 0 | } |
199 | 0 | else if (RB_BIGNUM_TYPE_P(x)) { |
200 | 0 | return Qfalse; |
201 | 0 | } |
202 | 0 | else if (RB_TYPE_P(x, T_RATIONAL)) { |
203 | 0 | VALUE num = RRATIONAL(x)->num; |
204 | 0 | VALUE den = RRATIONAL(x)->den; |
205 | |
|
206 | 0 | return num == LONG2FIX(-1) && den == LONG2FIX(1); |
207 | 0 | } |
208 | 0 | return (int)rb_equal(x, INT2FIX(-1)); |
209 | 0 | } |
210 | | |
211 | | inline static int |
212 | | f_kind_of_p(VALUE x, VALUE c) |
213 | 0 | { |
214 | 0 | return (int)rb_obj_is_kind_of(x, c); |
215 | 0 | } |
216 | | |
217 | | inline static int |
218 | | k_numeric_p(VALUE x) |
219 | 0 | { |
220 | 0 | return f_kind_of_p(x, rb_cNumeric); |
221 | 0 | } |
222 | | |
223 | | inline static int |
224 | | k_integer_p(VALUE x) |
225 | 0 | { |
226 | 0 | return RB_INTEGER_TYPE_P(x); |
227 | 0 | } |
228 | | |
229 | | inline static int |
230 | | k_float_p(VALUE x) |
231 | 0 | { |
232 | 0 | return RB_FLOAT_TYPE_P(x); |
233 | 0 | } |
234 | | |
235 | | inline static int |
236 | | k_rational_p(VALUE x) |
237 | 0 | { |
238 | 0 | return RB_TYPE_P(x, T_RATIONAL); |
239 | 0 | } |
240 | | |
241 | 0 | #define k_exact_p(x) (!k_float_p(x)) |
242 | | #define k_inexact_p(x) k_float_p(x) |
243 | | |
244 | 0 | #define k_exact_zero_p(x) (k_exact_p(x) && f_zero_p(x)) |
245 | 0 | #define k_exact_one_p(x) (k_exact_p(x) && f_one_p(x)) |
246 | | |
247 | | #if USE_GMP |
248 | | VALUE |
249 | | rb_gcd_gmp(VALUE x, VALUE y) |
250 | | { |
251 | | const size_t nails = (sizeof(BDIGIT)-SIZEOF_BDIGIT)*CHAR_BIT; |
252 | | mpz_t mx, my, mz; |
253 | | size_t count; |
254 | | VALUE z; |
255 | | long zn; |
256 | | |
257 | | mpz_init(mx); |
258 | | mpz_init(my); |
259 | | mpz_init(mz); |
260 | | mpz_import(mx, BIGNUM_LEN(x), -1, sizeof(BDIGIT), 0, nails, BIGNUM_DIGITS(x)); |
261 | | mpz_import(my, BIGNUM_LEN(y), -1, sizeof(BDIGIT), 0, nails, BIGNUM_DIGITS(y)); |
262 | | |
263 | | mpz_gcd(mz, mx, my); |
264 | | |
265 | | mpz_clear(mx); |
266 | | mpz_clear(my); |
267 | | |
268 | | zn = (mpz_sizeinbase(mz, 16) + SIZEOF_BDIGIT*2 - 1) / (SIZEOF_BDIGIT*2); |
269 | | z = rb_big_new(zn, 1); |
270 | | mpz_export(BIGNUM_DIGITS(z), &count, -1, sizeof(BDIGIT), 0, nails, mz); |
271 | | |
272 | | mpz_clear(mz); |
273 | | |
274 | | return rb_big_norm(z); |
275 | | } |
276 | | #endif |
277 | | |
278 | | #ifndef NDEBUG |
279 | | #define f_gcd f_gcd_orig |
280 | | #endif |
281 | | |
282 | | inline static long |
283 | | i_gcd(long x, long y) |
284 | 0 | { |
285 | 0 | unsigned long u, v, t; |
286 | 0 | int shift; |
287 | |
|
288 | 0 | if (x < 0) |
289 | 0 | x = -x; |
290 | 0 | if (y < 0) |
291 | 0 | y = -y; |
292 | |
|
293 | 0 | if (x == 0) |
294 | 0 | return y; |
295 | 0 | if (y == 0) |
296 | 0 | return x; |
297 | | |
298 | 0 | u = (unsigned long)x; |
299 | 0 | v = (unsigned long)y; |
300 | 0 | for (shift = 0; ((u | v) & 1) == 0; ++shift) { |
301 | 0 | u >>= 1; |
302 | 0 | v >>= 1; |
303 | 0 | } |
304 | |
|
305 | 0 | while ((u & 1) == 0) |
306 | 0 | u >>= 1; |
307 | |
|
308 | 0 | do { |
309 | 0 | while ((v & 1) == 0) |
310 | 0 | v >>= 1; |
311 | |
|
312 | 0 | if (u > v) { |
313 | 0 | t = v; |
314 | 0 | v = u; |
315 | 0 | u = t; |
316 | 0 | } |
317 | 0 | v = v - u; |
318 | 0 | } while (v != 0); |
319 | |
|
320 | 0 | return (long)(u << shift); |
321 | 0 | } |
322 | | |
323 | | inline static VALUE |
324 | | f_gcd_normal(VALUE x, VALUE y) |
325 | 0 | { |
326 | 0 | VALUE z; |
327 | |
|
328 | 0 | if (FIXNUM_P(x) && FIXNUM_P(y)) |
329 | 0 | return LONG2NUM(i_gcd(FIX2LONG(x), FIX2LONG(y))); |
330 | | |
331 | 0 | if (INT_NEGATIVE_P(x)) |
332 | 0 | x = rb_int_uminus(x); |
333 | 0 | if (INT_NEGATIVE_P(y)) |
334 | 0 | y = rb_int_uminus(y); |
335 | |
|
336 | 0 | if (INT_ZERO_P(x)) |
337 | 0 | return y; |
338 | 0 | if (INT_ZERO_P(y)) |
339 | 0 | return x; |
340 | | |
341 | 0 | for (;;) { |
342 | 0 | if (FIXNUM_P(x)) { |
343 | 0 | if (FIXNUM_ZERO_P(x)) |
344 | 0 | return y; |
345 | 0 | if (FIXNUM_P(y)) |
346 | 0 | return LONG2NUM(i_gcd(FIX2LONG(x), FIX2LONG(y))); |
347 | 0 | } |
348 | 0 | z = x; |
349 | 0 | x = rb_int_modulo(y, x); |
350 | 0 | y = z; |
351 | 0 | } |
352 | | /* NOTREACHED */ |
353 | 0 | } |
354 | | |
355 | | VALUE |
356 | | rb_gcd_normal(VALUE x, VALUE y) |
357 | 0 | { |
358 | 0 | return f_gcd_normal(x, y); |
359 | 0 | } |
360 | | |
361 | | inline static VALUE |
362 | | f_gcd(VALUE x, VALUE y) |
363 | 0 | { |
364 | | #if USE_GMP |
365 | | if (RB_BIGNUM_TYPE_P(x) && RB_BIGNUM_TYPE_P(y)) { |
366 | | size_t xn = BIGNUM_LEN(x); |
367 | | size_t yn = BIGNUM_LEN(y); |
368 | | if (GMP_GCD_DIGITS <= xn || GMP_GCD_DIGITS <= yn) |
369 | | return rb_gcd_gmp(x, y); |
370 | | } |
371 | | #endif |
372 | 0 | return f_gcd_normal(x, y); |
373 | 0 | } |
374 | | |
375 | | #ifndef NDEBUG |
376 | | #undef f_gcd |
377 | | |
378 | | inline static VALUE |
379 | | f_gcd(VALUE x, VALUE y) |
380 | | { |
381 | | VALUE r = f_gcd_orig(x, y); |
382 | | if (f_nonzero_p(r)) { |
383 | | RUBY_ASSERT(f_zero_p(f_mod(x, r))); |
384 | | RUBY_ASSERT(f_zero_p(f_mod(y, r))); |
385 | | } |
386 | | return r; |
387 | | } |
388 | | #endif |
389 | | |
390 | | inline static VALUE |
391 | | f_lcm(VALUE x, VALUE y) |
392 | 0 | { |
393 | 0 | if (INT_ZERO_P(x) || INT_ZERO_P(y)) |
394 | 0 | return ZERO; |
395 | 0 | return f_abs(f_mul(f_div(x, f_gcd(x, y)), y)); |
396 | 0 | } |
397 | | |
398 | | #define get_dat1(x) \ |
399 | 1 | struct RRational *dat = RRATIONAL(x) |
400 | | |
401 | | #define get_dat2(x,y) \ |
402 | 0 | struct RRational *adat = RRATIONAL(x), *bdat = RRATIONAL(y) |
403 | | |
404 | | inline static VALUE |
405 | | nurat_s_new_internal(VALUE klass, VALUE num, VALUE den) |
406 | 1 | { |
407 | 1 | NEWOBJ_OF(obj, struct RRational, klass, T_RATIONAL | (RGENGC_WB_PROTECTED_RATIONAL ? FL_WB_PROTECTED : 0), |
408 | 1 | sizeof(struct RRational), 0); |
409 | | |
410 | 1 | RATIONAL_SET_NUM((VALUE)obj, num); |
411 | 1 | RATIONAL_SET_DEN((VALUE)obj, den); |
412 | 1 | OBJ_FREEZE((VALUE)obj); |
413 | | |
414 | 1 | return (VALUE)obj; |
415 | 1 | } |
416 | | |
417 | | static VALUE |
418 | | nurat_s_alloc(VALUE klass) |
419 | 0 | { |
420 | 0 | return nurat_s_new_internal(klass, ZERO, ONE); |
421 | 0 | } |
422 | | |
423 | | inline static VALUE |
424 | | f_rational_new_bang1(VALUE klass, VALUE x) |
425 | 0 | { |
426 | 0 | return nurat_s_new_internal(klass, x, ONE); |
427 | 0 | } |
428 | | |
429 | | inline static void |
430 | | nurat_int_check(VALUE num) |
431 | 0 | { |
432 | 0 | if (!RB_INTEGER_TYPE_P(num)) { |
433 | 0 | if (!k_numeric_p(num) || !f_integer_p(num)) |
434 | 0 | rb_raise(rb_eTypeError, "not an integer"); |
435 | 0 | } |
436 | 0 | } |
437 | | |
438 | | inline static VALUE |
439 | | nurat_int_value(VALUE num) |
440 | 0 | { |
441 | 0 | nurat_int_check(num); |
442 | 0 | if (!k_integer_p(num)) |
443 | 0 | num = f_to_i(num); |
444 | 0 | return num; |
445 | 0 | } |
446 | | |
447 | | static void |
448 | | nurat_canonicalize(VALUE *num, VALUE *den) |
449 | 0 | { |
450 | 0 | RUBY_ASSERT(num); RUBY_ASSERT(RB_INTEGER_TYPE_P(*num)); |
451 | 0 | RUBY_ASSERT(den); RUBY_ASSERT(RB_INTEGER_TYPE_P(*den)); |
452 | 0 | if (INT_NEGATIVE_P(*den)) { |
453 | 0 | *num = rb_int_uminus(*num); |
454 | 0 | *den = rb_int_uminus(*den); |
455 | 0 | } |
456 | 0 | else if (INT_ZERO_P(*den)) { |
457 | 0 | rb_num_zerodiv(); |
458 | 0 | } |
459 | 0 | } |
460 | | |
461 | | static void |
462 | | nurat_reduce(VALUE *x, VALUE *y) |
463 | 0 | { |
464 | 0 | VALUE gcd; |
465 | 0 | if (*x == ONE || *y == ONE) return; |
466 | 0 | gcd = f_gcd(*x, *y); |
467 | 0 | *x = f_idiv(*x, gcd); |
468 | 0 | *y = f_idiv(*y, gcd); |
469 | 0 | } |
470 | | |
471 | | inline static VALUE |
472 | | nurat_s_canonicalize_internal(VALUE klass, VALUE num, VALUE den) |
473 | 0 | { |
474 | 0 | nurat_canonicalize(&num, &den); |
475 | 0 | nurat_reduce(&num, &den); |
476 | |
|
477 | 0 | return nurat_s_new_internal(klass, num, den); |
478 | 0 | } |
479 | | |
480 | | inline static VALUE |
481 | | nurat_s_canonicalize_internal_no_reduce(VALUE klass, VALUE num, VALUE den) |
482 | 0 | { |
483 | 0 | nurat_canonicalize(&num, &den); |
484 | |
|
485 | 0 | return nurat_s_new_internal(klass, num, den); |
486 | 0 | } |
487 | | |
488 | | inline static VALUE |
489 | | f_rational_new2(VALUE klass, VALUE x, VALUE y) |
490 | 0 | { |
491 | 0 | RUBY_ASSERT(!k_rational_p(x)); |
492 | 0 | RUBY_ASSERT(!k_rational_p(y)); |
493 | 0 | return nurat_s_canonicalize_internal(klass, x, y); |
494 | 0 | } |
495 | | |
496 | | inline static VALUE |
497 | | f_rational_new_no_reduce2(VALUE klass, VALUE x, VALUE y) |
498 | 0 | { |
499 | 0 | RUBY_ASSERT(!k_rational_p(x)); |
500 | 0 | RUBY_ASSERT(!k_rational_p(y)); |
501 | 0 | return nurat_s_canonicalize_internal_no_reduce(klass, x, y); |
502 | 0 | } |
503 | | |
504 | | static VALUE nurat_convert(VALUE klass, VALUE numv, VALUE denv, int raise); |
505 | | static VALUE nurat_s_convert(int argc, VALUE *argv, VALUE klass); |
506 | | |
507 | | /* |
508 | | * call-seq: |
509 | | * Rational(x, y, exception: true) -> rational or nil |
510 | | * Rational(arg, exception: true) -> rational or nil |
511 | | * |
512 | | * Returns +x/y+ or +arg+ as a Rational. |
513 | | * |
514 | | * Rational(2, 3) #=> (2/3) |
515 | | * Rational(5) #=> (5/1) |
516 | | * Rational(0.5) #=> (1/2) |
517 | | * Rational(0.3) #=> (5404319552844595/18014398509481984) |
518 | | * |
519 | | * Rational("2/3") #=> (2/3) |
520 | | * Rational("0.3") #=> (3/10) |
521 | | * |
522 | | * Rational("10 cents") #=> ArgumentError |
523 | | * Rational(nil) #=> TypeError |
524 | | * Rational(1, nil) #=> TypeError |
525 | | * |
526 | | * Rational("10 cents", exception: false) #=> nil |
527 | | * |
528 | | * Syntax of the string form: |
529 | | * |
530 | | * string form = extra spaces , rational , extra spaces ; |
531 | | * rational = [ sign ] , unsigned rational ; |
532 | | * unsigned rational = numerator | numerator , "/" , denominator ; |
533 | | * numerator = integer part | fractional part | integer part , fractional part ; |
534 | | * denominator = digits ; |
535 | | * integer part = digits ; |
536 | | * fractional part = "." , digits , [ ( "e" | "E" ) , [ sign ] , digits ] ; |
537 | | * sign = "-" | "+" ; |
538 | | * digits = digit , { digit | "_" , digit } ; |
539 | | * digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; |
540 | | * extra spaces = ? \s* ? ; |
541 | | * |
542 | | * See also String#to_r. |
543 | | */ |
544 | | static VALUE |
545 | | nurat_f_rational(int argc, VALUE *argv, VALUE klass) |
546 | 0 | { |
547 | 0 | VALUE a1, a2, opts = Qnil; |
548 | 0 | int raise = TRUE; |
549 | |
|
550 | 0 | if (rb_scan_args(argc, argv, "11:", &a1, &a2, &opts) == 1) { |
551 | 0 | a2 = Qundef; |
552 | 0 | } |
553 | 0 | if (!NIL_P(opts)) { |
554 | 0 | raise = rb_opts_exception_p(opts, raise); |
555 | 0 | } |
556 | 0 | return nurat_convert(rb_cRational, a1, a2, raise); |
557 | 0 | } |
558 | | |
559 | | /* |
560 | | * call-seq: |
561 | | * rat.numerator -> integer |
562 | | * |
563 | | * Returns the numerator. |
564 | | * |
565 | | * Rational(7).numerator #=> 7 |
566 | | * Rational(7, 1).numerator #=> 7 |
567 | | * Rational(9, -4).numerator #=> -9 |
568 | | * Rational(-2, -10).numerator #=> 1 |
569 | | */ |
570 | | static VALUE |
571 | | nurat_numerator(VALUE self) |
572 | 0 | { |
573 | 0 | get_dat1(self); |
574 | 0 | return dat->num; |
575 | 0 | } |
576 | | |
577 | | /* |
578 | | * call-seq: |
579 | | * rat.denominator -> integer |
580 | | * |
581 | | * Returns the denominator (always positive). |
582 | | * |
583 | | * Rational(7).denominator #=> 1 |
584 | | * Rational(7, 1).denominator #=> 1 |
585 | | * Rational(9, -4).denominator #=> 4 |
586 | | * Rational(-2, -10).denominator #=> 5 |
587 | | */ |
588 | | static VALUE |
589 | | nurat_denominator(VALUE self) |
590 | 0 | { |
591 | 0 | get_dat1(self); |
592 | 0 | return dat->den; |
593 | 0 | } |
594 | | |
595 | | /* |
596 | | * call-seq: |
597 | | * -self -> rational |
598 | | * |
599 | | * Returns +self+, negated: |
600 | | * |
601 | | * -(1/3r) # => (-1/3) |
602 | | * -(-1/3r) # => (1/3) |
603 | | * |
604 | | */ |
605 | | VALUE |
606 | | rb_rational_uminus(VALUE self) |
607 | 0 | { |
608 | 0 | const int unused = (RUBY_ASSERT(RB_TYPE_P(self, T_RATIONAL)), 0); |
609 | 0 | get_dat1(self); |
610 | 0 | (void)unused; |
611 | 0 | return f_rational_new2(CLASS_OF(self), rb_int_uminus(dat->num), dat->den); |
612 | 0 | } |
613 | | |
614 | | #ifndef NDEBUG |
615 | | #define f_imul f_imul_orig |
616 | | #endif |
617 | | |
618 | | inline static VALUE |
619 | | f_imul(long a, long b) |
620 | 0 | { |
621 | 0 | VALUE r; |
622 | |
|
623 | 0 | if (a == 0 || b == 0) |
624 | 0 | return ZERO; |
625 | 0 | else if (a == 1) |
626 | 0 | return LONG2NUM(b); |
627 | 0 | else if (b == 1) |
628 | 0 | return LONG2NUM(a); |
629 | | |
630 | 0 | if (MUL_OVERFLOW_LONG_P(a, b)) |
631 | 0 | r = rb_big_mul(rb_int2big(a), rb_int2big(b)); |
632 | 0 | else |
633 | 0 | r = LONG2NUM(a * b); |
634 | 0 | return r; |
635 | 0 | } |
636 | | |
637 | | #ifndef NDEBUG |
638 | | #undef f_imul |
639 | | |
640 | | inline static VALUE |
641 | | f_imul(long x, long y) |
642 | | { |
643 | | VALUE r = f_imul_orig(x, y); |
644 | | RUBY_ASSERT(f_eqeq_p(r, f_mul(LONG2NUM(x), LONG2NUM(y)))); |
645 | | return r; |
646 | | } |
647 | | #endif |
648 | | |
649 | | inline static VALUE |
650 | | f_addsub(VALUE self, VALUE anum, VALUE aden, VALUE bnum, VALUE bden, int k) |
651 | 0 | { |
652 | 0 | VALUE num, den; |
653 | |
|
654 | 0 | if (FIXNUM_P(anum) && FIXNUM_P(aden) && |
655 | 0 | FIXNUM_P(bnum) && FIXNUM_P(bden)) { |
656 | 0 | long an = FIX2LONG(anum); |
657 | 0 | long ad = FIX2LONG(aden); |
658 | 0 | long bn = FIX2LONG(bnum); |
659 | 0 | long bd = FIX2LONG(bden); |
660 | 0 | long ig = i_gcd(ad, bd); |
661 | |
|
662 | 0 | VALUE g = LONG2NUM(ig); |
663 | 0 | VALUE a = f_imul(an, bd / ig); |
664 | 0 | VALUE b = f_imul(bn, ad / ig); |
665 | 0 | VALUE c; |
666 | |
|
667 | 0 | if (k == '+') |
668 | 0 | c = rb_int_plus(a, b); |
669 | 0 | else |
670 | 0 | c = rb_int_minus(a, b); |
671 | |
|
672 | 0 | b = rb_int_idiv(aden, g); |
673 | 0 | g = f_gcd(c, g); |
674 | 0 | num = rb_int_idiv(c, g); |
675 | 0 | a = rb_int_idiv(bden, g); |
676 | 0 | den = rb_int_mul(a, b); |
677 | 0 | } |
678 | 0 | else if (RB_INTEGER_TYPE_P(anum) && RB_INTEGER_TYPE_P(aden) && |
679 | 0 | RB_INTEGER_TYPE_P(bnum) && RB_INTEGER_TYPE_P(bden)) { |
680 | 0 | VALUE g = f_gcd(aden, bden); |
681 | 0 | VALUE a = rb_int_mul(anum, rb_int_idiv(bden, g)); |
682 | 0 | VALUE b = rb_int_mul(bnum, rb_int_idiv(aden, g)); |
683 | 0 | VALUE c; |
684 | |
|
685 | 0 | if (k == '+') |
686 | 0 | c = rb_int_plus(a, b); |
687 | 0 | else |
688 | 0 | c = rb_int_minus(a, b); |
689 | |
|
690 | 0 | b = rb_int_idiv(aden, g); |
691 | 0 | g = f_gcd(c, g); |
692 | 0 | num = rb_int_idiv(c, g); |
693 | 0 | a = rb_int_idiv(bden, g); |
694 | 0 | den = rb_int_mul(a, b); |
695 | 0 | } |
696 | 0 | else { |
697 | 0 | double a = NUM2DBL(anum) / NUM2DBL(aden); |
698 | 0 | double b = NUM2DBL(bnum) / NUM2DBL(bden); |
699 | 0 | double c = k == '+' ? a + b : a - b; |
700 | 0 | return DBL2NUM(c); |
701 | 0 | } |
702 | 0 | return f_rational_new_no_reduce2(CLASS_OF(self), num, den); |
703 | 0 | } |
704 | | |
705 | | static double nurat_to_double(VALUE self); |
706 | | /* |
707 | | * call-seq: |
708 | | * self + other -> numeric |
709 | | * |
710 | | * Returns the sum of +self+ and +other+: |
711 | | * |
712 | | * Rational(2, 3) + 0 # => (2/3) |
713 | | * Rational(2, 3) + 1 # => (5/3) |
714 | | * Rational(2, 3) + -1 # => (-1/3) |
715 | | * |
716 | | * Rational(2, 3) + Complex(1, 0) # => ((5/3)+0i) |
717 | | * |
718 | | * Rational(2, 3) + Rational(1, 1) # => (5/3) |
719 | | * Rational(2, 3) + Rational(3, 2) # => (13/6) |
720 | | * Rational(2, 3) + Rational(3.0, 2.0) # => (13/6) |
721 | | * Rational(2, 3) + Rational(3.1, 2.1) # => (30399297484750849/14186338826217063) |
722 | | * |
723 | | * For a computation involving Floats, the result may be inexact (see Float#+): |
724 | | * |
725 | | * Rational(2, 3) + 1.0 # => 1.6666666666666665 |
726 | | * Rational(2, 3) + Complex(1.0, 0.0) # => (1.6666666666666665+0.0i) |
727 | | * |
728 | | */ |
729 | | VALUE |
730 | | rb_rational_plus(VALUE self, VALUE other) |
731 | 0 | { |
732 | 0 | if (RB_INTEGER_TYPE_P(other)) { |
733 | 0 | { |
734 | 0 | get_dat1(self); |
735 | |
|
736 | 0 | return f_rational_new_no_reduce2(CLASS_OF(self), |
737 | 0 | rb_int_plus(dat->num, rb_int_mul(other, dat->den)), |
738 | 0 | dat->den); |
739 | 0 | } |
740 | 0 | } |
741 | 0 | else if (RB_FLOAT_TYPE_P(other)) { |
742 | 0 | return DBL2NUM(nurat_to_double(self) + RFLOAT_VALUE(other)); |
743 | 0 | } |
744 | 0 | else if (RB_TYPE_P(other, T_RATIONAL)) { |
745 | 0 | { |
746 | 0 | get_dat2(self, other); |
747 | |
|
748 | 0 | return f_addsub(self, |
749 | 0 | adat->num, adat->den, |
750 | 0 | bdat->num, bdat->den, '+'); |
751 | 0 | } |
752 | 0 | } |
753 | 0 | else { |
754 | 0 | return rb_num_coerce_bin(self, other, '+'); |
755 | 0 | } |
756 | 0 | } |
757 | | |
758 | | /* |
759 | | * call-seq: |
760 | | * self - other -> numeric |
761 | | * |
762 | | * Returns the difference of +self+ and +other+: |
763 | | * |
764 | | * Rational(2, 3) - Rational(2, 3) #=> (0/1) |
765 | | * Rational(900) - Rational(1) #=> (899/1) |
766 | | * Rational(-2, 9) - Rational(-9, 2) #=> (77/18) |
767 | | * Rational(9, 8) - 4 #=> (-23/8) |
768 | | * Rational(20, 9) - 9.8 #=> -7.577777777777778 |
769 | | */ |
770 | | VALUE |
771 | | rb_rational_minus(VALUE self, VALUE other) |
772 | 0 | { |
773 | 0 | if (RB_INTEGER_TYPE_P(other)) { |
774 | 0 | { |
775 | 0 | get_dat1(self); |
776 | |
|
777 | 0 | return f_rational_new_no_reduce2(CLASS_OF(self), |
778 | 0 | rb_int_minus(dat->num, rb_int_mul(other, dat->den)), |
779 | 0 | dat->den); |
780 | 0 | } |
781 | 0 | } |
782 | 0 | else if (RB_FLOAT_TYPE_P(other)) { |
783 | 0 | return DBL2NUM(nurat_to_double(self) - RFLOAT_VALUE(other)); |
784 | 0 | } |
785 | 0 | else if (RB_TYPE_P(other, T_RATIONAL)) { |
786 | 0 | { |
787 | 0 | get_dat2(self, other); |
788 | |
|
789 | 0 | return f_addsub(self, |
790 | 0 | adat->num, adat->den, |
791 | 0 | bdat->num, bdat->den, '-'); |
792 | 0 | } |
793 | 0 | } |
794 | 0 | else { |
795 | 0 | return rb_num_coerce_bin(self, other, '-'); |
796 | 0 | } |
797 | 0 | } |
798 | | |
799 | | inline static VALUE |
800 | | f_muldiv(VALUE self, VALUE anum, VALUE aden, VALUE bnum, VALUE bden, int k) |
801 | 0 | { |
802 | 0 | VALUE num, den; |
803 | |
|
804 | 0 | RUBY_ASSERT(RB_TYPE_P(self, T_RATIONAL)); |
805 | | |
806 | | /* Integer#** can return Rational with Float right now */ |
807 | 0 | if (RB_FLOAT_TYPE_P(anum) || RB_FLOAT_TYPE_P(aden) || |
808 | 0 | RB_FLOAT_TYPE_P(bnum) || RB_FLOAT_TYPE_P(bden)) { |
809 | 0 | double an = NUM2DBL(anum), ad = NUM2DBL(aden); |
810 | 0 | double bn = NUM2DBL(bnum), bd = NUM2DBL(bden); |
811 | 0 | double x = (an * bn) / (ad * bd); |
812 | 0 | return DBL2NUM(x); |
813 | 0 | } |
814 | | |
815 | 0 | RUBY_ASSERT(RB_INTEGER_TYPE_P(anum)); |
816 | 0 | RUBY_ASSERT(RB_INTEGER_TYPE_P(aden)); |
817 | 0 | RUBY_ASSERT(RB_INTEGER_TYPE_P(bnum)); |
818 | 0 | RUBY_ASSERT(RB_INTEGER_TYPE_P(bden)); |
819 | |
|
820 | 0 | if (k == '/') { |
821 | 0 | VALUE t; |
822 | |
|
823 | 0 | if (INT_NEGATIVE_P(bnum)) { |
824 | 0 | anum = rb_int_uminus(anum); |
825 | 0 | bnum = rb_int_uminus(bnum); |
826 | 0 | } |
827 | 0 | t = bnum; |
828 | 0 | bnum = bden; |
829 | 0 | bden = t; |
830 | 0 | } |
831 | |
|
832 | 0 | if (FIXNUM_P(anum) && FIXNUM_P(aden) && |
833 | 0 | FIXNUM_P(bnum) && FIXNUM_P(bden)) { |
834 | 0 | long an = FIX2LONG(anum); |
835 | 0 | long ad = FIX2LONG(aden); |
836 | 0 | long bn = FIX2LONG(bnum); |
837 | 0 | long bd = FIX2LONG(bden); |
838 | 0 | long g1 = i_gcd(an, bd); |
839 | 0 | long g2 = i_gcd(ad, bn); |
840 | |
|
841 | 0 | num = f_imul(an / g1, bn / g2); |
842 | 0 | den = f_imul(ad / g2, bd / g1); |
843 | 0 | } |
844 | 0 | else { |
845 | 0 | VALUE g1 = f_gcd(anum, bden); |
846 | 0 | VALUE g2 = f_gcd(aden, bnum); |
847 | |
|
848 | 0 | num = rb_int_mul(rb_int_idiv(anum, g1), rb_int_idiv(bnum, g2)); |
849 | 0 | den = rb_int_mul(rb_int_idiv(aden, g2), rb_int_idiv(bden, g1)); |
850 | 0 | } |
851 | 0 | return f_rational_new_no_reduce2(CLASS_OF(self), num, den); |
852 | 0 | } |
853 | | |
854 | | /* |
855 | | * call-seq: |
856 | | * self * other -> numeric |
857 | | * |
858 | | * Returns the numeric product of +self+ and +other+: |
859 | | * |
860 | | * Rational(9, 8) * 4 #=> (9/2) |
861 | | * Rational(20, 9) * 9.8 #=> 21.77777777777778 |
862 | | * Rational(9, 8) * Complex(1, 2) # => ((9/8)+(9/4)*i) |
863 | | * Rational(2, 3) * Rational(2, 3) #=> (4/9) |
864 | | * Rational(900) * Rational(1) #=> (900/1) |
865 | | * Rational(-2, 9) * Rational(-9, 2) #=> (1/1) |
866 | | * |
867 | | */ |
868 | | VALUE |
869 | | rb_rational_mul(VALUE self, VALUE other) |
870 | 0 | { |
871 | 0 | if (RB_INTEGER_TYPE_P(other)) { |
872 | 0 | { |
873 | 0 | get_dat1(self); |
874 | |
|
875 | 0 | return f_muldiv(self, |
876 | 0 | dat->num, dat->den, |
877 | 0 | other, ONE, '*'); |
878 | 0 | } |
879 | 0 | } |
880 | 0 | else if (RB_FLOAT_TYPE_P(other)) { |
881 | 0 | return DBL2NUM(nurat_to_double(self) * RFLOAT_VALUE(other)); |
882 | 0 | } |
883 | 0 | else if (RB_TYPE_P(other, T_RATIONAL)) { |
884 | 0 | { |
885 | 0 | get_dat2(self, other); |
886 | |
|
887 | 0 | return f_muldiv(self, |
888 | 0 | adat->num, adat->den, |
889 | 0 | bdat->num, bdat->den, '*'); |
890 | 0 | } |
891 | 0 | } |
892 | 0 | else { |
893 | 0 | return rb_num_coerce_bin(self, other, '*'); |
894 | 0 | } |
895 | 0 | } |
896 | | |
897 | | /* |
898 | | * call-seq: |
899 | | * self / other -> numeric |
900 | | * |
901 | | * Returns the quotient of +self+ and +other+: |
902 | | * |
903 | | * Rational(2, 3) / Rational(2, 3) #=> (1/1) |
904 | | * Rational(900) / Rational(1) #=> (900/1) |
905 | | * Rational(-2, 9) / Rational(-9, 2) #=> (4/81) |
906 | | * Rational(9, 8) / 4 #=> (9/32) |
907 | | * Rational(20, 9) / 9.8 #=> 0.22675736961451246 |
908 | | */ |
909 | | VALUE |
910 | | rb_rational_div(VALUE self, VALUE other) |
911 | 0 | { |
912 | 0 | if (RB_INTEGER_TYPE_P(other)) { |
913 | 0 | if (f_zero_p(other)) |
914 | 0 | rb_num_zerodiv(); |
915 | 0 | { |
916 | 0 | get_dat1(self); |
917 | |
|
918 | 0 | return f_muldiv(self, |
919 | 0 | dat->num, dat->den, |
920 | 0 | other, ONE, '/'); |
921 | 0 | } |
922 | 0 | } |
923 | 0 | else if (RB_FLOAT_TYPE_P(other)) { |
924 | 0 | VALUE v = nurat_to_f(self); |
925 | 0 | return rb_flo_div_flo(v, other); |
926 | 0 | } |
927 | 0 | else if (RB_TYPE_P(other, T_RATIONAL)) { |
928 | 0 | if (f_zero_p(other)) |
929 | 0 | rb_num_zerodiv(); |
930 | 0 | { |
931 | 0 | get_dat2(self, other); |
932 | |
|
933 | 0 | if (f_one_p(self)) |
934 | 0 | return f_rational_new_no_reduce2(CLASS_OF(self), |
935 | 0 | bdat->den, bdat->num); |
936 | | |
937 | 0 | return f_muldiv(self, |
938 | 0 | adat->num, adat->den, |
939 | 0 | bdat->num, bdat->den, '/'); |
940 | 0 | } |
941 | 0 | } |
942 | 0 | else { |
943 | 0 | return rb_num_coerce_bin(self, other, '/'); |
944 | 0 | } |
945 | 0 | } |
946 | | |
947 | | /* |
948 | | * call-seq: |
949 | | * rat.fdiv(numeric) -> float |
950 | | * |
951 | | * Performs division and returns the value as a Float. |
952 | | * |
953 | | * Rational(2, 3).fdiv(1) #=> 0.6666666666666666 |
954 | | * Rational(2, 3).fdiv(0.5) #=> 1.3333333333333333 |
955 | | * Rational(2).fdiv(3) #=> 0.6666666666666666 |
956 | | */ |
957 | | VALUE |
958 | | rb_rational_fdiv(VALUE self, VALUE other) |
959 | 0 | { |
960 | 0 | VALUE div; |
961 | 0 | if (f_zero_p(other)) |
962 | 0 | return rb_rational_div(self, rb_float_new(0.0)); |
963 | 0 | if (FIXNUM_P(other) && other == LONG2FIX(1)) |
964 | 0 | return nurat_to_f(self); |
965 | 0 | div = rb_rational_div(self, other); |
966 | 0 | if (RB_TYPE_P(div, T_RATIONAL)) |
967 | 0 | return nurat_to_f(div); |
968 | 0 | if (RB_FLOAT_TYPE_P(div)) |
969 | 0 | return div; |
970 | 0 | return rb_funcall(div, idTo_f, 0); |
971 | 0 | } |
972 | | |
973 | | /* |
974 | | * call-seq: |
975 | | * self ** exponent -> numeric |
976 | | * |
977 | | * Returns +self+ raised to the power +exponent+: |
978 | | * |
979 | | * Rational(2) ** Rational(3) #=> (8/1) |
980 | | * Rational(10) ** -2 #=> (1/100) |
981 | | * Rational(10) ** -2.0 #=> 0.01 |
982 | | * Rational(-4) ** Rational(1, 2) #=> (0.0+2.0i) |
983 | | * Rational(1, 2) ** 0 #=> (1/1) |
984 | | * Rational(1, 2) ** 0.0 #=> 1.0 |
985 | | */ |
986 | | VALUE |
987 | | rb_rational_pow(VALUE self, VALUE other) |
988 | 0 | { |
989 | 0 | if (k_numeric_p(other) && k_exact_zero_p(other)) |
990 | 0 | return f_rational_new_bang1(CLASS_OF(self), ONE); |
991 | | |
992 | 0 | if (k_rational_p(other)) { |
993 | 0 | get_dat1(other); |
994 | |
|
995 | 0 | if (f_one_p(dat->den)) |
996 | 0 | other = dat->num; /* c14n */ |
997 | 0 | } |
998 | | |
999 | | /* Deal with special cases of 0**n and 1**n */ |
1000 | 0 | if (k_numeric_p(other) && k_exact_p(other)) { |
1001 | 0 | get_dat1(self); |
1002 | 0 | if (f_one_p(dat->den)) { |
1003 | 0 | if (f_one_p(dat->num)) { |
1004 | 0 | return f_rational_new_bang1(CLASS_OF(self), ONE); |
1005 | 0 | } |
1006 | 0 | else if (f_minus_one_p(dat->num) && RB_INTEGER_TYPE_P(other)) { |
1007 | 0 | return f_rational_new_bang1(CLASS_OF(self), INT2FIX(rb_int_odd_p(other) ? -1 : 1)); |
1008 | 0 | } |
1009 | 0 | else if (INT_ZERO_P(dat->num)) { |
1010 | 0 | if (rb_num_negative_p(other)) { |
1011 | 0 | rb_num_zerodiv(); |
1012 | 0 | } |
1013 | 0 | else { |
1014 | 0 | return f_rational_new_bang1(CLASS_OF(self), ZERO); |
1015 | 0 | } |
1016 | 0 | } |
1017 | 0 | } |
1018 | 0 | } |
1019 | | |
1020 | | /* General case */ |
1021 | 0 | if (FIXNUM_P(other)) { |
1022 | 0 | { |
1023 | 0 | VALUE num, den; |
1024 | |
|
1025 | 0 | get_dat1(self); |
1026 | |
|
1027 | 0 | if (INT_POSITIVE_P(other)) { |
1028 | 0 | num = rb_int_pow(dat->num, other); |
1029 | 0 | den = rb_int_pow(dat->den, other); |
1030 | 0 | } |
1031 | 0 | else if (INT_NEGATIVE_P(other)) { |
1032 | 0 | num = rb_int_pow(dat->den, rb_int_uminus(other)); |
1033 | 0 | den = rb_int_pow(dat->num, rb_int_uminus(other)); |
1034 | 0 | } |
1035 | 0 | else { |
1036 | 0 | num = ONE; |
1037 | 0 | den = ONE; |
1038 | 0 | } |
1039 | 0 | if (RB_FLOAT_TYPE_P(num)) { /* infinity due to overflow */ |
1040 | 0 | if (RB_FLOAT_TYPE_P(den)) |
1041 | 0 | return DBL2NUM(nan("")); |
1042 | 0 | return num; |
1043 | 0 | } |
1044 | 0 | if (RB_FLOAT_TYPE_P(den)) { /* infinity due to overflow */ |
1045 | 0 | num = ZERO; |
1046 | 0 | den = ONE; |
1047 | 0 | } |
1048 | 0 | return f_rational_new2(CLASS_OF(self), num, den); |
1049 | 0 | } |
1050 | 0 | } |
1051 | 0 | else if (RB_BIGNUM_TYPE_P(other)) { |
1052 | 0 | rb_raise(rb_eArgError, "exponent is too large"); |
1053 | 0 | } |
1054 | 0 | else if (RB_FLOAT_TYPE_P(other) || RB_TYPE_P(other, T_RATIONAL)) { |
1055 | 0 | return rb_float_pow(nurat_to_f(self), other); |
1056 | 0 | } |
1057 | 0 | else { |
1058 | 0 | return rb_num_coerce_bin(self, other, idPow); |
1059 | 0 | } |
1060 | 0 | } |
1061 | | #define nurat_expt rb_rational_pow |
1062 | | |
1063 | | /* |
1064 | | * call-seq: |
1065 | | * self <=> other -> -1, 0, 1, or nil |
1066 | | * |
1067 | | * Compares +self+ and +other+. |
1068 | | * |
1069 | | * Returns: |
1070 | | * |
1071 | | * - +-1+, if +self+ is less than +other+. |
1072 | | * - +0+, if the two values are the same. |
1073 | | * - +1+, if +self+ is greater than +other+. |
1074 | | * - +nil+, if the two values are incomparable. |
1075 | | * |
1076 | | * Examples: |
1077 | | * |
1078 | | * Rational(2, 3) <=> Rational(4, 3) # => -1 |
1079 | | * Rational(2, 1) <=> Rational(2, 1) # => 0 |
1080 | | * Rational(2, 1) <=> 2 # => 0 |
1081 | | * Rational(2, 1) <=> 2.0 # => 0 |
1082 | | * Rational(2, 1) <=> Complex(2, 0) # => 0 |
1083 | | * Rational(4, 3) <=> Rational(2, 3) # => 1 |
1084 | | * Rational(4, 3) <=> :foo # => nil |
1085 | | * |
1086 | | * \Class \Rational includes module Comparable, |
1087 | | * each of whose methods uses Rational#<=> for comparison. |
1088 | | * |
1089 | | */ |
1090 | | VALUE |
1091 | | rb_rational_cmp(VALUE self, VALUE other) |
1092 | 0 | { |
1093 | 0 | switch (TYPE(other)) { |
1094 | 0 | case T_FIXNUM: |
1095 | 0 | case T_BIGNUM: |
1096 | 0 | { |
1097 | 0 | get_dat1(self); |
1098 | |
|
1099 | 0 | if (dat->den == LONG2FIX(1)) |
1100 | 0 | return rb_int_cmp(dat->num, other); /* c14n */ |
1101 | 0 | other = f_rational_new_bang1(CLASS_OF(self), other); |
1102 | | /* FALLTHROUGH */ |
1103 | 0 | } |
1104 | | |
1105 | 0 | case T_RATIONAL: |
1106 | 0 | { |
1107 | 0 | VALUE num1, num2; |
1108 | |
|
1109 | 0 | get_dat2(self, other); |
1110 | |
|
1111 | 0 | if (FIXNUM_P(adat->num) && FIXNUM_P(adat->den) && |
1112 | 0 | FIXNUM_P(bdat->num) && FIXNUM_P(bdat->den)) { |
1113 | 0 | num1 = f_imul(FIX2LONG(adat->num), FIX2LONG(bdat->den)); |
1114 | 0 | num2 = f_imul(FIX2LONG(bdat->num), FIX2LONG(adat->den)); |
1115 | 0 | } |
1116 | 0 | else { |
1117 | 0 | num1 = rb_int_mul(adat->num, bdat->den); |
1118 | 0 | num2 = rb_int_mul(bdat->num, adat->den); |
1119 | 0 | } |
1120 | 0 | return rb_int_cmp(rb_int_minus(num1, num2), ZERO); |
1121 | 0 | } |
1122 | | |
1123 | 0 | case T_FLOAT: |
1124 | 0 | return rb_dbl_cmp(nurat_to_double(self), RFLOAT_VALUE(other)); |
1125 | | |
1126 | 0 | default: |
1127 | 0 | return rb_num_coerce_cmp(self, other, idCmp); |
1128 | 0 | } |
1129 | 0 | } |
1130 | | |
1131 | | /* |
1132 | | * call-seq: |
1133 | | * self == other -> true or false |
1134 | | * |
1135 | | * Returns whether +self+ and +other+ are numerically equal: |
1136 | | * |
1137 | | * Rational(2, 3) == Rational(2, 3) #=> true |
1138 | | * Rational(5) == 5 #=> true |
1139 | | * Rational(0) == 0.0 #=> true |
1140 | | * Rational('1/3') == 0.33 #=> false |
1141 | | * Rational('1/2') == '1/2' #=> false |
1142 | | */ |
1143 | | static VALUE |
1144 | | nurat_eqeq_p(VALUE self, VALUE other) |
1145 | 0 | { |
1146 | 0 | if (RB_INTEGER_TYPE_P(other)) { |
1147 | 0 | get_dat1(self); |
1148 | |
|
1149 | 0 | if (RB_INTEGER_TYPE_P(dat->num) && RB_INTEGER_TYPE_P(dat->den)) { |
1150 | 0 | if (INT_ZERO_P(dat->num) && INT_ZERO_P(other)) |
1151 | 0 | return Qtrue; |
1152 | | |
1153 | 0 | if (!FIXNUM_P(dat->den)) |
1154 | 0 | return Qfalse; |
1155 | 0 | if (FIX2LONG(dat->den) != 1) |
1156 | 0 | return Qfalse; |
1157 | 0 | return rb_int_equal(dat->num, other); |
1158 | 0 | } |
1159 | 0 | else { |
1160 | 0 | const double d = nurat_to_double(self); |
1161 | 0 | return RBOOL(FIXNUM_ZERO_P(rb_dbl_cmp(d, NUM2DBL(other)))); |
1162 | 0 | } |
1163 | 0 | } |
1164 | 0 | else if (RB_FLOAT_TYPE_P(other)) { |
1165 | 0 | const double d = nurat_to_double(self); |
1166 | 0 | return RBOOL(FIXNUM_ZERO_P(rb_dbl_cmp(d, RFLOAT_VALUE(other)))); |
1167 | 0 | } |
1168 | 0 | else if (RB_TYPE_P(other, T_RATIONAL)) { |
1169 | 0 | { |
1170 | 0 | get_dat2(self, other); |
1171 | |
|
1172 | 0 | if (INT_ZERO_P(adat->num) && INT_ZERO_P(bdat->num)) |
1173 | 0 | return Qtrue; |
1174 | | |
1175 | 0 | return RBOOL(rb_int_equal(adat->num, bdat->num) && |
1176 | 0 | rb_int_equal(adat->den, bdat->den)); |
1177 | 0 | } |
1178 | 0 | } |
1179 | 0 | else { |
1180 | 0 | return rb_equal(other, self); |
1181 | 0 | } |
1182 | 0 | } |
1183 | | |
1184 | | /* :nodoc: */ |
1185 | | static VALUE |
1186 | | nurat_coerce(VALUE self, VALUE other) |
1187 | 0 | { |
1188 | 0 | if (RB_INTEGER_TYPE_P(other)) { |
1189 | 0 | return rb_assoc_new(f_rational_new_bang1(CLASS_OF(self), other), self); |
1190 | 0 | } |
1191 | 0 | else if (RB_FLOAT_TYPE_P(other)) { |
1192 | 0 | return rb_assoc_new(other, nurat_to_f(self)); |
1193 | 0 | } |
1194 | 0 | else if (RB_TYPE_P(other, T_RATIONAL)) { |
1195 | 0 | return rb_assoc_new(other, self); |
1196 | 0 | } |
1197 | 0 | else if (RB_TYPE_P(other, T_COMPLEX)) { |
1198 | 0 | if (!k_exact_zero_p(RCOMPLEX(other)->imag)) |
1199 | 0 | return rb_assoc_new(other, rb_Complex(self, INT2FIX(0))); |
1200 | 0 | other = RCOMPLEX(other)->real; |
1201 | 0 | if (RB_FLOAT_TYPE_P(other)) { |
1202 | 0 | other = float_to_r(other); |
1203 | 0 | RBASIC_SET_CLASS(other, CLASS_OF(self)); |
1204 | 0 | } |
1205 | 0 | else { |
1206 | 0 | other = f_rational_new_bang1(CLASS_OF(self), other); |
1207 | 0 | } |
1208 | 0 | return rb_assoc_new(other, self); |
1209 | 0 | } |
1210 | | |
1211 | 0 | rb_raise(rb_eTypeError, "%s can't be coerced into %s", |
1212 | 0 | rb_obj_classname(other), rb_obj_classname(self)); |
1213 | 0 | return Qnil; |
1214 | 0 | } |
1215 | | |
1216 | | /* |
1217 | | * call-seq: |
1218 | | * rat.positive? -> true or false |
1219 | | * |
1220 | | * Returns +true+ if +rat+ is greater than 0. |
1221 | | */ |
1222 | | static VALUE |
1223 | | nurat_positive_p(VALUE self) |
1224 | 0 | { |
1225 | 0 | get_dat1(self); |
1226 | 0 | return RBOOL(INT_POSITIVE_P(dat->num)); |
1227 | 0 | } |
1228 | | |
1229 | | /* |
1230 | | * call-seq: |
1231 | | * rat.negative? -> true or false |
1232 | | * |
1233 | | * Returns +true+ if +rat+ is less than 0. |
1234 | | */ |
1235 | | static VALUE |
1236 | | nurat_negative_p(VALUE self) |
1237 | 0 | { |
1238 | 0 | get_dat1(self); |
1239 | 0 | return RBOOL(INT_NEGATIVE_P(dat->num)); |
1240 | 0 | } |
1241 | | |
1242 | | /* |
1243 | | * call-seq: |
1244 | | * rat.abs -> rational |
1245 | | * rat.magnitude -> rational |
1246 | | * |
1247 | | * Returns the absolute value of +rat+. |
1248 | | * |
1249 | | * (1/2r).abs #=> (1/2) |
1250 | | * (-1/2r).abs #=> (1/2) |
1251 | | * |
1252 | | */ |
1253 | | |
1254 | | VALUE |
1255 | | rb_rational_abs(VALUE self) |
1256 | 0 | { |
1257 | 0 | get_dat1(self); |
1258 | 0 | if (INT_NEGATIVE_P(dat->num)) { |
1259 | 0 | VALUE num = rb_int_abs(dat->num); |
1260 | 0 | return nurat_s_canonicalize_internal_no_reduce(CLASS_OF(self), num, dat->den); |
1261 | 0 | } |
1262 | 0 | return self; |
1263 | 0 | } |
1264 | | |
1265 | | static VALUE |
1266 | | nurat_floor(VALUE self) |
1267 | 0 | { |
1268 | 0 | get_dat1(self); |
1269 | 0 | return rb_int_idiv(dat->num, dat->den); |
1270 | 0 | } |
1271 | | |
1272 | | static VALUE |
1273 | | nurat_ceil(VALUE self) |
1274 | 0 | { |
1275 | 0 | get_dat1(self); |
1276 | 0 | return rb_int_uminus(rb_int_idiv(rb_int_uminus(dat->num), dat->den)); |
1277 | 0 | } |
1278 | | |
1279 | | /* |
1280 | | * call-seq: |
1281 | | * rat.to_i -> integer |
1282 | | * |
1283 | | * Returns the truncated value as an integer. |
1284 | | * |
1285 | | * Equivalent to Rational#truncate. |
1286 | | * |
1287 | | * Rational(2, 3).to_i #=> 0 |
1288 | | * Rational(3).to_i #=> 3 |
1289 | | * Rational(300.6).to_i #=> 300 |
1290 | | * Rational(98, 71).to_i #=> 1 |
1291 | | * Rational(-31, 2).to_i #=> -15 |
1292 | | */ |
1293 | | static VALUE |
1294 | | nurat_truncate(VALUE self) |
1295 | 0 | { |
1296 | 0 | get_dat1(self); |
1297 | 0 | if (INT_NEGATIVE_P(dat->num)) |
1298 | 0 | return rb_int_uminus(rb_int_idiv(rb_int_uminus(dat->num), dat->den)); |
1299 | 0 | return rb_int_idiv(dat->num, dat->den); |
1300 | 0 | } |
1301 | | |
1302 | | static VALUE |
1303 | | nurat_round_half_up(VALUE self) |
1304 | 0 | { |
1305 | 0 | VALUE num, den, neg; |
1306 | |
|
1307 | 0 | get_dat1(self); |
1308 | |
|
1309 | 0 | num = dat->num; |
1310 | 0 | den = dat->den; |
1311 | 0 | neg = INT_NEGATIVE_P(num); |
1312 | |
|
1313 | 0 | if (neg) |
1314 | 0 | num = rb_int_uminus(num); |
1315 | |
|
1316 | 0 | num = rb_int_plus(rb_int_mul(num, TWO), den); |
1317 | 0 | den = rb_int_mul(den, TWO); |
1318 | 0 | num = rb_int_idiv(num, den); |
1319 | |
|
1320 | 0 | if (neg) |
1321 | 0 | num = rb_int_uminus(num); |
1322 | |
|
1323 | 0 | return num; |
1324 | 0 | } |
1325 | | |
1326 | | static VALUE |
1327 | | nurat_round_half_down(VALUE self) |
1328 | 0 | { |
1329 | 0 | VALUE num, den, neg; |
1330 | |
|
1331 | 0 | get_dat1(self); |
1332 | |
|
1333 | 0 | num = dat->num; |
1334 | 0 | den = dat->den; |
1335 | 0 | neg = INT_NEGATIVE_P(num); |
1336 | |
|
1337 | 0 | if (neg) |
1338 | 0 | num = rb_int_uminus(num); |
1339 | |
|
1340 | 0 | num = rb_int_plus(rb_int_mul(num, TWO), den); |
1341 | 0 | num = rb_int_minus(num, ONE); |
1342 | 0 | den = rb_int_mul(den, TWO); |
1343 | 0 | num = rb_int_idiv(num, den); |
1344 | |
|
1345 | 0 | if (neg) |
1346 | 0 | num = rb_int_uminus(num); |
1347 | |
|
1348 | 0 | return num; |
1349 | 0 | } |
1350 | | |
1351 | | static VALUE |
1352 | | nurat_round_half_even(VALUE self) |
1353 | 0 | { |
1354 | 0 | VALUE num, den, neg, qr; |
1355 | |
|
1356 | 0 | get_dat1(self); |
1357 | |
|
1358 | 0 | num = dat->num; |
1359 | 0 | den = dat->den; |
1360 | 0 | neg = INT_NEGATIVE_P(num); |
1361 | |
|
1362 | 0 | if (neg) |
1363 | 0 | num = rb_int_uminus(num); |
1364 | |
|
1365 | 0 | num = rb_int_plus(rb_int_mul(num, TWO), den); |
1366 | 0 | den = rb_int_mul(den, TWO); |
1367 | 0 | qr = rb_int_divmod(num, den); |
1368 | 0 | num = RARRAY_AREF(qr, 0); |
1369 | 0 | if (INT_ZERO_P(RARRAY_AREF(qr, 1))) |
1370 | 0 | num = rb_int_and(num, LONG2FIX(((int)~1))); |
1371 | |
|
1372 | 0 | if (neg) |
1373 | 0 | num = rb_int_uminus(num); |
1374 | |
|
1375 | 0 | return num; |
1376 | 0 | } |
1377 | | |
1378 | | static VALUE |
1379 | | f_round_common(int argc, VALUE *argv, VALUE self, VALUE (*func)(VALUE)) |
1380 | 0 | { |
1381 | 0 | VALUE n, b, s; |
1382 | |
|
1383 | 0 | if (rb_check_arity(argc, 0, 1) == 0) |
1384 | 0 | return (*func)(self); |
1385 | | |
1386 | 0 | n = argv[0]; |
1387 | |
|
1388 | 0 | if (!k_integer_p(n)) |
1389 | 0 | rb_raise(rb_eTypeError, "not an integer"); |
1390 | | |
1391 | 0 | b = f_expt10(n); |
1392 | 0 | s = rb_rational_mul(self, b); |
1393 | |
|
1394 | 0 | if (k_float_p(s)) { |
1395 | 0 | if (INT_NEGATIVE_P(n)) |
1396 | 0 | return ZERO; |
1397 | 0 | return self; |
1398 | 0 | } |
1399 | | |
1400 | 0 | if (!k_rational_p(s)) { |
1401 | 0 | s = f_rational_new_bang1(CLASS_OF(self), s); |
1402 | 0 | } |
1403 | |
|
1404 | 0 | s = (*func)(s); |
1405 | |
|
1406 | 0 | s = rb_rational_div(f_rational_new_bang1(CLASS_OF(self), s), b); |
1407 | |
|
1408 | 0 | if (RB_TYPE_P(s, T_RATIONAL) && FIX2INT(rb_int_cmp(n, ONE)) < 0) |
1409 | 0 | s = nurat_truncate(s); |
1410 | |
|
1411 | 0 | return s; |
1412 | 0 | } |
1413 | | |
1414 | | VALUE |
1415 | | rb_rational_floor(VALUE self, int ndigits) |
1416 | 0 | { |
1417 | 0 | if (ndigits == 0) { |
1418 | 0 | return nurat_floor(self); |
1419 | 0 | } |
1420 | 0 | else { |
1421 | 0 | VALUE n = INT2NUM(ndigits); |
1422 | 0 | return f_round_common(1, &n, self, nurat_floor); |
1423 | 0 | } |
1424 | 0 | } |
1425 | | |
1426 | | /* |
1427 | | * call-seq: |
1428 | | * rat.floor([ndigits]) -> integer or rational |
1429 | | * |
1430 | | * Returns the largest number less than or equal to +rat+ with |
1431 | | * a precision of +ndigits+ decimal digits (default: 0). |
1432 | | * |
1433 | | * When the precision is negative, the returned value is an integer |
1434 | | * with at least <code>ndigits.abs</code> trailing zeros. |
1435 | | * |
1436 | | * Returns a rational when +ndigits+ is positive, |
1437 | | * otherwise returns an integer. |
1438 | | * |
1439 | | * Rational(3).floor #=> 3 |
1440 | | * Rational(2, 3).floor #=> 0 |
1441 | | * Rational(-3, 2).floor #=> -2 |
1442 | | * |
1443 | | * # decimal - 1 2 3 . 4 5 6 |
1444 | | * # ^ ^ ^ ^ ^ ^ |
1445 | | * # precision -3 -2 -1 0 +1 +2 |
1446 | | * |
1447 | | * Rational('-123.456').floor(+1).to_f #=> -123.5 |
1448 | | * Rational('-123.456').floor(-1) #=> -130 |
1449 | | */ |
1450 | | static VALUE |
1451 | | nurat_floor_n(int argc, VALUE *argv, VALUE self) |
1452 | 0 | { |
1453 | 0 | return f_round_common(argc, argv, self, nurat_floor); |
1454 | 0 | } |
1455 | | |
1456 | | /* |
1457 | | * call-seq: |
1458 | | * rat.ceil([ndigits]) -> integer or rational |
1459 | | * |
1460 | | * Returns the smallest number greater than or equal to +rat+ with |
1461 | | * a precision of +ndigits+ decimal digits (default: 0). |
1462 | | * |
1463 | | * When the precision is negative, the returned value is an integer |
1464 | | * with at least <code>ndigits.abs</code> trailing zeros. |
1465 | | * |
1466 | | * Returns a rational when +ndigits+ is positive, |
1467 | | * otherwise returns an integer. |
1468 | | * |
1469 | | * Rational(3).ceil #=> 3 |
1470 | | * Rational(2, 3).ceil #=> 1 |
1471 | | * Rational(-3, 2).ceil #=> -1 |
1472 | | * |
1473 | | * # decimal - 1 2 3 . 4 5 6 |
1474 | | * # ^ ^ ^ ^ ^ ^ |
1475 | | * # precision -3 -2 -1 0 +1 +2 |
1476 | | * |
1477 | | * Rational('-123.456').ceil(+1).to_f #=> -123.4 |
1478 | | * Rational('-123.456').ceil(-1) #=> -120 |
1479 | | */ |
1480 | | static VALUE |
1481 | | nurat_ceil_n(int argc, VALUE *argv, VALUE self) |
1482 | 0 | { |
1483 | 0 | return f_round_common(argc, argv, self, nurat_ceil); |
1484 | 0 | } |
1485 | | |
1486 | | /* |
1487 | | * call-seq: |
1488 | | * rat.truncate([ndigits]) -> integer or rational |
1489 | | * |
1490 | | * Returns +rat+ truncated (toward zero) to |
1491 | | * a precision of +ndigits+ decimal digits (default: 0). |
1492 | | * |
1493 | | * When the precision is negative, the returned value is an integer |
1494 | | * with at least <code>ndigits.abs</code> trailing zeros. |
1495 | | * |
1496 | | * Returns a rational when +ndigits+ is positive, |
1497 | | * otherwise returns an integer. |
1498 | | * |
1499 | | * Rational(3).truncate #=> 3 |
1500 | | * Rational(2, 3).truncate #=> 0 |
1501 | | * Rational(-3, 2).truncate #=> -1 |
1502 | | * |
1503 | | * # decimal - 1 2 3 . 4 5 6 |
1504 | | * # ^ ^ ^ ^ ^ ^ |
1505 | | * # precision -3 -2 -1 0 +1 +2 |
1506 | | * |
1507 | | * Rational('-123.456').truncate(+1).to_f #=> -123.4 |
1508 | | * Rational('-123.456').truncate(-1) #=> -120 |
1509 | | */ |
1510 | | static VALUE |
1511 | | nurat_truncate_n(int argc, VALUE *argv, VALUE self) |
1512 | 0 | { |
1513 | 0 | return f_round_common(argc, argv, self, nurat_truncate); |
1514 | 0 | } |
1515 | | |
1516 | | /* |
1517 | | * call-seq: |
1518 | | * rat.round([ndigits] [, half: mode]) -> integer or rational |
1519 | | * |
1520 | | * Returns +rat+ rounded to the nearest value with |
1521 | | * a precision of +ndigits+ decimal digits (default: 0). |
1522 | | * |
1523 | | * When the precision is negative, the returned value is an integer |
1524 | | * with at least <code>ndigits.abs</code> trailing zeros. |
1525 | | * |
1526 | | * Returns a rational when +ndigits+ is positive, |
1527 | | * otherwise returns an integer. |
1528 | | * |
1529 | | * Rational(3).round #=> 3 |
1530 | | * Rational(2, 3).round #=> 1 |
1531 | | * Rational(-3, 2).round #=> -2 |
1532 | | * |
1533 | | * # decimal - 1 2 3 . 4 5 6 |
1534 | | * # ^ ^ ^ ^ ^ ^ |
1535 | | * # precision -3 -2 -1 0 +1 +2 |
1536 | | * |
1537 | | * Rational('-123.456').round(+1).to_f #=> -123.5 |
1538 | | * Rational('-123.456').round(-1) #=> -120 |
1539 | | * |
1540 | | * The optional +half+ keyword argument is available |
1541 | | * similar to Float#round. |
1542 | | * |
1543 | | * Rational(25, 100).round(1, half: :up) #=> (3/10) |
1544 | | * Rational(25, 100).round(1, half: :down) #=> (1/5) |
1545 | | * Rational(25, 100).round(1, half: :even) #=> (1/5) |
1546 | | * Rational(35, 100).round(1, half: :up) #=> (2/5) |
1547 | | * Rational(35, 100).round(1, half: :down) #=> (3/10) |
1548 | | * Rational(35, 100).round(1, half: :even) #=> (2/5) |
1549 | | * Rational(-25, 100).round(1, half: :up) #=> (-3/10) |
1550 | | * Rational(-25, 100).round(1, half: :down) #=> (-1/5) |
1551 | | * Rational(-25, 100).round(1, half: :even) #=> (-1/5) |
1552 | | */ |
1553 | | static VALUE |
1554 | | nurat_round_n(int argc, VALUE *argv, VALUE self) |
1555 | 0 | { |
1556 | 0 | VALUE opt; |
1557 | 0 | enum ruby_num_rounding_mode mode = ( |
1558 | 0 | argc = rb_scan_args(argc, argv, "*:", NULL, &opt), |
1559 | 0 | rb_num_get_rounding_option(opt)); |
1560 | 0 | VALUE (*round_func)(VALUE) = ROUND_FUNC(mode, nurat_round); |
1561 | 0 | return f_round_common(argc, argv, self, round_func); |
1562 | 0 | } |
1563 | | |
1564 | | VALUE |
1565 | | rb_flo_round_by_rational(int argc, VALUE *argv, VALUE num) |
1566 | 0 | { |
1567 | 0 | return nurat_to_f(nurat_round_n(argc, argv, float_to_r(num))); |
1568 | 0 | } |
1569 | | |
1570 | | static double |
1571 | | nurat_to_double(VALUE self) |
1572 | 0 | { |
1573 | 0 | get_dat1(self); |
1574 | 0 | if (!RB_INTEGER_TYPE_P(dat->num) || !RB_INTEGER_TYPE_P(dat->den)) { |
1575 | 0 | return NUM2DBL(dat->num) / NUM2DBL(dat->den); |
1576 | 0 | } |
1577 | 0 | return rb_int_fdiv_double(dat->num, dat->den); |
1578 | 0 | } |
1579 | | |
1580 | | /* |
1581 | | * call-seq: |
1582 | | * rat.to_f -> float |
1583 | | * |
1584 | | * Returns the value as a Float. |
1585 | | * |
1586 | | * Rational(2).to_f #=> 2.0 |
1587 | | * Rational(9, 4).to_f #=> 2.25 |
1588 | | * Rational(-3, 4).to_f #=> -0.75 |
1589 | | * Rational(20, 3).to_f #=> 6.666666666666667 |
1590 | | */ |
1591 | | static VALUE |
1592 | | nurat_to_f(VALUE self) |
1593 | 0 | { |
1594 | 0 | return DBL2NUM(nurat_to_double(self)); |
1595 | 0 | } |
1596 | | |
1597 | | /* |
1598 | | * call-seq: |
1599 | | * rat.to_r -> self |
1600 | | * |
1601 | | * Returns self. |
1602 | | * |
1603 | | * Rational(2).to_r #=> (2/1) |
1604 | | * Rational(-8, 6).to_r #=> (-4/3) |
1605 | | */ |
1606 | | static VALUE |
1607 | | nurat_to_r(VALUE self) |
1608 | 0 | { |
1609 | 0 | return self; |
1610 | 0 | } |
1611 | | |
1612 | 0 | #define id_ceil rb_intern("ceil") |
1613 | | static VALUE |
1614 | | f_ceil(VALUE x) |
1615 | 0 | { |
1616 | 0 | if (RB_INTEGER_TYPE_P(x)) |
1617 | 0 | return x; |
1618 | 0 | if (RB_FLOAT_TYPE_P(x)) |
1619 | 0 | return rb_float_ceil(x, 0); |
1620 | | |
1621 | 0 | return rb_funcall(x, id_ceil, 0); |
1622 | 0 | } |
1623 | | |
1624 | 0 | #define id_quo idQuo |
1625 | | static VALUE |
1626 | | f_quo(VALUE x, VALUE y) |
1627 | 0 | { |
1628 | 0 | if (RB_INTEGER_TYPE_P(x)) |
1629 | 0 | return rb_int_div(x, y); |
1630 | 0 | if (RB_FLOAT_TYPE_P(x)) |
1631 | 0 | return DBL2NUM(RFLOAT_VALUE(x) / RFLOAT_VALUE(y)); |
1632 | | |
1633 | 0 | return rb_funcallv(x, id_quo, 1, &y); |
1634 | 0 | } |
1635 | | |
1636 | 0 | #define f_reciprocal(x) f_quo(ONE, (x)) |
1637 | | |
1638 | | /* |
1639 | | The algorithm here is the method described in CLISP. Bruno Haible has |
1640 | | graciously given permission to use this algorithm. He says, "You can use |
1641 | | it, if you present the following explanation of the algorithm." |
1642 | | |
1643 | | Algorithm (recursively presented): |
1644 | | If x is a rational number, return x. |
1645 | | If x = 0.0, return 0. |
1646 | | If x < 0.0, return (- (rationalize (- x))). |
1647 | | If x > 0.0: |
1648 | | Call (integer-decode-float x). It returns a m,e,s=1 (mantissa, |
1649 | | exponent, sign). |
1650 | | If m = 0 or e >= 0: return x = m*2^e. |
1651 | | Search a rational number between a = (m-1/2)*2^e and b = (m+1/2)*2^e |
1652 | | with smallest possible numerator and denominator. |
1653 | | Note 1: If m is a power of 2, we ought to take a = (m-1/4)*2^e. |
1654 | | But in this case the result will be x itself anyway, regardless of |
1655 | | the choice of a. Therefore we can simply ignore this case. |
1656 | | Note 2: At first, we need to consider the closed interval [a,b]. |
1657 | | but since a and b have the denominator 2^(|e|+1) whereas x itself |
1658 | | has a denominator <= 2^|e|, we can restrict the search to the open |
1659 | | interval (a,b). |
1660 | | So, for given a and b (0 < a < b) we are searching a rational number |
1661 | | y with a <= y <= b. |
1662 | | Recursive algorithm fraction_between(a,b): |
1663 | | c := (ceiling a) |
1664 | | if c < b |
1665 | | then return c ; because a <= c < b, c integer |
1666 | | else |
1667 | | ; a is not integer (otherwise we would have had c = a < b) |
1668 | | k := c-1 ; k = floor(a), k < a < b <= k+1 |
1669 | | return y = k + 1/fraction_between(1/(b-k), 1/(a-k)) |
1670 | | ; note 1 <= 1/(b-k) < 1/(a-k) |
1671 | | |
1672 | | You can see that we are actually computing a continued fraction expansion. |
1673 | | |
1674 | | Algorithm (iterative): |
1675 | | If x is rational, return x. |
1676 | | Call (integer-decode-float x). It returns a m,e,s (mantissa, |
1677 | | exponent, sign). |
1678 | | If m = 0 or e >= 0, return m*2^e*s. (This includes the case x = 0.0.) |
1679 | | Create rational numbers a := (2*m-1)*2^(e-1) and b := (2*m+1)*2^(e-1) |
1680 | | (positive and already in lowest terms because the denominator is a |
1681 | | power of two and the numerator is odd). |
1682 | | Start a continued fraction expansion |
1683 | | p[-1] := 0, p[0] := 1, q[-1] := 1, q[0] := 0, i := 0. |
1684 | | Loop |
1685 | | c := (ceiling a) |
1686 | | if c >= b |
1687 | | then k := c-1, partial_quotient(k), (a,b) := (1/(b-k),1/(a-k)), |
1688 | | goto Loop |
1689 | | finally partial_quotient(c). |
1690 | | Here partial_quotient(c) denotes the iteration |
1691 | | i := i+1, p[i] := c*p[i-1]+p[i-2], q[i] := c*q[i-1]+q[i-2]. |
1692 | | At the end, return s * (p[i]/q[i]). |
1693 | | This rational number is already in lowest terms because |
1694 | | p[i]*q[i-1]-p[i-1]*q[i] = (-1)^i. |
1695 | | */ |
1696 | | |
1697 | | static void |
1698 | | nurat_rationalize_internal(VALUE a, VALUE b, VALUE *p, VALUE *q) |
1699 | 0 | { |
1700 | 0 | VALUE c, k, t, p0, p1, p2, q0, q1, q2; |
1701 | |
|
1702 | 0 | p0 = ZERO; |
1703 | 0 | p1 = ONE; |
1704 | 0 | q0 = ONE; |
1705 | 0 | q1 = ZERO; |
1706 | |
|
1707 | 0 | while (1) { |
1708 | 0 | c = f_ceil(a); |
1709 | 0 | if (f_lt_p(c, b)) |
1710 | 0 | break; |
1711 | 0 | k = f_sub(c, ONE); |
1712 | 0 | p2 = f_add(f_mul(k, p1), p0); |
1713 | 0 | q2 = f_add(f_mul(k, q1), q0); |
1714 | 0 | t = f_reciprocal(f_sub(b, k)); |
1715 | 0 | b = f_reciprocal(f_sub(a, k)); |
1716 | 0 | a = t; |
1717 | 0 | p0 = p1; |
1718 | 0 | q0 = q1; |
1719 | 0 | p1 = p2; |
1720 | 0 | q1 = q2; |
1721 | 0 | } |
1722 | 0 | *p = f_add(f_mul(c, p1), p0); |
1723 | 0 | *q = f_add(f_mul(c, q1), q0); |
1724 | 0 | } |
1725 | | |
1726 | | /* |
1727 | | * call-seq: |
1728 | | * rat.rationalize -> self |
1729 | | * rat.rationalize(eps) -> rational |
1730 | | * |
1731 | | * Returns a simpler approximation of the value if the optional |
1732 | | * argument +eps+ is given (rat-|eps| <= result <= rat+|eps|), |
1733 | | * self otherwise. |
1734 | | * |
1735 | | * r = Rational(5033165, 16777216) |
1736 | | * r.rationalize #=> (5033165/16777216) |
1737 | | * r.rationalize(Rational('0.01')) #=> (3/10) |
1738 | | * r.rationalize(Rational('0.1')) #=> (1/3) |
1739 | | */ |
1740 | | static VALUE |
1741 | | nurat_rationalize(int argc, VALUE *argv, VALUE self) |
1742 | 0 | { |
1743 | 0 | VALUE e, a, b, p, q; |
1744 | 0 | VALUE rat = self; |
1745 | 0 | get_dat1(self); |
1746 | |
|
1747 | 0 | if (rb_check_arity(argc, 0, 1) == 0) |
1748 | 0 | return self; |
1749 | | |
1750 | 0 | e = f_abs(argv[0]); |
1751 | |
|
1752 | 0 | if (INT_NEGATIVE_P(dat->num)) { |
1753 | 0 | rat = f_rational_new2(RBASIC_CLASS(self), rb_int_uminus(dat->num), dat->den); |
1754 | 0 | } |
1755 | |
|
1756 | 0 | a = FIXNUM_ZERO_P(e) ? rat : rb_rational_minus(rat, e); |
1757 | 0 | b = FIXNUM_ZERO_P(e) ? rat : rb_rational_plus(rat, e); |
1758 | |
|
1759 | 0 | if (f_eqeq_p(a, b)) |
1760 | 0 | return self; |
1761 | | |
1762 | 0 | nurat_rationalize_internal(a, b, &p, &q); |
1763 | 0 | if (rat != self) { |
1764 | 0 | RATIONAL_SET_NUM(rat, rb_int_uminus(p)); |
1765 | 0 | RATIONAL_SET_DEN(rat, q); |
1766 | 0 | return rat; |
1767 | 0 | } |
1768 | 0 | return f_rational_new2(CLASS_OF(self), p, q); |
1769 | 0 | } |
1770 | | |
1771 | | /* :nodoc: */ |
1772 | | st_index_t |
1773 | | rb_rational_hash(VALUE self) |
1774 | 0 | { |
1775 | 0 | st_index_t v, h[2]; |
1776 | 0 | VALUE n; |
1777 | |
|
1778 | 0 | get_dat1(self); |
1779 | 0 | n = rb_hash(dat->num); |
1780 | 0 | h[0] = NUM2LONG(n); |
1781 | 0 | n = rb_hash(dat->den); |
1782 | 0 | h[1] = NUM2LONG(n); |
1783 | 0 | v = rb_memhash(h, sizeof(h)); |
1784 | 0 | return v; |
1785 | 0 | } |
1786 | | |
1787 | | static VALUE |
1788 | | nurat_hash(VALUE self) |
1789 | 0 | { |
1790 | 0 | return ST2FIX(rb_rational_hash(self)); |
1791 | 0 | } |
1792 | | |
1793 | | |
1794 | | static VALUE |
1795 | | f_format(VALUE self, VALUE (*func)(VALUE)) |
1796 | 1 | { |
1797 | 1 | VALUE s; |
1798 | 1 | get_dat1(self); |
1799 | | |
1800 | 1 | s = (*func)(dat->num); |
1801 | 1 | rb_str_cat2(s, "/"); |
1802 | 1 | rb_str_concat(s, (*func)(dat->den)); |
1803 | | |
1804 | 1 | return s; |
1805 | 1 | } |
1806 | | |
1807 | | /* |
1808 | | * call-seq: |
1809 | | * rat.to_s -> string |
1810 | | * |
1811 | | * Returns the value as a string. |
1812 | | * |
1813 | | * Rational(2).to_s #=> "2/1" |
1814 | | * Rational(-8, 6).to_s #=> "-4/3" |
1815 | | * Rational('1/2').to_s #=> "1/2" |
1816 | | */ |
1817 | | static VALUE |
1818 | | nurat_to_s(VALUE self) |
1819 | 0 | { |
1820 | 0 | return f_format(self, f_to_s); |
1821 | 0 | } |
1822 | | |
1823 | | /* |
1824 | | * call-seq: |
1825 | | * rat.inspect -> string |
1826 | | * |
1827 | | * Returns the value as a string for inspection. |
1828 | | * |
1829 | | * Rational(2).inspect #=> "(2/1)" |
1830 | | * Rational(-8, 6).inspect #=> "(-4/3)" |
1831 | | * Rational('1/2').inspect #=> "(1/2)" |
1832 | | */ |
1833 | | static VALUE |
1834 | | nurat_inspect(VALUE self) |
1835 | 1 | { |
1836 | 1 | VALUE s; |
1837 | | |
1838 | 1 | s = rb_usascii_str_new2("("); |
1839 | 1 | rb_str_concat(s, f_format(self, f_inspect)); |
1840 | 1 | rb_str_cat2(s, ")"); |
1841 | | |
1842 | 1 | return s; |
1843 | 1 | } |
1844 | | |
1845 | | /* :nodoc: */ |
1846 | | static VALUE |
1847 | | nurat_dumper(VALUE self) |
1848 | 0 | { |
1849 | 0 | return self; |
1850 | 0 | } |
1851 | | |
1852 | | /* :nodoc: */ |
1853 | | static VALUE |
1854 | | nurat_loader(VALUE self, VALUE a) |
1855 | 0 | { |
1856 | 0 | VALUE num, den; |
1857 | |
|
1858 | 0 | get_dat1(self); |
1859 | 0 | num = rb_ivar_get(a, id_i_num); |
1860 | 0 | den = rb_ivar_get(a, id_i_den); |
1861 | 0 | nurat_int_check(num); |
1862 | 0 | nurat_int_check(den); |
1863 | 0 | nurat_canonicalize(&num, &den); |
1864 | 0 | RATIONAL_SET_NUM((VALUE)dat, num); |
1865 | 0 | RATIONAL_SET_DEN((VALUE)dat, den); |
1866 | 0 | OBJ_FREEZE(self); |
1867 | |
|
1868 | 0 | return self; |
1869 | 0 | } |
1870 | | |
1871 | | /* :nodoc: */ |
1872 | | static VALUE |
1873 | | nurat_marshal_dump(VALUE self) |
1874 | 0 | { |
1875 | 0 | VALUE a; |
1876 | 0 | get_dat1(self); |
1877 | |
|
1878 | 0 | a = rb_assoc_new(dat->num, dat->den); |
1879 | 0 | rb_copy_generic_ivar(a, self); |
1880 | 0 | return a; |
1881 | 0 | } |
1882 | | |
1883 | | /* :nodoc: */ |
1884 | | static VALUE |
1885 | | nurat_marshal_load(VALUE self, VALUE a) |
1886 | 0 | { |
1887 | 0 | VALUE num, den; |
1888 | |
|
1889 | 0 | rb_check_frozen(self); |
1890 | |
|
1891 | 0 | Check_Type(a, T_ARRAY); |
1892 | 0 | if (RARRAY_LEN(a) != 2) |
1893 | 0 | rb_raise(rb_eArgError, "marshaled rational must have an array whose length is 2 but %ld", RARRAY_LEN(a)); |
1894 | | |
1895 | 0 | num = RARRAY_AREF(a, 0); |
1896 | 0 | den = RARRAY_AREF(a, 1); |
1897 | 0 | nurat_int_check(num); |
1898 | 0 | nurat_int_check(den); |
1899 | 0 | nurat_canonicalize(&num, &den); |
1900 | 0 | rb_ivar_set(self, id_i_num, num); |
1901 | 0 | rb_ivar_set(self, id_i_den, den); |
1902 | |
|
1903 | 0 | return self; |
1904 | 0 | } |
1905 | | |
1906 | | VALUE |
1907 | | rb_rational_reciprocal(VALUE x) |
1908 | 0 | { |
1909 | 0 | get_dat1(x); |
1910 | 0 | return nurat_convert(CLASS_OF(x), dat->den, dat->num, FALSE); |
1911 | 0 | } |
1912 | | |
1913 | | /* |
1914 | | * call-seq: |
1915 | | * int.gcd(other_int) -> integer |
1916 | | * |
1917 | | * Returns the greatest common divisor of the two integers. |
1918 | | * The result is always positive. 0.gcd(x) and x.gcd(0) return x.abs. |
1919 | | * |
1920 | | * 36.gcd(60) #=> 12 |
1921 | | * 2.gcd(2) #=> 2 |
1922 | | * 3.gcd(-7) #=> 1 |
1923 | | * ((1<<31)-1).gcd((1<<61)-1) #=> 1 |
1924 | | */ |
1925 | | VALUE |
1926 | | rb_gcd(VALUE self, VALUE other) |
1927 | 0 | { |
1928 | 0 | other = nurat_int_value(other); |
1929 | 0 | return f_gcd(self, other); |
1930 | 0 | } |
1931 | | |
1932 | | /* |
1933 | | * call-seq: |
1934 | | * int.lcm(other_int) -> integer |
1935 | | * |
1936 | | * Returns the least common multiple of the two integers. |
1937 | | * The result is always positive. 0.lcm(x) and x.lcm(0) return zero. |
1938 | | * |
1939 | | * 36.lcm(60) #=> 180 |
1940 | | * 2.lcm(2) #=> 2 |
1941 | | * 3.lcm(-7) #=> 21 |
1942 | | * ((1<<31)-1).lcm((1<<61)-1) #=> 4951760154835678088235319297 |
1943 | | */ |
1944 | | VALUE |
1945 | | rb_lcm(VALUE self, VALUE other) |
1946 | 0 | { |
1947 | 0 | other = nurat_int_value(other); |
1948 | 0 | return f_lcm(self, other); |
1949 | 0 | } |
1950 | | |
1951 | | /* |
1952 | | * call-seq: |
1953 | | * int.gcdlcm(other_int) -> array |
1954 | | * |
1955 | | * Returns an array with the greatest common divisor and |
1956 | | * the least common multiple of the two integers, [gcd, lcm]. |
1957 | | * |
1958 | | * 36.gcdlcm(60) #=> [12, 180] |
1959 | | * 2.gcdlcm(2) #=> [2, 2] |
1960 | | * 3.gcdlcm(-7) #=> [1, 21] |
1961 | | * ((1<<31)-1).gcdlcm((1<<61)-1) #=> [1, 4951760154835678088235319297] |
1962 | | */ |
1963 | | VALUE |
1964 | | rb_gcdlcm(VALUE self, VALUE other) |
1965 | 0 | { |
1966 | 0 | other = nurat_int_value(other); |
1967 | 0 | return rb_assoc_new(f_gcd(self, other), f_lcm(self, other)); |
1968 | 0 | } |
1969 | | |
1970 | | VALUE |
1971 | | rb_rational_raw(VALUE x, VALUE y) |
1972 | 1 | { |
1973 | 1 | if (! RB_INTEGER_TYPE_P(x)) |
1974 | 0 | x = rb_to_int(x); |
1975 | 1 | if (! RB_INTEGER_TYPE_P(y)) |
1976 | 0 | y = rb_to_int(y); |
1977 | 1 | if (INT_NEGATIVE_P(y)) { |
1978 | 0 | x = rb_int_uminus(x); |
1979 | 0 | y = rb_int_uminus(y); |
1980 | 0 | } |
1981 | 1 | return nurat_s_new_internal(rb_cRational, x, y); |
1982 | 1 | } |
1983 | | |
1984 | | VALUE |
1985 | | rb_rational_new(VALUE x, VALUE y) |
1986 | 0 | { |
1987 | 0 | return nurat_s_canonicalize_internal(rb_cRational, x, y); |
1988 | 0 | } |
1989 | | |
1990 | | VALUE |
1991 | | rb_Rational(VALUE x, VALUE y) |
1992 | 0 | { |
1993 | 0 | VALUE a[2]; |
1994 | 0 | a[0] = x; |
1995 | 0 | a[1] = y; |
1996 | 0 | return nurat_s_convert(2, a, rb_cRational); |
1997 | 0 | } |
1998 | | |
1999 | | VALUE |
2000 | | rb_rational_num(VALUE rat) |
2001 | 0 | { |
2002 | 0 | return nurat_numerator(rat); |
2003 | 0 | } |
2004 | | |
2005 | | VALUE |
2006 | | rb_rational_den(VALUE rat) |
2007 | 0 | { |
2008 | 0 | return nurat_denominator(rat); |
2009 | 0 | } |
2010 | | |
2011 | 0 | #define id_numerator rb_intern("numerator") |
2012 | 0 | #define f_numerator(x) rb_funcall((x), id_numerator, 0) |
2013 | | |
2014 | 0 | #define id_denominator rb_intern("denominator") |
2015 | 0 | #define f_denominator(x) rb_funcall((x), id_denominator, 0) |
2016 | | |
2017 | | #define id_to_r idTo_r |
2018 | | #define f_to_r(x) rb_funcall((x), id_to_r, 0) |
2019 | | |
2020 | | /* |
2021 | | * call-seq: |
2022 | | * num.numerator -> integer |
2023 | | * |
2024 | | * Returns the numerator. |
2025 | | */ |
2026 | | static VALUE |
2027 | | numeric_numerator(VALUE self) |
2028 | 0 | { |
2029 | 0 | return f_numerator(f_to_r(self)); |
2030 | 0 | } |
2031 | | |
2032 | | /* |
2033 | | * call-seq: |
2034 | | * num.denominator -> integer |
2035 | | * |
2036 | | * Returns the denominator (always positive). |
2037 | | */ |
2038 | | static VALUE |
2039 | | numeric_denominator(VALUE self) |
2040 | 0 | { |
2041 | 0 | return f_denominator(f_to_r(self)); |
2042 | 0 | } |
2043 | | |
2044 | | |
2045 | | /* |
2046 | | * call-seq: |
2047 | | * num.quo(int_or_rat) -> rat |
2048 | | * num.quo(flo) -> flo |
2049 | | * |
2050 | | * Returns the most exact division (rational for integers, float for floats). |
2051 | | */ |
2052 | | |
2053 | | VALUE |
2054 | | rb_numeric_quo(VALUE x, VALUE y) |
2055 | 0 | { |
2056 | 0 | if (RB_TYPE_P(x, T_COMPLEX)) { |
2057 | 0 | return rb_complex_div(x, y); |
2058 | 0 | } |
2059 | | |
2060 | 0 | if (RB_FLOAT_TYPE_P(y)) { |
2061 | 0 | return rb_funcallv(x, idFdiv, 1, &y); |
2062 | 0 | } |
2063 | | |
2064 | 0 | x = rb_convert_type(x, T_RATIONAL, "Rational", "to_r"); |
2065 | 0 | return rb_rational_div(x, y); |
2066 | 0 | } |
2067 | | |
2068 | | VALUE |
2069 | | rb_rational_canonicalize(VALUE x) |
2070 | 0 | { |
2071 | 0 | if (RB_TYPE_P(x, T_RATIONAL)) { |
2072 | 0 | get_dat1(x); |
2073 | 0 | if (f_one_p(dat->den)) return dat->num; |
2074 | 0 | } |
2075 | 0 | return x; |
2076 | 0 | } |
2077 | | |
2078 | | /* |
2079 | | * call-seq: |
2080 | | * flo.numerator -> integer |
2081 | | * |
2082 | | * Returns the numerator. The result is machine dependent. |
2083 | | * |
2084 | | * n = 0.3.numerator #=> 5404319552844595 |
2085 | | * d = 0.3.denominator #=> 18014398509481984 |
2086 | | * n.fdiv(d) #=> 0.3 |
2087 | | * |
2088 | | * See also Float#denominator. |
2089 | | */ |
2090 | | VALUE |
2091 | | rb_float_numerator(VALUE self) |
2092 | 0 | { |
2093 | 0 | double d = RFLOAT_VALUE(self); |
2094 | 0 | VALUE r; |
2095 | 0 | if (!isfinite(d)) |
2096 | 0 | return self; |
2097 | 0 | r = float_to_r(self); |
2098 | 0 | return nurat_numerator(r); |
2099 | 0 | } |
2100 | | |
2101 | | /* |
2102 | | * call-seq: |
2103 | | * flo.denominator -> integer |
2104 | | * |
2105 | | * Returns the denominator (always positive). The result is machine |
2106 | | * dependent. |
2107 | | * |
2108 | | * See also Float#numerator. |
2109 | | */ |
2110 | | VALUE |
2111 | | rb_float_denominator(VALUE self) |
2112 | 0 | { |
2113 | 0 | double d = RFLOAT_VALUE(self); |
2114 | 0 | VALUE r; |
2115 | 0 | if (!isfinite(d)) |
2116 | 0 | return INT2FIX(1); |
2117 | 0 | r = float_to_r(self); |
2118 | 0 | return nurat_denominator(r); |
2119 | 0 | } |
2120 | | |
2121 | | /* |
2122 | | * call-seq: |
2123 | | * int.to_r -> rational |
2124 | | * |
2125 | | * Returns the value as a rational. |
2126 | | * |
2127 | | * 1.to_r #=> (1/1) |
2128 | | * (1<<64).to_r #=> (18446744073709551616/1) |
2129 | | */ |
2130 | | static VALUE |
2131 | | integer_to_r(VALUE self) |
2132 | 0 | { |
2133 | 0 | return rb_rational_new1(self); |
2134 | 0 | } |
2135 | | |
2136 | | /* |
2137 | | * call-seq: |
2138 | | * int.rationalize([eps]) -> rational |
2139 | | * |
2140 | | * Returns the value as a rational. The optional argument +eps+ is |
2141 | | * always ignored. |
2142 | | */ |
2143 | | static VALUE |
2144 | | integer_rationalize(int argc, VALUE *argv, VALUE self) |
2145 | 0 | { |
2146 | 0 | rb_check_arity(argc, 0, 1); |
2147 | 0 | return integer_to_r(self); |
2148 | 0 | } |
2149 | | |
2150 | | static void |
2151 | | float_decode_internal(VALUE self, VALUE *rf, int *n) |
2152 | 0 | { |
2153 | 0 | double f; |
2154 | |
|
2155 | 0 | f = frexp(RFLOAT_VALUE(self), n); |
2156 | 0 | f = ldexp(f, DBL_MANT_DIG); |
2157 | 0 | *n -= DBL_MANT_DIG; |
2158 | 0 | *rf = rb_dbl2big(f); |
2159 | 0 | } |
2160 | | |
2161 | | /* |
2162 | | * call-seq: |
2163 | | * flt.to_r -> rational |
2164 | | * |
2165 | | * Returns the value as a rational. |
2166 | | * |
2167 | | * 2.0.to_r #=> (2/1) |
2168 | | * 2.5.to_r #=> (5/2) |
2169 | | * -0.75.to_r #=> (-3/4) |
2170 | | * 0.0.to_r #=> (0/1) |
2171 | | * 0.3.to_r #=> (5404319552844595/18014398509481984) |
2172 | | * |
2173 | | * NOTE: 0.3.to_r isn't the same as "0.3".to_r. The latter is |
2174 | | * equivalent to "3/10".to_r, but the former isn't so. |
2175 | | * |
2176 | | * 0.3.to_r == 3/10r #=> false |
2177 | | * "0.3".to_r == 3/10r #=> true |
2178 | | * |
2179 | | * See also Float#rationalize. |
2180 | | */ |
2181 | | static VALUE |
2182 | | float_to_r(VALUE self) |
2183 | 0 | { |
2184 | 0 | VALUE f; |
2185 | 0 | int n; |
2186 | |
|
2187 | 0 | float_decode_internal(self, &f, &n); |
2188 | 0 | #if FLT_RADIX == 2 |
2189 | 0 | if (n == 0) |
2190 | 0 | return rb_rational_new1(f); |
2191 | 0 | if (n > 0) |
2192 | 0 | return rb_rational_new1(rb_int_lshift(f, INT2FIX(n))); |
2193 | 0 | n = -n; |
2194 | 0 | return rb_rational_new2(f, rb_int_lshift(ONE, INT2FIX(n))); |
2195 | | #else |
2196 | | f = rb_int_mul(f, rb_int_pow(INT2FIX(FLT_RADIX), n)); |
2197 | | if (RB_TYPE_P(f, T_RATIONAL)) |
2198 | | return f; |
2199 | | return rb_rational_new1(f); |
2200 | | #endif |
2201 | 0 | } |
2202 | | |
2203 | | VALUE |
2204 | | rb_flt_rationalize_with_prec(VALUE flt, VALUE prec) |
2205 | 0 | { |
2206 | 0 | VALUE e, a, b, p, q; |
2207 | |
|
2208 | 0 | e = f_abs(prec); |
2209 | 0 | a = f_sub(flt, e); |
2210 | 0 | b = f_add(flt, e); |
2211 | |
|
2212 | 0 | if (f_eqeq_p(a, b)) |
2213 | 0 | return float_to_r(flt); |
2214 | | |
2215 | 0 | nurat_rationalize_internal(a, b, &p, &q); |
2216 | 0 | return rb_rational_new2(p, q); |
2217 | 0 | } |
2218 | | |
2219 | | VALUE |
2220 | | rb_flt_rationalize(VALUE flt) |
2221 | 0 | { |
2222 | 0 | VALUE a, b, f, p, q, den; |
2223 | 0 | int n; |
2224 | |
|
2225 | 0 | float_decode_internal(flt, &f, &n); |
2226 | 0 | if (INT_ZERO_P(f) || n >= 0) |
2227 | 0 | return rb_rational_new1(rb_int_lshift(f, INT2FIX(n))); |
2228 | | |
2229 | 0 | { |
2230 | 0 | VALUE radix_times_f; |
2231 | |
|
2232 | 0 | radix_times_f = rb_int_mul(INT2FIX(FLT_RADIX), f); |
2233 | | #if FLT_RADIX == 2 && 0 |
2234 | | den = rb_int_lshift(ONE, INT2FIX(1-n)); |
2235 | | #else |
2236 | 0 | den = rb_int_positive_pow(FLT_RADIX, 1-n); |
2237 | 0 | #endif |
2238 | |
|
2239 | 0 | a = rb_int_minus(radix_times_f, INT2FIX(FLT_RADIX - 1)); |
2240 | 0 | b = rb_int_plus(radix_times_f, INT2FIX(FLT_RADIX - 1)); |
2241 | 0 | } |
2242 | |
|
2243 | 0 | if (f_eqeq_p(a, b)) |
2244 | 0 | return float_to_r(flt); |
2245 | | |
2246 | 0 | a = rb_rational_new2(a, den); |
2247 | 0 | b = rb_rational_new2(b, den); |
2248 | 0 | nurat_rationalize_internal(a, b, &p, &q); |
2249 | 0 | return rb_rational_new2(p, q); |
2250 | 0 | } |
2251 | | |
2252 | | /* |
2253 | | * call-seq: |
2254 | | * flt.rationalize([eps]) -> rational |
2255 | | * |
2256 | | * Returns a simpler approximation of the value (flt-|eps| <= result |
2257 | | * <= flt+|eps|). If the optional argument +eps+ is not given, |
2258 | | * it will be chosen automatically. |
2259 | | * |
2260 | | * 0.3.rationalize #=> (3/10) |
2261 | | * 1.333.rationalize #=> (1333/1000) |
2262 | | * 1.333.rationalize(0.01) #=> (4/3) |
2263 | | * |
2264 | | * See also Float#to_r. |
2265 | | */ |
2266 | | static VALUE |
2267 | | float_rationalize(int argc, VALUE *argv, VALUE self) |
2268 | 0 | { |
2269 | 0 | double d = RFLOAT_VALUE(self); |
2270 | 0 | VALUE rat; |
2271 | 0 | int neg = d < 0.0; |
2272 | 0 | if (neg) self = DBL2NUM(-d); |
2273 | |
|
2274 | 0 | if (rb_check_arity(argc, 0, 1)) { |
2275 | 0 | rat = rb_flt_rationalize_with_prec(self, argv[0]); |
2276 | 0 | } |
2277 | 0 | else { |
2278 | 0 | rat = rb_flt_rationalize(self); |
2279 | 0 | } |
2280 | 0 | if (neg) RATIONAL_SET_NUM(rat, rb_int_uminus(RRATIONAL(rat)->num)); |
2281 | 0 | return rat; |
2282 | 0 | } |
2283 | | |
2284 | | inline static int |
2285 | | issign(int c) |
2286 | 0 | { |
2287 | 0 | return (c == '-' || c == '+'); |
2288 | 0 | } |
2289 | | |
2290 | | static int |
2291 | | read_sign(const char **s, const char *const e) |
2292 | 0 | { |
2293 | 0 | int sign = '?'; |
2294 | |
|
2295 | 0 | if (*s < e && issign(**s)) { |
2296 | 0 | sign = **s; |
2297 | 0 | (*s)++; |
2298 | 0 | } |
2299 | 0 | return sign; |
2300 | 0 | } |
2301 | | |
2302 | | inline static int |
2303 | | islettere(int c) |
2304 | 0 | { |
2305 | 0 | return (c == 'e' || c == 'E'); |
2306 | 0 | } |
2307 | | |
2308 | | static VALUE |
2309 | | negate_num(VALUE num) |
2310 | 0 | { |
2311 | 0 | if (FIXNUM_P(num)) { |
2312 | 0 | return rb_int_uminus(num); |
2313 | 0 | } |
2314 | 0 | else { |
2315 | 0 | BIGNUM_NEGATE(num); |
2316 | 0 | return rb_big_norm(num); |
2317 | 0 | } |
2318 | 0 | } |
2319 | | |
2320 | | static int |
2321 | | read_num(const char **s, const char *const end, VALUE *num, VALUE *nexp) |
2322 | 0 | { |
2323 | 0 | VALUE fp = ONE, exp, fn = ZERO, n = ZERO; |
2324 | 0 | int expsign = 0, ok = 0; |
2325 | 0 | char *e; |
2326 | |
|
2327 | 0 | *nexp = ZERO; |
2328 | 0 | *num = ZERO; |
2329 | 0 | if (*s < end && **s != '.') { |
2330 | 0 | n = rb_int_parse_cstr(*s, end-*s, &e, NULL, |
2331 | 0 | 10, RB_INT_PARSE_UNDERSCORE); |
2332 | 0 | if (NIL_P(n)) |
2333 | 0 | return 0; |
2334 | 0 | *s = e; |
2335 | 0 | *num = n; |
2336 | 0 | ok = 1; |
2337 | 0 | } |
2338 | | |
2339 | 0 | if (*s < end && **s == '.') { |
2340 | 0 | size_t count = 0; |
2341 | |
|
2342 | 0 | (*s)++; |
2343 | 0 | fp = rb_int_parse_cstr(*s, end-*s, &e, &count, |
2344 | 0 | 10, RB_INT_PARSE_UNDERSCORE); |
2345 | 0 | if (NIL_P(fp)) |
2346 | 0 | return 1; |
2347 | 0 | *s = e; |
2348 | 0 | { |
2349 | 0 | VALUE l = f_expt10(*nexp = SIZET2NUM(count)); |
2350 | 0 | n = n == ZERO ? fp : rb_int_plus(rb_int_mul(*num, l), fp); |
2351 | 0 | *num = n; |
2352 | 0 | fn = SIZET2NUM(count); |
2353 | 0 | } |
2354 | 0 | ok = 1; |
2355 | 0 | } |
2356 | | |
2357 | 0 | if (ok && *s + 1 < end && islettere(**s)) { |
2358 | 0 | (*s)++; |
2359 | 0 | expsign = read_sign(s, end); |
2360 | 0 | exp = rb_int_parse_cstr(*s, end-*s, &e, NULL, |
2361 | 0 | 10, RB_INT_PARSE_UNDERSCORE); |
2362 | 0 | if (NIL_P(exp)) |
2363 | 0 | return 1; |
2364 | 0 | *s = e; |
2365 | 0 | if (exp != ZERO) { |
2366 | 0 | if (expsign == '-') { |
2367 | 0 | if (fn != ZERO) exp = rb_int_plus(exp, fn); |
2368 | 0 | } |
2369 | 0 | else { |
2370 | 0 | if (fn != ZERO) exp = rb_int_minus(exp, fn); |
2371 | 0 | exp = negate_num(exp); |
2372 | 0 | } |
2373 | 0 | *nexp = exp; |
2374 | 0 | } |
2375 | 0 | } |
2376 | | |
2377 | 0 | return ok; |
2378 | 0 | } |
2379 | | |
2380 | | inline static const char * |
2381 | | skip_ws(const char *s, const char *e) |
2382 | 0 | { |
2383 | 0 | while (s < e && isspace((unsigned char)*s)) |
2384 | 0 | ++s; |
2385 | 0 | return s; |
2386 | 0 | } |
2387 | | |
2388 | | static VALUE |
2389 | | parse_rat(const char *s, const char *const e, int strict, int raise) |
2390 | 0 | { |
2391 | 0 | int sign; |
2392 | 0 | VALUE num, den, nexp, dexp; |
2393 | |
|
2394 | 0 | s = skip_ws(s, e); |
2395 | 0 | sign = read_sign(&s, e); |
2396 | |
|
2397 | 0 | if (!read_num(&s, e, &num, &nexp)) { |
2398 | 0 | if (strict) return Qnil; |
2399 | 0 | return nurat_s_alloc(rb_cRational); |
2400 | 0 | } |
2401 | 0 | den = ONE; |
2402 | 0 | if (s < e && *s == '/') { |
2403 | 0 | s++; |
2404 | 0 | if (!read_num(&s, e, &den, &dexp)) { |
2405 | 0 | if (strict) return Qnil; |
2406 | 0 | den = ONE; |
2407 | 0 | } |
2408 | 0 | else if (den == ZERO) { |
2409 | 0 | if (!raise) return Qnil; |
2410 | 0 | rb_num_zerodiv(); |
2411 | 0 | } |
2412 | 0 | else if (strict && skip_ws(s, e) != e) { |
2413 | 0 | return Qnil; |
2414 | 0 | } |
2415 | 0 | else { |
2416 | 0 | nexp = rb_int_minus(nexp, dexp); |
2417 | 0 | nurat_reduce(&num, &den); |
2418 | 0 | } |
2419 | 0 | } |
2420 | 0 | else if (strict && skip_ws(s, e) != e) { |
2421 | 0 | return Qnil; |
2422 | 0 | } |
2423 | | |
2424 | 0 | if (nexp != ZERO) { |
2425 | 0 | if (INT_NEGATIVE_P(nexp)) { |
2426 | 0 | VALUE mul; |
2427 | 0 | if (FIXNUM_P(nexp)) { |
2428 | 0 | mul = f_expt10(LONG2NUM(-FIX2LONG(nexp))); |
2429 | 0 | if (! RB_FLOAT_TYPE_P(mul)) { |
2430 | 0 | num = rb_int_mul(num, mul); |
2431 | 0 | goto reduce; |
2432 | 0 | } |
2433 | 0 | } |
2434 | 0 | return sign == '-' ? DBL2NUM(-HUGE_VAL) : DBL2NUM(HUGE_VAL); |
2435 | 0 | } |
2436 | 0 | else { |
2437 | 0 | VALUE div; |
2438 | 0 | if (FIXNUM_P(nexp)) { |
2439 | 0 | div = f_expt10(nexp); |
2440 | 0 | if (! RB_FLOAT_TYPE_P(div)) { |
2441 | 0 | den = rb_int_mul(den, div); |
2442 | 0 | goto reduce; |
2443 | 0 | } |
2444 | 0 | } |
2445 | 0 | return sign == '-' ? DBL2NUM(-0.0) : DBL2NUM(+0.0); |
2446 | 0 | } |
2447 | 0 | reduce: |
2448 | 0 | nurat_reduce(&num, &den); |
2449 | 0 | } |
2450 | | |
2451 | 0 | if (sign == '-') { |
2452 | 0 | num = negate_num(num); |
2453 | 0 | } |
2454 | |
|
2455 | 0 | return rb_rational_raw(num, den); |
2456 | 0 | } |
2457 | | |
2458 | | static VALUE |
2459 | | string_to_r_strict(VALUE self, int raise) |
2460 | 0 | { |
2461 | 0 | VALUE num; |
2462 | |
|
2463 | 0 | rb_must_asciicompat(self); |
2464 | |
|
2465 | 0 | num = parse_rat(RSTRING_PTR(self), RSTRING_END(self), 1, raise); |
2466 | 0 | if (NIL_P(num)) { |
2467 | 0 | if (!raise) return Qnil; |
2468 | 0 | rb_raise(rb_eArgError, "invalid value for convert(): %+"PRIsVALUE, |
2469 | 0 | self); |
2470 | 0 | } |
2471 | | |
2472 | 0 | if (RB_FLOAT_TYPE_P(num) && !FLOAT_ZERO_P(num)) { |
2473 | 0 | if (!raise) return Qnil; |
2474 | 0 | rb_raise(rb_eFloatDomainError, "Infinity"); |
2475 | 0 | } |
2476 | 0 | return num; |
2477 | 0 | } |
2478 | | |
2479 | | /* |
2480 | | * call-seq: |
2481 | | * str.to_r -> rational |
2482 | | * |
2483 | | * Returns the result of interpreting leading characters in +self+ as a rational value: |
2484 | | * |
2485 | | * '123'.to_r # => (123/1) # Integer literal. |
2486 | | * '300/2'.to_r # => (150/1) # Rational literal. |
2487 | | * '-9.2'.to_r # => (-46/5) # Float literal. |
2488 | | * '-9.2e2'.to_r # => (-920/1) # Float literal. |
2489 | | * |
2490 | | * Ignores leading and trailing whitespace, and trailing non-numeric characters: |
2491 | | * |
2492 | | * ' 2 '.to_r # => (2/1) |
2493 | | * '21-Jun-09'.to_r # => (21/1) |
2494 | | * |
2495 | | * Returns \Rational zero if there are no leading numeric characters. |
2496 | | * |
2497 | | * 'BWV 1079'.to_r # => (0/1) |
2498 | | * |
2499 | | * NOTE: <tt>'0.3'.to_r</tt> is equivalent to <tt>3/10r</tt>, |
2500 | | * but is different from <tt>0.3.to_r</tt>: |
2501 | | * |
2502 | | * '0.3'.to_r # => (3/10) |
2503 | | * 3/10r # => (3/10) |
2504 | | * 0.3.to_r # => (5404319552844595/18014398509481984) |
2505 | | * |
2506 | | * Related: see {Converting to Non-String}[rdoc-ref:String@Converting+to+Non--5CString]. |
2507 | | */ |
2508 | | static VALUE |
2509 | | string_to_r(VALUE self) |
2510 | 0 | { |
2511 | 0 | VALUE num; |
2512 | |
|
2513 | 0 | rb_must_asciicompat(self); |
2514 | |
|
2515 | 0 | num = parse_rat(RSTRING_PTR(self), RSTRING_END(self), 0, TRUE); |
2516 | |
|
2517 | 0 | if (RB_FLOAT_TYPE_P(num) && !FLOAT_ZERO_P(num)) |
2518 | 0 | rb_raise(rb_eFloatDomainError, "Infinity"); |
2519 | 0 | return num; |
2520 | 0 | } |
2521 | | |
2522 | | VALUE |
2523 | | rb_cstr_to_rat(const char *s, int strict) /* for complex's internal */ |
2524 | 0 | { |
2525 | 0 | VALUE num; |
2526 | |
|
2527 | 0 | num = parse_rat(s, s + strlen(s), strict, TRUE); |
2528 | |
|
2529 | 0 | if (RB_FLOAT_TYPE_P(num) && !FLOAT_ZERO_P(num)) |
2530 | 0 | rb_raise(rb_eFloatDomainError, "Infinity"); |
2531 | 0 | return num; |
2532 | 0 | } |
2533 | | |
2534 | | static VALUE |
2535 | | to_rational(VALUE val) |
2536 | 0 | { |
2537 | 0 | return rb_convert_type_with_id(val, T_RATIONAL, "Rational", idTo_r); |
2538 | 0 | } |
2539 | | |
2540 | | static VALUE |
2541 | | nurat_convert(VALUE klass, VALUE numv, VALUE denv, int raise) |
2542 | 0 | { |
2543 | 0 | VALUE a1 = numv, a2 = denv; |
2544 | 0 | int state; |
2545 | |
|
2546 | 0 | RUBY_ASSERT(!UNDEF_P(a1)); |
2547 | |
|
2548 | 0 | if (NIL_P(a1) || NIL_P(a2)) { |
2549 | 0 | if (!raise) return Qnil; |
2550 | 0 | rb_cant_convert(Qnil, "Rational"); |
2551 | 0 | } |
2552 | | |
2553 | 0 | if (RB_TYPE_P(a1, T_COMPLEX)) { |
2554 | 0 | if (k_exact_zero_p(RCOMPLEX(a1)->imag)) |
2555 | 0 | a1 = RCOMPLEX(a1)->real; |
2556 | 0 | } |
2557 | |
|
2558 | 0 | if (RB_TYPE_P(a2, T_COMPLEX)) { |
2559 | 0 | if (k_exact_zero_p(RCOMPLEX(a2)->imag)) |
2560 | 0 | a2 = RCOMPLEX(a2)->real; |
2561 | 0 | } |
2562 | |
|
2563 | 0 | if (RB_INTEGER_TYPE_P(a1)) { |
2564 | | // nothing to do |
2565 | 0 | } |
2566 | 0 | else if (RB_FLOAT_TYPE_P(a1)) { |
2567 | 0 | a1 = float_to_r(a1); |
2568 | 0 | } |
2569 | 0 | else if (RB_TYPE_P(a1, T_RATIONAL)) { |
2570 | | // nothing to do |
2571 | 0 | } |
2572 | 0 | else if (RB_TYPE_P(a1, T_STRING)) { |
2573 | 0 | a1 = string_to_r_strict(a1, raise); |
2574 | 0 | if (!raise && NIL_P(a1)) return Qnil; |
2575 | 0 | } |
2576 | 0 | else if (!rb_respond_to(a1, idTo_r)) { |
2577 | 0 | VALUE tmp = rb_protect(rb_check_to_int, a1, NULL); |
2578 | 0 | rb_set_errinfo(Qnil); |
2579 | 0 | if (!NIL_P(tmp)) { |
2580 | 0 | a1 = tmp; |
2581 | 0 | } |
2582 | 0 | } |
2583 | | |
2584 | 0 | if (RB_INTEGER_TYPE_P(a2)) { |
2585 | | // nothing to do |
2586 | 0 | } |
2587 | 0 | else if (RB_FLOAT_TYPE_P(a2)) { |
2588 | 0 | a2 = float_to_r(a2); |
2589 | 0 | } |
2590 | 0 | else if (RB_TYPE_P(a2, T_RATIONAL)) { |
2591 | | // nothing to do |
2592 | 0 | } |
2593 | 0 | else if (RB_TYPE_P(a2, T_STRING)) { |
2594 | 0 | a2 = string_to_r_strict(a2, raise); |
2595 | 0 | if (!raise && NIL_P(a2)) return Qnil; |
2596 | 0 | } |
2597 | 0 | else if (!UNDEF_P(a2) && !rb_respond_to(a2, idTo_r)) { |
2598 | 0 | VALUE tmp = rb_protect(rb_check_to_int, a2, NULL); |
2599 | 0 | rb_set_errinfo(Qnil); |
2600 | 0 | if (!NIL_P(tmp)) { |
2601 | 0 | a2 = tmp; |
2602 | 0 | } |
2603 | 0 | } |
2604 | | |
2605 | 0 | if (RB_TYPE_P(a1, T_RATIONAL)) { |
2606 | 0 | if (UNDEF_P(a2) || (k_exact_one_p(a2))) |
2607 | 0 | return a1; |
2608 | 0 | } |
2609 | | |
2610 | 0 | if (UNDEF_P(a2)) { |
2611 | 0 | if (!RB_INTEGER_TYPE_P(a1)) { |
2612 | 0 | if (!raise) { |
2613 | 0 | VALUE result = rb_protect(to_rational, a1, NULL); |
2614 | 0 | rb_set_errinfo(Qnil); |
2615 | 0 | return result; |
2616 | 0 | } |
2617 | 0 | return to_rational(a1); |
2618 | 0 | } |
2619 | 0 | } |
2620 | 0 | else { |
2621 | 0 | if (!k_numeric_p(a1)) { |
2622 | 0 | if (!raise) { |
2623 | 0 | a1 = rb_protect(to_rational, a1, &state); |
2624 | 0 | if (state) { |
2625 | 0 | rb_set_errinfo(Qnil); |
2626 | 0 | return Qnil; |
2627 | 0 | } |
2628 | 0 | } |
2629 | 0 | else { |
2630 | 0 | a1 = rb_check_convert_type_with_id(a1, T_RATIONAL, "Rational", idTo_r); |
2631 | 0 | } |
2632 | 0 | } |
2633 | 0 | if (!k_numeric_p(a2)) { |
2634 | 0 | if (!raise) { |
2635 | 0 | a2 = rb_protect(to_rational, a2, &state); |
2636 | 0 | if (state) { |
2637 | 0 | rb_set_errinfo(Qnil); |
2638 | 0 | return Qnil; |
2639 | 0 | } |
2640 | 0 | } |
2641 | 0 | else { |
2642 | 0 | a2 = rb_check_convert_type_with_id(a2, T_RATIONAL, "Rational", idTo_r); |
2643 | 0 | } |
2644 | 0 | } |
2645 | 0 | if ((k_numeric_p(a1) && k_numeric_p(a2)) && |
2646 | 0 | (!f_integer_p(a1) || !f_integer_p(a2))) { |
2647 | 0 | VALUE tmp = rb_protect(to_rational, a1, &state); |
2648 | 0 | if (!state) { |
2649 | 0 | a1 = tmp; |
2650 | 0 | } |
2651 | 0 | else { |
2652 | 0 | rb_set_errinfo(Qnil); |
2653 | 0 | } |
2654 | 0 | return f_div(a1, a2); |
2655 | 0 | } |
2656 | 0 | } |
2657 | | |
2658 | 0 | a1 = nurat_int_value(a1); |
2659 | |
|
2660 | 0 | if (UNDEF_P(a2)) { |
2661 | 0 | a2 = ONE; |
2662 | 0 | } |
2663 | 0 | else if (!k_integer_p(a2) && !raise) { |
2664 | 0 | return Qnil; |
2665 | 0 | } |
2666 | 0 | else { |
2667 | 0 | a2 = nurat_int_value(a2); |
2668 | 0 | } |
2669 | | |
2670 | | |
2671 | 0 | return nurat_s_canonicalize_internal(klass, a1, a2); |
2672 | 0 | } |
2673 | | |
2674 | | static VALUE |
2675 | | nurat_s_convert(int argc, VALUE *argv, VALUE klass) |
2676 | 0 | { |
2677 | 0 | VALUE a1, a2; |
2678 | |
|
2679 | 0 | if (rb_scan_args(argc, argv, "11", &a1, &a2) == 1) { |
2680 | 0 | a2 = Qundef; |
2681 | 0 | } |
2682 | |
|
2683 | 0 | return nurat_convert(klass, a1, a2, TRUE); |
2684 | 0 | } |
2685 | | |
2686 | | /* |
2687 | | * A rational number can be represented as a pair of integer numbers: |
2688 | | * a/b (b>0), where a is the numerator and b is the denominator. |
2689 | | * Integer a equals rational a/1 mathematically. |
2690 | | * |
2691 | | * You can create a \Rational object explicitly with: |
2692 | | * |
2693 | | * - A {rational literal}[rdoc-ref:syntax/literals.rdoc@Rational+Literals]. |
2694 | | * |
2695 | | * You can convert certain objects to Rationals with: |
2696 | | * |
2697 | | * - Method #Rational. |
2698 | | * |
2699 | | * Examples |
2700 | | * |
2701 | | * Rational(1) #=> (1/1) |
2702 | | * Rational(2, 3) #=> (2/3) |
2703 | | * Rational(4, -6) #=> (-2/3) # Reduced. |
2704 | | * 3.to_r #=> (3/1) |
2705 | | * 2/3r #=> (2/3) |
2706 | | * |
2707 | | * You can also create rational objects from floating-point numbers or |
2708 | | * strings. |
2709 | | * |
2710 | | * Rational(0.3) #=> (5404319552844595/18014398509481984) |
2711 | | * Rational('0.3') #=> (3/10) |
2712 | | * Rational('2/3') #=> (2/3) |
2713 | | * |
2714 | | * 0.3.to_r #=> (5404319552844595/18014398509481984) |
2715 | | * '0.3'.to_r #=> (3/10) |
2716 | | * '2/3'.to_r #=> (2/3) |
2717 | | * 0.3.rationalize #=> (3/10) |
2718 | | * |
2719 | | * A rational object is an exact number, which helps you to write |
2720 | | * programs without any rounding errors. |
2721 | | * |
2722 | | * 10.times.inject(0) {|t| t + 0.1 } #=> 0.9999999999999999 |
2723 | | * 10.times.inject(0) {|t| t + Rational('0.1') } #=> (1/1) |
2724 | | * |
2725 | | * However, when an expression includes an inexact component (numerical value |
2726 | | * or operation), it will produce an inexact result. |
2727 | | * |
2728 | | * Rational(10) / 3 #=> (10/3) |
2729 | | * Rational(10) / 3.0 #=> 3.3333333333333335 |
2730 | | * |
2731 | | * Rational(-8) ** Rational(1, 3) |
2732 | | * #=> (1.0000000000000002+1.7320508075688772i) |
2733 | | */ |
2734 | | void |
2735 | | Init_Rational(void) |
2736 | 9 | { |
2737 | 9 | VALUE compat; |
2738 | 9 | id_abs = rb_intern_const("abs"); |
2739 | 9 | id_integer_p = rb_intern_const("integer?"); |
2740 | 9 | id_i_num = rb_intern_const("@numerator"); |
2741 | 9 | id_i_den = rb_intern_const("@denominator"); |
2742 | | |
2743 | 9 | rb_cRational = rb_define_class("Rational", rb_cNumeric); |
2744 | | |
2745 | 9 | rb_define_alloc_func(rb_cRational, nurat_s_alloc); |
2746 | 9 | rb_undef_method(CLASS_OF(rb_cRational), "allocate"); |
2747 | | |
2748 | 9 | rb_undef_method(CLASS_OF(rb_cRational), "new"); |
2749 | | |
2750 | 9 | rb_define_global_function("Rational", nurat_f_rational, -1); |
2751 | | |
2752 | 9 | rb_define_method(rb_cRational, "numerator", nurat_numerator, 0); |
2753 | 9 | rb_define_method(rb_cRational, "denominator", nurat_denominator, 0); |
2754 | | |
2755 | 9 | rb_define_method(rb_cRational, "-@", rb_rational_uminus, 0); |
2756 | 9 | rb_define_method(rb_cRational, "+", rb_rational_plus, 1); |
2757 | 9 | rb_define_method(rb_cRational, "-", rb_rational_minus, 1); |
2758 | 9 | rb_define_method(rb_cRational, "*", rb_rational_mul, 1); |
2759 | 9 | rb_define_method(rb_cRational, "/", rb_rational_div, 1); |
2760 | 9 | rb_define_method(rb_cRational, "quo", rb_rational_div, 1); |
2761 | 9 | rb_define_method(rb_cRational, "fdiv", rb_rational_fdiv, 1); |
2762 | 9 | rb_define_method(rb_cRational, "**", nurat_expt, 1); |
2763 | | |
2764 | 9 | rb_define_method(rb_cRational, "<=>", rb_rational_cmp, 1); |
2765 | 9 | rb_define_method(rb_cRational, "==", nurat_eqeq_p, 1); |
2766 | 9 | rb_define_method(rb_cRational, "coerce", nurat_coerce, 1); |
2767 | | |
2768 | 9 | rb_define_method(rb_cRational, "positive?", nurat_positive_p, 0); |
2769 | 9 | rb_define_method(rb_cRational, "negative?", nurat_negative_p, 0); |
2770 | 9 | rb_define_method(rb_cRational, "abs", rb_rational_abs, 0); |
2771 | 9 | rb_define_method(rb_cRational, "magnitude", rb_rational_abs, 0); |
2772 | | |
2773 | 9 | rb_define_method(rb_cRational, "floor", nurat_floor_n, -1); |
2774 | 9 | rb_define_method(rb_cRational, "ceil", nurat_ceil_n, -1); |
2775 | 9 | rb_define_method(rb_cRational, "truncate", nurat_truncate_n, -1); |
2776 | 9 | rb_define_method(rb_cRational, "round", nurat_round_n, -1); |
2777 | | |
2778 | 9 | rb_define_method(rb_cRational, "to_i", nurat_truncate, 0); |
2779 | 9 | rb_define_method(rb_cRational, "to_f", nurat_to_f, 0); |
2780 | 9 | rb_define_method(rb_cRational, "to_r", nurat_to_r, 0); |
2781 | 9 | rb_define_method(rb_cRational, "rationalize", nurat_rationalize, -1); |
2782 | | |
2783 | 9 | rb_define_method(rb_cRational, "hash", nurat_hash, 0); |
2784 | | |
2785 | 9 | rb_define_method(rb_cRational, "to_s", nurat_to_s, 0); |
2786 | 9 | rb_define_method(rb_cRational, "inspect", nurat_inspect, 0); |
2787 | | |
2788 | 9 | rb_define_private_method(rb_cRational, "marshal_dump", nurat_marshal_dump, 0); |
2789 | | /* :nodoc: */ |
2790 | 9 | compat = rb_define_class_under(rb_cRational, "compatible", rb_cObject); |
2791 | 9 | rb_define_private_method(compat, "marshal_load", nurat_marshal_load, 1); |
2792 | 9 | rb_marshal_define_compat(rb_cRational, compat, nurat_dumper, nurat_loader); |
2793 | | |
2794 | 9 | rb_define_method(rb_cInteger, "gcd", rb_gcd, 1); |
2795 | 9 | rb_define_method(rb_cInteger, "lcm", rb_lcm, 1); |
2796 | 9 | rb_define_method(rb_cInteger, "gcdlcm", rb_gcdlcm, 1); |
2797 | | |
2798 | 9 | rb_define_method(rb_cNumeric, "numerator", numeric_numerator, 0); |
2799 | 9 | rb_define_method(rb_cNumeric, "denominator", numeric_denominator, 0); |
2800 | 9 | rb_define_method(rb_cNumeric, "quo", rb_numeric_quo, 1); |
2801 | | |
2802 | 9 | rb_define_method(rb_cFloat, "numerator", rb_float_numerator, 0); |
2803 | 9 | rb_define_method(rb_cFloat, "denominator", rb_float_denominator, 0); |
2804 | | |
2805 | 9 | rb_define_method(rb_cInteger, "to_r", integer_to_r, 0); |
2806 | 9 | rb_define_method(rb_cInteger, "rationalize", integer_rationalize, -1); |
2807 | 9 | rb_define_method(rb_cFloat, "to_r", float_to_r, 0); |
2808 | 9 | rb_define_method(rb_cFloat, "rationalize", float_rationalize, -1); |
2809 | | |
2810 | 9 | rb_define_method(rb_cString, "to_r", string_to_r, 0); |
2811 | | |
2812 | 9 | rb_define_private_method(CLASS_OF(rb_cRational), "convert", nurat_s_convert, -1); |
2813 | | |
2814 | 9 | rb_provide("rational.so"); /* for backward compatibility */ |
2815 | 9 | } |