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();
+	}
+	
+		
+	
+	
+}