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