from __future__ import print_function
import tensorflow as tf
import numpy as np

from datetime import date
date.today()

author = "kyubyong. https://github.com/Kyubyong/tensorflow-exercises"

tf.__version__

np.__version__

sess = tf.InteractiveSession()

"""
Explanation: Math Part 1
End of explanation
"""


_x = np.array([1, 2, 3])
_y = np.array([-1, -2, -3])
x = tf.convert_to_tensor(_x)
y = tf.convert_to_tensor(_y)
out1 = tf.add(x, y)
out2 = x + y 
print(out1.eval())
assert np.array_equal(out1.eval(), out2.eval()) # tf.add is the same as the `+` operator.

_out = np.add(_x, _y)
assert np.array_equal(out1.eval(), _out) # tf.add == np.add

"""
Explanation: NOTE on notation
* _x, _y, _z, ...: NumPy 0-d or 1-d arrays
* _X, _Y, _Z, ...: NumPy 2-d or higer dimensional arrays
* x, y, z, ...: 0-d or 1-d tensors
* X, Y, Z, ...: 2-d or higher dimensional tensors
Arithmetic Operators
Q1. Add x and y element-wise.
End of explanation
"""


_x = np.array([3, 4, 5])
_y = np.array(3)
x = tf.convert_to_tensor(_x)
y = tf.convert_to_tensor(_y)
out1 = tf.subtract(x, y)
out2 = x - y 
print(out1.eval())
assert np.array_equal(out1.eval(), out2.eval()) # tf.subtract is the same as the `-` operator.

_out = np.subtract(_x, _y)
assert np.array_equal(out1.eval(), _out) # tf.subtract == np.subtract
# `add`, `subtract`, `multiply`, `div`, and `mod` support broadcasting. 

"""
Explanation: Q2. Subtract y from x element-wise.
End of explanation
"""


_x = np.array([3, 4, 5])
_y = np.array([1, 0, -1])
x = tf.convert_to_tensor(_x)
y = tf.convert_to_tensor(_y)
out1 = tf.multiply(x, y)
out2 = x * y 
print(out1.eval())
assert np.array_equal(out1.eval(), out2.eval()) # tf.multiply is the same as the `*` operator.

_out = np.multiply(_x, _y)
assert np.array_equal(out1.eval(), _out) # tf.multiply == np.multiply

"""
Explanation: Q3. Multiply x by y element-wise.
End of explanation
"""


_x = np.array([1, 2, 3])
x = tf.convert_to_tensor(_x)
out1 = tf.scalar_mul(5, x)
out2 = x * 5
print(out1.eval())
assert np.array_equal(out1.eval(), out2.eval())

_out = _x * 5
assert np.array_equal(out1.eval(), _out)

"""
Explanation: Q4. Multiply x by 5 element-wise.
End of explanation
"""


_x = np.array([10, 20, 30], np.int32)
_y = np.array([2, 3, 5], np.int32)
x = tf.convert_to_tensor(_x)
y = tf.convert_to_tensor(_y)
out1 = tf.div(x, y)
out2 = tf.truediv(x, y)
print(np.array_equal(out1.eval(), out2.eval()))

print(out1.eval(), out1.eval().dtype) # tf.div() returns the same results as input tensors. 
print(out2.eval(), out2.eval().dtype)# tf.truediv() always returns floating point results.

_out1 = _x // _y
assert np.array_equal(out1.eval(), _out1)

_out2 = _x / _y
assert np.array_equal(out2.eval(), _out2)

"""
Explanation: Q5. Predict the result of this.
End of explanation
"""


_x = np.array([10, 20, 30], np.int32)
_y = np.array([2, 3, 7], np.int32)
x = tf.convert_to_tensor(_x)
y = tf.convert_to_tensor(_y)

out1 = tf.mod(x, y)
out2 = x % y
print(out1.eval())
assert np.array_equal(out1.eval(), out2.eval())# tf.mod() is the same as the `%` operator.

_out = _x % _y
assert np.array_equal(out1.eval(), _out)


"""
Explanation: Q6. Get the remainder of x / y element-wise.
End of explanation
"""


_x = np.array([1, 2, 3], np.int32)
_y = np.array([4, 5, 6], np.int32)
x = tf.convert_to_tensor(_x)
y = tf.convert_to_tensor(_y)

out1 = tf.cross(x, y)
print(out1.eval())

_out = np.cross(_x, _y)
assert np.array_equal(out1.eval(), _out) # tf.cross == np.cross

"""
Explanation: Q7. Compute the pairwise cross product of x and y.
End of explanation
"""


_x = np.array([1, 2, 3], np.int32)
_y = np.array([4, 5, 6], np.int32)
_z = np.array([7, 8, 9], np.int32)
x = tf.convert_to_tensor(_x)
y = tf.convert_to_tensor(_y)
z = tf.convert_to_tensor(_y)

out1 = tf.add_n([x, y, z])
out2 = x + y + z
print(out1.eval())

assert np.array_equal(out1.eval(), out2.eval())

"""
Explanation: Basic Math Functions
Q8. Add x, y, and z element-wise.
End of explanation
"""


_X = np.array([[1, -1], [3, -3]])
X = tf.convert_to_tensor(_X)

out = tf.abs(X)
print(out.eval())

_out = np.abs(_X)
assert np.array_equal(out.eval(), _out) # tf.abs == np.abs

"""
Explanation: Q9. Compute the absolute value of X element-wise.
End of explanation
"""


_x = np.array([1, -1])
x = tf.convert_to_tensor(_x)

out1 = tf.negative(x)
out2 = -x
print(out.eval())
assert np.array_equal(out1.eval(), out2.eval())

_out = np.negative(_x)
assert np.array_equal(out1.eval(), _out) # tf.negative == np.negative

"""
Explanation: Q10. Compute numerical negative value of x, elemet-wise.
End of explanation
"""


_x = np.array([1, 3, 0, -1, -3])
x = tf.convert_to_tensor(_x)

out = tf.sign(x)
print(out.eval())

_out = np.sign(_x)
assert np.array_equal(out.eval(), _out) # tf.sign == np.sign

"""
Explanation: Q11. Compute an element-wise indication of the sign of x, element-wise.
End of explanation
"""


_x = np.array([1, 2, 2/10])
x = tf.convert_to_tensor(_x)

out1 = tf.reciprocal(x)
out2 = 1/x
print(out1.eval())
assert np.array_equal(out1.eval(), out2.eval())

_out = np.reciprocal(_x)
assert np.array_equal(out1.eval(), _out) # tf.reciprocal == np.reciprocal

"""
Explanation: Q12. Compute the reciprocal of x, element-wise.
End of explanation
"""


_x = np.array([1, 2, -1])
x = tf.convert_to_tensor(_x)

out1 = tf.square(x)
out2 = x * x
print(out1.eval())
assert np.array_equal(out1.eval(), out2.eval())

_out = np.square(_x)
assert np.array_equal(out1.eval(), _out) # tf.square == np.square

"""
Explanation: Q13. Compute the square of x, element-wise.
End of explanation
"""


_x = np.array([2.1, 1.5, 2.5, 2.9, -2.1, -2.5, -2.9])
x = tf.convert_to_tensor(_x)

out1 = tf.round(x)
out2 = tf.floor(x)
out3 = tf.ceil(x)
print(out1.eval())
print(out2.eval())
print(out3.eval())

_out1 = np.around(_x)
_out2 = np.floor(_x)
_out3 = np.ceil(_x)
assert np.array_equal(out1.eval(), _out1) # tf.round == np.around
assert np.array_equal(out2.eval(), _out2) # tf.floor == np.floor
assert np.array_equal(out3.eval(), _out3) # tf.ceil == np.ceil

"""
Explanation: Q14. Predict the results of this, paying attention to the difference among the family functions.
End of explanation
"""


_x = np.array([1, 4, 9], dtype=np.float32)
x = tf.convert_to_tensor(_x)

out = tf.sqrt(x)
print(out.eval())

_out = np.sqrt(_x)
assert np.array_equal(out.eval(), _out) # tf.sqrt ≈ np.sqrt
# Note that in TensorFlow, the input tensor must be
# a float, whereas in Numpy, an integer is okay.

"""
Explanation: Q15. Compute square root of x element-wise.
End of explanation
"""


_x = np.array([1., 4., 9.])
x = tf.convert_to_tensor(_x)

out1 = tf.rsqrt(x)
out2 = tf.reciprocal(tf.sqrt(x))
print(out1.eval())
assert np.array_equal(out1.eval(), out2.eval())

"""
Explanation: Q16. Compute the reciprocal of square root of x element-wise.
End of explanation
"""


_x = np.array([[1, 2], [3, 4]])
_y = np.array([[1, 2], [1, 2]])
x = tf.convert_to_tensor(_x)
y = tf.convert_to_tensor(_y)

out = tf.pow(x, y)
print(out.eval())

_out = np.power(_x, _y)
assert np.array_equal(out.eval(), _out) # tf.pow == np.power

"""
Explanation: Q17. Compute $x^y$, element-wise.
End of explanation
"""


_x = np.array([1., 2., 3.], np.float32)
x = tf.convert_to_tensor(_x)

out1 = tf.exp(x)
out2 = tf.pow(np.e, x) #np.e = 2.718281828459045
print(out1.eval())
assert np.allclose(out1.eval(), out2.eval())

_out = np.exp(_x)
assert np.array_equal(out1.eval(), _out) # tf.exp == np.exp

"""
Explanation: Q17. Compute $e^x$, element-wise.
End of explanation
"""


_x = np.array([1, np.e, np.e**2])
x = tf.convert_to_tensor(_x)

out = tf.log(x)
print(out.eval())

_out = np.log(_x)
assert np.array_equal(out.eval(), _out) # tf.log == np.log

"""
Explanation: Q18. Compute natural logarithm of x element-wise.
End of explanation
"""


_x = np.array([2, 3, 4])
_y = np.array([1, 5, 2])
x = tf.convert_to_tensor(_x)
y = tf.convert_to_tensor(_y)

out1 = tf.maximum(x, y)
out2 = tf.where(x > y, x, y)
print(out1.eval())
assert np.array_equal(out1.eval(), out2.eval())

_out = np.maximum(_x, _y)
assert np.array_equal(out1.eval(), _out) # tf.maximum == np.maximum

"""
Explanation: Q19. Compute the max of x and y element-wise.
End of explanation
"""


_x = np.array([2, 3, 4])
_y = np.array([1, 5, 2])
x = tf.convert_to_tensor(_x)
y = tf.convert_to_tensor(_y)

out1 = tf.minimum(x, y)
out2 = tf.where(x < y, x, y)
print(out1.eval())
assert np.array_equal(out1.eval(), out2.eval())

_out = np.minimum(_x, _y)
assert np.array_equal(out1.eval(), _out) # tf.minimum == np.minimum

"""
Explanation: Q20. Compute the min of x and y element-wise.
End of explanation
"""


_x = np.array([-np.pi, np.pi, np.pi/2])
x = tf.convert_to_tensor(_x)

out1 = tf.sin(x)
out2 = tf.cos(x)
out3 = tf.tan(x)
print(out1.eval())
print(out2.eval())
print(out3.eval())

_out1 = np.sin(_x)
_out2 = np.cos(_x)
_out3 = np.tan(_x)

assert np.allclose(out1.eval(), _out1) # tf.sin == np.sin
assert np.allclose(out2.eval(), _out2) # tf.cos == np.cos
assert np.allclose(out3.eval(), _out3) # tf.tan == np.tan

"""
Explanation: Q21. Compuete the sine, cosine, and tangent of x, element-wise.
End of explanation
"""


_x = np.array([2, 3, 4])
_y = np.array([1, 5, 1])
x = tf.convert_to_tensor(_x)
y = tf.convert_to_tensor(_y)

out1 = tf.squared_difference(x, y)
out2 = tf.square(tf.subtract(x, y))
print(out1.eval())
assert np.array_equal(out1.eval(), out2.eval())


"""
Explanation: Q22. Compute (x - y)(x - y) element-wise.
End of explanation
"""


