com.google.uzaygezen.core.LongBitVector.java Source code

Java tutorial

Introduction

Here is the source code for com.google.uzaygezen.core.LongBitVector.java

Source

/*
 * Copyright (C) 2008 Google Inc.
 *
 * 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.
 */

package com.google.uzaygezen.core;

import java.math.BigInteger;
import java.util.BitSet;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import com.google.common.base.Preconditions;

/**
 * BitVector implementation for vectors of length 64 or less.
 * 
 * @author Mehmet Akin
 * @author Daniel Aioanei
 */
public final class LongBitVector implements BitVector, Cloneable {

    private static final long WORD_MASK = -1L;
    private static final int BITS_PER_WORD = 64;

    private final int size;

    // used to clear excess bits after operations.
    // Equal to WORD_MASK >>> BITS_PER_WORD - size
    private final long mask;

    private long data;

    public LongBitVector(int size) {
        this(size, 0);
        Preconditions.checkArgument(size >= 0 && size <= BITS_PER_WORD,
                "Size must be positive and <= {1} size: {2}", BITS_PER_WORD, size);
    }

    /**
     * Unsafe constructor. Keep it private.
     */
    private LongBitVector(int size, long data) {
        assert 64 - Long.numberOfLeadingZeros(data) <= size;
        this.size = size;
        this.data = data;
        mask = size == 0 ? 0 : WORD_MASK >>> BITS_PER_WORD - size;
    }

    private void checkSize(BitVector other) {
        if (size != other.size()) {
            throw new IllegalArgumentException("Sizes must be equal. " + this.size + " : " + other.size());
        }
    }

    private void checkIndex(int bitIndex) {
        if (bitIndex < 0 | bitIndex >= size) {
            throw new IndexOutOfBoundsException("Bit index out of range: " + bitIndex);
        }
    }

    private void checkBounds(int fromIndex, int toIndex) {
        if (fromIndex < 0 | toIndex > size | fromIndex > toIndex) {
            throw new IndexOutOfBoundsException(
                    "Range [" + fromIndex + ", " + toIndex + ") is invalid for this bit vector");
        }
    }

    @Override
    public void and(BitVector o) {
        checkSize(o);
        data &= o.toExactLong();
    }

    @Override
    public void andNot(BitVector o) {
        checkSize(o);
        data &= ~o.toExactLong();
    }

    @Override
    public int cardinality() {
        return Long.bitCount(data);
    }

    @Override
    public void clear() {
        data = 0;
    }

    @Override
    public void clear(int bitIndex) {
        checkIndex(bitIndex);
        data &= ~(1L << bitIndex);
    }

    @Override
    public void clear(int fromIndex, int toIndex) {
        checkBounds(fromIndex, toIndex);
        if (fromIndex != toIndex) {
            unsafeClearNonEmptySection(fromIndex, toIndex);
        }
    }

    private void unsafeClearNonEmptySection(int fromIndex, int toIndex) {
        data &= ~((WORD_MASK << fromIndex) & (WORD_MASK >>> -toIndex));
    }

    @Override
    public void copyFrom(BitVector from) {
        checkSize(from);
        data = from.toExactLong();
    }

    @Override
    public void flip(int bitIndex) {
        checkIndex(bitIndex);
        data ^= (1L << bitIndex);
    }

    @Override
    public void flip(int fromIndex, int toIndex) {
        checkBounds(fromIndex, toIndex);
        if (fromIndex != toIndex) {
            data ^= ((WORD_MASK << fromIndex) & (WORD_MASK >>> -toIndex));
        }
    }

    @Override
    public boolean get(int bitIndex) {
        checkIndex(bitIndex);
        return unsafeGet(bitIndex);
    }

    private boolean unsafeGet(int bitIndex) {
        return (data & (1L << bitIndex)) != 0;
    }

    @Override
    public void grayCode() {
        data ^= (data >>> 1);
    }

    @Override
    public void grayCodeInverse() {
        long localData = data;
        localData ^= localData >>> 1;
        localData ^= localData >>> 2;
        localData ^= localData >>> 4;
        localData ^= localData >>> 8;
        localData ^= localData >>> 16;
        localData ^= localData >>> 32;
        data = localData;
    }

    @Override
    public boolean increment() {
        // Check for overflow
        if (data == mask) {
            return false;
        }
        data++;
        return true;
    }

    @Override
    public boolean intersects(BitVector o) {
        checkSize(o);
        return (data & o.toExactLong()) != 0;
    }

    @Override
    public int length() {
        return BITS_PER_WORD - Long.numberOfLeadingZeros(data);
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public int nextClearBit(int fromIndex) {
        Preconditions.checkArgument(fromIndex >= 0);
        if (fromIndex >= size) {
            return -1;
        }
        long w = ~data & (WORD_MASK << fromIndex);
        int tcb = Long.numberOfTrailingZeros(w);
        return tcb == size ? -1 : tcb;
    }

    @Override
    public int nextSetBit(int fromIndex) {
        Preconditions.checkArgument(fromIndex >= 0);
        if (fromIndex >= size) {
            return -1;
        }
        long w = data & (WORD_MASK << fromIndex);
        int tcb = Long.numberOfTrailingZeros(w);
        return tcb == 64 ? -1 : tcb;
    }

    @Override
    public void or(BitVector o) {
        checkSize(o);
        this.data |= o.toExactLong();
    }

    @Override
    public void rotate(int count) {
        final int localSize = size;
        count %= localSize;
        final long localData = data;
        if (count > 0) {
            data = ((localData >>> count) | (localData << localSize - count)) & mask;
        } else {
            data = ((localData >>> localSize + count) | (localData << -count)) & mask;
        }
    }

    @Override
    public void set(int bitIndex) {
        checkIndex(bitIndex);
        data |= (1L << bitIndex);
    }

    public void set(int bitIndex, boolean value) {
        if (value) {
            set(bitIndex);
        } else {
            clear(bitIndex);
        }
    }

    @Override
    public void set(int fromIndex, int toIndex) {
        checkBounds(fromIndex, toIndex);
        if (fromIndex != toIndex) {
            data |= ((WORD_MASK << fromIndex) & (WORD_MASK >>> -toIndex));
        }
    }

    @Override
    public void set(int fromIndex, int toIndex, boolean value) {
        if (value) {
            set(fromIndex, toIndex);
        } else {
            clear(fromIndex, toIndex);
        }
    }

    @Override
    public void xor(BitVector o) {
        checkSize(o);
        this.data ^= o.toExactLong();
    }

    @Override
    public boolean isEmpty() {
        return data == 0;
    }

    @Override
    public LongBitVector clone() {
        try {
            return (LongBitVector) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new InternalError("Cloning error. ");
        }
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof BitVector) {
            BitVector other = (BitVector) obj;
            // optimisation
            if (size <= 64) {
                return size == other.size() && data == other.toExactLong();
            } else {
                return size == other.size() && toBitSet().equals(other.toBitSet());
            }
        } else {
            return false;
        }
    }

    @Override
    public int hashCode() {
        // We imitate BitSet's hashcode implementation 
        long h = 1234 ^ data;
        int bitSetHashCode = (int) ((h >> 32) ^ h);
        return size + 31 * bitSetHashCode;
    }

    @Override
    public String toString() {
        return StringUtils.leftPad(Long.toBinaryString(data), size, '0');
    }

    @Override
    public BitSet toBitSet() {
        BitSet b = new BitSet(size);
        for (int i = 0; i < size; i++) {
            if (unsafeGet(i)) {
                b.set(i);
            }
        }
        return b;
    }

    @Override
    public long toLong() {
        return data;
    }

    @Override
    public BigInteger toBigInteger() {
        final BigInteger result;
        if (data >= 0) {
            result = BigInteger.valueOf(data);
        } else {
            BigInteger missingLowestBit = BigInteger.valueOf(data >>> 1).shiftLeft(1);
            if ((data & 1) == 1) {
                result = missingLowestBit.setBit(0);
            } else {
                result = missingLowestBit;
            }
        }
        return result;
    }

    public void copyFrom(long value) {
        Preconditions.checkArgument(64 - Long.numberOfLeadingZeros(value) <= size, "value doesn't fit");
        data = value;
    }

    @Override
    public int compareTo(BitVector o) {
        checkSize(o);
        final int cmp;
        // optimisation
        if (o.size() <= 64) {
            // 0, positives, Long.MAX_VALUE, Long.MIN_VALUE, negatives, -1
            long x = data + Long.MIN_VALUE;
            long y = o.toExactLong() + Long.MIN_VALUE;
            cmp = Long.compare(x, y);
            assert Integer.signum(cmp) == Integer
                    .signum(BitSetComparator.INSTANCE.compare(toBitSet(), o.toBitSet()));
        } else {
            cmp = BitSetComparator.INSTANCE.compare(toBitSet(), o.toBitSet());
        }
        return cmp;
    }

    @Override
    public void copyFrom(BitSet from) {
        int localSize = size;
        long value = 0;
        for (int i = from.nextSetBit(0); i != -1; i = from.nextSetBit(i + 1)) {
            Preconditions.checkArgument(i < localSize, "bit set too large");
            value |= 1L << i;
        }
        data = value;
    }

    @Override
    public void copyFromSection(BitVector src, int fromIndex) {
        Preconditions.checkArgument(fromIndex >= 0, "fromIndex must be non-negative");
        int srcSize = src.size();
        int toIndex = fromIndex + size;
        Preconditions.checkArgument(toIndex <= srcSize, "not enough bits in src");
        long value;
        if (toIndex <= 64) {
            long srcData = src.toLong();
            value = (srcData >>> fromIndex) & mask;
        } else {
            value = 0;
            for (int i = src.nextSetBit(fromIndex); i < toIndex && i != -1; i = src.nextSetBit(i + 1)) {
                value |= 1L << (i - fromIndex);
            }
        }
        data = value;
    }

    @Override
    public long toExactLong() {
        return data;
    }

    @Override
    public void smallerEvenAndGrayCode() {
        long localData = data;
        if ((localData & 0x1) == 1) {
            assert size > 0;
            data = localData ^ (localData >>> 1) ^ 0x1;
        } else {
            if (localData != 0) {
                long dataMinusTwo = localData - 2;
                data = dataMinusTwo ^ (dataMinusTwo >>> 1);
            }
        }
    }

    @Override
    public void grayCodeRank(BitVector mu, BitVector w) {
        grayCodeRank(mu, w, true);
    }

    /**
     * Visible for testing.
     */
    void grayCodeRank(BitVector mu, BitVector w, boolean optimiseIfPossible) {
        int theirSize = mu.size();
        Preconditions.checkArgument(theirSize == w.size(), "mu/w size mismatch");
        int muLen = mu.length();
        long pow2pos = 1L;
        long value = 0;
        if (optimiseIfPossible & muLen <= 64) {
            // mu doesn't have any set bits over index 63
            long muLong = mu.toExactLong();
            // w might have some set bits over index 63, but they don't matter anyway
            long wLong = w.toLong();
            long pow2i = 1L;
            for (int i = 0; i < muLen; ++i) {
                if ((muLong & pow2i) != 0) {
                    if ((wLong & pow2i) != 0) {
                        value |= pow2pos;
                    }
                    pow2pos <<= 1;
                }
                pow2i <<= 1;
            }
        } else {
            for (int j = theirSize == 0 ? -1 : mu.nextSetBit(0); j != -1; j = j == theirSize - 1 ? -1
                    : mu.nextSetBit(j + 1)) {
                if (w.get(j)) {
                    value |= pow2pos;
                }
                pow2pos <<= 1;
            }
        }
        assert pow2pos == 1L << mu.cardinality();
        Preconditions.checkArgument(1L << size == pow2pos, "wrong size");
        data = value;
    }

    @Override
    public int lowestDifferentBit() {
        long localData = data;
        final int value;
        if ((localData & 0x1L) == 0) {
            if (localData == 0) {
                value = 0;
            } else {
                value = Long.numberOfTrailingZeros(localData);
            }
        } else {
            if (localData == mask) {
                value = 0;
            } else {
                value = Long.numberOfTrailingZeros(~localData);
            }
        }
        assert value == 0 || (0 < value & value < size);
        return value;
    }

    @Override
    public void grayCodeRankInverse(BitVector mu, BitVector known, BitVector r) {
        int muSize = mu.size();
        Preconditions.checkArgument(size == muSize, "i/mu size mismatch");
        // Will fail if the sizes are different.
        Preconditions.checkArgument(!known.intersects(mu), "known and mu must not intersect");

        long muLong = mu.toExactLong();
        long knownLong = known.toExactLong();

        // Will check r.size() against mu.cardinality later.
        int rSize = r.size();
        Preconditions.checkArgument(rSize <= muSize, "r is too large");
        long rLong = r.toExactLong();

        long value = 0;
        int pos = 0;
        int muLength = mu.length();

        long pow2k = 1L;
        for (int k = 0; k < muLength; ++k) {
            if ((muLong & pow2k) != 0) {
                if ((rLong >> pos & 1L) != 0) {
                    value |= pow2k;
                }
                ++pos;
            }
            pow2k <<= 1;
        }
        assert pos == mu.cardinality();
        Preconditions.checkArgument(pos == rSize, "r.size()/mu.cardinality() mismatch");
        int knownLength = known.length();
        for (int k = Math.max(muLength - 1, knownLength); --k >= 0;) {
            pow2k = 1L << k;
            if ((muLong & pow2k) == 0) {
                assert (value & pow2k) == 0;
                if (((knownLong & pow2k) ^ (value >> 1 & pow2k)) != 0) {
                    value |= pow2k;
                }
            }
        }
        data = value;
    }

    @Override
    public void copySectionFrom(int offset, BitVector src) {
        int srcSize = src.size();
        int toIndex = offset + srcSize;
        if (offset < 0 | toIndex > size) {
            throw new IndexOutOfBoundsException("invalid range: offset=" + offset + " src.size()=" + src.size());
        }
        if (offset != toIndex) {
            unsafeClearNonEmptySection(offset, toIndex);
            long srcData = src.toExactLong();
            data |= srcData << offset;
        }
    }

    @Override
    public long[] toLongArray() {
        return size == 0 ? ArrayUtils.EMPTY_LONG_ARRAY : new long[] { data };
    }

    @Override
    public byte[] toBigEndianByteArray() {
        int n = MathUtils.bitCountToByteCount(size);
        byte[] a = new byte[n];
        long x = data;
        for (int i = 0; i < n;) {
            a[n - ++i] = (byte) (x & 0xFF);
            x >>>= 8;
        }
        assert x == 0;
        return a;
    }

    @Override
    public void copyFrom(long[] array) {
        if (size == 0) {
            Preconditions.checkArgument(array.length == 0, "Array must be empty.");
        } else {
            Preconditions.checkArgument(array.length == 1, "Array length must be 1.");
            copyFrom(array[0]);
        }
    }

    @Override
    public void copyFromBigEndian(byte[] array) {
        int n = MathUtils.bitCountToByteCount(size);
        Preconditions.checkArgument(array.length == n, "Array length must be %s.", n);
        long x = 0;
        for (int i = 0; i < n - 1;) {
            x |= (array[i++] & 0xFF);
            x <<= 8;
        }
        if (n != 0) {
            x |= (array[n - 1] & 0xFF);
        }
        copyFrom(x);
    }

    @Override
    public boolean areAllLowestBitsClear(int bitCount) {
        Preconditions.checkArgument(0 <= bitCount & bitCount <= size, "bitCount is out of range");
        // Only bitCount == 64 is affected by xoring with (bitCount >> 6). 
        return (data & (((1L << bitCount) ^ (bitCount >> 6)) - 1)) == 0;
    }

    @Override
    public void copyFrom(BigInteger s) {
        Preconditions.checkArgument(s.signum() >= 0, s);
        Preconditions.checkArgument(s.bitLength() <= size());
        // Note that the long value will be negative iff bitLength == 644 and bit 63
        // is set.
        copyFrom(s.longValue());
    }
}