import numpy as np
from math import sqrt

## script 1


def dist(x1, x2):
    s = 0
    for k in range(len(x1)):
        s = s + (x1[k] - x2[k])**2
    return sqrt(s)


#test
print(dist([1, 2, 4], [0, 0, 1]))  # racine de 14 ok


## on execute le fichier donnees.py

print(joueuses)

print(performances)


########################
#### Note de performance

## script 2

def distances_au_point(joueuses, x):
    return [dist(y, x) for y in joueuses]


#test
nouvelle = [25, 39, 70]  # ;-)
print(distances_au_point(joueuses, nouvelle))


## question 3

def trier(distances):
    n = len(distances)

    def f(i):
        return distances[i]
    return sorted(range(n), key=f)


print(trier([5, 12, 30, 2, 15]))


## script 4 (facultatif)

def k_proche(L, k):
    """
    renvoie la liste des indices des k plus petits éléments de L.
    basé sur tri par sélection.
    """
    n = len(L)
    indices = [x for x in range(n)]
    for i in range(min(k, n)):
        i_mini = i
        for j in range(i + 1, n):
            if L[indices[j]] < L[indices[i_mini]]:
                i_mini = j
        indices[i], indices[i_mini] = indices[i_mini], indices[i]
    return indices[:k]


def k_proche_q(L, k):
    """
    renvoie la liste des indices des k plus petits éléments de L.
    basé sur tri par rapide.
    """
    def partition(indices):
        G = []
        D = []
        for i in range(1, len(indices)):
            if L[indices[i]] <= L[indices[0]]:
                G.append(indices[i])
            else:
                D.append(indices[i])
        return G, indices[0], D

    def inter(indices, k):
        G, x, D = partition(indices)
        if len(G) < k-1:
            return G + [x] + inter(D, k - len(G) - 1)
        elif len(G) == k - 1:
            return G + [x]
        elif len(G) == k:
            return G
        else:
            return inter(G, k)

    indices = [x for x in range(len(L))]
    return inter(indices, k)


print(trier([5, 12, 30, 2, 15]))
print(k_proche([5, 12, 30, 2, 15], 5))  # ok mm resultat
print(k_proche([5, 12, 30, 2, 15], 3))


## script 5

def moyenne_des_k_voisins(indices_tries, performances, k):
    s = 0
    for j in range(k):
        s = s + performances[indices_tries[j]]
    return s / k


## script 6

def note_nouvelle(joueuses, performances, x, k):
    distances = distances_au_point(joueuses, x)
    indices_tries = k_proche(distances, k)
    return moyenne_des_k_voisins(indices_tries, performances, k)


nouvelle = [50, 60, 65]
print(note_nouvelle(joueuses, performances, nouvelle, 3))




#############################
## Groupes de performances


## script 7

import random as rd


def initialisation_poles(saison2, k):
    p = len(saison2[0])  # nombre de compétences
    mini = [min(saison2[:, j]) for j in range(p)]
    maxi = [max(saison2[:, j]) for j in range(p)]
    L_poles = []
    for i in range(k):
        pole = []
        for j in range(p):
            pole.append(rd.uniform(mini[j], maxi[j]))
        L_poles.append(pole)
    return np.array(L_poles)






# test
poles = initialisation_poles(saison2, 3)
print(poles)


## script 8

def pole_le_plus_proche(poles, x):
    distances = distances_au_point(poles, x)
    return k_proche(distances, 1)[0]


#test
print(pole_le_plus_proche(poles, [30, 61, 75]))


## script 9


def poles_les_plus_proches(poles, saison2):
    return [pole_le_plus_proche(poles, x) for x in saison2]



#test
repartition = poles_les_plus_proches(poles, saison2)
print(repartition)


## script 10


def nouveaux_poles(saison2, repartition, k):
    n_poles = []
    n = len(saison2)  # nb de joueuses
    p = len(saison2[0])  # nb de compétences
    for j in range(k):
        S = [0 for m in range(p)]
        compt = 0
        for i in range(n):
            if repartition[i] == j:  # moyenne pour le pole j
                for m in range(p):
                    S[m] = S[m] + saison2[i][m]
                compt = compt + 1
        if compt != 0:
            for m in range(p):
                S[m] = S[m] / compt
        n_poles.append(S)
    return np.array(n_poles)


def nouveaux_poles_V2(saison2, repartition, k):
    n = len(saison2)  # nb de joueuses
    p = len(saison2[0])  # nb de compétences
    S = [[0 for m in range(p)] for j in range(k)]
    compt = [0 for j in range(k)]
    for i in range(n):
        j = repartition[i]
        for m in range(p):
            S[j][m] = S[j][m] + saison2[i][m]
        compt[j] = compt[j] + 1
    for j in range(k):
        if compt[j] != 0:
            for m in range(p):
                S[j][m] = S[j][m] / compt[j]
    return np.array(S)





#test
print(nouveaux_poles(saison2, repartition, 3))
print(nouveaux_poles_V2(saison2, repartition, 3))

## script 11


def k_moyennes(saison2, k):
    poles = initialisation_poles(saison2, k)
    repartition = poles_les_plus_proches(poles, saison2)
    newpoles = nouveaux_poles(saison2, repartition, k)
    while not (poles == newpoles).all():
        poles = newpoles
        repartition = poles_les_plus_proches(poles, saison2)
        newpoles = nouveaux_poles(saison2, repartition, k)
    return poles


def k_moyennes_V2(saison2, k):
    poles = initialisation_poles(saison2, k)
    while True:  # attention ça risque de ne pas passer au concours !
        repartition = poles_les_plus_proches(poles, saison2)
        newpoles = nouveaux_poles(saison2, repartition, k)
        if (poles == newpoles).all():
            return poles
        poles = newpoles


#test
polesdefinitifs = k_moyennes_V2(saison2, 3)
print(polesdefinitifs)

## question 12
# illustration avec la fct affiche présnte dans le fichier donnees.py

'''la nouvelle categorie de chaque joueuse X est donnée par
pole_le_plus_proche(polesdefinitifs, X)
essayons de représenter les nouvelles catégories'''

affiche(saison2, polesdefinitifs, 3)

'''c est normal de ne pas avoir un résultat identiques,
puisque le point de départ de l'algorithme est aléatoire.
De plus on sait d'après le cours que l'algorithme
 s'arrête lorsqu'on a trouvé un minimum local pour une certaine fonction
(liée à un calcul de distance catégorisées).

Le coach est-il content?
Sur les illustrations, on voit bien le cloissonnement des joueuses ,
c'est  ce qu'il demandait. Cependant, une demande implicite du coach
pourrait être de créer des catégories homogènes en nombre de joueuses
et dans ce cas notre résultat est critiquable'''


## question 13

def k_moyennes_V3(saison2, k):
    poles = initialisation_poles(saison2, k)
    compt = 0
    while True:  # attention ça risque de ne pas passer au concours !
        compt += 1
        repartition = poles_les_plus_proches(poles, saison2)
        newpoles = nouveaux_poles(saison2, repartition, k)
        if (poles == newpoles).all():
            return compt
        poles = newpoles


for j in range(10):
    print(k_moyennes_V3(saison2, 3))
