Switch to Maven project.
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
package org.insa.graphs.algorithm;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Base class for algorithm classes.
|
||||
*
|
||||
* @param <Observer> Observer type for the algorithm.
|
||||
*/
|
||||
public abstract class AbstractAlgorithm<Observer> {
|
||||
|
||||
// Input data for the algorithm
|
||||
protected final AbstractInputData data;
|
||||
|
||||
// List of observers for the algorithm
|
||||
protected final ArrayList<Observer> observers;
|
||||
|
||||
/**
|
||||
* Create a new algorithm with an empty list of observers.
|
||||
*
|
||||
* @param data Input data for the algorithm.
|
||||
*/
|
||||
protected AbstractAlgorithm(AbstractInputData data) {
|
||||
this.data = data;
|
||||
this.observers = new ArrayList<Observer>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new algorithm with the given list of observers.
|
||||
*
|
||||
* @param data Input data for the algorithm.
|
||||
* @param observers Initial list of observers for the algorithm.
|
||||
*/
|
||||
protected AbstractAlgorithm(AbstractInputData data, ArrayList<Observer> observers) {
|
||||
this.data = data;
|
||||
this.observers = observers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an observer to this algorithm.
|
||||
*
|
||||
* @param observer Observer to add to this algorithm.
|
||||
*/
|
||||
public void addObserver(Observer observer) {
|
||||
observers.add(observer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The list of observers for this algorithm.
|
||||
*/
|
||||
public ArrayList<Observer> getObservers() {
|
||||
return observers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Input for this algorithm.
|
||||
*/
|
||||
public AbstractInputData getInputData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the algorithm and return the solution.
|
||||
*
|
||||
* This methods internally time the call to doRun() and update the result of the
|
||||
* call with the computed solving time.
|
||||
*
|
||||
* @return The solution found by the algorithm (may not be a feasible solution).
|
||||
*/
|
||||
public AbstractSolution run() {
|
||||
Instant start = Instant.now();
|
||||
AbstractSolution solution = this.doRun();
|
||||
solution.setSolvingTime(Duration.between(start, Instant.now()));
|
||||
return solution;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract method that should be implemented by child class.
|
||||
*
|
||||
* @return The solution found, must not be null (use an infeasible or unknown
|
||||
* status if necessary).
|
||||
*/
|
||||
protected abstract AbstractSolution doRun();
|
||||
|
||||
}
|
@@ -0,0 +1,97 @@
|
||||
package org.insa.graphs.algorithm;
|
||||
|
||||
import org.insa.graphs.model.Arc;
|
||||
import org.insa.graphs.model.Graph;
|
||||
import org.insa.graphs.model.GraphStatistics;
|
||||
|
||||
/**
|
||||
* Base class for algorithm input data classes. This class contains the basic
|
||||
* data that are required by most graph algorithms, i.e. a graph, a mode (time /
|
||||
* length) and a filter for the arc.
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractInputData {
|
||||
|
||||
/**
|
||||
* Enum specifying the top mode of the algorithms.
|
||||
*
|
||||
* @see ArcInspector
|
||||
*/
|
||||
public enum Mode {
|
||||
TIME, LENGTH
|
||||
}
|
||||
|
||||
// Graph
|
||||
private final Graph graph;
|
||||
|
||||
// Arc filter.
|
||||
protected final ArcInspector arcInspector;
|
||||
|
||||
/**
|
||||
* Create a new AbstractInputData instance for the given graph, mode and filter.
|
||||
*
|
||||
* @param graph Graph for this input data.
|
||||
* @param arcInspector Arc inspector for this input data.
|
||||
*/
|
||||
protected AbstractInputData(Graph graph, ArcInspector arcInspector) {
|
||||
this.graph = graph;
|
||||
this.arcInspector = arcInspector;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Graph associated with this input.
|
||||
*/
|
||||
public Graph getGraph() {
|
||||
return graph;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public Mode getMode() {
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given arc is allowed for the filter corresponding to this input.
|
||||
*
|
||||
* @param arc Arc to check.
|
||||
*
|
||||
* @return true if the given arc is allowed.
|
||||
*
|
||||
* @see ArcInspector
|
||||
*/
|
||||
public boolean isAllowed(Arc arc) {
|
||||
return this.arcInspector.isAllowed(arc);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,88 @@
|
||||
package org.insa.graphs.algorithm;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
/**
|
||||
* Base class for solution classes returned by the algorithm. This class
|
||||
* contains the basic information that any solution should have: status of the
|
||||
* solution (unknown, infeasible, etc.), solving time and the original input
|
||||
* data.
|
||||
*/
|
||||
public abstract class AbstractSolution {
|
||||
|
||||
/**
|
||||
* Possible status for a solution.
|
||||
*
|
||||
*/
|
||||
public enum Status {
|
||||
UNKNOWN, INFEASIBLE, FEASIBLE, OPTIMAL,
|
||||
};
|
||||
|
||||
// Status of the solution.
|
||||
private final Status status;
|
||||
|
||||
// Solving time for the solution.
|
||||
private Duration solvingTime;
|
||||
|
||||
// Original input of the solution.
|
||||
private final AbstractInputData data;
|
||||
|
||||
/**
|
||||
* Create a new abstract solution with unknown status.
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
protected AbstractSolution(AbstractInputData data) {
|
||||
this.data = data;
|
||||
this.solvingTime = Duration.ZERO;
|
||||
this.status = Status.UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param data
|
||||
* @param status
|
||||
*/
|
||||
protected AbstractSolution(AbstractInputData data, Status status) {
|
||||
this.data = data;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Original input for this solution.
|
||||
*/
|
||||
public AbstractInputData getInputData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Status of this solution.
|
||||
*/
|
||||
public Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Solving time of this solution.
|
||||
*/
|
||||
public Duration getSolvingTime() {
|
||||
return solvingTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the solving time of this solution.
|
||||
*
|
||||
* @param solvingTime Solving time for the solution.
|
||||
*/
|
||||
protected void setSolvingTime(Duration solvingTime) {
|
||||
this.solvingTime = solvingTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the solution is feasible or optimal.
|
||||
*/
|
||||
public boolean isFeasible() {
|
||||
return status == Status.FEASIBLE || status == Status.OPTIMAL;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,128 @@
|
||||
package org.insa.graphs.algorithm;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.insa.graphs.algorithm.shortestpath.AStarAlgorithm;
|
||||
import org.insa.graphs.algorithm.shortestpath.BellmanFordAlgorithm;
|
||||
import org.insa.graphs.algorithm.shortestpath.DijkstraAlgorithm;
|
||||
import org.insa.graphs.algorithm.shortestpath.ShortestPathAlgorithm;
|
||||
import org.insa.graphs.algorithm.weakconnectivity.WeaklyConnectedComponentsAlgorithm;
|
||||
|
||||
/**
|
||||
* Factory class used to register and retrieve algorithms based on their common
|
||||
* ancestor and name.
|
||||
*
|
||||
*/
|
||||
public class AlgorithmFactory {
|
||||
|
||||
// Map between algorithm names and class.
|
||||
private final static Map<Class<? extends AbstractAlgorithm<?>>, Map<String, Class<? extends AbstractAlgorithm<?>>>> ALGORITHMS = new IdentityHashMap<>();
|
||||
|
||||
static {
|
||||
// Register weakly-connected components algorithm:
|
||||
registerAlgorithm(WeaklyConnectedComponentsAlgorithm.class, "WCC basic",
|
||||
WeaklyConnectedComponentsAlgorithm.class);
|
||||
|
||||
// Register shortest path algorithm:
|
||||
registerAlgorithm(ShortestPathAlgorithm.class, "Bellman-Ford", BellmanFordAlgorithm.class);
|
||||
registerAlgorithm(ShortestPathAlgorithm.class, "Dijkstra", DijkstraAlgorithm.class);
|
||||
registerAlgorithm(ShortestPathAlgorithm.class, "A*", AStarAlgorithm.class);
|
||||
|
||||
// Register your algorithms here:
|
||||
// registerAlgorithm(CarPoolingAlgorithm.class, "My Awesome Algorithm",
|
||||
// MyCarPoolingAlgorithm.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the given algorithm class with the given name as a child class of
|
||||
* the given base algorithm.
|
||||
*
|
||||
* @param baseAlgorithm Base algorithm class that corresponds to the newly
|
||||
* registered algorithm class (e.g., generic algorithm
|
||||
* class for the problem).
|
||||
* @param name Name for the registered algorithm class.
|
||||
* @param algoClass Algorithm class to register.
|
||||
*/
|
||||
public static void registerAlgorithm(Class<? extends AbstractAlgorithm<?>> baseAlgorithm,
|
||||
String name, Class<? extends AbstractAlgorithm<?>> algoClass) {
|
||||
if (!ALGORITHMS.containsKey(baseAlgorithm)) {
|
||||
ALGORITHMS.put(baseAlgorithm, new LinkedHashMap<>());
|
||||
}
|
||||
ALGORITHMS.get(baseAlgorithm).put(name, algoClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the given algorithm class using the given input data.
|
||||
* Assuming algorithm correspond to a class "Algorithm", this function returns
|
||||
* an object equivalent to `new Algorithm(data)`.
|
||||
*
|
||||
* @param algorithm Class of the algorithm to create.
|
||||
* @param data Input data for the algorithm.
|
||||
*
|
||||
* @return A new instance of the given algorithm class using the given data.
|
||||
*
|
||||
* @throws Exception if something wrong happens when constructing the object,
|
||||
* i.e. the given input data does not correspond to the given
|
||||
* algorithm and/or no constructor that takes a single
|
||||
* parameter of type (data.getClass()) exists.
|
||||
*/
|
||||
public static AbstractAlgorithm<?> createAlgorithm(
|
||||
Class<? extends AbstractAlgorithm<?>> algorithm, AbstractInputData data)
|
||||
throws Exception {
|
||||
// Retrieve the set of constructors for the given algorithm class.
|
||||
Constructor<?>[] constructors = algorithm.getDeclaredConstructors();
|
||||
|
||||
// Within this set, find the constructor that can be called with "data" (only).
|
||||
AbstractAlgorithm<?> constructed = null;
|
||||
for (Constructor<?> c: constructors) {
|
||||
Class<?>[] params = c.getParameterTypes();
|
||||
if (params.length == 1 && params[0].isAssignableFrom(data.getClass())) {
|
||||
c.setAccessible(true);
|
||||
constructed = (AbstractAlgorithm<?>) c.newInstance(new Object[] { data });
|
||||
break;
|
||||
}
|
||||
}
|
||||
return constructed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the algorithm class corresponding to the given base algorithm class
|
||||
* and name. The algorithm must have been previously registered using
|
||||
* registerAlgorithm.
|
||||
*
|
||||
* @param baseAlgorithm Base algorithm class for the algorithm to retrieve.
|
||||
* @param name Name of the algorithm to retrieve.
|
||||
*
|
||||
* @return Class corresponding to the given name.
|
||||
*
|
||||
* @see #registerAlgorithm
|
||||
*/
|
||||
public static Class<? extends AbstractAlgorithm<?>> getAlgorithmClass(
|
||||
Class<? extends AbstractAlgorithm<?>> baseAlgorithm, String name) {
|
||||
return ALGORITHMS.get(baseAlgorithm).get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of names corresponding to the registered algorithm classes
|
||||
* for the given base algorithm class.
|
||||
*
|
||||
* @param baseAlgorithm Base algorithm class for the algorithm class names to
|
||||
* retrieve.
|
||||
*
|
||||
* @return Names of the currently registered algorithms.
|
||||
*
|
||||
* @see #registerAlgorithm
|
||||
*/
|
||||
public static Set<String> getAlgorithmNames(
|
||||
Class<? extends AbstractAlgorithm<?>> baseAlgorithm) {
|
||||
if (!ALGORITHMS.containsKey(baseAlgorithm)) {
|
||||
return new TreeSet<>();
|
||||
}
|
||||
return ALGORITHMS.get(baseAlgorithm).keySet();
|
||||
}
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
package org.insa.graphs.algorithm;
|
||||
|
||||
import org.insa.graphs.algorithm.AbstractInputData.Mode;
|
||||
import org.insa.graphs.model.Arc;
|
||||
import org.insa.graphs.model.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();
|
||||
|
||||
}
|
@@ -0,0 +1,177 @@
|
||||
package org.insa.graphs.algorithm;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
import org.insa.graphs.algorithm.AbstractInputData.Mode;
|
||||
import org.insa.graphs.model.Arc;
|
||||
import org.insa.graphs.model.GraphStatistics;
|
||||
import org.insa.graphs.model.AccessRestrictions.AccessMode;
|
||||
import org.insa.graphs.model.AccessRestrictions.AccessRestriction;
|
||||
|
||||
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 true;
|
||||
}
|
||||
|
||||
@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, all roads allowed";
|
||||
}
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
package org.insa.graphs.algorithm.carpooling;
|
||||
|
||||
import org.insa.graphs.algorithm.AbstractAlgorithm;
|
||||
|
||||
public abstract class CarPoolingAlgorithm extends AbstractAlgorithm<CarPoolingObserver> {
|
||||
|
||||
protected CarPoolingAlgorithm(CarPoolingData data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CarPoolingSolution run() {
|
||||
return (CarPoolingSolution) super.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected abstract CarPoolingSolution doRun();
|
||||
|
||||
@Override
|
||||
public CarPoolingData getInputData() {
|
||||
return (CarPoolingData) super.getInputData();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
package org.insa.graphs.algorithm.carpooling;
|
||||
|
||||
import org.insa.graphs.algorithm.AbstractInputData;
|
||||
import org.insa.graphs.algorithm.ArcInspector;
|
||||
import org.insa.graphs.model.Graph;
|
||||
|
||||
public class CarPoolingData extends AbstractInputData {
|
||||
|
||||
protected CarPoolingData(Graph graph, ArcInspector arcFilter) {
|
||||
super(graph, arcFilter);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
package org.insa.graphs.algorithm.carpooling;
|
||||
|
||||
public class CarPoolingGraphicObserver implements CarPoolingObserver {
|
||||
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
package org.insa.graphs.algorithm.carpooling;
|
||||
|
||||
public interface CarPoolingObserver {
|
||||
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
package org.insa.graphs.algorithm.carpooling;
|
||||
|
||||
import org.insa.graphs.algorithm.AbstractSolution;
|
||||
|
||||
public class CarPoolingSolution extends AbstractSolution {
|
||||
|
||||
protected CarPoolingSolution(CarPoolingData data, Status status) {
|
||||
super(data, status);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
package org.insa.graphs.algorithm.carpooling;
|
||||
|
||||
public class CarPoolingTextObserver implements CarPoolingObserver {
|
||||
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package org.insa.graphs.algorithm.packageswitch;
|
||||
|
||||
import org.insa.graphs.algorithm.AbstractAlgorithm;
|
||||
|
||||
public abstract class PackageSwitchAlgorithm extends AbstractAlgorithm<PackageSwitchObserver> {
|
||||
|
||||
/**
|
||||
* Create a new PackageSwitchAlgorithm with the given data.
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
protected PackageSwitchAlgorithm(PackageSwitchData data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageSwitchSolution run() {
|
||||
return (PackageSwitchSolution) super.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected abstract PackageSwitchSolution doRun();
|
||||
|
||||
@Override
|
||||
public PackageSwitchData getInputData() {
|
||||
return (PackageSwitchData) super.getInputData();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
package org.insa.graphs.algorithm.packageswitch;
|
||||
|
||||
import org.insa.graphs.algorithm.AbstractInputData;
|
||||
import org.insa.graphs.algorithm.ArcInspector;
|
||||
import org.insa.graphs.model.Graph;
|
||||
|
||||
public class PackageSwitchData extends AbstractInputData {
|
||||
|
||||
protected PackageSwitchData(Graph graph, ArcInspector arcFilter) {
|
||||
super(graph, arcFilter);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
package org.insa.graphs.algorithm.packageswitch;
|
||||
|
||||
public class PackageSwitchGraphicObserver implements PackageSwitchObserver {
|
||||
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
package org.insa.graphs.algorithm.packageswitch;
|
||||
|
||||
public interface PackageSwitchObserver {
|
||||
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
package org.insa.graphs.algorithm.packageswitch;
|
||||
|
||||
import org.insa.graphs.algorithm.AbstractSolution;
|
||||
|
||||
public class PackageSwitchSolution extends AbstractSolution {
|
||||
|
||||
protected PackageSwitchSolution(PackageSwitchData data, Status status) {
|
||||
super(data, status);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
package org.insa.graphs.algorithm.packageswitch;
|
||||
|
||||
public class PackageSwitchTextObserver implements PackageSwitchObserver {
|
||||
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
package org.insa.graphs.algorithm.shortestpath;
|
||||
|
||||
public class AStarAlgorithm extends DijkstraAlgorithm {
|
||||
|
||||
public AStarAlgorithm(ShortestPathData data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,100 @@
|
||||
package org.insa.graphs.algorithm.shortestpath;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.insa.graphs.algorithm.AbstractSolution.Status;
|
||||
import org.insa.graphs.model.Arc;
|
||||
import org.insa.graphs.model.Graph;
|
||||
import org.insa.graphs.model.Node;
|
||||
import org.insa.graphs.model.Path;
|
||||
|
||||
public class BellmanFordAlgorithm extends ShortestPathAlgorithm {
|
||||
|
||||
public BellmanFordAlgorithm(ShortestPathData data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ShortestPathSolution doRun() {
|
||||
|
||||
// Retrieve the graph.
|
||||
ShortestPathData data = getInputData();
|
||||
Graph graph = data.getGraph();
|
||||
|
||||
final int nbNodes = graph.size();
|
||||
|
||||
// Initialize array of distances.
|
||||
double[] distances = new double[nbNodes];
|
||||
Arrays.fill(distances, Double.POSITIVE_INFINITY);
|
||||
distances[data.getOrigin().getId()] = 0;
|
||||
|
||||
// Notify observers about the first event (origin processed).
|
||||
notifyOriginProcessed(data.getOrigin());
|
||||
|
||||
// Initialize array of predecessors.
|
||||
Arc[] predecessorArcs = new Arc[nbNodes];
|
||||
|
||||
// Actual algorithm, we will assume the graph does not contain negative
|
||||
// cycle...
|
||||
boolean found = false;
|
||||
for (int i = 0; !found && i < nbNodes; ++i) {
|
||||
found = true;
|
||||
for (Node node: graph.getNodes()) {
|
||||
for (Arc arc: node.getSuccessors()) {
|
||||
|
||||
// Small test to check allowed roads...
|
||||
if (!data.isAllowed(arc)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Retrieve weight of the arc.
|
||||
double w = data.getCost(arc);
|
||||
double oldDistance = distances[arc.getDestination().getId()];
|
||||
double newDistance = distances[node.getId()] + w;
|
||||
|
||||
if (Double.isInfinite(oldDistance) && Double.isFinite(newDistance)) {
|
||||
notifyNodeReached(arc.getDestination());
|
||||
}
|
||||
|
||||
// Check if new distances would be better, if so update...
|
||||
if (newDistance < oldDistance) {
|
||||
found = false;
|
||||
distances[arc.getDestination().getId()] = distances[node.getId()] + w;
|
||||
predecessorArcs[arc.getDestination().getId()] = arc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ShortestPathSolution solution = null;
|
||||
|
||||
// Destination has no predecessor, the solution is infeasible...
|
||||
if (predecessorArcs[data.getDestination().getId()] == null) {
|
||||
solution = new ShortestPathSolution(data, Status.INFEASIBLE);
|
||||
}
|
||||
else {
|
||||
|
||||
// The destination has been found, notify the observers.
|
||||
notifyDestinationReached(data.getDestination());
|
||||
|
||||
// Create the path from the array of predecessors...
|
||||
ArrayList<Arc> arcs = new ArrayList<>();
|
||||
Arc arc = predecessorArcs[data.getDestination().getId()];
|
||||
while (arc != null) {
|
||||
arcs.add(arc);
|
||||
arc = predecessorArcs[arc.getOrigin().getId()];
|
||||
}
|
||||
|
||||
// Reverse the path...
|
||||
Collections.reverse(arcs);
|
||||
|
||||
// Create the final solution.
|
||||
solution = new ShortestPathSolution(data, Status.OPTIMAL, new Path(graph, arcs));
|
||||
}
|
||||
|
||||
return solution;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
package org.insa.graphs.algorithm.shortestpath;
|
||||
|
||||
public class DijkstraAlgorithm extends ShortestPathAlgorithm {
|
||||
|
||||
public DijkstraAlgorithm(ShortestPathData data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ShortestPathSolution doRun() {
|
||||
final ShortestPathData data = getInputData();
|
||||
ShortestPathSolution solution = null;
|
||||
// TODO:
|
||||
return solution;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
package org.insa.graphs.algorithm.shortestpath;
|
||||
|
||||
import org.insa.graphs.algorithm.AbstractAlgorithm;
|
||||
import org.insa.graphs.model.Node;
|
||||
|
||||
public abstract class ShortestPathAlgorithm extends AbstractAlgorithm<ShortestPathObserver> {
|
||||
|
||||
protected ShortestPathAlgorithm(ShortestPathData data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShortestPathSolution run() {
|
||||
return (ShortestPathSolution) super.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected abstract ShortestPathSolution doRun();
|
||||
|
||||
@Override
|
||||
public ShortestPathData getInputData() {
|
||||
return (ShortestPathData) super.getInputData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify all observers that the origin has been processed.
|
||||
*
|
||||
* @param node Origin.
|
||||
*/
|
||||
public void notifyOriginProcessed(Node node) {
|
||||
for (ShortestPathObserver obs: getObservers()) {
|
||||
obs.notifyOriginProcessed(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify all observers that a node has been reached for the first time.
|
||||
*
|
||||
* @param node Node that has been reached.
|
||||
*/
|
||||
public void notifyNodeReached(Node node) {
|
||||
for (ShortestPathObserver obs: getObservers()) {
|
||||
obs.notifyNodeReached(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify all observers that a node has been marked, i.e. its final value has
|
||||
* been set.
|
||||
*
|
||||
* @param node Node that has been marked.
|
||||
*/
|
||||
public void notifyNodeMarked(Node node) {
|
||||
for (ShortestPathObserver obs: getObservers()) {
|
||||
obs.notifyNodeMarked(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify all observers that the destination has been reached.
|
||||
*
|
||||
* @param node Destination.
|
||||
*/
|
||||
public void notifyDestinationReached(Node node) {
|
||||
for (ShortestPathObserver obs: getObservers()) {
|
||||
obs.notifyDestinationReached(node);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
package org.insa.graphs.algorithm.shortestpath;
|
||||
|
||||
import org.insa.graphs.algorithm.AbstractInputData;
|
||||
import org.insa.graphs.algorithm.ArcInspector;
|
||||
import org.insa.graphs.model.Graph;
|
||||
import org.insa.graphs.model.Node;
|
||||
|
||||
public class ShortestPathData extends AbstractInputData {
|
||||
|
||||
// Origin and destination nodes.
|
||||
private final Node origin, destination;
|
||||
|
||||
/**
|
||||
* Construct a new instance of ShortestPathInputData with the given parameters.
|
||||
*
|
||||
* @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 arcInspector Filter for arcs (used to allow only a specific set of
|
||||
* arcs in the graph to be used).
|
||||
*/
|
||||
public ShortestPathData(Graph graph, Node origin, Node destination, ArcInspector arcInspector) {
|
||||
super(graph, arcInspector);
|
||||
this.origin = origin;
|
||||
this.destination = destination;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Origin node for the path.
|
||||
*/
|
||||
public Node getOrigin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Destination node for the path.
|
||||
*/
|
||||
public Node getDestination() {
|
||||
return destination;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Shortest-path from #" + origin.getId() + " to #" + destination.getId() + " ["
|
||||
+ this.arcInspector.toString().toLowerCase() + "]";
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
package org.insa.graphs.algorithm.shortestpath;
|
||||
|
||||
import org.insa.graphs.model.Node;
|
||||
|
||||
public interface ShortestPathObserver {
|
||||
|
||||
/**
|
||||
* Notify the observer that the origin has been processed.
|
||||
*
|
||||
* @param node Origin.
|
||||
*/
|
||||
public void notifyOriginProcessed(Node node);
|
||||
|
||||
/**
|
||||
* Notify the observer that a node has been reached for the first
|
||||
* time.
|
||||
*
|
||||
* @param node Node that has been reached.
|
||||
*/
|
||||
public void notifyNodeReached(Node node);
|
||||
|
||||
/**
|
||||
* Notify the observer that a node has been marked, i.e. its final
|
||||
* value has been set.
|
||||
*
|
||||
* @param node Node that has been marked.
|
||||
*/
|
||||
public void notifyNodeMarked(Node node);
|
||||
|
||||
/**
|
||||
* Notify the observer that the destination has been reached.
|
||||
*
|
||||
* @param node Destination.
|
||||
*/
|
||||
public void notifyDestinationReached(Node node);
|
||||
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
package org.insa.graphs.algorithm.shortestpath;
|
||||
|
||||
import org.insa.graphs.algorithm.AbstractInputData.Mode;
|
||||
import org.insa.graphs.model.Arc;
|
||||
import org.insa.graphs.model.Path;
|
||||
import org.insa.graphs.algorithm.AbstractSolution;
|
||||
|
||||
public class ShortestPathSolution extends AbstractSolution {
|
||||
|
||||
// Optimal solution.
|
||||
private final Path path;
|
||||
|
||||
/**
|
||||
* Create a new infeasible shortest-path solution for the given input and
|
||||
* status.
|
||||
*
|
||||
* @param data Original input data for this solution.
|
||||
* @param status Status of the solution (UNKNOWN / INFEASIBLE).
|
||||
*/
|
||||
public ShortestPathSolution(ShortestPathData data, Status status) {
|
||||
super(data, status);
|
||||
this.path = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new shortest-path solution.
|
||||
*
|
||||
* @param data Original input data for this solution.
|
||||
* @param status Status of the solution (FEASIBLE / OPTIMAL).
|
||||
* @param path Path corresponding to the solution.
|
||||
*/
|
||||
public ShortestPathSolution(ShortestPathData data, Status status, Path path) {
|
||||
super(data, status);
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShortestPathData getInputData() {
|
||||
return (ShortestPathData) super.getInputData();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The path of this solution, if any.
|
||||
*/
|
||||
public Path getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String info = null;
|
||||
if (!isFeasible()) {
|
||||
info = String.format("No path found from node #%d to node #%d",
|
||||
getInputData().getOrigin().getId(), getInputData().getDestination().getId());
|
||||
}
|
||||
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",
|
||||
getInputData().getOrigin().getId(), getInputData().getDestination().getId());
|
||||
if (getInputData().getMode() == Mode.LENGTH) {
|
||||
info = String.format("%s, %.4f kilometers", info, cost / 1000.0);
|
||||
}
|
||||
else {
|
||||
info = String.format("%s, %.4f minutes", info, cost / 60.0);
|
||||
}
|
||||
}
|
||||
info += " in " + getSolvingTime().getSeconds() + " seconds.";
|
||||
return info;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
package org.insa.graphs.algorithm.shortestpath;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
import org.insa.graphs.model.Node;
|
||||
|
||||
public class ShortestPathTextObserver implements ShortestPathObserver {
|
||||
|
||||
private final PrintStream stream;
|
||||
|
||||
public ShortestPathTextObserver(PrintStream stream) {
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyOriginProcessed(Node node) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyNodeReached(Node node) {
|
||||
stream.println("Node " + node.getId() + " reached.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyNodeMarked(Node node) {
|
||||
stream.println("Node " + node.getId() + " marked.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyDestinationReached(Node node) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,205 @@
|
||||
package org.insa.graphs.algorithm.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Implements a binary heap containing elements of type E.
|
||||
*
|
||||
* Note that all comparisons are based on the compareTo method, hence E must
|
||||
* implement Comparable
|
||||
*
|
||||
* @author Mark Allen Weiss
|
||||
* @author DLB
|
||||
*/
|
||||
public class BinaryHeap<E extends Comparable<E>> implements PriorityQueue<E> {
|
||||
|
||||
// Number of elements in heap.
|
||||
private int currentSize;
|
||||
|
||||
// The heap array.
|
||||
protected final ArrayList<E> array;
|
||||
|
||||
/**
|
||||
* Construct a new empty binary heap.
|
||||
*/
|
||||
public BinaryHeap() {
|
||||
this.currentSize = 0;
|
||||
this.array = new ArrayList<E>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a copy of the given heap.
|
||||
*
|
||||
* @param heap Binary heap to copy.
|
||||
*/
|
||||
public BinaryHeap(BinaryHeap<E> heap) {
|
||||
this.currentSize = heap.currentSize;
|
||||
this.array = new ArrayList<E>(heap.array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an element at the given index.
|
||||
*
|
||||
* @param index Index at which the element should be set.
|
||||
* @param value Element to set.
|
||||
*/
|
||||
private void arraySet(int index, E value) {
|
||||
if (index == this.array.size()) {
|
||||
this.array.add(value);
|
||||
}
|
||||
else {
|
||||
this.array.set(index, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Index of the parent of the given index.
|
||||
*/
|
||||
protected int indexParent(int index) {
|
||||
return (index - 1) / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Index of the left child of the given index.
|
||||
*/
|
||||
protected int indexLeft(int index) {
|
||||
return index * 2 + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to percolate up in the heap.
|
||||
*
|
||||
* @param index Index at which the percolate begins.
|
||||
*/
|
||||
private void percolateUp(int index) {
|
||||
E x = this.array.get(index);
|
||||
|
||||
for (; index > 0
|
||||
&& x.compareTo(this.array.get(indexParent(index))) < 0; index = indexParent(
|
||||
index)) {
|
||||
E moving_val = this.array.get(indexParent(index));
|
||||
this.arraySet(index, moving_val);
|
||||
}
|
||||
|
||||
this.arraySet(index, x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to percolate down in the heap.
|
||||
*
|
||||
* @param index Index at which the percolate begins.
|
||||
*/
|
||||
private void percolateDown(int index) {
|
||||
int ileft = indexLeft(index);
|
||||
int iright = ileft + 1;
|
||||
|
||||
if (ileft < this.currentSize) {
|
||||
E current = this.array.get(index);
|
||||
E left = this.array.get(ileft);
|
||||
boolean hasRight = iright < this.currentSize;
|
||||
E right = (hasRight) ? this.array.get(iright) : null;
|
||||
|
||||
if (!hasRight || left.compareTo(right) < 0) {
|
||||
// Left is smaller
|
||||
if (left.compareTo(current) < 0) {
|
||||
this.arraySet(index, left);
|
||||
this.arraySet(ileft, current);
|
||||
this.percolateDown(ileft);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Right is smaller
|
||||
if (right.compareTo(current) < 0) {
|
||||
this.arraySet(index, right);
|
||||
this.arraySet(iright, current);
|
||||
this.percolateDown(iright);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return this.currentSize == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return this.currentSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(E x) {
|
||||
int index = this.currentSize++;
|
||||
this.arraySet(index, x);
|
||||
this.percolateUp(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(E x) throws ElementNotFoundException {
|
||||
// TODO:
|
||||
}
|
||||
|
||||
@Override
|
||||
public E findMin() throws EmptyPriorityQueueException {
|
||||
if (isEmpty())
|
||||
throw new EmptyPriorityQueueException();
|
||||
return this.array.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public E deleteMin() throws EmptyPriorityQueueException {
|
||||
E minItem = findMin();
|
||||
E lastItem = this.array.get(--this.currentSize);
|
||||
this.arraySet(0, lastItem);
|
||||
this.percolateDown(0);
|
||||
return minItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a multi-lines string representing a sorted view of this binary heap.
|
||||
*
|
||||
* @return a string containing a sorted view this binary heap.
|
||||
*/
|
||||
public String toStringSorted() {
|
||||
return BinaryHeapFormatter.toStringSorted(this, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a multi-lines string representing a sorted view of this binary heap.
|
||||
*
|
||||
* @param maxElement Maximum number of elements to display. or {@code -1} to
|
||||
* display all the elements.
|
||||
*
|
||||
* @return a string containing a sorted view this binary heap.
|
||||
*/
|
||||
public String toStringSorted(int maxElement) {
|
||||
return BinaryHeapFormatter.toStringSorted(this, maxElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a multi-lines string representing a tree view of this binary heap.
|
||||
*
|
||||
* @return a string containing a tree view of this binary heap.
|
||||
*/
|
||||
public String toStringTree() {
|
||||
return BinaryHeapFormatter.toStringTree(this, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a multi-lines string representing a tree view of this binary heap.
|
||||
*
|
||||
* @param maxDepth Maximum depth of the tree to display.
|
||||
*
|
||||
* @return a string containing a tree view of this binary heap.
|
||||
*/
|
||||
public String toStringTree(int maxDepth) {
|
||||
return BinaryHeapFormatter.toStringTree(this, maxDepth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return BinaryHeapFormatter.toStringTree(this, 8);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,198 @@
|
||||
package org.insa.graphs.algorithm.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class BinaryHeapFormatter {
|
||||
|
||||
/**
|
||||
* This class is used by {@link #toStringTree}, and simply contains three string
|
||||
* accumulating. This is an immutable class.
|
||||
*
|
||||
*/
|
||||
private static class Context {
|
||||
|
||||
// Output text:
|
||||
public final String acu;
|
||||
|
||||
// Margin to get back exactly under the current position:
|
||||
public final String margin;
|
||||
|
||||
// Last margin used for the last child of a node. The markers are different:
|
||||
public final String lastmargin;
|
||||
|
||||
/**
|
||||
* Creaet a new {@code Context}.
|
||||
*
|
||||
* @param acu The accumulated string.
|
||||
* @param margin The current margin.
|
||||
* @param lastMargin The last margin used.
|
||||
*/
|
||||
public Context(String acu, String margin, String lastMargin) {
|
||||
this.acu = acu;
|
||||
this.margin = margin;
|
||||
this.lastmargin = lastMargin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new context by appending newlines to this context.
|
||||
*
|
||||
* @param n Number of newlines to append.
|
||||
*
|
||||
* @return a new context with {@code n} newlines appended.
|
||||
*/
|
||||
public Context appendNewlines(int n) {
|
||||
if (n <= 0) {
|
||||
return this;
|
||||
}
|
||||
else {
|
||||
return (new Context(this.acu + "\n" + this.margin, this.margin, this.lastmargin)
|
||||
.appendNewlines(n - 1));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new context by appending the given string to this context.
|
||||
*
|
||||
* @param count Number of spaces to add to the margin, or {@code null} to use
|
||||
* the length of the string.
|
||||
* @param text String to append.
|
||||
*
|
||||
* @return a new context with {@code text} appended.
|
||||
*/
|
||||
public Context appendText(Integer count, String text) {
|
||||
int cnt = (count == null) ? text.length() : count;
|
||||
final String spaces = new String(new char[cnt]).replace('\0', ' ');
|
||||
return new Context(this.acu + text, this.margin + spaces, this.lastmargin + spaces);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new context by appending a branch to this context.
|
||||
*
|
||||
* @param n Number of spaces to add to the margin, or {@code null} to use
|
||||
* the length of the string.
|
||||
* @param label Name of the branch.
|
||||
*
|
||||
* @return a new context with the branch appended.
|
||||
*/
|
||||
public Context appendBranch(Integer count, String label) {
|
||||
final Context ctxt = this.appendText(count, label);
|
||||
|
||||
if (count == null) {
|
||||
return new Context(ctxt.acu + "_", ctxt.margin + "|", ctxt.margin + " ");
|
||||
}
|
||||
else {
|
||||
return new Context(ctxt.acu, ctxt.margin + "|", ctxt.margin + " ")
|
||||
.appendNewlines(1);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Input : ready to write the current node at the current context position.
|
||||
* Output : the last character of acu is the last character of the current node.
|
||||
*/
|
||||
protected static <E extends Comparable<E>> Context toStringLoop(BinaryHeap<E> heap,
|
||||
Context ctxt, int node, int max_depth) {
|
||||
|
||||
if (max_depth < 0) {
|
||||
return ctxt.appendText(null, "...");
|
||||
}
|
||||
else {
|
||||
E nodeval = heap.array.get(node);
|
||||
String nodevals = nodeval.toString();
|
||||
|
||||
ArrayList<Integer> childs = new ArrayList<Integer>();
|
||||
// Add childs
|
||||
int index_left = heap.indexLeft(node);
|
||||
int index_right = index_left + 1;
|
||||
|
||||
if (index_left < heap.size()) {
|
||||
childs.add(index_left);
|
||||
}
|
||||
if (index_right < heap.size()) {
|
||||
childs.add(index_right);
|
||||
}
|
||||
|
||||
Context ctxt2 = childs.isEmpty() ? ctxt.appendText(null, nodevals)
|
||||
: ctxt.appendBranch(1, nodevals);
|
||||
|
||||
for (int ch = 0; ch < childs.size(); ch++) {
|
||||
boolean is_last = (ch == childs.size() - 1);
|
||||
int child = childs.get(ch);
|
||||
|
||||
if (is_last) {
|
||||
Context ctxt3 = new Context(ctxt2.acu, ctxt2.lastmargin, ctxt2.lastmargin);
|
||||
ctxt2 = new Context(toStringLoop(heap, ctxt3.appendText(null, "___"), child,
|
||||
max_depth - 1).acu, ctxt2.margin, ctxt2.lastmargin);
|
||||
}
|
||||
else {
|
||||
ctxt2 = new Context(toStringLoop(heap, ctxt2.appendText(null, "___"), child,
|
||||
max_depth - 1).acu, ctxt2.margin, ctxt2.lastmargin).appendNewlines(2);
|
||||
}
|
||||
}
|
||||
|
||||
return ctxt2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a multi-lines string representing a tree view of the given binary
|
||||
* heap.
|
||||
*
|
||||
* @param heap The binary heap to display.
|
||||
* @param maxDepth Maximum depth of the tree to display.
|
||||
*
|
||||
* @return a string containing a tree view of the given binary heap.
|
||||
*/
|
||||
public static <E extends Comparable<E>> String toStringTree(BinaryHeap<E> heap, int maxDepth) {
|
||||
final Context init_context = new Context(" ", " ", " ");
|
||||
final Context result = toStringLoop(heap, init_context, 0, maxDepth);
|
||||
return result.acu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a multi-lines string representing a sorted view of the given binary
|
||||
* heap.
|
||||
*
|
||||
* @param heap The binary heap to display.
|
||||
* @param maxElement Maximum number of elements to display. or {@code -1} to
|
||||
* display all the elements.
|
||||
*
|
||||
* @return a string containing a sorted view the given binary heap.
|
||||
*/
|
||||
public static <E extends Comparable<E>> String toStringSorted(BinaryHeap<E> heap,
|
||||
int max_elements) {
|
||||
String result = "";
|
||||
final BinaryHeap<E> copy = new BinaryHeap<E>(heap);
|
||||
|
||||
final String truncate;
|
||||
if (max_elements < 0 || max_elements >= heap.size()) {
|
||||
truncate = "";
|
||||
}
|
||||
else {
|
||||
truncate = ", only " + max_elements + " elements are shown";
|
||||
}
|
||||
|
||||
result += "======== Sorted HEAP (size = " + heap.size() + truncate + ") ========\n\n";
|
||||
|
||||
while (!copy.isEmpty() && max_elements-- != 0) {
|
||||
result += copy.deleteMin() + "\n";
|
||||
}
|
||||
|
||||
result += "\n-------- End of heap --------";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
final BinaryHeap<Integer> heap = new BinaryHeap<Integer>();
|
||||
|
||||
for (int i = 0; i < 12; i++) {
|
||||
heap.insert(i);
|
||||
}
|
||||
|
||||
System.out.println(heap.toStringSorted(-1));
|
||||
System.out.println(heap.toStringTree(6));
|
||||
}
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
package org.insa.graphs.algorithm.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;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
package org.insa.graphs.algorithm.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;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
package org.insa.graphs.algorithm.utils;
|
||||
|
||||
public class EmptyPriorityQueueException extends RuntimeException {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public EmptyPriorityQueueException() {
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,81 @@
|
||||
package org.insa.graphs.algorithm.utils;
|
||||
|
||||
/**
|
||||
* Interface representing a basic priority queue.
|
||||
*
|
||||
* Implementation should enforce the required complexity of each method.
|
||||
*
|
||||
*/
|
||||
public interface PriorityQueue<E extends Comparable<E>> {
|
||||
|
||||
/**
|
||||
* Check if the priority queue is empty.
|
||||
*
|
||||
* <p>
|
||||
* <b>Complexity:</b> <i>O(1)</i>
|
||||
* </p>
|
||||
*
|
||||
* @return true if the queue is empty, false otherwise.
|
||||
*/
|
||||
public boolean isEmpty();
|
||||
|
||||
/**
|
||||
* Get the number of elements in this queue.
|
||||
*
|
||||
* <p>
|
||||
* <b>Complexity:</b> <i>O(1)</i>
|
||||
* </p>
|
||||
*
|
||||
* @return Current size (number of elements) of this queue.
|
||||
*/
|
||||
public int size();
|
||||
|
||||
/**
|
||||
* Insert the given element into the queue.
|
||||
*
|
||||
* <p>
|
||||
* <b>Complexity:</b> <i>O(log n)</i>
|
||||
* </p>
|
||||
*
|
||||
* @param x Item to insert.
|
||||
*/
|
||||
public void insert(E x);
|
||||
|
||||
/**
|
||||
* Remove the given element from the priority queue.
|
||||
*
|
||||
* <p>
|
||||
* <b>Complexity:</b> <i>O(log n)</i>
|
||||
* </p>
|
||||
*
|
||||
* @param x Item to remove.
|
||||
*/
|
||||
public void remove(E x) throws ElementNotFoundException;
|
||||
|
||||
/**
|
||||
* Retrieve (but not remove) the smallest item in the queue.
|
||||
*
|
||||
* <p>
|
||||
* <b>Complexity:</b> <i>O(1)</i>
|
||||
* </p>
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* <p>
|
||||
* <b>Complexity:</b> <i>O(log n)</i>
|
||||
* </p>
|
||||
*
|
||||
* @return The smallest item in the queue.
|
||||
*
|
||||
* @throws EmptyPriorityQueueException if this queue is empty.
|
||||
*/
|
||||
public E deleteMin() throws EmptyPriorityQueueException;
|
||||
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
package org.insa.graphs.algorithm.weakconnectivity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.insa.graphs.model.Node;
|
||||
|
||||
public interface WeaklyConnectedComponentObserver {
|
||||
|
||||
/**
|
||||
* Notify that the algorithm is entering a new component.
|
||||
*
|
||||
* @param curNode Starting node for the component.
|
||||
*/
|
||||
public void notifyStartComponent(Node curNode);
|
||||
|
||||
/**
|
||||
* Notify that a new node has been found for the current component.
|
||||
*
|
||||
* @param node New node found for the current component.
|
||||
*/
|
||||
public void notifyNewNodeInComponent(Node node);
|
||||
|
||||
/**
|
||||
* Notify that the algorithm has computed a new component.
|
||||
*
|
||||
* @param nodes List of nodes in the component.
|
||||
*/
|
||||
public void notifyEndComponent(ArrayList<Node> nodes);
|
||||
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
package org.insa.graphs.algorithm.weakconnectivity;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.insa.graphs.model.Node;
|
||||
|
||||
public class WeaklyConnectedComponentTextObserver implements WeaklyConnectedComponentObserver {
|
||||
|
||||
// Number of the current component.
|
||||
private int numComponent = 1;
|
||||
|
||||
// Output stream
|
||||
PrintStream stream;
|
||||
|
||||
public WeaklyConnectedComponentTextObserver(PrintStream stream) {
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyStartComponent(Node curNode) {
|
||||
stream.print("Entering component #" + numComponent + " from node #" + curNode.getId() + "... ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyNewNodeInComponent(Node node) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyEndComponent(ArrayList<Node> nodes) {
|
||||
stream.println(nodes.size() + " nodes found.");
|
||||
stream.flush();
|
||||
numComponent += 1;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,159 @@
|
||||
package org.insa.graphs.algorithm.weakconnectivity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
|
||||
import org.insa.graphs.algorithm.AbstractAlgorithm;
|
||||
import org.insa.graphs.algorithm.AbstractSolution.Status;
|
||||
import org.insa.graphs.model.Arc;
|
||||
import org.insa.graphs.model.Graph;
|
||||
import org.insa.graphs.model.Node;
|
||||
|
||||
public class WeaklyConnectedComponentsAlgorithm
|
||||
extends AbstractAlgorithm<WeaklyConnectedComponentObserver> {
|
||||
|
||||
/**
|
||||
* @param data Input data for this algorithm.
|
||||
*/
|
||||
public WeaklyConnectedComponentsAlgorithm(WeaklyConnectedComponentsData data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WeaklyConnectedComponentsSolution run() {
|
||||
return (WeaklyConnectedComponentsSolution) super.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WeaklyConnectedComponentsData getInputData() {
|
||||
return (WeaklyConnectedComponentsData) super.getInputData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify all observers that the algorithm is entering a new component.
|
||||
*
|
||||
* @param curNode Starting node for the component.
|
||||
*/
|
||||
protected void notifyStartComponent(Node curNode) {
|
||||
for (WeaklyConnectedComponentObserver obs: getObservers()) {
|
||||
obs.notifyStartComponent(curNode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify all observers that a new node has been found for the current
|
||||
* component.
|
||||
*
|
||||
* @param node New node found for the current component.
|
||||
*/
|
||||
protected void notifyNewNodeInComponent(Node node) {
|
||||
for (WeaklyConnectedComponentObserver obs: getObservers()) {
|
||||
obs.notifyNewNodeInComponent(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify all observers that the algorithm has computed a new component.
|
||||
*
|
||||
* @param nodes List of nodes in the component.
|
||||
*/
|
||||
protected void notifyEndComponent(ArrayList<Node> nodes) {
|
||||
for (WeaklyConnectedComponentObserver obs: getObservers()) {
|
||||
obs.notifyEndComponent(nodes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return An adjacency list for the undirected graph equivalent to the stored
|
||||
* graph.
|
||||
*/
|
||||
protected ArrayList<HashSet<Integer>> createUndirectedGraph() {
|
||||
int nNodes = getInputData().getGraph().size();
|
||||
ArrayList<HashSet<Integer>> res = new ArrayList<HashSet<Integer>>(nNodes);
|
||||
for (int i = 0; i < nNodes; ++i) {
|
||||
res.add(new HashSet<Integer>());
|
||||
}
|
||||
|
||||
for (Node node: getInputData().getGraph().getNodes()) {
|
||||
for (Arc arc: node.getSuccessors()) {
|
||||
res.get(node.getId()).add(arc.getDestination().getId());
|
||||
if (arc.getRoadInformation().isOneWay()) {
|
||||
res.get(arc.getDestination().getId()).add(node.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a breadth first search algorithm on the given undirected graph
|
||||
* (adjacency list), starting at node cur, and marking nodes in marked.
|
||||
*
|
||||
* @param marked
|
||||
* @param cur
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected ArrayList<Node> bfs(ArrayList<HashSet<Integer>> ugraph, boolean[] marked, int cur) {
|
||||
Graph graph = getInputData().getGraph();
|
||||
ArrayList<Node> component = new ArrayList<Node>();
|
||||
|
||||
// Using a queue because we are doing a BFS
|
||||
Queue<Integer> queue = new LinkedList<Integer>();
|
||||
|
||||
// Notify observers about the current component.
|
||||
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 = graph.get(queue.remove());
|
||||
component.add(node);
|
||||
|
||||
// Notify observers
|
||||
notifyNewNodeInComponent(node);
|
||||
|
||||
for (Integer destId: ugraph.get(node.getId())) {
|
||||
Node dest = graph.get(destId);
|
||||
if (!marked[dest.getId()]) {
|
||||
queue.add(destId);
|
||||
marked[destId] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
notifyEndComponent(component);
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WeaklyConnectedComponentsSolution doRun() {
|
||||
|
||||
Graph graph = getInputData().getGraph();
|
||||
ArrayList<HashSet<Integer>> ugraph = createUndirectedGraph();
|
||||
boolean[] marked = new boolean[graph.size()];
|
||||
Arrays.fill(marked, false);
|
||||
|
||||
ArrayList<ArrayList<Node>> components = new ArrayList<ArrayList<Node>>();
|
||||
|
||||
// perform algorithm
|
||||
int cur = 0;
|
||||
while (cur < marked.length) {
|
||||
// Apply BFS
|
||||
components.add(this.bfs(ugraph, marked, cur));
|
||||
|
||||
// Find next non-marked
|
||||
for (; cur < marked.length && marked[cur]; ++cur)
|
||||
;
|
||||
}
|
||||
|
||||
return new WeaklyConnectedComponentsSolution(getInputData(), Status.OPTIMAL, components);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
package org.insa.graphs.algorithm.weakconnectivity;
|
||||
|
||||
import org.insa.graphs.algorithm.AbstractInputData;
|
||||
import org.insa.graphs.model.Graph;
|
||||
|
||||
public class WeaklyConnectedComponentsData extends AbstractInputData {
|
||||
|
||||
/**
|
||||
* @param graph Graph for which components should be retrieved.
|
||||
*/
|
||||
public WeaklyConnectedComponentsData(Graph graph) {
|
||||
super(graph, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Weakly-connected components from #0.";
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
package org.insa.graphs.algorithm.weakconnectivity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.insa.graphs.algorithm.AbstractSolution;
|
||||
import org.insa.graphs.model.Node;
|
||||
|
||||
public class WeaklyConnectedComponentsSolution extends AbstractSolution {
|
||||
|
||||
// Components
|
||||
private ArrayList<ArrayList<Node>> components;
|
||||
|
||||
protected WeaklyConnectedComponentsSolution(WeaklyConnectedComponentsData data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
protected WeaklyConnectedComponentsSolution(WeaklyConnectedComponentsData data, Status status,
|
||||
ArrayList<ArrayList<Node>> components) {
|
||||
super(data, status);
|
||||
this.components = components;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WeaklyConnectedComponentsData getInputData() {
|
||||
return (WeaklyConnectedComponentsData) super.getInputData();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Components of the solution, if any.
|
||||
*/
|
||||
public ArrayList<ArrayList<Node>> getComponents() {
|
||||
return components;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
int nIsolated = 0;
|
||||
int nGt10 = 0;
|
||||
for (ArrayList<Node> component: components) {
|
||||
if (component.size() == 1) {
|
||||
nIsolated += 1;
|
||||
}
|
||||
else if (component.size() > 10) {
|
||||
nGt10 += 1;
|
||||
}
|
||||
}
|
||||
return "Found " + components.size() + " components (" + nGt10 + " with more than 10 nodes, "
|
||||
+ nIsolated + " isolated nodes) in " + getSolvingTime().getSeconds() + " seconds.";
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
package org.insa.graphs.algorithm.utils;
|
||||
|
||||
public class BinaryHeapTest extends PriorityQueueTest {
|
||||
|
||||
@Override
|
||||
public PriorityQueue<MutableInteger> createQueue() {
|
||||
return new BinaryHeap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PriorityQueue<MutableInteger> createQueue(PriorityQueue<MutableInteger> queue) {
|
||||
return new BinaryHeap<>((BinaryHeap<MutableInteger>) queue);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
package org.insa.graphs.algorithm.utils;
|
||||
|
||||
public class BinarySearchTreeTest extends PriorityQueueTest {
|
||||
|
||||
@Override
|
||||
public PriorityQueue<MutableInteger> createQueue() {
|
||||
return new BinarySearchTree<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PriorityQueue<MutableInteger> createQueue(PriorityQueue<MutableInteger> queue) {
|
||||
return new BinarySearchTree<>((BinarySearchTree<MutableInteger>) queue);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,320 @@
|
||||
package org.insa.graphs.algorithm.utils;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameter;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public abstract class PriorityQueueTest {
|
||||
|
||||
/**
|
||||
* Needs to be implemented by child class to actually provide priority queue
|
||||
* implementation.
|
||||
*
|
||||
* @return A new instance of a PriorityQueue implementation.
|
||||
*/
|
||||
public abstract PriorityQueue<MutableInteger> createQueue();
|
||||
|
||||
/**
|
||||
* Needs to be implemented by child class to actually provide priority queue
|
||||
* implementation.
|
||||
*
|
||||
* @param queue Queue to copy.
|
||||
*
|
||||
* @return Copy of the given queue.
|
||||
*/
|
||||
public abstract PriorityQueue<MutableInteger> createQueue(PriorityQueue<MutableInteger> queue);
|
||||
|
||||
protected static 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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Integer.toString(get());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
protected static class TestParameters<E extends Comparable<E>> {
|
||||
|
||||
// Data to insert
|
||||
public final E[] data;
|
||||
public final int[] deleteOrder;
|
||||
|
||||
public TestParameters(E[] data, int[] deleteOrder) {
|
||||
this.data = data;
|
||||
this.deleteOrder = deleteOrder;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Set of parameters.
|
||||
*
|
||||
*/
|
||||
@Parameters
|
||||
public static Collection<Object> data() {
|
||||
Collection<Object> objects = new ArrayList<>();
|
||||
|
||||
// Empty queue
|
||||
objects.add(new TestParameters<>(new MutableInteger[0], new int[0]));
|
||||
|
||||
// Queue with 50 elements from 0 to 49, inserted in order and deleted in order.
|
||||
objects.add(new TestParameters<>(
|
||||
IntStream.range(0, 50).mapToObj(MutableInteger::new).toArray(MutableInteger[]::new),
|
||||
IntStream.range(0, 50).toArray()));
|
||||
|
||||
// Queue with 20 elements from 0 to 19, inserted in order, deleted in the given
|
||||
// order.
|
||||
objects.add(new TestParameters<>(
|
||||
IntStream.range(0, 20).mapToObj(MutableInteger::new).toArray(MutableInteger[]::new),
|
||||
new int[] { 12, 17, 18, 19, 4, 5, 3, 2, 0, 9, 10, 16, 8, 14, 13, 15, 7, 6, 1,
|
||||
11 }));
|
||||
|
||||
// Queue with 7 elements.
|
||||
objects.add(
|
||||
new TestParameters<>(
|
||||
Arrays.stream(new int[] { 8, 1, 6, 3, 4, 5, 9 })
|
||||
.mapToObj(MutableInteger::new).toArray(MutableInteger[]::new),
|
||||
new int[] { 6, 5, 0, 1, 4, 2, 3 }));
|
||||
|
||||
// Queue with 7 elements.
|
||||
objects.add(
|
||||
new TestParameters<>(
|
||||
Arrays.stream(new int[] { 1, 7, 4, 8, 9, 6, 5 })
|
||||
.mapToObj(MutableInteger::new).toArray(MutableInteger[]::new),
|
||||
new int[] { 2, 0, 1, 3, 4, 5, 6 }));
|
||||
|
||||
// Queue with 13 elements.
|
||||
objects.add(new TestParameters<>(
|
||||
Arrays.stream(new int[] { 1, 7, 2, 8, 9, 3, 4, 10, 11, 12, 13, 5, 6 })
|
||||
.mapToObj(MutableInteger::new).toArray(MutableInteger[]::new),
|
||||
new int[] { 3, 4, 0, 2, 5, 6, 1, 7, 8, 9, 10, 11, 12 }));
|
||||
|
||||
return objects;
|
||||
}
|
||||
|
||||
@Parameter
|
||||
public TestParameters<MutableInteger> parameters;
|
||||
|
||||
// Actual queue.
|
||||
private PriorityQueue<MutableInteger> queue;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
// Create the range queue
|
||||
this.queue = createQueue();
|
||||
|
||||
for (MutableInteger v: parameters.data) {
|
||||
this.queue.insert(v);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsEmpty() {
|
||||
assertEquals(parameters.data.length == 0, this.queue.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSize() {
|
||||
assertEquals(parameters.data.length, this.queue.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsert() {
|
||||
PriorityQueue<MutableInteger> queue = createQueue();
|
||||
int size = 0;
|
||||
for (MutableInteger x: parameters.data) {
|
||||
queue.insert(x);
|
||||
assertEquals(++size, queue.size());
|
||||
}
|
||||
assertEquals(parameters.data.length, queue.size());
|
||||
MutableInteger[] range = Arrays.copyOf(parameters.data, parameters.data.length);
|
||||
Arrays.sort(range);
|
||||
|
||||
for (MutableInteger mi: range) {
|
||||
assertEquals(mi.get(), queue.deleteMin().value);
|
||||
assertEquals(--size, queue.size());
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = EmptyPriorityQueueException.class)
|
||||
public void testEmptyFindMin() {
|
||||
Assume.assumeTrue(queue.isEmpty());
|
||||
queue.findMin();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindMin() {
|
||||
Assume.assumeFalse(queue.isEmpty());
|
||||
assertEquals(Collections.min(Arrays.asList(parameters.data)).get(), queue.findMin().get());
|
||||
}
|
||||
|
||||
@Test(expected = EmptyPriorityQueueException.class)
|
||||
public void testEmptyDeleteMin() {
|
||||
Assume.assumeTrue(queue.isEmpty());
|
||||
queue.deleteMin();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteMin() {
|
||||
int size = parameters.data.length;
|
||||
assertEquals(size, queue.size());
|
||||
MutableInteger[] range = Arrays.copyOf(parameters.data, parameters.data.length);
|
||||
Arrays.sort(range);
|
||||
for (MutableInteger x: range) {
|
||||
assertEquals(x, queue.deleteMin());
|
||||
size -= 1;
|
||||
assertEquals(size, queue.size());
|
||||
}
|
||||
assertEquals(0, queue.size());
|
||||
assertTrue(queue.isEmpty());
|
||||
}
|
||||
|
||||
@Test(expected = ElementNotFoundException.class)
|
||||
public void testRemoveEmpty() {
|
||||
Assume.assumeTrue(queue.isEmpty());
|
||||
queue.remove(new MutableInteger(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveNotFound() {
|
||||
Assume.assumeFalse(queue.isEmpty());
|
||||
List<MutableInteger> data = Arrays.asList(parameters.data);
|
||||
MutableInteger min = new MutableInteger(Collections.min(data).get() - 1),
|
||||
max = new MutableInteger(Collections.max(data).get() + 1);
|
||||
try {
|
||||
queue.remove(min);
|
||||
fail("Expected exception " + ElementNotFoundException.class.getName());
|
||||
}
|
||||
catch (ElementNotFoundException e) {
|
||||
assertEquals(min, e.getElement());
|
||||
}
|
||||
try {
|
||||
queue.remove(max);
|
||||
fail("Expected exception " + ElementNotFoundException.class.getName());
|
||||
}
|
||||
catch (ElementNotFoundException e) {
|
||||
assertEquals(max, e.getElement());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteThenRemove() {
|
||||
Assume.assumeFalse(queue.isEmpty());
|
||||
while (!queue.isEmpty()) {
|
||||
MutableInteger min = queue.deleteMin();
|
||||
try {
|
||||
queue.remove(min);
|
||||
fail("Expected exception " + ElementNotFoundException.class.getName());
|
||||
}
|
||||
catch (ElementNotFoundException e) {
|
||||
assertEquals(min, e.getElement());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveTwice() {
|
||||
Assume.assumeFalse(queue.isEmpty());
|
||||
for (MutableInteger data: parameters.data) {
|
||||
PriorityQueue<MutableInteger> copyQueue = this.createQueue(this.queue);
|
||||
copyQueue.remove(data);
|
||||
try {
|
||||
copyQueue.remove(data);
|
||||
fail("Expected exception " + ElementNotFoundException.class.getName());
|
||||
}
|
||||
catch (ElementNotFoundException e) {
|
||||
assertEquals(data, e.getElement());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemove() {
|
||||
int size1 = queue.size();
|
||||
for (int i = 0; i < parameters.deleteOrder.length; ++i) {
|
||||
// Remove from structure
|
||||
queue.remove(parameters.data[parameters.deleteOrder[i]]);
|
||||
|
||||
// Copy the remaining elements
|
||||
PriorityQueue<MutableInteger> copyTree = createQueue(queue);
|
||||
|
||||
// Retrieve all remaining elements in both structures
|
||||
ArrayList<MutableInteger> remains_in = new ArrayList<>(),
|
||||
remains_cp = new ArrayList<>();
|
||||
for (int j = i + 1; j < parameters.deleteOrder.length; ++j) {
|
||||
remains_in.add(parameters.data[parameters.deleteOrder[j]]);
|
||||
remains_cp.add(copyTree.deleteMin());
|
||||
}
|
||||
|
||||
Collections.sort(remains_in);
|
||||
|
||||
// Check that the copy is now empty, and that both list contains all
|
||||
// elements.
|
||||
assertTrue(copyTree.isEmpty());
|
||||
assertEquals(remains_in, remains_cp);
|
||||
|
||||
// Check that the size of the original tree is correct.
|
||||
assertEquals(--size1, queue.size());
|
||||
}
|
||||
assertTrue(queue.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveThenAdd() {
|
||||
Assume.assumeFalse(queue.isEmpty());
|
||||
int min = Collections.min(Arrays.asList(parameters.data)).get();
|
||||
for (MutableInteger mi: parameters.data) {
|
||||
queue.remove(mi);
|
||||
assertEquals(parameters.data.length - 1, queue.size());
|
||||
mi.set(--min);
|
||||
queue.insert(mi);
|
||||
assertEquals(parameters.data.length, queue.size());
|
||||
assertEquals(min, queue.findMin().get());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user