1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
|
#include <mruby.h>
#include <mruby/class.h>
#include <mruby/string.h>
#include <mruby/numeric.h>
struct mrb_rational {
mrb_int numerator;
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)
static struct RBasic*
rational_alloc(mrb_state *mrb, struct RClass *c, struct mrb_rational **p)
{
struct RIStruct *s;
s = (struct RIStruct*)mrb_obj_alloc(mrb, MRB_TT_ISTRUCT, c);
*p = (struct mrb_rational*)s->inline_data;
return (struct RBasic*)s;
}
#else
/* use TT_DATA */
#include <mruby/data.h>
static const struct mrb_data_type mrb_rational_type = {"Rational", mrb_free};
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;
}
#endif
static mrb_value
rational_numerator(mrb_state *mrb, mrb_value self)
{
struct mrb_rational *p = rational_ptr(mrb, self);
return mrb_fixnum_value(p->numerator);
}
static mrb_value
rational_denominator(mrb_state *mrb, mrb_value self)
{
struct mrb_rational *p = rational_ptr(mrb, self);
return mrb_fixnum_value(p->denominator);
}
static mrb_value
rational_new(mrb_state *mrb, mrb_int numerator, mrb_int denominator)
{
struct RClass *c = mrb_class_get(mrb, "Rational");
struct mrb_rational *p;
struct RBasic *rat = rational_alloc(mrb, c, &p);
p->numerator = numerator;
p->denominator = denominator;
MRB_SET_FROZEN_FLAG(rat);
return mrb_obj_value(rat);
}
static mrb_value
rational_s_new(mrb_state *mrb, mrb_value self)
{
mrb_int numerator, denominator;
#ifdef MRB_WITHOUT_FLOAT
mrb_get_args(mrb, "ii", &numerator, &denominator);
#else
#define DROP_PRECISION(cond, num, denom) \
do { \
while (cond) { \
num /= 2; \
denom /= 2; \
} \
} while (0)
mrb_value numv, denomv;
mrb_get_args(mrb, "oo", &numv, &denomv);
if (mrb_fixnum_p(numv)) {
numerator = mrb_fixnum(numv);
if (mrb_fixnum_p(denomv)) {
denominator = mrb_fixnum(denomv);
}
else {
mrb_float denomf = mrb_to_flo(mrb, denomv);
DROP_PRECISION(denomf < MRB_INT_MIN || denomf > MRB_INT_MAX, numerator, denomf);
denominator = (mrb_int)denomf;
}
}
else {
mrb_float numf = mrb_to_flo(mrb, numv);
if (mrb_fixnum_p(denomv)) {
denominator = mrb_fixnum(denomv);
}
else {
mrb_float denomf = mrb_to_flo(mrb, denomv);
DROP_PRECISION(denomf < MRB_INT_MIN || denomf > MRB_INT_MAX, numf, denomf);
denominator = (mrb_int)denomf;
}
DROP_PRECISION(numf < MRB_INT_MIN || numf > MRB_INT_MAX, numf, denominator);
numerator = (mrb_int)numf;
}
#endif
return rational_new(mrb, numerator, denominator);
}
#ifndef MRB_WITHOUT_FLOAT
static mrb_value
rational_to_f(mrb_state *mrb, mrb_value self)
{
struct mrb_rational *p = rational_ptr(mrb, self);
mrb_float f = (mrb_float)p->numerator / (mrb_float)p->denominator;
return mrb_float_value(mrb, f);
}
#endif
static mrb_value
rational_to_i(mrb_state *mrb, mrb_value self)
{
struct mrb_rational *p = rational_ptr(mrb, self);
if (p->denominator == 0) {
mrb_raise(mrb, mrb_exc_get(mrb, "StandardError"), "divided by 0");
}
return mrb_fixnum_value(p->numerator / p->denominator);
}
static mrb_value
rational_to_r(mrb_state *mrb, mrb_value self)
{
return self;
}
static mrb_value
rational_negative_p(mrb_state *mrb, mrb_value self)
{
struct mrb_rational *p = rational_ptr(mrb, self);
if (p->numerator < 0) {
return mrb_true_value();
}
return mrb_false_value();
}
static mrb_value
fix_to_r(mrb_state *mrb, mrb_value self)
{
return rational_new(mrb, mrb_fixnum(self), 1);
}
void mrb_mruby_rational_gem_init(mrb_state *mrb)
{
struct RClass *rat;
rat = mrb_define_class(mrb, "Rational", mrb_class_get(mrb, "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_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());
mrb_define_method(mrb, rat, "denominator", rational_denominator, MRB_ARGS_NONE());
#ifndef MRB_WITHOUT_FLOAT
mrb_define_method(mrb, rat, "to_f", rational_to_f, MRB_ARGS_NONE());
#endif
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, mrb->fixnum_class, "to_r", fix_to_r, MRB_ARGS_NONE());
}
void
mrb_mruby_rational_gem_final(mrb_state* mrb)
{
}
|