# -*- coding:utf-8 -*-

__projet__ = "correction_examen_semestre_6_AF1"
__nom_fichier__ = "chiffres_romains"
__author__ = "Christine Fay-Varnier"
__date__ = "avril 2025"

from time import time


def creeDicos(lsymboles):
    """

    @param lsymbols:    liste de couples (valeur, symbole romain) lsymbols = [[1000, "M"], [500, "D"], [100, "C"], [50, "L"], [10, "X"], [5, "V"], [1, "I"]]
    @return:            le dictionnaire associant symbole romain et valeur
                        le dictionnaire associant valeur et symbole romain
    """

    dicoRomain = {}
    dicoDecimal = {}

    for valeur, symb in lsymboles:
        dicoRomain[symb] = valeur
        dicoDecimal[valeur] = symb

    return dicoRomain, dicoDecimal

def creeDicos_V2(lsymboles):
    """

    @param lsymbols:    liste de couples (valeur, symbole romain) lsymbols = [[1000, "M"], [500, "D"], [100, "C"], [50, "L"], [10, "X"], [5, "V"], [1, "I"]]
    @return:            le dictionnaire associant symbole romain et valeur
                        le dictionnaire associant valeur et symbole romain
    """
    # version pas forcément plus efficace que la version précédente
    # car dans la version précédente on ne fait qu'une itération sur la liste des symboles
    dicoDecimal = dict(lsymboles)
    dicoRomain = {}

    for valeur, symb in lsymboles:
        dicoRomain[symb] = valeur

    return dicoRomain, dicoDecimal

def romain2dec(romain, dicoRomain):
    """

    @param romain:      nombre romain ex : CCXII
    @param dicoRomain:  dictionnaire associant nombre romain, valeur
    @return:            valeur en décimal du nombre romain
    """
    # on construit la liste des valeurs des chiffres romains
    lvaleurs = [dicoRomain[symb] for symb in romain]
    # ou de façon classique :
    # lvaleurs = []
    # for symb in romain:
    #     lvaleurs.append(dicoRomain[symb])

    decimal = 0
    for i in range(len(lvaleurs)-1):
        # on parcourt cette liste en enlevant la dernière valeur pour éviter un indice out of range
        if lvaleurs[i] < lvaleurs[i+1]:
            decimal -= lvaleurs[i]
        else:
            decimal += lvaleurs[i]

    # on ajoute le dernier élément qui n'a pas été pris en compte dans l'itération précédente
    decimal += lvaleurs[-1]

    return decimal

def romain2dec_V2(romain, dicoRomain):
    """

    @param romain:      nombre romain ex : CCXII
    @param dicoRomain:  dictionnaire associant nombre romain et valeur
    @return:            valeur en décimal du nombre romain
    """
    # solution en une seule itération.
    # un peu plus efficace si on stocke kes valeurs dans les variables vali et valii
    decimal = 0
    for i in range(len(romain)-1):
        vali = dicoRomain[romain[i]]
        valii = dicoRomain[romain[i+1]]
        if vali < valii:
            # on a une valeur plus petite avant une valeur plus grande
            # en ce cas, on la soustrait
            decimal -= vali
        else:
            decimal += vali

    # on ajoute le dernire élément qui n'a pas été pris en compte dans l'itération précédente
    decimal += dicoRomain[romain[-1]]

    return decimal

def decomposition(nbre, dicoDecimal):
    """

    @param nbre:        nombre entier ex : 523
    @param dicoDecimal: dictionnaire qui associe à une valeur décimale le symbol romain qui lui correspond
    @return:            la décomposition de nbre sous forme d'une liste de couples [nb_repetitions, valeur] dans l'ordre décroissant des valeurs
                        ex : [(2, 100), (1, 50), (2, 10), (1, 5), (1, 1)]
    """

    decomp = []
    for val in dicoDecimal:
        repet = nbre // val # on récupère le nombre de fois où est répétré la valeur val dans le nombre
        if repet != 0:  # on peut faire le test if nbre >= val
            decomp.append((repet, val)) # on ajoute la décomposition correspondante
            nbre %= val                 # on met à jour le nombre qu'il rest à décomposer

    return decomp

def dec2romain(nbre, dicoDecimal):
    """

    @param nbre:        nombre sous format décimal
    @param dicoDecimal: dictionnaire associant à une valeur décimale le symbole romain associé
    @return:            la conversion de nombre en chiffres romains
    """
    # recherche de la decomposition (rep, val) avec les valeurs pouvant être représentées par les chiffres romains
    decomp = decomposition(nbre, dicoDecimal)

    # transformation en nombre romain
    romain = ""
    for repet, val in decomp:
        # repet nombre de répétition de la valeur val dans la décomposition
        romain += dicoDecimal[val] * repet
        # on peut écrire aussi
        # for _ in range(repet): romain += dicoDecimal[val]
        # mais c'est moins efficace

    return romain

if __name__ == '__main__':
    # on teste les différentes fonctinos écrites les unes après les autres
    lsymbols = [[1000, "M"], [500, "D"], [100, "C"], [50, "L"], [10, "X"], [5, "V"], [1, "I"]]

    dicoRomain, dicoDecimal = creeDicos(lsymbols)
    # print(dicoRomain)
    # print(dicoDecimal)
    #
    # dicoRomain, dicoDecimal = creeDicos_V2(lsymbols)
    # print(dicoRomain)
    # print(dicoDecimal)

    # romain = 'CLXXII'
    # decimal = romain2dec(romain, dicoRomain)
    # print(decimal)
    #
    # romain = 'XLIII'
    # decimal = romain2dec(romain, dicoRomain)
    # print(decimal)

    # romain = 'DCXIV'
    # decimal = romain2dec(romain, dicoRomain)
    # print(decimal)
    #
    # decimal = romain2dec_V2(romain, dicoRomain)
    # print(decimal)

    # print(dec2romain(172, dicoDecimal))
    # print(dec2romain(43, dicoDecimal))