diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9a9e3f5194a2d0423f64964a8bc8ea19dc661dde
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,14 @@
+cmake_minimum_required(VERSION 3.11)
+
+project(
+  Graph
+  VERSION 0.1
+  DESCRIPTION "COA TP4 - Graph class")
+
+add_compile_options(-fsanitize=address,undefined)
+add_link_options(-fsanitize=address,undefined)
+
+enable_testing()
+
+add_subdirectory(test)
+
diff --git a/README.md b/README.md
index 00c4de075b849412d07c7ed245a97b37ed24bd80..c7023b2ef878a941ccb9ff470d869adf3433dc4a 100644
--- a/README.md
+++ b/README.md
@@ -1,93 +1,5 @@
-# coa-tp4-graphs
+TP4 - Graphes
+---------------
 
-
-
-## Getting started
-
-To make it easy for you to get started with GitLab, here's a list of recommended next steps.
-
-Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
-
-## Add your files
-
-- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
-- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
-
-```
-cd existing_repo
-git remote add origin https://gitlab.univ-lille.fr/coa-2024/coa-tp4-graphs.git
-git branch -M main
-git push -uf origin main
-```
-
-## Integrate with your tools
-
-- [ ] [Set up project integrations](https://gitlab.univ-lille.fr/coa-2024/coa-tp4-graphs/-/settings/integrations)
-
-## Collaborate with your team
-
-- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
-- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
-- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
-- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
-- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
-
-## Test and Deploy
-
-Use the built-in continuous integration in GitLab.
-
-- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
-- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
-- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
-- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
-- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
-
-***
-
-# Editing this README
-
-When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template.
-
-## Suggestions for a good README
-
-Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
-
-## Name
-Choose a self-explaining name for your project.
-
-## Description
-Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
-
-## Badges
-On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
-
-## Visuals
-Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
-
-## Installation
-Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
-
-## Usage
-Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
-
-## Support
-Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
-
-## Roadmap
-If you have ideas for releases in the future, it is a good idea to list them in the README.
-
-## Contributing
-State if you are open to contributions and what your requirements are for accepting them.
-
-For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
-
-You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
-
-## Authors and acknowledgment
-Show your appreciation to those who have contributed to the project.
-
-## License
-For open source projects, say how it is licensed.
-
-## Project status
-If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
+Le sujet est dans le répertoire sujet, en format [pdf](sujet/tp4.pdf)
+et en format [md](sujet/tp4.md).
diff --git a/include/graph_nt.hpp b/include/graph_nt.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..3b13ceec7c3d4ca8615649495e8aecc4a0653911
--- /dev/null
+++ b/include/graph_nt.hpp
@@ -0,0 +1,98 @@
+#ifndef __GRAPH_HPP__
+#define __GRAPH_HPP__
+
+#include <exception>
+#include <map>
+#include <vector>
+#include <string>
+#include <algorithm>
+
+// Searches an element in the container, if it exists, it is removed
+template<class C, class E>
+void find_remove(C &cont, E elem)
+{
+    auto pos = cont.find(elem);
+    if (pos != end(cont))
+        cont.erase(pos);
+}
+
+// Exception : wrong node identifier
+class NodeNotFound {
+    const int id;
+public:
+    NodeNotFound(int node_id) : id(node_id) {}
+    std::string msg() const {
+        std::string m = "Node " + std::to_string(id) + " not found";
+        return m;
+    }
+};
+
+// Exception : wrong node identifier 
+class EdgeNotFound {
+    const int id;
+public:
+    EdgeNotFound(int edge_id) : id(edge_id) {}
+    std::string msg() const {
+        std::string m = "Edge " + std::to_string(id) + " not found";
+        return m;
+    }
+};
+
+class Graph {
+    struct Node {
+        int node_id;
+        std::string data;
+    };
+    struct Edge {
+        int edge_id;
+        std::string data;
+        int source_id;
+        int dest_id;
+    };
+
+    /* data structures */
+    std::map<int, Node> nodes;
+    std::map<int, Edge> edges;
+    std::map<int, std::vector<int>> dests;
+    int id_counter;
+    int edge_counter;
+
+public:
+
+    Graph() { /*todo*/ }
+    Graph(const Graph &other) { /* todo */ }
+
+    inline int add_node(const std::string &m) { /* todo */ return -1; }
+
+    inline bool node_exist(int id) const { /* todo */ return false; }
+
+    inline int add_edge(const std::string &m, int source_id, int dest_id)
+        { /* todo */ return -1;}
+    
+    inline void remove_node(int node_id) { /* todo */ }
+
+    inline int search_node(const std::string &m) const { /* todo */ return 0;}
+    
+    inline std::string get_node_data(int node_id) const { /* todo */ return "TODO";}
+
+    inline std::string get_edge_data(int edge_id) const { /* todo */ return "TODO";}
+
+    inline int get_edge_source(int edge_id) const { /* todo */ return -1;}
+    
+    inline int get_edge_dest(int edge_id) const { /* todo */ return -1;}
+
+    std::vector<int> get_successors(int node_id) const { /* todo */ return std::vector<int>();}
+    
+    std::vector<int> get_predecessors(int node_id) const { /* todo */ return std::vector<int>();}
+
+    using Path=std::vector<int>;
+
+    std::vector<Path> all_paths(int from, int to) const
+        { /* todo */ return std::vector<Path>();}
+
+    Path shortest_path(int from, int to) const
+        { /* todo */ return Path{};}
+};
+
+
+#endif
diff --git a/include/graph_nt.hpp~ b/include/graph_nt.hpp~
new file mode 100644
index 0000000000000000000000000000000000000000..157b9ebab58db837900768443f65bcacf6d20360
--- /dev/null
+++ b/include/graph_nt.hpp~
@@ -0,0 +1,98 @@
+#ifndef __GRAPH_HPP__
+#define __GRAPH_HPP__
+
+#include <exception>
+#include <map>
+#include <vector>
+#include <string>
+#include <algorithm>
+
+// Searches an element in the container, if it exists, it is removed
+template<class C, class E>
+void find_remove(C &cont, E elem)
+{
+    auto pos = cont.find(elem);
+    if (pos != end(cont))
+        cont.erase(pos);
+}
+
+// Exception : wrong node identifier
+class NodeNotFound {
+    const int id;
+public:
+    NodeNotFound(int node_id) : id(node_id) {}
+    std::string msg() const {
+        std::string m = "Node " + std::to_string(id) + " not found";
+        return m;
+    }
+};
+
+// Exception : wrong node identifier 
+class EdgeNotFound {
+    const int id;
+public:
+    EdgeNotFound(int edge_id) : id(edge_id) {}
+    std::string msg() const {
+        std::string m = "Edge " + std::to_string(id) + " not found";
+        return m;
+    }
+};
+
+class Graph {
+    struct Node {
+        int node_id;
+        std::string data;
+    };
+    struct Edge {
+        int edge_id;
+        std::string data;
+        int source_id;
+        int dest_id;
+    };
+
+    /* data structures */
+    std::map<int, Node> nodes;
+    std::map<int, Edge> edges;
+    std::map<int, std::vector<int>> dests;
+    int id_counter;
+    int edge_counter;
+
+public:
+
+    Graph() { /*todo*/ }
+    Graph(const Graph &other) { /* todo */ }
+
+    inline int add_node(const std::string &m) { /* todo */ }
+
+    inline bool node_exist(int id) const { /* todo */ }
+
+    inline int add_edge(const std::string &m, int source_id, int dest_id)
+        { /* todo */ }
+    
+    inline void remove_node(int node_id) { /* todo */ }
+
+    inline int search_node(const std::string &m) const { /* todo */ }
+    
+    inline std::string get_node_data(int node_id) const { /* todo */ }
+
+    inline std::string get_edge_data(int edge_id) const { /* todo */ }
+
+    inline int get_edge_source(int edge_id) const { /* todo */ }
+    
+    inline int get_edge_dest(int edge_id) const { /* todo */ }
+
+    std::vector<int> get_successors(int node_id) const { /* todo */ }
+    
+    std::vector<int> get_predecessors(int node_id) const { /* todo */ }
+
+    using Path=std::vector<int>;
+
+    std::vector<Path> all_paths(int from, int to) const
+        { /* todo */ }
+
+    Path shortest_path(int from, int to) const
+        { /* todo */ }
+};
+
+
+#endif
diff --git a/sujet/graph-ex.png b/sujet/graph-ex.png
new file mode 100644
index 0000000000000000000000000000000000000000..7dadbd536077fed4149a3e932141f5b8e5a58ca5
Binary files /dev/null and b/sujet/graph-ex.png differ
diff --git a/sujet/tp4.md b/sujet/tp4.md
new file mode 100644
index 0000000000000000000000000000000000000000..a14a399fabd4bc46bf187a8ace508432af0c8fd8
--- /dev/null
+++ b/sujet/tp4.md
@@ -0,0 +1,321 @@
+
+
+# Graph library
+
+Un *graphe orienté* est une [structure de données](https://fr.wikipedia.org/wiki/Graphe_(math%25C3%25A9matiques_discr%25C3%25A8tes)) utilisé en
+mathématique et en informatique pour représenter des réseaux
+d'objets. Le but de ce TP est de construire une *graph template
+library* pour créer et manipuler de graphes.
+
+
+## Définitions
+
+Un *graphe orienté* est un ensemble de noeuds connectés par des
+arêtes orientées (*edges*). Il est possible d'associer des objets
+(étiquettes) aux noeuds et aux arêtes. 
+
+Par exemple, on veut représenter le réseau des routes dans le
+Nord. Chaque noeud représentes une ville et les arêtes sont les
+routes. Dans ce cas, à chaque noeud on associe un objet de type
+`Ville` et à chaque arête on associe un objet de type `Route`.
+
+![img](graph-ex.png)
+
+Il y a plusieurs manières de [représenter un graphe](https://en.wikipedia.org/wiki/Graph_(abstract_data_type)#Common_Data_Structures_for_Graph_Representation).
+Dans ce TP nous utiliserons une implémentation très simple. 
+
+-   Chaque noeud est réprésenté par une structure de donné. Un noeud
+    possede un entier non négatif qui sert comme identifiant unique,
+    et l'objet associé au noeud (par exemple une string) ;
+-   La liste de noeuds est gardé dans une *map* qui associé à chaque
+    identifiants le noeud correspondant ;
+-   Une arête est une couple de noeuds, et elle est associé avec une
+    étiquette ; elle possede un identifiant unique.
+-   La liste des arêtes est gardée aussi dans une *map* qui associé à
+    chaque identifiant la structure representant l'arête.
+-   Finalement, on utilise une *map* qui associe à chaque noeuds la
+    liste des arêtes *sortantes* du noeud (c'est à dire : les arêtes
+    qui ont le noeud comme source).
+
+Voici la squelette de code que nous utiliserons. 
+
+    class Graph {
+        struct Node {
+            int node_id;
+            std::string data;
+        };
+        struct Edge {
+            int edge_id;
+            std::string data;
+            int source_id;
+            int dest_id;
+        };
+    
+        /* data structures */
+        std::map<int, Node> nodes;
+        std::map<int, Edge> edges;
+        std::map<int, std::vector<int>> dests;
+        int id_counter;
+        int edge_counter;
+    
+    public:
+    
+        Graph() : id_counter{0}, edge_counter{0} {}
+        Graph(const Graph &other) :
+            nodes(other.nodes), edges(other.edges), dests(other.dests), 
+            id_counter(other.id_counter), edge_counter(other.edge_counter) {}
+    
+        inline int add_node(const std::string &m) { /* todo */ }
+    
+        inline bool node_exist(int id) const { /* todo */ }
+    
+        inline int add_edge(const std::string &m, int source_id, int dest_id) {
+            /* todo */
+        }
+    
+        inline void remove_node(int node_id) {
+            /* todo */
+        }
+    
+        inline int search_node(const std::string &m) const {
+            /* todo */
+        }
+    
+        inline std::string get_node_data(int node_id) const {
+            /* todo */
+        }
+    
+        inline std::string get_edge_data(int edge_id) const {
+            /* todo */
+        }
+    
+        inline int get_edge_source(int edge_id) const {
+            /* todo */
+        }
+    
+        inline int get_edge_dest(int edge_id) const {
+            /* todo */
+        }
+    
+        std::vector<int> get_successors(int node_id) const {
+            /* todo */
+        }
+    
+        std::vector<int> get_predecessors(int node_id) const {
+            /* todo */
+        }
+    
+        using Path=std::vector<int>;
+    
+        std::vector<Path> all_paths(int from, int to) const {
+            /* todo */
+        }
+        Path shortest_path(int from, int to) const {
+            /* todo */
+        }
+    };
+
+Le but est de faire une librairie. D'abord on développe une classe
+non-template, ou les noeuds et les arêtes sont associés à des
+strings.
+
+
+## Question 1 : coder la classe
+
+Les méthodes à implémenter:
+
+-   un *copy constructor* ;
+-   `add_node` ajoute un noeud dans le graph et retourne
+    l'identifiant unique du noeud (un entier).
+-   `add_edge` ajoute une arête entre deux noeuds à partir de leur
+    ids. Il retourne l'identifiant unique de l'arête (un entier).
+-   `get_node_data()` et `get_edge_data()` retournent les contenus à partir des ids.
+-   pour une arête, `get_source` et `get_dest` retournent les
+    identifiants des noeuds source et destination.
+-   pour un noeud, `get_successors` retourne un vecteur d'arêtes
+    sortants; `get_predecessor` retourne un vecteur d'arêtes entrants
+-   Un `Path` est juste un vecteur d'arêtes qui marque un chemin dans
+    le graphe.
+-   La fonction `all_paths` retourne la liste de tous les chemins
+    possibles d'un noeud `from` au noeud `to`. Si aucun chemin
+    existe, il retourne un vecteur vide. **Attention : pour simplifier
+    le codage, on assume que le graphe ne contient jamais de boucles !**
+-   Pour l'algorithme `shortest_path`, on utilisera [l'algorithme de Djikstra](https://en.wikipedia.org/wiki/Dijkstra%2527s_algorithm#Description).
+
+Implémenter la classe. Écrire de tests pour vérifier qu'elle
+fonctionne correctement.
+
+
+## Question 2 : template
+
+Généraliser la classe Graph pour associer aux noeuds et aux arêtes
+des objets d'un type quelconque :
+
+-   La classe Graph devient une classe template: 
+    
+        template<class ND, class ED> 
+        class Graph {
+         ...
+        };
+
+-   Les fonctions `get_node_data(int node_id)` et `get_edge_data(int
+         edge_id)` doivent retourner les objets correspondants :
+    
+        template<class ND, class ED> 
+        class Graph {
+         ...
+        public:
+            ND get_node_data(int node_id) const { /* todo */ }
+            ED get_edge_data(int edge_id) const { /* todo */ }
+        };
+
+Vérifier que les tests sont encore correctes quand on spécifie des
+strings pour cette classe. 
+
+Suggestion : codez la classe template dans un fichier différent,
+par exemple dans `graph_t.hpp`. Il faut inclure l'un ou l'autre. 
+
+
+## Question 3: Décoration
+
+On voudrait décorer les arêtes avec des informations
+supplémentaires sans forcement modifier la classe associé aux
+arêtes.
+
+Par exemple, supposez que le graphe représente les routes dans le
+département du Nord. On associé un `std::string` aux arêtes avec le
+nom de la route. Plus tard, on voudrais ajouter l'information sur
+la longueur en Km de la route.
+
+Voici comment on fait: 
+
+-   On prépare une classe template variadique `EdgeData` qui contient
+    des strings.
+    
+        template <typename ...Tp>
+        class EdgeData : public Tp ... {
+            std::string str;
+        public:
+            void set_string(const std::string &s) { str = s; }
+            std::string get_string() const { return str; }
+        };
+
+-   On déclare une class `RouteLenght` : 
+    
+        class RouteLenght {
+            double l;
+        public:
+            void set_lenght(double len) { l = len; }
+            double get_lenght(double len) const { return l; }       
+        };
+
+-   Maintenant, la classe `EdgeData<RouteLenght>` contient une
+    `string` et un `double`, et on peut l'utiliser comme dans le code
+    suivant :
+    
+        EdgeData<RouteLenght> data;
+        data.set_string("Lille-Valenciennes");
+        data.set_lenght(44.14);
+
+-   On déclare une instance de `Graph` ayant comme paramètre la classe
+    `EdgeData<RouteLenght>` :
+    
+        Graph<string, EdgeData<RouteLenght>> mygraph;
+        
+        int lille = mygraph.add_node("Lille");
+        int valen = mygraph.add_node("Valenciennes");
+        mygraph.add_edge(data, lille, valen);
+
+Tester le bon fonctionnement de cette technique. Ajouter aussi une
+deuxième propriété `AverageTime` pour mémoriser le temps moyenne de
+parcours d'une route, et vérifier que tout fonctionne correctement.
+
+
+## Question 4: Généralisation de `shortest_path`
+
+On voudrait spécialiser `shortest_path` pour prendre en compte une
+propriété générique des edges. Par exemple, dans la cas d'un graphe
+qui représente les routes du département, on voudrais calculer le
+parcours plus court, ou le parcours avec le plus grande nombre de
+stations d'essence, etc.
+
+Pour faire ça, la méthode devient une méthode *template* qui prends
+comme paramètre une fonction d'évaluation de la métrique
+sur les arêtes. 
+
+Écrire la méthode template `shortest_path`, et tester avec la
+classe `EdgeData<RouteLenght>` implémentée dans la
+question précédente.
+
+
+## Question 5: shared pointers
+
+Dans les questions précédentes il n'y a pas manière de changer
+les informations associés aux noeuds et aux arêtes. Pour permettre
+ça, on va changer de strategie.
+
+-   Dans la struct `Node` et dans la struct `Edge` on memorise un
+    `shared_ptr` vers l'objet associé
+    
+        template <typename N, typename E> 
+        class Graph {
+            struct Node {
+                int node_id;
+                std::shared_ptr<N> data;
+            };
+            struct Edge {
+                int edge_id;
+                int source_id;
+                int dest_id;
+                std::shared_ptr<E> data;
+            };
+           /* etc. */
+        };
+
+-   Les fonctions `get_node_data_` et `get_edge_data` retournent un `shared_ptr<>`
+    vers l'objet associé qu'on peut modifier après:
+    
+        std::shared_ptr<N> get_node(int node_id) {/*todo*/}
+        std::shared_ptr<E> get_edge(int edge_id) {/*todo*/}      
+    
+    Implémentez cette nouvelle version.
+
+-   Tester le scénario suivant :
+    1.  Un utilisateur crée un graphe de distances entre des villes
+        dans le département du nord.
+    2.  Il calcule le chemin minimale en utilisant la technique
+        implémenté à la question 4.
+    3.  Il modifie un distance.
+    4.  Il recalcule la chemin optimale et il obtient un parcours
+        différent.
+
+-   Tester qu'une référence obtenue avec `get_node_data()` est toujours
+    valide après avoir détruit le graphe.
+
+
+## Question 6: copie profonde
+
+Le copy constructor par défaut fait une copie *shallow* du graph,
+et donc les objets pointés par le `shared_ptr` ne sont par copiés.
+
+Ajouter une fonction pour faire la copie profonde du graph:
+
+    template <typename N, typename E>
+    class Graph {
+        /* ... */
+    public:
+        Graph() {}
+        Graph (const Graph &other) { /* shallow copy */ }
+        Graph deep_copy() const { /* deep copy */ }
+        /* ... */
+     }; 
+
+La fonction vérifie si le type `N` est polymorphique: si oui, on
+utilise la fonction `clone`, si non on utilise le copy
+constructor. Même chose pour le type `E` (il faut utiliser la
+technique `if constexpr` du C++17 vue en cours).
+
+Tester que le copie sont effectué correctement: en particulier,
+dans le cas d'une copie profonde, si on modifie l'objet original,
+la copie n'est pas modifiée.
+
diff --git a/sujet/tp4.org b/sujet/tp4.org
new file mode 100644
index 0000000000000000000000000000000000000000..6d2c387887008a7ac42844b61a2cb0a51b509db6
--- /dev/null
+++ b/sujet/tp4.org
@@ -0,0 +1,372 @@
+#+OPTIONS:  toc:nil ^:nil num:nil
+
+#+BEGIN_SRC emacs-lisp :exports none :results silent
+  (setq org-latex-minted-options
+	'(("frame" "lines")
+          ;;("bgcolor" "mybg")
+          ;;("fontsize" "\\scriptsize")
+          ("mathescape" "")
+          ("samepage" "")
+          ("xrightmargin" "0.5cm")
+          ("xleftmargin"  "0.5cm")
+;;	  ("escapeinside" "@@")
+          ))
+#+END_SRC
+
+#+TITLE: Construction Objets Avancée : TP 4
+
+* Graph library 
+
+  Un /graphe orienté/ est une [[https://fr.wikipedia.org/wiki/Graphe_(math%25C3%25A9matiques_discr%25C3%25A8tes)][structure de données]] utilisé en
+  mathématique et en informatique pour représenter des réseaux
+  d'objets. Le but de ce TP est de construire une /graph template
+  library/ pour créer et manipuler de graphes.
+
+
+** Définitions
+
+   Un /graphe orienté/ est un ensemble de noeuds connectés par des
+   arêtes orientées (/edges/). Il est possible d'associer des objets
+   (étiquettes) aux noeuds et aux arêtes. 
+
+   Par exemple, on veut représenter le réseau des routes dans le
+   Nord. Chaque noeud représentes une ville et les arêtes sont les
+   routes. Dans ce cas, à chaque noeud on associe un objet de type
+   =Ville= et à chaque arête on associe un objet de type =Route=.
+
+   #+BEGIN_SRC dot :file graph-ex.png
+     digraph nord {
+        Lille -> Bethune;
+        Lille -> Lens;
+        Lille -> { Douai Valenciennes };
+        subgraph { rank = same; Douai; Valenciennes; }; 
+        Bethune -> Lens;
+        Lens -> Douai;
+        Douai -> Valenciennes;
+        Lens -> Arras;
+        Douai -> Arras;
+     }
+   #+END_SRC
+
+   #+attr_latex: :float t :width .4\textwidth
+   #+RESULTS:
+   [[file:graph-ex.png]]
+
+
+   Il y a plusieurs manières de [[https://en.wikipedia.org/wiki/Graph_(abstract_data_type)#Common_Data_Structures_for_Graph_Representation][représenter un graphe]].
+   Dans ce TP nous utiliserons une implémentation très simple. 
+   - Chaque noeud est réprésenté par une structure de donné. Un noeud
+     possede un entier non négatif qui sert comme identifiant unique,
+     et l'objet associé au noeud (par exemple une string) ;
+   - La liste de noeuds est gardé dans une /map/ qui associé à chaque
+     identifiants le noeud correspondant ;
+   - Une arête est une couple de noeuds, et elle est associé avec une
+     étiquette ; elle possede un identifiant unique.
+   - La liste des arêtes est gardée aussi dans une /map/ qui associé à
+     chaque identifiant la structure representant l'arête.
+   - Finalement, on utilise une /map/ qui associe à chaque noeuds la
+     liste des arêtes /sortantes/ du noeud (c'est à dire : les arêtes
+     qui ont le noeud comme source).
+
+   Voici la squelette de code que nous utiliserons. 
+
+   #+BEGIN_SRC c++
+     class Graph {
+         struct Node {
+             int node_id;
+             std::string data;
+         };
+         struct Edge {
+             int edge_id;
+             std::string data;
+             int source_id;
+             int dest_id;
+         };
+
+         /* data structures */
+         std::map<int, Node> nodes;
+         std::map<int, Edge> edges;
+         std::map<int, std::vector<int>> dests;
+         int id_counter;
+         int edge_counter;
+
+     public:
+
+         Graph() : id_counter{0}, edge_counter{0} {}
+         Graph(const Graph &other) :
+             nodes(other.nodes), edges(other.edges), dests(other.dests), 
+             id_counter(other.id_counter), edge_counter(other.edge_counter) {}
+
+         inline int add_node(const std::string &m) { /* todo */ }
+
+         inline bool node_exist(int id) const { /* todo */ }
+
+         inline int add_edge(const std::string &m, int source_id, int dest_id) {
+             /* todo */
+         }
+
+         inline void remove_node(int node_id) {
+             /* todo */
+         }
+
+         inline int search_node(const std::string &m) const {
+             /* todo */
+         }
+
+         inline std::string get_node_data(int node_id) const {
+             /* todo */
+         }
+
+         inline std::string get_edge_data(int edge_id) const {
+             /* todo */
+         }
+
+         inline int get_edge_source(int edge_id) const {
+             /* todo */
+         }
+
+         inline int get_edge_dest(int edge_id) const {
+             /* todo */
+         }
+
+         std::vector<int> get_successors(int node_id) const {
+             /* todo */
+         }
+
+         std::vector<int> get_predecessors(int node_id) const {
+             /* todo */
+         }
+
+         using Path=std::vector<int>;
+
+         std::vector<Path> all_paths(int from, int to) const {
+             /* todo */
+         }
+         Path shortest_path(int from, int to) const {
+             /* todo */
+         }
+     };
+   #+END_SRC
+
+   
+   Le but est de faire une librairie. D'abord on développe une classe
+   non-template, ou les noeuds et les arêtes sont associés à des
+   strings.
+
+** Question 1 : coder la classe
+   
+   Les méthodes à implémenter:
+   
+   - un /copy constructor/ ;
+   - =add_node= ajoute un noeud dans le graph et retourne
+     l'identifiant unique du noeud (un entier).
+   - =add_edge= ajoute une arête entre deux noeuds à partir de leur
+     ids. Il retourne l'identifiant unique de l'arête (un entier).
+   - =get_node_data()= et =get_edge_data()= retournent les contenus à partir des ids.
+   - pour une arête, =get_source= et =get_dest= retournent les
+     identifiants des noeuds source et destination.
+   - pour un noeud, =get_successors= retourne un vecteur d'arêtes
+     sortants; =get_predecessor= retourne un vecteur d'arêtes entrants
+   - Un =Path= est juste un vecteur d'arêtes qui marque un chemin dans
+     le graphe.
+   - La fonction =all_paths= retourne la liste de tous les chemins
+     possibles d'un noeud =from= au noeud =to=. Si aucun chemin
+     existe, il retourne un vecteur vide. *Attention : pour simplifier
+     le codage, on assume que le graphe ne contient jamais de boucles !*
+   - Pour l'algorithme =shortest_path=, on utilisera [[https://en.wikipedia.org/wiki/Dijkstra%2527s_algorithm#Description][l'algorithme de Djikstra]]. 
+
+   Implémenter la classe. Écrire de tests pour vérifier qu'elle
+   fonctionne correctement.
+     
+** Question 2 : template 
+
+   Généraliser la classe Graph pour associer aux noeuds et aux arêtes
+   des objets d'un type quelconque :
+
+   - La classe Graph devient une classe template: 
+
+     #+BEGIN_SRC c++
+       template<class ND, class ED> 
+       class Graph {
+        ...
+       };
+     #+END_SRC
+
+   - Les fonctions =get_node_data(int node_id)= et =get_edge_data(int
+     edge_id)= doivent retourner les objets correspondants :
+
+     #+BEGIN_SRC c++
+       template<class ND, class ED> 
+       class Graph {
+        ...
+       public:
+           ND get_node_data(int node_id) const { /* todo */ }
+           ED get_edge_data(int edge_id) const { /* todo */ }
+       };
+
+     #+END_SRC
+
+
+   Vérifier que les tests sont encore correctes quand on spécifie des
+   strings pour cette classe. 
+
+   Suggestion : codez la classe template dans un fichier différent,
+   par exemple dans =graph_t.hpp=. Il faut inclure l'un ou l'autre. 
+
+
+** Question 3: Décoration
+
+   On voudrait décorer les arêtes avec des informations
+   supplémentaires sans forcement modifier la classe associé aux
+   arêtes.
+
+   Par exemple, supposez que le graphe représente les routes dans le
+   département du Nord. On associé un =std::string= aux arêtes avec le
+   nom de la route. Plus tard, on voudrais ajouter l'information sur
+   la longueur en Km de la route.
+
+   Voici comment on fait: 
+  
+   - On prépare une classe template variadique =EdgeData= qui contient
+     des strings.
+     #+BEGIN_SRC C++
+       template <typename ...Tp>
+       class EdgeData : public Tp ... {
+	   std::string str;
+       public:
+           void set_string(const std::string &s) { str = s; }
+           std::string get_string() const { return str; }
+       };
+     #+END_SRC
+
+   - On déclare une class =RouteLenght= : 
+
+     #+BEGIN_SRC C++
+     class RouteLenght {
+         double l;
+     public:
+         void set_lenght(double len) { l = len; }
+         double get_lenght(double len) const { return l; }       
+     };
+     #+END_SRC
+
+   - Maintenant, la classe =EdgeData<RouteLenght>= contient une
+     =string= et un =double=, et on peut l'utiliser comme dans le code
+     suivant :
+
+     #+BEGIN_SRC c++
+       EdgeData<RouteLenght> data;
+       data.set_string("Lille-Valenciennes");
+       data.set_lenght(44.14);
+     #+END_SRC
+
+   - On déclare une instance de =Graph= ayant comme paramètre la classe
+     =EdgeData<RouteLenght>= :
+     #+BEGIN_SRC c++
+     Graph<string, EdgeData<RouteLenght>> mygraph;
+
+     int lille = mygraph.add_node("Lille");
+     int valen = mygraph.add_node("Valenciennes");
+     mygraph.add_edge(data, lille, valen);
+     #+END_SRC
+
+   Tester le bon fonctionnement de cette technique. Ajouter aussi une
+   deuxième propriété =AverageTime= pour mémoriser le temps moyenne de
+   parcours d'une route, et vérifier que tout fonctionne correctement.
+
+
+** Question 4: Généralisation de =shortest_path=
+
+   On voudrait spécialiser =shortest_path= pour prendre en compte une
+   propriété générique des edges. Par exemple, dans la cas d'un graphe
+   qui représente les routes du département, on voudrais calculer le
+   parcours plus court, ou le parcours avec le plus grande nombre de
+   stations d'essence, etc.
+
+   Pour faire ça, la méthode devient une méthode /template/ qui prends
+   comme paramètre une fonction d'évaluation de la métrique
+   sur les arêtes. 
+
+   Écrire la méthode template =shortest_path=, et tester avec la
+   classe =EdgeData<RouteLenght>= implémentée dans la
+   question précédente.
+   
+
+** Question 5: shared pointers
+
+   Dans les questions précédentes il n'y a pas manière de changer
+   les informations associés aux noeuds et aux arêtes. Pour permettre
+   ça, on va changer de strategie.
+
+   - Dans la struct =Node= et dans la struct =Edge= on memorise un
+     =shared_ptr= vers l'objet associé
+
+     #+BEGIN_SRC C++
+       template <typename N, typename E> 
+       class Graph {
+           struct Node {
+               int node_id;
+               std::shared_ptr<N> data;
+           };
+           struct Edge {
+               int edge_id;
+               int source_id;
+               int dest_id;
+               std::shared_ptr<E> data;
+           };
+          /* etc. */
+       };
+     #+END_SRC
+
+   - Les fonctions =get_node_data_= et =get_edge_data= retournent un =shared_ptr<>=
+     vers l'objet associé qu'on peut modifier après:
+
+     #+BEGIN_SRC C++
+	std::shared_ptr<N> get_node(int node_id) {/*todo*/}
+	std::shared_ptr<E> get_edge(int edge_id) {/*todo*/}      
+     #+END_SRC
+
+     Implémentez cette nouvelle version. 
+
+   - Tester le scénario suivant :
+
+     1) Un utilisateur crée un graphe de distances entre des villes
+        dans le département du nord.
+     2) Il calcule le chemin minimale en utilisant la technique
+        implémenté à la question 4.
+     3) Il modifie un distance.
+     4) Il recalcule la chemin optimale et il obtient un parcours
+        différent.
+
+   - Tester qu'une référence obtenue avec =get_node_data()= est toujours
+     valide après avoir détruit le graphe.
+
+
+** Question 6: copie profonde    
+
+   Le copy constructor par défaut fait une copie /shallow/ du graph,
+   et donc les objets pointés par le =shared_ptr= ne sont par copiés.
+
+   Ajouter une fonction pour faire la copie profonde du graph:
+   #+BEGIN_SRC C++
+   template <typename N, typename E>
+   class Graph {
+       /* ... */
+   public:
+       Graph() {}
+       Graph (const Graph &other) { /* shallow copy */ }
+       Graph deep_copy() const { /* deep copy */ }
+       /* ... */
+    }; 
+   #+END_SRC
+
+   La fonction vérifie si le type =N= est polymorphique: si oui, on
+   utilise la fonction =clone=, si non on utilise le copy
+   constructor. Même chose pour le type =E= (il faut utiliser la
+   technique =if constexpr= du C++17 vue en cours).
+
+   Tester que le copie sont effectué correctement: en particulier,
+   dans le cas d'une copie profonde, si on modifie l'objet original,
+   la copie n'est pas modifiée.
+
+
diff --git a/sujet/tp4.pdf b/sujet/tp4.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..f5930a9b62fd6c099534425d70b75fe25a6b8b73
Binary files /dev/null and b/sujet/tp4.pdf differ
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3cfed2a1ea97174776714221d0c379c658d627dd
--- /dev/null
+++ b/test/CMakeLists.txt
@@ -0,0 +1,22 @@
+cmake_minimum_required(VERSION 3.11)
+
+Include(FetchContent)
+
+FetchContent_Declare(
+  Catch2
+  GIT_REPOSITORY https://github.com/catchorg/Catch2.git
+  GIT_TAG        v3.0.1)
+
+FetchContent_MakeAvailable(Catch2)
+
+add_executable(test_gnt test_gnt.cpp)
+target_compile_features(test_gnt PRIVATE cxx_std_17)  
+if (WIN32)
+  target_compile_options(test_gnt PRIVATE /WX /W4)
+else()
+  target_compile_options(test_gnt PRIVATE -Wall)  
+endif()
+target_include_directories(test_gnt PUBLIC ${PROJECT_SOURCE_DIR}/include)
+target_link_libraries(test_gnt PRIVATE Catch2::Catch2WithMain)
+add_test (NAME test_gnt COMMAND test_gnt)
+
diff --git a/test/CMakeLists.txt~ b/test/CMakeLists.txt~
new file mode 100644
index 0000000000000000000000000000000000000000..5b526f6a9448e9395694fdb833d8f862b49102b4
--- /dev/null
+++ b/test/CMakeLists.txt~
@@ -0,0 +1,21 @@
+cmake_minimum_required(VERSION 3.11)
+
+Include(FetchContent)
+
+FetchContent_Declare(
+  Catch2
+  GIT_REPOSITORY https://github.com/catchorg/Catch2.git
+  GIT_TAG        v3.0.1)
+
+FetchContent_MakeAvailable(Catch2)
+
+add_executable(test_gnt test_gnt.cpp)
+target_compile_features(test_gnt PRIVATE cxx_std_17)  
+if (WIN32)
+  target_compile_options(test_gnt PRIVATE /WX /W4)
+else()
+  target_compile_options(test_gnt PRIVATE -Wall)  
+endif()
+target_include_directories(test_gnt PUBLIC ${PROJECT_SOURCE_DIR}/include)
+target_link_libraries(test_gnt PRIVATE Catch2::Catch2WithMain)
+
diff --git a/test/test.cpp b/test/test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2b1ce8dd5d6869509f358b13da1cff88bb286e06
--- /dev/null
+++ b/test/test.cpp
@@ -0,0 +1,10 @@
+#define CATCH_CONFIG_MAIN
+#include <catch2/catch.hpp>
+
+
+TEST_CASE("null test", "[nil]")
+{
+    REQUIRE(true);
+}
+
+
diff --git a/test/test_gnt.cpp b/test/test_gnt.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fd14267880d826a3c870f192812db9c9d1b75d08
--- /dev/null
+++ b/test/test_gnt.cpp
@@ -0,0 +1,82 @@
+#include <catch2/catch_test_macros.hpp>
+#include "graph_nt.hpp"
+
+using namespace std;
+
+SCENARIO("Graph properties", "[graph nt]")
+{
+    GIVEN("A graph with some items") {
+        Graph g;
+        int a = g.add_node("A");
+        int b = g.add_node("B");
+        int c = g.add_node("C");
+        int d = g.add_node("D");
+        int ab = g.add_edge("msg-ab", a,b);
+        int ac = g.add_edge("msg-ac", a,c);
+        int bd = g.add_edge("msg-bd", b,d);
+        int cd = g.add_edge("msg-cd", c,d);
+    
+        REQUIRE(g.get_node_data(a) == "A");
+        REQUIRE(g.get_node_data(b) == "B");
+        REQUIRE(g.get_node_data(c) == "C");
+        REQUIRE(g.get_node_data(d) == "D");
+        
+        REQUIRE(ab == 0);
+        REQUIRE(ac == 1);
+        
+        REQUIRE(g.get_edge_data(ab) == "msg-ab");
+        REQUIRE(g.get_edge_data(ac) == "msg-ac");
+        REQUIRE(g.get_edge_data(cd) == "msg-cd");
+        REQUIRE(g.get_edge_data(bd) == "msg-bd");
+
+        vector<int> succ_a = {b, c};
+        REQUIRE(g.get_successors(a) == succ_a);
+        vector<int> succ_b = {d};
+        REQUIRE(g.get_successors(b) == succ_b);
+        vector<int> prec_d = {b, c};
+        REQUIRE(g.get_predecessors(d) == prec_d);
+
+        WHEN("Copying the graph") {
+            Graph g1(g);
+            THEN ("We obtain the same graph, with the same indexes") {
+                REQUIRE(g1.get_predecessors(d) == prec_d);
+            }
+        }
+        WHEN("Changing the copy") {
+            Graph g2(g);
+            g2.add_edge("msg-bc", b,c);
+            THEN ("The original is not changed") {
+                REQUIRE(g.get_successors(b).size() == 1);
+                REQUIRE(g2.get_successors(b).size() == 2);
+            }
+        }
+    }
+}
+
+
+SCENARIO("Computing paths", "[graph nt]")
+{
+    GIVEN("A graph with some elements and no loops") {
+        Graph g;
+        int a = g.add_node("A");
+        int b = g.add_node("B");
+        int c = g.add_node("C");
+        int d = g.add_node("D");
+        int e = g.add_node("E");
+
+        g.add_edge("ab", a, b);
+        g.add_edge("ac", a, c);
+        g.add_edge("bc", b, c);
+        g.add_edge("bd", b, d);
+        g.add_edge("cd", c, d);
+        g.add_edge("de", d, e);
+        g.add_edge("ce", c, e);
+
+        WHEN ("Computing all paths") {
+            auto paths = g.all_paths(a, e);
+            REQUIRE(paths.size() == 5);
+        }
+    }
+}
+
+/** AJOUTER DES TESTS ... */
diff --git a/test/test_gnt.cpp~ b/test/test_gnt.cpp~
new file mode 100644
index 0000000000000000000000000000000000000000..c7a39641cfada4033ce1cc701603c9b22457cdff
--- /dev/null
+++ b/test/test_gnt.cpp~
@@ -0,0 +1,82 @@
+#include <catch2/catch.hpp>
+#include "graph_nt.hpp"
+
+using namespace std;
+
+SCENARIO("Graph properties", "[graph nt]")
+{
+    GIVEN("A graph with some items") {
+        Graph g;
+        int a = g.add_node("A");
+        int b = g.add_node("B");
+        int c = g.add_node("C");
+        int d = g.add_node("D");
+        int ab = g.add_edge("msg-ab", a,b);
+        int ac = g.add_edge("msg-ac", a,c);
+        int bd = g.add_edge("msg-bd", b,d);
+        int cd = g.add_edge("msg-cd", c,d);
+    
+        REQUIRE(g.get_node_data(a) == "A");
+        REQUIRE(g.get_node_data(b) == "B");
+        REQUIRE(g.get_node_data(c) == "C");
+        REQUIRE(g.get_node_data(d) == "D");
+        
+        REQUIRE(ab == 0);
+        REQUIRE(ac == 1);
+        
+        REQUIRE(g.get_edge_data(ab) == "msg-ab");
+        REQUIRE(g.get_edge_data(ac) == "msg-ac");
+        REQUIRE(g.get_edge_data(cd) == "msg-cd");
+        REQUIRE(g.get_edge_data(bd) == "msg-bd");
+
+        vector<int> succ_a = {b, c};
+        REQUIRE(g.get_successors(a) == succ_a);
+        vector<int> succ_b = {d};
+        REQUIRE(g.get_successors(b) == succ_b);
+        vector<int> prec_d = {b, c};
+        REQUIRE(g.get_predecessors(d) == prec_d);
+
+        WHEN("Copying the graph") {
+            Graph g1(g);
+            THEN ("We obtain the same graph, with the same indexes") {
+                REQUIRE(g1.get_predecessors(d) == prec_d);
+            }
+        }
+        WHEN("Changing the copy") {
+            Graph g2(g);
+            g2.add_edge("msg-bc", b,c);
+            THEN ("The original is not changed") {
+                REQUIRE(g.get_successors(b).size() == 1);
+                REQUIRE(g2.get_successors(b).size() == 2);
+            }
+        }
+    }
+}
+
+
+SCENARIO("Computing paths", "[graph nt]")
+{
+    GIVEN("A graph with some elements and no loops") {
+        Graph g;
+        int a = g.add_node("A");
+        int b = g.add_node("B");
+        int c = g.add_node("C");
+        int d = g.add_node("D");
+        int e = g.add_node("E");
+
+        g.add_edge("ab", a, b);
+        g.add_edge("ac", a, c);
+        g.add_edge("bc", b, c);
+        g.add_edge("bd", b, d);
+        g.add_edge("cd", c, d);
+        g.add_edge("de", d, e);
+        g.add_edge("ce", c, e);
+
+        WHEN ("Computing all paths") {
+            auto paths = g.all_paths(a, e);
+            REQUIRE(paths.size() == 5);
+        }
+    }
+}
+
+/** AJOUTER DES TESTS ... */