/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: BitVector.java
* Written by Eric Kim and JTom O'Neill, Sun Microsystems.
*
* Copyright (c) 2005 Sun Microsystems and Static Free Software
*
* Electric(tm) is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Electric(tm) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Electric(tm); see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, Mass 02111-1307, USA.
*/
package com.sun.electric.tool.simulation.test;
import java.math.BigInteger;
import java.util.BitSet;
/**
* Provides a fixed-length bit vector and methods for acting on it. Developed to
* represent scan chain state variables.
* <p>
* Bits can be in the <tt>invalid</tt>, <tt>true</tt>, or
* <tt>false</tt> state. The string representations for these values are "
* <tt>-</tt>", "<tt>1</tt>", and "<tt>0</tt>", respectively. To help
* catch bugs, bits are <tt>invalid</tt> (i.e., undefined or unknown) until
* they have been explicitly set and <code>BitVector</code> methods crash if
* you attempt to read an <tt>invalid</tt> bit. Here are some examples to
* indicate how BitVector values are represented and used:
* <p>
* <TABLE border="1">
* <TR>
* <TH ROWSPAN=2>Bit representation
* <TH COLSPAN=3>BitVector example
* <TR>
* <TH>example 1
* <TH>example 2
* <TH>example 3
* <TR>
* <TH>String
* <TD>011
* <TD>1-01
* <TD>1000000000
* <TR>
* <TH>BigInteger
* <TD>3
* <TD>undefined
* <TD>512
* <TR>
* <TH>"LittleInteger"
* <TD>6
* <TD>undefined
* <TD>1
* <TR>
* <TH>BitVector state at index 0,1,2,...
* <TD>false,true,true
* <TD>true,invalid,false,true
* <TD>true,false,false,...
* <TR>
* <TH>bits scanned into scan chain
* <TD>1,1,0
* <TD>undefined
* <TD>0,0,0,0,0,0,0,0,0,1
* <TR>
* <TH>chain element states along s_in
* <TD>0,1,1
* <TD>undefined
* <TD>1,0,0,0,0,0,0,0,0,0 </TABLE>
* <p>
* Bit zero is the LAST bit scanned into the scan chain. Thus the bit index
* matches the position of the corresponding scan chain element along the s_in
* chain. <code>BitVector</code> uses the big endian bit order convention for
* the String representation. Strings should be read from left to right,
* starting at bit zero. Thus the strings match the order in which scan chain
* elements appear in most schematics. The MSB of a <code>BigInteger</code> or
* <code>int</code> is stored in bit 0 of the <code>BitVector</code>. That
* is, the bit order is opposite to that of the integer.
* <p>
* Key differences from <code>BitSet</code>: 1) addition of the <tt>invalid
* </tt>
* state; 2) the bit vector is fixed length; 3) ranges are specified by
* (startIndex, numBits) rather than (startIndex, endIndex); 4) different
* toString() method; addition of BigInteger I/O methods.
* <p>
*/
public class BitVector {
/** Number of bits in the bit vector */
final private int numBits;
/** Bit vector */
private BitSet bitSet;
/** Whether state of each bit is accurately known */
private BitSet valid;
/** Identifying name of bit vector, for errors etc. */
private String name;
/**
* Severity of action when an unnamed BitVector is created. Default is
* {@link Infrastructure#SEVERITY_WARNING}
*/
public static int noNameSeverity = Infrastructure.SEVERITY_WARNING;
/**
* Deprecated. Constructor creates an unnamed vector of <code>numBits</code>
* bits, set to the <tt>invalid</tt> state.
*
* @param numBits
* number of bits in the vector
* @deprecated
*/
public BitVector(int numBits) {
this(numBits, "unnamed");
Infrastructure.error(noNameSeverity,
"Warning: creating unnamed length-" + numBits
+ " bit vector, use "
+ "two-parameter BitVector constructor instead");
}
/**
* Main constructor creates a vector of <code>numBits</code> bits, set to
* the <tt>invalid</tt> state.
*
* @param numBits
* number of bits in the vector
* @param name
* identifying name of the bit vector, e.g. for errors
*/
public BitVector(int numBits, String name) {
if (numBits < 0) {
Infrastructure.fatal("Bad BitVector length " + numBits
+ ", must be non-negative");
}
this.numBits = numBits;
this.bitSet = new BitSet(numBits);
this.valid = new BitSet(numBits);
this.name = name;
}
/**
* Deprecated. Convenience constructor creates a bit vector from the input
* string. The length of the string is used for the length of the bit
* vector.
*
* @param bitString
* bit sequence to initialize vector to
* @deprecated
*/
public BitVector(String bitString) {
this(bitString.length());
this.put(0, bitString);
}
/**
* Convenience constructor creates a bit vector from the input string. The
* length of the string is used for the length of the bit vector.
*
* @param bitString
* bit sequence to initialize vector to
* @param name
* identifying name of the bit vector, e.g. for errors
*/
public BitVector(String bitString, String name) {
this(bitString.length(), name);
this.put(0, bitString);
}
/**
* Convenience constructor copies contents of existing BitVector
*
* @param b BitVector to copy
*/
public BitVector(BitVector b) {
this(b.getState(), b.getName());
}
/**
* Convenience constructor creates a bit vector from the input string. The
* length of the string is used for the length of the bit vector.
*
* @param bitArray
* bit sequence to initialize vector to
* @param name
* identifying name of the bit vector, e.g. for errors
*/
public BitVector(int[] bitArray, String name) {
this(bitArray.length, name);
this.put(0, bitArray);
}
/**
* Returns number of bits in the bit vector
*
* @return Number of bits in the bit vector
*/
public int getNumBits() {
return numBits;
}
/**
* Returns the name of the bit vector.
*
* @return Returns the name of the bit vector.
*/
public String getName() {
return name;
}
/**
* Set the name of the bit vector.
*
* @param name
* The name to set.
*/
public void setName(String name) {
this.name = name;
}
/**
* Returns a string representation of the entire bit sequence, starting with
* the lowest-index bit.
*
* @return string representation of bit sequence
*/
public String getState() {
StringBuffer buf = new StringBuffer(numBits);
for (int ind = 0; ind < numBits; ind++) {
if (this.valid.get(ind) == false) {
buf.append("-");
} else if (this.get(ind)) {
buf.append("1");
} else {
buf.append("0");
}
}
return buf.toString();
}
/**
* Returns the name and the bit vector and a string representing the entire
* bit sequence, starting with the lowest-index bit.
*
* @return string representation of bit sequence
*/
public String toString() {
return getName() + ": " + getState();
}
/**
* Returns a <code>BigInteger</code> representation of the entire bit
* vector, with bit 0 in the MSB.
*
* @return <code>BigInteger</code> representation of bit sequence
*/
public BigInteger toBigInteger() {
checkRange(0, numBits, true);
BigInteger out = BigInteger.ZERO;
for (int ind = 0; ind < numBits; ind++) {
if (this.get(ind) == true) {
out = out.or(BigInteger.ONE.shiftLeft(numBits - ind - 1));
}
}
return out;
}
/**
* Returns a <code>BigInteger</code> representation of the entire bit
* vector, with bit 0 in the LSB. Compare to toBigInteger().
*
* @return <code>BigInteger</code> representation of bit sequence
* @see #toBigInteger
*/
public BigInteger toLittleInteger() {
checkRange(0, numBits, true);
BigInteger out = BigInteger.ZERO;
for (int ind = 0; ind < numBits; ind++) {
if (this.get(ind) == true) {
out = out.or(BigInteger.ONE.shiftLeft(ind));
}
}
return out;
}
/**
* Returns an <code>int</code> array representation of the entire bit
* vector. Values in the array are either 0, 1, or -1 for false, true, and
* undefined, respectively.
* @return an integer array representation of the bit vector.
*/
public int[] toIntArray() {
int [] array = new int[numBits];
for (int ind = 0; ind < numBits; ind++) {
if (this.valid.get(ind) == false) {
array[ind] = -1;
} else if (this.get(ind)) {
array[ind] = 1;
} else {
array[ind] = 0;
}
}
return array;
}
/**
* Returns <tt>true</tt> if specified bit has a known state.
*
* @param bitIndex
* index of bit to be checked
* @return whether bit has known state
*/
public boolean isValid(int bitIndex) {
checkIndex(bitIndex, false);
return valid.get(bitIndex);
}
/**
* Sets the bit specified by the index to the <tt>invalid</tt> state.
*
* @param bitIndex
* the index of the bit to be invalidated
*/
public void invalidate(int bitIndex) {
checkIndex(bitIndex, false);
valid.clear(bitIndex);
}
/**
* Sets the entire bit vector to the <tt>invalid</tt> state.
*/
public void invalidate() {
for (int ind = 0; ind < numBits; ind++) {
invalidate(ind);
}
}
/**
* Returns the value of the bit with the specified index. The value is
* <tt>true</tt> if the bit with the index <code>bitIndex</code> is
* currently set in this <code>BitVector</code>; otherwise, the result is
* <tt>false</tt>.
*
* @param bitIndex
* the bit index
* @return the value of the bit with the specified index
*/
public boolean get(int bitIndex) {
checkIndex(bitIndex, true);
return bitSet.get(bitIndex);
}
/**
* Sets the bit at the specified index to to the complement of its current
* value.
*
* @param bitIndex
* the index of the bit to flip
*/
public void flip(int bitIndex) {
checkIndex(bitIndex, true);
bitSet.flip(bitIndex);
}
/**
* Sets the bits in the specified index range to to the complements of their
* current values.
*
* @param fromIndex
* the starting index of the bits to flip
* @param nbits
* the number of bits to flip
*/
public void flip(int fromIndex, int nbits) {
checkRange(fromIndex, nbits, true);
for (int bitIndex = fromIndex; bitIndex < fromIndex + nbits; bitIndex++) {
this.flip(bitIndex);
}
}
/**
* Sets the bit specified by the index to <tt>false</tt>.
*
* @param bitIndex
* the index of the bit to be cleared
*/
public void clear(int bitIndex) {
checkIndex(bitIndex, false);
bitSet.clear(bitIndex);
valid.set(bitIndex);
}
/**
* Sets the bit at the specified index to <tt>true</tt>.
*
* @param bitIndex
* a bit index
*/
public void set(int bitIndex) {
checkIndex(bitIndex, false);
bitSet.set(bitIndex);
valid.set(bitIndex);
}
/**
* Sets the bit at the specified index to the specified value.
*
* @param bitIndex
* a bit index.
* @param value
* a boolean value to set
*/
public void set(int bitIndex, boolean value) {
checkIndex(bitIndex, false);
bitSet.set(bitIndex, value);
valid.set(bitIndex);
}
/**
* Sets the <code>nbits</code> bits starting at <code>fromIndex</code>
* (inclusive) to the specified value.
*
* @param fromIndex
* index of the first bit to be set
* @param nbits
* number of bits to set
* @param value
* value to set the selected bits to
*/
public void set(int fromIndex, int nbits, boolean value) {
checkRange(fromIndex, nbits, false);
bitSet.set(fromIndex, fromIndex + nbits, value);
valid.set(fromIndex, fromIndex + nbits);
}
/**
* Returns a new <code>BitVector</code> composed of a subset of
* <code>numBits</code> bits from this <code>BitVector</code>. Range is
* from <code>fromIndex</code> (inclusive) to
* <code>(toIndex + nbits)</code> (exclusive). Note this interface is
* different from that of <code>BitSet.get()</code>. The range is not
* allowed to include any invalid bits.
*
* @param fromIndex
* index of the first bit to include
* @param nbits
* number of bits to include
* @return a new BitSet from a range of this BitVector
*/
public BitVector get(int fromIndex, int nbits) {
checkRange(fromIndex, nbits, true);
return getIndiscriminate(fromIndex, nbits);
}
/**
* Like {@link #get(int, int)}, but the range is allowed to include invalid
* bits.
*
* @param fromIndex
* index of the first bit to include
* @param nbits
* number of bits to include
* @return a new BitSet from a range of this BitVector
*/
public BitVector getIndiscriminate(int fromIndex, int nbits) {
checkRange(fromIndex, nbits, false);
BitVector result = new BitVector(nbits, "bits [" + fromIndex + ":"
+ (fromIndex + nbits - 1) + "] of " + getName());
result.bitSet = bitSet.get(fromIndex, fromIndex + nbits);
result.valid = valid.get(fromIndex, fromIndex + nbits);
return result;
}
/**
* Copies the source bit vector into the receiver, starting at index
* <code>fromIndex</code>. If the source bit vector does not extend to
* the end of the receiver, the high-index bits will not be modified. The
* source bit vector is required to contain no invalid bits.
*
* @param fromIndex
* starting index
* @param source
* source bits
*/
public void put(int fromIndex, BitVector source) {
int nbits = source.getNumBits();
checkRange(fromIndex, nbits, false);
source.checkRange(0, nbits, true);
putIndiscriminate(fromIndex, source);
}
/**
* Like {@link #put(int, BitVector)}, but the source bit vector is allowed
* to have invalid bits.
*
* @param fromIndex
* starting index
* @param source
* source bits
*/
public void putIndiscriminate(int fromIndex, BitVector source) {
int nbits = source.getNumBits();
checkRange(fromIndex, nbits, false);
source.checkRange(0, nbits, false);
for (int ind = 0; ind < nbits; ind++) {
if (source.isValid(ind) == false) {
invalidate(fromIndex + ind);
} else if (source.get(ind)) {
set(fromIndex + ind);
} else {
clear(fromIndex + ind);
}
}
}
/**
* Sets bits in bit vector according to a big endian input
* <code>String</code>, starting at bit index <code>fromIndex</code>.
* If the string does not extend to the end of the bit vector, the
* high-index bits will not be modified.
*
* @param fromIndex
* index of the first bit to be set
* @param inp
* new bit values (e.g., "<tt>10101</tt>")
*/
public void put(int fromIndex, String inp) {
int length = inp.length();
if (length == 0) return;
checkRange(fromIndex, length, false);
for (int ind = 0; ind < length; ind++) {
char character = inp.charAt(ind);
if (character == '1') {
set(fromIndex + ind);
} else if (character == '0') {
clear(fromIndex + ind);
} else {
Infrastructure.fatal("Bad character " + character
+ " in bit string " + inp
+ ", only '0' and '1' are allowed for put method.");
}
}
}
/**
* Sets bits in bit vector according to an
* <code>int[]</code>, starting at bit index <code>fromIndex</code>.
* If the array does not extend to the end of the bit vector, the
* high-index bits will not be modified. The array must
* be an array of 1's and 0's (1 for true, 0 for false).
*
* @param fromIndex
* index of the first bit to be set
* @param array
* new bit values
*/
public void put(int fromIndex, int[] array) {
int length = array.length;
if (length == 0) return;
checkRange(fromIndex, length, false);
for (int ind = 0; ind < length; ind++) {
int i = array[ind];
if (i == 1) {
set(fromIndex + ind);
} else if (i == 0) {
clear(fromIndex + ind);
} else {
Infrastructure.fatal("Bad value " + i
+ " in int[] " + array.toString()
+ ", only 0 and 1 are allowed for put method.");
}
}
}
/**
* Sets <code>nbits</code> bits in bit vector according to the bit
* sequence in a <code>BigInteger</code>, starting at bit index
* <code>fromIndex</code>. The integer's bit sequence is reversed upon
* storage.
*
* @param fromIndex
* index of the first bit to be set
* @param nbits
* number of bits to set
* @param inp
* number containing new bit values in reverse order
*/
public void put(int fromIndex, int nbits, BigInteger inp) {
checkRange(fromIndex, nbits, false);
for (int ind = 0; ind < nbits; ind++) {
BigInteger bit = inp.shiftRight(nbits - ind - 1);
bit = bit.and(BigInteger.ONE);
if (bit.equals(BigInteger.ONE)) {
set(fromIndex + ind);
} else if (bit.equals(BigInteger.ZERO)) {
clear(fromIndex + ind);
} else {
Infrastructure.fatal("Programming error: bad bit value " + bit
+ " in BigInteger " + inp);
}
}
}
/**
* Sets <code>nbits</code> bits in bit vector according to the bit
* sequence in a <code>BigInteger</code>, starting at bit index
* <code>fromIndex</code>.
*
* @param fromIndex
* index of the first bit to be set
* @param nbits
* number of bits to set
* @param inp
* number containing new bit values in reverse order
*/
public void putLittle(int fromIndex, int nbits, BigInteger inp) {
checkRange(fromIndex, nbits, false);
for (int ind = 0; ind < nbits; ind++) {
BigInteger bit = inp.shiftRight(ind);
bit = bit.and(BigInteger.ONE);
if (bit.equals(BigInteger.ONE)) {
set(fromIndex + ind);
} else if (bit.equals(BigInteger.ZERO)) {
clear(fromIndex + ind);
} else {
Infrastructure.fatal("Programming error: bad bit value " + bit
+ " in BigInteger " + inp);
}
}
}
/**
* Returns the number of bits set to <tt>true</tt> in this
* <code>BitVector</code>.
*
* @return the number of bits set to <tt>true</tt> in this
* <code>BitVector</code>.
*/
public int cardinality() {
int numGoodBits = valid.cardinality();
if (numGoodBits < numBits) {
Infrastructure.fatal("Only " + numGoodBits + " out of " + numBits
+ " bits valid in BitVector '" + getName()
+ "'. Can only count cardinality of"
+ " a fully-valid (i.e. initialized) BitVector.");
}
return bitSet.cardinality();
}
/**
* Returns <tt>true</tt> if this <code>BitSet</code> contains only bits
* that are <tt>false</tt>.
*
* @return whether this <code>BitSet</code> is empty.
*/
public boolean isEmpty() {
checkRange(0, getNumBits(), true);
return bitSet.isEmpty();
}
/**
* Returns <tt>true</tt> if all of the bits in this <code>BitSet</code>
* are in the <tt>invalid</tt> state.
*
* @return whether this <code>BitSet</code> is completely invalid.
*/
public boolean isInvalid() {
checkRange(0, getNumBits(), false);
return valid.isEmpty();
}
//-------------------------------------------------------------------------
/** I'm adding a bunch of methods that will make BitVector convenient for
* writing test code. This code will behave as if BitVector is a big
* endian integer. RKao
*/
private void fatalIfAnyBitInvalid(String op) {
for (int i=0; i<getNumBits(); i++) {
if (!isValid(i)) {
Infrastructure.fatal(op+" operand contains invalid bits");
}
}
}
/** Compare BitVectors. number of bits must match. */
public boolean equals(Object o) {
if (!(o instanceof BitVector)) return false;
String s1 = getState();
String s2 = ((BitVector) o).getState();
return s1.equals(s2);
}
public int hashCode() {return getState().hashCode();}
/** Generate concatenation of bit vectors. Put bits from b on the right */
public BitVector cat(BitVector b) {
String s = getState() + b.getState();
return new BitVector(s, "cat");
}
/** Generate the complement all bits */
public BitVector not() {
BitVector r = new BitVector(this);
r.flip(0, r.getNumBits());
return r;
}
/**
* Logical AND this BitVector with b. Pad the shorter
* of the two operands with zeros on the left.
* @param b second operand
* @return a new BitVector that is this AND b
*/
public BitVector and(BitVector b) {
BitVector a = this;
int lenA = a.getNumBits();
int lenB = b.getNumBits();
int l = Math.max(lenA, lenB);
BitVector ans = new BitVector(l, "and");
int aNdx = lenA-1;
int bNdx = lenB-1;
for (int i=l-1; i>=0; i--) {
if (!a.isValid(aNdx) || !b.isValid(bNdx)) {
ans.invalidate(i);
} else {
boolean av = a.get(aNdx);
boolean bv = b.get(bNdx);
boolean ansv = av && bv;
ans.set(i, ansv);
}
if (aNdx>0) aNdx--;
if (bNdx>0) bNdx--;
}
return ans;
}
/**
* Return a new BitVector that is the bit reverse of the
* bits in this BitVector. That is the MSB
* moves to the LSB and all other bits are shifted to the left.
* @return the new rotated BitVector
*/
public BitVector bitReverse() {
int l = getNumBits();
BitVector ans = new BitVector(l, "bitReverse");
int j=l-1;
for (int i=0; i<l; i++, j--) {
if (!isValid(i)) continue;
ans.set(j, get(i));
}
return ans;
}
/** Set the value of BitVector from an long value. Right justify.
* Sign extend if longValue is too short. Truncate MSBs if longValue
* is too long */
public void setFromLong(long longValue) {
for (int i=getNumBits()-1; i>=0; i--) {
set(i, (longValue & 1)==1);
longValue = longValue >> 1;
}
}
/** Set the value of BitVector from a BigInteger. Right justify.
* Sign extend if bigValue is too short. Truncate MSBs if bigValue
* is too large */
public void setFromBigInteger(BigInteger bigValue) {
BigInteger ONE = BigInteger.ONE;
for (int i=getNumBits()-1; i>=0; i--) {
set(i, (bigValue.and(ONE)).equals(ONE));
bigValue = bigValue.shiftRight(1);
}
}
/** Sign extend the shorter operand to the length of the longer
* longer operand.
* Add the two numbers. Truncate the result to the length of the
* longer operand. */
public BitVector add(BitVector bv) {
fatalIfAnyBitInvalid("add");
bv.fatalIfAnyBitInvalid("add");
int len = Math.max(getNumBits(), bv.getNumBits());
BitVector ans = new BitVector(len, "add");
BigInteger a = this.toBigInteger();
BigInteger b = bv.toBigInteger();
ans.setFromBigInteger(a.add(b));
return ans;
}
/** Sign extend the shorter operand to the length of the longer
* longer operand.
* Subtract (this - bv). Truncate the result to the length of the
* longer operand. */
public BitVector subtract(BitVector bv) {
fatalIfAnyBitInvalid("subtract");
bv.fatalIfAnyBitInvalid("subtract");
int len = Math.max(getNumBits(), bv.getNumBits());
BitVector ans = new BitVector(len, "subtract");
BigInteger a = this.toBigInteger();
BigInteger b = bv.toBigInteger();
ans.setFromBigInteger(a.subtract(b));
return ans;
}
/** Shift right, filling with sign bit */
public BitVector shiftRight(int n) {
fatalIfAnyBitInvalid("shiftRight");
int len = getNumBits();
BitVector ans = new BitVector(len, "shiftRight");
for (int i=0; i<len; i++) {
int j = Math.max(0, i-n);
if (!isValid(j)) continue;
ans.set(i, get(j));
}
return ans;
}
/** convert to long. Truncate MSBs if BitVector is too large */
public long toLong() {
fatalIfAnyBitInvalid("toLong");
BigInteger big = toBigInteger();
return big.longValue();
}
/** sign extend shorter operand to length of longer operand.
* Then test for equality */
public boolean equalsLong(long v) {
fatalIfAnyBitInvalid("equalsLong");
int len = Math.max(64, getNumBits());
BitVector a = new BitVector(len, "equalsLong a");
BitVector b = new BitVector (len, "equalsLong b");
a.setFromBigInteger(toBigInteger());
b.setFromLong(v);
return a.equals(b);
}
/**
* Return a new BitVector that consists of the
* bits in this BitVector rotated left. That is the MSB
* moves to the LSB and all other bits are shifted to the
* left.
* @param amountToRotate number of bit positions to rotate
* @return the new rotated BitVector
*/
public BitVector rotateLeft(int amountToRotate) {
int len = getNumBits();
BitVector ans = new BitVector(len, "rotateLeft");
for (int i=0; i<len; i++) {
int j = (i+len-amountToRotate) % len;
if (this.isValid(i)) {
ans.set(j, this.get(i));
}
}
return ans;
}
/**
* Return a new BitVector that consists of the
* bits in this BitVector rotated right. That is the LSB
* moves to the MSB and all other bits are shifted to the
* right.
* @param amountToRotate number of bit positions to rotate
* @return the new rotated BitVector
*/
public BitVector rotateRight(int amountToRotate) {
int len = getNumBits();
BitVector ans = new BitVector(len, "rotateLeft");
for (int i=0; i<len; i++) {
int j = (i+len+amountToRotate) % len;
if (this.isValid(i)) {
ans.set(j, this.get(i));
}
}
return ans;
}
//-------------------------------------------------------------------------
/**
* Checks if <code>BitVector</code> includes a bit at index
* <code>bitIndex</code> and, optionally, if the bit has a known state. If
* not, prints an error message and exits the JVM.
*
* @param bitIndex
* bit index
* @param checkValidity
* whether to insist bit is valid
*/
private void checkIndex(int bitIndex, boolean checkValidity) {
if (bitIndex < 0 || bitIndex > numBits) {
Infrastructure.fatal("Bit index " + bitIndex
+ " outside allowed range of 0.." + numBits
+ " in BitVector " + toString());
}
if (checkValidity && this.valid.get(bitIndex) == false) {
printInvalidError(bitIndex);
}
}
/**
* @param bitIndex
* index of invalid bit being accessed
*/
private void printInvalidError(int bitIndex) {
Infrastructure.fatal("Attempt to access the bit at position "
+ bitIndex + " in BitVector '" + getName()
+ "', which is in the 'invalid' state. "
+ "Bits must be explicitly set before being read. "
+ "This error probably indicates incorrect "
+ "initialization of the BitVector. "
+ "The BitVector state is " + getState());
}
/**
* Checks if bit range is consistent with this <code>BitVector</code>. If
* not, prints an error message and exits the JVM.
*
* @param fromIndex
* starting index
* @param nbits
* number of bits in range
* @param checkValidity
* whether to insist bit is valid
*/
private void checkRange(int fromIndex, int nbits, boolean checkValidity) {
if (nbits <= 0) {
Infrastructure.fatal("Attempt to read " + nbits
+ " bits, number must be positive");
}
if ((fromIndex + nbits) > numBits) {
Infrastructure
.fatal("Attempt to read past the end" + " of BitVector '"
+ getName() + "', which has length " + numBits
+ ": fromIndex=" + fromIndex + ", nbits=" + nbits);
}
if (checkValidity) {
for (int ind = fromIndex; ind < fromIndex + nbits; ind++) {
if (this.valid.get(ind) == false) {
printInvalidError(ind);
}
}
}
}
/**
* Unit test, sets and prints some bit vectors. If args[0] is present, it is
* the string (e.g., "01110") to set <code>BitVector b1</code> to. If
* args[1] is prsent, it is the string to set <code>BitVector b2</code>
* to. Otherwise default values of "1101" and "1001" are used.
*/
public static void main(String[] args) {
String b2String;
// Test put(string), toString().
BitVector b1 = new BitVector(7, "b1");
if (args.length > 0) {
b1.put(0, args[0]);
} else {
b1.put(0, "1101");
}
System.out.print(b1 + ": bitSet=" + b1.bitSet);
System.out.println(", length=" + b1.numBits + ", size="
+ b1.bitSet.size());
// Test get(), set(), clear()
System.out.println("3 bits starting at 1: " + b1.get(1, 3));
b1.set(2);
b1.clear(1);
System.out.println("After setting 2, clearing 1: " + b1);
// Test put()
if (args.length > 1) {
b2String = args[1];
} else {
b2String = "1001";
}
BitVector b2 = new BitVector(b2String.length(), "b2");
b2.put(0, b2String);
b1.put(2, b2);
System.out.println("inserted " + b2 + " at position 2: " + b1);
b1.put(2, "010");
System.out.println("inserted 3 bits 010 at position 2: " + b1);
BitVector b3 = new BitVector(40, "b3");
BigInteger biggy = new BigInteger("3");
b3.set(0, b3.getNumBits(), false);
b3.put(0, 2, biggy);
System.out.println("biggy = " + biggy + ", b3 = " + b3.getState()
+ ", biggy2 = " + b3.toBigInteger() + " ("
+ b3.toLittleInteger() + ")");
biggy = new BigInteger("536870912");
b3.put(3, 30, biggy);
System.out.println("biggy = " + biggy + ", b3 = " + b3.getState()
+ ", biggy2 = " + b3.toBigInteger() + " ("
+ b3.toLittleInteger() + ")");
BitVector b4 = new BitVector(10, "b4");
biggy = new BigInteger("512");
b4.put(0, 10, biggy);
System.out.println("biggy = " + biggy + ", b4 = " + b4.getState()
+ ", biggy2 = " + b4.toBigInteger() + " ("
+ b4.toLittleInteger() + ")");
biggy = new BigInteger("11");
b4.putLittle(2, 5, biggy);
System.out.println("biggy = " + biggy + ", b4 = " + b4.getState()
+ ", biggy2 = " + b4.toBigInteger() + " ("
+ b4.toLittleInteger() + ")");
System.out.println("Convenience constructor: "
+ new BitVector("101", "convenience"));
}
}