@@ -135,7 +135,7 @@ def __float__(self):
135135 assert False , "__float__ should not be invoked"
136136
137137
138- class DummyFraction (fractions .Fraction ):
138+ class DummyFraction (quicktions .Fraction ):
139139 """Dummy Fraction subclass for copy and deepcopy testing."""
140140
141141
@@ -200,6 +200,197 @@ def test_quicktions_limits(self):
200200def _components (r ):
201201 return (r .numerator , r .denominator )
202202
203+ def typed_approx_eq (a , b ):
204+ return type (a ) == type (b ) and (a == b or math .isclose (a , b ))
205+
206+ class Symbolic :
207+ """Simple non-numeric class for testing mixed arithmetic.
208+ It is not Integral, Rational, Real or Complex, and cannot be conveted
209+ to int, float or complex. but it supports some arithmetic operations.
210+ """
211+ def __init__ (self , value ):
212+ self .value = value
213+ def __mul__ (self , other ):
214+ if isinstance (other , F ):
215+ return NotImplemented
216+ return self .__class__ (f'{ self } * { other } ' )
217+ def __rmul__ (self , other ):
218+ return self .__class__ (f'{ other } * { self } ' )
219+ def __truediv__ (self , other ):
220+ if isinstance (other , F ):
221+ return NotImplemented
222+ return self .__class__ (f'{ self } / { other } ' )
223+ def __rtruediv__ (self , other ):
224+ return self .__class__ (f'{ other } / { self } ' )
225+ def __mod__ (self , other ):
226+ if isinstance (other , F ):
227+ return NotImplemented
228+ return self .__class__ (f'{ self } % { other } ' )
229+ def __rmod__ (self , other ):
230+ return self .__class__ (f'{ other } % { self } ' )
231+ def __pow__ (self , other ):
232+ if isinstance (other , F ):
233+ return NotImplemented
234+ return self .__class__ (f'{ self } ** { other } ' )
235+ def __rpow__ (self , other ):
236+ return self .__class__ (f'{ other } ** { self } ' )
237+ def __eq__ (self , other ):
238+ if other .__class__ != self .__class__ :
239+ return NotImplemented
240+ return self .value == other .value
241+ def __str__ (self ):
242+ return f'{ self .value } '
243+ def __repr__ (self ):
244+ return f'{ self .__class__ .__name__ } ({ self .value !r} )'
245+
246+ class SymbolicReal (Symbolic ):
247+ pass
248+ numbers .Real .register (SymbolicReal )
249+
250+ class SymbolicComplex (Symbolic ):
251+ pass
252+ numbers .Complex .register (SymbolicComplex )
253+
254+ class Rat :
255+ """Simple Rational class for testing mixed arithmetic."""
256+ def __init__ (self , n , d ):
257+ self .numerator = n
258+ self .denominator = d
259+ def __mul__ (self , other ):
260+ if isinstance (other , F ):
261+ return NotImplemented
262+ return self .__class__ (self .numerator * other .numerator ,
263+ self .denominator * other .denominator )
264+ def __rmul__ (self , other ):
265+ return self .__class__ (other .numerator * self .numerator ,
266+ other .denominator * self .denominator )
267+ def __truediv__ (self , other ):
268+ if isinstance (other , F ):
269+ return NotImplemented
270+ return self .__class__ (self .numerator * other .denominator ,
271+ self .denominator * other .numerator )
272+ def __rtruediv__ (self , other ):
273+ return self .__class__ (other .numerator * self .denominator ,
274+ other .denominator * self .numerator )
275+ def __mod__ (self , other ):
276+ if isinstance (other , F ):
277+ return NotImplemented
278+ d = self .denominator * other .numerator
279+ return self .__class__ (self .numerator * other .denominator % d , d )
280+ def __rmod__ (self , other ):
281+ d = other .denominator * self .numerator
282+ return self .__class__ (other .numerator * self .denominator % d , d )
283+
284+ return self .__class__ (other .numerator / self .numerator ,
285+ other .denominator / self .denominator )
286+ def __pow__ (self , other ):
287+ if isinstance (other , F ):
288+ return NotImplemented
289+ return self .__class__ (self .numerator ** other ,
290+ self .denominator ** other )
291+ def __float__ (self ):
292+ return self .numerator / self .denominator
293+ def __eq__ (self , other ):
294+ if self .__class__ != other .__class__ :
295+ return NotImplemented
296+ return (typed_approx_eq (self .numerator , other .numerator ) and
297+ typed_approx_eq (self .denominator , other .denominator ))
298+ def __repr__ (self ):
299+ return f'{ self .__class__ .__name__ } ({ self .numerator !r} , { self .denominator !r} )'
300+ numbers .Rational .register (Rat )
301+
302+ class Root :
303+ """Simple Real class for testing mixed arithmetic."""
304+ def __init__ (self , v , n = F (2 )):
305+ self .base = v
306+ self .degree = n
307+ def __mul__ (self , other ):
308+ if isinstance (other , F ):
309+ return NotImplemented
310+ return self .__class__ (self .base * other ** self .degree , self .degree )
311+ def __rmul__ (self , other ):
312+ return self .__class__ (other ** self .degree * self .base , self .degree )
313+ def __truediv__ (self , other ):
314+ if isinstance (other , F ):
315+ return NotImplemented
316+ return self .__class__ (self .base / other ** self .degree , self .degree )
317+ def __rtruediv__ (self , other ):
318+ return self .__class__ (other ** self .degree / self .base , self .degree )
319+ def __pow__ (self , other ):
320+ if isinstance (other , F ):
321+ return NotImplemented
322+ return self .__class__ (self .base , self .degree / other )
323+ def __float__ (self ):
324+ return float (self .base ) ** (1 / float (self .degree ))
325+ def __eq__ (self , other ):
326+ if self .__class__ != other .__class__ :
327+ return NotImplemented
328+ return typed_approx_eq (self .base , other .base ) and typed_approx_eq (self .degree , other .degree )
329+ def __repr__ (self ):
330+ return f'{ self .__class__ .__name__ } ({ self .base !r} , { self .degree !r} )'
331+ numbers .Real .register (Root )
332+
333+ class Polar :
334+ """Simple Complex class for testing mixed arithmetic."""
335+ def __init__ (self , r , phi ):
336+ self .r = r
337+ self .phi = phi
338+ def __mul__ (self , other ):
339+ if isinstance (other , F ):
340+ return NotImplemented
341+ return self .__class__ (self .r * other , self .phi )
342+ def __rmul__ (self , other ):
343+ return self .__class__ (other * self .r , self .phi )
344+ def __truediv__ (self , other ):
345+ if isinstance (other , F ):
346+ return NotImplemented
347+ return self .__class__ (self .r / other , self .phi )
348+ def __rtruediv__ (self , other ):
349+ return self .__class__ (other / self .r , - self .phi )
350+ def __pow__ (self , other ):
351+ if isinstance (other , F ):
352+ return NotImplemented
353+ return self .__class__ (self .r ** other , self .phi * other )
354+ def __eq__ (self , other ):
355+ if self .__class__ != other .__class__ :
356+ return NotImplemented
357+ return typed_approx_eq (self .r , other .r ) and typed_approx_eq (self .phi , other .phi )
358+ def __repr__ (self ):
359+ return f'{ self .__class__ .__name__ } ({ self .r !r} , { self .phi !r} )'
360+ numbers .Complex .register (Polar )
361+
362+ class Rect :
363+ """Other simple Complex class for testing mixed arithmetic."""
364+ def __init__ (self , x , y ):
365+ self .x = x
366+ self .y = y
367+ def __mul__ (self , other ):
368+ if isinstance (other , F ):
369+ return NotImplemented
370+ return self .__class__ (self .x * other , self .y * other )
371+ def __rmul__ (self , other ):
372+ return self .__class__ (other * self .x , other * self .y )
373+ def __truediv__ (self , other ):
374+ if isinstance (other , F ):
375+ return NotImplemented
376+ return self .__class__ (self .x / other , self .y / other )
377+ def __rtruediv__ (self , other ):
378+ r = self .x * self .x + self .y * self .y
379+ return self .__class__ (other * (self .x / r ), other * (self .y / r ))
380+ def __rpow__ (self , other ):
381+ return Polar (other ** self .x , math .log (other ) * self .y )
382+ def __complex__ (self ):
383+ return complex (self .x , self .y )
384+ def __eq__ (self , other ):
385+ if self .__class__ != other .__class__ :
386+ return NotImplemented
387+ return typed_approx_eq (self .x , other .x ) and typed_approx_eq (self .y , other .y )
388+ def __repr__ (self ):
389+ return f'{ self .__class__ .__name__ } ({ self .x !r} , { self .y !r} )'
390+ numbers .Complex .register (Rect )
391+
392+ class RectComplex (Rect , complex ):
393+ pass
203394
204395class FractionTest (unittest .TestCase ):
205396
@@ -795,20 +986,66 @@ def testMixedArithmetic(self):
795986 self .assertTypedEquals (0.9 , 1.0 - F (1 , 10 ))
796987 self .assertTypedEquals (0.9 + 0j , (1.0 + 0j ) - F (1 , 10 ))
797988
989+ def testMixedMultiplication (self ):
798990 self .assertTypedEquals (F (1 , 10 ), F (1 , 10 ) * 1 )
799991 self .assertTypedEquals (0.1 , F (1 , 10 ) * 1.0 )
800992 self .assertTypedEquals (0.1 + 0j , F (1 , 10 ) * (1.0 + 0j ))
801993 self .assertTypedEquals (F (1 , 10 ), 1 * F (1 , 10 ))
802994 self .assertTypedEquals (0.1 , 1.0 * F (1 , 10 ))
803995 self .assertTypedEquals (0.1 + 0j , (1.0 + 0j ) * F (1 , 10 ))
804996
997+ self .assertTypedEquals (F (3 , 2 ) * DummyFraction (5 , 3 ), F (5 , 2 ))
998+ self .assertTypedEquals (DummyFraction (5 , 3 ) * F (3 , 2 ), F (5 , 2 ))
999+ self .assertTypedEquals (F (3 , 2 ) * Rat (5 , 3 ), Rat (15 , 6 ))
1000+ self .assertTypedEquals (Rat (5 , 3 ) * F (3 , 2 ), F (5 , 2 ))
1001+
1002+ self .assertTypedEquals (F (3 , 2 ) * Root (4 ), Root (F (9 , 1 )))
1003+ self .assertTypedEquals (Root (4 ) * F (3 , 2 ), 3.0 )
1004+ self .assertEqual (F (3 , 2 ) * SymbolicReal ('X' ), SymbolicReal ('3/2 * X' ))
1005+ self .assertRaises (TypeError , operator .mul , SymbolicReal ('X' ), F (3 , 2 ))
1006+
1007+ self .assertTypedEquals (F (3 , 2 ) * Polar (4 , 2 ), Polar (F (6 , 1 ), 2 ))
1008+ self .assertTypedEquals (F (3 , 2 ) * Polar (4.0 , 2 ), Polar (6.0 , 2 ))
1009+ self .assertTypedEquals (F (3 , 2 ) * Rect (4 , 3 ), Rect (F (6 , 1 ), F (9 , 2 )))
1010+ self .assertTypedEquals (F (3 , 2 ) * RectComplex (4 , 3 ), RectComplex (6.0 + 0j , 4.5 + 0j ))
1011+ self .assertRaises (TypeError , operator .mul , Polar (4 , 2 ), F (3 , 2 ))
1012+ self .assertTypedEquals (Rect (4 , 3 ) * F (3 , 2 ), 6.0 + 4.5j )
1013+ self .assertEqual (F (3 , 2 ) * SymbolicComplex ('X' ), SymbolicComplex ('3/2 * X' ))
1014+ self .assertRaises (TypeError , operator .mul , SymbolicComplex ('X' ), F (3 , 2 ))
1015+
1016+ self .assertEqual (F (3 , 2 ) * Symbolic ('X' ), Symbolic ('3/2 * X' ))
1017+ self .assertRaises (TypeError , operator .mul , Symbolic ('X' ), F (3 , 2 ))
1018+
1019+ def testMixedDivision (self ):
8051020 self .assertTypedEquals (F (1 , 10 ), F (1 , 10 ) / 1 )
8061021 self .assertTypedEquals (0.1 , F (1 , 10 ) / 1.0 )
8071022 self .assertTypedEquals (0.1 + 0j , F (1 , 10 ) / (1.0 + 0j ))
8081023 self .assertTypedEquals (F (10 , 1 ), 1 / F (1 , 10 ))
8091024 self .assertTypedEquals (10.0 , 1.0 / F (1 , 10 ))
8101025 self .assertTypedEquals (10.0 + 0j , (1.0 + 0j ) / F (1 , 10 ))
8111026
1027+ self .assertTypedEquals (F (3 , 2 ) / DummyFraction (3 , 5 ), F (5 , 2 ))
1028+ self .assertTypedEquals (DummyFraction (5 , 3 ) / F (2 , 3 ), F (5 , 2 ))
1029+ self .assertTypedEquals (F (3 , 2 ) / Rat (3 , 5 ), Rat (15 , 6 ))
1030+ self .assertTypedEquals (Rat (5 , 3 ) / F (2 , 3 ), F (5 , 2 ))
1031+
1032+ self .assertTypedEquals (F (2 , 3 ) / Root (4 ), Root (F (1 , 9 )))
1033+ self .assertTypedEquals (Root (4 ) / F (2 , 3 ), 3.0 )
1034+ self .assertEqual (F (3 , 2 ) / SymbolicReal ('X' ), SymbolicReal ('3/2 / X' ))
1035+ self .assertRaises (TypeError , operator .truediv , SymbolicReal ('X' ), F (3 , 2 ))
1036+
1037+ self .assertTypedEquals (F (3 , 2 ) / Polar (4 , 2 ), Polar (F (3 , 8 ), - 2 ))
1038+ self .assertTypedEquals (F (3 , 2 ) / Polar (4.0 , 2 ), Polar (0.375 , - 2 ))
1039+ self .assertTypedEquals (F (3 , 2 ) / Rect (4 , 3 ), Rect (0.24 , 0.18 ))
1040+ self .assertRaises (TypeError , operator .truediv , Polar (4 , 2 ), F (2 , 3 ))
1041+ self .assertTypedEquals (Rect (4 , 3 ) / F (2 , 3 ), 6.0 + 4.5j )
1042+ self .assertEqual (F (3 , 2 ) / SymbolicComplex ('X' ), SymbolicComplex ('3/2 / X' ))
1043+ self .assertRaises (TypeError , operator .truediv , SymbolicComplex ('X' ), F (3 , 2 ))
1044+
1045+ self .assertEqual (F (3 , 2 ) / Symbolic ('X' ), Symbolic ('3/2 / X' ))
1046+ self .assertRaises (TypeError , operator .truediv , Symbolic ('X' ), F (2 , 3 ))
1047+
1048+ def testMixedIntegerDivision (self ):
8121049 self .assertTypedEquals (0 , F (1 , 10 ) // 1 )
8131050 self .assertTypedEquals (0.0 , F (1 , 10 ) // 1.0 )
8141051 self .assertTypedEquals (10 , 1 // F (1 , 10 ))
@@ -835,6 +1072,26 @@ def testMixedArithmetic(self):
8351072 self .assertTypedTupleEquals (divmod (- 0.1 , float ('inf' )), divmod (F (- 1 , 10 ), float ('inf' )))
8361073 self .assertTypedTupleEquals (divmod (- 0.1 , float ('-inf' )), divmod (F (- 1 , 10 ), float ('-inf' )))
8371074
1075+ self .assertTypedEquals (F (3 , 2 ) % DummyFraction (3 , 5 ), F (3 , 10 ))
1076+ self .assertTypedEquals (DummyFraction (5 , 3 ) % F (2 , 3 ), F (1 , 3 ))
1077+ self .assertTypedEquals (F (3 , 2 ) % Rat (3 , 5 ), Rat (3 , 6 ))
1078+ self .assertTypedEquals (Rat (5 , 3 ) % F (2 , 3 ), F (1 , 3 ))
1079+
1080+ self .assertRaises (TypeError , operator .mod , F (2 , 3 ), Root (4 ))
1081+ self .assertTypedEquals (Root (4 ) % F (3 , 2 ), 0.5 )
1082+ self .assertEqual (F (3 , 2 ) % SymbolicReal ('X' ), SymbolicReal ('3/2 % X' ))
1083+ self .assertRaises (TypeError , operator .mod , SymbolicReal ('X' ), F (3 , 2 ))
1084+
1085+ self .assertRaises (TypeError , operator .mod , F (3 , 2 ), Polar (4 , 2 ))
1086+ self .assertRaises (TypeError , operator .mod , F (3 , 2 ), RectComplex (4 , 3 ))
1087+ self .assertRaises (TypeError , operator .mod , Rect (4 , 3 ), F (2 , 3 ))
1088+ self .assertEqual (F (3 , 2 ) % SymbolicComplex ('X' ), SymbolicComplex ('3/2 % X' ))
1089+ self .assertRaises (TypeError , operator .mod , SymbolicComplex ('X' ), F (3 , 2 ))
1090+
1091+ self .assertEqual (F (3 , 2 ) % Symbolic ('X' ), Symbolic ('3/2 % X' ))
1092+ self .assertRaises (TypeError , operator .mod , Symbolic ('X' ), F (2 , 3 ))
1093+
1094+ def testMixedPower (self ):
8381095 # ** has more interesting conversion rules.
8391096 self .assertTypedEquals (F (100 , 1 ), F (1 , 10 ) ** - 2 )
8401097 self .assertTypedEquals (F (100 , 1 ), F (10 , 1 ) ** 2 )
@@ -855,6 +1112,40 @@ def testMixedArithmetic(self):
8551112 self .assertRaises (ZeroDivisionError , operator .pow ,
8561113 F (0 , 1 ), - 2 )
8571114
1115+ self .assertTypedEquals (F (3 , 2 ) ** Rat (3 , 1 ), F (27 , 8 ))
1116+ self .assertTypedEquals (F (3 , 2 ) ** Rat (- 3 , 1 ), F (8 , 27 ))
1117+ self .assertTypedEquals (F (- 3 , 2 ) ** Rat (- 3 , 1 ), F (- 8 , 27 ))
1118+ self .assertTypedEquals (F (9 , 4 ) ** Rat (3 , 2 ), 3.375 )
1119+ self .assertIsInstance (F (4 , 9 ) ** Rat (- 3 , 2 ), float )
1120+ self .assertAlmostEqual (F (4 , 9 ) ** Rat (- 3 , 2 ), 3.375 )
1121+ self .assertAlmostEqual (F (- 4 , 9 ) ** Rat (- 3 , 2 ), 3.375j )
1122+ self .assertTypedEquals (Rat (9 , 4 ) ** F (3 , 2 ), 3.375 )
1123+ self .assertTypedEquals (Rat (3 , 2 ) ** F (3 , 1 ), Rat (27 , 8 ))
1124+ self .assertTypedEquals (Rat (3 , 2 ) ** F (- 3 , 1 ), F (8 , 27 ))
1125+ self .assertIsInstance (Rat (4 , 9 ) ** F (- 3 , 2 ), float )
1126+ self .assertAlmostEqual (Rat (4 , 9 ) ** F (- 3 , 2 ), 3.375 )
1127+
1128+ self .assertTypedEquals (Root (4 ) ** F (2 , 3 ), Root (4 , 3.0 ))
1129+ self .assertTypedEquals (Root (4 ) ** F (2 , 1 ), Root (4 , F (1 )))
1130+ self .assertTypedEquals (Root (4 ) ** F (- 2 , 1 ), Root (4 , - F (1 )))
1131+ self .assertTypedEquals (Root (4 ) ** F (- 2 , 3 ), Root (4 , - 3.0 ))
1132+ self .assertEqual (F (3 , 2 ) ** SymbolicReal ('X' ), SymbolicReal ('1.5 ** X' ))
1133+ self .assertEqual (SymbolicReal ('X' ) ** F (3 , 2 ), SymbolicReal ('X ** 1.5' ))
1134+
1135+ self .assertTypedEquals (F (3 , 2 ) ** Rect (2 , 0 ), Polar (2.25 , 0.0 ))
1136+ self .assertTypedEquals (F (1 , 1 ) ** Rect (2 , 3 ), Polar (1.0 , 0.0 ))
1137+ self .assertTypedEquals (F (3 , 2 ) ** RectComplex (2 , 0 ), Polar (2.25 , 0.0 ))
1138+ self .assertTypedEquals (F (1 , 1 ) ** RectComplex (2 , 3 ), Polar (1.0 , 0.0 ))
1139+ self .assertTypedEquals (Polar (4 , 2 ) ** F (3 , 2 ), Polar (8.0 , 3.0 ))
1140+ self .assertTypedEquals (Polar (4 , 2 ) ** F (3 , 1 ), Polar (64 , 6 ))
1141+ self .assertTypedEquals (Polar (4 , 2 ) ** F (- 3 , 1 ), Polar (0.015625 , - 6 ))
1142+ self .assertTypedEquals (Polar (4 , 2 ) ** F (- 3 , 2 ), Polar (0.125 , - 3.0 ))
1143+ self .assertEqual (F (3 , 2 ) ** SymbolicComplex ('X' ), SymbolicComplex ('1.5 ** X' ))
1144+ self .assertEqual (SymbolicComplex ('X' ) ** F (3 , 2 ), SymbolicComplex ('X ** 1.5' ))
1145+
1146+ self .assertEqual (F (3 , 2 ) ** Symbolic ('X' ), Symbolic ('1.5 ** X' ))
1147+ self .assertEqual (Symbolic ('X' ) ** F (3 , 2 ), Symbolic ('X ** 1.5' ))
1148+
8581149 def testMixingWithDecimal (self ):
8591150 # Decimal refuses mixed arithmetic (but not mixed comparisons)
8601151 self .assertRaises (TypeError , operator .add ,
@@ -1087,7 +1378,7 @@ def numerator(self):
10871378 def denominator (self ):
10881379 return type (self )(1 )
10891380
1090- f = fractions . Fraction (myint (1 * 3 ), myint (2 * 3 ))
1381+ f = F (myint (1 * 3 ), myint (2 * 3 ))
10911382 self .assertEqual (f .numerator , 1 )
10921383 self .assertEqual (f .denominator , 2 )
10931384 self .assertEqual (type (f .numerator ), myint )
0 commit comments