Repository: sympy/sympy

Issue:
Replace needs_brackets with parenthesize in the latex printer
The LaTeX printer has a `_needs_brackets` method, which checks if something needs parentheses. But there is already the parenthesize module, which works more generally. Uses of `_needs_brackets` should be replaced and `_needs_brackets` should be removed or deprecated.

I've already replaced one instance of this over at https://github.com/sympy/sympy/pull/10084, but I didn't bother with the rest. 

This shouldn't be too hard to fix, as long as you have an understanding of how operator precedence works. And hopefully there should be sufficient test coverage to avoid breaking things, although whoever fixes this should probably check that each instance of `_needs_brackets` is tested to be sure. 

Discussion:

Patch:
diff --git a/sympy/concrete/products.py b/sympy/concrete/products.py
index 7ef8e02724..5ee924355b 100644
--- a/sympy/concrete/products.py
+++ b/sympy/concrete/products.py
@@ -75,7 +75,7 @@ class Product(ExprWithIntLimits):
     >>> Product(k**2,(k, 1, m))
     Product(k**2, (k, 1, m))
     >>> Product(k**2,(k, 1, m)).doit()
-    (factorial(m))**2
+    factorial(m)**2
 
     Wallis' product for pi:
 
@@ -96,7 +96,7 @@ class Product(ExprWithIntLimits):
     Product(4*i**2/((2*i - 1)*(2*i + 1)), (i, 1, n))
     >>> W2e = W2.doit()
     >>> W2e
-    2**(-2*n)*4**n*(factorial(n))**2/(RisingFactorial(1/2, n)*RisingFactorial(3/2, n))
+    2**(-2*n)*4**n*factorial(n)**2/(RisingFactorial(1/2, n)*RisingFactorial(3/2, n))
     >>> limit(W2e, n, oo)
     pi/2
 
@@ -109,7 +109,7 @@ class Product(ExprWithIntLimits):
     pi**2*Product(1 - pi**2/(4*k**2), (k, 1, n))/2
     >>> Pe = P.doit()
     >>> Pe
-    pi**2*RisingFactorial(1 + pi/2, n)*RisingFactorial(-pi/2 + 1, n)/(2*(factorial(n))**2)
+    pi**2*RisingFactorial(1 + pi/2, n)*RisingFactorial(-pi/2 + 1, n)/(2*factorial(n)**2)
     >>> Pe = Pe.rewrite(gamma)
     >>> Pe
     pi**2*gamma(n + 1 + pi/2)*gamma(n - pi/2 + 1)/(2*gamma(1 + pi/2)*gamma(-pi/2 + 1)*gamma(n + 1)**2)
diff --git a/sympy/polys/fields.py b/sympy/polys/fields.py
index df79cfa2ca..cb98de721e 100644
--- a/sympy/polys/fields.py
+++ b/sympy/polys/fields.py
@@ -63,7 +63,7 @@ def sfield(exprs, *symbols, **options):
     >>> K
     Rational function field in x, exp(1/x), log(x), x**(1/3) over ZZ with lex order
     >>> f
-    (4*x**2*exp(1/x) + x*exp(1/x)*log(x))/((x**(1/3))**5)
+    (4*x**2*(exp(1/x)) + x*(exp(1/x))*(log(x)))/((x**(1/3))**5)
     """
     single = False
     if not is_sequence(exprs):
diff --git a/sympy/printing/latex.py b/sympy/printing/latex.py
index 01c84ae988..6d3496cbc8 100644
--- a/sympy/printing/latex.py
+++ b/sympy/printing/latex.py
@@ -423,11 +423,9 @@ def _print_Pow(self, expr):
         elif self._settings['fold_frac_powers'] \
             and expr.exp.is_Rational \
                 and expr.exp.q != 1:
-            base, p, q = self._print(expr.base), expr.exp.p, expr.exp.q
+            base, p, q = self.parenthesize(expr.base, PRECEDENCE['Pow']), expr.exp.p, expr.exp.q
             if expr.base.is_Function:
                 return self._print(expr.base, "%s/%s" % (p, q))
-            if self._needs_brackets(expr.base):
-                return r"\left(%s\right)^{%s/%s}" % (base, p, q)
             return r"%s^{%s/%s}" % (base, p, q)
         elif expr.exp.is_Rational and expr.exp.is_negative and expr.base.is_commutative:
             # Things like 1/x
@@ -446,12 +444,9 @@ def _print_Pow(self, expr):
                     #the result comes with a minus and a space, so we remove
                     if tex[:1] == "-":
                         return tex[1:].strip()
-                if self._needs_brackets(expr.base):
-                    tex = r"\left(%s\right)^{%s}"
-                else:
-                    tex = r"%s^{%s}"
+                tex = r"%s^{%s}"
 
-                return tex % (self._print(expr.base),
+                return tex % (self.parenthesize(expr.base, PRECEDENCE['Pow']),
                               self._print(expr.exp))
 
     def _print_Sum(self, expr):
@@ -777,18 +772,12 @@ def _print_Abs(self, expr, exp=None):
     _print_Determinant = _print_Abs
 
     def _print_re(self, expr, exp=None):
-        if self._needs_brackets(expr.args[0]):
-            tex = r"\Re {\left (%s \right )}" % self._print(expr.args[0])
-        else:
-            tex = r"\Re{%s}" % self._print(expr.args[0])
+        tex = r"\Re{%s}" % self.parenthesize(expr.args[0], PRECEDENCE['Func'])
 
         return self._do_exponent(tex, exp)
 
     def _print_im(self, expr, exp=None):
-        if self._needs_brackets(expr.args[0]):
-            tex = r"\Im {\left ( %s \right )}" % self._print(expr.args[0])
-        else:
-            tex = r"\Im{%s}" % self._print(expr.args[0])
+        tex = r"\Im{%s}" % self.parenthesize(expr.args[0], PRECEDENCE['Func'])
 
         return self._do_exponent(tex, exp)
 
@@ -951,11 +940,7 @@ def _print_fresnelc(self, expr, exp=None):
             return r"C%s" % tex
 
     def _print_subfactorial(self, expr, exp=None):
-        x = expr.args[0]
-        if self._needs_brackets(x):
-            tex = r"!\left(%s\right)" % self._print(x)
-        else:
-            tex = "!" + self._print(x)
+        tex = r"!%s" % self.parenthesize(expr.args[0], PRECEDENCE["Func"])
 
         if exp is not None:
             return r"%s^{%s}" % (tex, exp)
@@ -963,11 +948,7 @@ def _print_subfactorial(self, expr, exp=None):
             return tex
 
     def _print_factorial(self, expr, exp=None):
-        x = expr.args[0]
-        if self._needs_brackets(x):
-            tex = r"\left(%s\right)!" % self._print(x)
-        else:
-            tex = self._print(x) + "!"
+        tex = r"%s!" % self.parenthesize(expr.args[0], PRECEDENCE["Func"])
 
         if exp is not None:
             return r"%s^{%s}" % (tex, exp)
@@ -975,11 +956,7 @@ def _print_factorial(self, expr, exp=None):
             return tex
 
     def _print_factorial2(self, expr, exp=None):
-        x = expr.args[0]
-        if self._needs_brackets(x):
-            tex = r"\left(%s\right)!!" % self._print(x)
-        else:
-            tex = self._print(x) + "!!"
+        tex = r"%s!!" % self.parenthesize(expr.args[0], PRECEDENCE["Func"])
 
         if exp is not None:
             return r"%s^{%s}" % (tex, exp)
@@ -997,10 +974,7 @@ def _print_binomial(self, expr, exp=None):
 
     def _print_RisingFactorial(self, expr, exp=None):
         n, k = expr.args
-        if self._needs_brackets(n):
-            base = r"\left(%s\right)" % self._print(n)
-        else:
-            base = self._print(n)
+        base = r"%s" % self.parenthesize(n, PRECEDENCE['Func'])
 
         tex = r"{%s}^{\left(%s\right)}" % (base, self._print(k))
 
@@ -1008,10 +982,7 @@ def _print_RisingFactorial(self, expr, exp=None):
 
     def _print_FallingFactorial(self, expr, exp=None):
         n, k = expr.args
-        if self._needs_brackets(k):
-            sub = r"\left(%s\right)" % self._print(k)
-        else:
-            sub = self._print(k)
+        sub = r"%s" % self.parenthesize(k, PRECEDENCE['Func'])
 
         tex = r"{\left(%s\right)}_{%s}" % (self._print(n), sub)
 
diff --git a/sympy/printing/precedence.py b/sympy/printing/precedence.py
index 1f4b3eb874..83e503dc21 100644
--- a/sympy/printing/precedence.py
+++ b/sympy/printing/precedence.py
@@ -14,6 +14,7 @@
     "Add": 40,
     "Mul": 50,
     "Pow": 60,
+    "Func": 70,
     "Not": 100,
     "Atom": 1000
 }
@@ -32,8 +33,7 @@
     "Relational": PRECEDENCE["Relational"],
     "Sub": PRECEDENCE["Add"],
     "Not": PRECEDENCE["Not"],
-    "factorial": PRECEDENCE["Pow"],
-    "factorial2": PRECEDENCE["Pow"],
+    "Function" : PRECEDENCE["Func"],
     "NegativeInfinity": PRECEDENCE["Add"],
     "MatAdd": PRECEDENCE["Add"],
     "MatMul": PRECEDENCE["Mul"],
diff --git a/sympy/series/formal.py b/sympy/series/formal.py
index b7c2ded401..950bea189f 100644
--- a/sympy/series/formal.py
+++ b/sympy/series/formal.py
@@ -489,7 +489,7 @@ def rsolve_hypergeometric(f, x, P, Q, k, m):
     >>> from sympy.abc import x, k
 
     >>> rh(exp(x), x, -S.One, (k + 1), k, 1)
-    (Piecewise((1/(factorial(k)), Eq(Mod(k, 1), 0)), (0, True)), 1, 1)
+    (Piecewise((1/factorial(k), Eq(Mod(k, 1), 0)), (0, True)), 1, 1)
 
     >>> rh(ln(1 + x), x, k**2, k*(k + 1), k, 1)
     (Piecewise(((-1)**(k - 1)*factorial(k - 1)/RisingFactorial(2, k - 1),
@@ -672,7 +672,7 @@ def solve_de(f, x, DE, order, g, k):
     >>> from sympy.abc import x, k, f
 
     >>> solve_de(exp(x), x, D(f(x), x) - f(x), 1, f, k)
-    (Piecewise((1/(factorial(k)), Eq(Mod(k, 1), 0)), (0, True)), 1, 1)
+    (Piecewise((1/factorial(k), Eq(Mod(k, 1), 0)), (0, True)), 1, 1)
 
     >>> solve_de(ln(1 + x), x, (x + 1)*D(f(x), x, 2) + D(f(x)), 2, f, k)
     (Piecewise(((-1)**(k - 1)*factorial(k - 1)/RisingFactorial(2, k - 1),
@@ -717,7 +717,7 @@ def hyper_algorithm(f, x, k, order=4):
     >>> from sympy.abc import x, k
 
     >>> hyper_algorithm(exp(x), x, k)
-    (Piecewise((1/(factorial(k)), Eq(Mod(k, 1), 0)), (0, True)), 1, 1)
+    (Piecewise((1/factorial(k), Eq(Mod(k, 1), 0)), (0, True)), 1, 1)
 
     >>> hyper_algorithm(ln(1 + x), x, k)
     (Piecewise(((-1)**(k - 1)*factorial(k - 1)/RisingFactorial(2, k - 1),


Test patch:
diff --git a/sympy/physics/quantum/tests/test_printing.py b/sympy/physics/quantum/tests/test_printing.py
index 7a393d79a2..db5382ed22 100644
--- a/sympy/physics/quantum/tests/test_printing.py
+++ b/sympy/physics/quantum/tests/test_printing.py
@@ -85,7 +85,7 @@ def test_anticommutator():
 """)
     assert pretty(ac_tall) == ascii_str
     assert upretty(ac_tall) == ucode_str
-    assert latex(ac_tall) == r'\left\{\left(A\right)^{2},B\right\}'
+    assert latex(ac_tall) == r'\left\{A^{2},B\right\}'
     sT(ac_tall, "AntiCommutator(Pow(Operator(Symbol('A')), Integer(2)),Operator(Symbol('B')))")
 
 
@@ -194,7 +194,7 @@ def test_commutator():
 """)
     assert pretty(c_tall) == ascii_str
     assert upretty(c_tall) == ucode_str
-    assert latex(c_tall) == r'\left[\left(A\right)^{2},B\right]'
+    assert latex(c_tall) == r'\left[A^{2},B\right]'
     sT(c_tall, "Commutator(Pow(Operator(Symbol('A')), Integer(2)),Operator(Symbol('B')))")
 
 
@@ -529,7 +529,7 @@ def test_operator():
 """)
     assert pretty(inv) == ascii_str
     assert upretty(inv) == ucode_str
-    assert latex(inv) == r'\left(A\right)^{-1}'
+    assert latex(inv) == r'A^{-1}'
     sT(inv, "Pow(Operator(Symbol('A')), Integer(-1))")
     assert str(d) == 'DifferentialOperator(Derivative(f(x), x),f(x))'
     ascii_str = \
@@ -822,7 +822,7 @@ def test_big_expr():
     assert pretty(e1) == ascii_str
     assert upretty(e1) == ucode_str
     assert latex(e1) == \
-        r'{\left(J_z\right)^{2}}\otimes \left({A^{\dag} + B^{\dag}}\right) \left\{\left(DifferentialOperator\left(\frac{d}{d x} f{\left (x \right )},f{\left (x \right )}\right)^{\dag}\right)^{3},A^{\dag} + B^{\dag}\right\} \left({\left\langle 1,0\right|} + {\left\langle 1,1\right|}\right) \left({\left|0,0\right\rangle } + {\left|1,-1\right\rangle }\right)'
+        r'{J_z^{2}}\otimes \left({A^{\dag} + B^{\dag}}\right) \left\{\left(DifferentialOperator\left(\frac{d}{d x} f{\left (x \right )},f{\left (x \right )}\right)^{\dag}\right)^{3},A^{\dag} + B^{\dag}\right\} \left({\left\langle 1,0\right|} + {\left\langle 1,1\right|}\right) \left({\left|0,0\right\rangle } + {\left|1,-1\right\rangle }\right)'
     sT(e1, "Mul(TensorProduct(Pow(JzOp(Symbol('J')), Integer(2)), Add(Dagger(Operator(Symbol('A'))), Dagger(Operator(Symbol('B'))))), AntiCommutator(Pow(Dagger(DifferentialOperator(Derivative(Function('f')(Symbol('x')), Symbol('x')),Function('f')(Symbol('x')))), Integer(3)),Add(Dagger(Operator(Symbol('A'))), Dagger(Operator(Symbol('B'))))), Add(JzBra(Integer(1),Integer(0)), JzBra(Integer(1),Integer(1))), Add(JzKet(Integer(0),Integer(0)), JzKet(Integer(1),Integer(-1))))")
     assert str(e2) == '[Jz**2,A + B]*{E**(-2),Dagger(D)*Dagger(C)}*[J2,Jz]'
     ascii_str = \
@@ -840,7 +840,7 @@ def test_big_expr():
     assert pretty(e2) == ascii_str
     assert upretty(e2) == ucode_str
     assert latex(e2) == \
-        r'\left[\left(J_z\right)^{2},A + B\right] \left\{\left(E\right)^{-2},D^{\dag} C^{\dag}\right\} \left[J^2,J_z\right]'
+        r'\left[J_z^{2},A + B\right] \left\{E^{-2},D^{\dag} C^{\dag}\right\} \left[J^2,J_z\right]'
     sT(e2, "Mul(Commutator(Pow(JzOp(Symbol('J')), Integer(2)),Add(Operator(Symbol('A')), Operator(Symbol('B')))), AntiCommutator(Pow(Operator(Symbol('E')), Integer(-2)),Mul(Dagger(Operator(Symbol('D'))), Dagger(Operator(Symbol('C'))))), Commutator(J2Op(Symbol('J')),JzOp(Symbol('J'))))")
     assert str(e3) == \
         "Wigner3j(1, 2, 3, 4, 5, 6)*[Dagger(B) + A,C + D]x(-J2 + Jz)*|1,0><1,1|*(|1,0,j1=1,j2=1> + |1,1,j1=1,j2=1>)x|1,-1,j1=1,j2=1>"
diff --git a/sympy/physics/vector/tests/test_printing.py b/sympy/physics/vector/tests/test_printing.py
index d9639339c2..84472a44d8 100644
--- a/sympy/physics/vector/tests/test_printing.py
+++ b/sympy/physics/vector/tests/test_printing.py
@@ -129,7 +129,7 @@ def test_vector_latex_with_functions():
 
     v = omega.diff() ** alpha * N.x
 
-    assert v._latex() == (r'\left(\dot{\omega}\right)^{\alpha}'
+    assert v._latex() == (r'\dot{\omega}^{\alpha}'
                           r'\mathbf{\hat{n}_x}')
 
 
diff --git a/sympy/printing/tests/test_precedence.py b/sympy/printing/tests/test_precedence.py
index 9b3e0dfaec..686b574039 100644
--- a/sympy/printing/tests/test_precedence.py
+++ b/sympy/printing/tests/test_precedence.py
@@ -19,9 +19,10 @@ def test_Add():
 
 
 def test_Function():
-    assert precedence(sin(x)) == PRECEDENCE["Atom"]
-    assert precedence(Derivative(x, y)) == PRECEDENCE["Atom"]
+    assert precedence(sin(x)) == PRECEDENCE["Func"]
 
+def test_Derivative():
+    assert precedence(Derivative(x, y)) == PRECEDENCE["Atom"]
 
 def test_Integral():
     assert precedence(Integral(x, y)) == PRECEDENCE["Atom"]