diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..42161a500c325c4a47de09ae4cddc7b2fb67ea7e --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +# Cible qui pour compiler +compile: + +# Cible qui explique comment executer +exec: + +# Demarre automatiquement une demonstration de votre programme +# Il faut que cette demo soit convaincante +demo: + +# Executer automatiquent les test +# On s'attend (d'habitude) que pour claque classe MaClasse il y ait une +# classe TestMaClasse qui vorifie le bon comportment de chaque methode de la classe +# sur au moins une entrée +test: + +# Pour la cible suivante, on vous laisse faire +clean: + diff --git a/pdf/tp3.pdf b/pdf/tp3.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f4dbb03afda1929e99e2e6b6876ca2a27be2ffbc Binary files /dev/null and b/pdf/tp3.pdf differ diff --git a/resources/randomBFS.png b/resources/randomBFS.png new file mode 100644 index 0000000000000000000000000000000000000000..46663ee9bc30514ba44960fc4cfa4602d5db9243 Binary files /dev/null and b/resources/randomBFS.png differ diff --git a/resources/randomKruskal.png b/resources/randomKruskal.png new file mode 100644 index 0000000000000000000000000000000000000000..b5df8d08e0312102c8a4c7110c12c60b36ac458e Binary files /dev/null and b/resources/randomKruskal.png differ diff --git a/resources/randomWilson.png b/resources/randomWilson.png new file mode 100644 index 0000000000000000000000000000000000000000..0e919d027492716f8f3ac2a33723ed91d5a40c9b Binary files /dev/null and b/resources/randomWilson.png differ diff --git a/src/Arc.java b/src/Arc.java new file mode 100644 index 0000000000000000000000000000000000000000..f11872382d58e6dc00fb84d813166a6fb3462076 --- /dev/null +++ b/src/Arc.java @@ -0,0 +1,19 @@ + +public class Arc { + Edge support; + boolean reversed; + + public Arc(Edge e, boolean reversed) { + this.support = e; + this.reversed = reversed; + } + + public int getSource() { + return (reversed ? support.getDest() : support.getSource()); + } + + public int getDest() { + return (reversed ? support.getSource() : support.getDest()); + } + +} diff --git a/src/Complete.java b/src/Complete.java new file mode 100644 index 0000000000000000000000000000000000000000..15b22f37c51d119a21e746c4e7ef0c6a90f73485 --- /dev/null +++ b/src/Complete.java @@ -0,0 +1,17 @@ + +public class Complete { + + Graph graph; + + public Complete(int order) { + this.graph = new Graph(order); + for(int i = 0; i < order; i++) + for (int j = i+1; j < order; j++) + graph.addEdge(new Edge(i,j,0)); + + } + + + + +} diff --git a/src/Edge.java b/src/Edge.java new file mode 100644 index 0000000000000000000000000000000000000000..19c02e092afd4ca060f7a79b432f263430b6f6a6 --- /dev/null +++ b/src/Edge.java @@ -0,0 +1,32 @@ + +public class Edge implements Comparable<Edge> { + + protected int source; + protected int dest; + double weight; + + public Edge(int source, int dest, double weight) { + this.source = source; + this.dest = dest; + this.weight = weight; + } + + public int compareTo(Edge e) { + if (this.weight == e.weight) return 0; + if (this.weight < e.weight) return -1; + return 1; + } + + public int oppositeExtremity(int vertex) { + return (dest == vertex ? source : dest); + } + + public int getSource() { + return this.source; + } + + public int getDest() { + return this.dest; + } + +} diff --git a/src/ErdosRenyi.java b/src/ErdosRenyi.java new file mode 100644 index 0000000000000000000000000000000000000000..35991ae60fcf688ebf34e46621a28025c7c44dce --- /dev/null +++ b/src/ErdosRenyi.java @@ -0,0 +1,34 @@ +import java.util.ArrayList; +import java.util.Random; + + +public class ErdosRenyi { + + private final static Random gen = new Random(); + Graph graph; + private int order; + private double edgeProbability; + + + private boolean isConnected() { + if (graph == null) return false; + ArrayList<Arc> tree = BreadthFirstSearch.generateTree(graph, 0); + return tree.size() == order - 1; + + } + + private void genGraph() { + graph = new Graph(order); + for (int i = 0; i < order; i++) + for (int j = i+1; j < order; j++) { + if (gen.nextDouble() < edgeProbability) + graph.addEdge(new Edge(i,j,0)); + } + } + + public ErdosRenyi(int order, float expectedAverageDegree) { + this.edgeProbability = Math.max(1.5, expectedAverageDegree) / (order-1); + this.order = order; + while (!isConnected()) genGraph(); + } +} diff --git a/src/Graph.java b/src/Graph.java new file mode 100644 index 0000000000000000000000000000000000000000..4862b8e864f2963b32730560f4ef298df270bc19 --- /dev/null +++ b/src/Graph.java @@ -0,0 +1,51 @@ +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + + +public class Graph implements Iterable<Edge>{ + // classe de graphe non orientés permettant de manipuler + // en même temps des arcs (orientés) + // pour pouvoir stocker un arbre couvrant, en plus du graphe + + int order; + int edgeCardinality; + + ArrayList<LinkedList<Edge>> adjacency; + ArrayList<LinkedList<Arc>> inAdjacency; + ArrayList<LinkedList<Arc>> outAdjacency; + + public boolean isVertex(int index) { + // à remplir + } + + public <T> ArrayList<LinkedList<T>> makeList(int size) { + ArrayList<LinkedList<T>> res = new ArrayList<>(size); + for(int i = 0; i <= size; i++) { + res.add(null); + } + return res; + } + + public Graph(int upperBound) { + // à remplir + } + + public void addVertex(int indexVertex) { + // à remplir + } + + public void ensureVertex(int indexVertex) { + // à remplir + } + + public void addArc(Arc arc) { + // à remplir + } + + public void addEdge(Edge e) { + // à remplir + } + +} diff --git a/src/Grid.java b/src/Grid.java new file mode 100644 index 0000000000000000000000000000000000000000..db4e1d5974e7c1f1086e9dbfce34571abd2ddc5e --- /dev/null +++ b/src/Grid.java @@ -0,0 +1,95 @@ +import java.util.BitSet; + + +public class Grid { + + int width; + int height; + int maxVertex; + + Graph graph; + + public int abscissaOfVertex(int vertex) { + return vertex % width; + } + + public int ordinateOfVertex(int vertex) { + return vertex / width; + } + + private int vertexOfCoordinate(int abscissa, int ordinate) { + return ordinate * width + abscissa; + } + + public Grid(int width, int height) { + this.width = width; + this.height = height; + maxVertex = width * height - 1; + graph = new Graph(maxVertex); + for (int i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { + if (i < width - 1) + graph.addEdge(new Edge( + vertexOfCoordinate(i,j), + vertexOfCoordinate(i+1,j), + 0.0 + )); + if (j < height - 1) + graph.addEdge(new Edge( + vertexOfCoordinate(i,j), + vertexOfCoordinate(i,j+1), + 0.0 + )); + } + } + + } + + + public boolean isHorizontal(Edge e) { + return Math.abs(e.source - e.dest) == 1; + } + + public boolean isVertical(Edge e) { + return Math.abs(e.source - e.dest) == width; + } + + + private void drawLine(int h, BitSet right) { + for (int i = 0; i < width - 1; i++) { + System.out.print("o"); + if (right.get(vertexOfCoordinate(i,h))) System.out.print("--"); + else System.out.print(" "); + } + System.out.println("o"); + } + + private void drawInterline(int h, BitSet up) { + for (int i = 0; i < width; i++) { + if (up.get(vertexOfCoordinate(i,h))) System.out.print("|"); + else System.out.print(" "); + if (i < width-1) System.out.print(" "); + } + System.out.println(); + } + + public void drawSubgrid(Iterable<Edge> edges) { + BitSet up = new BitSet(maxVertex); + BitSet right = new BitSet(maxVertex); + for (Edge e : edges) { +// System.out.println(e.fromVertex + " -- " + e.toVertex); + if (isHorizontal(e)) + right.set(Math.min(e.source,e.dest)); + if (isVertical(e)) + up.set(Math.min(e.source,e.dest)); + } + + for (int j = 0; j < height; j++) { + drawLine(j,right); + if (j < height - 1) drawInterline(j,up); + } + + } + + +} diff --git a/src/Labyrinth.java b/src/Labyrinth.java new file mode 100644 index 0000000000000000000000000000000000000000..dd31a36eee9ddeacda9584eb3c297e697ef00b5f --- /dev/null +++ b/src/Labyrinth.java @@ -0,0 +1,209 @@ +import java.awt.Color; +import java.awt.Dimension; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.geom.Ellipse2D; +import java.awt.image.BufferedImage; +import java.awt.image.RenderedImage; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; + +import javax.imageio.ImageIO; +import javax.swing.JPanel; + + +public class Labyrinth extends JPanel { + + private static final long serialVersionUID = 2192694920147985L; + int halfSide = 5; + int vertexMargin = 1; + int corridorMargin = 2; + int corridorLength = 2 * halfSide; + int side; + int vertexRadius; + int vertexWidth; + int corridorWidth; + int corridorStartShift; + int colorGradientCycleLength = 150; // > 0 + int brightnessSlope= 3; // 0 <= x <= 100 + int minBrightness = 40; // 0 <= x <= 100 + Color backgroundColor = Color.black; + + BufferedImage img; + + private void recomputeDefaultValues() { + side = 2 * halfSide + 1; + vertexWidth = side - 2 * vertexMargin; + corridorWidth = side - 2 * corridorMargin; + corridorStartShift = side / 2 + 1; + vertexRadius = halfSide - vertexMargin; + } + + Grid grid; + RootedTree tree; + ArrayList<Edge> edges; + + public void setStyleBright() { + colorGradientCycleLength = 150; + brightnessSlope = 0; + minBrightness = 100; + backgroundColor = Color.black; + } + + public void setStyleInverted() { + colorGradientCycleLength = 150; + brightnessSlope = 2; + minBrightness = 10; + backgroundColor = Color.gray; + } + + public void setStyleBalanced() { + colorGradientCycleLength = 150; + brightnessSlope = 3; + minBrightness = 40; + backgroundColor = Color.black; + } + + public void setShapeBigNodes() { + halfSide = 10; + vertexMargin = 1; + corridorMargin = 5; + recomputeDefaultValues(); + } + + public void setShapeSmoothSmallNodes() { + halfSide = 5; + vertexMargin = 1; + corridorMargin = 1; + recomputeDefaultValues(); + } + + public void setShapeSmallAndFull() { + halfSide = 5; + vertexMargin = 0; + corridorMargin = 0; + recomputeDefaultValues(); + } + + public Labyrinth(Grid g, RootedTree tree) { + this.grid = g; + this.tree = tree; + edges = new ArrayList<>(); + recomputeDefaultValues(); + setPreferredSize(new Dimension(side * grid.width,side*grid.height)); + img = new BufferedImage( + side * grid.width, + side * grid.height, + BufferedImage.TYPE_3BYTE_BGR + ); + } + + public void addEdge(Edge e) { + edges.add(e); + } + + private Color getVertexColor(int vertex) { + if (tree == null) return Color.white; + int depth = tree.getDepth(vertex); + int height = tree.getHeight(vertex) + 1; + float hue = (float) + (depth % colorGradientCycleLength) / colorGradientCycleLength; + float saturation = (float) 0.7; + float brightness = (float) + Math.min(100, brightnessSlope * height + minBrightness) / 100; + Color col = Color.getHSBColor(hue, saturation, brightness); + return col; + + } + + private void drawVerticalEdge(Graphics2D g, Edge e) { + int source = Math.min(e.source, e.dest); + int dest = Math.max(e.source,e.dest); + int xMin = side * grid.abscissaOfVertex(source) + corridorMargin; + int yMin = side * grid.ordinateOfVertex(source) + corridorStartShift; + Rectangle rect = new Rectangle(xMin,yMin,corridorWidth, 2*halfSide); + GradientPaint gradient; + gradient = new GradientPaint( + xMin, yMin + vertexRadius - 1, getVertexColor(source), + xMin, yMin + 2*halfSide - vertexRadius, getVertexColor(dest) + ); + g.setPaint(gradient); + g.fill(rect); + } + + private void drawHorizontalEdge(Graphics2D g, Edge e) { + int source = Math.min(e.source, e.dest); + int dest = Math.max(e.source,e.dest); + int xMin = side * grid.abscissaOfVertex(source) + corridorStartShift; + int yMin = side * grid.ordinateOfVertex(source) + corridorMargin; + Rectangle rect = new Rectangle(xMin, yMin, 2*halfSide, corridorWidth); + GradientPaint gradient = new GradientPaint( + xMin + vertexRadius - 1, yMin, getVertexColor(source), + xMin + 2*halfSide - vertexRadius, yMin, getVertexColor(dest) + ); + g.setPaint(gradient); + g.fill(rect); + } + + private void drawVertex(Graphics2D g, int vertex) { + int xMin = side * grid.abscissaOfVertex(vertex) + vertexMargin; + int yMin = side * grid.ordinateOfVertex(vertex) + vertexMargin; + Shape ell = new Ellipse2D.Float(xMin,yMin,vertexWidth,vertexWidth); + g.setPaint(getVertexColor(vertex)); + g.fill(ell); + } + + private void drawRoot(Graphics2D g, int vertex) { + int i = grid.abscissaOfVertex(vertex); + int j = grid.ordinateOfVertex(vertex); + g.setColor(Color.white); + g.fillRect(side * i, side * j, side, side); + + } + + private void drawBackground(Graphics2D g) { + super.setBackground(backgroundColor); + super.paintComponent(g); + } + + + + public void drawLabyrinth() { + Graphics2D g = img.createGraphics(); + RenderingHints rh = new RenderingHints( + RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON + ); + g.setRenderingHints(rh); + drawBackground(g); + + g.setColor(Color.white); + for (Edge e : edges) { + if (grid.isHorizontal(e)) drawHorizontalEdge(g,e); + else drawVerticalEdge(g,e); + } + for (int i = 0; i < grid.graph.order; i++) { + drawVertex(g,i); + } + if (tree != null) drawRoot(g,tree.getRoot()); + + g.dispose(); + repaint(); + } + + @Override + public void paintComponent(Graphics graphics) { + Graphics2D g = (Graphics2D) graphics; + g.drawImage(img,0,0,null); + } + + public void saveImage(String path) throws IOException { + ImageIO.write((RenderedImage) img,"PNG", new File(path)); + } + +} diff --git a/src/Lollipop.java b/src/Lollipop.java new file mode 100644 index 0000000000000000000000000000000000000000..ea416a96c79e5896e6ef3c1896473afc0dd297bb --- /dev/null +++ b/src/Lollipop.java @@ -0,0 +1,28 @@ +import java.util.ArrayList; +import java.util.Collections; + + +public class Lollipop { + + Graph graph; + + public Lollipop(int order) { + graph = new Graph(order); + ArrayList<Integer> permutation = new ArrayList<>(order); + for (int i = 0; i < order; i++) + permutation.add(i); + Collections.shuffle(permutation); + int t = order / 3; + for (int i = 0; i < t; i++) + graph.addEdge(new Edge(permutation.get(i),permutation.get(i+1),0)); + for (int i = t; i < order; i++) + for (int j = i+1; j < order; j++) + graph.addEdge(new Edge(permutation.get(i), + permutation.get(j), + 0 + ) + ); + } + + +} diff --git a/src/Main.java b/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..5eeb465afbf1f520b2b74fcbd4ae40ec3595148b --- /dev/null +++ b/src/Main.java @@ -0,0 +1,125 @@ +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Random; + +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + + +public class MainStub { + + @SuppressWarnings("unused") + private final static Random gen = new Random(); + + public static ArrayList<Edge> genTree(Graph graph) { + ArrayList<Edge> randomTree; + + // TOOO : modifier l'algorithme utiliser ici. + + // Non-random BFS + ArrayList<Arc> randomArcTree = + BreadthFirstSearch.generateTree(graph,0); + randomTree = new ArrayList<>(); + for (Arc a : randomArcTree) randomTree.add(a.support); + + + + return randomTree; + } + + + public static void main(String argv[]) throws InterruptedException { + + Grid grid = null; + grid = new Grid(1920/11,1080/11); + Graph graph = grid.graph; + +// Graph graph = new Complete(400).graph; + +// Graph graph = new ErdosRenyi(1_000, 100).graph; + +// Graph graph = new Lollipop(1_000).graph; + + int nbrOfSamples = 10; + int diameterSum = 0; + double eccentricitySum = 0; + long wienerSum = 0; + int degreesSum[] = {0, 0, 0, 0, 0}; + int degrees[]; + + ArrayList<Edge> randomTree = null; + RootedTree rooted = null; + + long startingTime = System.nanoTime(); + for (int i = 0; i < nbrOfSamples; i++) { + randomTree= genTree(graph); + + rooted = new RootedTree(randomTree,0); +// rooted.printStats(); + diameterSum = diameterSum + rooted.getDiameter(); + eccentricitySum = eccentricitySum + rooted.getAverageEccentricity(); + wienerSum = wienerSum + rooted.getWienerIndex(); + + degrees = rooted.getDegreeDistribution(4); + for (int j = 1; j < 5; j++) { + degreesSum[j] = degreesSum[j] + degrees[j]; + } + } + long delay = System.nanoTime() - startingTime; + + System.out.println("On " + nbrOfSamples + " samples:"); + System.out.println("Average eccentricity: " + + (eccentricitySum / nbrOfSamples)); + System.out.println("Average wiener index: " + + (wienerSum / nbrOfSamples)); + System.out.println("Average diameter: " + + (diameterSum / nbrOfSamples)); + System.out.println("Average number of leaves: " + + (degreesSum[1] / nbrOfSamples)); + System.out.println("Average number of degree 2 vertices: " + + (degreesSum[2] / nbrOfSamples)); + System.out.println("Average computation time: " + + delay / (nbrOfSamples * 1_000_000) + "ms"); + + + if (grid != null) showGrid(grid,rooted,randomTree); + } + + private static void showGrid( + Grid grid, + RootedTree rooted, + ArrayList<Edge> randomTree + ) throws InterruptedException { + JFrame window = new JFrame("solution"); + final Labyrinth laby = new Labyrinth(grid, rooted); + + laby.setStyleBalanced(); +// laby.setShapeBigNodes(); +// laby.setShapeSmallAndFull(); + laby.setShapeSmoothSmallNodes(); + + window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + window.getContentPane().add(laby); + window.pack(); + window.setLocationRelativeTo(null); + + + for (final Edge e : randomTree) { + laby.addEdge(e); + } + laby.drawLabyrinth(); + + window.setVisible(true); + + // Pour générer un fichier image. +// try { +// laby.saveImage("resources/random.png"); +// } catch (IOException e1) { +// e1.printStackTrace(); +// } + + } + + +} diff --git a/src/Parcours.java b/src/Parcours.java new file mode 100644 index 0000000000000000000000000000000000000000..a6db088ea3b38019b8b63e28af09640ddcba6960 --- /dev/null +++ b/src/Parcours.java @@ -0,0 +1,65 @@ +import java.util.ArrayList; +import java.util.BitSet; +import java.util.LinkedList; +import java.util.Stack; + + +public class Parcours { + + Graph graph; + Stack<Arc> frontier; + BitSet reached; + ArrayList<Arc> predecessor; + + private void etendsFrontiere(int sommet) { + for (Arc a : graph.outNeighbours(sommet)) + frontier.add(a); + } + + + private void explore(Arc a) { + if (reached.get(a.getDest())) return; + reached.set(a.getDest()); + etendsFrontiere(a.getDest()); + predecessor.set(a.getDest(), a); + } + + private void parcours(int source) { + reached.set(source); + etendsFrontiere(source); + while (!frontier.isEmpty()) + explore(frontier.pop()); + + } + + private Parcours(Graph graph) { + this.graph = graph; + this.frontier = new Stack<>(); + this.reached = new BitSet(graph.order); + this.predecessor = new ArrayList<>(graph.order); + for (int i = 0; i < graph.order; i++) { + predecessor.add(null); + } + } + + + public static ArrayList<Arc> algo(Graph graph, int source) { + Parcours p = new Parcours(graph); + p.parcours(source); + return p.predecessor; + } + + + + + + + + + + + + + + +} diff --git a/src/RootedTree.java b/src/RootedTree.java new file mode 100644 index 0000000000000000000000000000000000000000..4e697184da8593584bc0e395b6cf53b973c06d73 --- /dev/null +++ b/src/RootedTree.java @@ -0,0 +1,317 @@ +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.Queue; + + +public class RootedTree { + + private int getHeight(Node n) { + return (n == null ? -1 : n.height); + } + + private int getSize(Node n) { + return (n == null ? -1 : n.size); + } + + + private class Node { + int vertex; + ArrayList<Node> sons; + + int height; + int size; + int depth; + + public Node(int vertex) { + this.vertex = vertex; + this.sons = new ArrayList<>(); + this.height = 0; + } + + + public void setHeight() { + int maxHeight = -1; + for (Node son : this.sons) + maxHeight = Math.max(maxHeight, son.height); + this.height = maxHeight + 1; + } + + + private void setSize() { + size = 1; + for (Node son : this.sons) size = size + son.size; + } + + private void setSonsDepth() { + for (Node son : this.sons) son.depth = this.depth + 1; + } + + private Node maxSizeSon() { + Node maxSon = null; + for (Node son : sons) { + if (son.size > getSize(maxSon)) maxSon = son; + } + return maxSon; + } + + private Node maxHeightSon() { + Node maxSon = null; + for (Node son : sons) { + if (son.height > getHeight(maxSon)) maxSon = son; + } + return maxSon; + } + + + private int secondMaxHeight() { + int maxHeight = -1; + int secondMaxHeight = -1; + for (Node son : sons) { + if (son.height > secondMaxHeight) { + secondMaxHeight = Math.min(maxHeight, son.height); + maxHeight = Math.max(maxHeight, son.height); + } + } + return secondMaxHeight; + } + + private void print() { + System.out.print("Node " + this.vertex + ", sons: "); + for (Node son : this.sons) { + System.out.print(son.vertex + " "); + } + System.out.println("(height: " + this.height + + ", size: " + this.size + + ", 2nd height: " + this.secondMaxHeight() + + ", depth: " + this.depth + + ")"); + } + + } + + // to write recursive algorithms without recursion + ArrayList<Node> inverseBfsOrder; + ArrayList<Node> bfsOrder; + + Node nodes[]; + Node root; + int order; + + + // Tree initialization + + public void computeAllHeights() { + for(Node n : inverseBfsOrder) n.setHeight(); + } + + + public void computeAllSizes() { + for (Node n : inverseBfsOrder) n.setSize(); + } + + + public void computeAllDepths() { + root.depth = 0; + for (Node n : bfsOrder) n.setSonsDepth(); + } + + + + // Tree invariants + + + // sum of distances between all pairs of vertices. + public long getWienerIndex() { + long count = 0; + for (Node n : bfsOrder) { + if (n == root) continue; + count = count + n.size * (order - n.size); + } + return count; + } + + + public int[] getDegreeDistribution(int maxDegree) { + int maxIndex = Math.min(maxDegree,order-1); + int[] degrees = new int[1+maxIndex]; + for(int i = 0; i <= maxIndex; i++) degrees[i] = 0; + int degree; + for (Node n : bfsOrder) { + degree = n.sons.size() + (n == root ? 0 : 1); + if (degree <= maxIndex) + degrees[degree]++; + } + return degrees; + } + + + public int getRadius() { + return root.height; + } + + + public int getDiameter() { + return root.height + root.secondMaxHeight() + 1; + } + + + private Node getCentroidNode() { + Node centroid = root; + while (centroid.maxSizeSon().size * 2 > order) + centroid = centroid.maxSizeSon(); + return centroid; + } + + public int getDistanceFromCenterToCentroid() { + return getCentroidNode().depth; + } + + public double getAverageEccentricity() { + int sumEccentricity = 0; + for (Node n : bfsOrder) + sumEccentricity = sumEccentricity + n.depth; + return (double) sumEccentricity / (double) order; + } + + + // Node accessors + + public int getRoot() { return root.vertex; } + + public int getHeight(int vertex) { + return nodes[vertex].height; + } + + public int getDepth(int vertex) { + return nodes[vertex].depth; + } + + public int getSubtreeSize(int vertex) { + return nodes[vertex].size; + } + + public int getCentroid() { + return getCentroidNode().vertex; + } + + + // printers + + public void printStats() { + System.out.println("Order: " + order); + System.out.println("Diameter: " + getDiameter()); + System.out.println("Radius: " + getRadius()); + System.out.println("Wiener index: " + getWienerIndex()); + System.out.println("Center to centroid: " + + getDistanceFromCenterToCentroid()); + System.out.println("Average eccentricity: " + + getAverageEccentricity()); + } + + + + public void printNode(int vertex) { + nodes[vertex].print(); + } + + public void printTree() { + for (Node n : bfsOrder) n.print(); + } + + // Below to end: building the tree from list of arcs. + // We want the center of the tree as root. + // 1) createTree: Gets a tree encoded in the Node structure. + // This is done by using bfs algorithm on the graph of edges. + // From the bfs list of arcs, creates each node and attach it to father + // Stores each node in an array indexed by vertices. + // 2) Computes the height of every node, in inverse bfs order. + // 3) rerootTree: Moves root toward center. + // the two highest sons must have almost the same height. + // it detects if it is balanced, + // and if not moves the root to the highest son (swapRootWith) + // 4) resetBfsOrdering : recomputes bfs and inverse bfs order. + // this time, a bfs on the node structure is enough + // 5) Computes height, size and depth of every node. + + private void resetBfsOrdering() { + Queue<Node> stack = new LinkedList<Node>(); + stack.offer(root); + bfsOrder.clear(); + inverseBfsOrder.clear(); + Node current; + while (!stack.isEmpty()) { + current = stack.poll(); + for (Node son : current.sons) stack.offer(son); + bfsOrder.add(current); + inverseBfsOrder.add(current); + } + Collections.reverse(inverseBfsOrder); + + } + + + private void swapRootWith(Node son) { + root.sons.remove(son); + root.setHeight(); + son.height = Math.max(root.height + 1, son.height); + son.sons.add(root); + root = son; + } + + + private boolean isUnbalanced() { + return root.height > root.secondMaxHeight() + 2; + } + + private void rerootTree() { + computeAllHeights(); + while (isUnbalanced()) + swapRootWith(root.maxHeightSon()); + resetBfsOrdering(); + } + + + private void createNode(Node nodes[], Arc arc) { + int son = arc.getDest(); + int father = arc.getSource(); + nodes[son] = new Node(son); + nodes[father].sons.add(nodes[son]); + } + + + private void createTree(int root, ArrayList<Arc> sortedArcs) { + this.bfsOrder = new ArrayList<>(order); + this.inverseBfsOrder = new ArrayList<>(order); + nodes = new Node[order]; + nodes[root] = new Node(root); + + this.bfsOrder.add(nodes[root]); + for (Arc arc : sortedArcs) { + createNode(nodes,arc); + this.bfsOrder.add(nodes[arc.getDest()]); + } + + inverseBfsOrder.addAll(bfsOrder); + Collections.reverse(inverseBfsOrder); + this.root = nodes[root]; + } + + + public RootedTree(ArrayList<Edge> edges, int root) { + this.order = edges.size() + 1; + Graph graph = new Graph(order); + for (Edge e : edges) graph.addEdge(e); + + createTree(root, BreadthFirstSearch.generateTree(graph, root)); + + rerootTree(); + computeAllHeights(); + computeAllSizes(); + computeAllDepths(); + } + + + + +}