'''
Skrevet av: Vegard G. Jervell
Hensikt: Hjelpe folk med å lære alt de ikke lærte i ITGK
Spesifikt: NumPy
'''
import numpy as np
import matplotlib.pyplot as plt

#Til å begynne med, er dette kjent som en list-comprehention:
en_liste = [x for x in range(10)]
# det er det samme som å skrive
en_liste = []
for x in range(10):
    en_liste.append(x) #append() er en drittreig funksjon, hvis du bruker den har du sannsynligvis planlagt dårlig.

#bortsett fra at det tar halvparten av tiden å skrive, og kjører ≈N ganger fortere (hvor N er lengden på lista)
# videre, hvis du skal kopiere en liste, IKKE skriv

en_annen_liste = en_liste #IKKE gjør dette!
#da skjer dette:
en_liste[0] = 'her har jeg endret noe'
print('en_liste :', en_liste)
print('en_annen_liste :', en_annen_liste) #denne har også blitt endret

#når du skal kopiere en liste gjør du sånn her:
en_annen_liste = [x for x in en_liste] #nå går det bra!

#Videre kan man generere fine lister ved å skrive ting som
noen_tall = [i for i in range(100)]
en_funksjon_av_de_tallene = [x**2 for x in noen_tall]

# Men hele poenget med denne guiden er at du skal lære en bedre måte å gjøre det på uansett.
# Det er bare viktig at du vet hva en list-comprehention er for at du skal kunne lese dette.
# så med det er vi klare til å sparke i gang :)

#konstanter
R = 8.314 #J/molK
p = 1e5 # = 10^5 [Pa]

#Dette har vi målt på labben
t = [0, 1, 2, 3, 4, 5] #s
T = [20, 19, 22, 21, 23, 20] #Celsius
V = [0, 0.7, 0.9, 1.5, 2.3, 2.9] #mL

#konverterer til np.array()
t = np.array(t)
T = np.array(T)
V = np.array(V)

print('T = ', T)
print('V = ', V)

#Gjør litt matte
T = T + 273 #K
V = V/10**6 #m^3

print('T = ', T)
print('V = ', V)

n = p*V/(R*T) # pV = nRT

print('n = ',n) #n blir en array, fordi V og T er arrays
                #hvis V og T har forskjellige lengder funker ikke dette

plt.scatter(t,n, label = 't_data,n') #plotter punkter
plt.ylim(min(n),max(n)) #justerer y_data-aksen

plt.xlabel('tid [s]') #putter navn på aksen
plt.ylabel('stoffmengde gass [mol]') #begge aksene
plt.title(r'$B_ru^k \frac{denne}{kommandoen}$ for å skrive $L_aTe^x_data$')
plt.legend() #legger inn hva ting er
plt.show()

coeff = np.polyfit(t,n, 1) #y_data = ax + b
print('regresjonskoeffisienter: ',coeff)
print('a = ', coeff[0])
print('b = ', coeff[1])

x_linje = np.linspace(min(t), max(t), 100)
print('En pen x_data-akse: ', x_linje)

#y_regresjon = [coeff[0] * x_data + coeff[1] for x_data in x_linje]
y_regresjon = np.polyval(coeff, x_linje) #gjør akkurat det samme som linjen over

plt.plot(x_linje, y_regresjon, color='red', label='regresjon')
plt.scatter(t, n, marker='x', color='black', label='eksperimentelt')
plt.legend()
plt.show()

sigma = 1e-5 #en eller annen usikkerhet

plt.plot(x_linje, y_regresjon, color='red', label='regresjon')
plt.fill_between(x_linje, y_regresjon + sigma, y_regresjon - sigma, color='red', alpha=0.2, linewidth=0)

plt.scatter(t, n, marker='x', color='black', label='eksperimentelt')
plt.legend()
plt.show()

# Nå som vi har gjort litt regresjon kommer litt småtriks
# Når du skal lage en array med N antall plasser kan du bruke

N = 1000
en_array = np.zeros(1000) #lager 1000 nuller
en_annen_array = np.ones(1000) #lager 1000 enere
en_tredje_array = np.empty(1000) #lager en tom liste med 1000 plasser (inneholder noe rare greier, denne kan være litt skummel)

#Matriser funker på samme måte, for en N x M matrise:
M = 500
en_matrise = np.zeros((N,M))

#Nå kan jeg fylle opp disse med verdier
en_verdi = 10
for i in range(len(en_array)):
    en_array[i] = en_verdi #mye, mye, mye (ca. 10^3 - 10^6 ganger) raskere enn å lage en tom python-liste og appende til den
                            # jo lengre listen er, jo mer kjøretid sparer du

#Litt lineæralgebra er heller ikke noe problem
en_transponert_matrise = en_matrise.transpose()

en_inverterbar_matrise = np.identity(N) * 10 #identitetsmatrisen er inverterbar, selv om du ganger den med 10
en_invertert_matrise = np.linalg.inv(en_inverterbar_matrise)

et_skalarprodukt = np.dot(en_array, en_annen_array)
et_matrise_vektor_produkt = np.dot(en_invertert_matrise, en_array)

#i stedet for a = np.dot(x,y) kan man også skrive a = x @ y
et_matriseprodukt = en_inverterbar_matrise @ en_invertert_matrise