All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.simiacryptus.binary.Bits Maven / Gradle / Ivy

There is a newer version: 2.1.0
Show newest version
package com.simiacryptus.binary;

import java.util.Arrays;
import java.util.Random;

public class Bits implements Comparable
{
  public static int dataCompare(final Bits left, final Bits right)
  {
    for (int i = 0; i < left.bytes.length; i++)
    {
      if (right.bytes.length <= i) { return 1; }
      final int a = left.bytes[i] & 0xFF;
      final int b = right.bytes[i] & 0xFF;
      if (a < b) { return -1; }
      if (a > b) { return 1; }
    }
    if (left.bitLength < right.bitLength) { return -1; }
    if (left.bitLength > right.bitLength) { return 1; }
    return 0;
  }
  
  public static byte highestOneBit(final long v)
  {
    final long h = Long.highestOneBit(v);
    if (0 == v) { return 0; }
    for (byte i = 0; i < 64; i++)
    {
      if (h == 1l << i) { return (byte) (i + 1); }
    }
    throw new RuntimeException();
  }
  
  public static byte[] padLeftBytes(final byte[] src, final int bytes)
  {
    final byte[] dst = new byte[bytes];
    for (int i = 1; i <= src.length; i++)
    {
      dst[dst.length - i] = src[src.length - i];
    }
    return dst;
  }
  
  public static byte[] shiftLeft(final byte[] src, final int bits)
  {
    final byte[] dst = new byte[src.length];
    shiftLeft(src, bits, dst);
    return dst;
  }
  
  public static void shiftLeft(final byte[] src, final int bits,
      final byte[] dst)
  {
    final int bitPart = bits % 8;
    final int bytePart = bits / 8;
    for (int i = 0; i < dst.length; i++)
    {
      final int a = i + bytePart;
      if (a >= 0 && src.length > a)
      {
        dst[i] |= (byte) ((src[a] & 0xFF) << bitPart & 0xFF);
      }
      final int b = i + bytePart + 1;
      if (b >= 0 && src.length > b)
      {
        dst[i] |= (byte) ((src[b] & 0xFF) >> 8 - bitPart & 0xFF);
      }
    }
  }
  
  public static byte[] shiftRight(final byte[] src, final int bits)
  {
    final byte[] dst = new byte[src.length];
    shiftRight(src, bits, dst);
    return dst;
  }
  
  private static void shiftRight(final byte[] src, final int bits,
      final byte[] dst)
  {
    final int bitPart = bits % 8;
    final int bytePart = bits / 8;
    for (int i = 0; i < dst.length; i++)
    {
      final int a = i - bytePart;
      if (a >= 0 && src.length > a)
      {
        dst[i] |= (byte) ((src[a] & 0xFF) >> bitPart & 0xFF);
      }
      final int b = i - bytePart - 1;
      if (b >= 0 && src.length > b)
      {
        dst[i] |= (byte) ((src[b] & 0xFF) << 8 - bitPart & 0xFF);
      }
    }
  }
  
  public static byte[] toBytes(final long data)
  {
    return new byte[] {
        (byte) (data >> 56 & 0xFF),
        (byte) (data >> 48 & 0xFF),
        (byte) (data >> 40 & 0xFF),
        (byte) (data >> 32 & 0xFF),
        (byte) (data >> 24 & 0xFF),
        (byte) (data >> 16 & 0xFF),
        (byte) (data >> 8 & 0xFF),
        (byte) (data & 0xFF) };
  }
  
  public static byte[] trim(final byte[] bytes)
  {
    for (int i = 0; i < bytes.length; i++)
    {
      if (bytes[i] != 0) { return Arrays.copyOfRange(bytes, i, bytes.length); }
    }
    return new byte[] {};
  }
  
  public final int     bitLength;
  
  private final byte[] bytes;
  
  public static Bits   ONE  = new Bits(1, 1);
  
  public static Bits   ZERO = new Bits(0, 1);
  
  public static Bits   NULL = new Bits(0, 0);
  
  public Bits(final byte... data)
  {
    this(data, data.length * 8);
  }
  
  private Bits(final byte[] data, final int length)
  {
    super();
    if (0 > length) { throw new IllegalArgumentException(); }
    if (data.length * 8 < length) { throw new IllegalArgumentException(); }
    if (length < (data.length - 1) * 8) { throw new IllegalArgumentException(); }
    this.bitLength = length;
    this.bytes = Bits.shiftLeft(data, (data.length * 8 - length) % 8);
  }
  
  public Bits(final long data)
  {
    this(data, highestOneBit(data));
  }
  
  public Bits(final long value, final int length)
  {
    super();
    
    if (0 > length) { throw new IllegalArgumentException(); }
    final byte highestOneBit = highestOneBit(value);
    if (highestOneBit > length) { throw new IllegalArgumentException(); }
    this.bitLength = length;
    this.bytes = new byte[(int) Math.ceil(length / 8.)];
    
    byte[] data = toBytes(value);
    data = trim(data);
    final int leftShift = (data.length * 8 - highestOneBit) % 8;
    final int rightShift = length - highestOneBit;
    
    if (leftShift > rightShift)
    {
      Bits.shiftLeft(data, leftShift - rightShift, this.bytes);
    }
    else
    {
      Bits.shiftRight(data, rightShift - leftShift, this.bytes);
    }
    assert value == this.toLong();
  }
  
  public Bits(final Random random, final int length)
  {
    this.bitLength = length;
    this.bytes = new byte[(int) Math.ceil(length / 8.)];
    random.nextBytes(this.bytes);
    final int excessBits = this.bytes.length * 8 - this.bitLength;
    this.bytes[this.bytes.length - 1] &= 0xFF << excessBits;
  }
  
  public Bits bitwiseAnd(final Bits right)
  {
    final int lengthDifference = this.bitLength - right.bitLength;
    if (lengthDifference < 0) { return this.concatenate(
        new Bits(0l, -lengthDifference)).bitwiseAnd(right); }
    if (lengthDifference > 0) { return this.bitwiseAnd(right
        .concatenate(new Bits(0l, lengthDifference))); }
    final Bits returnValue = new Bits(
        new byte[this.bytes.length],
        this.bitLength);
    for (int i = 0; i < this.bytes.length; i++)
    {
      returnValue.bytes[i] = this.bytes[i];
    }
    for (int i = 0; i < right.bytes.length; i++)
    {
      returnValue.bytes[i] &= right.bytes[i];
    }
    return returnValue;
  }
  
  public Bits bitwiseOr(final Bits right)
  {
    final int lengthDifference = this.bitLength - right.bitLength;
    if (lengthDifference < 0) { return this.concatenate(
        new Bits(0l, -lengthDifference)).bitwiseOr(right); }
    if (lengthDifference > 0) { return this.bitwiseOr(right
        .concatenate(new Bits(0l, lengthDifference))); }
    final Bits returnValue = new Bits(
        new byte[this.bytes.length],
        this.bitLength);
    for (int i = 0; i < this.bytes.length; i++)
    {
      returnValue.bytes[i] = this.bytes[i];
    }
    for (int i = 0; i < right.bytes.length; i++)
    {
      returnValue.bytes[i] |= right.bytes[i];
    }
    return returnValue;
  }
  
  public Bits bitwiseXor(final Bits right)
  {
    final int lengthDifference = this.bitLength - right.bitLength;
    if (lengthDifference < 0) { return this.concatenate(
        new Bits(0l, -lengthDifference)).bitwiseXor(right); }
    if (lengthDifference > 0) { return this.bitwiseXor(right
        .concatenate(new Bits(0l, lengthDifference))); }
    final Bits returnValue = new Bits(
        new byte[this.bytes.length],
        this.bitLength);
    for (int i = 0; i < this.bytes.length; i++)
    {
      returnValue.bytes[i] = this.bytes[i];
    }
    for (int i = 0; i < right.bytes.length; i++)
    {
      returnValue.bytes[i] ^= right.bytes[i];
    }
    return returnValue;
  }
  
  @Override
  public int compareTo(final Bits arg0)
  {
    return dataCompare(this, arg0);
  }
  
  public Bits concatenate(final Bits right)
  {
    final int newBitLength = this.bitLength + right.bitLength;
    final int newByteLength = (int) Math.ceil(newBitLength / 8.);
    final Bits result = new Bits(new byte[newByteLength], newBitLength);
    shiftLeft(this.bytes, 0, result.bytes);
    shiftRight(right.bytes, this.bitLength, result.bytes);
    return result;
  }
  
  @Override
  public boolean equals(final Object obj)
  {
    if (this == obj) { return true; }
    if (obj == null) { return false; }
    if (this.getClass() != obj.getClass()) { return false; }
    final Bits other = (Bits) obj;
    if (!Arrays.equals(this.bytes, other.bytes)) { return false; }
    if (this.bitLength != other.bitLength) { return false; }
    return true;
  }
  
  public byte[] getBytes()
  {
    return Arrays.copyOf(this.bytes, this.bytes.length);
  }
  
  @Override
  public int hashCode()
  {
    final int prime = 31;
    int result = 1;
    result = prime * result + Arrays.hashCode(this.bytes);
    result = prime * result + this.bitLength;
    return result;
  }
  
  public Bits leftShift(final int bits)
  {
    return this.concatenate(new Bits(0l, bits));
  }
  
  /**
   * Returns the next binary string which is of the same length but incremented by one.
   * Returns null on overflow
   * 
   * @return
   */
  public Bits next()
  {
    if (!this.toBitString().contains("0")) { return null; }
    return new Bits(this.toLong() + 1, this.bitLength);
  }
  
  public Bits range(final int i)
  {
    return this.range(i, this.bitLength - i);
  }
  
  public Bits range(final int start, final int length)
  {
    if (0 == length) { return new Bits(0l); }
    if (start < 0) { throw new IllegalArgumentException(); }
    if (start + length > this.bitLength) { throw new IllegalArgumentException(); }
    final Bits returnValue = new Bits(
        new byte[(int) Math.ceil(length / 8.)],
        length);
    shiftLeft(this.bytes, start, returnValue.bytes);
    int bitsInLastByte = length % 8;
    if (0 == bitsInLastByte)
    {
      bitsInLastByte = 8;
    }
    returnValue.bytes[returnValue.bytes.length - 1] &= 0xFF << 8 - bitsInLastByte;
    return returnValue;
  }
  
  public boolean startsWith(final Bits key)
  {
    if (key.bitLength > this.bitLength) { return false; }
    final Bits prefix = key.bitLength < this.bitLength ? this.range(0,
        key.bitLength) : this;
    return prefix.compareTo(key) == 0;
  }
  
  public String toBitString()
  {
    StringBuffer sb = new StringBuffer();
    final int shift = this.bytes.length * 8 - this.bitLength;
    final byte[] shiftRight = shiftRight(this.bytes, shift);
    for (final byte b : shiftRight)
    {
      String asString = Integer.toBinaryString(b & 0xFF);
      while (asString.length() < 8)
      {
        asString = "0" + asString;
      }
      sb.append(asString);
    }
    if (sb.length() >= this.bitLength)
    {
      return sb.substring(sb.length() - this.bitLength, sb.length());
    }
    else
    {
      final String n = sb.toString();
      sb = new StringBuffer();
      while (sb.length() + n.length() < this.bitLength)
      {
        sb.append("0");
      }
      return sb.toString() + n;
    }
  }
  
  public String toHexString()
  {
    final StringBuffer sb = new StringBuffer();
    for (final byte b : this.bytes)
    {
      sb.append(Integer.toHexString(b & 0xFF));
    }
    return sb.substring(0, this.bitLength / 4);
  }
  
  public long toLong()
  {
    long value = 0;
    for (final byte b : shiftRight(this.bytes, this.bytes.length * 8
        - this.bitLength))
    {
      final long shifted = value << 8;
      value = shifted;
      final int asInt = b & 0xFF;
      value += asInt;
    }
    return value;
  }
  
  @Override
  public String toString()
  {
    return this.toBitString();
  }
  
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy