Repository: sympy/sympy

Issue:
Canonical and pi
```
>>> from sympy import symbols, pi
>>> x = symbols('x')
>>> (x > 2).canonical
x > 2
>>> (x > pi).canonical
pi < x
>>> (pi < x).canonical
pi < x
>>> (-pi > -x).canonical
-pi > -x
>>> (-x < -pi).canonical
-pi > -x
>>> (x > -2).canonical
x > -2
>>> (x > -pi).canonical
x > -pi
>>> (x > pi/2).canonical
x > pi/2
>>> (pi/2 < x).canonical
x > pi/2
>>> (-pi < x).canonical
x > -pi
```

For all these expressions except the second to fifth, the method does what it is supposed to do, put the symbol on the left hand side and the number of the right hand side. Any idea @smichr ? (The same hold for `Eq`, just happened to try with `<` and `>`)

It messes things up a bit for #15898 (I do not think that it may be sorting things out directly anyway, but I ran across it trying to figure out to get some other cases to simplify as well and with the current behavior there is no way to simplify a piecewise with `Eq(x, pi)`).


Discussion:

Patch:
diff --git a/doc/src/tutorial/calculus.rst b/doc/src/tutorial/calculus.rst
index f4831ee135..2ee581bb94 100644
--- a/doc/src/tutorial/calculus.rst
+++ b/doc/src/tutorial/calculus.rst
@@ -211,7 +211,7 @@ definite integrals.  Here is a sampling of some of the power of ``integrate``.
     ⌡
     0
     >>> integ.doit()
-    ⎧ Γ(y + 1)    for -re(y) < 1
+    ⎧ Γ(y + 1)    for re(y) > -1
     ⎪
     ⎪∞
     ⎪⌠
diff --git a/sympy/concrete/summations.py b/sympy/concrete/summations.py
index 31f4bfa484..23c5d73caf 100644
--- a/sympy/concrete/summations.py
+++ b/sympy/concrete/summations.py
@@ -1135,7 +1135,7 @@ def _eval_sum_hyper(f, i, a):
     bq = params[1]
     x = ab[0]/ab[1]
     h = hyper(ap, bq, x)
-    f = combsimp(f)
+
     return f.subs(i, 0)*hyperexpand(h), h.convergence_statement
 
 
diff --git a/sympy/core/relational.py b/sympy/core/relational.py
index 379789e59c..a504be1497 100644
--- a/sympy/core/relational.py
+++ b/sympy/core/relational.py
@@ -104,7 +104,7 @@ def rhs(self):
 
     @property
     def reversed(self):
-        """Return the relationship with sides (and sign) reversed.
+        """Return the relationship with sides reversed.
 
         Examples
         ========
@@ -120,10 +120,35 @@ def reversed(self):
         >>> _.reversed
         1 > x
         """
-        ops = {Gt: Lt, Ge: Le, Lt: Gt, Le: Ge}
+        ops = {Eq: Eq, Gt: Lt, Ge: Le, Lt: Gt, Le: Ge, Ne: Ne}
         a, b = self.args
         return Relational.__new__(ops.get(self.func, self.func), b, a)
 
+    @property
+    def reversedsign(self):
+        """Return the relationship with signs reversed.
+
+        Examples
+        ========
+
+        >>> from sympy import Eq
+        >>> from sympy.abc import x
+        >>> Eq(x, 1)
+        Eq(x, 1)
+        >>> _.reversedsign
+        Eq(-x, -1)
+        >>> x < 1
+        x < 1
+        >>> _.reversedsign
+        -x > -1
+        """
+        a, b = self.args
+        if not (isinstance(a, BooleanAtom) or isinstance(b, BooleanAtom)):
+            ops = {Eq: Eq, Gt: Lt, Ge: Le, Lt: Gt, Le: Ge, Ne: Ne}
+            return Relational.__new__(ops.get(self.func, self.func), -a, -b)
+        else:
+            return self
+
     @property
     def negated(self):
         """Return the negated relationship.
@@ -162,7 +187,8 @@ def _eval_evalf(self, prec):
     @property
     def canonical(self):
         """Return a canonical form of the relational by putting a
-        Number on the rhs else ordering the args. No other
+        Number on the rhs else ordering the args. The relation is also changed
+        so that the left-hand side expression does not start with a `-`. No other
         simplification is attempted.
 
         Examples
@@ -180,13 +206,24 @@ def canonical(self):
         """
         args = self.args
         r = self
-        if r.rhs.is_Number:
-            if r.lhs.is_Number and r.lhs > r.rhs:
+        if r.rhs.is_number:
+            if r.rhs.is_Number and r.lhs.is_Number and r.lhs > r.rhs:
                 r = r.reversed
-        elif r.lhs.is_Number:
+        elif r.lhs.is_number:
             r = r.reversed
         elif tuple(ordered(args)) != args:
             r = r.reversed
+
+        # Check if first value has negative sign
+        if not isinstance(r.lhs, BooleanAtom) and r.lhs.could_extract_minus_sign():
+            r = r.reversedsign
+        elif not isinstance(r.rhs, BooleanAtom) and not r.rhs.is_number and r.rhs.could_extract_minus_sign():
+            # Right hand side has a minus, but not lhs.
+            # How does the expression with reversed signs behave?
+            # This is so that expressions of the type Eq(x, -y) and Eq(-x, y) have the same canonical representation
+            expr1, _ = ordered([r.lhs, -r.rhs])
+            if expr1 != r.lhs:
+                r = r.reversed.reversedsign
         return r
 
     def equals(self, other, failing_expression=False):
diff --git a/sympy/integrals/integrals.py b/sympy/integrals/integrals.py
index 073bc88552..7bcb8c2e75 100644
--- a/sympy/integrals/integrals.py
+++ b/sympy/integrals/integrals.py
@@ -1448,7 +1448,7 @@ def integrate(*args, **kwargs):
     in interactive sessions and should be avoided in library code.
 
     >>> integrate(x**a*exp(-x), (x, 0, oo)) # same as conds='piecewise'
-    Piecewise((gamma(a + 1), -re(a) < 1),
+    Piecewise((gamma(a + 1), re(a) > -1),
         (Integral(x**a*exp(-x), (x, 0, oo)), True))
 
     >>> integrate(x**a*exp(-x), (x, 0, oo), conds='none')
diff --git a/sympy/integrals/transforms.py b/sympy/integrals/transforms.py
index ca706c1235..f32feb9bc1 100644
--- a/sympy/integrals/transforms.py
+++ b/sympy/integrals/transforms.py
@@ -5,6 +5,7 @@
 from sympy.core import S
 from sympy.core.compatibility import reduce, range, iterable
 from sympy.core.function import Function
+from sympy.core.relational import _canonical, Ge, Gt
 from sympy.core.numbers import oo
 from sympy.core.symbol import Dummy
 from sympy.integrals import integrate, Integral
@@ -987,7 +988,6 @@ def process_conds(conds):
         a = -oo
         aux = S.true
         conds = conjuncts(to_cnf(conds))
-        u = Dummy('u', real=True)
         p, q, w1, w2, w3, w4, w5 = symbols(
             'p q w1 w2 w3 w4 w5', cls=Wild, exclude=[s])
         patterns = (
@@ -1003,21 +1003,23 @@ def process_conds(conds):
             for d in disjuncts(c):
                 if d.is_Relational and s in d.rhs.free_symbols:
                     d = d.reversed
+                if d.is_Relational and isinstance(d, (Ge, Gt)):
+                    d = d.reversedsign
                 for pat in patterns:
                     m = d.match(pat)
                     if m:
                         break
                 if m:
                     if m[q].is_positive and m[w2]/m[p] == pi/2:
-                        d = re(s + m[w3]) > 0
-                m = d.match(cos(w1*abs(arg(s*w5))*w2)*abs(s**w3)**w4 - p > 0)
+                        d = -re(s + m[w3]) < 0
+                m = d.match(p - cos(w1*abs(arg(s*w5))*w2)*abs(s**w3)**w4 < 0)
                 if not m:
                     m = d.match(
-                        cos(abs(arg_(s**w1*w5, q))*w2)*abs(s**w3)**w4 - p > 0)
+                        cos(p - abs(arg_(s**w1*w5, q))*w2)*abs(s**w3)**w4 < 0)
                 if not m:
                     m = d.match(
-                        cos(abs(arg_(polar_lift(s)**w1*w5, q))*w2
-                            )*abs(s**w3)**w4 - p > 0)
+                        p - cos(abs(arg_(polar_lift(s)**w1*w5, q))*w2
+                            )*abs(s**w3)**w4 < 0)
                 if m and all(m[wild].is_positive for wild in [w1, w2, w3, w4, w5]):
                     d = re(s) > m[p]
                 d_ = d.replace(
@@ -1064,7 +1066,7 @@ def sbs(expr):
     if simplify:
         F = _simplifyconds(F, s, a)
         aux = _simplifyconds(aux, s, a)
-    return _simplify(F.subs(s, s_), simplify), sbs(a), sbs(aux)
+    return _simplify(F.subs(s, s_), simplify), sbs(a), _canonical(sbs(aux))
 
 
 class LaplaceTransform(IntegralTransform):
@@ -1124,7 +1126,7 @@ def laplace_transform(f, t, s, **hints):
     >>> from sympy.integrals import laplace_transform
     >>> from sympy.abc import t, s, a
     >>> laplace_transform(t**a, t, s)
-    (s**(-a)*gamma(a + 1)/s, 0, -re(a) < 1)
+    (s**(-a)*gamma(a + 1)/s, 0, re(a) > -1)
 
     See Also
     ========


Test patch:
diff --git a/sympy/concrete/tests/test_sums_products.py b/sympy/concrete/tests/test_sums_products.py
index c5ea4bb57e..ab782e29e7 100644
--- a/sympy/concrete/tests/test_sums_products.py
+++ b/sympy/concrete/tests/test_sums_products.py
@@ -3,7 +3,7 @@
     factorial, Function, harmonic, I, Integral, KroneckerDelta, log,
     nan, Ne, Or, oo, pi, Piecewise, Product, product, Rational, S, simplify,
     sin, sqrt, Sum, summation, Symbol, symbols, sympify, zeta, gamma, Le,
-    Indexed, Idx, IndexedBase, prod, Dummy, lowergamma)
+    Indexed, Idx, IndexedBase, prod, Dummy)
 from sympy.abc import a, b, c, d, f, k, m, x, y, z
 from sympy.concrete.summations import telescopic
 from sympy.utilities.pytest import XFAIL, raises
@@ -1069,12 +1069,6 @@ def test_issue_14640():
     assert s.subs({a: 2, b: 3, n: 5}) == 122
 
 
-def test_issue_15943():
-    assert Sum(binomial(n, k)*factorial(n - k), (k, 0, n)).doit() == -E*(
-        n + 1)*gamma(n + 1)*lowergamma(n + 1, 1)/gamma(n + 2
-        ) + E*gamma(n + 1)
-
-
 def test_Sum_dummy_eq():
     assert not Sum(x, (x, a, b)).dummy_eq(1)
     assert not Sum(x, (x, a, b)).dummy_eq(Sum(x, (x, a, b), (a, 1, 2)))
diff --git a/sympy/core/tests/test_relational.py b/sympy/core/tests/test_relational.py
index 6b61c727f6..f0d0af7ed9 100644
--- a/sympy/core/tests/test_relational.py
+++ b/sympy/core/tests/test_relational.py
@@ -1,14 +1,16 @@
 from sympy.utilities.pytest import XFAIL, raises
 from sympy import (S, Symbol, symbols, nan, oo, I, pi, Float, And, Or,
-    Not, Implies, Xor, zoo, sqrt, Rational, simplify, Function, Eq,
+    Not, Implies, Xor, zoo, sqrt, Rational, simplify, Function,
     log, cos, sin, Add, floor, ceiling)
 from sympy.core.compatibility import range
 from sympy.core.relational import (Relational, Equality, Unequality,
                                    GreaterThan, LessThan, StrictGreaterThan,
                                    StrictLessThan, Rel, Eq, Lt, Le,
-                                   Gt, Ge, Ne, _canonical)
+                                   Gt, Ge, Ne)
 from sympy.sets.sets import Interval, FiniteSet
 
+from itertools import combinations
+
 x, y, z, t = symbols('x,y,z,t')
 
 
@@ -824,3 +826,54 @@ def test_negated_property():
 
     for f in (Eq, Ne, Ge, Gt, Le, Lt):
         assert f(x, y).negated.negated == f(x, y)
+
+
+def test_reversedsign_property():
+    eq = Eq(x, y)
+    assert eq.reversedsign == Eq(-x, -y)
+
+    eq = Ne(x, y)
+    assert eq.reversedsign == Ne(-x, -y)
+
+    eq = Ge(x + y, y - x)
+    assert eq.reversedsign == Le(-x - y, x - y)
+
+    for f in (Eq, Ne, Ge, Gt, Le, Lt):
+        assert f(x, y).reversedsign.reversedsign == f(x, y)
+
+    for f in (Eq, Ne, Ge, Gt, Le, Lt):
+        assert f(-x, y).reversedsign.reversedsign == f(-x, y)
+
+    for f in (Eq, Ne, Ge, Gt, Le, Lt):
+        assert f(x, -y).reversedsign.reversedsign == f(x, -y)
+
+    for f in (Eq, Ne, Ge, Gt, Le, Lt):
+        assert f(-x, -y).reversedsign.reversedsign == f(-x, -y)
+
+def test_reversed_reversedsign_property():
+    for f in (Eq, Ne, Ge, Gt, Le, Lt):
+        assert f(x, y).reversed.reversedsign == f(x, y).reversedsign.reversed
+
+    for f in (Eq, Ne, Ge, Gt, Le, Lt):
+        assert f(-x, y).reversed.reversedsign == f(-x, y).reversedsign.reversed
+
+    for f in (Eq, Ne, Ge, Gt, Le, Lt):
+        assert f(x, -y).reversed.reversedsign == f(x, -y).reversedsign.reversed
+
+    for f in (Eq, Ne, Ge, Gt, Le, Lt):
+        assert f(-x, -y).reversed.reversedsign == f(-x, -y).reversedsign.reversed
+
+def test_improved_canonical():
+    def test_different_forms(listofforms):
+        for form1, form2 in combinations(listofforms, 2):
+            assert form1.canonical == form2.canonical
+    def generate_forms(expr):
+        return [expr, expr.reversed, expr.reversedsign, expr.reversed.reversedsign]
+    test_different_forms(generate_forms(x > -y))
+    test_different_forms(generate_forms(x >= -y))
+    test_different_forms(generate_forms(Eq(x, -y)))
+    test_different_forms(generate_forms(Ne(x, -y)))
+    test_different_forms(generate_forms(pi < x))
+    test_different_forms(generate_forms(pi - 5*y < -x + 2*y**2 - 7))
+
+    assert (pi >= x).canonical == (x <= pi)
diff --git a/sympy/integrals/tests/test_meijerint.py b/sympy/integrals/tests/test_meijerint.py
index 091dc8cf2d..325e115a95 100644
--- a/sympy/integrals/tests/test_meijerint.py
+++ b/sympy/integrals/tests/test_meijerint.py
@@ -630,7 +630,7 @@ def test_messy():
 
     # TODO maybe simplify the inequalities?
     assert laplace_transform(besselj(a, x), x, s)[1:] == \
-        (0, And(S(0) < re(a/2) + S(1)/2, S(0) < re(a/2) + 1))
+        (0, And(re(a/2) + S(1)/2 > S(0), re(a/2) + 1 > S(0)))
 
     # NOTE s < 0 can be done, but argument reduction is not good enough yet
     assert fourier_transform(besselj(1, x)/x, x, s, noconds=False) == \
diff --git a/sympy/integrals/tests/test_transforms.py b/sympy/integrals/tests/test_transforms.py
index 04d3f891aa..8ccc4a1f14 100644
--- a/sympy/integrals/tests/test_transforms.py
+++ b/sympy/integrals/tests/test_transforms.py
@@ -99,10 +99,10 @@ def test_mellin_transform():
         (1/(nu + s), (-re(nu), oo), True)
 
     assert MT((1 - x)**(beta - 1)*Heaviside(1 - x), x, s) == \
-        (gamma(beta)*gamma(s)/gamma(beta + s), (0, oo), -re(beta) < 0)
+        (gamma(beta)*gamma(s)/gamma(beta + s), (0, oo), re(beta) > 0)
     assert MT((x - 1)**(beta - 1)*Heaviside(x - 1), x, s) == \
         (gamma(beta)*gamma(1 - beta - s)/gamma(1 - s),
-            (-oo, -re(beta) + 1), -re(beta) < 0)
+            (-oo, -re(beta) + 1), re(beta) > 0)
 
     assert MT((1 + x)**(-rho), x, s) == \
         (gamma(s)*gamma(rho - s)/gamma(rho), (0, re(rho)), True)
@@ -116,7 +116,7 @@ def test_mellin_transform():
         (0, re(rho)), And(re(rho) - 1 < 0, re(rho) < 1))
     mt = MT((1 - x)**(beta - 1)*Heaviside(1 - x)
             + a*(x - 1)**(beta - 1)*Heaviside(x - 1), x, s)
-    assert mt[1], mt[2] == ((0, -re(beta) + 1), -re(beta) < 0)
+    assert mt[1], mt[2] == ((0, -re(beta) + 1), re(beta) > 0)
 
     assert MT((x**a - b**a)/(x - b), x, s)[0] == \
         pi*b**(a + s - 1)*sin(pi*a)/(sin(pi*s)*sin(pi*(a + s)))
@@ -299,7 +299,7 @@ def test_expint():
     # TODO LT of Si, Shi, Chi is a mess ...
     assert laplace_transform(Ci(x), x, s) == (-log(1 + s**2)/2/s, 0, True)
     assert laplace_transform(expint(a, x), x, s) == \
-        (lerchphi(s*exp_polar(I*pi), 1, a), 0, S(0) < re(a))
+        (lerchphi(s*exp_polar(I*pi), 1, a), 0, re(a) > S(0))
     assert laplace_transform(expint(1, x), x, s) == (log(s + 1)/s, 0, True)
     assert laplace_transform(expint(2, x), x, s) == \
         ((s - log(s + 1))/s**2, 0, True)
@@ -808,14 +808,14 @@ def test_issue_7173():
     ans = laplace_transform(sinh(a*x)*cosh(a*x), x, s)
     r, e = cse(ans)
     assert r == [
-        (x0, pi/2),
-        (x1, arg(a)),
-        (x2, Abs(x1)),
-        (x3, Abs(x1 + pi))]
+        (x0, arg(a)),
+        (x1, Abs(x0)),
+        (x2, pi/2),
+        (x3, Abs(x0 + pi))]
     assert e == [
         a/(-4*a**2 + s**2),
         0,
-        ((x0 >= x2) | (x2 < x0)) & ((x0 >= x3) | (x3 < x0))]
+        ((x1 <= x2) | (x1 < x2)) & ((x3 <= x2) | (x3 < x2))]
 
 
 def test_issue_8514():