1 module tests.utils;
2 
3 import vivaldi;
4 
5 /**
6  * Generates and zero-fills an NxN matrix.
7  */
8 template matrix(size_t n) {
9     auto matrix() {
10         double[n][n] matrix = 0.0;
11         return matrix;
12     }
13 }
14 
15 /**
16  * Runs a number of cycles using the provided nodes and a matrix of
17  * true latencies among them.
18  *
19  * On each cycle, each node will choose a random peer and observe the
20  * true round-trip time, updating its coordinate estimate.
21  */
22 void simulate(T, size_t window, size_t n)(ref T[n] nodes, double[n][n] matrix, uint cycles)
23     @safe
24 {
25     import std.random;
26 
27     alias LF = LatencyFilter!(size_t, double, window);
28 
29     LF*[size_t] filters;
30 
31     for (uint cycle = 0; cycle < cycles; cycle++) {
32         foreach (i, ref node; nodes) {
33             const j = uniform(0, n);
34 
35             if (j != i) {
36                 const peer = nodes[j];
37 
38                 auto filter = filters.require(i, new LF);
39                 const rtt = filter.push(j, matrix[i][j]);
40 
41                 node.update(peer, rtt);
42             }
43         }
44     }
45 }
46 
47 /**
48  * Stats returned by evaluate().
49  */
50 struct Stats {
51     /**
52      * The maximum observed error between a latency matrix and a simulation.
53      */
54     double max = 0.0;
55 
56     /**
57      * The mean observed error between a latency matrix and a simulation.
58      */
59     double mean = 0.0;
60 }
61 
62 /**
63  * Evaluates the output of a simulation and a matrix of true
64  * latencies, returning the maximum and mean error between the
65  * simulated results and the truth.
66  */
67 Stats* evaluate(T, size_t n)(T[n] nodes, double[n][n]matrix) nothrow @safe {
68     import std.algorithm : max;
69     import std.math : abs;
70 
71     auto stats = new Stats;
72     double count = 0;
73 
74     for (size_t i = 0; i < n; i++) {
75         for (size_t j = i + 1; j < n; j++) {
76             const est = nodes[i].distanceTo(nodes[j]);
77             const actual = matrix[i][j];
78             const err = abs(est - actual) / actual;
79 
80             stats.max = max(stats.max, err);
81             stats.mean += err;
82             count += 1;
83         }
84     }
85 
86     stats.mean /= count;
87     return stats;
88 }