TD1 - SquareMatrix ****************** .. include:: ../BoutonGoogleTrad.rst L’objectif de cette page est de développer une classe template matrice carrée (square matrix). Sa réalisation demande une bonne maîtrise de plusieurs notions abordées dans les fiches de cours précédentes. Nous allons procéder itérativement en implémentant les fonctionnalités par étapes successives. Convention ========== Rappel ------ .. image:: conv.png :scale: 40% :align: center Notations : * Une matrice :math:`M_{mn}` contient *m* lignes et *n* colonnes. * L'élément :math:`m_{ij}` se trouve sur la ligne *i* colonne *j*. * La notion :math:`m_{ij}` peut être contre-intuitive car en géométrie 2D, elle correspond à :math:`m_{yx}` et non :math:`m_{xy}` * L'addition de deux matrices de taille :math:`M_{mn}` est une matrice de taille :math:`M_{mn}` * La multiplication de deux matrices de taille :math:`M_{mn}` et :math:`M_{np}` est une matrice de taille :math:`M_{mp}` Constructeurs et affichage ========================== Objectif -------- La taille de la matrice carrée est un paramètre du template. Les valeurs stockées dans les matrices seront de type *int* pour faciliter les tests unitaires. Pour cette étape, vous devez mettre en place : * Un paramètre template indiquant la taille de la matrice * Un container de données de la STL * Un constructeur par défaut sans initialisation des valeurs * Un constructeur initialisant la matrice ligne par ligne à partir de valeurs entières passées en paramètre. * Une fonction d'affichage *print()* * Le met clef const à tous les endroits vous semblant judicieux .. code-block:: cpp template ... class SquareMatrix { private: // internal data ... public: // default constructor Matrix() {} // constructor receiving a list of values Matrix(array<...> & data) { ... } // display void print() { ... } }; Remarque : l'écriture *{1,2,3,4,5,6,7,8,9}* est compatible avec l’initialisation d'un *array* (ou d'un vector), ainsi le constructeur paramétrique reçoit une const référence sur un *std::array* temporaire/anonyme. Affichage --------- L'affichage produit par la fonction *print* est optimisé pour les nombres entiers entre 0 et 99. Le format d'affichage à respecter est le suivant : * Deux colonnes par valeur. * Une colonne d'espacement entre les valeurs. Test ---- Vous devez mettre en place un code capable d'exécuter la fonction *test1()* ci-dessous. Vous devez obtenir un affichage identique. .. code-block:: cpp using SM3 = SquareMatrix; void test1() { // initialization with values SM3 M({ 11, 2, 3, 4, 55, 6, 7, 8, 99 }); M.print(); cout << "End of test 1------------" << endl; } >> 11 2 3 >> 4 55 6 >> 7 8 99 Opérateurs d'accès ================== Objectif -------- L'opérateur d'indexation [] n'accepte qu'un seul argument. Il n'est donc pas possible en C++ de mettre en place une syntaxe de la forme *M[i,j]*. Il est possible de chaîner deux opérateurs d'indexation en écrivant *[i][j]* mais cela suppose que l'on créé une classe intermédiaire pour retourner une ligne de la matrice ce qui complexifie l'exercice. Pour l'instant, nous choisissons l'option la plus simple en surchargeant l'opérateur () et lui transmettant les deux indices *i* et *j* : *(i,j)*. Nous vous demandons de respecter la convention suivante : .. image:: matrice.jpg :align: center :scale: 25% Fournissez une version const et non const de ce getter. Test ---- Vous devez mettre en place une classe capable d'exécuter le code de test fourni ci-dessous : .. code-block:: cpp const int N = 3; using SM3 = SquareMatrix; void fnt2(const SM3 & M) { cout << M(0, 0); } void test2() { // example of usage SM3 M; for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) M(i, j) = i * i + j; for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) if (M(i, j) != i * i + j) cout << "ERROR"; fnt2(M); cout << "End of test 2" << endl; } Initialiseurs supplémentaires ============================= Ajoutez à la classe matrice les fonctions suivantes : * Une fonction membre *diag()* initialisant la matrice pour qu'elle corresponde à une matrice diagonale. * Une fonction membre *fill(...)* qui initialise les valeurs de la matrice à partir de la valeur fournie en argument. Effectuez des affichages pour contrôler votre travail. Transposition ============= Ajoutez : * Une fonction membre *transpose()* qui transpose les valeurs actuelles de la matrice (inplace). * Une fonction externe *... t(...)* qui retourne une nouvelle matrice correspondant à la transposée de la matrice passée en paramètre. Écrivez un test pour valider votre implémentation. Affichage ========= A partir de maintenant, toutes les fonctions ajoutées dans le code sont des fonctions externes à la classe (pas de fonction membre). Surchargez l'opérateur << pour afficher le contenu de la matrice dans le même format que précédemment. .. code-block:: cpp ostream& operator << (ostream& os, ... ) { ... return os; } Lorsque l'on écrit : *cout << M*, l'objet *cout* est un objet global et unique correspondant à l'écran. Il faut donc le passer uniquement par référence pour éviter toute copie. Le paramètre de droite correspond à la matrice transmise lors de l'appel. L' opérateur << retourne l'objet *cout* afin de permettre le chaînage comme dans l'écriture suivante : *cout << M1 << M2 << endl;*. Écrivez un test pour valider votre implémentation. Opérateurs ========== Objectifs --------- Mettez en place les opérateurs externes suivant : * Addition et soustraction de deux matrices * Multiplication entre deux matrices * Multiplication d'une matrice par un entier (gauche et droite) friend keyword -------------- Comme nous créons des opérateurs externes (non membre de la classe), ils ne peuvent accéder aux données privées et doivent donc utiliser les accesseurs publics. Cependant, il est possible d'accéder aux données privées d'une classe depuis une fonction externe. Pour cela, il faut écrire la déclaration de cette fonction à l'intéreur de la classe et lui ajouter le qualificatif *friend* comme ceci : .. code-block:: cpp template ... class SquareMatrix { ... template ... friend ... operator+(...,...); }; Clarifions la syntaxe de déclaration d'une fonction en C++, on doit mettre dans l'ordre : * Le template en premier, si nécessaire * Les qualificatifs - 0 1 ou plusieurs - inline/static/friend * Le type de retour * Le nom de la fonction + ses paramètres Test final ========== Objectif -------- On doit pouvoir effectuer le calcul suivant avec votre classe : .. math:: A = A \cdot A^t + 2 \cdot A - A \cdot A Test ---- Vous devez pouvoir compiler et exécuter la ligne suivante : .. code-block:: cpp A = A * t(A) + 2 * A - A * A; Utilisez la matrice *A= [ [1,2,3], [2,3,4], [3,4,5]]* pour tester votre résultat. Vous devez obtenir : .. code-block:: cpp >> 2 4 6 >> 4 6 8 >> 6 8 10 Travail à rendre sur Github Classroom ===================================== * Tout le code doit contenir dans un seul fichier * Tous les tests demandés doivent être présents et actifs depuis le *main()* * Renommez votre fichier *FINAL_SquareMatrix.cpp* * Uploadez le fichier sur votre espace github **Ce projet est évalué et compte dans votre note finale**. Tout code généré automatiquement ou ne respectant pas les consignes rapporte 0 point.