New updates.

This commit is contained in:
Holt59 2018-03-25 14:35:15 +02:00
parent 5b733e25c7
commit aab8743d3c
24 changed files with 893 additions and 388 deletions

View File

@ -2,6 +2,7 @@ package org.insa.algo;
import org.insa.graph.Arc; import org.insa.graph.Arc;
import org.insa.graph.Graph; import org.insa.graph.Graph;
import org.insa.graph.GraphStatistics;
/** /**
* Base class for algorithm input data classes. This class contains the basic * Base class for algorithm input data classes. This class contains the basic
@ -12,77 +13,29 @@ import org.insa.graph.Graph;
public abstract class AbstractInputData { public abstract class AbstractInputData {
/** /**
* Mode for computing costs on the arc (time or length). * Enum specifying the top mode of the algorithms.
* *
* @see ArcInspector
*/ */
public enum Mode { public enum Mode {
TIME, LENGTH TIME, LENGTH
} }
/**
* Filtering interface for arcs - This class can be used to indicate to an
* algorithm which arc can be used.
*
*/
public interface ArcFilter {
/**
* Check if the given arc can be used (is allowed).
*
* @param arc Arc to check.
*
* @return true if the given arc is allowed.
*/
public boolean isAllowed(Arc arc);
}
// Graph // Graph
private final Graph graph; private final Graph graph;
// Mode for the computation of the costs.
private final Mode mode;
// Arc filter. // Arc filter.
private final ArcFilter arcFilter; protected final ArcInspector arcInspector;
/** /**
* Create a new AbstractInputData instance for the given graph, mode and filter. * Create a new AbstractInputData instance for the given graph, mode and filter.
* *
* @param graph * @param graph Graph for this input data.
* @parma mode * @param arcInspector Arc inspector for this input data.
* @param arcFilter
*/ */
protected AbstractInputData(Graph graph, Mode mode, ArcFilter arcFilter) { protected AbstractInputData(Graph graph, ArcInspector arcInspector) {
this.graph = graph; this.graph = graph;
this.mode = mode; this.arcInspector = arcInspector;
this.arcFilter = arcFilter;
}
/**
* Create a new AbstractInputData instance for the given graph and mode, with no
* filtering on the arc.
*
* @param graph
* @param mode
*/
protected AbstractInputData(Graph graph, Mode mode) {
this(graph, mode, new AbstractInputData.ArcFilter() {
@Override
public boolean isAllowed(Arc arc) {
return true;
}
});
}
/**
* Create a new AbstractInputData instance for the given graph, with default
* mode (LENGHT), with no filtering on the arc.
*
* @param graph
*/
protected AbstractInputData(Graph graph) {
this(graph, Mode.LENGTH);
} }
/** /**
@ -93,12 +46,39 @@ public abstract class AbstractInputData {
} }
/** /**
* @return Mode of the algorithm (time or length). * Retrieve the cost associated with the given arc according to the underlying
* arc inspector.
*
* @param arc Arc for which cost should be retrieved.
*
* @return Cost for the given arc.
*
* @see ArcInspector
*/
public double getCost(Arc arc) {
return this.arcInspector.getCost(arc);
}
/**
* @return Mode associated with this input data.
* *
* @see Mode * @see Mode
*/ */
public Mode getMode() { public Mode getMode() {
return mode; return this.arcInspector.getMode();
}
/**
* Retrieve the maximum speed associated with this input data, or
* {@link GraphStatistics#NO_MAXIMUM_SPEED} if none is associated. The maximum
* speed associated with input data is different from the maximum speed
* associated with graph (accessible via {@link Graph#getGraphInformation()}).
*
* @return The maximum speed for this inspector, or
* {@link GraphStatistics#NO_MAXIMUM_SPEED} if none is set.
*/
public int getMaximumSpeed() {
return this.arcInspector.getMaximumSpeed();
} }
/** /**
@ -108,10 +88,10 @@ public abstract class AbstractInputData {
* *
* @return true if the given arc is allowed. * @return true if the given arc is allowed.
* *
* @see ArcFilter * @see ArcInspector
*/ */
public boolean isAllowed(Arc arc) { public boolean isAllowed(Arc arc) {
return this.arcFilter.isAllowed(arc); return this.arcInspector.isAllowed(arc);
} }
} }

View File

@ -1,71 +0,0 @@
package org.insa.algo;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import org.insa.algo.AbstractInputData.ArcFilter;
import org.insa.graph.AccessRestrictions.AccessMode;
import org.insa.graph.AccessRestrictions.AccessRestriction;
import org.insa.graph.Arc;
public class ArcFilterFactory {
/**
* @return List of all arc filters in this factory.
*/
public static List<ArcFilter> getAllFilters() {
List<ArcFilter> filters = new ArrayList<>();
// Common filters:
// 1. No filter (all arcs allowed):
filters.add(new ArcFilter() {
@Override
public boolean isAllowed(Arc arc) {
return true;
}
@Override
public String toString() {
return "All roads are allowed.";
}
});
// 2. Only road allowed for cars:
filters.add(new ArcFilter() {
@Override
public boolean isAllowed(Arc arc) {
return arc.getRoadInformation().getAccessRestrictions()
.isAllowedForAny(AccessMode.MOTORCAR, EnumSet.complementOf(EnumSet
.of(AccessRestriction.FORBIDDEN, AccessRestriction.PRIVATE)));
}
@Override
public String toString() {
return "Only roads open for cars.";
}
});
// 3. Non-private roads for pedestrian and bicycle:
filters.add(new ArcFilter() {
@Override
public boolean isAllowed(Arc arc) {
return arc.getRoadInformation().getAccessRestrictions()
.isAllowedForAny(AccessMode.FOOT, EnumSet.complementOf(EnumSet
.of(AccessRestriction.FORBIDDEN, AccessRestriction.PRIVATE)));
}
@Override
public String toString() {
return "Non-private roads for pedestrian.";
}
});
// 3. Add your own filters here (do not forget to implement toString() to get an
// understandable output!):
return filters;
}
}

View File

@ -0,0 +1,43 @@
package org.insa.algo;
import org.insa.algo.AbstractInputData.Mode;
import org.insa.graph.Arc;
import org.insa.graph.GraphStatistics;
/**
* This class can be used to indicate to an algorithm which arcs can be used and
* the costs of the usable arcs..
*
*/
public interface ArcInspector {
/**
* Check if the given arc can be used (is allowed).
*
* @param arc Arc to check.
*
* @return true if the given arc is allowed.
*/
public boolean isAllowed(Arc arc);
/**
* Find the cost of the given arc.
*
* @param arc Arc for which the cost should be returned.
*
* @return Cost of the arc.
*/
public double getCost(Arc arc);
/**
* @return The maximum speed for this inspector, or
* {@link GraphStatistics#NO_MAXIMUM_SPEED} if none is set.
*/
public int getMaximumSpeed();
/**
* @return Mode for this arc inspector.
*/
public Mode getMode();
}

View File

@ -0,0 +1,149 @@
package org.insa.algo;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import org.insa.algo.AbstractInputData.Mode;
import org.insa.graph.AccessRestrictions.AccessMode;
import org.insa.graph.AccessRestrictions.AccessRestriction;
import org.insa.graph.Arc;
import org.insa.graph.GraphStatistics;
public class ArcInspectorFactory {
/**
* @return List of all arc filters in this factory.
*/
public static List<ArcInspector> getAllFilters() {
List<ArcInspector> filters = new ArrayList<>();
// Common filters:
// No filter (all arcs allowed):
filters.add(new ArcInspector() {
@Override
public boolean isAllowed(Arc arc) {
return true;
}
@Override
public double getCost(Arc arc) {
return arc.getLength();
}
@Override
public int getMaximumSpeed() {
return GraphStatistics.NO_MAXIMUM_SPEED;
}
@Override
public Mode getMode() {
return Mode.LENGTH;
}
@Override
public String toString() {
return "Shortest path, all roads allowed";
}
});
// Only road allowed for cars and length:
filters.add(new ArcInspector() {
@Override
public boolean isAllowed(Arc arc) {
return arc.getRoadInformation().getAccessRestrictions()
.isAllowedForAny(AccessMode.MOTORCAR, EnumSet.complementOf(EnumSet
.of(AccessRestriction.FORBIDDEN, AccessRestriction.PRIVATE)));
}
@Override
public double getCost(Arc arc) {
return arc.getLength();
}
@Override
public int getMaximumSpeed() {
return GraphStatistics.NO_MAXIMUM_SPEED;
}
@Override
public Mode getMode() {
return Mode.LENGTH;
}
@Override
public String toString() {
return "Shortest path, only roads open for cars";
}
});
// Only road allowed for cars and time:
filters.add(new ArcInspector() {
@Override
public boolean isAllowed(Arc arc) {
return arc.getRoadInformation().getAccessRestrictions()
.isAllowedForAny(AccessMode.MOTORCAR, EnumSet.complementOf(EnumSet
.of(AccessRestriction.FORBIDDEN, AccessRestriction.PRIVATE)));
}
@Override
public double getCost(Arc arc) {
return arc.getMinimumTravelTime();
}
@Override
public int getMaximumSpeed() {
return GraphStatistics.NO_MAXIMUM_SPEED;
}
@Override
public Mode getMode() {
return Mode.TIME;
}
@Override
public String toString() {
return "Fastest path, only roads open for cars";
}
});
// Non-private roads for pedestrian and bicycle:
filters.add(new ArcInspector() {
@Override
public boolean isAllowed(Arc arc) {
return arc.getRoadInformation().getAccessRestrictions()
.isAllowedForAny(AccessMode.FOOT, EnumSet.complementOf(EnumSet
.of(AccessRestriction.FORBIDDEN, AccessRestriction.PRIVATE)));
}
@Override
public double getCost(Arc arc) {
return arc.getTravelTime(
Math.min(getMaximumSpeed(), arc.getRoadInformation().getMaximumSpeed()));
}
@Override
public String toString() {
return "Fastest path for pedestrian";
}
@Override
public int getMaximumSpeed() {
return 5;
}
@Override
public Mode getMode() {
return Mode.TIME;
}
});
// Add your own filters here (do not forget to implement toString()
// to get an understandable output!):
return filters;
}
}

View File

@ -1,12 +1,13 @@
package org.insa.algo.carpooling; package org.insa.algo.carpooling;
import org.insa.algo.AbstractInputData; import org.insa.algo.AbstractInputData;
import org.insa.algo.ArcInspector;
import org.insa.graph.Graph; import org.insa.graph.Graph;
public class CarPoolingData extends AbstractInputData { public class CarPoolingData extends AbstractInputData {
protected CarPoolingData(Graph graph, Mode mode, ArcFilter arcFilter) { protected CarPoolingData(Graph graph, ArcInspector arcFilter) {
super(graph, mode, arcFilter); super(graph, arcFilter);
} }
} }

View File

@ -1,12 +1,13 @@
package org.insa.algo.packageswitch; package org.insa.algo.packageswitch;
import org.insa.algo.AbstractInputData; import org.insa.algo.AbstractInputData;
import org.insa.algo.ArcInspector;
import org.insa.graph.Graph; import org.insa.graph.Graph;
public class PackageSwitchData extends AbstractInputData { public class PackageSwitchData extends AbstractInputData {
protected PackageSwitchData(Graph graph, Mode mode, ArcFilter arcFilter) { protected PackageSwitchData(Graph graph, ArcInspector arcFilter) {
super(graph, mode, arcFilter); super(graph, arcFilter);
} }
} }

View File

@ -4,7 +4,6 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import org.insa.algo.AbstractInputData;
import org.insa.algo.AbstractSolution.Status; import org.insa.algo.AbstractSolution.Status;
import org.insa.graph.Arc; import org.insa.graph.Arc;
import org.insa.graph.Graph; import org.insa.graph.Graph;
@ -37,7 +36,8 @@ public class BellmanFordAlgorithm extends ShortestPathAlgorithm {
// Initialize array of predecessors. // Initialize array of predecessors.
Arc[] predecessorArcs = new Arc[nbNodes]; Arc[] predecessorArcs = new Arc[nbNodes];
// Actual algorithm, we will assume the graph does not contain negative cycle... // Actual algorithm, we will assume the graph does not contain negative
// cycle...
boolean found = false; boolean found = false;
for (int i = 0; !found && i < nbNodes; ++i) { for (int i = 0; !found && i < nbNodes; ++i) {
found = true; found = true;
@ -50,9 +50,7 @@ public class BellmanFordAlgorithm extends ShortestPathAlgorithm {
} }
// Retrieve weight of the arc. // Retrieve weight of the arc.
double w = data.getMode() == AbstractInputData.Mode.LENGTH ? arc.getLength() double w = data.getCost(arc);
: arc.getMinimumTravelTime();
double oldDistance = distances[arc.getDestination().getId()]; double oldDistance = distances[arc.getDestination().getId()];
double newDistance = distances[node.getId()] + w; double newDistance = distances[node.getId()] + w;
@ -75,8 +73,7 @@ public class BellmanFordAlgorithm extends ShortestPathAlgorithm {
// Destination has no predecessor, the solution is infeasible... // Destination has no predecessor, the solution is infeasible...
if (predecessorArcs[data.getDestination().getId()] == null) { if (predecessorArcs[data.getDestination().getId()] == null) {
solution = new ShortestPathSolution(data, Status.INFEASIBLE); solution = new ShortestPathSolution(data, Status.INFEASIBLE);
} } else {
else {
// The destination has been found, notify the observers. // The destination has been found, notify the observers.
notifyDestinationReached(data.getDestination()); notifyDestinationReached(data.getDestination());

View File

@ -1,6 +1,7 @@
package org.insa.algo.shortestpath; package org.insa.algo.shortestpath;
import org.insa.algo.AbstractInputData; import org.insa.algo.AbstractInputData;
import org.insa.algo.ArcInspector;
import org.insa.graph.Graph; import org.insa.graph.Graph;
import org.insa.graph.Node; import org.insa.graph.Node;
@ -9,34 +10,17 @@ public class ShortestPathData extends AbstractInputData {
// Origin and destination nodes. // Origin and destination nodes.
private final Node origin, destination; private final Node origin, destination;
/**
* Construct a new instance of ShortestPathData with the given parameters and
* for which all arcs are allowed.
*
* @param graph Graph in which the path should be looked for.
* @param origin Origin node of the path.
* @param destination Destination node of the path.
* @param mode Cost mode for the path.
*/
public ShortestPathData(Graph graph, Node origin, Node destination, Mode mode) {
super(graph, mode);
this.origin = origin;
this.destination = destination;
}
/** /**
* Construct a new instance of ShortestPathInputData with the given parameters. * Construct a new instance of ShortestPathInputData with the given parameters.
* *
* @param graph Graph in which the path should be looked for. * @param graph Graph in which the path should be looked for.
* @param origin Origin node of the path. * @param origin Origin node of the path.
* @param destination Destination node of the path. * @param destination Destination node of the path.
* @param mode Cost mode for the path. * @param arcInspector Filter for arcs (used to allow only a specific set of
* @param arcFilter Filter for arcs (used to allow only a specific set of arcs * arcs in the graph to be used).
* in the graph to be used).
*/ */
public ShortestPathData(Graph graph, Node origin, Node destination, Mode mode, public ShortestPathData(Graph graph, Node origin, Node destination, ArcInspector arcInspector) {
AbstractInputData.ArcFilter arcFilter) { super(graph, arcInspector);
super(graph, mode, arcFilter);
this.origin = origin; this.origin = origin;
this.destination = destination; this.destination = destination;
} }
@ -58,6 +42,6 @@ public class ShortestPathData extends AbstractInputData {
@Override @Override
public String toString() { public String toString() {
return "Shortest-path from #" + origin.getId() + " to #" + destination.getId() + " [" return "Shortest-path from #" + origin.getId() + " to #" + destination.getId() + " ["
+ getMode().toString().toLowerCase() + "]"; + this.arcInspector.toString().toLowerCase() + "]";
} }
} }

View File

@ -1,7 +1,8 @@
package org.insa.algo.shortestpath; package org.insa.algo.shortestpath;
import org.insa.algo.AbstractInputData; import org.insa.algo.AbstractInputData.Mode;
import org.insa.algo.AbstractSolution; import org.insa.algo.AbstractSolution;
import org.insa.graph.Arc;
import org.insa.graph.Path; import org.insa.graph.Path;
public class ShortestPathSolution extends AbstractSolution { public class ShortestPathSolution extends AbstractSolution {
@ -59,14 +60,17 @@ public class ShortestPathSolution extends AbstractSolution {
getInputData().getOrigin().getId(), getInputData().getDestination().getId()); getInputData().getOrigin().getId(), getInputData().getDestination().getId());
} }
else { else {
double cost = 0;
for (Arc arc: getPath().getArcs()) {
cost += getInputData().getCost(arc);
}
info = String.format("Found a path from node #%d to node #%d", info = String.format("Found a path from node #%d to node #%d",
getInputData().getOrigin().getId(), getInputData().getDestination().getId()); getInputData().getOrigin().getId(), getInputData().getDestination().getId());
if (getInputData().getMode() == AbstractInputData.Mode.LENGTH) { if (getInputData().getMode() == Mode.LENGTH) {
info = String.format("%s, %.4f kilometers", info, (getPath().getLength() / 1000.0)); info = String.format("%s, %.4f kilometers", info, cost / 1000.0);
} }
else { else {
info = String.format("%s, %.4f minutes", info, info = String.format("%s, %.4f minutes", info, cost / 60.0);
(getPath().getMinimumTravelTime() / 60.0));
} }
} }
info += " in " + getSolvingTime().getSeconds() + " seconds."; info += " in " + getSolvingTime().getSeconds() + " seconds.";

View File

@ -18,14 +18,13 @@ import java.util.ArrayList;
* @author Mark Allen Weiss * @author Mark Allen Weiss
* @author DLB * @author DLB
*/ */
public class BinaryHeap<E extends Comparable<E>> { public class BinaryHeap<E extends Comparable<E>> implements PriorityQueue<E> {
// Number of elements in heap. // Number of elements in heap.
private int currentSize; private int currentSize;
// The heap array. Java genericity does not work with arrays so we have to use // The heap array.
// an ArrayList. private final ArrayList<E> array;
private ArrayList<E> array;
/** /**
* Construct a new empty binary heap. * Construct a new empty binary heap.
@ -126,62 +125,37 @@ public class BinaryHeap<E extends Comparable<E>> {
} }
} }
/** @Override
* @return true if the heap is empty, false otherwise.
*/
public boolean isEmpty() { public boolean isEmpty() {
return this.currentSize == 0; return this.currentSize == 0;
} }
/** @Override
* @return Current size (number of elements) of this heap.
*/
public int size() { public int size() {
return this.currentSize; return this.currentSize;
} }
/** @Override
* Insert the given element into the heap. public void insert(E x) {
*
* @param x Item to insert.
*/
public void add(E x) {
int index = this.currentSize++; int index = this.currentSize++;
this.arraySet(index, x); this.arraySet(index, x);
this.percolateUp(index); this.percolateUp(index);
} }
/** @Override
* Tell the binary heap that the given element has been modified and should be public void remove(E x) throws ElementNotFoundException {
* re-positioned inside the heap.
*
* @param x Item to update.
*/
public void update(E x) {
// TODO: // TODO:
} }
/** @Override
* Find the smallest item in the heap. public E findMin() throws EmptyPriorityQueueException {
*
* @return The smallest item in the heap.
*
* @throws RuntimeException if this heap is empty.
*/
public E findMin() throws RuntimeException {
if (isEmpty()) if (isEmpty())
throw new RuntimeException("Empty binary heap."); throw new RuntimeException("Empty binary heap.");
return this.array.get(0); return this.array.get(0);
} }
/** @Override
* Remove the smallest item from the heap. public E deleteMin() throws EmptyPriorityQueueException {
*
* @return The smallest item in the heap.
*
* @throws RuntimeException if this heap is empty.
*/
public E deleteMin() throws RuntimeException {
E minItem = findMin(); E minItem = findMin();
E lastItem = this.array.get(--this.currentSize); E lastItem = this.array.get(--this.currentSize);
this.arraySet(0, lastItem); this.arraySet(0, lastItem);

View File

@ -0,0 +1,64 @@
package org.insa.algo.utils;
import java.util.SortedSet;
import java.util.TreeSet;
public class BinarySearchTree<E extends Comparable<E>> implements PriorityQueue<E> {
// Underlying implementation
private final SortedSet<E> sortedSet;
/**
* Create a new empty binary search tree.
*/
public BinarySearchTree() {
this.sortedSet = new TreeSet<>();
}
/**
* Create a copy of the given binary search tree.
*
* @param bst Binary search tree to copy.
*/
public BinarySearchTree(BinarySearchTree<E> bst) {
this.sortedSet = new TreeSet<>(bst.sortedSet);
}
@Override
public boolean isEmpty() {
return sortedSet.isEmpty();
}
@Override
public int size() {
return sortedSet.size();
}
@Override
public void insert(E x) {
sortedSet.add(x);
}
@Override
public void remove(E x) throws ElementNotFoundException {
if (!sortedSet.remove(x)) {
throw new ElementNotFoundException(x);
}
}
@Override
public E findMin() throws EmptyPriorityQueueException {
if (isEmpty()) {
throw new EmptyPriorityQueueException();
}
return sortedSet.first();
}
@Override
public E deleteMin() throws EmptyPriorityQueueException {
E min = findMin();
remove(min);
return min;
}
}

View File

@ -0,0 +1,32 @@
package org.insa.algo.utils;
public class ElementNotFoundException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = 1L;
// Element not found
private final Object element;
/**
* @param element Element that was not found.
*/
public ElementNotFoundException(Object element) {
this.element = element;
}
/**
* @return The element that was not found.
*/
public Object getElement() {
return this.element;
}
@Override
public String toString() {
return "element not found: " + element;
}
}

View File

@ -0,0 +1,16 @@
package org.insa.algo.utils;
public class EmptyPriorityQueueException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
*
*/
public EmptyPriorityQueueException() {
}
}

View File

@ -0,0 +1,54 @@
package org.insa.algo.utils;
/**
* Interface representing a basic priority queue.
*
* @see https://en.wikipedia.org/wiki/Priority_queue
*/
public interface PriorityQueue<E extends Comparable<E>> {
/**
* Check if the priority queue is empty.
*
* @return true if the queue is empty, false otherwise.
*/
public boolean isEmpty();
/**
* @return Current size (number of elements) of this queue.
*/
public int size();
/**
* Insert the given element into the queue.
*
* @param x Item to insert.
*/
public void insert(E x);
/**
* Remove the given element from the priority queue.
*
* @param x Item to remove.
*/
public void remove(E x) throws ElementNotFoundException;
/**
* Retrieve (but not remove) the smallest item in the queue.
*
* @return The smallest item in the queue.
*
* @throws EmptyPriorityQueueException if this queue is empty.
*/
public E findMin() throws EmptyPriorityQueueException;
/**
* Remove and return the smallest item from the priority queue.
*
* @return The smallest item in the queue.
*
* @throws EmptyPriorityQueueException if this queue is empty.
*/
public E deleteMin() throws EmptyPriorityQueueException;
}

View File

@ -9,7 +9,7 @@ public class WeaklyConnectedComponentsData extends AbstractInputData {
* @param graph Graph for which components should be retrieved. * @param graph Graph for which components should be retrieved.
*/ */
public WeaklyConnectedComponentsData(Graph graph) { public WeaklyConnectedComponentsData(Graph graph) {
super(graph); super(graph, null);
} }
@Override @Override

View File

@ -23,6 +23,8 @@ public class Launch {
* Create a new Drawing inside a JFrame an return it. * Create a new Drawing inside a JFrame an return it.
* *
* @return The created drawing. * @return The created drawing.
*
* @throws Exception if something wrong happens when creating the graph.
*/ */
public static Drawing createDrawing() throws Exception { public static Drawing createDrawing() throws Exception {
BasicDrawing basicDrawing = new BasicDrawing(); BasicDrawing basicDrawing = new BasicDrawing();

View File

@ -94,6 +94,8 @@ public class GraphStatistics {
} }
/** /**
* @param other Box to intersect.
*
* @return true if this box contains the given box. * @return true if this box contains the given box.
*/ */
public boolean contains(BoundingBox other) { public boolean contains(BoundingBox other) {

View File

@ -14,22 +14,18 @@ import java.util.List;
import javax.swing.Box; import javax.swing.Box;
import javax.swing.BoxLayout; import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JCheckBox; import javax.swing.JCheckBox;
import javax.swing.JComboBox; import javax.swing.JComboBox;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.border.EmptyBorder; import javax.swing.border.EmptyBorder;
import org.insa.algo.AbstractAlgorithm; import org.insa.algo.AbstractAlgorithm;
import org.insa.algo.AbstractInputData;
import org.insa.algo.AbstractInputData.ArcFilter;
import org.insa.algo.AbstractInputData.Mode;
import org.insa.algo.AlgorithmFactory; import org.insa.algo.AlgorithmFactory;
import org.insa.algo.ArcFilterFactory; import org.insa.algo.ArcInspector;
import org.insa.algo.ArcInspectorFactory;
import org.insa.graph.Node; import org.insa.graph.Node;
import org.insa.graphics.NodesInputPanel.InputChangedEvent; import org.insa.graphics.NodesInputPanel.InputChangedEvent;
import org.insa.graphics.drawing.Drawing; import org.insa.graphics.drawing.Drawing;
@ -55,20 +51,18 @@ public class AlgorithmPanel extends JPanel implements DrawingChangeListener {
protected static final int START_EVENT_ID = 0x1; protected static final int START_EVENT_ID = 0x1;
private final List<Node> nodes; private final List<Node> nodes;
private final AbstractInputData.Mode mode;
private final Class<? extends AbstractAlgorithm<?>> algoClass; private final Class<? extends AbstractAlgorithm<?>> algoClass;
private final AbstractInputData.ArcFilter arcFilter; private final ArcInspector arcFilter;
private final boolean graphicVisualization; private final boolean graphicVisualization;
private final boolean textualVisualization; private final boolean textualVisualization;
public StartActionEvent(Class<? extends AbstractAlgorithm<?>> algoClass, List<Node> nodes, public StartActionEvent(Class<? extends AbstractAlgorithm<?>> algoClass, List<Node> nodes,
Mode mode, ArcFilter arcFilter, boolean graphicVisualization, ArcInspector arcFilter, boolean graphicVisualization,
boolean textualVisualization) { boolean textualVisualization) {
super(AlgorithmPanel.this, START_EVENT_ID, START_EVENT_COMMAND); super(AlgorithmPanel.this, START_EVENT_ID, START_EVENT_COMMAND);
this.nodes = nodes; this.nodes = nodes;
this.mode = mode;
this.algoClass = algoClass; this.algoClass = algoClass;
this.graphicVisualization = graphicVisualization; this.graphicVisualization = graphicVisualization;
this.textualVisualization = textualVisualization; this.textualVisualization = textualVisualization;
@ -82,17 +76,10 @@ public class AlgorithmPanel extends JPanel implements DrawingChangeListener {
return this.nodes; return this.nodes;
} }
/**
* @return Mode associated with this event.
*/
public Mode getMode() {
return this.mode;
}
/** /**
* @return Arc filter associated with this event. * @return Arc filter associated with this event.
*/ */
public ArcFilter getArcFilter() { public ArcInspector getArcFilter() {
return this.arcFilter; return this.arcFilter;
} }
@ -147,16 +134,13 @@ public class AlgorithmPanel extends JPanel implements DrawingChangeListener {
* @param baseAlgorithm Base algorithm for this algorithm panel. * @param baseAlgorithm Base algorithm for this algorithm panel.
* @param title Title of the panel. * @param title Title of the panel.
* @param nodeNames Names of the input nodes. * @param nodeNames Names of the input nodes.
* @param enableModeSelection <code>true</code> to enable {@link Mode} * @param enableArcFilterSelection <code>true</code> to enable
* selection. * {@link ArcInspector} selection.
* @param enableArcFilterSelection <code>true</code> to enable {@link ArcFilter}
* selection.
* *
* @see ArcFilterFactory * @see ArcInspectorFactory
*/ */
public AlgorithmPanel(Component parent, Class<? extends AbstractAlgorithm<?>> baseAlgorithm, public AlgorithmPanel(Component parent, Class<? extends AbstractAlgorithm<?>> baseAlgorithm,
String title, String[] nodeNames, boolean enableModeSelection, String title, String[] nodeNames, boolean enableArcFilterSelection) {
boolean enableArcFilterSelection) {
super(); super();
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS)); setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
@ -178,20 +162,14 @@ public class AlgorithmPanel extends JPanel implements DrawingChangeListener {
add(this.nodesInputPanel); add(this.nodesInputPanel);
components.add(this.nodesInputPanel); components.add(this.nodesInputPanel);
JComboBox<ArcFilter> arcFilterSelect = new JComboBox<>( JComboBox<ArcInspector> arcFilterSelect = new JComboBox<>(
ArcFilterFactory.getAllFilters().toArray(new ArcFilter[0])); ArcInspectorFactory.getAllFilters().toArray(new ArcInspector[0]));
arcFilterSelect.setBackground(Color.WHITE); arcFilterSelect.setBackground(Color.WHITE);
// Add mode selection // Add mode selection
JPanel modeAndObserverPanel = new JPanel(); JPanel modeAndObserverPanel = new JPanel();
modeAndObserverPanel.setAlignmentX(Component.LEFT_ALIGNMENT); modeAndObserverPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
modeAndObserverPanel.setLayout(new GridBagLayout()); modeAndObserverPanel.setLayout(new GridBagLayout());
JRadioButton lengthModeButton = new JRadioButton("Length");
lengthModeButton.setSelected(true);
JRadioButton timeModeButton = new JRadioButton("Time");
ButtonGroup group = new ButtonGroup();
group.add(lengthModeButton);
group.add(timeModeButton);
graphicObserverCheckbox = new JCheckBox("Graphic"); graphicObserverCheckbox = new JCheckBox("Graphic");
graphicObserverCheckbox.setSelected(true); graphicObserverCheckbox.setSelected(true);
@ -201,19 +179,6 @@ public class AlgorithmPanel extends JPanel implements DrawingChangeListener {
c.fill = GridBagConstraints.HORIZONTAL; c.fill = GridBagConstraints.HORIZONTAL;
if (enableModeSelection) {
c.gridx = 0;
c.gridy = 0;
c.weightx = 0;
modeAndObserverPanel.add(new JLabel("Mode: "), c);
c.gridx = 1;
c.weightx = 1;
modeAndObserverPanel.add(lengthModeButton, c);
c.gridx = 2;
c.weightx = 1;
modeAndObserverPanel.add(timeModeButton, c);
}
c.gridy = 2; c.gridy = 2;
c.gridx = 0; c.gridx = 0;
c.weightx = 0; c.weightx = 0;
@ -236,8 +201,6 @@ public class AlgorithmPanel extends JPanel implements DrawingChangeListener {
modeAndObserverPanel.add(arcFilterSelect, c); modeAndObserverPanel.add(arcFilterSelect, c);
} }
components.add(timeModeButton);
components.add(lengthModeButton);
components.add(arcFilterSelect); components.add(arcFilterSelect);
components.add(textualObserverCheckbox); components.add(textualObserverCheckbox);
@ -258,16 +221,12 @@ public class AlgorithmPanel extends JPanel implements DrawingChangeListener {
startAlgoButton.addActionListener(new ActionListener() { startAlgoButton.addActionListener(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
AbstractInputData.Mode mode = lengthModeButton.isSelected()
? AbstractInputData.Mode.LENGTH
: AbstractInputData.Mode.TIME;
for (ActionListener lis: startActionListeners) { for (ActionListener lis: startActionListeners) {
lis.actionPerformed(new StartActionEvent( lis.actionPerformed(new StartActionEvent(
AlgorithmFactory.getAlgorithmClass(baseAlgorithm, AlgorithmFactory.getAlgorithmClass(baseAlgorithm,
(String) algoSelect.getSelectedItem()), (String) algoSelect.getSelectedItem()),
nodesInputPanel.getNodeForInputs(), mode, nodesInputPanel.getNodeForInputs(),
(AbstractInputData.ArcFilter) arcFilterSelect.getSelectedItem(), (ArcInspector) arcFilterSelect.getSelectedItem(),
graphicObserverCheckbox.isSelected(), graphicObserverCheckbox.isSelected(),
textualObserverCheckbox.isSelected())); textualObserverCheckbox.isSelected()));
} }

View File

@ -157,7 +157,7 @@ public class MainWindow extends JFrame {
this.currentPalette = this.basicPalette; this.currentPalette = this.basicPalette;
wccPanel = new AlgorithmPanel(this, WeaklyConnectedComponentsAlgorithm.class, wccPanel = new AlgorithmPanel(this, WeaklyConnectedComponentsAlgorithm.class,
"Weakly-Connected Components", new String[] {}, false, false); "Weakly-Connected Components", new String[] {}, false);
wccPanel.addStartActionListener(new ActionListener() { wccPanel.addStartActionListener(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
@ -202,13 +202,13 @@ public class MainWindow extends JFrame {
}); });
spPanel = new AlgorithmPanel(this, ShortestPathAlgorithm.class, "Shortest-Path", spPanel = new AlgorithmPanel(this, ShortestPathAlgorithm.class, "Shortest-Path",
new String[] { "Origin", "Destination" }, true, true); new String[] { "Origin", "Destination" }, true);
spPanel.addStartActionListener(new ActionListener() { spPanel.addStartActionListener(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
StartActionEvent evt = (StartActionEvent) e; StartActionEvent evt = (StartActionEvent) e;
ShortestPathData data = new ShortestPathData(graph, evt.getNodes().get(0), ShortestPathData data = new ShortestPathData(graph, evt.getNodes().get(0),
evt.getNodes().get(1), evt.getMode(), evt.getArcFilter()); evt.getNodes().get(1), evt.getArcFilter());
ShortestPathAlgorithm spAlgorithm = null; ShortestPathAlgorithm spAlgorithm = null;
try { try {
@ -257,11 +257,10 @@ public class MainWindow extends JFrame {
cpPanel = new AlgorithmPanel( cpPanel = new AlgorithmPanel(
this, CarPoolingAlgorithm.class, "Car-Pooling", new String[] { "Origin Car", this, CarPoolingAlgorithm.class, "Car-Pooling", new String[] { "Origin Car",
"Origin Pedestrian", "Destination Car", "Destination Pedestrian" }, "Origin Pedestrian", "Destination Car", "Destination Pedestrian" },
true, true); true);
psPanel = new AlgorithmPanel(this, PackageSwitchAlgorithm.class, "Car-Pooling", psPanel = new AlgorithmPanel(this, PackageSwitchAlgorithm.class, "Car-Pooling",
new String[] { "Oribin A", "Origin B", "Destination A", "Destination B" }, true, new String[] { "Oribin A", "Origin B", "Destination A", "Destination B" }, true);
true);
// add algorithm panels // add algorithm panels
algoPanels.add(wccPanel); algoPanels.add(wccPanel);

View File

@ -15,7 +15,6 @@ import java.awt.geom.Point2D;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -65,6 +64,11 @@ public class BasicDrawing extends JPanel implements Drawing {
this.color = color; this.color = color;
} }
/**
* @return The Z level of this overlay (>= 1).
*/
public abstract int getZLevel();
@Override @Override
public void setColor(Color color) { public void setColor(Color color) {
this.color = color; this.color = color;
@ -88,11 +92,8 @@ public class BasicDrawing extends JPanel implements Drawing {
@Override @Override
public void delete() { public void delete() {
synchronized (overlays) {
BasicDrawing.this.overlays.remove(this); BasicDrawing.this.overlays.remove(this);
} }
BasicDrawing.this.repaint();
}
/** /**
* Draw the given overlay. * Draw the given overlay.
@ -134,6 +135,10 @@ public class BasicDrawing extends JPanel implements Drawing {
this.alphaMode = alphaMode; this.alphaMode = alphaMode;
} }
public int getZLevel() {
return 3;
}
@Override @Override
public Point getPoint() { public Point getPoint() {
return point; return point;
@ -181,6 +186,10 @@ public class BasicDrawing extends JPanel implements Drawing {
this.color = color; this.color = color;
} }
public int getZLevel() {
return 2;
}
@Override @Override
public void setColor(Color color) { public void setColor(Color color) {
super.setColor(color); super.setColor(color);
@ -243,6 +252,10 @@ public class BasicDrawing extends JPanel implements Drawing {
this.graphics.setBackground(new Color(0, 0, 0, 0)); this.graphics.setBackground(new Color(0, 0, 0, 0));
} }
public int getZLevel() {
return 1;
}
@Override @Override
public void setColor(Color color) { public void setColor(Color color) {
super.setColor(color); super.setColor(color);
@ -294,6 +307,68 @@ public class BasicDrawing extends JPanel implements Drawing {
} }
/**
* Class encapsulating a set of overlays.
*
*/
private class BasicOverlays {
// List of overlays.
private ArrayList<ArrayList<BasicOverlay>> overlays = new ArrayList<>();
public synchronized void draw(Graphics2D g) {
// Clear overlays.
for (ArrayList<BasicOverlay> arr: this.overlays) {
for (BasicOverlay overlay: arr) {
overlay.draw(g);
}
}
}
public synchronized void remove(BasicOverlay overlay) {
overlays.get(overlay.getZLevel() - 1).remove(overlay);
BasicDrawing.this.repaint();
}
public void clear() {
clear(true);
}
public void clear(boolean repaint) {
// Clear overlays.
for (ArrayList<BasicOverlay> arr: this.overlays) {
arr.clear();
}
// Repaint if requested.
if (repaint) {
BasicDrawing.this.repaint();
}
}
public BasicOverlay add(BasicOverlay marker) {
return add(marker, true);
}
public synchronized BasicOverlay add(BasicOverlay overlay, boolean repaint) {
// Check if we have a level for this...
for (int i = overlays.size(); i < overlay.getZLevel(); ++i) {
overlays.add(new ArrayList<>());
}
// Add overlay to the given list.
overlays.get(overlay.getZLevel() - 1).add(overlay);
// Repaint if requested.
if (repaint) {
BasicDrawing.this.repaint();
}
return overlay;
}
};
// Default path color. // Default path color.
public static final Color DEFAULT_PATH_COLOR = new Color(66, 134, 244); public static final Color DEFAULT_PATH_COLOR = new Color(66, 134, 244);
@ -317,8 +392,7 @@ public class BasicDrawing extends JPanel implements Drawing {
private Graphics2D graphGraphics = null; private Graphics2D graphGraphics = null;
// List of image for markers // List of image for markers
private List<BasicOverlay> overlays = Collections private BasicOverlays overlays = new BasicOverlays();
.synchronizedList(new ArrayList<BasicOverlay>());
// Mapping DrawingClickListener -> MouseEventListener // Mapping DrawingClickListener -> MouseEventListener
private List<DrawingClickListener> drawingClickListeners = new ArrayList<>(); private List<DrawingClickListener> drawingClickListeners = new ArrayList<>();
@ -391,11 +465,7 @@ public class BasicDrawing extends JPanel implements Drawing {
} }
// Draw markers // Draw markers
synchronized (overlays) { this.overlays.draw(g);
for (BasicOverlay overlay: overlays) {
overlay.draw(g);
}
}
g.setTransform(sTransform); g.setTransform(sTransform);
if (this.zoomControls != null) { if (this.zoomControls != null) {
@ -416,9 +486,7 @@ public class BasicDrawing extends JPanel implements Drawing {
if (this.graphGraphics != null) { if (this.graphGraphics != null) {
this.graphGraphics.clearRect(0, 0, this.width, this.height); this.graphGraphics.clearRect(0, 0, this.width, this.height);
} }
synchronized (overlays) { this.overlays.clear(false);
this.overlays.clear();
}
this.repaint(); this.repaint();
} }
@ -429,11 +497,8 @@ public class BasicDrawing extends JPanel implements Drawing {
*/ */
@Override @Override
public void clearOverlays() { public void clearOverlays() {
synchronized (overlays) {
this.overlays.clear(); this.overlays.clear();
} }
this.repaint();
}
/** /**
* @return The current ZoomAndPanListener associated with this drawing. * @return The current ZoomAndPanListener associated with this drawing.
@ -495,21 +560,12 @@ public class BasicDrawing extends JPanel implements Drawing {
@Override @Override
public MarkerOverlay drawMarker(Point point, Color outer, Color inner, AlphaMode mode) { public MarkerOverlay drawMarker(Point point, Color outer, Color inner, AlphaMode mode) {
BasicMarkerOverlay marker = createMarker(point, outer, inner, mode); return (MarkerOverlay) this.overlays.add(createMarker(point, outer, inner, mode));
synchronized (overlays) {
this.overlays.add(marker);
}
this.repaint();
return marker;
} }
@Override @Override
public PointSetOverlay createPointSetOverlay() { public PointSetOverlay createPointSetOverlay() {
BasicPointSetOverlay ps = new BasicPointSetOverlay(); return (PointSetOverlay) this.overlays.add(new BasicPointSetOverlay(), false);
synchronized (overlays) {
this.overlays.add(ps);
}
return ps;
} }
@Override @Override
@ -671,12 +727,8 @@ public class BasicDrawing extends JPanel implements Drawing {
destination = createMarker(path.getDestination().getPoint(), color, color, destination = createMarker(path.getDestination().getPoint(), color, color,
AlphaMode.TRANSPARENT); AlphaMode.TRANSPARENT);
} }
BasicPathOverlay overlay = new BasicPathOverlay(points, color, origin, destination); return (PathOverlay) this.overlays
synchronized (overlays) { .add(new BasicPathOverlay(points, color, origin, destination));
this.overlays.add(overlay);
}
this.repaint();
return overlay;
} }
@Override @Override

View File

@ -18,7 +18,8 @@ import org.mapsforge.map.layer.overlay.Marker;
* correcting this. Internally, this image stores an {@link Image} instance and * correcting this. Internally, this image stores an {@link Image} instance and
* scale it when a redraw is requested. * scale it when a redraw is requested.
* *
* @see MarkerUtils#getMarkerForColor(java.awt.Color) * @see MarkerUtils#getMarkerForColor(java.awt.Color, java.awt.Color,
* org.insa.graphics.drawing.Drawing.AlphaMode)
* @see PaintUtils#getStrokeWidth(int, byte) * @see PaintUtils#getStrokeWidth(int, byte)
*/ */
public class MarkerAutoScaling extends Marker { public class MarkerAutoScaling extends Marker {

View File

@ -18,8 +18,6 @@ public class MarkerUtils {
* @param mode Mode to use to fill the inner part of the marker. * @param mode Mode to use to fill the inner part of the marker.
* *
* @return An image representing a marker. * @return An image representing a marker.
*
* @see MarkerUtils#getMarkerForColor(Color, AlphaMode)
*/ */
public static Image getMarkerForColor(Color outer, Color inner, AlphaMode mode) { public static Image getMarkerForColor(Color outer, Color inner, AlphaMode mode) {
// create image // create image

View File

@ -1,6 +1,7 @@
package org.insa.algo.utils; package org.insa.algo.utils;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.util.Arrays; import java.util.Arrays;
@ -59,46 +60,78 @@ public class BinaryHeapTest {
this.heap2 = new BinaryHeap<>(); this.heap2 = new BinaryHeap<>();
for (MutableInteger v: data1) { for (MutableInteger v: data1) {
this.heap1.add(v); this.heap1.insert(v);
} }
for (MutableInteger v: data2) { for (MutableInteger v: data2) {
this.heap2.add(v); this.heap2.insert(v);
} }
} }
@Test
public void testIsEmpty() {
BinaryHeap<MutableInteger> tree = new BinaryHeap<>();
assertTrue(tree.isEmpty());
assertFalse(this.heap1.isEmpty());
assertFalse(this.heap2.isEmpty());
}
@Test
public void testSize() {
BinaryHeap<MutableInteger> tree = new BinaryHeap<>();
assertEquals(0, tree.size());
assertEquals(20, this.heap1.size());
assertEquals(7, this.heap2.size());
}
@Test @Test
public void testInsert() { public void testInsert() {
BinaryHeap<MutableInteger> heap = new BinaryHeap<>(); BinaryHeap<MutableInteger> heap = new BinaryHeap<>();
int size = 0; int size = 0;
for (MutableInteger x: data1) { for (MutableInteger x: data1) {
heap.add(x); heap.insert(x);
size += 1; assertEquals(++size, heap.size());
assertEquals(heap.size(), size);
} }
assertEquals(data1.length, heap.size()); assertEquals(data1.length, heap.size());
heap = new BinaryHeap<>(); heap = new BinaryHeap<>();
size = 0; size = 0;
for (MutableInteger x: data2) { for (MutableInteger x: data2) {
heap.add(x); heap.insert(x);
size += 1; assertEquals(++size, heap.size());
assertEquals(heap.size(), size);
} }
assertEquals(data2.length, heap.size()); assertEquals(data2.length, heap.size());
} }
@Test(expected = EmptyPriorityQueueException.class)
public void testEmptyFindMin() {
BinaryHeap<MutableInteger> heap = new BinaryHeap<>();
heap.findMin();
}
@Test
public void testFindMin() {
assertEquals(0, heap1.findMin().get());
assertEquals(1, heap2.findMin().get());
}
@Test(expected = EmptyPriorityQueueException.class)
public void testEmptyDeleteMin() {
BinaryHeap<MutableInteger> heap = new BinaryHeap<>();
heap.deleteMin();
}
@Test @Test
public void testDeleteMin() { public void testDeleteMin() {
// range 1 (sorted) // range 1 (sorted)
int size = data1.length; int size = data1.length;
assertEquals(heap1.size(), size); assertEquals(heap1.size(), size);
for (MutableInteger x: data1) { for (MutableInteger x: data1) {
assertEquals(heap1.deleteMin(), x); assertEquals(x, heap1.deleteMin());
size -= 1; size -= 1;
assertEquals(heap1.size(), size); assertEquals(size, heap1.size());
} }
assertEquals(heap1.size(), 0); assertEquals(0, heap1.size());
assertTrue(heap1.isEmpty()); assertTrue(heap1.isEmpty());
// range 2 (was not sorted) // range 2 (was not sorted)
@ -107,20 +140,56 @@ public class BinaryHeapTest {
size = range2.length; size = range2.length;
assertEquals(heap2.size(), size); assertEquals(heap2.size(), size);
for (MutableInteger x: range2) { for (MutableInteger x: range2) {
assertEquals(heap2.deleteMin().get(), x.get()); assertEquals(x.get(), heap2.deleteMin().get());
size -= 1; size -= 1;
assertEquals(heap2.size(), size); assertEquals(size, heap2.size());
}
assertEquals(0, heap2.size());
assertTrue(heap2.isEmpty());
}
@Test(expected = ElementNotFoundException.class)
public void testRemoveEmpty() {
BinaryHeap<MutableInteger> heap = new BinaryHeap<>();
heap.remove(new MutableInteger(0));
}
@Test(expected = ElementNotFoundException.class)
public void testRemoveNotFound() {
heap1.remove(new MutableInteger(20));
}
@Test
public void testRemove() {
// heap 1
int size1 = heap1.size();
int[] deleteOrder1 = new int[] { 12, 17, 18, 19, 4, 5, 3, 2, 0, 9, 10, 16, 8, 14, 13, 15, 7,
6, 1, 11 };
for (int x: deleteOrder1) {
heap1.remove(this.data1[x]);
assertEquals(--size1, heap1.size());
}
assertTrue(heap1.isEmpty());
// heap 2
int size2 = heap2.size();
int[] deleteOrder2 = new int[] { 6, 5, 0, 1, 4, 2, 3 };
for (int x: deleteOrder2) {
heap2.remove(this.data2[x]);
assertEquals(--size2, heap2.size());
} }
assertEquals(heap2.size(), 0);
assertTrue(heap2.isEmpty()); assertTrue(heap2.isEmpty());
} }
@Test @Test
public void testUpdate() { public void testRemoveThenAdd() {
MutableInteger newMin = data2[data2.length - 1]; MutableInteger mi5 = this.data1[6];
newMin.set(0); heap1.remove(mi5);
heap2.update(newMin); assertEquals(19, heap1.size());
assertEquals(heap2.findMin(), newMin); mi5.set(-20);
heap1.insert(mi5);
assertEquals(20, heap1.size());
assertEquals(-20, heap1.findMin().get());
} }
} }

View File

@ -0,0 +1,195 @@
package org.insa.algo.utils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.Arrays;
import java.util.stream.IntStream;
import org.junit.Before;
import org.junit.Test;
public class BinarySearchTreeTest {
class MutableInteger implements Comparable<MutableInteger> {
// 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 searchTree.
private BinarySearchTree<MutableInteger> searchTree1, searchTree2;
@Before
public void init() {
// Create the range searchTree
this.searchTree1 = new BinarySearchTree<>();
this.searchTree2 = new BinarySearchTree<>();
for (MutableInteger v: data1) {
this.searchTree1.insert(v);
}
for (MutableInteger v: data2) {
this.searchTree2.insert(v);
}
}
@Test
public void testIsEmpty() {
BinarySearchTree<MutableInteger> tree = new BinarySearchTree<>();
assertTrue(tree.isEmpty());
assertFalse(this.searchTree1.isEmpty());
assertFalse(this.searchTree2.isEmpty());
}
@Test
public void testSize() {
BinarySearchTree<MutableInteger> tree = new BinarySearchTree<>();
assertEquals(0, tree.size());
assertEquals(20, this.searchTree1.size());
assertEquals(7, this.searchTree2.size());
}
@Test
public void testInsert() {
BinarySearchTree<MutableInteger> searchTree = new BinarySearchTree<>();
int size = 0;
for (MutableInteger x: data1) {
searchTree.insert(x);
assertEquals(++size, searchTree.size());
}
assertEquals(data1.length, searchTree.size());
searchTree = new BinarySearchTree<>();
size = 0;
for (MutableInteger x: data2) {
searchTree.insert(x);
assertEquals(++size, searchTree.size());
}
assertEquals(data2.length, searchTree.size());
}
@Test(expected = EmptyPriorityQueueException.class)
public void testEmptyFindMin() {
BinarySearchTree<MutableInteger> searchTree = new BinarySearchTree<>();
searchTree.findMin();
}
@Test
public void testFindMin() {
assertEquals(0, searchTree1.findMin().get());
assertEquals(1, searchTree2.findMin().get());
}
@Test(expected = EmptyPriorityQueueException.class)
public void testEmptyDeleteMin() {
BinarySearchTree<MutableInteger> searchTree = new BinarySearchTree<>();
searchTree.deleteMin();
}
@Test
public void testDeleteMin() {
// range 1 (sorted)
int size = data1.length;
assertEquals(searchTree1.size(), size);
for (MutableInteger x: data1) {
assertEquals(x, searchTree1.deleteMin());
size -= 1;
assertEquals(size, searchTree1.size());
}
assertEquals(0, searchTree1.size());
assertTrue(searchTree1.isEmpty());
// range 2 (was not sorted)
MutableInteger[] range2 = Arrays.copyOf(data2, data2.length);
Arrays.sort(range2);
size = range2.length;
assertEquals(searchTree2.size(), size);
for (MutableInteger x: range2) {
assertEquals(x.get(), searchTree2.deleteMin().get());
size -= 1;
assertEquals(size, searchTree2.size());
}
assertEquals(0, searchTree2.size());
assertTrue(searchTree2.isEmpty());
}
@Test(expected = ElementNotFoundException.class)
public void testRemoveEmpty() {
BinarySearchTree<MutableInteger> searchTree = new BinarySearchTree<>();
searchTree.remove(new MutableInteger(0));
}
@Test(expected = ElementNotFoundException.class)
public void testRemoveNotFound() {
searchTree1.remove(new MutableInteger(20));
}
@Test
public void testRemove() {
// searchTree 1
int size1 = searchTree1.size();
int[] deleteOrder1 = new int[] { 12, 17, 18, 19, 4, 5, 3, 2, 0, 9, 10, 16, 8, 14, 13, 15, 7,
6, 1, 11 };
for (int x: deleteOrder1) {
searchTree1.remove(this.data1[x]);
assertEquals(--size1, searchTree1.size());
}
assertTrue(searchTree1.isEmpty());
// searchTree 2
int size2 = searchTree2.size();
int[] deleteOrder2 = new int[] { 6, 5, 0, 1, 4, 2, 3 };
for (int x: deleteOrder2) {
searchTree2.remove(this.data2[x]);
assertEquals(--size2, searchTree2.size());
}
assertTrue(searchTree2.isEmpty());
}
@Test
public void testRemoveThenAdd() {
MutableInteger mi5 = this.data1[6];
searchTree1.remove(mi5);
assertEquals(19, searchTree1.size());
mi5.set(-20);
searchTree1.insert(mi5);
assertEquals(20, searchTree1.size());
assertEquals(-20, searchTree1.findMin().get());
}
}