import random
import math
import os
import sys
from tkinter import *

ocaml = False
camllight = False

if ocaml or camllight:
    cmt = "(*"
    fincmt = "*)"
else:
    cmt = "#"
    fincmt = ""

def enregistrer(mv,chemin):
    a = f'{mv}'
    b = ""
    for c in a:
        match c:
            case "(" : ()
            case ")" : ()
            case ":" : ()
            case "," : ()
            case " " : ()
            case "{" : ()
            case "}" : ()
            case _ : b += c
    with open(chemin,'w',encoding="utf-8") as f:
        f.write(b)

def lire_mouvement(chemin):
    def int_vect(s):
        b = []
        d = False
        for c in s:
            if c == "-":
                d = True
            else:
                j = int(c)
                if d :
                    j = -j
                    d = False
                b.append(j)
        return tuple(b)
    str = ""
    with open(chemin,'r',encoding="utf-8") as f:
        str = f.read()
    mv = dict()
    v = int_vect(str)
    for i in range(1 + (len(v) - 12)//12):
        ind = []
        for j in range(3):
            ind.append(v[i*12+j])
            mat = [[1,0,0],[0,1,0],[0,0,1]]
            for j in range(3):
                for k in range(3):
                    mat[j][k] = v[i * 12 + 3 + j * 3 + k]
        mv[tuple(ind)] = tuple(map(tuple,mat))
    return mv

groupe_du_cube = (
    ((1, 0, 0), (0, 1, 0), (0, 0, 1)),
    ((1, 0, 0), (0, -1, 0), (0, 0, -1)),
    ((-1, 0, 0), (0, 1, 0), (0, 0, -1)),
    ((-1, 0, 0), (0, -1, 0), (0, 0, 1)),
    ((1, 0, 0), (0, 0, -1), (0, 1, 0)),
    ((0, 0, 1), (0, 1, 0), (-1, 0, 0)),
    ((0, -1, 0), (1, 0, 0), (0, 0, 1)),
    ((1, 0, 0), (0, 0, 1), (0, -1, 0)),
    ((0, 0, -1), (0, 1, 0), (1, 0, 0)),
    ((0, 1, 0), (-1, 0, 0), (0, 0, 1)),
    ((0, -1, 0), (-1, 0, 0), (0, 0, -1)),
    ((0, 1, 0), (1, 0, 0), (0, 0, -1)),
    ((-1, 0, 0), (0, 0, -1), (0, -1, 0)),
    ((0, 0, -1), (0, -1, 0), (-1, 0, 0)),
    ((0, 0, 1), (0, -1, 0), (1, 0, 0)),
    ((-1, 0, 0), (0, 0, 1), (0, 1, 0)),
    ((0, 1, 0), (0, 0, 1), (1, 0, 0)),
    ((0, 0, -1), (-1, 0, 0), (0, 1, 0)),
    ((0, 0, 1), (-1, 0, 0), (0, -1, 0)),
    ((0, 1, 0), (0, 0, -1), (-1, 0, 0)),
    ((0, 0, -1), (1, 0, 0), (0, -1, 0)),
    ((0, -1, 0), (0, 0, -1), (1, 0, 0)),
    ((0, -1, 0), (0, 0, 1), (-1, 0, 0)),
    ((0, 0, 1), (1, 0, 0), (0, 1, 0))
)

# décompositions en quarts de tour autour des axes des inverses des éléments du groupe du cube
decompositions = ("", "a0a0", "d0d0", "h0h0", "a0'", "d0'", "h0'", "a0", "d0", "h0",
            "h0'a0a0", "h0a0a0", "d0d0a0", "d0a0a0", "d0'a0a0", "d0d0a0'", "h0a0",
            "d0a0'", "d0'a0", "h0a0'", "d0a0", "h0'a0'", "h0'a0", "d0'a0'")

# dictionnaire de ces décompositions 
dictionnaire = dict(zip(groupe_du_cube, decompositions))

# décomposition en quarts de tour autour des axes de l'inverse de l'élément m du groupe du cube
def decomposition(m):
    try:
        return dictionnaire[m]
    except KeyError: return ""

indices = ((-1, -1, -1), (0, -1, -1), (1, -1, -1), (-1, 0, -1), (0, 0, -1),
               (1, 0, -1), (-1, 1, -1), (0, 1, -1), (1, 1, -1), (-1, -1, 0),
               (0, -1, 0), (1, -1, 0), (-1, 0, 0), (1, 0, 0), (-1, 1, 0),
               (0, 1, 0), (1, 1, 0), (-1, -1, 1), (0, -1, 1), (1, -1, 1),
               (-1, 0, 1), (0, 0, 1), (1, 0, 1), (-1, 1, 1), (0, 1, 1),
               (1, 1, 1))
               
def produit_scalaire(a,b):
    return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]
               
def est_centre(v):
    return produit_scalaire(v,v) == 1
def est_angle(v):
    return produit_scalaire(v,v) == 2
def est_coin(v):
    return produit_scalaire(v,v) == 3

centres = tuple((v for v in indices if est_centre(v)))
angles = tuple((v for v in indices if est_angle(v))) 
coins = tuple((v for v in indices if est_coin(v)))

# signature d'une permutation p des indices
def sign(indices, p):
    def N(v):
        return v[0] + v[1] * 3 + v[2] * 9
    s = 1
    for i in range(len(indices)):
        for j in range(i+1, len(indices)):
            ii = N(indices[i])
            jj = N(indices[j])
            pii = N(p(indices[i]))
            pjj = N(p(indices[j]))
            if ii < jj and pii > pjj or ii > jj and pii < pjj :
                s *= -1
    return s

# permutation aléatoire d'un uplet a d'indices
def randomize(a):
    a1 = list(a)
    random.shuffle(a1)
    return a1

def matrice_scalaire(a):
    return ((a,0,0),(0,a,0),(0,0,a))

def rot (v):
    if v == (1,0,0): return ((1,0,0),(0,0,-1),(0,1,0))
    if v == (0,1,0): return ((0,0,1),(0,1,0),(-1,0,0))
    if v == (0,0,1): return ((0,-1,0),(1,0,0),(0,0,1))
    if v == (-1,0,0): return ((1,0,0),(0,0,1),(0,-1,0))
    if v == (0,-1,0): return ((0,0,-1),(0,1,0),(1,0,0))
    if v == (0,0,-1): return ((0,1,0),(-1,0,0),(0,0,1))
    return matrice_scalaire(1)

def transpose(m):
     return ((m[0][0],m[1][0],m[2][0]),(m[0][1],m[1][1],m[2][1]),(m[0][2],m[1][2],m[2][2]))

def roti(v):
    return transpose(rot(v))

def produit_vecteur_matrice(v,m):
    t = transpose(m)
    return (produit_scalaire(v,t[0]),produit_scalaire(v,t[1]),produit_scalaire(v,t[2]))

def produit_matrices(m1,m2):
    return (produit_vecteur_matrice(m1[0],m2),produit_vecteur_matrice(m1[1],m2),produit_vecteur_matrice(m1[2],m2))

#-----------------------------------------------------------------------------
# GROUPE DES MOUVEMENTS
#-----------------------------------------------------------------------------

def produit_mouvements(mv1,mv2):
    t = dict()
    for i in indices:
        t[i] = produit_matrices(mv1[i],mv2[produit_vecteur_matrice(i,mv1[i])])
    return t

def inverse(mv):
    t = dict()
    for i in indices:
        t[produit_vecteur_matrice(i,mv[i])] = transpose(mv[i])
    return t

def cst(m):
    t = dict()
    for i in indices:
        t[i] = m
    return t

e = cst(matrice_scalaire(1))

def mv1_of_fun(f):
    t = dict()
    for i in indices:
        t[i] = f(i)
    return t

def fun_of_mv1(mv):
    return lambda x: mv[x]

def rub(v):
    t = dict()
    for i in indices:
        if produit_scalaire(i,v) == 1:
            t[i] = rot(v)
        else:
            t[i] = matrice_scalaire(1)
    return t

def rubi(v):
    return inverse(rub(v))

def marque(x):
    if est_coin(x): return (0,0,x[2])
    if est_angle(x):
        (a,b,c) = x
        match (a,b,c):
            case (0,_,_): return (0,b,0)
            case (_,0,_): return (0,0,c)
            case (_,_,0): return (a,0,0)
    return(0,0,0)

def gg(i,j):
    for m in groupe_du_cube:
        if produit_vecteur_matrice(i,m) == j and produit_vecteur_matrice(marque(i),m) == marque(j):
            return m
    return matrice_scalaire(1)

def sur(mv):
    return lambda i: produit_vecteur_matrice(i,mv[i])

def sec(p):
    return mv1_of_fun (lambda i: gg(i,p(i)))

def ker(mv):
    return produit_mouvements(mv,inverse(sec(sur(mv))))

# stabilisateur du coin ou de l'angle i
def st(i):
    def stc(i):
        k = (1,1,1)
        m = ((0,0,1),(1,0,0),(0,1,0))
        return produit_matrices(gg(i,k),produit_matrices(m,gg(k,i)))
    def sta(i):
        k = (1,0,1)
        m = ((0,0,1),(0,-1,0),(1,0,0))
        return produit_matrices(gg(i,k),produit_matrices(m,gg(k,i)))
    if est_angle(i): return sta(i)
    if est_coin(i): return stc(i)
    return matrice_scalaire(1) #erreur dans st

# rotation totale des angles
def rta(mv):
    s = 0
    k = ker(mv)
    for i in angles:
        if k[i] != st(i): s += 1
    return s % 2

# rotation totale des coins
def rtc(mv):
    s = 0
    k = ker(mv)
    for i in coins:
        if k[i] == st(i): s += 1
        elif k[i] == transpose(st(i)): s += 2
    return s % 3

# test d'appartenance d'un mouvement au sous-groupe R des mouvements de Rubik
def est_dans_R(mv):
    p = sur(mv)
    return sign(angles,p) == sign(coins,p) and rtc(mv) == 0 and rta(mv) == 0

# mouvement défini par des permutations des angles et des coins
# et un exposant pour chaque angle, chaque coin et chaque centre
def nouveau_mv1(pa,pc,ea,ec,em):
    def aux_k(i):
        if est_angle(i):
            if ea(i) == 0: return matrice_scalaire(1)
            return st(i)
        if est_coin(i):
            match ec(i):
                case 0: return matrice_scalaire(1)
                case 1: return st(i)
                case _: return transpose(st(i))
        if est_centre(i):
            match em(i):
                case 0: return matrice_scalaire(1)
                case 1: return rot(i)
                case 2: return produit_matrices(rot(i),rot(i))
                case _: return roti(i)
    k = mv1_of_fun(aux_k)
    
    def aux_l(i):
        if est_angle(i): return gg(i,pa(i))
        if est_coin(i): return gg(i,pc(i))
        return matrice_scalaire(1)    
    l = mv1_of_fun(aux_l)
    
    return produit_mouvements(k,l)

# nombre de quarts de tours du mouvement mv autour de la face orthogonale à v
def nqt(mv,v):
    r = fun_of_mv1(mv)(v)
    if r == matrice_scalaire(1): return 0
    if r == rot(v): return 1
    if r == produit_matrices(rot(v),rot(v)): return 2
    if r == roti(v): return 3
    return 0 #erreur dans nqt

# nombre total de quarts de tour des faces dans le mouvement mv
def nqt_total(mv):
    s = 0
    for i in centres:
        s += nqt(mv,i)
    return s % 4

# rotation horaire d'un quart de tour r du centre orthogonal à v
def ajuste_centre(mv1, r, v):
    def aux(x):
        if x == v:
            return produit_matrices(fun_of_mv1(mv1)(v),r(v))
        else: 
            return fun_of_mv1(mv1)(x)
    return mv1_of_fun(aux)

# mouvement aléatoire
def mv1_r():
    # exposant aléatoire pour les angles
    def ea_r(i):
        return random.randrange(0,2)
    # exposant aléatoire pour les coins
    def ec_r(i):
        return random.randrange(0,3)
    # exposant aléatoire pour les milieux
    def em_r(i):
        return random.randrange(0,4)
    # permutation aléatoire des angles
    def pa_r():
        angles1 = randomize(angles)
        a = angles
        def perm(i):
            for j in range(len(a)):
                if a[j] == i :
                    return angles1[j]
        return perm
    # permutation aléatoire des coins
    def pc_r():
        coins1 = randomize(coins)
        a = coins
        def perm(i):
            for j in range(len(a)):
                if a[j] == i :
                    return coins1[j]
        return perm

    return nouveau_mv1(pa_r(),pc_r(),ea_r,ec_r,em_r)

# mouvement de rubik aléatoire
def mv1_rubik_r():
    def rot_angle(i,n):
        return nouveau_mv1((lambda x: x),(lambda x: x),(lambda x: (n if x == i else 0)),(lambda x: 0),(lambda x: 0))
    def rot_coin(i,n):
        return nouveau_mv1((lambda x: x),(lambda x: x),(lambda x: 0),(lambda x: (n if x == i else 0)),(lambda x: 0))
    m = mv1_r()
    if rta(m) != 0: m = produit_mouvements(m,rot_angle((0,1,1),1))
    if rtc(m) != 0: m = produit_mouvements(m,rot_coin((1,1,1), (3 - rtc(m))))
    p = sur(m)
    if sign(angles, p) != sign(coins, p):
        def tr(i,j):
            def aux(k):
                if k == i: return j
                if k == j: return i
                return k
            return sec(aux)
        m = produit_mouvements(m,tr((0,1,1),(1,0,1)))
    sgnc = sign(coins,sur(m))
    n = 0
    for k in centres: n += nqt(m,k)
    n %= 2
    if (sgnc == 1 and n != 0) or (sgnc == -1 and n == 0):
        def f(x):
            ff = fun_of_mv1(m)
            if x == (1,0,0): return produit_matrices(ff(x),rot(x))
            return ff(x)
        m = mv1_of_fun(f)
    return m

# exécution d'une liste de mouvements
def exe1(l):
    for x in l: x()

# liste des noms d'opérations se succédant du début à la fin d'une chaîne de caractères 's'
def op_names_from_string(s):
    if s == "": return []
    def tete(s):
        t = []
        for c in s: t.append(c)
        l = len(t)
        if l == 0: return []
        if l == 1: return [t[0]]
        if l == 2:
            if t[1] == "0" or t[1] == "'": return t
            return [t[0]]
        match (t[1], t[2]):
            case ["0", "'"]: return t[0:3]
            case ("0", _): return t[0:2]
            case ("'", _): return t[0:2]
            case _: return [t[0]]

    def scinde(s):
        t1 = []
        for c in s: t1.append(c)
        t = tete(s)
        r = t1[len(t):len(t1)]
        return ("".join(t), "".join(r))
 
    (t,r) = scinde(s)
    if r == "":  return [t]
    return [t] + op_names_from_string(r)

# formatage pour caml-light et ocaml
def format_string(op_names_string):
    if ocaml or camllight:
        return "exec " + '"' +op_names_string + '";;\r'
    return "exec1(" + '"' +op_names_string + '")\r'

# mouvements de rubik des faces du cube
def rotations_faces():
    def fct(x): # sens horaire
        global mouvement
        t = produit_vecteur_matrice(x,transpose(context))
        mouvement = produit_mouvements(mouvement,rub(t))
    def fcti(x): # sens inverse
        global mouvement
        t = produit_vecteur_matrice(x,transpose(context))
        mouvement = produit_mouvements(mouvement,rubi(t))
    adh = tuple(map(lambda i:(lambda:fct(i)),matrice_scalaire(1)))
    adhi = tuple(map(lambda i:(lambda:fcti(i)),matrice_scalaire(1)))
    pgb = tuple(map(lambda i:(lambda:fct(i)),matrice_scalaire(-1)))
    pgbi = tuple(map(lambda i:(lambda:fcti(i)),matrice_scalaire(-1)))
    return (adh,adhi,pgb,pgbi)

# mouvements globaux de rotation du cube
def rotations_cube():
    def rotate(pp):
        global context
        context = produit_matrices(context,pp)
        return 
    adh0 = tuple(map(lambda i:(lambda:rotate(rot(i))),matrice_scalaire(1)))
    adh0i = tuple(map(lambda i:(lambda:rotate(roti(i))),matrice_scalaire(1)))
    return (adh0,adh0i)

# opération associée au nom donné par la chaîne 's'
def op_with_name(s):
    x = rotations_cube()
    ((a0, d0, h0), (a0i, d0i, h0i)) = (x[0],x[1])
    y = rotations_faces()
    ((a, d, h),(ai, di, hi), (p, g, b), (pi, gi, bi)) = (y[0],y[1],y[2],y[3])
    dico = {"a0":a0, "d0":d0, "h0":h0, "a0'":a0i, "d0'":d0i, "h0'":h0i,
                    "a":a, "d": d, "h": h, "a'":ai, "d'":di, "h'":hi,
                    "p":p, "g":g, "b":b, "p'":pi, "g'":gi, "b'":bi}
    return dico[s]


def nom_de_face(v):
    match v:
        case (1, 0, 0) : return "a"
        case (0, 1, 0) : return "d"
        case (0, 0, 1) : return "h"
        case (-1, 0, 0) : return "p"
        case (0, -1, 0) : return "g"
        case (0, 0, -1) : return "b"
        case _ : return "" #erreur dans nom_de_face

# on associe au nom de face 's' une ligne de la matrice 'mat' ou de son opposée
# ces lignes sont censées être des triplets (x,y,z) où x,y,z valent 0 ou 1 ou -1 avec deux d'entre-eux valant 0
# considérées comme des normales aux faces. En fait 'mat' est censée un élément du 'groupe_du_cube' 
def associe(mat,s):
    n_plus = tuple(map(nom_de_face,mat))
    n_moins = tuple(map(nom_de_face,produit_matrices(matrice_scalaire(-1),mat)))
    match s:
        case "a": return n_plus[0]
        case "d": return n_plus[1]
        case "h": return n_plus[2]
        case "p": return n_moins[0]
        case "g": return n_moins[1]
        case "b": return n_moins[2]
        case "a'": return n_plus[0] + "'"
        case "d'": return n_plus[1] + "'"
        case "h'": return n_plus[2] + "'"
        case "p'": return n_moins[0] + "'"
        case "g'": return n_moins[1] + "'"
        case "b'": return n_moins[2] + "'"
        case _ : return "" #erreur dans associe

def face(c,v):
    e = produit_scalaire(v,(1,1,1))
    w = [e,e,e]
    w1 = list(produit_vecteur_matrice(w,rot(v)))
    w2 = list(produit_vecteur_matrice (w1,rot(v)))
    w3 = list(produit_vecteur_matrice(w2,rot(v)))
    l = [w,w1,w2,w3]
    coeff = 2
    for x in l:
        for j in range(3):
            x[j] += coeff * c[j]
    return l

# contours des faces externes (visibles) associées au centre 'c'
def faces(c):
    d = ((c[0],0,0),(0,c[1],0),(0,0,c[2]))
    return (d, tuple(map(lambda x: face(c,x),d)))

#-----------------------------------------------------------------------------
# COULEURS
#-----------------------------------------------------------------------------

class couleur():
    ROUGE = 1
    ORANGE = 2
    BLEU = 3
    VERT = 4
    JAUNE = 5
    BLANC = 6
    GRIS = 7
    NOIR = 8

def rgb(red,green,blue):
    return f'#{hex(red)[2:]:02}{hex(green)[2:]:02}{hex(blue)[2:]:02}'

def set_color(c):
    global couleur_courante
    couleur_courante = c

def couleur_rvb_de_couleur(c):
    match c:
        case couleur.ROUGE: return rgb(255,0,0)
        case couleur.ORANGE: return rgb(255,165,0)
        case couleur.BLEU: return rgb(0,150,225)
        case couleur.VERT: return rgb(0,255,0)
        case couleur.JAUNE: return rgb(255,255,0)
        case couleur.BLANC: return rgb(255,255,255)
        case couleur.GRIS: return rgb(100,100,100)
        case couleur.NOIR: return rgb(0,0,0)

def couleur_de_face(v):
    match v:
        case (1,0,0): return couleur.ORANGE
        case (-1,0,0): return couleur.ROUGE
        case (0,1,0): return couleur.VERT
        case (0,-1,0): return couleur.BLEU
        case (0,0,1): return couleur.BLANC
        case (0,0,-1): return couleur.JAUNE
        case _ : return couleur.GRIS

def couleur_rvb_de_face(v):
    return couleur_rvb_de_couleur (couleur_de_face(v))

def nom_de_couleur(c):
    match c:
        case couleur.ORANGE : return "ORANGE"
        case couleur.ROUGE : return "ROUGE"
        case couleur.VERT : return "VERT"
        case couleur.BLEU : return "BLEU"
        case couleur.BLANC : return "BLANC"
        case couleur.JAUNE : return "JAUNE"
        case _ : return "GRIS"

def nom_couleur_de_face(v):
    match v:
        case (1,0,0): return "orange"
        case (-1,0,0): return "rouge"
        case (0,1,0): return "verte"
        case (0,-1,0): return "bleue"
        case (0,0,1): return "blanche"
        case (0,0,-1): return "jaune"
        case _ : return "grise"

def couleurs_faces_adh():
    noms = tuple(map(nom_couleur_de_face,transpose(context)))
    return f"{cmt} Résultat avec : {fincmt}\r\n" + f"{cmt} face antérieure " + noms[0] + f" {fincmt}" + f"\r\n{cmt} face droite " + noms[1] + f" {fincmt}" + f"\r\n{cmt} face haute " + noms[2] + f" {fincmt}"

def couleurs_adh(x):
    def eclate(x):
        return ((x[0],0,0),(0,x[1],0),(0,0,x[2]))
    p = transpose(context)
    m = produit_matrices(eclate(x), produit_matrices(p, fun_of_mv1(inverse(mouvement))(produit_vecteur_matrice(x,p))))
    return tuple(map(couleur_de_face,m))

def noms_couleurs_coin_adh():
    noms = tuple(map(nom_de_couleur,couleurs_adh((1,1,1))))
    return "(" + noms[0] + ", " + noms[1] + ", " + noms[2] + ")"


#-----------------------------------------------------------------------------
#résolution du cube par étages
#-----------------------------------------------------------------------------

def resoudre_le_cube(completement):
    global context
    global mouvement
    global mctx
    global lo
    
    matr = context
    nbqt = 0
    lo = ""
    mctx = matrice_scalaire(1)
    def pos0(x):
        def pos(mv1,x):
            p = context
            m = fun_of_mv1(produit_mouvements((inverse(cst(p))), produit_mouvements(mouvement, cst(p))))(x)
            return (produit_vecteur_matrice(x,m),m)
        return pos(mouvement,x)

    def pos1(x):
        def pos(mv1,p,x):
            m = fun_of_mv1(produit_mouvements((inverse(cst(p))), produit_mouvements(mv1, cst(p))))(x)
            return (produit_vecteur_matrice(x,m),transpose(m))
        return pos(inverse(mouvement),context,x)

    def exec1(str):
        global mctx
        temp = context
        listop = op_names_from_string(str)
        exe1(tuple(map(op_with_name,listop)))
        temp1 = context
        mctx = produit_matrices(mctx,produit_matrices(transpose(temp),temp1))

    def exec(str):
        nonlocal matr
        global mctx
        global lo
        listop = op_names_from_string(str)
        nonlocal nbqt
        nbqt += len(listop)
        if mctx != matrice_scalaire(1):
            lo += format_string(decomposition(transpose(mctx))) + "\r\n"
            mctx = matrice_scalaire(1)
        exe1(tuple(map(op_with_name,listop)))
        lo += format_string(str) + "\r\n"
        temp = tuple(map(lambda x:(associe(produit_matrices(transpose(context),matr), x)),listop))       
        s = ""
        for i in temp:
            s += i
        print(format_string(s))

    def niveau_superieur():
        global lo
        lo += f"{cmt} NIVEAU SUPÉRIEUR {fincmt}\r\n";
        def orienter_les_centres():
            global lo
            n = nqt(mouvement, produit_vecteur_matrice((0,0,1),transpose(context)))
            if n > 0:
                print(f"{cmt} ORIENTATION DU CENTRE {fincmt}\r")
                lo += f"{cmt} ORIENTATION DU CENTRE {fincmt}\r\n"
            if n == 1: exec("h'")
            if n == 2: exec("hh")
            if n == 3: exec("h")
        def placer_et_orienter_les_angles():
            def placer_angle_frontal_haut():
                (v, _) = pos0((1,0,1))
                match(v):
                    case (1,0,1) : ()
                    case (1,1,0) : exec("a'")
                    case (1,0,-1) : exec("aa")
                    case (1,-1,0) : exec("a")
                    case (0,1,1) : exec("d'a'")
                    case (-1,1,0) : exec("h'd'h")
                    case (0,1,-1) : exec("da'd'")
                    case (0,-1,1) : exec("ga")
                    case (-1,-1,0) : exec("hgh'")
                    case (0,-1,-1) : exec("g'ag")
                    case (-1,0,1) : exec("ppbbaa")
                    case (-1,0,-1) : exec("bbaa")
                    case _ : () #erreur dans placer_angle_frontal_haut
            def mal_oriente():
                return pos0((1,0,1))[1][2] != (0,0,1)
            for _ in range(0,4):
                placer_angle_frontal_haut()
                if mal_oriente(): exec("h'd'ha'")
                exec1("h0")
        def placer_et_orienter_les_coins():
            def descendre_coin():
                (v, m) = pos0((1, 1, 1))
                if (v != (1, 1, 1)) or (m != matrice_scalaire(1)):
                    match(v):
                        case (-1, 1, 1): exec("p'b'p")
                        case (-1, -1, 1): exec("pbbp'")
                        case (1, -1, 1): exec("gbg'")
                        case (1, 1, 1): exec("aba'b'")
                        case (-1, 1, -1): exec("b'")
                        case(-1, -1, -1): exec("bb")
                        case (1, -1, -1): exec("b")
                        case (1,1, -1): ()
                        case _ : () #erreur dans descendre_coin
            def remonter_coin():
                (v, m) = pos0((1, 1, 1))
                if (v != [1, 1, 1]) or (m != matrice_scalaire(1)):
                    match(m[2]):
                        case (1,0,0): exec("da'd'a")
                        case (0,1,0): exec("a'dad'")
                        case (0,0,-1): exec("ab'a'bbda'd'a")
                        case _ : () #erreur dans remonter_coin
            for  _ in range(0,4):
                descendre_coin()
                remonter_coin()
                exec1("h0")
        if completement: orienter_les_centres()
        print(f"{cmt} LA CROIX {fincmt}\r")
        lo += f"{cmt} LA CROIX {fincmt}\r\n"
        placer_et_orienter_les_angles()
        print(f"{cmt} LES COINS {fincmt}\r")
        lo += f"{cmt} LES COINS {fincmt}\r\n"
        placer_et_orienter_les_coins()

    def niveau_median():
        global lo
        lo += f"\n{cmt} NIVEAU MÉDIAN {fincmt}\r\n"
        def orienter_les_centres_lateraux():
            global lo
            print(f"{cmt} ORIENTATION DES CENTRES {fincmt}\r")
            lo += f"{cmt} ORIENTATION DES CENTRES {fincmt}\r\n"
            for _ in range(0,4):
                n = nqt(mouvement, produit_vecteur_matrice((1,0,0),transpose(context)))
                if n == 1: exec("aabba'bbaa")
                if n == 2: exec("aabbaabbaa")
                if n == 3: exec("aabbabbaa")
                exec1("h0")
            print(f"{cmt} ORIENTATION DES CENTRES TERMINÉE {fincmt}\r")
            lo += f"{cmt} ORIENTATION DES CENTRES TERMINÉE {fincmt}\r\n"
        def placer_angle_frontal_droit():
            def descendre_angle():
                def aux():
                    exec("bab'a'b'd'bd")
                (x,_) = pos0((1,1,0))
                match(x):
                    case (1,1,0) : aux()
                    case (-1,1,0) :
                        exec1("h0")
                        aux()
                        exec1("h0'")
                    case (-1,-1,0) :
                        exec1("h0")
                        exec1("h0")
                        aux()
                        exec1("h0")
                        exec1("h0")
                    case (1,-1,0) :
                        exec1("h0'")
                        aux()
                        exec1("h0")
                    case _ : ()
            def remonter_angle():
                def aux_r():
                    exec("b'd'bdbab'a'")
                def aux_l():
                    exec("bab'a'b'd'bd")
                (x,m) = pos0((1,1,0))
                if m[0] != (0,0,-1):
                    match(x):
                        case (1,0,-1) : aux_r()
                        case (0,-1,-1) :
                            exec("b")
                            aux_r()
                        case (-1,0,-1) :
                            exec("bb")
                            aux_r()
                        case (0,1,-1) :
                            exec("b'")
                            aux_r()
                        case _ : () #erreur 1 dans remonter_angle
                else:
                    match(x):
                        case (1,0,-1) :
                            exec("b")
                            aux_l()
                        case (0,-1,-1) :
                            exec("bb");
                            aux_l()
                        case (-1,0,-1) :
                            exec("b'");
                            aux_l()
                        case (0,1,-1) : aux_l()
                        case _ : () #erreur 2 dans remonter_angle")
            (x,m) = pos0((1,1,0))
            if x != (1,1,0) or m != matrice_scalaire(1):
                descendre_angle()
                remonter_angle()
        if completement: orienter_les_centres_lateraux()
        for _ in range(0,4):
            placer_angle_frontal_droit()
            exec1("h0")

    def niveau_inferieur():
        global lo
        lo += f"\n{cmt} NIVEAU INFÉRIEUR {fincmt}\r\n"
        def orienter_les_angles():
            def est_mal_oriente(angle):
                (_,m) = pos1(angle)
                return m[2] != (0,0,1)
            angles_inferieurs = ((1,0,-1),(0,-1,-1),(-1,0,-1),(0,1,-1))
            match(tuple(map(est_mal_oriente,angles_inferieurs))):
                case (False,False,False,False) : ()
                case (True,True,True,True) : exec("dbab'a'd'bdaba'b'd'")
                    
                case (False,False,True,True) : exec1("h0");exec("dbab'a'd'")
                case (True,False,False,True) : exec("dbab'a'd'")
                case (True,True,False,False) : exec1("h0'");exec("dbab'a'd'")
                case (False,True,True,False) : exec1("h0");exec1("h0");exec("dbab'a'd'")                    
                case (False,_,False,_) : exec("daba'b'd'")
                case (_,False,_,False) : exec1("h0");exec("daba'b'd'")
                case _: () #erreur dans orienter_les_angles
        def placer_les_angles():
            global lo
            if completement:
                n = nqt(mouvement, produit_vecteur_matrice((0,0,-1),transpose(context)))
                if n == 1: exec("b'")
                if n == 3: exec("b")
                if n == 2: exec("bb")
            else:
                if sign(angles, sur(mouvement)) == -1:
                    print(f"{cmt} CONTRÔLE DE PARITÉ {fincmt}\r")
                    lo += f"{cmt} CONTRÔLE DE PARITÉ {fincmt}\r\n"
                    exec("b")
                    print(f"{cmt} FIN DE CONTRÔLE DE PARITÉ {fincmt}\r")
                    lo += f"{cmt} FIN DE CONTRÔLE DE PARITÉ {fincmt}\r\n"
            def permuter():
                exec("dbbd'b'db'd'")
            def permuterI():
                exec("dbd'bdbbd'")
            def chercher_angle_bien_place():
                i = 0
                while i < 4 and pos0((-1,0,-1))[0] != (-1,0,-1):
                    exec1("h0")
                    i += 1
                return i
            j = chercher_angle_bien_place()
            if j == 4:
                permuter()
                _ = chercher_angle_bien_place()
            (v,_) = pos0((1,0,-1))
            match(v):
                case (0,-1,-1) : permuter()
                case (0,1,-1) : permuterI()
                case (1,0,-1) : ()
                case _ : () #erreur dans placer_les_angles
        def placer_les_coins():
            def permuter():
                #sens horaire
                exec("bab'p'ba'b'p")
            def permuterI():
                #sens inverse
                exec("p'bab'pba'b'")
            def chercher_un_coin_bien_place():
                i = 0
                while i < 4 and (pos0((1, 1, -1)))[0] != (1, 1, -1):
                    exec1("h0")
                    i += 1
                return i
            j = chercher_un_coin_bien_place()
            if j == 4: # aucun coin bien placé
                permuter()
                _ = chercher_un_coin_bien_place()
            (v, _) = pos0((-1,-1,-1))
            match(v):
                case (1,-1,-1): permuter()
                case (-1,1,-1): permuterI()
                case (-1,-1,-1) : ()
                case _: () #erreur dans placer_les_coins
        def orienter_les_coins():
            global lo
            lo += f"{cmt} - Orienter les coins {fincmt}\r\n"
            def faire_tourner():
                #fait tourner les coins droits sur eux-mêmes: haut -> droite
                exec("p'b'pb'p'bbp")
                exec("aba'babba'")
            def faire_tournerI():
                #fait tourner les coins droits sur eux-mêmes: droite -> haut
                exec("abba'b'ab'a'")
                exec("p'bbpbp'bp")
            def orienter_frontal_inferieur_droit():
                (_, m) = pos0((1, 1, -1))
                match(m[2]):
                    case (0, 0, 1): ()
                    case (-1, 0, 0): faire_tournerI();
                    case (0, -1, 0): faire_tourner();
                    case _: () #erreur dans orienter_frontal_inferieur_droit
            for _ in range(3):
                orienter_frontal_inferieur_droit()
                exec1("h0'")
        print(f"{cmt} LA CROIX {fincmt}\r")
        lo += f"{cmt} LA CROIX {fincmt}\r\n"
        orienter_les_angles()
        placer_les_angles()
        print(f"{cmt} LES COINS {fincmt}\r")
        lo += f"{cmt} LES COINS {fincmt}\r\n"
        placer_les_coins()
        orienter_les_coins()
        
        s = decomposition(transpose(produit_matrices(mctx,produit_matrices(transpose(context), matr))))
        if s != "":
            lo += f"{cmt} Ramener le cube à sa position initiale {fincmt}\r\n"
            lo += format_string(s) + "\r\n"
    mat = context
    noms_couleurs = noms_couleurs_coin_adh()
    if ocaml:
        print("\r\n#use \"interfaces/interface3.oml\";;\r\n")
        print("Graphics.open_graph \" 612x612\";;\r\n")
    elif camllight:
        print("\r\ninclude \"interfaces/interface3.ml\";;\r\n")
        print("Graphics__open_graph \" 612x612\";;\r\n")
    if ocaml or camllight:
        print("let mv = lire_mouv \"mouv3333\";;\r")
        print(f'let ctx = context_adh {noms_couleurs} mv;;\r')
    print(f'\r\n{cmt} coin Antérieur Droit Haut : {noms_couleurs} {fincmt}\r\n')
        
    print(f"{cmt} RÉSOLUTION SANS ROTATIONS GLOBALES {fincmt}\r\n")
    print(f'{couleurs_faces_adh()}\r\n')
    if ocaml or camllight:
        print("cube.context1.matrice <- ctx.matrice;;\r")
        print("cube.mouvement1.mv1 <- mv;;\r")
        print("dessine_cube ctx mv;;\r\n")
        
    print(f"{cmt} NIVEAU SUPÉRIEUR {fincmt}\r")
    niveau_superieur()
    print(f"\n{cmt} NIVEAU MÉDIAN {fincmt}\r")
    niveau_median()
    print(f"\n{cmt} NIVEAU INFÉRIEUR {fincmt}\r")
    niveau_inferieur()
    context = mat;
    print("\r\n")
        
    print(f"{cmt} RÉSOLUTION AVEC ROTATIONS GLOBALES {fincmt}\r\n")
    if ocaml or camllight:
        print("cube.context1.matrice <- ctx.matrice;;\r")
        print("cube.mouvement1.mv1 <- mv;;\r")
        print("dessine_cube ctx mv;;\n\r")
    print(f"{lo}")
    return nbqt

#-----------------------------------------------------------------------------
# composer...
#-----------------------------------------------------------------------------

mvi = cst(matrice_scalaire(0))
for i in centres: mvi[i] = matrice_scalaire(1)

def est_vide_a_gauche(v):
    return mvi[v] != ((0,0,0),(0,0,0),(0,0,0))

def est_vide_a_droite(v):
    for i in indices:
        if produit_vecteur_matrice(i,mvi[i]) == v:
            return False
    return True

def tout_vide_a_gauche():
    for i in indices:
        if not est_vide_a_gauche(i): return False
    return True

def est_dans_poly(p,x): # (x intérieur au quadrilatère supposé convexe limité par p)
    def det(v1,v2):
        return v1[0] * v2[1] - v1[1] * v2[0]
    p0 = (p[0][0] - x[0],p[0][1]-x[1])
    p1 = (p[1][0] - x[0],p[1][1]-x[1])
    p2 = (p[2][0] - x[0],p[2][1]-x[1])
    p3 = (p[3][0] - x[0],p[3][1]-x[1])
    return  det(p0,p1) * det(p1,p2) > 0 and det(p1,p2) * det(p2,p3) > 0 and det(p2,p3) * det(p3,p0) > 0

#-----------------------------------------------------------------------------
# graphisme
#-----------------------------------------------------------------------------

def proj(x,y,z):
    c = math.sqrt(6.0)/2.0
    return (c * (y - x) / math.sqrt(2.0) , c * (-(x + y) + 2.0 * z) / math.sqrt(6.0))

class Plan:
    def __init__(self,ox,oy,ux,uy, largeur,hauteur,root):
        self.ox = ox
        self.oy = oy
        self.ux = ux
        self.uy = uy
        self.orig = (0,0)
        self.largeur = largeur
        self.hauteur = hauteur
        self.canvas = Canvas(root,bg="white",width=largeur,height=hauteur)
        self.boutons1 = []
        self.lines = dict()
        self.rectangles = dict()
        self.polygons = dict()
        self.texts = dict()
        self.res_gauche = None

    def xx(self,v,pt3):
        (x,y,z) = (pt3[0], pt3[1], pt3[2])
        def aux():
            if produit_scalaire(v , [1,1,1]) == 1: return (x, y, z)
            match (v[0],v[1],v[2]):
                case (_,0,0) : return (x - 7.0, y, z)
                case (0,_,0) : return (x, y - 7.0, z)
                case _ : return (x, y, z - 7.0)
        (x1, y1, z1) = aux()
        (a, _) = proj(x1, y1, z1)
        return round(self.ox + a * self.ux)

    def yy(self,v,pt3):
        (x, y, z) = (pt3[0], pt3[1], pt3[2])
        def aux():
            if produit_scalaire(v , (1,1,1)) == 1: return (x, y, z)
            match (v[0],v[1],v[2]):
                case (_,0,0) : return (x - 7.0, y, z)
                case (0,_,0) : return (x, y - 7.0, z)
                case _ : return (x, y, z - 7.0)
        (x1, y1, z1) = aux()
        (_, b) = proj(x1, y1, z1)
        return self.hauteur - round(self.oy + b * self.uy)

    def moveto(self,pt):
        self.orig = pt

    def lineto(self,pt):
        if (self.orig,pt) in self.lines:
            line = self.lines[(self.orig,pt)]
            self.lines.pop((self.orig,pt))
            self.canvas.delete(line)       
        line = self.canvas.create_line(self.orig,pt,fill = couleur_courante)
        self.lines[(self.orig,pt)] = line
        self.orig = pt

    def fill_rect(self,ox,oy,largeur,hauteur):
        if (ox,oy,largeur,hauteur) in self.rectangles:
            rectangle = self.rectangles[(ox,oy,largeur,hauteur)]
            self.rectangles.pop((ox,oy,largeur,hauteur))
            self.canvas.delete(rectangle)
        rectangle = self.canvas.create_rectangle(ox,oy,ox+largeur,oy+hauteur,fill=couleur_courante)
        self.rectangles[(ox,oy,largeur,hauteur)] = rectangle

    def fill_poly(self,poly):
        if poly in self.polygons:
            polygon = self.polygons[poly]
            self.polygons.pop(poly)
            self.canvas.delete(polygon)
            polygon = self.canvas.create_polygon(poly,fill = couleur_courante)
            self.polygons[poly] = polygon
        else:
            polygon = self.canvas.create_polygon(poly,fill = couleur_courante)
            self.polygons[poly] = polygon

    def drawPoly(self,poly):
        (x,y) = poly[0]
        self.moveto((x,y))
        for i in range(1,len(poly)):
            (x,y) = poly[i]
            self.lineto((x,y))
        (x,y) = poly[0]
        self.lineto((x,y))

    def draw(self,a,b):
        set_color(b)
        self.fill_poly(a)
        set_color(rgb(0,0,0))
        self.drawPoly(a)

    def draw_string(self,s):
        cle = self.orig
        if cle in self.texts:
            iden = self.texts[cle]
            self.texts.pop(cle)
            self.canvas.delete(iden)
        iden = self.canvas.create_text(cle,anchor='w',fill=couleur_courante, text=s)
        self.texts[cle] = iden

    def text_size(self,s):
        def ret(text):
            bbox = self.canvas.bbox(text)
            (l,h) = (bbox[2]-bbox[0],bbox[3]-bbox[1])
            self.canvas.delete(bbox)
            return (l,h)            
        if s in self.texts:
            text = self.texts[s]
            return ret(text)
        text = self.canvas.create_text(-10, -10, text=s)
        self.texts[s] = text
        return ret(text)
  
    def affiche1(self,mat,context,centre):
        p = context
        (d,f) = faces(centre)
        m = mat
        for i in range(len(d)):
            v = produit_vecteur_matrice(d[i],m)
            g = tuple(map(lambda x: produit_vecteur_matrice(x,m),f[i]))
            self.draw(tuple(map(lambda pt:(self.xx(v,pt), self.yy(v,pt)),g)),couleur_rvb_de_face(produit_vecteur_matrice(d[i],transpose(p))))

    def tag(self,mat,context,i):
        f = face(i,i)
        v = produit_vecteur_matrice(i,mat)
        def milieu(face):
            def aux(v,w):
                return ((v[0]+w[0])//2,(v[1]+w[1])//2,(v[2]+w[2])//2) 
            return aux(face[0],face[2])
        g = tuple(map((lambda x: produit_vecteur_matrice(x,mat)),f))
        m =milieu(f)
        p = context
        c1 = self.xx(produit_vecteur_matrice(i,p),produit_vecteur_matrice(m,p))
        c2 = self.yy(produit_vecteur_matrice(i,p),produit_vecteur_matrice(m,p))
        set_color(rgb(0,0,0))
        self.moveto((c1,c2))
        self.lineto((self.xx(produit_vecteur_matrice(v,p),produit_vecteur_matrice(g[0],p)), self.yy(produit_vecteur_matrice(v,p),produit_vecteur_matrice(g[0],p))))

    def affiche_mouvement(self,context,mv):
        p = context
        for x in indices:
            self.affiche1(produit_matrices(transpose(p), produit_matrices(mv[x],p)), context, produit_vecteur_matrice(x,p))
        for x in  centres:
            self.tag(fun_of_mv1(mv)(x), context, x)

    #-----------------------------------------------------------------------------
    # graphisme fenêtre composer
    # gestion souris de la composition d'un cube par tranfert des minicubes de gauche à droite
    #-----------------------------------------------------------------------------

    def choix_a_gauche(self,mousex,mousey):
        if mousex >= self.largeur // 2 : return
        for i in indices:
            (d, f) = faces(i)
            for j in range(3):
                face = tuple(map(lambda pt: (self.xx(d[j], pt), self.yy(d[j], pt)),f[j]))
                if est_dans_poly(face, (mousex, mousey)):
                    self.res_gauche = (i,d[j])

    def choix_a_droite(self,mousex,mousey):
        global mvi
        if mousex < self.largeur // 2 : return
        self.ox += self.largeur // 2
        for i in indices:
            (d, f) = faces(i)
            for j in range(3):
                face = tuple(map(lambda pt: (self.xx(d[j], pt), self.yy(d[j], pt)),f[j]))
                if est_dans_poly(face, (mousex, mousey)):
                    (w1,w2) = (i,d[j])
                    p = context
                    if est_vide_a_droite(produit_vecteur_matrice(w1,transpose(p))):
                        if self.res_gauche != None:           
                            (v1,v2) = self.res_gauche
                            mat = [m for m in groupe_du_cube if produit_vecteur_matrice(v1,m)==w1 and produit_vecteur_matrice(v2,m)==w2]
                            if mat != []:
                                i = produit_vecteur_matrice(v1,transpose(p))
                                if not est_centre(i):
                                    if produit_vecteur_matrice(i,p) != v1: mvi[i] = fun_of_mv1(mvi)(i)
                                    else: mvi[i] = produit_matrices(p,produit_matrices(mat[0],transpose(p)))
                                    self.res_gauche = None
                    else:
                        for j in indices:
                            if produit_vecteur_matrice(j,produit_matrices(mvi[j],p)) == w1 and not est_centre(j) :
                                mvi[j] = ((0,0,0),(0,0,0),(0,0,0))
                                break
                    self.res_gauche = None
        self.ox -=self.largeur // 2

    def griser(self,offset,centre):
        (d,f) = faces(centre)
        for i in range(len(d)):
            v = d[i]
            self.ox += offset
            self.draw(tuple(map(lambda pt: (self.xx(v,pt),self.yy(v,pt)),f[i])),couleur_rvb_de_couleur(couleur.GRIS))
            self.ox -= offset

    def affiche_mvi(self,context,mvi):
        p = context
        self.ox += self.largeur//2
        for i in indices:
            if not est_centre(i):
                self.griser(0,i)
        self.ox -= self.largeur//2
        for i in indices:
            if est_vide_a_gauche(i) and not est_centre(i):
                self.griser(0,produit_vecteur_matrice(i,p))
            else:
                self.affiche1(matrice_scalaire(1),p,produit_vecteur_matrice(i,p))
            if mvi[i] != ((0,0,0),(0,0,0),(0,0,0)) or est_centre(i):
                self.ox += self.largeur//2
                self.affiche1(produit_matrices(transpose(p),produit_matrices(mvi[i],p)),context,produit_vecteur_matrice(i,p))
                if est_centre(i): self.tag(mvi[i],p,i)
                self.ox -= self.largeur//2

    def gestion_centres(self, mousex, mousey):
        global mvi
        for i in centres:
            (d,f) = faces(i)
            for j in range(len(d)):
                self.ox += self.largeur//2
                face1 = tuple(map(lambda pt: (self.xx(d[j], pt), self.yy(d[j], pt)),f[j]))
                self.ox -= self.largeur//2
                if est_dans_poly(face1,(mousex,mousey)): mvi = ajuste_centre(mvi,rot,produit_vecteur_matrice(i,transpose(context)))

    #-------------------------------------------------------------------------
    # boutons
    #-------------------------------------------------------------------------
          
    class bouton:
        def __init__(self,titre,orx,ory,largeur,hauteur,couleur,action):
            self.titre = titre
            self.orx = orx
            self.ory = ory
            self.largeur = largeur
            self.hauteur = hauteur
            self.couleur = couleur
            self.action = action

    def dessine_bouton(self,b):
        set_color(couleur_rvb_de_couleur(b.couleur))
        self.fill_rect(b.orx, b.ory, b.largeur, b.hauteur)
        (x,y) = self.text_size(b.titre)
        self.moveto((b.orx + 603//36 - 3, b.ory + 15))
        set_color(rgb(0,0,0))
        self.draw_string(b.titre)
        p = [(b.orx, b.ory), (b.orx + b.largeur, b.ory), (b.orx + b.largeur, b.ory + b.hauteur), (b.orx, b.ory + b.hauteur), (b.orx, b.ory)]
        self.drawPoly(p)

    def gestion_bouton(self,b,mousex,mousey):
        if b.orx <= mousex and mousex <= b.orx + b.largeur and b.ory <= mousey and mousey <= b.ory + b.hauteur:
            b.action()
            self.affiche_mouvement(context, mouvement)
            self.dessine_bouton(b)

    # rangée des boutons en bas de fenêtre manipulant le cube
    def cree_boutons1(self):
        titres = ["A", "A'", "H", "H'", "D", "D'", "a", "a'", "h", "h'", "d", "d'", "p", "p'", "b", "b'", "g", "g'"]
        x = rotations_cube()
        ((a0, d0, h0), (a0i, d0i, h0i)) = (x[0],x[1])
        y = rotations_faces()
        ((a, d, h),(ai, di, hi), (p, g, b), (pi, gi, bi)) = (y[0],y[1],y[2],y[3])
        v = (a0, a0i, h0, h0i, d0, d0i, a, ai, h, hi, d, di, p, pi, b, bi, g, gi)
        n = len(titres)
        for i in range(n):
            self.boutons1.append(self.bouton(titres[i], i * 32 + 10, hauteurA - 40,32,30,couleur.BLANC, lambda j=i: (v[j](),print(titres[j], end = "", flush = True))))

    def dessine_boutons1(self):
        def couleur_titre(titre):
            def face(titre):
                match(titre):
                    case "A" | "A'" | "a" | "a'" : return (1,0,0)
                    case "D" | "D'" | "d" | "d'": return (0,1,0)
                    case "H" | "H'" | "h" | "h'": return (0,0,1)
                    case "p" | "p'": return (-1,0,0)
                    case "g" | "g'": return (0,-1,0)
                    case "b" | "b'": return (0,0,-1)
                    case _: return (0,0,0) #erreur dans face dans dessine_boutons1
            return couleur_de_face(produit_vecteur_matrice(face(titre),transpose(context)))
        for b in self.boutons1:
            b.couleur = couleur_titre(b.titre)
            self.dessine_bouton(b)

    def gestion_boutons1(self,mousex,mousey):
        for b in self.boutons1:
            self.gestion_bouton(b, mousex, mousey)
            if self.boutons1.index(b) < 6: self.dessine_boutons1()

    def bouton2(self,titre,orx,ory,couleur,action):
        (largeur,hauteur) = self.text_size(titre)
        hauteur = 30
        return self.bouton(titre,orx,ory,largeur,hauteur,couleur,action)

    def dessine_bouton2(self,b):
        set_color(couleur_rvb_de_couleur(b.couleur))
        self.fill_rect(b.orx, b.ory, b.largeur+5,b.hauteur)
        self.moveto((b.orx+5,b.ory + 15))
        set_color(rgb(0,0,0))
        self.draw_string(b.titre)

    def gestion_bouton2(self,b,mousex,mousey):
        if b.orx <= mousex and mousex <= b.orx + b.largeur and b.ory <= mousey and mousey <= b.ory + b.hauteur:
            b.action()                

###############################################################################################
# planB (fenêtre composer un mouvement)

def action_OK():
    global window
    global mouvement
    if tout_vide_a_gauche():
        mouvement = mvi
        print(f'rotation totale des coins: {rtc(mvi)}\r')
        print(f'rotation totale des angles: {rta(mvi)}\r')
        print('ces rotations totales devraient être nulles\r')
        p = sur(mouvement)
        print(f'signature des coins: {sign(coins,p)}\r')
        print(f'signature des angles: {sign(angles,p)}\r')
        print('ces signatures devraient être égales\r')
        print(f'nombre de quarts de tour des centres modulo 4: {nqt_total(mouvement)}\r')
        print('ce nombre devrait être 0 ou 2 si les signatures précédentes valent 1\r')
        print('il devrait être  1 ou 3 si ces signatures valent -1\r')

        if est_dans_R(mouvement):
            enregistrer(mouvement, 'mouv3333')
            print('mouvement de Rubik enregistré dans mouv3333 !\r')
    else: 
        print('composition de mouvement inachevée !\r')
    window.destroy()
    window = None

def composer():
    global window

    if window != None:
        window.focus_force()
        return

    largeurB=600
    hauteurB=300
    ux = 10  
    uy = 10
    ox = 150  #(largeurB//4)
    oy = 150  #(hauteurB//2)

    window = Toplevel()
    window.title("Composer un mouvement")
    window.geometry(f'{largeurB}x{hauteurB}')
    window.protocol("WM_DELETE_WINDOW", action_OK)

    planB = Plan(ox,oy,ux,uy,largeurB,hauteurB,window)
    planB.canvas.pack()

    set_color(rgb(0,0,0))
    planB.moveto((largeurB // 2, 0))
    planB.lineto((largeurB // 2, hauteurB))
    planB.affiche_mvi(context,mvi)

    bouton_OK=planB.bouton2('OK',530,20,couleur.JAUNE,action_OK)
    planB.dessine_bouton2(bouton_OK)

    def handle_clickB(event):
        mousex = event.x
        mousey = event.y
        planB.choix_a_gauche(mousex,mousey)
        planB.choix_a_droite(mousex,mousey)
        planB.gestion_centres(mousex,mousey)
        planB.affiche_mvi(context,mvi)
        planB.gestion_bouton2(bouton_OK,mousex,mousey)

    planB.canvas.bind("<Button-1>", handle_clickB)

###############################################################################################
#planA (fenêtre principale)
window = None
        
def action_resoudre():
    global mouvement_duplique
    mouvement_duplique=mouvement
    enregistrer(mouvement,"mouv3333")
    print(f"\r\n{cmt} RÉSOLUTION SIMPLE: {fincmt}\r")
    n=resoudre_le_cube(False)
    print(f'{cmt} nombre de quarts de tour : {n} {fincmt}\r')

def action_resoudre_completement():
    global mouvement_duplique
    mouvement_duplique=mouvement
    enregistrer(mouvement,"mouv3333")
    print(f"\r\n{cmt} RÉSOLUTION COMPLÈTE: {fincmt}\r")
    n=resoudre_le_cube(True)
    print(f'{cmt} nombre de quarts de tour : {n} {fincmt}\r')

def action_melanger():
    global mouvement
    global mouvement_duplique
    mouvement_duplique=mouvement
    mouvement=mv1_rubik_r()

def action_restituer():
    global mouvement
    mouvement=mouvement_duplique

def action_composer():
    composer()

def action_quitter():
    root.destroy()


context = matrice_scalaire(1)
if os.path.exists("mouv3333"):
    mouvement = lire_mouvement("mouv3333")
else:
    mouvement = e
mouvement_duplique = mouvement


largeurA = 600
hauteurA = 642

from tkinter import *
root = Tk()
root.title("Résoudre le cube")
root.geometry(f'{largeurA}x{hauteurA}')

planA = Plan(largeurA//2,hauteurA/2,20,20,largeurA,hauteurA,root)
planA.canvas.pack()

planA.affiche_mouvement(context, mouvement)

bouton_resoudre=planA.bouton2('Résoudre',20,20,couleur.JAUNE,action_resoudre)
planA.dessine_bouton2(bouton_resoudre)
bouton_resoudre_completement=planA.bouton2('Résoudre complètement',20,50,couleur.JAUNE,action_resoudre_completement)
planA.dessine_bouton2(bouton_resoudre_completement)
bouton_melanger=planA.bouton2('Mélanger',250,20,couleur.JAUNE,action_melanger)
planA.dessine_bouton2(bouton_melanger)
bouton_restituer=planA.bouton2('Restituer',250,50,couleur.JAUNE,action_restituer)
planA.dessine_bouton2(bouton_restituer)
bouton_composer=planA.bouton2('Composer',250,80,couleur.JAUNE,action_composer)
planA.dessine_bouton2(bouton_composer)
bouton_quitter=planA.bouton2('Quitter',530,20,couleur.JAUNE,action_quitter)
planA.dessine_bouton2(bouton_quitter)

planA.cree_boutons1()
planA.dessine_boutons1()

def handle_clickA(event):
    planA.gestion_boutons1(event.x,event.y)
    planA.gestion_bouton2(bouton_resoudre,event.x,event.y)
    planA.gestion_bouton2(bouton_resoudre_completement,event.x,event.y)
    planA.gestion_bouton2(bouton_melanger,event.x,event.y)
    planA.gestion_bouton2(bouton_restituer,event.x,event.y)
    planA.gestion_bouton2(bouton_composer,event.x,event.y)
    planA.affiche_mouvement(context, mouvement)
    planA.gestion_bouton2(bouton_quitter,event.x,event.y)

planA.canvas.bind("<Button-1>", handle_clickA)

mctx = context
def exec1(str):
    global mctx
    temp = context
    listop = op_names_from_string(str)
    exe1(tuple(map(op_with_name,listop)))
    temp1 = context
    mctx = produit_matrices(mctx,produit_matrices(transpose(temp),temp1))
    planA.affiche_mouvement(context, mouvement)

if not sys.flags.interactive:
    root.mainloop()


