diff --git a/.vscode/launch.json b/.vscode/launch.json
index fabc344..af5db31 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -30,7 +30,7 @@
"name": "Run Incremental",
"request": "launch",
"mainClass": "net.berack.upo.valpre.Main",
- "args": "simulation -net src/test/resources/example2.net -runs 1000 -i \"[Service1:throughput=0.98:0.01],[Service2:utilization=0.98:0.01],[Service2:unavailable=0.98:0.01]\""
+ "args": "simulation -net src/test/resources/example2.net -runs 1000 -indices \"[Service1:throughput=0.98:0.01],[Service2:utilization=0.98:0.01],[Service2:unavailable=0.98:0.01]\""
},
{
"type": "java",
diff --git a/README.md b/README.md
index f188c68..0a331c5 100644
--- a/README.md
+++ b/README.md
@@ -54,7 +54,7 @@ I percorsi che invece sono direttamente responsabili per la simulazione sono:
Nel jar sono presenti già 2 reti per fare degli esperimenti e/o testare se il tool funziona correttamente. Per poter vedere una run usando questi esempi basta far partire il tool in modalità interattiva e scegliere di caricare gli esempi.\
`java -jar upo-valpre.jar interactive`\
-Questa libreria è stata confrontata con il tool [JMT](https://jmt.sourceforge.net/Download.html) e le reti usate per fare il confronto si possono trovare sotto [le risorse del test](src/test/resources).\
+Questa libreria è stata confrontata con il tool [JMT](https://jmt.sourceforge.net/Download.html); le reti usate per fare il confronto si possono trovare sotto [le risorse del test](src/test/resources) e in esse ci sono anche i risultati ottenuti dalle run.\
Inoltre in alcune istanze state modificate le due reti di esempio in modo da mostrare cosa succede con l'aumento del numero di clienti nel sistema e cambiando la distribuzione di servizio di un nodo. Ogni valore ottenuto ha un sample di 1000 simulazioni. I risultati possono essere presi dal seguente [link](https://docs.google.com/spreadsheets/d/1yM1fvlpc2mIIpRe8M7_ry8m3DC3ZxNA204mM60O2hoQ/edit?usp=sharing)\
Le distribuzioni usate hanno tutte la stessa media μ:
- Normale(μ, 0.6)
@@ -67,13 +67,18 @@ Le distribuzioni usate hanno tutte la stessa media μ:
### Primo esempio
\
Il primo è `example1`; è una rete composta da una fonte di clienti (Source) che arrivano con tasso esponenziale (λ=0.222 e quindi media 4.5) e un centro di servizio (Queue) con tasso di servizio distribuito come una normale (μ=3.2, σ=0.6).\
-Se si effettua una simulazione si vedranno i risultati sulla console in questo modo:
+Se si effettua una simulazione si vedranno i risultati sulla console in questo modo:\

-Il tool JMT con la stessa rete produce i seguenti risultati che sono molto simili a quelli prodotti dalla libreria:\
-Queue Response Time = 7.3022 con un range [7.1456, 7.4589]\
-Queue Throughput = 0.2226 con un range [0.2182, 0.2271]\
-Queue Utilization = 0.7111 con un range [0.6959, 0.7262]
+O se si utilizza la parte grafica si possono vedere i risultati della Queue con l'incertezza:\
+
+
+Il tool JMT con la stessa rete produce i seguenti risultati che sono molto simili a quelli prodotti dalla libreria:
+```
+Response Time (Queue) 7.3022 con range [7.1456, 7.4589]
+Throughput (Queue) 0.2226 con range [0.2182, 0.2271]
+Utilization (Queue) 0.7111 con range [0.6959, 0.7262]
+```
Successivamente ho cambiato la distribuzione di servizio usata dal nodo "Queue".\
Come si può notare l'utilizzo e il throughput rimangono pressochè invariati tra le varie distribuzioni, ma convergono con l'aumentare dei clienti.\
@@ -85,19 +90,24 @@ Di seguito si può vedere il cambiamento del tempo medio di attesa, il numero me
### Secondo esempio
\
Il secondo esempio è `example2`; è una rete composta da una fonte di clienti (Source) che arrivano con tasso esponenziale (λ=1.5 e quindi media 0.666), un centro di servizio (Service1) con tasso di servizio distribuito come una esponenziale (λ=2.0 e quindi media 0.5) e un altro centro di servizio (Service2) con tasso di servizio distribuito come una esponenziale (λ=3.5 e quindi media 0.2857) e con un tempo di indisponibilità che viene attivato con probabilità 10% e distribuito con una eseponenziale (λ=10.0 e quindi media 0.1)\
-Se si effettua una simulazione si vedranno i risultati sulla console in questo modo:
+Se si effettua una simulazione si vedranno i risultati sulla console in questo modo:\

-Il tool JMT con la stessa rete produce i seguenti risultati che sono molto simili a quelli prodotti dalla libreria:\
-Service1 Response Time ~ 1.9866\
-Busy2 Response Time ~ 0.2825\
-Queue2 Response Time ~ 0.2279\
-Service1 Utilization ~ 0.7488\
-Calibration Number of Customers ~ 0.0150\
-Busy2 Number of Customers ~ 0.4279\
-Throughput ~ 1.5000
+O se si utilizza la parte grafica si possono vedere i risultati del Service2 con l'incertezza:\
+
+
+Il tool JMT con la stessa rete produce i seguenti risultati che sono molto simili a quelli prodotti dalla libreria:
+```
+Response Time (Service1) 2.0115 con range [1.9671, 2.0559]
+Response Time (Busy2) 0.2858 con range [0.2816, 0.2899]
+Response Time (Queue2) 0.2318 con range [0.2250, 0.2387]
+Utilization (Service1) 0.7472 con range [0.7410, 0.7534]
+Number of Customers (Busy2) 0.4285 con range [0.4248, 0.4322]
+Number of Customers (Calibration) 0.0149 con range [0.0148, 0.0151]
+Throughput del Sistema 1.5094 con range [1.5005, 1.5183]
+```
Successivamente ho cambiato la distribuzione di servizio usata dal nodo "Service2".\
-Anche in questo caso l'utilizzo e il throughput rimangono pressochè invariati e convergono con l'aumentare dei clienti nel sistema, ma cambiano il **numero medio della coda** e il **tempo medio di attesa**.\
+Anche in questo caso l'utilizzo e il throughput rimangono pressochè invariati e convergono con l'aumentare dei clienti nel sistema, ma cambiano il **numero medio della coda** e il **tempo medio di attesa**.\
Una particolarità di questa rete è il basso valore atteso per il tempo di servizio. Questo, in concomitanza con il resample in caso di valori negativi, fa si di aumentare la media della Distribuzione Normale. Nei grafici seguenti è stata inclusa la Normale solo nell'ultimo per mostrare la differenza rispetto le altre distribuzioni.\

diff --git a/image/README/1744786768084.png b/image/README/1744786768084.png
new file mode 100644
index 0000000..d98fba4
Binary files /dev/null and b/image/README/1744786768084.png differ
diff --git a/image/README/1744786814771.png b/image/README/1744786814771.png
new file mode 100644
index 0000000..b55fbeb
Binary files /dev/null and b/image/README/1744786814771.png differ
diff --git a/image/README/1744793029362.png b/image/README/1744793029362.png
new file mode 100644
index 0000000..6d14ff4
Binary files /dev/null and b/image/README/1744793029362.png differ
diff --git a/pom.xml b/pom.xml
index b0a0f94..3682dd2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -41,5 +41,10 @@
jfreechart
1.5.5
+
+ net.sourceforge.argparse4j
+ argparse4j
+ 0.9.0
+
\ No newline at end of file
diff --git a/src/main/java/net/berack/upo/valpre/InteractiveConsole.java b/src/main/java/net/berack/upo/valpre/InteractiveConsole.java
index 6d1b2b1..a013ec5 100644
--- a/src/main/java/net/berack/upo/valpre/InteractiveConsole.java
+++ b/src/main/java/net/berack/upo/valpre/InteractiveConsole.java
@@ -1,16 +1,15 @@
package net.berack.upo.valpre;
-import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Scanner;
-import java.util.concurrent.ExecutionException;
import java.util.function.Function;
import com.esotericsoftware.kryo.KryoException;
import net.berack.upo.valpre.rand.Distribution;
+import net.berack.upo.valpre.rand.Rng;
import net.berack.upo.valpre.sim.Net;
import net.berack.upo.valpre.sim.ServerNode;
@@ -137,16 +136,17 @@ public class InteractiveConsole {
/**
* Run the simulation with the net.
*/
- private void simpleRuns() throws InterruptedException, ExecutionException, IOException {
- var choice = choose("Choose what to do:", "100 Run", "1K Runs", "1K Runs + Plot");
+ private void simpleRuns() throws Exception {
+ var choice = choose("Choose what to do:", "100 Run", "1K Runs", "Run max 10K with confidence", "Back");
+ var seed = Rng.newSeed();
+ var csv = "run" + seed + ".csv";
switch (choice) {
- case 1 -> new SimulationBuilder(net).setMaxRuns(100).setParallel(true).run();
- case 2 -> new SimulationBuilder(net).setMaxRuns(1000).setParallel(true).run();
+ case 1 -> new SimulationBuilder(net).setSeed(seed).setMaxRuns(100).setParallel(true).setCsv(csv).run();
+ case 2 -> new SimulationBuilder(net).setSeed(seed).setMaxRuns(1000).setParallel(true).setCsv(csv).run();
case 3 -> {
- var randName = "rand" + System.currentTimeMillis() + ".csv";
- new SimulationBuilder(net).setMaxRuns(1000).setParallel(true).setCsv(randName).run();
- new Plot(randName).show();
- new File(randName).delete();
+ var indices = ask("Confidence indices with format [node:stat=confidence:relativeError];[..]\n");
+ new SimulationBuilder(net).setSeed(seed).setMaxRuns(10000).parseConfidenceIndices(indices).setCsv(csv)
+ .run();
}
default -> {
}
diff --git a/src/main/java/net/berack/upo/valpre/Main.java b/src/main/java/net/berack/upo/valpre/Main.java
index 8135468..f5833db 100644
--- a/src/main/java/net/berack/upo/valpre/Main.java
+++ b/src/main/java/net/berack/upo/valpre/Main.java
@@ -2,95 +2,93 @@ package net.berack.upo.valpre;
import java.io.File;
import java.net.URISyntaxException;
-import java.util.Arrays;
-import java.util.HashMap;
+import net.sourceforge.argparse4j.ArgumentParsers;
+import net.sourceforge.argparse4j.impl.Arguments;
+import net.sourceforge.argparse4j.inf.Namespace;
public class Main {
- public static void main(String[] args) {
- if (args.length == 0)
- exit("No program specified!");
+ private final static String NAME;
+ /**
+ * The name of the program, used for the help message.
+ */
+ static {
+ var name = "valpre";
try {
- var program = args[0];
- var subArgs = Arrays.copyOfRange(args, 1, args.length);
- switch (program) {
+ var uri = Main.class.getProtectionDomain().getCodeSource().getLocation().toURI();
+ name = new File(uri).getName();
+ } catch (URISyntaxException e) {
+ }
+ NAME = name;
+ }
+
+ /**
+ * The main method of the program. It parses the arguments and runs the
+ * simulation or the plotter.
+ *
+ * @param args the arguments to parse
+ */
+ public static void main(String[] args) {
+ try {
+ var param = Main.getParameters(args);
+ var command = param.getString("command");
+
+ switch (command) {
case "simulation" -> {
- var param = Main.getParameters(program, subArgs);
- new SimulationBuilder(param.get("net"))
- .setCsv(param.get("csv"))
- .setMaxRuns(param.getOrDefault("runs", Integer::parseInt, 100))
- .setSeed(param.getOrDefault("seed", Long::parseLong, 0L))
- .setParallel(param.get("p") != null)
- .parseEndCriteria(param.get("end"))
- .parseConfidenceIndices(param.get("i"))
+ new SimulationBuilder(param.getString("net"))
+ .setCsv(param.getString("csv"))
+ .setMaxRuns(param.getInt("runs"))
+ .setSeed(param.getLong("seed"))
+ .setParallel(param.getBoolean("p"))
+ .parseEndCriteria(param.getString("end"))
+ .parseConfidenceIndices(param.getString("indices"))
.run();
}
case "plot" -> {
- var param = Main.getParameters(program, subArgs);
- var csv = param.get("csv");
+ var csv = param.getString("csv");
var plot = new Plot(csv);
plot.show();
}
case "interactive" -> new InteractiveConsole().run();
- default -> exit("Invalid program!");
+ default -> throw new RuntimeException("Invalid program!"); // Should never happen
}
} catch (Exception e) {
- exit(e.getMessage());
- }
- }
-
- /**
- * Get the parameters from the arguments.
- *
- * @param program the program to run
- * @param args the arguments to parse
- * @return the parameters
- */
- private static Parameters getParameters(String program, String[] args) {
- var arguments = new HashMap();
- arguments.put("p", false);
- arguments.put("seed", true);
- arguments.put("runs", true);
- arguments.put("net", true);
- arguments.put("end", true);
- arguments.put("csv", true);
- arguments.put("i", true);
-
- var descriptions = new HashMap();
- descriptions.put("p", "Add this if you want the simulation to use threads (one each run).");
- descriptions.put("seed", "The seed of the simulation.");
- descriptions.put("runs", "How many runs the simulator should run.");
- descriptions.put("end", "When the simulation should end. Format is \"[ClassName:param1,..,paramN];[..]\"");
- descriptions.put("net", "The file net to use. Use example1.net or example2.net for the provided ones.");
- descriptions.put("i", "The confidence indices to use for the simulation. If active then p is ignored."
- + " Format is \"[node:stat=confidence:relativeError];[..]\"");
-
- var csvDesc = switch (program) {
- case "simulation" -> "The filename for saving every run statistics.";
- case "plot" -> "The filename that contains the previous saved runs.";
- default -> "";
- };
- descriptions.put("csv", csvDesc);
-
- return Parameters.getArgsOrHelper(args, "-", arguments, descriptions);
- }
-
- /**
- * Exit the program with an error message.
- */
- public static void exit(String message) {
- try {
- var uri = Main.class.getProtectionDomain().getCodeSource().getLocation().toURI();
- var name = new File(uri).getName();
- System.err.println(message);
- System.out.println("Usage: java -jar " + name + ".jar [simulation|plot|interactive] [args]");
- System.out.println("simulation args: -net [-csv ] [-runs ] [-seed ]"
- + "[-p] [-end ] [-i ]");
- System.out.println("plot args: -csv ");
- System.out.println("interactive: no args needed");
+ System.err.println(e.getMessage());
System.exit(1);
- } catch (URISyntaxException e) {
- e.printStackTrace();
}
}
+
+ /**
+ * Parses the arguments of the program. It uses the argparse4j library to parse
+ * the arguments and return a Namespace object with the parsed arguments.
+ *
+ * @param args the arguments to parse
+ * @return a Namespace object with the parsed arguments
+ */
+ private static Namespace getParameters(String[] args) {
+ var parser = ArgumentParsers.newFor(NAME).build()
+ .defaultHelp(true)
+ .description("Build a network simulation and/or plot the results of a simulation.");
+ var subparser = parser.addSubparsers().title("commands").description("valid commands").help("subcommand help");
+
+ var sim = subparser.addParser("simulation").help("Run a simulation of the network.");
+ sim.addArgument("-net").help("The file net to use.").required(true);
+ sim.addArgument("-csv").help("The filename for saving every run statistics.");
+ sim.addArgument("-runs").type(Integer.class).help("How many runs the simulator should run.").setDefault(100);
+ sim.addArgument("-seed").type(Long.class).help("The seed of the simulation.").setDefault(0L);
+ sim.addArgument("-p").action(Arguments.storeTrue()).help("Parallel (one thread each run).").setDefault(false);
+ sim.addArgument("-end").help("When the simulation should end. Format:\n\"[ClassName:param1,..,paramN];[..]\"");
+ sim.addArgument("-indices").help("The confidence indices to use for the simulation. If active -p is ignored."
+ + " Format:\n\"[node:stat=confidence:relativeError];[..]\"");
+
+ var plot = subparser.addParser("plot").help("Plot the results of a simulation.");
+ plot.addArgument("-csv").help("The filename for the csv file to plot.").required(true);
+
+ var _ = subparser.addParser("interactive").help("Run the interactive console.");
+ // Interactive console does not need any arguments
+
+ var namespace = parser.parseArgsOrFail(args);
+ namespace.getAttrs().put("command", args[0]);
+ return namespace;
+ }
}
\ No newline at end of file
diff --git a/src/main/java/net/berack/upo/valpre/NetExamples.java b/src/main/java/net/berack/upo/valpre/NetExamples.java
index 768daa5..d5bc27c 100644
--- a/src/main/java/net/berack/upo/valpre/NetExamples.java
+++ b/src/main/java/net/berack/upo/valpre/NetExamples.java
@@ -67,9 +67,6 @@ public final class NetExamples {
new double[] { 1 / (avg * 0.5), 1 / (avg * 1.5) },
new double[] { 0.5f, 0.5f });
- System.out.println("Normal: " + normal.mean);
- System.out.println("Uniform: " + uniform.min + " - " + uniform.max);
-
for (var spawn : spawnTotals) {
System.out.println("Spawn: " + spawn);
var nets = new Net[] {
diff --git a/src/main/java/net/berack/upo/valpre/Parameters.java b/src/main/java/net/berack/upo/valpre/Parameters.java
deleted file mode 100644
index 9c5eff4..0000000
--- a/src/main/java/net/berack/upo/valpre/Parameters.java
+++ /dev/null
@@ -1,194 +0,0 @@
-package net.berack.upo.valpre;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.function.Function;
-
-/**
- * Class that helps with parsing the parameters passed as input in the console.
- */
-public class Parameters {
- private final Map arguments;
- private final String prefix;
- private Map parameters;
-
- /**
- * Constructs a new Parameters object with the specified prefix and arguments.
- * The arguments can be with value, in that case in the map the boolean should
- * be true, otherwise it is only an argument that is a flag
- *
- * @param prefix the prefix to be used
- * @param arguments a map of arguments where the key is a string and if the
- * boolean is true then the argument expect a value
- * @throws IllegalArgumentException if the arguments map is null or empty
- */
- public Parameters(String prefix, Map arguments) {
- if (arguments == null || arguments.size() == 0)
- throw new IllegalArgumentException();
- this.arguments = arguments;
- this.prefix = prefix;
- }
-
- /**
- * Get the size of the parameters.
- *
- * @return the size of the parameters
- */
- public int size() {
- return this.parameters.size();
- }
-
- /**
- * Get the value of the argument passed as input.
- *
- * @param key the key of the argument
- * @return the value of the argument
- */
- public String get(String key) {
- if (this.parameters == null)
- return null;
- return this.parameters.get(key);
- }
-
- /**
- * Get the value from the arguments or the default value if it is not present.
- *
- * @param key The key to get the value from.
- * @param parse The function to parse the value.
- * @param value The default value if the key is not present.
- * @return The value from the arguments or the default value if it is not
- * present.
- */
- public T getOrDefault(String key, Function parse, T value) {
- var arg = this.get(key);
- return arg != null ? parse.apply(arg) : value;
- }
-
- /**
- * Return a string with the standard spaced enough
- *
- * @param eventualDescription the description for the argument, if not present
- * the argument will be shown anyway/
- * @return a string of arguments
- */
- public String helper(Map eventualDescription) {
- var size = 0;
- var parameters = new HashMap();
-
- for (var param : this.arguments.entrySet()) {
- var string = this.prefix + param.getKey();
- if (param.getValue())
- string += " ";
-
- parameters.put(param.getKey(), string);
- size = Math.max(size, string.length());
- }
- size += 2; // spacing
-
- var builder = new StringBuilder();
- for (var param : parameters.entrySet()) {
- var key = param.getKey();
- var args = param.getValue();
-
- builder.append(" ");
- builder.append(args);
-
- var desc = eventualDescription.get(key);
- if (desc != null) {
- builder.append(" ".repeat(size - args.length()));
- builder.append(desc);
- }
- builder.append("\n");
- }
-
- return builder.toString();
- }
-
- /**
- * Parse the arguments passed and build a map of Argument --> Value that can
- * be used to retrieve the information. In the case that the arguments are not
- * in the correct format then an exception is thrown.
- * To get the arguments use the {@link #get(String)} method.
- *
- * @param args the arguments in input
- * @throws IllegalArgumentException if the arguments are not formatted correctly
- * or if there is an unknown argument or there
- * are not arguments in the input
- */
- public void parse(String[] args) {
- if (args == null || args.length == 0)
- throw new IllegalArgumentException("No arguments passed");
-
- var result = new HashMap();
- for (var i = 0; i < args.length; i += 1) {
- var current = args[i];
- var next = i + 1 < args.length ? args[i + 1] : null;
-
- var updateI = this.parseSingle(current, next, result);
- if (updateI)
- i += 1;
- }
-
- this.parameters = result;
- }
-
- /**
- * Parse one single argument and put it into the map.
- *
- * @param current the current argument
- * @param next the next argument if present
- * @param result the map where to insert the value
- * @throws IllegalArgumentException if the arguments are not formatted correctly
- * or if there is an unknown argument
- * @return true if the next argument is used
- */
- private boolean parseSingle(String current, String next, Map result) {
- if (!current.startsWith(this.prefix))
- throw new IllegalArgumentException("Missing prefix [" + current + "]");
- current = current.substring(this.prefix.length());
-
- var value = this.arguments.get(current);
- if (value != null) {
- result.put(current, value ? next : "");
- return value;
- }
-
- var finalSize = result.size() + current.length();
- for (var letter : current.split(""))
- if (this.arguments.get(letter) != null)
- result.put(current, "");
-
- if (finalSize != result.size())
- throw new IllegalArgumentException("Unknown argument [" + current + "]");
- return false;
- }
-
- /**
- * Parse the arguments passed and returns a map of Argument --> Value that can
- * be used to retrieve the information. In the case that the arguments are not
- * in the correct format then an exception is thrown and the helper is printed.
- * If the arguments passed are 0 then the helper is printed.
- *
- * @param args the arguments in input
- * @param prefix the prefix to be used
- * @param arguments a map of arguments where the key is a string and if the
- * boolean is true then the argument expect a value
- * @param descriptions a map of descriptions for the arguments
- * @throws IllegalArgumentException if the arguments are not formatted correctly
- * or if there is an unknown argument
- * @return a map of the values
- */
- public static Parameters getArgsOrHelper(String[] args, String prefix, Map arguments,
- Map descriptions) {
-
- var param = new Parameters(prefix, arguments);
- try {
- param.parse(args);
- return param;
- } catch (IllegalArgumentException e) {
- System.out.println(e.getMessage());
- System.out.println(param.helper(descriptions));
- throw new IllegalArgumentException("Invalid arguments");
- }
- }
-}
diff --git a/src/main/java/net/berack/upo/valpre/Plot.java b/src/main/java/net/berack/upo/valpre/Plot.java
index 15c815c..471cd12 100644
--- a/src/main/java/net/berack/upo/valpre/Plot.java
+++ b/src/main/java/net/berack/upo/valpre/Plot.java
@@ -113,7 +113,7 @@ public class Plot {
var frame = new JFrame("Graph of the Simulation");
frame.add(rootPane);
frame.setSize(800, 600);
- frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setVisible(true);
});
}
diff --git a/src/main/java/net/berack/upo/valpre/rand/Rng.java b/src/main/java/net/berack/upo/valpre/rand/Rng.java
index 2a2cbd3..7b222f9 100644
--- a/src/main/java/net/berack/upo/valpre/rand/Rng.java
+++ b/src/main/java/net/berack/upo/valpre/rand/Rng.java
@@ -131,4 +131,13 @@ public class Rng {
var t = multiplier * (seed % Q) - R * (seed / Q);
return t > 0 ? t : (t + modulus);
}
+
+ /**
+ * This creates a new seed based on the current time.
+ *
+ * @return a new seed
+ */
+ public static long newSeed() {
+ return System.currentTimeMillis() % MODULUS;
+ }
}
diff --git a/src/main/java/net/berack/upo/valpre/rand/Rvgs.java b/src/main/java/net/berack/upo/valpre/rand/Rvgs.java
deleted file mode 100644
index c315d80..0000000
--- a/src/main/java/net/berack/upo/valpre/rand/Rvgs.java
+++ /dev/null
@@ -1,209 +0,0 @@
-package net.berack.upo.valpre.rand;
-
-/* --------------------------------------------------------------------------
- * This is a Java library for generating random variates from six discrete
- * distributions
- *
- * Generator Range (x) Mean Variance
- *
- * bernoulli(p) x = 0,1 p p*(1-p)
- * binomial(n, p) x = 0,...,n n*p n*p*(1-p)
- * equilikely(a, b) x = a,...,b (a+b)/2 ((b-a+1)*(b-a+1)-1)/12
- * Geometric(p) x = 0,... p/(1-p) p/((1-p)*(1-p))
- * pascal(n, p) x = 0,... n*p/(1-p) n*p/((1-p)*(1-p))
- * poisson(m) x = 0,... m m
- *
- * and seven continuous distributions
- *
- * uniform(a, b) a < x < b (a + b)/2 (b - a)*(b - a)/12
- * exponential(m) x > 0 m m*m
- * erlang(n, b) x > 0 n*b n*b*b
- * normal(m, s) all x m s*s
- * logNormal(a, b) x > 0 see below
- * chiSquare(n) x > 0 n 2*n
- * student(n) all x 0 (n > 1) n/(n - 2) (n > 2)
- *
- * For the a Lognormal(a, b) random variable, the mean and variance are
- *
- * mean = exp(a + 0.5*b*b)
- * variance = (exp(b*b) - 1) * exp(2*a + b*b)
- *
- * Name : Rvgs.java (Random Variate GeneratorS)
- * Authors : Steve Park & Dave Geyer
- * Translated by : Richard Dutton & Jun Wang
- * Language : Java
- * Latest Revision : 7-1-04
- * --------------------------------------------------------------------------
- */
-public class Rvgs {
-
- private final Rng rng;
-
- // public Rvgs() {
- // this.rngs = new Rngs(Rng.DEFAULT);
- // }
-
- public Rvgs(Rng rng) {
- if (rng == null)
- throw new NullPointerException();
- this.rng = rng;
- }
-
- /**
- * Returns 1 with probability p or 0 with probability 1 - p.
- * NOTE: use 0.0 < p < 1.0
- */
- public long bernoulli(double p) {
- return ((this.rng.random() < (1.0 - p)) ? 0 : 1);
- }
-
- /**
- * Returns a binomial distributed integer between 0 and n inclusive.
- * NOTE: use n > 0 and 0.0 < p < 1.0
- */
- public long binomial(long n, double p) {
- long i, x = 0;
-
- for (i = 0; i < n; i++)
- x += bernoulli(p);
- return (x);
- }
-
- /**
- * Returns an equilikely distributed integer between a and b inclusive.
- * NOTE: use a < b
- */
- public long equilikely(long a, long b) {
- return (a + (long) ((b - a + 1) * this.rng.random()));
- }
-
- /**
- * Returns a geometric distributed non-negative integer.
- * NOTE: use 0.0 < p < 1.0
- */
- public long geometric(double p) {
- return ((long) (Math.log(1.0 - this.rng.random()) / Math.log(p)));
- }
-
- /**
- * Returns a Pascal distributed non-negative integer.
- * NOTE: use n > 0 and 0.0 < p < 1.0
- */
- public long pascal(long n, double p) {
- long i, x = 0;
-
- for (i = 0; i < n; i++)
- x += geometric(p);
- return (x);
- }
-
- /**
- * Returns a Poisson distributed non-negative integer.
- * NOTE: use m > 0
- */
- public long poisson(double m) {
- double t = 0.0;
- long x = 0;
-
- while (t < m) {
- t += exponential(1.0);
- x++;
- }
- return (x - 1);
- }
-
- /**
- * Returns a uniformly distributed real number between a and b.
- * NOTE: use a < b
- */
- public double uniform(double a, double b) {
- return (a + (b - a) * this.rng.random());
- }
-
- /**
- * Returns an exponentially distributed positive real number.
- * NOTE: use m > 0.0
- */
- public double exponential(double m) {
- return (-m * Math.log(1.0 - this.rng.random()));
- }
-
- /**
- * Returns an Erlang distributed positive real number.
- * NOTE: use n > 0 and b > 0.0
- */
- public double erlang(long n, double b) {
- long i;
- double x = 0.0;
-
- for (i = 0; i < n; i++)
- x += exponential(b);
- return (x);
- }
-
- /**
- * Returns a normal (Gaussian) distributed real number.
- * NOTE: use s > 0.0
- *
- * Uses a very accurate approximation of the normal idf due to Odeh & Evans,
- * J. Applied Statistics, 1974, vol 23, pp 96-97.
- */
- public double normal(double m, double s) {
- final double p0 = 0.322232431088;
- final double q0 = 0.099348462606;
- final double p1 = 1.0;
- final double q1 = 0.588581570495;
- final double p2 = 0.342242088547;
- final double q2 = 0.531103462366;
- final double p3 = 0.204231210245e-1;
- final double q3 = 0.103537752850;
- final double p4 = 0.453642210148e-4;
- final double q4 = 0.385607006340e-2;
- double u, t, p, q, z;
-
- u = this.rng.random();
- if (u < 0.5)
- t = Math.sqrt(-2.0 * Math.log(u));
- else
- t = Math.sqrt(-2.0 * Math.log(1.0 - u));
- p = p0 + t * (p1 + t * (p2 + t * (p3 + t * p4)));
- q = q0 + t * (q1 + t * (q2 + t * (q3 + t * q4)));
- if (u < 0.5)
- z = (p / q) - t;
- else
- z = t - (p / q);
- return (m + s * z);
- }
-
- /**
- * Returns a lognormal distributed positive real number.
- * NOTE: use b > 0.0
- */
- public double logNormal(double a, double b) {
- return (Math.exp(a + b * normal(0.0, 1.0)));
- }
-
- /**
- * Returns a chi-square distributed positive real number.
- * NOTE: use n > 0
- */
- public double chiSquare(long n) {
- long i;
- double z, x = 0.0;
-
- for (i = 0; i < n; i++) {
- z = normal(0.0, 1.0);
- x += z * z;
- }
- return (x);
- }
-
- /**
- * Returns a student-t distributed real number.
- * NOTE: use n > 0
- */
- public double student(long n) {
- return (normal(0.0, 1.0) / Math.sqrt(chiSquare(n) / n));
- }
-
-}
diff --git a/src/main/java/net/berack/upo/valpre/sim/SimulationMultiple.java b/src/main/java/net/berack/upo/valpre/sim/SimulationMultiple.java
index 755b79d..dfc280d 100644
--- a/src/main/java/net/berack/upo/valpre/sim/SimulationMultiple.java
+++ b/src/main/java/net/berack/upo/valpre/sim/SimulationMultiple.java
@@ -146,5 +146,4 @@ public class SimulationMultiple {
stream.println(); // remove last printed line
return results;
}
-
}
diff --git a/src/main/java/net/berack/upo/valpre/sim/stats/ConsoleTable.java b/src/main/java/net/berack/upo/valpre/sim/stats/ConsoleTable.java
index 4ac3783..e75f51b 100644
--- a/src/main/java/net/berack/upo/valpre/sim/stats/ConsoleTable.java
+++ b/src/main/java/net/berack/upo/valpre/sim/stats/ConsoleTable.java
@@ -21,13 +21,27 @@ public class ConsoleTable {
* @throws NullPointerException if the array is null
*/
public ConsoleTable(String... header) {
- var max = 0;
+ this(-1, header);
+ }
+
+ /**
+ * Create a new table with the header passed as input.
+ * The table will have as many columns as the length of the header array.
+ * Each column will have the same size and will be the max length of all the
+ * headers string or the maxLen passed as input.
+ *
+ * @param maxLen the max length of the columns
+ * @param header an array of strings
+ * @throws NullPointerException if the array is null
+ */
+ public ConsoleTable(int maxLen, String... header) {
+ var max = Math.max(0, maxLen);
for (var name : header)
max = Math.max(max, name.length());
this.columns = header.length;
this.maxLen = max + 2;
- this.border = ("+" + "═".repeat(maxLen)).repeat(header.length) + "+\n";
+ this.border = ("+" + "═".repeat(this.maxLen)).repeat(header.length) + "+\n";
this.builder.append(border);
this.addRow(header);
}
@@ -45,7 +59,7 @@ public class ConsoleTable {
for (var val : values) {
var diff = maxLen - val.length();
- var first = (int) Math.ceil(diff / 2.0);
+ var first = Math.max((int) Math.ceil(diff / 2.0), 0);
builder.append('║');
builder.append(" ".repeat(first));
builder.append(val);
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 c9a708e..f5a529a 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
@@ -116,8 +116,14 @@ public class Result implements Iterable> {
var fFormat = "%" + (size + 4) + ".3f";
var builder = new StringBuilder();
- var table = new ConsoleTable("Node", "Departures", "Avg Queue", "Avg Wait", "Avg Response", "Throughput",
- "Utilization %", "Unavailable %", "Last Event");
+ var maxNameLen = 0;
+ for (var i = 0; i < nodes.length; i++) {
+ var node = nodes[i];
+ maxNameLen = Math.max(node.length(), maxNameLen);
+ }
+
+ var table = new ConsoleTable(maxNameLen, "Node", "Departures", "Avg Queue", "Avg Wait", "Avg Response",
+ "Throughput", "Utilization %", "Unavailable %", "Last Event");
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
diff --git a/src/test/resources/example2.jsimg b/src/test/resources/example2.jsimg
index 6ceb93e..7a9f579 100644
--- a/src/test/resources/example2.jsimg
+++ b/src/test/resources/example2.jsimg
@@ -1,6 +1,6 @@
-
-
+
+
@@ -853,13 +853,13 @@
-
+
-
-
+
+
-
+
@@ -923,78 +923,75 @@
-
-
-
-
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
-
-
-
+
+
+
+
+