"""

THEME 9 -Module numpy

"""

## Importations
import numpy as np
import numpy.linalg as la


## I opérations de base

ligne1 = [1,2,3,4]
ligne2 = [5,6,7,8]
liste = [ligne1,ligne2]
A = np.array(liste)  # Martice : opérateur np.array qui s'applique à une
                     #           liste de lignes
print(A)

## Q1
print(A.shape)

# Si A est dans Mnp(K), A.shape renvoie le tuple (n,p).
# Ici = Tuple de longueur : A est un tableau numpy 2-dimensionnel

##Q2

X = np.arange(0,10,1)
print(X)
print(X.shape) # tuple de longueur 1 : cela ne représente  pas une matrice

#L'OBJET PYTHON QUI REPRESENTE UNE MATRICE EST UN TABLEAU
# NUMPY 2-DIMENSIONNEL.





##Q3

L =X.reshape(1,10)  # ceci représente une matrice ligne (shape : 1,10)
print(L,L.shape)

 # ceci représente une matrice colonne (shape : 10,1)

# La commande reshape permet de redimensionner un tableau numpy en un
# tableau de format souhaité (pourvu qu'il y ait le bon nombre de coefficients)

## Q4
#Définition de B sans utiliser reshape :

B = np.array([[1,0,-1,1],
              [-1,1,0,-1],
              [1,1,1,0],
              [0,1,-1,0]])

# En utilisant reshape (pas obligatoire, mais évite
# de taper de nombreuses paires de crochets
L1 = [1,0,-1,1,-1,1,0,-1,1,1,1,0,0,1,-1,0] # liste des coeffs de B
L2 = np.array(L1)  # A ce stade on a un tableau 1-dimensionnel
B=L2.reshape(4,4)  # évite la définition de la liste des 4 listes
# (= des 4 lignes de B).
print(B)

##Q5

# A est la tableau
#array([[1, 2, 3, 4],
#       [5, 6, 7, 8]])

# A[1,3] vaut 8
# A[0,0] vaut 1
# A[2,1] renvoie une erreur : coefficient non défini !




# CONTRAIREMENT AU CAS DES MATHS, LES INDICES DES COEFFICIENTS
# DES MATRICES COMMENCENT A ZERO !

# maths | python
#-------+-------
# a1,1  | A[0,0]



##Q6

L = np.array([1,0,0,0,1]).reshape(1,5)
C = np.array([-1,-2,-3]).reshape(3,1)

##Q7

def alterne(n):
    return np.array([1,0]*n).reshape(1,2*n)

##Q8

def c(i,j):
    return i**j
n,p= 3,3
C = np.array([    [c(i,j) for j in range(p)]    for i in range(n)   ])

# Analyse syntaxique de l'expression ligne 103. On constate que :
#1. C = np.array(liste)
#2.              liste = [ truc(i) for i in range(n)]
#3.                        truc(i) =[ c(i,j) for j in range(p)]

# De 1. On voit qu'on est en train de définir la liste des lignes
#       du tableau numpy C.
# De 2. On voit que cette liste est une liste en compréhension
#       et que cette liste contient n items.
# De 3. On voit que chaque item truc(i) est lui-même une liste,
#       ce qui est syntaxiquement compatible avec la définition
#       d'un tableau numpy.
#       truc(i)  est une liste en compréhension de p flottants.
# Conclusion :
# on récupère donc une matrice C de taille (n,p) de coefficient
# général cij = (i-1)**(j-1) (dans la notation math. des indices
# pour les coefficients des matrices).
#   python   |    math
#   ---------+-----------
#    i,j   ----> i+1,j+1
#    i-1,j-1 <---- i,j



def Hilbert(n):
    return np.array([[1/(i+j+1) for j in range(n)] for i in range(n)])
    # ou, si on préfère rester avec la formule math. de h_i;j :
    # return np.array([[1/(i+j-1) for j in range(1,n+1)] for i in range(1,n+1)])
    # dans ce derner cas, il faut adapter les bornes des "range"


##Q10

# Extrait une ligne ou une colonne au format tableau 1-dimensionnel

##Q11

D =np.array([1]*25).reshape(5,5)
D[0,:] = 0 # première ligne
D[:,-2] =0 # avant-dernière colonne

##Q12
Z = np.zeros((2,2))   # matrice nulle de taille 2x2

U = np.ones((4,2))    # matrice Attila (= remplie de 1) de taille 4x2

U.T                   # matrice transposée de U

I = np.eye(3)         # matrice identité de taille 3x3

Z1 = np.zeros_like(A) # matrice nulle de même taille que A
Z2 = np.ones_like(A)  # matrice Attila de même taille que A

## Q13 CLASSIQUE ORAL -- A SAVOIR FAIRE RAPIDEMENT
# il faut calculer C[i,j] pour chaque valeur de i et de j : 2 boucles
# De plus; à i,j fixé, C[i,j] est une somme : une boucle sur k
# Cela fait 3 boucles imbriquées dans le calcul A x B.
def promat(A,B):
    n,p = A.shape
    r,q = B.shape
    # je suppose que r=p sinon il y aura une erreur de toutes
    # manières au moment de l'appel de la fonction
    C = np.zeros((n,q))
    for i in range(n):
        for j in range(q):
            for k in range(p):
                C[i,j]+=A[i,k]*B[k,j]
    return C

# Remarque  : la commande np.dot(A,B) renvoie le produit matriciel AxB


## Q14 CLASSIQUE ORAL -- A SAVOIR FAIRE RAPIDEMENT

def puissance(A,n):
    q,r= A.shape
    An = np.eye(q)  # Matrice identité d'ordre q
    if n>0:
        for _ in range(n):
            An=np.dot(A,An)
    return  An



## Q15 CLASSIQUE ORAL -- A SAVOIR FAIRE RAPIDEMENT

def estDansE(M):
    """
    entrée : M tableau numpy 2d de taille supposée 2x2
    """
    a,b = M[:,0] # j'extrais les coeffs la colonne 1 de M
    c,d = M[:,1] # ceux de la colonne 2 aussi
    return c==a+b and d==a-2*b # version attendue du jury





    # # version ultra-maladroite ultra pas aimée du jury :
    # if (c==a+b)==True and (d==a-2*b)==True:
    #     return True
    # else:
    #     return False

    # # version maladroite pas aimée du jury :
    # if (c==a+b and d==a-2*b):
    #     return True
    # else:
    #     return False

# Test de la fonction
A = np.array([1,0,-1,3]).reshape(2,2) # cette matrice est dans E
























