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;