#### TP n°9: Programmation avancée sur les listes ####
#################


### RAPPELS SUR QUELQUES OPÉRATIONS SUR LES LISTES ET LES CHAÎNES DE CARACTÈRES ###






















### EXERCICES SIMPLES SUR LES LISTES DÉFINIES PAR COMPRÉHENSION ###
## Question 1 
L=[2*n**2+3*n+5 for n in range(50)]
print(L)
## Question 2 
notes=[12.0,6.2,10.1,8.9,14.2,12.0,10.0,6.7,8.9,13.4]
print([note+1 for note in notes])
print([(i,notes[i]) for i in range(len(notes))])
import math
print([20*math.log10(note) for note in notes])
print([note for note in notes if note>=10])
print(["bon" if note>=10 else "mauvais" for note in notes])
## Question 3 
u=[(247*3**n)%17 for n in range(100)]
print(u)
print([un%2 for un in u])
print([un for un in u if un%2==0])
print(len([un for un in u if un==1]))
## Question 4 
phrase="Le lycée Fauriel abrite la meilleure prépa de France."
print([c for c in phrase])
print(len([c for c in phrase if c=="a"]))
print(len([c for c in phrase if c>="A" and c<="Z"]))
print("".join([c+c for c in phrase]))
## Question 5 
def affiche_matrice(M:[[float]]):
    print("\n".join([("[ " if i==0 else "  ")+(" ".join([str(v) for v in M[i]])) for i in range(len(M))])+" ]")
N=5
M1=[[1 for j in range(N)] for i in range(N)]
affiche_matrice(M1)
M2=[[1 if j==i else 0 for j in range(N)] for i in range(N)]
affiche_matrice(M2)
M3=[[i-j for j in range(N)] for i in range(N)]
affiche_matrice(M3)
## Question 6 
import math
def f(x):
    return math.cos(x)/(1+math.exp(x))
Lx=[k*4*math.pi/1000 for k in range(1001)]
Ly=[f(x) for x in Lx]
import matplotlib.pyplot as plt
plt.figure()
plt.plot(Lx,Ly)
plt.show()
### UTILISATION DES TRANCHES ###
## Question 7 
def enleve_caractere(chaine:str,indice:int)->str:
    return chaine[:indice]+chaine[indice+1:]
print(enleve_caractere("Allez les Verts",3))
## Question 8 
def periode(L:list)->int:
    for p in range(2,len(L)//2+1):
        if L[0:p]==L[p:2*p]:
            return p
    return -1        
print(periode([1,2,3,1,2,3,1,2,3,1,2,3,1,2]))
print(periode([1,2,3,1,2,4,1,2,3,1,2,4,1,2]))
print(periode([1,2,3,1,2,4,1,2,5,1,2,6,1,2]))
## Question 9 : Recherche d'un motif dans une chaîne
def indice_motif(chaine:str,motif:str)->int:
    n=len(motif)
    for i in range(len(chaine)-n+1):
        if chaine[i:i+n]==motif:
            return i
    return -1
## Test de la fonction `indice_motif`; **à exécuter sans éditer**
ok=True
if indice_motif("Voici une phrase de test","test")!=20:
    ok=False
if indice_motif("Voici une phrase de test","tast")!=-1:
    ok=False
if indice_motif("Voici une phrase de test","Voi")!=0:
    ok=False
if ok:
    print("Fonction indice_motif: test réussi!")
else:
    print("Fonction indice_motif: test échoué!")
## Question 10 
def indices_motif(chaine:str,motif:str)->int:
    n=len(motif)
    return [i for i in range(len(chaine)-n+1) if chaine[i:i+n]==motif]
print(indices_motif("C'est une phrase de test","est"))
print(indices_motif("C'est une phrase de test","une"))
print(indices_motif("C'est une phrase de test","pcsi"))
## Question 11 : Remplacement d'un motif dans une chaîne
def remplace_motif(chaine:str,motif:str,motif2:str)->str:
    n=len(motif)
    i=0
    while i<len(chaine)-n+1:
        if chaine[i:i+n]==motif:
            chaine=chaine[:i]+motif2+chaine[i+n:]
        i=i+1
    return chaine
print(remplace_motif("Le printemps est là, il est temps de planter les petits pois.","temps","schtroumph"))
## Test de la fonction `remplace_motif`; **à exécuter sans éditer**
ok=True
if remplace_motif("Voici une phrase de test","test","truc")!="Voici une phrase de truc":
    ok=False
if remplace_motif("Voici une phrase de test","e","yz")!="Voici unyz phrasyz dyz tyzst":
    ok=False
if remplace_motif("Voici une phrase de test","poule","canard")!="Voici une phrase de test":
    ok=False
if ok:
    print("Fonction remplace_motif: test réussi!")
else:
    print("Fonction remplace_motif: test échoué!")
## Question 12 
def inverse_mots(phrase:str)->str:
    liste_espaces=indices_motif(phrase," ")
    liste_debuts_mots=[0]+[n+1 for n in liste_espaces]
    liste_fins_mots=[n-1 for n in liste_espaces]+[len(phrase)-1]
    return " ".join([phrase[liste_debuts_mots[i]:liste_fins_mots[i]+1] for i in range(len(liste_fins_mots))][::-1])
print(inverse_mots("Allez les Verts"))
### FONCTIONS DIVERSES ###

## Question 13 : Miroir d'une liste
def miroir(L:list)->list:
    return [L[len(L)-1-i] for i in range(len(L))]
print(miroir([1,2,3,4]))
## Question 14 : Codage de César
def cesar(texte:str,decalage:int) -> str:
    codes=[ord(c)-97 for c in texte]
    codes_decales=[(n+decalage)%26 for n in codes]
    return "".join([chr(n+97) for n in codes_decales])
print(cesar("allezlesverts",4))
print(cesar(cesar("allezlesverts",4),-4))
## Question 15 : Application d'une fonction à une liste
import math
def applique(f:callable,L:list) -> list:
    return [f(x) for x in L]
liste=[1,2,3,4,5]
print(applique(math.exp,liste))
def f(x):
    return 1/(1+math.exp(x))
print(applique(f,liste))
## Question 16 : Filtrage d'une liste
def est_positif(x:float)->bool:
    return x>=0
L=[1,2,4,5,6,8,5,3,1,-1,-3,-2,0,3]
def filtre(f:callable,L:liste)->list:
    return [x for x in L if f(x)]
def est_mult3(x:float)->bool:
    return x%3==0
print(filtre(est_positif,L))
print(filtre(est_mult3,L))
### DÉCOMPOSITION DANS UNE BASE ###
## Question 17 
import math
def decompose_base_10(n:int)->[int]:
    l=int(math.log10(n))+1
    return [(n//10**(l-k-1))%10 for k in range(l)]
print(decompose_base_10(10223))
## Question 18 
import math
def decompose_base_b(n:int,b:int)->[int]:
    l=int(math.log(n)/math.log(b))+1
    return [(n//b**(l-k-1))%b for k in range(l)]
print(decompose_base_b(60,2))
## Question 19 : Tapis de Sierpinski
def un_en_commun(a:int,b:int)->bool:
    if a==0 or b==0:
        return False
    a3=decompose_base_b(a,3)
    b3=decompose_base_b(b,3)
    for i in range(min(len(a3),len(b3))):
        if a3[-i-1]==1 and b3[-i-1]==1:
            return True
    return False

def carre_sierpinski(n:int) -> [[str]]:
    dim=3**n
    return [[" " if un_en_commun(i,j) else "x" for j in range(dim)] for i in range(dim)]

affiche_matrice(carre_sierpinski(1))
affiche_matrice(carre_sierpinski(2))
affiche_matrice(carre_sierpinski(3))
### PRODUIT DE MATRICES ###
## Question 20 
def produit_scalaire(V1:[float],V2:[float])-> float:
    assert len(V1)==len(V2), "les deux vecteurs ne sont pas de même longueur"
    return sum([V1[i]*V2[i] for i in range(len(V1))])
print(produit_scalaire([1,2,3],[2,2,2]))
## Question 21 
def produit_matrices(M1:[[float]],M2:[[float]])-> [[float]]:
    # récupération dans n1 et p1 des dimensions de M1
    n1,p1=len(M1),len(M1[0])
    # récupération dans n2 et p2 des dimensions de M2
    n2,p2=len(M2),len(M2[0])
    # vérification avec un assert que les dimensions sont compatibles
    assert p1==n2, "les dimensions des matrices ne sont pas compatibles"
    # création et renvoi en UNE SEULE LIGNE de la matrice produit 
    return [[sum([M1[i][k]*M2[k][j] for k in range(p1)]) for j in range(p2)] for i in range(n1)]
M1=[[1,2],[2,3],[3,4]]
M2=[[1,2],[2,2]]
print(produit_matrices(M1,M2)) # doit renvoyer [[5,6],[8,10],[11,14]]
### OPÉRATIONS ENSEMBLISTES ###

## Question 22 
E1=[1,2,4,5,6,9,8,-1]
E2=[0,1,2,3]
def intersection(E1:list,E2:list)->list:
    return [x for x in E1 if x in E2]
print(intersection(E1,E2))
## Question 23 
E1=[1,2,4,5,6,9,8,-1]
E2=[0,1,2,3]
def union(E1:list,E2:list)->list:
    return E1+[x for x in E2 if x not in E1]
print(union(E1,E2))
## Question 24 
E1=[1,2,4,5,6,9,8,-1]
E2=[0,1,2,3]
def difference(E1:list,E2:list)->list:
    return [x for x in E1 if x not in E2]
print(difference(E1,E2))
