import tensorflow as tf
import numpy as np

print(tf.__version__)

"""
Explanation: <h1> Getting started with TensorFlow </h1>

In this notebook, you play around with the TensorFlow Python API.
End of explanation
"""


a = np.array([5, 3, 8])
b = np.array([3, -1, 2])
c = np.add(a, b)
print(c)

"""
Explanation: <h2> Adding two tensors </h2>

First, let's try doing this using numpy, the Python numeric package. numpy code is immediately evaluated.
End of explanation
"""


a = tf.constant([5, 3, 8])
b = tf.constant([3, -1, 2])
c = tf.add(a, b)
print(c)

"""
Explanation: The equivalent code in TensorFlow consists of two steps:
<p>
<h3> Step 1: Build the graph </h3>
End of explanation
"""


with tf.Session() as sess:
  result = sess.run(c)
  print(result)

"""
Explanation: c is an Op ("Add") that returns a tensor of shape (3,) and holds int32. The shape is inferred from the computation graph.
Try the following in the cell above:
<ol>
<li> Change the 5 to 5.0, and similarly the other five numbers. What happens when you run this cell? </li>
<li> Add an extra number to a, but leave b at the original (3,) shape. What happens when you run this cell? </li>
<li> Change the code back to a version that works </li>
</ol>

<p/>
<h3> Step 2: Run the graph
End of explanation
"""


a = tf.placeholder(dtype=tf.int32, shape=(None,))  # batchsize x scalar
b = tf.placeholder(dtype=tf.int32, shape=(None,))
c = tf.add(a, b)
with tf.Session() as sess:
  result = sess.run(c, feed_dict={
      a: [3, 4, 5],
      b: [-1, 2, 3]
    })
  print(result)

"""
Explanation: <h2> Using a feed_dict </h2>

Same graph, but without hardcoding inputs at build stage
End of explanation
"""


def compute_area(sides):
  #TODO: Write TensorFlow code to compute area of a triangle
  #  given its side lengths
  
  return area

with tf.Session() as sess:
  area = compute_area(tf.constant([5.0, 3.0, 7.1]))
  result = sess.run(area)
  print(result)

"""
Explanation: <h2> Heron's Formula in TensorFlow </h2>

The area of triangle whose three side lengths are $(a, b, c)$ is $\sqrt{s(s-a)(s-b)(s-c)}$ where $s=\frac{a+b+c}{2}$ 
Look up the available operations at: https://www.tensorflow.org/api_docs/python/tf. 
You'll need the tf.sqrt() operation. Remember tf.add(), tf.subtract() and tf.multiply() are overloaded with the +,- and * operators respectively.
You should get: 6.278497
End of explanation
"""


def compute_area(sides):
  #TODO: Write TensorFlow code to compute area of a 
  #  SET of triangles given by their side lengths

  return list_of_areas

with tf.Session() as sess:
  # pass in two triangles
  area = compute_area(tf.constant([
      [5.0, 3.0, 7.1],
      [2.3, 4.1, 4.8]
    ]))
  result = sess.run(area)
  print(result)

"""
Explanation: Extend your code to be able to compute the area for several triangles at once.
You should get: [6.278497 4.709139]
End of explanation
"""


with tf.Session() as sess:
  #TODO: Rather than feeding the side values as a constant, 
  #  use a placeholder and fill it using feed_dict instead.
  result = sess.run(...)
  print(result)

"""
Explanation: <h2> Placeholder and feed_dict </h2>

More common is to define the input to a program as a placeholder and then to feed in the inputs. The difference between the code below and the code above is whether the "area" graph is coded up with the input values or whether the "area" graph is coded up with a placeholder through which inputs will be passed in at run-time.
End of explanation
"""


import tensorflow as tf

tf.enable_eager_execution()

#TODO: Using your non-placeholder solution, 
#  try it now using tf.eager by removing the session


"""
Explanation: tf.eager
tf.eager allows you to avoid the build-then-run stages. However, most production code will follow the lazy evaluation paradigm because the lazy evaluation paradigm is what allows for multi-device support and distribution. 
<p>
One thing you could do is to develop using tf.eager and then comment out the eager execution and add in the session management code.

<b> You will need to restart your session to try this out.</b>
End of explanation
"""


