package ivory.lsh.data;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import tl.lin.data.array.ArrayListOfIntsWritable;
/**
*
* A version of BitsSignature specifically tuned for 64 bits. Uses an array of 8 bytes instead of the Bits object.
*
* @see ivory.lsh.data.NBitSignature
*
* @author ferhanture
*
*/
public class SixtyFourBitSignature extends Signature{//implements WritableComparable<BitsSignature64> {
private byte[] bits;
static int NUM_BITS = 64;
static int NUM_BYTES = 8;
/**
* Create a BitsSignature object with the specified number of bits, all initially set to 0.
*/
public SixtyFourBitSignature(){
super();
setBits(new byte[NUM_BYTES]);
}
public SixtyFourBitSignature(byte[] b){
setBits(new byte[NUM_BYTES]);
for(int i=0;i<b.length;i++){
getBits()[i]= b[i];
}
// bits = b;
}
public SixtyFourBitSignature(SixtyFourBitSignature other){
this(other.getBits());
}
public SixtyFourBitSignature(int numBits){ //need this constructor for general purposes.
super();
if(numBits!=NUM_BITS){
throw new RuntimeException("Wrong number of bits!");
}
setBits(new byte[NUM_BYTES]);
}
public void setBits(byte[] bits) {
this.bits = bits;
}
/**
* @param b
* first index to be included in sub-signature
* @param e
* last index to be included in sub-signature
* @return
* return a new BitsSignature object, containing the bits of this object from <code>start</code> to <code>end</code>
*/
public NBitSignature getSubSignature(int b, int e){
NBitSignature sub = new NBitSignature();
int l = 1 + ((b < e) ? e-b : b-e);
int r = l/8, m = l%8;
byte[] q = new byte[(m==0) ? r : r+1];
if (b <= e) // not reversed
for (int i=0; i<l; ++i) {
int p = b+i;
if ((getBits()[p/8] & (1<<(p%8))) != 0) q[i/8] |= 1<<(i%8);
}
else // reversed
for (int i=0; i<l; ++i) {
int p = b-i;
if ((getBits()[p/8] & (1<<(p%8))) != 0) q[i/8] |= 1<<(i%8);
}
sub.bits = new Bits(q,e-b+1);
return sub;
}
public NBitSignature getSubSignature(int b, int e, Signature subSign){
NBitSignature sub = (NBitSignature) subSign;
int l = 1 + ((b < e) ? e-b : b-e);
int r = l/8, m = l%8;
byte[] q = new byte[(m==0) ? r : r+1];
if (b <= e) // not reversed
for (int i=0; i<l; ++i) {
int p = b+i;
if ((getBits()[p/8] & (1<<(p%8))) != 0) q[i/8] |= 1<<(i%8);
}
else // reversed
for (int i=0; i<l; ++i) {
int p = b-i;
if ((getBits()[p/8] & (1<<(p%8))) != 0) q[i/8] |= 1<<(i%8);
}
sub.bits = new Bits(q,e-b+1);
return sub;
}
public void readFields(DataInput in) throws IOException {
// bits = new byte[NUM_BYTES];
for(int i=0;i<NUM_BYTES;i++){
getBits()[i]=in.readByte();
}
}
public void write(DataOutput out) throws IOException {
for(int i=0;i<NUM_BYTES;i++){
out.writeByte(getBits()[i]);
}
}
public boolean get(int i) {
return (((getBits()[i/8]>>(7-i%8)) & 1)==1);
}
public void set(int i, boolean sign) {
if (!sign) getBits()[i/8] &= ~(1<<(7-i%8)); // clear
else getBits()[i/8] |= 1<<(7-i%8); // set
}
public int size() {
return NUM_BITS;
}
@Override
public boolean equals(Object o){
if(o==null){
return false;
}
SixtyFourBitSignature other = (SixtyFourBitSignature) o;
if(other.size()!=this.size()){
return false;
}
for(int i=0;i<other.getBits().length;i++){
if((!other.get(i) && this.get(i))|| (other.get(i) && !this.get(i))){
return false;
}
}
return true;
}
public int compareTo(Object obj) {
SixtyFourBitSignature other = (SixtyFourBitSignature) obj;
return this.compareTo(other);
}
public int compareTo(SixtyFourBitSignature other) {
/*if(size()!=other.size()){
throw new RuntimeException("Cannot compare different sized signatures!");
}*/
boolean myBit, otherBit;
for(int i=0;i<size();i++){
myBit = get(i);
otherBit = other.get(i);
if(!myBit && otherBit){
return -1;
}else if(myBit && !otherBit){
return 1;
}
}
return 0;
}
@Override
public int hashCode(){
int h = getBits().toString().hashCode();
if(h>0){
return h ^ Integer.MAX_VALUE;
}else{
return h ^ Integer.MIN_VALUE;
}
}
@Override
public int hammingDistance(Signature signature, int threshold){
// float thr = (float) ((Math.acos(PWSimProbMethod.COSINE_TRESHOLD)/Math.PI)*this.size());
SixtyFourBitSignature s2 = (SixtyFourBitSignature) signature;
byte[] bb1 = this.getBits();
byte[] bb2 = s2.getBits();
int count=0;
/*byte by byte*/
for(int i=0;i<bb1.length;i++){
byte i1 = bb1[i], i2=bb2[i];
byte n = (byte) (i1 ^ i2);
while (n!=0) {
count++;
n &= (n - 1) ;
if(count>threshold){
return count;
}
}
}
return count;
}
@Override
public int hammingDistance(Signature s){
return this.hammingDistance(s, NUM_BITS);
}
public String toString(){
String s = "";
for(int i=0;i<size();i++){
s+=(get(i) ? "1" : "0");
}
return s;
}
@Override
public Signature perm(ArrayListOfIntsWritable p) {
int[] subs = p.getArray();
long b = 0; //byte array represented as a long
ByteArrayInputStream bis = new ByteArrayInputStream(getBits());
DataInputStream in = new DataInputStream(bis);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
try {
b = in.readLong();
long sbytes = 0;
for (int i = 0; i < NUM_BITS; i++) {
int shifts = subs[i];
//using a mask of intialized with one
long mask = 1;
mask = (mask << (NUM_BITS - shifts - 1));
long bit = (mask & b);
if (bit != 0) { //if the bit is one
mask = 1;
mask = (mask << (NUM_BITS - i - 1));
sbytes = (sbytes | mask);
}
}
dos.writeLong(sbytes);
dos.flush();
} catch (IOException e) {
e.printStackTrace();
}
byte[] permutedBytes = bos.toByteArray();
return new SixtyFourBitSignature(permutedBytes);
}
@Override
public void perm(ArrayListOfIntsWritable p, Signature permSign) {
int[] subs = p.getArray();
long b = 0; //byte array represented as a long
ByteArrayInputStream bis = new ByteArrayInputStream(getBits());
DataInputStream in = new DataInputStream(bis);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
try {
b = in.readLong();
long sbytes = 0;
for (int i = 0; i < NUM_BITS; i++) {
int shifts = subs[i];
//using a mask of intialized with one
long mask = 1;
mask = (mask << (NUM_BITS - shifts - 1));
long bit = (mask & b);
if (bit != 0) { //if the bit is one
mask = 1;
mask = (mask << (NUM_BITS - i - 1));
sbytes = (sbytes | mask);
}
}
dos.writeLong(sbytes);
dos.flush();
} catch (IOException e) {
e.printStackTrace();
}
byte[] permutedBytes = bos.toByteArray();
((SixtyFourBitSignature)permSign).setBits(permutedBytes);
}
public byte[] getBits() {
return bits;
}
}