/* * Copyright 2010 Georgios Migdos <cyberpython@gmail.com>. * * 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. * under the License. */ package util.binary.bitpattern; import java.util.Arrays; /** * * @author Georgios Migdos <cyberpython@gmail.com> */ public class BitPattern implements IBitPattern{ private final static int DEFAULT_LENGTH = 8; private int numberOfBits; private int max; private boolean[] bits; private String hexRegex; private String binaryRegex; public BitPattern() { this(DEFAULT_LENGTH); } public BitPattern(int length) { this.hexRegex = "0x[0-9a-fA-F]+"; this.binaryRegex = "[01]+"; setLength(length); } public BitPattern(int length, int value) throws BitPatternOverflowException{ this(length); setValue(value); } public BitPattern(int length, String bitPattern) { this(length); setValue(bitPattern); } /** * Returns this BitPattern's length (number of bits). * @return this BitPattern's length */ public int length() { return numberOfBits; } /** * Sets this BitPattern's length (number of bits). All bits are set to 0. * @param the new length */ private void setLength(int length) { this.bits = new boolean[length]; this.numberOfBits = length; max = (int) Math.pow(2, numberOfBits - 1); for (int i = 0; i < length; i++) { this.set(i, false); } } /** * Returns this BitPattern's bit value at index 'bitIndex'. * @param bitIndex the index of the bit to be returned * @return the bit value at index 'bitIndex' * @throws IndexOutOfBoundsException if bitIndex is less than 0 or greater than this BitPattern's length-1 */ public boolean get(int bitIndex) { if ((bitIndex>=0) && (bitIndex<numberOfBits)){ return this.bits[bitIndex]; }else{ throw new IndexOutOfBoundsException("Bit pattern index out of bounds: "+bitIndex+" - index should be in the range [0, "+(numberOfBits-1)+"]"); } } /** * Sets this BitPattern's bit value at index 'bitIndex'. * @param bitIndex the index of the bit to be set * @param value the bit's new value * @throws IndexOutOfBoundsException if bitIndex is less than 0 or greater than this BitPattern's length-1 */ public final void set(int bitIndex, boolean value) { if ((bitIndex>=0) && (bitIndex<numberOfBits)){ this.bits[bitIndex] = value; }else{ throw new IndexOutOfBoundsException("Bit pattern index out of bounds: "+bitIndex+" - index should be in the range [0, "+(numberOfBits-1)+"]"); } } /** * Sets this BitPattern's value using the integer 'value' as the source * The 'value' parameter must be an integer in the range * [-2<sup>length()-1</sup>, 2<sup>length()-1</sup>-1] * @param value an integer representing the new value * @throws ArithmeticException if value is not in the range [-2<sup>length()-1</sup>, 2<sup>length()-1</sup>-1] */ public final void setValue(int value) throws NumberFormatException, BitPatternOverflowException { if ((value < -max) || (value >= max)) { throw new BitPatternOverflowException("Invalid value for bit pattern : " + value + " - the value must be in the range [-" + max + ", " + (max - 1) + "]"); } if (value < 0) { value = 2 * max + value; } int tmp = value; StringBuilder sb = new StringBuilder(Integer.toBinaryString(tmp)); while (sb.length() < numberOfBits) { sb.insert(0, '0'); } setValue(sb.toString()); } private void setValueFromBinaryString(String bitPattern) throws NumberFormatException { if (bitPattern.length() != numberOfBits) { throw new NumberFormatException("Invalid bit pattern length : " + bitPattern.length() + " - expected: " + this.numberOfBits); } for (int i = 0; i < numberOfBits; i++) { if (bitPattern.charAt(i) == '0') { this.set(i, false); } else if (bitPattern.charAt(i) == '1') { this.set(i, true); } else { throw new NumberFormatException("Invalid character in bit pattern: " + bitPattern.charAt(i)); } } } private void setValueFromHexString(String hexPattern) throws NumberFormatException { try{ this.setValueFromBinaryString(BitPatternUtils.hexToBinaryString(hexPattern, this.numberOfBits)); }catch(NumberFormatException nfe){ throw nfe; } } /** * Sets this IBitPattern's value using the String 'pattern' as the source. * The 'pattern' parameter can be either a two's complement hexadecimal value matched by the * regular expression "0x[0-9a-fA-F]+" or a binary value in two's complement representation. * The value in binary form must have the same length as this IBitPattern. * @param pattern a String representing the new value in two's complement hex or binary representation * @throws NumberFormatException if pattern is not in binary or hex form, or the value in binary representation * does not have the same length with this IBitPattern. */ public final void setValue(String pattern) throws NumberFormatException{ if(pattern.matches(binaryRegex)){ try{ this.setValueFromBinaryString(pattern); }catch(NumberFormatException nfe){ throw nfe; } }else if(pattern.matches(hexRegex)){ setValueFromHexString(pattern); }else{ throw new NumberFormatException("Invalid pattern : " + pattern +" - not in hexadecimal or binary form."); } } /** * Returns thisIBitPattern's value as an integer * @return this BitPattern's value */ public int intValue() { if (this.get(0) == true) { return Integer.parseInt(this.toBinaryString().substring(1), 2) - max; } else { return Integer.parseInt(this.toBinaryString(), 2); } } /** * Returns thisIBitPattern's value as a String in two's complement binary representation * @return this BitPattern's value */ @Override public String toString() { return this.toBinaryString(); } /** * Returns this BitPattern's value in binary representation as a String. * @return this BitPattern's value */ public String toBinaryString() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < numberOfBits; i++) { sb.append(get(i) == false ? '0' : '1'); } return sb.toString(); } /** * Returns this BitPattern's value in hexadecimal representation as a String. * @return this BitPattern's value */ public String toHexString() { return "0x"+BitPatternUtils.toHexString(Integer.parseInt(this.toBinaryString(),2), numberOfBits/4).toUpperCase(); } /** * Rotates this BitPattern's bits to the left. */ public void rotateLeft() { int lastIndex = this.numberOfBits - 1; boolean tmp = this.get(0); for (int i = 0; i < lastIndex; i++) { this.set(i, this.get(i + 1)); } this.set(lastIndex, tmp); } /** * Rotates this BitPattern's bits to the right. */ public void rotateRight() { int lastIndex = this.numberOfBits - 1; boolean tmp = this.get(lastIndex); for (int i = lastIndex; i > 0; i--) { this.set(i, this.get(i - 1)); } this.set(0, tmp); } /** * Shifts (arithmetic shift) this BitPattern's bits one place to the left. */ public void shiftLeft() { //arithmetic shift int lastIndex = this.numberOfBits - 1; for (int i = 0; i < lastIndex; i++) { this.set(i, this.get(i + 1)); } this.set(lastIndex, false); } /** * Shifts (arithmetic shift) this BitPattern's bits one place to the right. */ public void shiftRight() { //arithmetic shift int lastIndex = this.numberOfBits - 1; boolean tmp = this.get(0); for (int i = lastIndex; i > 0; i--) { this.set(i, this.get(i - 1)); } this.set(0, tmp); } /** * Adds a BitPattern's value to this BitPattern's value * @param bitPattern the BitPattern whose value will be added to this BitPattern's value * @throws ArithmeticException if the two operands have different lengths or the result is not in the * range [-2<sup>length()-1</sup>, 2<sup>length()-1</sup>-1] */ public void add(IBitPattern bitPattern)throws BitPatternOverflowException{ if (numberOfBits == bitPattern.length()) { int value = this.intValue() + bitPattern.intValue(); this.setValue(value); } else { throw new ArithmeticException("Cannot add bit patterns of different length: " + this.length() + " and " + bitPattern.length()); } } /** * Applies the bitwise NOT operator to this BitPattern's bits. */ public void not() { for(int i=0; i<this.numberOfBits; i++){ this.bits[i] = !this.bits[i]; } } /** * Performs bitwise AND between this BitPattern's bits and the bits of the * second operand. * @param bitPattern the second AND operand * @throws ArithmeticException if the two operands have different lengths */ public void and(IBitPattern bitPattern) { if (numberOfBits == bitPattern.length()) { for(int i=0; i<numberOfBits; i++){ this.set(i, this.get(i) && bitPattern.get(i) ); } } else { throw new ArithmeticException("Cannot apply AND to bit patterns of different length: " + this.length() + " and " + bitPattern.length()); } } /** * Performs bitwise OR between this BitPattern's bits and the bits of the * second operand. * @param bitPattern the second OR operand * @throws ArithmeticException if the two operands have different lengths */ public void or(IBitPattern bitPattern) { if (numberOfBits == bitPattern.length()) { for(int i=0; i<numberOfBits; i++){ this.set(i, this.get(i) || bitPattern.get(i) ); } } else { throw new ArithmeticException("Cannot apply OR to bit patterns of different length: " + this.length() + " and " + bitPattern.length()); } } /** * Performs bitwise XOR between this BitPattern's bits and the bits of the * second operand. * @param bitPattern the second XOR operand * @throws ArithmeticException if the two operands have different lengths */ public void xor(IBitPattern bitPattern) { if (numberOfBits == bitPattern.length()) { for(int i=0; i<numberOfBits; i++){ this.set(i, this.get(i) ^ bitPattern.get(i) ); } } else { throw new ArithmeticException("Cannot apply XOR to bit patterns of different length: " + this.length() + " and " + bitPattern.length()); } } /** * Compares o to this BitPattern. * Only the values are compared - i.e. IBitPatterns with different lengths but * the same value are considered equal. * @param o an IBitPattern * @return -1, 1 or 0 if this is less, greater than or equal to o */ public int compareTo(IBitPattern o) { if(o ==null){ return 0; } int myValue = this.intValue(); int oValue = o.intValue(); if(myValue < oValue){ return -1; }else if(myValue > oValue){ return 1; }else{ return 0; } } /** * Checks if obj is equal to this BitPattern. * For obj to be equal to this BitPattern, it should be an IBitPattern * with the same length and bits as this BitPattern * @param obj an Object * @return true if obj is equal to this, false otherwise */ @Override public boolean equals(Object obj) { if ( (obj!=null) && (obj instanceof IBitPattern) ){ IBitPattern o = (IBitPattern) obj; return o.hashCode() == this.hashCode(); }else{ return false; } } @Override public int hashCode() { int hash = 5; hash = 29 * hash + this.numberOfBits; hash = 29 * hash + Arrays.hashCode(this.bits); return hash; } /** * Returns an IBitPattern whose value is the sum of the values of a and b * @param a an IBitPattern * @param b an IBitPattern * @return the result of a plus b */ public static IBitPattern add(IBitPattern a, IBitPattern b)throws BitPatternOverflowException { BitPattern result = new BitPattern(a.length()); result.setValue(a.intValue()); result.add(b); return result; } /** * Returns an IBitPattern whose value is the bitwise AND of a and b * @param a an IBitPattern * @param b an IBitPattern * @return the result of a AND b */ public static IBitPattern and(IBitPattern a, IBitPattern b)throws BitPatternOverflowException { BitPattern result = new BitPattern(a.length()); result.setValue(a.intValue()); result.and(b); return result; } /** * Returns an IBitPattern whose value is the bitwise OR of a and b * @param a an IBitPattern * @param b an IBitPattern * @return the result of a OR b */ public static IBitPattern or(IBitPattern a, IBitPattern b)throws BitPatternOverflowException { BitPattern result = new BitPattern(a.length()); result.setValue(a.intValue()); result.or(b); return result; } /** * Returns an IBitPattern whose value is the bitwise XOR of a and b * @param a an IBitPattern * @param b an IBitPattern * @return the result of a XOR b */ public static IBitPattern xor(IBitPattern a, IBitPattern b)throws BitPatternOverflowException { BitPattern result = new BitPattern(a.length()); result.setValue(a.intValue()); result.xor(b); return result; } }