Skip to content

Commit dcc607e

Browse files
authored
Two postponed TODOs in recent PRs... (#1148)
* Move eval functions to mathics.eval.numbers.numbers * Move "no_doc" after imports
1 parent f88d3c2 commit dcc607e

File tree

3 files changed

+101
-116
lines changed

3 files changed

+101
-116
lines changed

mathics/builtin/atomic/numbers.py

Lines changed: 10 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,7 @@
1717
However, things like 'N[Pi, 100]' should work as expected.
1818
"""
1919

20-
from functools import lru_cache
21-
2220
import mpmath
23-
import sympy
2421

2522
from mathics.core.atoms import (
2623
Integer,
@@ -33,6 +30,7 @@
3330
from mathics.core.attributes import A_LISTABLE, A_PROTECTED, A_READ_PROTECTED
3431
from mathics.core.builtin import Builtin, Predefined
3532
from mathics.core.convert.python import from_python
33+
from mathics.core.evaluation import Evaluation
3634
from mathics.core.expression import Expression
3735
from mathics.core.list import ListExpression
3836
from mathics.core.number import (
@@ -52,100 +50,19 @@
5250
SymbolRound,
5351
)
5452
from mathics.eval.nevaluator import eval_N
55-
from mathics.eval.numbers.numbers import eval_Accuracy, eval_Precision
53+
from mathics.eval.numbers.numbers import (
54+
check_finite_decimal,
55+
convert_float_base,
56+
convert_repeating_decimal,
57+
eval_Accuracy,
58+
eval_Precision,
59+
log_n_b,
60+
)
5661

5762
SymbolIntegerDigits = Symbol("IntegerDigits")
5863
SymbolIntegerExponent = Symbol("IntegerExponent")
5964

6065

61-
@lru_cache()
62-
def log_n_b(py_n, py_b) -> int:
63-
return (
64-
int(mpmath.floor(mpmath.log(py_n, py_b))) + 1 if py_n != 0 and py_n != 1 else 1
65-
)
66-
67-
68-
def check_finite_decimal(denominator):
69-
# The rational number is finite decimal if the denominator has form 2^a * 5^b
70-
while denominator % 5 == 0:
71-
denominator = denominator / 5
72-
73-
while denominator % 2 == 0:
74-
denominator = denominator / 2
75-
76-
return True if denominator == 1 else False
77-
78-
79-
def convert_repeating_decimal(numerator, denominator, base):
80-
head = [x for x in str(numerator // denominator)]
81-
tails = []
82-
subresults = [numerator % denominator]
83-
numerator %= denominator
84-
85-
while numerator != 0: # only rational input can go to this case
86-
numerator *= base
87-
result_digit, numerator = divmod(numerator, denominator)
88-
tails.append(str(result_digit))
89-
if numerator not in subresults:
90-
subresults.append(numerator)
91-
else:
92-
break
93-
94-
for i in range(len(head) - 1, -1, -1):
95-
j = len(tails) - 1
96-
if head[i] != tails[j]:
97-
break
98-
else:
99-
del tails[j]
100-
tails.insert(0, head[i])
101-
del head[i]
102-
103-
# truncate all leading 0's
104-
if all(elem == "0" for elem in head):
105-
for i in range(0, len(tails)):
106-
if tails[0] == "0":
107-
tails = tails[1:] + [str(0)]
108-
else:
109-
break
110-
return (head, tails)
111-
112-
113-
def convert_float_base(x, base, precision=10):
114-
length_of_int = 0 if x == 0 else int(mpmath.log(x, base))
115-
# iexps = list(range(length_of_int, -1, -1))
116-
117-
def convert_int(x, base, exponents):
118-
out = []
119-
for e in range(0, exponents + 1):
120-
d = x % base
121-
out.append(d)
122-
x = x / base
123-
if x == 0:
124-
break
125-
out.reverse()
126-
return out
127-
128-
def convert_float(x, base, exponents):
129-
out = []
130-
for e in range(0, exponents):
131-
d = int(x * base)
132-
out.append(d)
133-
x = (x * base) - d
134-
if x == 0:
135-
break
136-
return out
137-
138-
int_part = convert_int(int(x), base, length_of_int)
139-
if isinstance(x, (float, sympy.Float)):
140-
# fexps = list(range(-1, -int(precision + 1), -1))
141-
real_part = convert_float(x - int(x), base, precision + 1)
142-
return int_part + real_part
143-
elif isinstance(x, int):
144-
return int_part
145-
else:
146-
raise TypeError(x)
147-
148-
14966
class Accuracy(Builtin):
15067
"""
15168
<url>
@@ -255,7 +172,7 @@ class IntegerExponent(Builtin):
255172

256173
summary_text = "number of trailing 0s in a given base"
257174

258-
def eval_two_arg_integers(self, n: Integer, b: Integer, evaluation):
175+
def eval_two_arg_integers(self, n: Integer, b: Integer, evaluation: Evaluation):
259176
"""IntegerExponent[n_Integer, b_Integer]"""
260177

261178
py_n, py_b = n.value, b.value

mathics/builtin/box/expression.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
# This is never intended to go in Mathics3 docs
2-
no_doc = True
3-
41
from typing import Optional, Sequence, Union
52

63
from mathics.core.attributes import A_PROTECTED, A_READ_PROTECTED
@@ -10,6 +7,9 @@
107
from mathics.core.list import ListExpression
118
from mathics.core.symbols import Symbol, SymbolHoldForm, ensure_context
129

10+
# This is never intended to go in Mathics3 docs
11+
no_doc = True
12+
1313

1414
def split_name(name: str) -> str:
1515
"""

mathics/eval/numbers/numbers.py

Lines changed: 88 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
Implementation of numbers handling functions.
44
"""
55

6+
from functools import lru_cache
67
from typing import Optional
78

89
import mpmath
@@ -20,6 +21,87 @@
2021
from mathics.core.symbols import SymbolPlus
2122

2223

24+
def check_finite_decimal(denominator):
25+
# The rational number is finite decimal if the denominator has form 2^a * 5^b
26+
while denominator % 5 == 0:
27+
denominator = denominator / 5
28+
29+
while denominator % 2 == 0:
30+
denominator = denominator / 2
31+
32+
return True if denominator == 1 else False
33+
34+
35+
def convert_repeating_decimal(numerator, denominator, base):
36+
head = [x for x in str(numerator // denominator)]
37+
tails = []
38+
subresults = [numerator % denominator]
39+
numerator %= denominator
40+
41+
while numerator != 0: # only rational input can go to this case
42+
numerator *= base
43+
result_digit, numerator = divmod(numerator, denominator)
44+
tails.append(str(result_digit))
45+
if numerator not in subresults:
46+
subresults.append(numerator)
47+
else:
48+
break
49+
50+
for i in range(len(head) - 1, -1, -1):
51+
j = len(tails) - 1
52+
if head[i] != tails[j]:
53+
break
54+
else:
55+
del tails[j]
56+
tails.insert(0, head[i])
57+
del head[i]
58+
59+
# truncate all leading 0's
60+
if all(elem == "0" for elem in head):
61+
for i in range(0, len(tails)):
62+
if tails[0] == "0":
63+
tails = tails[1:] + [str(0)]
64+
else:
65+
break
66+
return (head, tails)
67+
68+
69+
def convert_float_base(x, base, precision=10):
70+
length_of_int = 0 if x == 0 else int(mpmath.log(x, base))
71+
# iexps = list(range(length_of_int, -1, -1))
72+
73+
def convert_int(x, base, exponents):
74+
out = []
75+
for e in range(0, exponents + 1):
76+
d = x % base
77+
out.append(d)
78+
x = x / base
79+
if x == 0:
80+
break
81+
out.reverse()
82+
return out
83+
84+
def convert_float(x, base, exponents):
85+
out = []
86+
for e in range(0, exponents):
87+
d = int(x * base)
88+
out.append(d)
89+
x = (x * base) - d
90+
if x == 0:
91+
break
92+
return out
93+
94+
int_part = convert_int(int(x), base, length_of_int)
95+
if isinstance(x, (float, sympy.Float)):
96+
# fexps = list(range(-1, -int(precision + 1), -1))
97+
real_part = convert_float(x - int(x), base, precision + 1)
98+
return int_part + real_part
99+
elif isinstance(x, int):
100+
return int_part
101+
else:
102+
raise TypeError(x)
103+
104+
23105
def eval_Accuracy(z: BaseElement) -> Optional[float]:
24106
"""
25107
Determine the accuracy of an expression expr.
@@ -129,7 +211,7 @@ def cancel(expr):
129211
return None
130212

131213
# result = sympy.powsimp(result, deep=True)
132-
result = sympy.cancel(result)
214+
result = tracing.run_sympy(sympy.cancel, result)
133215

134216
# cancel factors out rationals, so we factor them again
135217
result = sympy_factor(result)
@@ -140,25 +222,11 @@ def cancel(expr):
140222
return expr
141223

142224

143-
def cancel(expr):
144-
if expr.has_form("Plus", None):
145-
return Expression(SymbolPlus, *[cancel(element) for element in expr.elements])
146-
else:
147-
try:
148-
result = expr.to_sympy()
149-
if result is None:
150-
return None
151-
152-
# result = sympy.powsimp(result, deep=True)
153-
result = tracing.run_sympy(sympy.cancel, result)
154-
155-
# cancel factors out rationals, so we factor them again
156-
result = sympy_factor(result)
157-
158-
return from_sympy(result)
159-
except sympy.PolynomialError:
160-
# e.g. for non-commutative expressions
161-
return expr
225+
@lru_cache()
226+
def log_n_b(py_n, py_b) -> int:
227+
return (
228+
int(mpmath.floor(mpmath.log(py_n, py_b))) + 1 if py_n != 0 and py_n != 1 else 1
229+
)
162230

163231

164232
def sympy_factor(expr_sympy):

0 commit comments

Comments
 (0)