Java tutorial
/* * 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 com.google.common.base.Preconditions; import java.math.BigInteger; import java.util.*; import org.apache.commons.lang3.ArrayUtils; /** * An implementation of {@code BitVector} based on an array of longs. * It also has methods for easy concatenation and for easy slicing. * * @author Radu Grigore * @author Daniel Aioanei */ public class LongArrayBitVector implements BitVector { private final long[] data; private final int size; private static final int BYTE = 8; private static final int WORD = 64; private static final int BYTES_IN_WORD = WORD / BYTE; private LongArrayBitVector(long[] data, int size) { assert (size + WORD - 1) / WORD == data.length; this.data = data; this.size = size; assert checkSanity(); } public LongArrayBitVector(int size) { this(new long[(size + WORD - 1) / WORD], size); } public static LongArrayBitVector of(long value, int size) { LongArrayBitVector result = new LongArrayBitVector(size); result.copyFrom(value); return result; } public static LongArrayBitVector of(BitVector bitvector) { LongArrayBitVector result = new LongArrayBitVector(bitvector.size()); result.copyFrom(bitvector); return result; } public static LongArrayBitVector concat(Iterable<BitVector> bitVectors) { int size = 0; for (BitVector bv : bitVectors) { size += bv.size(); } long[] data = new long[(size + WORD - 1) / WORD]; int i = 0; for (BitVector bv : bitVectors) { copy(toPotentiallySharedLongArray(bv), 0, data, i, bv.size()); i += bv.size(); } return new LongArrayBitVector(data, size); } public static LongArrayBitVector concat(BitVector... bits) { return concat(Arrays.asList(bits)); } private void checkRange(int low, int high, int... values) { for (int v : values) { if (v < low || v > high) { throw new IndexOutOfBoundsException("index " + v + " should be in [" + low + ".." + high + "]"); } } } /* Should always return true. */ private boolean checkSanity() { return (size & (WORD - 1)) == 0 || (data[data.length - 1] & (-1L << size)) == 0L; } @Override public boolean isEmpty() { for (int i = 0; i < data.length; ++i) { if (data[i] != 0) { return false; } } return true; } @Override public void set(int bitIndex) { checkRange(0, size - 1, bitIndex); data[bitIndex / WORD] |= 1L << bitIndex; assert checkSanity(); } @Override public void set(int bitIndex, boolean value) { if (value) { set(bitIndex); } else { clear(bitIndex); } } @Override public void set(int fromIndex, int toIndex) { checkRange(0, size, fromIndex, toIndex); int fromBucket = fromIndex / WORD; int toBucket = toIndex / WORD; if (fromBucket == toBucket) { if (fromBucket != data.length) { data[fromBucket] |= (1L << toIndex) - (1L << fromIndex); } else { assert fromIndex == toIndex; assert toIndex == size; } } else { data[fromBucket] |= -(1L << fromIndex); if (toBucket != data.length) { data[toBucket] |= (1L << toIndex) - 1L; } else { assert toIndex == size; } Arrays.fill(data, fromBucket + 1, toBucket, -1L); } assert checkSanity(); } @Override public void set(int fromIndex, int toIndex, boolean value) { if (value) { set(fromIndex, toIndex); } else { clear(fromIndex, toIndex); } } @Override public boolean get(int bitIndex) { checkRange(0, size - 1, bitIndex); return (data[bitIndex / WORD] & (1L << bitIndex)) != 0L; } @Override public void copyFromSection(BitVector src, int fromIndex) { checkRange(0, src.size() - size, fromIndex); clear(); copyFromSection(toPotentiallySharedLongArray(src), fromIndex); assert checkSanity(); } @Override public void copySectionFrom(int offset, BitVector src) { checkRange(0, size - src.size(), offset); copySectionFrom(offset, toPotentiallySharedLongArray(src), src.size()); assert checkSanity(); } public LongArrayBitVector slice(int from, int to) { Preconditions.checkArgument(0 <= from && from <= to && to <= size); long[] newData = new long[(to - from + WORD - 1) / WORD]; copy(data, from, newData, 0, to - from); return new LongArrayBitVector(newData, to - from); } public LongArrayBitVector[] slice(int... widths) { LongArrayBitVector[] result = new LongArrayBitVector[widths.length]; int widthsSum = 0; for (int i = 0; i < widths.length; ++i) { Preconditions.checkArgument(widths[i] >= 0); Preconditions.checkArgument(widthsSum + widths[i] <= size); long[] newData = new long[(widths[i] + WORD - 1) / WORD]; copy(data, widthsSum, newData, 0, widths[i]); result[i] = new LongArrayBitVector(newData, widths[i]); widthsSum += widths[i]; } return result; } @Override public int length() { int i; for (i = data.length; --i >= 0 && data[i] == 0L;) ; if (i < 0) { return 0; } return WORD * (i + 1) - Long.numberOfLeadingZeros(data[i]); } @Override public int size() { return size; } @Override public void clear() { Arrays.fill(data, 0L); } @Override public void clear(int bitIndex) { checkRange(0, size - 1, bitIndex); data[bitIndex / WORD] &= ~(1L << bitIndex); assert checkSanity(); } @Override public void clear(int fromIndex, int toIndex) { checkRange(0, size, fromIndex, toIndex); int fromBucket = fromIndex / WORD; int toBucket = toIndex / WORD; if (fromBucket == toBucket) { if (fromBucket != data.length) { data[fromBucket] &= ~((1L << toIndex) - (1L << fromIndex)); } else { assert fromIndex == toIndex; assert toIndex == size; } } else { assert fromIndex != size; data[fromBucket] &= ~-(1L << fromIndex); if (toBucket != data.length) { data[toBucket] &= ~((1L << toIndex) - 1L); } else { assert toIndex == size; } Arrays.fill(data, fromBucket + 1, toBucket, 0L); } assert checkSanity(); } @Override public int cardinality() { int result = 0; for (int i = 0; i < data.length; ++i) { result += Long.bitCount(data[i]); } return result; } @Override public void flip(int bitIndex) { checkRange(0, size - 1, bitIndex); data[bitIndex / WORD] ^= 1L << bitIndex; assert checkSanity(); } @Override public void flip(int fromIndex, int toIndex) { checkRange(0, size, fromIndex, toIndex); int fromBucket = fromIndex / WORD; int toBucket = toIndex / WORD; if (fromBucket == toBucket) { if (fromBucket != data.length) { data[fromBucket] ^= (1L << toIndex) - (1L << fromIndex); } else { assert fromIndex == toIndex; assert toIndex == size; } } else { data[fromBucket] ^= -(1L << fromIndex); if (toBucket != data.length) { data[toBucket] ^= (1L << toIndex) - 1L; } else { assert toIndex == size; } for (++fromBucket; fromBucket < toBucket; ++fromBucket) { data[fromBucket] ^= -1L; } } assert checkSanity(); } @Override public boolean intersects(BitVector set) { return intersects(toPotentiallySharedLongArray(set)); } @Override public int nextSetBit(int fromIndex) { Preconditions.checkArgument(fromIndex >= 0); if (fromIndex >= size) { return -1; } int fromBucket = fromIndex / WORD; long word = data[fromBucket] & -(1L << fromIndex); while (word == 0L && ++fromBucket < data.length) { word = data[fromBucket]; } if (fromBucket == data.length) { return -1; } int result = WORD * fromBucket + Long.numberOfTrailingZeros(word); assert 0 <= result & result < size; return result; } @Override public int nextClearBit(int fromIndex) { Preconditions.checkArgument(fromIndex >= 0); if (fromIndex >= size) { return -1; } int fromBucket = fromIndex / WORD; long word = data[fromBucket] & -(1L << fromIndex); while (word == -1L && ++fromBucket < data.length) { word = data[fromBucket]; } if (fromBucket == data.length) { return -1; } int result = WORD * fromBucket + Long.numberOfTrailingZeros(~word); return result >= size ? -1 : result; } @Override public boolean increment() { int i; for (i = 0; i < data.length && data[i] == -1L; ++i) { data[i] = 0L; } if (i == data.length) { Arrays.fill(data, -1L); assert checkSanity(); return false; } if (i == data.length - 1 && data[i] == (1L << size) - 1L) { Arrays.fill(data, -1L); data[i] = (1L << size) - 1L; assert checkSanity(); return false; } ++data[i]; assert checkSanity(); return true; } /** If it is non-zero then it decreases the value by one and returns * {@code true}; otherwise it does nothing and returns false. */ public boolean decrement() { int i; for (i = 0; i < data.length && data[i] == 0L; ++i) { data[i] = -1L; } if (i == data.length) { Arrays.fill(data, 0L); assert checkSanity(); return false; } --data[i]; assert checkSanity(); return true; } @Override public void andNot(BitVector o) { Preconditions.checkArgument(size == o.size()); andNot(toPotentiallySharedLongArray(o)); assert checkSanity(); } @Override public void and(BitVector o) { Preconditions.checkArgument(size == o.size()); and(toPotentiallySharedLongArray(o)); assert checkSanity(); } @Override public void or(BitVector o) { Preconditions.checkArgument(size == o.size()); or(toPotentiallySharedLongArray(o)); assert checkSanity(); } @Override public void xor(BitVector o) { Preconditions.checkArgument(size == o.size()); xor(toPotentiallySharedLongArray(o)); assert checkSanity(); } @Override public void rotate(int count) { long[] old = Arrays.copyOf(data, data.length); count = ((count % size) + size) % size; copy(old, 0, data, size - count, count); copy(old, count, data, 0, size - count); assert checkSanity(); } @Override public void grayCode() { if (size == 0) { return; } int i; for (i = 0; i < data.length - 1; ++i) { data[i] ^= data[i] >>> 1; if ((data[i + 1] & 1L) != 0) { data[i] ^= 1L << (WORD - 1); } } data[i] ^= data[i] >>> 1; assert checkSanity(); } /* Let b[i] be the i-th bit of the result and a[i] the i-th bit in the * pre-state. Then b[i] = a[i] ^ a[i+1] ^ ... ^ a[size-1] = a[i] ^ b[i+1]. * So it can be computed with one loop from size-1 to 0. But for longs * we can compute y = x ^ (x/2) ^ (x/4) ^ ... in logarithmic time. */ @Override public void grayCodeInverse() { boolean last = false; for (int i = data.length; --i >= 0;) { data[i] ^= data[i] >>> 1; data[i] ^= data[i] >>> 2; data[i] ^= data[i] >>> 4; data[i] ^= data[i] >>> 8; data[i] ^= data[i] >>> 16; data[i] ^= data[i] >>> 32; if (last) { data[i] = ~data[i]; } last = (data[i] & 1L) != 0; } assert checkSanity(); } @Override public void smallerEvenAndGrayCode() { if (!decrement()) { return; } data[0] &= ~1L; grayCode(); assert checkSanity(); } @Override public int lowestDifferentBit() { if (size == 0) { return 0; } int i; boolean last = (data[0] & 1L) != 0; for (i = 0; i < data.length && data[i] == (last ? -1L : 0L); ++i) ; /* All bits in data[i-1], data[i-1], ..., data[0] are equal to last... */ if (i == data.length) { return 0; } /* ... and in data[i] there is a bit != last. */ int result = WORD * i + Long.numberOfTrailingZeros(last ? ~data[i] : data[i]); return result >= size ? 0 : result; } @Override public boolean areAllLowestBitsClear(int bitCount) { checkRange(0, size, bitCount); int i; int bucket = bitCount / WORD; for (i = 0; i < bucket && data[i] == 0L; ++i) ; return i == bucket && (size == 0 || (data[i] & ((1L << bitCount) - 1L)) == 0L); } /* This function selects bits of w according to the mask mu and * packs them. */ @Override public void grayCodeRank(BitVector mu, BitVector w) { Preconditions.checkArgument(mu.size() == w.size()); Preconditions.checkArgument(size == mu.cardinality()); clear(); int j = 0; if (mu.size() == 0) { assert checkSanity(); return; } for (int i = mu.nextSetBit(0); i >= 0; i = i + 1 < mu.size() ? mu.nextSetBit(i + 1) : -1) { if (w.get(i)) { set(j); } ++j; } assert checkSanity(); } /* We have x=R(m,g(x)&~m,r(m,x)), where * g=grayCode, G=grayCodeInverse, * r=grayCodeRank, R=grayCodeRankInverse. * In other words we know the bits of x for the positions given by the * mask mu and the bits of g(x) for the other positions. We want to * produce x. */ @Override public void grayCodeRankInverse(BitVector mu, BitVector known, BitVector r) { Preconditions.checkArgument(size == mu.size()); Preconditions.checkArgument(size == known.size()); Preconditions.checkArgument(r.size() == mu.cardinality()); Preconditions.checkArgument(!known.intersects(mu)); clear(); int j = r.size() - 1; boolean previous = false, current; for (int i = size - 1; i >= 0; --i) { current = mu.get(i) ? r.get(j--) : (previous ^ known.get(i)); if (current) { set(i); } previous = current; } } @Override public void copyFrom(BitVector from) { Preconditions.checkArgument(size == from.size()); copyFrom(toPotentiallySharedLongArray(from)); assert checkSanity(); } @Override public void copyFrom(BitSet from) { Preconditions.checkArgument(size >= from.length()); clear(); for (int i = from.nextSetBit(0); i >= 0; i = from.nextSetBit(i + 1)) { set(i); } assert checkSanity(); } @Override public LongArrayBitVector clone() { return new LongArrayBitVector(Arrays.copyOf(data, data.length), size); } @Override public BitSet toBitSet() { BitSet result = new BitSet(size); if (size != 0) { for (int i = nextSetBit(0); i != -1; i = nextSetBit(i + 1)) { result.set(i); } } return result; } @Override public long toLong() { if (size == 0) { return 0L; } else { return data[0]; } } @Override public long toExactLong() { Preconditions.checkState(size - length() <= WORD); return toLong(); } @Override public void copyFrom(long d) { Preconditions.checkArgument(WORD - Long.numberOfLeadingZeros(d) <= size); clear(); if (size > 0) { data[0] = d; } } @Override public long[] toLongArray() { return Arrays.copyOf(data, data.length); } @Override public byte[] toBigEndianByteArray() { int n = MathUtils.bitCountToByteCount(size); byte[] a = new byte[n]; long x = 0; int wordIndex = -1; for (int i = 0; i < n;) { if ((i & 7) == 0) { assert x == 0; x = data[++wordIndex]; } a[n - ++i] = (byte) (x & 0xFF); x >>>= 8; } assert x == 0; assert wordIndex == data.length - 1; return a; } @Override public BigInteger toBigInteger() { return new BigInteger(isEmpty() ? 0 : 1, toBigEndianByteArray()); } @Override public void copyFrom(long[] array) { Preconditions.checkArgument(data.length == array.length); System.arraycopy(array, 0, data, 0, data.length); assert checkSanity(); } @Override public void copyFromBigEndian(byte[] array) { ArrayUtils.reverse(array); try { copyFrom(array); } finally { ArrayUtils.reverse(array); } assert checkSanity(); } public void copyFrom(byte[] array) { Preconditions.checkArgument((size + BYTE - 1) / BYTE == array.length); clear(); for (int i = 0; i < array.length; ++i) { data[i / BYTES_IN_WORD] |= ((long) array[i] & 0xff) << (i % BYTES_IN_WORD * BYTE); } assert checkSanity(); } @Override public void copyFrom(BigInteger s) { byte[] array = BigIntegerMath.nonnegativeBigIntegerToBigEndianByteArrayForBitSize(s, size); copyFromBigEndian(array); } @Override public int hashCode() { // Imitate BitSet's hashCode. long h = 1234; for (int i = data.length; --i >= 0;) { h ^= data[i] * (i + 1); } return size + 31 * (int) ((h >> 32) ^ h); } @Override public boolean equals(Object o) { if (!(o instanceof BitVector)) { return false; } BitVector other = (BitVector) o; return size == other.size() && Arrays.equals(data, toPotentiallySharedLongArray(other)); } private void checkSize(BitVector other) { if (other.size() != this.size()) { throw new IllegalArgumentException("Sizes are not equal. " + "this:" + size + " other:" + other.size()); } } @Override public int compareTo(BitVector o) { checkSize(o); return compareTo(toPotentiallySharedLongArray(o)); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("[LongArrayBitVector: size="); sb.append(Integer.toString(size)); sb.append(" 0x"); if (size > 0) { sb.append(String.format("%X", data[data.length - 1])); } else { sb.append("0"); } for (int i = data.length - 1; --i >= 0;) { sb.append(String.format("%016X", data[i])); } sb.append("]"); return sb.toString(); } private static long[] toPotentiallySharedLongArray(BitVector vector) { if (vector instanceof LongArrayBitVector) { return ((LongArrayBitVector) vector).data; } else { return vector.toLongArray(); } } private void copyFromSection(long[] src, int fromIndex) { copy(src, fromIndex, data, 0, size); } private void copySectionFrom(int offset, long[] src, int srcSize) { copy(src, 0, data, offset, srcSize); } private void andNot(long[] other) { for (int i = 0; i < data.length; ++i) { data[i] &= ~other[i]; } } private void and(long[] other) { for (int i = 0; i < data.length; ++i) { data[i] &= other[i]; } } private void or(long[] other) { for (int i = 0; i < data.length; ++i) { data[i] |= other[i]; } } private void xor(long[] other) { for (int i = 0; i < data.length; ++i) { data[i] ^= other[i]; } } private int compareTo(long[] other) { int i; for (i = data.length; --i >= 0 && data[i] == other[i];) ; final int cmp; if (i == -1) { cmp = 0; } else { // 0, positives, Long.MAX_VALUE, Long.MIN_VALUE, negatives, -1 long x = data[i] + Long.MIN_VALUE; long y = other[i] + Long.MIN_VALUE; cmp = Long.compare(x, y); assert cmp != 0; } return cmp; } private boolean intersects(long[] other) { for (int i = 0; i < data.length; ++i) { if ((data[i] & other[i]) != 0) { return true; } } return false; } private static void copy(long[] source, int srcIndex, long[] destination, int destIndex, int length) { while (length > 0) { int toCopy = length; toCopy = Math.min(toCopy, left(srcIndex)); toCopy = Math.min(toCopy, left(destIndex)); int fromBucket = srcIndex / WORD; int toBucket = destIndex / WORD; long w = (mask(toCopy) << srcIndex) & source[fromBucket]; destination[toBucket] &= ~(mask(toCopy) << destIndex); destination[toBucket] |= (w >>> srcIndex) << destIndex; srcIndex += toCopy; destIndex += toCopy; length -= toCopy; } } private static int left(int index) { return (index + WORD) / WORD * WORD - index; } private static long mask(int k) { if (k == WORD) { return -1L; } else { return (1L << k) - 1L; } } }