1#!/usr/local/bin/python3.8
2# -*- coding: utf-8 -*-
3
4
5from mathics.core.evaluation import Evaluation
6from mathics.core.expression import (
7    Complex,
8    Expression,
9    Integer,
10    MachineReal,
11    Rational,
12    Real,
13    String,
14    Symbol,
15    SymbolFalse,
16)
17from mathics.core.definitions import Definitions
18import sys
19import unittest
20
21definitions = Definitions(add_builtin=True)
22
23
24def _symbol_truth_value(x):
25    if x.is_true():
26        return True
27    elif isinstance(x, Symbol) and x == SymbolFalse:
28        return False
29    else:
30        return "undefined"
31
32
33def _test_group(k, *group):
34    for i, a in enumerate(group):
35        sep = ""
36        for j, b in enumerate(group):
37            if i == j:
38                continue
39            evaluation = Evaluation(definitions, catch_interrupt=False)
40            try:
41                is_same_under_sameq = Expression("SameQ", a, b).evaluate(evaluation)
42            except Exception as exc:
43                print("Exception %s" % exc)
44                info = sys.exc_info()
45                sys.excepthook(*info)
46                return False
47
48            is_same = a.sameQ(b)
49            if is_same != _symbol_truth_value(is_same_under_sameq):
50                print(
51                    "%sTest failed: %s and %s are inconsistent under .sameQ() and SameQ\n"
52                    % (sep, repr(a), repr(b))
53                )
54                return False
55
56            if is_same and hash(a) != hash(b):
57                print(
58                    "%sTest failed: hashes for %s and %s must be equal but are not\n"
59                    % (sep, repr(a), repr(b))
60                )
61                return False
62            sep = "\n"
63
64
65class HashAndSameQ(unittest.TestCase):
66    # tests that the following assumption holds: if objects are same under SameQ, their
67    # hash is always equals. this assumption is relied upon by Gather[] and similar operations
68
69    def testInteger(self):
70        _test_group(Integer(5), Integer(3242), Integer(-1372))
71
72    def testRational(self):
73        _test_group(
74            Rational(1, 3),
75            Rational(1, 3),
76            Rational(2, 6),
77            Rational(-1, 3),
78            Rational(-10, 30),
79            Rational(10, 5),
80        )
81
82    def testReal(self):
83        _test_group(
84            MachineReal(1.17361),
85            MachineReal(-1.42),
86            MachineReal(42.846195714),
87            MachineReal(42.846195714),
88            MachineReal(42.846195713),
89            Real("42.846195713", 18),
90            Real("-1.42", 3),
91        )
92
93    def testComplex(self):
94        def c(i, r):
95            return Complex(MachineReal(i), MachineReal(i))
96
97        _test_group(c(1.2, 1.2), c(0.7, 1.8), c(1.8, 0.7), c(-0.7, 1.8), c(0.7, 1.81))
98
99    def testString(self):
100        _test_group(String("xy"), String("x"), String("xyz"), String("abc"))
101
102    def testSymbol(self):
103        _test_group(Symbol("xy"), Symbol("x"), Symbol("xyz"), Symbol("abc"))
104
105    def testAcrossTypes(self):
106        _test_group(
107            Integer(1),
108            Rational(1, 1),
109            Real(1),
110            Complex(Integer(1), Integer(1)),
111            String("1"),
112            Symbol("1"),
113        )
114
115    def testInstances(self):
116        # duplicate instantiations of same content (like Integer 5) to test for potential instantiation randomness.
117        _test_group(
118            list(
119                map(
120                    lambda f: (f(), f()),
121                    (
122                        lambda: Integer(5),
123                        lambda: Rational(5, 2),
124                        lambda: MachineReal(5.12345678),
125                        lambda: Complex(Integer(5), Integer(2)),
126                        lambda: String("xy"),
127                        lambda: Symbol("xy"),
128                    ),
129                )
130            )
131        )
132
133
134if __name__ == "__main__":
135    unittest.main()
136