diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 95c2cb156fc0e4f4a4654f8b65304c1bb982adf7..94a25f7f4cb416c083d265558da75d457237d671 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,6 +2,5 @@ - \ No newline at end of file diff --git a/m1graphs25 b/m1graphs25 deleted file mode 160000 index 3baf9d19e40b5b9ef6e31f7deb15413679dcf68e..0000000000000000000000000000000000000000 --- a/m1graphs25 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3baf9d19e40b5b9ef6e31f7deb15413679dcf68e diff --git a/src/main/java/m1graphs2025/Edge.java b/src/main/java/m1graphs2025/Edge.java index 14ef5e012baf8941461d3849cd8f4a2920566b49..0527c3decf55ac294812951f0fa6a7ff6d1967a6 100644 --- a/src/main/java/m1graphs2025/Edge.java +++ b/src/main/java/m1graphs2025/Edge.java @@ -134,7 +134,7 @@ public class Edge implements Comparable { /** * Retourne une arête symétrique (inversée) avec la même pondération. - * + * * @return une nouvelle arête inversée ou null si les nœuds n’appartiennent pas * au même graphe. */ diff --git a/src/main/java/m1graphs2025/Graph.java b/src/main/java/m1graphs2025/Graph.java index f565262a07584ec06b0d3675f8976c0cf14baa74..a4bf39c04161dc594756347463fc826fa3184ecc 100644 --- a/src/main/java/m1graphs2025/Graph.java +++ b/src/main/java/m1graphs2025/Graph.java @@ -101,10 +101,14 @@ public class Graph { if (entry != null && entry.getId() == id) return entry; } - System.err.println("Aucun nœud trouvé avec l’id " + id + "."); + return null; } + public Node getOrCreateNode(int id) { + return getNode(id) != null ? getNode(id) : new Node(id, this); + } + public boolean addNode(Node n) { if (n == null) { System.err.println("Erreur: tentative d’ajout d’un nœud null."); @@ -983,51 +987,135 @@ public class Graph { // DOT I/O // ====================== + /** + * Builds a {@link Graph} instance by parsing a DOT-like file. + *

+ * The expected format supports both isolated nodes and weighted edges. + * Examples of valid lines: + *

+ *

+ * + * @param filename the path to the DOT file to parse + * @return a new {@link Graph} built from the file contents + * @throws RuntimeException if the file cannot be read or parsed + */ + /** + * Reads a directed graph from a DOT-like file. + * Supports lines such as: + * 1 -> 2 [label="5"]; + * 3 + * (isolated node) + * + * @param filename the path to the DOT file + * @return a Graph instance representing the file content + * @throws RuntimeException if file reading fails + */ public static Graph fromDotFile(String filename) { - BufferedReader dot = new BufferedReader(new FileReader(filename)); Map> adjEdList = new HashMap<>(); Graph g = new Graph(adjEdList); - BufferedReader dot = new BufferedReader(new FileReader(filename)); - List nodes = new ArrayList<>(); - - String line = dot.readLine(); - while (line != null) { - int iteration = 0; - Edge edge = new Edge(); - Node nFrom = new Node(); - Node nTo = new Node(); - String[] tokens = line.split("[^0-9]"); - if (!tokens[0].equals("")) { - if (!nodes.contains(Integer.parseInt(tokens[0]))) { - List listEdges = new ArrayList<>(); + + try (BufferedReader reader = new BufferedReader(new FileReader(filename))) { + String line; + + while ((line = reader.readLine()) != null) { + line = line.trim(); + if (line.isEmpty()) + continue; // skip empty lines + + // --- Case 1: isolated node + if (line.matches("^\\d+$")) { + int id = Integer.parseInt(line); + Node node = g.getOrCreateNode(id); + adjEdList.putIfAbsent(node, new ArrayList<>()); + continue; } - nFrom.setId(Integer.parseInt(tokens[0])); - edge.setNodeFrom(nFrom); - nTo.setId(Integer.parseInt(tokens[1])); - edge.setNodeTo(nTo); - if (tokens[2] != "") { - edge.setWeight(Integer.parseInt(tokens[2])); + + // --- Case 2: edge line + String[] tokens = line.replaceAll("[^0-9]+", " ").trim().split("\\s+"); + if (tokens.length < 2) + continue; // invalid line → skip + + int fromId = Integer.parseInt(tokens[0]); + int toId = Integer.parseInt(tokens[1]); + + Node from = g.getOrCreateNode(fromId); + Node to = g.getOrCreateNode(toId); + + Edge edge; + if (tokens.length >= 3) { + int weight = Integer.parseInt(tokens[2]); + edge = new Edge(from, to, weight); + } else { + edge = new Edge(from, to); } + // Add to adjacency list + adjEdList.computeIfAbsent(from, k -> new ArrayList<>()).add(edge); + adjEdList.putIfAbsent(to, new ArrayList<>()); // ensure target node exists } + } catch (IOException e) { + throw new RuntimeException("Error reading file: " + filename, e); } + + return g; } + /** + * Generates a DOT-format string representation of this graph. + *

+ * Example output for a directed graph: + * + *

+     * digraph G {
+     *     rankdir=LR;
+     *     1 -> 2 [label="5"];
+     *     2 -> 3;
+     *     4;
+     * }
+     * 
+ *

+ * + * @return the DOT-format representation of the graph + */ public String toDotString() { + boolean isDirected = true; // adapt this if you support undirected graphs + String arrow = isDirected ? " -> " : " -- "; + StringBuilder sb = new StringBuilder(); - sb.append("digraph G {\n"); - List edges = getAllEdges(); - edges.sort(Comparator.comparingInt((Edge e) -> e.getNodeFrom().getId()) - .thenComparingInt(e -> e.getNodeTo().getId())); - for (Edge e : edges) { - sb.append(e.getNodeFrom().getId()).append(" -> ").append(e.getNodeTo().getId()); - if (e.isWeighted()) { - sb.append(" [label=").append(e.getWeight()).append(", len=").append(e.getWeight()).append("]"); + sb.append(isDirected ? "digraph {\n" : "graph {\n"); + sb.append(" rankdir=LR;\n"); + + for (Map.Entry> entry : adjEdList.entrySet()) { + Node from = entry.getKey(); + List edges = entry.getValue(); + + // Case: isolated node (no outgoing edges) + if (edges.isEmpty()) { + sb.append(" ").append(from.getId()).append(";\n"); + continue; + } + + for (Edge e : edges) { + sb.append(" ") + .append(e.from().getId()) + .append(arrow) + .append(e.to().getId()); + + // Add weight label if present + if (e.getWeight() != null) { + sb.append(" [label=\"").append(e.getWeight()).append("\"]"); + } + sb.append(";\n"); } - sb.append("\n"); } - sb.append("}\n"); + + sb.append("}"); return sb.toString(); } diff --git a/src/main/java/m1graphs2025/TestGraphsPW2.java b/src/main/java/m1graphs2025/TestGraphsPW2.java index 6f9fb9b84331dd56967c7253361da598d2dcfd0c..96a9d7c7fadb533cf2a442ba56f57d9c7fb57f25 100644 --- a/src/main/java/m1graphs2025/TestGraphsPW2.java +++ b/src/main/java/m1graphs2025/TestGraphsPW2.java @@ -1,4 +1,4 @@ -package m1graphs2025; +/**package m1graphs2025; import java.util.Arrays; import java.util.Collections; @@ -407,8 +407,8 @@ public class TestGraphsPW2 { * ); * System.out.println( * "*-----------------------------------------------------------------------*"); - * - * + * + * * // lecture example but undirected * UndirectedGraph gToVisit = new UndirectedGraph( * 2, 3, 4, 6, 0, //1 @@ -424,6 +424,6 @@ public class TestGraphsPW2 { * System.out.println(gToVisit.toDotString()); */ - } + /**} -} +}**/ diff --git a/src/main/java/m1graphs2025/UndirectedGraph.java b/src/main/java/m1graphs2025/UndirectedGraph.java index 595e22fa7ec17da23e0307defd65b686f7e1d7d5..9774326b1d61dd9acb8190287f3a054f9a08e33f 100644 --- a/src/main/java/m1graphs2025/UndirectedGraph.java +++ b/src/main/java/m1graphs2025/UndirectedGraph.java @@ -1,5 +1,11 @@ package m1graphs2025; +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.*; /** @@ -20,12 +26,60 @@ public class UndirectedGraph extends Graph { } public UndirectedGraph(int... successorArray) { - super(successorArray); - // Ici, successorArray est interprété comme orienté, - // donc on doit symétriser après construction - symmetrize(); + int taille = successorArray.length; + Map> adjEdList = new HashMap<>(); + int i = 0; + int id = 1; + + // D'abord, on crée tous les nœuds pour les réutiliser (sinon on crée des doublons) + Map nodes = new HashMap<>(); + + // Première passe : créer les nœuds + int tempId = 1; + while (i < taille) { + Node node = new Node(tempId, this); + nodes.put(tempId, node); + + while (i < taille && successorArray[i] != 0) { + int succ = successorArray[i]; + if (!nodes.containsKey(succ)) { + nodes.put(succ, new Node(succ, this)); + } + i++; + } + + i++; // sauter le 0 + tempId++; + } + + // Deuxième passe : créer les arêtes bidirectionnelles + i = 0; + id = 1; + while (i < taille) { + Node node = nodes.get(id); + List list = adjEdList.computeIfAbsent(node, k -> new ArrayList<>()); + + while (i < taille && successorArray[i] != 0) { + Node nodeTo = nodes.get(successorArray[i]); + Edge edge = new Edge(node, nodeTo); + list.add(edge); + + // Ajout de l’arête inverse (bidirectionnelle) + List reverseList = adjEdList.computeIfAbsent(nodeTo, k -> new ArrayList<>()); + Edge reverseEdge = new Edge(nodeTo, node); + reverseList.add(reverseEdge); + + i++; + } + + i++; // sauter le 0 + id++; + } + + this.adjEdList = adjEdList; } + @Override public boolean addNode(Node n) { return super.addNode(n); @@ -55,39 +109,32 @@ public class UndirectedGraph extends Graph { @Override public void addEdge(Node from, Node to) { - super.addEdge(from, to); - // Ajout symétrique - if (!existsEdge(to, from)) - super.addEdge(to, from); - } - - @Override - public void addEdge(int fromId, int toId) { - super.addEdge(fromId, toId); - if (!existsEdge(toId, fromId)) - super.addEdge(toId, fromId); + super.addEdge(from, to); // u -> v + super.addEdge(to, from); // v -> u pour non dirigé } @Override public void addEdge(Node from, Node to, int weight) { super.addEdge(from, to, weight); - if (!existsEdge(to, from)) - super.addEdge(to, from, weight); + super.addEdge(to, from, weight); } @Override - public void addEdge(int fromId, int toId, int weight) { - super.addEdge(fromId, toId, weight); - if (!existsEdge(toId, fromId)) - super.addEdge(toId, fromId, weight); + public void addEdge(int fromId, int toId) { + Node from = getNode(fromId); + Node to = getNode(toId); + if (from == null) from = new Node(fromId, this); + if (to == null) to = new Node(toId, this); + addEdge(from, to); } @Override - public void addEdge(Edge e) { - super.addEdge(e); - Edge sym = e.getSymmetric(); - if (sym != null && existsEdge(sym)) - super.addEdge(sym); + public void addEdge(int fromId, int toId, int weight) { + Node from = getNode(fromId); + Node to = getNode(toId); + if (from == null) from = new Node(fromId, this); + if (to == null) to = new Node(toId, this); + addEdge(from, to, weight); } @Override @@ -114,13 +161,12 @@ public class UndirectedGraph extends Graph { @Override public boolean existsEdge(Node u, Node v) { - // Pour un graphe non orienté, (u,v) == (v,u) return super.existsEdge(u, v) || super.existsEdge(v, u); } @Override - public boolean existsEdge(Edge e) { - return super.existsEdge(e); + public boolean existsEdge(int uId, int vId) { + return existsEdge(getNode(uId), getNode(vId)); } @Override @@ -158,9 +204,97 @@ public class UndirectedGraph extends Graph { } return closure; } - + // ====================== public static UndirectedGraph fromDotFile(String filename) { - return fromDotFile(filename, ".gv"); + Map> adjEdList = new HashMap<>(); + UndirectedGraph g = new UndirectedGraph(adjEdList); + + try (BufferedReader dot = new BufferedReader(new FileReader(filename))) { + String line; + + while ((line = dot.readLine()) != null) { + line = line.trim(); + if (line.isEmpty()) continue; // ignore lignes vides + + // Cas 1 : Ligne contenant seulement un numéro → nœud isolé + if (line.matches("^\\d+$")) { + int id = Integer.parseInt(line); + Node isolated = new Node(id, g, ""); + adjEdList.putIfAbsent(isolated, new ArrayList<>()); + continue; + } + + // Cas 2 : Ligne décrivant une arête, ex: "1 ------> 5 ----> 5" + String[] tokens = line.split("[^0-9]+"); + if (tokens.length < 3) continue; // ligne invalide → on saute + + int fromId = Integer.parseInt(tokens[0]); + int toId = Integer.parseInt(tokens[1]); + int weight = Integer.parseInt(tokens[2]); + + Node nFrom = new Node(fromId, g, ""); + Node nTo = new Node(toId, g, ""); + Edge edge = new Edge(nFrom, nTo, weight); + + // Ajout de l’arête dans la liste d’adjacence + adjEdList.computeIfAbsent(nFrom, k -> new ArrayList<>()).add(edge); + + // S’assurer que le nœud destination existe + adjEdList.putIfAbsent(nTo, new ArrayList<>()); + } + } catch (IOException e) { + throw new RuntimeException("Erreur lors de la lecture du fichier : " + filename, e); + } + + return g; + } + + public String toDotString() { + StringBuilder sb = new StringBuilder(); + sb.append("graph G {\n"); + sb.append("\trankdir=LR\n"); + + // Liste triée des arêtes + List edges = getAllEdges(); + edges.sort(Comparator.comparingInt((Edge e) -> e.getNodeFrom().getId()) + .thenComparingInt(e -> e.getNodeTo().getId())); + + // Affichage des arêtes + for (Edge e : edges) { + sb.append("\t") + .append(e.getNodeFrom().getId()) + .append(" -- ") + .append(e.getNodeTo().getId()); + if (e.isWeighted()) { + sb.append(" [label=").append(e.getWeight()) + .append(", len=").append(e.getWeight()).append("]"); + } + sb.append(";\n"); + } + + // Affichage des nœuds isolés (ceux qui n’ont aucune arête) + Set nodesWithEdges = new HashSet<>(); + for (Edge e : edges) { + nodesWithEdges.add(e.getNodeFrom()); + nodesWithEdges.add(e.getNodeTo()); + } + + for (Node node : adjEdList.keySet()) { + if (!nodesWithEdges.contains(node)) { + sb.append("\t").append(node.getId()).append(";\n"); + } + } + + sb.append("}\n"); + return sb.toString(); + } + + public void toDotFile(String filename) { + super.toDotFile(filename); + } + + public void toDotFile(String filename, String ext) { + super.toDotFile(filename, ".gv"); } // ====================== diff --git a/src/main/resources/isolatedNodes.gv b/src/main/resources/isolatedNodes.gv new file mode 100644 index 0000000000000000000000000000000000000000..221faf25bea089817d5b223621867353409a5cfa --- /dev/null +++ b/src/main/resources/isolatedNodes.gv @@ -0,0 +1,8 @@ +digraph { + rankdir=LR + 1 + 2 -> 3 + 4 + 5 + 8 +} \ No newline at end of file diff --git a/src/main/resources/multiGraph.gv b/src/main/resources/multiGraph.gv new file mode 100644 index 0000000000000000000000000000000000000000..d4bec97fe596a00d30501f8d12e9d59141a8f547 --- /dev/null +++ b/src/main/resources/multiGraph.gv @@ -0,0 +1,18 @@ +digraph { + rankdir=LR + 1 -> 2 + 2 -> 3 + 2 -> 4 + 2 -> 4 + 2 -> 5 + 3 -> 1 + 3 -> 1 + 3 -> 5 + 4 -> 1 + 4 -> 4 + 4 -> 5 + 6 -> 5 + 6 -> 5 + 6 -> 6 + 6 -> 6 +} \ No newline at end of file diff --git a/src/main/resources/simpleGraph.gv b/src/main/resources/simpleGraph.gv new file mode 100644 index 0000000000000000000000000000000000000000..9658c6d2483d233a2fc8f48b361e5dc8180eab69 --- /dev/null +++ b/src/main/resources/simpleGraph.gv @@ -0,0 +1,12 @@ +digraph { + rankdir=LR + 1 -> 2 + 2 -> 3 + 2 -> 4 + 2 -> 5 + 3 -> 1 + 3 -> 5 + 4 -> 1 + 4 -> 5 + 6 -> 5 +} \ No newline at end of file diff --git a/src/main/java/m1graphs2025/undirWeightedMultiGraph.gv b/src/main/resources/undirWeightedMultiGraph.gv similarity index 100% rename from src/main/java/m1graphs2025/undirWeightedMultiGraph.gv rename to src/main/resources/undirWeightedMultiGraph.gv diff --git a/src/main/resources/weightedMultiGraph.gv b/src/main/resources/weightedMultiGraph.gv new file mode 100644 index 0000000000000000000000000000000000000000..5ef48fd7155b8dcabc27417ce43c9d50c83b85ee --- /dev/null +++ b/src/main/resources/weightedMultiGraph.gv @@ -0,0 +1,18 @@ +digraph { + rankdir=LR + 1 -> 2 [label=4, len=4] + 2 -> 3 [label=1, len=1] + 2 -> 4 [label=2, len=2] + 2 -> 4 [label=50, len=50] + 2 -> 5 [label=3, len=3] + 3 -> 1 [label=3, len=3] + 3 -> 1 [label=6, len=6] + 3 -> 5 [label=2, len=2] + 4 -> 1 [label=0, len=0] + 4 -> 4 [label=14, len=14] + 4 -> 5 [label=2, len=2] + 6 -> 5 [label=7, len=7] + 6 -> 5 [label=10, len=10] + 6 -> 6 [label=5, len=5] + 6 -> 6 [label=8, len=8] +} \ No newline at end of file diff --git a/src/main/resources/weightedSimpleGraph.gv b/src/main/resources/weightedSimpleGraph.gv new file mode 100644 index 0000000000000000000000000000000000000000..c30550655950e595caef48e94d9f468b548b1797 --- /dev/null +++ b/src/main/resources/weightedSimpleGraph.gv @@ -0,0 +1,12 @@ +digraph { + rankdir=LR + 1 -> 2 [label=4, len=4] + 2 -> 3 [label=1, len=1] + 2 -> 4 [label=2, len=2] + 2 -> 5 [label=3, len=3] + 3 -> 1 [label=6, len=6] + 3 -> 5 [label=2, len=2] + 4 -> 1 [label=0, len=0] + 4 -> 5 [label=2, len=2] + 6 -> 5 [label=7, len=7] +} \ No newline at end of file diff --git a/src/test/java/m1graphs2025/TestGraphPart1.java b/src/test/java/m1graphs2025/TestGraphPart1.java new file mode 100644 index 0000000000000000000000000000000000000000..b2d8bb0389e0a9f7ea5a00e3d6869b8266079867 --- /dev/null +++ b/src/test/java/m1graphs2025/TestGraphPart1.java @@ -0,0 +1,183 @@ +package m1graphs2025; + +import java.util.Collections; +import java.util.List; + +public class TestGraphPart1 { + + public static void main(String[] args) { + System.out.println("*--------------------------------------------------------------------*"); + System.out.println("************ PART 1. UNWEIGTED DIRECTED GRAPHS ***********************"); + System.out.println("*--------------------------------------------------------------------*"); + System.out.println("\n>>>>>>>> SIMPLE GRAPH >>>>>>>>>>>>>>>>>>>>>>>>"); + System.out.println(">>>>>>>> Creating the subject example graph in G"); + Graph g = new Graph(2, 4, 0, 0, 6, 0, 2, 3, 5, 8, 0, 0, 4, 7, 0, 3, 0, 7, 0); + System.out.println(">>>> Graph information"); + System.out.println(">> DOT representation\n" + g.toDotString()); + System.out.println("" + g.nbNodes() + " nodes, " + g.nbEdges() + " edges"); + System.out.println(">> Nodes: "); + List nodes = g.getAllNodes(); + Collections.sort(nodes); + for (Node n : nodes) + System.out.println("Node " + n + ": degree " + g.degree(n) + " (in: " + g.inDegree(n) + "/ out: " + + g.outDegree(n) + ")"); + + List edges; + System.out.println(">> Edges: "); + System.out.println("---------------------------"); + System.out.println("Out-edges per node"); + for (Node n : nodes) { + edges = g.getOutEdges(n); + Collections.sort(edges); + System.out.println("" + n + ": " + edges); + } + + System.out.println("In-edges per node"); + for (Node n : nodes) { + edges = g.getInEdges(n); + Collections.sort(edges); + System.out.println("" + n + ": " + edges); + } + + ///////////////////////////////////////////////////// + + System.out.println("\n>>>>>>>> creating isolated node 12"); + if (!g.addNode(12)) + System.out.println("Error: failed to create node 12"); + System.out.println("Graph now:"); + System.out.println(g.toDotString()); + System.out.println("" + g.nbNodes() + " nodes, " + g.nbEdges() + " edges"); + nodes = g.getAllNodes(); + Collections.sort(nodes); + System.out.println("Nodes list: " + nodes); + + System.out.println("\n>>>>>>>> Removing node 3"); + g.removeNode(3); + System.out.println("Graph now:"); + System.out.println(g.toDotString()); + System.out.println("" + g.nbNodes() + " nodes, " + g.nbEdges() + " edges"); + nodes = g.getAllNodes(); + Collections.sort(nodes); + System.out.println("Nodes list: " + nodes); + + System.out.println(">> Edges: "); + System.out.println("---------------------------"); + System.out.println("Out-edges per node"); + for (Node n : nodes) { + edges = g.getOutEdges(n); + Collections.sort(edges); + System.out.println("" + n + ": " + edges); + } + + System.out.println("In-edges per node"); + for (Node n : nodes) { + edges = g.getInEdges(n); + Collections.sort(edges); + System.out.println("" + n + ": " + edges); + } + + System.out.println( + "\n>>>>>>>> Recreating edges (4, 3), (3, 6), (7, 3), adding edge (12, 3), creating edge (3, 25)"); + g.addEdge(new Edge(4, 3, g)); + g.addEdge(new Edge(3, 6, g)); + g.addEdge(new Edge(7, 3, g)); + g.addEdge(new Edge(12, 3, g)); + g.addEdge(3, 25); + System.out.println("Graph now:"); + System.out.println(g.toDotString()); + System.out.println("" + g.nbNodes() + " nodes, " + g.nbEdges() + " edges"); + nodes = g.getAllNodes(); + Collections.sort(nodes); + System.out.println("Nodes list: " + nodes); + + System.out.println(""); + System.out.println("\n>>>>>>>> Edges removal"); + System.out.println(">>>> Removing existing edges (7, 3) and (4, 8)"); + g.removeEdge(7, 3); + g.removeEdge(4, 8); + System.out.println(">>>> Removing absent edge (3, 4)"); + g.removeEdge(3, 4); + System.out.println( + ">>>> Removing edges whith 1 or 2 not existing end-points: (-3, 4), (6, 0), (4, 11), (-1, -2), (13, 3), (9, 10)"); + g.removeEdge(-3, 4); + g.removeEdge(6, 0); + g.removeEdge(4, 11); + g.removeEdge(-1, -2); + g.removeEdge(13, 3); + g.removeEdge(9, 10); + + System.out.println("Graph now:"); + System.out.println(g.toDotString()); + System.out.println("" + g.nbNodes() + " nodes, " + g.nbEdges() + " edges"); + nodes = g.getAllNodes(); + Collections.sort(nodes); + System.out.println("Nodes list: " + nodes); + + System.out.println( + "\nTesting that getSuccessors and getSuccessorsMulti give the same result for the simple graph:"); + boolean same = true; + for (Node u : nodes) { + List succs = g.getSuccessors(u), succsMulti = g.getSuccessorsMulti(u); + // sort the lists so that nodes always appear in the same order + Collections.sort(succs); + Collections.sort(succsMulti); + same = same && succs.equals(succsMulti); + } + System.out.println("\tgetSuccessors and getSuccessorsMulti " + (same ? "are identical" : "differ")); + + System.out.println("\n>>>>>>>> MULTIGRAPH: adding a self-loop on node 6, and a second edge (1, 4)"); + g.addEdge(6, 6); + g.addEdge(1, 4); + System.out.println("Graph now:"); + System.out.println(g.toDotString()); + System.out.println("" + g.nbNodes() + " nodes, " + g.nbEdges() + " edges"); + nodes = g.getAllNodes(); + Collections.sort(nodes); + System.out.println("Nodes list: " + nodes); + System.out.println( + "Degree of node 6: " + g.degree(6) + " (in: " + g.inDegree(6) + "/ out: " + g.outDegree(6) + ")"); + + System.out.println(">> Edges: "); + System.out.println("---------------------------"); + System.out.println("Out-edges per node"); + for (Node n : nodes) { + edges = g.getOutEdges(n); + Collections.sort(edges); + System.out.println("" + n + ": " + edges); + } + + System.out.println("In-edges per node"); + for (Node n : nodes) { + edges = g.getInEdges(n); + Collections.sort(edges); + System.out.println("" + n + ": " + edges); + } + + System.out.println("\n>>>>>>>>>> Get the reverse graph"); + System.out.println(g.getReverse().toDotString()); + + System.out.println(">>>>>>>>>> Get the transitive closure"); + System.out.println(g.getTransitiveClosure().toDotString()); + + System.out.println(">>>>>>>>>> Emptying the graph by removing all its nodes"); + nodes = g.getAllNodes(); + for (Node u : nodes) + g.removeNode(u); + System.out.println("Graph now:"); + System.out.println(g.toDotString()); + + System.out.println(">>>> Searching for node 7:"); + if (g.usesNode(7)) + System.out.println("\tNode 7 exists"); + else + System.out.println("\tThere is no Node 7"); + + System.out.println(">>>> Searching for edge (4, 2):"); + if (g.existsEdge(4, 2)) + System.out.println("\tEdge (4, 2) exists"); + else + System.out.println("\tThere is no edge (4, 2)"); + + System.out.println(); + } +} diff --git a/src/test/java/m1graphs2025/TestGraphPart2.java b/src/test/java/m1graphs2025/TestGraphPart2.java new file mode 100644 index 0000000000000000000000000000000000000000..d764977b1bc67be80b6b4271724548d99b5f5647 --- /dev/null +++ b/src/test/java/m1graphs2025/TestGraphPart2.java @@ -0,0 +1,58 @@ +package m1graphs2025; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class TestGraphPart2 { + + public static void main(String[] args) { + System.out.println("*------------------------------------------------------------------*"); + System.out.println("********* PART 2. READING GRAPHS FROM DOT FILES ****************"); + System.out.println("*------------------------------------------------------------------*"); + System.out.println("\n>>> Graph with isolated nodes: reading file 'isolatedNodes.gv'"); + Graph gin = Graph.fromDotFile("src/main/resources/isolatedNodes.gv"); + if (gin == null) + System.out.println("Null graph was created from 'isolatedNodes.gv'"); + else { + System.out.println("Read: OK. The graph with isolated nodes has been read as:"); + System.out.println("---------------------"); + System.out.println(gin.toDotString()); + System.out.println("---------------------"); + } + + System.out.println(">>> Simple graph: reading file 'simpleGraph.gv'"); + Graph sg = Graph.fromDotFile("src/main/resources/simpleGraph.gv"); + if (sg == null) + System.out.println("Null graph was created from 'simpleGraph.gv'"); + else { + System.out.println("Read: OK. The simple graph has been read as:"); + System.out.println("---------------------"); + System.out.println(sg.toDotString()); + System.out.println("---------------------"); + } + + System.out.println("\n>>> Multi-graph: reading file 'multiGraph.gv'"); + Graph mg = Graph.fromDotFile("src/main/resources/multiGraph.gv"); + if (mg == null) + System.out.println("Null graph was created from 'multiGraph.gv'"); + else { + System.out.println("Read: OK. The multi-graph has been read as:"); + System.out.println("---------------------"); + System.out.println(mg.toDotString()); + System.out.println("---------------------"); + } + + System.out.println("Comparing single and multi successors per node for 'multiGraph.gv'"); + for (Node u : mg.getAllNodes()) { + List succs = mg.getSuccessors(u), succsMulti = mg.getSuccessorsMulti(u); + // sort the lists so that nodes always appear in the same order + Collections.sort(succs); + Collections.sort(succsMulti); + System.out.println("" + u + " single successors: " + succs); + System.out.println("" + u + " multi successors: " + succsMulti); + } + + System.out.println("\n\n"); + } +} diff --git a/src/test/java/m1graphs2025/TestGraphPart3.java b/src/test/java/m1graphs2025/TestGraphPart3.java new file mode 100644 index 0000000000000000000000000000000000000000..84ed56ac4af7c473fa543d877520ae2e99b4a18e --- /dev/null +++ b/src/test/java/m1graphs2025/TestGraphPart3.java @@ -0,0 +1,50 @@ +package m1graphs2025; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class TestGraphPart3 { + + public static void main(String[] args) { + System.out.println("*----------------------------------------------------------------------*"); + System.out.println("************* PART 3. WEIGHTED DIRECTED GRAPHS ***********************"); + System.out.println("*----------------------------------------------------------------------*"); + int totalEdgesWeight = 0; + + System.out.println("\n>>>>>>>>>>"); + System.out.println("Reading a weighted directed simple graph from DOT file 'weightedSimpleGraph.gv'"); + Graph wsg = Graph.fromDotFile("src/main/resources/weightedSimpleGraph.gv"); + if (wsg == null) + System.out.println("Null graph was created from 'weightedSimpleGraph.gv'"); + else { + System.out.println("Read: OK. The weighted directed simple graph has been read as:"); + System.out.println("---------------------"); + System.out.println(wsg.toDotString()); + System.out.println("---------------------"); + + for (Edge e : wsg.getAllEdges()) + totalEdgesWeight += e.getWeight(); + System.out.println("The sum of all edges weights equals " + totalEdgesWeight); + } + + System.out.println("\n>>>>>>>>>>"); + System.out.println("Reading a weighted directed multi graph from DOT file 'weightedMultiGraph.gv'"); + Graph wmg = Graph.fromDotFile("src/main/resources/weightedMultiGraph.gv"); + if (wmg == null) + System.out.println("Null graph was created from 'weightedMultiGraph.gv'"); + else { + System.out.println("Read: OK. The weighted directed multi graph has been read as:"); + System.out.println("---------------------"); + System.out.println(wmg.toDotString()); + System.out.println("---------------------"); + + totalEdgesWeight = 0; + for (Edge e : wmg.getAllEdges()) + totalEdgesWeight += e.getWeight(); + System.out.println("The sum of all edges weights equals " + totalEdgesWeight); + } + + System.out.println("\n\n"); + } +} diff --git a/src/test/java/m1graphs2025/TestGraphPart4.java b/src/test/java/m1graphs2025/TestGraphPart4.java new file mode 100644 index 0000000000000000000000000000000000000000..720043517d45763537352e59d2b66a205a9fe79c --- /dev/null +++ b/src/test/java/m1graphs2025/TestGraphPart4.java @@ -0,0 +1,111 @@ +package m1graphs2025; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class TestGraphPart4 { + + public static void main(String[] args) { + + System.out.println("*-------------------------------------------------------------------------*"); + System.out.println("************ PART 4. UNDIRECTED UNWEIGHTED GRAPHS ***********************"); + System.out.println("*-------------------------------------------------------------------------*"); + + System.out.println("\nCreating an undirected simple graph 'usg' from scracth"); + UndirectedGraph usg = new UndirectedGraph(2, 3, 0, 3, 4, 0, 4, 0, 0); + System.out.println(usg.toDotString()); + System.out.println("usg has " + usg.nbNodes() + " nodes and " + usg.nbEdges() + " edges."); + + System.out.println("\n>>>>>> usg: Counting degrees and showing successors"); + for (Node u : usg.getAllNodes()) { + System.out.println("Node " + u + ". Degree: " + usg.degree(u.getId()) + " (In: " + usg.inDegree(u.getId()) + + " / Out: " + usg.outDegree(u.getId()) + ")"); + System.out.println("\tSuccessors: " + usg.getSuccessors(u)); + } + + System.out.println(">>>>>> usg: Edges of the graph"); + System.out.println("// N.B. The edges are printed as though they were directed. This is due to the toString()\n" + + "// method that was not overridden. It is possible to do better but not important.\n" + + "// What is important is that each edge appears only once per direction."); + System.out.println("All edges of the graph: " + usg.getAllEdges()); + System.out.println("Out-edges per node"); + for (Node u : usg.getAllNodes()) + System.out.println("" + u + ": " + usg.getOutEdges(u)); + System.out.println("In-edges per node"); + for (Node u : usg.getAllNodes()) + System.out.println("" + u + ": " + usg.getInEdges(u)); + System.out.println("Incident edges per node"); + for (Node u : usg.getAllNodes()) + System.out.println("" + u + ": " + usg.getIncidentEdges(u)); + + System.out.println("Creating an undirected multi-graph with self-loops 'umg' from scratch"); + UndirectedGraph umg = new UndirectedGraph(1, 1, 2, 2, 3, 0, 2, 3, 0, 0); + + String dotUMG = umg.toDotString(); + System.out.println(dotUMG); + System.out.println("umg has " + umg.nbNodes() + " nodes and " + umg.nbEdges() + " edges."); + + System.out.println("\n>>>>>> umg: Counting degrees and showing successors"); + for (Node u : umg.getAllNodes()) { + System.out.println("Node " + u + ". Degree: " + umg.degree(u.getId()) + " (In: " + umg.inDegree(u.getId()) + + " / Out: " + umg.outDegree(u.getId()) + ")"); + System.out.println("\tSuccessors: " + umg.getSuccessors(u)); + } + + System.out.println(">>>>>> umg: Edges of the graph"); + System.out.println("All edges of the graph: " + umg.getAllEdges()); + System.out.println("Out-edges per node"); + for (Node u : umg.getAllNodes()) + System.out.println("" + u + ": " + umg.getOutEdges(u)); + System.out.println("In-edges per node"); + for (Node u : umg.getAllNodes()) + System.out.println("" + u + ": " + umg.getInEdges(u)); + System.out.println("Incident edges per node"); + for (Node u : umg.getAllNodes()) + System.out.println("" + u + ": " + umg.getIncidentEdges(u)); + + System.out.println("\n>>>>>> umg: Successor Array, Adjacency Matrix, and Graph Reverse"); + System.out.println("umg Successor array\n" + Arrays.toString(umg.toSuccessorArray())); + + System.out.println("umg Adjacency Matrix"); + for (int[] row : umg.toAdjMatrix()) + System.out.println("\t" + Arrays.toString(row)); + + System.out.println("Testing via toDotString() the equality with the reverse graph"); + String dotRUMG = umg.getReverse().toDotString(); + System.out.println("DOT of the reverse of umg\n" + dotRUMG); + System.out.println("Graph gu and its reverse " + (dotUMG.equals(dotRUMG) ? "are identical" : "differ")); + + System.out.println("-----------------\n NOW a disconnected GRAPH \n----------------"); + System.out.println("Building 'guDisc', a disconnected undirected graph with multi-edges and self-loops"); + UndirectedGraph guDisc = new UndirectedGraph(1, 1, 2, 2, 6, 0, 2, 3, 6, 0, 0, 6, 0, 6, 0, 0, 0, 9, 10, 0, 0, 0); + System.out.println(guDisc.toDotString()); + + // delete + // Graph guDisc2 = new Graph(1,1,2,2,6,0, 2,3,6,0, 0, 6,0, 6,0, 0, 0, 9,10,0, 0, + // 0); + // System.out.println(guDisc2.toDotString()); + // System.exit(0); + // end delete + + System.out.println("Comparing single and multi successors per node for guDisc"); + for (Node u : guDisc.getAllNodes()) { + List succs = guDisc.getSuccessors(u), succsMulti = guDisc.getSuccessorsMulti(u); + // sort the lists so that nodes always appear in the same order + Collections.sort(succs); + Collections.sort(succsMulti); + System.out.println("" + u + " single successors: " + succs); + System.out.println("" + u + " multi successors: " + succsMulti); + } + + System.out.println(">>>> DFS of guDisc: " + guDisc.getDFS()); + System.out.println(">>>> BFS of guDisc: " + guDisc.getBFS()); + + System.out.println(">>>>>>> Computing guDisc's transitive closure"); + UndirectedGraph guDiscTC = (UndirectedGraph) guDisc.getTransitiveClosure(); + System.out.println(guDiscTC.toDotString()); + + System.out.println("\n\n"); + } +} diff --git a/src/test/java/m1graphs2025/TestGraphPart5.java b/src/test/java/m1graphs2025/TestGraphPart5.java new file mode 100644 index 0000000000000000000000000000000000000000..c9e4468faf7ec4e07686fcac2542d5513eab36c4 --- /dev/null +++ b/src/test/java/m1graphs2025/TestGraphPart5.java @@ -0,0 +1,71 @@ +package m1graphs2025; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class TestGraphPart5 { + + public static void main(String[] args) { + + System.out.println("*-----------------------------------------------------------------------*"); + System.out.println("************ PART 5. UNDIRECTED WEIGHTED GRAPHS ***********************"); + System.out.println("*-----------------------------------------------------------------------*"); + + System.out.println("\n>>>>>> Reading 'uwmg' an undirected weighted multi-graph with self loops\n" + + "from file 'undirWeightedMultiGraph.gv'"); + + UndirectedGraph uwmg = (UndirectedGraph) UndirectedGraph.fromDotFile("src/main/resources/undirWeightedMultiGraph.gv"); + if (uwmg == null) + System.out.println("Null graph was created from 'undirWeightedMultiGraph.gv'"); + else { + System.out.println("Read: OK. The undirected weighted multi-graph has been read as:"); + System.out.println("---------------------"); + System.out.println(uwmg.toDotString()); + System.out.println("---------------------"); + + Integer totalEdgesWeight = 0; + for (Edge e : uwmg.getAllEdges()) + totalEdgesWeight += e.getWeight(); + System.out.println("The sum of all edges weights equals " + totalEdgesWeight); + } + + System.out.println("\nComparing single and multi successors per node for uwmg"); + for (Node u : uwmg.getAllNodes()) { + List succs = uwmg.getSuccessors(u), succsMulti = uwmg.getSuccessorsMulti(u); + // sort the lists so that nodes always appear in the same order + Collections.sort(succs); + Collections.sort(succsMulti); + System.out.println("" + u + " single successors: " + succs); + System.out.println("" + u + " multi successors: " + succsMulti); + } + + /* + * TO BE CONTINUED ... + * System.out.println("\n\n"); + * System.out.println( + * "*-----------------------------------------------------------------------*"); + * System.out. + * println("************ PART 6. DFS and Node Visit Info ***********************" + * ); + * System.out.println( + * "*-----------------------------------------------------------------------*"); + * + * + * // lecture example but undirected + * UndirectedGraph gToVisit = new UndirectedGraph( + * 2, 3, 4, 6, 0, //1 + * 5, 6, 0, //2 + * 4, 0, //3 + * 8, 0, //4 + * 6, 7, 8, 0, //5 + * 7, 0, //6 + * 8,0, //7 + * 0 //8 + * ); + * System.out.println("\nAn undirected graph to visit:"); + * System.out.println(gToVisit.toDotString()); + */ + + } +}