from sympy import (symbols, integrate, Integral, exp, oo, Symbol, Function, Rational,
    log, sin, cos, pi, E, I, Poly, LambertW, diff, Matrix, sympify, sqrt, atan)
from sympy.utilities.pytest import XFAIL
from sympy.physics.units import m, s
import py
from py.test import skip

x,y,a,t = symbols('xyat')
n = Symbol('n', integer=True)
f = Function('f')

def test_improper_integral():
    assert integrate(log(x), (x, 0, 1)) == -1
    assert integrate(x**(-2), (x, 1, oo)) == 1

def test_basics():
    e=(t+1)**2
    assert integrate(e, (t,0,x), evaluate=False).diff(x)==((1+x)**2).expand()
    assert integrate(e, (t,0,x), evaluate=False).diff(a)==0

    assert integrate(e, (t,a,x), evaluate=False).diff(x)==((1+x)**2).expand()
    assert integrate(e, (t,x,a), evaluate=False).diff(x)==(-(1+x)**2).expand()

    assert integrate(t**2, (t,x,2*x), evaluate=False).diff(x)==7*x**2

@XFAIL
def test_unevaluated():
    py.test.raises(IntegralError,"integrate(e, (t,0,x), evaluate=False).diff(t)")
    py.test.raises(IntegralError,"integrate(e, t, evaluate=False).diff(x)")

def test_integration():
    assert integrate(0, (t,0,x)) == 0
    assert integrate(3, (t,0,x)) == 3*x
    assert integrate(t, (t,0,x)) == x**2/2
    assert integrate(3*t, (t,0,x))== 3*x**2/2
    assert integrate(3*t**2, (t,0,x)) == x**3
    assert integrate(1/t, (t,1,x)) == log(x)
    assert integrate(-1/t**2, (t,1,x)) == 1/x-1
    assert integrate(t**2+5*t-8, (t,0,x)) == x**3/3+5*x**2/2-8*x
    assert integrate(x**2, x) == x**3/3
    assert integrate((3*t*x)**5, x) == (3*t)**5 * x**6 / 6

    b = Symbol("b")
    c = Symbol("c")
    assert integrate(a*t, (t,0,x))==a*x**2/2
    assert integrate(a*t**4, (t,0,x))==a*x**5/5
    assert integrate(a*t**2+b*t+c, (t,0,x))==a*x**3/3+b*x**2/2+c*x

def test_multiple_integration():
    assert integrate((x**2)*(y**2), (x,0,1), (y,-1,2)) == Rational(1)
    assert integrate((y**2)*(x**2), x, y) == Rational(1,9)*(x**3)*(y**3)
    assert integrate(1/(x+3)/(1+x)**3, x) == \
        -(1 + x)**(-2)/4 - log(3 + x)/8 + 1/(1 + x)/4 + log(1 + x)/8

def test_issue433():
    assert integrate(exp(-x), (x,0,oo)) == 1

def test_issue461():
    assert integrate(x**Rational(3,2), x) == 2*x**Rational(5,2)/5
    assert integrate(x**Rational(1,2), x) == 2*x**Rational(3,2)/3
    assert integrate(x**Rational(-3,2), x) == -2*x**Rational(-1,2)

def test_integrate_poly():
    p = Poly(x + x**2*y + y**3, x, y)

    qx = integrate(p, x)
    qy = integrate(p, y)

    assert isinstance(qx, Poly) == True
    assert isinstance(qy, Poly) == True

    assert qx.symbols == (x, y)
    assert qy.symbols == (x, y)

    assert qx.as_basic() == x**2/2 + x**3*y/3 + x*y**3
    assert qy.as_basic() == x*y + x**2*y**2/2 + y**4/4

def test_integrate_poly_defined():
    p = Poly(x + x**2*y + y**3, x, y)

    Qx = integrate(p, (x, 0, 1))
    Qy = integrate(p, (y, 0, pi))

    assert isinstance(Qx, Poly) == True
    assert isinstance(Qy, Poly) == True

    assert Qx.symbols == (y,)
    assert Qy.symbols == (x,)

    assert Qx.as_basic() == Rational(1,2) + y/3 + y**3
    assert Qy.as_basic() == pi**4/4 + pi*x + pi**2*x**2/2

def test_integrate_varommited():
    y = Symbol('y')
    assert integrate(2)     == 2
    assert integrate(x)     == x**2/2
    assert integrate(x*y)   == x**2*y**2/4


def test_integrate_poly_accurately():
    y = Symbol('y')
    assert integrate(x*sin(y), x)       == x**2*sin(y)/2

    # when passed to risch_norman, this will be a CPU hog, so this really
    # checks, that integrated function is recognized as polynomial
    assert integrate(x**1000*sin(y), x) == x**1001*sin(y)/1001

def test_issue536():
    y = Symbol('y')
    assert integrate(x**2, y) == x**2*y
    assert integrate(x**2, (y, -1, 1)) == 2*x**2

# works in sympy and py.test but hangs in `setup.py test`
def test_integrate_linearterm_pow():
    # check integrate((a*x+b)^c, x)  --  #400
    y = Symbol('y')
    assert integrate(x**y, x) == x**(y+1)/(y+1)
    assert integrate((exp(y)*x + 1/y)**(1+sin(y)), x)   == exp(-y)*(exp(y)*x + 1/y)**(2+sin(y)) / (2+sin(y))

def test_issue519():
    assert integrate(pi*x**Rational(1,2),x) == 2*pi*x**Rational(3,2)/3
    assert integrate(pi*x**Rational(1,2) + E*x**Rational(3,2),x) == \
                                               2*pi*x**Rational(3,2)/3  + \
                                               2*E *x**Rational(5,2)/5
def test_issue524():
    assert integrate(cos((n+1) * x), x)   == sin(x*(n+1)) / (n+1)
    assert integrate(cos((n-1) * x), x)   == sin(x*(n-1)) / (n-1)

    assert integrate(cos((n+1) * x) + cos((n-1) * x), x) == \
                                             sin(x*(n+1)) / (n+1)  + \
                                             sin(x*(n-1)) / (n-1)

def test_issue565():
    assert integrate(-1./2 * x * sin(n * pi * x/2), [x, -2, 0])  == 2*cos(pi*n)/(pi*n)
    assert integrate(-Rational(1)/2 * x * sin(n * pi * x/2), [x, -2, 0]) \
                                                                 == 2*cos(pi*n)/(pi*n)


def test_rational_functions():
    half = Rational(1,2)
    integrate(1/(x**2+x+1), x) == -I*3**half*log(half + x - half*I*3**half)/3 +\
                                   I*3**half*log(half + x + half*I*3**half)/3

    integrate(1/(x**3+1), x) == log(1 + x)/3 - \
        (Rational(1,6) - I*3**half/6)*log(half - x - I*3**half/2) - \
        (Rational(1,6) + I*3**half/6)*log(half - x + I*3**half/2)

def test_issue587(): # remove this when fresnel itegrals are implemented
    assert integrate(sin(x**2), x) == Integral(sin(x**2), x)

def test_integrate_units():
    assert integrate(x * m/s, (x, 1*s, 5*s)) == 12*m*s

def test_transcendental_functions():
    assert integrate(LambertW(2*x), x) == -x + x*LambertW(2*x) + x/LambertW(2*x)

def test_issue641():
    f=4*log(x)-2*log(x)**2
    fid=diff(integrate(f,x),x)
    assert abs(f.subs(x,42).evalf() - fid.subs(x,42).evalf()) < 1e-10

def test_issue853():
    f = sin(x)
    assert integrate(f, x) == -cos(x)
    py.test.raises(ValueError, "integrate(f, 2*x)")
    assert f.integral(x) == Integral(sin(x), x)
    py.test.raises(TypeError, "f.integral(2*x)")

def test_matrices():
    M = Matrix(2, 2, lambda i, j: (i+j+1)*sin((i+j+1)*x))

    assert integrate(M, x) == Matrix([
        [-cos(x),   -cos(2*x)],
        [-cos(2*x), -cos(3*x)],
    ])

# issue1012
def test_integrate_functions():
    assert integrate(f(x), x)       == Integral(f(x), x)
    assert integrate(f(x), (x,0,1)) == Integral(f(x), (x,0,1))
    assert integrate(f(x)*diff(f(x), x), x) == f(x)**2/2
    assert integrate(diff(f(x),x) / f(x),x) == log(f(x))


def test_transform():
    a = Integral(x**2+1, (x, -1, 2))
    assert a.doit() == a.transform(x, 3*x+1).doit()
    assert a.transform(x, 3*x+1).transform(x, 3*x+1, inverse=True) == a
    assert a.transform(x, 3*x+1, inverse=True).transform(x, 3*x+1) == a
    a = Integral(sin(1/x), (x, 0, 1))
    assert a.transform(x, 1/x) == Integral(sin(x)/x**2, (x, 1, oo))
    assert a.transform(x, 1/x).transform(x, 1/x) == a
    a = Integral(exp(-x**2), (x, -oo, oo))
    assert a.transform(x, 2*x) == Integral(2*exp(-4*x**2), (x, -oo, oo))
    assert py.test.raises(ValueError, "a.transform(x, 1/x)")
    assert py.test.raises(ValueError, "a.transform(x, 1/x)")

def NS(e, n=15, **options):
    return str(sympify(e).evalf(n, **options))

def test_evalf_integrals():
    assert NS(Integral(x, (x, 2, 5)), 15) == '10.5'
    gauss = Integral(exp(-x**2), (x, -oo, oo))
    assert NS(gauss, 15) == '1.77245385090552'
    assert NS(gauss**2 - pi + E*Rational(1,10**20), 15) in ('2.71828182845904e-20', '2.71828182845905e-20')
    # A monster of an integral from http://mathworld.wolfram.com/DefiniteIntegral.html
    t = Symbol('t')
    a = 8*sqrt(3)/(1+3*t**2)
    b = 16*sqrt(2)*(3*t+1)*(4*t**2+t+1)**Rational(3,2)
    c = (3*t**2+1)*(11*t**2+2*t+3)**2
    d = sqrt(2)*(249*t**2+54*t+65)/(11*t**2+2*t+3)**2
    f = a - b/c - d
    assert NS(Integral(f, (t, 0, 1)), 50) == NS((3*sqrt(2)-49*pi+162*atan(sqrt(2)))/12,50)
    # http://mathworld.wolfram.com/VardisIntegral.html
    assert NS(Integral(log(log(1/x))/(1+x+x**2), (x, 0, 1)), 15) == NS('pi/sqrt(3) * log(2*pi**(5/6) / gamma(1/6))', 15)
    # http://mathworld.wolfram.com/AhmedsIntegral.html
    assert NS(Integral(atan(sqrt(x**2+2))/(sqrt(x**2+2)*(x**2+1)), (x, 0, 1)), 15) == NS(5*pi**2/96, 15)
    # http://mathworld.wolfram.com/AbelsIntegral.html
    assert NS(Integral(x/((exp(pi*x)-exp(-pi*x))*(x**2+1)), (x, 0, oo)), 15) == NS('log(2)/2-1/4',15)
    # Complex part trimming
    # http://mathworld.wolfram.com/VardisIntegral.html
    assert NS(Integral(log(log(sin(x)/cos(x))), (x, pi/4, pi/2)), 15, chop=True) == \
        NS('pi/4*log(4*pi**3/gamma(1/4)**4)', 15)
    #
    # Endpoints causing trouble (rounding error in integration points -> complex log)
    assert NS(2+Integral(log(2*cos(x/2)), (x, -pi, pi)), 17, chop=True) == '2.0'
    assert NS(2+Integral(log(2*cos(x/2)), (x, -pi, pi)), 20, chop=True) == '2.0'
    assert NS(2+Integral(log(2*cos(x/2)), (x, -pi, pi)), 22, chop=True) == '2.0'
    # Needs zero handling
    assert NS(pi - 4*Integral('sqrt(1-x**2)', (x, 0, 1)), 15, maxprec=30, chop=True) in ('0.0', '0')
    # Oscillatory quadrature
    a = Integral(sin(x)/x**2, (x, 1, oo)).evalf(maxprec=15)
    assert 0.49 < a < 0.51
    assert NS(Integral(sin(x)/x**2, (x, 1, oo)), quad='osc') == '0.504067061906928'
    assert NS(Integral(cos(pi*x+1)/x, (x, -oo, -1)), quad='osc') == '0.276374705640365'

def test_evalf_issue_939():
    # http://code.google.com/p/sympy/issues/detail?id=939
    assert NS(integrate(1/(x**5+1), x).subs(x, 4), chop=True) == '-0.000976138910649103'
    assert NS(Integral(1/(x**5+1), (x, 2, 4))) == '0.014436108888674'
    assert NS(integrate(1/(x**5+1), (x, 2, 4)), chop=True) == '0.014436108888674'

def xtest_failing_integrals():
    #---
    # Double integrals not implemented
    assert NS(Integral(sqrt(x)+x*y, (x, 1, 2), (y, -1, 1)), 15) == '2.43790283299492'
    # double integral + zero detection
    assert NS(Integral(sin(x+x*y), (x, -1, 1), (y, -1, 1)), 15) == '0.0'
