lista = [2, 3.4, 'teste']   # sintaxe para definir uma lista
print (lista)

x = 3.0
y = 5.3

lista = [x**2, x+y, x**y, 2*x+5*y]  # usando expressões para definir listas
print (lista)

"""
Explanation: Listas e arrays (vetores)
até agora vimos variáveis que representam valores unicos de um dado tipo
em física é comum querer representar vários numeros com um nome como $\vec{r}=(x,y,z)$
Outro exemplo seria uma tabela com milhares de valores medidos para uma dada grandeza $a_1,a_2,a_3$ de modo que possamos usar $A={a_i}$ onde i é iterado no total de elementos de $A$
Python permite lidar com essas situações usando containers como listas e arrays, que são as mais importantes e tuples, dicionarios e conjuntos, que são menos comuns
Listas
o tipo mais básico de container em Python é a lista
uma lista é uma lista de quantidades, que não precisam ser do mesmo tipo
Em física no entanto, em geral usamos listas de elementos do mesmo tipo
End of explanation
"""


from math import sqrt

r = [1.0, 1.5, -2.2]
tamanho = sqrt( r[0]**2 + r[1]**2 + r[2]**2 )   # calculando o módulo de um vetor
print (tamanho)

# podemos mudar o valor de elementos individuais na lista:
r[1] = 3.5
print (r)

"""
Explanation: tendo definido listas provavelmente vamos querer fazer operações com elas
os elementos de uma lista podem ser recuperados pela sua posição de alocação
considere a lista l=[3, 5, 2, 45, 23] onde temos os seguintes elementos individuais:
l[0] é 3, l[1] é 5, l[2] é 2 e assim por diante
note que a indexação se inicia em 0, ou seja, l[0] é o primeiro elemento da lista
os elementos individuais funcionam como variáveis únicas
End of explanation
"""


r = [1.0, 1.5, -2.2]
total = sum(r)           # essa função retorna a soma de todos os elementos da lista
print (total)

print (max(r), min(r))     # maximo e minimo da lista r

print (len(r))            # tamanho da lista

media = sum(r)/len(r)   # calculando a média
print (media)

"""
Explanation: uma capacidade importante e poderosa em Python é a de poder fazer operações com listas
Algumas usuais são: sum, max, min, len, mean
End of explanation
"""


from math import log

r = [1.0, 1.5, 2.2]

logr = list(map(log, r))
print (logr, type(logr))

"""
Explanation: Uma função especial em Python é a map. Ela permite aplicar funções a todos os elementos de uma lista
End of explanation
"""


r = [1.0, 1.5, 2.2]
r.append(6.2)
print (r)

x= 3
r.append(2*x+1)
print (r)

"""
Explanation: outra possibilidade importante com listas em Python é a abilidade de adicionar ou remover elementos
End of explanation
"""


r=[]
i = 0
while i < 10:
    r.append(i)
    i += 1
print (r)

# removendo elementos
r.pop()
print (r)

# removendo um elemento específico
r.pop(3)
print (r)

"""
Explanation: pode-se criar uma lista vazia e ir adicionando elementos a medida que o programa evolui
End of explanation
"""


from numpy import zeros
a = zeros(4,float)
print (a)


"""
Explanation: Arrays
arrays são semelhantes a listas, mas existem diferenças importantes:
1 - arrays tem tamanho fixo e não podem ser alterados depois de criados
2 - arrays tem que ter todos os elementos do mesmo tipo
mas arrays tem vantagens: podem ser multidimensionais, se comportam como vetores, podemos fazer aritmética com eles ( o que não funciona com listas) e as operações com eles são mais rápidas em geral.
antes de usar um array voce precisa cria-lo dizendo de que tipo será e com quantos elementos
o modulo em Python usado para lidar com arrays é o numpy
End of explanation
"""


a = zeros(10,int)        # array de inteiros
b = zeros(10,complex)    # array de complexos
c = zeros(10,str)        # array de strings

print (a)
print (b)
print (c)

# criando arrays multi dimensionais
from numpy import zeros

a = zeros([3,4,5],float)      # array de dimensões 3 linhas x 4 colunas
print (a)

print ([[0,1],[2,3]])

"""
Explanation: note que os arrays são impressos de modo distinto das listas, sem virgula separando os elementos
End of explanation
"""


from numpy import array

r = [ 1.0, 1.5, -2.2 ]   # lista
a = array(r,float)       # array
print (a)

# ou em uma unica linha:
a = array([1.0,1.5,-2.2],float)
print (a)

"""
Explanation: outra maneira de se criar um array em numpy é usando a função array para converter uma lista:
End of explanation
"""


a = array([[1,2,3],[4,5,6]],int)
print (a)


"""
Explanation: podemos também criar arrays multidimensionais definindo seus elementos:
End of explanation
"""


import numpy as np
# from numpy import zeros

a = np.zeros([10,10],int)
print (a,'\n')

print('')

a[5,:] = 99

print (a[5,:],'\n')

# mude o valor de alguns elementos
a[0,1] = 1
a[1,0] = -1

print (a)
print (a[1:4,5:7])


"""
Explanation: a indexação funciona de maneira similar a listas
End of explanation
"""


from numpy import array

a = array([1,2,3,0],int)
b = 2*a

print (a, '\n')
print (b, '\n')
print (b/a)       # em casos como esse os arrays tem que ter o memso tamanho!


"""
Explanation: Aritimética com arrays
com em listas, os elementos dos arrays funcionam como variaveis isoladas.
poderíamos fazer algo como a[0] = a[1] + 1 com elemntos do array a
ou algo como x = a[2]*2 - 2a[3]/y
uma das grandes vantagens em Python é fazer aritimética com todo o vetor de uma vez:
End of explanation
"""


a = array([1,2,3,4],int)
b = array([2,4,6,8],int)

print (a*b)


"""
Explanation: note que a multiplicação de arrays não funciona exatamente com em matemática, produto escalar de dois vetores:
End of explanation
"""


from numpy import array,dot

a = array([1,2,3,4],int)
b = array([2,4,6,8],int)

print (dot(a,b))             # imprime o produto escalar de a e b


"""
Explanation: veja que o que aconteceu é que cada elemento foi multiplicado pelo seu correspondente no outro vetor
Para fazer opeções de algebra linear como produto escalar o numpy tem funções específicas. Veja a lista completa em: 
https://docs.scipy.org/doc/numpy-1.13.0/reference/routines.linalg.html
End of explanation
"""


from numpy import array,dot, sqrt

a = array([[1,3],[2,4]],int)
b = array([[4,-2],[-3,1]],int)
c = array([[1,2],[2,1]],int)

print ((dot(a,b)+2*c)) 


"""
Explanation: operações com matrizes podem ser feitas de maneira simples.
Seja a operação:
$\left( \begin{array}{cc} 1 & 3 \ 2 & 4 \end{array} \right) \left( \begin{array}{cc} 4 & -2 \ -3 & 1 \end{array} \right) + 2 \left( \begin{array}{cc} 1 & 2 \ 2 & 1 \end{array} \right)  = \left( \begin{array}{cc} -3 & 5 \ 0 & 2 \end{array} \right) $
End of explanation
"""


from numpy import array

a = array([[1,3],[2,4]],int)

print (a.size)

"""
Explanation: podemos mapear funções em arrays como foi feito com listas
alguns cuidados devem ser tomados pois funções usuais como max, min len não dão resultados esperados quando aplicados em arrays multidimensionais
End of explanation
"""


import numpy as np      # repare na outra forma de importar o numpy!

a = array([[1,3],[2,4]],int)

print (a.size)            # imprime o tamanho de a
print (a.shape)           # imprime a forma de a
print ('\n')
print (np.max(a))         # imprime o valor maximo em a
print (np.mean(a))        # imprime a média dos elementos de a

print(a.max(),a.mean(),a.std())

"""
Explanation: Mas numpy contem funções específicas para essas tarefas com arrays
End of explanation
"""


from numpy import array
a = array([1,1],int)
b = a
print (a[0] == b[0])
a[0] = 3
print (a)
print (b)

"""
Explanation: Exercício
faça um programa para calcular a média geométrica de um conjunto de n valores
$\bar{x}=\left[\displaystyle\prod_{i=1}^{n} x_i\right]^{1/n}$ ou 
$\bar{x} = exp \left( \frac{1}{n} \displaystyle\sum_{i=1}^{n} ln (x_i) \right)$
Observe o programa abaixo:
End of explanation
"""


from numpy import array, copy
a = array([1,1],int)
b = copy(a)
a[0] = 3
print (a)
print (b)

"""
Explanation: observe que no caso de arrays quando fazemos b = a isso não cria uma nova cópia de a! Isso é feito para economizar memória em situações com muitos arrays de grande numero de elementos.
Para criar uma cópia o programa deveria ser:
End of explanation
"""


r = [1,3,5,7,9,11,13,15]
s = r[2:5]
print (s)

"""
Explanation: Com listas e arrays podemos fazer cortes
End of explanation
"""


r = [0,1,2,3,4,5,6,7,8,9]
print (r[2:5])
print (r[:5])      # imprime todos os elementos até mas não incluindo 5

import numpy as np

"""
Explanation: repare que o corte consiste em pegar os elementos 2, 3, 4
Isso porque a indexação começa em 0
End of explanation
"""


