# -*- coding: utf-8 -*-
"""
Created on Sun Oct  4 21:38:04 2020

@author: jbobowsk
"""

# This short script will show how to take symbolic integrals in Python.
# We will require the 'SymPy' module.
import sympy as sym

# First, let's define a symbol x.
x = sym.Symbol('x')

# Then we can define f in terms of x.
f = 2*x**2+3

# To evaluate f at a particular value of x, we can use 'subs()'.
z = f.subs(x, 2)
print('2(2)^2 + 3 =', z)

# To integrate f with respect to x, we use 'integrate()'.
intf = f.integrate(x)
print(intf)

# We can get the same result using 'integrate()' in the following way:
intf = sym.integrate(f, x)
print(intf)

# We can try to make the typeset of the output a little nicer using 'display()'.
# This assumes that you're use IPython as I am.
from IPython.display import display, Math, Latex
display(intf)

# The above examples are indefinite integrals of f.  We can evaluate
# definite integrals using the same 'integrate()' function.  We just
# need to pass the limits of integration...
intf = sym.integrate(f, (x, 0, 3))
print(intf)

# or:
intf = f.integrate((x, 0, 3))
print(intf)

# We can make our functions more complicated.  Here's a function of two variables.
y = sym.Symbol('y')
g = x*sym.sin(x*y)
display(g)

# Here's the x-integral of g...
display(g.integrate(x))

# and here's the y-integral.
display(g.integrate(y))

# Notice that the results of both of the g integrals depend on whether or not
# x and y are zero.  We can specify that our variables are non-zero when they
# are first declared.
x = sym.Symbol('x', nonzero = True)
y = sym.Symbol('y', nonzero = True)
g = x*sym.sin(x*y)

# Now the solutions of our integrals have no conditions.
display(g.integrate(x))
display(g.integrate(y))

# Alternatively, you can define Python functions.
def fcn(x):
    return 2*x**2 + 3

# These functions can be integrated in the same way.  Here's the indefinite
# integral...
display(fcn(x).integrate(x))

# ... and here's the indefinite integral.
print(sym.integrate(fcn(x), (x, 0, 3)))

# Of course, you can have functions of multiple variables.
def gcn(x, y):
    return x*sym.sin(x*y)

# Here's the indefinite y-integral followed by the indefinite x-integral.
z = sym.integrate(sym.integrate(gcn(x, y), y), x)
display(z)

# Here's the definite y-integral followed by the definite x-integral using
# the other method of calling the 'integrate()' function.
z = gcn(x, y).integrate((y, 1, 3)).integrate((x, -2, -1))
print(z)
print(sym.N(z))