Java tutorial
/* * Copyright (C) 2015 The greyfish authors * * This program 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. * * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ package org.asoem.greyfish.utils.collect; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.*; import com.google.common.base.Optional; import com.google.common.collect.AbstractIterator; import com.google.common.collect.*; import com.google.common.primitives.Longs; import org.apache.commons.math3.distribution.BinomialDistribution; import org.apache.commons.math3.random.RandomGenerator; import org.asoem.greyfish.utils.math.statistics.Samplings; import javax.annotation.Nullable; import javax.annotation.concurrent.ThreadSafe; import java.util.*; import static com.google.common.base.Preconditions.*; /** * An immutable, finite and ordered sequence of binary values. */ @ThreadSafe public abstract class BitString extends AbstractList<Boolean> { /** * Get the number of 1 bits in this string. * * @return the number of 1 bits */ public abstract int cardinality(); /** * Returns the bit set backend for this bit string. * * @return a {@code BitSet} */ protected abstract BitSet bitSet(); /** * Returns a new long array containing all the bits in this bit string. * * @return a long array containing a little-endian representation of all the bits in this bit string */ public abstract long[] toLongArray(); /** * Computes the <a href="https://en.wikipedia.org/wiki/Hamming_distance">hamming distance</a> between this string * and an {@code other} string which must be of equal length. * * @param other an other string * @return the number of positions where the elements in both strings are not equal * @throws java.lang.IllegalArgumentException is the lists are not of the same length */ public final int hammingDistance(final BitString other) { checkNotNull(other); checkArgument(other.size() == size()); return xor(other).cardinality(); } /** * Creates a new bit sequence by performing a logical <b>AND</b> of this bit sequence with an {@code other} bit * sequence. If the lengths do differ, the smaller one is padded with zeros. * * @param other the other bit sequence * @return a new bit sequence */ public abstract BitString and(BitString other); protected final BitString standardAnd(final BitString other) { final BitSet bitSetCopy = this.bitSet(); bitSetCopy.and(other.bitSet()); return BitString.create(bitSetCopy, Math.max(size(), other.size())); } /** * Creates a new bit sequence by performing a logical <b>OR</b> of this bit sequence with an {@code other} bit * sequence. If the lengths do differ, the smaller one is padded with zeros. * * @param other the other bit sequence * @return a new bit sequence */ public abstract BitString or(BitString other); protected final BitString standardOr(final BitString other) { final BitSet bitSetCopy = this.bitSet(); bitSetCopy.or(other.bitSet()); return BitString.create(bitSetCopy, Math.max(size(), other.size())); } /** * Creates a new bit sequence by performing a logical <b>XOR</b> of this bit sequence with an {@code other} bit * sequence. If the lengths do differ, the smaller one is padded with zeros. * * @param other the other bit sequence * @return a new bit sequence */ public abstract BitString xor(BitString other); protected final BitString standardXor(final BitString other) { final BitSet bitSetCopy = this.bitSet(); bitSetCopy.xor(other.bitSet()); return BitString.create(bitSetCopy, Math.max(size(), other.size())); } /** * Creates a new bit sequence by performing a logical <b>AND NOT</b> of this bit sequence with an {@code other} bit * sequence. If the lengths do differ, the smaller one is padded with zeros. * * @param other the other bit sequence * @return a new bit sequence */ public abstract BitString andNot(BitString other); protected final BitString standardAndNot(final BitString other) { final BitSet bitSetCopy = this.bitSet(); bitSetCopy.andNot(other.bitSet()); return BitString.create(bitSetCopy, Math.max(size(), other.size())); } /** * Return the inverse of this bit string. * * @return the inverse of this bit string */ public final BitString not() { if (this instanceof InverseString) { return ((InverseString) this).delegate; } return new InverseString(this); } /** * Create a bit string which is a view upon this string from start (inclusive) to end (exclusive). * * @param start the position in this string which should be the first in the new string * @param end the position in this string which is the first not to get included in the new string * @return a view upon the current string */ public final BitString subSequence(final int start, final int end) { checkPositionIndexes(start, end, size()); if (start == end) { return emptyBitSequence(); } return new SubString(start, end - start); } @Override public final String toString() { final StringBuilder builder = new StringBuilder(); for (int i = 0; i < size(); i++) { builder.append(get(i) ? '1' : '0'); } return builder.reverse().toString(); } @Override public final boolean equals(@Nullable final Object obj) { if (obj == this) { return true; } if (!(obj instanceof BitString)) { return false; } final BitString bitString = (BitString) obj; return this.size() == bitString.size() && Iterables.elementsEqual(this.asIndices(), bitString.asIndices()); } @Override public final int hashCode() { int hashCode = 1; for (Object o : this.asIndices()) { hashCode = 31 * hashCode + o.hashCode(); hashCode = ~~hashCode; // needed to deal with GWT integer overflow } hashCode = 31 * hashCode + size(); return hashCode; } private static BitString create(final BitSet bitSet, final int length) { if (length == 0) { return emptyBitSequence(); } return new BitSetString(bitSet, length); } private static BitString create(final BitSet bitSet, final int length, final int cardinality) { if (length == 0) { return emptyBitSequence(); } else if (cardinality == 0) { return zeros(length); } else if (cardinality == length) { return ones(length); } else { return new BitSetString((BitSet) bitSet.clone(), length, cardinality); } } public static BitString ones(final int length) { if (length == 0) { return emptyBitSequence(); } return new InverseString(zeros(length)); } public static BitString zeros(final int length) { if (length == 0) { return emptyBitSequence(); } return new IndexSetString(ImmutableSet.<Integer>of(), length); } public static BitString create(final Iterable<Boolean> val) { checkNotNull(val); final BitSet bs = new BitSet(); int idx = 0; int cardinality = 0; for (final boolean b : val) { bs.set(idx++, b); if (b) { ++cardinality; } } return create(bs, idx, cardinality); } public static BitString concat(final BitString sequence1, final BitString sequence2) { checkNotNull(sequence1); checkNotNull(sequence2); return new CombinedString(sequence1, sequence2); } /** * Create a new {@code BitString} from given string {@code s} of '0' and '1' chars * * @param s a string of '0' and '1' chars * @return a new BitString equal to the representation of {@code s} */ public static BitString parse(final String s) { return new BitSetString(BitSets.parse(s), s.length()); } /** * Create a random bit string of given {@code length}. * * @param length the length of the bit string * @param rng the random number generator to use * @return a new bit string of given length. */ public static BitString random(final int length, final RandomGenerator rng) { checkNotNull(rng); checkArgument(length >= 0); if (length == 0) { return emptyBitSequence(); } long[] longs = new long[(length + 63) / 64]; for (int i = 0; i < longs.length; i++) { longs[i] = rng.nextLong(); } longs[longs.length - 1] &= (~0L >>> (longs.length * 64 - length)); return new BitSetString(BitSet.valueOf(longs), length); } private static BitString emptyBitSequence() { return EmptyString.INSTANCE; } /** * Create a random bit string of given {@code length} where each bit is set with probability {@code p}, and not set * with probability {@code 1-p}. * * @param length the length of the bit string * @param rng the random number generator to use * @param p the probability for each bit in the new bit string to hold the value 1 * @return a new bit string */ public static BitString random(final int length, final RandomGenerator rng, final double p) { checkNotNull(rng); checkArgument(p >= 0 && p <= 1); checkArgument(length >= 0); if (length == 0) { return emptyBitSequence(); } if (p == 0.5) { return random(length, rng); // faster } final int n; if (p == 0) { n = 0; } else if (p == 1) { n = length; } else { final BinomialDistribution binomialDistribution = new BinomialDistribution(rng, length, p); n = binomialDistribution.sample(); } assert n >= 0 && n <= length : n; if (n == 0) { return zeros(length); } else if (n == length) { return ones(length); } final ContiguousSet<Integer> indexRange = ContiguousSet.create(Range.closedOpen(0, length), DiscreteDomain.integers()); final Iterable<Integer> uniqueIndexSample = Samplings.random(rng).withoutReplacement().sample(indexRange, n); if ((double) n / length < 1.0 / 32) { // < 1 bit per word? return new IndexSetString(ImmutableSortedSet.copyOf(uniqueIndexSample), length); } else { final BitSet bs = new BitSet(length); for (Integer index : uniqueIndexSample) { bs.set(index, true); } return new BitSetString(bs, length); } } public static BitString forBitSet(final BitSet bitSet, final int length) { return create(bitSet, length); } public static BitString create(final int length, final long... longs) { return create(BitSet.valueOf(longs), length); } /** * Create a new bit string of given {@code length} with bits set to one at given {@code indices}. The indices might * be given * * @param indices the indices of the bits to set * @param length the length of the bit string to create * @return a new bit string of given {@code length} */ public static BitString forIndices(final Set<Integer> indices, final int length) { if ((double) indices.size() / length < 1.0 / 32) { return new IndexSetString(indices, length); } else { final BitSet bitSet = new BitSet(length); for (Integer index : indices) { bitSet.set(index); } return forBitSet(bitSet, length); } } /** * Create an iterable of ordered indices where this string's bits are set to 1. * * @return iterable of indices */ public abstract Iterable<Integer> asIndices(); /** * Find the index of the next set bit starting at index {@code from}. * * @param from the index to start searching from * @return the index of the next set bit, or {@link com.google.common.base.Optional#absent()}. */ public abstract Optional<Integer> nextSetBit(final int from); /** * Find the index of the previous set bit starting at index {@code from}. * * @param from the index to start searching from * @return the index of the previous set bit, or {@link com.google.common.base.Optional#absent()}. */ public abstract Optional<Integer> previousSetBit(final int from); /** * Find the index of the next clear bit starting at index {@code from}. * * @param from the index to start searching from * @return the index of the next clear bit, or {@link com.google.common.base.Optional#absent()}. */ public abstract Optional<Integer> nextClearBit(final int from); /** * Find the index of the previous clear bit starting at index {@code from}. * * @param from the index to start searching from * @return the index of the previous clear bit, or {@link com.google.common.base.Optional#absent()}. */ public abstract Optional<Integer> previousClearBit(final int from); @VisibleForTesting static final class BitSetString extends RandomAccessBitString { private final BitSet bitSet; // is mutable, so don't expose outside of class private final int length; private final Supplier<Integer> cardinalitySupplier; BitSetString(final BitSet bitSet, final int length) { this(bitSet, length, Suppliers.memoize(new Supplier<Integer>() { @Override public Integer get() { return bitSet.cardinality(); } })); } private BitSetString(final BitSet bitSet, final int length, final int cardinality) { this(bitSet, length, new Supplier<Integer>() { @Override public Integer get() { return cardinality; } }); } private BitSetString(final BitSet bitSet, final int length, final Supplier<Integer> cardinalitySupplier) { assert bitSet != null; assert bitSet.length() <= length : "Length of bitSet was > length: " + bitSet.length() + " > " + length; assert cardinalitySupplier != null; this.bitSet = bitSet; this.length = length; this.cardinalitySupplier = cardinalitySupplier; } @Override public Boolean get(final int index) { checkElementIndex(index, size()); return bitSet.get(index); } @Override public int cardinality() { return cardinalitySupplier.get(); } protected BitSet bitSet() { return (BitSet) bitSet.clone(); } @Override public long[] toLongArray() { return bitSet().toLongArray(); } @Override public Optional<Integer> nextSetBit(final int from) { return optionalFromIndex(bitSet.nextSetBit(from)); } private static Optional<Integer> optionalFromIndex(final int index) { return index == -1 ? Optional.<Integer>absent() : Optional.of(index); } @Override public Optional<Integer> previousSetBit(final int from) { return optionalFromIndex(bitSet.previousSetBit(from)); } @Override public Optional<Integer> nextClearBit(final int from) { return optionalFromIndex(bitSet.nextClearBit(from)); } @Override public Optional<Integer> previousClearBit(final int from) { return optionalFromIndex(bitSet.previousClearBit(from)); } @Override public int size() { return length; } public BitString and(final BitString other) { return standardAnd(other); } public BitString or(final BitString other) { return standardOr(other); } public BitString xor(final BitString other) { return standardXor(other); } public BitString andNot(final BitString other) { return standardAndNot(other); } } @VisibleForTesting final class SubString extends BitString { private final int offset; private final int length; private final Supplier<Integer> cardinalityMemoizer = Suppliers.memoize(new Supplier<Integer>() { @Override public Integer get() { return computeCardinality(); } }); public SubString(final int offset, final int length) { this.offset = offset; this.length = length; } @Override public int cardinality() { return cardinalityMemoizer.get(); } private int computeCardinality() { return Iterables.size(asIndices()); } @Override protected BitSet bitSet() { return BitString.this.bitSet().get(offset, offset + length); } @Override public long[] toLongArray() { return bitSet().toLongArray(); } @Override public Boolean get(final int index) { checkElementIndex(index, size()); return BitString.this.get(offset + index); } @Override public int size() { return length; } public BitString and(final BitString other) { return standardAnd(other); } public BitString or(final BitString other) { return standardOr(other); } public BitString xor(final BitString other) { return standardXor(other); } public BitString andNot(final BitString other) { return standardAndNot(other); } @Override public Iterable<Integer> asIndices() { return FluentIterable.from(BitString.this.asIndices()).filter(new Predicate<Integer>() { @Override public boolean apply(final Integer input) { return input >= offset && input < offset + length; } }).transform(new Function<Integer, Integer>() { @Nullable @Override public Integer apply(final Integer input) { return input - offset; } }); } @Override public Optional<Integer> nextSetBit(final int from) { checkPositionIndex(from, size()); final Optional<Integer> nextSetBit = BitString.this.nextSetBit(offset + from); if (nextSetBit.isPresent()) { return nextSetBit.get() >= offset + length ? Optional.<Integer>absent() : Optional.of(nextSetBit.get() - offset); } else { return Optional.absent(); } } @Override public Optional<Integer> previousSetBit(final int from) { checkPositionIndex(from, size()); final Optional<Integer> previousSetBit = BitString.this.previousSetBit(offset + from); return previousSetBit.get() < offset ? Optional.<Integer>absent() : Optional.of(previousSetBit.get() - offset); } @Override public Optional<Integer> nextClearBit(final int from) { checkPositionIndex(from, size()); final Optional<Integer> nextClearBit = BitString.this.nextClearBit(offset + from); if (nextClearBit.isPresent()) { return nextClearBit.get() >= offset + length ? Optional.<Integer>absent() : Optional.of(nextClearBit.get() - offset); } else { return Optional.absent(); } } @Override public Optional<Integer> previousClearBit(final int from) { checkPositionIndex(from, size()); final Optional<Integer> previousClearBit = BitString.this.previousClearBit(offset + from); return previousClearBit.get() < offset ? Optional.<Integer>absent() : Optional.of(previousClearBit.get() - offset); } } private static class EmptyString extends BitString { public static final EmptyString INSTANCE = new EmptyString(); private EmptyString() { } @Override public int cardinality() { return 0; } @Override protected BitSet bitSet() { return new BitSet(0); } @Override public long[] toLongArray() { return new long[0]; } @Override public Boolean get(final int index) { checkElementIndex(index, size()); throw new AssertionError("Unreachable"); } @Override public final int size() { return 0; } public final BitString and(final BitString other) { return zeros(other.size()); } public final BitString or(final BitString other) { return other; } public final BitString xor(final BitString other) { return other; } public final BitString andNot(final BitString other) { return zeros(other.size()); } @Override public Iterable<Integer> asIndices() { return ImmutableSet.of(); } @Override public Optional<Integer> nextSetBit(final int from) { return Optional.absent(); } @Override public Optional<Integer> previousSetBit(final int from) { return Optional.absent(); } @Override public Optional<Integer> nextClearBit(final int from) { return Optional.absent(); } @Override public Optional<Integer> previousClearBit(final int from) { return Optional.absent(); } } @VisibleForTesting static final class CombinedString extends BitString { private final BitString lowString; private final BitString highString; public CombinedString(final BitString lowString, final BitString highString) { this.lowString = lowString; this.highString = highString; } @Override public int cardinality() { return lowString.cardinality() + highString.cardinality(); } @Override protected BitSet bitSet() { final BitSet combinedBitSet = (BitSet) lowString.bitSet().clone(); BitSets.set(combinedBitSet, lowString.size(), highString); return combinedBitSet; } @Override public long[] toLongArray() { return bitSet().toLongArray(); } @Override public Boolean get(final int index) { checkElementIndex(index, size()); if (index < lowString.size()) { return lowString.get(index); } else { return highString.get((index - lowString.size())); } } @Override public int size() { return lowString.size() + highString.size(); } public BitString and(final BitString other) { return standardAnd(other); } public BitString or(final BitString other) { return standardOr(other); } public BitString xor(final BitString other) { return standardXor(other); } public BitString andNot(final BitString other) { return standardAndNot(other); } @Override public Iterable<Integer> asIndices() { return Iterables.concat(lowString.asIndices(), FluentIterable.from(highString.asIndices()).transform(new Function<Integer, Integer>() { @Nullable @Override public Integer apply(final Integer input) { return input + lowString.size(); } })); } @Override public Optional<Integer> nextSetBit(final int from) { if (from < lowString.size()) { final Optional<Integer> bs1next = lowString.nextSetBit(from); if (bs1next.isPresent()) { return bs1next; } else { final Optional<Integer> bs2next = highString.nextSetBit(0); return bs2next.isPresent() ? Optional.of(lowString.size() + bs2next.get()) : Optional.<Integer>absent(); } } else { final Optional<Integer> index = highString.nextSetBit((from - lowString.size())); return index.isPresent() && index.get() < size() ? Optional.of(lowString.size() + index.get()) : Optional.<Integer>absent(); } } @Override public Optional<Integer> previousSetBit(final int from) { if (from >= lowString.size()) { final Optional<Integer> bs2previous = highString.previousSetBit(from - lowString.size()); if (bs2previous.isPresent()) { return Optional.of(lowString.size() + bs2previous.get()); } else { return lowString.previousSetBit(lowString.size()); } } else { return lowString.previousSetBit(from); } } @Override public Optional<Integer> nextClearBit(final int from) { if (from < lowString.size()) { final Optional<Integer> bs1next = lowString.nextClearBit(from); if (bs1next.isPresent()) { return bs1next; } else { final Optional<Integer> bs2next = highString.nextClearBit(0); return bs2next.isPresent() ? Optional.of(lowString.size() + bs2next.get()) : Optional.<Integer>absent(); } } else { final Optional<Integer> index = highString.nextClearBit((from - lowString.size())); return index.isPresent() && index.get() < size() ? Optional.of(lowString.size() + index.get()) : Optional.<Integer>absent(); } } @Override public Optional<Integer> previousClearBit(final int from) { if (from >= lowString.size()) { final Optional<Integer> bs2previous = highString.previousClearBit(from - lowString.size()); if (bs2previous.isPresent()) { return Optional.of(lowString.size() + bs2previous.get()); } else { return lowString.previousClearBit(lowString.size()); } } else { return lowString.previousClearBit(from); } } } @VisibleForTesting static final class IndexSetString extends BitString { private final SortedSet<Integer> indices; private final int length; @SuppressWarnings("unchecked") // safe cast IndexSetString(final Set<Integer> indices, final int length) { checkArgument(length >= 0); checkNotNull(indices, "indices"); final ImmutableSortedSet<Integer> sortedIndices = ImmutableSortedSet.copyOf(indices); checkArgument(sortedIndices.isEmpty() || sortedIndices.first() >= 0 && sortedIndices.last() < length); this.indices = sortedIndices; this.length = length; } @Override public int cardinality() { return indices.size(); } @Override protected BitSet bitSet() { final BitSet bitSet = new BitSet(length); for (Integer index : indices) { bitSet.set(index); } return bitSet; } @Override public long[] toLongArray() { final long[] longs = bitSet().toLongArray(); return Longs.concat(new long[(size() + 63) / 64 - longs.length], longs); } @Override public Boolean get(final int index) { checkElementIndex(index, size()); return indices.contains(index); } @Override public int size() { return length; } public BitString and(final BitString other) { if (other instanceof IndexSetString) { return forIndices(Sets.intersection(indices, ((IndexSetString) other).indices), Math.max(size(), other.size())); } return standardAnd(other); } public BitString or(final BitString other) { if (other instanceof IndexSetString) { return forIndices(Sets.union(indices, ((IndexSetString) other).indices), Math.max(size(), other.size())); } return standardOr(other); } public BitString xor(final BitString other) { if (other instanceof IndexSetString) { return forIndices(Sets.symmetricDifference(indices, ((IndexSetString) other).indices), Math.max(size(), other.size())); } return standardXor(other); } public BitString andNot(final BitString other) { return standardAndNot(other); } @Override public Iterable<Integer> asIndices() { return indices; } @Override public Optional<Integer> nextSetBit(final int from) { checkPositionIndex(from, size()); if (size() == 0) { return Optional.absent(); } for (Integer index : indices) { if (index >= from) { return Optional.of(index); } } return Optional.absent(); } @Override public Optional<Integer> previousSetBit(final int from) { checkPositionIndex(from, size()); if (size() == 0) { return Optional.absent(); } final ImmutableSortedSet<Integer> reversed = ImmutableSortedSet.<Integer>reverseOrder().addAll(indices) .build(); for (Integer index : reversed) { if (index <= from) { return Optional.of(index); } } return Optional.absent(); } @Override public Optional<Integer> nextClearBit(final int from) { checkPositionIndex(from, size()); if (size() == 0) { return Optional.absent(); } Optional<Integer> lastSet = Optional.absent(); for (Integer index : indices) { if (index <= from || lastSet.isPresent() && lastSet.get() + 1 == index) { lastSet = Optional.of(index); } else { break; } } if (lastSet.isPresent()) { if (lastSet.get() < size() - 1) { return Optional.of(lastSet.get() + 1); } else { return Optional.absent(); } } else { return Optional.of(from); } } @Override public Optional<Integer> previousClearBit(final int from) { checkPositionIndex(from, size()); if (size() == 0) { return Optional.absent(); } final ImmutableSortedSet<Integer> reversed = ImmutableSortedSet.<Integer>reverseOrder().addAll(indices) .build(); Optional<Integer> previousSet = Optional.absent(); for (Integer index : reversed) { if (index >= from || previousSet.isPresent() && index == previousSet.get() - 1) { previousSet = Optional.of(index); } else { break; } } if (previousSet.isPresent()) { if (previousSet.get() > 0) { return Optional.of(previousSet.get() - 1); } else { return Optional.absent(); } } else { return Optional.of(from); } } } static class InverseString extends BitString { private final BitString delegate; public InverseString(final BitString delegate) { this.delegate = delegate; } @Override public int cardinality() { return delegate.size() - delegate.cardinality(); } @Override protected BitSet bitSet() { return BitSet.valueOf(toLongArray()); } @Override public long[] toLongArray() { final long[] longs = delegate.toLongArray(); for (int i = 0; i < longs.length; i++) { longs[i] = ~longs[i]; if (i == longs.length - 1) { longs[i] = longs[i] & (~0L >>> (longs.length * 64 - size())); } } return longs; } @Override public BitString and(final BitString other) { return standardAnd(other); } @Override public BitString or(final BitString other) { return standardOr(other); } @Override public BitString xor(final BitString other) { return standardXor(other); } @Override public BitString andNot(final BitString other) { return standardAndNot(other); } @Override public Iterable<Integer> asIndices() { final Iterable<Integer> integers = delegate.asIndices(); final ContiguousSet<Integer> all = ContiguousSet.create(Range.closedOpen(0, size()), DiscreteDomain.integers()); if (integers instanceof Set) { return Sets.difference(all, (Set<Integer>) integers); } else { return new FluentIterable<Integer>() { @Override public Iterator<Integer> iterator() { return new AbstractIterator<Integer>() { private final Iterator<Integer> allBits = all.iterator(); private final Iterator<Integer> setBits = integers.iterator(); private Optional<Integer> nextSetBit = Optional.absent(); @Override protected Integer computeNext() { if (!nextSetBit.isPresent()) { setNextSetBit(); } while (allBits.hasNext()) { final Integer next = allBits.next(); if (nextSetBit.isPresent() && nextSetBit.get() <= next) { setNextSetBit(); } else { return next; } } return endOfData(); } private void setNextSetBit() { if (setBits.hasNext()) { nextSetBit = Optional.of(setBits.next()); } else { nextSetBit = Optional.absent(); } } }; } }; } } @Override public Optional<Integer> nextSetBit(final int from) { return delegate.nextClearBit(from); } @Override public Optional<Integer> previousSetBit(final int from) { return delegate.previousClearBit(from); } @Override public Optional<Integer> nextClearBit(final int from) { return delegate.nextSetBit(from); } @Override public Optional<Integer> previousClearBit(final int from) { return delegate.previousSetBit(from); } @Override public Boolean get(final int index) { return !delegate.get(index); } @Override public int size() { return delegate.size(); } } private abstract static class RandomAccessBitString extends BitString { @Override public final Iterable<Integer> asIndices() { return new FluentIterable<Integer>() { @Override public Iterator<Integer> iterator() { return new AbstractIterator<Integer>() { private int searchIndex = 0; @Override protected Integer computeNext() { final Optional<Integer> nextSetIndex = nextSetBit(searchIndex); if (!nextSetIndex.isPresent()) { return endOfData(); } searchIndex = nextSetIndex.get() + 1; return nextSetIndex.get(); } }; } }; } } }