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 }