diff --git a/mathics/builtin/arithmetic.py b/mathics/builtin/arithmetic.py index 19b2c3393..832cab44c 100644 --- a/mathics/builtin/arithmetic.py +++ b/mathics/builtin/arithmetic.py @@ -279,45 +279,6 @@ class Complex_(Builtin): = 1 + 2 I / 3 >> Abs[Complex[3, 4]] = 5 - - #> OutputForm[Complex[2.0 ^ 40, 3]] - = 1.09951×10^12 + 3. I - #> InputForm[Complex[2.0 ^ 40, 3]] - = 1.099511627776*^12 + 3.*I - - #> -2 / 3 - I - = -2 / 3 - I - - #> Complex[10, 0] - = 10 - - #> 0. + I - = 0. + 1. I - - #> 1 + 0 I - = 1 - #> Head[%] - = Integer - - #> Complex[0.0, 0.0] - = 0. + 0. I - #> 0. I - = 0. - #> 0. + 0. I - = 0. - - #> 1. + 0. I - = 1. - #> 0. + 1. I - = 0. + 1. I - - ## Check Nesting Complex - #> Complex[1, Complex[0, 1]] - = 0 - #> Complex[1, Complex[1, 0]] - = 1 + I - #> Complex[1, Complex[1, 1]] - = I """ summary_text = "head for complex numbers" @@ -442,10 +403,6 @@ class Conjugate(MPMathFunction): >> Conjugate[{{1, 2 + I 4, a + I b}, {I}}] = {{1, 2 - 4 I, Conjugate[a] - I Conjugate[b]}, {-I}} - ## Issue #272 - #> {Conjugate[Pi], Conjugate[E]} - = {Pi, E} - >> Conjugate[1.5 + 2.5 I] = 1.5 - 2.5 I """ @@ -485,11 +442,6 @@ class DirectedInfinity(SympyFunction): >> DirectedInfinity[0] = ComplexInfinity - #> DirectedInfinity[1+I]+DirectedInfinity[2+I] - = (2 / 5 + I / 5) Sqrt[5] Infinity + (1 / 2 + I / 2) Sqrt[2] Infinity - - #> DirectedInfinity[Sqrt[3]] - = Infinity """ summary_text = "infinite quantity with a defined direction in the complex plane" @@ -703,11 +655,6 @@ class Im(SympyFunction): >> Plot[{Sin[a], Im[E^(I a)]}, {a, 0, 2 Pi}] = -Graphics- - - #> Re[0.5 + 2.3 I] - = 0.5 - #> % // Precision - = MachinePrecision """ summary_text = "imaginary part" @@ -740,10 +687,6 @@ class Integer_(Builtin): >> Head[5] = Integer - - ## Test large Integer comparison bug - #> {a, b} = {2^10000, 2^10000 + 1}; {a == b, a < b, a <= b} - = {False, True, True} """ summary_text = "head for integer numbers" @@ -789,10 +732,6 @@ class Product(IterationFunction, SympyFunction): >> primorial[12] = 7420738134810 - ## Used to be a bug in sympy, but now it is solved exactly! - ## Again a bug in sympy - regressions between 0.7.3 and 0.7.6 (and 0.7.7?) - ## #> Product[1 + 1 / i ^ 2, {i, Infinity}] - ## = 1 / ((-I)! I!) """ summary_text = "discrete product" @@ -847,9 +786,6 @@ class Rational_(Builtin): >> Rational[1, 2] = 1 / 2 - - #> -2/3 - = -2 / 3 """ summary_text = "head for rational numbers" @@ -878,11 +814,6 @@ class Re(SympyFunction): >> Plot[{Cos[a], Re[E^(I a)]}, {a, 0, 2 Pi}] = -Graphics- - - #> Im[0.5 + 2.3 I] - = 2.3 - #> % // Precision - = MachinePrecision """ summary_text = "real part" @@ -919,61 +850,6 @@ class Real_(Builtin): >> Head[x] = Real - ## Formatting tests - #> 1. * 10^6 - = 1.×10^6 - #> 1. * 10^5 - = 100000. - #> -1. * 10^6 - = -1.×10^6 - #> -1. * 10^5 - = -100000. - #> 1. * 10^-6 - = 1.×10^-6 - #> 1. * 10^-5 - = 0.00001 - #> -1. * 10^-6 - = -1.×10^-6 - #> -1. * 10^-5 - = -0.00001 - - ## Mathematica treats zero strangely - #> 0.0000000000000 - = 0. - #> 0.0000000000000000000000000000 - = 0.×10^-28 - - ## Parse *^ Notation - #> 1.5×10^24 - = 1.5×10^24 - #> 1.5*^+24 - = 1.5×10^24 - #> 1.5*^-24 - = 1.5×10^-24 - - ## Don't accept *^ with spaces - #> 1.5 *^10 - : "1.5 *" cannot be followed by "^10" (line 1 of ""). - #> 1.5*^ 10 - : "1.5*" cannot be followed by "^ 10" (line 1 of ""). - - ## Issue654 - #> 1^^2 - : Requested base 1 in 1^^2 should be between 2 and 36. - : Expression cannot begin with "1^^2" (line 1 of ""). - #> 2^^0101 - = 5 - #> 2^^01210 - : Digit at position 3 in 01210 is too large to be used in base 2. - : Expression cannot begin with "2^^01210" (line 1 of ""). - #> 16^^5g - : Digit at position 2 in 5g is too large to be used in base 16. - : Expression cannot begin with "16^^5g" (line 1 of ""). - #> 36^^0123456789abcDEFxyzXYZ - = 14142263610074677021975869033659 - #> 37^^3 - : Requested base 37 in 37^^3 should be between 2 and 36. - : Expression cannot begin with "37^^3" (line 1 of ""). """ summary_text = "head for real numbers" @@ -999,7 +875,7 @@ class RealNumberQ(Test): >> RealNumberQ[0 * I] = True >> RealNumberQ[0.0 * I] - = True + = False """ attributes = A_NO_ATTRIBUTES @@ -1076,12 +952,6 @@ class Sum(IterationFunction, SympyFunction): >> Sum[x ^ 2, {x, 1, y}] - y * (y + 1) * (2 * y + 1) / 6 = 0 - ## >> (-1 + a^n) Sum[a^(k n), {k, 0, m-1}] // Simplify - ## = -1 + (a ^ n) ^ m # this is what I am getting - ## = Piecewise[{{m (-1 + a ^ n), a ^ n == 1}, {-1 + (a ^ n) ^ m, True}}] - - #> a=Sum[x^k*Sum[y^l,{l,0,4}],{k,0,4}]] - : "a=Sum[x^k*Sum[y^l,{l,0,4}],{k,0,4}]" cannot be followed by "]" (line 1 of ""). ## Issue #302 ## The sum should not converge since the first term is 1/0. diff --git a/mathics/builtin/numbers/calculus.py b/mathics/builtin/numbers/calculus.py index 3287d35c7..dffebb42c 100644 --- a/mathics/builtin/numbers/calculus.py +++ b/mathics/builtin/numbers/calculus.py @@ -697,7 +697,6 @@ def diff(evaluation): def eval_with_x_tuple(self, f, xtuple, evaluation: Evaluation, options: dict): "%(name)s[f_, xtuple_, OptionsPattern[]]" f_val = f.evaluate(evaluation) - if f_val.has_form("Equal", 2): f = Expression(SymbolPlus, f_val.elements[0], f_val.elements[1]) diff --git a/mathics/builtin/numbers/constants.py b/mathics/builtin/numbers/constants.py index 73779ff6a..32c0715cd 100644 --- a/mathics/builtin/numbers/constants.py +++ b/mathics/builtin/numbers/constants.py @@ -263,14 +263,6 @@ class ComplexInfinity(_SympyConstant): = ComplexInfinity >> FullForm[ComplexInfinity] = DirectedInfinity[] - - ## Issue689 - #> ComplexInfinity + ComplexInfinity - : Indeterminate expression ComplexInfinity + ComplexInfinity encountered. - = Indeterminate - #> ComplexInfinity + Infinity - : Indeterminate expression ComplexInfinity + Infinity encountered. - = Indeterminate """ summary_text = "infinite complex quantity of undetermined direction" @@ -302,15 +294,6 @@ class Degree(_MPMathConstant, _NumpyConstant, _SympyConstant): >> N[\\[Degree]] == N[Degree] = True - - #> Cos[Degree[x]] - = Cos[Degree[x]] - - - #> N[Degree] - = 0.0174533 - #> N[Degree, 30] - = 0.0174532925199432957692369076849 """ summary_text = "conversion factor from radians to degrees" @@ -367,9 +350,6 @@ class E(_MPMathConstant, _NumpyConstant, _SympyConstant): = 2.71828 >> N[E, 50] = 2.7182818284590452353602874713526624977572470937000 - - #> 5. E - = 13.5914 """ summary_text = "exponential constant E ≃ 2.7182" @@ -512,16 +492,6 @@ class Infinity(_SympyConstant): Use 'Infinity' in sum and limit calculations: >> Sum[1/x^2, {x, 1, Infinity}] = Pi ^ 2 / 6 - - #> FullForm[Infinity] - = DirectedInfinity[1] - #> (2 + 3.5*I) / Infinity - = 0. - #> Infinity + Infinity - = Infinity - #> Infinity / Infinity - : Indeterminate expression 0 Infinity encountered. - = Indeterminate """ sympy_name = "oo" diff --git a/mathics/core/convert/mpmath.py b/mathics/core/convert/mpmath.py index 485fd2740..d35597ead 100644 --- a/mathics/core/convert/mpmath.py +++ b/mathics/core/convert/mpmath.py @@ -18,7 +18,21 @@ from mathics.core.systemsymbols import SymbolIndeterminate -@lru_cache(maxsize=1024) +# Another issue with the lru_cache: for mpmath, mpmath.mpc(0.,0.) and +# mpmath.mpf(0.) produces the same hash. As a result, depending on +# what is called first, the output of this function is different. +# +# note mmatera: When I start to pass the private doctests +# to pytests, I realize that WMA evaluates `0. I` to `Complex[0.,0.]` +# instead of `0.`. To fix this incompatibility, I removed from +# `from_sympy` the lines that convert `mpc(0.,0.)` to `mpf(0.0)`, +# and then this issue becomes evident. +# +# As we decide by now that performance comes after ensuring the compatibility +# and clarity of the code, I propose here to conserve the right tests, +# and commented out the cache and the lines that convert mpc(0,0) to MachineReal(0). +# +# @lru_cache(maxsize=1024) def from_mpmath( value: Union[mpmath.mpf, mpmath.mpc], precision: Optional[int] = None, @@ -42,8 +56,8 @@ def from_mpmath( # HACK: use str here to prevent loss of precision return PrecisionReal(sympy.Float(str(value), precision=precision - 1)) elif isinstance(value, mpmath.mpc): - if value.imag == 0.0: - return from_mpmath(value.real, precision=precision) + # if value.imag == 0.0: + # return from_mpmath(value.real, precision=precision) val_re, val_im = value.real, value.imag if mpmath.isinf(val_re): if mpmath.isinf(val_im): diff --git a/mathics/format/latex.py b/mathics/format/latex.py index ac074bbbb..9a27b4290 100644 --- a/mathics/format/latex.py +++ b/mathics/format/latex.py @@ -234,7 +234,7 @@ def superscriptbox(self, **options): base = self.tex_block(tex1, True) superidx_to_tex = lookup_conversion_method(self.superindex, "latex") superindx = self.tex_block(superidx_to_tex(self.superindex, **options), True) - if isinstance(self.superindex, (String, StyleBox)): + if len(superindx) == 1 and isinstance(self.superindex, (String, StyleBox)): return "%s^%s" % ( base, superindx, diff --git a/test/builtin/arithmetic/test_basic.py b/test/builtin/arithmetic/test_basic.py index d99b0b9dc..371ae736b 100644 --- a/test/builtin/arithmetic/test_basic.py +++ b/test/builtin/arithmetic/test_basic.py @@ -129,6 +129,12 @@ def test_multiply(str_expr, str_expected, msg): "msg", ), [ + ( + "DirectedInfinity[1+I]+DirectedInfinity[2+I]", + "(2 / 5 + I / 5) Sqrt[5] Infinity + (1 / 2 + I / 2) Sqrt[2] Infinity", + None, + ), + ("DirectedInfinity[Sqrt[3]]", "Infinity", None), ( "a b DirectedInfinity[1. + 2. I]", "a b ((0.447214 + 0.894427 I) Infinity)", @@ -225,3 +231,102 @@ def test_directed_infinity_precedence(str_expr, str_expected, msg): ) def test_power(str_expr, str_expected, msg): check_evaluation(str_expr, str_expected, failure_message=msg) + + +@pytest.mark.parametrize( + ( + "str_expr", + "str_expected", + "msg", + ), + [ + (None, None, None), + # Private tests from mathics.arithmetic.Complex + ("Complex[1, Complex[0, 1]]", "0", "Iterated Complex (1 , I)"), + ("Complex[1, Complex[1, 0]]", "1 + I", "Iterated Complex (1, 1) "), + ("Complex[1, Complex[1, 1]]", "I", "Iterated Complex, (1, 1 + I)"), + ("Complex[0., 0.]", "0. + 0. I", "build complex 0.+0. I"), + ("Complex[10, 0.]", "10. + 0. I", "build complex"), + ("Complex[10, 0]", "10", "build complex"), + ("1 + 0. I", "1. + 0. I", None), + # Mathics produces "0." + # For some weird reason, the following tests + # pass if we run this unit test alone, but not + # if we run it together all the tests + ("0. + 0. I//FullForm", "Complex[0., 0.]", "WMA compatibility"), + ("0. I//FullForm", "Complex[0., 0.]", None), + ("1. + 0. I//FullForm", "Complex[1., 0.]", None), + ("0. + 1. I//FullForm", "Complex[0., 1.]", None), + ("1. + 0. I//OutputForm", "1. + 0. I", "Formatted"), + ("0. + 1. I//OutputForm", "0. + 1. I", "Formatting 1. I"), + ("-2/3-I//FullForm", "Complex[Rational[-2, 3], -1]", "Adding a rational"), + ], +) +def test_complex(str_expr, str_expected, msg): + check_evaluation( + str_expr, + str_expected, + failure_message=msg, + to_string_expected=True, + # to_string_expr=True, + hold_expected=True, + ) + + +@pytest.mark.parametrize( + ( + "str_expr", + "str_expected", + "msg", + ), + [ + (None, None, None), + ("{Conjugate[Pi], Conjugate[E]}", "{Pi, E}", "Issue #272"), + ("-2/3", "-2 / 3", "Rational"), + ("-2/3//Head", "Rational", "Rational"), + ( + "(-1 + a^n) Sum[a^(k n), {k, 0, m-1}] // Simplify", + "-1 + (a ^ n) ^ m", + "according to WMA. Now it fails", + ), + ( + "Sum[i / Log[i], {i, 1, Infinity}]", + "Sum[i / Log[i], {i, 1, Infinity}]", + "Issue #302", + ), + ( + "Sum[Cos[Pi i], {i, 1, Infinity}]", + "Sum[Cos[i Pi], {i, 1, Infinity}]", + "Issue #302", + ), + ( + "Sum[x^k*Sum[y^l,{l,0,4}],{k,0,4}]", + "1 + x (1 + y + y ^ 2 + y ^ 3 + y ^ 4) + x ^ 2 (1 + y + y ^ 2 + y ^ 3 + y ^ 4) + x ^ 3 (1 + y + y ^ 2 + y ^ 3 + y ^ 4) + x ^ 4 (1 + y + y ^ 2 + y ^ 3 + y ^ 4) + y + y ^ 2 + y ^ 3 + y ^ 4", + "Iterated sum", + ), + ], +) +def test_miscelanea_private_tests(str_expr, str_expected, msg): + check_evaluation(str_expr, str_expected, failure_message=msg) + + +@pytest.mark.parametrize( + ( + "str_expr", + "str_expected", + "msg", + ), + [ + ( + "Product[1 + 1 / i ^ 2, {i, Infinity}]", + "1 / ((-I)! I!)", + ( + "Used to be a bug in sympy, but now it is solved exactly!\n" + "Again a bug in sympy - regressions between 0.7.3 and 0.7.6 (and 0.7.7?)" + ), + ), + ], +) +@pytest.mark.xfail +def test_miscelanea_private_tests_xfail(str_expr, str_expected, msg): + check_evaluation(str_expr, str_expected, failure_message=msg) diff --git a/test/builtin/atomic/test_numbers.py b/test/builtin/atomic/test_numbers.py index da4d17a16..787bf1043 100644 --- a/test/builtin/atomic/test_numbers.py +++ b/test/builtin/atomic/test_numbers.py @@ -266,6 +266,9 @@ def test_accuracy(str_expr, str_expected): ('{{a, 2, 3.2`},{2.1``3, 3.2``5, "a"}}', "3."), ("{1, 0.}", "MachinePrecision"), ("{1, 0.``5}", "0."), + ("Re[0.5+2.3 I]", "MachinePrecision"), + ("Re[1+2.3 I]", "MachinePrecision"), + ("Im[0.5+2.3 I]", "MachinePrecision"), ], ) def test_precision(str_expr, str_expected): diff --git a/test/builtin/numbers/test_calculus.py b/test/builtin/numbers/test_calculus.py index 44d36a9e3..c256cb5ce 100644 --- a/test/builtin/numbers/test_calculus.py +++ b/test/builtin/numbers/test_calculus.py @@ -89,6 +89,42 @@ ] +@pytest.mark.parametrize( + ( + "str_expr", + "str_expected", + "msg", + ), + [ + (None, None, None), + # Private tests from mathics.arithmetic.Complex + ("Complex[1, Complex[0, 1]]", "0", "Iterated Complex (1 , I)"), + ("Complex[1, Complex[1, 0]]", "1 + I", "Iterated Complex (1, 1) "), + ("Complex[1, Complex[1, 1]]", "I", "Iterated Complex, (1, 1 + I)"), + ("Complex[0., 0.]", "0. + 0. I", "build complex 0.+0. I"), + ("Complex[10, 0.]", "10. + 0. I", "build complex"), + ("Complex[10, 0]", "10", "build complex"), + ("1 + 0. I", "1.", None), + ("0. + 0. I//FullForm", "Complex[0., 0.]", "WMA compatibility"), + ("0. I//FullForm", "Complex[0., 0.]", None), + ("1. + 0. I//FullForm", "1.", None), + ("0. + 1. I//FullForm", "Complex[0., 1.]", None), + ("1. + 0. I//OutputForm", "1.", "Formatted"), + ("0. + 1. I//OutputForm", "0. + 1. I", "Formatting 1. I"), + ("-2/3-I//FullForm", "Complex[Rational[-2, 3], -1]", "Adding a rational"), + ], +) +def test_do_complex(str_expr, str_expected, msg): + check_evaluation( + str_expr, + str_expected, + failure_message=msg, + to_string_expected=True, + # to_string_expr=True, + hold_expected=True, + ) + + @pytest.mark.parametrize( "str_expr, str_expected, assert_fail_message, expected_messages", tests_for_findminimum, diff --git a/test/builtin/numbers/test_constants.py b/test/builtin/numbers/test_constants.py index 700419a5a..c5b74a8a7 100644 --- a/test/builtin/numbers/test_constants.py +++ b/test/builtin/numbers/test_constants.py @@ -4,6 +4,8 @@ """ from test.helper import check_evaluation +import pytest + def test_Undefined(): for fn in [ @@ -50,3 +52,47 @@ def test_Undefined(): ]: check_evaluation(f"{fn}[a, Undefined]", "Undefined") check_evaluation(f"{fn}[Undefined, b]", "Undefined") + + +# This is a miscelanea of private tests. I put here to make it easier to check +# where these tests comes from. Then, we can move them to more suitable places. +@pytest.mark.parametrize( + ("expr_str", "expected_str", "fail_msg", "msgs"), + [ + ( + "ComplexInfinity + ComplexInfinity", + "Indeterminate", + "Issue689", + ["Indeterminate expression ComplexInfinity + ComplexInfinity encountered."], + ), + ( + "ComplexInfinity + Infinity", + "Indeterminate", + "Issue689", + ["Indeterminate expression ComplexInfinity + Infinity encountered."], + ), + ("Cos[Degree[x]]", "Cos[Degree[x]]", "Degree as a function", None), + ("N[Degree]//OutputForm", "0.0174533", "Degree", None), + ("5. E//OutputForm", "13.5914", "E", None), + ("N[Degree, 30]//OutputForm", "0.0174532925199432957692369076849", None, None), + ("FullForm[Infinity]", "DirectedInfinity[1]", None, None), + ("(2 + 3.5*I) / Infinity", "0. + 0. I", "Complex over Infinity", None), + ("Infinity + Infinity", "Infinity", "Infinity plus Infinity", None), + ( + "Infinity / Infinity", + "Indeterminate", + "Infinity over Infinity", + ["Indeterminate expression 0 Infinity encountered."], + ), + ], +) +def test_constants_private(expr_str, expected_str, fail_msg, msgs): + check_evaluation( + expr_str, + expected_str, + fail_msg, + expected_messages=msgs, + hold_expected=True, + to_string_expected=True, + to_string_expr=True, + ) diff --git a/test/builtin/test_assignment.py b/test/builtin/test_assignment.py index d8d34bd2a..e9c846aac 100644 --- a/test/builtin/test_assignment.py +++ b/test/builtin/test_assignment.py @@ -355,6 +355,11 @@ def test_set_and_clear_to_fix(str_expr, str_expected, msg): "This clears A and B, but not $ContextPath", ("Special symbol $ContextPath cannot be cleared.",), ), + # `This test was in mathics.builtin.arithmetic.Sum`. It is clear that it does not + # belongs there. On the other hand, this is something to check at the level of the interpreter, + # and is not related with Sum, or Set. + # ("a=Sum[x^k*Sum[y^l,{l,0,4}],{k,0,4}]]", "None" , "syntax error", + # ('"a=Sum[x^k*Sum[y^l,{l,0,4}],{k,0,4}]" cannot be followed by "]" (line 1 of "").',)) ], ) def test_set_and_clear_messages(str_expr, str_expected, message, out_msgs): diff --git a/test/builtin/test_comparison.py b/test/builtin/test_comparison.py index 432dd62b2..386b3123d 100644 --- a/test/builtin/test_comparison.py +++ b/test/builtin/test_comparison.py @@ -647,3 +647,24 @@ def test_cmp_compare_numbers(str_expr, str_expected, message): to_string_expr=True, to_string_expected=True, ) + + +@pytest.mark.parametrize( + ("str_expr", "str_expected", "message"), + [ + ( + "{a, b} = {2^10000, 2^10000 + 1}; {a == b, a < b, a <= b}", + "{False, True, True}", + "Test large Integer comparison bug", + ), + # (None, None, None), + ], +) +def test_misc_private_tests(str_expr, str_expected, message): + check_evaluation( + str_expr, + str_expected, + failure_message=message, + to_string_expr=True, + to_string_expected=True, + ) diff --git a/test/builtin/test_patterns.py b/test/builtin/test_patterns.py index 9fb3d8a0f..8e9edcb16 100644 --- a/test/builtin/test_patterns.py +++ b/test/builtin/test_patterns.py @@ -5,8 +5,11 @@ from test.helper import check_evaluation +# Clear all the variables + def test_blank(): + check_evaluation(None, None, None) for str_expr, str_expected, message in ( ( "g[i] /. _[i] :> a", @@ -18,6 +21,7 @@ def test_blank(): def test_replace_all(): + check_evaluation(None, None, None) for str_expr, str_expected, message in ( ( "a == d b + d c /. a_ x_ + a_ y_ -> a (x + y)", diff --git a/test/core/parser/test_convert.py b/test/core/parser/test_convert.py index ea99bf362..9b6e9d3d3 100644 --- a/test/core/parser/test_convert.py +++ b/test/core/parser/test_convert.py @@ -58,6 +58,10 @@ def testInteger(self): self.check("10*^3", Integer(10000)) self.check("10*^-3", Rational(1, 100)) self.check("8^^23*^2", Integer(1216)) + self.check("2^^0101", Integer(5)) + self.check( + "36^^0123456789abcDEFxyzXYZ", Integer(14142263610074677021975869033659) + ) n = random.randint(-sys.maxsize, sys.maxsize) self.check(str(n), Integer(n)) @@ -65,6 +69,15 @@ def testInteger(self): n = random.randint(sys.maxsize, sys.maxsize * sys.maxsize) self.check(str(n), Integer(n)) + # Requested base 1 in 1^^2 should be between 2 and 36. + self.invalid_error(r"1^^2") + # Requested base 37 in 37^^3 should be between 2 and 36. + self.invalid_error(r"37^^3") + # Digit at position 3 in 01210 is too large to be used in base 2. + self.invalid_error(r"2^^01210") + # "Digit at position 2 in 5g is too large to be used in base 16." + self.invalid_error(r"16^^5g") + def testReal(self): self.check("1.5", Real("1.5")) self.check("1.5`", Real("1.5")) @@ -74,9 +87,20 @@ def testReal(self): self.check("0``3", "0.000`3") self.check("0.`3", "0.000`3") self.check("0.``3", "0.000``3") + ## Mathematica treats zero strangely self.check("0.00000000000000000", "0.") self.check("0.000000000000000000`", "0.") self.check("0.000000000000000000", "0.``18") + # Parse *^ notation + self.check("1.5×10^24", Real(1.5) * Integer(10) ** Integer(24)) + self.check("1.5*^+24", Real("1.5e24")) + self.check("1.5*^-24", Real("1.5e-24")) + ## Don't accept *^ with spaces + # > 1.5 *^10 + # "1.5*" cannot be followed by "^ 10" + self.invalid_error("1.5 *^10") + # "1.5*" cannot be followed by "^ 10" + self.invalid_error("1.5*^ 10") def testString(self): self.check(r'"abc"', String("abc")) diff --git a/test/format/test_format.py b/test/format/test_format.py index 161ebc5df..cc01d3439 100644 --- a/test/format/test_format.py +++ b/test/format/test_format.py @@ -1,5 +1,5 @@ -# from .helper import session import os +from test.helper import check_evaluation from mathics.core.symbols import Symbol from mathics.session import MathicsSession @@ -130,6 +130,123 @@ "System`OutputForm": "-4.3", }, }, + "1. 10^6": { + "msg": "very large real number (>10^6)", + "text": { + "System`InputForm": r"1.000000*^6", + "System`OutputForm": "1.×10^6", + }, + "latex": { + "System`InputForm": r"1.000000\text{*${}^{\wedge}$}6", + "System`OutputForm": r"1.\times 10^6", + }, + "mathml": {}, + }, + "1. 10^5": { + "msg": "large real number (<10^6)", + "text": { + "System`InputForm": r"100000.", + "System`OutputForm": "100000.", + }, + "latex": { + "System`InputForm": r"100000.", + "System`OutputForm": "100000.", + }, + "mathml": {}, + }, + "-1. 10^6": { + "msg": "large negative real number (>10^6)", + "text": { + "System`InputForm": r"-1.000000*^6", + "System`OutputForm": "-1.×10^6", + }, + "latex": { + "System`InputForm": r"-1.000000\text{*${}^{\wedge}$}6", + "System`OutputForm": r"-1.\times 10^6", + }, + "mathml": {}, + }, + "-1. 10^5": { + "msg": "large negative real number (<10^6)", + "text": { + "System`InputForm": r"-100000.", + "System`OutputForm": "-100000.", + }, + "latex": { + "System`InputForm": r"-100000.", + "System`OutputForm": "-100000.", + }, + "mathml": {}, + }, + "1. 10^-6": { + "msg": "very small real number (<10^-6)", + "text": { + "System`InputForm": r"1.*^-6", + "System`OutputForm": "1.×10^-6", + }, + "latex": { + "System`InputForm": r"1.\text{*${}^{\wedge}$}-6", + "System`OutputForm": r"1.\times 10^{-6}", + }, + "mathml": {}, + }, + "1. 10^-5": { + "msg": "small real number (<10^-5)", + "text": { + "System`InputForm": r"0.00001", + "System`OutputForm": "0.00001", + }, + "latex": { + "System`InputForm": r"0.00001", + "System`OutputForm": "0.00001", + }, + "mathml": {}, + }, + "-1. 10^-6": { + "msg": "very small negative real number (<10^-6)", + "text": { + "System`InputForm": r"-1.*^-6", + "System`OutputForm": "-1.×10^-6", + }, + "latex": { + "System`InputForm": r"-1.\text{*${}^{\wedge}$}-6", + "System`OutputForm": r"-1.\times 10^{-6}", + }, + "mathml": {}, + }, + "-1. 10^-5": { + "msg": "small negative real number (>10^-5)", + "text": { + "System`InputForm": r"-0.00001", + "System`OutputForm": "-0.00001", + }, + "latex": { + "System`InputForm": r"-0.00001", + "System`OutputForm": "-0.00001", + }, + "mathml": {}, + }, + "Complex[1.09*^12,3.]": { + "msg": "Complex number", + "text": { + "System`StandardForm": "1.09*^12+3. I", + "System`TraditionalForm": "1.09×10^12+3.⁢I", + "System`InputForm": "1.090000000000*^12 + 3.*I", + "System`OutputForm": "1.09×10^12 + 3. I", + }, + "mathml": { + "System`StandardForm": r"1.09 *^ 12 + 3.   I", + "System`TraditionalForm": r'1.09 × 10 12 + 3. I', + "System`InputForm": r"1.090000000000 *^ 12  +  3. * I", + "System`OutputForm": r"1.09 × 10 12  +  3.   I", + }, + "latex": { + "System`StandardForm": r"1.09\text{*${}^{\wedge}$}12+3. I", + "System`TraditionalForm": r"1.09\times 10^{12}+3. I", + "System`InputForm": r"1.090000000000\text{*${}^{\wedge}$}12\text{ + }3.*I", + "System`OutputForm": r"1.09\times 10^{12}\text{ + }3. I", + }, + }, '"Hola!"': { "msg": "A String", "text": { @@ -792,3 +909,29 @@ def test_makeboxes_mathml(str_expr, str_expected, form, msg): else: strresult = format_result.boxes_to_mathml(evaluation=session.evaluation) assert strresult == str_expected + + +@pytest.mark.parametrize( + ("str_expr", "str_expected", "msg"), + [ + ( + "OutputForm[Complex[2.0 ^ 40, 3]]", + "1.09951×10^12 + 3. I", + "OutputForm Complex", + ), + ( + "InputForm[Complex[2.0 ^ 40, 3]]", + "1.099511627776*^12 + 3.*I", + "InputForm Complex", + ), + ], +) +def test_format_private_doctests(str_expr, str_expected, msg): + check_evaluation( + str_expr, + str_expected, + to_string_expr=True, + to_string_expected=True, + hold_expected=True, + failure_message=msg, + )