22# -*- coding: utf-8 -*-
33
44
5+ from mathics .version import __version__ # noqa used in loading to check consistency.
6+
57import itertools
68from typing import Optional , Union
79
810import sympy
9- from mathics .version import __version__ # noqa used in loading to check consistency.
1011
1112from mathics .builtin .base import (
1213 BinaryOperator ,
2122 Expression ,
2223 Integer ,
2324 Number ,
25+ Real ,
2426 String ,
2527 Symbol ,
2628 SymbolFalse ,
@@ -32,6 +34,9 @@ def cmp(a, b) -> int:
3234 "Returns 0 if a == b, -1 if a < b and 1 if a > b"
3335 return (a > b ) - (a < b )
3436
37+ def is_number (sympy_value ) -> bool :
38+ return hasattr (sympy_value , "is_number" ) or isinstance (sympy_value , sympy .Float )
39+
3540class SameQ (BinaryOperator ):
3641 """
3742 <dl>
@@ -231,17 +236,30 @@ def do_compare(self, l1, l2) -> Union[bool, None]:
231236 return result
232237 return True
233238
239+ # Use Mathics' built-in comparisons for Real and Integer. These use
240+ # WL's interpretation of Equal[] which allows for slop in Reals
241+ # in the least significant digit of precision, while for Integers, comparison
242+ # has to be exact.
243+
244+ if ((isinstance (l1 , Real ) and isinstance (l2 , Real )) or
245+ (isinstance (l1 , Integer ) and isinstance (l2 , Integer ))):
246+ return l1 == l2
247+
248+ # For everything else, use sympy.
249+
234250 l1_sympy = l1 .to_sympy (evaluate = True , prec = COMPARE_PREC )
235251 l2_sympy = l2 .to_sympy (evaluate = True , prec = COMPARE_PREC )
236252
237253 if l1_sympy is None or l2_sympy is None :
238254 return None
239255
240- if not hasattr (l1_sympy , "is_number" ):
256+
257+ if not is_number (l1_sympy ):
241258 l1_sympy = mp_convert_constant (l1_sympy , prec = COMPARE_PREC )
242- if not hasattr (l2_sympy , "is_number" ):
259+ if not is_number (l2_sympy ):
243260 l2_sympy = mp_convert_constant (l2_sympy , prec = COMPARE_PREC )
244261
262+
245263 if l1_sympy .is_number and l2_sympy .is_number :
246264 # assert min_prec(l1, l2) is None
247265 prec = COMPARE_PREC # TODO: Use $MaxExtraPrecision
@@ -254,8 +272,12 @@ def do_compare(self, l1, l2) -> Union[bool, None]:
254272 def apply (self , items , evaluation ):
255273 "%(name)s[items___]"
256274 items_sequence = items .get_sequence ()
257- if len (items_sequence ) <= 1 :
275+ n = len (items_sequence )
276+ if n <= 1 :
258277 return SymbolTrue
278+ is_exact_vals = [Expression ("ExactNumberQ" , arg ).evaluate (evaluation ) for arg in items_sequence ]
279+ if all (val == SymbolTrue for val in is_exact_vals ):
280+ return self .apply_other (items , evaluation )
259281 args = self .numerify_args (items , evaluation )
260282 wanted = operators [self .get_name ()]
261283 for x , y in itertools .combinations (args , 2 ):
@@ -274,7 +296,7 @@ def apply(self, items, evaluation):
274296 return SymbolTrue
275297
276298 def apply_other (self , args , evaluation ):
277- "%(name)s[args___?(!RealNumberQ [#]&)]"
299+ "%(name)s[args___?(!ExactNumberQ [#]&)]"
278300 args = args .get_sequence ()
279301 for x , y in itertools .combinations (args , 2 ):
280302 c = self .do_compare (x , y )
@@ -285,6 +307,7 @@ def apply_other(self, args, evaluation):
285307 return SymbolTrue
286308
287309
310+
288311class _ComparisonOperator (_InequalityOperator ):
289312 "Compares arguments in a chain e.g. a < b < c compares a < b and b < c."
290313
@@ -355,7 +378,6 @@ def apply(self, items, evaluation):
355378 ]
356379 return Expression ("And" , * groups )
357380
358-
359381def do_cmp (x1 , x2 ) -> Optional [int ]:
360382
361383 # don't attempt to compare complex numbers
@@ -369,8 +391,10 @@ def do_cmp(x1, x2) -> Optional[int]:
369391 s1 = x1 .to_sympy ()
370392 s2 = x2 .to_sympy ()
371393
372- # use internal comparisons only for Reals
373- # and use sympy for everything else
394+ # Use internal comparisons only for Real which is uses
395+ # WL's interpretation of equal (which allows for slop
396+ # in the least significant digit of precision), and use
397+ # use sympy for everything else
374398 if s1 .is_Float and s2 .is_Float :
375399 if x1 == x2 :
376400 return 0
@@ -450,8 +474,9 @@ class Equal(_EqualityOperator, SympyComparison):
450474 ## TODO Needs power precision tracking
451475 ## >> 0.1 ^ 10000 == 0.1 ^ 10000 + 0.1 ^ 10012
452476 ## = False
453- ## >> 0.1 ^ 10000 == 0.1 ^ 10000 + 0.1 ^ 10013
454- ## = True
477+
478+ #> 0.1 ^ 10000 == 0.1 ^ 10000 + 0.1 ^ 10013
479+ = True
455480
456481 #> 0.1111111111111111 == 0.1111111111111126
457482 = True
0 commit comments