1 module digitalnet.implementation; 2 3 import digitalnet.axiom; 4 import std.traits, std.algorithm, std.array, std.range, std.typecons, std.random, std.exception, std..string; 5 import std.conv : to; 6 debug import std.stdio; 7 8 /// 9 alias LinearScramble = Tuple!(size_t[], size_t); 10 11 static assert (isPointSet!(DigitalNet!uint)); 12 static assert (hasPrecision!(DigitalNet!uint)); 13 static assert (isPointSet!(ShiftedDigitalNet!uint)); 14 static assert (hasPrecision!(ShiftedDigitalNet!uint)); 15 16 /// 17 mixin template DigitalNetFunctions(U, Size) 18 { 19 const U[][] basis; 20 immutable size_t precision, dimensionF2, dimensionR; 21 immutable bool bisectable; 22 this (in U[][] basis, Precision precision = Precision(U.sizeof << 3)) 23 { 24 this.basis = basis; 25 this.precision = precision.getPrecision; 26 this.dimensionF2 = basis.length; 27 this.dimensionF2.enforce("Zero digital net is not constructible"); 28 this.dimensionR = basis[0].length; 29 foreach (b; basis) 30 assert (b.length == dimensionR); 31 this.front = new U[dimensionR]; 32 } 33 bool empty; 34 U[] front; 35 void popFront() 36 in 37 { 38 assert (!empty); 39 } 40 body 41 { 42 position += 1; 43 if (position >> dimensionF2) 44 { 45 empty = true; 46 return; 47 } 48 front[] ^= basis[position.bottom_zeros][]; 49 } 50 private: 51 Size position; 52 string _toString() const 53 { 54 return "%d %d %d %(%(%d %) %)".format(precision, dimensionF2, dimensionR, basis); 55 } 56 } 57 58 private U[] scramble(U)(in U[] vector, in size_t[] linearScramble, in size_t precision) 59 body 60 { 61 static if (is (size_t == ulong)) 62 enum p = 6, q = 63; 63 static if (is (size_t == uint)) 64 enum p = 5, q = 31; 65 auto ret = vector.dup; 66 size_t idx; 67 foreach (i, ref x; ret) 68 foreach (j; 1..precision) 69 foreach (U k; 0..j) 70 { 71 x ^= ((linearScramble[idx >> p] >> (idx & q)) & (x >> j) & 1) << k; 72 idx += 1; 73 } 74 return ret; 75 } 76 77 /// A digital net. 78 struct DigitalNet(U = uint, Size = GreaterInteger!U) 79 if (isUnsigned!U) 80 { 81 mixin DigitalNetFunctions!(U, Size); 82 /// Apply a digital shift. 83 ShiftedDigitalNet!(U, Size) opBinary(string op)(in U[] shift) const 84 if (op == "+") 85 { 86 return ShiftedDigitalNet!(U, Size)(basis, shift, Precision(precision)); 87 } 88 DigitalNet!(U, Size) removeShift() 89 { 90 return this; 91 } 92 ShiftedDigitalNet!(U, Size)[2] bisect() const 93 { 94 auto former = ShiftedDigitalNet!(U, Size)(this.basis[1..$], new U[this.dimensionR], Precision(this.precision)); 95 return [former, former + this.basis[0]]; 96 } 97 public string toString() const 98 { 99 return _toString(); 100 } 101 /// Apply a linear scramble. 102 DigitalNet!(U, Size) opBinary(string op)(in LinearScramble linearScramble) const 103 if (op == "*") 104 { 105 return DigitalNet!(U, Size)(basis.map!(b => b.scramble(linearScramble[0], precision)).array, Precision(precision)); 106 } 107 } 108 109 /// A digitally shifted digital net. 110 struct ShiftedDigitalNet(U = uint, Size = GreaterInteger!U) 111 if (isUnsigned!U) 112 { 113 mixin DigitalNetFunctions!(U, Size); 114 const U[] shift; 115 this (in U[][] basis, Precision precision = Precision(U.sizeof << 3)) 116 { 117 this.basis = basis; 118 this.precision = precision.getPrecision; 119 this.dimensionF2 = basis.length; 120 this.dimensionF2.enforce("Zero digital net is not constructible"); 121 this.dimensionR = basis[0].length; 122 foreach (b; basis) 123 assert (b.length == dimensionR); 124 this.front = new U[dimensionR]; 125 } 126 this (in U[][] basis, in U[] shift, Precision precision = Precision(U.sizeof << 3)) 127 { 128 this (basis, precision); 129 this.bisectable = bisectThreshold <= this.dimensionF2; 130 this.shift = shift; 131 this.front[] = shift[]; 132 } 133 /// Apply a digital shift. 134 ShiftedDigitalNet!(U, Size) opBinary(string op)(in U[] shift) const 135 if (op == "+") 136 { 137 auto newShift = new U[dimensionR]; 138 foreach (i, ref s; newShift) 139 s = this.shift[i] ^ shift[i]; 140 return ShiftedDigitalNet!(U, Size)(basis, newShift, Precision(precision)); 141 } 142 /// Remove a digital shift. 143 DigitalNet!(U, Size) removeShift() const 144 { 145 return DigitalNet!(U, Size)(basis, Precision(precision)); 146 } 147 ShiftedDigitalNet!(U, Size)[2] bisect() const 148 { 149 auto former = ShiftedDigitalNet!(U, Size)(this.basis[1..$], this.shift, Precision(this.precision)); 150 return [former, former + this.basis[0]]; 151 } 152 string toString() const 153 { 154 return _toString() ~ " %(%d %)".format(shift); 155 } 156 /// Apply a linear scramble. 157 ShiftedDigitalNet!(U, Size) opBinary(string op)(in LinearScramble linearScramble) const 158 if (op == "*") 159 { 160 return ShiftedDigitalNet!(U, Size)(basis.map!(b => b.scramble(linearScramble[0], precision)).array, shift.scramble(linearScramble[0], precision), Precision(precision)); 161 } 162 } 163 164 private U[] getShift(U)(ref U[][] basis, in size_t dimB, in size_t dimR) 165 { 166 if (basis.length == dimB) 167 { 168 stderr.writeln("run-time warning: add a zero shift"); 169 return new U[dimR]; 170 } 171 if (basis.length == dimB + 1) 172 { 173 auto s = basis[$-1]; 174 basis.length -= 1; 175 return s; 176 } 177 assert (false); 178 } 179 180 private U[][] noShift(U)(U[][] basis, in size_t dimB) 181 { 182 if (basis.length == dimB + 1) 183 { 184 stderr.writefln("run-time warning: ignoring shift, [%(%d %)]", basis[dimB..$][0]); 185 return basis[0..$-1]; 186 } 187 if (basis.length == dimB) 188 return basis; 189 assert (false); 190 } 191 192 private U[][] readDigitalNetParams(U)(const(char)[][] buf, out size_t prec, out size_t dimB, out size_t dimR) 193 { 194 prec = buf[0].to!size_t; 195 dimB = buf[1].to!size_t; 196 dimR = buf[2].to!size_t; 197 return buf[3..$].map!(to!U).array.chunks(dimR).array; 198 } 199 200 /// Read a digital net from a string. 201 DigitalNet!U toDigitalNet(U = uint)(const(char)[] x) 202 { 203 size_t prec, dimB, dimR; 204 auto basis = x.strip.split(',')[0].split.readDigitalNetParams!U(prec, dimB, dimR); 205 return DigitalNet!U(noShift(basis, dimB), Precision(prec)); 206 } 207 208 /// Read a digitally shifted digital net from a string. 209 ShiftedDigitalNet!U toShiftedDigitalNet(U = uint)(const(char)[] x) 210 { 211 size_t prec, dimB, dimR; 212 auto basis = x.strip.split(',')[0].split.readDigitalNetParams!U(prec, dimB, dimR); 213 auto shift = getShift(basis, dimB, dimR); 214 return ShiftedDigitalNet!U(basis, shift, Precision(prec)); 215 } 216 217 /// 218 template GreaterInteger(U) 219 if (isUnsigned!U) 220 { 221 import std.bigint; 222 static if (U.sizeof < ulong.sizeof) 223 alias GreaterInteger = ulong; 224 else 225 alias GreaterInteger = BigInt; 226 } 227 228 immutable(size_t) getPrecision(Precision n) 229 { 230 return cast(size_t)n; 231 } 232 immutable(size_t) getDimensionR(DimensionR s) 233 { 234 return cast(size_t)s; 235 } 236 immutable(size_t) getDimensionF2(DimensionF2 m) 237 { 238 return cast(size_t)m; 239 } 240 241 /// 242 alias Precision = Typedef!(size_t, 0, "prec"); 243 244 /// 245 alias DimensionR = Typedef!(size_t, 0, "dimR"); 246 247 /// 248 alias DimensionF2 = Typedef!(size_t, 0, "dimB"); 249 250 /// Construct a digital net by uniform random choice of its basis. 251 DigitalNet!U randomDigitalNet(U = uint)(Precision precision, DimensionR dimensionR, DimensionF2 dimensionF2) 252 if (isUnsigned!U) 253 { 254 immutable s = dimensionR.getDimensionR; 255 auto basis = randomVector!U(precision.getPrecision, s * dimensionF2.getDimensionF2).chunks(s).array; 256 return DigitalNet!U(basis, precision); 257 } 258 259 /// Uniformly and randomly pick a digital shift. 260 auto randomDigitalShift(S)(S P) 261 if (is (S == DigitalNet!(U, Size), U, Size) || is (S == ShiftedDigitalNet!(U, Size), U, Size)) 262 { 263 alias U = ElementType!(typeof (P.front)); 264 return randomVector!U(P.precision, P.dimensionR); 265 } 266 267 /// ditto 268 U[] randomDigitalShift(U)(Precision precision, DimensionR dimensionR) 269 if (isUnsigned!U) 270 { 271 return randomVector!U(precision.getPrecision, dimensionR.getDimensionR); 272 } 273 274 /// Uniformly and randomly pick a linear scramble. 275 auto randomLinearScramble(S)(S P) 276 if (is (S == DigitalNet!(U, Size), U, Size) || 277 is (S == ShiftedDigitalNet!(U, Size), U, Size)) 278 { 279 immutable size_t 280 wordSize = size_t.sizeof << 3, 281 numBits = P.dimensionR * (P.precision * (P.precision - 1) / 2), 282 backLength = numBits / wordSize + (numBits % wordSize ? 1 : 0); 283 import std.bitmanip : BitArray; 284 return LinearScramble(randomVector!size_t(wordSize, backLength), numBits); 285 } 286 287 /// Shuffle the basis of a digital net. 288 DigitalNet!(U, Size) shuffle(U, Size)(DigitalNet!(U, Size) P) 289 { 290 auto b = P.basis.map!(a => a.to!(U[])).array; 291 b.shuffleBasis; 292 return DigitalNet!(U, Size)(b, Precision(P.precision)); 293 } 294 295 /// Partially shuffle a sequence of vectors. 296 void partialShuffleBasis(U)(U[][] basis) 297 { 298 import std.random; 299 auto i = 0.uniform(basis.length); 300 swap(basis[0], basis[i]); 301 auto j = 0.uniform(1 << (basis.length - 1)); 302 foreach (k, b; basis[1..$]) 303 if (j >> k & 1) 304 basis[0][] ^= b[]; 305 } 306 307 /// Shuffle a sequence of vectors. 308 void shuffleBasis(U)(U[][] basis) 309 { 310 if (basis.length == 1) 311 return; 312 basis.partialShuffleBasis; 313 basis[1..$].shuffleBasis; 314 } 315 316 /** Obtain another digital net Q from a digital net P where dim P = 1 + dim(P cap Q) = dim Q. 317 */ 318 DigitalNet!(U, Size) changeVectorOfBasis(U, Size)(DigitalNet!(U, Size) P) 319 { 320 auto b = P.basis.map!(a => a.to!(U[])).array; 321 b.partialShuffleBasis; 322 b[0] = randomDigitalShift(P); 323 return DigitalNet!(U, Size)(b, Precision(P.precision)); 324 } 325 326 private: 327 328 U[] randomVector(U)(size_t precision, size_t length) 329 if (isUnsigned!U) 330 { 331 auto ret = new U[length]; 332 randomVector(precision, ret); 333 return ret; 334 } 335 336 void randomVector(U)(size_t precision, U[] buffer) 337 { 338 randomVector(buffer); 339 if (auto d = (U.sizeof << 3) - precision) 340 foreach (ref e; buffer) 341 e >>= d; 342 } 343 344 void randomVector(U)(U[] buffer) 345 { 346 foreach (ref e; buffer) 347 e = uniform!U; 348 } 349 350 auto bottom_zeros(Size)(Size x) 351 { 352 assert (x); 353 size_t ret; 354 while ((x & 1) == 0) 355 { 356 x >>= 1; 357 ret += 1; 358 } 359 return ret; 360 } 361 362 enum size_t bisectThreshold = 10;