# -*- coding: utf-8 -*-
# python version 3.4 
 
from affines import * 
from functools import reduce 
 
 
def Nul(x): 
    """Teste la nullité d'un élément d'un corps fini ou infini""" 
    if isinstance(x, int) or isinstance(x, float) or isinstance(x, complex) or isinstance(x, 
                                                                                          Fraction):  # cas des nombres classiques 
        return x == 0 
    else: 
        return x.nul()  # cas des corps Z/pZ 
 
 
class Systeme(): 
    """Modélisation d'un système d'équations linéaires de Cramer à coefficients dans K=Q,R,C ou Z/pZ""" 
 
    def __init__(self, L): 
        """constructeur à partir d'une liste de lignes""" 
        self.coeffs = L  # les coefficients 
        self.ordre = len(L)  # l'ordre du système 
 
    def __str__(self): 
        """représentation externe""" 
        return '\n'.join([str(v) for v in self.coeffs]) 
 
    def Ligne(self, i): 
        """ la i-ème ligne de la matrice""" 
        return self.coeffs[i][:] 
 
    def Colonne(self, j): 
        """ la j-ième colonne de la matrice""" 
        return [Y[j] for Y in [X for X in self.coeffs]] 
 
    def GetCoef(self, i, j): 
        """Obtenir un coefficient""" 
        return self.coeffs[i][j] 
 
    def SetCoef(self, i, j, v): 
        """Changer un coefficient""" 
        self.coeffs[i][j] = v 
 
    def EchLignes(self, i1, i2): 
        """ échange les lignes d'indices i1 et i2 """ 
        V1 = self.Ligne(i1) 
        V2 = self.Ligne(i2) 
        for j in range(0, self.ordre + 1): 
            self.SetCoef(i1, j, V2[j]) 
            self.SetCoef(i2, j, V1[j]) 
 
    def Pivot(self, j): 
        """ calcul du pivot de Gauss de la colonne j""" 
        Cj = self.Colonne(j) 
        for i in range(j, self.ordre): 
            if not Nul(Cj[i]): 
                return i 
        return -1  # ne doit jamais arriver avec un système de Cramer 
 
    def Solve(self): 
        """ Résolution par la méthode de Gauss""" 
        Solutions = [0 for i in range(0, self.ordre)]  # initialisation à zéros de la liste des solutions 
        for j in range(0, self.ordre - 1): 
            p = self.Pivot(j)  # détermination du pivot 
            if p != j: 
                self.EchLignes(j, p)  # échange de lignes si nécessaire 
            c0 = self.GetCoef(j, j) 
            L = self.Ligne(j) 
            for i in range(j + 1, self.ordre): 
                c = self.GetCoef(i, j) 
                for k in range(j, self.ordre + 1): 
                    cik = self.GetCoef(i, k) 
                    self.SetCoef(i, k, cik - L[k] * c / c0) 
        Solutions[self.ordre - 1] = self.GetCoef(self.ordre - 1, self.ordre) / self.GetCoef(self.ordre - 1, 
                                                                                            self.ordre - 1)  # calcul dernière variable 
        for i in [self.ordre - i for i in range(2, self.ordre + 1)]:  # récurrence remontante 
            s = self.GetCoef(i, self.ordre) 
            for j in range(i + 1, self.ordre): 
                s = s - self.GetCoef(i, j) * Solutions[j] 
            s = s / self.GetCoef(i, i) 
            Solutions[i] = s 
        return Solutions 
 
 
""" Fin de la classe Systeme """ 
 
 
class Repere: 
    """Modélisation des repères affines""" 
 
    def __init__(self, P, B): 
        """le constructeur""" 
        self.Origine = P 
        self.Base = B 
 
    def __str__(self): 
        """Affichage""" 
        L = [str(x) for x in self.Base] 
        b = '(' + ",".join(L) + ')' 
        return '(' + str(self.Origine) + b + ')' 
 
    def Coord(self, M): 
        """Calcul des coordonnées d'un point M dans le repère R""" 
        W = M - self.Origine 
        n = len(self.Base) 
        L = [Y.V for Y in self.Base] 
        T = [[X[i] for X in L] for i in range(0, n)] 
        S = [T[i] + [W.V[i]] for i in range(0, n)] 
        Sys = Systeme(S) 
        Coordonnees = Sys.Solve() 
        return Coordonnees 
 
    def Point(self, C): 
        """Calcule un point de coordonnées données dans le repère""" 
        n = len(C) 
        T = [C[i] * self.Base[i] for i in range(0, n)] 
        S = reduce(lambda x, y: x + y, T) 
        return self.Origine + S 
 
 
def main(): 
    P = PointKn([Fraction(1, 1), Fraction(2, 1), Fraction(3, 1)]) 
    u = VecteurKn([Fraction(1, 1), Fraction(2, 1), Fraction(1, 1)]) 
    v = VecteurKn([Fraction(0, 1), Fraction(1, 1), Fraction(3, 1)]) 
    w = VecteurKn([Fraction(1, 1), Fraction(0, 1), Fraction(2, 1)]) 
    B = ([u, v, w]) 
    R = Repere(P, B) 
    M = PointKn([Fraction(7, 1), Fraction(8, 1), Fraction(9, 1)]) 
    C = R.Coord(M) 
    print(C) 
    Q = R.Point(C) 
    print(Q)  # on retrouve M 
 
 
if __name__ == '__main__': 
    main()