#!/home/pi/miniconda3/bin/python3.4

from math import sin, cos, tan, asin, atan, log, exp, sqrt

class Val_der():
	def __init__(self, a_val, a_der):
		self.val = a_val
		self.der = a_der

	def u_minus(self):
		return Val_der(-self.val, -self.der)

	def plus(self, v):
		return Val_der(self.val + v.val, self.der + v.der)

	def minus(self, v):
		return Val_der(self.val - v.val, self.der - v.der)

	def times(self, v):
		return Val_der(self.val * v.val, self.val * v.der + v.val * self.der)

	def divide_by(self, v):
		return Val_der(self.val / v.val,  (v.val * self.der - self.val * v.der) / v.val / v.val)

	def pow(self, v):
		a = self.val**v.val
		
		return Val_der(a, a * (v.der * log(self.val) + v.val * self.der / self.val))

	def exp(self):
		a = exp(self.val)
		
		return Val_der(a, a * self.der)

	def log(self):
		return Val_der(log(self.val), self.der / self.val)

	def sqrt(self):
		a = sqrt(self.val)

		return Val_der(a, self.der / a / 2.0)

	def sin(self):
		return Val_der(sin(self.val), cos(self.val) * self.der)

	def cos(self):
		return Val_der(cos(self.val), -sin(self.val) * self.der)

	def tan(self):
		return Val_der(tan(self.val), self.der / cos(self.val)**2)

	def asin(self):
		return Val_der(asin(self.val), self.der / sqrt(1.0 - self.val * self.val))

	def atan(self):
		return Val_der(atan(self.val), self.der / (1.0 + self.val * self.val))

	def __str__(self):
		return '''Value      = {0:9.5f}
Derivative = {1:9.5f}
'''.format(self.val, self.der)

if __name__ == "__main__":
	c = Val_der(5.0, 0)
	x = Val_der(2.0, 1)
	x_sq = x.times(x)

	print('Unary minus test.')
	print(x.u_minus())
	assert "{0:7.5f}".format(x.u_minus().val) == "-2.00000"
	assert "{0:7.5f}".format(x.u_minus().der) == "-1.00000"

	print('Plus test.')
	print(x.plus(c))
	assert "{0:7.5f}".format(x.plus(c).val) == "7.00000"
	assert "{0:7.5f}".format(x.plus(c).der) == "1.00000"

	print('Minus test.')
	print(x.minus(c))
	assert "{0:7.5f}".format(x.minus(c).val) == "-3.00000"
	assert "{0:7.5f}".format(x.minus(c).der) == "1.00000"

	print('Times test.')
	print(x.times(c))
	assert "{0:7.5f}".format(x.times(c).val) == "10.00000"
	assert "{0:7.5f}".format(x.times(c).der) == "5.00000"

	print('Divide test.')
	print(x.divide_by(x_sq))
	assert "{0:7.5f}".format(x.divide_by(x_sq).val) == "0.50000"
	assert "{0:7.5f}".format(x.divide_by(x_sq).der) == "-0.25000"

	print('Power test.')
	print(x.pow(x))
	assert "{0:7.5f}".format(x.pow(x).val) == "4.00000"
	assert "{0:7.5f}".format(x.pow(x).der) == "6.77259"

	print('Exp test.')
	print(x_sq.exp())
	assert "{0:7.5f}".format(x_sq.exp().val) == "54.59815"
	assert "{0:7.5f}".format(x_sq.exp().der) == "218.39260"

	print('Log test.')
	print(x.log())
	assert "{0:7.5f}".format(x.log().val) == "0.69315"
	assert "{0:7.5f}".format(x.log().der) == "0.50000"

	print('Square root test.')
	print(x.sqrt())
	assert "{0:7.5f}".format(x.sqrt().val) == "1.41421"
	assert "{0:7.5f}".format(x.sqrt().der) == "0.35355"
	

	print('Sine test.')
	print(x_sq.sin())
	assert "{0:7.5f}".format(x_sq.sin().val) == "-0.75680"
	assert "{0:7.5f}".format(x_sq.sin().der) == "-2.61457"

	print('Cosine test.')
	print(x_sq.cos())
	assert "{0:7.5f}".format(x_sq.cos().val) == "-0.65364"
	assert "{0:7.5f}".format(x_sq.cos().der) == "3.02721"
	
	print('Tangent test.')
	print(x_sq.tan())
	assert "{0:7.5f}".format(x_sq.tan().val) == "1.15782"
	assert "{0:7.5f}".format(x_sq.tan().der) == "9.36220"

	z = Val_der(0.5, 1)
	z_sq = z.times(z)
	print('Arcsine test.')
	print(z_sq.asin())
	assert "{0:7.5f}".format(z_sq.asin().val) == "0.25268"
	assert "{0:7.5f}".format(z_sq.asin().der) == "1.03280"

	print('Arctangent test.')
	print(x_sq.atan())
	assert "{0:7.5f}".format(x_sq.atan().val) == "1.32582"
	assert "{0:7.5f}".format(x_sq.atan().der) == "0.23529"

# End of code

# Sample output:

# Unary minus test.
# Value      =  -2.00000
# Derivative =  -1.00000

# Plus test.
# Value      =   7.00000
# Derivative =   1.00000

# Minus test.
# Value      =  -3.00000
# Derivative =   1.00000

# Times test.
# Value      =  10.00000
# Derivative =   5.00000

# Divide test.
# Value      =   0.50000
# Derivative =  -0.25000

# Power test.
# Value      =   4.00000
# Derivative =   6.77259

# Exp test.
# Value      =  54.59815
# Derivative = 218.39260

# Log test.
# Value      =   0.69315
# Derivative =   0.50000

# Square root test.
# Value      =   1.41421
# Derivative =   0.35355

# Sine test.
# Value      =  -0.75680
# Derivative =  -2.61457

# Cosine test.
# Value      =  -0.65364
# Derivative =   3.02721

# Tangent test.
# Value      =   1.15782
# Derivative =   9.36220

# Arcsine test.
# Value      =   0.25268
# Derivative =   1.03280

# Arctangent test.
# Value      =   1.32582
# Derivative =   0.23529