package tc.oc.commons.bukkit.util; import java.util.Collection; import java.util.Iterator; import java.util.Random; import java.util.Set; import gnu.trove.TLongCollection; import gnu.trove.impl.Constants; import gnu.trove.iterator.TLongIterator; import gnu.trove.set.TLongSet; import gnu.trove.set.hash.TLongHashSet; import org.bukkit.util.BlockVector; import static tc.oc.commons.bukkit.util.BlockUtils.decodePos; import static tc.oc.commons.bukkit.util.BlockUtils.encodePos; /** * Optimized implementation of a set of block locations. Coordinates are * encoded into a single long integer with bit masking, and stored in * a Trove primitive collection. */ public class BlockVectorSet implements Set<BlockVector> { private final TLongSet set; BlockVectorSet(TLongSet set) { this.set = set; } public BlockVectorSet(int capacity) { this(new TLongHashSet(capacity, Constants.DEFAULT_LOAD_FACTOR, BlockUtils.ENCODED_NULL_POS)); } public BlockVectorSet(Collection<? extends BlockVector> that) { this(that.size()); addAll(that); } public BlockVectorSet() { this(Constants.DEFAULT_CAPACITY); } public static BlockVectorSet of(Collection<? extends BlockVector> that) { return that instanceof BlockVectorSet ? (BlockVectorSet) that : new BlockVectorSet(that); } @Override public int size() { return this.set.size(); } @Override public boolean isEmpty() { return this.set.isEmpty(); } @Override public Iterator<BlockVector> iterator() { final TLongIterator iter = this.set.iterator(); return new Iterator<BlockVector>() { @Override public boolean hasNext() { return iter.hasNext(); } @Override public BlockVector next() { return decodePos(iter.next()); } @Override public void remove() { iter.remove(); } }; } /** * Return an iterator that reuses a single BlockVector instance, * mutating it for each iteration. */ public Iterator<BlockVector> mutableIterator() { final TLongIterator iter = set.iterator(); return new Iterator<BlockVector>() { final BlockVector value = new BlockVector(); @Override public boolean hasNext() { return iter.hasNext(); } @Override public BlockVector next() { return decodePos(iter.next(), value); } }; } public boolean contains(long encoded) { return this.set.contains(encoded); } public boolean contains(int x, int y, int z) { return this.contains(encodePos(x, y, z)); } @Override public boolean contains(Object o) { return o instanceof BlockVector && this.set.contains(encodePos((BlockVector) o)); } public boolean add(long encoded) { return this.set.add(encoded); } public boolean add(int x, int y, int z) { return this.add(encodePos(x, y, z)); } @Override public boolean add(BlockVector vector) { return this.add(encodePos(vector)); } public boolean remove(long encoded) { return this.set.remove(encoded); } public boolean remove(int x, int y, int z) { return this.remove(encodePos(x, y, z)); } @Override public boolean remove(Object o) { return o instanceof BlockVector && this.remove(encodePos((BlockVector) o)); } public boolean containsAll(long[] encoded) { return this.set.containsAll(encoded); } public boolean containsAll(TLongCollection encoded) { return this.set.containsAll(encoded); } @Override public boolean containsAll(Collection<?> c) { for(Object o : c) { if(!this.contains(o)) return false; } return true; } public boolean addAll(long[] encoded) { return this.set.addAll(encoded); } public boolean addAll(TLongCollection encoded) { return this.set.addAll(encoded); } @Override public boolean addAll(Collection<? extends BlockVector> vectors) { for(BlockVector v : vectors) { this.add(v); } return false; } public boolean retainAll(long[] encoded) { return this.set.retainAll(encoded); } public boolean retainAll(TLongCollection encoded) { return this.set.retainAll(encoded); } @Override public boolean retainAll(Collection<?> vectors) { return this.retainAll(BlockUtils.encodePosSet(vectors)); } public boolean removeAll(TLongCollection encoded) { return set.removeAll(encoded); } public boolean removeAll(long[] encoded) { return set.removeAll(encoded); } @Override public boolean removeAll(Collection<?> vectors) { return this.removeAll(BlockUtils.encodePosSet(vectors)); } @Override public void clear() { this.set.clear(); } @Override public Object[] toArray() { throw new UnsupportedOperationException(); } @Override public <T> T[] toArray(T[] a) { throw new UnsupportedOperationException(); } public BlockVector chooseRandom(Random random) { // The Trove set uses a sparse array, so there isn't really any // faster way to do this, not even by messing with Trove internals. final TLongIterator iterator = set.iterator(); long encoded = 0; for(int n = random.nextInt(size()); n >= 0; n--) { encoded = iterator.next(); } return decodePos(encoded); } }