1 /** Statically sized variant of `std.bitmanip.BitArray.
2  *
3  * Copyright: Per Nordlöw 2022-.
4  * License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
5  * Authors: $(WEB Per Nordlöw)
6  */
7 module nxt.container.static_bitarray;
8 
9 @safe:
10 
11 alias DefaultBlock = size_t;	///< Default block type.
12 
13 import std.traits : isUnsigned;
14 
15 /** A statically sized `std.bitmanip.BitArray`.
16  *
17  * TODO: Infer `Block` from `len` as is done for `Bound` and `Mod`.
18  *
19  * TODO: Optimize `allOne`, `allZero` using intrinsic?
20  */
21 struct StaticBitArray(uint capacity, Block = DefaultBlock)
22 if (isUnsigned!DefaultBlock) {
23 @safe:
24 	import std.format : FormatSpec, format;
25 	import nxt.modulo : Mod;
26 
27 	/** Number of bits.
28 	 *
29 	 * Length equals capacity.
30 	 */
31 	enum length = capacity;
32 
33 	static if (capacity >= 1)
34 		alias Index = Mod!capacity;
35 
36 	static if (Block.sizeof == 8)
37 		import core.bitop : bt, bts, btr;
38 	else
39 		import nxt.bitop_ex : bt, bts, btr;
40 
41 	/** Number of bits per `Block`. */
42 	enum bitsPerBlock = 8*Block.sizeof;
43 	/** Number of `Block`s. */
44 	enum blockCount = (capacity + bitsPerBlock-1) / bitsPerBlock;
45 
46 	/** Data stored as `Block`s. */
47 	private Block[blockCount] _blocks;
48 
49 	/** Data as an array of unsigned bytes. */
50 	pragma(inline, true)
51 	inout(ubyte)[] ubytes()() inout @trusted => (cast(ubyte*)&_blocks)[0 .. _blocks.sizeof];
52 
53 	/** Get pointer to data blocks. */
54 	pragma(inline, true)
55 	@property inout(Block*) ptr() inout @system => _blocks.ptr;
56 
57 	/** Reset all bits (to zero). */
58 	pragma(inline, true)
59 	void reset()() {
60 		_blocks[] = 0;		  /+ TODO: is this fastest way? +/
61 	}
62 	alias clear = reset;
63 
64 	/** Gets the amount of native words backing `this`. */
65 	pragma(inline, true)
66 	@property static uint dim() => blockCount;
67 
68 	/** Bidirectional range into `BitArray`.
69 	 *
70 	 * TODO: Provide opSliceAssign for interopability with range algorithms via
71 	 * private static struct member `Range`.
72 	 *
73 	 * TODO: Look at how std.container.array implements this.
74 	 *
75 	 * See_Also: https://dlang.org/phobos/std_bitmanip.html#bitsSet
76 	*/
77 	struct Range()			  /*tlm*/
78 	{
79 		pure nothrow @safe @nogc:
80 		pragma(inline, true):
81 
82 		/// Returns: `true` iff `this` is empty.
83 		bool empty()  const => _i == _j;
84 
85 		/// Returns: `this` length.
86 		size_t length() const => _j - _i;
87 
88 		import std.traits : isMutable;
89 		static if (isMutable!(typeof(_store)))
90 			this(this) @disable; // Rust-style mutable reference semantics
91 
92 		bool front() const in(!empty) => (*_store)[_i];
93 		bool back() const in(!empty) => (*_store)[_j - 1];
94 		void popFront() in(!empty) { ++_i; }
95 		void popBack() in(!empty) { --_j; }
96 
97 	private:
98 		StaticBitArray* _store;
99 		size_t _i = 0;			 // front iterator into _store
100 		size_t _j = _store.length; // back iterator into _store
101 	}
102 
103 	pragma(inline, true)
104 	scope inout(Range!()) opSlice()() inout return @trusted => typeof(return)(&this);
105 	pragma(inline, true)
106 	scope inout(Range!()) opSlice()(in size_t i, size_t j) inout return @trusted => typeof(return)(&this, i, j);
107 
108 	/** Set all bits to `value` via slice assignment syntax. */
109 	ref typeof(this) opSliceAssign(bool value) {
110 		if (value)
111 			one();
112 		else
113 			zero();
114 		return this;
115 	}
116 
117 	/** Set all bits (to zero). */
118 	private void zero() {
119 		foreach (ref block; _blocks)
120 			block = Block.min;
121 	}
122 
123 	/** Set all bits (to one). */
124 	private void one() {
125 		foreach (ref block; _blocks)
126 			block = Block.max;
127 	}
128 
129 	/** Gets the $(D i)'th bit. */
130 	pragma(inline, true)
131 	bool opIndex(in size_t i) const @trusted
132 	in(i < capacity)			/+ TODO: nothrow or not? +/
133 	{
134 		// Andrei: review for @@@64-bit@@@
135 		static if (Block.sizeof == 8)
136 			return cast(bool)bt(ptr, i);
137 		else
138 			return bt(_blocks[i/bitsPerBlock], i%bitsPerBlock);
139 	}
140 
141 	/** Gets the $(D i)'th bit. No range checking needed. */
142 	static if (capacity >= 1) {
143 		/** Get the $(D i)'th bit.
144 		 *
145 		 * Avoids range-checking because `i` of type is bound to (0 .. capacity-1).
146 		 */
147 		bool opIndex(ModUInt)(Mod!(capacity, ModUInt) i) const @trusted
148 		if (isUnsigned!ModUInt) {
149 			pragma(inline, true);
150 			static if (Block.sizeof == 8)
151 				return cast(bool)bt(ptr, cast(size_t)i);
152 			else
153 				return bt(_blocks[i/bitsPerBlock], i%bitsPerBlock);
154 		}
155 
156 		/** Get the $(D i)'th bit.
157 		 *
158 		 * Statically verifies that i is < StaticBitArray length.
159 		 */
160 		pragma(inline, true)
161 		bool at(size_t i)() const if (i < capacity) => this[i];
162 	}
163 
164 	/** Puts the $(D i)'th bit to $(D b). */
165 	pragma(inline, true)
166 	void put()(in size_t i, bool b) @trusted { this[i] = b; }
167 
168 	/** Sets the $(D i)'th bit. */
169 	import std.traits : isIntegral;
170 	bool opIndexAssign(Index2)(bool b, Index2 i) @trusted
171 	if (isIntegral!Index2)
172 	in(i < capacity)
173 	in
174 	{
175 		// import std.traits: isMutable;
176 		// See_Also: http://stackoverflow.com/questions/19906516/static-parameter-function-specialization-in-d
177 		/* static if (!isMutable!Index2) { */
178 		/*	 import std.conv: to; */
179 		/*	 static assert(i < capacity, */
180 		/*				   "Index2 " ~ to!string(i) ~ " must be smaller than StaticBitArray length " ~  to!string(capacity)); */
181 		/* } */
182 	}
183 	do
184 	{
185 		pragma(inline, true);
186 		if (b)
187 			bts(ptr, cast(size_t)i);
188 		else
189 			btr(ptr, cast(size_t)i);
190 		return b;
191 	}
192 
193 	static if (capacity >= 1) {
194 		/** Sets the $(D i)'th bit. No range checking needed. */
195 		pragma(inline, true)
196 		bool opIndexAssign(ModUInt)(bool b, Mod!(capacity, ModUInt) i) @trusted
197 		if (isUnsigned!ModUInt) {
198 			if (b)
199 				bts(ptr, cast(size_t)i);
200 			else
201 				btr(ptr, cast(size_t)i);
202 			return b;
203 		}
204 	}
205 
206 	///
207 	pure nothrow @safe @nogc unittest
208 	{
209 		StaticBitArray!2 bs;
210 		bs[0] = true;
211 		assert(bs[0]);
212 		assert(!bs[1]);
213 		bs[1] = true;
214 		assert(bs[1]);
215 	}
216 
217 	/** Support for $(D foreach) loops for $(D StaticBitArray). */
218 	int opApply(scope int delegate(ref bool) dg) @trusted
219 	{
220 		int result;
221 		foreach (const size_t i; 0 .. capacity) {
222 			bool b = opIndex(i);
223 			result = dg(b);
224 			this[i] = b;
225 			if (result) { break; }
226 		}
227 		return result;
228 	}
229 
230 	/** ditto */
231 	int opApply(scope int delegate(bool) dg) const @trusted
232 	{
233 		int result;
234 		foreach (const size_t i; 0 .. capacity) {
235 			bool b = opIndex(i);
236 			result = dg(b);
237 			if (result) { break; }
238 		}
239 		return result;
240 	}
241 
242 	/** ditto */
243 	int opApply(scope int delegate(const ref size_t, ref bool) dg) @trusted
244 	{
245 		int result;
246 		foreach (const size_t i; 0 .. capacity) {
247 			bool b = opIndex(i);
248 			result = dg(i, b);
249 			this[i] = b;
250 			if (result) { break; }
251 		}
252 		return result;
253 	}
254 
255 	/** ditto */
256 	int opApply(scope int delegate(in size_t, bool) dg) const @trusted
257 	{
258 		int result;
259 		foreach (const size_t i; 0 .. capacity) {
260 			bool b = opIndex(i);
261 			result = dg(i, b);
262 			if (result) { break; }
263 		}
264 		return result;
265 	}
266 
267 	///
268 	unittest
269 	{
270 		static bool[] ba = [1,0,1];
271 		auto a = StaticBitArray!3(ba);
272 		size_t i;
273 		foreach (immutable b; a[]) /+ TODO: is `opSlice` the right thing? +/
274 		{
275 			switch (i) {
276 			case 0: assert(b == true); break;
277 			case 1: assert(b == false); break;
278 			case 2: assert(b == true); break;
279 			default: assert(0);
280 			}
281 			i++;
282 		}
283 		foreach (j, b; a)	   /+ TODO: is `opSlice` the right thing? +/
284 		{
285 			switch (j) {
286 			case 0: assert(b == true); break;
287 			case 1: assert(b == false); break;
288 			case 2: assert(b == true); break;
289 			default: assert(0);
290 			}
291 		}
292 	}
293 
294 	/** Reverse block `Block`. */
295 	static @property Block reverseBlock()(in Block block) {
296 		import core.bitop : bitswap;
297 		pragma(inline, true);
298 		static if (Block.sizeof == 4)
299 			return cast(uint)block.bitswap;
300 		else static if (Block.sizeof == 8)
301 			return (((cast(Block)((cast(uint)(block)).bitswap)) << 32) |
302 					(cast(Block)((cast(uint)(block >> 32)).bitswap)));
303 		else
304 			return block;
305 	}
306 
307 	/** Reverses the bits of the $(D StaticBitArray) in place. */
308 	@property typeof(this) reverse()()
309 	out (result) {
310 		assert(result == this);
311 	}
312 	do
313 	{
314 		static if (length == blockCount * bitsPerBlock) {
315 			static if (blockCount == 1)
316 				_blocks[0] = reverseBlock(_blocks[0]);
317 			else static if (blockCount == 2) {
318 				const tmp = _blocks[1];
319 				_blocks[1] = reverseBlock(_blocks[0]);
320 				_blocks[0] = reverseBlock(tmp);
321 			}
322 			else static if (blockCount == 3) {
323 				const tmp = _blocks[2];
324 				_blocks[2] = reverseBlock(_blocks[0]);
325 				_blocks[1] = reverseBlock(_blocks[1]);
326 				_blocks[0] = reverseBlock(tmp);
327 			}
328 			else
329 			{
330 				size_t lo = 0;
331 				size_t hi = _blocks.length - 1;
332 				for (; lo < hi; ++lo, --hi) {
333 					immutable t = reverseBlock(_blocks[lo]);
334 					_blocks[lo] = reverseBlock(_blocks[hi]);
335 					_blocks[hi] = t;
336 				}
337 				if (lo == hi)
338 					_blocks[lo] = reverseBlock(_blocks[lo]);
339 			}
340 		}
341 		else
342 		{
343 			static if (length >= 2) {
344 				size_t lo = 0;
345 				size_t hi = capacity - 1;
346 				for (; lo < hi; ++lo, --hi) {
347 					immutable t = this[lo];
348 					this[lo] = this[hi];
349 					this[hi] = t;
350 				}
351 			}
352 		}
353 		return this;
354 	}
355 
356 	///
357 	pure unittest
358 	{
359 		enum capacity = 64;
360 		static immutable bool[capacity] data = [0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0,
361 										   0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0];
362 		auto b = StaticBitArray!capacity(data);
363 		b.reverse();
364 		foreach (const i; 0 .. data.length)
365 			assert(b[i] == data[capacity - 1 - i]);
366 	}
367 
368 	///
369 	pure unittest
370 	{
371 		enum capacity = 64*2;
372 		static immutable bool[capacity] data = [0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0,
373 										   0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0,
374 										   0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0,
375 										   0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0];
376 		auto b = StaticBitArray!capacity(data);
377 		b.reverse();
378 		foreach (const i; 0 .. data.length)
379 			assert(b[i] == data[capacity - 1 - i]);
380 	}
381 
382 	///
383 	pure unittest
384 	{
385 		enum capacity = 64*3;
386 		static immutable bool[capacity] data = [0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0,
387 										   0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0,
388 										   0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0,
389 										   0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0,
390 										   0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0,
391 										   0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0, 0,1,1,0,1,0,1,0];
392 		auto b = StaticBitArray!capacity(data);
393 		b.reverse();
394 		foreach (const i; 0 .. data.length)
395 			assert(b[i] == data[capacity - 1 - i]);
396 	}
397 
398 	/** Sorts the $(D StaticBitArray)'s elements. */
399 	@property typeof(this) sort()()
400 	in(result == this)
401 	out(result) {
402 		if (capacity >= 2) {
403 			size_t lo, hi;
404 			lo = 0;
405 			hi = capacity - 1;
406 			while (1) {
407 				while (1) {
408 					if (lo >= hi)
409 						goto Ldone;
410 					if (this[lo] == true)
411 						break;
412 					lo++;
413 				}
414 				while (1) {
415 					if (lo >= hi)
416 						goto Ldone;
417 					if (this[hi] == false)
418 						break;
419 					hi--;
420 				}
421 				this[lo] = false;
422 				this[hi] = true;
423 				lo++;
424 				hi--;
425 			}
426 		}
427 	Ldone:
428 		return this;
429 	}
430 
431 	/* unittest */
432 	/*	 { */
433 	/*		 __gshared size_t x = 0b1100011000; */
434 	/*		 __gshared StaticBitArray ba = { 10, &x }; */
435 	/*		 ba.sort(); */
436 	/*		 for (size_t i = 0; i < 6; ++i) */
437 	/*			 assert(ba[i] == false); */
438 	/*		 for (size_t i = 6; i < 10; ++i) */
439 	/*			 assert(ba[i] == true); */
440 	/*	 } */
441 
442 
443 	/** Support for operators == and != for $(D StaticBitArray). */
444 	bool opEquals(Block2)(in StaticBitArray!(capacity, Block2) a2) const @trusted
445 	if (isUnsigned!Block2) {
446 		size_t i;
447 
448 		if (this.length != a2.length) { return 0; } // not equal
449 		auto p1 = this.ptr;
450 		auto p2 = a2.ptr;
451 		auto n = this.length / bitsPerBlock;
452 		for (i = 0; i < n; ++i)
453 			if (p1[i] != p2[i])
454 				return 0; // not equal
455 
456 		n = this.length & (bitsPerBlock-1);
457 		size_t mask = (1 << n) - 1;
458 		//printf("i = %d, n = %d, mask = %x, %x, %x\n", i, n, mask, p1[i], p2[i]);
459 		return (mask == 0) || (p1[i] & mask) == (p2[i] & mask);
460 	}
461 	///
462 	nothrow unittest
463 	{
464 		auto a = StaticBitArray!(5, ubyte)([1,0,1,0,1]);
465 		auto b = StaticBitArray!(5, ushort)([1,0,1,1,1]);
466 		auto c = StaticBitArray!(5, uint)([1,0,1,0,1]);
467 		auto d = StaticBitArray!(5, ulong)([1,1,1,1,1]);
468 		assert(a != b);
469 		assert(a == c);
470 		assert(a != d);
471 	}
472 
473 	/** Supports comparison operators for $(D StaticBitArray). */
474 	int opCmp(Block2)(in StaticBitArray!(capacity, Block2) a2) const @trusted
475 	if (isUnsigned!Block2) {
476 		uint i;
477 
478 		auto capacity = this.length;
479 		if (a2.length < capacity) { capacity = a2.length; }
480 		auto p1 = this.ptr;
481 		auto p2 = a2.ptr;
482 		auto n = capacity / bitsPerBlock;
483 		for (i = 0; i < n; ++i)
484 			if (p1[i] != p2[i]) { break; } // not equal
485 		for (size_t j = 0; j < capacity-i * bitsPerBlock; j++) {
486 			size_t mask = cast(size_t)(1 << j);
487 			auto c = (cast(long)(p1[i] & mask) - cast(long)(p2[i] & mask));
488 			if (c) { return c > 0 ? 1 : -1; }
489 		}
490 		return cast(int)this.length - cast(int)a2.length;
491 	}
492 
493 	///
494 	nothrow unittest
495 	{
496 		auto a = StaticBitArray!(5, ubyte)([1,0,1,0,1]);
497 		auto b = StaticBitArray!(5, ushort)([1,0,1,1,1]);
498 		auto c = StaticBitArray!(5, uint)([1,0,1,0,1]);
499 		auto d = StaticBitArray!(5, ulong)([1,1,1,1,1]);
500 		assert(a <  b);
501 		assert(a <= b);
502 		assert(a == c);
503 		assert(a <= c);
504 		assert(a >= c);
505 		assert(c < d);
506 	}
507 
508 	/** Support for hashing for $(D StaticBitArray). */
509 	extern(D) hash_t toHash() const @trusted pure nothrow
510 	{
511 		typeof(return) hash = 3557;
512 		auto n  = capacity / 8;
513 		foreach (const i; 0 .. n) {
514 			hash *= 3559;
515 			hash += (cast(byte*)this.ptr)[i];
516 		}
517 		for (size_t i = 8*n; i < capacity; ++i) {
518 			hash *= 3571;
519 			hash += this[i];
520 		}
521 		return hash;
522 	}
523 
524 	/** Set `this` to the contents of $(D ba). */
525 	this()(bool[] ba) in(length == ba.length) {
526 		foreach (immutable i, const b; ba)
527 			this[i] = b;
528 	}
529 
530 	/** Set `this` to the contents of $(D ba). */
531 	this()(const ref bool[capacity] ba) {
532 		foreach (immutable i, const b; ba)
533 			this[i] = b;
534 	}
535 
536 	bool opCast(T : bool)() const => !this.allZero;
537 
538 	/// construct from dynamic array
539 	@safe nothrow @nogc unittest
540 	{
541 		static bool[] ba = [1,0,1,0,1];
542 		auto a = StaticBitArray!5(ba);
543 		assert(a);
544 		assert(!a.allZero);
545 	}
546 	/// ditto
547 	@safe nothrow @nogc unittest
548 	{
549 		static bool[] ba = [0,0,0];
550 		auto a = StaticBitArray!3(ba);
551 		assert(!a);
552 		assert(a.allZero);
553 	}
554 	/// construct from static array
555 	@safe nothrow @nogc unittest
556 	{
557 		static bool[3] ba = [0,0,0];
558 		auto a = StaticBitArray!3(ba);
559 		assert(!a);
560 		assert(a.allZero);
561 	}
562 
563 	static if (capacity >= 1) {
564 		/** Lazy range of the indices of set bits.
565 
566 			Similar to: `std.bitmanip.bitsSet`
567 
568 			See_Also: https://dlang.org/phobos/std_bitmanip.html#bitsSet
569 		 */
570 		struct OneIndexes(Store) {
571 			/+ TODO: if (is(Store == StaticBitArray!(_), _)) +/
572 			pure nothrow @safe @nogc:
573 
574 			this(Store* store) {
575 				this._store = store;
576 
577 				// pre-adjust front index. TODO: make lazy and move to front
578 				while (_i < length && !(*_store)[_i])
579 					++_i;
580 
581 				// pre-adjust back index. TODO: make lazy and move to front
582 				while (_j > 1 && !(*_store)[_j])
583 					--_j;
584 			}
585 
586 			import std.traits : isMutable;
587 			static if (isMutable!(typeof(_store)))
588 				this(this) @disable; // Rust-style mutable reference semantics
589 
590 			pragma(inline, true):
591 
592 			bool empty() const @property => _i > _j;
593 			Mod!capacity front() const @property in(!empty) => typeof(return)(_i); /+ TODO: use enforce when it's @nogc +/
594 			Mod!capacity back() const @property in(!empty) => typeof(return)(_j); /+ TODO: use enforce when it's @nogc +/
595 			void popFront() in(!empty) {
596 				version (DigitalMars) pragma(inline);
597 				while (++_i <= _j)
598 					if ((*_store)[_i])
599 						break;
600 			}
601 			void popBack() in(!empty) {
602 				while (_i <= --_j)
603 					if ((*_store)[_j])
604 						break;
605 			}
606 
607 		private:
608 			Store* _store;				 // copy of store
609 			int _i = 0;					// front index into `_store`
610 			int _j = (*_store).length - 1; // back index into `_store`
611 		}
612 
613 		/** Returns: a lazy range of the indices of set bits.
614 		 */
615 		auto oneIndexes()() const => OneIndexes!(typeof(this))(&this);
616 		/// ditto
617 		alias bitsSet = oneIndexes;
618 
619 		/** Find index of first cleared (zero) bit or `typeof(return).max` if no bit set.
620 		 *
621 		 * Optimized for zeros-sparsity.
622 		 */
623 		size_t indexOfFirstZero()() const
624 		{
625 			import nxt.bitarray_algorithm;
626 			enum bool blockAlignedLength = capacity % (8*Block.sizeof) == 0;
627 			return indexOfFirstZero!(const(Block)[blockCount],
628 									 blockAlignedLength)(_blocks, length);
629 		}
630 
631 		/** Find index of first set (one) bit or `typeof(return).max` if no bit set.
632 		 *
633 		 * Optimized for ones-sparsity.
634 		 */
635 		size_t indexOfFirstOne()() const
636 		{
637 			import nxt.bitarray_algorithm : indexOfFirstOne;
638 			enum bool blockAlignedLength = capacity % (8*Block.sizeof) == 0;
639 			return indexOfFirstOne!(const(Block)[blockCount],
640 									blockAlignedLength)(_blocks, length);
641 		}
642 
643 		/** Get number of bits set. */
644 		Mod!(capacity + 1) countOnes()() const	/* tlm. TODO: unite with other definitions */
645 		{
646 			import nxt.bitarray_algorithm;
647 			enum bool blockAlignedLength = capacity % (8*Block.sizeof) == 0;
648 			return typeof(return)(nxt.bitarray_algorithm.countOnes!(const(Block)[blockCount],
649 																blockAlignedLength)(_blocks, length));
650 		}
651 
652 		/** Get number of (zero) bits unset. */
653 		size_t countZeros()() const  /*tlm*/
654 		{
655 			return length - countOnes;
656 		}
657 
658 		/** Get number of bits set divided by length. */
659 		version (none)
660 		auto denseness()(int depth = -1) const /*tlm*/
661 		{
662 			import nxt.rational : Rational;
663 			alias Q = Rational!ulong;
664 			return Q(countOnes, length);
665 		}
666 
667 		/** Get number of bits unset divided by length. */
668 		version (none)
669 		auto sparseness()(int depth = -1) const /*tlm*/
670 		{
671 			import nxt.rational : Rational;
672 			alias Q = Rational!ulong;
673 			return 1 - denseness(depth);
674 		}
675 
676 		/** Check if `this` has only zeros (is empty). */
677 		bool allZero()() const pure nothrow @safe @nogc
678 		{
679 			foreach (const block; _fullBlocks)
680 				if (block != Block.min)
681 					return false;
682 			static if (blockCount)
683 				if (_restBlock != Block.min)
684 					return false;
685 			return true;
686 		}
687 
688 		/** Check if `this` has only ones. */
689 		bool allOne()() const
690 		{
691 			const restBitCount = capacity % bitsPerBlock;
692 			const hasRest = restBitCount != 0;
693 			if (_blocks.length >= 1)
694 				foreach (const block; _blocks[0 .. $ - hasRest])
695 					if (block != Block.max) { return false; }
696 			if (restBitCount)
697 				return _blocks[$ - 1] == 2^^restBitCount - 1;
698 			else
699 				return true;
700 		}
701 
702 		/** Find index (starting at `currIx`) of first bit that equals `value`.
703 		 *
704 		 * Returns: `true` if index was found (hit index is put into `nextIx`), `false` otherwise.
705 		 *
706 		 * TODO: block-optimize for large BitSets
707 		 */
708 		bool canFindIndexOf(ModUInt)(bool value,
709 									 Mod!(capacity, ModUInt) currIx,
710 									 out Mod!(capacity, ModUInt) nextIx) const
711 		if (isUnsigned!ModUInt) {
712 			if (currIx >= length) { return false; }
713 			bool hit = false;
714 			foreach (immutable ix_; cast(uint)currIx .. cast(uint)length) {
715 				const bool bit = this[ix_];
716 				if (bit == value) {
717 					nextIx = typeof(nextIx)(ix_);
718 					hit = true;
719 					break;
720 				}
721 			}
722 			return hit;
723 		}
724 
725 		bool canFindIndexOf(UInt)(bool value,
726 								  UInt currIx,
727 								  out UInt nextIx) const
728 		if (isUnsigned!UInt) {
729 			if (currIx >= length) { return false; }
730 			bool hit = false;
731 			foreach (immutable ix_; cast(uint)currIx .. cast(uint)length) {
732 				const bool bit = this[ix_];
733 				if (bit == value) {
734 					nextIx = typeof(nextIx)(ix_);
735 					hit = true;
736 					break;
737 				}
738 			}
739 			return hit;
740 		}
741 
742 	}
743 
744 	/**
745 	 * Map the $(D StaticBitArray) onto $(D v), with $(D numbits) being the number of bits
746 	 * in the array. Does not copy the data.
747 	 *
748 	 * This is the inverse of $(D opCast).
749 	 */
750 	/* void init(void[] v, size_t numbits) in { */
751 	/*	 assert(numbits <= v.length * 8); */
752 	/*	 assert((v.length & 3) == 0); // must be whole bytes */
753 	/* } do { */
754 	/*	 _blocks[] = cast(in size_t*)v.ptr[0..v.length]; */
755 	/* } */
756 
757 	/** Convert to $(D void[]). */
758 	void[] opCast(T : void[])() @trusted => cast(void[])ptr[0 .. dim];
759 
760 	/** Convert to $(D size_t[]). */
761 	size_t[] opCast(T : size_t[])() => ptr[0 .. dim];
762 	///
763 	nothrow unittest
764 	{
765 		static bool[] ba = [1,0,1,0,1];
766 		auto a = StaticBitArray!5(ba);
767 		void[] v = cast(void[])a;
768 		assert(v.length == a.dim * size_t.sizeof);
769 	}
770 
771 	/** Complement operator. */
772 	typeof(this) opCom() const @trusted
773 	{
774 		StaticBitArray result;
775 		foreach (const i; 0 .. dim)
776 			result.ptr[i] = cast(Block)~cast(ulong)this.ptr[i];
777 		immutable rem = capacity & (bitsPerBlock-1); // number of rest bits in last block
778 		if (rem < bitsPerBlock) // rest bits in last block
779 			// make remaining bits zero in last block
780 			result.ptr[dim - 1] &= ~(~(cast(Block)0) << rem);
781 		return result;
782 	}
783 
784 	/** Support for binary operator & for $(D StaticBitArray). */
785 	typeof(this) opBinary(string op)(in typeof(this) e2) const
786 		if (op == "&") {
787 		StaticBitArray result;
788 		result._blocks[] = this._blocks[] & e2._blocks[];
789 		return result;
790 	}
791 	///
792 	nothrow unittest
793 	{
794 		const a = StaticBitArray!5([1,0,1,0,1]);
795 		auto b = StaticBitArray!5([1,0,1,1,0]);
796 		const c = a & b;
797 		auto d = StaticBitArray!5([1,0,1,0,0]);
798 		assert(c == d);
799 	}
800 
801 	/** Support for binary operator | for $(D StaticBitArray). */
802 	typeof(this) opBinary(string op)(in typeof(this) e2) const if (op == "|") {
803 		StaticBitArray result;
804 		result._blocks[] = this._blocks[] | e2._blocks[];
805 		return result;
806 	}
807 	///
808 	nothrow unittest
809 	{
810 		const a = StaticBitArray!5([1,0,1,0,1]);
811 		auto b = StaticBitArray!5([1,0,1,1,0]);
812 		const c = a | b;
813 		auto d = StaticBitArray!5([1,0,1,1,1]);
814 		assert(c == d);
815 	}
816 
817 	/** Support for binary operator ^ for $(D StaticBitArray). */
818 	typeof(this) opBinary(string op)(in typeof(this) e2) const if (op == "^") {
819 		StaticBitArray result;
820 		result._blocks[] = this._blocks[] ^ e2._blocks[];
821 		return result;
822 	}
823 	///
824 	nothrow unittest
825 	{
826 		const a = StaticBitArray!5([1,0,1,0,1]);
827 		auto b = StaticBitArray!5([1,0,1,1,0]);
828 		const c = a ^ b;
829 		auto d = StaticBitArray!5([0,0,0,1,1]);
830 		assert(c == d);
831 	}
832 
833 	/** Support for binary operator - for $(D StaticBitArray).
834 	 *
835 	 * $(D a - b) for $(D StaticBitArray) means the same thing as $(D a &amp; ~b).
836 	 */
837 	typeof(this) opBinary(string op)(in typeof(this) e2) const if (op == "-") {
838 		StaticBitArray result;
839 		result._blocks[] = this._blocks[] & ~e2._blocks[];
840 		return result;
841 	}
842 	///
843 	nothrow unittest
844 	{
845 		const a = StaticBitArray!5([1,0,1,0,1]);
846 		auto b = StaticBitArray!5([1,0,1,1,0]);
847 		const c = a - b;
848 		auto d = StaticBitArray!5([0,0,0,0,1]);
849 		assert(c == d);
850 	}
851 
852 	/** Support for operator &= for $(D StaticBitArray).
853 	 */
854 	typeof(this) opOpAssign(string op)(in typeof(this) e2) if (op == "&") {
855 		_blocks[] &= e2._blocks[];
856 		return this;
857 	}
858 	///
859 	nothrow unittest
860 	{
861 		auto a = StaticBitArray!5([1,0,1,0,1]);
862 		const b = StaticBitArray!5([1,0,1,1,0]);
863 		a &= b;
864 		const c = StaticBitArray!5([1,0,1,0,0]);
865 		assert(a == c);
866 	}
867 
868 	/** Support for operator |= for $(D StaticBitArray).
869 	 */
870 	typeof(this) opOpAssign(string op)(in typeof(this) e2) if (op == "|") {
871 		_blocks[] |= e2._blocks[];
872 		return this;
873 	}
874 	///
875 	nothrow unittest
876 	{
877 		auto a = StaticBitArray!5([1,0,1,0,1]);
878 		const b = StaticBitArray!5([1,0,1,1,0]);
879 		a |= b;
880 		const c = StaticBitArray!5([1,0,1,1,1]);
881 		assert(a == c);
882 	}
883 
884 	/** Support for operator ^= for $(D StaticBitArray).
885 	 */
886 	typeof(this) opOpAssign(string op)(in typeof(this) e2) if (op == "^") {
887 		_blocks[] ^= e2._blocks[];
888 		return this;
889 	}
890 	///
891 	nothrow unittest
892 	{
893 		auto a = StaticBitArray!5([1,0,1,0,1]);
894 		const b = StaticBitArray!5([1,0,1,1,0]);
895 		a ^= b;
896 		const c = StaticBitArray!5([0,0,0,1,1]);
897 		assert(a == c);
898 	}
899 
900 	/** Support for operator -= for $(D StaticBitArray).
901 	 *
902 	 * $(D a -= b) for $(D StaticBitArray) means the same thing as $(D a &amp;= ~b).
903 	 */
904 	typeof(this) opOpAssign(string op)(in typeof(this) e2) if (op == "-") {
905 		_blocks[] &= ~e2._blocks[];
906 		return this;
907 	}
908 	///
909 	nothrow unittest
910 	{
911 		auto a = StaticBitArray!5([1,0,1,0,1]);
912 		const b = StaticBitArray!5([1,0,1,1,0]);
913 		a -= b;
914 		const c = StaticBitArray!5([0,0,0,0,1]);
915 		assert(a == c);
916 	}
917 
918 	/** Return a string representation of this StaticBitArray.
919 	 *
920 	 * Two format specifiers are supported:
921 	 * $(LI $(B %s) which prints the bits as an array, and)
922 	 * $(LI $(B %b) which prints the bits as 8-bit byte packets)
923 	 * separated with an underscore.
924 	 */
925 	void toString(Sink)(ref scope Sink sink, FormatSpec!char fmt) const @trusted
926 	{
927 		switch(fmt.spec) {
928 		case 'b':
929 			return formatBitString(sink);
930 		case 's':
931 			return formatBitSet(sink);
932 		default:
933 			throw new Exception("Unknown format specifier: %" ~ fmt.spec);
934 		}
935 	}
936 	///
937 	unittest
938 	{
939 		const b = StaticBitArray!16(([0, 0, 0, 0, 1, 1, 1, 1,
940 									  0, 0, 0, 0, 1, 1, 1, 1]));
941 		const s1 = format("%s", b);
942 		version (none) assert(s1 == "[0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]"); /+ TODO: enable +/
943 
944 		version (none) const s2 = format("%b", b); /+ TODO: enable +/
945 		version (none) assert(s2 == "00001111_00001111"); /+ TODO: enable +/
946 	}
947 
948 	private void formatBitString(Sink)(ref scope Sink sink) const @trusted
949 	{
950 		import std.range.primitives : put;
951 
952 		static if (length) {
953 			const leftover = capacity % 8;
954 			foreach (immutable ix; 0 .. leftover) {
955 				const bit = this[ix];
956 				const char[1] res = cast(char)(bit + '0');
957 				sink.put(res[]);
958 			}
959 
960 			if (leftover &&
961 				capacity > 8)
962 				sink.put("_");	// separator
963 
964 			size_t cnt;
965 			foreach (immutable ix; leftover .. capacity) {
966 				const bit = this[ix];
967 				const char[1] res = cast(char)(bit + '0');
968 				sink.put(res[]);
969 				if (++cnt == 8 && ix != capacity - 1) {
970 					sink.put("_");  // separator
971 					cnt = 0;
972 				}
973 			}
974 		}
975 	}
976 
977 	private void formatBitSet(Sink)(ref scope Sink sink) const @trusted
978 	{
979 		sink("[");
980 		foreach (immutable ix; 0 .. capacity) {
981 			const bit = this[ix];
982 			const char[1] res = cast(char)(bit + '0');
983 			sink(res[]);
984 			if (ix+1 < capacity) { sink(", "); } // separator
985 		}
986 		sink("]");
987 	}
988 
989 private:
990 	pragma(inline, true)
991 	inout(Block)[] _fullBlocks() inout @trusted => _blocks.ptr[0 .. (length / bitsPerBlock)];
992 
993 	static if (blockCount) {
994 		Block _restBlock() const @trusted
995 		{
996 			const restBitCount = length % bitsPerBlock;
997 			return _blocks[blockCount-1] & ((1UL << restBitCount) - 1);
998 		}
999 	}
1000 }
1001 
1002 /// run-time
1003 pure nothrow @safe @nogc unittest {
1004 	import nxt.algorithm.comparison : equal;
1005 
1006 	enum m = 256;
1007 
1008 	StaticBitArray!m b0;
1009 
1010 	import nxt.modulo : Mod;
1011 	static assert(is(typeof(b0.oneIndexes.front()) == Mod!m));
1012 
1013 	b0[1] = 1;
1014 	b0[2] = 1;
1015 
1016 	b0[m/2 - 11] = 1;
1017 	b0[m/2 - 1] = 1;
1018 	b0[m/2] = 1;
1019 	b0[m/2 + 1] = 1;
1020 	b0[m/2 + 11] = 1;
1021 
1022 	b0[m - 3] = 1;
1023 	b0[m - 2] = 1;
1024 
1025 	assert(b0.oneIndexes.equal([1, 2,
1026 								m/2 - 11, m/2 - 1, m/2, m/2 + 1, m/2 + 11,
1027 								m - 3,
1028 								m - 2].s[]));
1029 	assert(b0.countOnes == 9);
1030 }
1031 
1032 /// run-time
1033 pure nothrow @safe @nogc unittest {
1034 	import nxt.algorithm.comparison : equal;
1035 
1036 	enum m = 256;
1037 
1038 	StaticBitArray!m b0;
1039 
1040 	import nxt.modulo : Mod;
1041 	static assert(is(typeof(b0.oneIndexes.front()) == Mod!m));
1042 
1043 	b0[0] = 1;
1044 	b0[1] = 1;
1045 	b0[m/2 - 11] = 1;
1046 	b0[m/2 - 1] = 1;
1047 	b0[m/2] = 1;
1048 	b0[m/2 + 1] = 1;
1049 	b0[m/2 + 11] = 1;
1050 	b0[m - 2] = 1;
1051 	b0[m - 1] = 1;
1052 
1053 	assert(b0.oneIndexes.equal([0, 1,
1054 								m/2 - 11, m/2 - 1, m/2, m/2 + 1, m/2 + 11,
1055 								m - 2,
1056 								m - 1].s[]));
1057 	assert(b0.countOnes == 9);
1058 }
1059 
1060 /// ditto
1061 pure nothrow @safe @nogc unittest {
1062 	import std.traits : isIterable;
1063 	static assert(isIterable!(StaticBitArray!256));
1064 }
1065 
1066 /// test ubyte access
1067 pure nothrow @safe @nogc unittest {
1068 	auto b8 = StaticBitArray!(8, ubyte)();
1069 	b8[0] = 1;
1070 	b8[1] = 1;
1071 	b8[3] = 1;
1072 	b8[6] = 1;
1073 
1074 	assert(b8.ubytes == [64 + 8 + 2 + 1].s[]);
1075 
1076 	alias Ix = b8.Index;
1077 	Ix nextIx;
1078 
1079 	assert(b8.canFindIndexOf(true, Ix(0), nextIx));
1080 	assert(nextIx == 0);
1081 
1082 	assert(b8.canFindIndexOf(true, Ix(1), nextIx));
1083 	assert(nextIx == 1);
1084 
1085 	assert(b8.canFindIndexOf(true, Ix(2), nextIx));
1086 	assert(nextIx == 3);
1087 
1088 	assert(b8.canFindIndexOf(true, Ix(3), nextIx));
1089 	assert(nextIx == 3);
1090 
1091 	assert(b8.canFindIndexOf(true, Ix(4), nextIx));
1092 	assert(nextIx == 6);
1093 
1094 	assert(!b8.canFindIndexOf(true, Ix(7), nextIx));
1095 }
1096 
1097 /// test all zero and all one predicates
1098 pure nothrow @safe @nogc unittest {
1099 	static void test(size_t restBitCount)() {
1100 		enum n = 8*size_t.sizeof + restBitCount;
1101 
1102 		auto bs = StaticBitArray!(n, size_t)();
1103 
1104 		assert(bs.allZero);
1105 		assert(!bs.allOne);
1106 
1107 		foreach (immutable i; 0 .. n - 1) {
1108 			bs[i] = true;
1109 			assert(!bs.allZero);
1110 			assert(!bs.allOne);
1111 		}
1112 		bs[n - 1] = true;
1113 
1114 		assert(bs.allOne);
1115 	}
1116 	test!0;
1117 	test!1;
1118 	test!2;
1119 	test!37;
1120 	test!62;
1121 	test!63;
1122 }
1123 
1124 /// ditto
1125 version (none) /+ TODO: enable +/
1126 @safe unittest {
1127 	import std.format : format;
1128 
1129 	const b0_ = StaticBitArray!0([]);
1130 	const b0 = b0_;
1131 	assert(format("%s", b0) == "[]");
1132 	assert(format("%b", b0) is null);
1133 
1134 	const b1_ = StaticBitArray!1([1]);
1135 	const b1 = b1_;
1136 	assert(format("%s", b1) == "[1]");
1137 	assert(format("%b", b1) == "1");
1138 
1139 	const b4 = StaticBitArray!4([0, 0, 0, 0]);
1140 	assert(format("%b", b4) == "0000");
1141 
1142 	const b8 = StaticBitArray!8([0, 0, 0, 0, 1, 1, 1, 1]);
1143 	assert(format("%s", b8) == "[0, 0, 0, 0, 1, 1, 1, 1]");
1144 	assert(format("%b", b8) == "00001111");
1145 
1146 	const b16 = StaticBitArray!16([0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]);
1147 	assert(format("%s", b16) == "[0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]");
1148 	assert(format("%b", b16) == "00001111_00001111");
1149 
1150 	const b9 = StaticBitArray!9([1, 0, 0, 0, 0, 1, 1, 1, 1]);
1151 	assert(format("%b", b9) == "1_00001111");
1152 
1153 	const b17 = StaticBitArray!17([1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]);
1154 	assert(format("%b", b17) == "1_00001111_00001111");
1155 }
1156 
1157 /// test range
1158 pure nothrow @safe unittest {
1159 	static testRange(Block)() {
1160 		StaticBitArray!(6, Block) bs = [false, 1, 0, 0, true, 0];
1161 		bs.put(3, true);
1162 
1163 		import nxt.algorithm.comparison : equal;
1164 
1165 		assert(bs[0] == false);
1166 		assert(bs[1] == true);
1167 		assert(bs[2] == false);
1168 		assert(bs[3] == true);
1169 		assert(bs[4] == true);
1170 		assert(bs[5] == false);
1171 
1172 		assert(bs.at!0 == false);
1173 		assert(bs.at!1 == true);
1174 		assert(bs.at!2 == false);
1175 		assert(bs.at!3 == true);
1176 		assert(bs.at!4 == true);
1177 		assert(bs.at!5 == false);
1178 
1179 		// test slicing
1180 		assert(bs[].equal([0, 1, 0, 1, 1, 0].s[]));
1181 		assert(bs[1 .. 4].equal([1, 0, 1].s[]));
1182 
1183 		auto rs = bs[1 .. 6 - 1]; /+ TODO: Use opDollar +/
1184 		assert(rs.length == 4);
1185 		assert(rs.front == true);
1186 		assert(rs.back == true);
1187 
1188 		rs.popFront();
1189 		assert(rs.front == false);
1190 		assert(rs.back == true);
1191 
1192 		rs.popBack();
1193 		assert(rs.front == false);
1194 		assert(rs.back == true);
1195 
1196 		rs.popFront();
1197 		assert(rs.front == true);
1198 		assert(rs.back == true);
1199 
1200 		rs.popBack();
1201 		assert(rs.length == 0);
1202 		assert(rs.empty);
1203 	}
1204 
1205 	import std.meta : AliasSeq;
1206 	foreach (Block; AliasSeq!(ubyte, ushort, uint, ulong, size_t))
1207 		testRange!Block;
1208 }
1209 
1210 ///
1211 pure nothrow @safe @nogc unittest {
1212 	alias Block = size_t;
1213 	enum blockCount = 2;
1214 	enum n = blockCount * 8*Block.sizeof - 1;
1215 	StaticBitArray!(n) x;
1216 	static assert(x.blockCount == blockCount);
1217 
1218 	assert(x.indexOfFirstOne == n);
1219 	x[n - 1] = true;
1220 	assert(x.indexOfFirstOne == x.length - 1);
1221 	x[n - 2] = true;
1222 	assert(x.indexOfFirstOne == x.length - 2);
1223 
1224 	x[n/2 + 1] = true;
1225 	assert(x.indexOfFirstOne == x.length/2 + 1);
1226 	x[n/2] = true;
1227 	assert(x.indexOfFirstOne == x.length/2);
1228 	x[n/2 - 1] = true;
1229 	assert(x.indexOfFirstOne == x.length/2 - 1);
1230 
1231 	x[0] = true;
1232 	assert(x.indexOfFirstOne == 0);
1233 	assert(x[0]);
1234 	assert(!x[1]);
1235 
1236 	x[1] = true;
1237 	assert(x[1]);
1238 
1239 	x[1] = false;
1240 	assert(!x[1]);
1241 }
1242 
1243 /// Test opSliceAssign.
1244 pure nothrow @safe @nogc unittest {
1245 	alias Block = size_t;
1246 	enum blockCount = 2;
1247 	enum n = blockCount * 8*Block.sizeof - 1;
1248 
1249 	StaticBitArray!(n) x;
1250 	assert(x.countOnes == 0);
1251 
1252 	x[] = true;
1253 	assert(x.countOnes == n);
1254 
1255 	x[] = false;
1256 	assert(x.countOnes == 0);
1257 }
1258 
1259 version (unittest) {
1260 	import nxt.array_help : s;
1261 }