diff --git a/src/main/org/insa/algo/shortestpath/BellmanFordAlgorithm.java b/src/main/org/insa/algo/shortestpath/BellmanFordAlgorithm.java index e423b9e..fddac4f 100644 --- a/src/main/org/insa/algo/shortestpath/BellmanFordAlgorithm.java +++ b/src/main/org/insa/algo/shortestpath/BellmanFordAlgorithm.java @@ -24,7 +24,7 @@ public class BellmanFordAlgorithm extends ShortestPathAlgorithm { ShortestPathData data = getInputData(); Graph graph = data.getGraph(); - final int nbNodes = graph.getNodes().size(); + final int nbNodes = graph.size(); // Initialize array of distances. double[] distances = new double[nbNodes]; @@ -41,8 +41,8 @@ public class BellmanFordAlgorithm extends ShortestPathAlgorithm { boolean found = false; for (int i = 0; !found && i < nbNodes; ++i) { found = true; - for (Node node: graph.getNodes()) { - for (Arc arc: node.getSuccessors()) { + for (Node node: graph) { + for (Arc arc: node) { // Small test to check allowed roads... if (!data.isAllowed(arc)) { diff --git a/src/main/org/insa/algo/utils/BinaryHeap.java b/src/main/org/insa/algo/utils/BinaryHeap.java index 09a208f..814c02a 100644 --- a/src/main/org/insa/algo/utils/BinaryHeap.java +++ b/src/main/org/insa/algo/utils/BinaryHeap.java @@ -1,9 +1,9 @@ // // ******************PUBLIC OPERATIONS********************* -// void insert( x ) --> Insert x +// void insert( x ) --> Insert x // Comparable deleteMin( )--> Return and remove smallest item -// Comparable findMin( ) --> Return smallest item -// boolean isEmpty( ) --> Return true if empty; else false +// Comparable findMin( ) --> Return smallest item +// boolean isEmpty( ) --> Return true if empty; else false // ******************ERRORS******************************** // Throws RuntimeException for findMin and deleteMin when empty @@ -60,20 +60,6 @@ public class BinaryHeap> { } } - /** - * @return true if the heap is empty, false otherwise. - */ - public boolean isEmpty() { - return this.currentSize == 0; - } - - /** - * @return Current size (number of elements) of this heap. - */ - public int size() { - return this.currentSize; - } - /** * @return Index of the parent of the given index. */ @@ -88,17 +74,6 @@ public class BinaryHeap> { return index * 2 + 1; } - /** - * Insert the given element into the heap. - * - * @param x Item to insert. - */ - public void insert(E x) { - int index = this.currentSize++; - this.arraySet(index, x); - this.percolateUp(index); - } - /** * Internal method to percolate up in the heap. * @@ -151,6 +126,41 @@ public class BinaryHeap> { } } + /** + * @return true if the heap is empty, false otherwise. + */ + public boolean isEmpty() { + return this.currentSize == 0; + } + + /** + * @return Current size (number of elements) of this heap. + */ + public int size() { + return this.currentSize; + } + + /** + * Insert the given element into the heap. + * + * @param x Item to insert. + */ + public void add(E x) { + int index = this.currentSize++; + this.arraySet(index, x); + this.percolateUp(index); + } + + /** + * Tell the binary heap that the given element has been modified and should be + * re-positioned inside the heap. + * + * @param x Item to update. + */ + public void update(E x) { + // TODO: + } + /** * Find the smallest item in the heap. * @@ -160,7 +170,7 @@ public class BinaryHeap> { */ public E findMin() throws RuntimeException { if (isEmpty()) - throw new RuntimeException("Empty binary heap"); + throw new RuntimeException("Empty binary heap."); return this.array.get(0); } diff --git a/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentsAlgorithm.java b/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentsAlgorithm.java index 436c8f8..04f5138 100644 --- a/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentsAlgorithm.java +++ b/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentsAlgorithm.java @@ -4,7 +4,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedList; -import java.util.List; import java.util.Queue; import org.insa.algo.AbstractAlgorithm; @@ -72,14 +71,14 @@ public class WeaklyConnectedComponentsAlgorithm * graph. */ protected ArrayList> createUndirectedGraph() { - int nNodes = getInputData().getGraph().getNodes().size(); + int nNodes = getInputData().getGraph().size(); ArrayList> res = new ArrayList>(nNodes); for (int i = 0; i < nNodes; ++i) { res.add(new HashSet()); } - for (Node node: getInputData().getGraph().getNodes()) { - for (Arc arc: node.getSuccessors()) { + for (Node node: getInputData().getGraph()) { + for (Arc arc: node) { res.get(node.getId()).add(arc.getDestination().getId()); if (arc.getRoadInformation().isOneWay()) { res.get(arc.getDestination().getId()).add(node.getId()); @@ -100,27 +99,27 @@ public class WeaklyConnectedComponentsAlgorithm * @return */ protected ArrayList bfs(ArrayList> ugraph, boolean[] marked, int cur) { - List nodes = getInputData().getGraph().getNodes(); + Graph graph = getInputData().getGraph(); ArrayList component = new ArrayList(); // Using a queue because we are doing a BFS Queue queue = new LinkedList(); // Notify observers about the current component. - notifyStartComponent(nodes.get(cur)); + notifyStartComponent(graph.get(cur)); // Add original node and loop until the queue is empty. queue.add(cur); marked[cur] = true; while (!queue.isEmpty()) { - Node node = nodes.get(queue.remove()); + Node node = graph.get(queue.remove()); component.add(node); // Notify observers notifyNewNodeInComponent(node); for (Integer destId: ugraph.get(node.getId())) { - Node dest = nodes.get(destId); + Node dest = graph.get(destId); if (!marked[dest.getId()]) { queue.add(destId); marked[destId] = true; @@ -138,7 +137,7 @@ public class WeaklyConnectedComponentsAlgorithm Graph graph = getInputData().getGraph(); ArrayList> ugraph = createUndirectedGraph(); - boolean[] marked = new boolean[graph.getNodes().size()]; + boolean[] marked = new boolean[graph.size()]; Arrays.fill(marked, false); ArrayList> components = new ArrayList>(); diff --git a/src/main/org/insa/graph/Graph.java b/src/main/org/insa/graph/Graph.java index 11dadb5..570dcbb 100644 --- a/src/main/org/insa/graph/Graph.java +++ b/src/main/org/insa/graph/Graph.java @@ -1,7 +1,7 @@ package org.insa.graph; import java.util.ArrayList; -import java.util.Collections; +import java.util.Iterator; import java.util.List; /** @@ -11,7 +11,7 @@ import java.util.List; * holds a list of nodes and each node holds a list of its successors. * */ -public class Graph { +public class Graph implements Iterable { // Map identifier. private final String mapId; @@ -48,10 +48,28 @@ public class Graph { } /** - * @return Immutable view of the list of nodes of this graph. + * Fetch the node with the given ID. + * + * Complexity: O(1). + * + * @param id ID of the node to fetch. + * + * @return Node with the given ID. */ - public List getNodes() { - return Collections.unmodifiableList(nodes); + public Node get(int id) { + return this.nodes.get(id); + } + + /** + * @return Number of nodes in this graph. + */ + public int size() { + return this.nodes.size(); + } + + @Override + public Iterator iterator() { + return this.nodes.iterator(); } /** @@ -78,7 +96,7 @@ public class Graph { } for (Node node: nodes) { Node orig = trNodes.get(node.getId()); - for (Arc arc: node.getSuccessors()) { + for (Arc arc: node) { if (arc.getRoadInformation().isOneWay()) { Node dest = trNodes.get(arc.getDestination().getId()); dest.addSuccessor(new ArcBackward(new ArcForward(orig, dest, arc.getLength(), diff --git a/src/main/org/insa/graph/Node.java b/src/main/org/insa/graph/Node.java index 4ba7917..3722bc9 100644 --- a/src/main/org/insa/graph/Node.java +++ b/src/main/org/insa/graph/Node.java @@ -2,7 +2,7 @@ package org.insa.graph; import java.util.ArrayList; import java.util.Collections; -import java.util.List; +import java.util.Iterator; /** * Class representing a Node in a {@link Graph}. @@ -13,7 +13,7 @@ import java.util.List; * Nodes are comparable based on their ID. * */ -public class Node implements Comparable { +public class Node implements Comparable, Iterable { /** * Link the two given nodes with one or two arcs (depending on roadInformation), @@ -94,10 +94,22 @@ public class Node implements Comparable { } /** - * @return Immutable view of the list of successors of this node. + * @return Number of successors of this node. */ - public List getSuccessors() { - return Collections.unmodifiableList(successors); + public int getNumberOfSuccessors() { + return this.successors.size(); + } + + /** + * @return true if this node has at least one successor. + */ + public boolean hasSuccessors() { + return !this.successors.isEmpty(); + } + + @Override + public Iterator iterator() { + return this.successors.iterator(); } /** diff --git a/src/main/org/insa/graph/io/BinaryPathReader.java b/src/main/org/insa/graph/io/BinaryPathReader.java index 91e7478..bb3336f 100644 --- a/src/main/org/insa/graph/io/BinaryPathReader.java +++ b/src/main/org/insa/graph/io/BinaryPathReader.java @@ -70,7 +70,7 @@ public class BinaryPathReader extends BinaryReader implements PathReader { * @throws IndexOutOfBoundsException if the node is not in the graph. */ protected Node readNode(Graph graph) throws IOException { - return graph.getNodes().get(dis.readInt()); + return graph.get(dis.readInt()); } } diff --git a/src/main/org/insa/graphics/MainWindow.java b/src/main/org/insa/graphics/MainWindow.java index 04409bf..b10f643 100644 --- a/src/main/org/insa/graphics/MainWindow.java +++ b/src/main/org/insa/graphics/MainWindow.java @@ -602,8 +602,8 @@ public class MainWindow extends JFrame { // name that are right-to-left (e.g. arabic names). info += " - " + graph.getMapName() + "\u200e"; } - info += ", " + graph.getNodes().size() + " nodes, " - + graph.getGraphInformation().getArcCount() + " arcs."; + info += ", " + graph.size() + " nodes, " + graph.getGraphInformation().getArcCount() + + " arcs."; graphInfoPanel.setText(info); drawGraph(); diff --git a/src/main/org/insa/graphics/NodesInputPanel.java b/src/main/org/insa/graphics/NodesInputPanel.java index 54cd9f8..b9a020e 100644 --- a/src/main/org/insa/graphics/NodesInputPanel.java +++ b/src/main/org/insa/graphics/NodesInputPanel.java @@ -65,7 +65,7 @@ public class NodesInputPanel extends JPanel public Node findClosestNode(Point point) { Node minNode = null; double minDis = Double.POSITIVE_INFINITY; - for (Node node: graph.getNodes()) { + for (Node node: graph) { double dlon = point.getLongitude() - node.getPoint().getLongitude(); double dlat = point.getLatitude() - node.getPoint().getLatitude(); double dis = dlon * dlon + dlat * dlat; // No need to square @@ -308,7 +308,7 @@ public class NodesInputPanel extends JPanel */ protected Node getNodeForInput(JTextField textfield) { try { - Node node = graph.getNodes().get(Integer.valueOf(textfield.getText().trim())); + Node node = graph.get(Integer.valueOf(textfield.getText().trim())); return node; } catch (IllegalArgumentException | IndexOutOfBoundsException ex) { diff --git a/src/main/org/insa/graphics/drawing/components/BasicDrawing.java b/src/main/org/insa/graphics/drawing/components/BasicDrawing.java index 2e9444e..88a87b9 100644 --- a/src/main/org/insa/graphics/drawing/components/BasicDrawing.java +++ b/src/main/org/insa/graphics/drawing/components/BasicDrawing.java @@ -614,7 +614,7 @@ public class BasicDrawing extends JPanel implements Drawing { @Override public void drawGraph(Graph graph, GraphPalette palette) { - int repaintModulo = Math.max(1, graph.getNodes().size() / 100); + int repaintModulo = Math.max(1, graph.size() / 100); // Initialize the buffered image @@ -625,8 +625,8 @@ public class BasicDrawing extends JPanel implements Drawing { this.removeMouseMotionListener(zoomAndPanListener); this.removeMouseWheelListener(zoomAndPanListener); - for (Node node: graph.getNodes()) { - for (Arc arc: node.getSuccessors()) { + for (Node node: graph) { + for (Arc arc: node) { // Draw arcs only if there are one-way arcs or if origin is lower than // destination, avoid drawing two-ways arc twice. if (arc.getRoadInformation().isOneWay() diff --git a/src/test/org/insa/algo/utils/BinaryHeapTest.java b/src/test/org/insa/algo/utils/BinaryHeapTest.java index 47a8345..3501e04 100644 --- a/src/test/org/insa/algo/utils/BinaryHeapTest.java +++ b/src/test/org/insa/algo/utils/BinaryHeapTest.java @@ -11,29 +11,68 @@ import org.junit.Test; public class BinaryHeapTest { - private int[] data1 = IntStream.range(0, 20).toArray(); - private int[] data2 = { 8, 1, 6, 3, 4, 5, 9 }; + class MutableInteger implements Comparable { - private BinaryHeap heap1, heap2; + // Actual value + private int value; + + public MutableInteger(int value) { + this.value = value; + } + + /** + * @return The integer value stored inside this MutableInteger. + */ + public int get() { + return this.value; + } + + /** + * Update the integer value stored inside this MutableInteger. + * + * @param value New value to set. + */ + public void set(int value) { + this.value = value; + } + + @Override + public int compareTo(MutableInteger other) { + return Integer.compare(this.value, other.value); + } + + }; + + // Raw data arrays. + private MutableInteger[] data1 = IntStream.range(0, 20).mapToObj(MutableInteger::new) + .toArray(MutableInteger[]::new); + private MutableInteger[] data2 = Arrays.stream(new int[] { 8, 1, 6, 3, 4, 5, 9 }) + .mapToObj(MutableInteger::new).toArray(MutableInteger[]::new); + + // Actual heap. + private BinaryHeap heap1, heap2; @Before public void init() { // Create the range heap - this.heap1 = new BinaryHeap(); - this.heap2 = new BinaryHeap(); + this.heap1 = new BinaryHeap<>(); + this.heap2 = new BinaryHeap<>(); - for (int v: data1) - this.heap1.insert(v); - for (int v: data2) - this.heap2.insert(v); + for (MutableInteger v: data1) { + this.heap1.add(v); + } + + for (MutableInteger v: data2) { + this.heap2.add(v); + } } @Test public void testInsert() { - BinaryHeap heap = new BinaryHeap(); + BinaryHeap heap = new BinaryHeap<>(); int size = 0; - for (int x: data1) { - heap.insert(x); + for (MutableInteger x: data1) { + heap.add(x); size += 1; assertEquals(heap.size(), size); } @@ -41,8 +80,8 @@ public class BinaryHeapTest { heap = new BinaryHeap<>(); size = 0; - for (int x: data2) { - heap.insert(x); + for (MutableInteger x: data2) { + heap.add(x); size += 1; assertEquals(heap.size(), size); } @@ -52,11 +91,10 @@ public class BinaryHeapTest { @Test public void testDeleteMin() { // range 1 (sorted) - int[] range1 = data1; - int size = range1.length; + int size = data1.length; assertEquals(heap1.size(), size); - for (int x: range1) { - assertEquals(heap1.deleteMin().intValue(), x); + for (MutableInteger x: data1) { + assertEquals(heap1.deleteMin(), x); size -= 1; assertEquals(heap1.size(), size); } @@ -64,12 +102,12 @@ public class BinaryHeapTest { assertTrue(heap1.isEmpty()); // range 2 (was not sorted) - int[] range2 = Arrays.copyOf(data2, data2.length); + MutableInteger[] range2 = Arrays.copyOf(data2, data2.length); Arrays.sort(range2); size = range2.length; assertEquals(heap2.size(), size); - for (int x: range2) { - assertEquals(heap2.deleteMin().intValue(), x); + for (MutableInteger x: range2) { + assertEquals(heap2.deleteMin().get(), x.get()); size -= 1; assertEquals(heap2.size(), size); } @@ -77,4 +115,12 @@ public class BinaryHeapTest { assertTrue(heap2.isEmpty()); } + @Test + public void testUpdate() { + MutableInteger newMin = data2[data2.length - 1]; + newMin.set(0); + heap2.update(newMin); + assertEquals(heap2.findMin(), newMin); + } + } diff --git a/src/test/org/insa/graph/GraphTest.java b/src/test/org/insa/graph/GraphTest.java index 915eaf2..25e934c 100644 --- a/src/test/org/insa/graph/GraphTest.java +++ b/src/test/org/insa/graph/GraphTest.java @@ -62,7 +62,7 @@ public class GraphTest { */ private List getArcsBetween(Node a, Node b) { List arcs = new ArrayList<>(); - for (Arc arc: a.getSuccessors()) { + for (Arc arc: a) { if (arc.getDestination().equals(b)) { arcs.add(arc); } @@ -76,53 +76,33 @@ public class GraphTest { // Basic asserts... assertEquals("R/" + graph.getMapId(), transpose.getMapId()); - assertEquals(graph.getNodes().size(), transpose.getNodes().size()); + assertEquals(graph.size(), transpose.size()); final int expNbSucc[] = { 4, 2, 2, 4, 2 }; for (int i = 0; i < expNbSucc.length; ++i) { - assertEquals(expNbSucc[i], transpose.getNodes().get(i).getSuccessors().size()); + assertEquals(expNbSucc[i], transpose.get(i).getNumberOfSuccessors()); } - assertEquals( - getArcsBetween(transpose.getNodes().get(0), transpose.getNodes().get(1)).size(), 1); - assertEquals( - getArcsBetween(transpose.getNodes().get(0), transpose.getNodes().get(2)).size(), 1); - assertEquals( - getArcsBetween(transpose.getNodes().get(0), transpose.getNodes().get(3)).size(), 1); - assertEquals( - getArcsBetween(transpose.getNodes().get(0), transpose.getNodes().get(4)).size(), 1); - assertEquals( - getArcsBetween(transpose.getNodes().get(1), transpose.getNodes().get(0)).size(), 1); - assertEquals( - getArcsBetween(transpose.getNodes().get(1), transpose.getNodes().get(2)).size(), 1); - assertEquals( - getArcsBetween(transpose.getNodes().get(1), transpose.getNodes().get(3)).size(), 0); - assertEquals( - getArcsBetween(transpose.getNodes().get(1), transpose.getNodes().get(4)).size(), 0); - assertEquals( - getArcsBetween(transpose.getNodes().get(2), transpose.getNodes().get(0)).size(), 1); - assertEquals( - getArcsBetween(transpose.getNodes().get(2), transpose.getNodes().get(1)).size(), 1); - assertEquals( - getArcsBetween(transpose.getNodes().get(2), transpose.getNodes().get(3)).size(), 0); - assertEquals( - getArcsBetween(transpose.getNodes().get(2), transpose.getNodes().get(4)).size(), 0); - assertEquals( - getArcsBetween(transpose.getNodes().get(3), transpose.getNodes().get(0)).size(), 1); - assertEquals( - getArcsBetween(transpose.getNodes().get(3), transpose.getNodes().get(1)).size(), 0); - assertEquals( - getArcsBetween(transpose.getNodes().get(3), transpose.getNodes().get(2)).size(), 3); - assertEquals( - getArcsBetween(transpose.getNodes().get(3), transpose.getNodes().get(4)).size(), 0); - assertEquals( - getArcsBetween(transpose.getNodes().get(4), transpose.getNodes().get(0)).size(), 1); - assertEquals( - getArcsBetween(transpose.getNodes().get(4), transpose.getNodes().get(1)).size(), 0); - assertEquals( - getArcsBetween(transpose.getNodes().get(4), transpose.getNodes().get(2)).size(), 0); - assertEquals( - getArcsBetween(transpose.getNodes().get(4), transpose.getNodes().get(3)).size(), 1); + assertEquals(getArcsBetween(transpose.get(0), transpose.get(1)).size(), 1); + assertEquals(getArcsBetween(transpose.get(0), transpose.get(2)).size(), 1); + assertEquals(getArcsBetween(transpose.get(0), transpose.get(3)).size(), 1); + assertEquals(getArcsBetween(transpose.get(0), transpose.get(4)).size(), 1); + assertEquals(getArcsBetween(transpose.get(1), transpose.get(0)).size(), 1); + assertEquals(getArcsBetween(transpose.get(1), transpose.get(2)).size(), 1); + assertEquals(getArcsBetween(transpose.get(1), transpose.get(3)).size(), 0); + assertEquals(getArcsBetween(transpose.get(1), transpose.get(4)).size(), 0); + assertEquals(getArcsBetween(transpose.get(2), transpose.get(0)).size(), 1); + assertEquals(getArcsBetween(transpose.get(2), transpose.get(1)).size(), 1); + assertEquals(getArcsBetween(transpose.get(2), transpose.get(3)).size(), 0); + assertEquals(getArcsBetween(transpose.get(2), transpose.get(4)).size(), 0); + assertEquals(getArcsBetween(transpose.get(3), transpose.get(0)).size(), 1); + assertEquals(getArcsBetween(transpose.get(3), transpose.get(1)).size(), 0); + assertEquals(getArcsBetween(transpose.get(3), transpose.get(2)).size(), 3); + assertEquals(getArcsBetween(transpose.get(3), transpose.get(4)).size(), 0); + assertEquals(getArcsBetween(transpose.get(4), transpose.get(0)).size(), 1); + assertEquals(getArcsBetween(transpose.get(4), transpose.get(1)).size(), 0); + assertEquals(getArcsBetween(transpose.get(4), transpose.get(2)).size(), 0); + assertEquals(getArcsBetween(transpose.get(4), transpose.get(3)).size(), 1); } } diff --git a/src/test/org/insa/graph/NodeTest.java b/src/test/org/insa/graph/NodeTest.java index eeba7ef..c64a5dd 100644 --- a/src/test/org/insa/graph/NodeTest.java +++ b/src/test/org/insa/graph/NodeTest.java @@ -18,7 +18,7 @@ public class NodeTest { public void initAll() throws IOException { // Create nodes - nodes = new Node[5]; + nodes = new Node[6]; for (int i = 0; i < nodes.length; ++i) { nodes[i] = new Node(i, null); } @@ -54,7 +54,7 @@ public class NodeTest { * @return The first arc between from a to b, or null. */ private Arc getFirstArcBetween(Node a, Node b) { - for (Arc arc: a.getSuccessors()) { + for (Arc arc: a) { if (arc.getDestination().equals(b)) { return arc; } @@ -63,13 +63,25 @@ public class NodeTest { } @Test - public void testLinkNodes() { - final int[] expNbSucc = { 4, 2, 5, 2, 1 }; + public void testGetNumberOfSuccessors() { + final int[] expNbSucc = { 4, 2, 5, 2, 1, 0 }; assertEquals(nodes.length, expNbSucc.length); for (int i = 0; i < expNbSucc.length; ++i) { - assertEquals(nodes[i].getSuccessors().size(), expNbSucc[i]); + assertEquals(nodes[i].getNumberOfSuccessors(), expNbSucc[i]); } + } + @Test + public void testHasSuccessors() { + final int[] expNbSucc = { 4, 2, 5, 2, 1, 0 }; + assertEquals(nodes.length, expNbSucc.length); + for (int i = 0; i < expNbSucc.length; ++i) { + assertEquals(nodes[i].hasSuccessors(), expNbSucc[i] != 0); + } + } + + @Test + public void testLinkNodes() { assertEquals(getFirstArcBetween(nodes[0], nodes[1]).getRoadInformation(), getFirstArcBetween(nodes[1], nodes[0]).getRoadInformation()); }