from __future__ import division

from sympy import (Abs, Catalan, cos, Derivative, E, EulerGamma, exp,
    factorial, factorial2, Function, GoldenRatio, I, Integer, Integral,
    Interval, Lambda, Limit, Matrix, nan, O, oo, pi, Pow, Rational, Float, Rel,
    S, sin, SparseMatrix, sqrt, summation, Sum, Symbol, symbols, Wild,
    WildFunction, zeta, zoo, Dummy, Dict, Tuple, FiniteSet, factor,
    subfactorial, true, false, Equivalent, Xor, Complement, SymmetricDifference,
    AccumBounds, UnevaluatedExpr)
from sympy.core import Expr
from sympy.physics.units import second, joule
from sympy.polys import Poly, rootof, RootSum, groebner, ring, field, ZZ, QQ, lex, grlex
from sympy.geometry import Point, Circle

from sympy.utilities.pytest import raises
from sympy.core.compatibility import range

from sympy.printing import sstr, sstrrepr, StrPrinter
from sympy.core.trace import Tr

x, y, z, w = symbols('x,y,z,w')
d = Dummy('d')


def test_printmethod():
    class R(Abs):
        def _sympystr(self, printer):
            return "foo(%s)" % printer._print(self.args[0])
    assert sstr(R(x)) == "foo(x)"

    class R(Abs):
        def _sympystr(self, printer):
            return "foo"
    assert sstr(R(x)) == "foo"


def test_Abs():
    assert str(Abs(x)) == "Abs(x)"
    assert str(Abs(Rational(1, 6))) == "1/6"
    assert str(Abs(Rational(-1, 6))) == "1/6"


def test_Add():
    assert str(x + y) == "x + y"
    assert str(x + 1) == "x + 1"
    assert str(x + x**2) == "x**2 + x"
    assert str(5 + x + y + x*y + x**2 + y**2) == "x**2 + x*y + x + y**2 + y + 5"
    assert str(1 + x + x**2/2 + x**3/3) == "x**3/3 + x**2/2 + x + 1"
    assert str(2*x - 7*x**2 + 2 + 3*y) == "-7*x**2 + 2*x + 3*y + 2"
    assert str(x - y) == "x - y"
    assert str(2 - x) == "-x + 2"
    assert str(x - 2) == "x - 2"
    assert str(x - y - z - w) == "-w + x - y - z"
    assert str(x - z*y**2*z*w) == "-w*y**2*z**2 + x"
    assert str(x - 1*y*x*y) == "-x*y**2 + x"
    assert str(sin(x).series(x, 0, 15)) == "x - x**3/6 + x**5/120 - x**7/5040 + x**9/362880 - x**11/39916800 + x**13/6227020800 + O(x**15)"


def test_Catalan():
    assert str(Catalan) == "Catalan"


def test_ComplexInfinity():
    assert str(zoo) == "zoo"


def test_Derivative():
    assert str(Derivative(x, y)) == "Derivative(x, y)"
    assert str(Derivative(x**2, x, evaluate=False)) == "Derivative(x**2, x)"
    assert str(Derivative(
        x**2/y, x, y, evaluate=False)) == "Derivative(x**2/y, x, y)"


def test_dict():
    assert str({1: 1 + x}) == sstr({1: 1 + x}) == "{1: x + 1}"
    assert str({1: x**2, 2: y*x}) in ("{1: x**2, 2: x*y}", "{2: x*y, 1: x**2}")
    assert sstr({1: x**2, 2: y*x}) == "{1: x**2, 2: x*y}"


def test_Dict():
    assert str(Dict({1: 1 + x})) == sstr({1: 1 + x}) == "{1: x + 1}"
    assert str(Dict({1: x**2, 2: y*x})) in (
        "{1: x**2, 2: x*y}", "{2: x*y, 1: x**2}")
    assert sstr(Dict({1: x**2, 2: y*x})) == "{1: x**2, 2: x*y}"


def test_Dummy():
    assert str(d) == "_d"
    assert str(d + x) == "_d + x"


def test_EulerGamma():
    assert str(EulerGamma) == "EulerGamma"


def test_Exp():
    assert str(E) == "E"


def test_factorial():
    n = Symbol('n', integer=True)
    assert str(factorial(-2)) == "zoo"
    assert str(factorial(0)) == "1"
    assert str(factorial(7)) == "5040"
    assert str(factorial(n)) == "factorial(n)"
    assert str(factorial(2*n)) == "factorial(2*n)"
    assert str(factorial(factorial(n))) == 'factorial(factorial(n))'
    assert str(factorial(factorial2(n))) == 'factorial(factorial2(n))'
    assert str(factorial2(factorial(n))) == 'factorial2(factorial(n))'
    assert str(factorial2(factorial2(n))) == 'factorial2(factorial2(n))'
    assert str(subfactorial(3)) == "2"
    assert str(subfactorial(n)) == "subfactorial(n)"
    assert str(subfactorial(2*n)) == "subfactorial(2*n)"


def test_Function():
    f = Function('f')
    fx = f(x)
    w = WildFunction('w')
    assert str(f) == "f"
    assert str(fx) == "f(x)"
    assert str(w) == "w_"


def test_Geometry():
    assert sstr(Point(0, 0)) == 'Point2D(0, 0)'
    assert sstr(Circle(Point(0, 0), 3)) == 'Circle(Point2D(0, 0), 3)'
    # TODO test other Geometry entities


def test_GoldenRatio():
    assert str(GoldenRatio) == "GoldenRatio"


def test_ImaginaryUnit():
    assert str(I) == "I"


def test_Infinity():
    assert str(oo) == "oo"
    assert str(oo*I) == "oo*I"


def test_Integer():
    assert str(Integer(-1)) == "-1"
    assert str(Integer(1)) == "1"
    assert str(Integer(-3)) == "-3"
    assert str(Integer(0)) == "0"
    assert str(Integer(25)) == "25"


def test_Integral():
    assert str(Integral(sin(x), y)) == "Integral(sin(x), y)"
    assert str(Integral(sin(x), (y, 0, 1))) == "Integral(sin(x), (y, 0, 1))"


def test_Interval():
    a = Symbol('a', real=True)
    assert str(Interval(0, a)) == "[0, a]"
    assert str(Interval(0, a, False, False)) == "[0, a]"
    assert str(Interval(0, a, True, False)) == "(0, a]"
    assert str(Interval(0, a, False, True)) == "[0, a)"
    assert str(Interval(0, a, True, True)) == "(0, a)"


def test_AccumBounds():
    a = Symbol('a', real=True)
    assert str(AccumBounds(0, a)) == "<0, a>"
    assert str(AccumBounds(0, 1)) == "<0, 1>"


def test_Lambda():
    assert str(Lambda(d, d**2)) == "Lambda(_d, _d**2)"
    # issue 2908
    assert str(Lambda((), 1)) == "Lambda((), 1)"
    assert str(Lambda((), x)) == "Lambda((), x)"


def test_Limit():
    assert str(Limit(sin(x)/x, x, y)) == "Limit(sin(x)/x, x, y)"
    assert str(Limit(1/x, x, 0)) == "Limit(1/x, x, 0)"
    assert str(
        Limit(sin(x)/x, x, y, dir="-")) == "Limit(sin(x)/x, x, y, dir='-')"


def test_list():
    assert str([x]) == sstr([x]) == "[x]"