Repository: sympy/sympy

Issue:
1/(exp(it) - 2) integrates wrong
```
>>> var('t', real=True)
t
>>> (1/(exp(I*t)-2)).integrate((t, 0, 2*pi))
-ⅈ⋅π
```

Correct answer is `-pi` according to wolframalpha

Discussion:

Patch:
diff --git a/sympy/assumptions/ask.py b/sympy/assumptions/ask.py
index 6dda473842..6956f373d3 100644
--- a/sympy/assumptions/ask.py
+++ b/sympy/assumptions/ask.py
@@ -358,16 +358,6 @@ def ask(proposition, assumptions=True, context=global_assumptions):
     """
     Function to evaluate the proposition with assumptions.
 
-    Explanation
-    ===========
-
-    This function evaluates the proposition to ``True`` or ``False`` if
-    the truth value can be determined. If not, it returns ``None``.
-
-    It should be discerned from :func:`~.refine()` which, when applied to a
-    proposition, simplifies the argument to symbolic ``Boolean`` instead of
-    Python built-in ``True``, ``False`` or ``None``.
-
     **Syntax**
 
         * ask(proposition)
@@ -377,32 +367,27 @@ def ask(proposition, assumptions=True, context=global_assumptions):
             Evaluate the *proposition* with respect to *assumptions* in
             global assumption context.
 
+    This function evaluates the proposition to ``True`` or ``False`` if
+    the truth value can be determined. If not, it returns ``None``.
+
+    It should be discerned from :func:`~.refine()` which, when applied to a
+    proposition, simplifies the argument to symbolic ``Boolean`` instead of
+    Python built-in ``True``, ``False`` or ``None``.
+
     Parameters
     ==========
 
-    proposition : Any boolean expression.
+    proposition : any boolean expression
         Proposition which will be evaluated to boolean value. If this is
         not ``AppliedPredicate``, it will be wrapped by ``Q.is_true``.
 
-    assumptions : Any boolean expression, optional.
+    assumptions : any boolean expression, optional
         Local assumptions to evaluate the *proposition*.
 
-    context : AssumptionsContext, optional.
+    context : AssumptionsContext, optional
         Default assumptions to evaluate the *proposition*. By default,
         this is ``sympy.assumptions.global_assumptions`` variable.
 
-    Returns
-    =======
-
-    ``True``, ``False``, or ``None``
-
-    Raises
-    ======
-
-    TypeError : *proposition* or *assumptions* is not valid logical expression.
-
-    ValueError : assumptions are inconsistent.
-
     Examples
     ========
 
@@ -420,22 +405,14 @@ def ask(proposition, assumptions=True, context=global_assumptions):
     >>> print(ask(Q.odd(3*x))) # cannot determine unless we know x
     None
 
-    ``ValueError`` is raised if assumptions are inconsistent.
-
-    >>> ask(Q.integer(x), Q.even(x) & Q.odd(x))
-    Traceback (most recent call last):
-      ...
-    ValueError: inconsistent assumptions Q.even(x) & Q.odd(x)
-
-    Notes
-    =====
+    **Remarks**
 
-    Relations in assumptions are not implemented (yet), so the following
-    will not give a meaningful result.
+        Relations in assumptions are not implemented (yet), so the following
+        will not give a meaningful result.
 
-    >>> ask(Q.positive(x), x > 0)
+        >>> ask(Q.positive(x), x > 0)
 
-    It is however a work in progress.
+        It is however a work in progress.
 
     See Also
     ========
diff --git a/sympy/assumptions/satask.py b/sympy/assumptions/satask.py
index 4858d2538f..897bce8ae0 100644
--- a/sympy/assumptions/satask.py
+++ b/sympy/assumptions/satask.py
@@ -1,7 +1,3 @@
-"""
-Module to evaluate the proposition with assumptions using SAT algorithm.
-"""
-
 from sympy import Symbol, S
 from sympy.assumptions.ask_generated import get_all_known_facts
 from sympy.assumptions.assume import global_assumptions, AppliedPredicate
@@ -13,68 +9,18 @@
 
 def satask(proposition, assumptions=True, context=global_assumptions,
         use_known_facts=True, iterations=oo):
-    """
-    Function to evaluate the proposition with assumptions using SAT algorithm.
-
-    This function extracts every fact relevant to the expressions composing
-    proposition and assumptions. For example, if a predicate containing
-    ``Abs(x)`` is proposed, then ``Q.zero(Abs(x)) | Q.positive(Abs(x))``
-    will be found and passed to SAT solver because ``Q.nonnegative`` is
-    registered as a fact for ``Abs``.
-
-    Proposition is evaluated to ``True`` or ``False`` if the truth value can be
-    determined. If not, ``None`` is returned.
-
-    Parameters
-    ==========
-
-    proposition : Any boolean expression.
-        Proposition which will be evaluated to boolean value.
-
-    assumptions : Any boolean expression, optional.
-        Local assumptions to evaluate the *proposition*.
-
-    context : AssumptionsContext, optional.
-        Default assumptions to evaluate the *proposition*. By default,
-        this is ``sympy.assumptions.global_assumptions`` variable.
-
-    use_known_facts : bool, optional.
-        If ``True``, facts from ``sympy.assumptions.ask_generated`` module
-        are passed to SAT solver as well.
-
-    iterations : int, optional.
-        Number of times that relevant facts are recursively extracted.
-        Default is infinite times until no new fact is found.
-
-    Returns
-    =======
-
-    ``True``, ``False``, or ``None``
-
-    Examples
-    ========
-
-    >>> from sympy import Abs, Q
-    >>> from sympy.assumptions.satask import satask
-    >>> from sympy.abc import x
-    >>> satask(Q.zero(Abs(x)), Q.zero(x))
-    True
-
-    """
     props = CNF.from_prop(proposition)
     _props = CNF.from_prop(~proposition)
-
-    assumptions = CNF.from_prop(assumptions)
-
-    context_cnf = CNF()
     if context:
-        context_cnf = context_cnf.extend(context)
+        tmp = CNF()
+        context = tmp.extend(context)
+    assumptions = CNF.from_prop(assumptions)
 
-    sat = get_all_relevant_facts(props, assumptions, context_cnf,
+    sat = get_all_relevant_facts(props, assumptions, context,
         use_known_facts=use_known_facts, iterations=iterations)
-    sat.add_from_cnf(assumptions)
     if context:
-        sat.add_from_cnf(context_cnf)
+        sat.add_from_cnf(context)
+    sat.add_from_cnf(assumptions)
 
     return check_satisfiability(props, _props, sat)
 
@@ -103,232 +49,81 @@ def check_satisfiability(prop, _prop, factbase):
         raise ValueError("Inconsistent assumptions")
 
 
-def extract_predargs(proposition, assumptions=None, context=None):
-    """
-    Extract every expression in the argument of predicates from *proposition*,
-    *assumptions* and *context*.
-
-    Parameters
-    ==========
-
-    proposition : sympy.assumptions.cnf.CNF
-
-    assumptions : sympy.assumptions.cnf.CNF, optional.
-
-    context : sympy.assumptions.cnf.CNF, optional.
-        CNF generated from assumptions context.
-
-    Examples
-    ========
-
-    >>> from sympy import Q, Abs
-    >>> from sympy.assumptions.cnf import CNF
-    >>> from sympy.assumptions.satask import extract_predargs
-    >>> from sympy.abc import x, y
-    >>> props = CNF.from_prop(Q.zero(Abs(x*y)))
-    >>> assump = CNF.from_prop(Q.zero(x) & Q.zero(y))
-    >>> extract_predargs(props, assump)
-    {x, y, Abs(x*y)}
-
-    """
-    req_keys = find_symbols(proposition)
-    keys = proposition.all_predicates()
-    # XXX: We need this since True/False are not Basic
-    lkeys = set()
-    if assumptions:
-        lkeys |= assumptions.all_predicates()
-    if context:
-        lkeys |= context.all_predicates()
-
-    lkeys = lkeys - {S.true, S.false}
-    tmp_keys = None
-    while tmp_keys != set():
-        tmp = set()
-        for l in lkeys:
-            syms = find_symbols(l)
-            if (syms & req_keys) != set():
-                tmp |= syms
-        tmp_keys = tmp - req_keys
-        req_keys |= tmp_keys
-    keys |= {l for l in lkeys if find_symbols(l) & req_keys != set()}
-
-    exprs = set()
-    for key in keys:
-        if isinstance(key, AppliedPredicate):
-            exprs |= set(key.arguments)
-        else:
-            exprs.add(key)
-    return exprs
-
-def find_symbols(pred):
-    """
-    Find every :obj:`~.Symbol` in *pred*.
-
-    Parameters
-    ==========
+def get_relevant_facts(proposition, assumptions=None,
+    context=None, exprs=None,
+    relevant_facts=None):
 
-    pred : sympy.assumptions.cnf.CNF, or any Expr.
-
-    """
-    if isinstance(pred, CNF):
-        symbols = set()
-        for a in pred.all_predicates():
-            symbols |= find_symbols(a)
-        return symbols
-    return pred.atoms(Symbol)
-
-
-def get_relevant_facts(exprs, relevant_facts=None):
-    """
-    Extract relevant facts from the items in *exprs*. Facts are defined in
-    ``assumptions.sathandlers`` module.
-
-    This function is recursively called by ``get_all_relevant_facts()``.
-
-    Parameters
-    ==========
-
-    exprs : set
-        Expressions whose relevant facts are searched.
-
-    relevant_facts : sympy.assumptions.cnf.CNF, optional.
-        Pre-discovered relevant facts.
-
-    Returns
-    =======
-
-    exprs : set
-        Candidates for next relevant fact searching.
-
-    relevant_facts : sympy.assumptions.cnf.CNF
-        Updated relevant facts.
-
-    Examples
-    ========
-
-    Here, we will see how facts relevant to ``Abs(x*y)`` are recursively
-    extracted. On the first run, set containing the expression is passed
-    without pre-discovered relevant facts. The result is a set containig
-    candidates for next run, and ``CNF()`` instance containing facts
-    which are relevant to ``Abs`` and its argument.
-
-    >>> from sympy import Abs
-    >>> from sympy.assumptions.satask import get_relevant_facts
-    >>> from sympy.abc import x, y
-    >>> exprs = {Abs(x*y)}
-    >>> exprs, facts = get_relevant_facts(exprs)
-    >>> exprs
-    {x*y}
-    >>> facts.clauses #doctest: +SKIP
-    {frozenset({Literal(Q.odd(Abs(x*y)), False), Literal(Q.odd(x*y), True)}),
-    frozenset({Literal(Q.zero(Abs(x*y)), False), Literal(Q.zero(x*y), True)}),
-    frozenset({Literal(Q.even(Abs(x*y)), False), Literal(Q.even(x*y), True)}),
-    frozenset({Literal(Q.zero(Abs(x*y)), True), Literal(Q.zero(x*y), False)}),
-    frozenset({Literal(Q.even(Abs(x*y)), False),
-                Literal(Q.odd(Abs(x*y)), False),
-                Literal(Q.odd(x*y), True)}),
-    frozenset({Literal(Q.even(Abs(x*y)), False),
-                Literal(Q.even(x*y), True),
-                Literal(Q.odd(Abs(x*y)), False)}),
-    frozenset({Literal(Q.positive(Abs(x*y)), False),
-                Literal(Q.zero(Abs(x*y)), False)})}
-
-    We pass the first run's results to the second run, and get the expressions
-    for next run and updated facts.
-
-    >>> exprs, facts = get_relevant_facts(exprs, relevant_facts=facts)
-    >>> exprs
-    {x, y}
-
-    On final run, no more candidate is returned thus we know that all
-    relevant facts are successfully retrieved.
+    newexprs = set()
 
-    >>> exprs, facts = get_relevant_facts(exprs, relevant_facts=facts)
-    >>> exprs
-    set()
+    if not assumptions:
+        assumptions = CNF({S.true})
 
-    """
     if not relevant_facts:
-        relevant_facts = CNF()
+        relevant_facts = set()
+
+    def find_symbols(pred):
+        if isinstance(pred, CNF):
+            symbols = set()
+            for a in pred.all_predicates():
+                symbols |= find_symbols(a)
+            return symbols
+        if isinstance(pred.args, AppliedPredicate):
+            return {pred.args[0]}
+        return pred.atoms(Symbol)
+
+    if not exprs:
+        req_keys = find_symbols(proposition)
+        keys = proposition.all_predicates()
+        # XXX: We need this since True/False are not Basic
+        lkeys = set()
+        lkeys |= assumptions.all_predicates()
+        if context:
+            lkeys |= context.all_predicates()
+
+        lkeys = lkeys - {S.true, S.false}
+        tmp_keys = None
+        while tmp_keys != set():
+            tmp = set()
+            for l in lkeys:
+                syms = find_symbols(l)
+                if (syms & req_keys) != set():
+                    tmp |= syms
+            tmp_keys = tmp - req_keys
+            req_keys |= tmp_keys
+        keys |= {l for l in lkeys if find_symbols(l) & req_keys != set()}
+
+        exprs = {key.arguments[0] if isinstance(key, AppliedPredicate) else key for key in keys}
+        return exprs, relevant_facts
 
-    newexprs = set()
     for expr in exprs:
         for fact in fact_registry[expr.func]:
             cnf_fact = CNF.to_CNF(fact)
             newfact = cnf_fact.rcall(expr)
             relevant_facts = relevant_facts._and(newfact)
-            for key in newfact.all_predicates():
-                if isinstance(key, AppliedPredicate):
-                    newexprs |= set(key.arguments)
+            newexprs |= {key.arguments[0] for key in newfact.all_predicates()
+                             if isinstance(key, AppliedPredicate)}
 
     return newexprs - exprs, relevant_facts
 
 
-def get_all_relevant_facts(proposition, assumptions, context,
-        use_known_facts=True, iterations=oo):
-    """
-    Extract all relevant facts from *proposition* and *assumptions*.
-
-    This function extracts the facts by recursively calling
-    ``get_relevant_facts()``. Extracted facts are converted to
-    ``EncodedCNF`` and returned.
-
-    Parameters
-    ==========
-
-    proposition : sympy.assumptions.cnf.CNF
-        CNF generated from proposition expression.
-
-    assumptions : sympy.assumptions.cnf.CNF
-        CNF generated from assumption expression.
-
-    context : sympy.assumptions.cnf.CNF
-        CNF generated from assumptions context.
-
-    use_known_facts : bool, optional.
-        If ``True``, facts from ``sympy.assumptions.ask_generated`` module
-        are encoded as well.
-
-    iterations : int, optional.
-        Number of times that relevant facts are recursively extracted.
-        Default is infinite times until no new fact is found.
-
-    Returns
-    =======
-
-    sympy.assumptions.cnf.EncodedCNF
-
-    Examples
-    ========
-
-    >>> from sympy import Q
-    >>> from sympy.assumptions.cnf import CNF
-    >>> from sympy.assumptions.satask import get_all_relevant_facts
-    >>> from sympy.abc import x, y
-    >>> props = CNF.from_prop(Q.nonzero(x*y))
-    >>> assump = CNF.from_prop(Q.nonzero(x))
-    >>> context = CNF.from_prop(Q.nonzero(y))
-    >>> get_all_relevant_facts(props, assump, context) #doctest: +SKIP
-    <sympy.assumptions.cnf.EncodedCNF at 0x7f09faa6ccd0>
-
-    """
+def get_all_relevant_facts(proposition, assumptions=True,
+    context=global_assumptions, use_known_facts=True, iterations=oo):
     # The relevant facts might introduce new keys, e.g., Q.zero(x*y) will
     # introduce the keys Q.zero(x) and Q.zero(y), so we need to run it until
     # we stop getting new things. Hopefully this strategy won't lead to an
     # infinite loop in the future.
     i = 0
     relevant_facts = CNF()
+    exprs = None
     all_exprs = set()
-    while True:
-        if i == 0:
-            exprs = extract_predargs(proposition, assumptions, context)
+    while exprs != set():
+        exprs, relevant_facts = get_relevant_facts(proposition,
+                assumptions, context, exprs=exprs,
+                relevant_facts=relevant_facts)
         all_exprs |= exprs
-        exprs, relevant_facts = get_relevant_facts(exprs, relevant_facts)
         i += 1
         if i >= iterations:
             break
-        if not exprs:
-            break
 
     if use_known_facts:
         known_facts_CNF = CNF()
diff --git a/sympy/core/compatibility.py b/sympy/core/compatibility.py
index 356cb060e7..f7abbf2c24 100644
--- a/sympy/core/compatibility.py
+++ b/sympy/core/compatibility.py
@@ -473,8 +473,6 @@ def default_sort_key(item, order=None):
     return (cls_index, 0, item.__class__.__name__
             ), args, S.One.sort_key(), S.One
 
-def _node_count(e):
-    return 1 + sum(map(_node_count, e.args))
 
 def _nodes(e):
     """
@@ -489,7 +487,7 @@ def _nodes(e):
     if isinstance(e, Basic):
         if isinstance(e, Derivative):
             return _nodes(e.expr) + len(e.variables)
-        return _node_count(e)
+        return e.count(Basic)
     elif iterable(e):
         return 1 + sum(_nodes(ei) for ei in e)
     elif isinstance(e, dict):
diff --git a/sympy/core/operations.py b/sympy/core/operations.py
index 6a25763adb..a6c0b92f2b 100644
--- a/sympy/core/operations.py
+++ b/sympy/core/operations.py
@@ -496,7 +496,7 @@ def __new__(cls, *args, **options):
         else:
             # XXX in almost every other case for __new__, *_args is
             # passed along, but the expectation here is for _args
-            obj = super(AssocOp, cls).__new__(cls, *ordered(_args))
+            obj = super(AssocOp, cls).__new__(cls, _args)
             obj._argset = _args
             return obj
 
@@ -524,6 +524,13 @@ def make_args(cls, expr):
         else:
             return frozenset([sympify(expr)])
 
+    # XXX: This should be cached on the object rather than using cacheit
+    # Maybe _argset can just be sorted in the constructor?
+    @property  # type: ignore
+    @cacheit
+    def args(self):
+        return tuple(ordered(self._argset))
+
     @staticmethod
     def _compare_pretty(a, b):
         return (str(a) > str(b)) - (str(a) < str(b))
diff --git a/sympy/functions/elementary/exponential.py b/sympy/functions/elementary/exponential.py
index bdedc455c2..a7a1176332 100644
--- a/sympy/functions/elementary/exponential.py
+++ b/sympy/functions/elementary/exponential.py
@@ -418,12 +418,12 @@ def as_real_imag(self, deep=True, **hints):
         sympy.functions.elementary.complexes.re
         sympy.functions.elementary.complexes.im
         """
-        from sympy.functions.elementary.trigonometric import cos, sin
+        import sympy
         re, im = self.args[0].as_real_imag()
         if deep:
             re = re.expand(deep, **hints)
             im = im.expand(deep, **hints)
-        cos, sin = cos(im), sin(im)
+        cos, sin = sympy.cos(im), sympy.sin(im)
         return (exp(re)*cos, exp(re)*sin)
 
     def _eval_subs(self, old, new):
diff --git a/sympy/functions/elementary/miscellaneous.py b/sympy/functions/elementary/miscellaneous.py
index 8466fe0ab5..3e958c55f8 100644
--- a/sympy/functions/elementary/miscellaneous.py
+++ b/sympy/functions/elementary/miscellaneous.py
@@ -1,7 +1,6 @@
 from sympy.core import Function, S, sympify
 from sympy.core.add import Add
 from sympy.core.containers import Tuple
-from sympy.core.compatibility import ordered
 from sympy.core.operations import LatticeOp, ShortCircuit
 from sympy.core.function import (Application, Lambda,
     ArgumentIndexError)
@@ -406,7 +405,7 @@ def __new__(cls, *args, **assumptions):
 
         # base creation
         _args = frozenset(args)
-        obj = Expr.__new__(cls, *ordered(_args), **assumptions)
+        obj = Expr.__new__(cls, _args, **assumptions)
         obj._argset = _args
         return obj
 
diff --git a/sympy/plotting/plot.py b/sympy/plotting/plot.py
index 36a49adf19..51ffceb54d 100644
--- a/sympy/plotting/plot.py
+++ b/sympy/plotting/plot.py
@@ -8,7 +8,7 @@
 This module gives only the essential. For all the fancy stuff use directly
 the backend. You can get the backend wrapper for every plot from the
 ``_backend`` attribute. Moreover the data series classes have various useful
-methods like ``get_points``, ``get_meshes``, etc, that may
+methods like ``get_points``, ``get_segments``, ``get_meshes``, etc, that may
 be useful if you wish to use another plotting library.
 
 Especially if you need publication ready graphs and this module is not enough
@@ -23,6 +23,7 @@
 """
 
 
+import warnings
 from collections.abc import Callable
 
 from sympy import sympify, Expr, Tuple, Dummy, Symbol
@@ -30,7 +31,6 @@
 from sympy.core.function import arity
 from sympy.utilities.iterables import is_sequence
 from .experimental_lambdify import (vectorized_lambdify, lambdify)
-from sympy.utilities.exceptions import SymPyDeprecationWarning
 
 # N.B.
 # When changing the minimum module version for matplotlib, please change
@@ -109,7 +109,6 @@ class Plot:
     - title : str
     - xlabel : str
     - ylabel : str
-    - zlabel : str
     - legend : bool
     - xscale : {'linear', 'log'}
     - yscale : {'linear', 'log'}
@@ -133,20 +132,7 @@ class Plot:
 
     Aesthetics:
 
-    - line_color : string, or float, or function, optional
-        Specifies the color for the plot, which depends on the backend being
-        used.
-
-        For example, if ``MatplotlibBackend`` is being used, then
-        Matplotlib string colors are acceptable ("red", "r", "cyan", "c", ...).
-        Alternatively, we can use a float number `0 < color < 1` wrapped in a
-        string (for example, `line_color="0.5"`) to specify grayscale colors.
-        Alternatively, We can specify a function returning a single
-        float value: this will be used to apply a color-loop (for example,
-        `line_color=lambda x: math.cos(x)`).
-
-        Note that by setting line_color, it would be applied simultaneously
-        to all the series.
+    - line_color : function which returns a float.
 
     options:
 
@@ -162,7 +148,7 @@ class Plot:
     """
 
     def __init__(self, *args,
-        title=None, xlabel=None, ylabel=None, zlabel=None, aspect_ratio='auto',
+        title=None, xlabel=None, ylabel=None, aspect_ratio='auto',
         xlim=None, ylim=None, axis_center='auto', axis=True,
         xscale='linear', yscale='linear', legend=False, autoscale=True,
         margin=0, annotations=None, markers=None, rectangles=None,
@@ -175,7 +161,6 @@ def __init__(self, *args,
         self.title = title
         self.xlabel = xlabel
         self.ylabel = ylabel
-        self.zlabel = zlabel
         self.aspect_ratio = aspect_ratio
         self.axis_center = axis_center
         self.axis = axis
@@ -501,12 +486,14 @@ class BaseSeries:
     is_2Dline = False
     # Some of the backends expect:
     #  - get_points returning 1D np.arrays list_x, list_y
+    #  - get_segments returning np.array (done in Line2DBaseSeries)
     #  - get_color_array returning 1D np.array (done in Line2DBaseSeries)
     # with the colors calculated at the points from get_points
 
     is_3Dline = False
     # Some of the backends expect:
     #  - get_points returning 1D np.arrays list_x, list_y, list_y
+    #  - get_segments returning np.array (done in Line2DBaseSeries)
     #  - get_color_array returning 1D np.array (done in Line2DBaseSeries)
     # with the colors calculated at the points from get_points
 
@@ -573,43 +560,13 @@ def __init__(self):
         self.only_integers = False
         self.line_color = None
 
-    def get_data(self):
-        """ Return lists of coordinates for plotting the line.
-
-        Returns
-        =======
-            x: list
-                List of x-coordinates
-
-            y: list
-                List of y-coordinates
-
-            y: list
-                List of z-coordinates in case of Parametric3DLineSeries
-        """
+    def get_segments(self):
         np = import_module('numpy')
         points = self.get_points()
         if self.steps is True:
-            if len(points) == 2:
-                x = np.array((points[0], points[0])).T.flatten()[1:]
-                y = np.array((points[1], points[1])).T.flatten()[:-1]
-                points = (x, y)
-            else:
-                x = np.repeat(points[0], 3)[2:]
-                y = np.repeat(points[1], 3)[:-2]
-                z = np.repeat(points[2], 3)[1:-1]
-                points = (x, y, z)
-        return points
-
-    def get_segments(self):
-        SymPyDeprecationWarning(
-                feature="get_segments",
-                issue=21329,
-                deprecated_since_version="1.9",
-                useinstead="MatplotlibBackend.get_segments").warn()
-
-        np = import_module('numpy')
-        points = type(self).get_data(self)
+            x = np.array((points[0], points[0])).T.flatten()[1:]
+            y = np.array((points[1], points[1])).T.flatten()[:-1]
+            points = (x, y)
         points = np.ma.array(points).T.reshape(-1, 1, self._dim)
         return np.ma.concatenate([points[:-1], points[1:]], axis=1)
 
@@ -671,19 +628,9 @@ def __str__(self):
         return 'cartesian line: %s for %s over %s' % (
             str(self.expr), str(self.var), str((self.start, self.end)))
 
-    def get_points(self):
-        """ Return lists of coordinates for plotting. Depending on the
-        `adaptive` option, this function will either use an adaptive algorithm
-        or it will uniformly sample the expression over the provided range.
-
-        Returns
-        =======
-            x: list
-                List of x-coordinates
-
-            y: list
-                List of y-coordinates
-
+    def get_segments(self):
+        """
+        Adaptively gets segments for plotting.
 
         Explanation
         ===========
@@ -699,12 +646,12 @@ def get_points(self):
                Luiz Henrique de Figueiredo.
 
         """
+
         if self.only_integers or not self.adaptive:
-            return self._uniform_sampling()
+            return super().get_segments()
         else:
             f = lambdify([self.var], self.expr)
-            x_coords = []
-            y_coords = []
+            list_segments = []
             np = import_module('numpy')
             def sample(p, q, depth):
                 """ Samples recursively if three points are almost collinear.
@@ -724,8 +671,7 @@ def sample(p, q, depth):
 
                 # Maximum depth
                 if depth > self.depth:
-                    x_coords.append(q[0])
-                    y_coords.append(q[1])
+                    list_segments.append([p, q])
 
                 # Sample irrespective of whether the line is flat till the
                 # depth of 6. We are not using linspace to avoid aliasing.
@@ -755,19 +701,16 @@ def sample(p, q, depth):
                     sample(p, new_point, depth + 1)
                     sample(new_point, q, depth + 1)
                 else:
-                    x_coords.append(q[0])
-                    y_coords.append(q[1])
+                    list_segments.append([p, q])
 
             f_start = f(self.start)
             f_end = f(self.end)
-            x_coords.append(self.start)
-            y_coords.append(f_start)
             sample(np.array([self.start, f_start]),
                    np.array([self.end, f_end]), 0)
 
-        return (x_coords, y_coords)
+            return list_segments
 
-    def _uniform_sampling(self):
+    def get_points(self):
         np = import_module('numpy')
         if self.only_integers is True:
             if self.xscale == 'log':
@@ -814,7 +757,7 @@ def get_parameter_points(self):
         np = import_module('numpy')
         return np.linspace(self.start, self.end, num=self.nb_of_points)
 
-    def _uniform_sampling(self):
+    def get_points(self):
         param = self.get_parameter_points()
         fx = vectorized_lambdify([self.var], self.expr_x)
         fy = vectorized_lambdify([self.var], self.expr_y)
@@ -822,19 +765,9 @@ def _uniform_sampling(self):
         list_y = fy(param)
         return (list_x, list_y)
 
-    def get_points(self):
-        """ Return lists of coordinates for plotting. Depending on the
-        `adaptive` option, this function will either use an adaptive algorithm
-        or it will uniformly sample the expression over the provided range.
-
-        Returns
-        =======
-            x: list
-                List of x-coordinates
-
-            y: list
-                List of y-coordinates
-
+    def get_segments(self):
+        """
+        Adaptively gets segments for plotting.
 
         Explanation
         ===========
@@ -851,12 +784,11 @@ def get_points(self):
 
         """
         if not self.adaptive:
-            return self._uniform_sampling()
+            return super().get_segments()
 
         f_x = lambdify([self.var], self.expr_x)
         f_y = lambdify([self.var], self.expr_y)
-        x_coords = []
-        y_coords = []
+        list_segments = []
 
         def sample(param_p, param_q, p, q, depth):
             """ Samples recursively if three points are almost collinear.
@@ -874,8 +806,7 @@ def sample(param_p, param_q, p, q, depth):
 
             # Maximum depth
             if depth > self.depth:
-                x_coords.append(q[0])
-                y_coords.append(q[1])
+                list_segments.append([p, q])
 
             # Sample irrespective of whether the line is flat till the
             # depth of 6. We are not using linspace to avoid aliasing.
@@ -909,8 +840,7 @@ def sample(param_p, param_q, p, q, depth):
                 sample(param_p, param_new, p, new_point, depth + 1)
                 sample(param_new, param_q, new_point, q, depth + 1)
             else:
-                x_coords.append(q[0])
-                y_coords.append(q[1])
+                list_segments.append([p, q])
 
         f_start_x = f_x(self.start)
         f_start_y = f_y(self.start)
@@ -918,11 +848,8 @@ def sample(param_p, param_q, p, q, depth):
         f_end_x = f_x(self.end)
         f_end_y = f_y(self.end)
         end = [f_end_x, f_end_y]
-        x_coords.append(f_start_x)
-        y_coords.append(f_start_y)
         sample(self.start, self.end, start, end, 0)
-
-        return x_coords, y_coords
+        return list_segments
 
 
 ### 3D lines
@@ -940,11 +867,9 @@ def __init__(self):
 
 
 class Parametric3DLineSeries(Line3DBaseSeries):
-    """Representation for a 3D line consisting of three parametric sympy
+    """Representation for a 3D line consisting of two parametric sympy
     expressions and a range."""
 
-    is_parametric = True
-
     def __init__(self, expr_x, expr_y, expr_z, var_start_end, **kwargs):
         super().__init__()
         self.expr_x = sympify(expr_x)
@@ -1022,10 +947,7 @@ def get_color_array(self):
             else:
                 return f(*variables)
         else:
-            if isinstance(self, SurfaceOver2DRangeSeries):
-                return c*np.ones(min(self.nb_of_points_x, self.nb_of_points_y))
-            else:
-                return c*np.ones(min(self.nb_of_points_u, self.nb_of_points_v))
+            return c*np.ones(self.nb_of_points)
 
 
 class SurfaceOver2DRangeSeries(SurfaceBaseSeries):
@@ -1304,32 +1226,6 @@ def __init__(self, parent):
                 self.ax[i].xaxis.set_ticks_position('bottom')
                 self.ax[i].yaxis.set_ticks_position('left')
 
-    @staticmethod
-    def get_segments(x, y, z=None):
-        """ Convert two list of coordinates to a list of segments to be used
-        with Matplotlib's LineCollection.
-
-        Parameters
-        ==========
-            x: list
-                List of x-coordinates
-
-            y: list
-                List of y-coordinates
-
-            z: list
-                List of z-coordinates for a 3D line.
-        """
-        np = import_module('numpy')
-        if z is not None:
-            dim = 3
-            points = (x, y, z)
-        else:
-            dim = 2
-            points = (x, y)
-        points = np.ma.array(points).T.reshape(-1, 1, dim)
-        return np.ma.concatenate([points[:-1], points[1:]], axis=1)
-
     def _process_series(self, series, ax, parent):
         np = import_module('numpy')
         mpl_toolkits = import_module(
@@ -1342,30 +1238,16 @@ def _process_series(self, series, ax, parent):
         for s in series:
             # Create the collections
             if s.is_2Dline:
-                x, y = s.get_data()
-                if (isinstance(s.line_color, (int, float)) or
-                        callable(s.line_color)):
-                    segments = self.get_segments(x, y)
-                    collection = self.LineCollection(segments)
-                    collection.set_array(s.get_color_array())
-                    ax.add_collection(collection)
-                else:
-                    line, = ax.plot(x, y, label=s.label, color=s.line_color)
+                collection = self.LineCollection(s.get_segments())
+                ax.add_collection(collection)
             elif s.is_contour:
                 ax.contour(*s.get_meshes())
             elif s.is_3Dline:
-                x, y, z = s.get_data()
-                if (isinstance(s.line_color, (int, float)) or
-                        callable(s.line_color)):
-                    art3d = mpl_toolkits.mplot3d.art3d
-                    segments = self.get_segments(x, y, z)
-                    collection = art3d.Line3DCollection(segments)
-                    collection.set_array(s.get_color_array())
-                    ax.add_collection(collection)
-                else:
-                    ax.plot(x, y, z, label=s.label,
-                        color=s.line_color)
-
+                # TODO too complicated, I blame matplotlib
+                art3d = mpl_toolkits.mplot3d.art3d
+                collection = art3d.Line3DCollection(s.get_segments())
+                ax.add_collection(collection)
+                x, y, z = s.get_points()
                 xlims.append(s._xlim)
                 ylims.append(s._ylim)
                 zlims.append(s._zlim)
@@ -1374,13 +1256,6 @@ def _process_series(self, series, ax, parent):
                 collection = ax.plot_surface(x, y, z,
                     cmap=getattr(self.cm, 'viridis', self.cm.jet),
                     rstride=1, cstride=1, linewidth=0.1)
-                if isinstance(s.surface_color, (float, int)) or isinstance(s.surface_color, Callable):
-                    color_array = s.get_color_array()
-                    color_array = color_array.reshape(color_array.size)
-                    collection.set_array(color_array)
-                else:
-                    collection.set_color(s.surface_color)
-
                 xlims.append(s._xlim)
                 ylims.append(s._ylim)
                 zlims.append(s._zlim)
@@ -1407,6 +1282,26 @@ def _process_series(self, series, ax, parent):
                     'with matplotlib backend. Please report this issue.'
                     .format(ax))
 
+            # Customise the collections with the corresponding per-series
+            # options.
+            if hasattr(s, 'label'):
+                collection.set_label(s.label)
+            if s.is_line and s.line_color:
+                if isinstance(s.line_color, (float, int)) or isinstance(s.line_color, Callable):
+                    color_array = s.get_color_array()
+                    collection.set_array(color_array)
+                else:
+                    collection.set_color(s.line_color)
+            if s.is_3Dsurface and s.surface_color:
+                if self.matplotlib.__version__ < "1.2.0":  # TODO in the distant future remove this check
+                    warnings.warn('The version of matplotlib is too old to use surface coloring.')
+                elif isinstance(s.surface_color, (float, int)) or isinstance(s.surface_color, Callable):
+                    color_array = s.get_color_array()
+                    color_array = color_array.reshape(color_array.size)
+                    collection.set_array(color_array)
+                else:
+                    collection.set_color(s.surface_color)
+
         Axes3D = mpl_toolkits.mplot3d.Axes3D
         if not isinstance(ax, Axes3D):
             ax.autoscale_view(
@@ -1476,8 +1371,6 @@ def _process_series(self, series, ax, parent):
             ax.set_xlabel(parent.xlabel, position=(1, 0))
         if parent.ylabel:
             ax.set_ylabel(parent.ylabel, position=(0, 1))
-        if isinstance(ax, Axes3D) and parent.zlabel:
-            ax.set_zlabel(parent.zlabel, position=(0, 1))
         if parent.annotations:
             for a in parent.annotations:
                 ax.annotate(**a)
@@ -1670,11 +1563,14 @@ def plot(*args, show=True, **kwargs):
         the ``Plot`` class can then be used to save or display the plot
         by calling the ``save()`` and ``show()`` methods respectively.
 
-    line_color : string, or float, or function, optional
+    line_color : float, optional
         Specifies the color for the plot.
         See ``Plot`` to see how to set color for the plots.
-        Note that by setting ``line_color``, it would be applied simultaneously
-        to all the series.
+
+        If there are multiple plots, then the same series series are
+        applied to all the plots. If you want to set these options
+        separately, you can index the ``Plot`` object returned and set
+        it.
 
     title : str, optional
         Title of the plot. It is set to the latex representation of
@@ -1897,11 +1793,12 @@ def plot_parametric(*args, show=True, **kwargs):
         Specifies the number of the points used for the uniform
         sampling.
 
-    line_color : string, or float, or function, optional
-        Specifies the color for the plot.
-        See ``Plot`` to see how to set color for the plots.
-        Note that by setting ``line_color``, it would be applied simultaneously
-        to all the series.
+    line_color : function
+        A function which returns a float.
+
+        Specifies the color of the plot.
+
+        See :class:`Plot` for more details.
 
     label : str, optional
         The label of the expression in the plot. It will be used when
@@ -2081,11 +1978,8 @@ def plot3d_parametric_line(*args, show=True, **kwargs):
 
     Aesthetics:
 
-    ``line_color``: string, or float, or function, optional
-        Specifies the color for the plot.
-        See ``Plot`` to see how to set color for the plots.
-        Note that by setting ``line_color``, it would be applied simultaneously
-        to all the series.
+    ``line_color``: function which returns a float. Specifies the color for the
+    plot. See ``sympy.plotting.Plot`` for more details.
 
     ``label``: str
         The label to the plot. It will be used when called with ``legend=True``
@@ -2152,9 +2046,6 @@ def plot3d_parametric_line(*args, show=True, **kwargs):
     series = []
     plot_expr = check_arguments(args, 3, 1)
     series = [Parametric3DLineSeries(*arg, **kwargs) for arg in plot_expr]
-    kwargs.setdefault("xlabel", "x")
-    kwargs.setdefault("ylabel", "y")
-    kwargs.setdefault("zlabel", "z")
     plots = Plot(*series, **kwargs)
     if show:
         plots.show()
@@ -2290,11 +2181,6 @@ def plot3d(*args, show=True, **kwargs):
     series = []
     plot_expr = check_arguments(args, 1, 2)
     series = [SurfaceOver2DRangeSeries(*arg, **kwargs) for arg in plot_expr]
-    xlabel = series[0].var_x.name
-    ylabel = series[0].var_y.name
-    kwargs.setdefault("xlabel", xlabel)
-    kwargs.setdefault("ylabel", ylabel)
-    kwargs.setdefault("zlabel", "f(%s, %s)" % (xlabel, ylabel))
     plots = Plot(*series, **kwargs)
     if show:
         plots.show()
@@ -2403,9 +2289,6 @@ def plot3d_parametric_surface(*args, show=True, **kwargs):
     series = []
     plot_expr = check_arguments(args, 3, 2)
     series = [ParametricSurfaceSeries(*arg, **kwargs) for arg in plot_expr]
-    kwargs.setdefault("xlabel", "x")
-    kwargs.setdefault("ylabel", "y")
-    kwargs.setdefault("zlabel", "z")
     plots = Plot(*series, **kwargs)
     if show:
         plots.show()
diff --git a/sympy/polys/domains/complexfield.py b/sympy/polys/domains/complexfield.py
index b2d40f72f1..484103a1a9 100644
--- a/sympy/polys/domains/complexfield.py
+++ b/sympy/polys/domains/complexfield.py
@@ -75,9 +75,6 @@ def from_sympy(self, expr):
     def from_ZZ(self, element, base):
         return self.dtype(element)
 
-    def from_QQ(self, element, base):
-        return self.dtype(int(element.numerator)) / int(element.denominator)
-
     def from_ZZ_python(self, element, base):
         return self.dtype(element)
 
@@ -90,18 +87,6 @@ def from_ZZ_gmpy(self, element, base):
     def from_QQ_gmpy(self, element, base):
         return self.dtype(int(element.numerator)) / int(element.denominator)
 
-    def from_GaussianIntegerRing(self, element, base):
-        return self.dtype(int(element.x), int(element.y))
-
-    def from_GaussianRationalField(self, element, base):
-        x = element.x
-        y = element.y
-        return (self.dtype(int(x.numerator)) / int(x.denominator) +
-                self.dtype(0, int(y.numerator)) / int(y.denominator))
-
-    def from_AlgebraicField(self, element, base):
-        return self.from_sympy(base.to_sympy(element).evalf(self.dps))
-
     def from_RealField(self, element, base):
         return self.dtype(element)
 
diff --git a/sympy/polys/domains/domain.py b/sympy/polys/domains/domain.py
index 0b80a7bf2c..a3b42256e4 100644
--- a/sympy/polys/domains/domain.py
+++ b/sympy/polys/domains/domain.py
@@ -728,8 +728,7 @@ def unify(K0, K1, symbols=None):
 
             if ((K0.is_FractionField and K1.is_PolynomialRing or
                  K1.is_FractionField and K0.is_PolynomialRing) and
-                 (not K0_ground.is_Field or not K1_ground.is_Field) and domain.is_Field
-                 and domain.has_assoc_Ring):
+                 (not K0_ground.is_Field or not K1_ground.is_Field) and domain.is_Field):
                 domain = domain.get_ring()
 
             if K0.is_Composite and (not K1.is_Composite or K0.is_FractionField or K1.is_PolynomialRing):
diff --git a/sympy/polys/domains/fractionfield.py b/sympy/polys/domains/fractionfield.py
index 3469ab9e1b..8070a2357c 100644
--- a/sympy/polys/domains/fractionfield.py
+++ b/sympy/polys/domains/fractionfield.py
@@ -112,30 +112,17 @@ def from_RealField(K1, a, K0):
         """Convert a mpmath ``mpf`` object to ``dtype``. """
         return K1(K1.domain.convert(a, K0))
 
-    def from_ComplexField(K1, a, K0):
-        """Convert a mpmath ``mpf`` object to ``dtype``. """
-        return K1(K1.domain.convert(a, K0))
-
     def from_AlgebraicField(K1, a, K0):
         """Convert an algebraic number to ``dtype``. """
-        if K1.domain != K0:
-            a = K1.domain.convert_from(a, K0)
-        if a is not None:
+        if K1.domain == K0:
             return K1.new(a)
 
     def from_PolynomialRing(K1, a, K0):
         """Convert a polynomial to ``dtype``. """
         try:
-            return K1.new(a.set_ring(K1.field.ring))
+            return K1.new(a)
         except (CoercionFailed, GeneratorsError):
-            # XXX: We get here if K1=ZZ(x,y) and K0=QQ[x,y]
-            # and the poly a in K0 has non-integer coefficients.
-            # It seems that K1.new can handle this but K1.new doesn't work
-            # when K0.domain is an algebraic field...
-            try:
-                return K1.new(a)
-            except (CoercionFailed, GeneratorsError):
-                return None
+            return None
 
     def from_FractionField(K1, a, K0):
         """Convert a rational function to ``dtype``. """
diff --git a/sympy/polys/domains/polynomialring.py b/sympy/polys/domains/polynomialring.py
index 37c26d6db2..2b401fecbd 100644
--- a/sympy/polys/domains/polynomialring.py
+++ b/sympy/polys/domains/polynomialring.py
@@ -122,15 +122,9 @@ def from_RealField(K1, a, K0):
         """Convert a mpmath `mpf` object to `dtype`. """
         return K1(K1.domain.convert(a, K0))
 
-    def from_ComplexField(K1, a, K0):
-        """Convert a mpmath `mpf` object to `dtype`. """
-        return K1(K1.domain.convert(a, K0))
-
     def from_AlgebraicField(K1, a, K0):
         """Convert an algebraic number to ``dtype``. """
-        if K1.domain != K0:
-            a = K1.domain.convert_from(a, K0)
-        if a is not None:
+        if K1.domain == K0:
             return K1.new(a)
 
     def from_PolynomialRing(K1, a, K0):
diff --git a/sympy/polys/domains/realfield.py b/sympy/polys/domains/realfield.py
index 4ae42359d2..8d6964c118 100644
--- a/sympy/polys/domains/realfield.py
+++ b/sympy/polys/domains/realfield.py
@@ -90,9 +90,6 @@ def from_ZZ_gmpy(self, element, base):
     def from_QQ_gmpy(self, element, base):
         return self.dtype(int(element.numerator)) / int(element.denominator)
 
-    def from_AlgebraicField(self, element, base):
-        return self.from_sympy(base.to_sympy(element).evalf(self.dps))
-
     def from_RealField(self, element, base):
         if self == base:
             return element
diff --git a/sympy/polys/euclidtools.py b/sympy/polys/euclidtools.py
index c0b6d13e79..7a772a012a 100644
--- a/sympy/polys/euclidtools.py
+++ b/sympy/polys/euclidtools.py
@@ -992,8 +992,7 @@ def dup_rr_prs_gcd(f, g, K):
     h = dup_subresultants(F, G, K)[-1]
     _, h = dup_primitive(h, K)
 
-    if K.is_negative(dup_LC(h, K)):
-        c = -c
+    c *= K.canonical_unit(dup_LC(h, K))
 
     h = dup_mul_ground(h, c, K)
 
diff --git a/sympy/polys/rings.py b/sympy/polys/rings.py
index ff20008a9f..ce9d33bf50 100644
--- a/sympy/polys/rings.py
+++ b/sympy/polys/rings.py
@@ -351,19 +351,19 @@ def ring_new(self, element):
 
     __call__ = ring_new
 
-    def from_dict(self, element, orig_domain=None):
+    def from_dict(self, element):
         domain_new = self.domain_new
         poly = self.zero
 
         for monom, coeff in element.items():
-            coeff = domain_new(coeff, orig_domain)
+            coeff = domain_new(coeff)
             if coeff:
                 poly[monom] = coeff
 
         return poly
 
-    def from_terms(self, element, orig_domain=None):
-        return self.from_dict(dict(element), orig_domain)
+    def from_terms(self, element):
+        return self.from_dict(dict(element))
 
     def from_list(self, element):
         return self.from_dict(dmp_to_dict(element, self.ngens-1, self.domain))
@@ -608,9 +608,9 @@ def set_ring(self, new_ring):
             return self
         elif self.ring.symbols != new_ring.symbols:
             terms = list(zip(*_dict_reorder(self, self.ring.symbols, new_ring.symbols)))
-            return new_ring.from_terms(terms, self.ring.domain)
+            return new_ring.from_terms(terms)
         else:
-            return new_ring.from_dict(self, self.ring.domain)
+            return new_ring.from_dict(self)
 
     def as_expr(self, *symbols):
         if symbols and len(symbols) != self.ring.ngens:
diff --git a/sympy/simplify/fu.py b/sympy/simplify/fu.py
index d4307f0a69..b13360705a 100644
--- a/sympy/simplify/fu.py
+++ b/sympy/simplify/fu.py
@@ -293,7 +293,7 @@ def _TR56(rv, f, g, h, max, pow):
     >>> from sympy import sin, cos
     >>> h = lambda x: 1 - x
     >>> T(sin(x)**3, sin, cos, h, 4, False)
-    (1 - cos(x)**2)*sin(x)
+    sin(x)**3
     >>> T(sin(x)**6, sin, cos, h, 6, False)
     (1 - cos(x)**2)**3
     >>> T(sin(x)**6, sin, cos, h, 6, True)
@@ -316,15 +316,10 @@ def _f(rv):
             return rv
         if (rv.exp > max) == True:
             return rv
-        if rv.exp == 1:
-            return rv
         if rv.exp == 2:
             return h(g(rv.base.args[0])**2)
         else:
-            if rv.exp % 2 == 1:
-                e = rv.exp//2
-                return f(rv.base.args[0])*h(g(rv.base.args[0])**2)**e
-            elif rv.exp == 4:
+            if rv.exp == 4:
                 e = 2
             elif not pow:
                 if rv.exp % 2:
@@ -1344,7 +1339,7 @@ def f(rv):
 
 
 def TR15(rv, max=4, pow=False):
-    """Convert sin(x)**-2 to 1 + cot(x)**2.
+    """Convert sin(x)*-2 to 1 + cot(x)**2.
 
     See _TR56 docstring for advanced use of ``max`` and ``pow``.
 
@@ -1363,10 +1358,6 @@ def f(rv):
         if not (isinstance(rv, Pow) and isinstance(rv.base, sin)):
             return rv
 
-        e = rv.exp
-        if e % 2 == 1:
-            return TR15(rv.base**(e + 1))/rv.base
-
         ia = 1/rv
         a = _TR56(ia, sin, cot, lambda x: 1 + x, max=max, pow=pow)
         if a != ia:
@@ -1377,7 +1368,7 @@ def f(rv):
 
 
 def TR16(rv, max=4, pow=False):
-    """Convert cos(x)**-2 to 1 + tan(x)**2.
+    """Convert cos(x)*-2 to 1 + tan(x)**2.
 
     See _TR56 docstring for advanced use of ``max`` and ``pow``.
 
@@ -1396,10 +1387,6 @@ def f(rv):
         if not (isinstance(rv, Pow) and isinstance(rv.base, cos)):
             return rv
 
-        e = rv.exp
-        if e % 2 == 1:
-            return TR15(rv.base**(e + 1))/rv.base
-
         ia = 1/rv
         a = _TR56(ia, cos, tan, lambda x: 1 + x, max=max, pow=pow)
         if a != ia:


Test patch:
diff --git a/sympy/assumptions/tests/test_satask.py b/sympy/assumptions/tests/test_satask.py
index b7ca4cca3f..ab30ba3d9f 100644
--- a/sympy/assumptions/tests/test_satask.py
+++ b/sympy/assumptions/tests/test_satask.py
@@ -1,8 +1,6 @@
-from sympy.assumptions.cnf import CNF, Literal
-from sympy.assumptions.satask import (satask, extract_predargs,
-    get_relevant_facts)
+from sympy.assumptions.satask import satask
 
-from sympy import S, symbols, Q, assuming, Implies, MatrixSymbol, I, pi, Abs
+from sympy import S, symbols, Q, assuming, Implies, MatrixSymbol, I, pi
 
 from sympy.testing.pytest import raises, XFAIL
 
@@ -337,31 +335,3 @@ def test_prime_composite():
     assert satask(Q.prime(4)) is False
     assert satask(Q.prime(1)) is False
     assert satask(Q.composite(1)) is False
-
-
-def test_extract_predargs():
-    props = CNF.from_prop(Q.zero(Abs(x*y)) & Q.zero(x*y))
-    assump = CNF.from_prop(Q.zero(x))
-    context = CNF.from_prop(Q.zero(y))
-    assert extract_predargs(props) == {Abs(x*y), x*y}
-    assert extract_predargs(props, assump) == {Abs(x*y), x*y, x}
-    assert extract_predargs(props, assump, context) == {Abs(x*y), x*y, x, y}
-
-
-def test_get_relevant_facts():
-    exprs = {Abs(x*y)}
-    exprs, facts = get_relevant_facts(exprs)
-    assert exprs == {x*y}
-    assert facts.clauses == \
-        {frozenset({Literal(Q.odd(Abs(x*y)), False), Literal(Q.odd(x*y), True)}),
-        frozenset({Literal(Q.zero(Abs(x*y)), False), Literal(Q.zero(x*y), True)}),
-        frozenset({Literal(Q.even(Abs(x*y)), False), Literal(Q.even(x*y), True)}),
-        frozenset({Literal(Q.zero(Abs(x*y)), True), Literal(Q.zero(x*y), False)}),
-        frozenset({Literal(Q.even(Abs(x*y)), False),
-                    Literal(Q.odd(Abs(x*y)), False),
-                    Literal(Q.odd(x*y), True)}),
-        frozenset({Literal(Q.even(Abs(x*y)), False),
-                    Literal(Q.even(x*y), True),
-                    Literal(Q.odd(Abs(x*y)), False)}),
-        frozenset({Literal(Q.positive(Abs(x*y)), False),
-                    Literal(Q.zero(Abs(x*y)), False)})}
diff --git a/sympy/integrals/tests/test_integrals.py b/sympy/integrals/tests/test_integrals.py
index 06537bdee5..ce7dd2d142 100644
--- a/sympy/integrals/tests/test_integrals.py
+++ b/sympy/integrals/tests/test_integrals.py
@@ -1604,12 +1604,16 @@ def test_integrate_with_complex_constants():
     K = Symbol('K', real=True, positive=True)
     x = Symbol('x', real=True)
     m = Symbol('m', real=True)
+    t = Symbol('t', real=True)
     assert integrate(exp(-I*K*x**2+m*x), x) == sqrt(I)*sqrt(pi)*exp(-I*m**2
                     /(4*K))*erfi((-2*I*K*x + m)/(2*sqrt(K)*sqrt(-I)))/(2*sqrt(K))
-    assert integrate(1/(1 + I*x**2), x) == -sqrt(I)*log(x - sqrt(I))/2 +\
-        sqrt(I)*log(x + sqrt(I))/2
+    assert integrate(1/(1 + I*x**2), x) == (-I*(sqrt(-I)*log(x - I*sqrt(-I))/2
+            - sqrt(-I)*log(x + I*sqrt(-I))/2))
     assert integrate(exp(-I*x**2), x) == sqrt(pi)*erf(sqrt(I)*x)/(2*sqrt(I))
 
+    assert integrate((1/(exp(I*t)-2)), t) == -t/2 - I*log(exp(I*t) - 2)/2
+    assert integrate((1/(exp(I*t)-2)), (t, 0, 2*pi)) == -pi
+
 
 def test_issue_14241():
     x = Symbol('x')
diff --git a/sympy/plotting/tests/test_plot.py b/sympy/plotting/tests/test_plot.py
index bbce2b7cfb..57ecb51f83 100644
--- a/sympy/plotting/tests/test_plot.py
+++ b/sympy/plotting/tests/test_plot.py
@@ -517,7 +517,7 @@ def test_issue_17405():
     p = plot(f, (x, -10, 10), show=False)
     # Random number of segments, probably more than 100, but we want to see
     # that there are segments generated, as opposed to when the bug was present
-    assert len(p[0].get_data()[0]) >= 30
+    assert len(p[0].get_segments()) >= 30
 
 
 def test_logplot_PR_16796():
@@ -528,7 +528,7 @@ def test_logplot_PR_16796():
     p = plot(x, (x, .001, 100), xscale='log', show=False)
     # Random number of segments, probably more than 100, but we want to see
     # that there are segments generated, as opposed to when the bug was present
-    assert len(p[0].get_data()[0]) >= 30
+    assert len(p[0].get_segments()) >= 30
     assert p[0].end == 100.0
     assert p[0].start == .001
 
@@ -541,7 +541,7 @@ def test_issue_16572():
     p = plot(LambertW(x), show=False)
     # Random number of segments, probably more than 50, but we want to see
     # that there are segments generated, as opposed to when the bug was present
-    assert len(p[0].get_data()[0]) >= 30
+    assert len(p[0].get_segments()) >= 30
 
 
 def test_issue_11865():
@@ -554,7 +554,7 @@ def test_issue_11865():
     # Random number of segments, probably more than 100, but we want to see
     # that there are segments generated, as opposed to when the bug was present
     # and that there are no exceptions.
-    assert len(p[0].get_data()[0]) >= 30
+    assert len(p[0].get_segments()) >= 30
 
 
 def test_issue_11461():
@@ -566,7 +566,7 @@ def test_issue_11461():
     # Random number of segments, probably more than 100, but we want to see
     # that there are segments generated, as opposed to when the bug was present
     # and that there are no exceptions.
-    assert len(p[0].get_data()[0]) >= 30
+    assert len(p[0].get_segments()) >= 30
 
 
 def test_issue_11764():
@@ -578,7 +578,7 @@ def test_issue_11764():
     p.aspect_ratio == (1, 1)
     # Random number of segments, probably more than 100, but we want to see
     # that there are segments generated, as opposed to when the bug was present
-    assert len(p[0].get_data()[0]) >= 30
+    assert len(p[0].get_segments()) >= 30
 
 
 def test_issue_13516():
@@ -589,19 +589,19 @@ def test_issue_13516():
 
     pm = plot(sin(x), backend="matplotlib", show=False)
     assert pm.backend == MatplotlibBackend
-    assert len(pm[0].get_data()[0]) >= 30
+    assert len(pm[0].get_segments()) >= 30
 
     pt = plot(sin(x), backend="text", show=False)
     assert pt.backend == TextBackend
-    assert len(pt[0].get_data()[0]) >= 30
+    assert len(pt[0].get_segments()) >= 30
 
     pd = plot(sin(x), backend="default", show=False)
     assert pd.backend == DefaultBackend
-    assert len(pd[0].get_data()[0]) >= 30
+    assert len(pd[0].get_segments()) >= 30
 
     p = plot(sin(x), show=False)
     assert p.backend == DefaultBackend
-    assert len(p[0].get_data()[0]) >= 30
+    assert len(p[0].get_segments()) >= 30
 
 
 def test_plot_limits():
@@ -684,44 +684,18 @@ def test_issue_20113():
         plot(sin(x), backend=Plot, show=False)
     p2 = plot(sin(x), backend=MatplotlibBackend, show=False)
     assert p2.backend == MatplotlibBackend
-    assert len(p2[0].get_data()[0]) >= 30
+    assert len(p2[0].get_segments()) >= 30
     p3 = plot(sin(x), backend=DummyBackendOk, show=False)
     assert p3.backend == DummyBackendOk
-    assert len(p3[0].get_data()[0]) >= 30
+    assert len(p3[0].get_segments()) >= 30
 
     # test for an improper coded backend
     p4 = plot(sin(x), backend=DummyBackendNotOk, show=False)
     assert p4.backend == DummyBackendNotOk
-    assert len(p4[0].get_data()[0]) >= 30
+    assert len(p4[0].get_segments()) >= 30
     with raises(NotImplementedError):
         p4.show()
     with raises(NotImplementedError):
         p4.save("test/path")
     with raises(NotImplementedError):
         p4._backend.close()
-
-def test_custom_coloring():
-    x = Symbol('x')
-    y = Symbol('y')
-    plot(cos(x), line_color=lambda a: a)
-    plot(cos(x), line_color=1)
-    plot(cos(x), line_color="r")
-    plot_parametric(cos(x), sin(x), line_color=lambda a: a)
-    plot_parametric(cos(x), sin(x), line_color=1)
-    plot_parametric(cos(x), sin(x), line_color="r")
-    plot3d_parametric_line(cos(x), sin(x), x, line_color=lambda a: a)
-    plot3d_parametric_line(cos(x), sin(x), x, line_color=1)
-    plot3d_parametric_line(cos(x), sin(x), x, line_color="r")
-    plot3d_parametric_surface(cos(x + y), sin(x - y), x - y,
-            (x, -5, 5), (y, -5, 5),
-            surface_color=lambda a, b: a**2 + b**2)
-    plot3d_parametric_surface(cos(x + y), sin(x - y), x - y,
-            (x, -5, 5), (y, -5, 5),
-            surface_color=1)
-    plot3d_parametric_surface(cos(x + y), sin(x - y), x - y,
-            (x, -5, 5), (y, -5, 5),
-            surface_color="r")
-    plot3d(x*y, (x, -5, 5), (y, -5, 5),
-            surface_color=lambda a, b: a**2 + b**2)
-    plot3d(x*y, (x, -5, 5), (y, -5, 5), surface_color=1)
-    plot3d(x*y, (x, -5, 5), (y, -5, 5), surface_color="r")
diff --git a/sympy/polys/domains/tests/test_domains.py b/sympy/polys/domains/tests/test_domains.py
index 2aee93d443..35a7b57f14 100644
--- a/sympy/polys/domains/tests/test_domains.py
+++ b/sympy/polys/domains/tests/test_domains.py
@@ -569,41 +569,12 @@ def test_Domain_is_unit():
 
 
 def test_Domain_convert():
-
-    def check_element(e1, e2, K1, K2, K3):
-        assert type(e1) is type(e2), '%s, %s: %s %s -> %s' % (e1, e2, K1, K2, K3)
-        assert e1 == e2, '%s, %s: %s %s -> %s' % (e1, e2, K1, K2, K3)
-
-    def check_domains(K1, K2):
-        K3 = K1.unify(K2)
-        check_element(K3.convert_from(K1.one,  K1), K3.one , K1, K2, K3)
-        check_element(K3.convert_from(K2.one,  K2), K3.one , K1, K2, K3)
-        check_element(K3.convert_from(K1.zero, K1), K3.zero, K1, K2, K3)
-        check_element(K3.convert_from(K2.zero, K2), K3.zero, K1, K2, K3)
-
-    def composite_domains(K):
-        return [K, K[y], K[z], K[y, z],
-                   K.frac_field(y), K.frac_field(z), K.frac_field(y, z)]
-
-    QQ2 = QQ.algebraic_field(sqrt(2))
-    QQ3 = QQ.algebraic_field(sqrt(3))
-    doms = [ZZ, QQ, QQ2, QQ3, QQ_I, ZZ_I, RR, CC]
-
-    for i, K1 in enumerate(doms):
-        for K2 in doms[i:]:
-            for K3 in composite_domains(K1):
-                for K4 in composite_domains(K2):
-                    check_domains(K3, K4)
-
     assert QQ.convert(10e-52) == QQ(1684996666696915, 1684996666696914987166688442938726917102321526408785780068975640576)
 
     R, x = ring("x", ZZ)
     assert ZZ.convert(x - x) == 0
     assert ZZ.convert(x - x, R.to_domain()) == 0
 
-    assert CC.convert(ZZ_I(1, 2)) == CC(1, 2)
-    assert CC.convert(QQ_I(1, 2)) == CC(1, 2)
-
 
 def test_GlobalPolynomialRing_convert():
     K1 = QQ.old_poly_ring(x)
diff --git a/sympy/polys/tests/test_polytools.py b/sympy/polys/tests/test_polytools.py
index f18778191e..8f7e464148 100644
--- a/sympy/polys/tests/test_polytools.py
+++ b/sympy/polys/tests/test_polytools.py
@@ -2049,10 +2049,12 @@ def test_gcd():
     assert lcm(f, g, modulus=11, symmetric=False) == l
 
     a, b = sqrt(2), -sqrt(2)
-    assert gcd(a, b) == gcd(b, a) == a
+    assert gcd(a, b) == gcd(b, a) == sqrt(2)
 
     a, b = sqrt(-2), -sqrt(-2)
-    assert gcd(a, b) in (a, b)
+    assert gcd(a, b) == gcd(b, a) == sqrt(2)
+
+    assert gcd(Poly(x - 2, x), Poly(I*x, x)) == Poly(1, x, domain=ZZ_I)
 
     raises(TypeError, lambda: gcd(x))
     raises(TypeError, lambda: lcm(x))
diff --git a/sympy/sets/tests/test_sets.py b/sympy/sets/tests/test_sets.py
index 666eecf304..6a6a00693f 100644
--- a/sympy/sets/tests/test_sets.py
+++ b/sympy/sets/tests/test_sets.py
@@ -1284,8 +1284,8 @@ def test_SymmetricDifference():
             == FiniteSet(5)
     assert FiniteSet(1, 2, 3, 4, 5) ^ FiniteSet(1, 2, 5, 6) == \
             FiniteSet(3, 4, 6)
-    assert Set(S(1), S(2) , S(3)) ^ Set(S(2), S(3), S(4)) == Union(Set(S(1), S(2), S(3)) - Set(S(2), S(3), S(4)), \
-            Set(S(2), S(3), S(4)) - Set(S(1), S(2), S(3)))
+    assert Set(1, 2 , 3) ^ Set(2, 3, 4) == Union(Set(1, 2, 3) - Set(2, 3, 4), \
+            Set(2, 3, 4) - Set(1, 2, 3))
     assert Interval(0, 4) ^ Interval(2, 5) == Union(Interval(0, 4) - \
             Interval(2, 5), Interval(2, 5) - Interval(0, 4))
 
diff --git a/sympy/simplify/tests/test_fu.py b/sympy/simplify/tests/test_fu.py
index 7332e80029..32442bf765 100644
--- a/sympy/simplify/tests/test_fu.py
+++ b/sympy/simplify/tests/test_fu.py
@@ -69,7 +69,7 @@ def test_TR3():
 
 def test__TR56():
     h = lambda x: 1 - x
-    assert T(sin(x)**3, sin, cos, h, 4, False) == sin(x)*(-cos(x)**2 + 1)
+    assert T(sin(x)**3, sin, cos, h, 4, False) == sin(x)**3
     assert T(sin(x)**10, sin, cos, h, 4, False) == sin(x)**10
     assert T(sin(x)**6, sin, cos, h, 6, False) == (-cos(x)**2 + 1)**3
     assert T(sin(x)**6, sin, cos, h, 6, True) == sin(x)**6
@@ -288,9 +288,6 @@ def test_fu():
     # issue #18059:
     assert fu(cos(x) + sqrt(sin(x)**2)) == cos(x) + sqrt(sin(x)**2)
 
-    assert fu((-14*sin(x)**3 + 35*sin(x) + 6*sqrt(3)*cos(x)**3 + 9*sqrt(3)*cos(x))/((cos(2*x) + 4))) == \
-        7*sin(x) + 3*sqrt(3)*cos(x)
-
 
 def test_objective():
     assert fu(sin(x)/cos(x), measure=lambda x: x.count_ops()) == \
diff --git a/sympy/unify/tests/test_sympy.py b/sympy/unify/tests/test_sympy.py
index e76636d779..4e84378cc9 100644
--- a/sympy/unify/tests/test_sympy.py
+++ b/sympy/unify/tests/test_sympy.py
@@ -1,4 +1,4 @@
-from sympy import Add, Basic, symbols, Symbol, And, S
+from sympy import Add, Basic, symbols, Symbol, And
 from sympy.core.symbol import Str
 from sympy.unify.core import Compound, Variable
 from sympy.unify.usympy import (deconstruct, construct, unify, is_associative,
@@ -124,11 +124,11 @@ def test_FiniteSet_commutivity():
 def test_FiniteSet_complex():
     from sympy import FiniteSet
     a, b, c, x, y, z = symbols('a,b,c,x,y,z')
-    expr = FiniteSet(Basic(S(1), x), y, Basic(x, z))
+    expr = FiniteSet(Basic(1, x), y, Basic(x, z))
     pattern = FiniteSet(a, Basic(x, b))
     variables = a, b
     expected = tuple([{b: 1, a: FiniteSet(y, Basic(x, z))},
-                      {b: z, a: FiniteSet(y, Basic(S(1), x))}])
+                      {b: z, a: FiniteSet(y, Basic(1, x))}])
     assert iterdicteq(unify(expr, pattern, variables=variables), expected)