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 }