package ivory.bloomir.data; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import com.google.common.base.Preconditions; import org.apache.hadoop.io.Writable; /** * Bits is a bit-packet abstraction, containing a number of bits * inside an int array for efficiency * * @author Nima Asadi */ public class Bits implements Writable { private static final int UNIT_SIZE = 32; private static final int UNIT_SIZE_1 = UNIT_SIZE - 1; private static final int UNIT_EXP = 5; private int[] bits; private int length; /** * Default constructor to be used by the read() method */ public Bits() { } /** * Copy constructor. * * @param other Bits object to be copied */ public Bits(Bits other) { Preconditions.checkNotNull(other); this.length = other.length; bits = new int[other.bits.length]; for(int i = 0; i < bits.length; i++) { bits[i] = other.bits[i]; } } public Bits(Bits other, int length) { this(length); Preconditions.checkNotNull(other); for(int i = 0; i < bits.length; i++) { bits[i] = other.bits[i]; } } /** * Constructs a Bits vector of a given length * * @param length Length of the vector. */ public Bits(int length) { this(length, false); } /** * Constructs a Bits vector of a given length filled with * the given value. * * @param length Length of the vector * @param value Default value for bits */ public Bits(int length, boolean value) { Preconditions.checkArgument(length > 0); this.length = length; int r = length >> UNIT_EXP; //Divide int m = length & (UNIT_SIZE - 1); //Modulus if(m != 0) { bits = new int[r+1]; } else { bits = new int[r]; } for (int i = 0; i < bits.length; i++) { bits[i] = (value == false) ? 0 : 0xffffffff; } } /** * Gets the total number of bits (i.e., the length of the vector) * * @return Length of the vector */ public int size() { return length; } /** * Sets the value of a bit. * * @param p Index of the bit * @param on Value of the bit */ public void set(int p, boolean on) { Preconditions.checkArgument(p < length); if(on) { set(p); } else { clear(p); } } /** * Sets a bit to 1 * * @param p Index of the bit */ public void set(int p) { Preconditions.checkArgument(p < length); bits[p>>UNIT_EXP] |= 1<<(UNIT_SIZE_1 - (p & (UNIT_SIZE_1))); } /** * Clears a bit * * @param p Index of the bit */ public void clear(int p) { Preconditions.checkArgument(p < length); bits[p>>UNIT_EXP] &= ~(1<<(UNIT_SIZE_1 - (p&(UNIT_SIZE_1)))); } /** * Gets the value of a given bit index * * @param p Index of the bit * @return A boolean value indicating whether a bit is on or off */ public boolean get(int p) { Preconditions.checkArgument(p < length); return ((bits[p>>UNIT_EXP]>>(UNIT_SIZE_1 - (p&(UNIT_SIZE_1)))) & 1) == 1; } public int getBits(int e, int b) { Preconditions.checkArgument(b < length && b - e < 32); if(e == b) { if(get(e)) { return 1; } return 0; } int p_e = e >> UNIT_EXP; int r_e = e & UNIT_SIZE_1; int p_b = b >> UNIT_EXP; int r_b = b & UNIT_SIZE_1; int s = UNIT_SIZE_1 - r_b; int v = bits[p_e]; v &= (0xFFFFFFFF >>> r_e); if(p_e == p_b) { return (v & (0xFFFFFFFF << s)) >>> s; } else { v = v << (r_b + 1); return v | ((bits[p_b] & (0xFFFFFFFF << s)) >>> s); } } /** * Clears the vector */ public void clear() { for(int i = 0; i < bits.length; i++) { bits[i] = 0; } } /** * Counts the number of set elements * * @return Number of elements that are 1 */ public int count() { int total = 0; for(int i = 0; i < bits.length; i++) { total += count(bits[i]); } return total; } /** * Counts the number of set elements in an integer (Popcount algorithm) * * @param value An integer value * @return Number of 1 elements in the given integer value */ private static int count(int value) { value = (value & 0x55555555) + ((value & 0xaaaaaaaa) >>> 1); value = (value & 0x33333333) + ((value & 0xcccccccc) >>> 2); value = (value & 0x0f0f0f0f) + ((value & 0xf0f0f0f0) >>> 4); value = (value & 0x00ff00ff) + ((value & 0xff00ff00) >>> 8); return (value & 0x0000ffff) + ((value & 0xffff0000) >>> 16); } /** * Retrieves the indexes of the set elements in the vector * * @return Array of indexes */ public int[] getOnes() { int[] documents = new int[count()]; int pos = 0; for(int i = 0; i < size(); i++) { if(get(i)) { documents[pos++] = i; } } return documents; } /** * Bitwise AND operator * * @param other A second Bits vector to AND */ public void and(Bits other) { Preconditions.checkNotNull(other); Preconditions.checkArgument(this.length == other.length); for(int i = 0; i < bits.length; i++) { bits[i] &= other.bits[i]; } } /** * Bitwise OR operator * * @param other A second Bits vector to OR */ public void or(Bits other) { Preconditions.checkNotNull(other); Preconditions.checkArgument(this.length == other.length); for(int i = 0; i < bits.length; i++) { bits[i] |= other.bits[i]; } } /** * Bitwise XOR operator * * @param other A second Bits vector to XOR */ public void xor(Bits other) { Preconditions.checkNotNull(other); Preconditions.checkArgument(this.length == other.length); for(int i = 0; i < bits.length; i++) { bits[i] ^= other.bits[i]; } } //Writable interface @Override public void readFields(DataInput in) throws IOException { Preconditions.checkNotNull(in); length = in.readInt(); bits = new int[in.readInt()]; for(int i = 0; i < bits.length; i++) { bits[i] = in.readInt(); } } @Override public void write(DataOutput out) throws IOException { Preconditions.checkNotNull(out); out.writeInt(length); out.writeInt(bits.length); for(int i = 0; i < bits.length; i++){ out.writeInt(bits[i]); } } @Override public String toString() { StringBuilder b = new StringBuilder(); for(int i = 0; i < length; i++) { if(get(i)) { b.append('1'); } else { b.append('0'); } } return b.toString(); } @Override public boolean equals(Object o) { Preconditions.checkNotNull(o); Preconditions.checkArgument(o instanceof Bits); Bits other = (Bits) o; if(this.length == other.length) { int index = 0; for(int i = 0; i < this.bits.length; i++) { index += (i + 1) * UNIT_SIZE; if(index > length) { for(int p = i * UNIT_SIZE; p < length; p++) { if(get(p) != other.get(p)) { return false; } } } else { if(this.bits[i] != other.bits[i]) { return false; } } } } else { return false; } return true; } }