rational.c: overhaul rational operators.

- define `MRB_TT_RATIONAL`
- change object structure (`struct RRational`)
- add memory management for `MRB_TT_RATIONAL`
- avoid operator overloading as much as possible
- implement division overloading in C
- as a result, performance improved a lot
parent eb070303
......@@ -129,6 +129,8 @@ enum mrb_vtype {
MRB_TT_FIBER,
MRB_TT_ISTRUCT,
MRB_TT_BREAK,
MRB_TT_COMPLEX,
MRB_TT_RATIONAL,
MRB_TT_MAXDEFINE
};
......
......@@ -2,4 +2,5 @@ MRuby::Gem::Specification.new('mruby-rational') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
spec.summary = 'Rational class'
spec.build.cc.defines << "MRB_USE_RATIONAL"
end
......@@ -64,17 +64,6 @@ class Rational < Numeric
nil
end
end
def ==(rhs)
return true if self.equal?(rhs)
case rhs
when Integer, Float
return numerator == rhs if denominator == 1
when Rational
return numerator * rhs.denominator == denominator * rhs.numerator
end
rhs == self
end
end
class Numeric
......@@ -82,28 +71,3 @@ class Numeric
Rational(self, 1)
end
end
module Kernel
[:+, :-, :*, :/, :<=>, :==, :<, :<=, :>, :>=].each do |op|
original_operator_name = :"__original_operator_#{op}_rational"
Integer.instance_eval do
alias_method original_operator_name, op
define_method op do |rhs|
if rhs.is_a? Rational
Rational(self).__send__(op, rhs)
else
__send__(original_operator_name, rhs)
end
end
end
Float.instance_eval do
alias_method original_operator_name, op
define_method op do |rhs|
if rhs.is_a? Rational
rhs = rhs.to_f
end
__send__(original_operator_name, rhs)
end
end if Object.const_defined?(:Float)
end
end
......@@ -9,53 +9,45 @@ struct mrb_rational {
mrb_int denominator;
};
#if MRB_INT_MAX <= INTPTR_MAX
#define RATIONAL_USE_ISTRUCT
/* use TT_ISTRUCT */
#include <mruby/istruct.h>
#define rational_ptr(mrb, v) (struct mrb_rational*)mrb_istruct_ptr(v)
#if defined(MRB_INT64) && defined(MRB_32BIT)
struct RRational {
MRB_OBJECT_HEADER;
struct mrb_rational *p;
};
static struct RBasic*
rational_alloc(mrb_state *mrb, struct RClass *c, struct mrb_rational **p)
static struct mrb_rational*
rational_ptr(mrb_state *mrb, mrb_value v)
{
struct RIStruct *s;
s = (struct RIStruct*)mrb_obj_alloc(mrb, MRB_TT_ISTRUCT, c);
*p = (struct mrb_rational*)s->inline_data;
struct RRational *r = (struct RRational*)mrb_obj_ptr(v);
return (struct RBasic*)s;
if (!r->p) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized rational");
}
return r->p;
}
#else
/* use TT_DATA */
#include <mruby/data.h>
static const struct mrb_data_type mrb_rational_type = {"Rational", mrb_free};
#define RATIONAL_INLINE
struct RRational {
MRB_OBJECT_HEADER;
struct mrb_rational r;
};
#define rational_ptr(mrb, v) (&((struct RRational*)mrb_obj_ptr(v))->r)
#endif
static struct RBasic*
rational_alloc(mrb_state *mrb, struct RClass *c, struct mrb_rational **p)
{
struct RData *d;
Data_Make_Struct(mrb, c, struct mrb_rational, &mrb_rational_type, *p, d);
return (struct RBasic*)d;
}
static struct mrb_rational*
rational_ptr(mrb_state *mrb, mrb_value v)
{
struct mrb_rational *p;
p = DATA_GET_PTR(mrb, v, &mrb_rational_type, struct mrb_rational);
if (!p) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized rational");
}
return p;
}
struct RRational *s;
s = (struct RRational*)mrb_obj_alloc(mrb, MRB_TT_RATIONAL, c);
#ifdef RATIONAL_INLINE
*p = &s->r;
#else
*p = s->p = (struct mrb_rational*)mrb_malloc(mrb, sizeof(struct mrb_rational));
#endif
return (struct RBasic*)s;
}
static mrb_value
rational_numerator(mrb_state *mrb, mrb_value self)
......@@ -356,17 +348,134 @@ rational_m(mrb_state *mrb, mrb_value self)
#endif
}
mrb_bool
mrb_rational_eq(mrb_state *mrb, mrb_value x, mrb_value y)
{
struct mrb_rational *p1 = rational_ptr(mrb, x);
switch (mrb_type(y)) {
case MRB_TT_INTEGER:
if (p1->denominator != 1) return FALSE;
return p1->numerator == mrb_integer(y);
#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
return ((double)p1->numerator/p1->denominator) == mrb_float(y);
#endif
case MRB_TT_RATIONAL:
{
struct mrb_rational *p2 = rational_ptr(mrb, y);
mrb_int a, b;
if (p1->numerator == p2->numerator && p1->denominator == p2->denominator) {
return TRUE;
}
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
return (double)p1->numerator*p2->denominator == (double)p2->numerator*p2->denominator;
#endif
}
return a == b;
}
#ifdef MRB_USE_COMPLEX
case MRB_TT_RATIONAL:
{
mrb_bool mrb_complex_eq(mrb_state *mrb, mrb_value, mrb_value);
return mrb_complex_eq(mrb, y, x);
}
#endif
default:
return mrb_equal(mrb, y, x);
}
}
static mrb_value
rational_eq(mrb_state *mrb, mrb_value x)
{
mrb_value y = mrb_get_arg1(mrb);
return mrb_bool_value(mrb_rational_eq(mrb, x, y));
}
static mrb_value
rational_minus(mrb_state *mrb, mrb_value x)
{
struct mrb_rational *p = rational_ptr(mrb, x);
return rational_new(mrb, -p->numerator, p->denominator);
}
mrb_int mrb_num_div_int(mrb_state *, mrb_int, mrb_int);
/* 15.2.8.3.4 */
/*
* redefine Integer#/
*/
static mrb_value
int_div(mrb_state *mrb, mrb_value x)
{
mrb_value y = mrb_get_arg1(mrb);
mrb_int a = mrb_integer(x);
if (mrb_integer_p(y)) {
mrb_int div = mrb_num_div_int(mrb, a, mrb_integer(y));
return mrb_int_value(mrb, div);
}
switch (mrb_type(y)) {
case MRB_TT_RATIONAL:
x = rational_new(mrb, a, 1);
return mrb_funcall_id(mrb, x, MRB_OPSYM(div), 1, y);
#if defined(MRB_USE_COMPLEX)
case MRB_TT_COMPLEX:
return mrb_funcall_id(mrb, x, MRB_OPSYM(div), 1, y);
#endif
case MRB_TT_FLOAT:
default:
#ifdef MRB_NO_FLOAT
mrb_raise(mrb, E_TYPE_ERROR, "non integer multiplication");
#else
return mrb_float_value(mrb, (mrb_float)a * mrb_to_flo(mrb, y));
#endif
}
}
/* 15.2.9.3.19(x) */
/*
* redefine Integer#quo
*/
static mrb_value
int_quo(mrb_state *mrb, mrb_value x)
{
mrb_value y = mrb_get_arg1(mrb);
mrb_int a = mrb_integer(x);
if (mrb_integer_p(y)) {
return rational_new(mrb, a, mrb_integer(y));
}
switch (mrb_type(y)) {
case MRB_TT_RATIONAL:
x = rational_new(mrb, a, 1);
return mrb_funcall_id(mrb, x, MRB_OPSYM(div), 1, y);
#if defined(MRB_USE_COMPLEX)
case MRB_TT_COMPLEX:
return mrb_funcall_id(mrb, x, MRB_OPSYM(div), 1, y);
#endif
default:
#ifdef MRB_NO_FLOAT
mrb_raise(mrb, E_TYPE_ERROR, "non integer multiplication");
#else
return mrb_float_value(mrb, (mrb_float)a * mrb_to_flo(mrb, y));
#endif
}
}
void mrb_mruby_rational_gem_init(mrb_state *mrb)
{
struct RClass *rat;
rat = mrb_define_class_id(mrb, MRB_SYM(Rational), mrb_class_get_id(mrb, MRB_SYM(Numeric)));
#ifdef RATIONAL_USE_ISTRUCT
MRB_SET_INSTANCE_TT(rat, MRB_TT_ISTRUCT);
mrb_assert(sizeof(struct mrb_rational) < ISTRUCT_DATA_SIZE);
#else
MRB_SET_INSTANCE_TT(rat, MRB_TT_DATA);
#endif
MRB_SET_INSTANCE_TT(rat, MRB_TT_RATIONAL);
mrb_undef_class_method(mrb, rat, "new");
mrb_define_class_method(mrb, rat, "_new", rational_s_new, MRB_ARGS_REQ(2));
mrb_define_method(mrb, rat, "numerator", rational_numerator, MRB_ARGS_NONE());
......@@ -377,7 +486,11 @@ void mrb_mruby_rational_gem_init(mrb_state *mrb)
mrb_define_method(mrb, rat, "to_i", rational_to_i, MRB_ARGS_NONE());
mrb_define_method(mrb, rat, "to_r", rational_to_r, MRB_ARGS_NONE());
mrb_define_method(mrb, rat, "negative?", rational_negative_p, MRB_ARGS_NONE());
mrb_define_method(mrb, rat, "==", rational_eq, MRB_ARGS_REQ(1));
mrb_define_method(mrb, rat, "-@", rational_minus, MRB_ARGS_NONE());
mrb_define_method(mrb, mrb->integer_class, "to_r", fix_to_r, MRB_ARGS_NONE());
mrb_define_method(mrb, mrb->integer_class, "/", int_div, MRB_ARGS_REQ(1)); /* overrride */
mrb_define_method(mrb, mrb->integer_class, "quo", int_quo, MRB_ARGS_REQ(1)); /* overrride */
mrb_define_method(mrb, mrb->kernel_module, "Rational", rational_m, MRB_ARGS_ARG(1,1));
}
......
......@@ -903,6 +903,17 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end)
}
break;
#if defined(MRB_INT64) && defined(MRB_32BIT)
#ifdef MRB_USE_RATIONAL
case MRB_TT_RATIONAL:
{
struct RData *o = (struct RData*)obj;
mrb_free(mrb, obj->iv_tbl);
}
break;
#endif
#endif
default:
break;
}
......
......@@ -118,7 +118,6 @@ int_pow(mrb_state *mrb, mrb_value x)
#endif
}
mrb_int
mrb_num_div_int(mrb_state *mrb, mrb_int x, mrb_int y)
{
......@@ -144,20 +143,27 @@ mrb_num_div_int(mrb_state *mrb, mrb_int x, mrb_int y)
/* 15.2.9.3.4 */
/*
* call-seq:
* int / other -> int
* int / other -> num
*
* Performs division: the class of the resulting object depends on
* the class of <code>num</code> and on the magnitude of the
* result.
*/
static mrb_value
int_div(mrb_state *mrb, mrb_value xv)
int_div(mrb_state *mrb, mrb_value x)
{
mrb_int y, div;
mrb_value y = mrb_get_arg1(mrb);
mrb_int a = mrb_integer(x);
mrb_get_args(mrb, "i", &y);
div = mrb_num_div_int(mrb, mrb_integer(xv), y);
return mrb_int_value(mrb, div);
if (mrb_integer_p(y)) {
mrb_int div = mrb_num_div_int(mrb, a, mrb_integer(y));
return mrb_int_value(mrb, div);
}
#ifdef MRB_NO_FLOAT
mrb_raise(mrb, E_TYPE_ERROR, "non integer division");
#else
return mrb_float_value(mrb, (mrb_float)a / mrb_to_flo(mrb, y));
#endif
}
/* 15.2.9.3.19(x) */
......@@ -168,17 +174,29 @@ int_div(mrb_state *mrb, mrb_value xv)
* Returns most exact division.
*/
/*
* call-seq:
* int.div(other) -> int
*
* Performs division: resulting integer.
*/
static mrb_value
int_quo(mrb_state *mrb, mrb_value xv)
int_idiv(mrb_state *mrb, mrb_value x)
{
#ifdef MRB_NO_FLOAT
mrb_int y;
mrb_get_args(mrb, "i", &y);
if (y == 0) {
int_zerodiv(mrb);
}
return mrb_fixnum_value(mrb_integer(xv) / y);
return mrb_fixnum_value(mrb_integer(x) / y);
}
static mrb_value
int_quo(mrb_state *mrb, mrb_value xv)
{
#ifdef MRB_NO_FLOAT
return int_idiv(mrb, xv);
#else
mrb_float y;
......@@ -478,6 +496,14 @@ flo_eq(mrb_state *mrb, mrb_value x)
return mrb_bool_value(mrb_float(x) == (mrb_float)mrb_integer(y));
case MRB_TT_FLOAT:
return mrb_bool_value(mrb_float(x) == mrb_float(y));
#ifdef MRB_USE_RATIONAL
case MRB_TT_RATIONAL:
return mrb_bool_value(mrb_float(x) == mrb_to_flo(mrb, y));
#endif
#ifdef MRB_USE_COMPLEX
case MRB_TT_COMPLEX:
return mrb_bool_value(mrb_equal(mrb, y, x));
#endif
default:
return mrb_false_value();
}
......@@ -876,13 +902,21 @@ fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y)
if (mrb_int_mul_overflow(a, b, &c)) {
int_overflow(mrb, "multiplication");
}
return mrb_fixnum_value(c);
return mrb_int_value(mrb, c);
}
switch (mrb_type(y)) {
#if defined(MRB_USE_RATIONAL) || defined(MRB_USE_COMPLEX)
case MRB_TT_RATIONAL:
case MRB_TT_COMPLEX:
return mrb_funcall_id(mrb, y, MRB_OPSYM(mul), 1, x);
#endif
default:
#ifdef MRB_NO_FLOAT
mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value");
mrb_raise(mrb, E_TYPE_ERROR, "non integer multiplication");
#else
return mrb_float_value(mrb, (mrb_float)a * mrb_to_flo(mrb, y));
return mrb_float_value(mrb, (mrb_float)a * mrb_to_flo(mrb, y));
#endif
}
}
MRB_API mrb_value
......@@ -895,6 +929,15 @@ mrb_num_mul(mrb_state *mrb, mrb_value x, mrb_value y)
if (mrb_float_p(x)) {
return mrb_float_value(mrb, mrb_float(x) * mrb_to_flo(mrb, y));
}
#endif
#if defined(MRB_USE_RATIONAL) || defined(MRB_USE_COMPLEX)
switch (mrb_type(x)) {
case MRB_TT_RATIONAL:
case MRB_TT_COMPLEX:
return mrb_funcall_id(mrb, x, MRB_OPSYM(mul), 1, y);
default:
break;
}
#endif
mrb_raise(mrb, E_TYPE_ERROR, "no number multiply");
return mrb_nil_value(); /* not reached */
......@@ -964,7 +1007,7 @@ int_mod(mrb_state *mrb, mrb_value x)
return mrb_fixnum_value(mod);
}
#ifdef MRB_NO_FLOAT
mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value");
mrb_raise(mrb, E_TYPE_ERROR, "non integer modulo");
#else
else {
mrb_float mod;
......@@ -993,7 +1036,7 @@ int_divmod(mrb_state *mrb, mrb_value x)
return mrb_assoc_new(mrb, mrb_int_value(mrb, div), mrb_int_value(mrb, mod));
}
#ifdef MRB_NO_FLOAT
mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value");
mrb_raise(mrb, E_TYPE_ERROR, "non integer divmod");
#else
else {
mrb_float div, mod;
......@@ -1048,6 +1091,14 @@ int_equal(mrb_state *mrb, mrb_value x)
#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
return mrb_bool_value((mrb_float)mrb_integer(x) == mrb_float(y));
#endif
#ifdef MRB_USE_RATIONAL
case MRB_TT_RATIONAL:
return mrb_bool_value(mrb_equal(mrb, y, x));
#endif
#ifdef MRB_USE_COMPLEX
case MRB_TT_COMPLEX:
return mrb_bool_value(mrb_equal(mrb, y, x));
#endif
default:
return mrb_false_value();
......@@ -1292,11 +1343,19 @@ fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y)
}
return mrb_int_value(mrb, c);
}
switch (mrb_type(y)) {
#if defined(MRB_USE_RATIONAL) || defined(MRB_USE_COMPLEX)
case MRB_TT_RATIONAL:
case MRB_TT_COMPLEX:
return mrb_funcall_id(mrb, y, MRB_OPSYM(add), 1, x);
#endif
default:
#ifdef MRB_NO_FLOAT
mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value");
mrb_raise(mrb, E_TYPE_ERROR, "non integer addition");
#else
return mrb_float_value(mrb, (mrb_float)a + mrb_to_flo(mrb, y));
return mrb_float_value(mrb, (mrb_float)a + mrb_to_flo(mrb, y));
#endif
}
}
MRB_API mrb_value
......@@ -1309,6 +1368,15 @@ mrb_num_plus(mrb_state *mrb, mrb_value x, mrb_value y)
if (mrb_float_p(x)) {
return mrb_float_value(mrb, mrb_float(x) + mrb_to_flo(mrb, y));
}
#endif
#if defined(MRB_USE_RATIONAL) || defined(MRB_USE_COMPLEX)
switch (mrb_type(x)) {
case MRB_TT_RATIONAL:
case MRB_TT_COMPLEX:
return mrb_funcall_id(mrb, x, MRB_OPSYM(add), 1, y);
default:
break;
}
#endif
mrb_raise(mrb, E_TYPE_ERROR, "no number addition");
return mrb_nil_value(); /* not reached */
......@@ -1346,11 +1414,20 @@ fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y)
}
return mrb_int_value(mrb, c);
}
switch (mrb_type(y)) {
#if defined(MRB_USE_RATIONAL) || defined(MRB_USE_COMPLEX)
case MRB_TT_RATIONAL:
case MRB_TT_COMPLEX:
x = mrb_funcall_id(mrb, y, MRB_OPSYM(sub), 1, x);
return mrb_funcall_id(mrb, x, MRB_OPSYM(minus), 0);
#endif
default:
#ifdef MRB_NO_FLOAT
mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value");
mrb_raise(mrb, E_TYPE_ERROR, "non integer subtraction");
#else
return mrb_float_value(mrb, (mrb_float)a - mrb_to_flo(mrb, y));
return mrb_float_value(mrb, (mrb_float)a - mrb_to_flo(mrb, y));
#endif
}
}
MRB_API mrb_value
......@@ -1363,6 +1440,15 @@ mrb_num_minus(mrb_state *mrb, mrb_value x, mrb_value y)
if (mrb_float_p(x)) {
return mrb_float_value(mrb, mrb_float(x) - mrb_to_flo(mrb, y));
}
#endif
#if defined(MRB_USE_RATIONAL) || defined(MRB_USE_COMPLEX)
switch (mrb_type(x)) {
case MRB_TT_RATIONAL:
case MRB_TT_COMPLEX:
return mrb_funcall_id(mrb, x, MRB_OPSYM(sub), 1, y);
default:
break;
}
#endif
mrb_raise(mrb, E_TYPE_ERROR, "no number subtraction");
return mrb_nil_value(); /* not reached */
......@@ -1471,6 +1557,11 @@ cmpnum(mrb_state *mrb, mrb_value v1, mrb_value v2)
case MRB_TT_FLOAT:
y = mrb_float(v2);
break;
#endif
#ifdef MRB_USE_RATIONAL
case MRB_TT_RATIONAL:
y = mrb_to_flo(mrb, v2);
break;
#endif
default:
return -2;
......@@ -1631,9 +1722,6 @@ mrb_init_numeric(mrb_state *mrb)
MRB_SET_INSTANCE_TT(integer, MRB_TT_INTEGER);
mrb_undef_class_method(mrb, integer, "new");
mrb_define_method(mrb, integer, "**", int_pow, MRB_ARGS_REQ(1));
mrb_define_method(mrb, integer, "/", int_div, MRB_ARGS_REQ(1)); /* 15.2.8.3.6 */
mrb_define_method(mrb, integer, "quo", int_quo, MRB_ARGS_REQ(1)); /* 15.2.7.4.5 (x) */
mrb_define_method(mrb, integer, "div", int_div, MRB_ARGS_REQ(1));
mrb_define_method(mrb, integer, "<=>", num_cmp, MRB_ARGS_REQ(1)); /* 15.2.8.3.1 */
mrb_define_method(mrb, integer, "<", num_lt, MRB_ARGS_REQ(1));
mrb_define_method(mrb, integer, "<=", num_le, MRB_ARGS_REQ(1));
......@@ -1653,6 +1741,9 @@ mrb_init_numeric(mrb_state *mrb)
mrb_define_method(mrb, integer, "-", int_minus, MRB_ARGS_REQ(1)); /* 15.2.8.3.2 */
mrb_define_method(mrb, integer, "*", int_mul, MRB_ARGS_REQ(1)); /* 15.2.8.3.3 */
mrb_define_method(mrb, integer, "%", int_mod, MRB_ARGS_REQ(1)); /* 15.2.8.3.5 */
mrb_define_method(mrb, integer, "/", int_div, MRB_ARGS_REQ(1)); /* 15.2.8.3.6 */
mrb_define_method(mrb, integer, "quo", int_quo, MRB_ARGS_REQ(1)); /* 15.2.7.4.5 (x) */
mrb_define_method(mrb, integer, "div", int_idiv, MRB_ARGS_REQ(1));
mrb_define_method(mrb, integer, "==", int_equal, MRB_ARGS_REQ(1)); /* 15.2.8.3.7 */
mrb_define_method(mrb, integer, "~", int_rev, MRB_ARGS_NONE()); /* 15.2.8.3.8 */
mrb_define_method(mrb, integer, "&", int_and, MRB_ARGS_REQ(1)); /* 15.2.8.3.9 */
......
......@@ -516,7 +516,10 @@ mrb_to_int(mrb_state *mrb, mrb_value val)
return mrb_flo_to_fixnum(mrb, val);
}
#endif
mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %Y to Integer", val);
if (mrb_string_p(val)) {
mrb_raise(mrb, E_TYPE_ERROR, "can't convert String to Integer");
}
return mrb_type_convert(mrb, val, MRB_TT_INTEGER, MRB_SYM(to_i));
}
return val;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment