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