diff --git a/CHANGES.rst b/CHANGES.rst index 41dd4d21c..c79859f93 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -8,6 +8,31 @@ New Builtins * ``CheckAbort`` * ``SetEnvironment`` +By `@davidar `_: + +* ``BellB`` +* ``DivisorSigma`` +* ``DivisorSum`` +* ``EulerE`` +* ``HypergeometricU`` +* ``IntegerPart`` +* ``IntegerPartitions`` +* ``JacobiSymbol`` +* ``KroneckerSymbol`` +* ``LambertW`` +* ``LinearRecurrence`` +* ``LucasL`` +* ``MersennePrimeExponent`` +* ``MoebiusMu`` +* ``NumberDigit`` +* ``PolygonalNumber`` +* ``PolyLog`` +* ``PowersRepresentations`` +* ``ReverseSort`` +* ``SeriesCoefficient`` +* ``SquaresR`` +* ``Subfactorial`` + ``mathics`` command line ++++++++++++++++++++++++ diff --git a/mathics/builtin/arithmetic.py b/mathics/builtin/arithmetic.py index 0313ceb94..5699b3897 100644 --- a/mathics/builtin/arithmetic.py +++ b/mathics/builtin/arithmetic.py @@ -959,6 +959,14 @@ class Sum(IterationFunction, SympyFunction): Verify algebraic identities: >> Sum[x ^ 2, {x, 1, y}] - y * (y + 1) * (2 * y + 1) / 6 = 0 + + Non-integer bounds: + >> Sum[i, {i, 1, 2.5}] + = 3 + >> Sum[i, {i, 1.1, 2.5}] + = 3.2 + >> Sum[k, {k, I, I + 1.5}] + = 1 + 2 I """ summary_text = "discrete sum" @@ -1020,7 +1028,11 @@ def to_sympy(self, expr, **kwargs) -> Optional[SympyExpression]: # If we have integer bounds, we'll use Mathics's iterator Sum # (which is Plus) - if all(hasattr(i, "is_integer") and i.is_integer for i in bounds[1:]): + if all( + (hasattr(i, "is_integer") and i.is_integer) + or (hasattr(i, "is_finite") and i.is_finite and i.is_constant()) + for i in bounds[1:] + ): # When we have integer bounds, it is better to not use Sympy but # use Mathics evaluation. We turn: # Sum[f[x], {}] into @@ -1029,9 +1041,10 @@ def to_sympy(self, expr, **kwargs) -> Optional[SympyExpression]: values = Expression(SymbolTable, *expr.elements).evaluate( evaluation ) - ret = self.get_result(values.elements).evaluate(evaluation) - # Make sure to convert the result back to sympy. - return ret.to_sympy() + if values.get_head_name() != SymbolTable.get_name(): + ret = self.get_result(values.elements).evaluate(evaluation) + # Make sure to convert the result back to sympy. + return ret.to_sympy() if None not in bounds: return sympy.summation(f_sympy, bounds) diff --git a/mathics/builtin/atomic/numbers.py b/mathics/builtin/atomic/numbers.py index df61cdf9e..e2c973b11 100644 --- a/mathics/builtin/atomic/numbers.py +++ b/mathics/builtin/atomic/numbers.py @@ -30,7 +30,7 @@ MachineReal, Rational, ) -from mathics.core.attributes import A_LISTABLE, A_PROTECTED +from mathics.core.attributes import A_LISTABLE, A_PROTECTED, A_READ_PROTECTED from mathics.core.builtin import Builtin, Predefined from mathics.core.convert.python import from_python from mathics.core.expression import Expression @@ -60,7 +60,9 @@ @lru_cache() def log_n_b(py_n, py_b) -> int: - return int(mpmath.ceil(mpmath.log(py_n, py_b))) if py_n != 0 and py_n != 1 else 1 + return ( + int(mpmath.floor(mpmath.log(py_n, py_b))) + 1 if py_n != 0 and py_n != 1 else 1 + ) def check_finite_decimal(denominator): @@ -376,6 +378,33 @@ def eval(self, n, b, evaluation): return Integer(j) +class NumberDigit(Builtin): + """ + :WMA link: + https://reference.wolfram.com/language/ref/NumberDigit.html + +
+
'NumberDigit[$x$, $n$, $b$]' +
returns the coefficient of $b^n$ in the base-$b$ representation of $x$. +
+ + >> NumberDigit[123456, 2] + = 4 + >> NumberDigit[12.3456, -1] + = 3 + + """ + + attributes = A_PROTECTED | A_READ_PROTECTED + + summary_text = "digits of a real number" + + rules = { + "NumberDigit[x_, n_Integer]": "NumberDigit[x, n, 10]", + "NumberDigit[x_, n_Integer, b_Integer]": "RealDigits[x, b, 1, n][[1]][[1]]", + } + + class RealDigits(Builtin): """ :WMA link: @@ -420,6 +449,9 @@ class RealDigits(Builtin): Return 25 digits of in base 10: >> RealDigits[Pi, 10, 25] = {{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 3, 2, 3, 8, 4, 6, 2, 6, 4, 3}, 1} + + >> RealDigits[10] + = {{1, 0}, 2} """ attributes = A_LISTABLE | A_PROTECTED @@ -445,7 +477,7 @@ def eval_rational_with_base(self, n, b, evaluation): if check_finite_decimal(n.denominator().get_int_value()) and not py_b % 2: return self.eval_with_base(n, b, evaluation) else: - exp = int(mpmath.ceil(mpmath.log(py_n, py_b))) + exp = log_n_b(py_n, py_b) (head, tails) = convert_repeating_decimal( py_n.as_numer_denom()[0], py_n.as_numer_denom()[1], py_b ) diff --git a/mathics/builtin/intfns/recurrence.py b/mathics/builtin/intfns/recurrence.py index 4d2d043c8..fe789b2b3 100644 --- a/mathics/builtin/intfns/recurrence.py +++ b/mathics/builtin/intfns/recurrence.py @@ -29,6 +29,8 @@ class Fibonacci(MPMathFunction):
'Fibonacci[$n$]'
computes the $n$th Fibonacci number. +
'Fibonacci[$n$, $x$]' +
computes the Fibonacci polynomial $F$_$n$($x$).
>> Fibonacci[0] @@ -39,6 +41,8 @@ class Fibonacci(MPMathFunction): = 55 >> Fibonacci[200] = 280571172992510140037611932413038677189525 + >> Fibonacci[7, x] + = 1 + 6 x ^ 2 + 5 x ^ 4 + x ^ 6 """ nargs = {1} @@ -47,6 +51,11 @@ class Fibonacci(MPMathFunction): mpmath_name = "fibonacci" summary_text = "Fibonacci's numbers" + rules = { + "Fibonacci[0, x_]": "0", + "Fibonacci[n_Integer?Negative, x_]": "Fibonacci[-n, x]", + } + class HarmonicNumber(MPMathFunction): """ @@ -73,6 +82,31 @@ class HarmonicNumber(MPMathFunction): sympy_name = "harmonic" +class LinearRecurrence(Builtin): + """ + :WMA link:https://reference.wolfram.com/language/ref/LinearRecurrence.html + +
+
'LinearRecurrence[$ker$, $init$, $n$]' +
computes $n$ terms of the linear recurrence with kernel $ker$ and intial values $init$ +
+ + >> LinearRecurrence[{1, 1}, {1, 1}, 10] + = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55} + >> LinearRecurrence[{1, 1}, {1, 1}, {5, 5}] + = {5} + """ + + attributes = A_PROTECTED | A_READ_PROTECTED + summary_text = "linear recurrence" + + rules = { + "LinearRecurrence[ker_List, init_List, n_Integer]": "Nest[Append[#, Reverse[ker] . Take[#, -Length[ker]]] &, init, n - Length[init]]", + "LinearRecurrence[ker_List, init_List, {n_Integer?Positive}]": "LinearRecurrence[ker, init, n][[n]]", + "LinearRecurrence[ker_List, init_List, {nmin_Integer?Positive, nmax_Integer?Positive}]": "LinearRecurrence[ker, init, nmax][[nmin;;nmax]]", + } + + # Note: WL allows StirlingS1[{2, 4, 6}, 2], but we don't (yet). class StirlingS1(Builtin): """ diff --git a/mathics/builtin/list/constructing.py b/mathics/builtin/list/constructing.py index 72c673b40..9ba9d87ac 100644 --- a/mathics/builtin/list/constructing.py +++ b/mathics/builtin/list/constructing.py @@ -522,6 +522,7 @@ class Table(IterationFunction): = {x, x, x} >> n = 0; Table[n = n + 1, {5}] = {1, 2, 3, 4, 5} + #> Clear[n] >> Table[i, {i, 4}] = {1, 2, 3, 4} >> Table[i, {i, 2, 5}] @@ -536,6 +537,14 @@ class Table(IterationFunction): 'Table' supports multi-dimensional tables: >> Table[{i, j}, {i, {a, b}}, {j, 1, 2}] = {{{a, 1}, {a, 2}}, {{b, 1}, {b, 2}}} + + Symbolic bounds: + >> Table[x, {x, a, a + 5 n, n}] + = {a, a + n, a + 2 n, a + 3 n, a + 4 n, a + 5 n} + + The lower bound is always included even for large step sizes: + >> Table[i, {i, 1, 9, Infinity}] + = {1} """ rules = { diff --git a/mathics/builtin/numbers/algebra.py b/mathics/builtin/numbers/algebra.py index 51c0a0591..6b71ec83c 100644 --- a/mathics/builtin/numbers/algebra.py +++ b/mathics/builtin/numbers/algebra.py @@ -901,6 +901,8 @@ class CoefficientList(Builtin): = {{5, d, 0, b}, {c, 0, 0, 0}, {a, 0, 0, 0}} >> CoefficientList[(x - 2 y + 3 z)^3, {x, y, z}] = {{{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}}} + >> CoefficientList[Series[Log[1-x], {x, 0, 9}], x] + = {0, -1, -1 / 2, -1 / 3, -1 / 4, -1 / 5, -1 / 6, -1 / 7, -1 / 8, -1 / 9} """ messages = { @@ -914,7 +916,7 @@ def eval_noform(self, expr, evaluation): "CoefficientList[expr_]" evaluation.message("CoefficientList", "argtu") - def eval(self, expr, form, evaluation): + def eval(self, expr: Expression, form: Expression, evaluation: Evaluation): "CoefficientList[expr_, form_]" vars = [form] if not form.has_form("List", None) else [v for v in form.elements] @@ -937,6 +939,18 @@ def eval(self, expr, form, evaluation): return ListExpression(expr) elif form.has_form("List", 0): return expr + elif expr.get_head_name() == "System`SeriesData": + coeffs: ListExpression + nmin: Integer + nmax: Integer + x, x0, coeffs, nmin, nmax, den = expr.elements + if x == form and x0 == Integer0 and den == Integer1: + return ListExpression( + *[ + coeffs.elements[i - nmin.value] if i >= nmin.value else Integer0 + for i in range(0, nmax.value) + ] + ) sympy_expr = expr.to_sympy() sympy_vars = [v.to_sympy() for v in vars] @@ -953,8 +967,7 @@ def eval(self, expr, form, evaluation): # single & multiple variables cases if not form.has_form("List", None): - return Expression( - SymbolList, + return ListExpression( *[ _coefficient( self.__class__.__name__, expr, form, Integer(n), evaluation @@ -964,8 +977,7 @@ def eval(self, expr, form, evaluation): ) elif form.has_form("List", 1): form = form.elements[0] - return Expression( - SymbolList, + return ListExpression( *[ _coefficient( self.__class__.__name__, expr, form, Integer(n), evaluation diff --git a/mathics/builtin/numbers/calculus.py b/mathics/builtin/numbers/calculus.py index 16a0ea3e4..5c6bd084f 100644 --- a/mathics/builtin/numbers/calculus.py +++ b/mathics/builtin/numbers/calculus.py @@ -63,6 +63,7 @@ SymbolConditionalExpression, SymbolD, SymbolDerivative, + SymbolIndeterminate, SymbolInfinity, SymbolInfix, SymbolIntegrate, @@ -1731,6 +1732,50 @@ def eval_multivariate_series(self, f, varspec, evaluation: Evaluation): return None +class SeriesCoefficient(Builtin): + """ + :WMA link:https://reference.wolfram.com/language/ref/SeriesCoefficient.html + +
+
'SeriesCoefficient[$series$, $n$]' +
Find the $n$th coefficient in the given $series$ +
+ + >> SeriesCoefficient[Series[Exp[Sin[x]], {x, 0, 10}], 8] + = 31 / 5760 + >> SeriesCoefficient[Exp[-x], {x, 0, 5}] + = -1 / 120 + + >> SeriesCoefficient[SeriesData[x, c, Table[i^2, {i, 10}], 7, 17, 3], 14/3] + = 64 + >> SeriesCoefficient[SeriesData[x, c, Table[i^2, {i, 10}], 7, 17, 3], 6/3] + = 0 + >> SeriesCoefficient[SeriesData[x, c, Table[i^2, {i, 10}], 7, 17, 3], 17/3] + = Indeterminate + """ + + attributes = A_PROTECTED + summary_text = "power series coefficient" + + rules = { + "SeriesCoefficient[f_, {x_Symbol, x0_, n_Integer}]": "SeriesCoefficient[Series[f, {x, x0, n}], n]" + } + + def eval(self, series: Expression, n: Rational, evaluation: Evaluation): + """SeriesCoefficient[series_SeriesData, n_]""" + coeffs: ListExpression + nmin: Integer + nmax: Integer + den: Integer + coeffs, nmin, nmax, den = series.elements[2:] + index = n.value * den.value - nmin.value + if index < 0: + return Integer0 + if index >= nmax.value - nmin.value: + return SymbolIndeterminate + return coeffs[index] + + class SeriesData(Builtin): """ diff --git a/mathics/builtin/specialfns/bessel.py b/mathics/builtin/specialfns/bessel.py index ebe20fea2..f5f2453e1 100644 --- a/mathics/builtin/specialfns/bessel.py +++ b/mathics/builtin/specialfns/bessel.py @@ -558,6 +558,28 @@ class HankelH2(_Bessel): sympy_name = "hankel2" +class HypergeometricU(MPMathFunction): + """ + + :Confluent hypergeometric function: https://en.wikipedia.org/wiki/Confluent_hypergeometric_function ( + :mpmath: https://mpmath.org/doc/current/functions/bessel.html#mpmath.hyperu, + :WMA: https://reference.wolfram.com/language/ref/HypergeometricU.html) +
+
'HypergeometricU[$a$, $b$, $z$]' +
returns $U$($a$, $b$, $z$). +
+ + >> HypergeometricU[3, 2, 1.] + = 0.105479 + """ + + attributes = A_LISTABLE | A_NUMERIC_FUNCTION | A_PROTECTED | A_READ_PROTECTED + summary_text = "Tricomi confluent hypergeometric function" + mpmath_name = "hyperu" + sympy_name = "" + nargs = {3} + + # Kelvin Functions diff --git a/mathics/builtin/specialfns/expintegral.py b/mathics/builtin/specialfns/expintegral.py index 6bf73e001..14602ccff 100644 --- a/mathics/builtin/specialfns/expintegral.py +++ b/mathics/builtin/specialfns/expintegral.py @@ -5,7 +5,13 @@ """ -from mathics.core.builtin import MPMathFunction +from mathics.core.attributes import ( + A_LISTABLE, + A_NUMERIC_FUNCTION, + A_PROTECTED, + A_READ_PROTECTED, +) +from mathics.core.builtin import Builtin, MPMathFunction class ExpIntegralE(MPMathFunction): @@ -14,13 +20,14 @@ class ExpIntegralE(MPMathFunction):
'ExpIntegralE[$n$, $z$]' -
returns the exponential integral function $E_n(z)$. +
returns the exponential integral function $E$_$n$($z$).
>> ExpIntegralE[2.0, 2.0] = 0.0375343 """ + attributes = A_LISTABLE | A_NUMERIC_FUNCTION | A_PROTECTED | A_READ_PROTECTED summary_text = "exponential integral function of order n" nargs = {2} sympy_name = "expint" @@ -33,7 +40,7 @@ class ExpIntegralEi(MPMathFunction):
'ExpIntegralEi[$z$]' -
returns the exponential integral function $Ei(z)$. +
returns the exponential integral function Ei($z$).
>> ExpIntegralEi[2.0] @@ -45,6 +52,27 @@ class ExpIntegralEi(MPMathFunction): mpmath_name = "ei" +class LambertW(Builtin): + """ + :Lambert W-Function: https://mathworld.wolfram.com/LambertW-Function.html + +
+
'LambertW[$k$, $z$]' +
alias for ProductLog[$k$, $z$]. +
+ + >> LambertW[k, z] + = ProductLog[k, z] + """ + + attributes = A_LISTABLE | A_PROTECTED + summary_text = "undocumented alias" + rules = { + "LambertW[z_]": "ProductLog[z]", + "LambertW[k_, z_]": "ProductLog[k, z]", + } + + class ProductLog(MPMathFunction): """ :WMA link:https://reference.wolfram.com/language/ref/ProductLog.html @@ -69,6 +97,7 @@ class ProductLog(MPMathFunction): = -Graphics- """ + attributes = A_LISTABLE | A_PROTECTED | A_READ_PROTECTED summary_text = "Lambert's W function" sympy_name = "LambertW" # function called LambertW in SymPy mpmath_name = "lambertw" diff --git a/mathics/builtin/specialfns/gamma.py b/mathics/builtin/specialfns/gamma.py index 9c51c13e5..8cedadb74 100644 --- a/mathics/builtin/specialfns/gamma.py +++ b/mathics/builtin/specialfns/gamma.py @@ -9,7 +9,12 @@ import sympy from mathics.core.atoms import Integer, Integer0, Number -from mathics.core.attributes import A_LISTABLE, A_NUMERIC_FUNCTION, A_PROTECTED +from mathics.core.attributes import ( + A_LISTABLE, + A_NUMERIC_FUNCTION, + A_PROTECTED, + A_READ_PROTECTED, +) from mathics.core.builtin import ( MPMathFunction, MPMathMultiFunction, @@ -160,7 +165,7 @@ class Factorial(PostfixOperator, MPMathFunction): """ - attributes = A_NUMERIC_FUNCTION | A_PROTECTED + attributes = A_LISTABLE | A_NUMERIC_FUNCTION | A_PROTECTED | A_READ_PROTECTED mpmath_name = "factorial" operator = "!" @@ -195,7 +200,7 @@ class Factorial2(PostfixOperator, MPMathFunction): = 3.35237 """ - attributes = A_NUMERIC_FUNCTION | A_PROTECTED + attributes = A_LISTABLE | A_NUMERIC_FUNCTION | A_PROTECTED | A_READ_PROTECTED operator = "!!" mpmath_name = "fac2" sympy_name = "factorial2" @@ -513,3 +518,26 @@ class StieltjesGamma(SympyFunction): summary_text = "Stieltjes' function" sympy_name = "stieltjes" + + +class Subfactorial(MPMathFunction): + """ + + :Derangement: https://en.wikipedia.org/wiki/Derangement ( + :SymPy: https://docs.sympy.org/latest/modules/functions/combinatorial.html#sympy.functions.combinatorial.factorials.subfactorial, + :WMA: https://reference.wolfram.com/language/ref/Subfactorial.html) + +
+
'Subfactorial[$n$]' +
computes the subfactorial of $n$. +
+ + >> Subfactorial[6] + = 265 + """ + + attributes = A_LISTABLE | A_NUMERIC_FUNCTION | A_PROTECTED | A_READ_PROTECTED + + mpmath_name = "subfactorial" + sympy_name = "subfactorial" + summary_text = "subfactorial" diff --git a/mathics/builtin/specialfns/zeta.py b/mathics/builtin/specialfns/zeta.py index dd1e49e8f..c2ea553f6 100644 --- a/mathics/builtin/specialfns/zeta.py +++ b/mathics/builtin/specialfns/zeta.py @@ -5,9 +5,17 @@ """ import mpmath - +import sympy + +from mathics.core.attributes import ( + A_LISTABLE, + A_NUMERIC_FUNCTION, + A_PROTECTED, + A_READ_PROTECTED, +) from mathics.core.builtin import MPMathFunction from mathics.core.convert.mpmath import from_mpmath +from mathics.core.convert.sympy import from_sympy class LerchPhi(MPMathFunction): @@ -28,6 +36,7 @@ class LerchPhi(MPMathFunction): = 17.1973 """ + attributes = A_LISTABLE | A_NUMERIC_FUNCTION | A_PROTECTED | A_READ_PROTECTED mpmath_name = "lerchphi" sympy_name = "lerchphi" summary_text = "Lerch's trascendental ϕ function" @@ -45,6 +54,36 @@ def eval(self, z, s, a, evaluation): # return sympy.expand_func(sympy.lerchphi(py_z, py_s, py_a)) +class PolyLog(MPMathFunction): + """ + + :WMA link: + https://reference.wolfram.com/language/ref/PolyLog.html + +
+
'PolyLog[$n$, $z$]' +
returns the polylogarithm function Li_$n$($z$). +
+ + >> PolyLog[s, 1] + = Zeta[s] + >> PolyLog[-7, I] //Chop + = 136. + """ + + attributes = A_LISTABLE | A_NUMERIC_FUNCTION | A_PROTECTED | A_READ_PROTECTED + summary_text = "Polylogarithm function" + sympy_name = "polylog" + mpmath_name = "polylog" + + def eval(self, n, z, evaluation): + "PolyLog[n_, z_]" + try: + return from_mpmath(mpmath.polylog(n.to_python(), z.to_python())) + except: + return from_sympy(sympy.polylog(n.to_sympy(), z.to_sympy())) + + class Zeta(MPMathFunction): """ @@ -63,9 +102,10 @@ class Zeta(MPMathFunction): = 0.0235936 + 0.0014078 I """ + attributes = A_LISTABLE | A_NUMERIC_FUNCTION | A_PROTECTED | A_READ_PROTECTED summary_text = "Riemann's ζ function" sympy_name = "zeta" mpmath_name = "zeta" -# TODO: PolyLog, ReimannSiegelTheta, ReimannSiegelZ, ReimannXi, ZetaZero +# TODO: ReimannSiegelTheta, ReimannSiegelZ, ReimannXi, ZetaZero diff --git a/mathics/core/atoms.py b/mathics/core/atoms.py index a265b52c1..936934396 100644 --- a/mathics/core/atoms.py +++ b/mathics/core/atoms.py @@ -775,8 +775,10 @@ def to_python(self, *args, **kwargs): self.real.to_python(*args, **kwargs), self.imag.to_python(*args, **kwargs) ) - def to_mpmath(self): - return mpmath.mpc(self.real.to_mpmath(), self.imag.to_mpmath()) + def to_mpmath(self, precision: Optional[int] = None): + return mpmath.mpc( + self.real.to_mpmath(precision), self.imag.to_mpmath(precision) + ) def default_format(self, evaluation, form) -> str: return "Complex[%s, %s]" % ( diff --git a/mathics/core/builtin.py b/mathics/core/builtin.py index 394f2b3d6..adff89932 100644 --- a/mathics/core/builtin.py +++ b/mathics/core/builtin.py @@ -30,6 +30,7 @@ Integer, Integer0, Integer1, + IntegerM1, MachineReal, Number, PrecisionReal, @@ -62,6 +63,8 @@ Symbol, SymbolFalse, SymbolPlus, + SymbolPower, + SymbolTimes, SymbolTrue, ensure_context, strip_context, @@ -923,18 +926,26 @@ def eval_iter(self, expr, i, imin, imax, di, evaluation): return result return - index = imin.evaluate(evaluation) + index = Integer0 + imin = imin.evaluate(evaluation) imax = imax.evaluate(evaluation) di = di.evaluate(evaluation) + # (imax - imin) / di + normalised_range = Expression( + Symbol("System`Chop"), + Expression( + SymbolTimes, + Expression(SymbolPlus, imax, Expression(SymbolTimes, IntegerM1, imin)), + Expression(SymbolPower, di, IntegerM1), + ), + ).evaluate(evaluation) + result = [] - compare_type = ( - SymbolGreaterEqual - if Expression(SymbolLess, di, Integer0).evaluate(evaluation).to_python() - else SymbolLessEqual - ) while True: - cont = Expression(compare_type, index, imax).evaluate(evaluation) + cont = Expression(SymbolLessEqual, index, normalised_range).evaluate( + evaluation + ) if cont is SymbolFalse: break if cont is not SymbolTrue: @@ -944,7 +955,19 @@ def eval_iter(self, expr, i, imin, imax, di, evaluation): evaluation.check_stopped() try: - item = dynamic_scoping(expr.evaluate, {i.name: index}, evaluation) + item = dynamic_scoping( + expr.evaluate, + { + i.name: ( + Expression( + SymbolPlus, imin, Expression(SymbolTimes, di, index) + ).evaluate(evaluation) + if index.value > 0 + else imin + ) + }, + evaluation, + ) result.append(item) except ContinueInterrupt: if self.allow_loopcontrol: @@ -961,7 +984,7 @@ def eval_iter(self, expr, i, imin, imax, di, evaluation): return e.expr else: raise - index = Expression(SymbolPlus, index, di).evaluate(evaluation) + index = Expression(SymbolPlus, index, Integer1).evaluate(evaluation) return self.get_result(result) def eval_list(self, expr, i, items, evaluation): diff --git a/mathics/main.py b/mathics/main.py index a29ee0d9c..0c3c53dce 100755 --- a/mathics/main.py +++ b/mathics/main.py @@ -124,7 +124,7 @@ def __init__( "NONE": (["", "", "", ""], ["", "", "", ""]), "LINUX": ( ["\033[32m", "\033[1m", "\033[0m\033[32m", "\033[39m"], - ["\033[31m", "\033[1m", "\033[0m\033[32m", "\033[39m"], + ["\033[31m", "\033[1m", "\033[0m\033[31m", "\033[39m"], ), "LIGHTBG": ( ["\033[34m", "\033[1m", "\033[22m", "\033[39m"], diff --git a/test/builtin/drawing/test_plot.py b/test/builtin/drawing/test_plot.py index 4e512a1b9..670ddbaf4 100644 --- a/test/builtin/drawing/test_plot.py +++ b/test/builtin/drawing/test_plot.py @@ -126,7 +126,7 @@ "Background 3D", ), ( - "Graphics3D[Point[Table[{Sin[t], Cos[t], 0}, {t, 0, 2. Pi, Pi / 15.}]]] // TeXForm//ToString", + "Graphics3D[Point[Table[{Sin[t], Cos[t], 0}, {t, 0, 2. Pi, Pi / 15.}]]] //Chop//TeXForm//ToString", None, ( "\n\\begin{asy}\nimport three;\nimport solids;\nsize(6.6667cm, 6.6667cm);\n" @@ -136,12 +136,12 @@ "(0.58779,0.80902,0)--(0.74314,0.66913,0)--(0.86603,0.5,0)--(0.95106,0.30902,0)--" "(0.99452,0.10453,0)--(0.99452,-0.10453,0)--(0.95106,-0.30902,0)--(0.86603,-0.5,0)" "--(0.74314,-0.66913,0)--(0.58779,-0.80902,0)--(0.40674,-0.91355,0)--" - "(0.20791,-0.97815,0)--(5.6655e-16,-1,0)--(-0.20791,-0.97815,0)--" + "(0.20791,-0.97815,0)--(0,-1,0)--(-0.20791,-0.97815,0)--" "(-0.40674,-0.91355,0)--(-0.58779,-0.80902,0)--(-0.74314,-0.66913,0)--" "(-0.86603,-0.5,0)--(-0.95106,-0.30902,0)--(-0.99452,-0.10453,0)--" "(-0.99452,0.10453,0)--(-0.95106,0.30902,0)--(-0.86603,0.5,0)--" "(-0.74314,0.66913,0)--(-0.58779,0.80902,0)--(-0.40674,0.91355,0)--" - "(-0.20791,0.97815,0)--(1.5314e-15,1,0)--cycle;dot(g, rgb(0, 0, 0));\n" + "(-0.20791,0.97815,0)--(0,1,0)--cycle;dot(g, rgb(0, 0, 0));\n" "draw(((-0.99452,-1,-1)--(0.99452,-1,-1)), rgb(0.4, 0.4, 0.4)+linewidth(1));\n" "draw(((-0.99452,1,-1)--(0.99452,1,-1)), rgb(0.4, 0.4, 0.4)+linewidth(1));\n" "draw(((-0.99452,-1,1)--(0.99452,-1,1)), rgb(0.4, 0.4, 0.4)+linewidth(1));\n" diff --git a/test/builtin/list/test_constructing.py b/test/builtin/list/test_constructing.py index e974ab38e..22159b969 100644 --- a/test/builtin/list/test_constructing.py +++ b/test/builtin/list/test_constructing.py @@ -63,6 +63,11 @@ def test_range(str_expr, str_expected, failure_message): "{-0.2, 0.8, 1.8, 2.8, 3.8}", None, ), + ( + "Table[i, {i, 1, 9, 0}]", + "Table[i, {i, 1, 9, 0}]", + "Table::iterb: Iterator does not have appropriate bounds.", + ), ], ) def test_table(str_expr, str_expected, failure_message):