diff --git a/mathics/builtin/arithfns/basic.py b/mathics/builtin/arithfns/basic.py
index ca56158fb..91edaaa3e 100644
--- a/mathics/builtin/arithfns/basic.py
+++ b/mathics/builtin/arithfns/basic.py
@@ -6,7 +6,6 @@
on a calculator.
"""
-
from mathics.builtin.arithmetic import create_infix
from mathics.builtin.base import (
BinaryOperator,
@@ -52,14 +51,29 @@
from mathics.core.systemsymbols import (
SymbolBlank,
SymbolComplexInfinity,
+ SymbolFractionBox,
+ SymbolI,
SymbolIndeterminate,
SymbolInfix,
+ SymbolInputForm,
SymbolLeft,
+ SymbolMakeBoxes,
SymbolMinus,
+ SymbolOutputForm,
SymbolPattern,
+ SymbolPrecedenceForm,
+ SymbolRowBox,
SymbolSequence,
+ SymbolStandardForm,
+ SymbolTraditionalForm,
)
from mathics.eval.arithmetic import eval_Plus, eval_Times
+from mathics.eval.makeboxes import (
+ builtins_precedence,
+ do_format_complex,
+ eval_makeboxes,
+ parenthesize,
+)
from mathics.eval.nevaluator import eval_N
from mathics.eval.numerify import numerify
@@ -151,7 +165,7 @@ class Divide(BinaryOperator):
default_formats = False
formats = {
- (("InputForm", "OutputForm"), "Divide[x_, y_]"): (
+ ("InputForm", "Divide[x_, y_]"): (
'Infix[{HoldForm[x], HoldForm[y]}, "/", 400, Left]'
),
}
@@ -162,13 +176,48 @@ class Divide(BinaryOperator):
rules = {
"Divide[x_, y_]": "Times[x, Power[y, -1]]",
- "MakeBoxes[Divide[x_, y_], f:StandardForm|TraditionalForm]": (
- "FractionBox[MakeBoxes[x, f], MakeBoxes[y, f]]"
- ),
+ # "MakeBoxes[Divide[x_, y_], f:StandardForm|TraditionalForm]": (
+ # "FractionBox[MakeBoxes[x, f], MakeBoxes[y, f]]"
+ # ),
}
-
summary_text = "divide"
+ # The reason why we need these MakeBoxes methods instead of rules
+ # is that we have still several incompatibilities with WMA.
+ # In particular, OutputForm should produce a "2D" text representation,
+ # similar to the pretty print of sympy. So, for example,
+ # OutputForm[Divide[a+b,c+d]] -> FractionBox[a+b, c+d]
+ # shoud produce something like
+ #
+ # a + b
+ # -------
+ # c + d
+ # which does not require parenthesis in the numerator and the denominator.
+ #
+ # On the other hand, with our current implementation, FractionBox[a+b, c+d]
+ # produces
+ # a + b / c + d
+ #
+ # For this reason, we need to add parenthesis to this expression.
+ # Then, when we translate it to some representation admitting the 2D representation
+ # like LaTeX or MathML, the parenthesis are removed.
+
+ def eval_makeboxes_input_output_form(self, x, y, form, evaluation):
+ """MakeBoxes[Divide[x_, y_], form:OutputForm]"""
+ # This adds the parenthesis when they are needed, for the case
+ # of 1D text output.
+ num = parenthesize(400, x, eval_makeboxes(x, evaluation, form), False)
+ den = parenthesize(400, y, eval_makeboxes(y, evaluation, form), True)
+ return Expression(SymbolFractionBox, num, den)
+
+ def eval_makeboxes_standardform(self, x, y, form, evaluation):
+ """MakeBoxes[Divide[x_, y_], form:(StandardForm|TraditionalForm)]"""
+ # This adds the parenthesis when they are needed, for the case
+ # of 1D text output.
+ num = parenthesize(400, x, eval_makeboxes(x, evaluation, form), False)
+ den = parenthesize(400, y, eval_makeboxes(y, evaluation, form), True)
+ return Expression(SymbolFractionBox, num, den)
+
class Minus(PrefixOperator):
"""
@@ -196,16 +245,7 @@ class Minus(PrefixOperator):
"""
attributes = A_LISTABLE | A_NUMERIC_FUNCTION | A_PROTECTED
-
- formats = {
- "Minus[x_]": 'Prefix[{HoldForm[x]}, "-", 480]',
- # don't put e.g. -2/3 in parentheses
- "Minus[expr_Divide]": 'Prefix[{HoldForm[expr]}, "-", 399]',
- "Minus[Infix[expr_, op_, 400, grouping_]]": (
- 'Prefix[{Infix[expr, op, 400, grouping]}, "-", 399]'
- ),
- }
-
+ default_formats = False
operator = "-"
precedence = 480
@@ -219,6 +259,13 @@ def eval_int(self, x: Integer, evaluation):
"Minus[x_Integer]"
return Integer(-x.value)
+ def eval_makeboxes(self, x, form, evaluation):
+ """MakeBoxes[Minus[x_], form:(InputForm|OutputForm|StandardForm|TraditionalForm)]"""
+ # This adds the parenthesis when they are needed, for the case
+ # of 1D text output.
+ factor = parenthesize(400, x, eval_makeboxes(x, evaluation, form), False)
+ return Expression(SymbolRowBox, ListExpression(String(self.operator), factor))
+
class Plus(BinaryOperator, SympyFunction):
"""
@@ -290,56 +337,78 @@ class Plus(BinaryOperator, SympyFunction):
# Remember to up sympy doc link when this is corrected
sympy_name = "Add"
- def format_plus(self, items, evaluation):
- "Plus[items__]"
-
- def negate(item): # -> Expression (see FIXME below)
- if item.has_form("Times", 1, None):
- if isinstance(item.elements[0], Number):
- neg = -item.elements[0]
- if neg.sameQ(Integer1):
- if len(item.elements) == 1:
- return neg
- else:
- return Expression(SymbolTimes, *item.elements[1:])
- else:
- return Expression(SymbolTimes, neg, *item.elements[1:])
- else:
- return Expression(SymbolTimes, IntegerM1, *item.elements)
- elif isinstance(item, Number):
- return from_sympy(-item.to_sympy())
- else:
- return Expression(SymbolTimes, IntegerM1, item)
+ def eval_makeboxes_plus(self, items, form, evaluation):
+ "MakeBoxes[Plus[items__], form:(InputForm|OutputForm|StandardForm|TraditionalForm)]"
+ if form in (SymbolInputForm, SymbolOutputForm):
+ ops = [String(" + "), String(" - ")]
+ else:
+ ops = [String("+"), String("-")]
+ return self.do_eval_makeboxes_plus_ops(
+ items.get_sequence(), form, evaluation, ops
+ )
- def is_negative(value) -> bool:
- if isinstance(value, Complex):
- real, imag = value.to_sympy().as_real_imag()
- if real <= 0 and imag <= 0:
+ def do_eval_makeboxes_plus_ops(
+ self, terms, form, evaluation, ops=[String("+"), String("-")]
+ ):
+ term = terms[0]
+ formatted_term = eval_makeboxes(term, evaluation, form)
+ # formatted_term = parenthesize(310, term, formatted_term, True)
+ formatted_terms = [formatted_term]
+
+ def is_negative(t):
+ if isinstance(t, Complex):
+ re_symp, im_sympy = t.to_sympy().as_real_imag()
+ if re_sympy == 0:
+ if im_sympy < 0:
+ return True
+ if re_sympy < 0:
return True
- elif isinstance(value, Number) and value.to_sympy() < 0:
- return True
+ if isinstance(t, Number):
+ return t.value < 0
+ if t.has_form("Minus", 1):
+ return not is_negative(t.elements[0])
+ if t.has_form("Times", 2, None):
+ return is_negative(t.elements[0])
+ if t.has_form("HoldForm", 1):
+ return is_negative(t.elements[0])
+ if t.has_form("Divide", 2):
+ return is_negative(t.elements[0])
return False
- elements = items.get_sequence()
- values = [to_expression(SymbolHoldForm, element) for element in elements[:1]]
- ops = []
- for element in elements[1:]:
- if (
- element.has_form("Times", 1, None) and is_negative(element.elements[0])
- ) or is_negative(element):
- element = negate(element)
- op = "-"
- else:
- op = "+"
- values.append(Expression(SymbolHoldForm, element))
- ops.append(String(op))
- return Expression(
- SymbolInfix,
- ListExpression(*values),
- ListExpression(*ops),
- Integer310,
- SymbolLeft,
- )
+ def negate(t):
+ if isinstance(t, Number):
+ return from_sympy(-t.to_sympy())
+ if t.has_form("Times", 2, None):
+ factors = t.elements
+ return Expression(SymbolTimes, negate(factors[0]), *factors[1:])
+ if t.has_form("Minus", 1):
+ return t.elements[0]
+ if t.has_form("HoldForm", 1):
+ return negate(t.elements[0])
+ if t.has_form("Divide", 2):
+ num, den = t.elements
+ return Expression(SymbolDivide, negate(num), den)
+ return Expression(Minus, t)
+
+ for term in terms[1:]:
+ minus = 0
+ while True:
+ if term.has_form("HoldForm", 1):
+ term = term.elements[0]
+ continue
+ if term.has_form("Minus", 1):
+ term = term.elements[0]
+ minus += 1
+ continue
+ if is_negative(term):
+ minus += 1
+ term = negate(term)
+ break
+ formatted_terms.append(ops[minus % 2])
+ formatted_term = eval_makeboxes(term, evaluation, form)
+ formatted_term = parenthesize(310, term, formatted_term, True)
+ formatted_terms.append(formatted_term)
+ return Expression(SymbolRowBox, ListExpression(*formatted_terms))
def eval(self, items, evaluation):
"Plus[items___]"
@@ -630,8 +699,49 @@ class Times(BinaryOperator, SympyFunction):
summary_text = "mutiply"
- def format_times(self, items, evaluation, op="\u2062"):
- "Times[items__]"
+ def do_eval_makeboxes_times(self, factors, form, evaluation, op=String("\u2062")):
+ op_str = String(op)
+ factor = factors[0]
+ formatted_factor = eval_makeboxes(factor, evaluation, form)
+ formatted_factor = parenthesize(400, factor, formatted_factor, False)
+ elements = [formatted_factor]
+ factors = factors[1:]
+
+ for factor in factors:
+ elements.append(op)
+ formatted_factor = eval_makeboxes(factor, evaluation, form)
+ formatted_factor = parenthesize(400, factor, formatted_factor, False)
+ elements.append(formatted_factor)
+
+ return Expression(SymbolRowBox, ListExpression(*elements))
+
+ def do_format_times(self, items, evaluation, form):
+ # format_times canonicalizes the form of a product in two ways:
+ # * removes (-1) from the first element of a product, replacing it
+ # by "Minus[Times[rest]]"
+ # * collect all Rational, Divide and negative power factors,
+ # and reformat the expression as Divide[numerator, denominator]
+ # with numerator and denominator free of these class of factors.
+
+ factors = items.get_sequence()
+
+ if factors[0] is IntegerM1:
+ rest = factors[1:]
+ if len(rest) > 1:
+ result = Expression(
+ SymbolHoldForm,
+ Expression(SymbolMinus, Expression(SymbolTimes, *rest)),
+ )
+ else:
+ result = Expression(SymbolHoldForm, Expression(SymbolMinus, rest[0]))
+ return result
+ if factors[0].has_form("Minus", 1):
+ factors[0] = factors[0].elements[0]
+ result = Expression(
+ SymbolHoldForm,
+ Expression(SymbolMinus, Expression(SymbolTimes, *factors)),
+ )
+ return result
def inverse(item):
if item.has_form("Power", 2) and isinstance( # noqa
@@ -645,16 +755,24 @@ def inverse(item):
else:
return item
- items = items.get_sequence()
+ # Segretate negative powers
positive = []
negative = []
- for item in items:
+ changed = False
+ for item in factors:
if (
item.has_form("Power", 2)
and isinstance(item.elements[1], (Integer, Rational, Real))
and item.elements[1].to_sympy() < 0
): # nopep8
negative.append(inverse(item))
+ elif isinstance(item, Complex):
+ positive.append(do_format_complex(item, evaluation, SymbolOutputForm))
+ elif item.has_form("Divide", 2):
+ numerator, denominator = item.elements()
+ if numerator is not Integer1:
+ positive.append(numerator)
+ negative.append(denominator)
elif isinstance(item, Rational):
numerator = item.numerator()
if not numerator.sameQ(Integer1):
@@ -662,46 +780,80 @@ def inverse(item):
negative.append(item.denominator())
else:
positive.append(item)
- if positive and positive[0].get_int_value() == -1:
- del positive[0]
- minus = True
- else:
- minus = False
- positive = [Expression(SymbolHoldForm, item) for item in positive]
- negative = [Expression(SymbolHoldForm, item) for item in negative]
- if positive:
- positive = create_infix(positive, op, 400, "None")
- else:
- positive = Integer1
+
+ # positive = [Expression(SymbolHoldForm, item) for item in positive]
+ # negative = [Expression(SymbolHoldForm, item) for item in negative]
if negative:
- negative = create_infix(negative, op, 400, "None")
- result = Expression(
- SymbolDivide,
- Expression(SymbolHoldForm, positive),
- Expression(SymbolHoldForm, negative),
+ den = (
+ negative[0]
+ if len(negative) == 1
+ else Expression(SymbolTimes, *negative)
)
+ change_sign = False
+ if positive:
+ if len(positive) == 1:
+ num = positive[0]
+ else:
+ first_factor = positive[0]
+ change_sign = (
+ isinstance(first_factor, (Integer, Real))
+ and first_factor.value < 0
+ )
+ if change_sign:
+ # negate first factor
+ first_factor = -first_factor.to_sympy()
+ if first_factor == 1:
+ positive = positive[1:]
+ else:
+ positive[0] = from_sympy(first_factor)
+ num = (
+ Expression(SymbolTimes, *positive)
+ if len(positive) > 1
+ else positive[0]
+ )
+ else:
+ num = Integer1
+ result = Expression(SymbolHoldForm, Expression(SymbolDivide, num, den))
+ if change_sign:
+ result = Expression(SymbolMinus, result)
+ return result
+ # This happends if there are pure imaginary factors
+ if changed or len(positive) != len(factors):
+ result = Expression(SymbolHoldForm, Expression(SymbolTimes, *positive))
+ return result
+
+ return None
+
+ def eval(self, items, evaluation):
+ "Times[items___]"
+ items = numerify(items, evaluation).get_sequence()
+ return eval_Times(*items)
+
+ def eval_makeboxes_times(self, items, form, evaluation):
+ "MakeBoxes[Times[items__], form:(InputForm|OutputForm|StandardForm|TraditionalForm)]"
+ factors = items.get_sequence()
+ if len(factors) < 2:
+ # Times[] and Times[...] are not evaluated here...
+ return None
+ if form is SymbolInputForm:
+ op = String("*")
else:
- result = positive
- if minus:
- result = Expression(
- SymbolMinus, result
- ) # Expression('PrecedenceForm', result, 481))
- result = Expression(SymbolHoldForm, result)
+ op = String(" ")
+ result = self.do_eval_makeboxes_times(factors, form, evaluation, op)
return result
- def format_inputform(self, items, evaluation):
+ def format_times_input(self, items, evaluation):
"InputForm: Times[items__]"
- return self.format_times(items, evaluation, op="*")
-
- def format_standardform(self, items, evaluation):
- "StandardForm: Times[items__]"
- return self.format_times(items, evaluation, op=" ")
+ return self.do_format_times(items, evaluation, SymbolInputForm)
- def format_outputform(self, items, evaluation):
+ def format_times_output(self, items, evaluation):
"OutputForm: Times[items__]"
- return self.format_times(items, evaluation, op=" ")
+ return self.do_format_times(items, evaluation, SymbolOutputForm)
- def eval(self, items, evaluation):
- "Times[items___]"
- items = numerify(items, evaluation).get_sequence()
- return eval_Times(*items)
+ def format_times_standardform(self, items, evaluation):
+ "StandardForm: Times[items__]"
+ return self.do_format_times(items, evaluation, SymbolStandardForm)
+
+ def format_times_traditionalform(self, items, evaluation):
+ "TraditionalForm: Times[items__]"
+ return self.do_format_times(items, evaluation, SymbolStandardForm)
diff --git a/mathics/builtin/arithmetic.py b/mathics/builtin/arithmetic.py
index 832cab44c..60817cb8a 100644
--- a/mathics/builtin/arithmetic.py
+++ b/mathics/builtin/arithmetic.py
@@ -476,7 +476,8 @@ class DirectedInfinity(SympyFunction):
formats = {
"DirectedInfinity[1]": "HoldForm[Infinity]",
- "DirectedInfinity[-1]": "HoldForm[-Infinity]",
+ "DirectedInfinity[-1]": "HoldForm[Minus[Infinity]]",
+ "DirectedInfinity[-I]": "HoldForm[Minus[Infinity] I]",
"DirectedInfinity[]": "HoldForm[ComplexInfinity]",
"DirectedInfinity[DirectedInfinity[z_]]": "DirectedInfinity[z]",
"DirectedInfinity[z_?NumericQ]": "HoldForm[z Infinity]",
diff --git a/mathics/builtin/forms/output.py b/mathics/builtin/forms/output.py
index 76f05d1e3..e9e28d93f 100644
--- a/mathics/builtin/forms/output.py
+++ b/mathics/builtin/forms/output.py
@@ -1060,9 +1060,9 @@ class MatrixForm(TableForm):
## Issue #182
#> {{2*a, 0},{0,0}}//MatrixForm
- = 2 a 0
+ = 2 a 0
.
- . 0 0
+ . 0 0
"""
in_outputforms = True
diff --git a/mathics/builtin/layout.py b/mathics/builtin/layout.py
index 0397b09b3..4fca006d1 100644
--- a/mathics/builtin/layout.py
+++ b/mathics/builtin/layout.py
@@ -500,7 +500,7 @@ class Superscript(Builtin):
summary_text = "format an expression with a superscript"
rules = {
- "MakeBoxes[Superscript[x_, y_], f:StandardForm|TraditionalForm]": (
+ "MakeBoxes[Superscript[x_, y_], f:OutputForm|StandardForm|TraditionalForm]": (
"SuperscriptBox[MakeBoxes[x, f], MakeBoxes[y, f]]"
)
}
diff --git a/mathics/builtin/numbers/calculus.py b/mathics/builtin/numbers/calculus.py
index dffebb42c..9e41ab1f5 100644
--- a/mathics/builtin/numbers/calculus.py
+++ b/mathics/builtin/numbers/calculus.py
@@ -1712,8 +1712,10 @@ class Series(Builtin):
= 17
>> Clear[s];
We can also expand over multiple variables
+ ## TODO: In WMA, the first term is also sorounded by parenthesis. This is
+ ## to fix in another round, after complete the refactor of Infix.
>> Series[Exp[x-y], {x, 0, 2}, {y, 0, 2}]
- = (1 - y + 1 / 2 y ^ 2 + O[y] ^ 3) + (1 - y + 1 / 2 y ^ 2 + O[y] ^ 3) x + (1 / 2 + (-1 / 2) y + 1 / 4 y ^ 2 + O[y] ^ 3) x ^ 2 + O[x] ^ 3
+ = 1 - y + 1 / 2 y ^ 2 + O[y] ^ 3 + (1 - y + 1 / 2 y ^ 2 + O[y] ^ 3) x + (1 / 2 - 1 / 2 y + 1 / 4 y ^ 2 + O[y] ^ 3) x ^ 2 + O[x] ^ 3
"""
@@ -2096,7 +2098,7 @@ def pre_makeboxes(self, x, x0, data, nmin, nmax, den, form, evaluation: Evaluati
Expression(SymbolPlus, *expansion),
Expression(SymbolPower, Expression(SymbolO, variable), powers[-1]),
)
- return Expression(SymbolInfix, expansion, String("+"), Integer(300), SymbolLeft)
+ return Expression(SymbolInfix, expansion, String("+"), Integer(299), SymbolLeft)
def eval_makeboxes(
self,
diff --git a/mathics/builtin/quantum_mechanics/angular.py b/mathics/builtin/quantum_mechanics/angular.py
index b063c7081..36505aa7d 100644
--- a/mathics/builtin/quantum_mechanics/angular.py
+++ b/mathics/builtin/quantum_mechanics/angular.py
@@ -107,7 +107,7 @@ class PauliMatrix(SympyFunction):
= True
>> MatrixExp[I \[Phi]/2 PauliMatrix[3]]
- = {{E ^ (I / 2 ϕ), 0}, {0, E ^ ((-I / 2) ϕ)}}
+ = {{E ^ (I / 2 ϕ), 0}, {0, E ^ (-I / 2 ϕ)}}
>> % /. \[Phi] -> 2 Pi
= {{-1, 0}, {0, -1}}
diff --git a/mathics/builtin/statistics/orderstats.py b/mathics/builtin/statistics/orderstats.py
index 5a8f63f0c..dae8be89c 100644
--- a/mathics/builtin/statistics/orderstats.py
+++ b/mathics/builtin/statistics/orderstats.py
@@ -263,7 +263,7 @@ class Sort(Builtin):
Sort uses 'OrderedQ' to determine ordering by default.
You can sort patterns according to their precedence using 'PatternsOrderedQ':
>> Sort[{items___, item_, OptionsPattern[], item_symbol, item_?test}, PatternsOrderedQ]
- = {item_symbol, item_ ? test, item_, items___, OptionsPattern[]}
+ = {item_symbol, (item_) ? test, item_, items___, OptionsPattern[]}
When sorting patterns, values of atoms do not matter:
>> Sort[{a, b/;t}, PatternsOrderedQ]
diff --git a/mathics/core/convert/sympy.py b/mathics/core/convert/sympy.py
index 843d679f3..bb7bfe887 100644
--- a/mathics/core/convert/sympy.py
+++ b/mathics/core/convert/sympy.py
@@ -463,7 +463,10 @@ def old_from_sympy(expr) -> BaseElement:
else:
factors.append(Expression(SymbolPower, slot, from_sympy(exp)))
if factors:
- result.append(Expression(SymbolTimes, *factors))
+ if len(factors) == 1:
+ result.append(factors[0])
+ else:
+ result.append(Expression(SymbolTimes, *factors))
else:
result.append(Integer1)
return Expression(SymbolFunction, Expression(SymbolPlus, *result))
diff --git a/mathics/core/systemsymbols.py b/mathics/core/systemsymbols.py
index fa388a92d..c3f1f756e 100644
--- a/mathics/core/systemsymbols.py
+++ b/mathics/core/systemsymbols.py
@@ -190,6 +190,7 @@
SymbolPolygon = Symbol("System`Polygon")
SymbolPossibleZeroQ = Symbol("System`PossibleZeroQ")
SymbolPrecision = Symbol("System`Precision")
+SymbolPrecedenceForm = Symbol("System`PrecedenceForm")
SymbolQuantity = Symbol("System`Quantity")
SymbolQuiet = Symbol("System`Quiet")
SymbolQuotient = Symbol("System`Quotient")
diff --git a/mathics/doc/documentation/1-Manual.mdoc b/mathics/doc/documentation/1-Manual.mdoc
index 9b3477920..6703c13e6 100644
--- a/mathics/doc/documentation/1-Manual.mdoc
+++ b/mathics/doc/documentation/1-Manual.mdoc
@@ -1186,15 +1186,15 @@ A dice object shall be displayed as a rectangle with the given number of points
#> Definition[Dice]
= Attributes[Dice] = {Orderless}
.
- . Format[Dice[n_Integer ? (1 <= #1 <= 6&)], MathMLForm] = Block[{p = 0.2, r = 0.05}, Graphics[{EdgeForm[Black], White, Rectangle[], Black, EdgeForm[], If[OddQ[n], Disk[{0.5, 0.5}, r]], If[MemberQ[{2, 3, 4, 5, 6}, n], Disk[{p, p}, r]], If[MemberQ[{2, 3, 4, 5, 6}, n], Disk[{Plus[1, Times[-1, p]], Plus[1, Times[-1, p]]}, r]], If[MemberQ[{4, 5, 6}, n], Disk[{p, Plus[1, Times[-1, p]]}, r]], If[MemberQ[{4, 5, 6}, n], Disk[{Plus[1, Times[-1, p]], p}, r]], If[n === 6, {Disk[{p, 0.5}, r], Disk[{Plus[1, Times[-1, p]], 0.5}, r]}]}, ImageSize -> Tiny]]
+ . Format[Dice[(n_Integer) ? (1 <= #1 <= 6&)], MathMLForm] = Block[{p = 0.2, r = 0.05}, Graphics[{EdgeForm[Black], White, Rectangle[], Black, EdgeForm[], If[OddQ[n], Disk[{0.5, 0.5}, r]], If[MemberQ[{2, 3, 4, 5, 6}, n], Disk[{p, p}, r]], If[MemberQ[{2, 3, 4, 5, 6}, n], Disk[{1 - 1*p, 1 - 1*p}, r]], If[MemberQ[{4, 5, 6}, n], Disk[{p, 1 - 1*p}, r]], If[MemberQ[{4, 5, 6}, n], Disk[{1 - 1*p, p}, r]], If[n === 6, {Disk[{p, 0.5}, r], Disk[{1 - 1*p, 0.5}, r]}]}, ImageSize -> Tiny]]
.
- . Format[Dice[n_Integer ? (1 <= #1 <= 6&)], OutputForm] = Block[{p = 0.2, r = 0.05}, Graphics[{EdgeForm[Black], White, Rectangle[], Black, EdgeForm[], If[OddQ[n], Disk[{0.5, 0.5}, r]], If[MemberQ[{2, 3, 4, 5, 6}, n], Disk[{p, p}, r]], If[MemberQ[{2, 3, 4, 5, 6}, n], Disk[{Plus[1, Times[-1, p]], Plus[1, Times[-1, p]]}, r]], If[MemberQ[{4, 5, 6}, n], Disk[{p, Plus[1, Times[-1, p]]}, r]], If[MemberQ[{4, 5, 6}, n], Disk[{Plus[1, Times[-1, p]], p}, r]], If[n === 6, {Disk[{p, 0.5}, r], Disk[{Plus[1, Times[-1, p]], 0.5}, r]}]}, ImageSize -> Tiny]]
+ . Format[Dice[(n_Integer) ? (1 <= #1 <= 6&)], OutputForm] = Block[{p = 0.2, r = 0.05}, Graphics[{EdgeForm[Black], White, Rectangle[], Black, EdgeForm[], If[OddQ[n], Disk[{0.5, 0.5}, r]], If[MemberQ[{2, 3, 4, 5, 6}, n], Disk[{p, p}, r]], If[MemberQ[{2, 3, 4, 5, 6}, n], Disk[{1 - 1*p, 1 - 1*p}, r]], If[MemberQ[{4, 5, 6}, n], Disk[{p, 1 - 1*p}, r]], If[MemberQ[{4, 5, 6}, n], Disk[{1 - 1*p, p}, r]], If[n === 6, {Disk[{p, 0.5}, r], Disk[{1 - 1*p, 0.5}, r]}]}, ImageSize -> Tiny]]
.
- . Format[Dice[n_Integer ? (1 <= #1 <= 6&)], StandardForm] = Block[{p = 0.2, r = 0.05}, Graphics[{EdgeForm[Black], White, Rectangle[], Black, EdgeForm[], If[OddQ[n], Disk[{0.5, 0.5}, r]], If[MemberQ[{2, 3, 4, 5, 6}, n], Disk[{p, p}, r]], If[MemberQ[{2, 3, 4, 5, 6}, n], Disk[{Plus[1, Times[-1, p]], Plus[1, Times[-1, p]]}, r]], If[MemberQ[{4, 5, 6}, n], Disk[{p, Plus[1, Times[-1, p]]}, r]], If[MemberQ[{4, 5, 6}, n], Disk[{Plus[1, Times[-1, p]], p}, r]], If[n === 6, {Disk[{p, 0.5}, r], Disk[{Plus[1, Times[-1, p]], 0.5}, r]}]}, ImageSize -> Tiny]]
+ . Format[Dice[(n_Integer) ? (1 <= #1 <= 6&)], StandardForm] = Block[{p = 0.2, r = 0.05}, Graphics[{EdgeForm[Black], White, Rectangle[], Black, EdgeForm[], If[OddQ[n], Disk[{0.5, 0.5}, r]], If[MemberQ[{2, 3, 4, 5, 6}, n], Disk[{p, p}, r]], If[MemberQ[{2, 3, 4, 5, 6}, n], Disk[{1 - 1*p, 1 - 1*p}, r]], If[MemberQ[{4, 5, 6}, n], Disk[{p, 1 - 1*p}, r]], If[MemberQ[{4, 5, 6}, n], Disk[{1 - 1*p, p}, r]], If[n === 6, {Disk[{p, 0.5}, r], Disk[{1 - 1*p, 0.5}, r]}]}, ImageSize -> Tiny]]
.
- . Format[Dice[n_Integer ? (1 <= #1 <= 6&)], TeXForm] = Block[{p = 0.2, r = 0.05}, Graphics[{EdgeForm[Black], White, Rectangle[], Black, EdgeForm[], If[OddQ[n], Disk[{0.5, 0.5}, r]], If[MemberQ[{2, 3, 4, 5, 6}, n], Disk[{p, p}, r]], If[MemberQ[{2, 3, 4, 5, 6}, n], Disk[{Plus[1, Times[-1, p]], Plus[1, Times[-1, p]]}, r]], If[MemberQ[{4, 5, 6}, n], Disk[{p, Plus[1, Times[-1, p]]}, r]], If[MemberQ[{4, 5, 6}, n], Disk[{Plus[1, Times[-1, p]], p}, r]], If[n === 6, {Disk[{p, 0.5}, r], Disk[{Plus[1, Times[-1, p]], 0.5}, r]}]}, ImageSize -> Tiny]]
+ . Format[Dice[(n_Integer) ? (1 <= #1 <= 6&)], TeXForm] = Block[{p = 0.2, r = 0.05}, Graphics[{EdgeForm[Black], White, Rectangle[], Black, EdgeForm[], If[OddQ[n], Disk[{0.5, 0.5}, r]], If[MemberQ[{2, 3, 4, 5, 6}, n], Disk[{p, p}, r]], If[MemberQ[{2, 3, 4, 5, 6}, n], Disk[{1 - 1*p, 1 - 1*p}, r]], If[MemberQ[{4, 5, 6}, n], Disk[{p, 1 - 1*p}, r]], If[MemberQ[{4, 5, 6}, n], Disk[{1 - 1*p, p}, r]], If[n === 6, {Disk[{p, 0.5}, r], Disk[{1 - 1*p, 0.5}, r]}]}, ImageSize -> Tiny]]
.
- . Format[Dice[n_Integer ? (1 <= #1 <= 6&)], TraditionalForm] = Block[{p = 0.2, r = 0.05}, Graphics[{EdgeForm[Black], White, Rectangle[], Black, EdgeForm[], If[OddQ[n], Disk[{0.5, 0.5}, r]], If[MemberQ[{2, 3, 4, 5, 6}, n], Disk[{p, p}, r]], If[MemberQ[{2, 3, 4, 5, 6}, n], Disk[{Plus[1, Times[-1, p]], Plus[1, Times[-1, p]]}, r]], If[MemberQ[{4, 5, 6}, n], Disk[{p, Plus[1, Times[-1, p]]}, r]], If[MemberQ[{4, 5, 6}, n], Disk[{Plus[1, Times[-1, p]], p}, r]], If[n === 6, {Disk[{p, 0.5}, r], Disk[{Plus[1, Times[-1, p]], 0.5}, r]}]}, ImageSize -> Tiny]]
+ . Format[Dice[(n_Integer) ? (1 <= #1 <= 6&)], TraditionalForm] = Block[{p = 0.2, r = 0.05}, Graphics[{EdgeForm[Black], White, Rectangle[], Black, EdgeForm[], If[OddQ[n], Disk[{0.5, 0.5}, r]], If[MemberQ[{2, 3, 4, 5, 6}, n], Disk[{p, p}, r]], If[MemberQ[{2, 3, 4, 5, 6}, n], Disk[{1 - 1*p, 1 - 1*p}, r]], If[MemberQ[{4, 5, 6}, n], Disk[{p, 1 - 1*p}, r]], If[MemberQ[{4, 5, 6}, n], Disk[{1 - 1*p, p}, r]], If[n === 6, {Disk[{p, 0.5}, r], Disk[{1 - 1*p, 0.5}, r]}]}, ImageSize -> Tiny]]
The empty series of dice shall be displayed as an empty dice:
>> Format[Dice[]] := Graphics[{EdgeForm[Black], White, Rectangle[]}, ImageSize -> Tiny]
diff --git a/mathics/eval/makeboxes.py b/mathics/eval/makeboxes.py
index 5ecd4e65e..df32eb71c 100644
--- a/mathics/eval/makeboxes.py
+++ b/mathics/eval/makeboxes.py
@@ -81,9 +81,7 @@ def eval_fullform_makeboxes(
return Expression(SymbolMakeBoxes, expr, form).evaluate(evaluation)
-def eval_makeboxes(
- self, expr, evaluation: Evaluation, form=SymbolStandardForm
-) -> Expression:
+def eval_makeboxes(expr, evaluation: Evaluation, form=SymbolStandardForm) -> Expression:
"""
This function takes the definitions provided by the evaluation
object, and produces a boxed fullform for expr.
@@ -334,13 +332,21 @@ def parenthesize(
elif element.has_form("PrecedenceForm", 2):
element_prec = element.elements[1].value
# If "element" is a negative number, we need to parenthesize the number. (Fixes #332)
- elif isinstance(element, (Integer, Real)) and element.value < 0:
- # Force parenthesis by adjusting the surrounding context's precedence value,
- # We can't change the precedence for the number since it, doesn't
- # have a precedence value.
- element_prec = precedence
+ elif isinstance(element, (Integer, Real)):
+ if element.value < 0:
+ # Force parenthesis by adjusting the surrounding context's precedence value,
+ # We can't change the precedence for the number since it, doesn't
+ # have a precedence value.
+ element_prec = 480
+ else:
+ element_prec = 999
+ when_equal = False
+ elif isinstance(element, Symbol):
+ precedence = precedence
+ element_prec = 999
+ when_equal = False
else:
- element_prec = builtins_precedence.get(element.get_head())
+ element_prec = builtins_precedence.get(element.get_head(), 670)
if precedence is not None and element_prec is not None:
if precedence > element_prec or (precedence == element_prec and when_equal):
return Expression(
diff --git a/mathics/format/latex.py b/mathics/format/latex.py
index 9a27b4290..be2efdd3a 100644
--- a/mathics/format/latex.py
+++ b/mathics/format/latex.py
@@ -146,9 +146,31 @@ def fractionbox(self, **options) -> str:
_options = self.box_options.copy()
_options.update(options)
options = _options
+
+ # This removes the auxiliar parentheses in
+ # numerator and denominator if they are not
+ # needed. See comment in `mathics.builtin.arithfns.basic.Divide`
+
+ def remove_trivial_parentheses(x):
+ if not isinstance(x, RowBox):
+ return x
+ elements = x.elements
+ if len(elements) != 3:
+ return x
+ left, center, right = elements
+ if not (isinstance(left, String) and isinstance(right, String)):
+ return x
+ open_p, close_p = elements[0].value, elements[-1].value
+ if open_p == "(" and close_p == ")":
+ return center
+ return x
+
+ num = remove_trivial_parentheses(self.num)
+ den = remove_trivial_parentheses(self.den)
+
return "\\frac{%s}{%s}" % (
- lookup_conversion_method(self.num, "latex")(self.num, **options),
- lookup_conversion_method(self.den, "latex")(self.den, **options),
+ lookup_conversion_method(num, "latex")(num, **options),
+ lookup_conversion_method(den, "latex")(den, **options),
)
diff --git a/mathics/format/mathml.py b/mathics/format/mathml.py
index c3ffbd010..e7478f45d 100644
--- a/mathics/format/mathml.py
+++ b/mathics/format/mathml.py
@@ -114,9 +114,30 @@ def fractionbox(self, **options) -> str:
_options = self.box_options.copy()
_options.update(options)
options = _options
+
+ # This removes the auxiliar parentheses in
+ # numerator and denominator if they are not
+ # needed. See comment in `mathics.builtin.arithfns.basic.Divide`
+ def remove_trivial_parentheses(x):
+ if not isinstance(x, RowBox):
+ return x
+ elements = x.elements
+ if len(elements) != 3:
+ return x
+ left, center, right = elements
+ if not (isinstance(left, String) and isinstance(right, String)):
+ return x
+ open_p, close_p = elements[0].value, elements[-1].value
+ if open_p == "(" and close_p == ")":
+ return center
+ return x
+
+ num = remove_trivial_parentheses(self.num)
+ den = remove_trivial_parentheses(self.den)
+
return "%s %s" % (
- lookup_conversion_method(self.num, "mathml")(self.num, **options),
- lookup_conversion_method(self.den, "mathml")(self.den, **options),
+ lookup_conversion_method(num, "mathml")(num, **options),
+ lookup_conversion_method(den, "mathml")(den, **options),
)
diff --git a/mathics/format/text.py b/mathics/format/text.py
index 3d4be51e4..3ac897730 100644
--- a/mathics/format/text.py
+++ b/mathics/format/text.py
@@ -47,9 +47,9 @@ def fractionbox(self, **options) -> str:
num_text = boxes_to_text(self.num, **options)
den_text = boxes_to_text(self.den, **options)
if isinstance(self.num, RowBox):
- num_text = f"({num_text})"
+ num_text = f"{num_text}"
if isinstance(self.den, RowBox):
- den_text = f"({den_text})"
+ den_text = f"{den_text}"
return " / ".join([num_text, den_text])
diff --git a/test/builtin/arithmetic/test_basic.py b/test/builtin/arithmetic/test_basic.py
index 1bec99de7..0d3c479be 100644
--- a/test/builtin/arithmetic/test_basic.py
+++ b/test/builtin/arithmetic/test_basic.py
@@ -137,23 +137,25 @@ def test_multiply(str_expr, str_expected, msg):
("DirectedInfinity[Sqrt[3]]", "Infinity", None),
(
"a b DirectedInfinity[1. + 2. I]",
- "a b ((0.447214 + 0.894427 I) Infinity)",
+ "a b (0.447214 + 0.894427 I) Infinity",
"symbols times floating point complex directed infinity",
),
- ("a b DirectedInfinity[I]", "a b (I Infinity)", ""),
+ ("a b DirectedInfinity[I]", "a b I Infinity", ""),
(
"a b (-1 + 2 I) Infinity",
- "a b ((-1 / 5 + 2 I / 5) Sqrt[5] Infinity)",
+ "a b (-1 / 5 + 2 I / 5) Sqrt[5] Infinity",
"symbols times algebraic exact factor times infinity",
),
(
"a b (-1 + 2 Pi I) Infinity",
- "a b (Infinity (-1 + 2 I Pi) / Sqrt[1 + 4 Pi ^ 2])",
+ "a b (-0.157177 + 0.98757 I) Infinity",
+ # TODO: Improve handling irrational directions
+ # "a b Infinity (-1 + 2 I Pi) / Sqrt[1 + 4 Pi ^ 2]",
"complex irrational exact",
),
(
"a b DirectedInfinity[(1 + 2 I)/ Sqrt[5]]",
- "a b ((1 / 5 + 2 I / 5) Sqrt[5] Infinity)",
+ "a b (1 / 5 + 2 I / 5) Sqrt[5] Infinity",
"symbols times algebraic complex directed infinity",
),
("a b DirectedInfinity[q]", "a b (q Infinity)", ""),
diff --git a/test/format/test_format.py b/test/format/test_format.py
index cc01d3439..a3792a921 100644
--- a/test/format/test_format.py
+++ b/test/format/test_format.py
@@ -528,13 +528,13 @@
"System`StandardForm": "a b c",
"System`TraditionalForm": "a b c",
"System`InputForm": "a ^ ( b / c )",
- "System`OutputForm": "a ^ ( b / c )",
+ "System`OutputForm": r"a ^ ( b c )",
},
"latex": {
"System`StandardForm": "a^{\\frac{b}{c}}",
"System`TraditionalForm": "a^{\\frac{b}{c}}",
"System`InputForm": "a{}^{\\wedge}\\left(b\\text{ / }c\\right)",
- "System`OutputForm": "a\\text{ ${}^{\\wedge}$ }\\left(b\\text{ / }c\\right)",
+ "System`OutputForm": "a\\text{ ${}^{\\wedge}$ }\\left(\\frac{b}{c}\\right)",
},
},
"1/(1+1/(1+1/a))": {
@@ -559,7 +559,12 @@
"Fragile!",
),
"System`OutputForm": (
- "1 / ( 1 + 1 / ( 1 + 1 / a ) )",
+ (
+ r"1 1 + "
+ r"1 1 + "
+ r"1 a"
+ r""
+ ),
"Fragile!",
),
},
@@ -567,7 +572,7 @@
"System`StandardForm": "\\frac{1}{1+\\frac{1}{1+\\frac{1}{a}}}",
"System`TraditionalForm": "\\frac{1}{1+\\frac{1}{1+\\frac{1}{a}}}",
"System`InputForm": "1\\text{ / }\\left(1\\text{ + }1\\text{ / }\\left(1\\text{ + }1\\text{ / }a\\right)\\right)",
- "System`OutputForm": "1\\text{ / }\\left(1\\text{ + }1\\text{ / }\\left(1\\text{ + }1\\text{ / }a\\right)\\right)",
+ "System`OutputForm": r"\frac{1}{1\text{ + }\frac{1}{1\text{ + }\frac{1}{a}}}",
},
},
"Sqrt[1/(1+1/(1+1/a))]": {
@@ -592,7 +597,11 @@
"Fragile!",
),
"System`OutputForm": (
- "Sqrt [ 1 / ( 1 + 1 / ( 1 + 1 / a ) ) ]",
+ (
+ r"Sqrt [ 1 1 + "
+ r"1 1 + 1 a"
+ r" ]"
+ ),
"Fragile!",
),
},
@@ -600,7 +609,7 @@
"System`StandardForm": "\\sqrt{\\frac{1}{1+\\frac{1}{1+\\frac{1}{a}}}}",
"System`TraditionalForm": "\\sqrt{\\frac{1}{1+\\frac{1}{1+\\frac{1}{a}}}}",
"System`InputForm": "\\text{Sqrt}\\left[1\\text{ / }\\left(1\\text{ + }1\\text{ / }\\left(1\\text{ + }1\\text{ / }a\\right)\\right)\\right]",
- "System`OutputForm": "\\text{Sqrt}\\left[1\\text{ / }\\left(1\\text{ + }1\\text{ / }\\left(1\\text{ + }1\\text{ / }a\\right)\\right)\\right]",
+ "System`OutputForm": r"\text{Sqrt}\left[\frac{1}{1\text{ + }\frac{1}{1\text{ + }\frac{1}{a}}}\right]",
},
},
# Grids, arrays and matrices