Skip to content

Commit e4bb914

Browse files
authored
Miscellaneous fixes (#1153)
This just fixes a few crashes I was seeing running Mathics through OEIS programs, with corresponding doctests (and a few more type error fixes)
1 parent 732d33f commit e4bb914

File tree

8 files changed

+54
-20
lines changed

8 files changed

+54
-20
lines changed

mathics/builtin/atomic/numbers.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -486,11 +486,14 @@ def eval_with_base(self, n, b, evaluation, nr_elements=None, pos=None):
486486
digits = []
487487
if not py_b == 10:
488488
digits = convert_float_base(py_n, py_b, display_len - exp)
489-
# truncate all the leading 0's
490-
i = 0
491-
while digits and digits[i] == 0:
492-
i += 1
493-
digits = digits[i:]
489+
if all(d == 0 for d in digits):
490+
digits = [0]
491+
else:
492+
# truncate all the leading 0's
493+
i = 0
494+
while digits and digits[i] == 0:
495+
i += 1
496+
digits = digits[i:]
494497

495498
if not isinstance(n, Integer):
496499
if len(digits) > display_len:

mathics/builtin/list/constructing.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,14 +184,21 @@ class Normal(Builtin):
184184
<dd> Brings special expressions to a normal expression from different special \
185185
forms.
186186
</dl>
187+
188+
>> Normal[Pi]
189+
= Pi
190+
>> Series[Exp[x], {x, 0, 5}]
191+
= 1 + x + 1 / 2 x ^ 2 + 1 / 6 x ^ 3 + 1 / 24 x ^ 4 + 1 / 120 x ^ 5 + O[x] ^ 6
192+
>> Normal[%]
193+
= 1 + x + x ^ 2 / 2 + x ^ 3 / 6 + x ^ 4 / 24 + x ^ 5 / 120
187194
"""
188195

189196
summary_text = "convert objects to normal expressions"
190197

191198
def eval_general(self, expr: Expression, evaluation: Evaluation):
192199
"Normal[expr_]"
193200
if isinstance(expr, Atom):
194-
return
201+
return expr
195202
if expr.has_form("RootSum", 2):
196203
return from_sympy(expr.to_sympy().doit(roots=True))
197204
return Expression(

mathics/builtin/list/rearrange.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@
88
import functools
99
from collections import defaultdict
1010
from itertools import chain
11-
from typing import Callable
11+
from typing import Callable, Optional
1212

13-
from mathics.core.atoms import Integer, Integer0
13+
from mathics.core.atoms import Integer, Integer0, Number
1414
from mathics.core.attributes import A_FLAT, A_ONE_IDENTITY, A_PROTECTED
1515
from mathics.core.builtin import Builtin, MessageException
16+
from mathics.core.element import BaseElement
1617
from mathics.core.evaluation import Evaluation
1718
from mathics.core.expression import Expression, structure
1819
from mathics.core.expression_predefined import MATHICS3_INFINITY
@@ -655,6 +656,7 @@ class Flatten(Builtin):
655656
"Level `1` specified in `2` exceeds the levels, `3`, "
656657
"which can be flattened together in `4`."
657658
),
659+
"normal": "Nonatomic expression expected at position `1` in `2`.",
658660
}
659661

660662
rules = {
@@ -759,21 +761,25 @@ def insert_element(elements):
759761

760762
return Expression(h, *insert_element(elements))
761763

762-
def eval(self, expr, n, h, evaluation):
764+
def eval(self, expr: BaseElement, n: Number, h, evaluation):
763765
"Flatten[expr_, n_, h_]"
764766

767+
n_int: Optional[int]
765768
if n.sameQ(MATHICS3_INFINITY):
766-
n = -1 # a negative number indicates an unbounded level
769+
n_int = -1 # a negative number indicates an unbounded level
767770
else:
768771
n_int = n.get_int_value()
769772
# Here we test for negative since in Mathics Flatten[] as opposed to flatten_with_respect_to_head()
770773
# negative numbers (and None) are not allowed.
771774
if n_int is None or n_int < 0:
772775
evaluation.message("Flatten", "flpi", n)
773776
return
774-
n = n_int
775777

776-
return expr.flatten_with_respect_to_head(h, level=n)
778+
if not isinstance(expr, Expression):
779+
evaluation.message("Flatten", "normal", 1, expr)
780+
return
781+
782+
return expr.flatten_with_respect_to_head(h, level=n_int)
777783

778784

779785
class GatherBy(_GatherOperation):
@@ -814,14 +820,15 @@ class GatherBy(_GatherOperation):
814820
summary_text = "gather based on values of a function applied to elements"
815821
_bin = _GatherBin
816822

817-
def eval(self, values, func, evaluation: Evaluation):
823+
def eval(self, values: BaseElement, func, evaluation: Evaluation):
818824
"%(name)s[values_, func_]"
819825

820826
if not self._check_list(values, func, evaluation):
821827
return
822828

823829
keys = Expression(SymbolMap, func, values).evaluate(evaluation)
824-
if len(keys.elements) != len(values.elements):
830+
assert keys is not None
831+
if len(keys.get_elements()) != len(values.get_elements()):
825832
return
826833

827834
return self._gather(keys, values, _FastEquivalence())

mathics/builtin/numbers/algebra.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,8 @@ class CoefficientList(Builtin):
899899
= {{{0, 0, 0, 27}, {0, 0, -54, 0}, {0, 36, 0, 0}, {-8, 0, 0, 0}}, {{0, 0, 27, 0}, {0, -36, 0, 0}, {12, 0, 0, 0}, {0, 0, 0, 0}}, {{0, 9, 0, 0}, {-6, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, {{1, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}}
900900
>> CoefficientList[Series[Log[1-x], {x, 0, 9}], x]
901901
= {0, -1, -1 / 2, -1 / 3, -1 / 4, -1 / 5, -1 / 6, -1 / 7, -1 / 8, -1 / 9}
902+
>> CoefficientList[Series[2x, {x, 0, 9}], x]
903+
= {0, 2}
902904
"""
903905

904906
messages = {
@@ -944,7 +946,9 @@ def eval(self, expr: Expression, form: Expression, evaluation: Evaluation):
944946
return ListExpression(
945947
*[
946948
coeffs.elements[i - nmin.value] if i >= nmin.value else Integer0
947-
for i in range(0, nmax.value)
949+
for i in range(
950+
0, min(nmax.value, nmin.value + len(coeffs.elements))
951+
)
948952
]
949953
)
950954

mathics/builtin/numbers/calculus.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1818,6 +1818,8 @@ class SeriesCoefficient(Builtin):
18181818
= 31 / 5760
18191819
>> SeriesCoefficient[Exp[-x], {x, 0, 5}]
18201820
= -1 / 120
1821+
>> SeriesCoefficient[2x, {x, 0, 2}]
1822+
= 0
18211823
18221824
>> SeriesCoefficient[SeriesData[x, c, Table[i^2, {i, 10}], 7, 17, 3], 14/3]
18231825
= 64
@@ -1842,10 +1844,10 @@ def eval(self, series: Expression, n: Rational, evaluation: Evaluation):
18421844
den: Integer
18431845
coeffs, nmin, nmax, den = series.elements[2:]
18441846
index = n.value * den.value - nmin.value
1845-
if index < 0:
1846-
return Integer0
18471847
if index >= nmax.value - nmin.value:
18481848
return SymbolIndeterminate
1849+
if index < 0 or index >= len(coeffs.elements):
1850+
return Integer0
18491851
return coeffs[index]
18501852

18511853

mathics/builtin/numbers/numbertheory.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,8 @@ class EulerPhi(SympyFunction):
217217
'EulerPhi' of a negative integer is same as its positive counterpart:
218218
>> EulerPhi[-11] == EulerPhi[11]
219219
= True
220+
>> EulerPhi[0]
221+
= 0
220222
221223
Large arguments are computed quickly:
222224
>> EulerPhi[40!]
@@ -238,8 +240,16 @@ class EulerPhi(SympyFunction):
238240

239241
def eval(self, n: Integer, evaluation: Evaluation):
240242
"EulerPhi[n_Integer]"
243+
if n.is_zero:
244+
return Integer0
241245
return super().eval(abs(n), evaluation)
242246

247+
def to_sympy(self, expr, **kwargs):
248+
try:
249+
return super().to_sympy(expr, **kwargs)
250+
except ValueError: # n must be a positive integer
251+
return None
252+
243253

244254
class FactorInteger(Builtin):
245255
"""
@@ -441,7 +451,7 @@ class IntegerPartitions(Builtin):
441451
"IntegerPartitions[n_Integer, All]": "IntegerPartitions[n, n]",
442452
"IntegerPartitions[n_Integer, k_Integer]": "IntegerPartitions[n, {1, k}]",
443453
"IntegerPartitions[n_Integer, {k_Integer}]": "IntegerPartitions[n, {k, k}]",
444-
"IntegerPartitions[n_Integer, kspec_, s_List] /; SubsetQ[Range[n], s]": "Select[IntegerPartitions[n, kspec], SubsetQ[s, #] &]",
454+
"IntegerPartitions[n_Integer, kspec_, s_List] /; SubsetQ[Range[n], s] && s == Union[s]": "Select[IntegerPartitions[n, kspec], SubsetQ[s, #] &]",
445455
"IntegerPartitions[n_Integer, kspec_, All]": "IntegerPartitions[n, kspec]",
446456
"IntegerPartitions[n_Integer, kspec_, sspec_, m_]": "Take[IntegerPartitions[n, kspec, sspec], m]",
447457
"IntegerPartitions[n_Integer, {k_Integer}, s_List]": "ReverseSort@Select[Union[ReverseSort /@ Tuples[s, k]], Total[#] == n &]",

mathics/core/element.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ def get_head_name(self) -> str:
301301
def get_float_value(self, permit_complex=False):
302302
return None
303303

304-
def get_int_value(self):
304+
def get_int_value(self) -> Optional[int]:
305305
return None
306306

307307
def get_lookup_name(self) -> str:

mathics/core/expression.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -662,7 +662,7 @@ def sequence(element):
662662
return self._flatten_sequence(sequence, evaluation)
663663

664664
def flatten_with_respect_to_head(
665-
self, head, pattern_only=False, callback=None, level=100
665+
self, head: Symbol, pattern_only=False, callback=None, level=100
666666
) -> "Expression":
667667
"""
668668
Flatten elements in self which have `head` in them.
@@ -1304,6 +1304,7 @@ def flatten_callback(new_elements, old):
13041304
element.unevaluated = old.unevaluated
13051305

13061306
if A_FLAT & attributes:
1307+
assert isinstance(new._head, Symbol)
13071308
new = new.flatten_with_respect_to_head(new._head, callback=flatten_callback)
13081309
if new.elements_properties is None:
13091310
new._build_elements_properties()

0 commit comments

Comments
 (0)