From c4d7f04d0da33d5ff530b716fd3d09532c6d3d58 Mon Sep 17 00:00:00 2001 From: Berack96 Date: Tue, 28 Jan 2025 15:03:42 +0100 Subject: [PATCH] Added Unavailable times - added event unavailable - added event start time - changed servernode logic for unavailability - changed simulation logic - added unavailable time to results - added unavailable statistics - fixed tests --- src/main/java/net/berack/upo/valpre/Main.java | 12 ++- .../berack/upo/valpre/rand/Distribution.java | 52 +++++++++++++ .../java/net/berack/upo/valpre/sim/Event.java | 40 +++++----- .../net/berack/upo/valpre/sim/ServerNode.java | 73 +++++++++++++----- .../net/berack/upo/valpre/sim/Simulation.java | 76 ++++++++++++++----- .../berack/upo/valpre/sim/stats/Result.java | 13 +++- .../upo/valpre/sim/stats/Statistics.java | 42 ++++++---- .../berack/upo/valpre/sim/TestSimulation.java | 29 +++---- 8 files changed, 247 insertions(+), 90 deletions(-) diff --git a/src/main/java/net/berack/upo/valpre/Main.java b/src/main/java/net/berack/upo/valpre/Main.java index f919f20..00df773 100644 --- a/src/main/java/net/berack/upo/valpre/Main.java +++ b/src/main/java/net/berack/upo/valpre/Main.java @@ -29,13 +29,21 @@ public class Main { csv = arguments.get("csv"); var parallel = arguments.containsKey("p"); + // varius distributions + var distrExp = new Distribution.Exponential(lambda); + var distrNorm = new Distribution.NormalBoxMuller(mu, sigma); + var distrUnav = new Distribution.UnavailableTime(0.2, distrNorm); + // Build the network var net = new Net(); - var node1 = ServerNode.createLimitedSource("Source", new Distribution.Exponential(lambda), total); - var node2 = ServerNode.createQueue("Queue", 1, new Distribution.NormalBoxMuller(mu, sigma)); + var node1 = ServerNode.createLimitedSource("Source", distrExp, total); + var node2 = ServerNode.createQueue("Queue", 1, distrNorm); + var node3 = ServerNode.createQueue("Queue Wait", 1, distrNorm, distrUnav); net.addNode(node1); net.addNode(node2); + net.addNode(node3); net.addConnection(node1, node2, 1.0); + net.addConnection(node2, node3, 1.0); net.normalizeWeights(); /// Run multiple simulations diff --git a/src/main/java/net/berack/upo/valpre/rand/Distribution.java b/src/main/java/net/berack/upo/valpre/rand/Distribution.java index 4d06cda..f598477 100644 --- a/src/main/java/net/berack/upo/valpre/rand/Distribution.java +++ b/src/main/java/net/berack/upo/valpre/rand/Distribution.java @@ -4,8 +4,34 @@ package net.berack.upo.valpre.rand; * Represents a probability distribution. */ public interface Distribution { + /** + * Return a sample from the distribution. + * + * @param rng The random number generator to use. + * @return A number given from the distribution. + */ public double sample(Rng rng); + /** + * Gets a positive sample from the distribution. + * This is useful if you need to generate a positive value from a distribution + * that can generate negative values. For example, the normal distribution. + * + * @param distribution The distribution to sample + * @param rng The random number generator to use. + * @return A positive or 0 value from the distribution. + */ + public static double getPositiveSample(Distribution distribution, Rng rng) { + if (distribution == null) + return 0; + + double sample; + do { + sample = distribution.sample(rng); + } while (sample < 0); + return sample; + } + /** * Represents an exponential distribution. */ @@ -172,4 +198,30 @@ public interface Distribution { return -Math.log(rng.random()) / lambdas[i]; } } + + /** + * TODO + */ + public static class UnavailableTime implements Distribution { + public final double probability; + public final Distribution distribution; + + /** + * TODO + * + * @param probability + * @param distribution + */ + public UnavailableTime(double probability, Distribution distribution) { + this.probability = probability; + this.distribution = distribution; + } + + @Override + public double sample(Rng rng) { + if (rng.random() < this.probability) + return Distribution.getPositiveSample(this.distribution, rng); + return 0.0; + } + } } diff --git a/src/main/java/net/berack/upo/valpre/sim/Event.java b/src/main/java/net/berack/upo/valpre/sim/Event.java index 68004e3..f605f3d 100644 --- a/src/main/java/net/berack/upo/valpre/sim/Event.java +++ b/src/main/java/net/berack/upo/valpre/sim/Event.java @@ -5,6 +5,7 @@ package net.berack.upo.valpre.sim; */ public class Event implements Comparable { public final double time; + public final double started; public final Type type; public final ServerNode node; @@ -15,10 +16,11 @@ public class Event implements Comparable { * @param node The node that the event is associated with. * @param time The time at which the event occurs. */ - private Event(Type type, ServerNode node, double time) { + private Event(Type type, ServerNode node, double now, double time) { this.type = type; this.time = time; this.node = node; + this.started = now; } @Override @@ -30,39 +32,40 @@ public class Event implements Comparable { return 1; } - /** - * Create a new event. - * - * @param node The node that the event is associated with. - * @param time The time at which the event occurs. - * @param type The type of event. - * - * @return The new event. - */ - public static Event newType(ServerNode node, double time, Type type) { - return new Event(type, node, time); - } - /** * Create a new arrival event. * * @param node The node that the event is associated with. + * @param now The time at which the event has been created. * @param time The time at which the event occurs. * @return The new event. */ - public static Event newArrival(ServerNode node, double time) { - return new Event(Type.ARRIVAL, node, time); + public static Event newArrival(ServerNode node, double now, double time) { + return new Event(Type.ARRIVAL, node, now, time); } /** * Create a new departure event. * * @param node The node that the event is associated with. + * @param now The time at which the event has been created. * @param time The time at which the event occurs. * @return The new event. */ - public static Event newDeparture(ServerNode node, double time) { - return new Event(Type.DEPARTURE, node, time); + public static Event newDeparture(ServerNode node, double now, double time) { + return new Event(Type.DEPARTURE, node, now, time); + } + + /** + * Create a new unavailable event. + * + * @param node The node that the event is associated with. + * @param now The time at which the event has been created. + * @param time The time at which the event occurs. + * @return The new event. + */ + public static Event newUnavailable(ServerNode node, double now, double time) { + return new Event(Type.UNAVAILABLE, node, now, time); } /** @@ -71,5 +74,6 @@ public class Event implements Comparable { public static enum Type { ARRIVAL, DEPARTURE, + UNAVAILABLE, } } diff --git a/src/main/java/net/berack/upo/valpre/sim/ServerNode.java b/src/main/java/net/berack/upo/valpre/sim/ServerNode.java index 63cb395..3ceff3c 100644 --- a/src/main/java/net/berack/upo/valpre/sim/ServerNode.java +++ b/src/main/java/net/berack/upo/valpre/sim/ServerNode.java @@ -11,7 +11,8 @@ public class ServerNode { public final String name; public final int maxServers; public final int spawnArrivals; - public final Distribution distribution; + public final Distribution service; + public final Distribution unavailable; /** * Creates a source node with the given name and distribution. @@ -23,7 +24,7 @@ public class ServerNode { * @return The created source node. */ public static ServerNode createSource(String name, Distribution distribution) { - return new ServerNode(name, Integer.MAX_VALUE, distribution, Integer.MAX_VALUE); + return new ServerNode(name, Integer.MAX_VALUE, distribution, null, Integer.MAX_VALUE); } /** @@ -31,25 +32,39 @@ public class ServerNode { * arrivals to spawn that are served by infinite servers (Integer.MAX_VALUE). * * @param name The name of the node. - * @param distribution The distribution of the inter-arrival times. + * @param service The distribution of the inter-arrival times. * @param spawnArrivals The number of arrivals to spawn. * @return The created source node. */ - public static ServerNode createLimitedSource(String name, Distribution distribution, int spawnArrivals) { - return new ServerNode(name, Integer.MAX_VALUE, distribution, spawnArrivals); + public static ServerNode createLimitedSource(String name, Distribution service, int spawnArrivals) { + return new ServerNode(name, Integer.MAX_VALUE, service, null, spawnArrivals); } /** * Creates a queue node with the given name, maximum number of servers, and * distribution. * - * @param name The name of the node. - * @param maxServers The maximum number of servers in the queue. - * @param distribution The distribution of the service times. + * @param name The name of the node. + * @param maxServers The maximum number of servers in the queue. + * @param service The distribution of the service times. * @return The created queue node. */ - public static ServerNode createQueue(String name, int maxServers, Distribution distribution) { - return new ServerNode(name, maxServers, distribution, 0); + public static ServerNode createQueue(String name, int maxServers, Distribution service) { + return new ServerNode(name, maxServers, service, null, 0); + } + + /** + * Creates a queue node with the given name, maximum number of servers, and + * distribution. + * + * @param name The name of the node. + * @param maxServers The maximum number of servers in the queue. + * @param service The distribution of the service times. + * @param unavailable The distribution of the unavailable times after service. + * @return The created queue node. + */ + public static ServerNode createQueue(String name, int maxServers, Distribution service, Distribution unavailable) { + return new ServerNode(name, maxServers, service, unavailable, 0); } /** @@ -60,13 +75,14 @@ public class ServerNode { * * @param name The name of the node. * @param maxServers The maximum number of servers in the queue. - * @param distribution The distribution of the service times. + * @param service The distribution of the service times. + * @param unavailable The distribution of the unavailable times after service. * @param spawnArrivals The number of arrivals to spawn. * @throws NullPointerException if the distribution is null */ - public ServerNode(String name, int maxServers, Distribution distribution, int spawnArrivals) { - if (distribution == null) - throw new NullPointerException("Distribution can't be null"); + private ServerNode(String name, int maxServers, Distribution service, Distribution unavailable, int spawnArrivals) { + if (service == null) + throw new NullPointerException("Service distribution can't be null"); if (maxServers <= 0) maxServers = 1; if (spawnArrivals < 0) @@ -74,8 +90,9 @@ public class ServerNode { this.name = name; this.maxServers = maxServers; - this.distribution = distribution; this.spawnArrivals = spawnArrivals; + this.service = service; + this.unavailable = unavailable; } /** @@ -86,12 +103,18 @@ public class ServerNode { * @param rng The random number generator to use. * @return A positive sample from the distribution. */ - public double getPositiveSample(Rng rng) { - double sample; - do { - sample = this.distribution.sample(rng); - } while (sample < 0); - return sample; + public double getServiceTime(Rng rng) { + return Distribution.getPositiveSample(this.service, rng); + } + + /** + * Return the unavailable time after a service + * + * @param rng The random number generator to use. + * @return A positive or 0 value from the distribution. + */ + public double getUnavailableTime(Rng rng) { + return Distribution.getPositiveSample(this.unavailable, rng); } /** @@ -105,6 +128,14 @@ public class ServerNode { return this.spawnArrivals > Math.max(0, numArrivals); } + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ServerNode)) + return false; + var other = (ServerNode) obj; + return obj.hashCode() == other.hashCode(); + } + @Override public int hashCode() { return this.name.hashCode(); diff --git a/src/main/java/net/berack/upo/valpre/sim/Simulation.java b/src/main/java/net/berack/upo/valpre/sim/Simulation.java index 31a4c75..79e7389 100644 --- a/src/main/java/net/berack/upo/valpre/sim/Simulation.java +++ b/src/main/java/net/berack/upo/valpre/sim/Simulation.java @@ -26,7 +26,7 @@ public final class Simulation { * Creates a new run of the simulation with the given nodes and random number * generator. * - * @param states The nodes in the network. + * @param states The nodes in the network. * @param rng The random number generator to use. * @param criterias when the simulation has to end. */ @@ -75,21 +75,18 @@ public final class Simulation { switch (event.type) { case ARRIVAL -> { state.queue.add(this.time); - state.stats.updateArrival(this.time, state.queue.size(), state.numServerBusy != 0); - - if (state.numServerBusy < node.maxServers) { - state.numServerBusy++; - this.addDeparture(node); - } + state.stats.updateArrival(event, state.queue.size()); + this.addDeparture(node, state); } case DEPARTURE -> { - var startService = state.queue.poll(); - state.stats.updateDeparture(this.time, this.time - startService); + var arrivalTime = state.queue.poll(); + state.stats.updateDeparture(event, arrivalTime); + state.numServerBusy--; - if (state.numServerBusy > state.queue.size()) { - state.numServerBusy--; + if (this.addUnavailable(node)) { + state.numServerUnavailable++; } else { - this.addDeparture(node); + this.addDeparture(node, state); } var next = this.net.getChildOf(node, this.rng); @@ -100,6 +97,11 @@ public final class Simulation { this.addArrival(node); } } + case UNAVAILABLE -> { + state.numServerUnavailable--; + state.stats.updateUnavailable(event); + this.addDeparture(node, state); + } } } @@ -143,20 +145,42 @@ public final class Simulation { * @param node The node to create the event for. */ public void addArrival(ServerNode node) { - var event = Event.newArrival(node, this.time); + var event = Event.newArrival(node, this.time, this.time); fel.add(event); } /** * Adds a departure event to the future event list. The event is created based - * on the given node, and the delay is determined by the node's distribution. + * on the given node, and the delay is determined by the node's service + * distribution. * * @param node The node to create the event for. */ - public void addDeparture(ServerNode node) { - var delay = node.getPositiveSample(this.rng); - var event = Event.newDeparture(node, this.time + delay); - fel.add(event); + public void addDeparture(ServerNode node, NodeState state) { + if (state.canServeRequest(node)) { + state.numServerBusy++; + var delay = node.getServiceTime(this.rng); + var event = Event.newDeparture(node, this.time, this.time + delay); + fel.add(event); + } + } + + /** + * Adds an unavailable event to the future event list if the delay is > 0. + * The event is created based on the given node, and the delay is determined by + * the node's unavailability distribution. + * + * @param node The node to create the event for. + * @return if the event has been added to the FEL. + */ + public boolean addUnavailable(ServerNode node) { + var delay = node.getUnavailableTime(rng); + if (delay > 0) { + var event = Event.newUnavailable(node, this.time, this.time + delay); + fel.add(event); + return true; + } + return false; } /** @@ -183,7 +207,21 @@ public final class Simulation { */ public static class NodeState { public int numServerBusy = 0; + public int numServerUnavailable = 0; public final Statistics stats = new Statistics(); - private final ArrayDeque queue = new ArrayDeque<>(); + public final ArrayDeque queue = new ArrayDeque<>(); + + /** + * TODO + * + * @param node + * @return + */ + public boolean canServeRequest(ServerNode node) { + var totalOccupied = this.numServerBusy + this.numServerUnavailable; + var canServe = node.maxServers > totalOccupied; + var hasRequests = this.numServerBusy < this.queue.size(); + return canServe && hasRequests; + } } } \ No newline at end of file diff --git a/src/main/java/net/berack/upo/valpre/sim/stats/Result.java b/src/main/java/net/berack/upo/valpre/sim/stats/Result.java index 711e154..1ade4a2 100644 --- a/src/main/java/net/berack/upo/valpre/sim/stats/Result.java +++ b/src/main/java/net/berack/upo/valpre/sim/stats/Result.java @@ -31,7 +31,7 @@ public class Result { this.simulationTime = time; this.timeElapsedMS = elapsed; this.nodes = nodes; - this.size = (int) Math.ceil(Math.log10(this.simulationTime)); + this.size = (int) Math.ceil(Math.max(Math.log10(this.simulationTime), 1)); this.iFormat = "%" + this.size + ".0f"; this.fFormat = "%" + (this.size + 4) + ".3f"; } @@ -56,8 +56,8 @@ public class Result { * the statistics for each node in the network. */ public String getSummary() { - String[] h = { "Node", "Departures", "Avg Queue", "Avg Wait", "Avg Response", "Throughput", "Utilization %", - "Last Event" }; + String[] h = { "Node", "Departures", "Avg Queue", "Avg Wait", "Avg Unavailable", "Avg Response", "Throughput", + "Utilization %", "Last Event" }; var table = new ConsoleTable(h); for (var entry : this.nodes.entrySet()) { @@ -67,6 +67,7 @@ public class Result { iFormat.formatted(stats.numDepartures), fFormat.formatted(stats.avgQueueLength), fFormat.formatted(stats.avgWaitTime), + fFormat.formatted(stats.avgUnavailableTime), fFormat.formatted(stats.avgResponse), fFormat.formatted(stats.troughput), fFormat.formatted(stats.utilization * 100), @@ -86,7 +87,7 @@ public class Result { if (tableHeader) builder.append( - "Seed,Node,Arrivals,Departures,MaxQueue,AvgQueue,AvgWait,AvgResponse,BusyTime,WaitTime,ResponseTime,LastEventTime,Throughput,Utilization\n"); + "Seed,Node,Arrivals,Departures,MaxQueue,AvgQueue,AvgWait,AvgUnavailable,AvgResponse,BusyTime,WaitTime,UnavailableTime,ResponseTime,LastEventTime,Throughput,Utilization\n"); for (var entry : this.nodes.entrySet()) { var stats = entry.getValue(); builder.append(this.seed); @@ -103,12 +104,16 @@ public class Result { builder.append(','); builder.append(stats.avgWaitTime); builder.append(','); + builder.append(stats.avgUnavailableTime); + builder.append(','); builder.append(stats.avgResponse); builder.append(','); builder.append(stats.busyTime); builder.append(','); builder.append(stats.waitTime); builder.append(','); + builder.append(stats.unavailableTime); + builder.append(','); builder.append(stats.responseTime); builder.append(','); builder.append(stats.lastEventTime); diff --git a/src/main/java/net/berack/upo/valpre/sim/stats/Statistics.java b/src/main/java/net/berack/upo/valpre/sim/stats/Statistics.java index a84286b..f326e46 100644 --- a/src/main/java/net/berack/upo/valpre/sim/stats/Statistics.java +++ b/src/main/java/net/berack/upo/valpre/sim/stats/Statistics.java @@ -3,6 +3,8 @@ package net.berack.upo.valpre.sim.stats; import java.util.function.BiFunction; import java.util.function.Function; +import net.berack.upo.valpre.sim.Event; + /** * TODO */ @@ -11,12 +13,14 @@ public class Statistics { public double numDepartures = 0.0d; public double maxQueueLength = 0.0d; public double avgQueueLength = 0.0d; + public double unavailableTime = 0.0d; public double busyTime = 0.0d; public double waitTime = 0.0d; public double responseTime = 0.0d; public double lastEventTime = 0.0d; // derived stats, you can calculate them even at the end + public double avgUnavailableTime = 0.0d; public double avgWaitTime = 0.0d; public double avgResponse = 0.0d; public double troughput = 0.0d; @@ -25,39 +29,49 @@ public class Statistics { /** * TODO * - * @param time + * @param event * @param newQueueSize - * @param updateBusy */ - public void updateArrival(double time, double newQueueSize, boolean updateBusy) { + public void updateArrival(Event event, double newQueueSize) { var total = this.avgQueueLength * this.numArrivals; this.numArrivals++; this.avgQueueLength = (total + newQueueSize) / this.numArrivals; this.maxQueueLength = Math.max(this.maxQueueLength, newQueueSize); - if (updateBusy) - this.busyTime += time - this.lastEventTime; - this.lastEventTime = time; + this.lastEventTime = event.time; } /** * TODO * - * @param time + * @param event * @param response */ - public void updateDeparture(double time, double response) { + public void updateDeparture(Event event, double arrivalTime) { this.numDepartures++; - this.responseTime += response; - this.busyTime += time - this.lastEventTime; - this.lastEventTime = time; + this.responseTime += event.time - arrivalTime; + this.busyTime += event.time - event.started; this.waitTime = this.responseTime - this.busyTime; this.avgWaitTime = this.waitTime / this.numDepartures; this.avgResponse = this.responseTime / this.numDepartures; - this.troughput = this.numDepartures / this.lastEventTime; - this.utilization = this.busyTime / this.lastEventTime; + this.troughput = this.numDepartures / event.time; + this.utilization = this.busyTime / event.time; + + this.lastEventTime = event.time; + } + + /** + * TODO + * + * @param event + */ + public void updateUnavailable(Event event) { + this.unavailableTime += event.time - event.started; + this.avgUnavailableTime = this.unavailableTime / event.time; + + this.lastEventTime = event.time; } /** @@ -106,11 +120,13 @@ public class Statistics { save.avgQueueLength = func.apply(val1.avgQueueLength, val2.avgQueueLength); save.busyTime = func.apply(val1.busyTime, val2.busyTime); save.responseTime = func.apply(val1.responseTime, val2.responseTime); + save.unavailableTime = func.apply(val1.unavailableTime, val2.unavailableTime); save.waitTime = func.apply(val1.waitTime, val2.waitTime); save.lastEventTime = func.apply(val1.lastEventTime, val2.lastEventTime); // derived stats save.avgWaitTime = func.apply(val1.avgWaitTime, val2.avgWaitTime); save.avgResponse = func.apply(val1.avgResponse, val2.avgResponse); + save.avgUnavailableTime = func.apply(val1.avgUnavailableTime, val2.avgUnavailableTime); save.troughput = func.apply(val1.troughput, val2.troughput); save.utilization = func.apply(val1.utilization, val2.utilization); } diff --git a/src/test/java/net/berack/upo/valpre/sim/TestSimulation.java b/src/test/java/net/berack/upo/valpre/sim/TestSimulation.java index 2c55f32..f223424 100644 --- a/src/test/java/net/berack/upo/valpre/sim/TestSimulation.java +++ b/src/test/java/net/berack/upo/valpre/sim/TestSimulation.java @@ -14,7 +14,7 @@ public class TestSimulation { private static double DELTA = 0.0000001; private static Rng rigged = new RiggedRng(); private static Distribution const0 = new Constant(0.0); - private static Distribution const1 = new Constant(0.0); + private static Distribution const1 = new Constant(1.0); private final static class RiggedRng extends Rng { @Override @@ -38,7 +38,7 @@ public class TestSimulation { @Test public void serverNode() { - var node = new ServerNode("Nodo", 0, const1, 0); + var node = ServerNode.createQueue("Nodo", 0, const1); assertEquals("Nodo", node.name); assertEquals(1, node.maxServers); assertFalse(node.shouldSpawnArrival(0)); @@ -46,7 +46,7 @@ public class TestSimulation { assertFalse(node.shouldSpawnArrival(1000)); assertFalse(node.shouldSpawnArrival(Integer.MAX_VALUE)); assertFalse(node.shouldSpawnArrival(-1)); - assertEquals(1.0, node.getPositiveSample(null), DELTA); + assertEquals(1.0, node.getServiceTime(null), DELTA); node = ServerNode.createQueue("Queue", 50, const1); assertEquals("Queue", node.name); @@ -56,7 +56,7 @@ public class TestSimulation { assertFalse(node.shouldSpawnArrival(1000)); assertFalse(node.shouldSpawnArrival(Integer.MAX_VALUE)); assertFalse(node.shouldSpawnArrival(-1)); - assertEquals(1.0, node.getPositiveSample(null), DELTA); + assertEquals(1.0, node.getServiceTime(null), DELTA); node = ServerNode.createSource("Source", const1); assertEquals("Source", node.name); @@ -67,7 +67,7 @@ public class TestSimulation { assertTrue(node.shouldSpawnArrival(Integer.MAX_VALUE - 1)); assertFalse(node.shouldSpawnArrival(Integer.MAX_VALUE)); assertTrue(node.shouldSpawnArrival(-1)); - assertEquals(1.0, node.getPositiveSample(null), DELTA); + assertEquals(1.0, node.getServiceTime(null), DELTA); node = ServerNode.createLimitedSource("Source", const1, 50); assertEquals("Source", node.name); @@ -78,25 +78,28 @@ public class TestSimulation { assertFalse(node.shouldSpawnArrival(1000)); assertFalse(node.shouldSpawnArrival(Integer.MAX_VALUE)); assertTrue(node.shouldSpawnArrival(-1)); - assertEquals(1.0, node.getPositiveSample(null), DELTA); + assertEquals(1.0, node.getServiceTime(null), DELTA); } @Test public void event() { var node = ServerNode.createSource("Source", const0); - var event = Event.newType(node, 0, Event.Type.ARRIVAL); + var event = Event.newUnavailable(node, 0, 1); assertEquals(node, event.node); - assertEquals(0.0, event.time, 0.000000000001); - assertEquals(Event.Type.ARRIVAL, event.type); + assertEquals(0.0, event.started, 0.000000000001); + assertEquals(1.0, event.time, 0.000000000001); + assertEquals(Event.Type.UNAVAILABLE, event.type); - var event2 = Event.newArrival(node, 1.0); + var event2 = Event.newArrival(node, 1.0, 5.0); assertEquals(node, event2.node); - assertEquals(1.0, event2.time, 0.000000000001); + assertEquals(1.0, event2.started, 0.000000000001); + assertEquals(5.0, event2.time, 0.000000000001); assertEquals(Event.Type.ARRIVAL, event2.type); - var event3 = Event.newDeparture(node, 5.0); + var event3 = Event.newDeparture(node, 7.0, 8.0); assertEquals(node, event3.node); - assertEquals(5.0, event3.time, 0.000000000001); + assertEquals(7.0, event3.started, 0.000000000001); + assertEquals(8.0, event3.time, 0.000000000001); assertEquals(Event.Type.DEPARTURE, event3.type); assertEquals(0, event2.compareTo(event2));