2 + 3

2*3

2**3

sin(pi)

"""
Explanation: Introducción a Jupyter
Expresiones aritmeticas y algebraicas
Empezaremos esta práctica con algo de conocimientos previos de programación. Se que muchos de ustedes no han tenido la oportunidad de utilizar Python como lenguaje de programación y mucho menos Jupyter como ambiente de desarrollo para computo cientifico, asi que el primer objetivo de esta práctica será acostumbrarnos a la sintaxis del lenguaje y a las funciones que hacen especial a Jupyter.
Primero tratemos de evaluar una expresión aritmetica. Para correr el código en la siguiente celda, tan solo tienes que hacer clic en cualquier punto de ella y presionar las teclas Shift + Return.
End of explanation
"""


from math import sin, pi
sin(pi)

"""
Explanation: Sin embargo no existen funciones trigonométricas cargadas por default. Para esto tenemos que importarlas de la libreria math:
End of explanation
"""


a = 10

a

"""
Explanation: Variables
Las variables pueden ser utilizadas en cualquier momento, sin necesidad de declararlas, tan solo usalas!
End of explanation
"""


# YOUR CODE HERE
raise NotImplementedError()

# YOUR CODE HERE
raise NotImplementedError()

"""
Explanation: Ejercicio
Ejecuta el siguiente calculo y guardalo en una variable c:
$$
c = \pi *10^2
$$

Nota: Una vez que hayas concluido el calculo y guardado el valor en una variable, despliega el valor de la variable al ejecutar en una celda el nombre de la variable
End of explanation
"""


from nose.tools import assert_equal
assert_equal(_, c)
print("Sin errores")

"""
Explanation: Ejecuta la prueba de abajo para saber si has creado el codigo correcto
End of explanation
"""


A = [2, 4, 8, 10]
A

"""
Explanation: Listas
Las listas son una manera de guardar varios datos en un mismo arreglo. Podemos tener por ejemplo:
End of explanation
"""


A*2

"""
Explanation: Pero si intentamos multiplicar estos datos por un numero, no tendrá el comportamiento esperado.
End of explanation
"""


f = lambda x: x**2 + 1

"""
Explanation: Funciones
Podemos definir funciones propias de la siguiente manera:
End of explanation
"""


f(2)

"""
Explanation: Esta linea de codigo es equivalente a definir una función matemática de la siguiente manera:
$$
f(x) = x^2 + 1
$$
Por lo que si la evaluamos con $x = 2$, obviamente obtendremos como resultado $5$.
End of explanation
"""


def g(x):
    y = x**2 + 1
    return y

"""
Explanation: Esta notación que introducimos es muy util para funciones matemáticas, pero esto nos obliga a pensar en las definiciones de una manera funcional, lo cual no siempre es la solución (sobre todo en un lenguaje con un paradigma de programación orientado a objetos).
Esta función tambien puede ser escrita de la siguiente manera:
End of explanation
"""


g(2)

"""
Explanation: Con los mismos resultados:
End of explanation
"""


def cel_a_faren(grados_cel):
    # YOUR CODE HERE
    raise NotImplementedError()
    return grados_faren

"""
Explanation: Ejercicio
Define una función que convierta grados Celsius a grados Farenheit, de acuerdo a la siguiente formula:
$$
F = \frac{9}{5} C + 32
$$
End of explanation
"""


cel_a_faren(-1)

from nose.tools import assert_equal
assert_equal(cel_a_faren(10), 50)
assert_equal(cel_a_faren(50), 122)
print("Sin errores")

"""
Explanation: Y para probar trata de convertir algunos datos:
End of explanation
"""


for dato in A:
    print(dato*2)

"""
Explanation: Ciclos de control
Cuando queremos ejecutar código varias veces tenemos varias opciones, vamos a explorar rapidamente el ciclo for.
python
    for paso in pasos:
        ...
        codigo_a_ejecutar(paso)
        ...
En este caso el codigo se ejecutará tantas veces sean necesarias para usar todos los elementos que hay en pasos.
Por ejemplo, pordemos ejecutar la multiplicacion por 2 en cada uno de los datos:
End of explanation
"""


B = []
for dato in A:
    B.append(dato*2)
    
B

"""
Explanation: ó agregarlo en una lista nueva:
End of explanation
"""


# YOUR CODE HERE
raise NotImplementedError()
C

# YOUR CODE HERE
raise NotImplementedError()
D

"""
Explanation: Ejercicio

Crea una lista C con los enteros positivos de un solo digito, es decir: $\left{ x \in \mathbb{Z} \mid 0 \leq x < 10\right}$
Crea una segunda lista D con los cuadrados de cada elemento de C
End of explanation
"""


from numpy.testing import assert_array_equal
print("Sin errores")

"""
Explanation: Ejecuta las pruebas de abajo
End of explanation
"""


from numpy import matrix

A = matrix([[1, 2], [3, 4]])
A

v1 = matrix([[1], [2]])
v1

# Dependiendo de la version de python que exista en tu computadora,
# esta operacion pudiera no funcionar, en dado caso solo hay que 
# cambiar @ por *
A@v1

# La siguiente linea no va a funcionar, porque?
v1@A

"""
Explanation: Matrices
Para trabajar con matrices el flujo de trabajo es un poco diferente, ya que los arreglos manejan un conjunto diferente de operaciones; afortunadamente la librería numpy ya tiene definido un objeto de tipo matriz, con el cual podemos hacer las operaciones que queramos:
End of explanation
"""


from numpy import sin, cos, pi
τ = 2*pi
# YOUR CODE HERE
raise NotImplementedError()
vec_rot

from numpy.testing import assert_array_equal
assert_array_equal(vec_rot, matrix([[2*(cos(τ/12)-sin(τ/12))], [2*(cos(τ/12)+sin(τ/12))]]))
print("Sin errores")

"""
Explanation: Ejercicio

Declara una matriz de rotación rot, en dos dimensiones con un angulo de 30º
Aplica esta matriz de rotación al vector vec definido como $\begin{pmatrix} 2 \ 2\end{pmatrix}$
End of explanation
"""


