Repository: sympy/sympy

Issue:
reduce_inequalities error
```python
>>> reduce_inequalities(x**2>=0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "sympy\solvers\inequalities.py", line 605, in reduce_inequalities
    rv = _reduce_inequalities(inequalities, symbols)
  File "sympy\solvers\inequalities.py", line 512, in _reduce_inequalities
    expr, rel = inequality.lhs, inequality.rel_op  # rhs is 0
AttributeError: 'BooleanTrue' object has no attribute 'lhs'
```

Discussion:

Patch:
diff --git a/sympy/core/mul.py b/sympy/core/mul.py
index 30a51adb8e..2a0e6090e4 100644
--- a/sympy/core/mul.py
+++ b/sympy/core/mul.py
@@ -368,17 +368,17 @@ def flatten(cls, seq):
 
         # gather exponents of common bases...
         def _gather(c_powers):
+            new_c_powers = []
             common_b = {}  # b:e
             for b, e in c_powers:
                 co = e.as_coeff_Mul()
-                common_b.setdefault(b, {}).setdefault(
-                    co[1], []).append(co[0])
+                common_b.setdefault(b, {}).setdefault(co[1], []).append(co[0])
             for b, d in common_b.items():
                 for di, li in d.items():
                     d[di] = Add(*li)
-            new_c_powers = []
             for b, e in common_b.items():
-                new_c_powers.extend([(b, c*t) for t, c in e.items()])
+                for t, c in e.items():
+                    new_c_powers.append((b, c*t))
             return new_c_powers
 
         # in c_powers
@@ -402,45 +402,14 @@ def _gather(c_powers):
 
         #  0             1
         # x  -> 1       x  -> x
-
-        # this should only need to run twice; if it fails because
-        # it needs to be run more times, perhaps this should be
-        # changed to a "while True" loop -- the only reason it
-        # isn't such now is to allow a less-than-perfect result to
-        # be obtained rather than raising an error or entering an
-        # infinite loop
-        for i in range(2):
-            new_c_powers = []
-            changed = False
-            for b, e in c_powers:
-                if e.is_zero:
-                    continue
-                if e is S.One:
-                    if b.is_Number:
-                        coeff *= b
-                        continue
-                    p = b
-                if e is not S.One:
-                    p = Pow(b, e)
-                    # check to make sure that the base doesn't change
-                    # after exponentiation; to allow for unevaluated
-                    # Pow, we only do so if b is not already a Pow
-                    if p.is_Pow and not b.is_Pow:
-                        bi = b
-                        b, e = p.as_base_exp()
-                        if b != bi:
-                            changed = True
-                c_part.append(p)
-                new_c_powers.append((b, e))
-            # there might have been a change, but unless the base
-            # matches some other base, there is nothing to do
-            if changed and len(set(
-                    b for b, e in new_c_powers)) != len(new_c_powers):
-                # start over again
-                c_part = []
-                c_powers = _gather(new_c_powers)
-            else:
-                break
+        for b, e in c_powers:
+            if e is S.One:
+                if b.is_Number:
+                    coeff *= b
+                else:
+                    c_part.append(b)
+            elif e is not S.Zero:
+                c_part.append(Pow(b, e))
 
         #  x    x     x
         # 2  * 3  -> 6
diff --git a/sympy/polys/polyutils.py b/sympy/polys/polyutils.py
index 28f9876313..0e74704deb 100644
--- a/sympy/polys/polyutils.py
+++ b/sympy/polys/polyutils.py
@@ -262,7 +262,7 @@ def _is_coeff(factor):
                     else:
                         base, exp = decompose_power_rat(factor)
 
-                    elements[base] = elements.setdefault(base, 0) + exp
+                    elements[base] = exp
                     gens.add(base)
 
             terms.append((coeff, elements))
diff --git a/sympy/solvers/inequalities.py b/sympy/solvers/inequalities.py
index 4a9176d95e..e5c5563e3a 100644
--- a/sympy/solvers/inequalities.py
+++ b/sympy/solvers/inequalities.py
@@ -2,7 +2,7 @@
 
 from __future__ import print_function, division
 
-from sympy.core import Symbol, Dummy
+from sympy.core import Symbol, Dummy, sympify
 from sympy.core.compatibility import iterable, reduce
 from sympy.sets import Interval
 from sympy.core.relational import Relational, Eq, Ge, Lt
@@ -481,8 +481,6 @@ def valid(x):
 def _solve_inequality(ie, s):
     """ A hacky replacement for solve, since the latter only works for
         univariate inequalities. """
-    if not ie.rel_op in ('>', '>=', '<', '<='):
-        raise NotImplementedError
     expr = ie.lhs - ie.rhs
     try:
         p = Poly(expr, s)
@@ -490,15 +488,14 @@ def _solve_inequality(ie, s):
             raise NotImplementedError
     except (PolynomialError, NotImplementedError):
         try:
-            n, d = expr.as_numer_denom()
             return reduce_rational_inequalities([[ie]], s)
         except PolynomialError:
             return solve_univariate_inequality(ie, s)
     a, b = p.all_coeffs()
-    if a.is_positive:
+    if a.is_positive or ie.rel_op in ('!=', '=='):
         return ie.func(s, -b/a)
     elif a.is_negative:
-        return ie.func(-b/a, s)
+        return ie.reversed.func(s, -b/a)
     else:
         raise NotImplementedError
 
@@ -572,6 +569,22 @@ def reduce_inequalities(inequalities, symbols=[]):
     """
     if not iterable(inequalities):
         inequalities = [inequalities]
+    inequalities = [sympify(i) for i in inequalities]
+
+    gens = set().union(*[i.free_symbols for i in inequalities])
+
+    if not iterable(symbols):
+        symbols = [symbols]
+    symbols = (set(symbols) or gens) & gens
+    if any(i.is_real is False for i in symbols):
+        raise TypeError(filldedent('''
+            inequalities cannot contain symbols that are not real.'''))
+
+    # make vanilla symbol real
+    recast = dict([(i, Dummy(i.name, real=True))
+        for i in gens if i.is_real is None])
+    inequalities = [i.xreplace(recast) for i in inequalities]
+    symbols = set([i.xreplace(recast) for i in symbols])
 
     # prefilter
     keep = []
@@ -591,18 +604,6 @@ def reduce_inequalities(inequalities, symbols=[]):
     inequalities = keep
     del keep
 
-    gens = reduce(set.union, [i.free_symbols for i in inequalities], set())
-
-    if not iterable(symbols):
-        symbols = [symbols]
-    symbols = set(symbols) or gens
-
-    # make vanilla symbol real
-    recast = dict([(i, Dummy(i.name, real=True))
-        for i in gens if i.is_real is None])
-    inequalities = [i.xreplace(recast) for i in inequalities]
-    symbols = set([i.xreplace(recast) for i in symbols])
-
     # solve system
     rv = _reduce_inequalities(inequalities, symbols)
 
diff --git a/sympy/solvers/solveset.py b/sympy/solvers/solveset.py
index c60073f977..132c550e37 100644
--- a/sympy/solvers/solveset.py
+++ b/sympy/solvers/solveset.py
@@ -12,6 +12,7 @@
 from sympy.core.numbers import I, Number, Rational, oo
 from sympy.core.function import (Lambda, expand, expand_complex)
 from sympy.core.relational import Eq
+from sympy.core.symbol import Symbol
 from sympy.simplify.simplify import fraction, trigsimp
 from sympy.functions import (log, Abs, tan, cot, sin, cos, sec, csc, exp,
                              acos, asin, atan, acsc, asec, arg,
@@ -183,6 +184,7 @@ def _invert_real(f, g_ys, symbol):
             tan_cot_invs = Union(*[imageset(Lambda(n, n*pi + f.inverse()(g_y)), \
                                         S.Integers) for g_y in g_ys])
             return _invert_real(f.args[0], tan_cot_invs, symbol)
+
     return (f, g_ys)
 
 
@@ -446,12 +448,13 @@ def solveset_real(f, symbol):
     (-oo, oo)
 
     """
-    if not symbol.is_Symbol:
-        raise ValueError(" %s is not a symbol" % (symbol))
+    if not getattr(symbol, 'is_Symbol', False):
+        raise ValueError('A Symbol must be given, not type %s: %s' %
+            (type(symbol), symbol))
 
     f = sympify(f)
     if not isinstance(f, (Expr, Number)):
-        raise ValueError(" %s is not a valid sympy expression" % (f))
+        raise ValueError("%s is not a valid SymPy expression" % (f))
 
     original_eq = f
     f = together(f)
@@ -462,8 +465,8 @@ def solveset_real(f, symbol):
     if not symbol in fraction(f)[1].free_symbols and f.is_rational_function():
         f = expand(f)
 
-    if f.has(Piecewise):
-        f = piecewise_fold(f)
+    f = piecewise_fold(f)
+
     result = EmptySet()
 
     if f.expand().is_zero:
@@ -770,8 +773,9 @@ def solveset_complex(f, symbol):
     ImageSet(Lambda(_n, 2*_n*I*pi), Integers())
 
     """
-    if not symbol.is_Symbol:
-        raise ValueError(" %s is not a symbol" % (symbol))
+    if not getattr(symbol, 'is_Symbol', False):
+        raise ValueError('A Symbol must be given, not type %s: %s' %
+            (type(symbol), symbol))
 
     f = sympify(f)
     original_eq = f
@@ -840,7 +844,7 @@ def solveset(f, symbol=None, domain=S.Complexes):
 
     Set
         A set of values for `symbol` for which `f` is True or is equal to
-        zero. An `EmptySet` is returned if no solution is found.
+        zero. An `EmptySet` is returned if `f` is False or nonzero.
         A `ConditionSet` is returned as unsolved object if algorithms
         to evaluatee complete solution are not yet implemented.
 
@@ -864,6 +868,15 @@ def solveset(f, symbol=None, domain=S.Complexes):
     variable being solved for and instead, uses the `domain` parameter to
     decide which solver to use.
 
+    Notes
+    =====
+
+    Python interprets 0 and 1 as False and True, respectively, but
+    in this function they refer to solutions of an expression. So 0 and 1
+    return the Domain and EmptySet, respectively, while True and False
+    return the opposite (as they are assumed to be solutions of relational
+    expressions).
+
 
     See Also
     ========
@@ -901,26 +914,36 @@ def solveset(f, symbol=None, domain=S.Complexes):
 
     """
 
-    from sympy.solvers.inequalities import solve_univariate_inequality
+    f = sympify(f)
+
+    if f is S.true:
+        return domain
+
+    if f is S.false:
+        return S.EmptySet
+
+    free_symbols = f.free_symbols
+
+    if not free_symbols:
+        b = Eq(f, 0)
+        if b is S.true:
+            return domain
+        elif b is S.false:
+            return S.EmptySet
+        else:
+            raise NotImplementedError(filldedent('''
+                relationship between value and 0 is unknown: %s''' % b))
 
     if symbol is None:
-        free_symbols = f.free_symbols
         if len(free_symbols) == 1:
             symbol = free_symbols.pop()
         else:
             raise ValueError(filldedent('''
                 The independent variable must be specified for a
                 multivariate equation.'''))
-    elif not symbol.is_Symbol:
-        raise ValueError('A Symbol must be given, not type %s: %s' % (type(symbol), symbol))
-
-    f = sympify(f)
-
-    if f is S.false:
-        return EmptySet()
-
-    if f is S.true:
-        return domain
+    elif not getattr(symbol, 'is_Symbol', False):
+        raise ValueError('A Symbol must be given, not type %s: %s' %
+            (type(symbol), symbol))
 
     if isinstance(f, Eq):
         from sympy.core import Add


Test patch:
diff --git a/sympy/core/tests/test_expr.py b/sympy/core/tests/test_expr.py
index 5313766db9..49b3d5e614 100644
--- a/sympy/core/tests/test_expr.py
+++ b/sympy/core/tests/test_expr.py
@@ -1686,13 +1686,7 @@ def test_issue_6325():
     e.diff(t, 2) == ans
     assert diff(e, t, 2, simplify=False) != ans
 
-
 def test_issue_7426():
     f1 = a % c
     f2 = x % z
     assert f1.equals(f2) == False
-
-
-def test_issue_10161():
-    x = symbols('x', real=True)
-    assert x*abs(x)*abs(x) == x**3
diff --git a/sympy/polys/tests/test_polyutils.py b/sympy/polys/tests/test_polyutils.py
index 7b27d42f14..2ea39b7b7a 100644
--- a/sympy/polys/tests/test_polyutils.py
+++ b/sympy/polys/tests/test_polyutils.py
@@ -1,7 +1,6 @@
 """Tests for useful utilities for higher level polynomial classes. """
 
-from sympy import (S, Integer, sin, cos, sqrt, symbols, pi,
-    Eq, Integral, exp, Mul)
+from sympy import S, Integer, sin, cos, sqrt, symbols, pi, Eq, Integral, exp
 from sympy.utilities.pytest import raises
 
 from sympy.polys.polyutils import (
@@ -270,8 +269,6 @@ def test__parallel_dict_from_expr_no_gens():
     assert parallel_dict_from_expr([x*y, 2*z, Integer(3)]) == \
         ([{(1, 1, 0): Integer(
             1)}, {(0, 0, 1): Integer(2)}, {(0, 0, 0): Integer(3)}], (x, y, z))
-    assert parallel_dict_from_expr((Mul(x, x**2, evaluate=False),)) == \
-        ([{(3,): 1}], (x,))
 
 
 def test_parallel_dict_from_expr():
diff --git a/sympy/solvers/tests/test_inequalities.py b/sympy/solvers/tests/test_inequalities.py
index ef9ca711ab..2c595b787d 100644
--- a/sympy/solvers/tests/test_inequalities.py
+++ b/sympy/solvers/tests/test_inequalities.py
@@ -185,6 +185,7 @@ def test_reduce_abs_inequalities():
 
     nr = Symbol('nr', real=False)
     raises(TypeError, lambda: reduce_inequalities(abs(nr - 5) < 3))
+    assert reduce_inequalities(x < 3, symbols=[x, nr]) == And(-oo < x, x < 3)
 
 
 def test_reduce_inequalities_general():
@@ -196,6 +197,7 @@ def test_reduce_inequalities_boolean():
     assert reduce_inequalities(
         [Eq(x**2, 0), True]) == Eq(x, 0)
     assert reduce_inequalities([Eq(x**2, 0), False]) == False
+    assert reduce_inequalities(x**2 >= 0) is S.true  # issue 10196
 
 
 def test_reduce_inequalities_multivariate():
@@ -212,6 +214,8 @@ def test_reduce_inequalities_errors():
 def test_hacky_inequalities():
     assert reduce_inequalities(x + y < 1, symbols=[x]) == (x < 1 - y)
     assert reduce_inequalities(x + y >= 1, symbols=[x]) == (x >= 1 - y)
+    assert reduce_inequalities(Eq(0, x - y), symbols=[x]) == Eq(x, y)
+    assert reduce_inequalities(Ne(0, x - y), symbols=[x]) == Ne(x, y)
 
 
 def test_issue_6343():
@@ -242,11 +246,11 @@ def test_issue_8235():
 def test_issue_5526():
     assert reduce_inequalities(S(0) <=
         x + Integral(y**2, (y, 1, 3)) - 1, [x]) == \
-        (-Integral(y**2, (y, 1, 3)) + 1 <= x)
+        (x >= -Integral(y**2, (y, 1, 3)) + 1)
     f = Function('f')
     e = Sum(f(x), (x, 1, 3))
     assert reduce_inequalities(S(0) <= x + e + y**2, [x]) == \
-        (-y**2 - Sum(f(x), (x, 1, 3)) <= x)
+        (x >= -y**2 - Sum(f(x), (x, 1, 3)))
 
 
 def test_solve_univariate_inequality():
diff --git a/sympy/solvers/tests/test_solveset.py b/sympy/solvers/tests/test_solveset.py
index 8f96ab440a..ba77f552fc 100644
--- a/sympy/solvers/tests/test_solveset.py
+++ b/sympy/solvers/tests/test_solveset.py
@@ -225,6 +225,7 @@ def test_is_function_class_equation():
 
 
 def test_garbage_input():
+    raises(ValueError, lambda: solveset_real(x, 1))
     raises(ValueError, lambda: solveset_real([x], x))
     raises(ValueError, lambda: solveset_real(x, pi))
     raises(ValueError, lambda: solveset_real(x, x**2))
@@ -743,6 +744,7 @@ def test_solve_complex_unsolvable():
     solution = solveset_complex(cos(x) - S.Half, x)
     assert solution == unsolved_object
 
+
 @XFAIL
 def test_solve_trig_simplified():
     from sympy.abc import n
@@ -852,6 +854,12 @@ def test_solve_lambert():
 def test_solveset():
     x = Symbol('x')
     raises(ValueError, lambda: solveset(x + y))
+    raises(ValueError, lambda: solveset(x, 1))
+
+    assert solveset(0, domain=S.Reals) == S.Reals
+    assert solveset(1) == S.EmptySet
+    assert solveset(True, domain=S.Reals) == S.Reals  # issue 10197
+    assert solveset(False, domain=S.Reals) == S.EmptySet
 
     assert solveset(exp(x) - 1, domain=S.Reals) == FiniteSet(0)
     assert solveset(exp(x) - 1, x, S.Reals) == FiniteSet(0)