1 module bitarray.bitarray;
2 
3 import std.conv : to;
4 import lib.exception : ValueError;
5 
6 /**
7   Count 1 bits in x reperesented as binary.
8 
9 Parameters:
10 n_bits = The size of the counted range in bits. The maximum value is 8.
11  */
12 uint countOneBits(int x, uint n_bits) {
13     assert(n_bits <= 8);
14     x = x & ((1 << n_bits) - 1); //bitmask
15     x = x - ((x >> 1) & 0x55555555);
16     x = (x & 0x33333333)  + ((x >> 2) & 0x33333333);
17     x = (x + (x >> 4)) & 0x0f0f0f0f;
18     x = x + (x >> 8);
19     x = x + (x >> 16);
20     x = x & 0x0000003f;
21     return x;
22 }
23 
24 ///
25 unittest {
26     uint[] bits = [0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1];
27     SuccinctBitVector bitvector = new SuccinctBitVector();
28     foreach(uint bit; bits) {
29         bitvector.append(bit);
30     }
31 
32     ///bitvector.bitarray[0] = [0, 1, 1, 0, 1, 1, 0, 0]
33     //countOneBits(bitvector.bitarray[0], 2) should return the number of 1 bits
34     //in [0, 1]
35     assert(countOneBits(bitvector.bitarray[0], 2) == 1);
36 
37     //countOneBits(bitvector.bitarray[0], 3) should return the number of 1 bits
38     //in [0, 1, 1]
39     assert(countOneBits(bitvector.bitarray[0], 3) == 2);
40 
41     ///bitvector.bitarray[1] = [1, 0, 0, 0, 0, 0, 0, 1]
42     //countOneBits(bitvector.bitarray[1], 2) should return the number of 1 bits
43     //in [1, 0]
44     assert(countOneBits(bitvector.bitarray[1], 2) == 1);
45     //countOneBits(bitvector.bitarray[1], 8) should return the number of 1 bits
46     //in [1, 0, 0, 0, 0, 0, 0, 1]
47     assert(countOneBits(bitvector.bitarray[1], 8) == 2);
48 }
49 
50 
51 class BitArray {
52     private int[] bitarray;
53     private uint length_;  //the length of the bitarray
54 
55     this() {
56         this.length_ = 0;
57     }
58 
59     @property uint length() {
60         return this.length_;
61     }
62 
63     /**
64       Push one bit to the bitarray.
65      */
66     void append(uint bit)
67     in {
68         if(!(bit == 0 || bit == 1)) {
69             throw new ValueError("Bit must be 0 or 1.");
70         }
71     }
72     body {
73         uint shift = this.length_ % 8;
74         //append 0 to expand bitarray.
75         if(shift == 0) {
76             this.bitarray ~= 0;
77         }
78 
79         this.bitarray[$-1] |= (bit & 1) << shift;
80         this.length_ += 1;
81     }
82     unittest {
83         SuccinctBitVector bitvector = new SuccinctBitVector();
84 
85         bool error_thrown = false;
86         try {
87             bitvector.append(2);
88         } catch(ValueError e) {
89             error_thrown = true;
90         }
91         assert(error_thrown);
92     }
93 
94     /**
95       Get bit at the specified position.
96      */
97     uint opIndex(uint position)
98     in {
99         if(position >= this.length_) {
100             throw new ValueError("The position is out of range.");
101         }
102     }
103     body {
104         uint shift = position % 8;
105         return 0x1 & (this.bitarray[position/8] >> shift);
106     }
107     unittest {
108         import std.random : uniform;
109         import lib.random.random : randomArray;
110 
111         uint size = 20;
112         uint[] bits = cast(uint[])randomArray(0, 2, size);
113         SuccinctBitVector bitvector = new SuccinctBitVector();
114         foreach(uint bit; bits) {
115             bitvector.append(bit);
116         }
117 
118         foreach(uint i, uint bit; bits) {
119             assert(bitvector[i] == bit);
120         }
121     }
122     unittest {
123         uint[20] bits = 0;
124 
125         SuccinctBitVector bitvector = new SuccinctBitVector();
126         foreach(uint bit; bits) {
127             bitvector.append(bit);
128         }
129         bitvector.build();
130 
131         bool error_thrown = false;
132         try {
133             bitvector[20];
134         } catch(ValueError e) {
135             error_thrown = true;
136         }
137         assert(error_thrown);
138     }
139 
140     private string bitToString(int bit) {
141         string bits = "";
142 
143         for(int i = 0; i < 8; i++) {
144             int b = bit & (1 << i);
145             if(b > 0) {
146                 bits ~= '1';
147             } else {
148                 bits ~= '0';
149             }
150         }
151         return to!string(bits);
152     }
153 
154     override string toString() {
155         string bits = "";
156 
157         foreach(int e; bitarray) {
158             bits ~= bitToString(e);
159         }
160         return bits;
161     }
162 
163     /**
164       Returns the bitarray.
165      */
166     string dump() {
167         return to!string(this.bitarray);
168     }
169 }
170 
171 
172 /**
173   Implementation of succinct bit vector.
174  */
175 class SuccinctBitVector : BitArray {
176     //HACK tune the size
177     private static immutable uint large_block_size = 1 << 8;
178     //the size of a small block is the size of a block in bits.
179     private static immutable uint small_block_size = 8;
180     private uint n_large_blocks;
181     private uint n_small_blocks;
182     private uint[] large_blocks;
183     private uint[] small_blocks;
184     private bool built = false;
185 
186     void build() {
187         //TODO show an error message
188         assert(this.length_ > 0);
189 
190         this.n_large_blocks = this.length_/this.large_block_size + 1;
191         this.n_small_blocks = this.length_/this.small_block_size + 1;
192         this.fillLargeBlocks();
193         this.fillSmallBlocks();
194         this.built = true;
195     }
196 
197     private uint countOneBitsInBlock(uint position) {
198         assert(position % 8 == 0);
199 
200         uint n_bits;
201         foreach(int block; this.bitarray[0..position/8]) {
202             n_bits += countOneBits(block, 8);
203         }
204         return n_bits;
205     }
206 
207     unittest {
208         uint[] bits = [0, 1, 0, 1, 0, 0, 0, 1, 1, 1];
209 
210         SuccinctBitVector bitvector = new SuccinctBitVector();
211         foreach(uint bit; bits) {
212             bitvector.append(bit);
213         }
214         bitvector.build();
215 
216         assert(bitvector.countOneBitsInBlock(0) == 0);
217         assert(bitvector.countOneBitsInBlock(8) == 3);
218     }
219 
220     void fillLargeBlocks() {
221         this.large_blocks = new uint[this.n_large_blocks];
222         for(uint i = 0; i < this.n_large_blocks; i++) {
223             uint t = i * this.large_block_size;
224             this.large_blocks[i] = this.countOneBitsInBlock(t);
225         }
226     }
227 
228     void fillSmallBlocks() {
229         this.small_blocks = new uint[this.n_small_blocks];
230         for(uint i = 0; i < this.n_small_blocks; i++) {
231             uint s = i * this.small_block_size;
232             uint t = s / this.large_block_size;
233             this.small_blocks[i] = this.countOneBitsInBlock(s);
234             this.small_blocks[i] -= this.large_blocks[t];
235         }
236     }
237 
238     /*
239        Returns the number of '0' bits in the bitvector.
240 
241 Parameters:
242 position = The end point of a counted range in the bitarray.
243      */
244     uint rank0(uint position)
245     in {
246         assert(this.built);
247     }
248     body {
249         return position+1-this.rank1(position);
250     }
251 
252     /*
253        Returns the number of '1' bits in the blocks.
254 
255 Parameters:
256 position = The end point of a counted range in the bitarray.
257      */
258     uint rank1(uint position)
259     in {
260         assert(this.built);
261     }
262     out(n_bits) {
263         assert(n_bits <= this.length_);
264     }
265     body {
266         uint s = position/this.large_block_size;
267         uint t = position/this.small_block_size;
268         uint u = t*this.small_block_size;
269         uint n_bits = 0;
270 
271         n_bits += this.large_blocks[s];
272         n_bits += this.small_blocks[t];
273 
274         //count bits in a remaining range
275         int block = this.bitarray[position/this.small_block_size];
276         n_bits += countOneBits(block, position-u+1);
277         return n_bits;
278     }
279 
280     /**
281      * The basic function of select.
282      */
283     uint selectBase(uint delegate(uint) rank, uint n)
284     out(location) {
285         //if select(n) doesn't exist
286         if(location >= this.length_) {
287             import std..string : format;
288             throw new ValueError(format("select(%d) doesn't exist.", n));
289         }
290     }
291     body {
292         //search the location by Binary Search.
293         //rank(select(i)) = i+1
294         int lower = 0;
295         int upper = this.length_ - 1;
296         int middle;
297 
298         while(lower <= upper) {
299             middle = lower + (upper-lower) / 2;
300             int r = rank(middle);
301             if(r < n+1) {
302                 lower = middle+1;
303             } else if(r == n+1) {
304                 break;
305             } else {
306                 upper = middle-1;
307             }
308         }
309 
310         while(middle >= 0 && rank(middle) == n+1) {
311             middle -= 1;
312         }
313 
314         return middle+1;
315     }
316 
317     /**
318       Return the location of the (n+1)th '0' bit in the bitarray.
319       Raise ValueError if select0(n) doesn't exist.
320      */
321     uint select0(uint n) {
322         return this.selectBase(&this.rank0, n);
323     }
324 
325     /**
326       Return the location of the (n+1)th '1' bit in the bitarray.
327       Raise ValueError if select1(n) doesn't exist.
328      */
329     uint select1(uint n) {
330         return this.selectBase(&this.rank1, n);
331     }
332 }
333 
334 
335 //smoke test
336 unittest {
337     uint[] bits = [0, 1, 0, 1, 0, 0, 0, 1, 1, 1];
338 
339     SuccinctBitVector bitvector = new SuccinctBitVector();
340     foreach(uint bit; bits) {
341         bitvector.append(bit);
342     }
343     bitvector.build();
344 
345     uint[] rank0_answers = [1, 1, 2, 2, 3, 4, 5, 5, 5, 5];
346     uint[] rank1_answers = [0, 1, 1, 2, 2, 2, 2, 3, 4, 5];
347     for(auto i = 0; i < 10; i++) {
348         assert(bitvector.rank0(i) == rank0_answers[i]);
349         assert(bitvector.rank1(i) == rank1_answers[i]);
350     }
351 
352     uint[] select0_answers = [0, 2, 4, 5, 6];
353     uint[] select1_answers = [1, 3, 7, 8, 9];
354 
355     for(auto i = 0; i < 5; i++) {
356         assert(bitvector.select0(i) == select0_answers[i]);
357         assert(bitvector.select1(i) == select1_answers[i]);
358     }
359 }
360 
361 
362 //boundary value analysis
363 unittest {
364     uint[10] bits = 0;
365 
366     SuccinctBitVector bitvector = new SuccinctBitVector();
367     foreach(uint bit; bits) {
368         bitvector.append(bit);
369     }
370     bitvector.build();
371 
372     assert(bitvector.rank0(bits.length-1) == bits.length);
373     assert(bitvector.rank1(bits.length-1) == 0);
374 }
375 
376 
377 unittest {
378     uint[10] bits = 0;
379 
380     SuccinctBitVector bitvector = new SuccinctBitVector();
381     foreach(uint bit; bits) {
382         bitvector.append(bit);
383     }
384     bitvector.build();
385 
386     //Asserterror should be thrown because select1(0) does not exist
387     bool error_thrown = false;
388     try {
389         bitvector.select1(0);
390     } catch(Error e) {
391         error_thrown = true;
392     }
393     assert(error_thrown);
394 }
395 
396 
397 //random test of select and rank
398 unittest {
399     import std.random : uniform;
400     import lib.random.random : randomArray;
401 
402     uint select(uint[] bits, uint n, uint targetBit) {
403         uint n_appearances = 0;
404         for(uint i = 0; i < bits.length; i++) {
405             if(bits[i] == targetBit) {
406                 n_appearances += 1;
407                 if(n_appearances == n+1) {
408                     return i;
409                 }
410             }
411         }
412         return cast(uint)bits.length;
413     }
414 
415     uint rank(uint[] bits, uint position, uint targetBit) {
416         uint n = 0;
417         foreach(uint bit; bits[0..position+1]) {
418             if(bit == targetBit) {
419                 n += 1;
420             }
421         }
422         return n;
423     }
424 
425     void testRandom() {
426         uint size = uniform(10, 1000);
427         uint[] bits = cast(uint[])randomArray(0, 2, size);
428 
429         SuccinctBitVector bitvector = new SuccinctBitVector();
430         foreach(uint bit; bits) {
431             bitvector.append(bit);
432         }
433         bitvector.build();
434 
435         uint position = uniform(0, size);
436         assert(bitvector.rank0(position) == rank(bits, position, 0));
437         assert(bitvector.rank1(position) == rank(bits, position, 1));
438 
439         uint n = uniform(0, size);
440 
441         if(n < rank(bits, size-1, 0)) {
442             assert(bitvector.select0(n) == select(bits, n, 0));
443         } else {
444             //AssertError should be thrown if n is greater than or
445             //equal to the number of 0 bits in bitvector.
446             bool error_thrown = false;
447             try {
448                 bitvector.select0(n);
449             } catch(Error e) {
450                 error_thrown = true;
451             }
452             assert(error_thrown);
453         }
454 
455         if(n < rank(bits, size-1, 1)) {
456             assert(bitvector.select1(n) == select(bits, n, 1));
457         } else {
458             //AssertError should be thrown if n is greater than or
459             //equal to the number of 1 bits in bitvector.
460             bool error_thrown = false;
461             try {
462                 bitvector.select1(n);
463             } catch(Error e) {
464                 error_thrown = true;
465             }
466             assert(error_thrown);
467         }
468     }
469 
470     for(auto i = 0; i < 200; i++) {
471         testRandom();
472     }
473 }