Input:
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import routing.util.RoutingInfo;
import util.Tuple;
import core.Connection;
import core.DTNHost;
import core.Message;
import core.Settings;
import core.SimClock;

Context:
src/routing/ProphetRouterWithEstimation.java
/*
 * Copyright 2010 Aalto University, ComNet
 * Released under GPLv3. See LICENSE.txt for details.
 */
package routing;

/**
 * Implementation of PRoPHET router as described in
 * Probabilistic routing in intermittently connected networks by
 * Anders Lindgren et al.
 *
 *
 * This version tries to estimate a good value of protocol parameters from
 * a timescale parameter given by the user, and from the encounters the node
 * sees during simulation.
 * Refer to Karvo and Ott, Time Scales and Delay-Tolerant Routing
 * Protocols Chants, 2008
 *
 */
public class ProphetRouterWithEstimation extends ActiveRouter {
	/** delivery predictability initialization constant*/
	public static final double P_INIT = 0.75;
	/** delivery predictability transitivity scaling constant default value */
	public static final double DEFAULT_BETA = 0.25;
	/** delivery predictability aging constant */
	public static final double GAMMA = 0.98;
	/** default P target */
	public static final double DEFAULT_PTARGET = .2;

	/** Prophet router's setting namespace ({@value})*/
	public static final String PROPHET_NS = "ProphetRouterWithEstimation";
	/**
	 * Number of seconds in time scale.*/
	public static final String TIME_SCALE_S ="timeScale";
	/**
	 * Target P_avg
	 *
	 */
	public static final String P_AVG_TARGET_S = "targetPavg";

	/**
	 * Transitivity scaling constant (beta) -setting id ({@value}).
	 * Default value for setting is {@link #DEFAULT_BETA}.
	 */
	public static final String BETA_S = "beta";

	/** values of parameter settings */
	private double beta;
	private double gamma;
	private double pinit;

	/** value of time scale variable */
	private int timescale;
	private double ptavg;

	/** delivery predictabilities */
	private Map<DTNHost, Double> preds;

	/** last meeting time with a node */
	private Map<DTNHost, Double> meetings;
	private int nrofSamples;
	private double meanIET;

	/** last delivery predictability update (sim)time */
	private double lastAgeUpdate;

	/**
	 * Constructor. Creates a new message router based on the settings in
	 * the given Settings object.
	 * @param s The settings object
	 */

src/core/Connection.java
public abstract class Connection {
	protected DTNHost toNode;
	protected NetworkInterface toInterface;
	protected DTNHost fromNode;
	protected NetworkInterface fromInterface;
	protected DTNHost msgFromNode;

	private boolean isUp;
	protected Message msgOnFly;
	/** how many bytes this connection has transferred */
	protected int bytesTransferred;

	/**
	 * Creates a new connection between nodes and sets the connection
	 * state to "up".
	 * @param fromNode The node that initiated the connection
	 * @param fromInterface The interface that initiated the connection
	 * @param toNode The node in the other side of the connection
	 * @param toInterface The interface in the other side of the connection
	 */
	public Connection(DTNHost fromNode, NetworkInterface fromInterface,
			DTNHost toNode, NetworkInterface toInterface) {
		this.fromNode = fromNode;
		this.fromInterface = fromInterface;
		this.toNode = toNode;
		this.toInterface = toInterface;
		this.isUp = true;
		this.bytesTransferred = 0;
	}

	/**
	 * Returns true if the connection is up
	 * @return state of the connection
	 */
	public boolean isUp() {
		return this.isUp;
	}

	/**
	 * Returns true if the connections is transferring a message
	 * @return true if the connections is transferring a message
	 */
	public boolean isTransferring() {
		return this.msgOnFly != null;
	}

	/**
	 * Returns true if the given node is the initiator of the connection, false
	 * otherwise
	 * @param node The node to check
	 * @return true if the given node is the initiator of the connection
	 */
	public boolean isInitiator(DTNHost node) {
		return node == this.fromNode;
	}

	/**
	 * Sets the state of the connection.
	 * @param state True if the connection is up, false if not
	 */
	public void setUpState(boolean state) {
		this.isUp = state;
	}

	/**
	 * Sets a message that this connection is currently transferring. If message
	 * passing is controlled by external events, this method is not needed
	 * (but then e.g. {@link #finalizeTransfer()} and
	 * {@link #isMessageTransferred()} will not work either). Only a one message
	 * at a time can be transferred using one connection.
	 * @param m The message
	 * @return The value returned by
	 * {@link MessageRouter#receiveMessage(Message, DTNHost)}
	 */
	public abstract int startTransfer(DTNHost from, Message m);

	/**
	 * Calculate the current transmission speed from the information
	 * given by the interfaces, and calculate the missing data amount.
	 */
	public void update() {};

	/**
     * Aborts the transfer of the currently transferred message.
     */
	public void abortTransfer() {
		assert msgOnFly != null : "No message to abort at " + msgFromNode;
		int bytesRemaining = getRemainingByteCount();

		this.bytesTransferred += msgOnFly.getSize() - bytesRemaining;

		getOtherNode(msgFromNode).messageAborted(this.msgOnFly.getId(),
				msgFromNode, bytesRemaining);
		clearMsgOnFly();
	}

	/**
	 * Returns the amount of bytes to be transferred before ongoing transfer
	 * is ready or 0 if there's no ongoing transfer or it has finished
	 * already
	 * @return the amount of bytes to be transferred
	 */
	public abstract int getRemainingByteCount();

	/**
	 * Clears the message that is currently being transferred.
	 * Calls to {@link #getMessage()} will return null after this.
	 */
	protected void clearMsgOnFly() {
		this.msgOnFly = null;
		this.msgFromNode = null;
	}

	/**
	 * Finalizes the transfer of the currently transferred message.
	 * The message that was being transferred can not be
	 * retrieved from this connections after calling this method (using
	 * {@link #getMessage()}).
	 */
	public void finalizeTransfer() {
		assert this.msgOnFly != null : "Nothing to finalize in " + this;
		assert msgFromNode != null : "msgFromNode is not set";

		this.bytesTransferred += msgOnFly.getSize();

		getOtherNode(msgFromNode).messageTransferred(this.msgOnFly.getId(),
				msgFromNode);
		clearMsgOnFly();
	}

	/**
	 * Returns true if the current message transfer is done
	 * @return True if the transfer is done, false if not
	 */
	public abstract boolean isMessageTransferred();

	/**
	 * Returns true if the connection is ready to transfer a message (connection
	 * is up and there is no message being transferred).
	 * @return true if the connection is ready to transfer a message
	 */
	public boolean isReadyForTransfer() {
		return this.isUp && this.msgOnFly == null;
	}

	/**
	 * Gets the message that this connection is currently transferring.
	 * @return The message or null if no message is being transferred
	 */
	public Message getMessage() {
		return this.msgOnFly;
	}

	/**
	 * Gets the current connection speed
	 */
	public abstract double getSpeed();

	/**
	 * Returns the total amount of bytes this connection has transferred so far
	 * (including all transfers).
	 */
	public int getTotalBytesTransferred() {
		if (this.msgOnFly == null) {
			return this.bytesTransferred;
		}
		else {
			if (isMessageTransferred()) {
				return this.bytesTransferred + this.msgOnFly.getSize();
			}
			else {
				return this.bytesTransferred +
				(msgOnFly.getSize() - getRemainingByteCount());
			}
		}
	}

	/**
	 * Returns the node in the other end of the connection
	 * @param node The node in this end of the connection
	 * @return The requested node
	 */
	public DTNHost getOtherNode(DTNHost node) {
		if (node == this.fromNode) {
			return this.toNode;
		}
		else {
			return this.fromNode;
		}
	}

	/**
	 * Returns the interface in the other end of the connection
	 * @param i The interface in this end of the connection
	 * @return The requested interface
	 */
	public NetworkInterface getOtherInterface(NetworkInterface i) {
		if (i == this.fromInterface) {
			return this.toInterface;
		}
		else {
			return this.fromInterface;
		}
	}

	/**
	 * Returns a String presentation of the connection.
	 */
	public String toString() {
		return fromNode + "<->" + toNode + " (" + getSpeed()/1000 + " kBps) is " +
		(isUp() ? "up":"down") +
		(isTransferring() ? " transferring " + this.msgOnFly  +
				" from " + this.msgFromNode : "");
	}

}

src/core/Settings.java
public class Settings {
	/** properties object where the setting files are read into */
	protected static Properties props;
	/** file name of the default settings file ({@value}) */
	public static final String DEF_SETTINGS_FILE ="default_settings.txt";

	/**
	 * Setting to define the file name where all read settings are written
	 * ({@value}. If set to an empty string, standard output is used.
	 * By default setting are not written anywhere.
	 */
	public static final String SETTING_OUTPUT_S = "Settings.output";

	/** delimiter for requested values in strings ({@value})
	 * @see #valueFillString(String) */
	public static final String FILL_DELIMITER = "%%";

	/** Stream where all read settings are written to */
	private static PrintStream out = null;
	private static Set<String> writtenSettings = new HashSet<String>();

	/** run index for run-specific settings */
	private static int runIndex = 0;
	private String namespace = null; // namespace to look the settings from
	private String secondaryNamespace = null;
	private Stack<String> oldNamespaces;
	private Stack<String> secondaryNamespaces;

	/**
	 * Creates a setting object with a namespace. Namespace is the prefix
	 * of the all subsequent setting requests.
	 * @param namespace Namespace to use
	 */
	public Settings(String namespace) {
		this.oldNamespaces = new Stack<String>();
		this.secondaryNamespaces = new Stack<String>();
		setNameSpace(namespace);
	}

	/**
	 * Create a setting object without namespace. All setting requests must
	 * be prefixed with a valid namespace (e.g. "Report.nrofReports").
	 */
	public Settings() {
		this(null);
	}

	/**
	 * Sets the run index for the settings (only has effect on settings with
	 * run array). A run array can be defined with syntax

	 * [settingFor1stRun ; settingFor2ndRun ; SettingFor3rdRun]
	 *
I.e. settings are put in brackets and delimited with semicolon.
	 * First run's setting is returned when index is 0, second when index is
	 * 1 etc. If run index is bigger than run array's length, indexing wraps
	 * around in run array (i.e. return value is the value at index
	 * runIndex % arrayLength).
	 * To disable whole run-index-thing, set index to value smaller than
	 * zero (e.g. -1). When disabled, run-arrays are returned as normal values,
	 * including the brackets.
	 * @param index The run index to use for subsequent settings calls, or
	 * -1 to disable run indexing
	 */
	public static void setRunIndex(int index) {
		runIndex = index;
		writtenSettings.clear();
	}

	/**
	 * Checks that the given integer array contains a valid range. I.e.,
	 * the length of the array must be two and
	 * first_value <= second_value.
	 * @param range The range array
	 * @param sname Name of the setting (for error messages)
	 * @throws SettingsError If the given array didn't qualify as a range
	 */
	public void assertValidRange(int range[], String sname)
		throws SettingsError {
		if (range.length != 2) {
			throw new SettingsError("Range setting " +
					getFullPropertyName(sname) +
					" should contain only two comma separated integer values");
		}
		if (range[0] > range[1]) {
			throw new SettingsError("Range setting's " +
					getFullPropertyName(sname) +
					" first value should be smaller or equal to second value");
		}
	}

	/**
	 * Makes sure that the given settings value is positive
	 * @param value Value to check
	 * @param settingName Name of the setting (for error's message)
	 * @throws SettingsError if the value was not positive
	 */
	public void ensurePositiveValue(double value, String settingName) {
		if (value < 0) {
			throw new SettingsError("Negative value (" + value +
					") not accepted for setting " + settingName);
		}
	}

	/**
	 * Sets the namespace to something else than the current namespace.
	 * This change can be reverted using {@link #restoreNameSpace()}
	 * @param namespace The new namespace
	 */
	public void setNameSpace(String namespace) {
		this.oldNamespaces.push(this.namespace);
		this.namespace = namespace;
	}

	/**
	 * Appends the given namespace to the the current namespace,
	 * for both the primary and secondary namespace .
	 * This change can be reverted using {@link #restoreNameSpace()} and
	 * {@link #restoreSecondaryNamespace()}.
	 * @param namespace The new namespace to append
	 */
	public void setSubNameSpace(String namespace) {
		this.oldNamespaces.push(this.namespace);
		this.namespace = this.namespace + "." + namespace;
		this.secondaryNamespaces.push(this.secondaryNamespace);
		this.secondaryNamespace = this.secondaryNamespace + "." + namespace;
	}

	/**
	 * Returns full (namespace prefixed) property name for a setting.
	 * @param setting The name of the setting
	 * @return The setting name prefixed with fully qualified name of the
	 * namespace where the requested setting would be retrieved from or null
	 * if that setting is not found from any of the current namespace(s)
	 */
	public String getFullPropertyName(String setting) {
		if (!contains(setting)) {
			return null;
		}

		if (props.getProperty(getFullPropertyName(setting, false)) != null) {
			return getFullPropertyName(setting, false);
		}

		// not found from primary, but Settings contains -> must be from 2ndary
		else return getFullPropertyName(setting, true);
	}

	/**
	 * Returns the namespace of the settings object
	 * @return the namespace of the settings object
	 */
	public String getNameSpace() {
		return this.namespace;
	}

	/**
	 * Returns the secondary namespace of the settings object
	 * @return the secondary namespace of the settings object
	 */
	public String getSecondaryNameSpace() {
		return this.secondaryNamespace;
	}

	/**
	 * Sets a secondary namespace where a setting is searched from if it
	 * isn't found from the primary namespace. Secondary namespace can
	 * be used e.g. as a "default" space where the settings are looked from
	 * if no specific setting is set.
	 * This change can be reverted using {@link #restoreSecondaryNamespace()}
	 * @param namespace The new secondary namespace or null if secondary
	 * namespace is not used (default behavior)
	 */
	public void setSecondaryNamespace(String namespace) {
		this.secondaryNamespaces.push(this.secondaryNamespace);
		this.secondaryNamespace = namespace;
	}

	/**
	 * Restores the namespace that was in use before a call to setNameSpace
	 * @see #setNameSpace(String)
	 */
	public void restoreNameSpace() {
		this.namespace = this.oldNamespaces.pop();
	}

	/**
	 * Restores the secondary namespace that was in use before a call to
	 * setSecondaryNameSpace
	 * @see #setSecondaryNamespace(String)
	 */
	public void restoreSecondaryNamespace() {
		this.secondaryNamespace = this.secondaryNamespaces.pop();
	}

	/**
	 * Reverts the change made with {@link #setSubNameSpace(String)}, i.e.,
	 * restores both the primary and secondary namespace.
	 */
	public void restoreSubNameSpace() {
		restoreNameSpace();
		restoreSecondaryNamespace();
	}

	/**
	 * Initializes the settings all Settings objects will use. This should be
	 * called before any setting requests. Subsequent calls replace all
	 * old settings and then Settings contains only the new settings.
	 * The file {@link #DEF_SETTINGS_FILE}, if exists, is always read.
	 * @param propFile Path to the property file where additional settings
	 * are read from or null if no additional settings files are needed.
	 * @throws SettingsError If loading the settings file(s) didn't succeed
	 */
	public static void init(String propFile) throws SettingsError {
		String outFile;
		try {
			if (new File(DEF_SETTINGS_FILE).exists()) {
				Properties defProperties = new Properties();
				defProperties.load(new FileInputStream(DEF_SETTINGS_FILE));
				props = new Properties(defProperties);
			}
			else {
				props = new Properties();
			}
			if (propFile != null) {
				props.load(new FileInputStream(propFile));
			}
		} catch (IOException e) {
			throw new SettingsError(e);
		}

		outFile = props.getProperty(SETTING_OUTPUT_S);
		if (outFile != null) {
			if (outFile.trim().length() == 0) {
				out = System.out;
			} else {
				try {
					out = new PrintStream(new File(outFile));
				} catch (FileNotFoundException e) {
					throw new SettingsError("Can't open Settings output file:" +
							e);
				}
			}
		}
	}

	/**
	 * Initializes the settings all Settings objects will use. This should be
	 * called before any setting requests. Subsequent calls replace all
	 * old settings and then Settings contains only the new settings.
	 *
	 * @param settingsStream
	 * 		InputStream where the properties are read.
	 * @throws SettingsError
	 * 		If loading the settings didn't succeed
	 */
	public static void initFromStream(final InputStream settingsStream)
	throws SettingsError {
		props = new Properties();
		try {
			props.load(settingsStream);
		} catch (IOException e) {
			throw new SettingsError(e);
		}

		String outFile = props.getProperty(SETTING_OUTPUT_S);
		if (outFile != null) {
			if (outFile.trim().length() == 0) {
				out = System.out;
			} else {
				try {
					out = new PrintStream(new File(outFile));
				} catch (FileNotFoundException e) {
					throw new SettingsError("Can't open Settings output file:" +
							e);
				}
			}
		}
	}

	/**
	 * Reads another settings file and adds the key-value pairs to the current
	 * settings overriding any values that already existed with the same keys.
	 * @param propFile Path to the property file
	 * @throws SettingsError If loading the settings file didn't succeed
	 * @see #init(String)
	 */
	public static void addSettings(String propFile) throws SettingsError {
		try {
			props.load(new FileInputStream(propFile));
		} catch (IOException e) {
			throw new SettingsError(e);
		}
	}

	/**
	 * Writes the given setting string to the settings output (if any)
	 * @param setting The string to write
	 */
	private static void outputSetting(String setting) {
		if (out != null && !writtenSettings.contains(setting)) {
			if (writtenSettings.size() == 0) {
				out.println("# Settings for run " + (runIndex + 1));
			}
			out.println(setting);
			writtenSettings.add(setting);
		}
	}

	/**
	 * Returns true if a setting with defined name (in the current namespace
	 * or secondary namespace if such is set) exists and has some value
	 * (not just white space)
	 * @param name Name of the setting to check
	 * @return True if the setting exists, false if not
	 */
	public boolean contains(String name) {
		try {
			String value = getSetting(name);
			if (value == null) {
				return false;
			}

			else return value.trim().length() > 0;
		}
		catch (SettingsError e) {
			return false; // didn't find the setting
		}
	}

	/**
	 * Returns full (namespace prefixed) property name for setting.
	 * @param name Name of the settings
	 * @param secondary If true, the secondary namespace is used.
	 * @return full (prefixed with current namespace) property name for setting
	 */
	private String getFullPropertyName(String name, boolean secondary) {
		String usedNamespace = (secondary ? secondaryNamespace : namespace);

		if (usedNamespace != null) {
			return usedNamespace + "." + name;
		}
		else {
			return name;
		}
	}

	/**
	 * Returns a String-valued setting. Setting is first looked from the
	 * namespace that is set (if any) and then from the secondary namespace
	 * (if any). All other getters use this method as their first step too
	 * (so all getters may throw SettingsError and look from both namespaces).
	 * @param name Name of the setting to get
	 * @return The contents of the setting in a String
	 * @throws SettingsError if the setting is not found from either one of
	 * the namespaces
	 */
	public String getSetting(String name) {
		String fullPropName;
		if (props == null) {
			init(null);
		}
		fullPropName = getFullPropertyName(name, false);
		String value = props.getProperty(fullPropName);

		if (value != null) { // found value, check if run setting can be parsed
			value = parseRunSetting(value.trim());
		}

		if ((value == null || value.length() == 0) &&
				this.secondaryNamespace != null) {
			// try secondary namespace if the value wasn't found from primary
			fullPropName = getFullPropertyName(name, true);
			value = props.getProperty(fullPropName);

			if (value != null) {
				value = parseRunSetting(value.trim());
			}
		}

		if (value == null || value.length() == 0) {
			throw new SettingsError("Can't find setting " +
					getPropertyNamesString(name));
		}

		outputSetting(fullPropName + " = " + value);
		return value;
	}

	/**
	 * Returns the given setting if it exists, or defaultValue if the setting
	 * does not exist
	 * @param name The name of the setting
	 * @param defaultValue The value to return if the given setting didn't exist
	 * @return The setting value or the default value if the setting didn't
	 * exist
	 */
	public String getSetting(String name, String defaultValue) {
		if (!contains(name)) {
			return defaultValue;
		} else {
			return getSetting(name);
		}
	}

	/**
	 * Parses run-specific settings from a String value
	 * @param value The String to parse
	 * @return The runIndex % arrayLength'th value of the run array
	 */
	private static String parseRunSetting(String value) {
		final String RUN_ARRAY_START = "[";
		final String RUN_ARRAY_END = "]";
		final String RUN_ARRAY_DELIM = ";";
		final int MIN_LENGTH = 3; // minimum run is one value. e.g. "[v]"

		if (!value.startsWith(RUN_ARRAY_START) ||
			!value.endsWith(RUN_ARRAY_END) ||
			runIndex < 0 ||
			value.length() < MIN_LENGTH) {
			return value; // standard format setting -> return
		}

		value = value.substring(1,value.length()-1); // remove brackets
		String[] valueArr = value.split(RUN_ARRAY_DELIM);
		int arrIndex = runIndex % valueArr.length;
		value = valueArr[arrIndex].trim();

		return value;
	}

	/**
	 * Returns the setting name appended to namespace name(s) on a String
	 * (for error messages)
	 * @param name Name of the setting
	 * @return the setting name appended to namespace name(s) on a String
	 */
	private String getPropertyNamesString(String name) {
		if (this.secondaryNamespace != null) {
			return "'"+ this.secondaryNamespace + "." + name + "' nor '" +
				this.namespace + "." + name + "'";
		}
		else if (this.namespace != null){
			return "'" + this.namespace + "." + name + "'";
		}
		else {
			return "'" + name + "'";
		}
	}

	/**
	 * Returns a double-valued setting
	 * @param name Name of the setting to get
	 * @return Value of the setting as a double
	 */
	public double getDouble(String name) {
		return parseDouble(getSetting(name), name);
	}

	/**
	 * Returns a double-valued setting, or the default value if the given
	 * setting does not exist
	 * @param name Name of the setting to get
	 * @param defaultValue The value to return if the setting doesn't exist
	 * @return Value of the setting as a double (or the default value)
	 */
	public double getDouble(String name, double defaultValue) {
		return parseDouble(getSetting(name, ""+defaultValue), name);
	}

	/**
	 * Parses a double value from a String valued setting. Supports
	 * kilo (k), mega (M) and giga (G) suffixes.
	 * @param value String value to parse
	 * @param setting The setting where this value was from (for error msgs)
	 * @return The value as a double
	 * @throws SettingsError if the value wasn't a numeric value
	 * (or the suffix wasn't recognized)
	 */
	private double parseDouble(String value, String setting) {
		double number;
		int multiplier = getMultiplier(value);

		if (multiplier > 1) { // take the suffix away before parsing
			value = value.replaceAll("[^\\d.]","");
			//replaceAll removes everything which is not a digit or point
		}

		try {
			number = Double.parseDouble(value) * multiplier;
		} catch (NumberFormatException e) {
			throw new SettingsError("Invalid numeric setting '" + value +
					"' for '" + setting +"'\n" + e.getMessage());
		}
		return number;
	}

	/**
	 * Parses a long value from a String valued setting. Supports
	 * kilo (k), mega (M) and giga (G) suffixes.
	 * @param value String value to parse
	 * @param setting The setting where this value was from (for error msgs)
	 * @return The value as a long
	 * @throws SettingsError if the value wasn't a numeric value
	 * (or the suffix wasn't recognized)
	 */
	private long parseLong(String value, String setting) {
		long number;
		int multiplier = getMultiplier(value);

		if (multiplier > 1) { // take the suffix away before parsing
			value = value.replaceAll("[^\\d.]","");
		}

		try {
			number = Long.parseLong(value) * multiplier;
		} catch (NumberFormatException e) {
			throw new SettingsError("Invalid numeric setting '" + value +
					"' for '" + setting +"'\n" + e.getMessage());
		}
		return number;
	}

	/**
	 * Parses the multiplier suffix from numeric setting
	 * @param value The setting value
	 * @return The muliplier as a number
	 */
	private int getMultiplier(String value) {
		value = value.trim();

		if (value.endsWith("k")) {
			return 1000;
		}
		else if (value.endsWith("M")) {
			return 1000000;
		}
		else if (value.endsWith("G")) {
			return 1000000000;
		}
		else if (value.endsWith("kiB")) {
			//2^10
			return 1024;
		}
		else if (value.endsWith("MiB")) {
			//2^20
			return 1048576;
		}
		else if (value.endsWith("GiB")) {
			//2^30
			return 1073741824;
		} else {
			// no multiplier
			return 1;
		}
	}

	/**
	 * Returns a CSV setting. Value part of the setting must be a list of
	 * comma separated values. Whitespace between values is trimmed away.
	 * @param name Name of the setting
	 * @return Array of values that were comma-separated
	 * @throws SettingsError if something went wrong with reading
	 */
	public String[] getCsvSetting(String name) {
		ArrayList<String> values = new ArrayList<String>();
		String csv = getSetting(name);
		Scanner s = new Scanner(csv);
		s.useDelimiter(",");

		while (s.hasNext()) {
			values.add(s.next().trim());
		}

		return values.toArray(new String[0]);
	}

	/**
	 * Returns a CSV setting containing expected amount of values.
	 * Value part of the setting must be a list of
	 * comma separated values. Whitespace between values is trimmed away.
	 * @param name Name of the setting
	 * @param expectedCount how many values are expected
	 * @return Array of values that were comma-separated
	 * @throws SettingsError if something went wrong with reading or didn't
	 * read the expected amount of values.
	 */
	public String[] getCsvSetting(String name, int expectedCount) {
		String[] values = getCsvSetting(name);

		if (values.length != expectedCount) {
			throw new SettingsError("Read unexpected amount (" + values.length +
					") of comma separated values for setting '"
					+ name + "' (expected " + expectedCount + ")");
		}

		return values;
	}

	/**
	 * Returns an array of CSV setting double values containing expected
	 * amount of values.
	 * @param name Name of the setting
	 * @param expectedCount how many values are expected
	 * @return Array of values that were comma-separated
	 * @see #getCsvSetting(String, int)
	 */
	public double[] getCsvDoubles(String name, int expectedCount) {
		return parseDoubles(getCsvSetting(name, expectedCount),name);
	}

	/**
	 * Returns an array of CSV setting double values.
	 * @param name Name of the setting
	 * @return Array of values that were comma-separated
	 * @see #getCsvSetting(String)
	 */
	public double[] getCsvDoubles(String name) {
		return parseDoubles(getCsvSetting(name), name);
	}

	/**
	 * Parses a double array out of a String array
	 * @param strings The array of strings containing double values
	 * @param name Name of the setting
	 * @return Array of double values parsed from the string values
	 */
	private double[] parseDoubles(String[] strings, String name) {
		double[] values = new double[strings.length];
		for (int i=0; i<values.length; i++) {
			values[i] = parseDouble(strings[i], name);
		}
		return values;
	}

	/**
	 * Returns an array of CSV setting integer values
	 * @param name Name of the setting
	 * @param expectedCount how many values are expected
	 * @return Array of values that were comma-separated
	 * @see #getCsvSetting(String, int)
	 */
	public int[] getCsvInts(String name, int expectedCount) {
		return convertToInts(getCsvDoubles(name, expectedCount), name);
	}

	/**
	 * Returns an array of CSV setting integer values
	 * @param name Name of the setting
	 * @return Array of values that were comma-separated
	 * @see #getCsvSetting(String, int)
	 */
	public int[] getCsvInts(String name) {
		return convertToInts(getCsvDoubles(name), name);
	}

	/**
	 * Returns comma-separated ranges (e.g., "3-5, 17-20, 15")
	 * @param name Name of the setting
	 * @return Array of ranges that were comma-separated in the setting
	 * @throws SettingsError if something went wrong with reading
	 */
	public Range[] getCsvRanges(String name) {
		String[] strRanges = getCsvSetting(name);
		Range[] ranges = new Range[strRanges.length];

		try {
			for (int i=0; i < strRanges.length; i++) {
				ranges[i] = new Range(strRanges[i]);
			}
		} catch (NumberFormatException nfe) {
			throw new SettingsError("Invalid numeric range value in " +
					name, nfe);
		}

		return ranges;
	}

	/**
	 * Returns an integer-valued setting
	 * @param name Name of the setting to get
	 * @return Value of the setting as an integer
	 */
	public int getInt(String name) {
		return convertToInt(getDouble(name), name);
	}

	/**
	 * Returns a long-valued setting
	 * @param name Name of the setting to get
	 * @return Value of the setting as an integer
	 */
	public long getLong(String name) {
		return parseLong(getSetting(name), name);
	}

	/**
	 * Returns an integer-valued setting, or the default value if the
	 * setting does not exist
	 * @param name Name of the setting to get
	 * @param defaultValue The value to return if the setting didn't exist
	 * @return Value of the setting as an integer
	 */
	public int getInt(String name, int defaultValue) {
		return convertToInt(getDouble(name, defaultValue), name);
	}

	/**
	 * Converts a double value that is supposedly equal to an integer value
	 * to an integer value.
	 * @param doubleValue The double value to convert
	 * @param name Name of the setting where this value is from (for
	 * SettingsError)
	 * @return The integer value
	 * @throws SettingsError if the double value was not equal to any integer
	 * value
	 */
	private int convertToInt(double doubleValue, String name) {
		int number = (int)doubleValue;

		if (number != doubleValue) {
			throw new SettingsError("Expected integer value for setting '" +
					name + "' " + " got '" + doubleValue + "'");
		}
		return number;
	}

	/**
	 * Converts an array of double values to int values using
	 * {@link #convertToInt(double, String)}.
	 * @param doubleValues The double valued array
	 * @param name Name of the setting where this value is from (for
	 * SettingsError)
	 * @return Array of integer values
	 * @see #convertToInt(double, String)
	 */
	private int[] convertToInts(double [] doubleValues, String name) {
		int[] values = new int[doubleValues.length];
		for (int i=0; i<values.length; i++) {
			values[i] = convertToInt(doubleValues[i], name);
		}
		return values;
	}

	/**
	 * Returns a boolean-valued setting
	 * @param name Name of the setting to get
	 * @return True if the settings value was either "true" (case ignored)
	 *  or "1", false is the settings value was either "false" (case ignored)
	 *  or "0".
	 *  @throws SettingsError if the value wasn't any recognized value
	 *  @see #getSetting(String)
	 */
	public boolean getBoolean(String name) {
		String stringValue = getSetting(name);
		boolean value;

		if (stringValue.equalsIgnoreCase("true") ||
				stringValue.equals("1")) {
			value = true;
		}
		else if (stringValue.equalsIgnoreCase("false") ||
				stringValue.equals("0")) {
			value = false;
		}
		else {
			throw new SettingsError("Not a boolean value: '"+stringValue+
					"' for setting " + name);
		}

		return value;
	}

	/**
	 * Returns the given boolean setting if it exists, or defaultValue if the
	 * setting does not exist
	 * @param name The name of the setting
	 * @param defaultValue The value to return if the given setting didn't exist
	 * @return The setting value or the default value if the setting didn't
	 * exist
	 */
	public boolean getBoolean(String name, boolean defaultValue) {
		if (!contains(name)) {
			return defaultValue;
		} else {
			return getBoolean(name);
		}
	}

	/**
	 * Returns an ArithmeticCondition setting. See {@link ArithmeticCondition}
	 * @param name Name of the setting to get
	 * @return ArithmeticCondition from the setting
	 * @throws SettingsError if the value wasn't a valid arithmetic expression
	 */
	public ArithmeticCondition getCondition(String name) {
		return new ArithmeticCondition(getSetting(name));
	}

	/**
	 * Creates (and dynamically loads the class of) an object that
	 * initializes itself using the settings in this Settings object
	 * (given as the only parameter to the constructor).
	 * @param className Name of the class of the object
	 * @return Initialized object
	 * @throws SettingsError if object couldn't be created
	 */
	public Object createIntializedObject(String className) {
		Class<?>[] argsClass = {Settings.class};
		Object[] args = {this};

		return loadObject(className, argsClass, args);
	}

	/**
	 * Creates (and dynamically loads the class of) an object using the
	 * constructor without any parameters.
	 * @param className Name of the class of the object
	 * @return Initialized object
	 * @throws SettingsError if object couldn't be created
	 */
	public Object createObject(String className) {
		return loadObject(className, null, null);
	}

	/**
	 * Dynamically loads and creates an object.
	 * @param className Name of the class of the object
	 * @param argsClass Class(es) of the argument(s) or null if no-argument
	 * constructor should be called
	 * @param args Argument(s)
	 * @return The new object
	 * @throws SettingsError if object couldn't be created
	 */
	private Object loadObject(String className, Class<?>[] argsClass,
			Object[] args) {
		Object o = null;
		Class<?> objClass = getClass(className);
		Constructor<?> constructor;

		try {
			if (argsClass != null) { // use a specific constructor
				constructor = objClass.getConstructor((Class[])argsClass);
				o = constructor.newInstance(args);
			}
			else { // call empty constructor
				o = objClass.newInstance();
			}
		} catch (SecurityException e) {
			e.printStackTrace();
			throw new SettingsError("Fatal exception " + e, e);
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
			throw new SettingsError("Fatal exception " + e, e);
		} catch (NoSuchMethodException e) {
			throw new SettingsError("Class '" + className +
					"' doesn't have a suitable constructor", e);
		} catch (InstantiationException e) {
			throw new SettingsError("Can't create an instance of '" +
					className + "'", e);
		} catch (IllegalAccessException e) {
			e.printStackTrace();
			throw new SettingsError("Fatal exception " + e, e);
		} catch (InvocationTargetException e) {
			// this exception occurs if initialization of the object fails
			if (e.getCause() instanceof SettingsError) {
				throw (SettingsError)e.getCause();
			}
			else {
				e.printStackTrace();
				throw new SimError("Couldn't create settings-accepting object"+
					" for '" + className + "'\n" + "cause: " + e.getCause(), e);
			}
		}

		return o;
	}

	/**
	 * Returns a Class object for the name of class of throws SettingsError
	 * if such class wasn't found.
	 * @param name Full name of the class (including package name)
	 * @return A Class object of that class
	 * @throws SettingsError if such class wasn't found or couldn't be loaded
	 */
	private Class<?> getClass(String name) {
		String className = name;
		Class<?> c;

		try {
			c = Class.forName(className);
		} catch (ClassNotFoundException e) {
			throw new SettingsError("Couldn't find class '" + className + "'"+
					"\n" + e.getMessage(),e);
		}

		return c;
	}

	/**
	 * Fills a String formatted in a special way with values from Settings.
	 * String can contain (fully qualified) setting names surrounded by
	 * delimiters (see {@link #FILL_DELIMITER}). Values for those settings
	 * are retrieved and filled in the place of place holders.
	 * @param input The input string that may contain value requests
	 * @return A string filled with requested values (or the original string
	 * if no requests were found)
	 * @throws SettingsError if such settings were not found
	 */
	public String valueFillString(String input) {
		if (!input.contains(FILL_DELIMITER)) {
			return input;	// nothing to fill
		}

		Settings s = new Settings(); // don't use any namespace
		String result = "";
		Scanner scan = new Scanner(input);
		scan.useDelimiter(FILL_DELIMITER);

		if (input.startsWith(FILL_DELIMITER)) {
			result += s.getSetting(scan.next());
		}

		while(scan.hasNext()) {
			result += scan.next();
			if (!scan.hasNext()) {
				break;
			}
			result += s.getSetting(scan.next());
		}

		return result;
	}

	/**
	 * Returns a String representation of the stored settings
	 * @return a String representation of the stored settings
	 */
	public String toString() {
		return props.toString();
	}

}

src/core/DTNHost.java
public class DTNHost implements Comparable<DTNHost> {
	private static int count = 0;
	private int ID;

	private Coord destination;	// where is it going

	private MessageRouter router;
	private MovementEngine movementEngine;
	private MovementModel movementModel;
	private Path path;
	private double speed;
	public final String groupId;
	private String name;
	private List<MessageListener> msgListeners;
	private List<MovementListener> movListeners;
	private List<NetworkInterface> net;
	private ModuleCommunicationBus comBus;

	static {
		DTNSim.registerForReset(DTNHost.class.getCanonicalName());
		reset();
	}
	/**
	 * Creates a new DTNHost.
	 * @param msgLs Message listeners
	 * @param movLs Movement listeners
	 * @param groupId GroupID of this host
	 * @param interf List of NetworkInterfaces for the class
	 * @param comBus Module communication bus object
	 * @param mmProto Prototype of the movement model of this host
	 * @param mRouterProto Prototype of the message router of this host
	 */
	public DTNHost(List<MessageListener> msgLs,
			List<MovementListener> movLs,
			String groupId, List<NetworkInterface> interf,
			ModuleCommunicationBus comBus,
			MovementEngine me, MovementModel mmProto, MessageRouter mRouterProto) {
		this.comBus = comBus;
		this.ID = getNextID();
		this.groupId = groupId;
		this.name = groupId+ ID;
		this.net = new ArrayList<NetworkInterface>();

		for (NetworkInterface i : interf) {
			NetworkInterface ni = i.replicate();
			ni.setHost(this);
			net.add(ni);
		}

		// TODO - think about the names of the interfaces and the nodes
		//this.name = groupId + ((NetworkInterface)net.get(1)).getAddress();

		this.msgListeners = msgLs;
		this.movListeners = movLs;

		this.movementEngine = me;

		// create instances by replicating the prototypes
		this.movementModel = mmProto.replicate();
		this.movementModel.setComBus(comBus);
		this.movementModel.setHost(this);
		setRouter(mRouterProto.replicate());

		this.path = null;
	}

	/**
	 * Returns a new unique ID and increment the host count.
	 * @return The next ID.
	 */
	private synchronized static int getNextID() {
		return count++;
	}

	/**
	 * Reset the host and its interfaces
	 */
	public static void reset() {
		count = 0;
	}

	/**
	 * Returns true if this node is actively moving (false if not)
	 * @return true if this node is actively moving (false if not)
	 */
	public boolean isMovementActive() {
		return this.movementModel.isActive();
	}

	/**
	 * Returns true if this node's radio is active (false if not)
	 * @return true if this node's radio is active (false if not)
	 */
	public boolean isRadioActive() {
		// Radio is active if any of the network interfaces are active.
		for (final NetworkInterface i : this.net) {
			if (i.isActive()) return true;
		}
		return false;
	}

	/**
	 * Set a router for this host
	 * @param router The router to set
	 */
	private void setRouter(MessageRouter router) {
		router.init(this, msgListeners);
		this.router = router;
	}

	/**
	 * Returns the router of this host
	 * @return the router of this host
	 */
	public MessageRouter getRouter() {
		return this.router;
	}

	/**
	 * Returns the ID of this host.
	 */
	public int getID() {
		return this.ID;
	}

	/**
	 * Returns this hosts's ModuleCommunicationBus
	 * @return this hosts's ModuleCommunicationBus
	 */
	public ModuleCommunicationBus getComBus() {
		return this.comBus;
	}

    /**
	 * Informs the router of this host about state change in a connection
	 * object.
	 * @param con  The connection object whose state changed
	 */
	public void connectionUp(Connection con) {
		this.router.changedConnection(con);
	}

	public void connectionDown(Connection con) {
		this.router.changedConnection(con);
	}

	/**
	 * Returns a copy of the list of connections this host has with other hosts
	 * @return a copy of the list of connections this host has with other hosts
	 */
	public List<Connection> getConnections() {
		List<Connection> lc = new ArrayList<Connection>();

		for (NetworkInterface i : net) {
			lc.addAll(i.getConnections());
		}

		return lc;
	}

	/**
	 * Sets the host's location
	 * @param location The location to set
	 */
	public void setLocation(Coord location) {
		movementEngine.setLocation(ID, location);
	}

	/**
	 * Returns the current location of this host.
	 * @return The location
	 */
	public Coord getLocation() {
		return movementEngine.getLocation(ID);
	}

	/**
	 * Sets the Node's next destination and speed
	 * @param destination The destination to set
	 */
	public void setDestination(Coord destination, double speed) {
		if (destination == null) {
			this.destination = null;
			return;
		}
		this.destination = destination.clone();
		this.speed = speed;

		if (this.movListeners != null) {
			for (MovementListener l : this.movListeners) {
				l.newDestination(this, this.destination, this.speed);
			}
		}
	}

	/**
	 * Returns the current destination of this host.
	 * @return The destination
	 */
	public Coord getDestination() {
		return this.destination;
	}

	/**
	 * Returns the current speed of this host.
	 * @return The speed
	 */
	public double getSpeed() {
		return this.speed;
	}

	/**
	 * Sets the Path this node is currently traveling or null if no
	 * path is in use at the moment. (Node is waiting for new path or stationary)
	 * @return The path this node is traveling
	 */
	public void setPath(Path path) {
		this.path = path;
	}

	/**
	 * Returns the Path this node is currently traveling or null if no
	 * path is in use at the moment.
	 * @return The path this node is traveling
	 */
	public Path getPath() {
		return this.path;
	}

	/**
	 * Returns the movement model of this node
	 * @return The movement model of this node
	 */
	public MovementModel getMovementModel() {
		return this.movementModel;
	}

	/**
	 * Sets the Node's name overriding the default name (groupId + netAddress)
	 * @param name The name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * Returns the messages in a collection.
	 * @return Messages in a collection
	 */
	public Collection<Message> getMessageCollection() {
		return this.router.getMessageCollection();
	}

	/**
	 * Returns the number of messages this node is carrying.
	 * @return How many messages the node is carrying currently.
	 */
	public int getNrofMessages() {
		return this.router.getNrofMessages();
	}

	/**
	 * Returns the buffer occupancy percentage. Occupancy is 0 for empty
	 * buffer but can be over 100 if a created message is bigger than buffer
	 * space that could be freed.
	 * @return Buffer occupancy percentage
	 */
	public double getBufferOccupancy() {
		long bSize = router.getBufferSize();
		long freeBuffer = router.getFreeBufferSize();
		return 100*((bSize-freeBuffer)/(bSize * 1.0));
	}

	/**
	 * Returns routing info of this host's router.
	 * @return The routing info.
	 */
	public RoutingInfo getRoutingInfo() {
		return this.router.getRoutingInfo();
	}

	/**
	 * Returns the interface objects of the node
	 */
	public List<NetworkInterface> getInterfaces() {
		return net;
	}

	/**
	 * Find the network interface based on the index
	 */
	public NetworkInterface getInterface(int interfaceNo) {
		NetworkInterface ni = null;
		try {
			ni = net.get(interfaceNo-1);
		} catch (IndexOutOfBoundsException ex) {
			throw new SimError("No such interface: "+interfaceNo +
					" at " + this);
		}
		return ni;
	}

	/**
	 * Find the network interface based on the interfacetype
	 */
	protected NetworkInterface getInterface(String interfacetype) {
		for (NetworkInterface ni : net) {
			if (ni.getInterfaceType().equals(interfacetype)) {
				return ni;
			}
		}
		return null;
	}

	/**
	 * Force a connection event
	 */
	public void forceConnection(DTNHost anotherHost, String interfaceId,
			boolean up) {
		NetworkInterface ni;
		NetworkInterface no;

		if (interfaceId != null) {
			ni = getInterface(interfaceId);
			no = anotherHost.getInterface(interfaceId);

			assert (ni != null) : "Tried to use a nonexisting interfacetype "+interfaceId;
			assert (no != null) : "Tried to use a nonexisting interfacetype "+interfaceId;
		} else {
			ni = getInterface(1);
			no = anotherHost.getInterface(1);

			assert (ni.getInterfaceType().equals(no.getInterfaceType())) :
				"Interface types do not match.  Please specify interface type explicitly";
		}

		if (up) {
			ni.createConnection(no);
		} else {
			ni.disconnect(no);
		}
	}

	/**
	 * for tests only --- do not use!!!
	 */
	public void connect(DTNHost h) {
		if (DEBUG) Debug.p("WARNING: using deprecated DTNHost.connect" +
			"(DTNHost) Use DTNHost.forceConnection(DTNHost,null,true) instead");
		forceConnection(h,null,true);
	}

	/**
	 * Updates node's network layer and router.
	 * @param simulateConnections Should network layer be updated too
	 */
	public void update(boolean simulateConnections) {
		if (!isRadioActive()) {
			// Make sure inactive nodes don't have connections
			tearDownAllConnections();
			return;
		}

		if (simulateConnections) {
			for (NetworkInterface i : net) {
				i.update();
			}
		}
		this.router.update();
	}

	/**
	 * Tears down all connections for this host.
	 */
	private void tearDownAllConnections() {
		for (NetworkInterface i : net) {
			// Get all connections for the interface
			List<Connection> conns = i.getConnections();
			if (conns.size() == 0) continue;

			// Destroy all connections
			List<NetworkInterface> removeList =
				new ArrayList<NetworkInterface>(conns.size());
			for (Connection con : conns) {
				removeList.add(con.getOtherInterface(i));
			}
			for (NetworkInterface inf : removeList) {
				i.disconnect(inf);
			}
		}
	}

	/**
	 * Sends a message from this host to another host
	 * @param id Identifier of the message
	 * @param to Host the message should be sent to
	 */
	public void sendMessage(String id, DTNHost to) {
		this.router.sendMessage(id, to);
	}

	/**
	 * Start receiving a message from another host
	 * @param m The message
	 * @param from Who the message is from
	 * @return The value returned by
	 * {@link MessageRouter#receiveMessage(Message, DTNHost)}
	 */
	public int receiveMessage(Message m, DTNHost from) {
		int retVal = this.router.receiveMessage(m, from);

		if (retVal == MessageRouter.RCV_OK) {
			m.addNodeOnPath(this);	// add this node on the messages path
		}

		return retVal;
	}

	/**
	 * Requests for deliverable message from this host to be sent trough a
	 * connection.
	 * @param con The connection to send the messages trough
	 * @return True if this host started a transfer, false if not
	 */
	public boolean requestDeliverableMessages(Connection con) {
		return this.router.requestDeliverableMessages(con);
	}

	/**
	 * Informs the host that a message was successfully transferred.
	 * @param id Identifier of the message
	 * @param from From who the message was from
	 */
	public void messageTransferred(String id, DTNHost from) {
		this.router.messageTransferred(id, from);
	}

	/**
	 * Informs the host that a message transfer was aborted.
	 * @param id Identifier of the message
	 * @param from From who the message was from
	 * @param bytesRemaining Nrof bytes that were left before the transfer
	 * would have been ready; or -1 if the number of bytes is not known
	 */
	public void messageAborted(String id, DTNHost from, int bytesRemaining) {
		this.router.messageAborted(id, from, bytesRemaining);
	}

	/**
	 * Creates a new message to this host's router
	 * @param m The message to create
	 */
	public void createNewMessage(Message m) {
		this.router.createNewMessage(m);
	}

	/**
	 * Deletes a message from this host
	 * @param id Identifier of the message
	 * @param drop True if the message is deleted because of "dropping"
	 * (e.g. buffer is full) or false if it was deleted for some other reason
	 * (e.g. the message got delivered to final destination). This effects the
	 * way the removing is reported to the message listeners.
	 */
	public void deleteMessage(String id, boolean drop) {
		this.router.deleteMessage(id, drop);
	}

	/**
	 * Informs movement listeners about the initial location.
	 * Called by movement engine after setting the initial location.
	 */
	public void notifyInitialLocation() {
		if (movListeners != null) {
			for (MovementListener l : movListeners) {
				l.initialLocation(this, getLocation());
			}
		}
	}

	/**
	 * Returns a string presentation of the host.
	 * @return Host's name
	 */
	public String toString() {
		return name;
	}

	/**
	 * Checks if a host is the same as this host by comparing the object
	 * reference
	 * @param otherHost The other host
	 * @return True if the hosts objects are the same object
	 */
	public boolean equals(DTNHost otherHost) {
		return this == otherHost;
	}

	/**
	 * Compares two DTNHosts by their IDs.
	 * @see Comparable#compareTo(Object)
	 */
	public int compareTo(DTNHost h) {
		return this.getID() - h.getID();
	}

}

Answer: public ProphetRouterWithEstimation(Settings s) {

<think>
The snippet ends right after the Javadoc comment for the constructor of ProphetRouterWithEstimation. The next logical line is the constructor declaration itself, matching the pattern used in other router classes (e.g., calling the superclass constructor). Therefore the next line should be the constructor signature.
</think>
public ProphetRouterWithEstimation(Settings s) {