/*
* 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.DataInputStream;
import java.io.DataOutputStream;
import java.io.Externalizable;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.OutputStream;
import java.util.Arrays;
public class BytesRank1OnlySuccinctBitVector
implements Externalizable, SuccinctBitVector{
public BytesRank1OnlySuccinctBitVector(){
this(16);
}
public BytesRank1OnlySuccinctBitVector(int initialCapacity){
bytes = new byte[containerCount(initialCapacity, 8)];
countCache1 = new int[containerCount(bytes.length, CACHE_WIDTH / 8)];
}
public BytesRank1OnlySuccinctBitVector(byte[] bytes, int bits){
this.size = bits;
this.bytes = Arrays.copyOf(bytes, containerCount(bits, 8));
this.countCache1 = new int[containerCount(bytes.length, 8)];
int sum = BITCOUNTS1[bytes[0] & 0xff];
int n = bytes.length;
for(int i = 1; i < n; i++){
if(i % 8 == 0) countCache1[(i / 8) - 1] = sum;
sum += BITCOUNTS1[bytes[i] & 0xff];
}
if(countCache1.length > n / 8){
countCache1[n / 8] = sum;
}
}
public BytesRank1OnlySuccinctBitVector(byte[] bytes, int size, int[] countCache1) {
this.bytes = bytes;
this.size = size;
this.countCache1 = countCache1;
}
public byte[] getBytes() {
return bytes;
}
public int[] getCountCache1() {
return countCache1;
}
@Override
public String toString() {
StringBuilder b = new StringBuilder();
int n = Math.min(size, 32);
for(int i = 0; i < n; i++){
b.append((bytes[(i / 8)] & (0x80 >> (i % 8))) != 0 ? "1" : "0");
}
return b.toString();
}
@Override
public boolean get(int pos) {
return isOne(pos);
}
@Override
public boolean isZero(int pos){
return (bytes[pos / 8] & BITS[pos % 8]) == 0;
}
@Override
public boolean isOne(int pos) {
return (bytes[pos / 8] & BITS[pos % 8]) != 0;
}
@Override
public int size(){
return this.size;
}
public void trimToSize(){
int vectorSize = size / 8 + 1;
bytes = Arrays.copyOf(bytes, Math.min(bytes.length, vectorSize));
int blockSize = CACHE_WIDTH / 8;
int size = vectorSize / blockSize + (((vectorSize % blockSize) != 0) ? 1 : 0);
int countCacheSize0 = size;
countCache1 = Arrays.copyOf(countCache1, Math.min(countCache1.length, countCacheSize0));
}
public void append1(){
int i = size / 8;
int ci = size / CACHE_WIDTH;
prepareAppend(i, ci);
countCache1[ci]++;
int r = size % 8;
bytes[i] |= BITS[r];
size++;
}
public void append0(){
int i = size / 8;
int ci = size / CACHE_WIDTH;
prepareAppend(i, 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();
}
@Override
public int rank0(int pos) {
int cn = pos / CACHE_WIDTH;
if((pos + 1) % CACHE_WIDTH == 0) return (cn + 1) * CACHE_WIDTH - countCache1[cn];
int ret = (cn > 0) ? ret = cn * CACHE_WIDTH - countCache1[cn - 1] : 0;
int n = pos / 8;
for(int i = (cn * (CACHE_WIDTH / 8)); i < n; i++){
ret += 8 - BITCOUNTS1[bytes[i] & 0xff];
}
return ret + 8 - BITCOUNTS1[bytes[n] & MASKS[pos % 8]];
}
@Override
public int rank1(int pos){
int cn = pos / CACHE_WIDTH;
if((pos + 1) % CACHE_WIDTH == 0) return countCache1[cn];
if(cn > 0){
int ret = countCache1[cn - 1];
int n = pos / 8;
for(int i = (cn * (CACHE_WIDTH / 8)); i < n; i++){
ret += BITCOUNTS1[bytes[i] & 0xff];
}
return ret + BITCOUNTS1[bytes[n] & MASKS[pos % 8]];
} else{
int ret = 0;
int n = pos / 8;
for(int i = 0; i < n; i++){
ret += BITCOUNTS1[bytes[i] & 0xff];
}
return ret + BITCOUNTS1[bytes[n] & MASKS[pos % 8]];
}
/* int ret = 0;
/*
if(cn > 0){
ret = countCache1[cn - 1];
}
int n = pos / 8;
for(int i = (cn * (CACHE_WIDTH / 8)); i < n; i++){
ret += BITCOUNTS1[vector[i] & 0xff];
}
/*
int i = 0;
if(cn > 0){
ret = countCache1[cn - 1];
i = cn * CACHE_WIDTH / 8;
}
int n = pos / 8;
for(; i < n; i++){
ret += BITCOUNTS1[vector[i] & 0xff];
}
//*/
// return ret + BITCOUNTS1[vector[n] & MASKS[pos % 8]];
}
public void save(OutputStream os) throws IOException{
DataOutputStream dos = new DataOutputStream(os);
dos.writeInt(size);
trimToSize();
dos.writeInt(bytes.length);
dos.write(bytes);
dos.writeInt(countCache1.length);
for(int e : countCache1){
dos.writeInt(e);
}
dos.flush();
}
public void load(InputStream is) throws IOException{
DataInputStream dis = new DataInputStream(is);
size = dis.readInt();
int vectorSize = dis.readInt();
bytes = new byte[vectorSize];
dis.read(bytes, 0, vectorSize);
int size = dis.readInt();
countCache1 = new int[size];
for(int i = 0; i < size; i++){
countCache1[i] = dis.readInt();
}
}
private static int containerCount(int size, int unitSize){
return size / unitSize + ((size % unitSize) != 0 ? 1 : 0);
}
private int prepareAppend(int i, int ci){
if(i >= bytes.length){
extend();
}
if(size % CACHE_WIDTH == 0 && ci > 0){
countCache1[ci] = countCache1[ci - 1];
}
return i;
}
@Override
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException {
bytes = (byte[])in.readObject();
size = in.readInt();
countCache1 = (int[])in.readObject();
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(bytes);
out.writeInt(size);
out.writeObject(countCache1);
}
private void extend(){
int vectorSize = (int)(bytes.length * 1.2) + 1;
bytes = Arrays.copyOf(bytes, vectorSize);
int blockSize = CACHE_WIDTH / 8;
int size = vectorSize / blockSize + (((vectorSize % blockSize) != 0) ? 1 : 0);
countCache1 = Arrays.copyOf(countCache1, size);
}
private static final int CACHE_WIDTH = 64;
private byte[] bytes;
private int size;
private int[] countCache1;
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[] 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;
}