55import math
66import re
77from functools import cache
8- from typing import Any , Dict , Generic , Optional , Tuple , TypeVar , Union
8+ from typing import Any , Dict , Generic , Optional , Tuple , TypeVar , Union , cast
99
1010import mpmath
1111import numpy
@@ -239,11 +239,14 @@ def __new__(cls, value) -> "Integer":
239239 return self
240240
241241 def __eq__ (self , other ) -> bool :
242- return (
243- self ._value == other .value
244- if isinstance (other , Integer )
245- else super ().__eq__ (other )
246- )
242+ if isinstance (other , Integer ):
243+ return self ._value == other ._value
244+ if isinstance (other , Number ):
245+ # If other is a number of a wider class, use
246+ # its implementation:
247+ return other .__eq__ (self )
248+
249+ return super ().__eq__ (other )
247250
248251 def __ge__ (self , other ) -> bool :
249252 return (
@@ -346,7 +349,7 @@ def to_sympy(self, **_) -> sympy_numbers.Integer:
346349
347350 def sameQ (self , rhs ) -> bool :
348351 """Mathics SameQ"""
349- return isinstance (rhs , Integer ) and self ._value == rhs .value
352+ return isinstance (rhs , Integer ) and self ._value == rhs ._value
350353
351354 def do_copy (self ) -> "Integer" :
352355 return Integer (self ._value )
@@ -404,17 +407,18 @@ def __new__(cls, value, p: Optional[int] = None) -> "Real":
404407 return PrecisionReal .__new__ (PrecisionReal , value )
405408
406409 def __eq__ (self , other ) -> bool :
407- if isinstance (other , Real ):
408- # MMA Docs: "Approximate numbers that differ in their last seven
409- # binary digits are considered equal"
410- _prec = min_prec (self , other )
411- if _prec is not None :
412- with mpmath .workprec (_prec ):
413- rel_eps = 0.5 ** float (_prec - 7 )
414- return mpmath .almosteq (
415- self .to_mpmath (), other .to_mpmath (), abs_eps = 0 , rel_eps = rel_eps
416- )
417- return super ().__eq__ (other )
410+ if not isinstance (other , Number ):
411+ return super ().__eq__ (other )
412+
413+ _prec : Optional [int ] = min_prec (self , other )
414+ if _prec is None :
415+ return self ._value == other ._value
416+
417+ with mpmath .workprec (_prec ):
418+ rel_eps = 0.5 ** float (_prec - 7 )
419+ return mpmath .almosteq (
420+ self .to_mpmath (), other .to_mpmath (), abs_eps = 0 , rel_eps = rel_eps
421+ )
418422
419423 def __hash__ (self ):
420424 # ignore last 7 binary digits when hashing
@@ -492,6 +496,20 @@ def get_precision(self) -> int:
492496 def get_float_value (self , permit_complex = False ) -> float :
493497 return self ._value
494498
499+ @property
500+ def element_order (self ) -> tuple :
501+ """
502+ Return a tuple value that is used in ordering elements
503+ of an expression. The tuple is ultimately compared lexicographically.
504+ """
505+ return (
506+ BASIC_ATOM_NUMBER_ELT_ORDER ,
507+ self ._value ,
508+ 0 ,
509+ 1 ,
510+ 0 , # Machine precision comes first, and after Integers
511+ )
512+
495513 @property
496514 def is_approx_zero (self ) -> bool :
497515 # In WMA, Chop[10.^(-10)] == 0,
@@ -514,7 +532,7 @@ def make_boxes(self, form):
514532
515533 @property
516534 def is_zero (self ) -> bool :
517- return self .value == 0.0
535+ return self ._value == 0.0
518536
519537 def sameQ (self , rhs ) -> bool :
520538 """Mathics SameQ for MachineReal.
@@ -524,9 +542,9 @@ def sameQ(self, rhs) -> bool:
524542 rhs-value's precision. For any rhs type, sameQ is False.
525543 """
526544 if isinstance (rhs , MachineReal ):
527- return self .value == rhs .value
545+ return self ._value == rhs ._value
528546 if isinstance (rhs , PrecisionReal ):
529- rhs_value = rhs .value
547+ rhs_value = rhs ._value
530548 value = self .to_sympy ()
531549 # If sympy fixes the issue, this comparison would be
532550 # enough
@@ -603,6 +621,21 @@ def get_precision(self) -> int:
603621 """Returns the default specification for precision (in binary digits) in N and other numerical functions."""
604622 return self .value ._prec + 1
605623
624+ @property
625+ def element_order (self ) -> tuple :
626+ """
627+ Return a tuple value that is used in ordering elements
628+ of an expression. The tuple is ultimately compared lexicographically.
629+ """
630+
631+ value = self ._value
632+ value , prec = float (value ), value ._prec
633+ # For large values, use the sympy.Float value...
634+ if math .isinf (value ):
635+ value , prec = self ._value , value ._prec
636+
637+ return (BASIC_ATOM_NUMBER_ELT_ORDER , value , 0 , 2 , prec )
638+
606639 @property
607640 def is_zero (self ) -> bool :
608641 # self.value == 0 does not work for sympy >=1.13
@@ -757,7 +790,7 @@ def sameQ(self, rhs) -> bool:
757790 """Mathics3 SameQ"""
758791 # FIX: check
759792 if isinstance (rhs , ByteArray ):
760- return self .value == rhs .value
793+ return self ._value == rhs ._value
761794 return False
762795
763796 def get_string_value (self ) -> Optional [str ]:
@@ -902,12 +935,15 @@ def element_order(self) -> tuple:
902935 Return a tuple value that is used in ordering elements
903936 of an expression. The tuple is ultimately compared lexicographically.
904937 """
905- return (
906- BASIC_ATOM_NUMBER_ELT_ORDER ,
907- self .real .element_order [1 ],
908- self .imag .element_order [1 ],
909- 1 ,
910- )
938+ order_real , order_imag = self .real .element_order , self .imag .element_order
939+
940+ # If the real of the imag parts are real numbers, sort according
941+ # the minimum precision.
942+ # Example:
943+ # Sort[{1+2I, 1.+2.I, 1.`4+2.`5I, 1.`2+2.`7 I}]
944+ #
945+ # = {1+2I, 1.+2.I, 1.`2+2.`7 I, 1.`4+2.`5I}
946+ return order_real + order_imag
911947
912948 @property
913949 def pattern_precedence (self ) -> tuple :
@@ -965,9 +1001,13 @@ def user_hash(self, update) -> None:
9651001
9661002 def __eq__ (self , other ) -> bool :
9671003 if isinstance (other , Complex ):
968- return self .real == other .real and self .imag == other .imag
969- else :
970- return super ().__eq__ (other )
1004+ return self .real .__eq__ (other .real ) and self .imag .__eq__ (other .imag )
1005+ if isinstance (other , Number ):
1006+ if abs (self .imag ._value ) != 0 :
1007+ return False
1008+ return self .real .__eq__ (other )
1009+
1010+ return super ().__eq__ (other )
9711011
9721012 @property
9731013 def is_zero (self ) -> bool :
@@ -1019,6 +1059,17 @@ def __new__(cls, numerator, denominator=1) -> "Rational":
10191059 self .hash = hash (key )
10201060 return self
10211061
1062+ def __eq__ (self , other ) -> bool :
1063+ if isinstance (other , Rational ):
1064+ return self .value .as_numer_denom () == other .value .as_numer_denom ()
1065+ if isinstance (other , Integer ):
1066+ return (other ._value , 1 ) == self .value .as_numer_denom ()
1067+ if isinstance (other , Number ):
1068+ # For general numbers, rely on Real or Complex implementations.
1069+ return other .__eq__ (self )
1070+ # General expressions
1071+ return super ().__eq__ (other )
1072+
10221073 def __getnewargs__ (self ) -> tuple :
10231074 return (self .numerator ().value , self .denominator ().value )
10241075
@@ -1078,7 +1129,7 @@ def element_order(self) -> tuple:
10781129 return (
10791130 BASIC_ATOM_NUMBER_ELT_ORDER ,
10801131 sympy .Float (self .value ),
1081- 0 ,
1132+ 1 ,
10821133 1 ,
10831134 )
10841135
0 commit comments