/*
* Copyright 2012 Takao Nakaguchi
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.trie4j.bv;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Arrays;
public class BytesRank0OnlySuccinctBitVector
implements Externalizable, SuccinctBitVector{
public BytesRank0OnlySuccinctBitVector(){
this(16);
}
public BytesRank0OnlySuccinctBitVector(int initialCapacity){
vector = new byte[containerCount(initialCapacity, 8)];
countCache0 = new int[containerCount(vector.length, CACHE_WIDTH / 8)];
}
public BytesRank0OnlySuccinctBitVector(byte[] bytes, int bits){
this.size = bits;
this.vector = Arrays.copyOf(bytes, containerCount(bits, 8));
this.countCache0 = new int[containerCount(vector.length, 8)];
int sum = BITCOUNTS0[bytes[0] & 0xff];
int n = vector.length;
for(int i = 1; i < n; i++){
if(i % 8 == 0) countCache0[(i / 8) - 1] = sum;
sum += BITCOUNTS0[bytes[i] & 0xff];
}
if(countCache0.length > 0){
countCache0[n / 8] = sum;
}
}
public BytesRank0OnlySuccinctBitVector(byte[] vector, int size, int[] countCache0) {
this.vector = vector;
this.size = size;
this.countCache0 = countCache0;
}
public byte[] getVector() {
return vector;
}
public int[] getCountCache0() {
return countCache0;
}
@Override
public String toString() {
StringBuilder b = new StringBuilder();
int n = Math.min(size, 32);
for(int i = 0; i < n; i++){
b.append((vector[(i / 8)] & (0x80 >> (i % 8))) != 0 ? "1" : "0");
}
return b.toString();
}
@Override
public boolean get(int pos) {
return isOne(pos);
}
public boolean isZero(int pos){
return (vector[pos / 8] & BITS[pos % 8]) == 0;
}
@Override
public boolean isOne(int pos) {
return (vector[pos / 8] & BITS[pos % 8]) != 0;
}
public int size(){
return this.size;
}
public void trimToSize(){
int vectorSize = size / 8 + 1;
vector = Arrays.copyOf(vector, Math.min(vector.length, vectorSize));
int blockSize = CACHE_WIDTH / 8;
int size = vectorSize / blockSize + (((vectorSize % blockSize) != 0) ? 1 : 0);
int countCacheSize0 = size;
countCache0 = Arrays.copyOf(countCache0, Math.min(countCache0.length, countCacheSize0));
}
public void append1(){
int i = size / 8;
int ci = size / CACHE_WIDTH;
if(i >= vector.length){
extend();
}
if(size % CACHE_WIDTH == 0 && ci > 0){
countCache0[ci] = countCache0[ci - 1];
}
int r = size % 8;
vector[i] |= BITS[r];
size++;
}
public void append0(){
int i = size / 8;
int ci = size / CACHE_WIDTH;
if(i >= vector.length){
extend();
}
if(size % CACHE_WIDTH == 0 && ci > 0){
countCache0[ci] = countCache0[ci - 1];
}
// int r = size % 8;
// vector[i] &= ~BITS[r];
countCache0[ci]++;
size++;
}
@Override
public int select0(int num) {
throw new UnsupportedOperationException();
}
@Override
public int next0(int count) {
throw new UnsupportedOperationException();
}
@Override
public int select1(int num) {
throw new UnsupportedOperationException();
}
public int rank0(int pos){
int cn = pos / CACHE_WIDTH;
if((pos + 1) % CACHE_WIDTH == 0) return countCache0[cn];
int ret = (cn > 0) ? ret = countCache0[cn - 1] : 0;
int n = pos / 8;
for(int i = (cn * (CACHE_WIDTH / 8)); i < n; i++){
ret += BITCOUNTS0[vector[i] & 0xff];
}
return ret + BITCOUNTS0[(vector[n] | ~MASKS[pos % 8]) & 0xff];
}
@Override
public int rank1(int pos) {
int cn = pos / CACHE_WIDTH;
if((pos + 1) % CACHE_WIDTH == 0) return (cn + 1) * CACHE_WIDTH - countCache0[cn];
int ret = (cn > 0) ? cn * CACHE_WIDTH - countCache0[cn - 1] : 0;
int n = pos / 8;
for(int i = (cn * CACHE_WIDTH / 8); i < n; i++){
ret += BITCOUNTS1[vector[i] & 0xff];
}
return ret + BITCOUNTS1[(vector[n] & (0x80 >> (pos % 8))) & 0xff];
}
@Override
public void readExternal(ObjectInput in)
throws ClassNotFoundException, IOException{
size = in.readInt();
vector = (byte[])in.readObject();
countCache0 = (int[])in.readObject();
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
trimToSize();
out.writeInt(size);
out.writeObject(vector);
out.writeObject(countCache0);
}
private static int containerCount(int size, int unitSize){
return size / unitSize + ((size % unitSize) != 0 ? 1 : 0);
}
private void extend(){
int vectorSize = (int)(vector.length * 1.2) + 1;
vector = Arrays.copyOf(vector, vectorSize);
int blockSize = CACHE_WIDTH / 8;
int size = vectorSize / blockSize + (((vectorSize % blockSize) != 0) ? 1 : 0);
countCache0 = Arrays.copyOf(countCache0, size);
}
private static final int CACHE_WIDTH = 64;
private byte[] vector;
private int size;
private int[] countCache0;
private static final int[] MASKS = {
0x80, 0xc0, 0xe0, 0xf0
, 0xf8, 0xfc, 0xfe, 0xff
};
private static final byte[] BITS = {
(byte)0x80, (byte)0x40, (byte)0x20, (byte)0x10
, (byte)0x08, (byte)0x04, (byte)0x02, (byte)0x01
};
private static final byte[] BITCOUNTS0 = {
8, 7, 7, 6, 7, 6, 6, 5, 7, 6, 6, 5, 6, 5, 5, 4,
7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3,
7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3,
6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2,
7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3,
6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2,
6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2,
5, 4, 4, 3, 4, 3, 3, 2, 4, 3, 3, 2, 3, 2, 2, 1,
7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3,
6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2,
6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2,
5, 4, 4, 3, 4, 3, 3, 2, 4, 3, 3, 2, 3, 2, 2, 1,
6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2,
5, 4, 4, 3, 4, 3, 3, 2, 4, 3, 3, 2, 3, 2, 2, 1,
5, 4, 4, 3, 4, 3, 3, 2, 4, 3, 3, 2, 3, 2, 2, 1,
4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0,
};
private static final byte[] BITCOUNTS1 = {
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
};
private static final long serialVersionUID = -7658605229245494623L;
}