Files
mruby-mruby/mrbgems/mruby-rational/src/rational.c
T
Yukihiro "Matz" Matsumoto 8956c5abb5 mruby.h: include mruby/presym.h for all source files
Since presym is now mandatory, mruby.h includes presym.h so that
MRB_SYM() macros are available everywhere without explicit include.
Remove redundant #include <mruby/presym.h> from all source files.

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-09 16:50:58 +09:00

1269 lines
32 KiB
C

#include <mruby.h>
#include <mruby/class.h>
#include <mruby/numeric.h>
#include <mruby/internal.h>
#ifndef MRB_NO_FLOAT
#include <math.h>
mrb_value mrb_complex_new(mrb_state *, mrb_float, mrb_float);
#endif
mrb_bool mrb_complex_eq(mrb_state *mrb, mrb_value, mrb_value);
mrb_value mrb_complex_add(mrb_state *mrb, mrb_value, mrb_value);
mrb_value mrb_complex_sub(mrb_state *mrb, mrb_value, mrb_value);
mrb_value mrb_complex_mul(mrb_state *mrb, mrb_value, mrb_value);
mrb_value mrb_complex_div(mrb_state *mrb, mrb_value, mrb_value);
mrb_value mrb_bint_mul_n(mrb_state *mrb, mrb_value x, mrb_value y);
void mrb_bint_reduce(mrb_state *mrb, mrb_value *x, mrb_value *y);
#ifdef MRB_USE_BIGINT
struct mrb_rational {
union {
struct {
mrb_int num;
mrb_int den;
} i;
struct {
struct RBasic *num;
struct RBasic *den;
} b;
};
};
#define numerator i.num
#define denominator i.den
#define RAT_BIGINT 1
#define RAT_BIGINT_P(obj) (mrb_obj_ptr(obj)->flags & RAT_BIGINT)
#else
struct mrb_rational {
mrb_int numerator;
mrb_int denominator;
};
#endif
#define ONE mrb_fixnum_value(1)
#define ZERO mrb_fixnum_value(0)
#if defined(MRB_INT64) && defined(MRB_32BIT)
struct RRational {
MRB_OBJECT_HEADER;
struct mrb_rational *p;
};
static struct mrb_rational*
rat_ptr(mrb_state *mrb, mrb_value v)
{
struct RRational *r = (struct RRational*)mrb_obj_ptr(v);
if (!r->p) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized rational");
}
return r->p;
}
#else
#define RATIONAL_INLINE
struct RRational {
MRB_OBJECT_HEADER;
struct mrb_rational r;
};
#define rat_ptr(mrb, v) (&((struct RRational*)mrb_obj_ptr(v))->r)
#endif
mrb_static_assert_object_size(struct RRational);
static struct mrb_rational*
rat_alloc(mrb_state *mrb, struct RClass *c, struct RBasic **obj)
{
struct RRational *s = MRB_OBJ_ALLOC(mrb, MRB_TT_RATIONAL, c);
struct mrb_rational *p;
#ifdef RATIONAL_INLINE
p = &s->r;
#else
p = s->p = (struct mrb_rational*)mrb_malloc(mrb, sizeof(struct mrb_rational));
#endif
*obj = (struct RBasic*)s;
return p;
}
#ifdef RAT_BIGINT
int
mrb_rational_mark(mrb_state *mrb, struct RBasic *rat)
{
if (!(rat->flags & RAT_BIGINT)) return 0;
mrb_value self = mrb_obj_value(rat);
struct mrb_rational *p = rat_ptr(mrb, self);
mrb_gc_mark(mrb, p->b.num);
mrb_gc_mark(mrb, p->b.den);
return 2;
}
#endif
static mrb_value
rat_numerator(mrb_state *mrb, mrb_value self)
{
struct mrb_rational *p = rat_ptr(mrb, self);
#ifdef RAT_BIGINT
if (RAT_BIGINT_P(self)) {
return mrb_obj_value(p->b.num);
}
#endif
return mrb_int_value(mrb, p->numerator);
}
/*
* call-seq:
* rational.numerator -> integer
*
* Returns the numerator of the rational number.
*
* Rational(3, 4).numerator #=> 3
* Rational(-2, 5).numerator #=> -2
* Rational(6, 8).numerator #=> 3 (reduced form)
*/
/* normalized version of rat_numerator() */
static mrb_value
rational_numerator(mrb_state *mrb, mrb_value self)
{
mrb_value n = rat_numerator(mrb, self);
if (mrb_bigint_p(n)) {
/* normalize bigint */
return mrb_bint_mul(mrb, n, ONE);
}
return n;
}
static mrb_value
rat_denominator(mrb_state *mrb, mrb_value self)
{
struct mrb_rational *p = rat_ptr(mrb, self);
#ifdef RAT_BIGINT
if (RAT_BIGINT_P(self)) {
return mrb_obj_value(p->b.den);
}
#endif
return mrb_int_value(mrb, p->denominator);
}
/*
* call-seq:
* rational.denominator -> integer
*
* Returns the denominator of the rational number.
* The denominator is always positive.
*
* Rational(3, 4).denominator #=> 4
* Rational(-2, 5).denominator #=> 5
* Rational(6, 8).denominator #=> 4 (reduced form)
*/
/* normalized version of rat_denominator() */
static mrb_value
rational_denominator(mrb_state *mrb, mrb_value self)
{
mrb_value n = rat_denominator(mrb, self);
if (mrb_bigint_p(n)) {
/* normalize bigint */
return mrb_bint_mul(mrb, n, ONE);
}
return n;
}
static mrb_noreturn void
rat_overflow(mrb_state *mrb)
{
mrb_raise(mrb, E_RANGE_ERROR, "integer overflow in rational");
}
static mrb_noreturn void
rat_zerodiv(mrb_state *mrb)
{
mrb_raise(mrb, E_ZERODIV_ERROR, "divided by 0 in rational");
}
static mrb_noreturn void
rat_type_error(mrb_state *mrb, mrb_value x)
{
mrb_raisef(mrb, E_TYPE_ERROR, "%T cannot be converted to Rational", x);
}
void
mrb_rational_copy(mrb_state *mrb, mrb_value x, mrb_value y)
{
struct mrb_rational *p1 = rat_ptr(mrb, x);
struct mrb_rational *p2 = rat_ptr(mrb, y);
#ifdef RAT_BIGINT
struct RRational *r = (struct RRational*)mrb_obj_ptr(x);
if (RAT_BIGINT_P(y)) {
p1->b.num = p2->b.num;
p1->b.den = p2->b.den;
r->flags |= RAT_BIGINT;
return;
}
r->flags &= ~RAT_BIGINT;
#endif
p1->numerator = p2->numerator;
p1->denominator = p2->denominator;
}
inline static mrb_int
i_gcd(mrb_int x, mrb_int y)
{
mrb_uint u, v, t;
int shift;
if (x < 0)
x = -x;
if (y < 0)
y = -y;
if (x == 0)
return y;
if (y == 0)
return x;
u = (mrb_uint)x;
v = (mrb_uint)y;
for (shift = 0; ((u | v) & 1) == 0; shift++) {
u >>= 1;
v >>= 1;
}
while ((u & 1) == 0)
u >>= 1;
do {
while ((v & 1) == 0)
v >>= 1;
if (u > v) {
t = v;
v = u;
u = t;
}
v = v - u;
} while (v != 0);
return (mrb_int)(u << shift);
}
#ifdef RAT_BIGINT
static mrb_value
rational_new_b(mrb_state *mrb, mrb_value n, mrb_value d)
{
/* bigint check */
mrb_assert(mrb_bigint_p(n));
d = mrb_as_bint(mrb, d);
mrb_int cmp = mrb_bint_cmp(mrb, d, ZERO);
if (cmp == 0) {
rat_zerodiv(mrb);
}
/* negative */
if (cmp < 0) {
n = mrb_bint_neg(mrb, n);
d = mrb_bint_neg(mrb, d);
}
/* normalize (n/gcd, d/gcd) */
mrb_bint_reduce(mrb, &n, &d);
struct RClass *c = mrb_class_get_id(mrb, MRB_SYM(Rational));
struct RBasic *rat;
struct mrb_rational *p = rat_alloc(mrb, c, &rat);
rat->flags |= RAT_BIGINT;
p->b.num = (struct RBasic*)mrb_obj_ptr(n);
p->b.den = (struct RBasic*)mrb_obj_ptr(d);
rat->frozen = 1;
return mrb_obj_value(rat);
}
#endif
mrb_value
mrb_rational_new(mrb_state *mrb, mrb_int nume, mrb_int deno)
{
if (deno == 0) {
rat_zerodiv(mrb);
}
if (nume == MRB_INT_MIN || deno == MRB_INT_MIN) {
#ifdef RAT_BIGINT
mrb_value num = mrb_as_bint(mrb, mrb_int_value(mrb, nume));
mrb_value den = mrb_as_bint(mrb, mrb_int_value(mrb, deno));
return rational_new_b(mrb, num, den);
#else
rat_overflow(mrb);
#endif
}
if (deno < 0) {
nume *= -1;
deno *= -1;
}
mrb_int a = i_gcd(nume, deno);
nume /= a;
deno /= a;
struct RClass *c = mrb_class_get_id(mrb, MRB_SYM(Rational));
struct RBasic *rat;
struct mrb_rational *p = rat_alloc(mrb, c, &rat);
p->numerator = nume;
p->denominator = deno;
rat->frozen = 1;
return mrb_obj_value(rat);
}
#define rational_new_i(mrb,n,d) mrb_rational_new(mrb, n, d)
#ifndef MRB_NO_FLOAT
#if defined(MRB_INT32) || defined(MRB_USE_FLOAT32)
#define frexp_rat(x,exp) frexpf((float)x, exp)
#define ldexp_rat(x,exp) ldexpf((float)x, exp)
#define RAT_MANT_DIG FLT_MANT_DIG
#define RAT_INT_LIMIT 30
#define RAT_HUGE_VAL HUGE_VALF
#else
#define frexp_rat frexp
#define ldexp_rat ldexp
#define RAT_MANT_DIG DBL_MANT_DIG
#define RAT_INT_LIMIT 62
#define RAT_HUGE_VAL HUGE_VAL
#endif
static mrb_value
rational_new_f(mrb_state *mrb, mrb_float f)
{
mrb_check_num_exact(mrb, f);
if (f == 0.0) {
return rational_new_i(mrb, 0, 1);
}
int exp;
// Extract mantissa and exponent
double mantissa = frexp_rat(f, &exp);
const mrb_int precision = ((mrb_int)1) << RAT_MANT_DIG;
mrb_int nume = (mrb_int)(mantissa * precision);
mrb_int deno = precision;
if (exp > 0) {
mrb_int temp;
/* Check exp < MRB_INT_BIT-1 to avoid undefined behavior from shifting into sign bit */
if (exp >= MRB_INT_BIT - 1 || mrb_int_mul_overflow(nume, ((mrb_int)1)<<exp, &temp)) {
#ifndef RAT_BIGINT
rat_overflow(mrb);
#else
mrb_value n = mrb_bint_lshift(mrb, mrb_bint_new_int(mrb, nume), exp);
return rational_new_b(mrb, n, mrb_int_value(mrb, deno));
#endif
}
nume = temp;
}
else if (exp < 0) {
/* exp is negative, so we need to multiply denominator by 2^(-exp) */
int neg_exp = -exp;
if (neg_exp >= MRB_INT_BIT - 1 || mrb_int_mul_overflow(deno, ((mrb_int)1)<<neg_exp, &deno)) {
#ifndef RAT_BIGINT
rat_overflow(mrb);
#else
mrb_value d = mrb_bint_lshift(mrb, mrb_bint_new_int(mrb, deno), neg_exp);
return rational_new_b(mrb, mrb_bint_new_int(mrb, nume), d);
#endif
}
}
return rational_new_i(mrb, nume, deno);
}
static mrb_float
rat_float(mrb_state *mrb, mrb_value x)
{
struct mrb_rational *p = rat_ptr(mrb, x);
#ifdef RAT_BIGINT
if (RAT_BIGINT_P(x)) {
return mrb_bint_as_float(mrb, mrb_obj_value(p->b.num)) / mrb_bint_as_float(mrb, mrb_obj_value(p->b.den));
}
#endif
return (mrb_float)p->numerator / (mrb_float)p->denominator;
}
mrb_value
mrb_rational_to_f(mrb_state *mrb, mrb_value self)
{
mrb_float f = rat_float(mrb, self);
return mrb_float_value(mrb, f);
}
#endif
/*
* call-seq:
* rational.to_i -> integer
*
* Returns the rational number truncated to an integer.
*
* Rational(3, 4).to_i #=> 0
* Rational(7, 3).to_i #=> 2
* Rational(-5, 2).to_i #=> -2
*/
mrb_value
mrb_rational_to_i(mrb_state *mrb, mrb_value self)
{
struct mrb_rational *p = rat_ptr(mrb, self);
#ifdef RAT_BIGINT
if (RAT_BIGINT_P(self)) {
return mrb_bint_div(mrb, mrb_obj_value(p->b.num), mrb_obj_value(p->b.den));
}
#endif
return mrb_int_value(mrb, p->numerator / p->denominator);
}
mrb_value
mrb_as_rational(mrb_state *mrb, mrb_value x)
{
switch(mrb_type(x)) {
case MRB_TT_INTEGER:
return rational_new_i(mrb, mrb_integer(x), 1);
#ifdef RAT_BIGINT
case MRB_TT_BIGINT:
return rational_new_b(mrb, x, ONE);
#endif
case MRB_TT_RATIONAL:
return x;
#ifndef MRB_NO_FLOAT
#ifdef MRB_USE_COMPLEX
case MRB_TT_COMPLEX:
#endif
case MRB_TT_FLOAT:
return rational_new_f(mrb, mrb_as_float(mrb, x));
#endif
default:
rat_type_error(mrb, x);
}
}
/*
* call-seq:
* rational.negative? -> true or false
*
* Returns true if the rational number is negative, false otherwise.
*
* Rational(-1, 2).negative? #=> true
* Rational(1, 2).negative? #=> false
* Rational(0, 1).negative? #=> false
*/
static mrb_value
rational_negative_p(mrb_state *mrb, mrb_value self)
{
struct mrb_rational *p = rat_ptr(mrb, self);
#ifdef RAT_BIGINT
if (RAT_BIGINT_P(self)) {
mrb_int cmp = mrb_bint_cmp(mrb, mrb_obj_value(p->b.num), ZERO);
return mrb_bool_value(cmp < 0);
}
#endif
return mrb_bool_value(p->numerator < 0);
}
#ifndef MRB_NO_FLOAT
/*
* call-seq:
* float.to_r -> rational
*
* Converts the float to a rational number. The conversion preserves
* the exact value of the float as a fraction.
*
* 0.5.to_r #=> Rational(1, 2)
* 0.25.to_r #=> Rational(1, 4)
* 1.5.to_r #=> Rational(3, 2)
*/
static mrb_value
float_to_r(mrb_state *mrb, mrb_value self)
{
return rational_new_f(mrb, mrb_float(self));
}
#endif
/*
* call-seq:
* integer.to_r -> rational
*
* Converts the integer to a rational number with denominator 1.
*
* 5.to_r #=> Rational(5, 1)
* (-3).to_r #=> Rational(-3, 1)
* 0.to_r #=> Rational(0, 1)
*/
static mrb_value
int_to_r(mrb_state *mrb, mrb_value self)
{
#ifdef RAT_BIGINT
if (mrb_bigint_p(self)) {
return rational_new_b(mrb, self, ONE);
}
#endif
return rational_new_i(mrb, mrb_integer(self), 1);
}
/*
* call-seq:
* nil.to_r -> rational
*
* Converts nil to Rational(0, 1).
*
* nil.to_r #=> Rational(0, 1)
*/
static mrb_value
nil_to_r(mrb_state *mrb, mrb_value self)
{
return rational_new_i(mrb, 0, 1);
}
#if !defined(MRB_NO_FLOAT) || defined(RAT_BIGINT)
static mrb_value
rational_new(mrb_state *mrb, mrb_value a, mrb_value b)
{
#ifdef MRB_NO_FLOAT
a = mrb_as_int(mrb, a);
b = mrb_as_int(mrb, b);
return rational_new_i(mrb, mrb_integer(a), mrb_integer(b));
#else
if (mrb_integer_p(a) && mrb_integer_p(b)) {
return rational_new_i(mrb, mrb_integer(a), mrb_integer(b));
}
#ifdef RAT_BIGINT
else if (mrb_bigint_p(a) || mrb_bigint_p(b)) {
return rational_new_b(mrb, mrb_as_bint(mrb, a), b);
}
#endif
else {
mrb_float x = mrb_as_float(mrb, a);
mrb_float y = mrb_as_float(mrb, b);
return rational_new_f(mrb, x/y);
}
#endif
}
/*
* call-seq:
* Rational(numerator, denominator = 1) -> rational
*
* Creates a rational number from numerator and denominator.
* The rational is automatically reduced to lowest terms.
*
* Rational(1, 2) #=> Rational(1, 2)
* Rational(6, 8) #=> Rational(3, 4)
* Rational(5) #=> Rational(5, 1)
* Rational(-2, 4) #=> Rational(-1, 2)
*/
static mrb_value
rational_m(mrb_state *mrb, mrb_value self)
{
mrb_value a, b = ONE;
mrb_get_args(mrb, "o|o", &a, &b);
return rational_new(mrb, a, b);
}
#else
/*
* call-seq:
* Rational(numerator, denominator = 1) -> rational
*
* Creates a rational number from numerator and denominator.
* The rational is automatically reduced to lowest terms.
*
* Rational(1, 2) #=> Rational(1, 2)
* Rational(6, 8) #=> Rational(3, 4)
* Rational(5) #=> Rational(5, 1)
* Rational(-2, 4) #=> Rational(-1, 2)
*/
static mrb_value
rational_m(mrb_state *mrb, mrb_value self)
{
mrb_int n, d = 1;
mrb_get_args(mrb, "i|i", &n, &d);
return rational_new_i(mrb, n, d);
}
#endif
static mrb_value
rational_eq_b(mrb_state *mrb, mrb_value x, mrb_value y)
{
struct mrb_rational *p1 = rat_ptr(mrb, x);
mrb_bool result;
switch (mrb_type(y)) {
case MRB_TT_INTEGER:
{
/* For bigint-backed rationals, check if denominator is 1 */
mrb_value den = mrb_obj_value(p1->b.den);
mrb_int den_cmp = mrb_bint_cmp(mrb, den, mrb_int_value(mrb, 1));
if (den_cmp != 0) return mrb_false_value();
mrb_value num = mrb_obj_value(p1->b.num);
result = mrb_bint_cmp(mrb, num, y) == 0;
break;
}
#ifdef MRB_USE_BIGINT
case MRB_TT_BIGINT:
{
/* For bigint-backed rationals comparing with bigint */
mrb_value den = mrb_obj_value(p1->b.den);
mrb_int den_cmp = mrb_bint_cmp(mrb, den, mrb_int_value(mrb, 1));
if (den_cmp != 0) return mrb_false_value();
mrb_value num = mrb_obj_value(p1->b.num);
result = mrb_bint_cmp(mrb, num, y) == 0;
break;
}
#endif
#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
{
/* For bigint-backed rationals, convert to float and compare */
mrb_float num_f = mrb_bint_as_float(mrb, mrb_obj_value(p1->b.num));
mrb_float den_f = mrb_bint_as_float(mrb, mrb_obj_value(p1->b.den));
result = (num_f / den_f) == mrb_float(y);
break;
}
#endif
case MRB_TT_RATIONAL:
{
/* Compare by converting to float - less precise but safe */
mrb_float v1 = mrb_bint_as_float(mrb, mrb_obj_value(p1->b.num)) /
mrb_bint_as_float(mrb, mrb_obj_value(p1->b.den));
mrb_float v2 = rat_float(mrb, y);
result = v1 == v2;
break;
}
#ifdef MRB_USE_COMPLEX
case MRB_TT_COMPLEX:
{
result = mrb_complex_eq(mrb, y, mrb_rational_to_f(mrb, x));
break;
}
#endif
default:
result = mrb_equal(mrb, y, x);
break;
}
return mrb_bool_value(result);
}
/*
* call-seq:
* rational == other -> true or false
*
* Returns true if rational equals other. Comparison is done by cross-multiplication
* to avoid floating point precision issues.
*
* Rational(1, 2) == Rational(2, 4) #=> true
* Rational(1, 2) == 0.5 #=> true
* Rational(1, 2) == Rational(1, 3) #=> false
*/
static mrb_value
rational_eq(mrb_state *mrb, mrb_value x)
{
mrb_value y = mrb_get_arg1(mrb);
#ifdef RAT_BIGINT
if (RAT_BIGINT_P(x)) return rational_eq_b(mrb, x, y);
#endif
struct mrb_rational *p1 = rat_ptr(mrb, x);
mrb_bool result;
switch (mrb_type(y)) {
case MRB_TT_INTEGER:
if (p1->denominator != 1) return mrb_false_value();
result = p1->numerator == mrb_integer(y);
break;
#ifdef MRB_USE_BIGINT
case MRB_TT_BIGINT:
/* Non-bigint rational comparing with bigint */
if (p1->denominator != 1) return mrb_false_value();
result = mrb_bint_cmp(mrb, y, mrb_int_value(mrb, p1->numerator)) == 0;
break;
#endif
#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
result = ((double)p1->numerator/p1->denominator) == mrb_float(y);
break;
#endif
case MRB_TT_RATIONAL:
{
struct mrb_rational *p2 = rat_ptr(mrb, y);
mrb_int a, b;
if (p1->numerator == p2->numerator && p1->denominator == p2->denominator) {
return mrb_true_value();
}
if (mrb_int_mul_overflow(p1->numerator, p2->denominator, &a) ||
mrb_int_mul_overflow(p2->numerator, p1->denominator, &b)) {
#ifdef MRB_NO_FLOAT
rat_overflow(mrb);
#else
result = (double)p1->numerator*p2->denominator == (double)p2->numerator*p2->denominator;
break;
#endif
}
result = a == b;
break;
}
#ifdef MRB_USE_COMPLEX
case MRB_TT_COMPLEX:
{
result = mrb_complex_eq(mrb, y, mrb_rational_to_f(mrb, x));
break;
}
#endif
default:
result = mrb_equal(mrb, y, x);
break;
}
return mrb_bool_value(result);
}
/*
* call-seq:
* -rational -> rational
*
* Returns the negation of the rational number.
*
* -Rational(1, 2) #=> Rational(-1, 2)
* -Rational(-3, 4) #=> Rational(3, 4)
*/
static mrb_value
rational_minus(mrb_state *mrb, mrb_value x)
{
struct mrb_rational *p = rat_ptr(mrb, x);
#ifdef RAT_BIGINT
mrb_value num;
if (RAT_BIGINT_P(x)) {
num = mrb_obj_value(p->b.num);
bint:
return rational_new_b(mrb, mrb_bint_neg(mrb, num), mrb_obj_value(p->b.den));
}
#endif
mrb_int n = p->numerator;
if (n == MRB_INT_MIN) {
#ifdef RAT_BIGINT
num = mrb_as_bint(mrb, mrb_int_value(mrb, p->numerator));
goto bint;
#else
rat_overflow(mrb);
#endif
}
return rational_new_i(mrb, -n, p->denominator);
}
#ifdef RAT_BIGINT
static mrb_value
rat_add_b(mrb_state *mrb, mrb_value x, mrb_value y)
{
mrb_value num1 = rat_numerator(mrb, x);
mrb_value den1 = rat_denominator(mrb, x);
mrb_value num2, den2;
switch(mrb_type(y)) {
case MRB_TT_RATIONAL:
num2 = rat_numerator(mrb, y);
den2 = rat_denominator(mrb, y);
break;
case MRB_TT_INTEGER:
case MRB_TT_BIGINT:
num2 = y;
den2 = ONE;
break;
default:
/* should not happen */
rat_type_error(mrb, y);
}
mrb_value a = mrb_bint_mul_n(mrb, mrb_as_bint(mrb, num1), den2);
mrb_value b = mrb_bint_mul_n(mrb, mrb_as_bint(mrb, num2), den1);
a = mrb_bint_add_n(mrb, a, b);
b = mrb_bint_mul_n(mrb, mrb_as_bint(mrb, den1), den2);
return rational_new_b(mrb, a, b);
}
#endif
mrb_value
mrb_rational_add(mrb_state *mrb, mrb_value x, mrb_value y)
{
struct mrb_rational *p1 = rat_ptr(mrb, x);
switch (mrb_type(y)) {
case MRB_TT_INTEGER:
#ifdef RAT_BIGINT
if (RAT_BIGINT_P(x)) return rat_add_b(mrb, x, y);
#endif
{
mrb_int z = mrb_integer(y);
if (mrb_int_mul_overflow(z, p1->denominator, &z)) rat_overflow(mrb);
if (mrb_int_add_overflow(p1->numerator, z, &z)) rat_overflow(mrb);
return rational_new_i(mrb, z, p1->denominator);
}
case MRB_TT_RATIONAL:
#ifdef RAT_BIGINT
if (RAT_BIGINT_P(x) || RAT_BIGINT_P(y))
return rat_add_b(mrb, x, y);
#endif
{
struct mrb_rational *p2 = rat_ptr(mrb, y);
mrb_int a, b;
if (mrb_int_mul_overflow(p1->numerator, p2->denominator, &a)) rat_overflow(mrb);
if (mrb_int_mul_overflow(p2->numerator, p1->denominator, &b)) rat_overflow(mrb);
if (mrb_int_add_overflow(a, b, &a)) rat_overflow(mrb);
if (mrb_int_mul_overflow(p1->denominator, p2->denominator, &b)) rat_overflow(mrb);
return rational_new_i(mrb, a, b);
}
#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
{
mrb_float z = p1->numerator + mrb_float(y) * p1->denominator;
return mrb_float_value(mrb, mrb_div_float(z, (mrb_float)p1->denominator));
}
#endif
#ifdef RAT_BIGINT
case MRB_TT_BIGINT:
return rat_add_b(mrb, x, y);
#endif
#if defined(MRB_USE_COMPLEX)
case MRB_TT_COMPLEX:
return mrb_complex_add(mrb, mrb_complex_new(mrb, rat_float(mrb, x), 0), y);
#endif
default:
return mrb_funcall_argv(mrb, y, MRB_OPSYM(add), 1, &x);
}
}
/*
* call-seq:
* rational + numeric -> rational or numeric
*
* Returns the sum of rational and numeric. If numeric is a rational,
* returns a rational. If numeric is a float, returns a float.
*
* Rational(1, 2) + Rational(1, 3) #=> Rational(5, 6)
* Rational(1, 2) + 1 #=> Rational(3, 2)
* Rational(1, 2) + 0.5 #=> 1.0
*/
static mrb_value
rational_add(mrb_state *mrb, mrb_value x)
{
mrb_value y = mrb_get_arg1(mrb);
return mrb_rational_add(mrb, x, y);
}
#ifdef RAT_BIGINT
static mrb_value
rat_sub_b(mrb_state *mrb, mrb_value x, mrb_value y)
{
mrb_value num1 = rat_numerator(mrb, x);
mrb_value den1 = rat_denominator(mrb, x);
mrb_value num2, den2;
switch(mrb_type(y)) {
case MRB_TT_RATIONAL:
num2 = rat_numerator(mrb, y);
den2 = rat_denominator(mrb, y);
break;
case MRB_TT_INTEGER:
case MRB_TT_BIGINT:
num2 = y;
den2 = ONE;
break;
default:
/* should not happen */
rat_type_error(mrb, y);
}
mrb_value a = mrb_bint_mul_n(mrb, mrb_as_bint(mrb, num1), den2);
mrb_value b = mrb_bint_mul_n(mrb, mrb_as_bint(mrb, num2), den1);
a = mrb_bint_sub_n(mrb, a, b);
b = mrb_bint_mul_n(mrb, mrb_as_bint(mrb, den1), den2);
return rational_new_b(mrb, a, b);
}
#endif
mrb_value
mrb_rational_sub(mrb_state *mrb, mrb_value x, mrb_value y)
{
struct mrb_rational *p1 = rat_ptr(mrb, x);
switch (mrb_type(y)) {
case MRB_TT_INTEGER:
#ifdef RAT_BIGINT
if (RAT_BIGINT_P(x)) return rat_sub_b(mrb, x, y);
#endif
{
mrb_int z = mrb_integer(y);
if (mrb_int_mul_overflow(z, p1->denominator, &z)) rat_overflow(mrb);
if (mrb_int_sub_overflow(p1->numerator, z, &z)) rat_overflow(mrb);
return rational_new_i(mrb, z, p1->denominator);
}
case MRB_TT_RATIONAL:
#ifdef RAT_BIGINT
if (RAT_BIGINT_P(x) || RAT_BIGINT_P(y))
return rat_sub_b(mrb, x, y);
#endif
{
struct mrb_rational *p2 = rat_ptr(mrb, y);
mrb_int a, b;
if (mrb_int_mul_overflow(p1->numerator, p2->denominator, &a)) rat_overflow(mrb);
if (mrb_int_mul_overflow(p2->numerator, p1->denominator, &b)) rat_overflow(mrb);
if (mrb_int_sub_overflow(a, b, &a)) rat_overflow(mrb);
if (mrb_int_mul_overflow(p1->denominator, p2->denominator, &b)) rat_overflow(mrb);
return rational_new_i(mrb, a, b);
}
#ifdef RAT_BIGINT
case MRB_TT_BIGINT:
return rat_sub_b(mrb, x, y);
#endif
#if defined(MRB_USE_COMPLEX)
case MRB_TT_COMPLEX:
return mrb_complex_sub(mrb, mrb_complex_new(mrb, rat_float(mrb, x), 0), y);
#endif
#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
default:
{
mrb_float z = p1->numerator - mrb_as_float(mrb, y) * p1->denominator;
return mrb_float_value(mrb, mrb_div_float(z, (mrb_float)p1->denominator));
}
#else
default:
rat_type_error(mrb, y);
#endif
}
}
/*
* call-seq:
* rational - numeric -> rational or numeric
*
* Returns the difference of rational and numeric. If numeric is a rational,
* returns a rational. If numeric is a float, returns a float.
*
* Rational(1, 2) - Rational(1, 3) #=> Rational(1, 6)
* Rational(3, 2) - 1 #=> Rational(1, 2)
* Rational(1, 2) - 0.25 #=> 0.25
*/
static mrb_value
rational_sub(mrb_state *mrb, mrb_value x)
{
mrb_value y = mrb_get_arg1(mrb);
return mrb_rational_sub(mrb, x, y);
}
#ifdef RAT_BIGINT
static mrb_value
rat_mul_b(mrb_state *mrb, mrb_value x, mrb_value y)
{
mrb_value num, den;
switch(mrb_type(y)) {
case MRB_TT_RATIONAL:
num = rat_numerator(mrb, y);
den = rat_denominator(mrb, y);
break;
case MRB_TT_INTEGER:
case MRB_TT_BIGINT:
num = y;
den = ONE;
break;
default:
/* should not happen */
rat_type_error(mrb, y);
}
mrb_value a = mrb_bint_mul_n(mrb, mrb_as_bint(mrb, rat_numerator(mrb, x)), num);
mrb_value b = mrb_bint_mul_n(mrb, mrb_as_bint(mrb, rat_denominator(mrb, x)), den);
return rational_new_b(mrb, a, b);
}
#endif
mrb_value
mrb_rational_mul(mrb_state *mrb, mrb_value x, mrb_value y)
{
switch (mrb_type(y)) {
case MRB_TT_INTEGER:
#ifdef RAT_BIGINT
if (RAT_BIGINT_P(x)) return rat_mul_b(mrb, x, y);
#endif
{
struct mrb_rational *p1 = rat_ptr(mrb, x);
mrb_int z = mrb_integer(y);
if (mrb_int_mul_overflow(p1->numerator, z, &z)) rat_overflow(mrb);
return rational_new_i(mrb, z, p1->denominator);
}
case MRB_TT_RATIONAL:
#ifdef RAT_BIGINT
if (RAT_BIGINT_P(x) || RAT_BIGINT_P(y))
return rat_mul_b(mrb, x, y);
#endif
{
struct mrb_rational *p1 = rat_ptr(mrb, x);
struct mrb_rational *p2 = rat_ptr(mrb, y);
mrb_int a, b;
if (mrb_int_mul_overflow(p1->numerator, p2->numerator, &a)) rat_overflow(mrb);
if (mrb_int_mul_overflow(p1->denominator, p2->denominator, &b)) rat_overflow(mrb);
return rational_new_i(mrb, a, b);
}
#ifdef RAT_BIGINT
case MRB_TT_BIGINT:
return rat_mul_b(mrb, x, y);
#endif
#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
{
struct mrb_rational *p1 = rat_ptr(mrb, x);
mrb_float z = p1->numerator * mrb_float(y);
return mrb_float_value(mrb, mrb_div_float(z, (mrb_float)p1->denominator));
}
#endif
#if defined(MRB_USE_COMPLEX)
case MRB_TT_COMPLEX:
return mrb_complex_mul(mrb, mrb_complex_new(mrb, rat_float(mrb, x), 0), y);
#endif
default:
return mrb_funcall_argv(mrb, y, MRB_OPSYM(mul), 1, &x);
}
}
/*
* call-seq:
* rational * numeric -> rational or numeric
*
* Returns the product of rational and numeric. Uses standard rational
* multiplication: (a/b) * (c/d) = (a*c)/(b*d).
*
* Rational(1, 2) * Rational(2, 3) #=> Rational(1, 3)
* Rational(1, 2) * 3 #=> Rational(3, 2)
* Rational(1, 2) * 2.0 #=> 1.0
*/
static mrb_value
rational_mul(mrb_state *mrb, mrb_value x)
{
mrb_value y = mrb_get_arg1(mrb);
return mrb_rational_mul(mrb, x, y);
}
#ifdef RAT_BIGINT
static mrb_value
rat_div_b(mrb_state *mrb, mrb_value x, mrb_value y)
{
mrb_value num, den;
switch(mrb_type(y)) {
case MRB_TT_RATIONAL:
num = rat_numerator(mrb, y);
den = rat_denominator(mrb, y);
break;
case MRB_TT_INTEGER:
#ifdef MRB_USE_BIGINT
case MRB_TT_BIGINT:
#endif
num = y;
den = ONE;
break;
default:
/* should not happen */
rat_type_error(mrb, y);
}
mrb_value a = mrb_bint_mul_n(mrb, mrb_as_bint(mrb, rat_numerator(mrb, x)), den);
mrb_value b = mrb_bint_mul_n(mrb, mrb_as_bint(mrb, rat_denominator(mrb, x)), num);
return rational_new_b(mrb, a, b);
}
#endif
mrb_value
mrb_rational_div(mrb_state *mrb, mrb_value x, mrb_value y)
{
switch (mrb_type(y)) {
case MRB_TT_INTEGER:
#ifdef RAT_BIGINT
if (RAT_BIGINT_P(x)) return rat_div_b(mrb, x, y);
#endif
{
struct mrb_rational *p1 = rat_ptr(mrb, x);
mrb_int z = mrb_integer(y);
if (z == 0) mrb_int_zerodiv(mrb);
if (mrb_int_mul_overflow(p1->denominator, z, &z)) rat_overflow(mrb);
return rational_new_i(mrb, p1->numerator, z);
}
case MRB_TT_RATIONAL:
#ifdef RAT_BIGINT
if (RAT_BIGINT_P(x) || RAT_BIGINT_P(y)) return rat_div_b(mrb, x, y);
#endif
{
struct mrb_rational *p1 = rat_ptr(mrb, x);
struct mrb_rational *p2 = rat_ptr(mrb, y);
mrb_int a, b;
if (mrb_int_mul_overflow(p1->numerator, p2->denominator, &a)) rat_overflow(mrb);
if (mrb_int_mul_overflow(p2->numerator, p1->denominator, &b)) rat_overflow(mrb);
return rational_new_i(mrb, a, b);
}
#ifdef RAT_BIGINT
case MRB_TT_BIGINT:
return rat_div_b(mrb, x, y);
#endif
#ifdef MRB_USE_COMPLEX
case MRB_TT_COMPLEX:
return mrb_complex_div(mrb, mrb_complex_new(mrb, rat_float(mrb, x), 0), y);
#endif
#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
{
struct mrb_rational *p1 = rat_ptr(mrb, x);
mrb_float z = mrb_div_float((mrb_float)p1->numerator, mrb_as_float(mrb, y));
return mrb_float_value(mrb, mrb_div_float(z, (mrb_float)p1->denominator));
}
#endif
default:
rat_type_error(mrb, y);
/* not reached */
return mrb_nil_value();
}
}
/*
* call-seq:
* rational / numeric -> rational or numeric
* rational.quo(numeric) -> rational or numeric
*
* Returns the quotient of rational divided by numeric. Uses standard rational
* division: (a/b) / (c/d) = (a/b) * (d/c) = (a*d)/(b*c).
*
* Rational(1, 2) / Rational(1, 3) #=> Rational(3, 2)
* Rational(3, 4) / 2 #=> Rational(3, 8)
* Rational(1, 2) / 0.5 #=> 1.0
*/
static mrb_value
rational_div(mrb_state *mrb, mrb_value x)
{
mrb_value y = mrb_get_arg1(mrb);
return mrb_rational_div(mrb, x, y);
}
mrb_value mrb_int_pow(mrb_state *mrb, mrb_value x, mrb_value y);
/*
* call-seq:
* rational ** numeric -> numeric
*
* Returns rational raised to the power of numeric. The result is typically
* a float unless the result can be exactly represented as a rational.
*
* Rational(1, 2) ** 2 #=> Rational(1, 4)
* Rational(4, 1) ** 0.5 #=> 2.0
* Rational(2, 1) ** 3 #=> Rational(8, 1)
*/
static mrb_value
rational_pow(mrb_state *mrb, mrb_value x)
{
#ifndef MRB_NO_FLOAT
mrb_value y = mrb_get_arg1(mrb);
double d1 = rat_float(mrb, x);
double d2 = mrb_as_float(mrb, y);
d1 = pow(d1, d2);
switch (mrb_type(y)) {
case MRB_TT_FLOAT:
return mrb_float_value(mrb, d1);
case MRB_TT_INTEGER:
case MRB_TT_RATIONAL:
return rational_new_f(mrb, d1);
case MRB_TT_BIGINT:
default:
return mrb_float_value(mrb, d1);
}
#else
mrb_raisef(mrb, E_NOTIMP_ERROR, "Rational#** not implemented with MRB_NO_FLOAT");
/* not reached */
return mrb_nil_value();
#endif
}
/*
* call-seq:
* rational.hash -> integer
*
* Returns a hash value for the rational number. Two rationals with
* the same value will have the same hash value.
*
* Rational(1, 2).hash == Rational(2, 4).hash #=> true
*/
static mrb_value
rational_hash(mrb_state *mrb, mrb_value rat)
{
struct mrb_rational *r = rat_ptr(mrb, rat);
uint32_t hash;
#ifdef RAT_BIGINT
if (RAT_BIGINT_P(rat)) {
mrb_value tmp = mrb_bint_hash(mrb, mrb_obj_value(r->b.num));
hash = (uint32_t)mrb_integer(tmp);
tmp = mrb_bint_hash(mrb, mrb_obj_value(r->b.den));
hash ^= (uint32_t)mrb_integer(tmp);
return mrb_int_value(mrb, hash);
}
#endif
hash = mrb_byte_hash((uint8_t*)&r->numerator, sizeof(mrb_int));
hash = mrb_byte_hash_step((uint8_t*)&r->denominator, sizeof(mrb_int), hash);
return mrb_int_value(mrb, hash);
}
/* ---------------------------*/
static const mrb_mt_entry rational_rom_entries[] = {
MRB_MT_ENTRY(rational_numerator, MRB_SYM(numerator), MRB_ARGS_NONE()),
MRB_MT_ENTRY(rational_denominator, MRB_SYM(denominator), MRB_ARGS_NONE()),
MRB_MT_ENTRY(mrb_rational_to_i, MRB_SYM(to_i), MRB_ARGS_NONE()),
MRB_MT_ENTRY(mrb_obj_itself, MRB_SYM(to_r), MRB_ARGS_NONE()), /* Returns self - already a rational */
MRB_MT_ENTRY(rational_negative_p, MRB_SYM_Q(negative), MRB_ARGS_NONE()),
MRB_MT_ENTRY(rational_eq, MRB_OPSYM(eq), MRB_ARGS_REQ(1)),
MRB_MT_ENTRY(rational_minus, MRB_OPSYM(minus), MRB_ARGS_NONE()),
MRB_MT_ENTRY(rational_add, MRB_OPSYM(add), MRB_ARGS_REQ(1)),
MRB_MT_ENTRY(rational_sub, MRB_OPSYM(sub), MRB_ARGS_REQ(1)),
MRB_MT_ENTRY(rational_mul, MRB_OPSYM(mul), MRB_ARGS_REQ(1)),
MRB_MT_ENTRY(rational_div, MRB_OPSYM(div), MRB_ARGS_REQ(1)),
MRB_MT_ENTRY(rational_div, MRB_SYM(quo), MRB_ARGS_REQ(1)),
MRB_MT_ENTRY(rational_pow, MRB_OPSYM(pow), MRB_ARGS_REQ(1)),
MRB_MT_ENTRY(rational_hash, MRB_SYM(hash), MRB_ARGS_NONE()),
#ifndef MRB_NO_FLOAT
MRB_MT_ENTRY(mrb_rational_to_f, MRB_SYM(to_f), MRB_ARGS_NONE()),
#endif
};
void mrb_mruby_rational_gem_init(mrb_state *mrb)
{
struct RClass *rat = mrb_define_class_id(mrb, MRB_SYM(Rational), mrb_class_get_id(mrb, MRB_SYM(Numeric)));
MRB_SET_INSTANCE_TT(rat, MRB_TT_RATIONAL);
MRB_UNDEF_ALLOCATOR(rat);
mrb_undef_class_method_id(mrb, rat, MRB_SYM(new));
MRB_MT_INIT_ROM(mrb, rat, rational_rom_entries);
mrb_define_method_id(mrb, mrb->integer_class, MRB_SYM(to_r), int_to_r, MRB_ARGS_NONE());
mrb_define_method_id(mrb, mrb->nil_class, MRB_SYM(to_r), nil_to_r, MRB_ARGS_NONE());
mrb_define_private_method_id(mrb, mrb->kernel_module, MRB_SYM(Rational), rational_m, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
#ifndef MRB_NO_FLOAT
mrb_define_method_id(mrb, mrb->float_class, MRB_SYM(to_r), float_to_r, MRB_ARGS_NONE());
#endif
}
void
mrb_mruby_rational_gem_final(mrb_state* mrb)
{
}