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

org.chocosolver.memory.structure.S64BitSet Maven / Gradle / Ivy

The newest version!
/*
 * This file is part of choco-solver, http://choco-solver.org/
 *
 * Copyright (c) 2024, 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;
import org.chocosolver.memory.IStateLong;

import java.lang.reflect.Array;


public class S64BitSet implements IStateBitSet {

    private final static boolean CHECK = false;

    /*
    * BitSets are packed into arrays of "words."  Currently a word is
    * a long, which consists of 64 bits, requiring 6 address bits.
    * The choice of word size is determined purely by performance concerns.
    */
    private final static int ADDRESS_BITS_PER_WORD = 6;
    protected final static int BITS_PER_WORD = 1 << ADDRESS_BITS_PER_WORD;

    /* Used to shift left or right for a partial word mask */
    protected static final long WORD_MASK = 0xffffffffffffffffL;

    /**
     * The current environment.
     */
    private final IEnvironment environment;

    /**
     * The internal field corresponding to the serialField "bits".
     */
    protected IStateLong[] words;

    /**
     * The number of words in the logical size of this BitSet.
     */
    protected final IStateInt wordsInUse;


    /**
     * Given a bit index, return word index containing it.
     *
     * @param bitIndex bit index
     */
    protected static int wordIndex(int bitIndex) {
        return bitIndex >> ADDRESS_BITS_PER_WORD;
    }

    /**
     * Check that the given index is strictly positive.
     *
     * @param index the index
     * @throws IndexOutOfBoundsException if the index is negative
     */
    private static void requirePositiveIndex(final int index) {
        if (index < 0) {
            throw new IndexOutOfBoundsException(
                    "Positive index expected. Got " + index);
        }
    }

    /**
     * Every public method must preserve these invariants.
     */
    private void checkInvariants() {
        assert (wordsInUse.get() == 0 || words[wordsInUse.get() - 1].get() != 0);
        assert (wordsInUse.get() >= 0 && wordsInUse.get() <= words.length);
        assert (wordsInUse.get() == words.length || words[wordsInUse.get()].get() == 0);
    }

    /**
     * Set the field wordsInUse with the logical size in words of the bit
     * set.  WARNING:This method assumes that the number of words actually
     * in use is less than or equal to the current value of wordsInUse!
     */
    private void recalculateWordsInUse() {
        // Traverse the bitset until a used word is found
        int i;
        int n = wordsInUse.get();
        for (i = n - 1; i >= 0; i--)
            if (words[i].get() != 0)
                break;
        if (i + 1 < n) {
            wordsInUse.set(i + 1); // The new logical size
        }
    }

    /**
     * Creates a new bit set. All bits are initially false.
     *
     * @param environment bactrackable environment
     */
    public S64BitSet(IEnvironment environment) {
        this.environment = environment;
        this.wordsInUse = environment.makeInt(0);
        initWords(BITS_PER_WORD);
    }

    /**
     * Creates a bit set whose initial size is large enough to explicitly
     * represent bits with indices in the range 0 through
     * nbits-1. All bits are initially false.
     *
     * @param environment backtrackable environment
     * @param nbits       the initial size of the bit set.
     * @throws NegativeArraySizeException if the specified initial size
     *                                    is negative.
     */
    public S64BitSet(IEnvironment environment, int nbits) {
        this.environment = environment;
        this.wordsInUse = environment.makeInt(0);
        // nbits can't be negative; size 0 is OK
        if (nbits < 0)
            throw new NegativeArraySizeException("nbits < 0: " + nbits);

        initWords(nbits);
    }

    private void initWords(int nbits) {
        words = new IStateLong[wordIndex(nbits - 1) + 1];
        for (int i = 0; i < words.length; i++) words[i] = this.environment.makeLong(0);
        if (CHECK) checkInvariants();
    }


    /**
     * Ensures that the BitSet can hold enough words.
     *
     * @param wordsRequired the minimum acceptable number of words.
     */
    public void ensureCapacity(int wordsRequired) {
        if (words.length < wordsRequired) {
            // Allocate larger of doubled size or required size
            int request = Math.max(2 * words.length, wordsRequired);
            int oldSize = words.length;
            words = copyOf(words, request);
            for (int i = oldSize; i < request; i++) {
                words[i] = environment.makeLong(0);
            }
        }
    }

    @SuppressWarnings({"unchecked"})
    public static  T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
    }

    @SuppressWarnings({"unchecked", "SuspiciousSystemArraycopy", "RedundantCast"})
    public static  T[] copyOf(U[] original, int newLength, Class newType) {
        T[] copy = ((Object) newType == (Object) Object[].class)
                ? (T[]) new Object[newLength]
                : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                Math.min(original.length, newLength));
        return copy;
    }

    /**
     * Ensures that the BitSet can accommodate a given wordIndex,
     * temporarily violating the invariants.  The caller must
     * restore the invariants before returning to the user,
     * possibly using recalculateWordsInUse().
     *
     * @param wordIndex the index to be accommodated.
     */
    private void expandTo(int wordIndex) {
        int wordsRequired = wordIndex + 1;
        if (wordsInUse.get() < wordsRequired) {
            ensureCapacity(wordsRequired);
            wordsInUse.set(wordsRequired);
        }
    }

    /**
     * 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) {
        if (fromIndex < 0)
            throw new IndexOutOfBoundsException("fromIndex < 0: " + fromIndex);
        if (toIndex < 0)
            throw new IndexOutOfBoundsException("toIndex < 0: " + 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
     */
    public void set(int bitIndex) {
        requirePositiveIndex(bitIndex);
        if (CHECK) checkInvariants();
        int wordIndex = wordIndex(bitIndex);
        expandTo(wordIndex);

        words[wordIndex].set(words[wordIndex].get() | (1L << bitIndex)); // Restores invariants

        if (CHECK) checkInvariants();
    }

    /**
     * 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
     */
    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
     */
    public void set(int fromIndex, int toIndex) {
        checkRange(fromIndex, toIndex);

        if (fromIndex == toIndex)
            return;

        // Increase capacity if necessary
        int startWordIndex = wordIndex(fromIndex);
        int endWordIndex = wordIndex(toIndex - 1);
        expandTo(endWordIndex);

        long firstWordMask = WORD_MASK << fromIndex;
        long lastWordMask = WORD_MASK >>> -toIndex;
        if (startWordIndex == endWordIndex) {
            // Case 1: One word
            words[startWordIndex].set(words[startWordIndex].get() | (firstWordMask & lastWordMask));
        } else {
            // Case 2: Multiple words
            // Handle first word
            words[startWordIndex].set(words[startWordIndex].get() | firstWordMask);

            // Handle intermediate words, if any
            for (int i = startWordIndex + 1; i < endWordIndex; i++)
                words[i].set(WORD_MASK);

            // Handle last word (restores invariants)
            words[endWordIndex].set(words[endWordIndex].get() | lastWordMask);
        }

        if (CHECK) checkInvariants();
    }

    /**
     * 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
     */
    public void clear(int bitIndex) {
        requirePositiveIndex(bitIndex);

        int wordIndex = wordIndex(bitIndex);
        int n = wordsInUse.get();
        if (wordIndex >= n)
            return;

        words[wordIndex].set(words[wordIndex].get() & ~(1L << bitIndex));

        //if(wordIndex == n-1)
        recalculateWordsInUse();
        if (CHECK) checkInvariants();
    }

    /**
     * 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
     */
    public void clear(int fromIndex, int toIndex) {
        checkRange(fromIndex, toIndex);

        if (fromIndex == toIndex)
            return;

        int wiu = wordsInUse.get();
        int startWordIndex = wordIndex(fromIndex);
        if (startWordIndex >= wiu)
            return;

        int endWordIndex = wordIndex(toIndex - 1);
        if (endWordIndex >= wiu) {
            toIndex = length();
            endWordIndex = wiu - 1;
        }

        long firstWordMask = WORD_MASK << fromIndex;
        long lastWordMask = WORD_MASK >>> -toIndex;
        if (startWordIndex == endWordIndex) {
            // Case 1: One word
            words[startWordIndex].set(words[startWordIndex].get() & ~(firstWordMask & lastWordMask));
        } else {
            // Case 2: Multiple words
            // Handle first word
            words[startWordIndex].set(words[startWordIndex].get() & ~firstWordMask);

            // Handle intermediate words, if any
            for (int i = startWordIndex + 1; i < endWordIndex; i++)
                words[i].set(0);

            // Handle last word
            words[endWordIndex].set(words[endWordIndex].get() & ~lastWordMask);
        }

        //if(endWordIndex < wiu)
        recalculateWordsInUse();
        if (CHECK) checkInvariants();
    }

    /**
     * Sets all of the bits in this BitSet to false.
     *
     * @since 1.4
     */
    public void clear() {
        /*while (wordsInUse.get() > 0)
            wordsInUse.set(wordsInUse.get() - 1);
        words[wordsInUse.get()].set(0);      */
        for (IStateLong word : words) {
            word.set(0);
        }
        wordsInUse.set(0);
        if (CHECK) checkInvariants();
    }

    /**
     * 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.
     */
    final public boolean get(final int bitIndex) {
        requirePositiveIndex(bitIndex);

        if (CHECK) checkInvariants();

        int wordIndex = bitIndex >> ADDRESS_BITS_PER_WORD; //wordIndex(bitIndex);
        return (wordIndex < wordsInUse.get())
                && ((words[wordIndex].get() & (1L << 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 */ public int nextSetBit(int fromIndex) { if (fromIndex < 0) { fromIndex = 0; } int wiu = wordsInUse.get(); int u = wordIndex(fromIndex); if (u >= wiu) return -1; long word = words[u].get() & (WORD_MASK << fromIndex); while (true) { if (word != 0) return (u * BITS_PER_WORD) + Long.numberOfTrailingZeros(word); if (++u == wiu) return -1; word = words[u].get(); } } /** * 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 */ public int nextClearBit(int fromIndex) { // Neither spec nor implementation handle bitsets of maximal length. // See 4816253. if (fromIndex < 0) { fromIndex = 0; } int wiu = wordsInUse.get(); int u = wordIndex(fromIndex); if (u >= wiu) return fromIndex; long word = ~words[u].get() & (WORD_MASK << fromIndex); while (true) { if (word != 0) return (u * BITS_PER_WORD) + Long.numberOfTrailingZeros(word); if (++u == wiu) return wiu * BITS_PER_WORD; word = ~words[u].get(); } } /** * 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 */ public int prevSetBit(int fromIndex) { if (fromIndex < 0) { return -1; } int u = wordIndex(fromIndex); if (u >= wordsInUse.get()) return length() - 1; long word = words[u].get() & (WORD_MASK >>> -(fromIndex + 1)); while (true) { if (word != 0) return (u + 1) * BITS_PER_WORD - 1 - Long.numberOfLeadingZeros(word); if (u-- == 0) return -1; word = words[u].get(); } } /** * 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 */ public int prevClearBit(int fromIndex) { if (fromIndex < 0) { return -1; } int u = wordIndex(fromIndex); if (u >= wordsInUse.get()) return fromIndex; long word = ~words[u].get() & (WORD_MASK >>> -(fromIndex + 1)); while (true) { if (word != 0) return (u + 1) * BITS_PER_WORD - 1 - Long.numberOfLeadingZeros(word); if (u-- == 0) return -1; word = ~words[u].get(); } } /** * 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() { int wiu = wordsInUse.get(); if (wiu == 0) return 0; return BITS_PER_WORD * (wiu - 1) + (BITS_PER_WORD - Long.numberOfLeadingZeros(words[wiu - 1].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 */ public boolean isEmpty() { return wordsInUse.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 */ public int cardinality() { int sum = 0; for (int i = wordsInUse.get() - 1; i >= 0; i--) sum += Long.bitCount(words[i].get()); return sum; } public int hashCode() { long h = 1234; for (int i = wordsInUse.get(); --i >= 0; ) h ^= words[i].get() * (i + 1); return (int) ((h >> 32) ^ 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. */ public int size() { return words.length * BITS_PER_WORD; } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (!(o instanceof IStateBitSet)) { return false; } final IStateBitSet that = (IStateBitSet) o; // Fail fast. if (this.cardinality() != that.cardinality()) { return false; } // Same cardinality. Iterate over the bit sets. Those must be sets in 'that' // as well. for (int i = nextSetBit(0); i >= 0; i = nextSetBit(i + 1)) { if (!that.get(i)) { return false; } } return true; } public String toString() { if (CHECK) checkInvariants(); int numBits = (wordsInUse.get() > 128) ? cardinality() : wordsInUse.get() * BITS_PER_WORD; StringBuilder b = new StringBuilder(6 * numBits + 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(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy