package vroom.common.modeling.dataModel;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import vroom.common.modeling.util.DefaultRouteCostDelegate;
import vroom.common.modeling.util.IRouteCostDelegate;
import vroom.common.modeling.util.SolutionChecker;
/**
* <code>RouteBase</code> is a base type for classes used to represent a route.
* <p>
* In this implementation a route is modeled as a sequence of {@link INodeVisit} that are used to add additional
* information on the actual serving of a request at a node by a vehicle.
* </p>
* <p>
* The Class <code>RouteBase</code> defines a set of basic operations that are used by common optimization to modify a
* route, with for instance the insertion or removal of single {@link INodeVisit} or subroutes.
* </p>
*
* @see Vehicle
* @see INodeVisit
* @see Node
* @see Request
* @author Victor Pillac, <a href="http://uniandes.edu.co">Universidad de Los Andes</a> - <a
* href="http://copa.uniandes.edu.co">Copa</a>, <a href="http://www.emn.fr">Ecole des Mines de Nantes</a>-<a
* href="http://www.irccyn.ec-nantes.fr/irccyn/d/en/equipes/Slp">SLP</a>
* @version 1.0 - created 15-Feb-2010 11:29:51 a.m.
*/
public abstract class RouteBase implements IRoute<INodeVisit> {
private static boolean checkRoute = false;
/** The m cost changed. */
private boolean mCostChanged = true;
/** The m cap changed. */
private boolean mCapChanged = true;
/**
* Sets the changed flags to true.
*/
protected synchronized final void setChanged() {
mCostChanged = true;
mCapChanged = true;
}
/** The cost. */
private double mCost;
/** The loads */
private final double[] mLoads;
/** The parent solution. */
private IVRPSolution<?> mParentSolution;
/** The vehicle. */
private final Vehicle mVehicle;
/** the route cost calculation delegate **/
private IRouteCostDelegate mCostDelegate;
/**
* Getter for the route cost calculation delegate
*
* @return the cost delegate
*/
public IRouteCostDelegate getCostDelegate() {
return this.mCostDelegate;
}
/**
* Setter for the route cost calculation delegate.
* <p>
* The route cost will automatically be re-evaluated
* </p>
*
* @param delegate
* the value to be set for the route cost calculation delegate
*/
public void setCostDelegate(IRouteCostDelegate delegate) {
if (delegate == null)
throw new IllegalArgumentException("The cost delegate cannot be null");
this.mCostDelegate = delegate;
this.mCostDelegate.evaluateRoute(this);
}
/**
* Creates a new <code>VRPRoute</code> associated with the given <code>parentSolution</code> and
* <code>vehicle</code>.
*
* @param parentSolution
* the {@link Solution} that contains this route
* @param vehicle
* the {@link Vehicle} associated wiht this solution
*/
public RouteBase(IVRPSolution<?> parentSolution, Vehicle vehicle) {
super();
mParentSolution = parentSolution;
mVehicle = vehicle;
mLoads = new double[getVehicle().getCompartmentCount()];
mCostDelegate = new DefaultRouteCostDelegate(parentSolution.getParentInstance().getCostDelegate());
setChanged();
}
/*
* (non-Javadoc)
*
* @see vroom.common.modeling.dataModel.Route#getParentSolution()
*/
@Override
public IVRPSolution<?> getParentSolution() {
return mParentSolution;
}
/**
* Changes the parent solution of this route.
* <p>
* Useful to copy a route to a new solution without copying the actual information
* </p>
* <p>
* Warning: This method will remove this route from the current parent solution, but will not add it to the new one
* </p>
*
* @param parentSolution
* the new parent solution
*/
public void changeParentSolution(IVRPSolution<?> parentSolution) {
if (parentSolution == null) {
throw new IllegalArgumentException("Argument parentSolution cannot be null");
}
if (mParentSolution != null) {
mParentSolution.removeRoute(this);
}
mParentSolution = parentSolution;
}
/*
* (non-Javadoc)
*
* @see vroom.common.modeling.dataModel.Route#getVehicle()
*/
@Override
public Vehicle getVehicle() {
return mVehicle;
}
/*
* (non-Javadoc)
*
* @see vroom.common.modeling.dataModel.Route#getCost()
*/
@Override
public synchronized final double getCost() {
// calculateCost(false);
return mCost;
}
/*
* (non-Javadoc)
*
* @see vroom.common.modeling.dataModel.Route#calculateCost(boolean)
*/
@Override
public final void calculateCost(boolean force) {
if (force || mCostChanged) {
mCost = 0;
if (length() > 1) {
Iterator<INodeVisit> it = iterator();
INodeVisit pred = it.next();
while (it.hasNext()) {
INodeVisit succ = it.next();
mCost += getArcCost(pred, succ);
pred = succ;
}
}
mCostChanged = false;
}
}
/*
* (non-Javadoc)
*
* @see vroom.common.modeling.dataModel.Route#updateCost(double)
*/
@Override
public final synchronized void updateCost(double delta) {
mCost += delta;
}
/*
* (non-Javadoc)
*
* @see vroom.common.modeling.dataModel.Route#getRemainingCapacity()
*/
@Override
public synchronized final double getLoad() {
// calculateLoad(false);
return mLoads[0];
}
/*
* (non-Javadoc)
*
* @see vroom.common.modeling.dataModel.Route#getRemainingCapacity(int)
*/
@Override
public synchronized final double getLoad(int product) {
// calculateLoad(false);
return mLoads[product];
}
/*
* (non-Javadoc)
*
* @see vroom.common.modeling.dataModel.Route#getRemainingCapacities()
*/
@Override
public synchronized final double[] getLoads() {
// calculateLoad(false);
return Arrays.copyOf(mLoads, mLoads.length);
}
/*
* (non-Javadoc)
*
* @see
* vroom.common.modeling.dataModel.Route#calculateRemaingCapacities(boolean
* )
*/
@Override
public final synchronized void calculateLoad(boolean force) {
if (force || mCapChanged) {
for (int i = 0; i < mLoads.length; i++) {
mLoads[i] = 0;
for (INodeVisit v : this) {
if (v.getParentRequest() != null) {
mLoads[i] += v.getDemand(i);
}
}
}
mCapChanged = false;
}
}
/**
* Changes the stored load by a given delta.
*
* @param product
* the considered product
* @param delta
* the value to be added to the currently stored load
*/
@Override
public final synchronized void updateLoad(int product, double delta) {
mLoads[product] += delta;
}
/**
* Changes the stored load by a given delta.
*
* @param isInsertion
* @param nodes
* the nodes
*/
final public synchronized void updateLoad(boolean isInsertion, INodeVisit... nodes) {
double sign = isInsertion ? 1 : -1;
for (INodeVisit n : nodes) {
for (int p = 0; p < mLoads.length; p++) {
double demand = n.getDemand(p);
updateLoad(p, sign * demand);
}
}
}
/**
* Changes the stored load by a given delta.
*
* @param isInsertion
* @param nodes
* the nodes
*/
final public synchronized void updateLoad(boolean isInsertion, Collection<? extends INodeVisit> nodes) {
double sign = isInsertion ? 1 : -1;
for (INodeVisit n : nodes) {
for (int p = 0; p < mLoads.length; p++) {
double demand = n.getDemand(p);
updateLoad(p, sign * demand);
}
}
}
/*
* (non-Javadoc)
*
* @see
* vroom.common.modeling.dataModel.Route#canAccommodateRequest(vroom.modelling
* .VroomModelling.dataModel.Request)
*/
@Override
public boolean canAccommodateRequest(IVRPRequest request) {
calculateLoad(false);
for (int p = 0; p < mLoads.length; p++) {
if (getLoad(p) + request.getDemand(p) > getVehicle().getCapacity(p)) {
return false;
}
}
return true;
}
/*
* (non-Javadoc)
*
* @see vroom.common.modeling.dataModel.Route#getNodeAt(int)
*/
@Override
public synchronized INodeVisit getNodeAt(int index) {
// Check indices
String e = checkIndex(index, "index", false);
if (e != null) {
throw new IllegalArgumentException(e);
}
return getNodeAtImplem(index);
}
/**
* Implementation of {@link #getNodeAt(int)}<br/>
* Index do not need to be checked in this method as it is done in the parent method.
*
* @param index
* the index of the desired vertex
* @return the that is at position
*/
protected abstract INodeVisit getNodeAtImplem(int index);
/*
* (non-Javadoc)
*
* @see vroom.common.modeling.dataModel.Route#subroute(int, int)
*/
@Override
public synchronized List<INodeVisit> subroute(int start, int end) {
// Check indices
String e = checkSequenceIndexes(start, "start", end, "end", false);
if (e != null) {
throw new IllegalArgumentException(e);
}
return subrouteImplem(start, end);
}
/**
* Implementation of {@link #subroute(int, int)} Indexes do not need to be checked in this method as it is done in
* the parent method.
*
* @param start
* the position of the first node of the subroute
* @param end
* the position of the last node of the subroute
* @return a list containing the nodes in the positions between
*/
protected abstract List<INodeVisit> subrouteImplem(int start, int end);
/*
* Route manipulation methods
*/
/*
* (non-Javadoc)
*
* @see
* vroom.common.modeling.dataModel.Route#appendNode(vroom.common.modeling.
* dataModel.INodeVisit)
*/
@Override
public boolean appendNode(INodeVisit node) {
INodeVisit last = getLastNode();
boolean b = appendNodeImplem(node);
if (b) {
updateLoad(true, node);
getCostDelegate().nodeInserted(this, last, node, null);
}
if (checkRoute) {
String err = SolutionChecker.checkRoute(this, false, false, false);
if (err != null) {
System.out.println("RouteBase.appendNode: " + err);
}
}
return b;
}
/**
* Implementation of {@link #appendNode(INodeVisit)}.
*
* @param node
* the node to be added at the end of the route
* @return if the node was successfully added, otherwise
*/
protected abstract boolean appendNodeImplem(INodeVisit node);
/*
* (non-Javadoc)
*
* @see vroom.common.modeling.dataModel.IRoute#append(java.util.List)
*/
@Override
public boolean appendNodes(List<? extends INodeVisit> nodes) {
if (nodes.isEmpty()) {
return true;
}
INodeVisit last = getLastNode();
boolean b = appendNodesImplem(nodes);
if (b) {
updateLoad(true, nodes);
getCostDelegate().routeInserted(this, last, nodes, null);
}
if (checkRoute) {
String err = SolutionChecker.checkRoute(this, false, false, false);
if (err != null) {
System.out.println("RouteBase.appendNodes: " + err);
}
}
return b;
}
/**
* Implementation of {@link #appendNodes(List)}.
*
* @param nodes
* the nodes to be added at the end of the route
* @return <code>true</code> if the nodes were successfully added,
*/
protected abstract boolean appendNodesImplem(List<? extends INodeVisit> nodes);
/*
* (non-Javadoc)
*
* @see
* vroom.common.modeling.dataModel.IRoute#appendRoute(vroom.common.modeling
* .dataModel.IRoute)
*/
@Override
public boolean appendRoute(IRoute<? extends INodeVisit> appendedRoute) {
// Ignore empty route
if (appendedRoute.length() == 0) {
return true;
}
INodeVisit last = getLastNode();
boolean b = appendRouteImplem(appendedRoute);
if (b) {
// Update the load
for (int p = 0; p < mLoads.length; p++) {
updateLoad(p, appendedRoute.getLoad(p));
}
getCostDelegate().routeInserted(this, last, appendedRoute, null);
}
if (checkRoute) {
String err = SolutionChecker.checkRoute(this, false, false, false);
if (err != null) {
System.out.println("RouteBase.appendRoute: " + err);
}
}
return b;
}
/**
* Implementation of {@link #appendRoute(IRoute)}
*
* @param appendedRoute
* the route to be appended to this route
* @return <code>true</code>if the route was successfully appended
*/
protected abstract boolean appendRouteImplem(IRoute<? extends INodeVisit> appendedRoute);
/*
* (non-Javadoc)
*
* @see vroom.common.modeling.dataModel.Route#setNodeAt(int,
* vroom.common.modeling.dataModel.INodeVisit)
*/
@Override
public synchronized final INodeVisit setNodeAt(int index, INodeVisit node) {
String e = checkIndex(index, "index", true);
if (e != null) {
throw new IllegalArgumentException(e);
}
INodeVisit[] r = setNodeAtImplem(index, node);
updateLoad(false, r[1]);
updateLoad(true, node);
getCostDelegate().nodeReplaced(this, r[0], r[1], node, r[2]);
setChanged();
if (checkRoute) {
String err = SolutionChecker.checkRoute(this, false, false, false);
if (err != null) {
System.out.println("RouteBase.setNodeAt: " + err);
}
}
return r[1];
}
/**
* Implementation of {@link #setNodeAt(int, INodeVisit)}<br/>
* Indexes do not need to be checked in this method as it is done in the parent method.
*
* @param index
* the index of the node to be set
* @param node
* the node that will be visited at position <code>index</code>
* @return an array <code>[predecessor,previousNode,successor]</code>
*/
protected abstract INodeVisit[] setNodeAtImplem(int index, INodeVisit node);
/*
* (non-Javadoc)
*
* @see vroom.common.modeling.dataModel.Route#extractNode(int)
*/
@Override
public final INodeVisit extractNode(int index) {
// Check indices
String e = checkIndex(index, "index", false);
if (e != null) {
throw new IllegalArgumentException(e);
}
INodeVisit[] r = extractNodeImplem(index);
INodeVisit node = r[1];
updateLoad(false, node);
getCostDelegate().nodeRemoved(this, r[0], node, r[2]);
setChanged();
if (checkRoute) {
String err = SolutionChecker.checkRoute(this, false, false, false);
if (err != null) {
System.out.println("RouteBase.bestInsertion: " + err);
}
}
return node;
}
/**
* A default implementation of {@link #extractNodes(int, int)}.<br/>
* Indexes do not need to be checked in this method as it is done in the parent method
*
* @param index
* the index of the node to be extracted
* @return an array containing <code>[pred,node,succ]</code>
*/
protected abstract INodeVisit[] extractNodeImplem(int index);
/*
* (non-Javadoc)
*
* @see vroom.common.modeling.dataModel.IRoute#extractSubRoute(int, int)
*/
@Override
public final RouteBase extractSubroute(int start, int end) {
// Check indices
String e = checkSequenceIndexes(start, "start", end, "end", false);
if (e != null) {
throw new IllegalArgumentException(e);
}
Object[] r = extractSubrouteImplem(start, end);
RouteBase route = (RouteBase) r[1];
getCostDelegate().subrouteRemoved(this, (INodeVisit) r[0], route, (INodeVisit) r[2]);
for (int p = 0; p < mLoads.length; p++) {
updateLoad(p, -route.getLoad(p));
}
setChanged();
if (checkRoute) {
String err = SolutionChecker.checkRoute(this, false, false, false);
if (err != null) {
System.out.println("RouteBase.extractSubroute: " + err);
}
}
return route;
}
/**
* A default implementation of {@link #extractSubroute(int, int)}.<br/>
* Indexes do not need to be checked in this method as it is done in the parent method
*
* @param start
* the subroute start
* @param end
* the subroute end
* @return an array containing <code>[pred,subroute,succ]</code>
*/
protected abstract Object[] extractSubrouteImplem(int start, int end);
/*
* (non-Javadoc)
*
* @see vroom.common.modeling.dataModel.Route#extractSubroute(int, int)
*/
@Override
public final List<INodeVisit> extractNodes(int start, int end) {
// Check indices
String e = checkSequenceIndexes(start, "start", end, "end", false);
if (e != null) {
throw new IllegalArgumentException(e);
}
setChanged();
Object[] r = extractNodesImplem(start, end);
@SuppressWarnings("unchecked")
List<INodeVisit> nodes = (List<INodeVisit>) r[1];
getCostDelegate().subrouteRemoved(this, (INodeVisit) r[0], nodes, (INodeVisit) r[2]);
updateLoad(false, nodes);
if (checkRoute) {
String err = SolutionChecker.checkRoute(this, false, false, false);
if (err != null) {
System.out.println("RouteBase.extractNodes: " + err);
}
}
return nodes;
}
/**
* Implementation of {@link #extractNodes(int, int)}.<br/>
* Indexes do not need to be checked in this method as it is done in the parent method
*
* @param start
* the index of first node of the subroute
* @param end
* the index of the last node of the subroute
* @return an array containing <code>[INodeVisit pred, List<INodeVisit> subroute, INodeVisit succ]</code>
*/
protected abstract Object[] extractNodesImplem(int start, int end);
@Override
public boolean remove(INodeVisit node) {
INodeVisit[] neigh = removeImplem(node);
if (neigh != null) {
updateLoad(false, node);
INodeVisit p = neigh[0];
INodeVisit s = neigh[1];
getCostDelegate().nodeRemoved(this, p, node, s);
if (checkRoute) {
String err = SolutionChecker.checkRoute(this, false, true, true);
if (err != null) {
System.out.println("RouteBase.remove: " + err);
}
}
return true;
} else {
return false;
}
}
/**
* Implementation of {@link #remove(INodeVisit)}
*
* @param node
* the node to be removed
* @return an array containing the predecessor and successor of the removed node, or <code>null</code> if the node
* was not in this route
*/
protected abstract INodeVisit[] removeImplem(INodeVisit node);
@Override
public final NodeInsertion getBestNodeInsertion(INodeVisit node) {
return getBestNodeInsertion(node, 0, length());
}
@Override
public final NodeInsertion getBestNodeInsertion(INodeVisit node, int min, int max) {
if (length() == 0) {
return new NodeInsertion(node, 0, 0, this);
}
String s = checkSequenceIndexes(min, "min", max, "max", true);
if (s != null) {
throw new IllegalArgumentException(s);
}
if (min == max) {
double cost = 0;
if (min == 0) {
cost = getParentSolution().getParentInstance().getCost(node, getFirstNode(), getVehicle());
} else if (max == length()) {
cost = getParentSolution().getParentInstance().getCost(getLastNode(), node, getVehicle());
} else {
cost = getParentSolution().getParentInstance().getInsertionCost(node, getNodeAt(min - 1),
getNodeAt(min), getVehicle());
}
return new NodeInsertion(node, cost, min, this);
}
double cost = Integer.MAX_VALUE;
double c;
int pos = -1;
if (length() == 0) {
cost = 0;
pos = 0;
} else if (length() == 1) {
// Assume that the first node is a depot
cost = getParentSolution().getParentInstance().getCost(getFirstNode(), node, getVehicle());
pos = 1;
} else {
if (min == 0 && getFirstNode().isFixed()) {
min = 1;
}
Iterator<INodeVisit> route = iterator();
INodeVisit pred = null, succ = null;
int index = 0;
while (index < min) {
pred = route.next();
index++;
}
while (index <= max) {
if (route.hasNext()) {
succ = route.next();
} else {
succ = null;
}
if (pred == null) {
// First position insertion
c = getParentSolution().getParentInstance().getCost(node, succ, getVehicle());
} else if (succ == null) {
// Last position insertion
if (!getLastNode().isFixed()) {
c = getParentSolution().getParentInstance().getCost(pred, node, getVehicle());
} else {
c = Double.MAX_VALUE;
}
} else {
c = getParentSolution().getParentInstance().getInsertionCost(node, pred, succ, getVehicle());
}
if (c < cost) {
cost = c;
pos = index;
}
index++;
pred = succ;
if (succ == null) {
break;
}
}
}
return new NodeInsertion(node, cost, pos, this);
}
@Override
public boolean bestInsertion(INodeVisit node) {
NodeInsertion ins = getBestNodeInsertion(node);
INodeVisit[] r = insertNodeImplem(ins.getPosition(), node);
getCostDelegate().nodeInserted(this, r[0], node, r[1]);
updateLoad(true, node);
if (checkRoute) {
String err = SolutionChecker.checkRoute(this, false, false, false);
if (err != null) {
System.out.println("RouteBase.bestInsertion: " + err);
}
}
return true;
}
/*
* (non-Javadoc)
*
* @see vroom.common.modeling.dataModel.Route#insertNode(int,
* vroom.common.modeling.dataModel.INodeVisit)
*/
@Override
public final boolean insertNode(int index, INodeVisit node) {
// Check indices
String e = checkIndex(index, "index", true);
if (e != null) {
throw new IllegalArgumentException(e);
}
INodeVisit pred = index > 0 ? getNodeAt(index - 1) : null;
INodeVisit succ = index < length() - 1 ? getNodeAt(index + 1) : null;
double delta = getParentSolution().getParentInstance().getInsertionCost(node, pred, succ, getVehicle());
return insertNode(new NodeInsertion(node, delta, index, this), node);
}
/*
* (non-Javadoc)
*
* @see
* vroom.common.modeling.dataModel.Route#insertNode(vroom.common.modeling.
* dataModel.RouteBase.NodeInsertion,
* vroom.common.modeling.dataModel.INodeVisit)
*/
@Override
public final boolean insertNode(NodeInsertion ins, INodeVisit node) {
INodeVisit[] r = insertNodeImplem(ins.getPosition(), node);
getCostDelegate().nodeInserted(this, r[0], node, r[1]);
updateLoad(true, node);
if (checkRoute) {
String err = SolutionChecker.checkRoute(this, false, false, false);
if (err != null) {
System.out.println("RouteBase.insertNode: " + err);
}
}
return true;
}
/**
* Implementation of the {@link #insertNode(int, INodeVisit)} method.<br/>
* Indexes do not need to be checked in this method as it is done in the parent method
*
* @param index
* the index at which the given node should be inserted.
* @param node
* the node that will be visited at position <code>index</code>
* @return an array <code>[predecessor,successor]</code>
*/
protected abstract INodeVisit[] insertNodeImplem(int index, INodeVisit node);
/*
* (non-Javadoc)
*
* @see vroom.common.modeling.dataModel.IRoute#insertSubroute(int,
* vroom.common.modeling.dataModel.IRoute)
*/
@Override
public boolean insertSubroute(int index, IRoute<? extends INodeVisit> subroute) {
// Ignore empty routes
if (subroute.length() == 0) {
return true;
}
// Check indices
String e = checkIndex(index, "index", true);
if (e != null) {
throw new IllegalArgumentException(e);
}
INodeVisit[] r = insertNodesImplem(index, subroute.getNodeSequence());
// Update the load
for (int p = 0; p < mLoads.length; p++) {
updateLoad(p, subroute.getLoad(p));
}
getCostDelegate().routeInserted(this, r[0], subroute, r[1]);
if (checkRoute) {
String err = SolutionChecker.checkRoute(this, false, false, false);
if (err != null) {
System.out.println("RouteBase.insertSubroute: " + err);
}
}
return true;
}
/*
* (non-Javadoc)
*
* @see vroom.common.modeling.dataModel.Route#insertSubroute(int,
* java.util.List)
*/
@Override
public final boolean insertNodes(int index, List<? extends INodeVisit> subroute) {
if (subroute.isEmpty()) {
return true;
}
// Check indices
String e = checkIndex(index, "index", true);
if (e != null) {
throw new IllegalArgumentException(e);
}
INodeVisit[] r = insertNodesImplem(index, subroute);
// Update the load
updateLoad(true, subroute);
getCostDelegate().routeInserted(this, r[0], subroute, r[1]);
setChanged();
if (checkRoute) {
String err = SolutionChecker.checkRoute(this, false, false, false);
if (err != null) {
System.out.println("RouteBase.insertNodes: " + err);
}
}
return true;
}
/**
* Implementation of {@link #insertNodes(int, List)}.<br/>
* Indexes do not need to be checked in this method as it is done in the parent method
*
* @param index
* the position at which the <code>subroute</code> will be inserted.
* @param subroute
* the sequence of nodes to be inserter.
* @return an array <code>[predecessor, successor]</code>
*/
protected abstract INodeVisit[] insertNodesImplem(int index, List<? extends INodeVisit> subroute);
/*
* (non-Javadoc)
*
* @see vroom.common.modeling.dataModel.Route#swapNodes(int, int)
*/
@Override
public final boolean swapNodes(int i, int j) {
if (i == j) {
return true;
}
// Check indices
String e = checkIndex(i, "node1", false);
if (e != null) {
throw new IllegalArgumentException(e);
}
e = checkIndex(j, "node2", false);
if (e != null) {
throw new IllegalArgumentException(e);
}
// Check nodes order
if (j < i) {
int t = i;
i = j;
j = t;
}
INodeVisit[] r = swapNodesImplem(i, j);
getCostDelegate().nodesSwapped(this, r[0], r[1], r[2], r[3], r[4], r[5]);
if (checkRoute) {
String err = SolutionChecker.checkRoute(this, false, false, false);
if (err != null) {
System.out.println("RouteBase.swapNodes: " + err);
}
}
setChanged();
return true;
}
/**
* Default implementation of {@link #swapNodes(int, int)}.<br/>
* Indexes do not need to be checked in this method as it is done in the parent method: <code>node1 < node2</code>
*
* @param node1
* the index of the first node
* @param node2
* the index of the second node
* @return an array <code>[pred1,node1,succ1,pred2,node2,succ2]</code>
* @see IRouteCostDelegate#nodesSwapped(IRoute, INodeVisit, INodeVisit, INodeVisit, INodeVisit, INodeVisit,
* INodeVisit)
*/
INodeVisit[] swapNodesImplem(int node1, int node2) {
INodeVisit n2 = getNodeAt(node2);
INodeVisit[] r = setNodeAtImplem(node1, n2);
INodeVisit[] s = setNodeAtImplem(node2, r[1]);
return new INodeVisit[] { r[0], r[1], r[2], s[0], s[1], s[2] };
}
/*
* (non-Javadoc)
*
* @see vroom.common.modeling.dataModel.Route#revertSubRoute(int, int)
*/
@Override
public void reverseSubRoute(int start, int end) {
if (start != end) {
// Check indices
String e = checkSequenceIndexes(start, "start", end, "end", false);
if (e != null) {
throw new IllegalArgumentException(e);
}
INodeVisit[] r = reverseSubrouteImplem(start, end);
setChanged();
getCostDelegate().subrouteReversed(this, r[0], r[1], r[2], r[3]);
if (checkRoute) {
String err = SolutionChecker.checkRoute(this, false, true, true);
if (err != null) {
System.out.println("RouteBase.reverseSubRoute: " + err);
}
}
}
}
/**
* Implementation of {@link #reverseSubRoute(int, int)}.<br/>
* Indexes do not need to be checked in this method as it is done in the parent method:<br/>
* <code>0 <= start <= end < this.length()</code>
*
* @param start
* the position of the first node of the desired subroute
* @param end
* the index
* @return an array <code>[pred,first,last,succ]</code>
*/
protected abstract INodeVisit[] reverseSubrouteImplem(int start, int end);
/*
* (non-Javadoc)
*
* @see vroom.common.modeling.dataModel.IRoute#reverseRoute()
*/
@Override
public void reverseRoute() {
reverseSubrouteImplem(0, length() - 1);
}
/*
* Index checks
*/
/**
* Check if <code>index</code> is a valid index.<br/>
* <code>0<idnex<this.length()</code>
*
* @param index
* the index to checked
* @param argName
* the name of the corresponding argument
* @param allowLength
* <code>true</code> if <code>index</code> can take the value of length (used in set* and insert methods)
* @return a String describing the detected error if any, otherwise
*/
synchronized String checkIndex(int index, String argName, boolean allowLength) {
if (!(allowLength && index == length()) && (index < 0 || index >= length())) {
return String.format("%1$s is out of range (%1$s=%2$s, length=%3$s)", argName, index, length());
} else {
return null;
}
}
/**
* Check if <code>index1</code> and <code>index2</code> are valids subroute indexes:<br/>
* <code>0<idnex1<=index2<this.length()</code>
*
* @param index1
* the first index of the subroute
* @param arg1Name
* the name of the argument corresponding to <code>index1</code>
* @param index2
* the last index of the subroute
* @param arg2Name
* the name of the argument corresponding to <code>index2</code>
* @param allowLength
* <code>true</code> if index2 can be equal to the route length
* @return a String describing the detected error if any, otherwise
*/
String checkSequenceIndexes(int index1, String arg1Name, int index2, String arg2Name, boolean allowLength) {
String r = null;
String i1 = checkIndex(index1, arg1Name, allowLength);
if (i1 != null) {
r = i1;
}
String i2 = checkIndex(index2, arg2Name, allowLength);
if (i2 != null) {
r = r == null ? i2 : r + "," + i2;
}
if (index1 > index2) {
String le = String.format("%1$s should be lower than %2$s (%1$s=%3$s while %2$s=%4$s)", arg1Name, arg2Name,
index1, index2);
r = r == null ? le : r + "," + le;
}
return r;
}
@Override
public String getNodeSeqString() {
StringBuilder sb = new StringBuilder(length() * 3);
sb.append("<");
for (INodeVisit v : this) {
sb.append(v.getID());
sb.append(',');
}
if (length() > 0) {
sb.setCharAt(sb.length() - 1, '>');
} else {
sb.append('>');
}
return sb.toString();
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public synchronized String toString() {
StringBuilder b = new StringBuilder(length() * 5);
b.append(String.format("cost:%.2f", getCost()));
b.append(" length:");
b.append(length());
b.append(" load:");
b.append(Arrays.toString(getLoads()));
b.append(" ");
b.append(getNodeSeqString());
return b.toString();
}
/*
* (non-Javadoc)
*
* @see vroom.common.modeling.dataModel.Route#length()
*/
@Override
public abstract int length();
/*
* (non-Javadoc)
*
* @see vroom.common.modeling.dataModel.Route#getNodeSequence()
*/
@Override
public abstract List<INodeVisit> getNodeSequence();
/*
* Static utility methods
*/
/**
* Reverts a {@link List} of {@link INodeVisit}.
*
* @param subroute
* the list of nodes to be revered
* @return a list of {@link INodeVisit} such as
*/
public static List<INodeVisit> revertSubRoute(List<INodeVisit> subroute) {
LinkedList<INodeVisit> reverted = new LinkedList<INodeVisit>();
for (INodeVisit n : subroute) {
reverted.addFirst(n);
}
return reverted;
}
/**
* Utility method for cost calculation
*
* @param i
* @param j
* @return the vehicle dependent cost of traveling arc (i,j) in this route
*/
public double getArcCost(int i, int j) {
return i == j ? 0 : getArcCost(getNodeAt(i), getNodeAt(j));
}
/**
* Utility method for cost calculation
*
* @param n1
* @param n2
* @return the vehicle dependent cost of traveling arc (n1,n2) in this route
*/
public double getArcCost(INodeVisit n1, INodeVisit n2) {
return n1 == n2 ? 0 : getParentSolution().getParentInstance().getCostDelegate().getCost(n1, n2, getVehicle());
}
/**
* Utility method for the calculation of a node sequence cost
*
* @param subroute
* @return the cost of the given subroute if traveled by the vehicle associted with this toute
*/
public double getSubrouteCost(List<? extends INodeVisit> subroute) {
if (subroute.isEmpty()) {
return 0;
}
double cost = 0;
Iterator<? extends INodeVisit> it = subroute.iterator();
INodeVisit pred = it.next();
INodeVisit succ;
while (it.hasNext()) {
succ = it.next();
cost += getArcCost(pred, succ);
pred = succ;
}
return cost;
}
@Override
public abstract boolean contains(INodeVisit node);
@Override
public abstract RouteBase clone();
}// end VRPRoute