org.chocosolver.memory.structure.OneWordS32BitSet Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of choco-solver Show documentation
Show all versions of choco-solver Show documentation
Open-source constraint solver.
/*
* This file is part of choco-solver, http://choco-solver.org/
*
* Copyright (c) 2023, IMT Atlantique. All rights reserved.
*
* Licensed under the BSD 4-clause license.
*
* See LICENSE file in the project root for full license information.
*/
package org.chocosolver.memory.structure;
import org.chocosolver.memory.IEnvironment;
import org.chocosolver.memory.IStateBitSet;
import org.chocosolver.memory.IStateInt;
public class OneWordS32BitSet implements IStateBitSet {
/*
* BitSets are packed into arrays of "word." Currently a word is
* an int, which consists of 32 bits, requiring 6 address bits.
* The choice of word size is determined purely by performance concerns.
*/
private final static int ADDRESS_BITS_PER_WORD = 5;
private final static int BITS_PER_WORD = 1 << ADDRESS_BITS_PER_WORD;
/* Used to shift left or right for a partial word mask */
private static final int WORD_MASK = 0xffffffff;
/**
* The internal field corresponding to the serialField "bits".
*/
private final IStateInt word;
/**
* Creates a bit set whose initial size is 32.
* All bits are initially false
.
*
* @param environment backtrackable environment
* @throws NegativeArraySizeException if the specified initial size
* is negative.
*/
public OneWordS32BitSet(IEnvironment environment) {
word = environment.makeInt(0);
}
/**
* Checks that index is a valid range of bit indices.
*
* @param index index
*/
private static void checkIndex(int index) {
if (index < 0)
throw new IndexOutOfBoundsException("fromIndex < 0: " + index);
if (index > 31)
throw new IndexOutOfBoundsException("fromIndex > 31: " + index);
}
/**
* Checks that fromIndex ... toIndex is a valid range of bit indices.
*
* @param fromIndex starting index
* @param toIndex ending index
*/
private static void checkRange(int fromIndex, int toIndex) {
checkIndex(fromIndex);
checkIndex(toIndex);
if (fromIndex > toIndex)
throw new IndexOutOfBoundsException("fromIndex: " + fromIndex + " > toIndex: " + toIndex);
}
/**
* Sets the bit at the specified index to true
.
*
* @param bitIndex a bit index.
* @throws IndexOutOfBoundsException if the specified index is negative.
* @since JDK1.0
*/
@Override
public void set(int bitIndex) {
checkIndex(bitIndex);
word.set(word.get() | (1 << bitIndex)); // Restores invariants
}
/**
* Sets the bit at the specified index to the specified value.
*
* @param bitIndex a bit index.
* @param value a boolean value to set.
* @throws IndexOutOfBoundsException if the specified index is negative.
* @since 1.4
*/
@Override
public void set(int bitIndex, boolean value) {
if (value)
set(bitIndex);
else
clear(bitIndex);
}
/**
* Sets the bits from the specified fromIndex (inclusive) to the
* specified toIndex (exclusive) to true
.
*
* @param fromIndex index of the first bit to be set.
* @param toIndex index after the last bit to be set.
* @throws IndexOutOfBoundsException if fromIndex is negative,
* or toIndex is negative, or fromIndex is
* larger than toIndex.
* @since 1.4
*/
@Override
public void set(int fromIndex, int toIndex) {
checkRange(fromIndex, toIndex);
if (fromIndex == toIndex)
return;
int firstWordMask = WORD_MASK << fromIndex;
int lastWordMask = WORD_MASK >>> -toIndex;
word.set(word.get() | (firstWordMask & lastWordMask));
}
/**
* Sets the bit specified by the index to false
.
*
* @param bitIndex the index of the bit to be cleared.
* @throws IndexOutOfBoundsException if the specified index is negative.
* @since JDK1.0
*/
@Override
public void clear(int bitIndex) {
checkIndex(bitIndex);
word.set(word.get() & ~(1 << bitIndex));
}
/**
* Sets the bits from the specified fromIndex (inclusive) to the
* specified toIndex (exclusive) to false
.
*
* @param fromIndex index of the first bit to be cleared.
* @param toIndex index after the last bit to be cleared.
* @throws IndexOutOfBoundsException if fromIndex is negative,
* or toIndex is negative, or fromIndex is
* larger than toIndex.
* @since 1.4
*/
@Override
public void clear(int fromIndex, int toIndex) {
checkRange(fromIndex, toIndex);
if (fromIndex == toIndex)
return;
int firstWordMask = WORD_MASK << fromIndex;
int lastWordMask = WORD_MASK >>> -toIndex;
word.set(word.get() & ~(firstWordMask & lastWordMask));
}
/**
* Sets all of the bits in this BitSet to false
.
*
* @since 1.4
*/
@Override
public void clear() {
word.set(0);
}
/**
* Returns the value of the bit with the specified index. The value
* is true
if the bit with the index bitIndex
* is currently set in this BitSet
; otherwise, the result
* is false
.
*
* @param bitIndex the bit index.
* @return the value of the bit with the specified index.
* @throws IndexOutOfBoundsException if the specified index is negative.
*/
@Override
final public boolean get(final int bitIndex) {
return bitIndex < 32 && ((word.get() & (1 << bitIndex)) != 0);
}
/**
* Returns the index of the first bit that is set to {@code true}
* that occurs on or after the specified starting index. If no such
* bit exists then {@code -1} is returned.
*
* To iterate over the {@code true} bits in a {@code BitSet},
* use the following loop:
*
* {@code
* for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i+1)) {
* // operate on index i here
* }}
*
* @param fromIndex the index to start checking from (inclusive)
* @return the index of the next set bit, or {@code -1} if there
* is no such bit
* @throws IndexOutOfBoundsException if the specified index is negative
* @since 1.4
*/
@Override
public int nextSetBit(int fromIndex) {
if (fromIndex < 0) {
fromIndex = 0;
}
if (fromIndex >= 32)
return -1;
int word = this.word.get() & (WORD_MASK << fromIndex);
if (word != 0)
return Integer.numberOfTrailingZeros(word);
else
return -1;
}
/**
* Returns the index of the first bit that is set to {@code false}
* that occurs on or after the specified starting index.
*
* @param fromIndex the index to start checking from (inclusive)
* @return the index of the next clear bit
* @throws IndexOutOfBoundsException if the specified index is negative
* @since 1.4
*/
@Override
public int nextClearBit(int fromIndex) {
// Neither spec nor implementation handle bitsets of maximal length.
// See 4816253.
if (fromIndex < 0) {
fromIndex = 0;
}
if (fromIndex >= 32)
return fromIndex;
int word = ~this.word.get() & (WORD_MASK << fromIndex);
if (word != 0)
return Integer.numberOfTrailingZeros(word);
else
return BITS_PER_WORD;
}
/**
* Returns the index of the nearest bit that is set to {@code true}
* that occurs on or before the specified starting index.
* If no such bit exists, or if {@code -1} is given as the
* starting index, then {@code -1} is returned.
*
* To iterate over the {@code true} bits in a {@code BitSet},
* use the following loop:
*
* {@code
* for (int i = bs.length(); (i = bs.previousSetBit(i-1)) >= 0; ) {
* // operate on index i here
* }}
*
* @param fromIndex the index to start checking from (inclusive)
* @return the index of the previous set bit, or {@code -1} if there
* is no such bit
* @throws IndexOutOfBoundsException if the specified index is less
* than {@code -1}
* @since 1.7
*/
@Override
public int prevSetBit(int fromIndex) {
if (fromIndex < 0) {
return -1;
}
if (fromIndex >= 32)
return length() - 1;
int word = this.word.get() & (WORD_MASK >>> -(fromIndex + 1));
if (word != 0)
return BITS_PER_WORD - 1 - Integer.numberOfLeadingZeros(word);
else
return -1;
}
/**
* Returns the index of the nearest bit that is set to {@code false}
* that occurs on or before the specified starting index.
* If no such bit exists, or if {@code -1} is given as the
* starting index, then {@code -1} is returned.
*
* @param fromIndex the index to start checking from (inclusive)
* @return the index of the previous clear bit, or {@code -1} if there
* is no such bit
* @throws IndexOutOfBoundsException if the specified index is less
* than {@code -1}
* @since 1.7
*/
@Override
public int prevClearBit(int fromIndex) {
if (fromIndex < 0) {
return -1;
}
if (fromIndex >= 32)
return fromIndex;
int word = ~this.word.get() & (WORD_MASK >>> -(fromIndex + 1));
if (word != 0)
return BITS_PER_WORD - 1 - Integer.numberOfLeadingZeros(word);
else
return -1;
}
/**
* Returns the "logical size" of this BitSet
: the index of
* the highest set bit in the BitSet
plus one. Returns zero
* if the BitSet
contains no set bits.
*
* @return the logical size of this BitSet
.
* @since 1.2
*/
public int length() {
return (BITS_PER_WORD - Integer.numberOfLeadingZeros(word.get()));
}
/**
* Returns true if this BitSet
contains no bits that are set
* to true
.
*
* @return boolean indicating whether this BitSet
is empty.
* @since 1.4
*/
@Override
public boolean isEmpty() {
return word.get() == 0;
}
/**
* Returns the number of bits set to true in this
* BitSet
.
*
* @return the number of bits set to true in this
* BitSet
.
* @since 1.4
*/
@Override
public int cardinality() {
return Integer.bitCount(word.get());
}
@Override
public int hashCode() {
int h = 1234;
h ^= word.get();
return (h >> 14) ^ h;
}
/**
* Returns the number of bits of space actually in use by this
* BitSet
to represent bit values.
* The maximum element in the set is the size - 1st element.
*
* @return the number of bits currently in this bit set.
*/
@Override
public int size() {
return BITS_PER_WORD;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof OneWordS32BitSet))
return false;
if (this == obj)
return true;
OneWordS32BitSet set = (OneWordS32BitSet) obj;
// Check word in use by both BitSets
return word == set.word;
}
@Override
public String toString() {
StringBuilder b = new StringBuilder(6 * BITS_PER_WORD + 2);
b.append('{');
int i = nextSetBit(0);
if (i != -1) {
b.append(i);
for (i = nextSetBit(i + 1); i >= 0; i = nextSetBit(i + 1)) {
int endOfRun = nextClearBit(i);
do {
b.append(", ").append(i);
}
while (++i < endOfRun);
}
}
b.append('}');
return b.toString();
}
}