1 /// Pseudo random range 2 module tagion.utils.Random; 3 @safe: 4 5 import std.format; 6 import std.range; 7 import std.traits : isNumeric; 8 9 /// Generates a pseudo random sequence 10 @nogc 11 struct Random(T = uint) { 12 @nogc { 13 private T m_z; 14 private T m_w; 15 this(const T seed_value) pure nothrow { 16 seed(seed_value); 17 } 18 19 private this(T m_z, T m_w) pure nothrow { 20 this.m_z = m_z; 21 this.m_w = m_w; 22 } 23 24 /** 25 * 26 * Params: 27 * seed_value = is the seend of the pseudo random 28 */ 29 void seed(const T seed_value) pure nothrow { 30 m_z = 13 * seed_value; 31 m_w = 7 * seed_value; 32 } 33 34 /** 35 * 36 * Returns: next random value 37 */ 38 T value() { 39 popFront; 40 return front; 41 } 42 43 /** 44 * 45 * Params: 46 * max = sets the max value 47 * Returns: 48 * next random value less than max 49 */ 50 T value(const(T) max) { 51 return value % max; 52 } 53 54 /** 55 * 56 * Params: 57 * from = start value 58 * to = until value 59 * Returns: random value in the range from..to 60 */ 61 T value(const(T) from, const(T) to) 62 in { 63 assert(to > from); 64 } 65 do { 66 immutable range = to - from; 67 return (value % range) + from; 68 } 69 70 /** 71 * pops the next random value 72 */ 73 void popFront() pure nothrow { 74 m_z = 36_969 * (m_z & T.max) + (m_z >> 16); 75 m_w = 18_000 * (m_w & T.max) + (m_w >> 16); 76 } 77 78 /** 79 * 80 * Returns: current random value 81 */ 82 T front() const pure nothrow { 83 return (m_z << 16) + m_w; 84 } 85 86 enum bool empty = false; 87 88 /** 89 * Saves the forward range 90 * Returns: new random 91 */ 92 Random save() const pure nothrow { 93 return Random(m_z, m_w); 94 } 95 96 import std.range.primitives : isForwardRange, isInfinite, isInputRange; 97 98 static assert(isInputRange!(Random)); 99 static assert(isForwardRange!(Random)); 100 static assert(isInfinite!(Random)); 101 } 102 /** 103 * 104 * Returns: inner parameter for the random sequnece 105 */ 106 string toString() const pure { 107 return format("m_z %s, m_w %s, value %s", m_z, m_w, front); 108 } 109 110 } 111 112 /// 113 unittest { 114 import std.algorithm.comparison : equal; 115 import std.range : drop, take; 116 117 auto r = Random!uint(1234); 118 auto r_forward = r.save; 119 120 assert(equal(r.take(5), r_forward.take(5))); 121 122 r = r.drop(7); 123 assert(r != r_forward); 124 r_forward = r.save; 125 assert(r == r_forward); 126 assert(equal(r.take(4), r_forward.take(4))); 127 } 128 129 /// This data type can be used to group a sequence of pseudo random sequency 130 @nogc 131 struct Sequence(T = uint) { 132 import std.range; 133 134 Random!T rand; 135 T size; 136 /** 137 * Returns: sequence of random numbers 138 */ 139 auto list() const pure nothrow { 140 return rand.save.take(size); 141 } 142 143 /** 144 * Progress the random sequency next_size creates a new sequency 145 * Params: 146 * next_size = the number of random to the next sequency 147 * Returns: 148 * the net sequence after next_size randoms 149 */ 150 Sequence progress(T next_size) const pure nothrow { 151 Sequence result; 152 result.rand = rand.save.drop(size); 153 result.size = next_size; 154 return result; 155 } 156 } 157 158 /// 159 unittest { 160 import std.algorithm; 161 import std.range; 162 import std.stdio; 163 164 alias RandomT = Random!uint; 165 166 enum sample = 5; 167 { // Simple range of sequences with fixed size 168 169 auto start = RandomT(1234); 170 auto rand_range = recurrence!(q{ 171 a[n-1].drop(3) 172 })(start); 173 assert(equal( 174 rand_range 175 .take(sample) 176 .map!q{a.take(3)} 177 .joiner, 178 start.save 179 .take(sample * 3))); 180 } 181 182 { // Range of sequences with random size 183 auto start = RandomT(1234); 184 // Generate sequency range with variable size in the range 4..8 185 auto rand_range = recurrence!( 186 (a, n) => 187 a[n - 1].progress(start.value(4, 8)) 188 )(Sequence!uint(start.save, 5)); 189 190 // Store the begen of the random start 191 auto begin = start; 192 const total_number = rand_range.take(sample).map!q{a.size}.sum; 193 // restore start random 194 auto expected = start = begin; 195 assert(equal(expected.take(total_number), 196 rand_range.take(sample).map!q{a.list}.joiner)); 197 198 } 199 200 } 201 202 /** 203 * Generate a random id 204 * Returns: random id 205 **/ 206 const(T) generateId(T = uint)() nothrow if(isNumeric!T) { 207 T id = 0; 208 import stdrnd = std.random; 209 210 auto rnd = Random!T(stdrnd.unpredictableSeed); 211 do { 212 id = rnd.value(); 213 } 214 while (id is 0 || id is T.max); 215 return id; 216 }