44import base64
55import math
66import re
7+ from functools import cache
78from typing import Any , Dict , Generic , Optional , Tuple , TypeVar , Union
89
910import mpmath
@@ -56,10 +57,16 @@ class Number(Atom, ImmutableValueMixin, NumericOperators, Generic[T]):
5657 being: Integer, Rational, Real, Complex.
5758 """
5859
59- _value : T
60+ _value : Any
6061 hash : int
6162
62- def __getnewargs__ (self ):
63+ def __eq__ (self , other ):
64+ if isinstance (other , Number ):
65+ return self .element_order == other .element_order
66+ else :
67+ return False
68+
69+ def __getnewargs__ (self ) -> tuple :
6370 """
6471 __getnewargs__ is used in pickle loading to ensure __new__ is
6572 called with the right value.
@@ -71,12 +78,6 @@ def __getnewargs__(self):
7178 """
7279 return (self ._value ,)
7380
74- def __eq__ (self , other ):
75- if isinstance (other , Number ):
76- return self .element_order == other .element_order
77- else :
78- return False
79-
8081 def __str__ (self ) -> str :
8182 return str (self .value )
8283
@@ -208,6 +209,7 @@ class Integer(Number[int]):
208209 # The key is the Integer's Python `int` value, and the
209210 # dictionary's value is the corresponding Mathics Integer object.
210211 _integers : Dict [Any , "Integer" ] = {}
212+ _value : int
211213
212214 _sympy : sympy_numbers .Integer
213215
@@ -448,6 +450,7 @@ class MachineReal(Real[float]):
448450 # The key is the MachineReal's Python `float` value, and the
449451 # dictionary's value is the corresponding Mathics MachineReal object.
450452 _machine_reals : Dict [Any , "MachineReal" ] = {}
453+ _value : float
451454
452455 def __new__ (cls , value ) -> "MachineReal" :
453456 n = float (value )
@@ -487,7 +490,7 @@ def get_precision(self) -> int:
487490 return FP_MANTISA_BINARY_DIGITS
488491
489492 def get_float_value (self , permit_complex = False ) -> float :
490- return self .value
493+ return self ._value
491494
492495 @property
493496 def is_approx_zero (self ) -> bool :
@@ -561,7 +564,10 @@ class PrecisionReal(Real[sympy.Float]):
561564 # The key is the PrecisionReal's sympy.Float, and the
562565 # dictionary's value is the corresponding Mathics PrecisionReal object.
563566 _precision_reals : Dict [Any , "PrecisionReal" ] = {}
564- _sympy : Number
567+ _sympy : sympy .Float
568+
569+ # Note: We have no _value attribute or value property .
570+ # value attribute comes from Number.value
565571
566572 def __new__ (cls , value ) -> "PrecisionReal" :
567573 n = sympy .Float (value )
@@ -635,10 +641,10 @@ def sameQ(self, rhs) -> bool:
635641 diff = abs (value - other_value )
636642 return diff < 0.5 ** prec
637643
638- def to_python (self , * args , ** kwargs ):
644+ def to_python (self , * args , ** kwargs ) -> float :
639645 return float (self .value )
640646
641- def to_sympy (self , * args , ** kwargs ):
647+ def to_sympy (self , * args , ** kwargs ) -> sympy . Float :
642648 return self .value
643649
644650
@@ -683,7 +689,7 @@ def __getitem__(self, index: int) -> int:
683689 """
684690 return self .value [index ]
685691
686- def __getnewargs__ (self ):
692+ def __getnewargs__ (self ) -> tuple :
687693 return (self .value ,)
688694
689695 def __hash__ (self ) -> int :
@@ -795,11 +801,12 @@ class Complex(Number[Tuple[Number[T], Number[T], Optional[int]]]):
795801 # dictionary's value is the corresponding Mathics Complex object.
796802 _complex_numbers : Dict [Any , "Complex" ] = {}
797803
798- # We use __new__ here to ensure that two Integer's that have the same value
799- # return the same object, and to set an object hash value.
800- # Consider also @lru_cache, and mechanisms for limiting and
801- # clearing the cache and the object store which might be useful in implementing
802- # Builtin Share[].
804+ # We use __new__ here to ensure that two Complex number that have
805+ # down to the type on the imaginary and real parts and precision of those --
806+ # the same value return the same object, and to set an object hash
807+ # value. Consider also @lru_cache, and mechanisms for limiting
808+ # and clearing the cache and the object store which might be
809+ # useful in implementing Builtin Share[].
803810 def __new__ (cls , real , imag ):
804811 if not isinstance (real , (Integer , Real , Rational )):
805812 raise ValueError (
@@ -852,9 +859,16 @@ def __new__(cls, real, imag):
852859
853860 return self
854861
862+ def __getnewargs__ (self ) -> tuple :
863+ return (self .real , self .imag )
864+
855865 def __hash__ (self ):
856866 return self .hash
857867
868+ @cache
869+ def __neg__ (self ):
870+ return Complex (- self .real , - self .imag )
871+
858872 def __str__ (self ) -> str :
859873 return str (self .to_sympy ())
860874
@@ -955,12 +969,6 @@ def __eq__(self, other) -> bool:
955969 else :
956970 return super ().__eq__ (other )
957971
958- def __getnewargs__ (self ):
959- return (self .real , self .imag )
960-
961- def __neg__ (self ):
962- return Complex (- self .real , - self .imag )
963-
964972 @property
965973 def is_zero (self ) -> bool :
966974 return self .real .is_zero and self .imag .is_zero
@@ -985,6 +993,9 @@ class Rational(Number[sympy.Rational]):
985993
986994 # Collection of integers defined so far.
987995 _rationals : Dict [Any , "Rational" ] = {}
996+ _value : Union [
997+ sympy .Rational , sympy .core .numbers .NaN , sympy .core .numbers .ComplexInfinity
998+ ]
988999
9891000 # We use __new__ here to ensure that two Rationals's that have the same value
9901001 # return the same object, and to set an object hash value.
@@ -1008,16 +1019,28 @@ def __new__(cls, numerator, denominator=1) -> "Rational":
10081019 self .hash = hash (key )
10091020 return self
10101021
1022+ def __getnewargs__ (self ) -> tuple :
1023+ return (self .numerator ().value , self .denominator ().value )
1024+
10111025 # __hash__ is defined so that we can store Number-derived objects
10121026 # in a set or dictionary.
10131027 def __hash__ (self ):
10141028 return self .hash
10151029
1030+ def __neg__ (self ) -> "Rational" :
1031+ return Rational (- self .numerator ().value , self .denominator ().value )
1032+
10161033 def atom_to_boxes (self , f , evaluation ):
10171034 from mathics .eval .makeboxes import format_element
10181035
10191036 return format_element (self , evaluation , f )
10201037
1038+ @property
1039+ def is_zero (self ) -> bool :
1040+ return (
1041+ self .numerator ().is_zero
1042+ ) # (implicit) and not (self.denominator().is_zero)
1043+
10211044 def to_sympy (self , ** kwargs ):
10221045 return self .value
10231046
@@ -1034,9 +1057,11 @@ def sameQ(self, rhs) -> bool:
10341057 """Mathics SameQ"""
10351058 return isinstance (rhs , Rational ) and self .value == rhs .value
10361059
1060+ @cache
10371061 def numerator (self ) -> "Integer" :
10381062 return Integer (self .value .as_numer_denom ()[0 ])
10391063
1064+ @cache
10401065 def denominator (self ) -> "Integer" :
10411066 return Integer (self .value .as_numer_denom ()[1 ])
10421067
@@ -1073,18 +1098,6 @@ def user_hash(self, update) -> None:
10731098 b"System`Rational>" + ("%s>%s" % self .value .as_numer_denom ()).encode ("utf8" )
10741099 )
10751100
1076- def __getnewargs__ (self ):
1077- return (self .numerator ().value , self .denominator ().value )
1078-
1079- def __neg__ (self ) -> "Rational" :
1080- return Rational (- self .numerator ().value , self .denominator ().value )
1081-
1082- @property
1083- def is_zero (self ) -> bool :
1084- return (
1085- self .numerator ().is_zero
1086- ) # (implicit) and not (self.denominator().is_zero)
1087-
10881101
10891102RationalOneHalf = Rational (1 , 2 )
10901103RationalMinusOneHalf = Rational (- 1 , 2 )
@@ -1299,7 +1312,7 @@ def user_hash(self, update):
12991312 # hash value of the string's text. this corresponds to MMA behavior.
13001313 update (self .value .encode ("utf8" ))
13011314
1302- def __getnewargs__ (self ):
1315+ def __getnewargs__ (self ) -> tuple :
13031316 return (self .value ,)
13041317
13051318
0 commit comments