########################
#   Thème 1 : Bases    #
########################

## Importations
import numpy as np
import math as m
import matplotlib.pyplot as plt

## I) Range
# exercice 1

# range(0,10) contient 10 termes.
# range(25) contient 25 termes.
# range(1,11) contient 11-1 = 10 termes.
# range(12,2,-1) contient 12-2 = 10 termes.

# exercice 2
R1 = range(11)
R2 = range(1,15)
R3 = range(41,41+57)
R4 = range(-21,-21+33)
R5 = range(85-73,85)
R6 = range(104-11,104)
R7 = range(0,45,2)
R8 = range(1,44,2)
R9 = range(12,94,3)
R10 = range(12,195,7)
R11 = range(18,0,-1)
R12 = range(18,1,-2)
R13 = range(18,5,-3)

#vérification grâce au script ci-dessous
for k in R10 :
    print(k)

## II) Fonctions
# exercice 3

def f1(x) :
    return x**2-2*x-1

def f2(x) :
    if x<0 :
        return x**2+1
    elif 0<=x and x<=2 :
        return x**3
    else :
        return np.log(x)

def f3(x) :
    return 1/(1+np.floor(x**2))

def f4(x) :
    return x**.5*np.log(x)

## III) Boucles

# exercice 4
# a) boucle for sans interruption :
#     on sait à l'avance la taille de la boucle
#     et tous les termes doivent être calculés
#
# b) boucle for sans interruption :
#     on veut une boucle de taille 10
#
# c) boucle while : on ne sait pas à l'avance
#     combien de termes il faudra lire.
#
# d) boucle for avec interruption :
#     on lit au maximum toute la liste, mais on
#     s'arrête dès qu'on trouve une terme négatif.
#
# e) comme d)
#
# f) boucle while : on ne sait pas combien
#     de tirages seront nécessaires.
#
# g) boucle for sans interruption : il faut lire toute
#     la liste pour connaître son maximum.


# exercice 5
# # 5a)
# n = 0
# while u[n] <= 100 :
#     n = n+1
# # 5b)
# n = 0
# while u[n] <= 100 and n <= 50 :
#     n = n+1
#
# #5c)
# n = 1001
# while u[n] <= 100 :
#     n = n+1
#
# #5d)
# while s <= t :
#
# #5e)
# while x<=y<=z or x>=y>=z :

## IV) Boucles

# exercice 6

def Sa(n) :
    S = 0
    for k in range(1,n+1) :
        S += k
    return S

def Sb(n) :
    S = 0
    for k in range(n+1) :
        S += 2**k
    return S

def Sc(n) :
    S = 0
    for k in range(2*n+1) :
        S += k*(k-1)/2
    return S

def Sd(n) :
    S = 0
    for k in range(n+1) :
        if k%2 == 1 :
            S += 1/k
        else :
            S += np.exp(-k)
    return S

# compléments : vérification de sommes de séries convergentes
# (voir TD math)

N = 150
def S1(N) :
    S = 0
    for k in range(1,N+1) :
        S += (-2)**k/3**k
    return S

print(S1(N))
print(-2/5)

x = 0.5
def S2(N,x) :
    S = 0
    for k in range(0,N+1) :
        S += k*(k+1)*x**(2*k)
    return S

print(S2(N,x))
print(2*x**2 / (1-x**2)**3)

def S3(N,x) :
    S = 0
    for k in range(0,N+1) :
        S += (k**2-k+1)*x**k / m.factorial(k)
    return S

print(S3(N,x))
print((1+x**2)*np.exp(x))

def S4(N) :
    S = 0
    for k in range(0,N+1) :
        S += 4/(2*k+1)/(2*k+3)
    return S

print(S4(N))
print(2)


## V) Suites

# exercice 7
def u(n) :
    u1 = 3
    for k in range(1,n) :
        u1 = (k*u1-1)/2
    return u1

# exercice 8
def liste_concatenation(n) :
    L = [0]
    for k in range(n) :
        un = L[-1]
        L += [np.log(2+un)]
    return L

def liste_append(n) :
    L = [0]
    for k in range(n) :
        un = np.log(2+L[-1])
        L.append(un)
    return L

# exercice 9
def Fibo(n) :
    f0,f1 = 1,1
    if n == 0 :
        return f0
    if n == 1 :
        return f1
    for k in range(n-1) :
        f2 = f0+f1  # f0,f1 = f1,f1+f2
        f0 = f1     # remplace ces 3 lignes
        f1 = f2     # (affectations simultanées)
    return f2

# exercice 10
def rang(M) :
    n = 1
    S = 1
    while M>=S :    # la terminaison de cette boucle 'while'
        n += 1      # est assurée par la divergence de la
        S += 1/n    # série harmonique : ses sommes partielles
    return n        # croissent strictement vers +infini.
print(rang(10))     # Vérification.

## VI) Listes

## VI)A Slicing

# exercice 11
n = 15
L = [k**2 for k in range(1,n+1)]
L1 = L[:3]
L2 = L[len(L)-5:]
L3 = L[1::2]
L4 = L[::2]
L5 = L[::3]
L6 = L[1::5]
M = L[:8] + [0] + L[8:]
R = L[::-1]

# exercice 12
def reverse(L) :
    R = []
    for elt in L :
        R = [elt] + R
    return R

def reverse2(L) :
    return L[::-1] # déjà fait

# exercice 13
def saute(L,p) :
    return L[p::2]

## VI)B Listes en compréhension

# exercice 14
E1 = [1/k for k in range(1,16)]
E2 = [2**k for k in range(1,16)]
E3 = [1/n/(n+1) for n in range(1,11)]
E4 = [abs(m.cos(n)-m.sin(n)) for n in range(1,11)]
I = [3,8,11]
E5 = [1/k for k in range(1,16) if k not in I]
E6 = [(1/k,2**k) for k in range(1,16)]
E7 = [(k,k**2,k**3,k**4) for k in range(2,6)]
E8 = [tuple([n for n in range(k,2*k)]) for k in range(1,6)]
E9 = [[i,j] for i in [-1,0,1] for j in [-1,0,1]]
E10 = [[i,j] for i in [-1,0,1] for j in [-1,0,1] if i!=0 or j!=0]

## VI)C Exercices complémentaires

# exercice 15
def diff(L) :
    return [L[i+1]-L[i] for i in range(len(L)-1)]

# exercice 16
def cumsum(L) :
    M = []
    s = 0
    for elt in L :
        s += elt
        M.append(s)
    return M

# exercice 17
def est_dans(L1,L2) :
    for elt in L2 :
        if elt not in L1 :
            return False
    return True

# exercice 18
def maxi(L) :
    if len(L) == 0 :
        return 'liste vide'
    M = L[0]
    for elt in L :
        if elt > M :
            M = elt
    return M

# exercice 19
def pos_max(L) :
    M = maxi(L)
    P = []
    for k in range(len(L)) :
        if L[k] == M :
            P.append(k)
    return P

# exercice 20
def sans_rep(L) :
    M = []
    for elt in L :
        if elt not in M :
            M.append(elt)
    return M

# exercice 21
def multiplicite(L) :
    M = []
    C = []
    for elt in L :
        absent = True
        for k in range(len(M)) :
            if elt == M[k] :
                C[k] += 1
                absent = False
        if absent :
            M.append(elt)
            C.append(1)
    return M,C

## VII Tracé de courbes

# exercice 22
##a
X = np.linspace(0,1,100)
Y = [x**2-x+1 for x in X]
plt.plot(X,Y)
plt.show()
##b
X = np.linspace(-1,1,100)
Y = [x*m.cos(2*m.pi*x) for x in X]
plt.plot(X,Y)
plt.show()
##c
X = np.linspace(1,2,100)
Y = [x**2-2 for x in X]
plt.plot(X,Y)
plt.show()
##d
X = np.linspace(0,1,100)
Y = [1/(1+x**2) for x in X]
plt.plot(X,Y)
plt.show()
##e
X = np.linspace(0,7,200)  # choix d'une borne b finie
Y = [x*m.exp(-x) for x in X]
plt.plot(X,Y)
plt.show()
##f
X = np.linspace(1,2,100)
Y = [m.log(1+x) for x in X]
plt.plot(X,Y)
plt.show()
##g
X = np.linspace(-5,5,100) # choix des bornes a,b
Y = [m.atan(x) for x in X]
plt.plot(X,Y)
plt.show()
##h
X = np.linspace(0.1,1,100) # choix d'une borne a
Y = [m.log(x)/x for x in X]
plt.plot(X,Y)
plt.show()
