From a7a77e35c747f322fec38ec51546afd48729538d Mon Sep 17 00:00:00 2001 From: "Hugh.C" Date: Fri, 6 Sep 2019 17:18:05 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E6=97=A0JIRA=E4=BB=BB=E5=8A=A1=E3=80=81jgi?= =?UTF-8?q?t=20gc=20=E9=9C=80=E8=A6=81googlecode=E3=80=81=E4=B9=8B?= =?UTF-8?q?=E5=89=8D=E6=B2=A1=E6=9C=89=E6=89=93=E5=8C=85=E5=AE=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fine-jgit/lib/JavaEWAH-0.7.9.jar | Bin 125146 -> 0 bytes .../storage/dfs/RemoteRepository.java | 272 +++ .../storage/file/BasePackBitmapIndex.java | 2 +- .../jgit/internal/storage/file/BitSet.java | 2 +- .../storage/file/BitmapIndexImpl.java | 4 +- .../jgit/internal/storage/file/GC.java | 5 +- .../storage/file/InflatingBitSet.java | 4 +- .../storage/file/PackBitmapIndex.java | 2 +- .../storage/file/PackBitmapIndexBuilder.java | 2 +- .../storage/file/PackBitmapIndexRemapper.java | 4 +- .../storage/file/PackBitmapIndexV1.java | 2 +- .../storage/file/PackBitmapIndexWriterV1.java | 2 +- .../pack/PackWriterBitmapPreparer.java | 2 +- .../third/googlecode/javaewah/BitCounter.java | 106 ++ .../googlecode/javaewah/BitmapStorage.java | 71 + .../googlecode/javaewah/BufferedIterator.java | 151 ++ .../javaewah/BufferedRunningLengthWord.java | 175 ++ .../javaewah/CloneableIterator.java | 24 + .../javaewah/EWAHCompressedBitmap.java | 1631 +++++++++++++++++ .../googlecode/javaewah/EWAHIterator.java | 98 + .../googlecode/javaewah/FastAggregation.java | 436 +++++ .../googlecode/javaewah/IntIterator.java | 31 + .../googlecode/javaewah/IntIteratorImpl.java | 87 + .../javaewah/IntIteratorOverIteratingRLW.java | 89 + .../IteratingBufferedRunningLengthWord.java | 276 +++ .../googlecode/javaewah/IteratingRLW.java | 49 + .../javaewah/IteratorAggregation.java | 616 +++++++ .../googlecode/javaewah/IteratorUtil.java | 132 ++ .../googlecode/javaewah/LogicalElement.java | 61 + .../javaewah/NonEmptyVirtualStorage.java | 92 + .../javaewah/RunningLengthWord.java | 152 ++ .../javaewah/benchmark/Benchmark.java | 284 +++ .../javaewah/benchmark/Benchmark32.java | 212 +++ .../benchmark/BenchmarkIntersection.java | 130 ++ .../benchmark/BenchmarkIntersection32.java | 130 ++ .../javaewah/benchmark/BenchmarkUnion.java | 164 ++ .../javaewah/benchmark/BenchmarkUnion32.java | 165 ++ .../javaewah/benchmark/BenchmarkXOR.java | 134 ++ .../javaewah/benchmark/BenchmarkXOR32.java | 137 ++ .../benchmark/ClusteredDataGenerator.java | 78 + .../benchmark/UniformDataGenerator.java | 114 ++ .../googlecode/javaewah32/BitCounter32.java | 102 ++ .../javaewah32/BitmapStorage32.java | 60 + .../javaewah32/BufferedIterator32.java | 152 ++ .../BufferedRunningLengthWord32.java | 174 ++ .../javaewah32/EWAHCompressedBitmap32.java | 1608 ++++++++++++++++ .../googlecode/javaewah32/EWAHIterator32.java | 98 + .../javaewah32/FastAggregation32.java | 377 ++++ .../javaewah32/IntIteratorImpl32.java | 90 + .../IntIteratorOverIteratingRLW32.java | 91 + .../IteratingBufferedRunningLengthWord32.java | 274 +++ .../googlecode/javaewah32/IteratingRLW32.java | 42 + .../javaewah32/IteratorAggregation32.java | 601 ++++++ .../googlecode/javaewah32/IteratorUtil32.java | 135 ++ .../javaewah32/NonEmptyVirtualStorage32.java | 87 + .../javaewah32/RunningLengthWord32.java | 152 ++ 56 files changed, 10156 insertions(+), 15 deletions(-) delete mode 100644 fine-jgit/lib/JavaEWAH-0.7.9.jar create mode 100644 fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/dfs/RemoteRepository.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah/BitCounter.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah/BitmapStorage.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah/BufferedIterator.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah/BufferedRunningLengthWord.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah/CloneableIterator.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah/EWAHCompressedBitmap.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah/EWAHIterator.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah/FastAggregation.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah/IntIterator.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah/IntIteratorImpl.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah/IntIteratorOverIteratingRLW.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah/IteratingBufferedRunningLengthWord.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah/IteratingRLW.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah/IteratorAggregation.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah/IteratorUtil.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah/LogicalElement.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah/NonEmptyVirtualStorage.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah/RunningLengthWord.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/Benchmark.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/Benchmark32.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/BenchmarkIntersection.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/BenchmarkIntersection32.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/BenchmarkUnion.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/BenchmarkUnion32.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/BenchmarkXOR.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/BenchmarkXOR32.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/ClusteredDataGenerator.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/UniformDataGenerator.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah32/BitCounter32.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah32/BitmapStorage32.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah32/BufferedIterator32.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah32/BufferedRunningLengthWord32.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah32/EWAHCompressedBitmap32.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah32/EWAHIterator32.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah32/FastAggregation32.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah32/IntIteratorImpl32.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah32/IntIteratorOverIteratingRLW32.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah32/IteratingBufferedRunningLengthWord32.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah32/IteratingRLW32.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah32/IteratorAggregation32.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah32/IteratorUtil32.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah32/NonEmptyVirtualStorage32.java create mode 100644 fine-jgit/src/com/fr/third/googlecode/javaewah32/RunningLengthWord32.java diff --git a/fine-jgit/lib/JavaEWAH-0.7.9.jar b/fine-jgit/lib/JavaEWAH-0.7.9.jar deleted file mode 100644 index ccdb37143d654ca0fd70dbee00721d9baa64cade..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 125146 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μ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 { + @Override + public RemoteRepository build() throws IOException { + throw new UnsupportedOperationException(); + } + }.setRepositoryDescription(repoDesc)); + + objdb = new RemoteRepository.RemoteObjDatabase(this); + refdb = new RemoteRepository.RemoteRefDatabase(); + } + + @Override + public DfsObjDatabase getObjectDatabase() { + return objdb; + } + + @Override + public DfsRefDatabase getRefDatabase() { + return refdb; + } + + private class RemoteObjDatabase extends DfsObjDatabase { + private List packs = new ArrayList(); + + RemoteObjDatabase(DfsRepository repo) { + super(repo, new DfsReaderOptions()); + } + + @Override + protected synchronized List listPacks() { + return packs; + } + + @Override + protected DfsPackDescription newPack(PackSource source) { + int id = packId.incrementAndGet(); + DfsPackDescription desc = new RemoteRepository.MemPack( + "pack-" + id + "-" + source.name(), //$NON-NLS-1$ //$NON-NLS-2$ + getRepository().getDescription()); + return desc.setPackSource(source); + } + + @Override + protected synchronized void commitPackImpl( + Collection desc, + Collection replace) { + List n; + n = new ArrayList(desc.size() + packs.size()); + n.addAll(desc); + n.addAll(packs); + if (replace != null) + n.removeAll(replace); + packs = n; + } + + @Override + protected void rollbackPack(Collection desc) { + // Do nothing. Pack is not recorded until commitPack. + } + + @Override + protected ReadableChannel openFile(DfsPackDescription desc, PackExt ext) + throws FileNotFoundException, IOException { + RemoteRepository.MemPack memPack = (RemoteRepository.MemPack) desc; + byte[] file = memPack.fileMap.get(ext); + if (file == null) + throw new FileNotFoundException(desc.getFileName(ext)); + return new RemoteRepository.ByteArrayReadableChannel(file); + } + + @Override + protected DfsOutputStream writeFile( + DfsPackDescription desc, final PackExt ext) throws IOException { + final RemoteRepository.MemPack memPack = (RemoteRepository.MemPack) desc; + return new RemoteRepository.Out() { + @Override + public void flush() { + memPack.fileMap.put(ext, getData()); + } + }; + } + } + + private static class MemPack extends DfsPackDescription { + private final Map + fileMap = new HashMap(); + + MemPack(String name, DfsRepositoryDescription repoDesc) { + super(repoDesc, name); + } + } + + private abstract static class Out extends DfsOutputStream { + private final ByteArrayOutputStream dst = new ByteArrayOutputStream(); + + private byte[] data; + + @Override + public void write(byte[] buf, int off, int len) { + data = null; + dst.write(buf, off, len); + } + + @Override + public int read(long position, ByteBuffer buf) { + byte[] d = getData(); + int n = Math.min(buf.remaining(), d.length - (int) position); + if (n == 0) + return -1; + buf.put(d, (int) position, n); + return n; + } + + byte[] getData() { + if (data == null) + data = dst.toByteArray(); + return data; + } + + @Override + public abstract void flush(); + + @Override + public void close() { + flush(); + } + + } + + private static class ByteArrayReadableChannel implements ReadableChannel { + private final byte[] data; + + private int position; + + private boolean open = true; + + ByteArrayReadableChannel(byte[] buf) { + data = buf; + } + + public int read(ByteBuffer dst) { + int n = Math.min(dst.remaining(), data.length - position); + if (n == 0) + return -1; + dst.put(data, position, n); + position += n; + return n; + } + + public void close() { + open = false; + } + + public boolean isOpen() { + return open; + } + + public long position() { + return position; + } + + public void position(long newPosition) { + position = (int) newPosition; + } + + public long size() { + return data.length; + } + + public int blockSize() { + return 0; + } + } + + private class RemoteRefDatabase extends DfsRefDatabase { + private final ConcurrentMap refs = new ConcurrentHashMap(); + + RemoteRefDatabase() { + super(RemoteRepository.this); + } + + @Override + protected RefCache scanAllRefs() throws IOException { + RefList.Builder ids = new RefList.Builder(); + RefList.Builder sym = new RefList.Builder(); + for (Ref ref : refs.values()) { + if (ref.isSymbolic()) + sym.add(ref); + ids.add(ref); + } + ids.sort(); + sym.sort(); + return new RefCache(ids.toRefList(), sym.toRefList()); + } + + @Override + protected boolean compareAndPut(Ref oldRef, Ref newRef) + throws IOException { + ObjectId id = newRef.getObjectId(); + if (id != null) { + RevWalk rw = new RevWalk(getRepository()); + try { + // Validate that the target exists in a new RevWalk, as the RevWalk + // from the RefUpdate might be reading back unflushed objects. + rw.parseAny(id); + } finally { + rw.release(); + } + } + String name = newRef.getName(); + if (oldRef == null || oldRef.getStorage() == Ref.Storage.NEW) + return refs.putIfAbsent(name, newRef) == null; + Ref cur = refs.get(name); + if (cur != null && eq(cur, oldRef)) + return refs.replace(name, cur, newRef); + else + return false; + + } + + @Override + protected boolean compareAndRemove(Ref oldRef) throws IOException { + String name = oldRef.getName(); + Ref cur = refs.get(name); + if (cur != null && eq(cur, oldRef)) + return refs.remove(name, cur); + else + return false; + } + + private boolean eq(Ref a, Ref b) { + if (a.getObjectId() == null && b.getObjectId() == null) + return true; + if (a.getObjectId() != null) + return a.getObjectId().equals(b.getObjectId()); + return false; + } + } +} + diff --git a/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/BasePackBitmapIndex.java b/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/BasePackBitmapIndex.java index ef00dc7a1..b5ee537ef 100755 --- a/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/BasePackBitmapIndex.java +++ b/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/BasePackBitmapIndex.java @@ -45,7 +45,7 @@ package com.fr.third.eclipse.jgit.internal.storage.file; import com.fr.third.eclipse.jgit.lib.AnyObjectId; import com.fr.third.eclipse.jgit.lib.ObjectIdOwnerMap; -import com.googlecode.javaewah.EWAHCompressedBitmap; +import com.fr.third.googlecode.javaewah.EWAHCompressedBitmap; /** * Base implementation of the PackBitmapIndex. diff --git a/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/BitSet.java b/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/BitSet.java index f86208d35..26633b626 100755 --- a/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/BitSet.java +++ b/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/BitSet.java @@ -43,7 +43,7 @@ package com.fr.third.eclipse.jgit.internal.storage.file; -import com.googlecode.javaewah.EWAHCompressedBitmap; +import com.fr.third.googlecode.javaewah.EWAHCompressedBitmap; import java.util.Arrays; diff --git a/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/BitmapIndexImpl.java b/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/BitmapIndexImpl.java index ebd829334..856fcbccb 100755 --- a/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/BitmapIndexImpl.java +++ b/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/BitmapIndexImpl.java @@ -51,8 +51,8 @@ import com.fr.third.eclipse.jgit.lib.Constants; import com.fr.third.eclipse.jgit.lib.ObjectId; import com.fr.third.eclipse.jgit.lib.ObjectIdOwnerMap; import com.fr.third.eclipse.jgit.util.BlockList; -import com.googlecode.javaewah.EWAHCompressedBitmap; -import com.googlecode.javaewah.IntIterator; +import com.fr.third.googlecode.javaewah.EWAHCompressedBitmap; +import com.fr.third.googlecode.javaewah.IntIterator; import java.text.MessageFormat; import java.util.Iterator; diff --git a/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/GC.java b/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/GC.java index 2e856451b..fec34f17e 100755 --- a/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/GC.java +++ b/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/GC.java @@ -181,8 +181,9 @@ public class GC { * @param newPacks */ private void deleteOldPacks(Collection oldPacks, - Collection newPacks) { - oldPackLoop: for (PackFile oldPack : oldPacks) { + Collection newPacks) { + oldPackLoop: + for (PackFile oldPack : oldPacks) { String oldName = oldPack.getPackName(); // check whether an old pack file is also among the list of new // pack files. Then we must not delete it. diff --git a/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/InflatingBitSet.java b/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/InflatingBitSet.java index 096be5f33..ac1705f43 100755 --- a/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/InflatingBitSet.java +++ b/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/InflatingBitSet.java @@ -43,8 +43,8 @@ package com.fr.third.eclipse.jgit.internal.storage.file; -import com.googlecode.javaewah.EWAHCompressedBitmap; -import com.googlecode.javaewah.IntIterator; +import com.fr.third.googlecode.javaewah.EWAHCompressedBitmap; +import com.fr.third.googlecode.javaewah.IntIterator; /** * A wrapper around the EWAHCompressedBitmap optimized for the contains diff --git a/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/PackBitmapIndex.java b/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/PackBitmapIndex.java index 48c3c681f..ef5c5d15e 100755 --- a/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/PackBitmapIndex.java +++ b/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/PackBitmapIndex.java @@ -47,7 +47,7 @@ import com.fr.third.eclipse.jgit.errors.CorruptObjectException; import com.fr.third.eclipse.jgit.internal.JGitText; import com.fr.third.eclipse.jgit.lib.AnyObjectId; import com.fr.third.eclipse.jgit.lib.ObjectId; -import com.googlecode.javaewah.EWAHCompressedBitmap; +import com.fr.third.googlecode.javaewah.EWAHCompressedBitmap; import java.io.File; import java.io.FileInputStream; diff --git a/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/PackBitmapIndexBuilder.java b/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/PackBitmapIndexBuilder.java index fec58ad7a..aa6c9f3c3 100755 --- a/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/PackBitmapIndexBuilder.java +++ b/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/PackBitmapIndexBuilder.java @@ -50,7 +50,7 @@ import com.fr.third.eclipse.jgit.lib.Constants; import com.fr.third.eclipse.jgit.lib.ObjectId; import com.fr.third.eclipse.jgit.lib.ObjectIdOwnerMap; import com.fr.third.eclipse.jgit.util.BlockList; -import com.googlecode.javaewah.EWAHCompressedBitmap; +import com.fr.third.googlecode.javaewah.EWAHCompressedBitmap; import com.fr.third.eclipse.jgit.internal.storage.file.BitmapIndexImpl.CompressedBitmap; import com.fr.third.eclipse.jgit.lib.BitmapIndex.Bitmap; import com.fr.third.eclipse.jgit.lib.BitmapIndex.BitmapBuilder; diff --git a/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java b/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java index c896a5db5..20d5c79f6 100755 --- a/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java +++ b/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java @@ -47,8 +47,8 @@ import com.fr.third.eclipse.jgit.lib.AnyObjectId; import com.fr.third.eclipse.jgit.lib.BitmapIndex; import com.fr.third.eclipse.jgit.lib.ObjectId; import com.fr.third.eclipse.jgit.lib.ObjectIdOwnerMap; -import com.googlecode.javaewah.EWAHCompressedBitmap; -import com.googlecode.javaewah.IntIterator; +import com.fr.third.googlecode.javaewah.EWAHCompressedBitmap; +import com.fr.third.googlecode.javaewah.IntIterator; import com.fr.third.eclipse.jgit.internal.storage.file.BasePackBitmapIndex.StoredBitmap; import java.util.Collections; diff --git a/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/PackBitmapIndexV1.java b/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/PackBitmapIndexV1.java index f103996d7..b099c004c 100755 --- a/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/PackBitmapIndexV1.java +++ b/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/PackBitmapIndexV1.java @@ -50,7 +50,7 @@ import com.fr.third.eclipse.jgit.lib.ObjectId; import com.fr.third.eclipse.jgit.lib.ObjectIdOwnerMap; import com.fr.third.eclipse.jgit.util.IO; import com.fr.third.eclipse.jgit.util.NB; -import com.googlecode.javaewah.EWAHCompressedBitmap; +import com.fr.third.googlecode.javaewah.EWAHCompressedBitmap; import java.io.DataInput; import java.io.IOException; diff --git a/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/PackBitmapIndexWriterV1.java b/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/PackBitmapIndexWriterV1.java index 70c5d0457..13552f8f1 100755 --- a/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/PackBitmapIndexWriterV1.java +++ b/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/file/PackBitmapIndexWriterV1.java @@ -46,7 +46,7 @@ package com.fr.third.eclipse.jgit.internal.storage.file; import com.fr.third.eclipse.jgit.internal.JGitText; import com.fr.third.eclipse.jgit.lib.Constants; import com.fr.third.eclipse.jgit.util.io.SafeBufferedOutputStream; -import com.googlecode.javaewah.EWAHCompressedBitmap; +import com.fr.third.googlecode.javaewah.EWAHCompressedBitmap; import com.fr.third.eclipse.jgit.internal.storage.file.PackBitmapIndexBuilder.StoredEntry; import java.io.BufferedOutputStream; diff --git a/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java b/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java index 6f3d1fcd4..534d9c890 100755 --- a/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java +++ b/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java @@ -60,7 +60,7 @@ import com.fr.third.eclipse.jgit.revwalk.RevCommit; import com.fr.third.eclipse.jgit.revwalk.RevObject; import com.fr.third.eclipse.jgit.revwalk.RevWalk; import com.fr.third.eclipse.jgit.util.BlockList; -import com.googlecode.javaewah.EWAHCompressedBitmap; +import com.fr.third.googlecode.javaewah.EWAHCompressedBitmap; import com.fr.third.eclipse.jgit.lib.BitmapIndex.BitmapBuilder; import java.io.IOException; diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah/BitCounter.java b/fine-jgit/src/com/fr/third/googlecode/javaewah/BitCounter.java new file mode 100644 index 000000000..85df35fe5 --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah/BitCounter.java @@ -0,0 +1,106 @@ +package com.fr.third.googlecode.javaewah; + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ +/** + * BitCounter is a fake bitset data structure. Instead of storing the actual + * data, it only records the number of set bits. + * + * @since 0.4.0 + * @author David McIntosh + */ + +public final class BitCounter implements BitmapStorage { + + /** + * Virtually add words directly to the bitmap + * + * @param newdata + * the word + */ + @Override + public void add(final long newdata) { + this.oneBits += Long.bitCount(newdata); + return; + } + + /** + * virtually add several literal words. + * + * @param data + * the literal words + * @param start + * the starting point in the array + * @param number + * the number of literal words to add + */ + @Override + public void addStreamOfLiteralWords(long[] data, int start, int number) { + for (int i = start; i < start + number; i++) { + add(data[i]); + } + return; + } + + /** + * virtually add many zeroes or ones. + * + * @param v + * zeros or ones + * @param number + * how many to words add + */ + @Override +public void addStreamOfEmptyWords(boolean v, long number) { + if (v) { + this.oneBits += number * EWAHCompressedBitmap.wordinbits; + } + return; + } + + /** + * virtually add several negated literal words. + * + * @param data + * the literal words + * @param start + * the starting point in the array + * @param number + * the number of literal words to add + */ + // @Override : causes problems with Java 1.5 + @Override +public void addStreamOfNegatedLiteralWords(long[] data, int start, int number) { + for (int i = start; i < start + number; i++) { + add(~data[i]); + } + return; + } + + /** + * As you act on this class, it records the number of set (true) bits. + * + * @return number of set bits + */ + public int getCount() { + return this.oneBits; + } + + /** + * should directly set the sizeinbits field, but is effectively ignored in + * this class. + * + * @param bits + * number of bits + */ + // @Override : causes problems with Java 1.5 + @Override +public void setSizeInBits(int bits) { + // no action + } + + private int oneBits; + +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah/BitmapStorage.java b/fine-jgit/src/com/fr/third/googlecode/javaewah/BitmapStorage.java new file mode 100644 index 000000000..929b2b8b3 --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah/BitmapStorage.java @@ -0,0 +1,71 @@ +package com.fr.third.googlecode.javaewah; + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ + +/** + * Low level bitset writing methods. + * + * @since 0.4.0 + * @author David McIntosh + */ +public interface BitmapStorage { + + /** + * Adding words directly to the bitmap (for expert use). + * + * This is normally how you add data to the array. So you add bits in streams + * of 8*8 bits. + * + * @param newdata + * the word + */ + public void add(final long newdata); + + /** + * if you have several literal words to copy over, this might be faster. + * + * @param data + * the literal words + * @param start + * the starting point in the array + * @param number + * the number of literal words to add + */ + public void addStreamOfLiteralWords(final long[] data, final int start, + final int number); + + /** + * For experts: You want to add many zeroes or ones? This is the method you + * use. + * + * @param v + * zeros or ones + * @param number + * how many to words add + */ + public void addStreamOfEmptyWords(final boolean v, final long number); + + /** + * Like "addStreamOfLiteralWords" but negates the words being added. + * + * @param data + * the literal words + * @param start + * the starting point in the array + * @param number + * the number of literal words to add + */ + public void addStreamOfNegatedLiteralWords(long[] data, final int start, + final int number); + + /** + * directly set the sizeinbits field + * + * @param bits + * number of bits + */ + public void setSizeInBits(final int bits); +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah/BufferedIterator.java b/fine-jgit/src/com/fr/third/googlecode/javaewah/BufferedIterator.java new file mode 100644 index 000000000..bf0e0c94a --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah/BufferedIterator.java @@ -0,0 +1,151 @@ +package com.fr.third.googlecode.javaewah; + + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ +/** + * This class can be used to iterate over blocks of bitmap data. + * + * @author Daniel Lemire + * + */ +public class BufferedIterator implements IteratingRLW { + /** + * Instantiates a new iterating buffered running length word. + * + * @param iterator iterator + */ + public BufferedIterator(final CloneableIterator iterator) { + this.masteriterator = iterator; + if(this.masteriterator.hasNext()) { + this.iterator = this.masteriterator.next(); + this.brlw = new BufferedRunningLengthWord(this.iterator.next()); + this.literalWordStartPosition = this.iterator.literalWords() + this.brlw.literalwordoffset; + this.buffer = this.iterator.buffer(); + } + } + + + /** + * Discard first words, iterating to the next running length word if needed. + * + * @param x the number of words to be discarded + */ + @Override + public void discardFirstWords(long x) { + while (x > 0) { + if (this.brlw.RunningLength > x) { + this.brlw.RunningLength -= x; + return; + } + x -= this.brlw.RunningLength; + this.brlw.RunningLength = 0; + long toDiscard = x > this.brlw.NumberOfLiteralWords ? this.brlw.NumberOfLiteralWords : x; + + this.literalWordStartPosition += toDiscard; + this.brlw.NumberOfLiteralWords -= toDiscard; + x -= toDiscard; + if ((x > 0) || (this.brlw.size() == 0)) { + if (!this.next()) { + break; + } + } + } + } + /** + * Move to the next RunningLengthWord + * @return whether the move was possible + */ + @Override + public boolean next() { + if (!this.iterator.hasNext()) { + if(!reload()) { + this.brlw.NumberOfLiteralWords = 0; + this.brlw.RunningLength = 0; + return false; + } + } + this.brlw.reset(this.iterator.next()); + this.literalWordStartPosition = this.iterator.literalWords(); // + this.brlw.literalwordoffset ==0 + return true; + } + private boolean reload() { + if(!this.masteriterator.hasNext()) { + return false; + } + this.iterator = this.masteriterator.next(); + this.buffer = this.iterator.buffer(); + return true; + } + + + /** + * Get the nth literal word for the current running length word + * @param index zero based index + * @return the literal word + */ + @Override + public long getLiteralWordAt(int index) { + return this.buffer[this.literalWordStartPosition + index]; + } + + /** + * Gets the number of literal words for the current running length word. + * + * @return the number of literal words + */ + @Override + public int getNumberOfLiteralWords() { + return this.brlw.NumberOfLiteralWords; + } + + /** + * Gets the running bit. + * + * @return the running bit + */ + @Override + public boolean getRunningBit() { + return this.brlw.RunningBit; + } + + /** + * Gets the running length. + * + * @return the running length + */ + @Override + public long getRunningLength() { + return this.brlw.RunningLength; + } + + /** + * Size in uncompressed words of the current running length word. + * + * @return the size + */ + @Override + public long size() { + return this.brlw.size(); + } + + + @Override + public BufferedIterator clone() throws CloneNotSupportedException { + BufferedIterator answer = (BufferedIterator) super.clone(); + answer.brlw = this.brlw.clone(); + answer.buffer = this.buffer; + answer.iterator = this.iterator.clone(); + answer.literalWordStartPosition = this.literalWordStartPosition; + answer.masteriterator = this.masteriterator.clone(); + return answer; + } + + private BufferedRunningLengthWord brlw; + private long[] buffer; + private int literalWordStartPosition; + private EWAHIterator iterator; + private CloneableIterator masteriterator; + } \ No newline at end of file diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah/BufferedRunningLengthWord.java b/fine-jgit/src/com/fr/third/googlecode/javaewah/BufferedRunningLengthWord.java new file mode 100644 index 000000000..82afd2014 --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah/BufferedRunningLengthWord.java @@ -0,0 +1,175 @@ +package com.fr.third.googlecode.javaewah; + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ + + + +/** + * Mostly for internal use. Similar to RunningLengthWord, but can + * be modified without access to the array, and has faster access. + * + * @author Daniel Lemire + * @since 0.1.0 + * + */ +public final class BufferedRunningLengthWord implements Cloneable { + + /** + * Instantiates a new buffered running length word. + * + * @param a the word + */ + public BufferedRunningLengthWord(final long a) { + this.NumberOfLiteralWords = (int) (a >>> (1 + RunningLengthWord.runninglengthbits)); + this.RunningBit = (a & 1) != 0; + this.RunningLength = (int) ((a >>> 1) & RunningLengthWord.largestrunninglengthcount); + } + + /** + * Instantiates a new buffered running length word. + * + * @param rlw the rlw + */ + public BufferedRunningLengthWord(final RunningLengthWord rlw) { + this(rlw.parent.buffer[rlw.position]); + } + + /** + * Discard first words. + * + * @param x the x + */ + public void discardFirstWords(long x) { + if (this.RunningLength >= x) { + this.RunningLength -= x; + return; + } + x -= this.RunningLength; + this.RunningLength = 0; + this.literalwordoffset += x; + this.NumberOfLiteralWords -= x; + } + + /** + * Gets the number of literal words. + * + * @return the number of literal words + */ + public int getNumberOfLiteralWords() { + return this.NumberOfLiteralWords; + } + + /** + * Gets the running bit. + * + * @return the running bit + */ + public boolean getRunningBit() { + return this.RunningBit; + } + + /** + * Gets the running length. + * + * @return the running length + */ + public long getRunningLength() { + return this.RunningLength; + } + + /** + * Reset the values using the provided word. + * + * @param a the word + */ + public void reset(final long a) { + this.NumberOfLiteralWords = (int) (a >>> (1 + RunningLengthWord.runninglengthbits)); + this.RunningBit = (a & 1) != 0; + this.RunningLength = (int) ((a >>> 1) & RunningLengthWord.largestrunninglengthcount); + this.literalwordoffset = 0; + } + + /** + * Reset the values of this running length word so that it has the same values + * as the other running length word. + * + * @param rlw the other running length word + */ + public void reset(final RunningLengthWord rlw) { + reset(rlw.parent.buffer[rlw.position]); + } + + /** + * Sets the number of literal words. + * + * @param number the new number of literal words + */ + public void setNumberOfLiteralWords(final int number) { + this.NumberOfLiteralWords = number; + } + + /** + * Sets the running bit. + * + * @param b the new running bit + */ + public void setRunningBit(final boolean b) { + this.RunningBit = b; + } + + /** + * Sets the running length. + * + * @param number the new running length + */ + public void setRunningLength(final long number) { + this.RunningLength = number; + } + + /** + * Size in uncompressed words. + * + * @return the long + */ + public long size() { + return this.RunningLength + this.NumberOfLiteralWords; + } + + /* + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "running bit = " + getRunningBit() + " running length = " + + getRunningLength() + " number of lit. words " + + getNumberOfLiteralWords(); + } + + @Override +public BufferedRunningLengthWord clone() throws CloneNotSupportedException { + BufferedRunningLengthWord answer = (BufferedRunningLengthWord) super.clone(); + answer.literalwordoffset = this.literalwordoffset; + answer.NumberOfLiteralWords = this.NumberOfLiteralWords; + answer.RunningBit = this.RunningBit; + answer.RunningLength = this.RunningLength; + return answer; + } + + + /** how many literal words have we read so far? */ + public int literalwordoffset = 0; + + /** The Number of literal words. */ + public int NumberOfLiteralWords; + + /** The Running bit. */ + public boolean RunningBit; + + /** The Running length. */ + public long RunningLength; + + +} \ No newline at end of file diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah/CloneableIterator.java b/fine-jgit/src/com/fr/third/googlecode/javaewah/CloneableIterator.java new file mode 100644 index 000000000..a17eded16 --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah/CloneableIterator.java @@ -0,0 +1,24 @@ +package com.fr.third.googlecode.javaewah; + +/** + * Like a standard Java iterator, except that you can clone it. + * + * @param the data type of the iterator + */ +public interface CloneableIterator extends Cloneable { + + /** + * @return whether there is more + */ + public boolean hasNext(); + /** + * @return the next element + */ + public E next(); + /** + * @return a copy + * @throws CloneNotSupportedException this should never happen in practice + */ + public CloneableIterator clone() throws CloneNotSupportedException; + +} \ No newline at end of file diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah/EWAHCompressedBitmap.java b/fine-jgit/src/com/fr/third/googlecode/javaewah/EWAHCompressedBitmap.java new file mode 100644 index 000000000..bc9397da9 --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah/EWAHCompressedBitmap.java @@ -0,0 +1,1631 @@ +package com.fr.third.googlecode.javaewah; + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ + +import java.util.*; +import java.io.*; + + + +/** + *

+ * This implements the patent-free(1) EWAH scheme. Roughly speaking, it is a + * 64-bit variant of the BBC compression scheme used by Oracle for its bitmap + * indexes. + *

+ * + *

+ * The objective of this compression type is to provide some compression, while + * reducing as much as possible the CPU cycle usage. + *

+ * + * + *

+ * This implementation being 64-bit, it assumes a 64-bit CPU together with a + * 64-bit Java Virtual Machine. This same code on a 32-bit machine may not be as + * fast. + *

+ * + *

+ * There is also a 32-bit version of this code in the class + * javaewah32.EWAHCompressedBitmap32 + *

+ * + * @see com.googlecode.javaewah32.EWAHCompressedBitmap32 + * + *

+ * For more details, see the following paper: + *

+ * + *
    + *
  • Daniel Lemire, Owen Kaser, Kamel Aouiche, Sorting improves + * word-aligned bitmap indexes. Data & Knowledge Engineering 69 (1), pages + * 3-28, 2010. http://arxiv.org/abs/0901.3751
  • + *
+ * + *

+ * A 32-bit version of the compressed format was described by Wu et al. and + * named WBC: + *

+ * + *
    + *
  • K. Wu, E. J. Otoo, A. Shoshani, H. Nordberg, Notes on design and + * implementation of compressed bit vectors, Tech. Rep. LBNL/PUB-3161, + * Lawrence Berkeley National Laboratory, available from http://crd.lbl. + * gov/~kewu/ps/PUB-3161.html (2001).
  • + *
+ * + *

+ * Probably, the best prior art is the Oracle bitmap compression scheme + * (BBC): + *

+ *
    + *
  • G. Antoshenkov, Byte-Aligned Bitmap Compression, DCC'95, 1995.
  • + *
+ * + *

+ * 1- The authors do not know of any patent infringed by the following + * implementation. However, similar schemes, like WAH are covered by + * patents. + *

+ * + * @since 0.1.0 + */ +public final class EWAHCompressedBitmap implements Cloneable, Externalizable, + Iterable, BitmapStorage, LogicalElement { + + /** + * Creates an empty bitmap (no bit set to true). + */ + public EWAHCompressedBitmap() { + this.buffer = new long[defaultbuffersize]; + this.rlw = new RunningLengthWord(this, 0); + } + + /** + * Sets explicitly the buffer size (in 64-bit words). The initial memory usage + * will be "buffersize * 64". For large poorly compressible bitmaps, using + * large values may improve performance. + * + * @param buffersize + * number of 64-bit words reserved when the object is created) + */ + public EWAHCompressedBitmap(final int buffersize) { + this.buffer = new long[buffersize]; + this.rlw = new RunningLengthWord(this, 0); + } + + /** + * Adding words directly to the bitmap (for expert use). + * + * This is normally how you add data to the array. So you add bits in streams + * of 8*8 bits. + * + * Example: if you add 321, you are have added (in binary notation) + * 0b101000001, so you have effectively called set(0), set(6), set(8) + * in sequence. + * + * @param newdata + * the word + */ + @Override +public void add(final long newdata) { + add(newdata, wordinbits); + } + + /** + * Adding words directly to the bitmap (for expert use). + * + * @param newdata + * the word + * @param bitsthatmatter + * the number of significant bits (by default it should be 64) + */ + public void add(final long newdata, final int bitsthatmatter) { + this.sizeinbits += bitsthatmatter; + if (newdata == 0) { + addEmptyWord(false); + } else if (newdata == ~0l) { + addEmptyWord(true); + } else { + addLiteralWord(newdata); + } + } + + /** + * For internal use. + * + * @param v + * the boolean value + */ + private void addEmptyWord(final boolean v) { + final boolean noliteralword = (this.rlw.getNumberOfLiteralWords() == 0); + final long runlen = this.rlw.getRunningLength(); + if ((noliteralword) && (runlen == 0)) { + this.rlw.setRunningBit(v); + } + if ((noliteralword) && (this.rlw.getRunningBit() == v) + && (runlen < RunningLengthWord.largestrunninglengthcount)) { + this.rlw.setRunningLength(runlen + 1); + return; + } + push_back(0); + this.rlw.position = this.actualsizeinwords - 1; + this.rlw.setRunningBit(v); + this.rlw.setRunningLength(1); + return; + } + + /** + * For internal use. + * + * @param newdata + * the literal word + */ + private void addLiteralWord(final long newdata) { + final int numbersofar = this.rlw.getNumberOfLiteralWords(); + if (numbersofar >= RunningLengthWord.largestliteralcount) { + push_back(0); + this.rlw.position = this.actualsizeinwords - 1; + this.rlw.setNumberOfLiteralWords(1); + push_back(newdata); + } + this.rlw.setNumberOfLiteralWords(numbersofar + 1); + push_back(newdata); + } + + /** + * if you have several literal words to copy over, this might be faster. + * + * + * @param data + * the literal words + * @param start + * the starting point in the array + * @param number + * the number of literal words to add + */ + @Override +public void addStreamOfLiteralWords(final long[] data, final int start, + final int number) { + int leftovernumber = number; + while(leftovernumber > 0) { + final int NumberOfLiteralWords = this.rlw.getNumberOfLiteralWords(); + final int whatwecanadd = leftovernumber < RunningLengthWord.largestliteralcount + - NumberOfLiteralWords ? leftovernumber : RunningLengthWord.largestliteralcount + - NumberOfLiteralWords; + this.rlw.setNumberOfLiteralWords(NumberOfLiteralWords + whatwecanadd); + leftovernumber -= whatwecanadd; + push_back(data, start, whatwecanadd); + this.sizeinbits += whatwecanadd * wordinbits; + if (leftovernumber > 0) { + push_back(0); + this.rlw.position = this.actualsizeinwords - 1; + } + } + } + + /** + * For experts: You want to add many zeroes or ones? This is the method you + * use. + * + * @param v + * the boolean value + * @param number + * the number + */ + @Override +public void addStreamOfEmptyWords(final boolean v, long number) { + if (number == 0) + return; + this.sizeinbits += number * wordinbits; + if ((this.rlw.getRunningBit() != v) && (this.rlw.size() == 0)) { + this.rlw.setRunningBit(v); + } else if ((this.rlw.getNumberOfLiteralWords() != 0) + || (this.rlw.getRunningBit() != v)) { + push_back(0); + this.rlw.position = this.actualsizeinwords - 1; + if (v) + this.rlw.setRunningBit(v); + } + final long runlen = this.rlw.getRunningLength(); + final long whatwecanadd = number < RunningLengthWord.largestrunninglengthcount + - runlen ? number : RunningLengthWord.largestrunninglengthcount - runlen; + this.rlw.setRunningLength(runlen + whatwecanadd); + number -= whatwecanadd; + while (number >= RunningLengthWord.largestrunninglengthcount) { + push_back(0); + this.rlw.position = this.actualsizeinwords - 1; + if (v) + this.rlw.setRunningBit(v); + this.rlw.setRunningLength(RunningLengthWord.largestrunninglengthcount); + number -= RunningLengthWord.largestrunninglengthcount; + } + if (number > 0) { + push_back(0); + this.rlw.position = this.actualsizeinwords - 1; + if (v) + this.rlw.setRunningBit(v); + this.rlw.setRunningLength(number); + } + } + + /** + * Same as addStreamOfLiteralWords, but the words are negated. + * + * @param data + * the literal words + * @param start + * the starting point in the array + * @param number + * the number of literal words to add + */ + @Override + public void addStreamOfNegatedLiteralWords(final long[] data, + final int start, final int number) { + int leftovernumber = number; + while (leftovernumber > 0) { + final int NumberOfLiteralWords = this.rlw.getNumberOfLiteralWords(); + final int whatwecanadd = leftovernumber < RunningLengthWord.largestliteralcount + - NumberOfLiteralWords ? leftovernumber + : RunningLengthWord.largestliteralcount + - NumberOfLiteralWords; + this.rlw.setNumberOfLiteralWords(NumberOfLiteralWords + + whatwecanadd); + leftovernumber -= whatwecanadd; + negative_push_back(data, start, whatwecanadd); + this.sizeinbits += whatwecanadd * wordinbits; + if (leftovernumber > 0) { + push_back(0); + this.rlw.position = this.actualsizeinwords - 1; + } + } + } + + /** + * Returns a new compressed bitmap containing the bitwise AND values of the + * current bitmap with some other bitmap. + * + * The running time is proportional to the sum of the compressed sizes (as + * reported by sizeInBytes()). + * + * If you are not planning on adding to the resulting bitmap, you may call the trim() + * method to reduce memory usage. + * + * @since 0.4.3 + * @param a + * the other bitmap + * @return the EWAH compressed bitmap + */ + @Override +public EWAHCompressedBitmap and(final EWAHCompressedBitmap a) { + final EWAHCompressedBitmap container = new EWAHCompressedBitmap(); + container + .reserve(this.actualsizeinwords > a.actualsizeinwords ? this.actualsizeinwords + : a.actualsizeinwords); + andToContainer(a, container); + return container; + } + /** + * Computes new compressed bitmap containing the bitwise AND values of the + * current bitmap with some other bitmap. + * + * The running time is proportional to the sum of the compressed sizes (as + * reported by sizeInBytes()). + * + * @since 0.4.0 + * @param a + * the other bitmap + * @param container + * where we store the result + */ + public void andToContainer(final EWAHCompressedBitmap a, final BitmapStorage container) { + final EWAHIterator i = a.getEWAHIterator(); + final EWAHIterator j = getEWAHIterator(); + final IteratingBufferedRunningLengthWord rlwi = new IteratingBufferedRunningLengthWord(i); + final IteratingBufferedRunningLengthWord rlwj = new IteratingBufferedRunningLengthWord(j); + while ((rlwi.size()>0) && (rlwj.size()>0)) { + while ((rlwi.getRunningLength() > 0) || (rlwj.getRunningLength() > 0)) { + final boolean i_is_prey = rlwi.getRunningLength() < rlwj + .getRunningLength(); + final IteratingBufferedRunningLengthWord prey = i_is_prey ? rlwi : rlwj; + final IteratingBufferedRunningLengthWord predator = i_is_prey ? rlwj + : rlwi; + if (predator.getRunningBit() == false) { + container.addStreamOfEmptyWords(false, predator.getRunningLength()); + prey.discardFirstWords(predator.getRunningLength()); + predator.discardFirstWords(predator.getRunningLength()); + } else { + final long index = prey.discharge(container, predator.getRunningLength()); + container.addStreamOfEmptyWords(false, predator.getRunningLength() + - index); + predator.discardFirstWords(predator.getRunningLength()); + } + } + final int nbre_literal = Math.min(rlwi.getNumberOfLiteralWords(), + rlwj.getNumberOfLiteralWords()); + if (nbre_literal > 0) { + for (int k = 0; k < nbre_literal; ++k) + container.add(rlwi.getLiteralWordAt(k) & rlwj.getLiteralWordAt(k)); + rlwi.discardFirstWords(nbre_literal); + rlwj.discardFirstWords(nbre_literal); + } + } + if(adjustContainerSizeWhenAggregating) { + final boolean i_remains = rlwi.size()>0; + final IteratingBufferedRunningLengthWord remaining = i_remains ? rlwi : rlwj; + remaining.dischargeAsEmpty(container); + container.setSizeInBits(Math.max(sizeInBits(), a.sizeInBits())); + } + } + + + /** + * Returns the cardinality of the result of a bitwise AND of the values of the + * current bitmap with some other bitmap. Avoids needing to allocate an + * intermediate bitmap to hold the result of the OR. + * + * @since 0.4.0 + * @param a + * the other bitmap + * @return the cardinality + */ + public int andCardinality(final EWAHCompressedBitmap a) { + final BitCounter counter = new BitCounter(); + andToContainer(a, counter); + return counter.getCount(); + } + + /** + * Returns a new compressed bitmap containing the bitwise AND NOT values of + * the current bitmap with some other bitmap. + * + * The running time is proportional to the sum of the compressed sizes (as + * reported by sizeInBytes()). + * + * If you are not planning on adding to the resulting bitmap, you may call the trim() + * method to reduce memory usage. + * + * @param a + * the other bitmap + * @return the EWAH compressed bitmap + */ + @Override +public EWAHCompressedBitmap andNot(final EWAHCompressedBitmap a) { + final EWAHCompressedBitmap container = new EWAHCompressedBitmap(); + container + .reserve(this.actualsizeinwords > a.actualsizeinwords ? this.actualsizeinwords + : a.actualsizeinwords); + andNotToContainer(a, container); + return container; + } + + /** + * Returns a new compressed bitmap containing the bitwise AND NOT values of + * the current bitmap with some other bitmap. This method is expected to + * be faster than doing A.and(B.clone().not()). + * + * The running time is proportional to the sum of the compressed sizes (as + * reported by sizeInBytes()). + * + * @since 0.4.0 + * @param a the other bitmap + * @param container where to store the result + */ + public void andNotToContainer(final EWAHCompressedBitmap a, + final BitmapStorage container) { + final EWAHIterator i = getEWAHIterator(); + final EWAHIterator j = a.getEWAHIterator(); + final IteratingBufferedRunningLengthWord rlwi = new IteratingBufferedRunningLengthWord(i); + final IteratingBufferedRunningLengthWord rlwj = new IteratingBufferedRunningLengthWord(j); + while ((rlwi.size()>0) && (rlwj.size()>0)) { + while ((rlwi.getRunningLength() > 0) || (rlwj.getRunningLength() > 0)) { + final boolean i_is_prey = rlwi.getRunningLength() < rlwj + .getRunningLength(); + final IteratingBufferedRunningLengthWord prey = i_is_prey ? rlwi : rlwj; + final IteratingBufferedRunningLengthWord predator = i_is_prey ? rlwj + : rlwi; + if ( ((predator.getRunningBit() == true) && (i_is_prey)) + || ((predator.getRunningBit() == false) && (!i_is_prey))){ + container.addStreamOfEmptyWords(false, predator.getRunningLength()); + prey.discardFirstWords(predator.getRunningLength()); + predator.discardFirstWords(predator.getRunningLength()); + } else if (i_is_prey) { + long index = prey.discharge(container, predator.getRunningLength()); + container.addStreamOfEmptyWords(false, predator.getRunningLength() + - index); + predator.discardFirstWords(predator.getRunningLength()); + } else { + long index = prey.dischargeNegated(container, predator.getRunningLength()); + container.addStreamOfEmptyWords(true, predator.getRunningLength() + - index); + predator.discardFirstWords(predator.getRunningLength()); + } + } + final int nbre_literal = Math.min(rlwi.getNumberOfLiteralWords(), + rlwj.getNumberOfLiteralWords()); + if (nbre_literal > 0) { + for (int k = 0; k < nbre_literal; ++k) + container.add(rlwi.getLiteralWordAt(k) & (~rlwj.getLiteralWordAt(k))); + rlwi.discardFirstWords(nbre_literal); + rlwj.discardFirstWords(nbre_literal); + } + } + final boolean i_remains = rlwi.size()>0; + final IteratingBufferedRunningLengthWord remaining = i_remains ? rlwi : rlwj; + if(i_remains) + remaining.discharge(container); + else if(adjustContainerSizeWhenAggregating) + remaining.dischargeAsEmpty(container); + if(adjustContainerSizeWhenAggregating) + container.setSizeInBits(Math.max(sizeInBits(), a.sizeInBits())); + } + + /** + * Returns the cardinality of the result of a bitwise AND NOT of the values of + * the current bitmap with some other bitmap. Avoids needing to allocate an + * intermediate bitmap to hold the result of the OR. + * + * @since 0.4.0 + * @param a + * the other bitmap + * @return the cardinality + */ + public int andNotCardinality(final EWAHCompressedBitmap a) { + final BitCounter counter = new BitCounter(); + andNotToContainer(a, counter); + return counter.getCount(); + } + + /** + * reports the number of bits set to true. Running time is proportional to + * compressed size (as reported by sizeInBytes). + * + * @return the number of bits set to true + */ + public int cardinality() { + int counter = 0; + final EWAHIterator i = new EWAHIterator(this, this.actualsizeinwords); + while (i.hasNext()) { + RunningLengthWord localrlw = i.next(); + if (localrlw.getRunningBit()) { + counter += wordinbits * localrlw.getRunningLength(); + } + for (int j = 0; j < localrlw.getNumberOfLiteralWords(); ++j) { + counter += Long.bitCount(i.buffer()[i.literalWords() + j]); + } + } + return counter; + } + + /** + * Clear any set bits and set size in bits back to 0 + */ + public void clear() { + this.sizeinbits = 0; + this.actualsizeinwords = 1; + this.rlw.position = 0; + // buffer is not fully cleared but any new set operations should overwrite + // stale data + this.buffer[0] = 0; + } + + /* + * @see java.lang.Object#clone() + */ + @Override + public EWAHCompressedBitmap clone() throws CloneNotSupportedException { + final EWAHCompressedBitmap clone = (EWAHCompressedBitmap) super.clone(); + clone.buffer = this.buffer.clone(); + clone.rlw = new RunningLengthWord(clone, this.rlw.position); + clone.actualsizeinwords = this.actualsizeinwords; + clone.sizeinbits = this.sizeinbits; + return clone; + } + + /** + * Deserialize. + * + * @param in + * the DataInput stream + * @throws IOException + * Signals that an I/O exception has occurred. + */ + public void deserialize(DataInput in) throws IOException { + this.sizeinbits = in.readInt(); + this.actualsizeinwords = in.readInt(); + if (this.buffer.length < this.actualsizeinwords) { + this.buffer = new long[this.actualsizeinwords]; + } + for (int k = 0; k < this.actualsizeinwords; ++k) + this.buffer[k] = in.readLong(); + this.rlw = new RunningLengthWord(this, in.readInt()); + } + + /** + * Check to see whether the two compressed bitmaps contain the same set bits. + * + * @see Object#equals(Object) + */ + @Override + public boolean equals(Object o) { + if (o instanceof EWAHCompressedBitmap) { + try { + this.xorToContainer((EWAHCompressedBitmap) o, new NonEmptyVirtualStorage()); + return true; + } catch (NonEmptyVirtualStorage.NonEmptyException e) { + return false; + } + } + return false; + } + + /** + * For experts: You want to add many zeroes or ones faster? + * + * This method does not update sizeinbits. + * + * @param v + * the boolean value + * @param number + * the number (must be greater than 0) + */ + private void fastaddStreamOfEmptyWords(final boolean v, long number) { + if ((this.rlw.getRunningBit() != v) && (this.rlw.size() == 0)) { + this.rlw.setRunningBit(v); + } else if ((this.rlw.getNumberOfLiteralWords() != 0) + || (this.rlw.getRunningBit() != v)) { + push_back(0); + this.rlw.position = this.actualsizeinwords - 1; + if (v) + this.rlw.setRunningBit(v); + } + + final long runlen = this.rlw.getRunningLength(); + final long whatwecanadd = number < RunningLengthWord.largestrunninglengthcount + - runlen ? number : RunningLengthWord.largestrunninglengthcount - runlen; + this.rlw.setRunningLength(runlen + whatwecanadd); + number -= whatwecanadd; + + while (number >= RunningLengthWord.largestrunninglengthcount) { + push_back(0); + this.rlw.position = this.actualsizeinwords - 1; + if (v) + this.rlw.setRunningBit(v); + this.rlw.setRunningLength(RunningLengthWord.largestrunninglengthcount); + number -= RunningLengthWord.largestrunninglengthcount; + } + if (number > 0) { + push_back(0); + this.rlw.position = this.actualsizeinwords - 1; + if (v) + this.rlw.setRunningBit(v); + this.rlw.setRunningLength(number); + } + } + + /** + * Gets an EWAHIterator over the data. This is a customized iterator which + * iterates over run length word. For experts only. + * + * @return the EWAHIterator + */ + public EWAHIterator getEWAHIterator() { + return new EWAHIterator(this, this.actualsizeinwords); + } + + /** + * @return the IteratingRLW iterator corresponding to this bitmap + */ + public IteratingRLW getIteratingRLW() { + return new IteratingBufferedRunningLengthWord(this); + } + /** + * get the locations of the true values as one vector. (may use more memory + * than iterator()) + * + * @return the positions + */ + public List getPositions() { + final ArrayList v = new ArrayList(); + final EWAHIterator i = new EWAHIterator(this, this.actualsizeinwords); + int pos = 0; + while (i.hasNext()) { + RunningLengthWord localrlw = i.next(); + if (localrlw.getRunningBit()) { + for (int j = 0; j < localrlw.getRunningLength(); ++j) { + for (int c = 0; c < wordinbits; ++c) + v.add(new Integer(pos++)); + } + } else { + pos += wordinbits * localrlw.getRunningLength(); + } + for (int j = 0; j < localrlw.getNumberOfLiteralWords(); ++j) { + long data = i.buffer()[i.literalWords() + j]; + while (data != 0) { + final int ntz = Long.numberOfTrailingZeros(data); + data ^= (1l << ntz); + v.add(new Integer(ntz + pos)); + } + pos += wordinbits; + } + } + while ((v.size() > 0) + && (v.get(v.size() - 1).intValue() >= this.sizeinbits)) + v.remove(v.size() - 1); + return v; + } + + /** + * Returns a customized hash code (based on Karp-Rabin). Naturally, if the + * bitmaps are equal, they will hash to the same value. + * + */ + @Override + public int hashCode() { + int karprabin = 0; + final int B = 31; + final EWAHIterator i = new EWAHIterator(this, this.actualsizeinwords); + while( i.hasNext() ) { + i.next(); + if (i.rlw.getRunningBit() == true) { + karprabin += B * karprabin + + (i.rlw.getRunningLength() & ((1l << 32) - 1)); + karprabin += B * karprabin + (i.rlw.getRunningLength() >>> 32); + } + for (int k = 0; k < i.rlw.getNumberOfLiteralWords(); ++k) { + karprabin += B * karprabin + (this.buffer[i.literalWords() + k] & ((1l << 32) - 1)); + karprabin += B * karprabin + (this.buffer[i.literalWords() + k] >>> 32); + } + } + return karprabin; + } + + /** + * Return true if the two EWAHCompressedBitmap have both at least one true bit + * in the same position. Equivalently, you could call "and" and check whether + * there is a set bit, but intersects will run faster if you don't need the + * result of the "and" operation. + * + * @since 0.3.2 + * @param a + * the other bitmap + * @return whether they intersect + */ + public boolean intersects(final EWAHCompressedBitmap a) { + NonEmptyVirtualStorage nevs = new NonEmptyVirtualStorage(); + try { + this.andToContainer(a, nevs); + } catch (NonEmptyVirtualStorage.NonEmptyException nee) { + return true; + } + return false; + } + + /** + * Iterator over the set bits (this is what most people will want to use to + * browse the content if they want an iterator). The location of the set bits + * is returned, in increasing order. + * + * @return the int iterator + */ + public IntIterator intIterator() { + return new IntIteratorImpl( + new EWAHIterator(this, this.actualsizeinwords)); + } + + /** + * iterate over the positions of the true values. This is similar to + * intIterator(), but it uses Java generics. + * + * @return the iterator + */ + @Override +public Iterator iterator() { + return new Iterator() { + @Override + public boolean hasNext() { + return this.under.hasNext(); + } + + @Override + public Integer next() { + return new Integer(this.under.next()); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("bitsets do not support remove"); + } + + final private IntIterator under = intIterator(); + }; + } + + /** + * For internal use. + * + * @param data + * the array of words to be added + * @param start + * the starting point + * @param number + * the number of words to add + */ + private void negative_push_back(final long[] data, final int start, + final int number) { + while (this.actualsizeinwords + number >= this.buffer.length) { + final long oldbuffer[] = this.buffer; + if((this.actualsizeinwords + number) < 32768) + this.buffer = new long[ (this.actualsizeinwords + number) * 2]; + else if((this.actualsizeinwords + number) * 3 / 2 < this.actualsizeinwords + number) // overflow + this.buffer = new long[Integer.MAX_VALUE]; + else + this.buffer = new long[(this.actualsizeinwords + number) * 3 / 2]; + System.arraycopy(oldbuffer, 0, this.buffer, 0, oldbuffer.length); + this.rlw.parent.buffer = this.buffer; + } + for (int k = 0; k < number; ++k) + this.buffer[this.actualsizeinwords + k] = ~data[start + k]; + this.actualsizeinwords += number; + } + + /** + * Negate (bitwise) the current bitmap. To get a negated copy, do + * EWAHCompressedBitmap x= ((EWAHCompressedBitmap) mybitmap.clone()); x.not(); + * + * The running time is proportional to the compressed size (as reported by + * sizeInBytes()). + * + */ + @Override +public void not() { + final EWAHIterator i = new EWAHIterator(this, this.actualsizeinwords); + if (!i.hasNext()) + return; + + while (true) { + final RunningLengthWord rlw1 = i.next(); + rlw1.setRunningBit(!rlw1.getRunningBit()); + for (int j = 0; j < rlw1.getNumberOfLiteralWords(); ++j) { + i.buffer()[i.literalWords() + j] = ~i.buffer()[i.literalWords() + j]; + } + + if (!i.hasNext()) {// must potentially adjust the last literal word + final int usedbitsinlast = this.sizeinbits % wordinbits; + if (usedbitsinlast == 0) + return; + + if (rlw1.getNumberOfLiteralWords() == 0) { + if((rlw1.getRunningLength()>0) && (rlw1.getRunningBit())) { + rlw1.setRunningLength(rlw1.getRunningLength()-1); + this.addLiteralWord((~0l) >>> (wordinbits - usedbitsinlast)); + } + return; + } + i.buffer()[i.literalWords() + rlw1.getNumberOfLiteralWords() - 1] &= ((~0l) >>> (wordinbits - usedbitsinlast)); + return; + } + } + } + + /** + * Returns a new compressed bitmap containing the bitwise OR values of the + * current bitmap with some other bitmap. + * + * The running time is proportional to the sum of the compressed sizes (as + * reported by sizeInBytes()). + * + * If you are not planning on adding to the resulting bitmap, you may call the trim() + * method to reduce memory usage. + * + * @param a + * the other bitmap + * @return the EWAH compressed bitmap + */ + @Override +public EWAHCompressedBitmap or(final EWAHCompressedBitmap a) { + final EWAHCompressedBitmap container = new EWAHCompressedBitmap(); + container.reserve(this.actualsizeinwords + a.actualsizeinwords); + orToContainer(a, container); + return container; + } + + + + /** + * Computes the bitwise or between the current bitmap and the bitmap "a". + * Stores the result in the container. + * + * @since 0.4.0 + * @param a + * the other bitmap + * @param container + * where we store the result + */ + public void orToContainer(final EWAHCompressedBitmap a, final BitmapStorage container) { + final EWAHIterator i = a.getEWAHIterator(); + final EWAHIterator j = getEWAHIterator(); + final IteratingBufferedRunningLengthWord rlwi = new IteratingBufferedRunningLengthWord(i); + final IteratingBufferedRunningLengthWord rlwj = new IteratingBufferedRunningLengthWord(j); + while ((rlwi.size()>0) && (rlwj.size()>0)) { + while ((rlwi.getRunningLength() > 0) || (rlwj.getRunningLength() > 0)) { + final boolean i_is_prey = rlwi.getRunningLength() < rlwj + .getRunningLength(); + final IteratingBufferedRunningLengthWord prey = i_is_prey ? rlwi + : rlwj; + final IteratingBufferedRunningLengthWord predator = i_is_prey ? rlwj + : rlwi; + if (predator.getRunningBit() == true) { + container.addStreamOfEmptyWords(true, predator.getRunningLength()); + prey.discardFirstWords(predator.getRunningLength()); + predator.discardFirstWords(predator.getRunningLength()); + } else { + long index = prey.discharge(container, predator.getRunningLength()); + container.addStreamOfEmptyWords(false, predator.getRunningLength() + - index); + predator.discardFirstWords(predator.getRunningLength()); + } + } + final int nbre_literal = Math.min(rlwi.getNumberOfLiteralWords(), + rlwj.getNumberOfLiteralWords()); + if (nbre_literal > 0) { + for (int k = 0; k < nbre_literal; ++k) { + container.add(rlwi.getLiteralWordAt(k) | rlwj.getLiteralWordAt(k)); + } + rlwi.discardFirstWords(nbre_literal); + rlwj.discardFirstWords(nbre_literal); + } + } + final boolean i_remains = rlwi.size()>0; + final IteratingBufferedRunningLengthWord remaining = i_remains ? rlwi : rlwj; + remaining.discharge(container); + container.setSizeInBits(Math.max(sizeInBits(), a.sizeInBits())); + } + + /** + * Returns the cardinality of the result of a bitwise OR of the values of the + * current bitmap with some other bitmap. Avoids needing to allocate an + * intermediate bitmap to hold the result of the OR. + * + * @since 0.4.0 + * @param a + * the other bitmap + * @return the cardinality + */ + public int orCardinality(final EWAHCompressedBitmap a) { + final BitCounter counter = new BitCounter(); + orToContainer(a, counter); + return counter.getCount(); + } + + /** + * For internal use. + * + * @param data + * the word to be added + */ + private void push_back(final long data) { + if (this.actualsizeinwords == this.buffer.length) { + final long oldbuffer[] = this.buffer; + if(oldbuffer.length < 32768) + this.buffer = new long[ oldbuffer.length * 2]; + else if(oldbuffer.length * 3 / 2 < oldbuffer.length) // overflow + this.buffer = new long[Integer.MAX_VALUE]; + else + this.buffer = new long[oldbuffer.length * 3 / 2]; + System.arraycopy(oldbuffer, 0, this.buffer, 0, oldbuffer.length); + this.rlw.parent.buffer = this.buffer; + } + this.buffer[this.actualsizeinwords++] = data; + } + + /** + * For internal use. + * + * @param data + * the array of words to be added + * @param start + * the starting point + * @param number + * the number of words to add + */ + private void push_back(final long[] data, final int start, final int number) { + if (this.actualsizeinwords + number >= this.buffer.length) { + final long oldbuffer[] = this.buffer; + if(this.actualsizeinwords + number < 32768) + this.buffer = new long[(this.actualsizeinwords + number) * 2]; + else if ((this.actualsizeinwords + number) * 3 / 2 < this.actualsizeinwords + number) // overflow + this.buffer = new long[Integer.MAX_VALUE]; + else + this.buffer = new long[( this.actualsizeinwords + number) * 3 / 2]; + System.arraycopy(oldbuffer, 0, this.buffer, 0, oldbuffer.length); + this.rlw.parent.buffer = this.buffer; + } + System.arraycopy(data, start, this.buffer, this.actualsizeinwords, number); + this.actualsizeinwords += number; + } + + /* + * @see java.io.Externalizable#readExternal(java.io.ObjectInput) + */ + @Override +public void readExternal(ObjectInput in) throws IOException { + deserialize(in); + } + + /** + * For internal use (trading off memory for speed). + * + * @param size + * the number of words to allocate + * @return True if the operation was a success. + */ + private boolean reserve(final int size) { + if (size > this.buffer.length) { + final long oldbuffer[] = this.buffer; + this.buffer = new long[size]; + System.arraycopy(oldbuffer, 0, this.buffer, 0, oldbuffer.length); + this.rlw.parent.buffer = this.buffer; + return true; + } + return false; + } + + /** + * Serialize. + * + * @param out + * the DataOutput stream + * @throws IOException + * Signals that an I/O exception has occurred. + */ + public void serialize(DataOutput out) throws IOException { + out.writeInt(this.sizeinbits); + out.writeInt(this.actualsizeinwords); + for (int k = 0; k < this.actualsizeinwords; ++k) + out.writeLong(this.buffer[k]); + out.writeInt(this.rlw.position); + } + + /** + * Report the size required to serialize this bitmap + * + * @return the size in bytes + */ + public int serializedSizeInBytes() { + return this.sizeInBytes() + 3 * 4; + } + + + /** + * Query the value of a single bit. Relying on this method when speed is + * needed is discouraged. The complexity is linear with the size of the + * bitmap. + * + * (This implementation is based on zhenjl's Go version of JavaEWAH.) + * + * @param i + * the bit we are interested in + * @return whether the bit is set to true + */ + public boolean get(final int i) { + if ((i < 0) || (i >= this.sizeinbits)) + return false; + int WordChecked = 0; + final IteratingRLW j = getIteratingRLW(); + final int wordi = i/wordinbits; + while (WordChecked <= wordi ) { + WordChecked += j.getRunningLength(); + if (wordi < WordChecked) { + return j.getRunningBit(); + } + if (wordi < WordChecked + j.getNumberOfLiteralWords()) { + final long w = j.getLiteralWordAt(wordi - WordChecked); + return (w & (1l << i)) != 0; + } + WordChecked += j.getNumberOfLiteralWords(); + j.next(); + } + return false; + } + + /** + * Set the bit at position i to true, the bits must be set in (strictly) increasing + * order. For example, set(15) and then set(7) will fail. You must do set(7) + * and then set(15). + * + * @param i + * the index + * @return true if the value was set (always true when i greater or equal to sizeInBits()). + * @throws IndexOutOfBoundsException + * if i is negative or greater than Integer.MAX_VALUE - 64 + */ + public boolean set(final int i) { + if ((i > Integer.MAX_VALUE - wordinbits) || (i < 0)) + throw new IndexOutOfBoundsException("Set values should be between 0 and " + + (Integer.MAX_VALUE - wordinbits)); + if (i < this.sizeinbits) + return false; + // distance in words: + final int dist = (i + wordinbits) / wordinbits + - (this.sizeinbits + wordinbits - 1) / wordinbits; + this.sizeinbits = i + 1; + if (dist > 0) {// easy + if (dist > 1) + fastaddStreamOfEmptyWords(false, dist - 1); + addLiteralWord(1l << (i % wordinbits)); + return true; + } + if (this.rlw.getNumberOfLiteralWords() == 0) { + this.rlw.setRunningLength(this.rlw.getRunningLength() - 1); + addLiteralWord(1l << (i % wordinbits)); + return true; + } + this.buffer[this.actualsizeinwords - 1] |= 1l << (i % wordinbits); + if (this.buffer[this.actualsizeinwords - 1] == ~0l) { + this.buffer[this.actualsizeinwords - 1] = 0; + --this.actualsizeinwords; + this.rlw.setNumberOfLiteralWords(this.rlw.getNumberOfLiteralWords() - 1); + // next we add one clean word + addEmptyWord(true); + } + return true; + } + + /** + * Set the size in bits. This does not change the compressed bitmap. + * + * @since 0.4.0 + */ + @Override +public void setSizeInBits(final int size) { + if((size+EWAHCompressedBitmap.wordinbits-1)/EWAHCompressedBitmap.wordinbits!= (this.sizeinbits+EWAHCompressedBitmap.wordinbits-1)/EWAHCompressedBitmap.wordinbits) + throw new RuntimeException("You can only reduce the size of the bitmap within the scope of the last word. To extend the bitmap, please call setSizeInbits(int,boolean)."); + this.sizeinbits = size; + } + + /** + * Change the reported size in bits of the *uncompressed* bitmap represented + * by this compressed bitmap. It may change the underlying compressed bitmap. + * It is not possible to reduce the sizeInBits, but + * it can be extended. The new bits are set to false or true depending on the + * value of defaultvalue. + * + * @param size + * the size in bits + * @param defaultvalue + * the default boolean value + * @return true if the update was possible + */ + public boolean setSizeInBits(final int size, final boolean defaultvalue) { + if (size < this.sizeinbits) + return false; + if (defaultvalue == false) + extendEmptyBits(this, this.sizeinbits, size); + else { + // next bit could be optimized + while (((this.sizeinbits % wordinbits) != 0) && (this.sizeinbits < size)) { + this.set(this.sizeinbits); + } + this.addStreamOfEmptyWords(defaultvalue, (size / wordinbits) + - this.sizeinbits / wordinbits); + // next bit could be optimized + while (this.sizeinbits < size) { + this.set(this.sizeinbits); + } + } + this.sizeinbits = size; + return true; + } + + /** + * Returns the size in bits of the *uncompressed* bitmap represented by this + * compressed bitmap. Initially, the sizeInBits is zero. It is extended + * automatically when you set bits to true. + * + * @return the size in bits + */ + @Override +public int sizeInBits() { + return this.sizeinbits; + } + + /** + * Report the *compressed* size of the bitmap (equivalent to memory usage, + * after accounting for some overhead). + * + * @return the size in bytes + */ + @Override +public int sizeInBytes() { + return this.actualsizeinwords * (wordinbits / 8); + } + + /** + * Populate an array of (sorted integers) corresponding to the location of the + * set bits. + * + * @return the array containing the location of the set bits + */ + public int[] toArray() { + int[] ans = new int[this.cardinality()]; + int inanspos = 0; + int pos = 0; + final EWAHIterator i = new EWAHIterator(this, this.actualsizeinwords); + while (i.hasNext()) { + RunningLengthWord localrlw = i.next(); + if (localrlw.getRunningBit()) { + for (int j = 0; j < localrlw.getRunningLength(); ++j) { + for (int c = 0; c < wordinbits; ++c) { + ans[inanspos++] = pos++; + } + } + } else { + pos += wordinbits * localrlw.getRunningLength(); + } + for (int j = 0; j < localrlw.getNumberOfLiteralWords(); ++j) { + long data = i.buffer()[i.literalWords() + j]; + if (!usetrailingzeros) { + for (int c = 0; c < wordinbits; ++c) { + if ((data & (1l << c)) != 0) + ans[inanspos++] = c + pos; + } + pos += wordinbits; + } else { + while (data != 0) { + final int ntz = Long.numberOfTrailingZeros(data); + data ^= (1l << ntz); + ans[inanspos++] = ntz + pos; + } + pos += wordinbits; + } + } + } + return ans; + + } + + /** + * A more detailed string describing the bitmap (useful for debugging). + * + * @return the string + */ + public String toDebugString() { + String ans = " EWAHCompressedBitmap, size in bits = " + this.sizeinbits + + " size in words = " + this.actualsizeinwords + "\n"; + final EWAHIterator i = new EWAHIterator(this, this.actualsizeinwords); + while (i.hasNext()) { + RunningLengthWord localrlw = i.next(); + if (localrlw.getRunningBit()) { + ans += localrlw.getRunningLength() + " 1x11\n"; + } else { + ans += localrlw.getRunningLength() + " 0x00\n"; + } + ans += localrlw.getNumberOfLiteralWords() + " dirties\n"; + for (int j = 0; j < localrlw.getNumberOfLiteralWords(); ++j) { + long data = i.buffer()[i.literalWords() + j]; + ans += "\t" + data + "\n"; + } + } + return ans; + } + + /** + * A string describing the bitmap. + * + * @return the string + */ + @Override + public String toString() { + StringBuffer answer = new StringBuffer(); + IntIterator i = this.intIterator(); + answer.append("{"); + if (i.hasNext()) + answer.append(i.next()); + while (i.hasNext()) { + answer.append(","); + answer.append(i.next()); + } + answer.append("}"); + return answer.toString(); + } + + /** + * swap the content of the bitmap with another. + * @param other bitmap to swap with + */ +public void swap(final EWAHCompressedBitmap other) { + long[] tmp = this.buffer; + this.buffer = other.buffer; + other.buffer = tmp; + + + int tmp2 = this.rlw.position; + this.rlw.position = other.rlw.position; + other.rlw.position = tmp2; + + int tmp3 = this.actualsizeinwords; + this.actualsizeinwords = other.actualsizeinwords; + other.actualsizeinwords = tmp3; + + int tmp4 = this.sizeinbits; + this.sizeinbits = other.sizeinbits; + other.sizeinbits = tmp4; + } + + /** + * Reduce the internal buffer to its minimal allowable size (given + * by this.actualsizeinwords). This can free memory. + */ + public void trim() { + this.buffer = Arrays.copyOf(this.buffer, this.actualsizeinwords); + } + + /* + * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput) + */ + @Override +public void writeExternal(ObjectOutput out) throws IOException { + serialize(out); + } + + /** + * Returns a new compressed bitmap containing the bitwise XOR values of the + * current bitmap with some other bitmap. + * + * The running time is proportional to the sum of the compressed sizes (as + * reported by sizeInBytes()). + * + * If you are not planning on adding to the resulting bitmap, you may call the trim() + * method to reduce memory usage. + * + * @param a + * the other bitmap + * @return the EWAH compressed bitmap + */ + @Override +public EWAHCompressedBitmap xor(final EWAHCompressedBitmap a) { + final EWAHCompressedBitmap container = new EWAHCompressedBitmap(); + container.reserve(this.actualsizeinwords + a.actualsizeinwords); + xorToContainer(a, container); + return container; + } + + /** + * Computes a new compressed bitmap containing the bitwise XOR values of the + * current bitmap with some other bitmap. + * + * The running time is proportional to the sum of the compressed sizes (as + * reported by sizeInBytes()). + * + * @since 0.4.0 + * @param a + * the other bitmap + * @param container + * where we store the result + */ + public void xorToContainer(final EWAHCompressedBitmap a, final BitmapStorage container) { + final EWAHIterator i = a.getEWAHIterator(); + final EWAHIterator j = getEWAHIterator(); + final IteratingBufferedRunningLengthWord rlwi = new IteratingBufferedRunningLengthWord(i); + final IteratingBufferedRunningLengthWord rlwj = new IteratingBufferedRunningLengthWord(j); + while ((rlwi.size()>0) && (rlwj.size()>0)) { + while ((rlwi.getRunningLength() > 0) || (rlwj.getRunningLength() > 0)) { + final boolean i_is_prey = rlwi.getRunningLength() < rlwj + .getRunningLength(); + final IteratingBufferedRunningLengthWord prey = i_is_prey ? rlwi : rlwj; + final IteratingBufferedRunningLengthWord predator = i_is_prey ? rlwj + : rlwi; + if (predator.getRunningBit() == false) { + long index = prey.discharge(container, predator.getRunningLength()); + container.addStreamOfEmptyWords(false, predator.getRunningLength() + - index); + predator.discardFirstWords(predator.getRunningLength()); + } else { + long index = prey.dischargeNegated(container, predator.getRunningLength()); + container.addStreamOfEmptyWords(true, predator.getRunningLength() + - index); + predator.discardFirstWords(predator.getRunningLength()); + } + } + final int nbre_literal = Math.min(rlwi.getNumberOfLiteralWords(), + rlwj.getNumberOfLiteralWords()); + if (nbre_literal > 0) { + for (int k = 0; k < nbre_literal; ++k) + container.add(rlwi.getLiteralWordAt(k) ^ rlwj.getLiteralWordAt(k)); + rlwi.discardFirstWords(nbre_literal); + rlwj.discardFirstWords(nbre_literal); + } + } + final boolean i_remains = rlwi.size()>0; + final IteratingBufferedRunningLengthWord remaining = i_remains ? rlwi : rlwj; + remaining.discharge(container); + container.setSizeInBits(Math.max(sizeInBits(), a.sizeInBits())); + } + + /** + * Returns the cardinality of the result of a bitwise XOR of the values of the + * current bitmap with some other bitmap. Avoids needing to allocate an + * intermediate bitmap to hold the result of the OR. + * + * @since 0.4.0 + * @param a + * the other bitmap + * @return the cardinality + */ + public int xorCardinality(final EWAHCompressedBitmap a) { + final BitCounter counter = new BitCounter(); + xorToContainer(a, counter); + return counter.getCount(); + } + + /** + * For internal use. Computes the bitwise and of the provided bitmaps and + * stores the result in the container. + * + * @param container + * where the result is stored + * @param bitmaps + * bitmaps to AND + * @since 0.4.3 + */ + public static void andWithContainer(final BitmapStorage container, + final EWAHCompressedBitmap... bitmaps) { + if(bitmaps.length == 1) throw new IllegalArgumentException("Need at least one bitmap"); + if(bitmaps.length == 2) { + bitmaps[0].andToContainer(bitmaps[1],container); + return; + } + EWAHCompressedBitmap answer = new EWAHCompressedBitmap(); + EWAHCompressedBitmap tmp = new EWAHCompressedBitmap(); + bitmaps[0].andToContainer(bitmaps[1], answer); + for(int k = 2; k < bitmaps.length - 1; ++k) { + answer.andToContainer(bitmaps[k], tmp); + tmp.swap(answer); + tmp.clear(); + } + answer.andToContainer(bitmaps[bitmaps.length - 1], container); + } + + /** + * Returns a new compressed bitmap containing the bitwise AND values of the + * provided bitmaps. + * + * It may or may not be faster than doing the aggregation two-by-two (A.and(B).and(C)). + * + * If only one bitmap is provided, it is returned as is. + * + * If you are not planning on adding to the resulting bitmap, you may call the trim() + * method to reduce memory usage. + * + * @since 0.4.3 + * @param bitmaps + * bitmaps to AND together + * @return result of the AND + */ + public static EWAHCompressedBitmap and(final EWAHCompressedBitmap... bitmaps) { + if(bitmaps.length == 1) return bitmaps[0]; + if(bitmaps.length == 2) return bitmaps[0].and(bitmaps[1]); + EWAHCompressedBitmap answer = new EWAHCompressedBitmap(); + EWAHCompressedBitmap tmp = new EWAHCompressedBitmap(); + bitmaps[0].andToContainer(bitmaps[1], answer); + for(int k = 2; k < bitmaps.length; ++k) { + answer.andToContainer(bitmaps[k], tmp); + tmp.swap(answer); + tmp.clear(); + } + return answer; + } + + /** + * Returns the cardinality of the result of a bitwise AND of the values of the + * provided bitmaps. Avoids needing to allocate an intermediate bitmap to hold + * the result of the AND. + * + * @since 0.4.3 + * @param bitmaps + * bitmaps to AND + * @return the cardinality + */ + public static int andCardinality(final EWAHCompressedBitmap... bitmaps) { + if(bitmaps.length == 1) return bitmaps[0].cardinality(); + final BitCounter counter = new BitCounter(); + andWithContainer(counter, bitmaps); + return counter.getCount(); + } + + /** + * Return a bitmap with the bit set to true at the given + * positions. The positions should be given in sorted order. + * + * (This is a convenience method.) + * + * @since 0.4.5 + * @param setbits list of set bit positions + * @return the bitmap + */ + public static EWAHCompressedBitmap bitmapOf(int ... setbits) { + EWAHCompressedBitmap a = new EWAHCompressedBitmap(); + for (int k : setbits) + a.set(k); + return a; + } + + + + + /** + * For internal use. This simply adds a stream of words made of zeroes so that + * we pad to the desired size. + * + * @param storage + * bitmap to extend + * @param currentSize + * current size (in bits) + * @param newSize + * new desired size (in bits) + * @since 0.4.3 + */ + private static void extendEmptyBits(final BitmapStorage storage, + final int currentSize, final int newSize) { + final int currentLeftover = currentSize % wordinbits; + final int finalLeftover = newSize % wordinbits; + storage.addStreamOfEmptyWords(false, (newSize / wordinbits) - currentSize + / wordinbits + (finalLeftover != 0 ? 1 : 0) + + (currentLeftover != 0 ? -1 : 0)); + } + + + /** + * Uses an adaptive technique to compute the logical OR. + * Mostly for internal use. + * + * @param container where the aggregate is written. + * @param bitmaps to be aggregated + */ + public static void orWithContainer(final BitmapStorage container, + final EWAHCompressedBitmap... bitmaps) { + if (bitmaps.length < 2) + throw new IllegalArgumentException("You should provide at least two bitmaps, provided "+bitmaps.length); + long size = 0L; + long sinbits = 0L; + for (EWAHCompressedBitmap b : bitmaps) { + size += b.sizeInBytes(); + if (sinbits < b.sizeInBits()) + sinbits = b.sizeInBits(); + } + if (size * 8 > sinbits) { + FastAggregation.bufferedorWithContainer(container, 65536, bitmaps); + } else { + FastAggregation.orToContainer(container, bitmaps); + } + } + + + /** + * Uses an adaptive technique to compute the logical XOR. + * Mostly for internal use. + * + * @param container where the aggregate is written. + * @param bitmaps to be aggregated + */ + public static void xorWithContainer(final BitmapStorage container, + final EWAHCompressedBitmap... bitmaps) { + if (bitmaps.length < 2) + throw new IllegalArgumentException("You should provide at least two bitmaps, provided "+bitmaps.length); + long size = 0L; + long sinbits = 0L; + for (EWAHCompressedBitmap b : bitmaps) { + size += b.sizeInBytes(); + if (sinbits < b.sizeInBits()) + sinbits = b.sizeInBits(); + } + if (size * 8 > sinbits) { + FastAggregation.bufferedxorWithContainer(container, 65536, bitmaps); + } else { + FastAggregation.xorToContainer(container, bitmaps); + } + } + /** + * Returns a new compressed bitmap containing the bitwise OR values of the + * provided bitmaps. This is typically faster than doing the aggregation + * two-by-two (A.or(B).or(C).or(D)). + * + * If only one bitmap is provided, it is returned as is. + * + * If you are not planning on adding to the resulting bitmap, you may call the trim() + * method to reduce memory usage. + * + * @since 0.4.0 + * @param bitmaps + * bitmaps to OR together + * @return result of the OR + */ + public static EWAHCompressedBitmap or(final EWAHCompressedBitmap... bitmaps) { + if(bitmaps.length == 1) + return bitmaps[0]; + final EWAHCompressedBitmap container = new EWAHCompressedBitmap(); + int largestSize = 0; + for (EWAHCompressedBitmap bitmap : bitmaps) { + largestSize = Math.max(bitmap.actualsizeinwords, largestSize); + } + container.reserve((int) (largestSize * 1.5)); + orWithContainer(container, bitmaps); + return container; + } + /** + * Returns a new compressed bitmap containing the bitwise XOR values of the + * provided bitmaps. This is typically faster than doing the aggregation + * two-by-two (A.xor(B).xor(C).xor(D)). + * + * If only one bitmap is provided, it is returned as is. + * + * If you are not planning on adding to the resulting bitmap, you may call the trim() + * method to reduce memory usage. + * + * @param bitmaps + * bitmaps to XOR together + * @return result of the XOR + */ + public static EWAHCompressedBitmap xor(final EWAHCompressedBitmap... bitmaps) { + if(bitmaps.length == 1) + return bitmaps[0]; + final EWAHCompressedBitmap container = new EWAHCompressedBitmap(); + int largestSize = 0; + for (EWAHCompressedBitmap bitmap : bitmaps) { + largestSize = Math.max(bitmap.actualsizeinwords, largestSize); + } + container.reserve((int) (largestSize * 1.5)); + xorWithContainer(container, bitmaps); + return container; + } + + /** + * Returns the cardinality of the result of a bitwise OR of the values of the + * provided bitmaps. Avoids needing to allocate an intermediate bitmap to hold + * the result of the OR. + * + * @since 0.4.0 + * @param bitmaps + * bitmaps to OR + * @return the cardinality + */ + public static int orCardinality(final EWAHCompressedBitmap... bitmaps) { + if(bitmaps.length == 1) return bitmaps[0].cardinality(); + final BitCounter counter = new BitCounter(); + orWithContainer(counter, bitmaps); + return counter.getCount(); + } + + /** The actual size in words. */ + int actualsizeinwords = 1; + + /** The buffer (array of 64-bit words) */ + long buffer[] = null; + + /** The current (last) running length word. */ + RunningLengthWord rlw = null; + + /** sizeinbits: number of bits in the (uncompressed) bitmap. */ + int sizeinbits = 0; + + /** + * The Constant defaultbuffersize: default memory allocation when the object + * is constructed. + */ + static final int defaultbuffersize = 4; + + /** optimization option **/ + public static final boolean usetrailingzeros = true; + + /** whether we adjust after some aggregation by adding in zeroes **/ + public static final boolean adjustContainerSizeWhenAggregating = true; + + /** The Constant wordinbits represents the number of bits in a long. */ + public static final int wordinbits = 64; + +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah/EWAHIterator.java b/fine-jgit/src/com/fr/third/googlecode/javaewah/EWAHIterator.java new file mode 100644 index 000000000..991698814 --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah/EWAHIterator.java @@ -0,0 +1,98 @@ +package com.fr.third.googlecode.javaewah; + + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ + +/** + * The class EWAHIterator represents a special type of + * efficient iterator iterating over (uncompressed) words of bits. + * It is not meant for end users. + * @author Daniel Lemire + * @since 0.1.0 + * + */ +public final class EWAHIterator implements Cloneable { + + /** + * Instantiates a new EWAH iterator. + * + * @param a the array of words + * @param sizeinwords the number of words that are significant in the array of words + */ + public EWAHIterator(final EWAHCompressedBitmap a, final int sizeinwords) { + this.rlw = new RunningLengthWord(a, 0); + this.size = sizeinwords; + this.pointer = 0; + } + + /** + * Allow expert developers to instantiate an EWAHIterator. + * + * @param bitmap we want to iterate over + * @return an iterator + */ + public static EWAHIterator getEWAHIterator(EWAHCompressedBitmap bitmap) { + return bitmap.getEWAHIterator(); + } + + + /** + * Access to the array of words + * + * @return the long[] + */ + public long[] buffer() { + return this.rlw.parent.buffer; + } + + /** + * Position of the literal words represented by this running length word. + * + * @return the int + */ + public int literalWords() { + return this.pointer - this.rlw.getNumberOfLiteralWords(); + } + + /** + * Checks for next. + * + * @return true, if successful + */ + public boolean hasNext() { + return this.pointer < this.size; + } + + /** + * Next running length word. + * + * @return the running length word + */ + public RunningLengthWord next() { + this.rlw.position = this.pointer; + this.pointer += this.rlw.getNumberOfLiteralWords() + 1; + return this.rlw; + } + + @Override + public EWAHIterator clone() throws CloneNotSupportedException { + EWAHIterator ans = (EWAHIterator) super.clone(); + ans.rlw = this.rlw.clone(); + ans.size = this.size; + ans.pointer = this.pointer; + return ans; + } + /** The pointer represent the location of the current running length + * word in the array of words (embedded in the rlw attribute). */ + int pointer; + + /** The current running length word. */ + RunningLengthWord rlw; + + /** The size in words. */ + int size; + +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah/FastAggregation.java b/fine-jgit/src/com/fr/third/googlecode/javaewah/FastAggregation.java new file mode 100644 index 000000000..80d378469 --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah/FastAggregation.java @@ -0,0 +1,436 @@ +package com.fr.third.googlecode.javaewah; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.PriorityQueue; + + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ + +/** + * Fast algorithms to aggregate many bitmaps. These algorithms are just given as + * reference. They may not be faster than the corresponding methods in the + * EWAHCompressedBitmap class. + * + * @author Daniel Lemire + * + */ +public class FastAggregation { + /** + * Compute the and aggregate using a temporary uncompressed bitmap. + * @param bitmaps the source bitmaps + * @param bufsize buffer size used during the computation in 64-bit words (per input bitmap) + * @return the or aggregate. + */ + public static EWAHCompressedBitmap bufferedand(final int bufsize, + final EWAHCompressedBitmap... bitmaps) { + EWAHCompressedBitmap answer = new EWAHCompressedBitmap(); + bufferedandWithContainer(answer,bufsize, bitmaps); + return answer; + } + /** + * Compute the and aggregate using a temporary uncompressed bitmap. + * + * @param container where the aggregate is written + * @param bufsize buffer size used during the computation in 64-bit words (per input bitmap) + * @param bitmaps the source bitmaps + */ + public static void bufferedandWithContainer(final BitmapStorage container,final int bufsize, + final EWAHCompressedBitmap... bitmaps) { + + java.util.LinkedList al = new java.util.LinkedList(); + for (EWAHCompressedBitmap bitmap : bitmaps) { + al.add(new IteratingBufferedRunningLengthWord(bitmap)); + } + + long[] hardbitmap = new long[bufsize*bitmaps.length]; + + for(IteratingRLW i : al) + if (i.size() == 0) { + al.clear(); + break; + } + + while (!al.isEmpty()) { + Arrays.fill(hardbitmap, ~0l); + long effective = Integer.MAX_VALUE; + for(IteratingRLW i : al) { + int eff = IteratorAggregation.inplaceand(hardbitmap, i); + if (eff < effective) + effective = eff; + } + for (int k = 0; k < effective; ++k) + container.add(hardbitmap[k]); + for(IteratingRLW i : al) + if (i.size() == 0) { + al.clear(); + break; + } + } + } + + /** + * Compute the or aggregate using a temporary uncompressed bitmap. + * @param bitmaps the source bitmaps + * @param bufsize buffer size used during the computation in 64-bit words + * @return the or aggregate. + */ + public static EWAHCompressedBitmap bufferedor(final int bufsize, + final EWAHCompressedBitmap... bitmaps) { + EWAHCompressedBitmap answer = new EWAHCompressedBitmap(); + bufferedorWithContainer(answer, bufsize, bitmaps); + return answer; + } + + /** + * Compute the or aggregate using a temporary uncompressed bitmap. + * + * @param container where the aggregate is written + * @param bufsize buffer size used during the computation in 64-bit words + * @param bitmaps the source bitmaps + */ + public static void bufferedorWithContainer(final BitmapStorage container, final int bufsize, + final EWAHCompressedBitmap... bitmaps) { + int range = 0; + EWAHCompressedBitmap[] sbitmaps = bitmaps.clone(); + Arrays.sort(sbitmaps, new Comparator() { + @Override + public int compare(EWAHCompressedBitmap a, EWAHCompressedBitmap b) { + return b.sizeinbits - a.sizeinbits; + } + }); + + java.util.ArrayList al = new java.util.ArrayList(); + for (EWAHCompressedBitmap bitmap : sbitmaps) { + if (bitmap.sizeinbits > range) + range = bitmap.sizeinbits; + al.add(new IteratingBufferedRunningLengthWord(bitmap)); + } + long[] hardbitmap = new long[bufsize]; + int maxr = al.size(); + while (maxr > 0) { + long effective = 0; + for (int k = 0; k < maxr; ++k) { + if (al.get(k).size() > 0) { + int eff = IteratorAggregation.inplaceor(hardbitmap, al.get(k)); + if (eff > effective) + effective = eff; + } else + maxr = k; + } + for (int k = 0; k < effective; ++k) + container.add(hardbitmap[k]); + Arrays.fill(hardbitmap, 0); + + } + container.setSizeInBits(range); + } + + /** + * Compute the xor aggregate using a temporary uncompressed bitmap. + * @param bitmaps the source bitmaps + * @param bufsize buffer size used during the computation in 64-bit words + * @return the xor aggregate. + */ + public static EWAHCompressedBitmap bufferedxor(final int bufsize, + final EWAHCompressedBitmap... bitmaps) { + EWAHCompressedBitmap answer = new EWAHCompressedBitmap(); + bufferedxorWithContainer(answer, bufsize,bitmaps); + return answer; + } + + + /** + * Compute the xor aggregate using a temporary uncompressed bitmap. + * + * @param container where the aggregate is written + * @param bufsize buffer size used during the computation in 64-bit words + * @param bitmaps the source bitmaps + */ + public static void bufferedxorWithContainer(final BitmapStorage container, final int bufsize, + final EWAHCompressedBitmap... bitmaps) { + int range = 0; + EWAHCompressedBitmap[] sbitmaps = bitmaps.clone(); + Arrays.sort(sbitmaps, new Comparator() { + @Override + public int compare(EWAHCompressedBitmap a, EWAHCompressedBitmap b) { + return b.sizeinbits - a.sizeinbits; + } + }); + + java.util.ArrayList al = new java.util.ArrayList(); + for (EWAHCompressedBitmap bitmap : sbitmaps) { + if (bitmap.sizeinbits > range) + range = bitmap.sizeinbits; + al.add(new IteratingBufferedRunningLengthWord(bitmap)); + } + long[] hardbitmap = new long[bufsize]; + int maxr = al.size(); + while (maxr > 0) { + long effective = 0; + for (int k = 0; k < maxr; ++k) { + if (al.get(k).size() > 0) { + int eff = IteratorAggregation.inplacexor(hardbitmap, al.get(k)); + if (eff > effective) + effective = eff; + } else + maxr = k; + } + for (int k = 0; k < effective; ++k) + container.add(hardbitmap[k]); + Arrays.fill(hardbitmap, 0); + } + container.setSizeInBits(range); + } + + /** + * Uses a priority queue to compute the or aggregate. + * @param a class extending LogicalElement (like a compressed bitmap) + * @param bitmaps + * bitmaps to be aggregated + * @return the or aggregate + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static T or(T... bitmaps) { + PriorityQueue pq = new PriorityQueue(bitmaps.length, + new Comparator() { + @Override + public int compare(T a, T b) { + return a.sizeInBytes() - b.sizeInBytes(); + } + }); + for (T x : bitmaps) { + pq.add(x); + } + while (pq.size() > 1) { + T x1 = pq.poll(); + T x2 = pq.poll(); + pq.add((T) x1.or(x2)); + } + return pq.poll(); + } + /** + * Uses a priority queue to compute the or aggregate. + * @param container where we write the result + * @param bitmaps to be aggregated + */ + public static void orToContainer(final BitmapStorage container, + final EWAHCompressedBitmap ... bitmaps) { + if(bitmaps.length < 2) throw new IllegalArgumentException("We need at least two bitmaps"); + PriorityQueue pq = new PriorityQueue(bitmaps.length, + new Comparator() { + @Override + public int compare(EWAHCompressedBitmap a, EWAHCompressedBitmap b) { + return a.sizeInBytes() - b.sizeInBytes(); + } + }); + for (EWAHCompressedBitmap x : bitmaps) { + pq.add(x); + } + while (pq.size() > 2) { + EWAHCompressedBitmap x1 = pq.poll(); + EWAHCompressedBitmap x2 = pq.poll(); + pq.add(x1.or(x2)); + } + pq.poll().orToContainer(pq.poll(), container); + } + + + /** + * Uses a priority queue to compute the xor aggregate. + * + * @param a class extending LogicalElement (like a compressed bitmap) + * @param bitmaps + * bitmaps to be aggregated + * @return the xor aggregate + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static T xor(T... bitmaps) { + PriorityQueue pq = new PriorityQueue(bitmaps.length, + new Comparator() { + + @Override + public int compare(T a, T b) { + return a.sizeInBytes() - b.sizeInBytes(); + } + }); + for (T x : bitmaps) + pq.add(x); + while (pq.size() > 1) { + T x1 = pq.poll(); + T x2 = pq.poll(); + pq.add((T) x1.xor(x2)); + } + return pq.poll(); + } + + /** + * Uses a priority queue to compute the xor aggregate. + * @param container where we write the result + * @param bitmaps to be aggregated + */ + public static void xorToContainer(final BitmapStorage container, + final EWAHCompressedBitmap ... bitmaps) { + if(bitmaps.length < 2) throw new IllegalArgumentException("We need at least two bitmaps"); + PriorityQueue pq = new PriorityQueue(bitmaps.length, + new Comparator() { + @Override + public int compare(EWAHCompressedBitmap a, EWAHCompressedBitmap b) { + return a.sizeInBytes() - b.sizeInBytes(); + } + }); + for (EWAHCompressedBitmap x : bitmaps) { + pq.add(x); + } + while (pq.size() > 2) { + EWAHCompressedBitmap x1 = pq.poll(); + EWAHCompressedBitmap x2 = pq.poll(); + pq.add(x1.xor(x2)); + } + pq.poll().xorToContainer(pq.poll(), container); + } + + /** + * For internal use. Computes the bitwise or of the provided bitmaps and + * stores the result in the container. (This used to be the default.) + * + * @deprecated use EWAHCompressedBitmap.or instead + * @since 0.4.0 + * @param container where store the result + * @param bitmaps to be aggregated + */ + @Deprecated + public static void legacy_orWithContainer(final BitmapStorage container, + final EWAHCompressedBitmap... bitmaps) { + if (bitmaps.length == 2) { + // should be more efficient + bitmaps[0].orToContainer(bitmaps[1], container); + return; + } + + // Sort the bitmaps in descending order by sizeinbits. We will exhaust the + // sorted bitmaps from right to left. + final EWAHCompressedBitmap[] sortedBitmaps = bitmaps.clone(); + Arrays.sort(sortedBitmaps, new Comparator() { + @Override + public int compare(EWAHCompressedBitmap a, EWAHCompressedBitmap b) { + return a.sizeinbits < b.sizeinbits ? 1 + : a.sizeinbits == b.sizeinbits ? 0 : -1; + } + }); + + final IteratingBufferedRunningLengthWord[] rlws = new IteratingBufferedRunningLengthWord[bitmaps.length]; + int maxAvailablePos = 0; + for (EWAHCompressedBitmap bitmap : sortedBitmaps) { + EWAHIterator iterator = bitmap.getEWAHIterator(); + if (iterator.hasNext()) { + rlws[maxAvailablePos++] = new IteratingBufferedRunningLengthWord( + iterator); + } + } + + if (maxAvailablePos == 0) { // this never happens... + container.setSizeInBits(0); + return; + } + + int maxSize = sortedBitmaps[0].sizeinbits; + + while (true) { + long maxOneRl = 0; + long minZeroRl = Long.MAX_VALUE; + long minSize = Long.MAX_VALUE; + int numEmptyRl = 0; + for (int i = 0; i < maxAvailablePos; i++) { + IteratingBufferedRunningLengthWord rlw = rlws[i]; + long size = rlw.size(); + if (size == 0) { + maxAvailablePos = i; + break; + } + minSize = Math.min(minSize, size); + + if (rlw.getRunningBit()) { + long rl = rlw.getRunningLength(); + maxOneRl = Math.max(maxOneRl, rl); + minZeroRl = 0; + if (rl == 0 && size > 0) { + numEmptyRl++; + } + } else { + long rl = rlw.getRunningLength(); + minZeroRl = Math.min(minZeroRl, rl); + if (rl == 0 && size > 0) { + numEmptyRl++; + } + } + } + + if (maxAvailablePos == 0) { + break; + } else if (maxAvailablePos == 1) { + // only one bitmap is left so just write the rest of it out + rlws[0].discharge(container); + break; + } + + if (maxOneRl > 0) { + container.addStreamOfEmptyWords(true, maxOneRl); + for (int i = 0; i < maxAvailablePos; i++) { + IteratingBufferedRunningLengthWord rlw = rlws[i]; + rlw.discardFirstWords(maxOneRl); + } + } else if (minZeroRl > 0) { + container.addStreamOfEmptyWords(false, minZeroRl); + for (int i = 0; i < maxAvailablePos; i++) { + IteratingBufferedRunningLengthWord rlw = rlws[i]; + rlw.discardFirstWords(minZeroRl); + } + } else { + int index = 0; + + if (numEmptyRl == 1) { + // if one rlw has literal words to process and the rest have a run of + // 0's we can write them out here + IteratingBufferedRunningLengthWord emptyRl = null; + long minNonEmptyRl = Long.MAX_VALUE; + for (int i = 0; i < maxAvailablePos; i++) { + IteratingBufferedRunningLengthWord rlw = rlws[i]; + long rl = rlw.getRunningLength(); + if (rl == 0) { + assert emptyRl == null; + emptyRl = rlw; + } else { + minNonEmptyRl = Math.min(minNonEmptyRl, rl); + } + } + long wordsToWrite = minNonEmptyRl > minSize ? minSize : minNonEmptyRl; + if (emptyRl != null) + emptyRl.writeLiteralWords((int) wordsToWrite, container); + index += wordsToWrite; + } + + while (index < minSize) { + long word = 0; + for (int i = 0; i < maxAvailablePos; i++) { + IteratingBufferedRunningLengthWord rlw = rlws[i]; + if (rlw.getRunningLength() <= index) { + word |= rlw.getLiteralWordAt(index - (int) rlw.getRunningLength()); + } + } + container.add(word); + index++; + } + for (int i = 0; i < maxAvailablePos; i++) { + IteratingBufferedRunningLengthWord rlw = rlws[i]; + rlw.discardFirstWords(minSize); + } + } + } + container.setSizeInBits(maxSize); + } + +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah/IntIterator.java b/fine-jgit/src/com/fr/third/googlecode/javaewah/IntIterator.java new file mode 100644 index 000000000..2aa5ef020 --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah/IntIterator.java @@ -0,0 +1,31 @@ +package com.fr.third.googlecode.javaewah; + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ + +/** + * + * The IntIterator interface is used to iterate over a stream of integers. + * + * @author Daniel Lemire + * @since 0.2.0 + * + */ +public interface IntIterator { + + /** + * Is there more? + * + * @return true, if there is more, false otherwise + */ + public boolean hasNext(); + + /** + * Return the next integer + * + * @return the integer + */ + public int next(); +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah/IntIteratorImpl.java b/fine-jgit/src/com/fr/third/googlecode/javaewah/IntIteratorImpl.java new file mode 100644 index 000000000..0728ce643 --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah/IntIteratorImpl.java @@ -0,0 +1,87 @@ +package com.fr.third.googlecode.javaewah; + +/* + * Copyright 2012, Google Inc. + * Licensed under the Apache License, Version 2.0. + */ + +import static com.fr.third.googlecode.javaewah.EWAHCompressedBitmap.wordinbits; + +/** + * The IntIteratorImpl is the 64 bit implementation of the + * IntIterator interface, which efficiently returns the stream of integers + * represented by an EWAHIterator. + * + * @author Colby Ranger + * @since 0.5.6 + */ +final class IntIteratorImpl implements IntIterator { + + private final EWAHIterator ewahIter; + private final long[] ewahBuffer; + private int position; + private int runningLength; + private long word; + private int wordPosition; + private int wordLength; + private int literalPosition; + private boolean hasnext; + + IntIteratorImpl(EWAHIterator ewahIter) { + this.ewahIter = ewahIter; + this.ewahBuffer = ewahIter.buffer(); + this.hasnext = this.moveToNext(); + } + + public final boolean moveToNext() { + while (!runningHasNext() && !literalHasNext()) { + if (!this.ewahIter.hasNext()) { + return false; + } + setRunningLengthWord(this.ewahIter.next()); + } + return true; + } + + @Override +public boolean hasNext() { + return this.hasnext; + } + + @Override +public final int next() { + final int answer; + if (runningHasNext()) { + answer = this.position++; + } else { + final int bit = Long.numberOfTrailingZeros(this.word); + this.word ^= (1l << bit); + answer = this.literalPosition + bit; + } + this.hasnext = this.moveToNext(); + return answer; + } + + private final void setRunningLengthWord(RunningLengthWord rlw) { + this.runningLength = wordinbits * (int) rlw.getRunningLength() + this.position; + if (!rlw.getRunningBit()) { + this.position = this.runningLength; + } + + this.wordPosition = this.ewahIter.literalWords(); + this.wordLength = this.wordPosition + rlw.getNumberOfLiteralWords(); + } + + private final boolean runningHasNext() { + return this.position < this.runningLength; + } + + private final boolean literalHasNext() { + while (this.word == 0 && this.wordPosition < this.wordLength) { + this.word = this.ewahBuffer[this.wordPosition++]; + this.literalPosition = this.position; + this.position += wordinbits; + } + return this.word != 0; + } +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah/IntIteratorOverIteratingRLW.java b/fine-jgit/src/com/fr/third/googlecode/javaewah/IntIteratorOverIteratingRLW.java new file mode 100644 index 000000000..c4bf20f63 --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah/IntIteratorOverIteratingRLW.java @@ -0,0 +1,89 @@ +package com.fr.third.googlecode.javaewah; + +import static com.fr.third.googlecode.javaewah.EWAHCompressedBitmap.wordinbits; + + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ +/** + * Implementation of an IntIterator over an IteratingRLW. + * + * + */ +public class IntIteratorOverIteratingRLW implements IntIterator { + IteratingRLW parent; + private int position; + private int runningLength; + private long word; + private int wordPosition; + private int wordLength; + private int literalPosition; + private boolean hasnext; + + /** + * @param p iterator we wish to iterate over + */ + public IntIteratorOverIteratingRLW(final IteratingRLW p) { + this.parent = p; + this.position = 0; + setupForCurrentRunningLengthWord(); + this.hasnext = moveToNext(); + } + + /** + * @return whether we could find another set bit; don't move if there is an unprocessed value + */ + private final boolean moveToNext() { + while (!runningHasNext() && !literalHasNext()) { + if (this.parent.next()) + setupForCurrentRunningLengthWord(); + else return false; + } + return true; + } + + @Override + public boolean hasNext() { + return this.hasnext; + } + + @Override + public final int next() { + final int answer; + if (runningHasNext()) { + answer = this.position++; + } else { + final int bit = Long.numberOfTrailingZeros(this.word); + this.word ^= (1l << bit); + answer = this.literalPosition + bit; + } + this.hasnext = this.moveToNext(); + return answer; + } + + private final void setupForCurrentRunningLengthWord() { + this.runningLength = wordinbits * (int) this.parent.getRunningLength() + + this.position; + + if (!this.parent.getRunningBit()) { + this.position = this.runningLength; + } + this.wordPosition = 0; + this.wordLength = this.parent.getNumberOfLiteralWords(); + } + + private final boolean runningHasNext() { + return this.position < this.runningLength; + } + + private final boolean literalHasNext() { + while (this.word == 0 && this.wordPosition < this.wordLength) { + this.word = this.parent.getLiteralWordAt(this.wordPosition++); + this.literalPosition = this.position; + this.position += wordinbits; + } + return this.word != 0; + } +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah/IteratingBufferedRunningLengthWord.java b/fine-jgit/src/com/fr/third/googlecode/javaewah/IteratingBufferedRunningLengthWord.java new file mode 100644 index 000000000..bb8f2edc5 --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah/IteratingBufferedRunningLengthWord.java @@ -0,0 +1,276 @@ +package com.fr.third.googlecode.javaewah; + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ +/** + * Mostly for internal use. Similar to BufferedRunningLengthWord, but automatically + * advances to the next BufferedRunningLengthWord as words are discarded. + * + * @since 0.4.0 + * @author David McIntosh + */ +public final class IteratingBufferedRunningLengthWord implements IteratingRLW, Cloneable{ + /** + * Instantiates a new iterating buffered running length word. + * + * @param iterator iterator + */ + public IteratingBufferedRunningLengthWord(final EWAHIterator iterator) { + this.iterator = iterator; + this.brlw = new BufferedRunningLengthWord(this.iterator.next()); + this.literalWordStartPosition = this.iterator.literalWords() + this.brlw.literalwordoffset; + this.buffer = this.iterator.buffer(); + } + + + + /** + * Instantiates a new iterating buffered running length word. + * @param bitmap over which we want to iterate + * + */ +public IteratingBufferedRunningLengthWord(final EWAHCompressedBitmap bitmap) { + this.iterator = EWAHIterator.getEWAHIterator(bitmap); + this.brlw = new BufferedRunningLengthWord(this.iterator.next()); + this.literalWordStartPosition = this.iterator.literalWords() + this.brlw.literalwordoffset; + this.buffer = this.iterator.buffer(); + } + + + + + /** + * Discard first words, iterating to the next running length word if needed. + * + * @param x the number of words to be discarded + */ + @Override +public void discardFirstWords(long x) { + while (x > 0) { + if (this.brlw.RunningLength > x) { + this.brlw.RunningLength -= x; + return; + } + x -= this.brlw.RunningLength; + this.brlw.RunningLength = 0; + long toDiscard = x > this.brlw.NumberOfLiteralWords ? this.brlw.NumberOfLiteralWords : x; + + this.literalWordStartPosition += toDiscard; + this.brlw.NumberOfLiteralWords -= toDiscard; + x -= toDiscard; + if ((x > 0) || (this.brlw.size() == 0)) { + if (!this.iterator.hasNext()) { + break; + } + this.brlw.reset(this.iterator.next()); + this.literalWordStartPosition = this.iterator.literalWords(); // + this.brlw.literalwordoffset ==0 + } + } + } + /** + * Move to the next RunningLengthWord + * @return whether the move was possible + */ + @Override +public boolean next() { + if (!this.iterator.hasNext()) { + this.brlw.NumberOfLiteralWords = 0; + this.brlw.RunningLength = 0; + return false; + } + this.brlw.reset(this.iterator.next()); + this.literalWordStartPosition = this.iterator.literalWords(); // + this.brlw.literalwordoffset ==0 + return true; + } + + /** + * Write out up to max words, returns how many were written + * @param container target for writes + * @param max maximal number of writes + * @return how many written + */ + public long discharge(BitmapStorage container, long max) { + long index = 0; + while ((index < max) && (size() > 0)) { + // first run + long pl = getRunningLength(); + if (index + pl > max) { + pl = max - index; + } + container.addStreamOfEmptyWords(getRunningBit(), pl); + index += pl; + int pd = getNumberOfLiteralWords(); + if (pd + index > max) { + pd = (int) (max - index); + } + writeLiteralWords(pd, container); + discardFirstWords(pl+pd); + index += pd; + } + return index; + } + + /** + * Write out up to max words (negated), returns how many were written + * @param container target for writes + * @param max maximal number of writes + * @return how many written + */ + public long dischargeNegated(BitmapStorage container, long max) { + long index = 0; + while ((index < max) && (size() > 0)) { + // first run + long pl = getRunningLength(); + if (index + pl > max) { + pl = max - index; + } + container.addStreamOfEmptyWords(!getRunningBit(), pl); + index += pl; + int pd = getNumberOfLiteralWords(); + if (pd + index > max) { + pd = (int) (max - index); + } + writeNegatedLiteralWords(pd, container); + discardFirstWords(pl+pd); + index += pd; + } + return index; + } + + + /** + * Write out the remain words, transforming them to zeroes. + * @param container target for writes + */ + public void dischargeAsEmpty(BitmapStorage container) { + while(size()>0) { + container.addStreamOfEmptyWords(false, size()); + discardFirstWords(size()); + } + } + + + + /** + * Write out the remaining words + * @param container target for writes + */ + public void discharge(BitmapStorage container) { + this.brlw.literalwordoffset = this.literalWordStartPosition - this.iterator.literalWords(); + discharge(this.brlw, this.iterator, container); + } + + /** + * Get the nth literal word for the current running length word + * @param index zero based index + * @return the literal word + */ + @Override +public long getLiteralWordAt(int index) { + return this.buffer[this.literalWordStartPosition + index]; + } + + /** + * Gets the number of literal words for the current running length word. + * + * @return the number of literal words + */ + @Override +public int getNumberOfLiteralWords() { + return this.brlw.NumberOfLiteralWords; + } + + /** + * Gets the running bit. + * + * @return the running bit + */ + @Override +public boolean getRunningBit() { + return this.brlw.RunningBit; + } + + /** + * Gets the running length. + * + * @return the running length + */ + @Override +public long getRunningLength() { + return this.brlw.RunningLength; + } + + /** + * Size in uncompressed words of the current running length word. + * + * @return the long + */ + @Override +public long size() { + return this.brlw.size(); + } + + /** + * write the first N literal words to the target bitmap. Does not discard the words or perform iteration. + * @param numWords number of words to be written + * @param container where we write + */ + public void writeLiteralWords(int numWords, BitmapStorage container) { + container.addStreamOfLiteralWords(this.buffer, this.literalWordStartPosition, numWords); + } + + /** + * write the first N literal words (negated) to the target bitmap. Does not discard the words or perform iteration. + * @param numWords number of words to be written + * @param container where we write + */ + public void writeNegatedLiteralWords(int numWords, BitmapStorage container) { + container.addStreamOfNegatedLiteralWords(this.buffer, this.literalWordStartPosition, numWords); + } + + /** + * For internal use. (One could use the non-static discharge method instead, + * but we expect them to be slower.) + * + * @param initialWord + * the initial word + * @param iterator + * the iterator + * @param container + * the container + */ + private static void discharge(final BufferedRunningLengthWord initialWord, + final EWAHIterator iterator, final BitmapStorage container) { + BufferedRunningLengthWord runningLengthWord = initialWord; + for (;;) { + final long runningLength = runningLengthWord.getRunningLength(); + container.addStreamOfEmptyWords(runningLengthWord.getRunningBit(), + runningLength); + container.addStreamOfLiteralWords(iterator.buffer(), iterator.literalWords() + + runningLengthWord.literalwordoffset, + runningLengthWord.getNumberOfLiteralWords()); + if (!iterator.hasNext()) + break; + runningLengthWord = new BufferedRunningLengthWord(iterator.next()); + } + } + + + @Override + public IteratingBufferedRunningLengthWord clone() throws CloneNotSupportedException { + IteratingBufferedRunningLengthWord answer = (IteratingBufferedRunningLengthWord) super.clone(); + answer.brlw = this.brlw.clone(); + answer.buffer = this.buffer; + answer.iterator = this.iterator.clone(); + answer.literalWordStartPosition = this.literalWordStartPosition; + return answer; + } + + + private BufferedRunningLengthWord brlw; + private long[] buffer; + private int literalWordStartPosition; + private EWAHIterator iterator; +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah/IteratingRLW.java b/fine-jgit/src/com/fr/third/googlecode/javaewah/IteratingRLW.java new file mode 100644 index 000000000..868b93c5e --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah/IteratingRLW.java @@ -0,0 +1,49 @@ +package com.fr.third.googlecode.javaewah; + + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ + +/** + * High-level iterator over a compressed bitmap. + * + */ +public interface IteratingRLW { + /** + * @return whether there is more + */ + public boolean next() ; + /** + * @param index where the literal word is + * @return the literal word at the given index. + */ + public long getLiteralWordAt(int index); + /** + * @return the number of literal (non-fill) words + */ + public int getNumberOfLiteralWords() ; + /** + * @return the bit used for the fill bits + */ + public boolean getRunningBit() ; + /** + * @return sum of getRunningLength() and getNumberOfLiteralWords() + */ + public long size() ; + /** + * @return length of the run of fill words + */ + public long getRunningLength() ; + /** + * @param x the number of words to discard + */ + public void discardFirstWords(long x); + + /** + * @return a copy of the iterator + * @throws CloneNotSupportedException this should not be thrown in theory + */ + public IteratingRLW clone() throws CloneNotSupportedException; +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah/IteratorAggregation.java b/fine-jgit/src/com/fr/third/googlecode/javaewah/IteratorAggregation.java new file mode 100644 index 000000000..87386625a --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah/IteratorAggregation.java @@ -0,0 +1,616 @@ +package com.fr.third.googlecode.javaewah; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedList; + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ + +/** + * Set of helper functions to aggregate bitmaps. + * + */ +public class IteratorAggregation { + + /** + * @param x iterator to negate + * @return negated version of the iterator + */ + public static IteratingRLW not(final IteratingRLW x) { + return new IteratingRLW() { + + @Override + public boolean next() { + return x.next(); + } + + @Override + public long getLiteralWordAt(int index) { + return ~x.getLiteralWordAt(index); + } + + @Override + public int getNumberOfLiteralWords() { + return x.getNumberOfLiteralWords(); + } + + @Override + public boolean getRunningBit() { + return ! x.getRunningBit(); + } + + @Override + public long size() { + return x.size(); + } + + @Override + public long getRunningLength() { + return x.getRunningLength(); + } + + @Override + public void discardFirstWords(long y) { + x.discardFirstWords(y); + } + + @Override + public IteratingRLW clone() throws CloneNotSupportedException { + throw new CloneNotSupportedException(); + } + + + }; + } + + /** + * Aggregate the iterators using a bitmap buffer. + * + * @param al set of iterators to aggregate + * @return and aggregate + */ + public static IteratingRLW bufferedand(final IteratingRLW... al) { + return bufferedand(DEFAULTMAXBUFSIZE,al); + } + + /** + * Aggregate the iterators using a bitmap buffer. + * + * @param al set of iterators to aggregate + * @param bufsize size of the internal buffer used by the iterator in 64-bit words (per input iterator) + * @return and aggregate + */ + public static IteratingRLW bufferedand(final int bufsize, final IteratingRLW... al) { + if (al.length == 0) + throw new IllegalArgumentException("Need at least one iterator"); + if (al.length == 1) + return al[0]; + final LinkedList basell = new LinkedList(); + for (IteratingRLW i : al) + basell.add(i); + return new BufferedIterator(new BufferedAndIterator(basell,bufsize)); + } + + /** + * Aggregate the iterators using a bitmap buffer. + * + * @param al set of iterators to aggregate + * @return or aggregate + */ + public static IteratingRLW bufferedor(final IteratingRLW... al) { + return bufferedor(DEFAULTMAXBUFSIZE,al); + } + + + /** + * Aggregate the iterators using a bitmap buffer. + * + * @param al iterators to aggregate + * @param bufsize size of the internal buffer used by the iterator in 64-bit words + * @return or aggregate + */ + public static IteratingRLW bufferedor(final int bufsize, final IteratingRLW... al) { + if (al.length == 0) + throw new IllegalArgumentException("Need at least one iterator"); + if (al.length == 1) + return al[0]; + + final LinkedList basell = new LinkedList(); + for (IteratingRLW i : al) + basell.add(i); + return new BufferedIterator(new BufferedORIterator(basell,bufsize)); + } + + /** + * Aggregate the iterators using a bitmap buffer. + * + * @param al set of iterators to aggregate + * @return xor aggregate + */ + public static IteratingRLW bufferedxor(final IteratingRLW... al) { + return bufferedxor(DEFAULTMAXBUFSIZE,al); + } + + + /** + * Aggregate the iterators using a bitmap buffer. + * + * @param al iterators to aggregate + * @param bufsize size of the internal buffer used by the iterator in 64-bit words + * @return xor aggregate + */ + public static IteratingRLW bufferedxor(final int bufsize, final IteratingRLW... al) { + if (al.length == 0) + throw new IllegalArgumentException("Need at least one iterator"); + if (al.length == 1) + return al[0]; + + + final LinkedList basell = new LinkedList(); + for (IteratingRLW i : al) + basell.add(i); + + return new BufferedIterator(new BufferedXORIterator(basell, bufsize)); + } + + + /** + * Write out the content of the iterator, but as if it were all zeros. + * + * @param container + * where we write + * @param i + * the iterator + */ + protected static void dischargeAsEmpty(final BitmapStorage container, + final IteratingRLW i) { + while (i.size() > 0) { + container.addStreamOfEmptyWords(false, i.size()); + i.next(); + + } + } + + /** + * Write out up to max words, returns how many were written + * @param container target for writes + * @param i source of data + * @param max maximal number of writes + * @return how many written + */ + + protected static long discharge(final BitmapStorage container, IteratingRLW i, long max) { + long counter = 0; + while (i.size() > 0 && counter < max) { + long L1 = i.getRunningLength(); + if (L1 > 0) { + if (L1 + counter > max) + L1 = max - counter; + container.addStreamOfEmptyWords(i.getRunningBit(), L1); + counter += L1; + } + long L = i.getNumberOfLiteralWords(); + if(L + counter > max) L = max - counter; + for (int k = 0; k < L; ++k) { + container.add(i.getLiteralWordAt(k)); + } + counter += L; + i.discardFirstWords(L+L1); + } + return counter; + } + + + /** + * Write out up to max negated words, returns how many were written + * @param container target for writes + * @param i source of data + * @param max maximal number of writes + * @return how many written + */ + protected static long dischargeNegated(final BitmapStorage container, IteratingRLW i, long max) { + long counter = 0; + while (i.size() > 0 && counter < max) { + long L1 = i.getRunningLength(); + if (L1 > 0) { + if (L1 + counter > max) + L1 = max - counter; + container.addStreamOfEmptyWords(!i.getRunningBit(), L1); + counter += L1; + } + long L = i.getNumberOfLiteralWords(); + if(L + counter > max) L = max - counter; + for (int k = 0; k < L; ++k) { + container.add(~i.getLiteralWordAt(k)); + } + counter += L; + i.discardFirstWords(L+L1); + } + return counter; + } + + static void andToContainer(final BitmapStorage container, + int desiredrlwcount, final IteratingRLW rlwi, IteratingRLW rlwj) { + while ((rlwi.size()>0) && (rlwj.size()>0) && (desiredrlwcount-- >0) ) { + while ((rlwi.getRunningLength() > 0) || (rlwj.getRunningLength() > 0)) { + final boolean i_is_prey = rlwi.getRunningLength() < rlwj + .getRunningLength(); + final IteratingRLW prey = i_is_prey ? rlwi : rlwj; + final IteratingRLW predator = i_is_prey ? rlwj + : rlwi; + if (predator.getRunningBit() == false) { + container.addStreamOfEmptyWords(false, predator.getRunningLength()); + prey.discardFirstWords(predator.getRunningLength()); + predator.discardFirstWords(predator.getRunningLength()); + } else { + final long index = discharge(container, prey, predator.getRunningLength()); + container.addStreamOfEmptyWords(false, predator.getRunningLength() + - index); + predator.discardFirstWords(predator.getRunningLength()); + } + } + final int nbre_literal = Math.min(rlwi.getNumberOfLiteralWords(), + rlwj.getNumberOfLiteralWords()); + if (nbre_literal > 0) { + desiredrlwcount -= nbre_literal; + for (int k = 0; k < nbre_literal; ++k) + container.add(rlwi.getLiteralWordAt(k) & rlwj.getLiteralWordAt(k)); + rlwi.discardFirstWords(nbre_literal); + rlwj.discardFirstWords(nbre_literal); + } + } + } + + static void andToContainer(final BitmapStorage container, + final IteratingRLW rlwi, IteratingRLW rlwj) { + while ((rlwi.size()>0) && (rlwj.size()>0) ) { + while ((rlwi.getRunningLength() > 0) || (rlwj.getRunningLength() > 0)) { + final boolean i_is_prey = rlwi.getRunningLength() < rlwj + .getRunningLength(); + final IteratingRLW prey = i_is_prey ? rlwi : rlwj; + final IteratingRLW predator = i_is_prey ? rlwj + : rlwi; + if (predator.getRunningBit() == false) { + container.addStreamOfEmptyWords(false, predator.getRunningLength()); + prey.discardFirstWords(predator.getRunningLength()); + predator.discardFirstWords(predator.getRunningLength()); + } else { + final long index = discharge(container, prey, predator.getRunningLength()); + container.addStreamOfEmptyWords(false, predator.getRunningLength() + - index); + predator.discardFirstWords(predator.getRunningLength()); + } + } + final int nbre_literal = Math.min(rlwi.getNumberOfLiteralWords(), + rlwj.getNumberOfLiteralWords()); + if (nbre_literal > 0) { + for (int k = 0; k < nbre_literal; ++k) + container.add(rlwi.getLiteralWordAt(k) & rlwj.getLiteralWordAt(k)); + rlwi.discardFirstWords(nbre_literal); + rlwj.discardFirstWords(nbre_literal); + } + } + } + + + /** + * Compute the first few words of the XOR aggregate between two iterators. + * + * @param container where to write + * @param desiredrlwcount number of words to be written (max) + * @param rlwi first iterator to aggregate + * @param rlwj second iterator to aggregate + */ + public static void xorToContainer(final BitmapStorage container, + int desiredrlwcount, final IteratingRLW rlwi, final IteratingRLW rlwj) { + while ((rlwi.size()>0) && (rlwj.size()>0) && (desiredrlwcount-- >0) ) { + while ((rlwi.getRunningLength() > 0) || (rlwj.getRunningLength() > 0)) { + final boolean i_is_prey = rlwi.getRunningLength() < rlwj + .getRunningLength(); + final IteratingRLW prey = i_is_prey ? rlwi : rlwj; + final IteratingRLW predator = i_is_prey ? rlwj + : rlwi; + if (predator.getRunningBit() == false) { + long index = discharge(container, prey, predator.getRunningLength()); + container.addStreamOfEmptyWords(false, predator.getRunningLength() + - index); + predator.discardFirstWords(predator.getRunningLength()); + } else { + long index = dischargeNegated(container, prey, predator.getRunningLength()); + container.addStreamOfEmptyWords(true, predator.getRunningLength() + - index); + predator.discardFirstWords(predator.getRunningLength()); + } + } + final int nbre_literal = Math.min(rlwi.getNumberOfLiteralWords(), + rlwj.getNumberOfLiteralWords()); + if (nbre_literal > 0) { + desiredrlwcount -= nbre_literal; + for (int k = 0; k < nbre_literal; ++k) + container.add(rlwi.getLiteralWordAt(k) ^ rlwj.getLiteralWordAt(k)); + rlwi.discardFirstWords(nbre_literal); + rlwj.discardFirstWords(nbre_literal); + } + } + } + + protected static int inplaceor(long[] bitmap, + IteratingRLW i) { + + int pos = 0; + long s; + while ((s = i.size()) > 0) { + if (pos + s < bitmap.length) { + final int L = (int) i.getRunningLength(); + if (i.getRunningBit()) + Arrays.fill(bitmap, pos, pos + L, ~0l); + pos += L; + final int LR = i.getNumberOfLiteralWords(); + + for (int k = 0; k < LR; ++k) + bitmap[pos++] |= i.getLiteralWordAt(k); + if (!i.next()) { + return pos; + } + } else { + int howmany = bitmap.length - pos; + int L = (int) i.getRunningLength(); + + if (pos + L > bitmap.length) { + if (i.getRunningBit()) { + Arrays.fill(bitmap, pos, bitmap.length, ~0l); + } + i.discardFirstWords(howmany); + return bitmap.length; + } + if (i.getRunningBit()) + Arrays.fill(bitmap, pos, pos + L, ~0l); + pos += L; + for (int k = 0; pos < bitmap.length; ++k) + bitmap[pos++] |= i.getLiteralWordAt(k); + i.discardFirstWords(howmany); + return pos; + } + } + return pos; + } + + protected static int inplacexor(long[] bitmap, + IteratingRLW i) { + int pos = 0; + long s; + while ((s = i.size()) > 0) { + if (pos + s < bitmap.length) { + final int L = (int) i.getRunningLength(); + if (i.getRunningBit()) { + for(int k = pos ; k < pos + L; ++k) + bitmap[k] = ~bitmap[k]; + } + pos += L; + final int LR = i.getNumberOfLiteralWords(); + for (int k = 0; k < LR; ++k) + bitmap[pos++] ^= i.getLiteralWordAt(k); + if (!i.next()) { + return pos; + } + } else { + int howmany = bitmap.length - pos; + int L = (int) i.getRunningLength(); + if (pos + L > bitmap.length) { + if (i.getRunningBit()) { + for(int k = pos ; k < bitmap.length; ++k) + bitmap[k] = ~bitmap[k]; + } + i.discardFirstWords(howmany); + return bitmap.length; + } + if (i.getRunningBit()) + for(int k = pos ; k < pos + L; ++k) + bitmap[k] = ~bitmap[k]; + pos += L; + for (int k = 0; pos < bitmap.length; ++k) + bitmap[pos++] ^= i.getLiteralWordAt(k); + i.discardFirstWords(howmany); + return pos; + } + } + return pos; + } + protected static int inplaceand(long[] bitmap, + IteratingRLW i) { + int pos = 0; + long s; + while ((s = i.size()) > 0) { + if (pos + s < bitmap.length) { + final int L = (int) i.getRunningLength(); + if (!i.getRunningBit()) { + for(int k = pos ; k < pos + L; ++k) + bitmap[k] = 0; + } + pos += L; + final int LR = i.getNumberOfLiteralWords(); + for (int k = 0; k < LR; ++k) + bitmap[pos++] &= i.getLiteralWordAt(k); + if (!i.next()) { + return pos; + } + } else { + int howmany = bitmap.length - pos; + int L = (int) i.getRunningLength(); + if (pos + L > bitmap.length) { + if (!i.getRunningBit()) { + for(int k = pos ; k < bitmap.length; ++k) + bitmap[k] = 0; + } + i.discardFirstWords(howmany); + return bitmap.length; + } + if (!i.getRunningBit()) + for(int k = pos ; k < pos + L; ++k) + bitmap[k] = 0; + pos += L; + for (int k = 0; pos < bitmap.length; ++k) + bitmap[pos++] &= i.getLiteralWordAt(k); + i.discardFirstWords(howmany); + return pos; + } + } + return pos; + } + + /** + * An optimization option. Larger values may improve speed, but at + * the expense of memory. + */ + public final static int DEFAULTMAXBUFSIZE = 65536; +} +class BufferedORIterator implements CloneableIterator { + EWAHCompressedBitmap buffer = new EWAHCompressedBitmap(); + long[] hardbitmap; + LinkedList ll; + int buffersize; + + BufferedORIterator(LinkedList basell, int bufsize) { + this.ll = basell; + this.hardbitmap = new long[bufsize]; + } + + @Override + public BufferedXORIterator clone() throws CloneNotSupportedException { + BufferedXORIterator answer = (BufferedXORIterator) super.clone(); + answer.buffer = this.buffer.clone(); + answer.hardbitmap = this.hardbitmap.clone(); + answer.ll = (LinkedList) this.ll.clone(); + return answer; + } + + @Override + public boolean hasNext() { + return !this.ll.isEmpty(); + } + + @Override + public EWAHIterator next() { + this.buffer.clear(); + long effective = 0; + Iterator i = this.ll.iterator(); + while (i.hasNext()) { + IteratingRLW rlw = i.next(); + if (rlw.size() > 0) { + int eff = IteratorAggregation.inplaceor(this.hardbitmap, rlw); + if (eff > effective) + effective = eff; + } else + i.remove(); + } + for (int k = 0; k < effective; ++k) { + this.buffer.add(this.hardbitmap[k]); + } + + Arrays.fill(this.hardbitmap, 0); + return this.buffer.getEWAHIterator(); + } +} + +class BufferedXORIterator implements CloneableIterator { + EWAHCompressedBitmap buffer = new EWAHCompressedBitmap(); + long[] hardbitmap; + LinkedList ll; + int buffersize; + + BufferedXORIterator(LinkedList basell, int bufsize) { + this.ll = basell; + this.hardbitmap = new long[bufsize]; + } + + @Override + public BufferedXORIterator clone() throws CloneNotSupportedException { + BufferedXORIterator answer = (BufferedXORIterator) super.clone(); + answer.buffer = this.buffer.clone(); + answer.hardbitmap = this.hardbitmap.clone(); + answer.ll = (LinkedList) this.ll.clone(); + return answer; + } + + @Override + public boolean hasNext() { + return !this.ll.isEmpty(); + } + + @Override + public EWAHIterator next() { + this.buffer.clear(); + long effective = 0; + Iterator i = this.ll.iterator(); + while (i.hasNext()) { + IteratingRLW rlw = i.next(); + if (rlw.size() > 0) { + int eff = IteratorAggregation.inplacexor(this.hardbitmap, rlw); + if (eff > effective) + effective = eff; + } else + i.remove(); + } + for (int k = 0; k < effective; ++k) + this.buffer.add(this.hardbitmap[k]); + Arrays.fill(this.hardbitmap, 0); + return this.buffer.getEWAHIterator(); + } +} + + +class BufferedAndIterator implements CloneableIterator { + EWAHCompressedBitmap buffer = new EWAHCompressedBitmap(); + LinkedList ll; + int buffersize; + + public BufferedAndIterator(LinkedList basell, int bufsize) { + this.ll = basell; + this.buffersize = bufsize; + + } + + @Override + public boolean hasNext() { + return !this.ll.isEmpty(); + } + + @Override + public BufferedAndIterator clone() throws CloneNotSupportedException { + BufferedAndIterator answer = (BufferedAndIterator) super.clone(); + answer.buffer = this.buffer.clone(); + answer.ll = (LinkedList) this.ll.clone(); + return answer; + } + + @Override + public EWAHIterator next() { + this.buffer.clear(); + IteratorAggregation.andToContainer(this.buffer, this.buffersize * this.ll.size(), + this.ll.get(0), this.ll.get(1)); + if (this.ll.size() > 2) { + Iterator i = this.ll.iterator(); + i.next(); + i.next(); + EWAHCompressedBitmap tmpbuffer = new EWAHCompressedBitmap(); + while (i.hasNext() && this.buffer.sizeInBytes() > 0) { + IteratorAggregation.andToContainer(tmpbuffer, + this.buffer.getIteratingRLW(), i.next()); + this.buffer.swap(tmpbuffer); + tmpbuffer.clear(); + } + } + Iterator i = this.ll.iterator(); + while(i.hasNext()) { + if(i.next().size() == 0) { + this.ll.clear(); + break; + } + } + return this.buffer.getEWAHIterator(); + } + +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah/IteratorUtil.java b/fine-jgit/src/com/fr/third/googlecode/javaewah/IteratorUtil.java new file mode 100644 index 000000000..628ae46a8 --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah/IteratorUtil.java @@ -0,0 +1,132 @@ +package com.fr.third.googlecode.javaewah; + +import java.util.Iterator; + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ +/** + * Convenience functions for working over iterators + * + */ +public class IteratorUtil { + + /** + * @param i iterator we wish to iterate over + * @return an iterator over the set bits corresponding to the iterator + */ + public static IntIterator toSetBitsIntIterator(final IteratingRLW i) { + return new IntIteratorOverIteratingRLW(i); + } + + /** + * @param i iterator we wish to iterate over + * @return an iterator over the set bits corresponding to the iterator + */ + public static Iterator toSetBitsIterator(final IteratingRLW i) { + return new Iterator() { + @Override + public boolean hasNext() { + return this.under.hasNext(); + } + + @Override + public Integer next() { + return new Integer(this.under.next()); + } + + @Override + public void remove() { + } + + final private IntIterator under = toSetBitsIntIterator(i); + }; + + } + + /** + * Generate a bitmap from an iterator + * + * @param i iterator we wish to materialize + * @param c where we write + */ + public static void materialize(final IteratingRLW i, final BitmapStorage c) { + while (true) { + if (i.getRunningLength() > 0) { + c.addStreamOfEmptyWords(i.getRunningBit(), i.getRunningLength()); + } + for (int k = 0; k < i.getNumberOfLiteralWords(); ++k) + c.add(i.getLiteralWordAt(k)); + if (!i.next()) + break; + } + } + + /** + * @param i iterator we wish to iterate over + * @return the cardinality (number of set bits) corresponding to the iterator + */ + public static int cardinality(final IteratingRLW i) { + int answer = 0; + while (true) { + if(i.getRunningBit()) answer += i.getRunningLength() * EWAHCompressedBitmap.wordinbits; + for (int k = 0; k < i.getNumberOfLiteralWords(); ++k) + answer += Long.bitCount(i.getLiteralWordAt(k)); + if(!i.next()) break; + } + return answer; + } + + /** + * @param x set of bitmaps + * @return an array of iterators corresponding to the array of bitmaps + */ + public static IteratingRLW[] toIterators(final EWAHCompressedBitmap... x) { + IteratingRLW[] X = new IteratingRLW[x.length]; + for (int k = 0; k < X.length; ++k) { + X[k] = new IteratingBufferedRunningLengthWord(x[k]); + } + return X; + } + /** + * Turn an iterator into a bitmap. + * + * @param i iterator we wish to materialize + * @param c where we write + * @param Max maximum number of words we wish to materialize + * @return how many words were actually materialized + */ + public static long materialize(final IteratingRLW i, final BitmapStorage c, long Max) { + final long origMax = Max; + while (true) { + if (i.getRunningLength() > 0) { + long L = i.getRunningLength(); + if(L > Max) L = Max; + c.addStreamOfEmptyWords(i.getRunningBit(), L); + Max -= L; + } + long L = i.getNumberOfLiteralWords(); + for (int k = 0; k < L; ++k) + c.add(i.getLiteralWordAt(k)); + if(Max>0) { + if (!i.next()) + break; + } + else break; + } + return origMax - Max; + } + /** + * Turn an iterator into a bitmap + * + * @param i iterator we wish to materialize + * @return materialized version of the iterator + */ + public static EWAHCompressedBitmap materialize(final IteratingRLW i) { + EWAHCompressedBitmap ewah = new EWAHCompressedBitmap(); + materialize(i, ewah); + return ewah; + } + +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah/LogicalElement.java b/fine-jgit/src/com/fr/third/googlecode/javaewah/LogicalElement.java new file mode 100644 index 000000000..b6300410c --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah/LogicalElement.java @@ -0,0 +1,61 @@ +package com.fr.third.googlecode.javaewah; + +/** + * A prototypical model for bitmaps. Used by the + * class FastAggregation. Users should probably not + * be concerned by this class. + * + * @author Daniel Lemire + * @param the type of element (e.g., a bitmap class) + * + */ +public interface LogicalElement { + /** + * Compute the bitwise logical and + * @param le element + * @return the result of the operation + */ + public T and(T le); + + /** + * Compute the bitwise logical and not + * @param le element + * @return the result of the operation + */ + public T andNot(T le); + + /** + * Compute the bitwise logical not (in place) + */ + public void not(); + + + @SuppressWarnings({ "rawtypes", "javadoc" }) + /** + * Compute the bitwise logical or + * @param le another element + * @return the result of the operation + */ + public LogicalElement or(T le); + + /** + * How many logical bits does this element represent? + * + * @return the number of bits represented by this element + */ + public int sizeInBits(); + + /** + * Should report the storage requirement + * @return How many bytes + * @since 0.6.2 + */ + public int sizeInBytes(); + + /** + * Compute the bitwise logical Xor + * @param le element + * @return the results of the operation + */ + public T xor(T le); +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah/NonEmptyVirtualStorage.java b/fine-jgit/src/com/fr/third/googlecode/javaewah/NonEmptyVirtualStorage.java new file mode 100644 index 000000000..96ea6cfea --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah/NonEmptyVirtualStorage.java @@ -0,0 +1,92 @@ +package com.fr.third.googlecode.javaewah; + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ +/** + * This is a BitmapStorage that can be used to determine quickly if the result + * of an operation is non-trivial... that is, whether there will be at least on + * set bit. + * + * @since 0.4.2 + * @author Daniel Lemire and Veronika Zenz + * + */ +public class NonEmptyVirtualStorage implements BitmapStorage { + static class NonEmptyException extends RuntimeException { + private static final long serialVersionUID = 1L; + + /** + * Do not fill in the stack trace for this exception + * for performance reasons. + * + * @return this instance + * @see Throwable#fillInStackTrace() + */ + @Override + public synchronized Throwable fillInStackTrace() { + return this; + } + } + + private static final NonEmptyException nonEmptyException = new NonEmptyException(); + + /** + * If the word to be added is non-zero, a NonEmptyException exception is + * thrown. + * + * @see com.googlecode.javaewah.BitmapStorage#add(long) + */ + @Override +public void add(long newdata) { + if (newdata != 0) + throw nonEmptyException; + return; + } + + /** + * throws a NonEmptyException exception when number is greater than 0 + * + */ + @Override +public void addStreamOfLiteralWords(long[] data, int start, int number) { + if(number>0){ + throw nonEmptyException; + } + } + + /** + * If the boolean value is true and number is greater than 0, then it throws a NonEmptyException exception, + * otherwise, nothing happens. + * + * @see com.googlecode.javaewah.BitmapStorage#addStreamOfEmptyWords(boolean, long) + */ + @Override +public void addStreamOfEmptyWords(boolean v, long number) { + if (v && (number>0)) + throw nonEmptyException; + return; + } + + /** + * throws a NonEmptyException exception when number is greater than 0 + * + */ + @Override +public void addStreamOfNegatedLiteralWords(long[] data, int start, int number) { + if(number>0){ + throw nonEmptyException; + } + } + + /** + * Does nothing. + * + * @see com.googlecode.javaewah.BitmapStorage#setSizeInBits(int) + */ + @Override +public void setSizeInBits(int bits) { + } + +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah/RunningLengthWord.java b/fine-jgit/src/com/fr/third/googlecode/javaewah/RunningLengthWord.java new file mode 100644 index 000000000..969a78c48 --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah/RunningLengthWord.java @@ -0,0 +1,152 @@ +package com.fr.third.googlecode.javaewah; + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ + +/** + * Mostly for internal use. + * + * @since 0.1.0 + * @author Daniel Lemire + */ +public final class RunningLengthWord implements Cloneable { + + /** + * Instantiates a new running length word. + * + * @param a + * an array of 64-bit words + * @param p + * position in the array where the running length word is + * located. + */ + RunningLengthWord(final EWAHCompressedBitmap a, final int p) { + this.parent = a; + this.position = p; + } + + /** + * Gets the number of literal words. + * + * @return the number of literal words + */ + public int getNumberOfLiteralWords() { + return (int) (this.parent.buffer[this.position] >>> (1 + runninglengthbits)); + } + + /** + * Gets the running bit. + * + * @return the running bit + */ + public boolean getRunningBit() { + return (this.parent.buffer[this.position] & 1) != 0; + } + + /** + * Gets the running length. + * + * @return the running length + */ + public long getRunningLength() { + return (this.parent.buffer[this.position] >>> 1) + & largestrunninglengthcount; + } + + /** + * Sets the number of literal words. + * + * @param number + * the new number of literal words + */ + public void setNumberOfLiteralWords(final long number) { + this.parent.buffer[this.position] |= notrunninglengthplusrunningbit; + this.parent.buffer[this.position] &= (number << (runninglengthbits + 1)) + | runninglengthplusrunningbit; + } + + /** + * Sets the running bit. + * + * @param b + * the new running bit + */ + public void setRunningBit(final boolean b) { + if (b) + this.parent.buffer[this.position] |= 1l; + else + this.parent.buffer[this.position] &= ~1l; + } + + /** + * Sets the running length. + * + * @param number + * the new running length + */ + public void setRunningLength(final long number) { + this.parent.buffer[this.position] |= shiftedlargestrunninglengthcount; + this.parent.buffer[this.position] &= (number << 1) + | notshiftedlargestrunninglengthcount; + } + + /** + * Return the size in uncompressed words represented by this running + * length word. + * + * @return the size + */ + public long size() { + return getRunningLength() + getNumberOfLiteralWords(); + } + + /* + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "running bit = " + getRunningBit() + + " running length = " + getRunningLength() + + " number of lit. words " + getNumberOfLiteralWords(); + } + + @Override + public RunningLengthWord clone() throws CloneNotSupportedException { + RunningLengthWord answer; + answer = (RunningLengthWord) super.clone(); + answer.parent = this.parent; + answer.position = this.position; + return answer; + } + + /** The array of words. */ + public EWAHCompressedBitmap parent; + + /** The position in array. */ + public int position; + + /** + * number of bits dedicated to marking of the running length of clean + * words + */ + public static final int runninglengthbits = 32; + + private static final int literalbits = 64 - 1 - runninglengthbits; + + /** largest number of literal words in a run. */ + public static final int largestliteralcount = (1 << literalbits) - 1; + + /** largest number of clean words in a run */ + public static final long largestrunninglengthcount = (1l << runninglengthbits) - 1; + + private static final long runninglengthplusrunningbit = (1l << (runninglengthbits + 1)) - 1; + + private static final long shiftedlargestrunninglengthcount = largestrunninglengthcount << 1; + + private static final long notrunninglengthplusrunningbit = ~runninglengthplusrunningbit; + + private static final long notshiftedlargestrunninglengthcount = ~shiftedlargestrunninglengthcount; + +} \ No newline at end of file diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/Benchmark.java b/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/Benchmark.java new file mode 100644 index 000000000..e2ffb8fe4 --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/Benchmark.java @@ -0,0 +1,284 @@ +package com.fr.third.googlecode.javaewah.benchmark; + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ + +import java.text.DecimalFormat; +import java.util.Arrays; +import java.util.List; +import com.fr.third.googlecode.javaewah.EWAHCompressedBitmap; +import com.fr.third.googlecode.javaewah.FastAggregation; +import com.fr.third.googlecode.javaewah.IntIterator; +import com.fr.third.googlecode.javaewah.IteratingRLW; +import com.fr.third.googlecode.javaewah.IteratorAggregation; +import com.fr.third.googlecode.javaewah.IteratorUtil; + +/** + * This class is used to benchmark the performance EWAH. + * + * @author Daniel Lemire + */ +public class Benchmark { + + /** + * Compute the union between two sorted arrays + * @param set1 first sorted array + * @param set2 second sorted array + * @return merged array + */ + static public int[] unite2by2(final int[] set1, final int[] set2) { + int pos = 0; + int k1 = 0, k2 = 0; + if (0 == set1.length) + return Arrays.copyOf(set2, set2.length); + if (0 == set2.length) + return Arrays.copyOf(set1, set1.length); + int[] buffer = new int[set1.length + set2.length]; + while (true) { + if (set1[k1] < set2[k2]) { + buffer[pos++] = set1[k1]; + ++k1; + if (k1 >= set1.length) { + for (; k2 < set2.length; ++k2) + buffer[pos++] = set2[k2]; + break; + } + } else if (set1[k1] == set2[k2]) { + buffer[pos++] = set1[k1]; + ++k1; + ++k2; + if (k1 >= set1.length) { + for (; k2 < set2.length; ++k2) + buffer[pos++] = set2[k2]; + break; + } + if (k2 >= set2.length) { + for (; k1 < set1.length; ++k1) + buffer[pos++] = set1[k1]; + break; + } + } else {// if (set1[k1]>set2[k2]) { + buffer[pos++] = set2[k2]; + ++k2; + if (k2 >= set2.length) { + for (; k1 < set1.length; ++k1) + buffer[pos++] = set1[k1]; + break; + } + } + } + return Arrays.copyOf(buffer, pos); + } + + + @SuppressWarnings("javadoc") + public static void main(String args[]) { + //test(2, 24, 1); + test(100, 16, 1); + } + + @SuppressWarnings("javadoc") + public static void test(int N, int nbr, int repeat) { + DecimalFormat df = new DecimalFormat("0.###"); + ClusteredDataGenerator cdg = new ClusteredDataGenerator(); + for (int sparsity = 1; sparsity < 30 - nbr; sparsity += 2) { + long bogus = 0; + String line = ""; + long bef, aft; + line += sparsity; + int[][] data = new int[N][]; + int Max = (1 << (nbr + sparsity)); + System.out.println("# generating random data..."); + int[] inter = cdg.generateClustered(1 << (nbr/2), Max); + for (int k = 0; k < N; ++k) + data[k] = unite2by2(cdg.generateClustered(1 << nbr, Max),inter); + System.out.println("# generating random data... ok."); + // building + bef = System.currentTimeMillis(); + EWAHCompressedBitmap[] ewah = new EWAHCompressedBitmap[N]; + int size = 0; + for (int r = 0; r < repeat; ++r) { + size = 0; + for (int k = 0; k < N; ++k) { + ewah[k] = new EWAHCompressedBitmap(); + for (int x = 0; x < data[k].length; ++x) { + ewah[k].set(data[k][x]); + } + size += ewah[k].sizeInBytes(); + } + } + aft = System.currentTimeMillis(); + line += "\t" + size; + line += "\t" + df.format((aft - bef) / 1000.0); + // uncompressing + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + int[] array = ewah[k].toArray(); + bogus += array.length; + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + // uncompressing + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + int[] array = new int[ewah[k].cardinality()]; + int c = 0; + for (int x : ewah[k]) + array[c++] = x; + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + // uncompressing + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + List L = ewah[k].getPositions(); + int[] array = new int[L.size()]; + int c = 0; + for (int x : L) + array[c++] = x; + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + // uncompressing + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + IntIterator iter = ewah[k].intIterator(); + while (iter.hasNext()) { + bogus += iter.next(); + } + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + line += "\t\t\t"; + // logical or + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap ewahor = ewah[0]; + for (int j = 1; j < k + 1; ++j) { + ewahor = ewahor.or(ewah[j]); + } + bogus += ewahor.sizeInBits(); + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + // fast logical or + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap[] ewahcp = new EWAHCompressedBitmap[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = ewah[j]; + } + EWAHCompressedBitmap ewahor = EWAHCompressedBitmap + .or(ewahcp); + bogus += ewahor.sizeInBits(); + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + + // fast logical or + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap[] ewahcp = new EWAHCompressedBitmap[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = ewah[j]; + } + EWAHCompressedBitmap ewahor = FastAggregation.or(ewahcp); + bogus += ewahor.sizeInBits(); + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + // fast logical or + // run sanity check + for (int k = 0; k < N; ++k) { + IteratingRLW[] ewahcp = new IteratingRLW[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = ewah[j].getIteratingRLW(); + } + IteratingRLW ewahor = IteratorAggregation.bufferedor(ewahcp); + EWAHCompressedBitmap ewahorp = EWAHCompressedBitmap.or(Arrays.copyOf(ewah, k+1)); + EWAHCompressedBitmap mewahor = IteratorUtil.materialize(ewahor); + if(!ewahorp.equals(mewahor)) throw new RuntimeException("bug"); + } + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + IteratingRLW[] ewahcp = new IteratingRLW[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = ewah[j].getIteratingRLW(); + } + IteratingRLW ewahor = IteratorAggregation.bufferedor(ewahcp); + bogus += IteratorUtil.materialize(ewahor).sizeInBits(); + } + aft = System.currentTimeMillis(); + + line += "\t" + df.format((aft - bef) / 1000.0); + line += "\t\t\t"; + // logical and + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap ewahand = ewah[0]; + for (int j = 1; j < k + 1; ++j) { + ewahand = ewahand.and(ewah[j]); + } + bogus += ewahand.sizeInBits(); + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + // fast logical and + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap[] ewahcp = new EWAHCompressedBitmap[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = ewah[j]; + } + EWAHCompressedBitmap ewahand = EWAHCompressedBitmap + .and(ewahcp); + bogus += ewahand.sizeInBits(); + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + + for (int k = 0; k < N; ++k) { + IteratingRLW[] ewahcp = new IteratingRLW[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = ewah[j].getIteratingRLW(); + } + IteratingRLW ewahand = IteratorAggregation.bufferedand(ewahcp); + EWAHCompressedBitmap ewahandp = EWAHCompressedBitmap.and(Arrays.copyOf(ewah, k+1)); + EWAHCompressedBitmap mewahand = IteratorUtil.materialize(ewahand); + if(!ewahandp.equals(mewahand)) throw new RuntimeException("bug"); + } + // fast logical and + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + IteratingRLW[] ewahcp = new IteratingRLW[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = ewah[j].getIteratingRLW(); + } + IteratingRLW ewahand = IteratorAggregation.bufferedand(ewahcp); + bogus += IteratorUtil.materialize(ewahand).sizeInBits(); + } + aft = System.currentTimeMillis(); + + line += "\t" + df.format((aft - bef) / 1000.0); + + + System.out + .println("time for building, toArray(), Java iterator, intIterator,\t\t\t logical or (2-by-2), logical or (grouped), FastAggregation.or, iterator-based or, \t\t\t (2-by-2) and, logical and (grouped), iterator-based and"); + System.out.println(line); + System.out.println("# bogus =" + bogus); + } + } +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/Benchmark32.java b/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/Benchmark32.java new file mode 100644 index 000000000..b221b911c --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/Benchmark32.java @@ -0,0 +1,212 @@ +package com.fr.third.googlecode.javaewah.benchmark; + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ + +import java.text.DecimalFormat; +import java.util.List; +import com.fr.third.googlecode.javaewah32.EWAHCompressedBitmap32; +import com.fr.third.googlecode.javaewah.FastAggregation; +import com.fr.third.googlecode.javaewah.IntIterator; +import com.fr.third.googlecode.javaewah32.IteratingRLW32; +import com.fr.third.googlecode.javaewah32.IteratorAggregation32; +import com.fr.third.googlecode.javaewah32.IteratorUtil32; + +/** + * This class is used to benchmark the performance EWAH. + * + * @author Daniel Lemire + */ +public class Benchmark32 { + + @SuppressWarnings("javadoc") + public static void main(String args[]) { + test(100, 16, 1); +// test(2, 24, 1); + } + + @SuppressWarnings("javadoc") + public static void test(int N, int nbr, int repeat) { + DecimalFormat df = new DecimalFormat("0.###"); + ClusteredDataGenerator cdg = new ClusteredDataGenerator(); + for (int sparsity = 1; sparsity < 30 - nbr; sparsity += 2) { + long bogus = 0; + String line = ""; + long bef, aft; + line += sparsity; + int[][] data = new int[N][]; + int Max = (1 << (nbr + sparsity)); + System.out.println("# generating random data..."); + int[] inter = cdg.generateClustered(1 << (nbr/2), Max); + for (int k = 0; k < N; ++k) + data[k] = Benchmark.unite2by2(cdg.generateClustered(1 << nbr, Max),inter); + System.out.println("# generating random data... ok."); + // building + bef = System.currentTimeMillis(); + EWAHCompressedBitmap32[] ewah = new EWAHCompressedBitmap32[N]; + int size = 0; + for (int r = 0; r < repeat; ++r) { + size = 0; + for (int k = 0; k < N; ++k) { + ewah[k] = new EWAHCompressedBitmap32(); + for (int x = 0; x < data[k].length; ++x) { + ewah[k].set(data[k][x]); + } + size += ewah[k].sizeInBytes(); + } + } + aft = System.currentTimeMillis(); + line += "\t" + size; + line += "\t" + df.format((aft - bef) / 1000.0); + // uncompressing + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + int[] array = ewah[k].toArray(); + bogus += array.length; + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + // uncompressing + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + int[] array = new int[ewah[k].cardinality()]; + int c = 0; + for (int x : ewah[k]) + array[c++] = x; + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + // uncompressing + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + List L = ewah[k].getPositions(); + int[] array = new int[L.size()]; + int c = 0; + for (int x : L) + array[c++] = x; + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + // uncompressing + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + IntIterator iter = ewah[k].intIterator(); + while (iter.hasNext()) { + bogus += iter.next(); + } + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + line += "\t\t\t"; + // logical or + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap32 ewahor = ewah[0]; + for (int j = 1; j < k + 1; ++j) { + ewahor = ewahor.or(ewah[j]); + } + bogus += ewahor.sizeInBits(); + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + // fast logical or + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap32[] ewahcp = new EWAHCompressedBitmap32[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = ewah[j]; + } + EWAHCompressedBitmap32 ewahor = EWAHCompressedBitmap32 + .or(ewahcp); + bogus += ewahor.sizeInBits(); + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + + // fast logical or + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap32[] ewahcp = new EWAHCompressedBitmap32[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = ewah[j]; + } + EWAHCompressedBitmap32 ewahor = FastAggregation.or(ewahcp); + bogus += ewahor.sizeInBits(); + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + // fast logical or + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + IteratingRLW32[] ewahcp = new IteratingRLW32[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = ewah[j].getIteratingRLW(); + } + IteratingRLW32 ewahor = IteratorAggregation32.bufferedor(ewahcp); + bogus += IteratorUtil32.materialize(ewahor).sizeInBits(); + } + aft = System.currentTimeMillis(); + + line += "\t" + df.format((aft - bef) / 1000.0); + line += "\t\t\t"; + // logical and + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap32 ewahand = ewah[0]; + for (int j = 1; j < k + 1; ++j) { + ewahand = ewahand.and(ewah[j]); + } + bogus += ewahand.sizeInBits(); + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + // fast logical and + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap32[] ewahcp = new EWAHCompressedBitmap32[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = ewah[j]; + } + EWAHCompressedBitmap32 ewahand = EWAHCompressedBitmap32 + .and(ewahcp); + bogus += ewahand.sizeInBits(); + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + + + // fast logical and + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + IteratingRLW32[] ewahcp = new IteratingRLW32[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = ewah[j].getIteratingRLW(); + } + IteratingRLW32 ewahand = IteratorAggregation32.bufferedand(ewahcp); + bogus += IteratorUtil32.materialize(ewahand).sizeInBits(); + } + aft = System.currentTimeMillis(); + + line += "\t" + df.format((aft - bef) / 1000.0); + + + System.out + .println("time for building, toArray(), Java iterator, intIterator,\t\t\t logical or (2-by-2), logical or (grouped), FastAggregation.or, iterator-based or, \t\t\t (2-by-2) and, logical and (grouped), iterator-based and"); + System.out.println(line); + System.out.println("# bogus =" + bogus); + } + } +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/BenchmarkIntersection.java b/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/BenchmarkIntersection.java new file mode 100644 index 000000000..943c76bc6 --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/BenchmarkIntersection.java @@ -0,0 +1,130 @@ +package com.fr.third.googlecode.javaewah.benchmark; + +import java.text.DecimalFormat; +import com.fr.third.googlecode.javaewah.*; + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ +/** + * To benchmark the logical and (intersection) aggregate. + */ +public class BenchmarkIntersection { + + @SuppressWarnings("javadoc") + public static void main(String args[]) { + test(10, 18, 1); + } + + @SuppressWarnings({ "javadoc"}) + public static void test(int N, int nbr, int repeat) { + long bogus = 0; + + DecimalFormat df = new DecimalFormat("0.###"); + ClusteredDataGenerator cdg = new ClusteredDataGenerator(); + for (int sparsity = 1; sparsity < 30 - nbr; sparsity++) { + for (int times = 0; times < 2; ++times) { + String line = ""; + long bef, aft; + line += sparsity; + int[][] data = new int[N][]; + int Max = (1 << (nbr + sparsity)); + int[] inter = cdg.generateClustered(1 << (nbr/2), Max); + for (int k = 0; k < N; ++k) + data[k] = Benchmark.unite2by2(cdg.generateClustered(1 << nbr, Max),inter); + // building + EWAHCompressedBitmap[] ewah = new EWAHCompressedBitmap[N]; + for (int k = 0; k < N; ++k) { + ewah[k] = new EWAHCompressedBitmap(); + for (int x = 0; x < data[k].length; ++x) { + ewah[k].set(data[k][x]); + } + data[k] = null; + } + // sanity check + if (true) { + EWAHCompressedBitmap answer = ewah[0].and(ewah[1]); + for (int k = 2; k < ewah.length; ++k) + answer = answer.and(ewah[k]); + + EWAHCompressedBitmap ewahand = EWAHCompressedBitmap.and(ewah); + if (!answer.equals(ewahand)) + throw new RuntimeException( + "bug EWAHCompressedBitmap.and"); + EWAHCompressedBitmap ewahand2 = FastAggregation + .bufferedand(65536,ewah); + if (!ewahand.equals(ewahand2)) + throw new RuntimeException( + "bug FastAggregation.bufferedand "); + + } + + // logical or + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap ewahor = ewah[0]; + for (int j = 1; j < k + 1; ++j) { + ewahor = ewahor.and(ewah[j]); + } + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + + // fast logical or + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap[] ewahcp = new EWAHCompressedBitmap[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = ewah[j]; + } + EWAHCompressedBitmap ewahor = EWAHCompressedBitmap + .and(ewahcp); + bogus += ewahor.sizeInBits(); + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + // fast logical or + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap[] ewahcp = new EWAHCompressedBitmap[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = ewah[j]; + } + EWAHCompressedBitmap ewahor = FastAggregation + .bufferedand(65536,ewahcp); + bogus += ewahor.sizeInBits(); + } + aft = System.currentTimeMillis(); + + line += "\t" + df.format((aft - bef) / 1000.0); + // fast logical or + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + IteratingRLW[] ewahcp = new IteratingRLW[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = new IteratingBufferedRunningLengthWord( + ewah[j]); + } + IteratingRLW ewahor = IteratorAggregation.bufferedand(ewahcp); + int wordcounter = IteratorUtil.cardinality(ewahor); + bogus += wordcounter; + } + aft = System.currentTimeMillis(); + + line += "\t" + df.format((aft - bef) / 1000.0); + + System.out + .println("# times for: 2by2 EWAHCompressedBitmap.and bufferedand iterator-bufferedand"); + + System.out.println(line); + } + System.out.println("# bogus =" + bogus); + + } + } +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/BenchmarkIntersection32.java b/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/BenchmarkIntersection32.java new file mode 100644 index 000000000..a36ba889b --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/BenchmarkIntersection32.java @@ -0,0 +1,130 @@ +package com.fr.third.googlecode.javaewah.benchmark; + +import java.text.DecimalFormat; +import com.fr.third.googlecode.javaewah32.*; + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ +/** + * To benchmark the logical and (intersection) aggregate. + */ +public class BenchmarkIntersection32 { + + @SuppressWarnings("javadoc") + public static void main(String args[]) { + test(10, 18, 1); + } + + @SuppressWarnings({ "javadoc" }) + public static void test(int N, int nbr, int repeat) { + long bogus = 0; + + DecimalFormat df = new DecimalFormat("0.###"); + ClusteredDataGenerator cdg = new ClusteredDataGenerator(); + for (int sparsity = 1; sparsity < 30 - nbr; sparsity++) { + for (int times = 0; times < 2; ++times) { + String line = ""; + long bef, aft; + line += sparsity; + int[][] data = new int[N][]; + int Max = (1 << (nbr + sparsity)); + int[] inter = cdg.generateClustered(1 << (nbr/2), Max); + for (int k = 0; k < N; ++k) + data[k] = Benchmark.unite2by2(cdg.generateClustered(1 << nbr, Max),inter); + // building + EWAHCompressedBitmap32[] ewah = new EWAHCompressedBitmap32[N]; + for (int k = 0; k < N; ++k) { + ewah[k] = new EWAHCompressedBitmap32(); + for (int x = 0; x < data[k].length; ++x) { + ewah[k].set(data[k][x]); + } + data[k] = null; + } + // sanity check + if (true) { + EWAHCompressedBitmap32 answer = ewah[0].and(ewah[1]); + for (int k = 2; k < ewah.length; ++k) + answer = answer.and(ewah[k]); + + EWAHCompressedBitmap32 ewahand = EWAHCompressedBitmap32.and(ewah); + if (!answer.equals(ewahand)) + throw new RuntimeException( + "bug EWAHCompressedBitmap.and"); + EWAHCompressedBitmap32 ewahand2 = FastAggregation32 + .bufferedand(65536,ewah); + if (!ewahand.equals(ewahand2)) + throw new RuntimeException( + "bug FastAggregation.bufferedand "); + + } + + // logical or + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap32 ewahor = ewah[0]; + for (int j = 1; j < k + 1; ++j) { + ewahor = ewahor.and(ewah[j]); + } + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + + // fast logical or + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap32[] ewahcp = new EWAHCompressedBitmap32[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = ewah[j]; + } + EWAHCompressedBitmap32 ewahor = EWAHCompressedBitmap32 + .and(ewahcp); + bogus += ewahor.sizeInBits(); + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + // fast logical or + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap32[] ewahcp = new EWAHCompressedBitmap32[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = ewah[j]; + } + EWAHCompressedBitmap32 ewahor = FastAggregation32 + .bufferedand(65536,ewahcp); + bogus += ewahor.sizeInBits(); + } + aft = System.currentTimeMillis(); + + line += "\t" + df.format((aft - bef) / 1000.0); + // fast logical or + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + IteratingRLW32[] ewahcp = new IteratingRLW32[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = new IteratingBufferedRunningLengthWord32( + ewah[j]); + } + IteratingRLW32 ewahor = IteratorAggregation32.bufferedand(ewahcp); + int wordcounter = IteratorUtil32.cardinality(ewahor); + bogus += wordcounter; + } + aft = System.currentTimeMillis(); + + line += "\t" + df.format((aft - bef) / 1000.0); + + System.out + .println("# times for: 2by2 EWAHCompressedBitmap.and bufferedand iterator-bufferedand"); + + System.out.println(line); + } + System.out.println("# bogus =" + bogus); + + } + } +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/BenchmarkUnion.java b/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/BenchmarkUnion.java new file mode 100644 index 000000000..55453bbe8 --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/BenchmarkUnion.java @@ -0,0 +1,164 @@ +package com.fr.third.googlecode.javaewah.benchmark; + +import java.text.DecimalFormat; +import com.fr.third.googlecode.javaewah.*; + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ +/** + * To benchmark the logical or (union) aggregate. + */ +public class BenchmarkUnion { + + @SuppressWarnings("javadoc") + public static void main(String args[]) { + test(10, 18, 1); + } + + @SuppressWarnings({ "javadoc", "deprecation" }) + public static void test(int N, int nbr, int repeat) { + long bogus = 0; + + DecimalFormat df = new DecimalFormat("0.###"); + ClusteredDataGenerator cdg = new ClusteredDataGenerator(); + for (int sparsity = 1; sparsity < 30 - nbr; sparsity++) { + for (int times = 0; times < 2; ++times) { + String line = ""; + long bef, aft; + line += sparsity; + int[][] data = new int[N][]; + int Max = (1 << (nbr + sparsity)); + for (int k = 0; k < N; ++k) + data[k] = cdg.generateClustered(1 << nbr, Max); + // building + EWAHCompressedBitmap[] ewah = new EWAHCompressedBitmap[N]; + for (int k = 0; k < N; ++k) { + ewah[k] = new EWAHCompressedBitmap(); + for (int x = 0; x < data[k].length; ++x) { + ewah[k].set(data[k][x]); + } + data[k] = null; + } + // sanity check + if (true) { + EWAHCompressedBitmap answer = ewah[0].or(ewah[1]); + for (int k = 2; k < ewah.length; ++k) + answer = answer.or(ewah[k]); + + EWAHCompressedBitmap ewahor = EWAHCompressedBitmap.or(ewah); + if (!answer.equals(ewahor)) + throw new RuntimeException( + "bug EWAHCompressedBitmap.or"); + EWAHCompressedBitmap ewahor3 = FastAggregation.or(ewah); + if (!ewahor.equals(ewahor3)) + throw new RuntimeException("bug FastAggregation.or"); + EWAHCompressedBitmap ewahor2 = FastAggregation + .bufferedor(65536,ewah); + if (!ewahor.equals(ewahor2)) + throw new RuntimeException( + "bug FastAggregation.bufferedor "); + + } + + // logical or + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap ewahor = ewah[0]; + for (int j = 1; j < k + 1; ++j) { + ewahor = ewahor.or(ewah[j]); + } + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + + // fast logical or + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap[] ewahcp = new EWAHCompressedBitmap[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = ewah[j]; + } + EWAHCompressedBitmap ewahor = EWAHCompressedBitmap + .or(ewahcp); + bogus += ewahor.sizeInBits(); + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + + // fast logical or + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap[] ewahcp = new EWAHCompressedBitmap[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = ewah[j]; + } + EWAHCompressedBitmap ewahor = FastAggregation + .or(ewahcp); + bogus += ewahor.sizeInBits(); + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + + + // fast logical or + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap[] ewahcp = new EWAHCompressedBitmap[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = ewah[j]; + } + EWAHCompressedBitmap ewahor = FastAggregation + .bufferedor(65536,ewahcp); + bogus += ewahor.sizeInBits(); + } + aft = System.currentTimeMillis(); + + line += "\t" + df.format((aft - bef) / 1000.0); + // fast logical or + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap[] ewahcp = new EWAHCompressedBitmap[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = ewah[j]; + } + EWAHCompressedBitmap x = new EWAHCompressedBitmap(); + FastAggregation.legacy_orWithContainer(x, ewahcp); + bogus += x.sizeInBits(); + } + aft = System.currentTimeMillis(); + + line += "\t" + df.format((aft - bef) / 1000.0); + // fast logical or + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + IteratingRLW[] ewahcp = new IteratingRLW[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = new IteratingBufferedRunningLengthWord( + ewah[j]); + } + IteratingRLW ewahor = IteratorAggregation.bufferedor(ewahcp); + int wordcounter = IteratorUtil.cardinality(ewahor); + bogus += wordcounter; + } + aft = System.currentTimeMillis(); + + line += "\t" + df.format((aft - bef) / 1000.0); + + System.out + .println("# times for: 2by2 EWAHCompressedBitmap.or FastAggregation.or experimentalor bufferedor legacygroupedor iterator-bufferedor"); + + System.out.println(line); + } + System.out.println("# bogus =" + bogus); + + } + } +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/BenchmarkUnion32.java b/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/BenchmarkUnion32.java new file mode 100644 index 000000000..249354944 --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/BenchmarkUnion32.java @@ -0,0 +1,165 @@ +package com.fr.third.googlecode.javaewah.benchmark; + +import java.text.DecimalFormat; + +import com.fr.third.googlecode.javaewah.FastAggregation; +import com.fr.third.googlecode.javaewah32.*; + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ +/** + * To benchmark the logical or (union) aggregate. + */ +public class BenchmarkUnion32 { + + @SuppressWarnings("javadoc") + public static void main(String args[]) { + test(10, 18, 1); + } + + @SuppressWarnings({ "javadoc", "deprecation" }) + public static void test(int N, int nbr, int repeat) { + long bogus = 0; + + DecimalFormat df = new DecimalFormat("0.###"); + ClusteredDataGenerator cdg = new ClusteredDataGenerator(); + for (int sparsity = 1; sparsity < 30 - nbr; sparsity++) { + for (int times = 0; times < 2; ++times) { + String line = ""; + long bef, aft; + line += sparsity; + int[][] data = new int[N][]; + int Max = (1 << (nbr + sparsity)); + for (int k = 0; k < N; ++k) + data[k] = cdg.generateClustered(1 << nbr, Max); + // building + EWAHCompressedBitmap32[] ewah = new EWAHCompressedBitmap32[N]; + for (int k = 0; k < N; ++k) { + ewah[k] = new EWAHCompressedBitmap32(); + for (int x = 0; x < data[k].length; ++x) { + ewah[k].set(data[k][x]); + } + data[k] = null; + } + // sanity check + if(true){ + EWAHCompressedBitmap32 answer = ewah[0].or(ewah[1]); + for(int k = 2; k < ewah.length; ++k) + answer = answer.or(ewah[k]); + + EWAHCompressedBitmap32 ewahor = EWAHCompressedBitmap32 + .or(ewah); + if(!answer.equals(ewahor)) throw new RuntimeException("bug EWAHCompressedBitmap.or"); + EWAHCompressedBitmap32 ewahor3 = FastAggregation + .or(ewah); + if(!ewahor.equals(ewahor3)) throw new RuntimeException("bug FastAggregation.or"); + EWAHCompressedBitmap32 ewahor2 = FastAggregation32 + .bufferedor(65536,ewah); + if(!ewahor.equals(ewahor2)) throw new RuntimeException("bug FastAggregation.bufferedor "); + + } + + // logical or + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap32 ewahor = ewah[0]; + for (int j = 1; j < k + 1; ++j) { + ewahor = ewahor.or(ewah[j]); + } + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + + + // fast logical or + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap32[] ewahcp = new EWAHCompressedBitmap32[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = ewah[j]; + } + EWAHCompressedBitmap32 ewahor = EWAHCompressedBitmap32 + .or(ewahcp); + bogus += ewahor.sizeInBits(); + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + + // fast logical or + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap32[] ewahcp = new EWAHCompressedBitmap32[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = ewah[j]; + } + EWAHCompressedBitmap32 ewahor = FastAggregation + .or(ewahcp); + bogus += ewahor.sizeInBits(); + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + + + // fast logical or + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap32[] ewahcp = new EWAHCompressedBitmap32[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = ewah[j]; + } + EWAHCompressedBitmap32 ewahor = FastAggregation32 + .bufferedor(65536,ewahcp); + bogus += ewahor.sizeInBits(); + } + aft = System.currentTimeMillis(); + + line += "\t" + df.format((aft - bef) / 1000.0); + // fast logical or + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap32[] ewahcp = new EWAHCompressedBitmap32[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = ewah[j]; + } + EWAHCompressedBitmap32 x = new EWAHCompressedBitmap32(); + FastAggregation32.legacy_orWithContainer(x, ewahcp); + bogus += x.sizeInBits(); + } + aft = System.currentTimeMillis(); + + line += "\t" + df.format((aft - bef) / 1000.0); + // fast logical or + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + IteratingRLW32[] ewahcp = new IteratingRLW32[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = new IteratingBufferedRunningLengthWord32(ewah[j]); + } + IteratingRLW32 ewahor = IteratorAggregation32 + .bufferedor(ewahcp); + int wordcounter = IteratorUtil32.cardinality(ewahor); + bogus += wordcounter; + } + aft = System.currentTimeMillis(); + + line += "\t" + df.format((aft - bef) / 1000.0); + + + System.out + .println("# times for: 2by2 EWAHCompressedBitmap.or FastAggregation.or experimentalor bufferedor legacygroupedor iterator-bufferedor"); + + System.out.println(line); + } + System.out.println("# bogus =" + bogus); + + } + } +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/BenchmarkXOR.java b/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/BenchmarkXOR.java new file mode 100644 index 000000000..7f036d690 --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/BenchmarkXOR.java @@ -0,0 +1,134 @@ +package com.fr.third.googlecode.javaewah.benchmark; + +import java.text.DecimalFormat; +import com.fr.third.googlecode.javaewah.*; + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ +/** + * To benchmark the logical xor aggregate. + */ +public class BenchmarkXOR { + + @SuppressWarnings("javadoc") + public static void main(String args[]) { + //test(10, 18, 1); + test(2, 22, 1); + } + + @SuppressWarnings({ "javadoc" }) + public static void test(int N, int nbr, int repeat) { + long bogus = 0; + + DecimalFormat df = new DecimalFormat("0.###"); + ClusteredDataGenerator cdg = new ClusteredDataGenerator(); + for (int sparsity = 1; sparsity < 30 - nbr; sparsity++) { + for (int times = 0; times < 2; ++times) { + String line = ""; + long bef, aft; + line += sparsity; + int[][] data = new int[N][]; + int Max = (1 << (nbr + sparsity)); + for (int k = 0; k < N; ++k) + data[k] = cdg.generateClustered(1 << nbr, Max); + // building + EWAHCompressedBitmap[] ewah = new EWAHCompressedBitmap[N]; + for (int k = 0; k < N; ++k) { + ewah[k] = new EWAHCompressedBitmap(); + for (int x = 0; x < data[k].length; ++x) { + ewah[k].set(data[k][x]); + } + data[k] = null; + } + // sanity check + if (true) { + EWAHCompressedBitmap answer = ewah[0].xor(ewah[1]); + for (int k = 2; k < ewah.length; ++k) + answer = answer.xor(ewah[k]); + EWAHCompressedBitmap ewahor3 = FastAggregation.xor(ewah); + if (!answer.equals(ewahor3)) + throw new RuntimeException("bug FastAggregation.xor"); + EWAHCompressedBitmap ewahor2 = FastAggregation + .bufferedxor(65536,ewah); + if (!answer.equals(ewahor2)) + throw new RuntimeException( + "bug FastAggregation.bufferedxor "); + EWAHCompressedBitmap iwah = IteratorUtil.materialize(IteratorAggregation.bufferedxor(IteratorUtil.toIterators(ewah))); + if (!answer.equals(iwah)) + throw new RuntimeException( + "bug xor it "); + + + } + + // logical xor + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap ewahor = ewah[0]; + for (int j = 1; j < k + 1; ++j) { + ewahor = ewahor.xor(ewah[j]); + } + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + + // fast logical xor + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap[] ewahcp = new EWAHCompressedBitmap[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = ewah[j]; + } + EWAHCompressedBitmap ewahor = FastAggregation + .xor(ewahcp); + bogus += ewahor.sizeInBits(); + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + + + // fast logical xor + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap[] ewahcp = new EWAHCompressedBitmap[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = ewah[j]; + } + EWAHCompressedBitmap ewahor = FastAggregation + .bufferedxor(65536,ewahcp); + bogus += ewahor.sizeInBits(); + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + // fast logical xor + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + IteratingRLW[] ewahcp = new IteratingRLW[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = new IteratingBufferedRunningLengthWord( + ewah[j]); + } + IteratingRLW ewahor = IteratorAggregation.bufferedxor(ewahcp); + int wordcounter = IteratorUtil.cardinality(ewahor); + bogus += wordcounter; + } + aft = System.currentTimeMillis(); + + line += "\t" + df.format((aft - bef) / 1000.0); + + System.out + .println("# times for: 2by2 FastAggregation.xor bufferedxor iterator-based"); + + System.out.println(line); + } + System.out.println("# bogus =" + bogus); + + } + } +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/BenchmarkXOR32.java b/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/BenchmarkXOR32.java new file mode 100644 index 000000000..4fb057fab --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/BenchmarkXOR32.java @@ -0,0 +1,137 @@ +package com.fr.third.googlecode.javaewah.benchmark; + +import java.text.DecimalFormat; + +import com.fr.third.googlecode.javaewah.FastAggregation; +import com.fr.third.googlecode.javaewah32.*; + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ +/** + * To benchmark the logical xor aggregate. + */ +public class BenchmarkXOR32 { + + @SuppressWarnings("javadoc") + public static void main(String args[]) { + test(10, 18, 1); + //test(2, 22, 1); + } + + @SuppressWarnings({ "javadoc" }) + public static void test(int N, int nbr, int repeat) { + long bogus = 0; + + DecimalFormat df = new DecimalFormat("0.###"); + ClusteredDataGenerator cdg = new ClusteredDataGenerator(); + for (int sparsity = 1; sparsity < 30 - nbr; sparsity++) { + for (int times = 0; times < 2; ++times) { + String line = ""; + long bef, aft; + line += sparsity; + int[][] data = new int[N][]; + int Max = (1 << (nbr + sparsity)); + for (int k = 0; k < N; ++k) + data[k] = cdg.generateClustered(1 << nbr, Max); + // building + EWAHCompressedBitmap32[] ewah = new EWAHCompressedBitmap32[N]; + for (int k = 0; k < N; ++k) { + ewah[k] = new EWAHCompressedBitmap32(); + for (int x = 0; x < data[k].length; ++x) { + ewah[k].set(data[k][x]); + } + data[k] = null; + } + // sanity check + if (true) { + EWAHCompressedBitmap32 answer = ewah[0].xor(ewah[1]); + for (int k = 2; k < ewah.length; ++k) + answer = answer.xor(ewah[k]); + EWAHCompressedBitmap32 ewahor3 = FastAggregation.xor(ewah); + if (!answer.equals(ewahor3)) + throw new RuntimeException("bug FastAggregation.xor"); + EWAHCompressedBitmap32 ewahor2 = FastAggregation32 + .bufferedxor(65536,ewah); + if (!answer.equals(ewahor2)) + throw new RuntimeException( + "bug FastAggregation.bufferedxor "); + EWAHCompressedBitmap32 iwah = IteratorUtil32.materialize(IteratorAggregation32.bufferedxor(IteratorUtil32.toIterators(ewah))); + if (!answer.equals(iwah)) + throw new RuntimeException( + "bug xor it "); + + } + + // logical xor + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap32 ewahor = ewah[0]; + for (int j = 1; j < k + 1; ++j) { + ewahor = ewahor.xor(ewah[j]); + } + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + + // fast logical xor + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap32[] ewahcp = new EWAHCompressedBitmap32[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = ewah[j]; + } + EWAHCompressedBitmap32 ewahor = FastAggregation + .xor(ewahcp); + bogus += ewahor.sizeInBits(); + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + + + // fast logical xor + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + EWAHCompressedBitmap32[] ewahcp = new EWAHCompressedBitmap32[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = ewah[j]; + } + EWAHCompressedBitmap32 ewahor = FastAggregation32 + .bufferedxor(65536,ewahcp); + bogus += ewahor.sizeInBits(); + } + aft = System.currentTimeMillis(); + line += "\t" + df.format((aft - bef) / 1000.0); + + // fast logical xor + bef = System.currentTimeMillis(); + for (int r = 0; r < repeat; ++r) + for (int k = 0; k < N; ++k) { + IteratingRLW32[] ewahcp = new IteratingRLW32[k + 1]; + for (int j = 0; j < k + 1; ++j) { + ewahcp[j] = new IteratingBufferedRunningLengthWord32( + ewah[j]); + } + IteratingRLW32 ewahor = IteratorAggregation32.bufferedxor(ewahcp); + int wordcounter = IteratorUtil32.cardinality(ewahor); + bogus += wordcounter; + } + aft = System.currentTimeMillis(); + + line += "\t" + df.format((aft - bef) / 1000.0); + + + System.out + .println("# times for: 2by2 FastAggregation.xor bufferedxor iterator-based"); + + System.out.println(line); + } + System.out.println("# bogus =" + bogus); + + } + } +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/ClusteredDataGenerator.java b/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/ClusteredDataGenerator.java new file mode 100644 index 000000000..29078ca21 --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/ClusteredDataGenerator.java @@ -0,0 +1,78 @@ +package com.fr.third.googlecode.javaewah.benchmark; + + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ + + +/** + * This class will generate lists of random integers with a "clustered" distribution. + * Reference: + * Anh VN, Moffat A. Index compression using 64-bit words. Software: Practice and Experience 2010; 40(2):131-147. + * + * @author Daniel Lemire + */ +public class ClusteredDataGenerator { + + /** + * + */ +public ClusteredDataGenerator() { + this.unidg = new UniformDataGenerator(); + } + + /** + * @param seed random seed + */ +public ClusteredDataGenerator(final int seed) { + this.unidg = new UniformDataGenerator(seed); +} + +/** + * generates randomly N distinct integers from 0 to Max. + * @param N number of integers + * @param Max maximum integer value + * @return a randomly generated array + */ + public int[] generateClustered(int N, int Max) { + int[] array = new int[N]; + fillClustered(array, 0, N, 0, Max); + return array; + } + + void fillClustered(int[] array, int offset, int length, int Min, int Max) { + final int range = Max - Min; + if ((range == length) || (length <= 10)) { + fillUniform(array, offset, length, Min, Max); + return; + } + final int cut = length / 2 + + ((range - length - 1 > 0) ? this.unidg.rand.nextInt(range - length - 1) : 0); + final double p = this.unidg.rand.nextDouble(); + if (p < 0.25) { + fillUniform(array, offset, length / 2, Min, Min + cut); + fillClustered(array, offset + length / 2, length - length / 2, Min + cut, + Max); + } else if (p < 0.5) { + fillClustered(array, offset, length / 2, Min, Min + cut); + fillUniform(array, offset + length / 2, length - length / 2, Min + cut, + Max); + } else { + fillClustered(array, offset, length / 2, Min, Min + cut); + fillClustered(array, offset + length / 2, length - length / 2, Min + cut, + Max); + } + } + + void fillUniform(int[] array, int offset, int length, int Min, int Max) { + int[] v = this.unidg.generateUniform(length, Max - Min); + for (int k = 0; k < v.length; ++k) + array[k + offset] = Min + v[k]; + } + + UniformDataGenerator unidg; + +} + diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/UniformDataGenerator.java b/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/UniformDataGenerator.java new file mode 100644 index 000000000..6c91ea67e --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah/benchmark/UniformDataGenerator.java @@ -0,0 +1,114 @@ +package com.fr.third.googlecode.javaewah.benchmark; + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ + +import java.util.Arrays; +import java.util.BitSet; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Random; + +/** + * This class will generate "uniform" lists of random integers. + * + * @author Daniel Lemire + */ +public class UniformDataGenerator { + /** + * construct generator of random arrays. + */ + public UniformDataGenerator() { + this.rand = new Random(); + } + + /** + * @param seed random seed + */ + public UniformDataGenerator(final int seed) { + this.rand = new Random(seed); + } + + /** + * generates randomly N distinct integers from 0 to Max. + */ + int[] generateUniformHash(int N, int Max) { + if (N > Max) + throw new RuntimeException("not possible"); + int[] ans = new int[N]; + HashSet s = new HashSet(); + while (s.size() < N) + s.add(new Integer(this.rand.nextInt(Max))); + Iterator i = s.iterator(); + for (int k = 0; k < N; ++k) + ans[k] = i.next().intValue(); + Arrays.sort(ans); + return ans; + } + + /** + * output all integers from the range [0,Max) that are not + * in the array + */ + static int[] negate(int[] x, int Max) { + int[] ans = new int[Max - x.length]; + int i = 0; + int c = 0; + for (int j = 0; j < x.length; ++j) { + int v = x[j]; + for (; i < v; ++i) + ans[c++] = i; + ++i; + } + while (c < ans.length) + ans[c++] = i++; + return ans; + } + + + /** + * generates randomly N distinct integers from 0 to Max. + * @param N Number of integers to generate + * @param Max Maximum value of the integers + * @return array containing random integers + */ + public int[] generateUniform(int N, int Max) { + if(N * 2 > Max) { + return negate( generateUniform(Max - N, Max), Max ); + } + if (2048 * N > Max) + return generateUniformBitmap(N, Max); + return generateUniformHash(N, Max); + } + + /** + * generates randomly N distinct integers from 0 to Max using a bitmap. + * @param N Number of integers to generate + * @param Max Maximum value of the integers + * @return array containing random integers + */ + int[] generateUniformBitmap(int N, int Max) { + if (N > Max) + throw new RuntimeException("not possible"); + int[] ans = new int[N]; + BitSet bs = new BitSet(Max); + int cardinality = 0; + while (cardinality < N) { + int v = this.rand.nextInt(Max); + if (!bs.get(v)) { + bs.set(v); + cardinality++; + } + } + int pos = 0; + for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) { + ans[pos++] = i; + } + return ans; + } + + Random rand = new Random(); + +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah32/BitCounter32.java b/fine-jgit/src/com/fr/third/googlecode/javaewah32/BitCounter32.java new file mode 100644 index 000000000..53a9a0ee5 --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah32/BitCounter32.java @@ -0,0 +1,102 @@ +package com.fr.third.googlecode.javaewah32; + + + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc. and Veronika Zenz + * Licensed under the Apache License, Version 2.0. + */ +/** + * BitCounter is a fake bitset data structure. Instead of storing the actual data, + * it only records the number of set bits. + * + * @since 0.5.0 + * @author Daniel Lemire and David McIntosh + */ + +public final class BitCounter32 implements BitmapStorage32 { + + /** + * Virtually add words directly to the bitmap + * + * @param newdata the word + */ + // @Override : causes problems with Java 1.5 + @Override +public void add(final int newdata) { + this.oneBits += Integer.bitCount(newdata); + } + + + /** + * virtually add several literal words. + * + * @param data the literal words + * @param start the starting point in the array + * @param number the number of literal words to add + */ + // @Override : causes problems with Java 1.5 + @Override +public void addStreamOfLiteralWords(int[] data, int start, int number) { + for(int i=start;i iterator) { + this.masteriterator = iterator; + if(this.masteriterator.hasNext()) { + this.iterator = this.masteriterator.next(); + this.brlw = new BufferedRunningLengthWord32(this.iterator.next()); + this.literalWordStartPosition = this.iterator.literalWords() + this.brlw.literalwordoffset; + this.buffer = this.iterator.buffer(); + } + } + + + /** + * Discard first words, iterating to the next running length word if needed. + * + * @param x the number of words to be discarded + */ + @Override + public void discardFirstWords(int x) { + while (x > 0) { + if (this.brlw.RunningLength > x) { + this.brlw.RunningLength -= x; + return; + } + x -= this.brlw.RunningLength; + this.brlw.RunningLength = 0; + int toDiscard = x > this.brlw.NumberOfLiteralWords ? this.brlw.NumberOfLiteralWords : x; + + this.literalWordStartPosition += toDiscard; + this.brlw.NumberOfLiteralWords -= toDiscard; + x -= toDiscard; + if ((x > 0) || (this.brlw.size() == 0)) { + if (!this.next()) { + break; + } + } + } + } + /** + * Move to the next RunningLengthWord + * @return whether the move was possible + */ + @Override + public boolean next() { + if (!this.iterator.hasNext()) { + if(!reload()) { + this.brlw.NumberOfLiteralWords = 0; + this.brlw.RunningLength = 0; + return false; + } + } + this.brlw.reset(this.iterator.next()); + this.literalWordStartPosition = this.iterator.literalWords(); // + this.brlw.literalwordoffset ==0 + return true; + } + private boolean reload() { + if(!this.masteriterator.hasNext()) { + return false; + } + this.iterator = this.masteriterator.next(); + this.buffer = this.iterator.buffer(); + return true; + } + + + /** + * Get the nth literal word for the current running length word + * @param index zero based index + * @return the literal word + */ + @Override + public int getLiteralWordAt(int index) { + return this.buffer[this.literalWordStartPosition + index]; + } + + /** + * Gets the number of literal words for the current running length word. + * + * @return the number of literal words + */ + @Override + public int getNumberOfLiteralWords() { + return this.brlw.NumberOfLiteralWords; + } + + /** + * Gets the running bit. + * + * @return the running bit + */ + @Override + public boolean getRunningBit() { + return this.brlw.RunningBit; + } + + /** + * Gets the running length. + * + * @return the running length + */ + @Override + public int getRunningLength() { + return this.brlw.RunningLength; + } + + /** + * Size in uncompressed words of the current running length word. + * + * @return the size + */ + @Override + public int size() { + return this.brlw.size(); + } + + @Override + public BufferedIterator32 clone() throws CloneNotSupportedException { + BufferedIterator32 answer = (BufferedIterator32) super.clone(); + answer.brlw = this.brlw.clone(); + answer.buffer = this.buffer; + answer.iterator = this.iterator.clone(); + answer.literalWordStartPosition = this.literalWordStartPosition; + answer.masteriterator = this.masteriterator.clone(); + return answer; + } + + private BufferedRunningLengthWord32 brlw; + private int[] buffer; + private int literalWordStartPosition; + private EWAHIterator32 iterator; + private CloneableIterator masteriterator; + } \ No newline at end of file diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah32/BufferedRunningLengthWord32.java b/fine-jgit/src/com/fr/third/googlecode/javaewah32/BufferedRunningLengthWord32.java new file mode 100644 index 000000000..80b665e81 --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah32/BufferedRunningLengthWord32.java @@ -0,0 +1,174 @@ +package com.fr.third.googlecode.javaewah32; + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ + + + +/** + * Mostly for internal use. Similar to RunningLengthWord, but can + * be modified without access to the array, and has faster access. + * + * @author Daniel Lemire + * @since 0.5.0 + * + */ +public final class BufferedRunningLengthWord32 implements Cloneable { + + /** + * Instantiates a new buffered running length word. + * + * @param a the word + */ + public BufferedRunningLengthWord32(final int a) { + this.NumberOfLiteralWords = (a >>> (1 + RunningLengthWord32.runninglengthbits)); + this.RunningBit = (a & 1) != 0; + this.RunningLength = ((a >>> 1) & RunningLengthWord32.largestrunninglengthcount); + } + + /** + * Instantiates a new buffered running length word. + * + * @param rlw the rlw + */ + public BufferedRunningLengthWord32(final RunningLengthWord32 rlw) { + this(rlw.parent.buffer[rlw.position]); + } + + /** + * Discard first words. + * + * @param x the number of words to be discarded + */ + public void discardFirstWords(int x) { + if (this.RunningLength >= x) { + this.RunningLength -= x; + return; + } + x -= this.RunningLength; + this.RunningLength = 0; + this.literalwordoffset += x; + this.NumberOfLiteralWords -= x; + } + + /** + * Gets the number of literal words. + * + * @return the number of literal words + */ + public int getNumberOfLiteralWords() { + return this.NumberOfLiteralWords; + } + + /** + * Gets the running bit. + * + * @return the running bit + */ + public boolean getRunningBit() { + return this.RunningBit; + } + + /** + * Gets the running length. + * + * @return the running length + */ + public int getRunningLength() { + return this.RunningLength; + } + + /** + * Reset the values using the provided word. + * + * @param a the word + */ + public void reset(final int a) { + this.NumberOfLiteralWords = (a >>> (1 + RunningLengthWord32.runninglengthbits)); + this.RunningBit = (a & 1) != 0; + this.RunningLength = ((a >>> 1) & RunningLengthWord32.largestrunninglengthcount); + this.literalwordoffset = 0; + } + + /** + * Reset the values of this running length word so that it has the same values + * as the other running length word. + * + * @param rlw the other running length word + */ + public void reset(final RunningLengthWord32 rlw) { + reset(rlw.parent.buffer[rlw.position]); + } + + /** + * Sets the number of literal words. + * + * @param number the new number of literal words + */ + public void setNumberOfLiteralWords(final int number) { + this.NumberOfLiteralWords = number; + } + + /** + * Sets the running bit. + * + * @param b the new running bit + */ + public void setRunningBit(final boolean b) { + this.RunningBit = b; + } + + /** + * Sets the running length. + * + * @param number the new running length + */ + public void setRunningLength(final int number) { + this.RunningLength = number; + } + + /** + * Size in uncompressed words. + * + * @return the int + */ + public int size() { + return this.RunningLength + this.NumberOfLiteralWords; + } + + /* + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "running bit = " + getRunningBit() + " running length = " + + getRunningLength() + " number of lit. words " + + getNumberOfLiteralWords(); + } + + @Override +public BufferedRunningLengthWord32 clone() throws CloneNotSupportedException { + BufferedRunningLengthWord32 answer = (BufferedRunningLengthWord32) super.clone(); + answer.literalwordoffset = this.literalwordoffset; + answer.NumberOfLiteralWords = this.NumberOfLiteralWords; + answer.RunningBit = this.RunningBit; + answer.RunningLength = this.RunningLength; + return answer; + } + + /** how many literal words have we read so far? */ + public int literalwordoffset = 0; + + /** The Number of literal words. */ + public int NumberOfLiteralWords; + + /** The Running bit. */ + public boolean RunningBit; + + /** The Running length. */ + public int RunningLength; + + +} \ No newline at end of file diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah32/EWAHCompressedBitmap32.java b/fine-jgit/src/com/fr/third/googlecode/javaewah32/EWAHCompressedBitmap32.java new file mode 100644 index 000000000..56259ffee --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah32/EWAHCompressedBitmap32.java @@ -0,0 +1,1608 @@ +package com.fr.third.googlecode.javaewah32; + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ + +import java.util.*; +import java.io.*; + +import com.fr.third.googlecode.javaewah.IntIterator; +import com.fr.third.googlecode.javaewah.LogicalElement; + + +/** + *

+ * This implements the patent-free EWAH scheme. Roughly speaking, it is a 32-bit + * variant of the BBC compression scheme used by Oracle for its bitmap indexes. + *

+ * + *

+ * In contrast with the 64-bit EWAH scheme (javaewah.EWAHCompressedBitmap), you + * can expect this class to compress better, but to be slower at processing the + * data. In effect, there is a trade-off between memory usage and performances. + *

+ * + * @see com.fr.third.googlecode.javaewah.EWAHCompressedBitmap + * + *

+ * The objective of this compression type is to provide some compression, + * while reducing as much as possible the CPU cycle usage. + *

+ * + * + *

+ * For more details, see the following paper: + *

+ * + *
    + *
  • Daniel Lemire, Owen Kaser, Kamel Aouiche, Sorting improves + * word-aligned bitmap indexes. Data & Knowledge Engineering 69 (1), pages + * 3-28, 2010. http://arxiv.org/abs/0901.3751
  • + *
+ * + * @since 0.5.0 + */ +public final class EWAHCompressedBitmap32 implements Cloneable, Externalizable, + Iterable, BitmapStorage32, LogicalElement { + + /** + * Creates an empty bitmap (no bit set to true). + */ + public EWAHCompressedBitmap32() { + this.buffer = new int[defaultbuffersize]; + this.rlw = new RunningLengthWord32(this, 0); + } + + /** + * Sets explicitly the buffer size (in 32-bit words). The initial memory usage + * will be "buffersize * 32". For large poorly compressible bitmaps, using + * large values may improve performance. + * + * @param buffersize + * number of 32-bit words reserved when the object is created) + */ + public EWAHCompressedBitmap32(final int buffersize) { + this.buffer = new int[buffersize]; + this.rlw = new RunningLengthWord32(this, 0); + } + + /** + * Adding words directly to the bitmap (for expert use). + * + * This is normally how you add data to the array. So you add bits in streams + * of 4*8 bits. + * + * Example: if you add 321, you are have added (in binary notation) + * 0b101000001, so you have effectively called set(0), set(6), set(8) + * in sequence. + * + * @param newdata + * the word + */ + @Override +public void add(final int newdata) { + add(newdata, wordinbits); + } + + /** + * Adding words directly to the bitmap (for expert use). + * + * @param newdata + * the word + * @param bitsthatmatter + * the number of significant bits (by default it should be 32) + */ + public void add(final int newdata, final int bitsthatmatter) { + this.sizeinbits += bitsthatmatter; + if (newdata == 0) { + addEmptyWord(false); + } else if (newdata == ~0) { + addEmptyWord(true); + } else { + addLiteralWord(newdata); + } + } + + /** + * For internal use. + * + * @param v + * the boolean value + * @return the storage cost of the addition + */ + private int addEmptyWord(final boolean v) { + final boolean noliteralword = (this.rlw.getNumberOfLiteralWords() == 0); + final int runlen = this.rlw.getRunningLength(); + if ((noliteralword) && (runlen == 0)) { + this.rlw.setRunningBit(v); + } + if ((noliteralword) && (this.rlw.getRunningBit() == v) + && (runlen < RunningLengthWord32.largestrunninglengthcount)) { + this.rlw.setRunningLength(runlen + 1); + return 0; + } + push_back(0); + this.rlw.position = this.actualsizeinwords - 1; + this.rlw.setRunningBit(v); + this.rlw.setRunningLength(1); + return 1; + } + + /** + * For internal use. + * + * @param newdata + * the literal word + * @return the storage cost of the addition + */ + private int addLiteralWord(final int newdata) { + final int numbersofar = this.rlw.getNumberOfLiteralWords(); + if (numbersofar >= RunningLengthWord32.largestliteralcount) { + push_back(0); + this.rlw.position = this.actualsizeinwords - 1; + this.rlw.setNumberOfLiteralWords(1); + push_back(newdata); + return 2; + } + this.rlw.setNumberOfLiteralWords(numbersofar + 1); + push_back(newdata); + return 1; + } + + /** + * if you have several literal words to copy over, this might be faster. + * + * + * @param data + * the literal words + * @param start + * the starting point in the array + * @param number + * the number of literal words to add + */ + @Override +public void addStreamOfLiteralWords(final int[] data, final int start, + final int number) { + int leftovernumber = number; + while (leftovernumber > 0) { + final int NumberOfLiteralWords = this.rlw.getNumberOfLiteralWords(); + final int whatwecanadd = leftovernumber < RunningLengthWord32.largestliteralcount + - NumberOfLiteralWords ? leftovernumber + : RunningLengthWord32.largestliteralcount + - NumberOfLiteralWords; + this.rlw.setNumberOfLiteralWords(NumberOfLiteralWords + + whatwecanadd); + leftovernumber -= whatwecanadd; + push_back(data, start, whatwecanadd); + this.sizeinbits += whatwecanadd * wordinbits; + if (leftovernumber > 0) { + push_back(0); + this.rlw.position = this.actualsizeinwords - 1; + } + } + } + + /** + * For experts: You want to add many zeroes or ones? This is the method you + * use. + * + * @param v + * the boolean value + * @param number + * the number + */ + @Override +public void addStreamOfEmptyWords(final boolean v, int number) { + if (number == 0) + return; + this.sizeinbits += number * wordinbits; + if ((this.rlw.getRunningBit() != v) && (this.rlw.size() == 0)) { + this.rlw.setRunningBit(v); + } else if ((this.rlw.getNumberOfLiteralWords() != 0) + || (this.rlw.getRunningBit() != v)) { + push_back(0); + this.rlw.position = this.actualsizeinwords - 1; + if (v) + this.rlw.setRunningBit(v); + } + final int runlen = this.rlw.getRunningLength(); + final int whatwecanadd = number < RunningLengthWord32.largestrunninglengthcount + - runlen ? number : RunningLengthWord32.largestrunninglengthcount + - runlen; + this.rlw.setRunningLength(runlen + whatwecanadd); + number -= whatwecanadd; + while (number >= RunningLengthWord32.largestrunninglengthcount) { + push_back(0); + this.rlw.position = this.actualsizeinwords - 1; + if (v) + this.rlw.setRunningBit(v); + this.rlw.setRunningLength(RunningLengthWord32.largestrunninglengthcount); + number -= RunningLengthWord32.largestrunninglengthcount; + } + if (number > 0) { + push_back(0); + this.rlw.position = this.actualsizeinwords - 1; + if (v) + this.rlw.setRunningBit(v); + this.rlw.setRunningLength(number); + } + } + + /** + * Same as addStreamOfLiteralWords, but the words are negated. + * + * @param data + * the literal words + * @param start + * the starting point in the array + * @param number + * the number of literal words to add + */ + @Override +public void addStreamOfNegatedLiteralWords(final int[] data, final int start, + final int number) { + int leftovernumber = number; + while (leftovernumber > 0) { + final int NumberOfLiteralWords = this.rlw.getNumberOfLiteralWords(); + final int whatwecanadd = leftovernumber < RunningLengthWord32.largestliteralcount + - NumberOfLiteralWords ? leftovernumber + : RunningLengthWord32.largestliteralcount + - NumberOfLiteralWords; + this.rlw.setNumberOfLiteralWords(NumberOfLiteralWords + + whatwecanadd); + leftovernumber -= whatwecanadd; + negative_push_back(data, start, whatwecanadd); + this.sizeinbits += whatwecanadd * wordinbits; + if (leftovernumber > 0) { + push_back(0); + this.rlw.position = this.actualsizeinwords - 1; + } + } + } + + /** + * Returns a new compressed bitmap containing the bitwise AND values of the + * current bitmap with some other bitmap. + * + * The running time is proportional to the sum of the compressed sizes (as + * reported by sizeInBytes()). + * + * If you are not planning on adding to the resulting bitmap, you may call the trim() + * method to reduce memory usage. + * + * @param a + * the other bitmap + * @return the EWAH compressed bitmap + */ + @Override +public EWAHCompressedBitmap32 and(final EWAHCompressedBitmap32 a) { + final EWAHCompressedBitmap32 container = new EWAHCompressedBitmap32(); + container + .reserve(this.actualsizeinwords > a.actualsizeinwords ? this.actualsizeinwords + : a.actualsizeinwords); + andToContainer(a, container); + return container; + } + + /** + * Computes new compressed bitmap containing the bitwise AND values of the + * current bitmap with some other bitmap. + * + * The running time is proportional to the sum of the compressed sizes (as + * reported by sizeInBytes()). + * + * @param a + * the other bitmap + * @param container + * where we store the result + */ + /** + * Computes new compressed bitmap containing the bitwise AND values of the + * current bitmap with some other bitmap. + * + * The running time is proportional to the sum of the compressed sizes (as + * reported by sizeInBytes()). + * + * @since 0.4.0 + * @param a + * the other bitmap + * @param container + * where we store the result + */ + public void andToContainer(final EWAHCompressedBitmap32 a, final BitmapStorage32 container) { + final EWAHIterator32 i = a.getEWAHIterator(); + final EWAHIterator32 j = getEWAHIterator(); + final IteratingBufferedRunningLengthWord32 rlwi = new IteratingBufferedRunningLengthWord32(i); + final IteratingBufferedRunningLengthWord32 rlwj = new IteratingBufferedRunningLengthWord32(j); + while ((rlwi.size()>0) && (rlwj.size()>0)) { + while ((rlwi.getRunningLength() > 0) || (rlwj.getRunningLength() > 0)) { + final boolean i_is_prey = rlwi.getRunningLength() < rlwj + .getRunningLength(); + final IteratingBufferedRunningLengthWord32 prey = i_is_prey ? rlwi + : rlwj; + final IteratingBufferedRunningLengthWord32 predator = i_is_prey ? rlwj + : rlwi; + if (predator.getRunningBit() == false) { + container.addStreamOfEmptyWords(false, predator.getRunningLength()); + prey.discardFirstWords(predator.getRunningLength()); + predator.discardFirstWords(predator.getRunningLength()); + } else { + final int index = prey.discharge(container, predator.getRunningLength()); + container.addStreamOfEmptyWords(false, predator.getRunningLength() + - index); + predator.discardFirstWords(predator.getRunningLength()); + } + } + final int nbre_literal = Math.min(rlwi.getNumberOfLiteralWords(), + rlwj.getNumberOfLiteralWords()); + if (nbre_literal > 0) { + for (int k = 0; k < nbre_literal; ++k) + container.add(rlwi.getLiteralWordAt(k) & rlwj.getLiteralWordAt(k)); + rlwi.discardFirstWords(nbre_literal); + rlwj.discardFirstWords(nbre_literal); + } + } + if (adjustContainerSizeWhenAggregating) { + final boolean i_remains = rlwi.size() > 0; + final IteratingBufferedRunningLengthWord32 remaining = i_remains ? rlwi + : rlwj; + remaining.dischargeAsEmpty(container); + container.setSizeInBits(Math.max(sizeInBits(), a.sizeInBits())); + } + } + + + /** + * Returns the cardinality of the result of a bitwise AND of the values of the + * current bitmap with some other bitmap. Avoids needing to allocate an + * intermediate bitmap to hold the result of the OR. + * + * @param a + * the other bitmap + * @return the cardinality + */ + public int andCardinality(final EWAHCompressedBitmap32 a) { + final BitCounter32 counter = new BitCounter32(); + andToContainer(a, counter); + return counter.getCount(); + } + + /** + * Returns a new compressed bitmap containing the bitwise AND NOT values of + * the current bitmap with some other bitmap. + * + * The running time is proportional to the sum of the compressed sizes (as + * reported by sizeInBytes()). + * + * If you are not planning on adding to the resulting bitmap, you may call the trim() + * method to reduce memory usage. + * + * @param a + * the other bitmap + * @return the EWAH compressed bitmap + */ + @Override +public EWAHCompressedBitmap32 andNot(final EWAHCompressedBitmap32 a) { + final EWAHCompressedBitmap32 container = new EWAHCompressedBitmap32(); + container + .reserve(this.actualsizeinwords > a.actualsizeinwords ? this.actualsizeinwords + : a.actualsizeinwords); + andNotToContainer(a, container); + return container; + } + + /** + * Returns a new compressed bitmap containing the bitwise AND NOT values of + * the current bitmap with some other bitmap. + * + * The running time is proportional to the sum of the compressed sizes (as + * reported by sizeInBytes()). + * + * @param a the other bitmap + * @param container where we store the result + */ + public void andNotToContainer(final EWAHCompressedBitmap32 a, + final BitmapStorage32 container) { + final EWAHIterator32 i = getEWAHIterator(); + final EWAHIterator32 j = a.getEWAHIterator(); + final IteratingBufferedRunningLengthWord32 rlwi = new IteratingBufferedRunningLengthWord32(i); + final IteratingBufferedRunningLengthWord32 rlwj = new IteratingBufferedRunningLengthWord32(j); + while ((rlwi.size()>0) && (rlwj.size()>0)) { + while ((rlwi.getRunningLength() > 0) || (rlwj.getRunningLength() > 0)) { + final boolean i_is_prey = rlwi.getRunningLength() < rlwj + .getRunningLength(); + final IteratingBufferedRunningLengthWord32 prey = i_is_prey ? rlwi : rlwj; + final IteratingBufferedRunningLengthWord32 predator = i_is_prey ? rlwj + : rlwi; + if ( ((predator.getRunningBit() == true) && (i_is_prey)) + || ((predator.getRunningBit() == false) && (!i_is_prey))){ + container.addStreamOfEmptyWords(false, predator.getRunningLength()); + prey.discardFirstWords(predator.getRunningLength()); + predator.discardFirstWords(predator.getRunningLength()); + } else if (i_is_prey) { + int index = prey.discharge(container, predator.getRunningLength()); + container.addStreamOfEmptyWords(false, predator.getRunningLength() + - index); + predator.discardFirstWords(predator.getRunningLength()); + } else { + int index = prey.dischargeNegated(container, predator.getRunningLength()); + container.addStreamOfEmptyWords(true, predator.getRunningLength() + - index); + predator.discardFirstWords(predator.getRunningLength()); + } + } + final int nbre_literal = Math.min(rlwi.getNumberOfLiteralWords(), + rlwj.getNumberOfLiteralWords()); + if (nbre_literal > 0) { + for (int k = 0; k < nbre_literal; ++k) + container.add(rlwi.getLiteralWordAt(k) & (~rlwj.getLiteralWordAt(k))); + rlwi.discardFirstWords(nbre_literal); + rlwj.discardFirstWords(nbre_literal); + } + } + final boolean i_remains = rlwi.size()>0; + final IteratingBufferedRunningLengthWord32 remaining = i_remains ? rlwi : rlwj; + if(i_remains) + remaining.discharge(container); + else if (adjustContainerSizeWhenAggregating) + remaining.dischargeAsEmpty(container); + if (adjustContainerSizeWhenAggregating) + container.setSizeInBits(Math.max(sizeInBits(), a.sizeInBits())); + + } + + /** + * Returns the cardinality of the result of a bitwise AND NOT of the values of + * the current bitmap with some other bitmap. Avoids needing to allocate an + * intermediate bitmap to hold the result of the OR. + * + * @param a + * the other bitmap + * @return the cardinality + */ + public int andNotCardinality(final EWAHCompressedBitmap32 a) { + final BitCounter32 counter = new BitCounter32(); + andNotToContainer(a, counter); + return counter.getCount(); + } + + /** + * reports the number of bits set to true. Running time is proportional to + * compressed size (as reported by sizeInBytes). + * + * @return the number of bits set to true + */ + public int cardinality() { + int counter = 0; + final EWAHIterator32 i = new EWAHIterator32(this, + this.actualsizeinwords); + while (i.hasNext()) { + RunningLengthWord32 localrlw = i.next(); + if (localrlw.getRunningBit()) { + counter += wordinbits * localrlw.getRunningLength(); + } + for (int j = 0; j < localrlw.getNumberOfLiteralWords(); ++j) { + counter += Integer.bitCount(i.buffer()[i.literalWords() + j]); + } + } + return counter; + } + + /** + * Clear any set bits and set size in bits back to 0 + */ + public void clear() { + this.sizeinbits = 0; + this.actualsizeinwords = 1; + this.rlw.position = 0; + // buffer is not fully cleared but any new set operations should overwrite + // stale data + this.buffer[0] = 0; + } + + /* + * @see java.lang.Object#clone() + */ + @Override + public EWAHCompressedBitmap32 clone() throws CloneNotSupportedException { + final EWAHCompressedBitmap32 clone = (EWAHCompressedBitmap32) super.clone(); + clone.buffer = this.buffer.clone(); + clone.actualsizeinwords = this.actualsizeinwords; + clone.sizeinbits = this.sizeinbits; + return clone; + } + + /** + * Deserialize. + * + * @param in + * the DataInput stream + * @throws IOException + * Signals that an I/O exception has occurred. + */ + public void deserialize(DataInput in) throws IOException { + this.sizeinbits = in.readInt(); + this.actualsizeinwords = in.readInt(); + if (this.buffer.length < this.actualsizeinwords) { + this.buffer = new int[this.actualsizeinwords]; + } + for (int k = 0; k < this.actualsizeinwords; ++k) + this.buffer[k] = in.readInt(); + this.rlw = new RunningLengthWord32(this, in.readInt()); + } + + /** + * Check to see whether the two compressed bitmaps contain the same set bits. + * + * @see Object#equals(Object) + */ + @Override + public boolean equals(Object o) { + if (o instanceof EWAHCompressedBitmap32) { + try { + this.xorToContainer((EWAHCompressedBitmap32) o, new NonEmptyVirtualStorage32()); + return true; + } catch (NonEmptyVirtualStorage32.NonEmptyException e) { + return false; + } + } + return false; + } + + /** + * For experts: You want to add many zeroes or ones faster? + * + * This method does not update sizeinbits. + * + * @param v + * the boolean value + * @param number + * the number (must be greater than 0) + */ + private void fastaddStreamOfEmptyWords(final boolean v, int number) { + if ((this.rlw.getRunningBit() != v) && (this.rlw.size() == 0)) { + this.rlw.setRunningBit(v); + } else if ((this.rlw.getNumberOfLiteralWords() != 0) + || (this.rlw.getRunningBit() != v)) { + push_back(0); + this.rlw.position = this.actualsizeinwords - 1; + if (v) + this.rlw.setRunningBit(v); + } + final int runlen = this.rlw.getRunningLength(); + final int whatwecanadd = number < RunningLengthWord32.largestrunninglengthcount + - runlen ? number : RunningLengthWord32.largestrunninglengthcount + - runlen; + this.rlw.setRunningLength(runlen + whatwecanadd); + number -= whatwecanadd; + while (number >= RunningLengthWord32.largestrunninglengthcount) { + push_back(0); + this.rlw.position = this.actualsizeinwords - 1; + if (v) + this.rlw.setRunningBit(v); + this.rlw.setRunningLength(RunningLengthWord32.largestrunninglengthcount); + number -= RunningLengthWord32.largestrunninglengthcount; + } + if (number > 0) { + push_back(0); + this.rlw.position = this.actualsizeinwords - 1; + if (v) + this.rlw.setRunningBit(v); + this.rlw.setRunningLength(number); + } + } + + /** + * Gets an EWAHIterator over the data. This is a customized iterator which + * iterates over run length word. For experts only. + * + * @return the EWAHIterator + */ + public EWAHIterator32 getEWAHIterator() { + return new EWAHIterator32(this, this.actualsizeinwords); + } + + /** + * @return the IteratingRLW iterator corresponding to this bitmap + */ + public IteratingRLW32 getIteratingRLW() { + return new IteratingBufferedRunningLengthWord32(this); + } + + /** + * get the locations of the true values as one vector. (may use more memory + * than iterator()) + * + * @return the positions + */ + public List getPositions() { + final ArrayList v = new ArrayList(); + final EWAHIterator32 i = new EWAHIterator32(this, + this.actualsizeinwords); + int pos = 0; + while (i.hasNext()) { + RunningLengthWord32 localrlw = i.next(); + if (localrlw.getRunningBit()) { + for (int j = 0; j < localrlw.getRunningLength(); ++j) { + for (int c = 0; c < wordinbits; ++c) + v.add(new Integer(pos++)); + } + } else { + pos += wordinbits * localrlw.getRunningLength(); + } + for (int j = 0; j < localrlw.getNumberOfLiteralWords(); ++j) { + int data = i.buffer()[i.literalWords() + j]; + while (data != 0) { + final int ntz = Integer.numberOfTrailingZeros(data); + data ^= (1 << ntz); + v.add(new Integer(ntz + pos)); + } + pos += wordinbits; + } + } + while ((v.size() > 0) + && (v.get(v.size() - 1).intValue() >= this.sizeinbits)) + v.remove(v.size() - 1); + return v; + } + + /** + * Returns a customized hash code (based on Karp-Rabin). Naturally, if the + * bitmaps are equal, they will hash to the same value. + * + */ + @Override + public int hashCode() { + int karprabin = 0; + final int B = 31; + final EWAHIterator32 i = new EWAHIterator32(this, + this.actualsizeinwords); + while( i.hasNext() ) { + i.next(); + if (i.rlw.getRunningBit() == true) { + karprabin += B * karprabin + i.rlw.getRunningLength(); + } + for (int k = 0; k < i.rlw.getNumberOfLiteralWords(); ++k) { + karprabin += B * karprabin + this.buffer[k + i.literalWords()]; + } + } + return karprabin; + } + + /** + * Return true if the two EWAHCompressedBitmap have both at least one true bit + * in the same position. Equivalently, you could call "and" and check whether + * there is a set bit, but intersects will run faster if you don't need the + * result of the "and" operation. + * + * @param a + * the other bitmap + * @return whether they intersect + */ + public boolean intersects(final EWAHCompressedBitmap32 a) { + NonEmptyVirtualStorage32 nevs = new NonEmptyVirtualStorage32(); + try { + this.andToContainer(a, nevs); + } catch (NonEmptyVirtualStorage32.NonEmptyException nee) { + return true; + } + return false; + } + + /** + * Iterator over the set bits (this is what most people will want to use to + * browse the content if they want an iterator). The location of the set bits + * is returned, in increasing order. + * + * @return the int iterator + */ + public IntIterator intIterator() { + return new IntIteratorImpl32( + new EWAHIterator32(this, this.actualsizeinwords)); + } + + /** + * iterate over the positions of the true values. This is similar to + * intIterator(), but it uses Java generics. + * + * @return the iterator + */ + @Override +public Iterator iterator() { + return new Iterator() { + @Override + public boolean hasNext() { + return this.under.hasNext(); + } + + @Override + public Integer next() { + return new Integer(this.under.next()); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("bitsets do not support remove"); + } + + final private IntIterator under = intIterator(); + }; + } + + /** + * For internal use. + * + * @param data + * the array of words to be added + * @param start + * the starting point + * @param number + * the number of words to add + */ + private void negative_push_back(final int[] data, final int start, + final int number) { + while (this.actualsizeinwords + number >= this.buffer.length) { + final int oldbuffer[] = this.buffer; + if(this.actualsizeinwords + number < 32768) + this.buffer = new int[(this.actualsizeinwords + number) * 2]; + else if ((this.actualsizeinwords + number) * 3 / 2 < this.actualsizeinwords + number) + this.buffer = new int[Integer.MAX_VALUE]; + else + this.buffer = new int[(this.actualsizeinwords + number) * 3 / 2]; + System.arraycopy(oldbuffer, 0, this.buffer, 0, oldbuffer.length); + this.rlw.parent.buffer = this.buffer; + } + for (int k = 0; k < number; ++k) + this.buffer[this.actualsizeinwords + k] = ~data[start + k]; + this.actualsizeinwords += number; + } + + /** + * Negate (bitwise) the current bitmap. To get a negated copy, do + * EWAHCompressedBitmap x= ((EWAHCompressedBitmap) mybitmap.clone()); x.not(); + * + * The running time is proportional to the compressed size (as reported by + * sizeInBytes()). + * + */ + @Override +public void not() { + final EWAHIterator32 i = new EWAHIterator32(this, + this.actualsizeinwords); + if (!i.hasNext()) + return; + while (true) { + final RunningLengthWord32 rlw1 = i.next(); + rlw1.setRunningBit(!rlw1.getRunningBit()); + for (int j = 0; j < rlw1.getNumberOfLiteralWords(); ++j) { + i.buffer()[i.literalWords() + j] = ~i.buffer()[i.literalWords() + j]; + } + if (!i.hasNext()) {// must potentially adjust the last literal word + final int usedbitsinlast = this.sizeinbits % wordinbits; + if (usedbitsinlast == 0) + return; + + if (rlw1.getNumberOfLiteralWords() == 0) { + if((rlw1.getRunningLength()>0) && (rlw1.getRunningBit())) { + rlw1.setRunningLength(rlw1.getRunningLength()-1); + this.addLiteralWord((~0) >>> (wordinbits - usedbitsinlast)); + } + return; + } + i.buffer()[i.literalWords() + rlw1.getNumberOfLiteralWords() - 1] &= ((~0) >>> (wordinbits - usedbitsinlast)); + return; + } + + } + } + + + /** + * Returns a new compressed bitmap containing the bitwise OR values of the + * current bitmap with some other bitmap. + * + * The running time is proportional to the sum of the compressed sizes (as + * reported by sizeInBytes()). + * + * If you are not planning on adding to the resulting bitmap, you may call the trim() + * method to reduce memory usage. + * + * @param a + * the other bitmap + * @return the EWAH compressed bitmap + */ + @Override +public EWAHCompressedBitmap32 or(final EWAHCompressedBitmap32 a) { + final EWAHCompressedBitmap32 container = new EWAHCompressedBitmap32(); + container.reserve(this.actualsizeinwords + a.actualsizeinwords); + orToContainer(a, container); + return container; + } + + /** + * Computes the bitwise or between the current bitmap and the bitmap "a". + * Stores the result in the container. + * + * @param a + * the other bitmap + * @param container + * where we store the result + */ + public void orToContainer(final EWAHCompressedBitmap32 a, final BitmapStorage32 container) { + final EWAHIterator32 i = a.getEWAHIterator(); + final EWAHIterator32 j = getEWAHIterator(); + final IteratingBufferedRunningLengthWord32 rlwi = new IteratingBufferedRunningLengthWord32(i); + final IteratingBufferedRunningLengthWord32 rlwj = new IteratingBufferedRunningLengthWord32(j); + while ((rlwi.size()>0) && (rlwj.size()>0)) { + while ((rlwi.getRunningLength() > 0) || (rlwj.getRunningLength() > 0)) { + final boolean i_is_prey = rlwi.getRunningLength() < rlwj + .getRunningLength(); + final IteratingBufferedRunningLengthWord32 prey = i_is_prey ? rlwi + : rlwj; + final IteratingBufferedRunningLengthWord32 predator = i_is_prey ? rlwj + : rlwi; + if (predator.getRunningBit() == true) { + container.addStreamOfEmptyWords(true, predator.getRunningLength()); + prey.discardFirstWords(predator.getRunningLength()); + predator.discardFirstWords(predator.getRunningLength()); + } else { + int index = prey.discharge(container, predator.getRunningLength()); + container.addStreamOfEmptyWords(false, predator.getRunningLength() + - index); + predator.discardFirstWords(predator.getRunningLength()); + } + } + final int nbre_literal = Math.min(rlwi.getNumberOfLiteralWords(), + rlwj.getNumberOfLiteralWords()); + if (nbre_literal > 0) { + for (int k = 0; k < nbre_literal; ++k) { + container.add(rlwi.getLiteralWordAt(k) | rlwj.getLiteralWordAt(k)); + } + rlwi.discardFirstWords(nbre_literal); + rlwj.discardFirstWords(nbre_literal); + } + } + final boolean i_remains = rlwi.size()>0; + final IteratingBufferedRunningLengthWord32 remaining = i_remains ? rlwi : rlwj; + remaining.discharge(container); + container.setSizeInBits(Math.max(sizeInBits(), a.sizeInBits())); + } + /** + * Returns the cardinality of the result of a bitwise OR of the values of the + * current bitmap with some other bitmap. Avoids needing to allocate an + * intermediate bitmap to hold the result of the OR. + * + * @param a + * the other bitmap + * @return the cardinality + */ + public int orCardinality(final EWAHCompressedBitmap32 a) { + final BitCounter32 counter = new BitCounter32(); + orToContainer(a, counter); + return counter.getCount(); + } + + /** + * For internal use. + * + * @param data + * the word to be added + */ + private void push_back(final int data) { + if (this.actualsizeinwords == this.buffer.length) { + final int oldbuffer[] = this.buffer; + if(oldbuffer.length < 32768) + this.buffer = new int[oldbuffer.length * 2]; + else if (oldbuffer.length * 3 / 2 < oldbuffer.length) + this.buffer = new int[Integer.MAX_VALUE]; + else + this.buffer = new int[oldbuffer.length * 3 / 2]; + System.arraycopy(oldbuffer, 0, this.buffer, 0, oldbuffer.length); + this.rlw.parent.buffer = this.buffer; + } + this.buffer[this.actualsizeinwords++] = data; + } + + /** + * For internal use. + * + * @param data + * the array of words to be added + * @param start + * the starting point + * @param number + * the number of words to add + */ + private void push_back(final int[] data, final int start, final int number) { + if (this.actualsizeinwords + number >= this.buffer.length) { + final int oldbuffer[] = this.buffer; + if(this.actualsizeinwords + number < 32768) + this.buffer = new int[(this.actualsizeinwords + number) * 2]; + else if((this.actualsizeinwords + number) * 3 / 2 < this.actualsizeinwords + number) //overflow + this.buffer = new int[Integer.MAX_VALUE]; + else + this.buffer = new int[(this.actualsizeinwords + number) * 3 / 2]; + System.arraycopy(oldbuffer, 0, this.buffer, 0, oldbuffer.length); + this.rlw.parent.buffer = this.buffer; + } + System.arraycopy(data, start, this.buffer, this.actualsizeinwords, number); + this.actualsizeinwords += number; + } + + /* + * @see java.io.Externalizable#readExternal(java.io.ObjectInput) + */ + @Override +public void readExternal(ObjectInput in) throws IOException { + deserialize(in); + } + + /** + * For internal use (trading off memory for speed). + * + * @param size + * the number of words to allocate + * @return True if the operation was a success. + */ + private boolean reserve(final int size) { + if (size > this.buffer.length) { + final int oldbuffer[] = this.buffer; + this.buffer = new int[size]; + System.arraycopy(oldbuffer, 0, this.buffer, 0, oldbuffer.length); + this.rlw.parent.buffer = this.buffer; + return true; + } + return false; + } + + /** + * Serialize. + * + * @param out + * the DataOutput stream + * @throws IOException + * Signals that an I/O exception has occurred. + */ + public void serialize(DataOutput out) throws IOException { + out.writeInt(this.sizeinbits); + out.writeInt(this.actualsizeinwords); + for (int k = 0; k < this.actualsizeinwords; ++k) + out.writeInt(this.buffer[k]); + out.writeInt(this.rlw.position); + } + + /** + * Report the size required to serialize this bitmap + * + * @return the size in bytes + */ + public int serializedSizeInBytes() { + return this.sizeInBytes() + 3 * 4; + } + + /** + * Query the value of a single bit. Relying on this method when speed is + * needed is discouraged. The complexity is linear with the size of the + * bitmap. + * + * (This implementation is based on zhenjl's Go version of JavaEWAH.) + * + * @param i + * the bit we are interested in + * @return whether the bit is set to true + */ + public boolean get(final int i) { + if ((i < 0) || (i >= this.sizeinbits)) + return false; + int WordChecked = 0; + final IteratingRLW32 j = getIteratingRLW(); + final int wordi = i / wordinbits; + while (WordChecked <= wordi) { + WordChecked += j.getRunningLength(); + if (wordi < WordChecked) { + return j.getRunningBit(); + } + if (wordi < WordChecked + j.getNumberOfLiteralWords()) { + final int w = j.getLiteralWordAt(wordi + - WordChecked); + return (w & (1 << i)) != 0; + } + WordChecked += j.getNumberOfLiteralWords(); + j.next(); + } + return false; + } + + /** + * Set the bit at position i to true, the bits must be set in (strictly) increasing + * order. For example, set(15) and then set(7) will fail. You must do set(7) + * and then set(15). + * + * @param i + * the index + * @return true if the value was set (always true when i is greater or equal to sizeInBits()). + * @throws IndexOutOfBoundsException + * if i is negative or greater than Integer.MAX_VALUE - 32 + */ + + public boolean set(final int i) { + if ((i > Integer.MAX_VALUE - wordinbits) || (i < 0)) + throw new IndexOutOfBoundsException("Set values should be between 0 and " + + (Integer.MAX_VALUE - wordinbits)); + if (i < this.sizeinbits) + return false; + // distance in words: + final int dist = (i + wordinbits) / wordinbits + - (this.sizeinbits + wordinbits - 1) / wordinbits; + this.sizeinbits = i + 1; + if (dist > 0) {// easy + if (dist > 1) + fastaddStreamOfEmptyWords(false, dist - 1); + addLiteralWord(1 << (i % wordinbits)); + return true; + } + if (this.rlw.getNumberOfLiteralWords() == 0) { + this.rlw.setRunningLength(this.rlw.getRunningLength() - 1); + addLiteralWord(1 << (i % wordinbits)); + return true; + } + this.buffer[this.actualsizeinwords - 1] |= 1 << (i % wordinbits); + if (this.buffer[this.actualsizeinwords - 1] == ~0) { + this.buffer[this.actualsizeinwords - 1] = 0; + --this.actualsizeinwords; + this.rlw.setNumberOfLiteralWords(this.rlw.getNumberOfLiteralWords() - 1); + // next we add one clean word + addEmptyWord(true); + } + return true; + } + + /** + * Set the size in bits. This does not change the compressed bitmap. + * + */ + @Override +public void setSizeInBits(final int size) { + if((size+EWAHCompressedBitmap32.wordinbits-1)/EWAHCompressedBitmap32.wordinbits!= (this.sizeinbits+EWAHCompressedBitmap32.wordinbits-1)/EWAHCompressedBitmap32.wordinbits) + throw new RuntimeException("You can only reduce the size of the bitmap within the scope of the last word. To extend the bitmap, please call setSizeInbits(int,boolean): "+size+" "+this.sizeinbits); + this.sizeinbits = size; + } + + /** + * Change the reported size in bits of the *uncompressed* bitmap represented + * by this compressed bitmap. It may change the underlying compressed bitmap. + * It is not possible to reduce the sizeInBits, but + * it can be extended. The new bits are set to false or true depending on the + * value of defaultvalue. + * + * @param size + * the size in bits + * @param defaultvalue + * the default boolean value + * @return true if the update was possible + */ + public boolean setSizeInBits(final int size, final boolean defaultvalue) { + if (size < this.sizeinbits) + return false; + if (defaultvalue == false) + extendEmptyBits(this, this.sizeinbits, size); + else { + // next bit could be optimized + while (((this.sizeinbits % wordinbits) != 0) && (this.sizeinbits < size)) { + this.set(this.sizeinbits); + } + this.addStreamOfEmptyWords(defaultvalue, (size / wordinbits) + - this.sizeinbits / wordinbits); + // next bit could be optimized + while (this.sizeinbits < size) { + this.set(this.sizeinbits); + } + } + this.sizeinbits = size; + return true; + } + + /** + * Returns the size in bits of the *uncompressed* bitmap represented by this + * compressed bitmap. Initially, the sizeInBits is zero. It is extended + * automatically when you set bits to true. + * + * @return the size in bits + */ + @Override +public int sizeInBits() { + return this.sizeinbits; + } + + /** + * Report the *compressed* size of the bitmap (equivalent to memory usage, + * after accounting for some overhead). + * + * @return the size in bytes + */ + @Override +public int sizeInBytes() { + return this.actualsizeinwords * (wordinbits / 8); + } + + /** + * Populate an array of (sorted integers) corresponding to the location of the + * set bits. + * + * @return the array containing the location of the set bits + */ + public int[] toArray() { + int[] ans = new int[this.cardinality()]; + int inanspos = 0; + int pos = 0; + final EWAHIterator32 i = new EWAHIterator32(this, + this.actualsizeinwords); + while (i.hasNext()) { + RunningLengthWord32 localrlw = i.next(); + if (localrlw.getRunningBit()) { + for (int j = 0; j < localrlw.getRunningLength(); ++j) { + for (int c = 0; c < wordinbits; ++c) { + ans[inanspos++] = pos++; + } + } + } else { + pos += wordinbits * localrlw.getRunningLength(); + } + for (int j = 0; j < localrlw.getNumberOfLiteralWords(); ++j) { + int data = i.buffer()[i.literalWords() + j]; + if (!usetrailingzeros) { + for (int c = 0; c < wordinbits; ++c) { + if ((data & (1 << c)) != 0) + ans[inanspos++] = c + pos; + } + pos += wordinbits; + } else { + while (data != 0) { + final int ntz = Integer.numberOfTrailingZeros(data); + data ^= (1l << ntz); + ans[inanspos++] = ntz + pos; + } + pos += wordinbits; + } + } + } + return ans; + + } + + /** + * A more detailed string describing the bitmap (useful for debugging). + * + * @return the string + */ + public String toDebugString() { + String ans = " EWAHCompressedBitmap, size in bits = " + this.sizeinbits + + " size in words = " + this.actualsizeinwords + "\n"; + final EWAHIterator32 i = new EWAHIterator32(this, + this.actualsizeinwords); + while (i.hasNext()) { + RunningLengthWord32 localrlw = i.next(); + if (localrlw.getRunningBit()) { + ans += localrlw.getRunningLength() + " 1x11\n"; + } else { + ans += localrlw.getRunningLength() + " 0x00\n"; + } + ans += localrlw.getNumberOfLiteralWords() + " dirties\n"; + for (int j = 0; j < localrlw.getNumberOfLiteralWords(); ++j) { + int data = i.buffer()[i.literalWords() + j]; + ans += "\t" + data + "\n"; + } + } + return ans; + } + + /** + * A string describing the bitmap. + * + * @return the string + */ + @Override + public String toString() { + StringBuffer answer = new StringBuffer(); + IntIterator i = this.intIterator(); + answer.append("{"); + if (i.hasNext()) + answer.append(i.next()); + while (i.hasNext()) { + answer.append(","); + answer.append(i.next()); + } + answer.append("}"); + return answer.toString(); + } + /** + * swap the content of the bitmap with another. + * + * @param other + * bitmap to swap with + */ + public void swap(final EWAHCompressedBitmap32 other) { + int[] tmp = this.buffer; + this.buffer = other.buffer; + other.buffer = tmp; + + int tmp2 = this.rlw.position; + this.rlw.position = other.rlw.position; + other.rlw.position = tmp2; + + int tmp3 = this.actualsizeinwords; + this.actualsizeinwords = other.actualsizeinwords; + other.actualsizeinwords = tmp3; + + int tmp4 = this.sizeinbits; + this.sizeinbits = other.sizeinbits; + other.sizeinbits = tmp4; + } + /** + * Reduce the internal buffer to its minimal allowable size (given + * by this.actualsizeinwords). This can free memory. + */ + public void trim() { + this.buffer = Arrays.copyOf(this.buffer, this.actualsizeinwords); + } + + /* + * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput) + */ + @Override +public void writeExternal(ObjectOutput out) throws IOException { + serialize(out); + } + + /** + * Returns a new compressed bitmap containing the bitwise XOR values of the + * current bitmap with some other bitmap. + * + * The running time is proportional to the sum of the compressed sizes (as + * reported by sizeInBytes()). + * + * If you are not planning on adding to the resulting bitmap, you may call the trim() + * method to reduce memory usage. + * + * @param a + * the other bitmap + * @return the EWAH compressed bitmap + */ + @Override +public EWAHCompressedBitmap32 xor(final EWAHCompressedBitmap32 a) { + final EWAHCompressedBitmap32 container = new EWAHCompressedBitmap32(); + container.reserve(this.actualsizeinwords + a.actualsizeinwords); + xorToContainer(a, container); + return container; + } + + /** + * Computes a new compressed bitmap containing the bitwise XOR values of the + * current bitmap with some other bitmap. + * + * The running time is proportional to the sum of the compressed sizes (as + * reported by sizeInBytes()). + * + * @param a + * the other bitmap + * @param container + * where we store the result + */ + public void xorToContainer(final EWAHCompressedBitmap32 a, + final BitmapStorage32 container) { + final EWAHIterator32 i = a.getEWAHIterator(); + final EWAHIterator32 j = getEWAHIterator(); + final IteratingBufferedRunningLengthWord32 rlwi = new IteratingBufferedRunningLengthWord32(i); + final IteratingBufferedRunningLengthWord32 rlwj = new IteratingBufferedRunningLengthWord32(j); + while ((rlwi.size()>0) && (rlwj.size()>0)) { + while ((rlwi.getRunningLength() > 0) || (rlwj.getRunningLength() > 0)) { + final boolean i_is_prey = rlwi.getRunningLength() < rlwj + .getRunningLength(); + final IteratingBufferedRunningLengthWord32 prey = i_is_prey ? rlwi : rlwj; + final IteratingBufferedRunningLengthWord32 predator = i_is_prey ? rlwj + : rlwi; + if (predator.getRunningBit() == false) { + int index = prey.discharge(container, predator.getRunningLength()); + container.addStreamOfEmptyWords(false, predator.getRunningLength() + - index); + predator.discardFirstWords(predator.getRunningLength()); + } else { + int index = prey.dischargeNegated(container, predator.getRunningLength()); + container.addStreamOfEmptyWords(true, predator.getRunningLength() + - index); + predator.discardFirstWords(predator.getRunningLength()); + } + } + final int nbre_literal = Math.min(rlwi.getNumberOfLiteralWords(), + rlwj.getNumberOfLiteralWords()); + if (nbre_literal > 0) { + for (int k = 0; k < nbre_literal; ++k) + container.add(rlwi.getLiteralWordAt(k) ^ rlwj.getLiteralWordAt(k)); + rlwi.discardFirstWords(nbre_literal); + rlwj.discardFirstWords(nbre_literal); + } + } + final boolean i_remains = rlwi.size()>0; + final IteratingBufferedRunningLengthWord32 remaining = i_remains ? rlwi : rlwj; + remaining.discharge(container); + container.setSizeInBits(Math.max(sizeInBits(), a.sizeInBits())); + } + + /** + * Returns the cardinality of the result of a bitwise XOR of the values of the + * current bitmap with some other bitmap. Avoids needing to allocate an + * intermediate bitmap to hold the result of the OR. + * + * @param a + * the other bitmap + * @return the cardinality + */ + public int xorCardinality(final EWAHCompressedBitmap32 a) { + final BitCounter32 counter = new BitCounter32(); + xorToContainer(a, counter); + return counter.getCount(); + } + + /** + * For internal use. Computes the bitwise and of the provided bitmaps and + * stores the result in the container. + * + * @param container + * where the result is stored + * @param bitmaps + * bitmaps to AND + */ + public static void andWithContainer(final BitmapStorage32 container, + final EWAHCompressedBitmap32... bitmaps) { + if(bitmaps.length == 1) throw new IllegalArgumentException("Need at least one bitmap"); + if(bitmaps.length == 2) { + bitmaps[0].andToContainer(bitmaps[1],container); + return; + } + EWAHCompressedBitmap32 answer = new EWAHCompressedBitmap32(); + EWAHCompressedBitmap32 tmp = new EWAHCompressedBitmap32(); + bitmaps[0].andToContainer(bitmaps[1], answer); + for(int k = 2; k < bitmaps.length - 1; ++k) { + answer.andToContainer(bitmaps[k], tmp); + tmp.swap(answer); + tmp.clear(); + } + answer.andToContainer(bitmaps[bitmaps.length - 1], container); + } + + /** + * Returns a new compressed bitmap containing the bitwise AND values of the + * provided bitmaps. + * + * It may or may not be faster than doing the aggregation two-by-two (A.and(B).and(C)). + * + * If only one bitmap is provided, it is returned as is. + * + * If you are not planning on adding to the resulting bitmap, you may call the trim() + * method to reduce memory usage. + * + * @param bitmaps + * bitmaps to AND together + * @return result of the AND + */ + public static EWAHCompressedBitmap32 and( + final EWAHCompressedBitmap32... bitmaps) { + if(bitmaps.length == 1) return bitmaps[0]; + if(bitmaps.length == 2) return bitmaps[0].and(bitmaps[1]); + EWAHCompressedBitmap32 answer = new EWAHCompressedBitmap32(); + EWAHCompressedBitmap32 tmp = new EWAHCompressedBitmap32(); + bitmaps[0].andToContainer(bitmaps[1], answer); + for(int k = 2; k < bitmaps.length; ++k) { + answer.andToContainer(bitmaps[k], tmp); + tmp.swap(answer); + tmp.clear(); + } + return answer; + } + + /** + * Returns the cardinality of the result of a bitwise AND of the values of the + * provided bitmaps. Avoids needing to allocate an intermediate bitmap to hold + * the result of the AND. + * + * @param bitmaps + * bitmaps to AND + * @return the cardinality + */ + public static int andCardinality(final EWAHCompressedBitmap32... bitmaps) { + if(bitmaps.length == 1) return bitmaps[0].cardinality(); + final BitCounter32 counter = new BitCounter32(); + andWithContainer(counter, bitmaps); + return counter.getCount(); + } + + + /** + * Return a bitmap with the bit set to true at the given + * positions. The positions should be given in sorted order. + * + * (This is a convenience method.) + * + * @since 0.4.5 + * @param setbits list of set bit positions + * @return the bitmap + */ + public static EWAHCompressedBitmap32 bitmapOf(int ... setbits) { + EWAHCompressedBitmap32 a = new EWAHCompressedBitmap32(); + for (int k : setbits) + a.set(k); + return a; + } + + + + + /** + * For internal use. This simply adds a stream of words made of zeroes so that + * we pad to the desired size. + * + * @param storage + * bitmap to extend + * @param currentSize + * current size (in bits) + * @param newSize + * new desired size (in bits) + */ + private static void extendEmptyBits(final BitmapStorage32 storage, + final int currentSize, final int newSize) { + final int currentLeftover = currentSize % wordinbits; + final int finalLeftover = newSize % wordinbits; + storage.addStreamOfEmptyWords(false, (newSize / wordinbits) - currentSize + / wordinbits + (finalLeftover != 0 ? 1 : 0) + + (currentLeftover != 0 ? -1 : 0)); + } + + /** + * For internal use. Computes the bitwise or of the provided bitmaps and + * stores the result in the container. + * @param container where store the result + * @param bitmaps to be aggregated + */ + public static void orWithContainer(final BitmapStorage32 container, + final EWAHCompressedBitmap32... bitmaps) { + if (bitmaps.length < 2) + throw new IllegalArgumentException("You should provide at least two bitmaps, provided "+bitmaps.length); + int size = 0; + int sinbits = 0; + for (EWAHCompressedBitmap32 b : bitmaps) { + size += b.sizeInBytes(); + if (sinbits < b.sizeInBits()) + sinbits = b.sizeInBits(); + } + if (size * 8 > sinbits) { + FastAggregation32.bufferedorWithContainer(container, 65536, bitmaps); + } else { + FastAggregation32.orToContainer(container, bitmaps); + } + } + + /** + * For internal use. Computes the bitwise xor of the provided bitmaps and + * stores the result in the container. + * @param container where store the result + * @param bitmaps to be aggregated + */ + public static void xorWithContainer(final BitmapStorage32 container, + final EWAHCompressedBitmap32... bitmaps) { + if (bitmaps.length < 2) + throw new IllegalArgumentException("You should provide at least two bitmaps, provided "+bitmaps.length); + int size = 0; + int sinbits = 0; + for (EWAHCompressedBitmap32 b : bitmaps) { + size += b.sizeInBytes(); + if (sinbits < b.sizeInBits()) + sinbits = b.sizeInBits(); + } + if (size * 8 > sinbits) { + FastAggregation32.bufferedxorWithContainer(container, 65536, bitmaps); + } else { + FastAggregation32.xorToContainer(container, bitmaps); + } + } + + /** + * Returns a new compressed bitmap containing the bitwise OR values of the + * provided bitmaps. This is typically faster than doing the aggregation + * two-by-two (A.or(B).or(C).or(D)). + * + * If only one bitmap is provided, it is returned as is. + * + * If you are not planning on adding to the resulting bitmap, you may call the trim() + * method to reduce memory usage. + * + * @param bitmaps + * bitmaps to OR together + * @return result of the OR + */ + public static EWAHCompressedBitmap32 or( + final EWAHCompressedBitmap32... bitmaps) { + if(bitmaps.length == 1) return bitmaps[0]; + final EWAHCompressedBitmap32 container = new EWAHCompressedBitmap32(); + int largestSize = 0; + for (EWAHCompressedBitmap32 bitmap : bitmaps) { + largestSize = Math.max(bitmap.actualsizeinwords, largestSize); + } + container.reserve((int) (largestSize * 1.5)); + orWithContainer(container, bitmaps); + return container; + } + + + /** + * Returns a new compressed bitmap containing the bitwise XOR values of the + * provided bitmaps. This is typically faster than doing the aggregation + * two-by-two (A.xor(B).xor(C).xor(D)). + * + * If only one bitmap is provided, it is returned as is. + * + * If you are not planning on adding to the resulting bitmap, you may call the trim() + * method to reduce memory usage. + * + * @param bitmaps + * bitmaps to XOR together + * @return result of the XOR + */ + public static EWAHCompressedBitmap32 xor( + final EWAHCompressedBitmap32... bitmaps) { + if(bitmaps.length == 1) return bitmaps[0]; + final EWAHCompressedBitmap32 container = new EWAHCompressedBitmap32(); + int largestSize = 0; + for (EWAHCompressedBitmap32 bitmap : bitmaps) { + largestSize = Math.max(bitmap.actualsizeinwords, largestSize); + } + container.reserve((int) (largestSize * 1.5)); + xorWithContainer(container, bitmaps); + return container; + } + + /** + * Returns the cardinality of the result of a bitwise OR of the values of the + * provided bitmaps. Avoids needing to allocate an intermediate bitmap to hold + * the result of the OR. + * + * @param bitmaps + * bitmaps to OR + * @return the cardinality + */ + public static int orCardinality(final EWAHCompressedBitmap32... bitmaps) { + if(bitmaps.length == 1) return bitmaps[0].cardinality(); + final BitCounter32 counter = new BitCounter32(); + orWithContainer(counter, bitmaps); + return counter.getCount(); + } + + /** The actual size in words. */ + int actualsizeinwords = 1; + + /** The buffer (array of 32-bit words) */ + int buffer[] = null; + + /** The current (last) running length word. */ + RunningLengthWord32 rlw = null; + + /** sizeinbits: number of bits in the (uncompressed) bitmap. */ + int sizeinbits = 0; + + /** + * The Constant defaultbuffersize: default memory allocation when the object + * is constructed. + */ + static final int defaultbuffersize = 4; + + /** optimization option **/ + public static final boolean usetrailingzeros = true; + + /** whether we adjust after some aggregation by adding in zeroes **/ + public static final boolean adjustContainerSizeWhenAggregating = true; + + /** The Constant wordinbits represents the number of bits in a int. */ + public static final int wordinbits = 32; + +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah32/EWAHIterator32.java b/fine-jgit/src/com/fr/third/googlecode/javaewah32/EWAHIterator32.java new file mode 100644 index 000000000..dee08341d --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah32/EWAHIterator32.java @@ -0,0 +1,98 @@ +package com.fr.third.googlecode.javaewah32; + + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ + +/** + * The class EWAHIterator represents a special type of + * efficient iterator iterating over (uncompressed) words of bits. + * + * @author Daniel Lemire + * @since 0.5.0 + * + */ +public final class EWAHIterator32 implements Cloneable { + + /** + * Instantiates a new eWAH iterator. + * + * @param a the array of words + * @param sizeinwords the number of words that are significant in the array of words + */ + public EWAHIterator32(final EWAHCompressedBitmap32 a, final int sizeinwords) { + this.rlw = new RunningLengthWord32(a, 0); + this.size = sizeinwords; + this.pointer = 0; + } + + /** + * Allow expert developers to instantiate an EWAHIterator. + * + * @param bitmap we want to iterate over + * @return an iterator + */ + public static EWAHIterator32 getEWAHIterator(EWAHCompressedBitmap32 bitmap) { + return bitmap.getEWAHIterator(); + } + + /** + * Access to the array of words + * + * @return the int[] + */ + public int[] buffer() { + return this.rlw.parent.buffer; + } + + /** + * Position of the literal words represented by this running length word. + * + * @return the int + */ + public int literalWords() { + return this.pointer - this.rlw.getNumberOfLiteralWords(); + } + + /** + * Checks for next. + * + * @return true, if successful + */ + public boolean hasNext() { + return this.pointer < this.size; + } + + /** + * Next running length word. + * + * @return the running length word + */ + public RunningLengthWord32 next() { + this.rlw.position = this.pointer; + this.pointer += this.rlw.getNumberOfLiteralWords() + 1; + return this.rlw; + } + + @Override + public EWAHIterator32 clone() throws CloneNotSupportedException { + EWAHIterator32 ans = (EWAHIterator32) super.clone(); + ans.rlw = this.rlw.clone(); + ans.size = this.size; + ans.pointer = this.pointer; + return ans; + } + + /** The pointer represent the location of the current running length + * word in the array of words (embedded in the rlw attribute). */ + int pointer; + + /** The current running length word. */ + RunningLengthWord32 rlw; + + /** The size in words. */ + int size; + +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah32/FastAggregation32.java b/fine-jgit/src/com/fr/third/googlecode/javaewah32/FastAggregation32.java new file mode 100644 index 000000000..7ecd45fcd --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah32/FastAggregation32.java @@ -0,0 +1,377 @@ +package com.fr.third.googlecode.javaewah32; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.PriorityQueue; + + +/** + * Fast algorithms to aggregate many bitmaps. These algorithms are just given as + * reference. They may not be faster than the corresponding methods in the + * EWAHCompressedBitmap class. + * + * @author Daniel Lemire + * + */ +public class FastAggregation32 { + + /** + * Compute the and aggregate using a temporary uncompressed bitmap. + * @param bitmaps the source bitmaps + * @param bufsize buffer size used during the computation in 64-bit words (per input bitmap) + * @return the or aggregate. + */ + public static EWAHCompressedBitmap32 bufferedand(final int bufsize, + final EWAHCompressedBitmap32... bitmaps) { + EWAHCompressedBitmap32 answer = new EWAHCompressedBitmap32(); + bufferedandWithContainer(answer,bufsize, bitmaps); + return answer; + } + /** + * Compute the and aggregate using a temporary uncompressed bitmap. + * + * @param container where the aggregate is written + * @param bufsize buffer size used during the computation in 64-bit words (per input bitmap) + * @param bitmaps the source bitmaps + */ + public static void bufferedandWithContainer(final BitmapStorage32 container,final int bufsize, + final EWAHCompressedBitmap32... bitmaps) { + + java.util.LinkedList al = new java.util.LinkedList(); + for (EWAHCompressedBitmap32 bitmap : bitmaps) { + al.add(new IteratingBufferedRunningLengthWord32(bitmap)); + } + int[] hardbitmap = new int[bufsize*bitmaps.length]; + + for(IteratingRLW32 i : al) + if (i.size() == 0) { + al.clear(); + break; + } + + while (!al.isEmpty()) { + Arrays.fill(hardbitmap, ~0); + int effective = Integer.MAX_VALUE; + for(IteratingRLW32 i : al) { + int eff = IteratorAggregation32.inplaceand(hardbitmap, i); + if (eff < effective) + effective = eff; + } + for (int k = 0; k < effective; ++k) + container.add(hardbitmap[k]); + for(IteratingRLW32 i : al) + if (i.size() == 0) { + al.clear(); + break; + } + } + } + + /** + * Compute the or aggregate using a temporary uncompressed bitmap. + * @param bitmaps the source bitmaps + * @param bufsize buffer size used during the computation in 64-bit words + * @return the or aggregate. + */ + public static EWAHCompressedBitmap32 bufferedor(final int bufsize, + final EWAHCompressedBitmap32... bitmaps) { + EWAHCompressedBitmap32 answer = new EWAHCompressedBitmap32(); + bufferedorWithContainer(answer, bufsize, bitmaps); + return answer; + } + + /** + * Compute the or aggregate using a temporary uncompressed bitmap. + * + * @param container where the aggregate is written + * @param bufsize buffer size used during the computation in 64-bit words + * @param bitmaps the source bitmaps + */ + public static void bufferedorWithContainer(final BitmapStorage32 container,final int bufsize, + final EWAHCompressedBitmap32... bitmaps) { + int range = 0; + EWAHCompressedBitmap32[] sbitmaps = bitmaps.clone(); + Arrays.sort(sbitmaps, new Comparator() { + @Override + public int compare(EWAHCompressedBitmap32 a, EWAHCompressedBitmap32 b) { + return b.sizeinbits - a.sizeinbits; + } + }); + + java.util.ArrayList al = new java.util.ArrayList(); + for (EWAHCompressedBitmap32 bitmap : sbitmaps) { + if (bitmap.sizeinbits > range) + range = bitmap.sizeinbits; + al.add(new IteratingBufferedRunningLengthWord32(bitmap)); + } + int[] hardbitmap = new int[bufsize]; + int maxr = al.size(); + while (maxr > 0) { + int effective = 0; + for (int k = 0; k < maxr; ++k) { + if (al.get(k).size() > 0) { + int eff = IteratorAggregation32.inplaceor(hardbitmap, al.get(k)); + if (eff > effective) + effective = eff; + } else + maxr = k; + } + for (int k = 0; k < effective; ++k) + container.add(hardbitmap[k]); + Arrays.fill(hardbitmap, 0); + + } + container.setSizeInBits(range); + } + + /** + * Compute the xor aggregate using a temporary uncompressed bitmap. + * @param bitmaps the source bitmaps + * @param bufsize buffer size used during the computation in 64-bit words + * @return the xor aggregate. + */ + public static EWAHCompressedBitmap32 bufferedxor(final int bufsize, + final EWAHCompressedBitmap32... bitmaps) { + EWAHCompressedBitmap32 answer = new EWAHCompressedBitmap32(); + bufferedxorWithContainer(answer, bufsize, bitmaps); + return answer; + } + + + /** + * Compute the xor aggregate using a temporary uncompressed bitmap. + * + * @param container where the aggregate is written + * @param bufsize buffer size used during the computation in 64-bit words + * @param bitmaps the source bitmaps + */ + public static void bufferedxorWithContainer(final BitmapStorage32 container,final int bufsize, + final EWAHCompressedBitmap32... bitmaps) { + int range = 0; + EWAHCompressedBitmap32[] sbitmaps = bitmaps.clone(); + Arrays.sort(sbitmaps, new Comparator() { + @Override + public int compare(EWAHCompressedBitmap32 a, EWAHCompressedBitmap32 b) { + return b.sizeinbits - a.sizeinbits; + } + }); + + java.util.ArrayList al = new java.util.ArrayList(); + for (EWAHCompressedBitmap32 bitmap : sbitmaps) { + if (bitmap.sizeinbits > range) + range = bitmap.sizeinbits; + al.add(new IteratingBufferedRunningLengthWord32(bitmap)); + } + int[] hardbitmap = new int[bufsize]; + int maxr = al.size(); + while (maxr > 0) { + int effective = 0; + for (int k = 0; k < maxr; ++k) { + if (al.get(k).size() > 0) { + int eff = IteratorAggregation32.inplacexor(hardbitmap, al.get(k)); + if (eff > effective) + effective = eff; + } else + maxr = k; + } + for (int k = 0; k < effective; ++k) + container.add(hardbitmap[k]); + Arrays.fill(hardbitmap, 0); + } + container.setSizeInBits(range); + } + + /** + * Uses a priority queue to compute the or aggregate. + * @param container where we write the result + * @param bitmaps to be aggregated + */ + public static void orToContainer(final BitmapStorage32 container, + final EWAHCompressedBitmap32 ... bitmaps) { + if(bitmaps.length < 2) throw new IllegalArgumentException("We need at least two bitmaps"); + PriorityQueue pq = new PriorityQueue(bitmaps.length, + new Comparator() { + @Override + public int compare(EWAHCompressedBitmap32 a, EWAHCompressedBitmap32 b) { + return a.sizeInBytes() - b.sizeInBytes(); + } + }); + for (EWAHCompressedBitmap32 x : bitmaps) { + pq.add(x); + } + while (pq.size() > 2) { + EWAHCompressedBitmap32 x1 = pq.poll(); + EWAHCompressedBitmap32 x2 = pq.poll(); + pq.add(x1.or(x2)); + } + pq.poll().orToContainer(pq.poll(), container); + } + + + /** + * Uses a priority queue to compute the xor aggregate. + * @param container where we write the result + * @param bitmaps to be aggregated + */ + public static void xorToContainer(final BitmapStorage32 container, + final EWAHCompressedBitmap32 ... bitmaps) { + if(bitmaps.length < 2) throw new IllegalArgumentException("We need at least two bitmaps"); + PriorityQueue pq = new PriorityQueue(bitmaps.length, + new Comparator() { + @Override + public int compare(EWAHCompressedBitmap32 a, EWAHCompressedBitmap32 b) { + return a.sizeInBytes() - b.sizeInBytes(); + } + }); + for (EWAHCompressedBitmap32 x : bitmaps) { + pq.add(x); + } + while (pq.size() > 2) { + EWAHCompressedBitmap32 x1 = pq.poll(); + EWAHCompressedBitmap32 x2 = pq.poll(); + pq.add(x1.xor(x2)); + } + pq.poll().xorToContainer(pq.poll(), container); + } + + /** + * For internal use. Computes the bitwise or of the provided bitmaps and + * stores the result in the container. (This used to be the default.) + * + * @deprecated use EWAHCompressedBitmap32.or instead + * @since 0.4.0 + * @param container where store the result + * @param bitmaps to be aggregated + */ + @Deprecated + public static void legacy_orWithContainer(final BitmapStorage32 container, + final EWAHCompressedBitmap32... bitmaps) { + if (bitmaps.length == 2) { + // should be more efficient + bitmaps[0].orToContainer(bitmaps[1], container); + return; + } + + // Sort the bitmaps in descending order by sizeinbits. We will exhaust the + // sorted bitmaps from right to left. + final EWAHCompressedBitmap32[] sortedBitmaps = bitmaps.clone(); + Arrays.sort(sortedBitmaps, new Comparator() { + @Override + public int compare(EWAHCompressedBitmap32 a, EWAHCompressedBitmap32 b) { + return a.sizeinbits < b.sizeinbits ? 1 + : a.sizeinbits == b.sizeinbits ? 0 : -1; + } + }); + + final IteratingBufferedRunningLengthWord32[] rlws = new IteratingBufferedRunningLengthWord32[bitmaps.length]; + int maxAvailablePos = 0; + for (EWAHCompressedBitmap32 bitmap : sortedBitmaps) { + EWAHIterator32 iterator = bitmap.getEWAHIterator(); + if (iterator.hasNext()) { + rlws[maxAvailablePos++] = new IteratingBufferedRunningLengthWord32( + iterator); + } + } + + if (maxAvailablePos == 0) { // this never happens... + container.setSizeInBits(0); + return; + } + + int maxSize = sortedBitmaps[0].sizeinbits; + + while (true) { + int maxOneRl = 0; + int minZeroRl = Integer.MAX_VALUE; + int minSize = Integer.MAX_VALUE; + int numEmptyRl = 0; + for (int i = 0; i < maxAvailablePos; i++) { + IteratingBufferedRunningLengthWord32 rlw = rlws[i]; + int size = rlw.size(); + if (size == 0) { + maxAvailablePos = i; + break; + } + minSize = Math.min(minSize, size); + + if (rlw.getRunningBit()) { + int rl = rlw.getRunningLength(); + maxOneRl = Math.max(maxOneRl, rl); + minZeroRl = 0; + if (rl == 0 && size > 0) { + numEmptyRl++; + } + } else { + int rl = rlw.getRunningLength(); + minZeroRl = Math.min(minZeroRl, rl); + if (rl == 0 && size > 0) { + numEmptyRl++; + } + } + } + + if (maxAvailablePos == 0) { + break; + } else if (maxAvailablePos == 1) { + // only one bitmap is left so just write the rest of it out + rlws[0].discharge(container); + break; + } + + if (maxOneRl > 0) { + container.addStreamOfEmptyWords(true, maxOneRl); + for (int i = 0; i < maxAvailablePos; i++) { + IteratingBufferedRunningLengthWord32 rlw = rlws[i]; + rlw.discardFirstWords(maxOneRl); + } + } else if (minZeroRl > 0) { + container.addStreamOfEmptyWords(false, minZeroRl); + for (int i = 0; i < maxAvailablePos; i++) { + IteratingBufferedRunningLengthWord32 rlw = rlws[i]; + rlw.discardFirstWords(minZeroRl); + } + } else { + int index = 0; + + if (numEmptyRl == 1) { + // if one rlw has literal words to process and the rest have a run of + // 0's we can write them out here + IteratingBufferedRunningLengthWord32 emptyRl = null; + int minNonEmptyRl = Integer.MAX_VALUE; + for (int i = 0; i < maxAvailablePos; i++) { + IteratingBufferedRunningLengthWord32 rlw = rlws[i]; + int rl = rlw.getRunningLength(); + if (rl == 0) { + assert emptyRl == null; + emptyRl = rlw; + } else { + minNonEmptyRl = Math.min(minNonEmptyRl, rl); + } + } + int wordsToWrite = minNonEmptyRl > minSize ? minSize : minNonEmptyRl; + if (emptyRl != null) + emptyRl.writeLiteralWords(wordsToWrite, container); + index += wordsToWrite; + } + + while (index < minSize) { + int word = 0; + for (int i = 0; i < maxAvailablePos; i++) { + IteratingBufferedRunningLengthWord32 rlw = rlws[i]; + if (rlw.getRunningLength() <= index) { + word |= rlw.getLiteralWordAt(index - rlw.getRunningLength()); + } + } + container.add(word); + index++; + } + for (int i = 0; i < maxAvailablePos; i++) { + IteratingBufferedRunningLengthWord32 rlw = rlws[i]; + rlw.discardFirstWords(minSize); + } + } + } + container.setSizeInBits(maxSize); + } + +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah32/IntIteratorImpl32.java b/fine-jgit/src/com/fr/third/googlecode/javaewah32/IntIteratorImpl32.java new file mode 100644 index 000000000..041b5d8ff --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah32/IntIteratorImpl32.java @@ -0,0 +1,90 @@ +package com.fr.third.googlecode.javaewah32; + +/* + * Copyright 2012, Google Inc. + * Licensed under the Apache License, Version 2.0. + */ + +import static com.fr.third.googlecode.javaewah32.EWAHCompressedBitmap32.wordinbits; + +import com.fr.third.googlecode.javaewah.IntIterator; + +/** + * The IntIteratorImpl32 is the 32 bit implementation of the IntIterator + * interface, which efficiently returns the stream of integers represented by an + * EWAHIterator32. + * + * @author Colby Ranger + * @since 0.5.6 + */ +final class IntIteratorImpl32 implements IntIterator { + + private final EWAHIterator32 ewahIter; + private final int[] ewahBuffer; + private int position; + private int runningLength; + private int word; + private int wordPosition; + private int wordLength; + private int literalPosition; + private boolean hasnext; + + IntIteratorImpl32(EWAHIterator32 ewahIter) { + this.ewahIter = ewahIter; + this.ewahBuffer = ewahIter.buffer(); + this.hasnext = this.moveToNext(); + } + + public final boolean moveToNext() { + while (!runningHasNext() && !literalHasNext()) { + if (!this.ewahIter.hasNext()) { + return false; + } + setRunningLengthWord(this.ewahIter.next()); + } + return true; + } + + @Override + public final boolean hasNext() { + return this.hasnext; + } + + @Override + public final int next() { + final int answer; + if (runningHasNext()) { + answer = this.position++; + } else { + final int bit = Long.numberOfTrailingZeros(this.word); + this.word ^= (1l << bit); + answer = this.literalPosition + bit; + } + this.hasnext = this.moveToNext(); + return answer; + } + + private final void setRunningLengthWord(RunningLengthWord32 rlw) { + this.runningLength = wordinbits * rlw.getRunningLength() + + this.position; + if (!rlw.getRunningBit()) { + this.position = this.runningLength; + } + + this.wordPosition = this.ewahIter.literalWords(); + this.wordLength = this.wordPosition + rlw.getNumberOfLiteralWords(); + } + + private final boolean runningHasNext() { + return this.position < this.runningLength; + } + + private final boolean literalHasNext() { + while (this.word == 0 && this.wordPosition < this.wordLength) { + this.word = this.ewahBuffer[this.wordPosition++]; + this.literalPosition = this.position; + this.position += wordinbits; + } + return this.word != 0; + } +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah32/IntIteratorOverIteratingRLW32.java b/fine-jgit/src/com/fr/third/googlecode/javaewah32/IntIteratorOverIteratingRLW32.java new file mode 100644 index 000000000..6ad8c47a2 --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah32/IntIteratorOverIteratingRLW32.java @@ -0,0 +1,91 @@ +package com.fr.third.googlecode.javaewah32; + +import static com.fr.third.googlecode.javaewah.EWAHCompressedBitmap.wordinbits; + +import com.fr.third.googlecode.javaewah.IntIterator; + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ +/** + * Implementation of an IntIterator over an IteratingRLW. + * + * + */ +public class IntIteratorOverIteratingRLW32 implements IntIterator { + IteratingRLW32 parent; + private int position; + private int runningLength; + private int word; + private int wordPosition; + private int wordLength; + private int literalPosition; + private boolean hasnext; + + /** + * @param p iterator we wish to iterate over + */ + public IntIteratorOverIteratingRLW32(final IteratingRLW32 p) { + this.parent = p; + this.position = 0; + setupForCurrentRunningLengthWord(); + this.hasnext = moveToNext(); + } + + /** + * @return whether we could find another set bit; don't move if there is an unprocessed value + */ + private final boolean moveToNext() { + while (!runningHasNext() && !literalHasNext()) { + if (this.parent.next()) + setupForCurrentRunningLengthWord(); + else return false; + } + return true; + } + + @Override + public boolean hasNext() { + return this.hasnext; + } + + @Override + public final int next() { + final int answer; + if (runningHasNext()) { + answer = this.position++; + } else { + final int bit = Long.numberOfTrailingZeros(this.word); + this.word ^= (1l << bit); + answer = this.literalPosition + bit; + } + this.hasnext = this.moveToNext(); + return answer; + } + + private final void setupForCurrentRunningLengthWord() { + this.runningLength = wordinbits * this.parent.getRunningLength() + + this.position; + + if (!this.parent.getRunningBit()) { + this.position = this.runningLength; + } + this.wordPosition = 0; + this.wordLength = this.parent.getNumberOfLiteralWords(); + } + + private final boolean runningHasNext() { + return this.position < this.runningLength; + } + + private final boolean literalHasNext() { + while (this.word == 0 && this.wordPosition < this.wordLength) { + this.word = this.parent.getLiteralWordAt(this.wordPosition++); + this.literalPosition = this.position; + this.position += wordinbits; + } + return this.word != 0; + } +} + diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah32/IteratingBufferedRunningLengthWord32.java b/fine-jgit/src/com/fr/third/googlecode/javaewah32/IteratingBufferedRunningLengthWord32.java new file mode 100644 index 000000000..f09906274 --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah32/IteratingBufferedRunningLengthWord32.java @@ -0,0 +1,274 @@ +package com.fr.third.googlecode.javaewah32; + + + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ +/** + * Mostly for internal use. Similar to BufferedRunningLengthWord32, but automatically + * advances to the next BufferedRunningLengthWord32 as words are discarded. + * + * @since 0.5.0 + * @author Daniel Lemire and David McIntosh + */ +public final class IteratingBufferedRunningLengthWord32 implements IteratingRLW32, Cloneable { + /** + * Instantiates a new iterating buffered running length word. + * + * @param iterator iterator + */ + public IteratingBufferedRunningLengthWord32(final EWAHIterator32 iterator) { + this.iterator = iterator; + this.brlw = new BufferedRunningLengthWord32(this.iterator.next()); + this.literalWordStartPosition = this.iterator.literalWords() + this.brlw.literalwordoffset; + this.buffer = this.iterator.buffer(); + } + + + /** + * Instantiates a new iterating buffered running length word. + * @param bitmap over which we want to iterate + * + */ + public IteratingBufferedRunningLengthWord32(final EWAHCompressedBitmap32 bitmap) { + this(EWAHIterator32.getEWAHIterator(bitmap)); + } + + + /** + * Discard first words, iterating to the next running length word if needed. + * + * @param x the x + */ + @Override +public void discardFirstWords(int x) { + + while (x > 0) { + if (this.brlw.RunningLength > x) { + this.brlw.RunningLength -= x; + return; + } + x -= this.brlw.RunningLength; + this.brlw.RunningLength = 0; + int toDiscard = x > this.brlw.NumberOfLiteralWords ? this.brlw.NumberOfLiteralWords : x; + + this.literalWordStartPosition += toDiscard; + this.brlw.NumberOfLiteralWords -= toDiscard; + x -= toDiscard; + if ((x > 0) || (this.brlw.size() == 0)) { + if (!this.iterator.hasNext()) { + break; + } + this.brlw.reset(this.iterator.next()); + this.literalWordStartPosition = this.iterator.literalWords(); // + this.brlw.literalwordoffset == 0; + } + } + } + /** + * Write out up to max words, returns how many were written + * @param container target for writes + * @param max maximal number of writes + * @return how many written + */ + public int discharge(BitmapStorage32 container, int max) { + int index = 0; + while ((index < max) && (size() > 0)) { + // first run + int pl = getRunningLength(); + if (index + pl > max) { + pl = max - index; + } + container.addStreamOfEmptyWords(getRunningBit(), pl); + index += pl; + int pd = getNumberOfLiteralWords(); + if (pd + index > max) { + pd = max - index; + } + writeLiteralWords(pd, container); + discardFirstWords(pl+pd); + index += pd; + } + return index; + } + + /** + * Write out up to max words (negated), returns how many were written + * @param container target for writes + * @param max maximal number of writes + * @return how many written + */ + public int dischargeNegated(BitmapStorage32 container, int max) { + int index = 0; + while ((index < max) && (size() > 0)) { + // first run + int pl = getRunningLength(); + if (index + pl > max) { + pl = max - index; + } + container.addStreamOfEmptyWords(!getRunningBit(), pl); + index += pl; + int pd = getNumberOfLiteralWords(); + if (pd + index > max) { + pd = max - index; + } + writeNegatedLiteralWords(pd, container); + discardFirstWords(pl+pd); + index += pd; + } + return index; + } + + /** + * Move to the next RunningLengthWord + * @return whether the move was possible + */ + @Override +public boolean next() { + if (!this.iterator.hasNext()) { + this.brlw.NumberOfLiteralWords = 0; + this.brlw.RunningLength = 0; + return false; + } + this.brlw.reset(this.iterator.next()); + this.literalWordStartPosition = this.iterator.literalWords(); // + this.brlw.literalwordoffset ==0 + return true; + } + + /** + * Write out the remain words, transforming them to zeroes. + * @param container target for writes + */ + public void dischargeAsEmpty(BitmapStorage32 container) { + while(size()>0) { + container.addStreamOfEmptyWords(false, size()); + discardFirstWords(size()); + } + } + + /** + * Write out the remaining words + * @param container target for writes + */ + public void discharge(BitmapStorage32 container) { + // fix the offset + this.brlw.literalwordoffset = this.literalWordStartPosition - this.iterator.literalWords(); + discharge(this.brlw, this.iterator, container); + } + + /** + * Get the nth literal word for the current running length word + * @param index zero based index + * @return the literal word + */ + @Override +public int getLiteralWordAt(int index) { + return this.buffer[this.literalWordStartPosition + index]; + } + + /** + * Gets the number of literal words for the current running length word. + * + * @return the number of literal words + */ + @Override +public int getNumberOfLiteralWords() { + return this.brlw.NumberOfLiteralWords; + } + + /** + * Gets the running bit. + * + * @return the running bit + */ + @Override +public boolean getRunningBit() { + return this.brlw.RunningBit; + } + + /** + * Gets the running length. + * + * @return the running length + */ + @Override +public int getRunningLength() { + return this.brlw.RunningLength; + } + + /** + * Size in uncompressed words of the current running length word. + * + * @return the int + */ + @Override +public int size() { + return this.brlw.size(); + } + + /** + * write the first N literal words to the target bitmap. Does not discard the words or perform iteration. + * @param numWords number of words to be written + * @param container where we write the data + */ + public void writeLiteralWords(int numWords, BitmapStorage32 container) { + container.addStreamOfLiteralWords(this.buffer, this.literalWordStartPosition, numWords); + } + + + /** + * write the first N literal words (negated) to the target bitmap. Does not discard the words or perform iteration. + * @param numWords number of words to be written + * @param container where we write the data + */ + public void writeNegatedLiteralWords(int numWords, BitmapStorage32 container) { + container.addStreamOfNegatedLiteralWords(this.buffer, this.literalWordStartPosition, numWords); + } + + + /** + * For internal use. (One could use the non-static discharge method instead, + * but we expect them to be slower.) + * + * @param initialWord + * the initial word + * @param iterator + * the iterator + * @param container + * the container + */ + protected static void discharge( + final BufferedRunningLengthWord32 initialWord, + final EWAHIterator32 iterator, final BitmapStorage32 container) { + BufferedRunningLengthWord32 runningLengthWord = initialWord; + for (;;) { + final int runningLength = runningLengthWord.getRunningLength(); + container.addStreamOfEmptyWords(runningLengthWord.getRunningBit(), + runningLength); + container.addStreamOfLiteralWords(iterator.buffer(), iterator.literalWords() + + runningLengthWord.literalwordoffset, + runningLengthWord.getNumberOfLiteralWords()); + if (!iterator.hasNext()) + break; + runningLengthWord = new BufferedRunningLengthWord32(iterator.next()); + } + } + + + + @Override +public IteratingBufferedRunningLengthWord32 clone() throws CloneNotSupportedException { + IteratingBufferedRunningLengthWord32 answer = (IteratingBufferedRunningLengthWord32) super.clone(); + answer.brlw = this.brlw.clone(); + answer.buffer = this.buffer; + answer.iterator = this.iterator.clone(); + answer.literalWordStartPosition = this.literalWordStartPosition; + return answer; + } + + private BufferedRunningLengthWord32 brlw; + private int[] buffer; + private int literalWordStartPosition; + private EWAHIterator32 iterator; +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah32/IteratingRLW32.java b/fine-jgit/src/com/fr/third/googlecode/javaewah32/IteratingRLW32.java new file mode 100644 index 000000000..1ebeb2f62 --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah32/IteratingRLW32.java @@ -0,0 +1,42 @@ +package com.fr.third.googlecode.javaewah32; + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ + +/** + * High-level iterator over a compressed bitmap. + * + */ +public interface IteratingRLW32 { + /** + * @return whether there is more + */ + public boolean next() ; + /** + * @param index where the literal word is + * @return the literal word at the given index. + */ + public int getLiteralWordAt(int index); + /** + * @return the number of literal (non-fill) words + */ + public int getNumberOfLiteralWords() ; + /** + * @return the bit used for the fill bits + */ + public boolean getRunningBit() ; + /** + * @return sum of getRunningLength() and getNumberOfLiteralWords() + */ + public int size() ; + /** + * @return length of the run of fill words + */ + public int getRunningLength() ; + /** + * @param x the number of words to discard + */ + public void discardFirstWords(int x); +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah32/IteratorAggregation32.java b/fine-jgit/src/com/fr/third/googlecode/javaewah32/IteratorAggregation32.java new file mode 100644 index 000000000..cfd5a0adc --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah32/IteratorAggregation32.java @@ -0,0 +1,601 @@ +package com.fr.third.googlecode.javaewah32; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedList; + +import com.fr.third.googlecode.javaewah.CloneableIterator; + + + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ + +/** + * Set of helper functions to aggregate bitmaps. + * + */ +public class IteratorAggregation32 { + /** + * @param x iterator to negate + * @return negated version of the iterator + */ + public static IteratingRLW32 not(final IteratingRLW32 x) { + return new IteratingRLW32() { + + @Override + public boolean next() { + return x.next(); + } + + @Override + public int getLiteralWordAt(int index) { + return ~x.getLiteralWordAt(index); + } + + @Override + public int getNumberOfLiteralWords() { + return x.getNumberOfLiteralWords(); + } + + @Override + public boolean getRunningBit() { + return ! x.getRunningBit(); + } + + @Override + public int size() { + return x.size(); + } + + @Override + public int getRunningLength() { + return x.getRunningLength(); + } + + @Override + public void discardFirstWords(int y) { + x.discardFirstWords(y); + } + + }; + } + + /** + * Aggregate the iterators using a bitmap buffer. + * + * @param al iterators to aggregate + * @return and aggregate + */ + public static IteratingRLW32 bufferedand(final IteratingRLW32... al) { + return bufferedand (DEFAULTMAXBUFSIZE,al); + } + + /** + * Aggregate the iterators using a bitmap buffer. + * + * @param al iterators to aggregate + * @param bufsize size of the internal buffer used by the iterator in 64-bit words + * @return and aggregate + */ + public static IteratingRLW32 bufferedand(final int bufsize, final IteratingRLW32... al) { + if (al.length == 0) + throw new IllegalArgumentException("Need at least one iterator"); + if (al.length == 1) + return al[0]; + final LinkedList basell = new LinkedList(); + for (IteratingRLW32 i : al) + basell.add(i); + return new BufferedIterator32(new AndIt(basell,bufsize)); + } + + /** + * Aggregate the iterators using a bitmap buffer. + * + * @param al iterators to aggregate + * @return or aggregate + */ + public static IteratingRLW32 bufferedor(final IteratingRLW32... al) { + return bufferedor(DEFAULTMAXBUFSIZE,al); + } + + /** + * Aggregate the iterators using a bitmap buffer. + * + * @param al iterators to aggregate + * @param bufsize size of the internal buffer used by the iterator in 64-bit words + * @return or aggregate + */ + public static IteratingRLW32 bufferedor(final int bufsize, final IteratingRLW32... al) { + if (al.length == 0) + throw new IllegalArgumentException("Need at least one iterator"); + if (al.length == 1) + return al[0]; + + final LinkedList basell = new LinkedList(); + for (IteratingRLW32 i : al) + basell.add(i); + return new BufferedIterator32(new ORIt(basell,bufsize)); + } + + /** + * Aggregate the iterators using a bitmap buffer. + * + * @param al iterators to aggregate + * @return xor aggregate + */ + public static IteratingRLW32 bufferedxor(final IteratingRLW32... al) { + return bufferedxor (DEFAULTMAXBUFSIZE,al); + } + /** + * Aggregate the iterators using a bitmap buffer. + * + * @param al iterators to aggregate + * @param bufsize size of the internal buffer used by the iterator in 64-bit words + * @return xor aggregate + */ + public static IteratingRLW32 bufferedxor(final int bufsize, final IteratingRLW32... al) { + if (al.length == 0) + throw new IllegalArgumentException("Need at least one iterator"); + if (al.length == 1) + return al[0]; + + final LinkedList basell = new LinkedList(); + for (IteratingRLW32 i : al) + basell.add(i); + return new BufferedIterator32(new XORIt(basell,bufsize)); + } + /** + * Write out the content of the iterator, but as if it were all zeros. + * + * @param container + * where we write + * @param i + * the iterator + */ + protected static void dischargeAsEmpty(final BitmapStorage32 container, + final IteratingRLW32 i) { + while (i.size() > 0) { + container.addStreamOfEmptyWords(false, i.size()); + i.next(); + + } + } + + /** + * Write out up to max words, returns how many were written + * @param container target for writes + * @param i source of data + * @param max maximal number of writes + * @return how many written + */ + protected static int discharge(final BitmapStorage32 container, IteratingRLW32 i, int max) { + int counter = 0; + while (i.size() > 0 && counter < max) { + int L1 = i.getRunningLength(); + if (L1 > 0) { + if (L1 + counter > max) + L1 = max - counter; + container.addStreamOfEmptyWords(i.getRunningBit(), L1); + counter += L1; + } + int L = i.getNumberOfLiteralWords(); + if(L + counter > max) L = max - counter; + for (int k = 0; k < L; ++k) { + container.add(i.getLiteralWordAt(k)); + } + counter += L; + i.discardFirstWords(L+L1); + } + return counter; + } + + /** + * Write out up to max negated words, returns how many were written + * @param container target for writes + * @param i source of data + * @param max maximal number of writes + * @return how many written + */ + protected static int dischargeNegated(final BitmapStorage32 container, IteratingRLW32 i, int max) { + int counter = 0; + while (i.size() > 0 && counter < max) { + int L1 = i.getRunningLength(); + if (L1 > 0) { + if (L1 + counter > max) + L1 = max - counter; + container.addStreamOfEmptyWords(i.getRunningBit(), L1); + counter += L1; + } + int L = i.getNumberOfLiteralWords(); + if(L + counter > max) L = max - counter; + for (int k = 0; k < L; ++k) { + container.add(i.getLiteralWordAt(k)); + } + counter += L; + i.discardFirstWords(L+L1); + } + return counter; + } + + static void andToContainer(final BitmapStorage32 container, + int desiredrlwcount, final IteratingRLW32 rlwi, IteratingRLW32 rlwj) { + while ((rlwi.size()>0) && (rlwj.size()>0) && (desiredrlwcount-- >0) ) { + while ((rlwi.getRunningLength() > 0) || (rlwj.getRunningLength() > 0)) { + final boolean i_is_prey = rlwi.getRunningLength() < rlwj + .getRunningLength(); + final IteratingRLW32 prey = i_is_prey ? rlwi : rlwj; + final IteratingRLW32 predator = i_is_prey ? rlwj + : rlwi; + if (predator.getRunningBit() == false) { + container.addStreamOfEmptyWords(false, predator.getRunningLength()); + prey.discardFirstWords(predator.getRunningLength()); + predator.discardFirstWords(predator.getRunningLength()); + } else { + final int index = discharge(container, prey, predator.getRunningLength()); + container.addStreamOfEmptyWords(false, predator.getRunningLength() + - index); + predator.discardFirstWords(predator.getRunningLength()); + } + } + final int nbre_literal = Math.min(rlwi.getNumberOfLiteralWords(), + rlwj.getNumberOfLiteralWords()); + if (nbre_literal > 0) { + desiredrlwcount -= nbre_literal; + for (int k = 0; k < nbre_literal; ++k) + container.add(rlwi.getLiteralWordAt(k) & rlwj.getLiteralWordAt(k)); + rlwi.discardFirstWords(nbre_literal); + rlwj.discardFirstWords(nbre_literal); + } + } + } + + static void andToContainer(final BitmapStorage32 container, + final IteratingRLW32 rlwi, IteratingRLW32 rlwj) { + while ((rlwi.size()>0) && (rlwj.size()>0) ) { + while ((rlwi.getRunningLength() > 0) || (rlwj.getRunningLength() > 0)) { + final boolean i_is_prey = rlwi.getRunningLength() < rlwj + .getRunningLength(); + final IteratingRLW32 prey = i_is_prey ? rlwi : rlwj; + final IteratingRLW32 predator = i_is_prey ? rlwj + : rlwi; + if (predator.getRunningBit() == false) { + container.addStreamOfEmptyWords(false, predator.getRunningLength()); + prey.discardFirstWords(predator.getRunningLength()); + predator.discardFirstWords(predator.getRunningLength()); + } else { + final int index = discharge(container, prey, predator.getRunningLength()); + container.addStreamOfEmptyWords(false, predator.getRunningLength() + - index); + predator.discardFirstWords(predator.getRunningLength()); + } + } + final int nbre_literal = Math.min(rlwi.getNumberOfLiteralWords(), + rlwj.getNumberOfLiteralWords()); + if (nbre_literal > 0) { + for (int k = 0; k < nbre_literal; ++k) + container.add(rlwi.getLiteralWordAt(k) & rlwj.getLiteralWordAt(k)); + rlwi.discardFirstWords(nbre_literal); + rlwj.discardFirstWords(nbre_literal); + } + } + } + + + /** + * Compute the first few words of the XOR aggregate between two iterators. + * + * @param container where to write + * @param desiredrlwcount number of words to be written (max) + * @param rlwi first iterator to aggregate + * @param rlwj second iterator to aggregate + */ + public static void xorToContainer(final BitmapStorage32 container, + int desiredrlwcount, final IteratingRLW32 rlwi, IteratingRLW32 rlwj) { + while ((rlwi.size()>0) && (rlwj.size()>0) && (desiredrlwcount-- >0) ) { + while ((rlwi.getRunningLength() > 0) || (rlwj.getRunningLength() > 0)) { + final boolean i_is_prey = rlwi.getRunningLength() < rlwj + .getRunningLength(); + final IteratingRLW32 prey = i_is_prey ? rlwi : rlwj; + final IteratingRLW32 predator = i_is_prey ? rlwj + : rlwi; + if (predator.getRunningBit() == false) { + int index = discharge(container, prey, predator.getRunningLength()); + container.addStreamOfEmptyWords(false, predator.getRunningLength() + - index); + predator.discardFirstWords(predator.getRunningLength()); + } else { + int index = dischargeNegated(container, prey, predator.getRunningLength()); + container.addStreamOfEmptyWords(true, predator.getRunningLength() + - index); + predator.discardFirstWords(predator.getRunningLength()); + } + } + final int nbre_literal = Math.min(rlwi.getNumberOfLiteralWords(), + rlwj.getNumberOfLiteralWords()); + if (nbre_literal > 0) { + desiredrlwcount -= nbre_literal; + for (int k = 0; k < nbre_literal; ++k) + container.add(rlwi.getLiteralWordAt(k) ^ rlwj.getLiteralWordAt(k)); + rlwi.discardFirstWords(nbre_literal); + rlwj.discardFirstWords(nbre_literal); + } + } + } + + protected static int inplaceor(int[] bitmap, + IteratingRLW32 i) { + int pos = 0; + int s; + while ((s = i.size()) > 0) { + if (pos + s < bitmap.length) { + final int L = i.getRunningLength(); + if (i.getRunningBit()) + Arrays.fill(bitmap, pos, pos + L, ~0); + pos += L; + final int LR = i.getNumberOfLiteralWords(); + for (int k = 0; k < LR; ++k) + bitmap[pos++] |= i.getLiteralWordAt(k); + if (!i.next()) { + return pos; + } + } else { + int howmany = bitmap.length - pos; + int L = i.getRunningLength(); + if (pos + L > bitmap.length) { + if (i.getRunningBit()) { + Arrays.fill(bitmap, pos, bitmap.length, ~0); + } + i.discardFirstWords(howmany); + return bitmap.length; + } + if (i.getRunningBit()) + Arrays.fill(bitmap, pos, pos + L, ~0); + pos += L; + for (int k = 0; pos < bitmap.length; ++k) + bitmap[pos++] |= i.getLiteralWordAt(k); + i.discardFirstWords(howmany); + return pos; + } + } + return pos; + } + + + protected static int inplacexor(int[] bitmap, + IteratingRLW32 i) { + int pos = 0; + int s; + while ((s = i.size()) > 0) { + if (pos + s < bitmap.length) { + final int L = i.getRunningLength(); + if (i.getRunningBit()) { + for(int k = pos ; k < pos + L; ++k) + bitmap[k] = ~bitmap[k]; + } + pos += L; + final int LR = i.getNumberOfLiteralWords(); + for (int k = 0; k < LR; ++k) + bitmap[pos++] ^= i.getLiteralWordAt(k); + if (!i.next()) { + return pos; + } + } else { + int howmany = bitmap.length - pos; + int L = i.getRunningLength(); + if (pos + L > bitmap.length) { + if (i.getRunningBit()) { + for(int k = pos ; k < bitmap.length; ++k) + bitmap[k] = ~bitmap[k]; + } + i.discardFirstWords(howmany); + return bitmap.length; + } + if (i.getRunningBit()) + for(int k = pos ; k < pos + L; ++k) + bitmap[k] = ~bitmap[k]; + pos += L; + for (int k = 0; pos < bitmap.length; ++k) + bitmap[pos++] ^= i.getLiteralWordAt(k); + i.discardFirstWords(howmany); + return pos; + } + } + return pos; + } + protected static int inplaceand(int[] bitmap, + IteratingRLW32 i) { + int pos = 0; + int s; + while ((s = i.size()) > 0) { + if (pos + s < bitmap.length) { + final int L = i.getRunningLength(); + if (!i.getRunningBit()) { + for(int k = pos ; k < pos + L; ++k) + bitmap[k] = 0; + } + pos += L; + final int LR = i.getNumberOfLiteralWords(); + for (int k = 0; k < LR; ++k) + bitmap[pos++] &= i.getLiteralWordAt(k); + if (!i.next()) { + return pos; + } + } else { + int howmany = bitmap.length - pos; + int L = i.getRunningLength(); + if (pos + L > bitmap.length) { + if (!i.getRunningBit()) { + for(int k = pos ; k < bitmap.length; ++k) + bitmap[k] = 0; + } + i.discardFirstWords(howmany); + return bitmap.length; + } + if (!i.getRunningBit()) + for(int k = pos ; k < pos + L; ++k) + bitmap[k] = 0; + pos += L; + for (int k = 0; pos < bitmap.length; ++k) + bitmap[pos++] &= i.getLiteralWordAt(k); + i.discardFirstWords(howmany); + return pos; + } + } + return pos; + } + + /** + * An optimization option. Larger values may improve speed, but at + * the expense of memory. + */ + public final static int DEFAULTMAXBUFSIZE = 65536; + + +} + + +class ORIt implements CloneableIterator { + EWAHCompressedBitmap32 buffer = new EWAHCompressedBitmap32(); + int[] hardbitmap; + LinkedList ll; + + ORIt(LinkedList basell, final int bufsize) { + this.ll = basell; + this.hardbitmap = new int[bufsize]; + } + + @Override + public XORIt clone() throws CloneNotSupportedException { + XORIt answer = (XORIt) super.clone(); + answer.buffer = this.buffer.clone(); + answer.hardbitmap = this.hardbitmap.clone(); + answer.ll = (LinkedList) this.ll.clone(); + return answer; + } + + @Override + public boolean hasNext() { + return !this.ll.isEmpty(); + } + + @Override + public EWAHIterator32 next() { + this.buffer.clear(); + int effective = 0; + Iterator i = this.ll.iterator(); + while (i.hasNext()) { + IteratingRLW32 rlw = i.next(); + if (rlw.size() > 0) { + int eff = IteratorAggregation32.inplaceor(this.hardbitmap, rlw); + if (eff > effective) + effective = eff; + } else + i.remove(); + } + for (int k = 0; k < effective; ++k) + this.buffer.add(this.hardbitmap[k]); + Arrays.fill(this.hardbitmap, 0); + return this.buffer.getEWAHIterator(); + } +} + +class XORIt implements CloneableIterator { + EWAHCompressedBitmap32 buffer = new EWAHCompressedBitmap32(); + int[] hardbitmap; + LinkedList ll; + + XORIt(LinkedList basell, final int bufsize) { + this.ll = basell; + this.hardbitmap = new int[bufsize]; + + } + + @Override + public XORIt clone() throws CloneNotSupportedException { + XORIt answer = (XORIt) super.clone(); + answer.buffer = this.buffer.clone(); + answer.hardbitmap = this.hardbitmap.clone(); + answer.ll = (LinkedList) this.ll.clone(); + return answer; + } + + @Override + public boolean hasNext() { + return !this.ll.isEmpty(); + } + + @Override + public EWAHIterator32 next() { + this.buffer.clear(); + int effective = 0; + Iterator i = this.ll.iterator(); + while (i.hasNext()) { + IteratingRLW32 rlw = i.next(); + if (rlw.size() > 0) { + int eff = IteratorAggregation32.inplacexor(this.hardbitmap, rlw); + if (eff > effective) + effective = eff; + } else + i.remove(); + } + for (int k = 0; k < effective; ++k) + this.buffer.add(this.hardbitmap[k]); + Arrays.fill(this.hardbitmap, 0); + return this.buffer.getEWAHIterator(); + } +} + +class AndIt implements CloneableIterator { + EWAHCompressedBitmap32 buffer = new EWAHCompressedBitmap32(); + LinkedList ll; + int buffersize; + + public AndIt(LinkedList basell, final int bufsize) { + this.ll = basell; + this.buffersize = bufsize; + } + + @Override + public boolean hasNext() { + return !this.ll.isEmpty(); + } + + @Override + public AndIt clone() throws CloneNotSupportedException { + AndIt answer = (AndIt) super.clone(); + answer.buffer = this.buffer.clone(); + answer.ll = (LinkedList) this.ll.clone(); + return answer; + } + + @Override + public EWAHIterator32 next() { + this.buffer.clear(); + IteratorAggregation32.andToContainer(this.buffer, this.buffersize * this.ll.size(), + this.ll.get(0), this.ll.get(1)); + if (this.ll.size() > 2) { + Iterator i = this.ll.iterator(); + i.next(); + i.next(); + EWAHCompressedBitmap32 tmpbuffer = new EWAHCompressedBitmap32(); + while (i.hasNext() && this.buffer.sizeInBytes() > 0) { + IteratorAggregation32.andToContainer(tmpbuffer, + this.buffer.getIteratingRLW(), i.next()); + this.buffer.swap(tmpbuffer); + tmpbuffer.clear(); + } + } + Iterator i = this.ll.iterator(); + while(i.hasNext()) { + if(i.next().size() == 0) { + this.ll.clear(); + break; + } + } + return this.buffer.getEWAHIterator(); + } + +} \ No newline at end of file diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah32/IteratorUtil32.java b/fine-jgit/src/com/fr/third/googlecode/javaewah32/IteratorUtil32.java new file mode 100644 index 000000000..d8e415101 --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah32/IteratorUtil32.java @@ -0,0 +1,135 @@ +package com.fr.third.googlecode.javaewah32; + +import java.util.Iterator; + +import com.fr.third.googlecode.javaewah.IntIterator; + + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ +/** + * Convenience functions for working over iterators + * + */ +public class IteratorUtil32 { + + /** + * @param i iterator we wish to iterate over + * @return an iterator over the set bits corresponding to the iterator + */ + public static IntIterator toSetBitsIntIterator(final IteratingRLW32 i) { + return new IntIteratorOverIteratingRLW32(i); + } + + /** + * @param i iterator we wish to iterate over + * @return an iterator over the set bits corresponding to the iterator + */ + public static Iterator toSetBitsIterator(final IteratingRLW32 i) { + return new Iterator() { + @Override + public boolean hasNext() { + return this.under.hasNext(); + } + + @Override + public Integer next() { + return new Integer(this.under.next()); + } + + @Override + public void remove() { + } + + final private IntIterator under = toSetBitsIntIterator(i); + }; + + } + + /** + * Turn an iterator into a bitmap + * @param i iterator we wish to materialize + * @param c where we write + */ + public static void materialize(final IteratingRLW32 i, final BitmapStorage32 c) { + while (true) { + if (i.getRunningLength() > 0) { + c.addStreamOfEmptyWords(i.getRunningBit(), i.getRunningLength()); + } + for (int k = 0; k < i.getNumberOfLiteralWords(); ++k) + c.add(i.getLiteralWordAt(k)); + if (!i.next()) + break; + } + } + + /** + * @param i iterator we wish to iterate over + * @return the cardinality (number of set bits) corresponding to the iterator + */ + public static int cardinality(final IteratingRLW32 i) { + int answer = 0; + while (true) { + if(i.getRunningBit()) answer += i.getRunningLength() * EWAHCompressedBitmap32.wordinbits; + for (int k = 0; k < i.getNumberOfLiteralWords(); ++k) + answer += Long.bitCount(i.getLiteralWordAt(k)); + if(!i.next()) break; + } + return answer; + } + + /** + * + * @param x set of bitmaps we wish to iterate over + * @return an array of iterators corresponding to the array of bitmaps + */ + public static IteratingRLW32[] toIterators(final EWAHCompressedBitmap32... x) { + IteratingRLW32[] X = new IteratingRLW32[x.length]; + for (int k = 0; k < X.length; ++k) { + X[k] = new IteratingBufferedRunningLengthWord32(x[k]); + } + return X; + } + /** + * Turn an iterator into a bitmap + * + * @param i iterator we wish to materialize + * @param c where we write + * @param Max maximum number of words to materialize + * @return how many words were actually materialized + */ + public static long materialize(final IteratingRLW32 i, final BitmapStorage32 c, int Max) { + final int origMax = Max; + while (true) { + if (i.getRunningLength() > 0) { + int L = i.getRunningLength(); + if(L > Max) L = Max; + c.addStreamOfEmptyWords(i.getRunningBit(), L); + Max -= L; + } + long L = i.getNumberOfLiteralWords(); + for (int k = 0; k < L; ++k) + c.add(i.getLiteralWordAt(k)); + if(Max>0) { + if (!i.next()) + break; + } + else break; + } + return origMax - Max; + } + /** + * Turn an iterator into a bitmap + * + * @param i iterator we wish to materialize + * @return materialized version of the iterator + */ + public static EWAHCompressedBitmap32 materialize(final IteratingRLW32 i) { + EWAHCompressedBitmap32 ewah = new EWAHCompressedBitmap32(); + materialize(i, ewah); + return ewah; + } + +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah32/NonEmptyVirtualStorage32.java b/fine-jgit/src/com/fr/third/googlecode/javaewah32/NonEmptyVirtualStorage32.java new file mode 100644 index 000000000..94e8bd96a --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah32/NonEmptyVirtualStorage32.java @@ -0,0 +1,87 @@ +package com.fr.third.googlecode.javaewah32; + + + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ +/** + * This is a BitmapStorage that can be used to determine quickly + * if the result of an operation is non-trivial... that is, whether + * there will be at least on set bit. + * + * @since 0.5.0 + * @author Daniel Lemire and Veronika Zenz + * + */ +public class NonEmptyVirtualStorage32 implements BitmapStorage32 { + static class NonEmptyException extends RuntimeException { + private static final long serialVersionUID = 1L; + + /** + * Do not fill in the stack trace for this exception + * for performance reasons. + * + * @return this instance + * @see Throwable#fillInStackTrace() + */ + @Override + public synchronized Throwable fillInStackTrace() { + return this; + } + } + + private static final NonEmptyException nonEmptyException = new NonEmptyException(); + + + /** + * If the word to be added is non-zero, a NonEmptyException exception is thrown. + */ + @Override +public void add(int newdata) { + if(newdata!=0) throw nonEmptyException; + } + + /** + * throws a NonEmptyException exception when number is greater than 0 + * + */ + @Override +public void addStreamOfLiteralWords(int[] data, int start, int number) { + if (number > 0){ + throw nonEmptyException; + } + } + + /** + * If the boolean value is true and number is greater than 0, then it throws a NonEmptyException exception, + * otherwise, nothing happens. + * + */ + @Override +public void addStreamOfEmptyWords(boolean v, int number) { + if(v && (number>0)) throw nonEmptyException; + } + + /** + * throws a NonEmptyException exception when number is greater than 0 + * + */ + @Override +public void addStreamOfNegatedLiteralWords(int[] data, int start, int number) { + if (number > 0){ + throw nonEmptyException; + } + } + + /** + * Does nothing. + * + * @see com.googlecode.javaewah.BitmapStorage#setSizeInBits(int) + */ + @Override +public void setSizeInBits(int bits) { + } + +} diff --git a/fine-jgit/src/com/fr/third/googlecode/javaewah32/RunningLengthWord32.java b/fine-jgit/src/com/fr/third/googlecode/javaewah32/RunningLengthWord32.java new file mode 100644 index 000000000..37f52ee83 --- /dev/null +++ b/fine-jgit/src/com/fr/third/googlecode/javaewah32/RunningLengthWord32.java @@ -0,0 +1,152 @@ +package com.fr.third.googlecode.javaewah32; + +/* + * Copyright 2009-2013, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz and Owen Kaser + * Licensed under the Apache License, Version 2.0. + */ + +/** + * Mostly for internal use. + * + * @since 0.5.0 + * @author Daniel Lemire + */ +public final class RunningLengthWord32 implements Cloneable { + + /** + * Instantiates a new running length word. + * + * @param a + * an array of 32-bit words + * @param p + * position in the array where the running length word is + * located. + */ + RunningLengthWord32(final EWAHCompressedBitmap32 a, final int p) { + this.parent = a; + this.position = p; + } + + /** + * Gets the number of literal words. + * + * @return the number of literal words + */ + public int getNumberOfLiteralWords() { + return (this.parent.buffer[this.position] >>> (1 + runninglengthbits)); + } + + /** + * Gets the running bit. + * + * @return the running bit + */ + public boolean getRunningBit() { + return (this.parent.buffer[this.position] & 1) != 0; + } + + /** + * Gets the running length. + * + * @return the running length + */ + public int getRunningLength() { + return (this.parent.buffer[this.position] >>> 1) + & largestrunninglengthcount; + } + + /** + * Sets the number of literal words. + * + * @param number + * the new number of literal words + */ + public void setNumberOfLiteralWords(final int number) { + this.parent.buffer[this.position] |= notrunninglengthplusrunningbit; + this.parent.buffer[this.position] &= (number << (runninglengthbits + 1)) + | runninglengthplusrunningbit; + } + + /** + * Sets the running bit. + * + * @param b + * the new running bit + */ + public void setRunningBit(final boolean b) { + if (b) + this.parent.buffer[this.position] |= 1; + else + this.parent.buffer[this.position] &= ~1; + } + + /** + * Sets the running length. + * + * @param number + * the new running length + */ + public void setRunningLength(final int number) { + this.parent.buffer[this.position] |= shiftedlargestrunninglengthcount; + this.parent.buffer[this.position] &= (number << 1) + | notshiftedlargestrunninglengthcount; + } + + /** + * Return the size in uncompressed words represented by this running + * length word. + * + * @return the int + */ + public int size() { + return getRunningLength() + getNumberOfLiteralWords(); + } + + /* + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "running bit = " + getRunningBit() + + " running length = " + getRunningLength() + + " number of lit. words " + getNumberOfLiteralWords(); + } + + @Override + public RunningLengthWord32 clone() throws CloneNotSupportedException { + RunningLengthWord32 answer; + answer = (RunningLengthWord32) super.clone(); + answer.parent = this.parent; + answer.position = this.position; + return answer; + } + + /** The array of words. */ + public EWAHCompressedBitmap32 parent; + + /** The position in array. */ + public int position; + + /** + * number of bits dedicated to marking of the running length of clean + * words + */ + public static final int runninglengthbits = 16; + + private static final int literalbits = 32 - 1 - runninglengthbits; + + /** largest number of literal words in a run. */ + public static final int largestliteralcount = (1 << literalbits) - 1; + + /** largest number of clean words in a run */ + public static final int largestrunninglengthcount = (1 << runninglengthbits) - 1; + + private static final int runninglengthplusrunningbit = (1 << (runninglengthbits + 1)) - 1; + + private static final int shiftedlargestrunninglengthcount = largestrunninglengthcount << 1; + + private static final int notrunninglengthplusrunningbit = ~runninglengthplusrunningbit; + + private static final int notshiftedlargestrunninglengthcount = ~shiftedlargestrunninglengthcount; + +} \ No newline at end of file From 665622b93f2a2882ac0ec8458ac74d278c84adc3 Mon Sep 17 00:00:00 2001 From: "Hugh.C" Date: Fri, 6 Sep 2019 17:26:51 +0800 Subject: [PATCH 2/2] =?UTF-8?q?REPORT-20981=20=E5=88=A0=E9=99=A4=E8=87=AA?= =?UTF-8?q?=E5=B7=B1=E5=86=99=E7=9A=84=E5=A4=9A=E4=BD=99=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../storage/dfs/RemoteRepository.java | 272 ------------------ 1 file changed, 272 deletions(-) delete mode 100644 fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/dfs/RemoteRepository.java diff --git a/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/dfs/RemoteRepository.java b/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/dfs/RemoteRepository.java deleted file mode 100644 index 0bbc4a2cc..000000000 --- a/fine-jgit/src/com/fr/third/eclipse/jgit/internal/storage/dfs/RemoteRepository.java +++ /dev/null @@ -1,272 +0,0 @@ -package com.fr.third.eclipse.jgit.internal.storage.dfs; - -import com.fr.third.eclipse.jgit.internal.storage.pack.PackExt; -import com.fr.third.eclipse.jgit.lib.ObjectId; -import com.fr.third.eclipse.jgit.lib.Ref; -import com.fr.third.eclipse.jgit.revwalk.RevWalk; -import com.fr.third.eclipse.jgit.util.RefList; - -import java.io.ByteArrayOutputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.atomic.AtomicInteger; - -public class RemoteRepository extends DfsRepository { - private static final AtomicInteger packId = new AtomicInteger(); - - private final DfsObjDatabase objdb; - - private final DfsRefDatabase refdb; - - /** - * Initialize a new remote repository. - * - * @param repoDesc - * description of the repository. - * @since 2.0 - */ - public RemoteRepository(DfsRepositoryDescription repoDesc) { - super(new DfsRepositoryBuilder() { - @Override - public RemoteRepository build() throws IOException { - throw new UnsupportedOperationException(); - } - }.setRepositoryDescription(repoDesc)); - - objdb = new RemoteRepository.RemoteObjDatabase(this); - refdb = new RemoteRepository.RemoteRefDatabase(); - } - - @Override - public DfsObjDatabase getObjectDatabase() { - return objdb; - } - - @Override - public DfsRefDatabase getRefDatabase() { - return refdb; - } - - private class RemoteObjDatabase extends DfsObjDatabase { - private List packs = new ArrayList(); - - RemoteObjDatabase(DfsRepository repo) { - super(repo, new DfsReaderOptions()); - } - - @Override - protected synchronized List listPacks() { - return packs; - } - - @Override - protected DfsPackDescription newPack(PackSource source) { - int id = packId.incrementAndGet(); - DfsPackDescription desc = new RemoteRepository.MemPack( - "pack-" + id + "-" + source.name(), //$NON-NLS-1$ //$NON-NLS-2$ - getRepository().getDescription()); - return desc.setPackSource(source); - } - - @Override - protected synchronized void commitPackImpl( - Collection desc, - Collection replace) { - List n; - n = new ArrayList(desc.size() + packs.size()); - n.addAll(desc); - n.addAll(packs); - if (replace != null) - n.removeAll(replace); - packs = n; - } - - @Override - protected void rollbackPack(Collection desc) { - // Do nothing. Pack is not recorded until commitPack. - } - - @Override - protected ReadableChannel openFile(DfsPackDescription desc, PackExt ext) - throws FileNotFoundException, IOException { - RemoteRepository.MemPack memPack = (RemoteRepository.MemPack) desc; - byte[] file = memPack.fileMap.get(ext); - if (file == null) - throw new FileNotFoundException(desc.getFileName(ext)); - return new RemoteRepository.ByteArrayReadableChannel(file); - } - - @Override - protected DfsOutputStream writeFile( - DfsPackDescription desc, final PackExt ext) throws IOException { - final RemoteRepository.MemPack memPack = (RemoteRepository.MemPack) desc; - return new RemoteRepository.Out() { - @Override - public void flush() { - memPack.fileMap.put(ext, getData()); - } - }; - } - } - - private static class MemPack extends DfsPackDescription { - private final Map - fileMap = new HashMap(); - - MemPack(String name, DfsRepositoryDescription repoDesc) { - super(repoDesc, name); - } - } - - private abstract static class Out extends DfsOutputStream { - private final ByteArrayOutputStream dst = new ByteArrayOutputStream(); - - private byte[] data; - - @Override - public void write(byte[] buf, int off, int len) { - data = null; - dst.write(buf, off, len); - } - - @Override - public int read(long position, ByteBuffer buf) { - byte[] d = getData(); - int n = Math.min(buf.remaining(), d.length - (int) position); - if (n == 0) - return -1; - buf.put(d, (int) position, n); - return n; - } - - byte[] getData() { - if (data == null) - data = dst.toByteArray(); - return data; - } - - @Override - public abstract void flush(); - - @Override - public void close() { - flush(); - } - - } - - private static class ByteArrayReadableChannel implements ReadableChannel { - private final byte[] data; - - private int position; - - private boolean open = true; - - ByteArrayReadableChannel(byte[] buf) { - data = buf; - } - - public int read(ByteBuffer dst) { - int n = Math.min(dst.remaining(), data.length - position); - if (n == 0) - return -1; - dst.put(data, position, n); - position += n; - return n; - } - - public void close() { - open = false; - } - - public boolean isOpen() { - return open; - } - - public long position() { - return position; - } - - public void position(long newPosition) { - position = (int) newPosition; - } - - public long size() { - return data.length; - } - - public int blockSize() { - return 0; - } - } - - private class RemoteRefDatabase extends DfsRefDatabase { - private final ConcurrentMap refs = new ConcurrentHashMap(); - - RemoteRefDatabase() { - super(RemoteRepository.this); - } - - @Override - protected RefCache scanAllRefs() throws IOException { - RefList.Builder ids = new RefList.Builder(); - RefList.Builder sym = new RefList.Builder(); - for (Ref ref : refs.values()) { - if (ref.isSymbolic()) - sym.add(ref); - ids.add(ref); - } - ids.sort(); - sym.sort(); - return new RefCache(ids.toRefList(), sym.toRefList()); - } - - @Override - protected boolean compareAndPut(Ref oldRef, Ref newRef) - throws IOException { - ObjectId id = newRef.getObjectId(); - if (id != null) { - RevWalk rw = new RevWalk(getRepository()); - try { - // Validate that the target exists in a new RevWalk, as the RevWalk - // from the RefUpdate might be reading back unflushed objects. - rw.parseAny(id); - } finally { - rw.release(); - } - } - String name = newRef.getName(); - if (oldRef == null || oldRef.getStorage() == Ref.Storage.NEW) - return refs.putIfAbsent(name, newRef) == null; - Ref cur = refs.get(name); - if (cur != null && eq(cur, oldRef)) - return refs.replace(name, cur, newRef); - else - return false; - - } - - @Override - protected boolean compareAndRemove(Ref oldRef) throws IOException { - String name = oldRef.getName(); - Ref cur = refs.get(name); - if (cur != null && eq(cur, oldRef)) - return refs.remove(name, cur); - else - return false; - } - - private boolean eq(Ref a, Ref b) { - if (a.getObjectId() == null && b.getObjectId() == null) - return true; - if (a.getObjectId() != null) - return a.getObjectId().equals(b.getObjectId()); - return false; - } - } -} -