@@ -45,6 +45,23 @@ def float_val(self):
4545 def type (self ):
4646 return Float ._type
4747
48+ class Ratio (Number ):
49+ _type = object .Type (u"pixie.stdlib.Ratio" )
50+ _immutable_fields_ = ["_numerator" , "_denominator" ]
51+
52+ def __init__ (self , numerator , denominator ):
53+ self ._numerator = numerator
54+ self ._denominator = denominator
55+
56+ def numerator (self ):
57+ return self ._numerator
58+
59+ def denominator (self ):
60+ return self ._denominator
61+
62+ def type (self ):
63+ return Ratio ._type
64+
4865IMath = as_var ("IMath" )(Protocol (u"IMath" ))
4966_add = as_var ("-add" )(DoublePolymorphicFn (u"-add" , IMath ))
5067_sub = as_var ("-sub" )(DoublePolymorphicFn (u"-sub" , IMath ))
@@ -72,13 +89,117 @@ def define_num_ops():
7289 for (c1 , conv1 ) in num_classes :
7390 for (c2 , conv2 ) in num_classes :
7491 for (op , sym ) in [("_add" , "+" ), ("_sub" , "-" ), ("_mul" , "*" ), ("_div" , "/" )]:
92+ if op == "_div" and c1 == Integer and c2 == Integer :
93+ continue
7594 extend_num_op (op , c1 , c2 , conv1 , sym , conv2 )
7695 extend_num_op ("_num_eq" , c1 , c2 , conv1 , "==" , conv2 ,
7796 wrap_start = "true if " , wrap_end = " else false" )
7897
7998define_num_ops ()
8099
81100
101+ def gcd (u , v ):
102+ while v != 0 :
103+ r = u % v
104+ u = v
105+ v = r
106+ return u
107+
108+ @extend (_div , Integer ._type , Integer ._type )
109+ def _div (n , d ):
110+ assert isinstance (n , Integer ) and isinstance (d , Integer )
111+ nv = n .int_val ()
112+ dv = d .int_val ()
113+ object .affirm (dv != 0 , u"Divide by zero" )
114+ g = gcd (nv , dv )
115+ if g == 0 :
116+ return rt .wrap (0 )
117+ nv = nv / g
118+ dv = dv / g
119+ if dv == 1 :
120+ return rt .wrap (nv )
121+ elif dv == - 1 :
122+ return rt .wrap (- 1 * nv )
123+ else :
124+ if dv < 0 :
125+ nv = nv * - 1
126+ dv = dv * - 1
127+ return Ratio (nv , dv )
128+
129+ @extend (_add , Ratio ._type , Ratio ._type )
130+ def _add (a , b ):
131+ assert isinstance (a , Ratio ) and isinstance (b , Ratio )
132+ return rt ._div (rt ._add (rt .wrap (b .numerator () * a .denominator ()),
133+ rt .wrap (a .numerator () * b .denominator ())),
134+ rt .wrap (a .denominator () * b .denominator ()))
135+
136+ @extend (_sub , Ratio ._type , Ratio ._type )
137+ def _sub (a , b ):
138+ assert isinstance (a , Ratio ) and isinstance (b , Ratio )
139+ return rt ._div (rt ._add (rt .wrap (- 1 * b .numerator () * a .denominator ()),
140+ rt .wrap (a .numerator () * b .denominator ())),
141+ rt .wrap (a .denominator () * b .denominator ()))
142+
143+ @extend (_mul , Ratio ._type , Ratio ._type )
144+ def _mul (a , b ):
145+ assert isinstance (a , Ratio ) and isinstance (b , Ratio )
146+ return rt ._div (rt .wrap (b .numerator () * a .numerator ()),
147+ rt .wrap (b .denominator () * a .denominator ()))
148+
149+ @extend (_div , Ratio ._type , Ratio ._type )
150+ def _div (a , b ):
151+ assert isinstance (a , Ratio ) and isinstance (b , Ratio )
152+ return rt ._div (rt .wrap (b .denominator () * a .numerator ()),
153+ rt .wrap (b .numerator () * a .denominator ()))
154+
155+ @extend (_num_eq , Ratio ._type , Ratio ._type )
156+ def _num_eq (a , b ):
157+ assert isinstance (a , Ratio ) and isinstance (b , Ratio )
158+ return true if a .numerator () == b .numerator () and a .denominator () == b .denominator () else false
159+
160+ ratio_op_tmpl = """@extend({pfn}, {ty1}._type, {ty2}._type)
161+ def {pfn}_{ty1}_{ty2}(a, b):
162+ assert isinstance(a, {ty1}) and isinstance(b, {ty2})
163+ return rt.{pfn}({conv1}(a), {conv2}(b))
164+ """
165+
166+ def to_ratio (x ):
167+ if isinstance (x , Ratio ):
168+ return x
169+ else :
170+ return Ratio (x .int_val (), 1 )
171+
172+ def to_ratio_conv (c ):
173+ if c == Ratio :
174+ return ""
175+ else :
176+ return "to_ratio"
177+
178+ def to_float (x ):
179+ if isinstance (x , Float ):
180+ return x
181+ else :
182+ return rt .wrap (x .numerator () / float (x .denominator ()))
183+
184+ def to_float_conv (c ):
185+ if c == Float :
186+ return ""
187+ else :
188+ return "to_float"
189+
190+ def define_ratio_ops ():
191+ for (c1 , c2 ) in [(Integer , Ratio ), (Ratio , Integer )]:
192+ for op in ["_add" , "_sub" , "_mul" , "_div" , "_num_eq" ]:
193+ code = ratio_op_tmpl .format (pfn = op , ty1 = c1 .__name__ , ty2 = c2 .__name__ , conv1 = to_ratio_conv (c1 ), conv2 = to_ratio_conv (c2 ))
194+ exec code
195+
196+ for (c1 , c2 ) in [(Float , Ratio ), (Ratio , Float )]:
197+ for op in ["_add" , "_sub" , "_mul" , "_div" , "_num_eq" ]:
198+ code = ratio_op_tmpl .format (pfn = op , ty1 = c1 .__name__ , ty2 = c2 .__name__ , conv1 = to_float_conv (c1 ), conv2 = to_float_conv (c2 ))
199+ exec code
200+
201+ define_ratio_ops ()
202+
82203# def add(a, b):
83204# if isinstance(a, Integer):
84205# if isinstance(b, Integer):
@@ -113,3 +234,11 @@ def _str(f):
113234 @extend (proto ._repr , Float ._type )
114235 def _repr (f ):
115236 return rt .wrap (unicode (str (f .float_val ())))
237+
238+ @extend (proto ._repr , Ratio ._type )
239+ def _repr (r ):
240+ return rt .wrap (unicode (str (r .numerator ()) + "/" + str (r .denominator ())))
241+
242+ @extend (proto ._str , Ratio ._type )
243+ def _str (r ):
244+ return rt .wrap (unicode (str (r .numerator ()) + "/" + str (r .denominator ())))
0 commit comments