You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datasketches.apache.org by le...@apache.org on 2025/05/23 23:04:47 UTC

(datasketches-java) branch thetaRework updated: Theta rework, phase 1, no testing yet!

This is an automated email from the ASF dual-hosted git repository.

leerho pushed a commit to branch thetaRework
in repository https://gitbox.apache.org/repos/asf/datasketches-java.git


The following commit(s) were added to refs/heads/thetaRework by this push:
     new 6008cb35 Theta rework, phase 1, no testing yet!
6008cb35 is described below

commit 6008cb35f7dc075d24d0a47af687f15b36a79eee
Author: Lee Rhodes <le...@gmail.com>
AuthorDate: Fri May 23 16:04:37 2025 -0700

    Theta rework, phase 1, no testing yet!
---
 .../java/org/apache/datasketches/common/Util.java  |  112 +-
 .../java/org/apache/datasketches/theta2/AnotB.java |  205 +
 .../org/apache/datasketches/theta2/AnotBimpl.java  |  241 +
 .../org/apache/datasketches/theta2/BitPacking.java | 6292 ++++++++++++++++++++
 .../theta2/BytesCompactCompressedHashIterator.java |   93 +
 .../theta2/BytesCompactHashIterator.java           |   53 +
 .../datasketches/theta2/CompactOperations.java     |  388 ++
 .../apache/datasketches/theta2/CompactSketch.java  |  478 ++
 .../ConcurrentBackgroundThetaPropagation.java      |  110 +
 .../theta2/ConcurrentDirectQuickSelectSketch.java  |  270 +
 .../theta2/ConcurrentHeapQuickSelectSketch.java    |  266 +
 .../theta2/ConcurrentHeapThetaBuffer.java          |  223 +
 .../theta2/ConcurrentPropagationService.java       |   72 +
 .../theta2/ConcurrentSharedThetaSketch.java        |  187 +
 .../theta2/DirectCompactCompressedSketch.java      |  142 +
 .../datasketches/theta2/DirectCompactSketch.java   |  174 +
 .../theta2/DirectQuickSelectSketch.java            |  339 ++
 .../theta2/DirectQuickSelectSketchR.java           |  284 +
 .../datasketches/theta2/EmptyCompactSketch.java    |  147 +
 .../datasketches/theta2/ForwardCompatibility.java  |  164 +
 .../apache/datasketches/theta2/HashIterator.java   |   40 +
 .../datasketches/theta2/HeapAlphaSketch.java       |  601 ++
 .../theta2/HeapCompactHashIterator.java            |   41 +
 .../datasketches/theta2/HeapCompactSketch.java     |  158 +
 .../datasketches/theta2/HeapHashIterator.java      |   54 +
 .../datasketches/theta2/HeapQuickSelectSketch.java |  326 +
 .../datasketches/theta2/HeapUpdateSketch.java      |  139 +
 .../apache/datasketches/theta2/Intersection.java   |  218 +
 .../datasketches/theta2/IntersectionImpl.java      |  561 ++
 .../MemoryCompactCompressedHashIterator.java       |  108 +
 .../datasketches/theta2/MemoryHashIterator.java    |   62 +
 .../apache/datasketches/theta2/PreambleUtil.java   |  533 ++
 .../org/apache/datasketches/theta2/Rebuilder.java  |  175 +
 .../apache/datasketches/theta2/SetOperation.java   |  259 +
 .../datasketches/theta2/SetOperationBuilder.java   |  275 +
 .../datasketches/theta2/SingleItemSketch.java      |  413 ++
 .../org/apache/datasketches/theta2/Sketch.java     |  695 +++
 .../java/org/apache/datasketches/theta2/Union.java |  231 +
 .../org/apache/datasketches/theta2/UnionImpl.java  |  365 ++
 .../datasketches/theta2/UpdateReturnState.java     |   79 +
 .../apache/datasketches/theta2/UpdateSketch.java   |  469 ++
 .../datasketches/theta2/UpdateSketchBuilder.java   |  493 ++
 .../theta2/WrappedCompactCompressedSketch.java     |  111 +
 .../datasketches/theta2/WrappedCompactSketch.java  |  159 +
 .../apache/datasketches/theta2/package-info.java   |   27 +
 .../datasketches/thetacommon/HashOperations.java   |  104 +
 46 files changed, 16934 insertions(+), 2 deletions(-)

diff --git a/src/main/java/org/apache/datasketches/common/Util.java b/src/main/java/org/apache/datasketches/common/Util.java
index 19a8ee61..969cdc38 100644
--- a/src/main/java/org/apache/datasketches/common/Util.java
+++ b/src/main/java/org/apache/datasketches/common/Util.java
@@ -24,9 +24,11 @@ import static java.lang.Math.floor;
 import static java.lang.Math.log;
 import static java.lang.Math.pow;
 import static java.lang.Math.round;
-import static java.util.Arrays.fill;
+import static java.lang.foreign.ValueLayout.JAVA_BYTE;
 
+import java.lang.foreign.MemorySegment;
 import java.util.Comparator;
+import java.util.Objects;
 
 /**
  * Common utility functions.
@@ -257,7 +259,7 @@ public final class Util {
     final int sLen = s.length();
     if (sLen < fieldLength) {
       final char[] cArr = new char[fieldLength - sLen];
-      fill(cArr, padChar);
+      java.util.Arrays.fill(cArr, padChar);
       final String addstr = String.valueOf(cArr);
       return (postpend) ? s.concat(addstr) : addstr.concat(s);
     }
@@ -798,4 +800,110 @@ public final class Util {
     return c.compare((T)item1, (T)item2) <= 0;
   }
 
+  //MemorySegment related
+
+  /**
+   * Returns true if the two given MemorySegments refer to the same backing resource,
+   * which is either an off-heap memory location and size, or the same on-heap array object.
+   *
+   * <p>If both segment are off-heap, they both must have the same starting address and the same size.</p>
+   *
+   * <p>For on-heap segments, both segments must be based on or derived from the same array object and neither segment
+   * can be read-only.</p>
+   *
+   * @param seg1 The first given MemorySegment
+   * @param seg2 The second given MemorySegment
+   * @return true if both MemorySegments are determined to be the same backing memory.
+   */
+  public static boolean isSameResource(final MemorySegment seg1, final MemorySegment seg2) {
+    Objects.requireNonNull(seg1, "seg1 must not be null.");
+    Objects.requireNonNull(seg2, "seg2 must not be null.");
+    if (!seg1.scope().isAlive() || !seg2.scope().isAlive()) {
+      throw new IllegalArgumentException("Both arguments must be alive.");
+    }
+    final boolean seg1Native = seg1.isNative();
+    final boolean seg2Native = seg2.isNative();
+    if (seg1Native ^ seg2Native) { return false; }
+    if (seg1Native && seg2Native) { //both off heap
+      return (seg1.address() == seg2.address()) && (seg1.byteSize() == seg2.byteSize());
+    }
+    //both on heap
+    if (seg1.isReadOnly() || seg2.isReadOnly()) {
+      throw new IllegalArgumentException("Cannot determine 'isSameBackingMemory(..)' on heap if either MemorySegment is Read-only.");
+    }
+    return (seg1.heapBase().orElse(null) == seg2.heapBase().orElse(null));
+  }
+
+  /**
+   * Request a new heap MemorySegment with the given capacityBytes.
+   *
+   * <p>The returned MemorySegment will be constructed from a <i>long[]</i> array.
+   * As a result, it will be on-heap and have a memory alignment of 8.
+   * If the requested capacity is not divisible by eight, the returned size
+   * will be rolled up to the next multiple of eight.</p>
+   *
+   * @param capacityBytes The new capacity being requested.
+   * @return a new MemorySegment with the requested capacity.
+   */
+  public static MemorySegment newHeapSegment(final int capacityBytes) {
+    if (capacityBytes < 0) {
+      throw new IllegalArgumentException("Requested capacity must be positive.");
+    }
+    final long[] array = ((capacityBytes * 0x7) == 0) ? new long[capacityBytes >>> 3] : new long[(capacityBytes >>> 3) + 1];
+    return MemorySegment.ofArray(array);
+  }
+
+  /**
+   * Clears all bytes of this MemorySegment to zero.
+   * @param seg the given MemorySegment
+   */
+  public static void clear(final MemorySegment seg) {
+    seg.fill((byte)0);
+  }
+
+  /**
+   * Clears a portion of this MemorySegment to zero.
+   * @param seg the given MemorySegment
+   * @param offsetBytes offset bytes relative to this MemorySegment start
+   * @param lengthBytes the length in bytes
+   */
+  public static void clear(final MemorySegment seg, final long offsetBytes, final long lengthBytes) {
+    final MemorySegment slice = seg.asSlice(offsetBytes, lengthBytes);
+    slice.fill((byte)0);
+  }
+
+  /**
+   * Fills a portion of this Memory region to the given byte value.
+   * @param seg the given MemorySegment
+   * @param offsetBytes offset bytes relative to this Memory start
+   * @param lengthBytes the length in bytes
+   * @param value the given byte value
+   */
+  public static void fill(final MemorySegment seg, final long offsetBytes, final long lengthBytes, final byte value) {
+    final MemorySegment slice = seg.asSlice(offsetBytes, lengthBytes);
+    slice.fill(value);
+  }
+
+  /**
+   * Clears the bits defined by the bitMask
+   * @param seg the given MemorySegment
+   * @param offsetBytes offset bytes relative to this Memory start.
+   * @param bitMask the bits set to one will be cleared
+   */
+  public static void clearBits(final MemorySegment seg, final long offsetBytes, final byte bitMask) {
+    final byte b = seg.get(JAVA_BYTE, offsetBytes);
+    seg.set(JAVA_BYTE, offsetBytes, (byte)(b & ~bitMask));
+  }
+
+  /**
+   * Sets the bits defined by the bitMask
+   * @param seg the given MemorySegment
+   * @param offsetBytes offset bytes relative to this Memory start
+   * @param bitMask the bits set to one will be set
+   */
+  public static void setBits(final MemorySegment seg, final long offsetBytes, final byte bitMask) {
+    final byte b = seg.get(JAVA_BYTE, offsetBytes);
+    seg.set(JAVA_BYTE, offsetBytes, (byte)(b | bitMask));
+  }
+
 }
diff --git a/src/main/java/org/apache/datasketches/theta2/AnotB.java b/src/main/java/org/apache/datasketches/theta2/AnotB.java
new file mode 100644
index 00000000..72f7e89b
--- /dev/null
+++ b/src/main/java/org/apache/datasketches/theta2/AnotB.java
@@ -0,0 +1,205 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.datasketches.theta2;
+
+import java.lang.foreign.MemorySegment;
+
+import org.apache.datasketches.common.Family;
+
+/**
+ * Computes a set difference, A-AND-NOT-B, of two theta sketches.
+ * This class includes both stateful and stateless operations.
+ *
+ * <p>The stateful operation is as follows:</p>
+ * <pre><code>
+ * AnotB anotb = SetOperationBuilder.buildAnotB();
+ *
+ * anotb.setA(Sketch skA); //The first argument.
+ * anotb.notB(Sketch skB); //The second (subtraction) argument.
+ * anotb.notB(Sketch skC); // ...any number of additional subtractions...
+ * anotb.getResult(false); //Get an interim result.
+ * anotb.notB(Sketch skD); //Additional subtractions.
+ * anotb.getResult(true);  //Final result and resets the AnotB operator.
+ * </code></pre>
+ *
+ * <p>The stateless operation is as follows:</p>
+ * <pre><code>
+ * AnotB anotb = SetOperationBuilder.buildAnotB();
+ *
+ * CompactSketch csk = anotb.aNotB(Sketch skA, Sketch skB);
+ * </code></pre>
+ *
+ * <p>Calling the <i>setA</i> operation a second time essentially clears the internal state and loads
+ * the new sketch.</p>
+ *
+ * <p>The stateless and stateful operations are independent of each other with the exception of
+ * sharing the same update hash seed loaded as the default seed or specified by the user as an
+ * argument to the builder.</p>
+ *
+ * @author Lee Rhodes
+ */
+public abstract class AnotB extends SetOperation {
+
+  /**
+   * Constructor
+   */
+  AnotB() {}
+
+  @Override
+  public Family getFamily() {
+    return Family.A_NOT_B;
+  }
+
+  /**
+   * This is part of a multistep, stateful AnotB operation and sets the given Theta sketch as the
+   * first argument <i>A</i> of <i>A-AND-NOT-B</i>. This overwrites the internal state of this
+   * AnotB operator with the contents of the given sketch.
+   * This sets the stage for multiple following <i>notB</i> steps.
+   *
+   * <p>An input argument of null will throw an exception.</p>
+   *
+   * <p>Rationale: In mathematics a "null set" is a set with no members, which we call an empty set.
+   * That is distinctly different from the java <i>null</i>, which represents a nonexistent object.
+   * In most cases it is a programming error due to some object that was not properly initialized.
+   * With a null as the first argument, we cannot know what the user's intent is.
+   * Since it is very likely that a <i>null</i> is a programming error, we throw a an exception.</p>
+   *
+   * <p>An empty input argument will set the internal state to empty.</p>
+   *
+   * <p>Rationale: An empty set is a mathematically legal concept. Although it makes any subsequent,
+   * valid argument for B irrelevant, we must allow this and assume the user knows what they are
+   * doing.</p>
+   *
+   * <p>Performing {@link #getResult(boolean)} just after this step will return a compact form of
+   * the given argument.</p>
+   *
+   * @param skA The incoming sketch for the first argument, <i>A</i>.
+   */
+  public abstract void setA(Sketch skA);
+
+  /**
+   * This is part of a multistep, stateful AnotB operation and sets the given Theta sketch as the
+   * second (or <i>n+1</i>th) argument <i>B</i> of <i>A-AND-NOT-B</i>.
+   * Performs an <i>AND NOT</i> operation with the existing internal state of this AnotB operator.
+   *
+   * <p>An input argument of null or empty is ignored.</p>
+   *
+   * <p>Rationale: A <i>null</i> for the second or following arguments is more tolerable because
+   * <i>A NOT null</i> is still <i>A</i> even if we don't know exactly what the null represents. It
+   * clearly does not have any content that overlaps with <i>A</i>. Also, because this can be part of
+   * a multistep operation with multiple <i>notB</i> steps. Other following steps can still produce
+   * a valid result.</p>
+   *
+   * <p>Use {@link #getResult(boolean)} to obtain the result.</p>
+   *
+   * @param skB The incoming Theta sketch for the second (or following) argument <i>B</i>.
+   */
+  public abstract void notB(Sketch skB);
+
+  /**
+   * Gets the result of the multistep, stateful operation AnotB that have been executed with calls
+   * to {@link #setA(Sketch)} and ({@link #notB(Sketch)} or
+   * {@link #notB(org.apache.datasketches.theta2.Sketch)}).
+   *
+   * @param reset If <i>true</i>, clears this operator to the empty state after this result is
+   * returned. Set this to <i>false</i> if you wish to obtain an intermediate result.
+   *
+   * @return the result of this operation as an ordered, on-heap {@link CompactSketch}.
+   */
+  public abstract CompactSketch getResult(boolean reset);
+
+  /**
+   * Gets the result of the multistep, stateful operation AnotB that have been executed with calls
+   * to {@link #setA(Sketch)} and ({@link #notB(Sketch)} or
+   * {@link #notB(org.apache.datasketches.theta2.Sketch)}).
+   *
+   * @param dstOrdered If <i>true</i>, the result will be an ordered {@link CompactSketch}.
+   * <a href="{@docRoot}/resources/dictionary.html#dstOrdered">See Destination Ordered</a>.
+   *
+   * @param dstSeg if not <i>null</i> the given MemorySegment will be the target location of the result.
+   * <a href="{@docRoot}/resources/dictionary.html#dstMem">See Destination MemorySegment</a>.
+   *
+   * @param reset If <i>true</i>, clears this operator to the empty state after this result is
+   * returned. Set this to <i>false</i> if you wish to obtain an intermediate result.
+   *
+   * @return the result of this operation as a {@link CompactSketch} in the given dstMem.
+   */
+  public abstract CompactSketch getResult(boolean dstOrdered, MemorySegment dstSeg, boolean reset);
+
+  /**
+   * Perform A-and-not-B set operation on the two given sketches and return the result as an
+   * ordered CompactSketch on the heap.
+   *
+   * <p>This a stateless operation and has no impact on the internal state of this operator.
+   * Thus, this is not an accumulating update and does not interact with the {@link #setA(Sketch)},
+   * {@link #notB(Sketch)}, {@link #getResult(boolean)}, or
+   * {@link #getResult(boolean, MemorySegment, boolean)} methods.</p>
+   *
+   * <p>If either argument is null an exception is thrown.</p>
+   *
+   * <p>Rationale: In mathematics a "null set" is a set with no members, which we call an empty set.
+   * That is distinctly different from the java <i>null</i>, which represents a nonexistent object.
+   * In most cases <i>null</i> is a programming error due to a non-initialized object. </p>
+   *
+   * <p>With a null as the first argument we cannot know what the user's intent is and throw an
+   * exception. With a null as the second argument for this method we must return a result and
+   * there is no following possible viable arguments for the second argument so we thrown an
+   * exception.</p>
+   *
+   * @param skA The incoming sketch for the first argument. It must not be null.
+   * @param skB The incoming sketch for the second argument. It must not be null.
+   * @return an ordered CompactSketch on the heap
+   */
+  public CompactSketch aNotB(final Sketch skA, final Sketch skB) {
+    return aNotB(skA, skB, true, null);
+  }
+
+  /**
+   * Perform A-and-not-B set operation on the two given sketches and return the result as a
+   * CompactSketch.
+   *
+   * <p>This a stateless operation and has no impact on the internal state of this operator.
+   * Thus, this is not an accumulating update and does not interact with the {@link #setA(Sketch)},
+   * {@link #notB(Sketch)}, {@link #getResult(boolean)}, or
+   * {@link #getResult(boolean, MemorySegment, boolean)} methods.</p>
+   *
+   * <p>If either argument is null an exception is thrown.</p>
+   *
+   * <p>Rationale: In mathematics a "null set" is a set with no members, which we call an empty set.
+   * That is distinctly different from the java <i>null</i>, which represents a nonexistent object.
+   * In most cases <i>null</i> is a programming error due to a non-initialized object. </p>
+   *
+   * <p>With a null as the first argument we cannot know what the user's intent is and throw an
+   * exception. With a null as the second argument for this method we must return a result and
+   * there is no following possible viable arguments for the second argument so we thrown an
+   * exception.</p>
+   *
+   * @param skA The incoming sketch for the first argument. It must not be null.
+   * @param skB The incoming sketch for the second argument. It must not be null.
+   * @param dstOrdered
+   * <a href="{@docRoot}/resources/dictionary.html#dstOrdered">See Destination Ordered</a>.
+   * @param dstSeg
+   * <a href="{@docRoot}/resources/dictionary.html#dstMem">See Destination MemorySegment</a>.
+   * @return the result as a CompactSketch.
+   */
+  public abstract CompactSketch aNotB(Sketch skA, Sketch skB, boolean dstOrdered,
+      MemorySegment dstSeg);
+
+}
diff --git a/src/main/java/org/apache/datasketches/theta2/AnotBimpl.java b/src/main/java/org/apache/datasketches/theta2/AnotBimpl.java
new file mode 100644
index 00000000..4931bb68
--- /dev/null
+++ b/src/main/java/org/apache/datasketches/theta2/AnotBimpl.java
@@ -0,0 +1,241 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.datasketches.theta2;
+
+import static org.apache.datasketches.common.Util.exactLog2OfLong;
+import static org.apache.datasketches.thetacommon.HashOperations.checkThetaCorruption;
+import static org.apache.datasketches.thetacommon.HashOperations.continueCondition;
+import static org.apache.datasketches.thetacommon.HashOperations.hashSearch;
+import static org.apache.datasketches.thetacommon.HashOperations.hashSearchOrInsert;
+import static org.apache.datasketches.thetacommon.HashOperations.minLgHashTableSize;
+
+import java.lang.foreign.MemorySegment;
+import java.util.Arrays;
+
+import org.apache.datasketches.common.SketchesArgumentException;
+import org.apache.datasketches.thetacommon.ThetaUtil;
+
+/**
+ * Implements the A-and-not-B operations.
+ * @author Lee Rhodes
+ * @author Kevin Lang
+ */
+final class AnotBimpl extends AnotB {
+  private final short seedHash_;
+  private boolean empty_;
+  private long thetaLong_;
+  private long[] hashArr_ = new long[0]; //compact array w curCount_ entries
+  private int curCount_;
+
+  /**
+   * Construct a new AnotB SetOperation on the java heap.  Called by SetOperation.Builder.
+   *
+   * @param seed <a href="{@docRoot}/resources/dictionary.html#seed">See seed</a>
+   */
+  AnotBimpl(final long seed) {
+    this(ThetaUtil.computeSeedHash(seed));
+  }
+
+  /**
+   * Construct a new AnotB SetOperation on the java heap.
+   *
+   * @param seedHash 16 bit hash of the chosen update seed.
+   */
+  private AnotBimpl(final short seedHash) {
+    seedHash_ = seedHash;
+    reset();
+  }
+
+  @Override
+  public void setA(final Sketch skA) {
+    if (skA == null) {
+      reset();
+      throw new SketchesArgumentException("The input argument <i>A</i> must not be null");
+    }
+    if (skA.isEmpty()) {
+      reset();
+      return;
+    }
+    //skA is not empty
+    ThetaUtil.checkSeedHashes(seedHash_, skA.getSeedHash());
+
+    //process A
+    hashArr_ = getHashArrA(skA);
+    empty_ = false;
+    thetaLong_ = skA.getThetaLong();
+    curCount_ = hashArr_.length;
+  }
+
+  @Override
+  public void notB(final Sketch skB) {
+    if (empty_ || skB == null || skB.isEmpty()) { return; }
+    //local and skB is not empty
+    ThetaUtil.checkSeedHashes(seedHash_, skB.getSeedHash());
+
+    thetaLong_ = Math.min(thetaLong_,  skB.getThetaLong());
+
+    //process B
+    hashArr_ = getResultHashArr(thetaLong_, curCount_, hashArr_, skB);
+    curCount_ = hashArr_.length;
+    empty_ = curCount_ == 0 && thetaLong_ == Long.MAX_VALUE;
+  }
+
+  @Override
+  public CompactSketch getResult(final boolean reset) {
+    return getResult(true, null, reset);
+  }
+
+  @Override
+  public CompactSketch getResult(final boolean dstOrdered, final MemorySegment dstSeg,
+      final boolean reset) {
+    final CompactSketch result = CompactOperations.componentsToCompact(
+      thetaLong_, curCount_, seedHash_, empty_, true, false, dstOrdered, dstSeg, hashArr_.clone());
+    if (reset) { reset(); }
+    return result;
+  }
+
+  @Override
+  public CompactSketch aNotB(final Sketch skA, final Sketch skB, final boolean dstOrdered,
+      final MemorySegment dstSeg) {
+    if (skA == null || skB == null) {
+      throw new SketchesArgumentException("Neither argument may be null");
+    }
+    //Both skA & skB are not null
+
+    final long minThetaLong = Math.min(skA.getThetaLong(), skB.getThetaLong());
+
+    if (skA.isEmpty()) { return skA.compact(dstOrdered, dstSeg); }
+    //A is not Empty
+    ThetaUtil.checkSeedHashes(skA.getSeedHash(), seedHash_);
+
+    if (skB.isEmpty()) {
+      return skA.compact(dstOrdered, dstSeg);
+    }
+    ThetaUtil.checkSeedHashes(skB.getSeedHash(), seedHash_);
+    //Both skA & skB are not empty
+
+    //process A
+    final long[] hashArrA = getHashArrA(skA);
+    final int countA = hashArrA.length;
+
+    //process B
+    final long[] hashArrOut = getResultHashArr(minThetaLong, countA, hashArrA, skB); //out is clone
+    final int countOut = hashArrOut.length;
+    final boolean empty = countOut == 0 && minThetaLong == Long.MAX_VALUE;
+
+    final CompactSketch result = CompactOperations.componentsToCompact(
+          minThetaLong, countOut, seedHash_, empty, true, false, dstOrdered, dstSeg, hashArrOut);
+    return result;
+  }
+
+  @Override
+  int getRetainedEntries() {
+    return curCount_;
+  }
+
+  //restricted
+
+  private static long[] getHashArrA(final Sketch skA) { //returns a new array
+    //Get skA cache as array
+    final CompactSketch cskA = skA.compact(false, null); //sorting not required
+    final long[] hashArrA = cskA.getCache().clone();
+    return hashArrA;
+  }
+
+  private static long[] getResultHashArr( //returns a new array
+      final long minThetaLong,
+      final int countA,
+      final long[] hashArrA,
+      final Sketch skB) {
+
+    // Rebuild or get hashtable of skB
+    final long[] hashTableB; //read only
+    if (skB instanceof CompactSketch) {
+      hashTableB = convertToHashTable(skB, minThetaLong, ThetaUtil.REBUILD_THRESHOLD);
+    } else {
+      hashTableB = skB.getCache();
+    }
+
+    //build temporary result arrays of skA
+    final long[] tmpHashArrA = new long[countA];
+
+    //search for non matches and build temp arrays
+    final int lgHTBLen = exactLog2OfLong(hashTableB.length);
+    int nonMatches = 0;
+    for (int i = 0; i < countA; i++) {
+      final long hash = hashArrA[i];
+      if (hash != 0 && hash < minThetaLong) { //only allows hashes of A < minTheta
+        final int index = hashSearch(hashTableB, lgHTBLen, hash);
+        if (index == -1) {
+          tmpHashArrA[nonMatches] = hash;
+          nonMatches++;
+        }
+      }
+    }
+    return Arrays.copyOfRange(tmpHashArrA, 0, nonMatches);
+  }
+
+  private static long[] convertToHashTable(
+      final Sketch sketch,
+      final long thetaLong,
+      final double rebuildThreshold) {
+    final int lgArrLongs = minLgHashTableSize(sketch.getRetainedEntries(true), rebuildThreshold);
+    final int arrLongs = 1 << lgArrLongs;
+    final long[] hashTable = new long[arrLongs];
+    checkThetaCorruption(thetaLong);
+    final HashIterator it = sketch.iterator();
+    while (it.next()) {
+      final long hash = it.get();
+      if (continueCondition(thetaLong, hash) ) {
+        continue;
+      }
+      hashSearchOrInsert(hashTable, lgArrLongs, hash);
+    }
+    return hashTable;
+  }
+
+  private void reset() {
+    thetaLong_ = Long.MAX_VALUE;
+    empty_ = true;
+    hashArr_ = new long[0];
+    curCount_ = 0;
+  }
+
+  @Override
+  long[] getCache() {
+    return hashArr_.clone();
+  }
+
+  @Override
+  short getSeedHash() {
+    return seedHash_;
+  }
+
+  @Override
+  long getThetaLong() {
+    return thetaLong_;
+  }
+
+  @Override
+  boolean isEmpty() {
+    return empty_;
+  }
+
+}
diff --git a/src/main/java/org/apache/datasketches/theta2/BitPacking.java b/src/main/java/org/apache/datasketches/theta2/BitPacking.java
new file mode 100644
index 00000000..e2b6be2f
--- /dev/null
+++ b/src/main/java/org/apache/datasketches/theta2/BitPacking.java
@@ -0,0 +1,6292 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.datasketches.theta2;
+
+import org.apache.datasketches.common.SketchesArgumentException;
+
+/**
+ * Used as part of Theta compression.
+ */
+public class BitPacking {
+
+  /**
+   * The bit packing operation
+   * @param value the value to pack
+   * @param bits number of bits to pack
+   * @param buffer the output byte array buffer
+   * @param bufOffset the byte offset in the buffer
+   * @param bitOffset the bit offset
+   */
+  public static void packBits(final long value, int bits, final byte[] buffer, int bufOffset, final int bitOffset) {
+    if (bitOffset > 0) {
+      final int chunkBits = 8 - bitOffset;
+      final int mask = (1 << chunkBits) - 1;
+      if (bits < chunkBits) {
+        buffer[bufOffset] |= (value << (chunkBits - bits)) & mask;
+        return;
+      }
+      buffer[bufOffset++] |= (value >>> (bits - chunkBits)) & mask;
+      bits -= chunkBits;
+    }
+    while (bits >= 8) {
+      buffer[bufOffset++] = (byte)(value >>> (bits - 8));
+      bits -= 8;
+    }
+    if (bits > 0) {
+      buffer[bufOffset] = (byte)(value << (8 - bits));
+    }
+  }
+
+  /**
+   * The unpacking operation
+   * @param value the output array
+   * @param index index of the value array
+   * @param bits the number of bits to unpack
+   * @param buffer the input packed buffer
+   * @param bufOffset the buffer offset
+   * @param bitOffset the bit offset
+   */
+  public static void unpackBits(final long[] value, final int index, int bits, final byte[] buffer,
+      int bufOffset,final int bitOffset) {
+    final int availBits = 8 - bitOffset;
+    final int chunkBits = availBits <= bits ? availBits : bits;
+    final int mask = (1 << chunkBits) - 1;
+    value[index] = (buffer[bufOffset] >>> (availBits - chunkBits)) & mask;
+    bufOffset += availBits == chunkBits ? 1 : 0;
+    bits -= chunkBits;
+    while (bits >= 8) {
+      value[index] <<= 8;
+      value[index] |= (Byte.toUnsignedLong(buffer[bufOffset++]));
+      bits -= 8;
+    }
+    if (bits > 0) {
+      value[index] <<= bits;
+      value[index] |= Byte.toUnsignedLong(buffer[bufOffset]) >>> (8 - bits);
+    }
+  }
+
+  // pack given number of bits from a block of 8 64-bit values into bytes
+  // we don't need 0 and 64 bits
+  // we assume that higher bits (which we are not packing) are zeros
+  // this assumption allows to avoid masking operations
+
+  static void packBitsBlock8(final long[] values, final int i, final byte[] buf, final int off, final int bits) {
+    switch (bits) {
+      case 1: packBits1(values, i, buf, off); break;
+      case 2: packBits2(values, i, buf, off); break;
+      case 3: packBits3(values, i, buf, off); break;
+      case 4: packBits4(values, i, buf, off); break;
+      case 5: packBits5(values, i, buf, off); break;
+      case 6: packBits6(values, i, buf, off); break;
+      case 7: packBits7(values, i, buf, off); break;
+      case 8: packBits8(values, i, buf, off); break;
+      case 9: packBits9(values, i, buf, off); break;
+      case 10: packBits10(values, i, buf, off); break;
+      case 11: packBits11(values, i, buf, off); break;
+      case 12: packBits12(values, i, buf, off); break;
+      case 13: packBits13(values, i, buf, off); break;
+      case 14: packBits14(values, i, buf, off); break;
+      case 15: packBits15(values, i, buf, off); break;
+      case 16: packBits16(values, i, buf, off); break;
+      case 17: packBits17(values, i, buf, off); break;
+      case 18: packBits18(values, i, buf, off); break;
+      case 19: packBits19(values, i, buf, off); break;
+      case 20: packBits20(values, i, buf, off); break;
+      case 21: packBits21(values, i, buf, off); break;
+      case 22: packBits22(values, i, buf, off); break;
+      case 23: packBits23(values, i, buf, off); break;
+      case 24: packBits24(values, i, buf, off); break;
+      case 25: packBits25(values, i, buf, off); break;
+      case 26: packBits26(values, i, buf, off); break;
+      case 27: packBits27(values, i, buf, off); break;
+      case 28: packBits28(values, i, buf, off); break;
+      case 29: packBits29(values, i, buf, off); break;
+      case 30: packBits30(values, i, buf, off); break;
+      case 31: packBits31(values, i, buf, off); break;
+      case 32: packBits32(values, i, buf, off); break;
+      case 33: packBits33(values, i, buf, off); break;
+      case 34: packBits34(values, i, buf, off); break;
+      case 35: packBits35(values, i, buf, off); break;
+      case 36: packBits36(values, i, buf, off); break;
+      case 37: packBits37(values, i, buf, off); break;
+      case 38: packBits38(values, i, buf, off); break;
+      case 39: packBits39(values, i, buf, off); break;
+      case 40: packBits40(values, i, buf, off); break;
+      case 41: packBits41(values, i, buf, off); break;
+      case 42: packBits42(values, i, buf, off); break;
+      case 43: packBits43(values, i, buf, off); break;
+      case 44: packBits44(values, i, buf, off); break;
+      case 45: packBits45(values, i, buf, off); break;
+      case 46: packBits46(values, i, buf, off); break;
+      case 47: packBits47(values, i, buf, off); break;
+      case 48: packBits48(values, i, buf, off); break;
+      case 49: packBits49(values, i, buf, off); break;
+      case 50: packBits50(values, i, buf, off); break;
+      case 51: packBits51(values, i, buf, off); break;
+      case 52: packBits52(values, i, buf, off); break;
+      case 53: packBits53(values, i, buf, off); break;
+      case 54: packBits54(values, i, buf, off); break;
+      case 55: packBits55(values, i, buf, off); break;
+      case 56: packBits56(values, i, buf, off); break;
+      case 57: packBits57(values, i, buf, off); break;
+      case 58: packBits58(values, i, buf, off); break;
+      case 59: packBits59(values, i, buf, off); break;
+      case 60: packBits60(values, i, buf, off); break;
+      case 61: packBits61(values, i, buf, off); break;
+      case 62: packBits62(values, i, buf, off); break;
+      case 63: packBits63(values, i, buf, off); break;
+      default: throw new SketchesArgumentException("wrong number of bits in packBitsBlock8: " + bits);
+    }
+  }
+
+  static void unpackBitsBlock8(final long[] values, final int i, final byte[] buf, final int off, final int bits) {
+    switch (bits) {
+      case 1: unpackBits1(values, i, buf, off); break;
+      case 2: unpackBits2(values, i, buf, off); break;
+      case 3: unpackBits3(values, i, buf, off); break;
+      case 4: unpackBits4(values, i, buf, off); break;
+      case 5: unpackBits5(values, i, buf, off); break;
+      case 6: unpackBits6(values, i, buf, off); break;
+      case 7: unpackBits7(values, i, buf, off); break;
+      case 8: unpackBits8(values, i, buf, off); break;
+      case 9: unpackBits9(values, i, buf, off); break;
+      case 10: unpackBits10(values, i, buf, off); break;
+      case 11: unpackBits11(values, i, buf, off); break;
+      case 12: unpackBits12(values, i, buf, off); break;
+      case 13: unpackBits13(values, i, buf, off); break;
+      case 14: unpackBits14(values, i, buf, off); break;
+      case 15: unpackBits15(values, i, buf, off); break;
+      case 16: unpackBits16(values, i, buf, off); break;
+      case 17: unpackBits17(values, i, buf, off); break;
+      case 18: unpackBits18(values, i, buf, off); break;
+      case 19: unpackBits19(values, i, buf, off); break;
+      case 20: unpackBits20(values, i, buf, off); break;
+      case 21: unpackBits21(values, i, buf, off); break;
+      case 22: unpackBits22(values, i, buf, off); break;
+      case 23: unpackBits23(values, i, buf, off); break;
+      case 24: unpackBits24(values, i, buf, off); break;
+      case 25: unpackBits25(values, i, buf, off); break;
+      case 26: unpackBits26(values, i, buf, off); break;
+      case 27: unpackBits27(values, i, buf, off); break;
+      case 28: unpackBits28(values, i, buf, off); break;
+      case 29: unpackBits29(values, i, buf, off); break;
+      case 30: unpackBits30(values, i, buf, off); break;
+      case 31: unpackBits31(values, i, buf, off); break;
+      case 32: unpackBits32(values, i, buf, off); break;
+      case 33: unpackBits33(values, i, buf, off); break;
+      case 34: unpackBits34(values, i, buf, off); break;
+      case 35: unpackBits35(values, i, buf, off); break;
+      case 36: unpackBits36(values, i, buf, off); break;
+      case 37: unpackBits37(values, i, buf, off); break;
+      case 38: unpackBits38(values, i, buf, off); break;
+      case 39: unpackBits39(values, i, buf, off); break;
+      case 40: unpackBits40(values, i, buf, off); break;
+      case 41: unpackBits41(values, i, buf, off); break;
+      case 42: unpackBits42(values, i, buf, off); break;
+      case 43: unpackBits43(values, i, buf, off); break;
+      case 44: unpackBits44(values, i, buf, off); break;
+      case 45: unpackBits45(values, i, buf, off); break;
+      case 46: unpackBits46(values, i, buf, off); break;
+      case 47: unpackBits47(values, i, buf, off); break;
+      case 48: unpackBits48(values, i, buf, off); break;
+      case 49: unpackBits49(values, i, buf, off); break;
+      case 50: unpackBits50(values, i, buf, off); break;
+      case 51: unpackBits51(values, i, buf, off); break;
+      case 52: unpackBits52(values, i, buf, off); break;
+      case 53: unpackBits53(values, i, buf, off); break;
+      case 54: unpackBits54(values, i, buf, off); break;
+      case 55: unpackBits55(values, i, buf, off); break;
+      case 56: unpackBits56(values, i, buf, off); break;
+      case 57: unpackBits57(values, i, buf, off); break;
+      case 58: unpackBits58(values, i, buf, off); break;
+      case 59: unpackBits59(values, i, buf, off); break;
+      case 60: unpackBits60(values, i, buf, off); break;
+      case 61: unpackBits61(values, i, buf, off); break;
+      case 62: unpackBits62(values, i, buf, off); break;
+      case 63: unpackBits63(values, i, buf, off); break;
+      default: throw new SketchesArgumentException("wrong number of bits unpackBitsBlock8: " + bits);
+    }
+  }
+
+  static void packBits1(final long[] values, final int i, final byte[] buf, final int off) {
+    buf[off] = (byte) (values[i + 0] << 7);
+    buf[off] |= values[i + 1] << 6;
+    buf[off] |= values[i + 2] << 5;
+    buf[off] |= values[i + 3] << 4;
+    buf[off] |= values[i + 4] << 3;
+    buf[off] |= values[i + 5] << 2;
+    buf[off] |= values[i + 6] << 1;
+    buf[off] |= values[i + 7];
+  }
+
+  static void packBits2(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off] = (byte) (values[i + 0] << 6);
+    buf[off] |= values[i + 1] << 4;
+    buf[off] |= values[i + 2] << 2;
+    buf[off++] |= values[i + 3];
+
+    buf[off] = (byte) (values[i + 4] << 6);
+    buf[off] |= values[i + 5] << 4;
+    buf[off] |= values[i + 6] << 2;
+    buf[off] |= values[i + 7];
+  }
+
+  static void packBits3(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off] = (byte) (values[i + 0] << 5);
+    buf[off] |= values[i + 1] << 2;
+    buf[off++] |= values[i + 2] >>> 1;
+
+    buf[off] = (byte) (values[i + 2] << 7);
+    buf[off] |= values[i + 3] << 4;
+    buf[off] |= values[i + 4] << 1;
+    buf[off++] |= values[i + 5] >>> 2;
+
+    buf[off] = (byte) (values[i + 5] << 6);
+    buf[off] |= values[i + 6] << 3;
+    buf[off] |= values[i + 7];
+  }
+
+  static void packBits4(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off] = (byte) (values[i + 0] << 4);
+    buf[off++] |= values[i + 1];
+
+    buf[off] = (byte) (values[i + 2] << 4);
+    buf[off++] |= values[i + 3];
+
+    buf[off] = (byte) (values[i + 4] << 4);
+    buf[off++] |= values[i + 5];
+
+    buf[off] = (byte) (values[i + 6] << 4);
+    buf[off] |= values[i + 7];
+  }
+
+  static void packBits5(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off] = (byte) (values[i + 0] << 3);
+    buf[off++] |= values[i + 1] >>> 2;
+
+    buf[off] = (byte) (values[i + 1] << 6);
+    buf[off] |= values[i + 2] << 1;
+    buf[off++] |= values[i + 3] >>> 4;
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 1;
+
+    buf[off] = (byte) (values[i + 4] << 7);
+    buf[off] |= values[i + 5] << 2;
+    buf[off++] |= values[i + 6] >>> 3;
+
+    buf[off] = (byte) (values[i + 6] << 5);
+    buf[off] |= values[i + 7];
+  }
+
+  static void packBits6(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off] = (byte) (values[i + 0] << 2);
+    buf[off++] |= values[i + 1] >>> 4;
+
+    buf[off] = (byte) (values[i + 1] << 4);
+    buf[off++] |= values[i + 2] >>> 2;
+
+    buf[off] = (byte) (values[i + 2] << 6);
+    buf[off++] |= values[i + 3];
+
+    buf[off] = (byte) (values[i + 4] << 2);
+    buf[off++] |= values[i + 5] >>> 4;
+
+    buf[off] = (byte) (values[i + 5] << 4);
+    buf[off++] |= values[i + 6] >>> 2;
+
+    buf[off] = (byte) (values[i + 6] << 6);
+    buf[off] |= values[i + 7];
+  }
+
+  static void packBits7(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off] = (byte) (values[i + 0] << 1);
+    buf[off++] |= values[i + 1] >>> 6;
+
+    buf[off] = (byte) (values[i + 1] << 2);
+    buf[off++] |= values[i + 2] >>> 5;
+
+    buf[off] = (byte) (values[i + 2] << 3);
+    buf[off++] |= values[i + 3] >>> 4;
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 3;
+
+    buf[off] = (byte) (values[i + 4] << 5);
+    buf[off++] |= values[i + 5] >>> 2;
+
+    buf[off] = (byte) (values[i + 5] << 6);
+    buf[off++] |= values[i + 6] >>> 1;
+
+    buf[off] = (byte) (values[i + 6] << 7);
+    buf[off] |= values[i + 7];
+  }
+
+  static void packBits8(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0]);
+    buf[off++] = (byte) (values[i + 1]);
+    buf[off++] = (byte) (values[i + 2]);
+    buf[off++] = (byte) (values[i + 3]);
+    buf[off++] = (byte) (values[i + 4]);
+    buf[off++] = (byte) (values[i + 5]);
+    buf[off++] = (byte) (values[i + 6]);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits9(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 1);
+
+    buf[off] = (byte) (values[i + 0] << 7);
+    buf[off++] |= values[i + 1] >>> 2;
+
+    buf[off] = (byte) (values[i + 1] << 6);
+    buf[off++] |= values[i + 2] >>> 3;
+
+    buf[off] = (byte) (values[i + 2] << 5);
+    buf[off++] |= values[i + 3] >>> 4;
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 5;
+
+    buf[off] = (byte) (values[i + 4] << 3);
+    buf[off++] |= values[i + 5] >>> 6;
+
+    buf[off] = (byte) (values[i + 5] << 2);
+    buf[off++] |= values[i + 6] >>> 7;
+
+    buf[off] = (byte) (values[i + 6] << 1);
+    buf[off++] |= values[i + 7] >>> 8;
+
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits10(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 2);
+
+    buf[off] = (byte) (values[i + 0] << 6);
+    buf[off++] |= values[i + 1] >>> 4;
+
+    buf[off] = (byte) (values[i + 1] << 4);
+    buf[off++] |= values[i + 2] >>> 6;
+
+    buf[off] = (byte) (values[i + 2] << 2);
+    buf[off++] |= values[i + 3] >>> 8;
+
+    buf[off++] = (byte) (values[i + 3]);
+
+    buf[off++] = (byte) (values[i + 4] >>> 2);
+
+    buf[off] = (byte) (values[i + 4] << 6);
+    buf[off++] |= values[i + 5] >>> 4;
+
+    buf[off] = (byte) (values[i + 5] << 4);
+    buf[off++] |= values[i + 6] >>> 6;
+
+    buf[off] = (byte) (values[i + 6] << 2);
+    buf[off++] |= values[i + 7] >>> 8;
+
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits11(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 3);
+
+    buf[off] = (byte) (values[i + 0] << 5);
+    buf[off++] |= values[i + 1] >>> 6;
+
+    buf[off] = (byte) (values[i + 1] << 2);
+    buf[off++] |= values[i + 2] >>> 9;
+
+    buf[off++] = (byte) (values[i + 2] >>> 1);
+
+    buf[off] = (byte) (values[i + 2] << 7);
+    buf[off++] |= values[i + 3] >>> 4;
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 7;
+
+    buf[off] = (byte) (values[i + 4] << 1);
+    buf[off++] |= values[i + 5] >>> 10;
+
+    buf[off++] = (byte) (values[i + 5] >>> 2);
+
+    buf[off] = (byte) (values[i + 5] << 6);
+    buf[off++] |= values[i + 6] >>> 5;
+
+    buf[off] = (byte) (values[i + 6] << 3);
+    buf[off++] |= values[i + 7] >>> 8;
+
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits12(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 4);
+
+    buf[off] = (byte) (values[i + 0] << 4);
+    buf[off++] |= values[i + 1] >>> 8;
+
+    buf[off++] = (byte) (values[i + 1]);
+
+    buf[off++] = (byte) (values[i + 2] >>> 4);
+
+    buf[off] = (byte) (values[i + 2] << 4);
+    buf[off++] |= values[i + 3] >>> 8;
+
+    buf[off++] = (byte) (values[i + 3]);
+
+    buf[off++] = (byte) (values[i + 4] >>> 4);
+
+    buf[off] = (byte) (values[i + 4] << 4);
+    buf[off++] |= values[i + 5] >>> 8;
+
+    buf[off++] = (byte) (values[i + 5]);
+
+    buf[off++] = (byte) (values[i + 6] >>> 4);
+
+    buf[off] = (byte) (values[i + 6] << 4);
+    buf[off++] |= values[i + 7] >>> 8;
+
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits13(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 5);
+
+    buf[off] = (byte) (values[i + 0] << 3);
+    buf[off++] |= values[i + 1] >>> 10;
+
+    buf[off++] = (byte) (values[i + 1] >>> 2);
+
+    buf[off] = (byte) (values[i + 1] << 6);
+    buf[off++] |= values[i + 2] >>> 7;
+
+    buf[off] = (byte) (values[i + 2] << 1);
+    buf[off++] |= values[i + 3] >>> 12;
+
+    buf[off++] = (byte) (values[i + 3] >>> 4);
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 9;
+
+    buf[off++] = (byte) (values[i + 4] >>> 1);
+
+    buf[off] = (byte) (values[i + 4] << 7);
+    buf[off++] |= values[i + 5] >>> 6;
+
+    buf[off] = (byte) (values[i + 5] << 2);
+    buf[off++] |= values[i + 6] >>> 11;
+
+    buf[off++] = (byte) (values[i + 6] >>> 3);
+
+    buf[off] = (byte) (values[i + 6] << 5);
+    buf[off++] |= values[i + 7] >>> 8;
+
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits14(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 6);
+
+    buf[off] = (byte) (values[i + 0] << 2);
+    buf[off++] |= values[i + 1] >>> 12;
+
+    buf[off++] = (byte) (values[i + 1] >>> 4);
+
+    buf[off] = (byte) (values[i + 1] << 4);
+    buf[off++] |= values[i + 2] >>> 10;
+
+    buf[off++] = (byte) (values[i + 2] >>> 2);
+
+    buf[off] = (byte) (values[i + 2] << 6);
+    buf[off++] |= values[i + 3] >>> 8;
+
+    buf[off++] = (byte) (values[i + 3]);
+
+    buf[off++] = (byte) (values[i + 4] >>> 6);
+
+    buf[off] = (byte) (values[i + 4] << 2);
+    buf[off++] |= values[i + 5] >>> 12;
+
+    buf[off++] = (byte) (values[i + 5] >>> 4);
+
+    buf[off] = (byte) (values[i + 5] << 4);
+    buf[off++] |= values[i + 6] >>> 10;
+
+    buf[off++] = (byte) (values[i + 6] >>> 2);
+
+    buf[off] = (byte) (values[i + 6] << 6);
+    buf[off++] |= values[i + 7] >>> 8;
+
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits15(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 7);
+
+    buf[off] = (byte) (values[i + 0] << 1);
+    buf[off++] |= values[i + 1] >>> 14;
+
+    buf[off++] = (byte) (values[i + 1] >>> 6);
+
+    buf[off] = (byte) (values[i + 1] << 2);
+    buf[off++] |= values[i + 2] >>> 13;
+
+    buf[off++] = (byte) (values[i + 2] >>> 5);
+
+    buf[off] = (byte) (values[i + 2] << 3);
+    buf[off++] |= values[i + 3] >>> 12;
+
+    buf[off++] = (byte) (values[i + 3] >>> 4);
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 11;
+
+    buf[off++] = (byte) (values[i + 4] >>> 3);
+
+    buf[off] = (byte) (values[i + 4] << 5);
+    buf[off++] |= values[i + 5] >>> 10;
+
+    buf[off++] = (byte) (values[i + 5] >>> 2);
+
+    buf[off] = (byte) (values[i + 5] << 6);
+    buf[off++] |= values[i + 6] >>> 9;
+
+    buf[off++] = (byte) (values[i + 6] >>> 1);
+
+    buf[off] = (byte) (values[i + 6] << 7);
+    buf[off++] |= values[i + 7] >>> 8;
+
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits16(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 8);
+    buf[off++] = (byte) (values[i + 0]);
+
+    buf[off++] = (byte) (values[i + 1] >>> 8);
+    buf[off++] = (byte) (values[i + 1]);
+
+    buf[off++] = (byte) (values[i + 2] >>> 8);
+    buf[off++] = (byte) (values[i + 2]);
+
+    buf[off++] = (byte) (values[i + 3] >>> 8);
+    buf[off++] = (byte) (values[i + 3]);
+
+    buf[off++] = (byte) (values[i + 4] >>> 8);
+    buf[off++] = (byte) (values[i + 4]);
+
+    buf[off++] = (byte) (values[i + 5] >>> 8);
+    buf[off++] = (byte) (values[i + 5]);
+
+    buf[off++] = (byte) (values[i + 6] >>> 8);
+    buf[off++] = (byte) (values[i + 6]);
+
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits17(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 9);
+
+    buf[off++] = (byte) (values[i + 0] >>> 1);
+
+    buf[off] = (byte) (values[i + 0] << 7);
+    buf[off++] |= values[i + 1] >>> 10;
+
+    buf[off++] = (byte) (values[i + 1] >>> 2);
+
+    buf[off] = (byte) (values[i + 1] << 6);
+    buf[off++] |= values[i + 2] >>> 11;
+
+    buf[off++] = (byte) (values[i + 2] >>> 3);
+
+    buf[off] = (byte) (values[i + 2] << 5);
+    buf[off++] |= values[i + 3] >>> 12;
+
+    buf[off++] = (byte) (values[i + 3] >>> 4);
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 13;
+
+    buf[off++] = (byte) (values[i + 4] >>> 5);
+
+    buf[off] = (byte) (values[i + 4] << 3);
+    buf[off++] |= values[i + 5] >>> 14;
+
+    buf[off++] = (byte) (values[i + 5] >>> 6);
+
+    buf[off] = (byte) (values[i + 5] << 2);
+    buf[off++] |= values[i + 6] >>> 15;
+
+    buf[off++] = (byte) (values[i + 6] >>> 7);
+
+    buf[off] = (byte) (values[i + 6] << 1);
+    buf[off++] |= values[i + 7] >>> 16;
+
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits18(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 10);
+
+    buf[off++] = (byte) (values[i + 0] >>> 2);
+
+    buf[off] = (byte) (values[i + 0] << 6);
+    buf[off++] |= values[i + 1] >>> 12;
+
+    buf[off++] = (byte) (values[i + 1] >>> 4);
+
+    buf[off] = (byte) (values[i + 1] << 4);
+    buf[off++] |= values[i + 2] >>> 14;
+
+    buf[off++] = (byte) (values[i + 2] >>> 6);
+
+    buf[off] = (byte) (values[i + 2] << 2);
+    buf[off++] |= values[i + 3] >>> 16;
+
+    buf[off++] = (byte) (values[i + 3] >>> 8);
+
+    buf[off++] = (byte) (values[i + 3]);
+
+    buf[off++] = (byte) (values[i + 4] >>> 10);
+
+    buf[off++] = (byte) (values[i + 4] >>> 2);
+
+    buf[off] = (byte) (values[i + 4] << 6);
+    buf[off++] |= values[i + 5] >>> 12;
+
+    buf[off++] = (byte) (values[i + 5] >>> 4);
+
+    buf[off] = (byte) (values[i + 5] << 4);
+    buf[off++] |= values[i + 6] >>> 14;
+
+    buf[off++] = (byte) (values[i + 6] >>> 6);
+
+    buf[off] = (byte) (values[i + 6] << 2);
+    buf[off++] |= values[i + 7] >>> 16;
+
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits19(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 11);
+
+    buf[off++] = (byte) (values[i + 0] >>> 3);
+
+    buf[off] = (byte) (values[i + 0] << 5);
+    buf[off++] |= values[i + 1] >>> 14;
+
+    buf[off++] = (byte) (values[i + 1] >>> 6);
+
+    buf[off] = (byte) (values[i + 1] << 2);
+    buf[off++] |= values[i + 2] >>> 17;
+
+    buf[off++] = (byte) (values[i + 2] >>> 9);
+
+    buf[off++] = (byte) (values[i + 2] >>> 1);
+
+    buf[off] = (byte) (values[i + 2] << 7);
+    buf[off++] |= values[i + 3] >>> 12;
+
+    buf[off++] = (byte) (values[i + 3] >>> 4);
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 15;
+
+    buf[off++] |= values[i + 4] >>> 7;
+
+    buf[off] = (byte) (values[i + 4] << 1);
+    buf[off++] |= values[i + 5] >>> 18;
+
+    buf[off++] = (byte) (values[i + 5] >>> 10);
+
+    buf[off++] = (byte) (values[i + 5] >>> 2);
+
+    buf[off] = (byte) (values[i + 5] << 6);
+    buf[off++] |= values[i + 6] >>> 13;
+
+    buf[off++] = (byte) (values[i + 6] >>> 5);
+
+    buf[off] = (byte) (values[i + 6] << 3);
+    buf[off++] |= values[i + 7] >>> 16;
+
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits20(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 12);
+
+    buf[off++] = (byte) (values[i + 0] >>> 4);
+
+    buf[off] = (byte) (values[i + 0] << 4);
+    buf[off++] |= values[i + 1] >>> 16;
+
+    buf[off++] = (byte) (values[i + 1] >>> 8);
+
+    buf[off++] = (byte) (values[i + 1]);
+
+    buf[off++] = (byte) (values[i + 2] >>> 12);
+
+    buf[off++] = (byte) (values[i + 2] >>> 4);
+
+    buf[off] = (byte) (values[i + 2] << 4);
+    buf[off++] |= values[i + 3] >>> 16;
+
+    buf[off++] = (byte) (values[i + 3] >>> 8);
+
+    buf[off++] = (byte) (values[i + 3]);
+
+    buf[off++] = (byte) (values[i + 4] >>> 12);
+
+    buf[off++] = (byte) (values[i + 4] >>> 4);
+
+    buf[off] = (byte) (values[i + 4] << 4);
+    buf[off++] |= values[i + 5] >>> 16;
+
+    buf[off++] = (byte) (values[i + 5] >>> 8);
+
+    buf[off++] = (byte) (values[i + 5]);
+
+    buf[off++] = (byte) (values[i + 6] >>> 12);
+
+    buf[off++] = (byte) (values[i + 6] >>> 4);
+
+    buf[off] = (byte) (values[i + 6] << 4);
+    buf[off++] |= values[i + 7] >>> 16;
+
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits21(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 13);
+
+    buf[off++] = (byte) (values[i + 0] >>> 5);
+
+    buf[off] = (byte) (values[i + 0] << 3);
+    buf[off++] |= values[i + 1] >>> 18;
+
+    buf[off++] = (byte) (values[i + 1] >>> 10);
+
+    buf[off++] = (byte) (values[i + 1] >>> 2);
+
+    buf[off] = (byte) (values[i + 1] << 6);
+    buf[off++] |= values[i + 2] >>> 15;
+
+    buf[off++] = (byte) (values[i + 2] >>> 7);
+
+    buf[off] = (byte) (values[i + 2] << 1);
+    buf[off++] |= values[i + 3] >>> 20;
+
+    buf[off++] = (byte) (values[i + 3] >>> 12);
+
+    buf[off++] = (byte) (values[i + 3] >>> 4);
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 17;
+
+    buf[off++] = (byte) (values[i + 4] >>> 9);
+
+    buf[off++] = (byte) (values[i + 4] >>> 1);
+
+    buf[off] = (byte) (values[i + 4] << 7);
+    buf[off++] |= values[i + 5] >>> 14;
+
+    buf[off++] = (byte) (values[i + 5] >>> 6);
+
+    buf[off] = (byte) (values[i + 5] << 2);
+    buf[off++] |= values[i + 6] >>> 19;
+
+    buf[off++] = (byte) (values[i + 6] >>> 11);
+
+    buf[off++] = (byte) (values[i + 6] >>> 3);
+
+    buf[off] = (byte) (values[i + 6] << 5);
+    buf[off++] |= values[i + 7] >>> 16;
+
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits22(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 14);
+
+    buf[off++] = (byte) (values[i + 0] >>> 6);
+
+    buf[off] = (byte) (values[i + 0] << 2);
+    buf[off++] |= values[i + 1] >>> 20;
+
+    buf[off++] = (byte) (values[i + 1] >>> 12);
+
+    buf[off++] = (byte) (values[i + 1] >>> 4);
+
+    buf[off] = (byte) (values[i + 1] << 4);
+    buf[off++] |= values[i + 2] >>> 18;
+
+    buf[off++] = (byte) (values[i + 2] >>> 10);
+
+    buf[off++] = (byte) (values[i + 2] >>> 2);
+
+    buf[off] = (byte) (values[i + 2] << 6);
+    buf[off++] |= values[i + 3] >>> 16;
+
+    buf[off++] = (byte) (values[i + 3] >>> 8);
+
+    buf[off++] = (byte) (values[i + 3]);
+
+    buf[off++] = (byte) (values[i + 4] >>> 14);
+
+    buf[off++] = (byte) (values[i + 4] >>> 6);
+
+    buf[off] = (byte) (values[i + 4] << 2);
+    buf[off++] |= values[i + 5] >>> 20;
+
+    buf[off++] = (byte) (values[i + 5] >>> 12);
+
+    buf[off++] = (byte) (values[i + 5] >>> 4);
+
+    buf[off] = (byte) (values[i + 5] << 4);
+    buf[off++] |= values[i + 6] >>> 18;
+
+    buf[off++] = (byte) (values[i + 6] >>> 10);
+
+    buf[off++] = (byte) (values[i + 6] >>> 2);
+
+    buf[off] = (byte) (values[i + 6] << 6);
+    buf[off++] |= values[i + 7] >>> 16;
+
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits23(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 15);
+
+    buf[off++] = (byte) (values[i + 0] >>> 7);
+
+    buf[off] = (byte) (values[i + 0] << 1);
+    buf[off++] |= values[i + 1] >>> 22;
+
+    buf[off++] = (byte) (values[i + 1] >>> 14);
+
+    buf[off++] = (byte) (values[i + 1] >>> 6);
+
+    buf[off] = (byte) (values[i + 1] << 2);
+    buf[off++] |= values[i + 2] >>> 21;
+
+    buf[off++] = (byte) (values[i + 2] >>> 13);
+
+    buf[off++] = (byte) (values[i + 2] >>> 5);
+
+    buf[off] = (byte) (values[i + 2] << 3);
+    buf[off++] |= values[i + 3] >>> 20;
+
+    buf[off++] = (byte) (values[i + 3] >>> 12);
+
+    buf[off++] = (byte) (values[i + 3] >>> 4);
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 19;
+
+    buf[off++] = (byte) (values[i + 4] >>> 11);
+
+    buf[off++] = (byte) (values[i + 4] >>> 3);
+
+    buf[off] = (byte) (values[i + 4] << 5);
+    buf[off++] |= values[i + 5] >>> 18;
+
+    buf[off++] = (byte) (values[i + 5] >>> 10);
+
+    buf[off++] = (byte) (values[i + 5] >>> 2);
+
+    buf[off] = (byte) (values[i + 5] << 6);
+    buf[off++] |= values[i + 6] >>> 17;
+
+    buf[off++] = (byte) (values[i + 6] >>> 9);
+
+    buf[off++] = (byte) (values[i + 6] >>> 1);
+
+    buf[off] = (byte) (values[i + 6] << 7);
+    buf[off++] |= values[i + 7] >>> 16;
+
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits24(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 16);
+    buf[off++] = (byte) (values[i + 0] >>> 8);
+    buf[off++] = (byte) (values[i + 0]);
+
+    buf[off++] = (byte) (values[i + 1] >>> 16);
+    buf[off++] = (byte) (values[i + 1] >>> 8);
+    buf[off++] = (byte) (values[i + 1]);
+
+    buf[off++] = (byte) (values[i + 2] >>> 16);
+    buf[off++] = (byte) (values[i + 2] >>> 8);
+    buf[off++] = (byte) (values[i + 2]);
+
+    buf[off++] = (byte) (values[i + 3] >>> 16);
+    buf[off++] = (byte) (values[i + 3] >>> 8);
+    buf[off++] = (byte) (values[i + 3]);
+
+    buf[off++] = (byte) (values[i + 4] >>> 16);
+    buf[off++] = (byte) (values[i + 4] >>> 8);
+    buf[off++] = (byte) (values[i + 4]);
+
+    buf[off++] = (byte) (values[i + 5] >>> 16);
+    buf[off++] = (byte) (values[i + 5] >>> 8);
+    buf[off++] = (byte) (values[i + 5]);
+
+    buf[off++] = (byte) (values[i + 6] >>> 16);
+    buf[off++] = (byte) (values[i + 6] >>> 8);
+    buf[off++] = (byte) (values[i + 6]);
+
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits25(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 17);
+
+    buf[off++] = (byte) (values[i + 0] >>> 9);
+
+    buf[off++] = (byte) (values[i + 0] >>> 1);
+
+    buf[off] = (byte) (values[i + 0] << 7);
+    buf[off++] |= values[i + 1] >>> 18;
+
+    buf[off++] = (byte) (values[i + 1] >>> 10);
+
+    buf[off++] = (byte) (values[i + 1] >>> 2);
+
+    buf[off] = (byte) (values[i + 1] << 6);
+    buf[off++] |= values[i + 2] >>> 19;
+
+    buf[off++] = (byte) (values[i + 2] >>> 11);
+
+    buf[off++] = (byte) (values[i + 2] >>> 3);
+
+    buf[off] = (byte) (values[i + 2] << 5);
+    buf[off++] |= values[i + 3] >>> 20;
+
+    buf[off++] = (byte) (values[i + 3] >>> 12);
+
+    buf[off++] = (byte) (values[i + 3] >>> 4);
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 21;
+
+    buf[off++] = (byte) (values[i + 4] >>> 13);
+
+    buf[off++] = (byte) (values[i + 4] >>> 5);
+
+    buf[off] = (byte) (values[i + 4] << 3);
+    buf[off++] |= values[i + 5] >>> 22;
+
+    buf[off++] = (byte) (values[i + 5] >>> 14);
+
+    buf[off++] = (byte) (values[i + 5] >>> 6);
+
+    buf[off] = (byte) (values[i + 5] << 2);
+    buf[off++] |= values[i + 6] >>> 23;
+
+    buf[off++] = (byte) (values[i + 6] >>> 15);
+
+    buf[off++] = (byte) (values[i + 6] >>> 7);
+
+    buf[off] = (byte) (values[i + 6] << 1);
+    buf[off++] |= values[i + 7] >>> 24;
+
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits26(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 18);
+
+    buf[off++] = (byte) (values[i + 0] >>> 10);
+
+    buf[off++] = (byte) (values[i + 0] >>> 2);
+
+    buf[off] = (byte) (values[i + 0] << 6);
+    buf[off++] |= values[i + 1] >>> 20;
+
+    buf[off++] = (byte) (values[i + 1] >>> 12);
+
+    buf[off++] = (byte) (values[i + 1] >>> 4);
+
+    buf[off] = (byte) (values[i + 1] << 4);
+    buf[off++] |= values[i + 2] >>> 22;
+
+    buf[off++] = (byte) (values[i + 2] >>> 14);
+
+    buf[off++] = (byte) (values[i + 2] >>> 6);
+
+    buf[off] = (byte) (values[i + 2] << 2);
+    buf[off++] |= values[i + 3] >>> 24;
+
+    buf[off++] = (byte) (values[i + 3] >>> 16);
+
+    buf[off++] = (byte) (values[i + 3] >>> 8);
+
+    buf[off++] = (byte) (values[i + 3]);
+
+    buf[off++] = (byte) (values[i + 4] >>> 18);
+
+    buf[off++] = (byte) (values[i + 4] >>> 10);
+
+    buf[off++] = (byte) (values[i + 4] >>> 2);
+
+    buf[off] = (byte) (values[i + 4] << 6);
+    buf[off++] |= values[i + 5] >>> 20;
+
+    buf[off++] = (byte) (values[i + 5] >>> 12);
+
+    buf[off++] = (byte) (values[i + 5] >>> 4);
+
+    buf[off] = (byte) (values[i + 5] << 4);
+    buf[off++] |= values[i + 6] >>> 22;
+
+    buf[off++] = (byte) (values[i + 6] >>> 14);
+
+    buf[off++] = (byte) (values[i + 6] >>> 6);
+
+    buf[off] = (byte) (values[i + 6] << 2);
+    buf[off++] |= values[i + 7] >>> 24;
+
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits27(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 19);
+
+    buf[off++] = (byte) (values[i + 0] >>> 11);
+
+    buf[off++] = (byte) (values[i + 0] >>> 3);
+
+    buf[off] = (byte) (values[i + 0] << 5);
+    buf[off++] |= values[i + 1] >>> 22;
+
+    buf[off++] = (byte) (values[i + 1] >>> 14);
+
+    buf[off++] = (byte) (values[i + 1] >>> 6);
+
+    buf[off] = (byte) (values[i + 1] << 2);
+    buf[off++] |= values[i + 2] >>> 25;
+
+    buf[off++] = (byte) (values[i + 2] >>> 17);
+
+    buf[off++] = (byte) (values[i + 2] >>> 9);
+
+    buf[off++] = (byte) (values[i + 2] >>> 1);
+
+    buf[off] = (byte) (values[i + 2] << 7);
+    buf[off++] |= values[i + 3] >>> 20;
+
+    buf[off++] = (byte) (values[i + 3] >>> 12);
+
+    buf[off++] = (byte) (values[i + 3] >>> 4);
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 23;
+
+    buf[off++] = (byte) (values[i + 4] >>> 15);
+
+    buf[off++] = (byte) (values[i + 4] >>> 7);
+
+    buf[off] = (byte) (values[i + 4] << 1);
+    buf[off++] |= values[i + 5] >>> 26;
+
+    buf[off++] = (byte) (values[i + 5] >>> 18);
+
+    buf[off++] = (byte) (values[i + 5] >>> 10);
+
+    buf[off++] = (byte) (values[i + 5] >>> 2);
+
+    buf[off] = (byte) (values[i + 5] << 6);
+    buf[off++] |= values[i + 6] >>> 21;
+
+    buf[off++] = (byte) (values[i + 6] >>> 13);
+
+    buf[off++] = (byte) (values[i + 6] >>> 5);
+
+    buf[off] = (byte) (values[i + 6] << 3);
+    buf[off++] |= values[i + 7] >>> 24;
+
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits28(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 20);
+    buf[off++] = (byte) (values[i + 0] >>> 12);
+    buf[off++] = (byte) (values[i + 0] >>> 4);
+    buf[off] = (byte) (values[i + 0] << 4);
+    buf[off++] |= values[i + 1] >>> 24;
+    buf[off++] = (byte) (values[i + 1] >>> 16);
+    buf[off++] = (byte) (values[i + 1] >>> 8);
+    buf[off++] = (byte) (values[i + 1]);
+    buf[off++] = (byte) (values[i + 2] >>> 20);
+    buf[off++] = (byte) (values[i + 2] >>> 12);
+    buf[off++] = (byte) (values[i + 2] >>> 4);
+    buf[off] = (byte) (values[i + 2] << 4);
+    buf[off++] |= values[i + 3] >>> 24;
+    buf[off++] = (byte) (values[i + 3] >>> 16);
+    buf[off++] = (byte) (values[i + 3] >>> 8);
+    buf[off++] = (byte) (values[i + 3]);
+    buf[off++] = (byte) (values[i + 4] >>> 20);
+    buf[off++] = (byte) (values[i + 4] >>> 12);
+    buf[off++] = (byte) (values[i + 4] >>> 4);
+    buf[off] = (byte) (values[i + 4] << 4);
+    buf[off++] |= values[i + 5] >>> 24;
+    buf[off++] = (byte) (values[i + 5] >>> 16);
+    buf[off++] = (byte) (values[i + 5] >>> 8);
+    buf[off++] = (byte) (values[i + 5]);
+    buf[off++] = (byte) (values[i + 6] >>> 20);
+    buf[off++] = (byte) (values[i + 6] >>> 12);
+    buf[off++] = (byte) (values[i + 6] >>> 4);
+    buf[off] = (byte) (values[i + 6] << 4);
+    buf[off++] |= values[i + 7] >>> 24;
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits29(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 21);
+
+    buf[off++] = (byte) (values[i + 0] >>> 13);
+
+    buf[off++] = (byte) (values[i + 0] >>> 5);
+
+    buf[off] = (byte) (values[i + 0] << 3);
+    buf[off++] |= values[i + 1] >>> 26;
+
+    buf[off++] = (byte) (values[i + 1] >>> 18);
+
+    buf[off++] = (byte) (values[i + 1] >>> 10);
+
+    buf[off++] = (byte) (values[i + 1] >>> 2);
+
+    buf[off] = (byte) (values[i + 1] << 6);
+    buf[off++] |= values[i + 2] >>> 23;
+
+    buf[off++] = (byte) (values[i + 2] >>> 15);
+
+    buf[off++] = (byte) (values[i + 2] >>> 7);
+
+    buf[off] = (byte) (values[i + 2] << 1);
+    buf[off++] |= values[i + 3] >>> 28;
+
+    buf[off++] = (byte) (values[i + 3] >>> 20);
+
+    buf[off++] = (byte) (values[i + 3] >>> 12);
+
+    buf[off++] = (byte) (values[i + 3] >>> 4);
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 25;
+
+    buf[off++] = (byte) (values[i + 4] >>> 17);
+
+    buf[off++] = (byte) (values[i + 4] >>> 9);
+
+    buf[off++] = (byte) (values[i + 4] >>> 1);
+
+    buf[off] = (byte) (values[i + 4] << 7);
+    buf[off++] |= values[i + 5] >>> 22;
+
+    buf[off++] = (byte) (values[i + 5] >>> 14);
+
+    buf[off++] = (byte) (values[i + 5] >>> 6);
+
+    buf[off] = (byte) (values[i + 5] << 2);
+    buf[off++] |= values[i + 6] >>> 27;
+
+    buf[off++] = (byte) (values[i + 6] >>> 19);
+
+    buf[off++] = (byte) (values[i + 6] >>> 11);
+
+    buf[off++] = (byte) (values[i + 6] >>> 3);
+
+    buf[off] = (byte) (values[i + 6] << 5);
+    buf[off++] |= values[i + 7] >>> 24;
+
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits30(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 22);
+    buf[off++] = (byte) (values[i + 0] >>> 14);
+    buf[off++] = (byte) (values[i + 0] >>> 6);
+
+    buf[off] = (byte) (values[i + 0] << 2);
+    buf[off++] |= values[i + 1] >>> 28;
+    buf[off++] = (byte) (values[i + 1] >>> 20);
+    buf[off++] = (byte) (values[i + 1] >>> 12);
+    buf[off++] = (byte) (values[i + 1] >>> 4);
+
+    buf[off] = (byte) (values[i + 1] << 4);
+    buf[off++] |= values[i + 2] >>> 26;
+    buf[off++] = (byte) (values[i + 2] >>> 18);
+    buf[off++] = (byte) (values[i + 2] >>> 10);
+    buf[off++] = (byte) (values[i + 2] >>> 2);
+
+    buf[off] = (byte) (values[i + 2] << 6);
+    buf[off++] |= values[i + 3] >>> 24;
+    buf[off++] = (byte) (values[i + 3] >>> 16);
+    buf[off++] = (byte) (values[i + 3] >>> 8);
+    buf[off++] = (byte) (values[i + 3]);
+
+    buf[off++] = (byte) (values[i + 4] >>> 22);
+    buf[off++] = (byte) (values[i + 4] >>> 14);
+    buf[off++] = (byte) (values[i + 4] >>> 6);
+
+    buf[off] = (byte) (values[i + 4] << 2);
+    buf[off++] |= values[i + 5] >>> 28;
+    buf[off++] = (byte) (values[i + 5] >>> 20);
+    buf[off++] = (byte) (values[i + 5] >>> 12);
+    buf[off++] = (byte) (values[i + 5] >>> 4);
+
+    buf[off] = (byte) (values[i + 5] << 4);
+    buf[off++] |= values[i + 6] >>> 26;
+    buf[off++] = (byte) (values[i + 6] >>> 18);
+    buf[off++] = (byte) (values[i + 6] >>> 10);
+    buf[off++] = (byte) (values[i + 6] >>> 2);
+
+    buf[off] = (byte) (values[i + 6] << 6);
+    buf[off++] |= values[i + 7] >>> 24;
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits31(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 23);
+    buf[off++] = (byte) (values[i + 0] >>> 15);
+    buf[off++] = (byte) (values[i + 0] >>> 7);
+
+    buf[off] = (byte) (values[i + 0] << 1);
+    buf[off++] |= values[i + 1] >>> 30;
+    buf[off++] = (byte) (values[i + 1] >>> 22);
+    buf[off++] = (byte) (values[i + 1] >>> 14);
+    buf[off++] = (byte) (values[i + 1] >>> 6);
+
+    buf[off] = (byte) (values[i + 1] << 2);
+    buf[off++] |= values[i + 2] >>> 29;
+    buf[off++] = (byte) (values[i + 2] >>> 21);
+    buf[off++] = (byte) (values[i + 2] >>> 13);
+    buf[off++] = (byte) (values[i + 2] >>> 5);
+
+    buf[off] = (byte) (values[i + 2] << 3);
+    buf[off++] |= values[i + 3] >>> 28;
+    buf[off++] = (byte) (values[i + 3] >>> 20);
+    buf[off++] = (byte) (values[i + 3] >>> 12);
+    buf[off++] = (byte) (values[i + 3] >>> 4);
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 27;
+    buf[off++] = (byte) (values[i + 4] >>> 19);
+    buf[off++] = (byte) (values[i + 4] >>> 11);
+    buf[off++] = (byte) (values[i + 4] >>> 3);
+
+    buf[off] = (byte) (values[i + 4] << 5);
+    buf[off++] |= values[i + 5] >>> 26;
+    buf[off++] = (byte) (values[i + 5] >>> 18);
+    buf[off++] = (byte) (values[i + 5] >>> 10);
+    buf[off++] = (byte) (values[i + 5] >>> 2);
+
+    buf[off] = (byte) (values[i + 5] << 6);
+    buf[off++] |= values[i + 6] >>> 25;
+    buf[off++] = (byte) (values[i + 6] >>> 17);
+    buf[off++] = (byte) (values[i + 6] >>> 9);
+    buf[off++] = (byte) (values[i + 6] >>> 1);
+
+    buf[off] = (byte) (values[i + 6] << 7);
+    buf[off++] |= values[i + 7] >>> 24;
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits32(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 24);
+    buf[off++] = (byte) (values[i + 0] >>> 16);
+    buf[off++] = (byte) (values[i + 0] >>> 8);
+    buf[off++] = (byte) (values[i + 0]);
+
+    buf[off++] = (byte) (values[i + 1] >>> 24);
+    buf[off++] = (byte) (values[i + 1] >>> 16);
+    buf[off++] = (byte) (values[i + 1] >>> 8);
+    buf[off++] = (byte) (values[i + 1]);
+
+    buf[off++] = (byte) (values[i + 2] >>> 24);
+    buf[off++] = (byte) (values[i + 2] >>> 16);
+    buf[off++] = (byte) (values[i + 2] >>> 8);
+    buf[off++] = (byte) (values[i + 2]);
+
+    buf[off++] = (byte) (values[i + 3] >>> 24);
+    buf[off++] = (byte) (values[i + 3] >>> 16);
+    buf[off++] = (byte) (values[i + 3] >>> 8);
+    buf[off++] = (byte) (values[i + 3]);
+
+    buf[off++] = (byte) (values[i + 4] >>> 24);
+    buf[off++] = (byte) (values[i + 4] >>> 16);
+    buf[off++] = (byte) (values[i + 4] >>> 8);
+    buf[off++] = (byte) (values[i + 4]);
+
+    buf[off++] = (byte) (values[i + 5] >>> 24);
+    buf[off++] = (byte) (values[i + 5] >>> 16);
+    buf[off++] = (byte) (values[i + 5] >>> 8);
+    buf[off++] = (byte) (values[i + 5]);
+
+    buf[off++] = (byte) (values[i + 6] >>> 24);
+    buf[off++] = (byte) (values[i + 6] >>> 16);
+    buf[off++] = (byte) (values[i + 6] >>> 8);
+    buf[off++] = (byte) (values[i + 6]);
+
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits33(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 25);
+    buf[off++] = (byte) (values[i + 0] >>> 17);
+    buf[off++] = (byte) (values[i + 0] >>> 9);
+    buf[off++] = (byte) (values[i + 0] >>> 1);
+
+    buf[off] = (byte) (values[i + 0] << 7);
+    buf[off++] |= values[i + 1] >>> 26;
+    buf[off++] = (byte) (values[i + 1] >>> 18);
+    buf[off++] = (byte) (values[i + 1] >>> 10);
+    buf[off++] = (byte) (values[i + 1] >>> 2);
+
+    buf[off] = (byte) (values[i + 1] << 6);
+    buf[off++] |= values[i + 2] >>> 27;
+    buf[off++] = (byte) (values[i + 2] >>> 19);
+    buf[off++] = (byte) (values[i + 2] >>> 11);
+    buf[off++] = (byte) (values[i + 2] >>> 3);
+
+    buf[off] = (byte) (values[i + 2] << 5);
+    buf[off++] |= values[i + 3] >>> 28;
+    buf[off++] = (byte) (values[i + 3] >>> 20);
+    buf[off++] = (byte) (values[i + 3] >>> 12);
+    buf[off++] = (byte) (values[i + 3] >>> 4);
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 29;
+    buf[off++] = (byte) (values[i + 4] >>> 21);
+    buf[off++] = (byte) (values[i + 4] >>> 13);
+    buf[off++] = (byte) (values[i + 4] >>> 5);
+
+    buf[off] = (byte) (values[i + 4] << 3);
+    buf[off++] |= values[i + 5] >>> 30;
+    buf[off++] = (byte) (values[i + 5] >>> 22);
+    buf[off++] = (byte) (values[i + 5] >>> 14);
+    buf[off++] = (byte) (values[i + 5] >>> 6);
+
+    buf[off] = (byte) (values[i + 5] << 2);
+    buf[off++] |= values[i + 6] >>> 31;
+    buf[off++] = (byte) (values[i + 6] >>> 23);
+    buf[off++] = (byte) (values[i + 6] >>> 15);
+    buf[off++] = (byte) (values[i + 6] >>> 7);
+
+    buf[off] = (byte) (values[i + 6] << 1);
+    buf[off++] |= values[i + 7] >>> 32;
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits34(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 26);
+    buf[off++] = (byte) (values[i + 0] >>> 18);
+    buf[off++] = (byte) (values[i + 0] >>> 10);
+    buf[off++] = (byte) (values[i + 0] >>> 2);
+
+    buf[off] = (byte) (values[i + 0] << 6);
+    buf[off++] |= values[i + 1] >>> 28;
+    buf[off++] = (byte) (values[i + 1] >>> 20);
+    buf[off++] = (byte) (values[i + 1] >>> 12);
+    buf[off++] = (byte) (values[i + 1] >>> 4);
+
+    buf[off] = (byte) (values[i + 1] << 4);
+    buf[off++] |= values[i + 2] >>> 30;
+    buf[off++] = (byte) (values[i + 2] >>> 22);
+    buf[off++] = (byte) (values[i + 2] >>> 14);
+    buf[off++] = (byte) (values[i + 2] >>> 6);
+
+    buf[off] = (byte) (values[i + 2] << 2);
+    buf[off++] |= values[i + 3] >>> 32;
+    buf[off++] = (byte) (values[i + 3] >>> 24);
+    buf[off++] = (byte) (values[i + 3] >>> 16);
+    buf[off++] = (byte) (values[i + 3] >>> 8);
+    buf[off++] = (byte) (values[i + 3]);
+
+    buf[off++] = (byte) (values[i + 4] >>> 26);
+    buf[off++] = (byte) (values[i + 4] >>> 18);
+    buf[off++] = (byte) (values[i + 4] >>> 10);
+    buf[off++] = (byte) (values[i + 4] >>> 2);
+
+    buf[off] = (byte) (values[i + 4] << 6);
+    buf[off++] |= values[i + 5] >>> 28;
+    buf[off++] = (byte) (values[i + 5] >>> 20);
+    buf[off++] = (byte) (values[i + 5] >>> 12);
+    buf[off++] = (byte) (values[i + 5] >>> 4);
+
+    buf[off] = (byte) (values[i + 5] << 4);
+    buf[off++] |= values[i + 6] >>> 30;
+    buf[off++] = (byte) (values[i + 6] >>> 22);
+    buf[off++] = (byte) (values[i + 6] >>> 14);
+    buf[off++] = (byte) (values[i + 6] >>> 6);
+
+    buf[off] = (byte) (values[i + 6] << 2);
+    buf[off++] |= values[i + 7] >>> 32;
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits35(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 27);
+    buf[off++] = (byte) (values[i + 0] >>> 19);
+    buf[off++] = (byte) (values[i + 0] >>> 11);
+    buf[off++] = (byte) (values[i + 0] >>> 3);
+
+    buf[off] = (byte) (values[i + 0] << 5);
+    buf[off++] |= values[i + 1] >>> 30;
+    buf[off++] = (byte) (values[i + 1] >>> 22);
+    buf[off++] = (byte) (values[i + 1] >>> 14);
+    buf[off++] = (byte) (values[i + 1] >>> 6);
+
+    buf[off] = (byte) (values[i + 1] << 2);
+    buf[off++] |= values[i + 2] >>> 33;
+    buf[off++] = (byte) (values[i + 2] >>> 25);
+    buf[off++] = (byte) (values[i + 2] >>> 17);
+    buf[off++] = (byte) (values[i + 2] >>> 9);
+    buf[off++] = (byte) (values[i + 2] >>> 1);
+
+    buf[off] = (byte) (values[i + 2] << 7);
+    buf[off++] |= values[i + 3] >>> 28;
+    buf[off++] = (byte) (values[i + 3] >>> 20);
+    buf[off++] = (byte) (values[i + 3] >>> 12);
+    buf[off++] = (byte) (values[i + 3] >>> 4);
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 31;
+    buf[off++] = (byte) (values[i + 4] >>> 23);
+    buf[off++] = (byte) (values[i + 4] >>> 15);
+    buf[off++] = (byte) (values[i + 4] >>> 7);
+
+    buf[off] = (byte) (values[i + 4] << 1);
+    buf[off++] |= values[i + 5] >>> 34;
+    buf[off++] = (byte) (values[i + 5] >>> 26);
+    buf[off++] = (byte) (values[i + 5] >>> 18);
+    buf[off++] = (byte) (values[i + 5] >>> 10);
+    buf[off++] = (byte) (values[i + 5] >>> 2);
+
+    buf[off] = (byte) (values[i + 5] << 6);
+    buf[off++] |= values[i + 6] >>> 29;
+    buf[off++] = (byte) (values[i + 6] >>> 21);
+    buf[off++] = (byte) (values[i + 6] >>> 13);
+    buf[off++] = (byte) (values[i + 6] >>> 5);
+
+    buf[off] = (byte) (values[i + 6] << 3);
+    buf[off++] |= values[i + 7] >>> 32;
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits36(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 28);
+    buf[off++] = (byte) (values[i + 0] >>> 20);
+    buf[off++] = (byte) (values[i + 0] >>> 12);
+    buf[off++] = (byte) (values[i + 0] >>> 4);
+
+    buf[off] = (byte) (values[i + 0] << 4);
+    buf[off++] |= values[i + 1] >>> 32;
+    buf[off++] = (byte) (values[i + 1] >>> 24);
+    buf[off++] = (byte) (values[i + 1] >>> 16);
+    buf[off++] = (byte) (values[i + 1] >>> 8);
+    buf[off++] = (byte) (values[i + 1]);
+
+    buf[off++] = (byte) (values[i + 2] >>> 28);
+    buf[off++] = (byte) (values[i + 2] >>> 20);
+    buf[off++] = (byte) (values[i + 2] >>> 12);
+    buf[off++] = (byte) (values[i + 2] >>> 4);
+
+    buf[off] = (byte) (values[i + 2] << 4);
+    buf[off++] |= values[i + 3] >>> 32;
+    buf[off++] = (byte) (values[i + 3] >>> 24);
+    buf[off++] = (byte) (values[i + 3] >>> 16);
+    buf[off++] = (byte) (values[i + 3] >>> 8);
+    buf[off++] = (byte) (values[i + 3]);
+
+    buf[off++] = (byte) (values[i + 4] >>> 28);
+    buf[off++] = (byte) (values[i + 4] >>> 20);
+    buf[off++] = (byte) (values[i + 4] >>> 12);
+    buf[off++] = (byte) (values[i + 4] >>> 4);
+
+    buf[off] = (byte) (values[i + 4] << 4);
+    buf[off++] |= values[i + 5] >>> 32;
+    buf[off++] = (byte) (values[i + 5] >>> 24);
+    buf[off++] = (byte) (values[i + 5] >>> 16);
+    buf[off++] = (byte) (values[i + 5] >>> 8);
+    buf[off++] = (byte) (values[i + 5]);
+
+    buf[off++] = (byte) (values[i + 6] >>> 28);
+    buf[off++] = (byte) (values[i + 6] >>> 20);
+    buf[off++] = (byte) (values[i + 6] >>> 12);
+    buf[off++] = (byte) (values[i + 6] >>> 4);
+
+    buf[off] = (byte) (values[i + 6] << 4);
+    buf[off++] |= values[i + 7] >>> 32;
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits37(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 29);
+    buf[off++] = (byte) (values[i + 0] >>> 21);
+    buf[off++] = (byte) (values[i + 0] >>> 13);
+    buf[off++] = (byte) (values[i + 0] >>> 5);
+
+    buf[off] = (byte) (values[i + 0] << 3);
+    buf[off++] |= values[i + 1] >>> 34;
+    buf[off++] = (byte) (values[i + 1] >>> 26);
+    buf[off++] = (byte) (values[i + 1] >>> 18);
+    buf[off++] = (byte) (values[i + 1] >>> 10);
+    buf[off++] = (byte) (values[i + 1] >>> 2);
+
+    buf[off] = (byte) (values[i + 1] << 6);
+    buf[off++] |= values[i + 2] >>> 31;
+    buf[off++] = (byte) (values[i + 2] >>> 23);
+    buf[off++] = (byte) (values[i + 2] >>> 15);
+    buf[off++] = (byte) (values[i + 2] >>> 7);
+
+    buf[off] = (byte) (values[i + 2] << 1);
+    buf[off++] |= values[i + 3] >>> 36;
+    buf[off++] = (byte) (values[i + 3] >>> 28);
+    buf[off++] = (byte) (values[i + 3] >>> 20);
+    buf[off++] = (byte) (values[i + 3] >>> 12);
+    buf[off++] = (byte) (values[i + 3] >>> 4);
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 33;
+    buf[off++] = (byte) (values[i + 4] >>> 25);
+    buf[off++] = (byte) (values[i + 4] >>> 17);
+    buf[off++] = (byte) (values[i + 4] >>> 9);
+    buf[off++] = (byte) (values[i + 4] >>> 1);
+
+    buf[off] = (byte) (values[i + 4] << 7);
+    buf[off++] |= values[i + 5] >>> 30;
+    buf[off++] = (byte) (values[i + 5] >>> 22);
+    buf[off++] = (byte) (values[i + 5] >>> 14);
+    buf[off++] = (byte) (values[i + 5] >>> 6);
+
+    buf[off] = (byte) (values[i + 5] << 2);
+    buf[off++] |= values[i + 6] >>> 35;
+    buf[off++] = (byte) (values[i + 6] >>> 27);
+    buf[off++] = (byte) (values[i + 6] >>> 19);
+    buf[off++] = (byte) (values[i + 6] >>> 11);
+    buf[off++] = (byte) (values[i + 6] >>> 3);
+
+    buf[off] = (byte) (values[i + 6] << 5);
+    buf[off++] |= values[i + 7] >>> 32;
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits38(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 30);
+    buf[off++] = (byte) (values[i + 0] >>> 22);
+    buf[off++] = (byte) (values[i + 0] >>> 14);
+    buf[off++] = (byte) (values[i + 0] >>> 6);
+
+    buf[off] = (byte) (values[i + 0] << 2);
+    buf[off++] |= values[i + 1] >>> 36;
+    buf[off++] = (byte) (values[i + 1] >>> 28);
+    buf[off++] = (byte) (values[i + 1] >>> 20);
+    buf[off++] = (byte) (values[i + 1] >>> 12);
+    buf[off++] = (byte) (values[i + 1] >>> 4);
+
+    buf[off] = (byte) (values[i + 1] << 4);
+    buf[off++] |= values[i + 2] >>> 34;
+    buf[off++] = (byte) (values[i + 2] >>> 26);
+    buf[off++] = (byte) (values[i + 2] >>> 18);
+    buf[off++] = (byte) (values[i + 2] >>> 10);
+    buf[off++] = (byte) (values[i + 2] >>> 2);
+
+    buf[off] = (byte) (values[i + 2] << 6);
+    buf[off++] |= values[i + 3] >>> 32;
+    buf[off++] = (byte) (values[i + 3] >>> 24);
+    buf[off++] = (byte) (values[i + 3] >>> 16);
+    buf[off++] = (byte) (values[i + 3] >>> 8);
+    buf[off++] = (byte) (values[i + 3]);
+
+    buf[off++] = (byte) (values[i + 4] >>> 30);
+    buf[off++] = (byte) (values[i + 4] >>> 22);
+    buf[off++] = (byte) (values[i + 4] >>> 14);
+    buf[off++] = (byte) (values[i + 4] >>> 6);
+
+    buf[off] = (byte) (values[i + 4] << 2);
+    buf[off++] |= values[i + 5] >>> 36;
+    buf[off++] = (byte) (values[i + 5] >>> 28);
+    buf[off++] = (byte) (values[i + 5] >>> 20);
+    buf[off++] = (byte) (values[i + 5] >>> 12);
+    buf[off++] = (byte) (values[i + 5] >>> 4);
+
+    buf[off] = (byte) (values[i + 5] << 4);
+    buf[off++] |= values[i + 6] >>> 34;
+    buf[off++] = (byte) (values[i + 6] >>> 26);
+    buf[off++] = (byte) (values[i + 6] >>> 18);
+    buf[off++] = (byte) (values[i + 6] >>> 10);
+    buf[off++] = (byte) (values[i + 6] >>> 2);
+
+    buf[off] = (byte) (values[i + 6] << 6);
+    buf[off++] |= values[i + 7] >>> 32;
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits39(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 31);
+    buf[off++] = (byte) (values[i + 0] >>> 23);
+    buf[off++] = (byte) (values[i + 0] >>> 15);
+    buf[off++] = (byte) (values[i + 0] >>> 7);
+
+    buf[off] = (byte) (values[i + 0] << 1);
+    buf[off++] |= values[i + 1] >>> 38;
+    buf[off++] = (byte) (values[i + 1] >>> 30);
+    buf[off++] = (byte) (values[i + 1] >>> 22);
+    buf[off++] = (byte) (values[i + 1] >>> 14);
+    buf[off++] = (byte) (values[i + 1] >>> 6);
+
+    buf[off] = (byte) (values[i + 1] << 2);
+    buf[off++] |= values[i + 2] >>> 37;
+    buf[off++] = (byte) (values[i + 2] >>> 29);
+    buf[off++] = (byte) (values[i + 2] >>> 21);
+    buf[off++] = (byte) (values[i + 2] >>> 13);
+    buf[off++] = (byte) (values[i + 2] >>> 5);
+
+    buf[off] = (byte) (values[i + 2] << 3);
+    buf[off++] |= values[i + 3] >>> 36;
+    buf[off++] = (byte) (values[i + 3] >>> 28);
+    buf[off++] = (byte) (values[i + 3] >>> 20);
+    buf[off++] = (byte) (values[i + 3] >>> 12);
+    buf[off++] = (byte) (values[i + 3] >>> 4);
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 35;
+    buf[off++] = (byte) (values[i + 4] >>> 27);
+    buf[off++] = (byte) (values[i + 4] >>> 19);
+    buf[off++] = (byte) (values[i + 4] >>> 11);
+    buf[off++] = (byte) (values[i + 4] >>> 3);
+
+    buf[off] = (byte) (values[i + 4] << 5);
+    buf[off++] |= values[i + 5] >>> 34;
+    buf[off++] = (byte) (values[i + 5] >>> 26);
+    buf[off++] = (byte) (values[i + 5] >>> 18);
+    buf[off++] = (byte) (values[i + 5] >>> 10);
+    buf[off++] = (byte) (values[i + 5] >>> 2);
+
+    buf[off] = (byte) (values[i + 5] << 6);
+    buf[off++] |= values[i + 6] >>> 33;
+    buf[off++] = (byte) (values[i + 6] >>> 25);
+    buf[off++] = (byte) (values[i + 6] >>> 17);
+    buf[off++] = (byte) (values[i + 6] >>> 9);
+    buf[off++] = (byte) (values[i + 6] >>> 1);
+
+    buf[off] = (byte) (values[i + 6] << 7);
+    buf[off++] |= values[i + 7] >>> 32;
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits40(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 32);
+    buf[off++] = (byte) (values[i + 0] >>> 24);
+    buf[off++] = (byte) (values[i + 0] >>> 16);
+    buf[off++] = (byte) (values[i + 0] >>> 8);
+    buf[off++] = (byte) (values[i + 0]);
+
+    buf[off++] = (byte) (values[i + 1] >>> 32);
+    buf[off++] = (byte) (values[i + 1] >>> 24);
+    buf[off++] = (byte) (values[i + 1] >>> 16);
+    buf[off++] = (byte) (values[i + 1] >>> 8);
+    buf[off++] = (byte) (values[i + 1]);
+
+    buf[off++] = (byte) (values[i + 2] >>> 32);
+    buf[off++] = (byte) (values[i + 2] >>> 24);
+    buf[off++] = (byte) (values[i + 2] >>> 16);
+    buf[off++] = (byte) (values[i + 2] >>> 8);
+    buf[off++] = (byte) (values[i + 2]);
+
+    buf[off++] = (byte) (values[i + 3] >>> 32);
+    buf[off++] = (byte) (values[i + 3] >>> 24);
+    buf[off++] = (byte) (values[i + 3] >>> 16);
+    buf[off++] = (byte) (values[i + 3] >>> 8);
+    buf[off++] = (byte) (values[i + 3]);
+
+    buf[off++] = (byte) (values[i + 4] >>> 32);
+    buf[off++] = (byte) (values[i + 4] >>> 24);
+    buf[off++] = (byte) (values[i + 4] >>> 16);
+    buf[off++] = (byte) (values[i + 4] >>> 8);
+    buf[off++] = (byte) (values[i + 4]);
+
+    buf[off++] = (byte) (values[i + 5] >>> 32);
+    buf[off++] = (byte) (values[i + 5] >>> 24);
+    buf[off++] = (byte) (values[i + 5] >>> 16);
+    buf[off++] = (byte) (values[i + 5] >>> 8);
+    buf[off++] = (byte) (values[i + 5]);
+
+    buf[off++] = (byte) (values[i + 6] >>> 32);
+    buf[off++] = (byte) (values[i + 6] >>> 24);
+    buf[off++] = (byte) (values[i + 6] >>> 16);
+    buf[off++] = (byte) (values[i + 6] >>> 8);
+    buf[off++] = (byte) (values[i + 6]);
+
+    buf[off++] = (byte) (values[i + 7] >>> 32);
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits41(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 33);
+    buf[off++] = (byte) (values[i + 0] >>> 25);
+    buf[off++] = (byte) (values[i + 0] >>> 17);
+    buf[off++] = (byte) (values[i + 0] >>> 9);
+    buf[off++] = (byte) (values[i + 0] >>> 1);
+
+    buf[off] = (byte) (values[i + 0] << 7);
+    buf[off++] |= values[i + 1] >>> 34;
+    buf[off++] = (byte) (values[i + 1] >>> 26);
+    buf[off++] = (byte) (values[i + 1] >>> 18);
+    buf[off++] = (byte) (values[i + 1] >>> 10);
+    buf[off++] = (byte) (values[i + 1] >>> 2);
+
+    buf[off] = (byte) (values[i + 1] << 6);
+    buf[off++] |= values[i + 2] >>> 35;
+    buf[off++] = (byte) (values[i + 2] >>> 27);
+    buf[off++] = (byte) (values[i + 2] >>> 19);
+    buf[off++] = (byte) (values[i + 2] >>> 11);
+    buf[off++] = (byte) (values[i + 2] >>> 3);
+
+    buf[off] = (byte) (values[i + 2] << 5);
+    buf[off++] |= values[i + 3] >>> 36;
+    buf[off++] = (byte) (values[i + 3] >>> 28);
+    buf[off++] = (byte) (values[i + 3] >>> 20);
+    buf[off++] = (byte) (values[i + 3] >>> 12);
+    buf[off++] = (byte) (values[i + 3] >>> 4);
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 37;
+    buf[off++] = (byte) (values[i + 4] >>> 29);
+    buf[off++] = (byte) (values[i + 4] >>> 21);
+    buf[off++] = (byte) (values[i + 4] >>> 13);
+    buf[off++] = (byte) (values[i + 4] >>> 5);
+
+    buf[off] = (byte) (values[i + 4] << 3);
+    buf[off++] |= values[i + 5] >>> 38;
+    buf[off++] = (byte) (values[i + 5] >>> 30);
+    buf[off++] = (byte) (values[i + 5] >>> 22);
+    buf[off++] = (byte) (values[i + 5] >>> 14);
+    buf[off++] = (byte) (values[i + 5] >>> 6);
+
+    buf[off] = (byte) (values[i + 5] << 2);
+    buf[off++] |= values[i + 6] >>> 39;
+    buf[off++] = (byte) (values[i + 6] >>> 31);
+    buf[off++] = (byte) (values[i + 6] >>> 23);
+    buf[off++] = (byte) (values[i + 6] >>> 15);
+    buf[off++] = (byte) (values[i + 6] >>> 7);
+
+    buf[off] = (byte) (values[i + 6] << 1);
+    buf[off++] |= values[i + 7] >>> 40;
+    buf[off++] = (byte) (values[i + 7] >>> 32);
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits42(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 34);
+    buf[off++] = (byte) (values[i + 0] >>> 26);
+    buf[off++] = (byte) (values[i + 0] >>> 18);
+    buf[off++] = (byte) (values[i + 0] >>> 10);
+    buf[off++] = (byte) (values[i + 0] >>> 2);
+
+    buf[off] = (byte) (values[i + 0] << 6);
+    buf[off++] |= values[i + 1] >>> 36;
+    buf[off++] = (byte) (values[i + 1] >>> 28);
+    buf[off++] = (byte) (values[i + 1] >>> 20);
+    buf[off++] = (byte) (values[i + 1] >>> 12);
+    buf[off++] = (byte) (values[i + 1] >>> 4);
+
+    buf[off] = (byte) (values[i + 1] << 4);
+    buf[off++] |= values[i + 2] >>> 38;
+    buf[off++] = (byte) (values[i + 2] >>> 30);
+    buf[off++] = (byte) (values[i + 2] >>> 22);
+    buf[off++] = (byte) (values[i + 2] >>> 14);
+    buf[off++] = (byte) (values[i + 2] >>> 6);
+
+    buf[off] = (byte) (values[i + 2] << 2);
+    buf[off++] |= values[i + 3] >>> 40;
+    buf[off++] = (byte) (values[i + 3] >>> 32);
+    buf[off++] = (byte) (values[i + 3] >>> 24);
+    buf[off++] = (byte) (values[i + 3] >>> 16);
+    buf[off++] = (byte) (values[i + 3] >>> 8);
+    buf[off++] = (byte) (values[i + 3]);
+
+    buf[off++] = (byte) (values[i + 4] >>> 34);
+    buf[off++] = (byte) (values[i + 4] >>> 26);
+    buf[off++] = (byte) (values[i + 4] >>> 18);
+    buf[off++] = (byte) (values[i + 4] >>> 10);
+    buf[off++] = (byte) (values[i + 4] >>> 2);
+
+    buf[off] = (byte) (values[i + 4] << 6);
+    buf[off++] |= values[i + 5] >>> 36;
+    buf[off++] = (byte) (values[i + 5] >>> 28);
+    buf[off++] = (byte) (values[i + 5] >>> 20);
+    buf[off++] = (byte) (values[i + 5] >>> 12);
+    buf[off++] = (byte) (values[i + 5] >>> 4);
+
+    buf[off] = (byte) (values[i + 5] << 4);
+    buf[off++] |= values[i + 6] >>> 38;
+    buf[off++] = (byte) (values[i + 6] >>> 30);
+    buf[off++] = (byte) (values[i + 6] >>> 22);
+    buf[off++] = (byte) (values[i + 6] >>> 14);
+    buf[off++] = (byte) (values[i + 6] >>> 6);
+
+    buf[off] = (byte) (values[i + 6] << 2);
+    buf[off++] |= values[i + 7] >>> 40;
+    buf[off++] = (byte) (values[i + 7] >>> 32);
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits43(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 35);
+    buf[off++] = (byte) (values[i + 0] >>> 27);
+    buf[off++] = (byte) (values[i + 0] >>> 19);
+    buf[off++] = (byte) (values[i + 0] >>> 11);
+    buf[off++] = (byte) (values[i + 0] >>> 3);
+
+    buf[off] = (byte) (values[i + 0] << 5);
+    buf[off++] |= values[i + 1] >>> 38;
+    buf[off++] = (byte) (values[i + 1] >>> 30);
+    buf[off++] = (byte) (values[i + 1] >>> 22);
+    buf[off++] = (byte) (values[i + 1] >>> 14);
+    buf[off++] = (byte) (values[i + 1] >>> 6);
+
+    buf[off] = (byte) (values[i + 1] << 2);
+    buf[off++] |= values[i + 2] >>> 41;
+    buf[off++] = (byte) (values[i + 2] >>> 33);
+    buf[off++] = (byte) (values[i + 2] >>> 25);
+    buf[off++] = (byte) (values[i + 2] >>> 17);
+    buf[off++] = (byte) (values[i + 2] >>> 9);
+    buf[off++] = (byte) (values[i + 2] >>> 1);
+
+    buf[off] = (byte) (values[i + 2] << 7);
+    buf[off++] |= values[i + 3] >>> 36;
+    buf[off++] = (byte) (values[i + 3] >>> 28);
+    buf[off++] = (byte) (values[i + 3] >>> 20);
+    buf[off++] = (byte) (values[i + 3] >>> 12);
+    buf[off++] = (byte) (values[i + 3] >>> 4);
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 39;
+    buf[off++] = (byte) (values[i + 4] >>> 31);
+    buf[off++] = (byte) (values[i + 4] >>> 23);
+    buf[off++] = (byte) (values[i + 4] >>> 15);
+    buf[off++] = (byte) (values[i + 4] >>> 7);
+
+    buf[off] = (byte) (values[i + 4] << 1);
+    buf[off++] |= values[i + 5] >>> 42;
+    buf[off++] = (byte) (values[i + 5] >>> 34);
+    buf[off++] = (byte) (values[i + 5] >>> 26);
+    buf[off++] = (byte) (values[i + 5] >>> 18);
+    buf[off++] = (byte) (values[i + 5] >>> 10);
+    buf[off++] = (byte) (values[i + 5] >>> 2);
+
+    buf[off] = (byte) (values[i + 5] << 6);
+    buf[off++] |= values[i + 6] >>> 37;
+    buf[off++] = (byte) (values[i + 6] >>> 29);
+    buf[off++] = (byte) (values[i + 6] >>> 21);
+    buf[off++] = (byte) (values[i + 6] >>> 13);
+    buf[off++] = (byte) (values[i + 6] >>> 5);
+
+    buf[off] = (byte) (values[i + 6] << 3);
+    buf[off++] |= values[i + 7] >>> 40;
+    buf[off++] = (byte) (values[i + 7] >>> 32);
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits44(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 36);
+    buf[off++] = (byte) (values[i + 0] >>> 28);
+    buf[off++] = (byte) (values[i + 0] >>> 20);
+    buf[off++] = (byte) (values[i + 0] >>> 12);
+    buf[off++] = (byte) (values[i + 0] >>> 4);
+
+    buf[off] = (byte) (values[i + 0] << 4);
+    buf[off++] |= values[i + 1] >>> 40;
+    buf[off++] = (byte) (values[i + 1] >>> 32);
+    buf[off++] = (byte) (values[i + 1] >>> 24);
+    buf[off++] = (byte) (values[i + 1] >>> 16);
+    buf[off++] = (byte) (values[i + 1] >>> 8);
+    buf[off++] = (byte) (values[i + 1]);
+
+    buf[off++] = (byte) (values[i + 2] >>> 36);
+    buf[off++] = (byte) (values[i + 2] >>> 28);
+    buf[off++] = (byte) (values[i + 2] >>> 20);
+    buf[off++] = (byte) (values[i + 2] >>> 12);
+    buf[off++] = (byte) (values[i + 2] >>> 4);
+
+    buf[off] = (byte) (values[i + 2] << 4);
+    buf[off++] |= values[i + 3] >>> 40;
+    buf[off++] = (byte) (values[i + 3] >>> 32);
+    buf[off++] = (byte) (values[i + 3] >>> 24);
+    buf[off++] = (byte) (values[i + 3] >>> 16);
+    buf[off++] = (byte) (values[i + 3] >>> 8);
+    buf[off++] = (byte) (values[i + 3]);
+
+    buf[off++] = (byte) (values[i + 4] >>> 36);
+    buf[off++] = (byte) (values[i + 4] >>> 28);
+    buf[off++] = (byte) (values[i + 4] >>> 20);
+    buf[off++] = (byte) (values[i + 4] >>> 12);
+    buf[off++] = (byte) (values[i + 4] >>> 4);
+
+    buf[off] = (byte) (values[i + 4] << 4);
+    buf[off++] |= values[i + 5] >>> 40;
+    buf[off++] = (byte) (values[i + 5] >>> 32);
+    buf[off++] = (byte) (values[i + 5] >>> 24);
+    buf[off++] = (byte) (values[i + 5] >>> 16);
+    buf[off++] = (byte) (values[i + 5] >>> 8);
+    buf[off++] = (byte) (values[i + 5]);
+
+    buf[off++] = (byte) (values[i + 6] >>> 36);
+    buf[off++] = (byte) (values[i + 6] >>> 28);
+    buf[off++] = (byte) (values[i + 6] >>> 20);
+    buf[off++] = (byte) (values[i + 6] >>> 12);
+    buf[off++] = (byte) (values[i + 6] >>> 4);
+
+    buf[off] = (byte) (values[i + 6] << 4);
+    buf[off++] |= values[i + 7] >>> 40;
+    buf[off++] = (byte) (values[i + 7] >>> 32);
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits45(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 37);
+    buf[off++] = (byte) (values[i + 0] >>> 29);
+    buf[off++] = (byte) (values[i + 0] >>> 21);
+    buf[off++] = (byte) (values[i + 0] >>> 13);
+    buf[off++] = (byte) (values[i + 0] >>> 5);
+
+    buf[off] = (byte) (values[i + 0] << 3);
+    buf[off++] |= values[i + 1] >>> 42;
+    buf[off++] = (byte) (values[i + 1] >>> 34);
+    buf[off++] = (byte) (values[i + 1] >>> 26);
+    buf[off++] = (byte) (values[i + 1] >>> 18);
+    buf[off++] = (byte) (values[i + 1] >>> 10);
+    buf[off++] = (byte) (values[i + 1] >>> 2);
+
+    buf[off] = (byte) (values[i + 1] << 6);
+    buf[off++] |= values[i + 2] >>> 39;
+    buf[off++] = (byte) (values[i + 2] >>> 31);
+    buf[off++] = (byte) (values[i + 2] >>> 23);
+    buf[off++] = (byte) (values[i + 2] >>> 15);
+    buf[off++] = (byte) (values[i + 2] >>> 7);
+
+    buf[off] = (byte) (values[i + 2] << 1);
+    buf[off++] |= values[i + 3] >>> 44;
+    buf[off++] = (byte) (values[i + 3] >>> 36);
+    buf[off++] = (byte) (values[i + 3] >>> 28);
+    buf[off++] = (byte) (values[i + 3] >>> 20);
+    buf[off++] = (byte) (values[i + 3] >>> 12);
+    buf[off++] = (byte) (values[i + 3] >>> 4);
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 41;
+    buf[off++] = (byte) (values[i + 4] >>> 33);
+    buf[off++] = (byte) (values[i + 4] >>> 25);
+    buf[off++] = (byte) (values[i + 4] >>> 17);
+    buf[off++] = (byte) (values[i + 4] >>> 9);
+    buf[off++] = (byte) (values[i + 4] >>> 1);
+
+    buf[off] = (byte) (values[i + 4] << 7);
+    buf[off++] |= values[i + 5] >>> 38;
+    buf[off++] = (byte) (values[i + 5] >>> 30);
+    buf[off++] = (byte) (values[i + 5] >>> 22);
+    buf[off++] = (byte) (values[i + 5] >>> 14);
+    buf[off++] = (byte) (values[i + 5] >>> 6);
+
+    buf[off] = (byte) (values[i + 5] << 2);
+    buf[off++] |= values[i + 6] >>> 43;
+    buf[off++] = (byte) (values[i + 6] >>> 35);
+    buf[off++] = (byte) (values[i + 6] >>> 27);
+    buf[off++] = (byte) (values[i + 6] >>> 19);
+    buf[off++] = (byte) (values[i + 6] >>> 11);
+    buf[off++] = (byte) (values[i + 6] >>> 3);
+
+    buf[off] = (byte) (values[i + 6] << 5);
+    buf[off++] |= values[i + 7] >>> 40;
+    buf[off++] = (byte) (values[i + 7] >>> 32);
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits46(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 38);
+    buf[off++] = (byte) (values[i + 0] >>> 30);
+    buf[off++] = (byte) (values[i + 0] >>> 22);
+    buf[off++] = (byte) (values[i + 0] >>> 14);
+    buf[off++] = (byte) (values[i + 0] >>> 6);
+
+    buf[off] = (byte) (values[i + 0] << 2);
+    buf[off++] |= values[i + 1] >>> 44;
+    buf[off++] = (byte) (values[i + 1] >>> 36);
+    buf[off++] = (byte) (values[i + 1] >>> 28);
+    buf[off++] = (byte) (values[i + 1] >>> 20);
+    buf[off++] = (byte) (values[i + 1] >>> 12);
+    buf[off++] = (byte) (values[i + 1] >>> 4);
+
+    buf[off] = (byte) (values[i + 1] << 4);
+    buf[off++] |= values[i + 2] >>> 42;
+    buf[off++] = (byte) (values[i + 2] >>> 34);
+    buf[off++] = (byte) (values[i + 2] >>> 26);
+    buf[off++] = (byte) (values[i + 2] >>> 18);
+    buf[off++] = (byte) (values[i + 2] >>> 10);
+    buf[off++] = (byte) (values[i + 2] >>> 2);
+
+    buf[off] = (byte) (values[i + 2] << 6);
+    buf[off++] |= values[i + 3] >>> 40;
+    buf[off++] = (byte) (values[i + 3] >>> 32);
+    buf[off++] = (byte) (values[i + 3] >>> 24);
+    buf[off++] = (byte) (values[i + 3] >>> 16);
+    buf[off++] = (byte) (values[i + 3] >>> 8);
+    buf[off++] = (byte) (values[i + 3]);
+
+    buf[off++] = (byte) (values[i + 4] >>> 38);
+    buf[off++] = (byte) (values[i + 4] >>> 30);
+    buf[off++] = (byte) (values[i + 4] >>> 22);
+    buf[off++] = (byte) (values[i + 4] >>> 14);
+    buf[off++] = (byte) (values[i + 4] >>> 6);
+
+    buf[off] = (byte) (values[i + 4] << 2);
+    buf[off++] |= values[i + 5] >>> 44;
+    buf[off++] = (byte) (values[i + 5] >>> 36);
+    buf[off++] = (byte) (values[i + 5] >>> 28);
+    buf[off++] = (byte) (values[i + 5] >>> 20);
+    buf[off++] = (byte) (values[i + 5] >>> 12);
+    buf[off++] = (byte) (values[i + 5] >>> 4);
+
+    buf[off] = (byte) (values[i + 5] << 4);
+    buf[off++] |= values[i + 6] >>> 42;
+    buf[off++] = (byte) (values[i + 6] >>> 34);
+    buf[off++] = (byte) (values[i + 6] >>> 26);
+    buf[off++] = (byte) (values[i + 6] >>> 18);
+    buf[off++] = (byte) (values[i + 6] >>> 10);
+    buf[off++] = (byte) (values[i + 6] >>> 2);
+
+    buf[off] = (byte) (values[i + 6] << 6);
+    buf[off++] |= values[i + 7] >>> 40;
+    buf[off++] = (byte) (values[i + 7] >>> 32);
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits47(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 39);
+    buf[off++] = (byte) (values[i + 0] >>> 31);
+    buf[off++] = (byte) (values[i + 0] >>> 23);
+    buf[off++] = (byte) (values[i + 0] >>> 15);
+    buf[off++] = (byte) (values[i + 0] >>> 7);
+
+    buf[off] = (byte) (values[i + 0] << 1);
+    buf[off++] |= values[i + 1] >>> 46;
+    buf[off++] = (byte) (values[i + 1] >>> 38);
+    buf[off++] = (byte) (values[i + 1] >>> 30);
+    buf[off++] = (byte) (values[i + 1] >>> 22);
+    buf[off++] = (byte) (values[i + 1] >>> 14);
+    buf[off++] = (byte) (values[i + 1] >>> 6);
+
+    buf[off] = (byte) (values[i + 1] << 2);
+    buf[off++] |= values[i + 2] >>> 45;
+    buf[off++] = (byte) (values[i + 2] >>> 37);
+    buf[off++] = (byte) (values[i + 2] >>> 29);
+    buf[off++] = (byte) (values[i + 2] >>> 21);
+    buf[off++] = (byte) (values[i + 2] >>> 13);
+    buf[off++] = (byte) (values[i + 2] >>> 5);
+
+    buf[off] = (byte) (values[i + 2] << 3);
+    buf[off++] |= values[i + 3] >>> 44;
+    buf[off++] = (byte) (values[i + 3] >>> 36);
+    buf[off++] = (byte) (values[i + 3] >>> 28);
+    buf[off++] = (byte) (values[i + 3] >>> 20);
+    buf[off++] = (byte) (values[i + 3] >>> 12);
+    buf[off++] = (byte) (values[i + 3] >>> 4);
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 43;
+    buf[off++] = (byte) (values[i + 4] >>> 35);
+    buf[off++] = (byte) (values[i + 4] >>> 27);
+    buf[off++] = (byte) (values[i + 4] >>> 19);
+    buf[off++] = (byte) (values[i + 4] >>> 11);
+    buf[off++] = (byte) (values[i + 4] >>> 3);
+
+    buf[off] = (byte) (values[i + 4] << 5);
+    buf[off++] |= values[i + 5] >>> 42;
+    buf[off++] = (byte) (values[i + 5] >>> 34);
+    buf[off++] = (byte) (values[i + 5] >>> 26);
+    buf[off++] = (byte) (values[i + 5] >>> 18);
+    buf[off++] = (byte) (values[i + 5] >>> 10);
+    buf[off++] = (byte) (values[i + 5] >>> 2);
+
+    buf[off] = (byte) (values[i + 5] << 6);
+    buf[off++] |= values[i + 6] >>> 41;
+    buf[off++] = (byte) (values[i + 6] >>> 33);
+    buf[off++] = (byte) (values[i + 6] >>> 25);
+    buf[off++] = (byte) (values[i + 6] >>> 17);
+    buf[off++] = (byte) (values[i + 6] >>> 9);
+    buf[off++] = (byte) (values[i + 6] >>> 1);
+
+    buf[off] = (byte) (values[i + 6] << 7);
+    buf[off++] |= values[i + 7] >>> 40;
+    buf[off++] = (byte) (values[i + 7] >>> 32);
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits48(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 40);
+    buf[off++] = (byte) (values[i + 0] >>> 32);
+    buf[off++] = (byte) (values[i + 0] >>> 24);
+    buf[off++] = (byte) (values[i + 0] >>> 16);
+    buf[off++] = (byte) (values[i + 0] >>> 8);
+    buf[off++] = (byte) (values[i + 0]);
+
+    buf[off++] = (byte) (values[i + 1] >>> 40);
+    buf[off++] = (byte) (values[i + 1] >>> 32);
+    buf[off++] = (byte) (values[i + 1] >>> 24);
+    buf[off++] = (byte) (values[i + 1] >>> 16);
+    buf[off++] = (byte) (values[i + 1] >>> 8);
+    buf[off++] = (byte) (values[i + 1]);
+
+    buf[off++] = (byte) (values[i + 2] >>> 40);
+    buf[off++] = (byte) (values[i + 2] >>> 32);
+    buf[off++] = (byte) (values[i + 2] >>> 24);
+    buf[off++] = (byte) (values[i + 2] >>> 16);
+    buf[off++] = (byte) (values[i + 2] >>> 8);
+    buf[off++] = (byte) (values[i + 2]);
+
+    buf[off++] = (byte) (values[i + 3] >>> 40);
+    buf[off++] = (byte) (values[i + 3] >>> 32);
+    buf[off++] = (byte) (values[i + 3] >>> 24);
+    buf[off++] = (byte) (values[i + 3] >>> 16);
+    buf[off++] = (byte) (values[i + 3] >>> 8);
+    buf[off++] = (byte) (values[i + 3]);
+
+    buf[off++] = (byte) (values[i + 4] >>> 40);
+    buf[off++] = (byte) (values[i + 4] >>> 32);
+    buf[off++] = (byte) (values[i + 4] >>> 24);
+    buf[off++] = (byte) (values[i + 4] >>> 16);
+    buf[off++] = (byte) (values[i + 4] >>> 8);
+    buf[off++] = (byte) (values[i + 4]);
+
+    buf[off++] = (byte) (values[i + 5] >>> 40);
+    buf[off++] = (byte) (values[i + 5] >>> 32);
+    buf[off++] = (byte) (values[i + 5] >>> 24);
+    buf[off++] = (byte) (values[i + 5] >>> 16);
+    buf[off++] = (byte) (values[i + 5] >>> 8);
+    buf[off++] = (byte) (values[i + 5]);
+
+    buf[off++] = (byte) (values[i + 6] >>> 40);
+    buf[off++] = (byte) (values[i + 6] >>> 32);
+    buf[off++] = (byte) (values[i + 6] >>> 24);
+    buf[off++] = (byte) (values[i + 6] >>> 16);
+    buf[off++] = (byte) (values[i + 6] >>> 8);
+    buf[off++] = (byte) (values[i + 6]);
+
+    buf[off++] = (byte) (values[i + 7] >>> 40);
+    buf[off++] = (byte) (values[i + 7] >>> 32);
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits49(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 41);
+    buf[off++] = (byte) (values[i + 0] >>> 33);
+    buf[off++] = (byte) (values[i + 0] >>> 25);
+    buf[off++] = (byte) (values[i + 0] >>> 17);
+    buf[off++] = (byte) (values[i + 0] >>> 9);
+    buf[off++] = (byte) (values[i + 0] >>> 1);
+
+    buf[off] = (byte) (values[i + 0] << 7);
+    buf[off++] |= values[i + 1] >>> 42;
+    buf[off++] = (byte) (values[i + 1] >>> 34);
+    buf[off++] = (byte) (values[i + 1] >>> 26);
+    buf[off++] = (byte) (values[i + 1] >>> 18);
+    buf[off++] = (byte) (values[i + 1] >>> 10);
+    buf[off++] = (byte) (values[i + 1] >>> 2);
+
+    buf[off] = (byte) (values[i + 1] << 6);
+    buf[off++] |= values[i + 2] >>> 43;
+    buf[off++] = (byte) (values[i + 2] >>> 35);
+    buf[off++] = (byte) (values[i + 2] >>> 27);
+    buf[off++] = (byte) (values[i + 2] >>> 19);
+    buf[off++] = (byte) (values[i + 2] >>> 11);
+    buf[off++] = (byte) (values[i + 2] >>> 3);
+
+    buf[off] = (byte) (values[i + 2] << 5);
+    buf[off++] |= values[i + 3] >>> 44;
+    buf[off++] = (byte) (values[i + 3] >>> 36);
+    buf[off++] = (byte) (values[i + 3] >>> 28);
+    buf[off++] = (byte) (values[i + 3] >>> 20);
+    buf[off++] = (byte) (values[i + 3] >>> 12);
+    buf[off++] = (byte) (values[i + 3] >>> 4);
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 45;
+    buf[off++] = (byte) (values[i + 4] >>> 37);
+    buf[off++] = (byte) (values[i + 4] >>> 29);
+    buf[off++] = (byte) (values[i + 4] >>> 21);
+    buf[off++] = (byte) (values[i + 4] >>> 13);
+    buf[off++] = (byte) (values[i + 4] >>> 5);
+
+    buf[off] = (byte) (values[i + 4] << 3);
+    buf[off++] |= values[i + 5] >>> 46;
+    buf[off++] = (byte) (values[i + 5] >>> 38);
+    buf[off++] = (byte) (values[i + 5] >>> 30);
+    buf[off++] = (byte) (values[i + 5] >>> 22);
+    buf[off++] = (byte) (values[i + 5] >>> 14);
+    buf[off++] = (byte) (values[i + 5] >>> 6);
+
+    buf[off] = (byte) (values[i + 5] << 2);
+    buf[off++] |= values[i + 6] >>> 47;
+    buf[off++] = (byte) (values[i + 6] >>> 39);
+    buf[off++] = (byte) (values[i + 6] >>> 31);
+    buf[off++] = (byte) (values[i + 6] >>> 23);
+    buf[off++] = (byte) (values[i + 6] >>> 15);
+    buf[off++] = (byte) (values[i + 6] >>> 7);
+
+    buf[off] = (byte) (values[i + 6] << 1);
+    buf[off++] |= values[i + 7] >>> 48;
+    buf[off++] = (byte) (values[i + 7] >>> 40);
+    buf[off++] = (byte) (values[i + 7] >>> 32);
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits50(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 42);
+    buf[off++] = (byte) (values[i + 0] >>> 34);
+    buf[off++] = (byte) (values[i + 0] >>> 26);
+    buf[off++] = (byte) (values[i + 0] >>> 18);
+    buf[off++] = (byte) (values[i + 0] >>> 10);
+    buf[off++] = (byte) (values[i + 0] >>> 2);
+
+    buf[off] = (byte) (values[i + 0] << 6);
+    buf[off++] |= values[i + 1] >>> 44;
+    buf[off++] = (byte) (values[i + 1] >>> 36);
+    buf[off++] = (byte) (values[i + 1] >>> 28);
+    buf[off++] = (byte) (values[i + 1] >>> 20);
+    buf[off++] = (byte) (values[i + 1] >>> 12);
+    buf[off++] = (byte) (values[i + 1] >>> 4);
+
+    buf[off] = (byte) (values[i + 1] << 4);
+    buf[off++] |= values[i + 2] >>> 46;
+    buf[off++] = (byte) (values[i + 2] >>> 38);
+    buf[off++] = (byte) (values[i + 2] >>> 30);
+    buf[off++] = (byte) (values[i + 2] >>> 22);
+    buf[off++] = (byte) (values[i + 2] >>> 14);
+    buf[off++] = (byte) (values[i + 2] >>> 6);
+
+    buf[off] = (byte) (values[i + 2] << 2);
+    buf[off++] |= values[i + 3] >>> 48;
+    buf[off++] = (byte) (values[i + 3] >>> 40);
+    buf[off++] = (byte) (values[i + 3] >>> 32);
+    buf[off++] = (byte) (values[i + 3] >>> 24);
+    buf[off++] = (byte) (values[i + 3] >>> 16);
+    buf[off++] = (byte) (values[i + 3] >>> 8);
+    buf[off++] = (byte) (values[i + 3]);
+
+    buf[off++] = (byte) (values[i + 4] >>> 42);
+    buf[off++] = (byte) (values[i + 4] >>> 34);
+    buf[off++] = (byte) (values[i + 4] >>> 26);
+    buf[off++] = (byte) (values[i + 4] >>> 18);
+    buf[off++] = (byte) (values[i + 4] >>> 10);
+    buf[off++] = (byte) (values[i + 4] >>> 2);
+
+    buf[off] = (byte) (values[i + 4] << 6);
+    buf[off++] |= values[i + 5] >>> 44;
+    buf[off++] = (byte) (values[i + 5] >>> 36);
+    buf[off++] = (byte) (values[i + 5] >>> 28);
+    buf[off++] = (byte) (values[i + 5] >>> 20);
+    buf[off++] = (byte) (values[i + 5] >>> 12);
+    buf[off++] = (byte) (values[i + 5] >>> 4);
+
+    buf[off] = (byte) (values[i + 5] << 4);
+    buf[off++] |= values[i + 6] >>> 46;
+    buf[off++] = (byte) (values[i + 6] >>> 38);
+    buf[off++] = (byte) (values[i + 6] >>> 30);
+    buf[off++] = (byte) (values[i + 6] >>> 22);
+    buf[off++] = (byte) (values[i + 6] >>> 14);
+    buf[off++] = (byte) (values[i + 6] >>> 6);
+
+    buf[off] = (byte) (values[i + 6] << 2);
+    buf[off++] |= values[i + 7] >>> 48;
+    buf[off++] = (byte) (values[i + 7] >>> 40);
+    buf[off++] = (byte) (values[i + 7] >>> 32);
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits51(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 43);
+    buf[off++] = (byte) (values[i + 0] >>> 35);
+    buf[off++] = (byte) (values[i + 0] >>> 27);
+    buf[off++] = (byte) (values[i + 0] >>> 19);
+    buf[off++] = (byte) (values[i + 0] >>> 11);
+    buf[off++] = (byte) (values[i + 0] >>> 3);
+
+    buf[off] = (byte) (values[i + 0] << 5);
+    buf[off++] |= values[i + 1] >>> 46;
+    buf[off++] = (byte) (values[i + 1] >>> 38);
+    buf[off++] = (byte) (values[i + 1] >>> 30);
+    buf[off++] = (byte) (values[i + 1] >>> 22);
+    buf[off++] = (byte) (values[i + 1] >>> 14);
+    buf[off++] = (byte) (values[i + 1] >>> 6);
+
+    buf[off] = (byte) (values[i + 1] << 2);
+    buf[off++] |= values[i + 2] >>> 49;
+    buf[off++] = (byte) (values[i + 2] >>> 41);
+    buf[off++] = (byte) (values[i + 2] >>> 33);
+    buf[off++] = (byte) (values[i + 2] >>> 25);
+    buf[off++] = (byte) (values[i + 2] >>> 17);
+    buf[off++] = (byte) (values[i + 2] >>> 9);
+    buf[off++] = (byte) (values[i + 2] >>> 1);
+
+    buf[off] = (byte) (values[i + 2] << 7);
+    buf[off++] |= values[i + 3] >>> 44;
+    buf[off++] = (byte) (values[i + 3] >>> 36);
+    buf[off++] = (byte) (values[i + 3] >>> 28);
+    buf[off++] = (byte) (values[i + 3] >>> 20);
+    buf[off++] = (byte) (values[i + 3] >>> 12);
+    buf[off++] = (byte) (values[i + 3] >>> 4);
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 47;
+    buf[off++] = (byte) (values[i + 4] >>> 39);
+    buf[off++] = (byte) (values[i + 4] >>> 31);
+    buf[off++] = (byte) (values[i + 4] >>> 23);
+    buf[off++] = (byte) (values[i + 4] >>> 15);
+    buf[off++] = (byte) (values[i + 4] >>> 7);
+
+    buf[off] = (byte) (values[i + 4] << 1);
+    buf[off++] |= values[i + 5] >>> 50;
+    buf[off++] = (byte) (values[i + 5] >>> 42);
+    buf[off++] = (byte) (values[i + 5] >>> 34);
+    buf[off++] = (byte) (values[i + 5] >>> 26);
+    buf[off++] = (byte) (values[i + 5] >>> 18);
+    buf[off++] = (byte) (values[i + 5] >>> 10);
+    buf[off++] = (byte) (values[i + 5] >>> 2);
+
+    buf[off] = (byte) (values[i + 5] << 6);
+    buf[off++] |= values[i + 6] >>> 45;
+    buf[off++] = (byte) (values[i + 6] >>> 37);
+    buf[off++] = (byte) (values[i + 6] >>> 29);
+    buf[off++] = (byte) (values[i + 6] >>> 21);
+    buf[off++] = (byte) (values[i + 6] >>> 13);
+    buf[off++] = (byte) (values[i + 6] >>> 5);
+
+    buf[off] = (byte) (values[i + 6] << 3);
+    buf[off++] |= values[i + 7] >>> 48;
+    buf[off++] = (byte) (values[i + 7] >>> 40);
+    buf[off++] = (byte) (values[i + 7] >>> 32);
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits52(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 44);
+    buf[off++] = (byte) (values[i + 0] >>> 36);
+    buf[off++] = (byte) (values[i + 0] >>> 28);
+    buf[off++] = (byte) (values[i + 0] >>> 20);
+    buf[off++] = (byte) (values[i + 0] >>> 12);
+    buf[off++] = (byte) (values[i + 0] >>> 4);
+
+    buf[off] = (byte) (values[i + 0] << 4);
+    buf[off++] |= values[i + 1] >>> 48;
+    buf[off++] = (byte) (values[i + 1] >>> 40);
+    buf[off++] = (byte) (values[i + 1] >>> 32);
+    buf[off++] = (byte) (values[i + 1] >>> 24);
+    buf[off++] = (byte) (values[i + 1] >>> 16);
+    buf[off++] = (byte) (values[i + 1] >>> 8);
+    buf[off++] = (byte) (values[i + 1]);
+
+    buf[off++] = (byte) (values[i + 2] >>> 44);
+    buf[off++] = (byte) (values[i + 2] >>> 36);
+    buf[off++] = (byte) (values[i + 2] >>> 28);
+    buf[off++] = (byte) (values[i + 2] >>> 20);
+    buf[off++] = (byte) (values[i + 2] >>> 12);
+    buf[off++] = (byte) (values[i + 2] >>> 4);
+
+    buf[off] = (byte) (values[i + 2] << 4);
+    buf[off++] |= values[i + 3] >>> 48;
+    buf[off++] = (byte) (values[i + 3] >>> 40);
+    buf[off++] = (byte) (values[i + 3] >>> 32);
+    buf[off++] = (byte) (values[i + 3] >>> 24);
+    buf[off++] = (byte) (values[i + 3] >>> 16);
+    buf[off++] = (byte) (values[i + 3] >>> 8);
+    buf[off++] = (byte) (values[i + 3]);
+
+    buf[off++] = (byte) (values[i + 4] >>> 44);
+    buf[off++] = (byte) (values[i + 4] >>> 36);
+    buf[off++] = (byte) (values[i + 4] >>> 28);
+    buf[off++] = (byte) (values[i + 4] >>> 20);
+    buf[off++] = (byte) (values[i + 4] >>> 12);
+    buf[off++] = (byte) (values[i + 4] >>> 4);
+
+    buf[off] = (byte) (values[i + 4] << 4);
+    buf[off++] |= values[i + 5] >>> 48;
+    buf[off++] = (byte) (values[i + 5] >>> 40);
+    buf[off++] = (byte) (values[i + 5] >>> 32);
+    buf[off++] = (byte) (values[i + 5] >>> 24);
+    buf[off++] = (byte) (values[i + 5] >>> 16);
+    buf[off++] = (byte) (values[i + 5] >>> 8);
+    buf[off++] = (byte) (values[i + 5]);
+
+    buf[off++] = (byte) (values[i + 6] >>> 44);
+    buf[off++] = (byte) (values[i + 6] >>> 36);
+    buf[off++] = (byte) (values[i + 6] >>> 28);
+    buf[off++] = (byte) (values[i + 6] >>> 20);
+    buf[off++] = (byte) (values[i + 6] >>> 12);
+    buf[off++] = (byte) (values[i + 6] >>> 4);
+
+    buf[off] = (byte) (values[i + 6] << 4);
+    buf[off++] |= values[i + 7] >>> 48;
+    buf[off++] = (byte) (values[i + 7] >>> 40);
+    buf[off++] = (byte) (values[i + 7] >>> 32);
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits53(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 45);
+    buf[off++] = (byte) (values[i + 0] >>> 37);
+    buf[off++] = (byte) (values[i + 0] >>> 29);
+    buf[off++] = (byte) (values[i + 0] >>> 21);
+    buf[off++] = (byte) (values[i + 0] >>> 13);
+    buf[off++] = (byte) (values[i + 0] >>> 5);
+
+    buf[off] = (byte) (values[i + 0] << 3);
+    buf[off++] |= values[i + 1] >>> 50;
+    buf[off++] = (byte) (values[i + 1] >>> 42);
+    buf[off++] = (byte) (values[i + 1] >>> 34);
+    buf[off++] = (byte) (values[i + 1] >>> 26);
+    buf[off++] = (byte) (values[i + 1] >>> 18);
+    buf[off++] = (byte) (values[i + 1] >>> 10);
+    buf[off++] = (byte) (values[i + 1] >>> 2);
+
+    buf[off] = (byte) (values[i + 1] << 6);
+    buf[off++] |= values[i + 2] >>> 47;
+    buf[off++] = (byte) (values[i + 2] >>> 39);
+    buf[off++] = (byte) (values[i + 2] >>> 31);
+    buf[off++] = (byte) (values[i + 2] >>> 23);
+    buf[off++] = (byte) (values[i + 2] >>> 15);
+    buf[off++] = (byte) (values[i + 2] >>> 7);
+
+    buf[off] = (byte) (values[i + 2] << 1);
+    buf[off++] |= values[i + 3] >>> 52;
+    buf[off++] = (byte) (values[i + 3] >>> 44);
+    buf[off++] = (byte) (values[i + 3] >>> 36);
+    buf[off++] = (byte) (values[i + 3] >>> 28);
+    buf[off++] = (byte) (values[i + 3] >>> 20);
+    buf[off++] = (byte) (values[i + 3] >>> 12);
+    buf[off++] = (byte) (values[i + 3] >>> 4);
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 49;
+    buf[off++] = (byte) (values[i + 4] >>> 41);
+    buf[off++] = (byte) (values[i + 4] >>> 33);
+    buf[off++] = (byte) (values[i + 4] >>> 25);
+    buf[off++] = (byte) (values[i + 4] >>> 17);
+    buf[off++] = (byte) (values[i + 4] >>> 9);
+    buf[off++] = (byte) (values[i + 4] >>> 1);
+
+    buf[off] = (byte) (values[i + 4] << 7);
+    buf[off++] |= values[i + 5] >>> 46;
+    buf[off++] = (byte) (values[i + 5] >>> 38);
+    buf[off++] = (byte) (values[i + 5] >>> 30);
+    buf[off++] = (byte) (values[i + 5] >>> 22);
+    buf[off++] = (byte) (values[i + 5] >>> 14);
+    buf[off++] = (byte) (values[i + 5] >>> 6);
+
+    buf[off] = (byte) (values[i + 5] << 2);
+    buf[off++] |= values[i + 6] >>> 51;
+    buf[off++] = (byte) (values[i + 6] >>> 43);
+    buf[off++] = (byte) (values[i + 6] >>> 35);
+    buf[off++] = (byte) (values[i + 6] >>> 27);
+    buf[off++] = (byte) (values[i + 6] >>> 19);
+    buf[off++] = (byte) (values[i + 6] >>> 11);
+    buf[off++] = (byte) (values[i + 6] >>> 3);
+
+    buf[off] = (byte) (values[i + 6] << 5);
+    buf[off++] |= values[i + 7] >>> 48;
+    buf[off++] = (byte) (values[i + 7] >>> 40);
+    buf[off++] = (byte) (values[i + 7] >>> 32);
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits54(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 46);
+    buf[off++] = (byte) (values[i + 0] >>> 38);
+    buf[off++] = (byte) (values[i + 0] >>> 30);
+    buf[off++] = (byte) (values[i + 0] >>> 22);
+    buf[off++] = (byte) (values[i + 0] >>> 14);
+    buf[off++] = (byte) (values[i + 0] >>> 6);
+
+    buf[off] = (byte) (values[i + 0] << 2);
+    buf[off++] |= values[i + 1] >>> 52;
+    buf[off++] = (byte) (values[i + 1] >>> 44);
+    buf[off++] = (byte) (values[i + 1] >>> 36);
+    buf[off++] = (byte) (values[i + 1] >>> 28);
+    buf[off++] = (byte) (values[i + 1] >>> 20);
+    buf[off++] = (byte) (values[i + 1] >>> 12);
+    buf[off++] = (byte) (values[i + 1] >>> 4);
+
+    buf[off] = (byte) (values[i + 1] << 4);
+    buf[off++] |= values[i + 2] >>> 50;
+    buf[off++] = (byte) (values[i + 2] >>> 42);
+    buf[off++] = (byte) (values[i + 2] >>> 34);
+    buf[off++] = (byte) (values[i + 2] >>> 26);
+    buf[off++] = (byte) (values[i + 2] >>> 18);
+    buf[off++] = (byte) (values[i + 2] >>> 10);
+    buf[off++] = (byte) (values[i + 2] >>> 2);
+
+    buf[off] = (byte) (values[i + 2] << 6);
+    buf[off++] |= values[i + 3] >>> 48;
+    buf[off++] = (byte) (values[i + 3] >>> 40);
+    buf[off++] = (byte) (values[i + 3] >>> 32);
+    buf[off++] = (byte) (values[i + 3] >>> 24);
+    buf[off++] = (byte) (values[i + 3] >>> 16);
+    buf[off++] = (byte) (values[i + 3] >>> 8);
+    buf[off++] = (byte) (values[i + 3]);
+
+    buf[off++] = (byte) (values[i + 4] >>> 46);
+    buf[off++] = (byte) (values[i + 4] >>> 38);
+    buf[off++] = (byte) (values[i + 4] >>> 30);
+    buf[off++] = (byte) (values[i + 4] >>> 22);
+    buf[off++] = (byte) (values[i + 4] >>> 14);
+    buf[off++] = (byte) (values[i + 4] >>> 6);
+
+    buf[off] = (byte) (values[i + 4] << 2);
+    buf[off++] |= values[i + 5] >>> 52;
+    buf[off++] = (byte) (values[i + 5] >>> 44);
+    buf[off++] = (byte) (values[i + 5] >>> 36);
+    buf[off++] = (byte) (values[i + 5] >>> 28);
+    buf[off++] = (byte) (values[i + 5] >>> 20);
+    buf[off++] = (byte) (values[i + 5] >>> 12);
+    buf[off++] = (byte) (values[i + 5] >>> 4);
+
+    buf[off] = (byte) (values[i + 5] << 4);
+    buf[off++] |= values[i + 6] >>> 50;
+    buf[off++] = (byte) (values[i + 6] >>> 42);
+    buf[off++] = (byte) (values[i + 6] >>> 34);
+    buf[off++] = (byte) (values[i + 6] >>> 26);
+    buf[off++] = (byte) (values[i + 6] >>> 18);
+    buf[off++] = (byte) (values[i + 6] >>> 10);
+    buf[off++] = (byte) (values[i + 6] >>> 2);
+
+    buf[off] = (byte) (values[i + 6] << 6);
+    buf[off++] |= values[i + 7] >>> 48;
+    buf[off++] = (byte) (values[i + 7] >>> 40);
+    buf[off++] = (byte) (values[i + 7] >>> 32);
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits55(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 47);
+    buf[off++] = (byte) (values[i + 0] >>> 39);
+    buf[off++] = (byte) (values[i + 0] >>> 31);
+    buf[off++] = (byte) (values[i + 0] >>> 23);
+    buf[off++] = (byte) (values[i + 0] >>> 15);
+    buf[off++] = (byte) (values[i + 0] >>> 7);
+
+    buf[off] = (byte) (values[i + 0] << 1);
+    buf[off++] |= values[i + 1] >>> 54;
+    buf[off++] = (byte) (values[i + 1] >>> 46);
+    buf[off++] = (byte) (values[i + 1] >>> 38);
+    buf[off++] = (byte) (values[i + 1] >>> 30);
+    buf[off++] = (byte) (values[i + 1] >>> 22);
+    buf[off++] = (byte) (values[i + 1] >>> 14);
+    buf[off++] = (byte) (values[i + 1] >>> 6);
+
+    buf[off] = (byte) (values[i + 1] << 2);
+    buf[off++] |= values[i + 2] >>> 53;
+    buf[off++] = (byte) (values[i + 2] >>> 45);
+    buf[off++] = (byte) (values[i + 2] >>> 37);
+    buf[off++] = (byte) (values[i + 2] >>> 29);
+    buf[off++] = (byte) (values[i + 2] >>> 21);
+    buf[off++] = (byte) (values[i + 2] >>> 13);
+    buf[off++] = (byte) (values[i + 2] >>> 5);
+
+    buf[off] = (byte) (values[i + 2] << 3);
+    buf[off++] |= values[i + 3] >>> 52;
+    buf[off++] = (byte) (values[i + 3] >>> 44);
+    buf[off++] = (byte) (values[i + 3] >>> 36);
+    buf[off++] = (byte) (values[i + 3] >>> 28);
+    buf[off++] = (byte) (values[i + 3] >>> 20);
+    buf[off++] = (byte) (values[i + 3] >>> 12);
+    buf[off++] = (byte) (values[i + 3] >>> 4);
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 51;
+    buf[off++] = (byte) (values[i + 4] >>> 43);
+    buf[off++] = (byte) (values[i + 4] >>> 35);
+    buf[off++] = (byte) (values[i + 4] >>> 27);
+    buf[off++] = (byte) (values[i + 4] >>> 19);
+    buf[off++] = (byte) (values[i + 4] >>> 11);
+    buf[off++] = (byte) (values[i + 4] >>> 3);
+
+    buf[off] = (byte) (values[i + 4] << 5);
+    buf[off++] |= values[i + 5] >>> 50;
+    buf[off++] = (byte) (values[i + 5] >>> 42);
+    buf[off++] = (byte) (values[i + 5] >>> 34);
+    buf[off++] = (byte) (values[i + 5] >>> 26);
+    buf[off++] = (byte) (values[i + 5] >>> 18);
+    buf[off++] = (byte) (values[i + 5] >>> 10);
+    buf[off++] = (byte) (values[i + 5] >>> 2);
+
+    buf[off] = (byte) (values[i + 5] << 6);
+    buf[off++] |= values[i + 6] >>> 49;
+    buf[off++] = (byte) (values[i + 6] >>> 41);
+    buf[off++] = (byte) (values[i + 6] >>> 33);
+    buf[off++] = (byte) (values[i + 6] >>> 25);
+    buf[off++] = (byte) (values[i + 6] >>> 17);
+    buf[off++] = (byte) (values[i + 6] >>> 9);
+    buf[off++] = (byte) (values[i + 6] >>> 1);
+
+    buf[off] = (byte) (values[i + 6] << 7);
+    buf[off++] |= values[i + 7] >>> 48;
+    buf[off++] = (byte) (values[i + 7] >>> 40);
+    buf[off++] = (byte) (values[i + 7] >>> 32);
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits56(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 48);
+    buf[off++] = (byte) (values[i + 0] >>> 40);
+    buf[off++] = (byte) (values[i + 0] >>> 32);
+    buf[off++] = (byte) (values[i + 0] >>> 24);
+    buf[off++] = (byte) (values[i + 0] >>> 16);
+    buf[off++] = (byte) (values[i + 0] >>> 8);
+    buf[off++] = (byte) (values[i + 0]);
+
+    buf[off++] = (byte) (values[i + 1] >>> 48);
+    buf[off++] = (byte) (values[i + 1] >>> 40);
+    buf[off++] = (byte) (values[i + 1] >>> 32);
+    buf[off++] = (byte) (values[i + 1] >>> 24);
+    buf[off++] = (byte) (values[i + 1] >>> 16);
+    buf[off++] = (byte) (values[i + 1] >>> 8);
+    buf[off++] = (byte) (values[i + 1]);
+
+    buf[off++] = (byte) (values[i + 2] >>> 48);
+    buf[off++] = (byte) (values[i + 2] >>> 40);
+    buf[off++] = (byte) (values[i + 2] >>> 32);
+    buf[off++] = (byte) (values[i + 2] >>> 24);
+    buf[off++] = (byte) (values[i + 2] >>> 16);
+    buf[off++] = (byte) (values[i + 2] >>> 8);
+    buf[off++] = (byte) (values[i + 2]);
+
+    buf[off++] = (byte) (values[i + 3] >>> 48);
+    buf[off++] = (byte) (values[i + 3] >>> 40);
+    buf[off++] = (byte) (values[i + 3] >>> 32);
+    buf[off++] = (byte) (values[i + 3] >>> 24);
+    buf[off++] = (byte) (values[i + 3] >>> 16);
+    buf[off++] = (byte) (values[i + 3] >>> 8);
+    buf[off++] = (byte) (values[i + 3]);
+
+    buf[off++] = (byte) (values[i + 4] >>> 48);
+    buf[off++] = (byte) (values[i + 4] >>> 40);
+    buf[off++] = (byte) (values[i + 4] >>> 32);
+    buf[off++] = (byte) (values[i + 4] >>> 24);
+    buf[off++] = (byte) (values[i + 4] >>> 16);
+    buf[off++] = (byte) (values[i + 4] >>> 8);
+    buf[off++] = (byte) (values[i + 4]);
+
+    buf[off++] = (byte) (values[i + 5] >>> 48);
+    buf[off++] = (byte) (values[i + 5] >>> 40);
+    buf[off++] = (byte) (values[i + 5] >>> 32);
+    buf[off++] = (byte) (values[i + 5] >>> 24);
+    buf[off++] = (byte) (values[i + 5] >>> 16);
+    buf[off++] = (byte) (values[i + 5] >>> 8);
+    buf[off++] = (byte) (values[i + 5]);
+
+    buf[off++] = (byte) (values[i + 6] >>> 48);
+    buf[off++] = (byte) (values[i + 6] >>> 40);
+    buf[off++] = (byte) (values[i + 6] >>> 32);
+    buf[off++] = (byte) (values[i + 6] >>> 24);
+    buf[off++] = (byte) (values[i + 6] >>> 16);
+    buf[off++] = (byte) (values[i + 6] >>> 8);
+    buf[off++] = (byte) (values[i + 6]);
+
+    buf[off++] = (byte) (values[i + 7] >>> 48);
+    buf[off++] = (byte) (values[i + 7] >>> 40);
+    buf[off++] = (byte) (values[i + 7] >>> 32);
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits57(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 49);
+    buf[off++] = (byte) (values[i + 0] >>> 41);
+    buf[off++] = (byte) (values[i + 0] >>> 33);
+    buf[off++] = (byte) (values[i + 0] >>> 25);
+    buf[off++] = (byte) (values[i + 0] >>> 17);
+    buf[off++] = (byte) (values[i + 0] >>> 9);
+    buf[off++] = (byte) (values[i + 0] >>> 1);
+
+    buf[off] = (byte) (values[i + 0] << 7);
+    buf[off++] |= values[i + 1] >>> 50;
+    buf[off++] = (byte) (values[i + 1] >>> 42);
+    buf[off++] = (byte) (values[i + 1] >>> 34);
+    buf[off++] = (byte) (values[i + 1] >>> 26);
+    buf[off++] = (byte) (values[i + 1] >>> 18);
+    buf[off++] = (byte) (values[i + 1] >>> 10);
+    buf[off++] = (byte) (values[i + 1] >>> 2);
+
+    buf[off] = (byte) (values[i + 1] << 6);
+    buf[off++] |= values[i + 2] >>> 51;
+    buf[off++] = (byte) (values[i + 2] >>> 43);
+    buf[off++] = (byte) (values[i + 2] >>> 35);
+    buf[off++] = (byte) (values[i + 2] >>> 27);
+    buf[off++] = (byte) (values[i + 2] >>> 19);
+    buf[off++] = (byte) (values[i + 2] >>> 11);
+    buf[off++] = (byte) (values[i + 2] >>> 3);
+
+    buf[off] = (byte) (values[i + 2] << 5);
+    buf[off++] |= values[i + 3] >>> 52;
+    buf[off++] = (byte) (values[i + 3] >>> 44);
+    buf[off++] = (byte) (values[i + 3] >>> 36);
+    buf[off++] = (byte) (values[i + 3] >>> 28);
+    buf[off++] = (byte) (values[i + 3] >>> 20);
+    buf[off++] = (byte) (values[i + 3] >>> 12);
+    buf[off++] = (byte) (values[i + 3] >>> 4);
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 53;
+    buf[off++] = (byte) (values[i + 4] >>> 45);
+    buf[off++] = (byte) (values[i + 4] >>> 37);
+    buf[off++] = (byte) (values[i + 4] >>> 29);
+    buf[off++] = (byte) (values[i + 4] >>> 21);
+    buf[off++] = (byte) (values[i + 4] >>> 13);
+    buf[off++] = (byte) (values[i + 4] >>> 5);
+
+    buf[off] = (byte) (values[i + 4] << 3);
+    buf[off++] |= values[i + 5] >>> 54;
+    buf[off++] = (byte) (values[i + 5] >>> 46);
+    buf[off++] = (byte) (values[i + 5] >>> 38);
+    buf[off++] = (byte) (values[i + 5] >>> 30);
+    buf[off++] = (byte) (values[i + 5] >>> 22);
+    buf[off++] = (byte) (values[i + 5] >>> 14);
+    buf[off++] = (byte) (values[i + 5] >>> 6);
+
+    buf[off] = (byte) (values[i + 5] << 2);
+    buf[off++] |= values[i + 6] >>> 55;
+    buf[off++] = (byte) (values[i + 6] >>> 47);
+    buf[off++] = (byte) (values[i + 6] >>> 39);
+    buf[off++] = (byte) (values[i + 6] >>> 31);
+    buf[off++] = (byte) (values[i + 6] >>> 23);
+    buf[off++] = (byte) (values[i + 6] >>> 15);
+    buf[off++] = (byte) (values[i + 6] >>> 7);
+
+    buf[off] = (byte) (values[i + 6] << 1);
+    buf[off++] |= values[i + 7] >>> 56;
+    buf[off++] = (byte) (values[i + 7] >>> 48);
+    buf[off++] = (byte) (values[i + 7] >>> 40);
+    buf[off++] = (byte) (values[i + 7] >>> 32);
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits58(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 50);
+    buf[off++] = (byte) (values[i + 0] >>> 42);
+    buf[off++] = (byte) (values[i + 0] >>> 34);
+    buf[off++] = (byte) (values[i + 0] >>> 26);
+    buf[off++] = (byte) (values[i + 0] >>> 18);
+    buf[off++] = (byte) (values[i + 0] >>> 10);
+    buf[off++] = (byte) (values[i + 0] >>> 2);
+
+    buf[off] = (byte) (values[i + 0] << 6);
+    buf[off++] |= values[i + 1] >>> 52;
+    buf[off++] = (byte) (values[i + 1] >>> 44);
+    buf[off++] = (byte) (values[i + 1] >>> 36);
+    buf[off++] = (byte) (values[i + 1] >>> 28);
+    buf[off++] = (byte) (values[i + 1] >>> 20);
+    buf[off++] = (byte) (values[i + 1] >>> 12);
+    buf[off++] = (byte) (values[i + 1] >>> 4);
+
+    buf[off] = (byte) (values[i + 1] << 4);
+    buf[off++] |= values[i + 2] >>> 54;
+    buf[off++] = (byte) (values[i + 2] >>> 46);
+    buf[off++] = (byte) (values[i + 2] >>> 38);
+    buf[off++] = (byte) (values[i + 2] >>> 30);
+    buf[off++] = (byte) (values[i + 2] >>> 22);
+    buf[off++] = (byte) (values[i + 2] >>> 14);
+    buf[off++] = (byte) (values[i + 2] >>> 6);
+
+    buf[off] = (byte) (values[i + 2] << 2);
+    buf[off++] |= values[i + 3] >>> 56;
+    buf[off++] = (byte) (values[i + 3] >>> 48);
+    buf[off++] = (byte) (values[i + 3] >>> 40);
+    buf[off++] = (byte) (values[i + 3] >>> 32);
+    buf[off++] = (byte) (values[i + 3] >>> 24);
+    buf[off++] = (byte) (values[i + 3] >>> 16);
+    buf[off++] = (byte) (values[i + 3] >>> 8);
+    buf[off++] = (byte) (values[i + 3]);
+
+    buf[off++] = (byte) (values[i + 4] >>> 50);
+    buf[off++] = (byte) (values[i + 4] >>> 42);
+    buf[off++] = (byte) (values[i + 4] >>> 34);
+    buf[off++] = (byte) (values[i + 4] >>> 26);
+    buf[off++] = (byte) (values[i + 4] >>> 18);
+    buf[off++] = (byte) (values[i + 4] >>> 10);
+    buf[off++] = (byte) (values[i + 4] >>> 2);
+
+    buf[off] = (byte) (values[i + 4] << 6);
+    buf[off++] |= values[i + 5] >>> 52;
+    buf[off++] = (byte) (values[i + 5] >>> 44);
+    buf[off++] = (byte) (values[i + 5] >>> 36);
+    buf[off++] = (byte) (values[i + 5] >>> 28);
+    buf[off++] = (byte) (values[i + 5] >>> 20);
+    buf[off++] = (byte) (values[i + 5] >>> 12);
+    buf[off++] = (byte) (values[i + 5] >>> 4);
+
+    buf[off] = (byte) (values[i + 5] << 4);
+    buf[off++] |= values[i + 6] >>> 54;
+    buf[off++] = (byte) (values[i + 6] >>> 46);
+    buf[off++] = (byte) (values[i + 6] >>> 38);
+    buf[off++] = (byte) (values[i + 6] >>> 30);
+    buf[off++] = (byte) (values[i + 6] >>> 22);
+    buf[off++] = (byte) (values[i + 6] >>> 14);
+    buf[off++] = (byte) (values[i + 6] >>> 6);
+
+    buf[off] = (byte) (values[i + 6] << 2);
+    buf[off++] |= values[i + 7] >>> 56;
+    buf[off++] = (byte) (values[i + 7] >>> 48);
+    buf[off++] = (byte) (values[i + 7] >>> 40);
+    buf[off++] = (byte) (values[i + 7] >>> 32);
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits59(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 51);
+    buf[off++] = (byte) (values[i + 0] >>> 43);
+    buf[off++] = (byte) (values[i + 0] >>> 35);
+    buf[off++] = (byte) (values[i + 0] >>> 27);
+    buf[off++] = (byte) (values[i + 0] >>> 19);
+    buf[off++] = (byte) (values[i + 0] >>> 11);
+    buf[off++] = (byte) (values[i + 0] >>> 3);
+
+    buf[off] = (byte) (values[i + 0] << 5);
+    buf[off++] |= values[i + 1] >>> 54;
+    buf[off++] = (byte) (values[i + 1] >>> 46);
+    buf[off++] = (byte) (values[i + 1] >>> 38);
+    buf[off++] = (byte) (values[i + 1] >>> 30);
+    buf[off++] = (byte) (values[i + 1] >>> 22);
+    buf[off++] = (byte) (values[i + 1] >>> 14);
+    buf[off++] = (byte) (values[i + 1] >>> 6);
+
+    buf[off] = (byte) (values[i + 1] << 2);
+    buf[off++] |= values[i + 2] >>> 57;
+    buf[off++] = (byte) (values[i + 2] >>> 49);
+    buf[off++] = (byte) (values[i + 2] >>> 41);
+    buf[off++] = (byte) (values[i + 2] >>> 33);
+    buf[off++] = (byte) (values[i + 2] >>> 25);
+    buf[off++] = (byte) (values[i + 2] >>> 17);
+    buf[off++] = (byte) (values[i + 2] >>> 9);
+    buf[off++] = (byte) (values[i + 2] >>> 1);
+
+    buf[off] = (byte) (values[i + 2] << 7);
+    buf[off++] |= values[i + 3] >>> 52;
+    buf[off++] = (byte) (values[i + 3] >>> 44);
+    buf[off++] = (byte) (values[i + 3] >>> 36);
+    buf[off++] = (byte) (values[i + 3] >>> 28);
+    buf[off++] = (byte) (values[i + 3] >>> 20);
+    buf[off++] = (byte) (values[i + 3] >>> 12);
+    buf[off++] = (byte) (values[i + 3] >>> 4);
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 55;
+    buf[off++] = (byte) (values[i + 4] >>> 47);
+    buf[off++] = (byte) (values[i + 4] >>> 39);
+    buf[off++] = (byte) (values[i + 4] >>> 31);
+    buf[off++] = (byte) (values[i + 4] >>> 23);
+    buf[off++] = (byte) (values[i + 4] >>> 15);
+    buf[off++] = (byte) (values[i + 4] >>> 7);
+
+    buf[off] = (byte) (values[i + 4] << 1);
+    buf[off++] |= values[i + 5] >>> 58;
+    buf[off++] = (byte) (values[i + 5] >>> 50);
+    buf[off++] = (byte) (values[i + 5] >>> 42);
+    buf[off++] = (byte) (values[i + 5] >>> 34);
+    buf[off++] = (byte) (values[i + 5] >>> 26);
+    buf[off++] = (byte) (values[i + 5] >>> 18);
+    buf[off++] = (byte) (values[i + 5] >>> 10);
+    buf[off++] = (byte) (values[i + 5] >>> 2);
+
+    buf[off] = (byte) (values[i + 5] << 6);
+    buf[off++] |= values[i + 6] >>> 53;
+    buf[off++] = (byte) (values[i + 6] >>> 45);
+    buf[off++] = (byte) (values[i + 6] >>> 37);
+    buf[off++] = (byte) (values[i + 6] >>> 29);
+    buf[off++] = (byte) (values[i + 6] >>> 21);
+    buf[off++] = (byte) (values[i + 6] >>> 13);
+    buf[off++] = (byte) (values[i + 6] >>> 5);
+
+    buf[off] = (byte) (values[i + 6] << 3);
+    buf[off++] |= values[i + 7] >>> 56;
+    buf[off++] = (byte) (values[i + 7] >>> 48);
+    buf[off++] = (byte) (values[i + 7] >>> 40);
+    buf[off++] = (byte) (values[i + 7] >>> 32);
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits60(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 52);
+    buf[off++] = (byte) (values[i + 0] >>> 44);
+    buf[off++] = (byte) (values[i + 0] >>> 36);
+    buf[off++] = (byte) (values[i + 0] >>> 28);
+    buf[off++] = (byte) (values[i + 0] >>> 20);
+    buf[off++] = (byte) (values[i + 0] >>> 12);
+    buf[off++] = (byte) (values[i + 0] >>> 4);
+
+    buf[off] = (byte) (values[i + 0] << 4);
+    buf[off++] |= values[i + 1] >>> 56;
+    buf[off++] = (byte) (values[i + 1] >>> 48);
+    buf[off++] = (byte) (values[i + 1] >>> 40);
+    buf[off++] = (byte) (values[i + 1] >>> 32);
+    buf[off++] = (byte) (values[i + 1] >>> 24);
+    buf[off++] = (byte) (values[i + 1] >>> 16);
+    buf[off++] = (byte) (values[i + 1] >>> 8);
+    buf[off++] = (byte) (values[i + 1]);
+
+    buf[off++] = (byte) (values[i + 2] >>> 52);
+    buf[off++] = (byte) (values[i + 2] >>> 44);
+    buf[off++] = (byte) (values[i + 2] >>> 36);
+    buf[off++] = (byte) (values[i + 2] >>> 28);
+    buf[off++] = (byte) (values[i + 2] >>> 20);
+    buf[off++] = (byte) (values[i + 2] >>> 12);
+    buf[off++] = (byte) (values[i + 2] >>> 4);
+
+    buf[off] = (byte) (values[i + 2] << 4);
+    buf[off++] |= values[i + 3] >>> 56;
+    buf[off++] = (byte) (values[i + 3] >>> 48);
+    buf[off++] = (byte) (values[i + 3] >>> 40);
+    buf[off++] = (byte) (values[i + 3] >>> 32);
+    buf[off++] = (byte) (values[i + 3] >>> 24);
+    buf[off++] = (byte) (values[i + 3] >>> 16);
+    buf[off++] = (byte) (values[i + 3] >>> 8);
+    buf[off++] = (byte) (values[i + 3]);
+
+    buf[off++] = (byte) (values[i + 4] >>> 52);
+    buf[off++] = (byte) (values[i + 4] >>> 44);
+    buf[off++] = (byte) (values[i + 4] >>> 36);
+    buf[off++] = (byte) (values[i + 4] >>> 28);
+    buf[off++] = (byte) (values[i + 4] >>> 20);
+    buf[off++] = (byte) (values[i + 4] >>> 12);
+    buf[off++] = (byte) (values[i + 4] >>> 4);
+
+    buf[off] = (byte) (values[i + 4] << 4);
+    buf[off++] |= values[i + 5] >>> 56;
+    buf[off++] = (byte) (values[i + 5] >>> 48);
+    buf[off++] = (byte) (values[i + 5] >>> 40);
+    buf[off++] = (byte) (values[i + 5] >>> 32);
+    buf[off++] = (byte) (values[i + 5] >>> 24);
+    buf[off++] = (byte) (values[i + 5] >>> 16);
+    buf[off++] = (byte) (values[i + 5] >>> 8);
+    buf[off++] = (byte) (values[i + 5]);
+
+    buf[off++] = (byte) (values[i + 6] >>> 52);
+    buf[off++] = (byte) (values[i + 6] >>> 44);
+    buf[off++] = (byte) (values[i + 6] >>> 36);
+    buf[off++] = (byte) (values[i + 6] >>> 28);
+    buf[off++] = (byte) (values[i + 6] >>> 20);
+    buf[off++] = (byte) (values[i + 6] >>> 12);
+    buf[off++] = (byte) (values[i + 6] >>> 4);
+
+    buf[off] = (byte) (values[i + 6] << 4);
+    buf[off++] |= values[i + 7] >>> 56;
+    buf[off++] = (byte) (values[i + 7] >>> 48);
+    buf[off++] = (byte) (values[i + 7] >>> 40);
+    buf[off++] = (byte) (values[i + 7] >>> 32);
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits61(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 53);
+    buf[off++] = (byte) (values[i + 0] >>> 45);
+    buf[off++] = (byte) (values[i + 0] >>> 37);
+    buf[off++] = (byte) (values[i + 0] >>> 29);
+    buf[off++] = (byte) (values[i + 0] >>> 21);
+    buf[off++] = (byte) (values[i + 0] >>> 13);
+    buf[off++] = (byte) (values[i + 0] >>> 5);
+
+    buf[off] = (byte) (values[i + 0] << 3);
+    buf[off++] |= values[i + 1] >>> 58;
+    buf[off++] = (byte) (values[i + 1] >>> 50);
+    buf[off++] = (byte) (values[i + 1] >>> 42);
+    buf[off++] = (byte) (values[i + 1] >>> 34);
+    buf[off++] = (byte) (values[i + 1] >>> 26);
+    buf[off++] = (byte) (values[i + 1] >>> 18);
+    buf[off++] = (byte) (values[i + 1] >>> 10);
+    buf[off++] = (byte) (values[i + 1] >>> 2);
+
+    buf[off] = (byte) (values[i + 1] << 6);
+    buf[off++] |= values[i + 2] >>> 55;
+    buf[off++] = (byte) (values[i + 2] >>> 47);
+    buf[off++] = (byte) (values[i + 2] >>> 39);
+    buf[off++] = (byte) (values[i + 2] >>> 31);
+    buf[off++] = (byte) (values[i + 2] >>> 23);
+    buf[off++] = (byte) (values[i + 2] >>> 15);
+    buf[off++] = (byte) (values[i + 2] >>> 7);
+
+    buf[off] = (byte) (values[i + 2] << 1);
+    buf[off++] |= values[i + 3] >>> 60;
+    buf[off++] = (byte) (values[i + 3] >>> 52);
+    buf[off++] = (byte) (values[i + 3] >>> 44);
+    buf[off++] = (byte) (values[i + 3] >>> 36);
+    buf[off++] = (byte) (values[i + 3] >>> 28);
+    buf[off++] = (byte) (values[i + 3] >>> 20);
+    buf[off++] = (byte) (values[i + 3] >>> 12);
+    buf[off++] = (byte) (values[i + 3] >>> 4);
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 57;
+    buf[off++] = (byte) (values[i + 4] >>> 49);
+    buf[off++] = (byte) (values[i + 4] >>> 41);
+    buf[off++] = (byte) (values[i + 4] >>> 33);
+    buf[off++] = (byte) (values[i + 4] >>> 25);
+    buf[off++] = (byte) (values[i + 4] >>> 17);
+    buf[off++] = (byte) (values[i + 4] >>> 9);
+    buf[off++] = (byte) (values[i + 4] >>> 1);
+
+    buf[off] = (byte) (values[i + 4] << 7);
+    buf[off++] |= values[i + 5] >>> 54;
+    buf[off++] = (byte) (values[i + 5] >>> 46);
+    buf[off++] = (byte) (values[i + 5] >>> 38);
+    buf[off++] = (byte) (values[i + 5] >>> 30);
+    buf[off++] = (byte) (values[i + 5] >>> 22);
+    buf[off++] = (byte) (values[i + 5] >>> 14);
+    buf[off++] = (byte) (values[i + 5] >>> 6);
+
+    buf[off] = (byte) (values[i + 5] << 2);
+    buf[off++] |= values[i + 6] >>> 59;
+    buf[off++] = (byte) (values[i + 6] >>> 51);
+    buf[off++] = (byte) (values[i + 6] >>> 43);
+    buf[off++] = (byte) (values[i + 6] >>> 35);
+    buf[off++] = (byte) (values[i + 6] >>> 27);
+    buf[off++] = (byte) (values[i + 6] >>> 19);
+    buf[off++] = (byte) (values[i + 6] >>> 11);
+    buf[off++] = (byte) (values[i + 6] >>> 3);
+
+    buf[off] = (byte) (values[i + 6] << 5);
+    buf[off++] |= values[i + 7] >>> 56;
+    buf[off++] = (byte) (values[i + 7] >>> 48);
+    buf[off++] = (byte) (values[i + 7] >>> 40);
+    buf[off++] = (byte) (values[i + 7] >>> 32);
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits62(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 54);
+    buf[off++] = (byte) (values[i + 0] >>> 46);
+    buf[off++] = (byte) (values[i + 0] >>> 38);
+    buf[off++] = (byte) (values[i + 0] >>> 30);
+    buf[off++] = (byte) (values[i + 0] >>> 22);
+    buf[off++] = (byte) (values[i + 0] >>> 14);
+    buf[off++] = (byte) (values[i + 0] >>> 6);
+
+    buf[off] = (byte) (values[i + 0] << 2);
+    buf[off++] |= values[i + 1] >>> 60;
+    buf[off++] = (byte) (values[i + 1] >>> 52);
+    buf[off++] = (byte) (values[i + 1] >>> 44);
+    buf[off++] = (byte) (values[i + 1] >>> 36);
+    buf[off++] = (byte) (values[i + 1] >>> 28);
+    buf[off++] = (byte) (values[i + 1] >>> 20);
+    buf[off++] = (byte) (values[i + 1] >>> 12);
+    buf[off++] = (byte) (values[i + 1] >>> 4);
+
+    buf[off] = (byte) (values[i + 1] << 4);
+    buf[off++] |= values[i + 2] >>> 58;
+    buf[off++] = (byte) (values[i + 2] >>> 50);
+    buf[off++] = (byte) (values[i + 2] >>> 42);
+    buf[off++] = (byte) (values[i + 2] >>> 34);
+    buf[off++] = (byte) (values[i + 2] >>> 26);
+    buf[off++] = (byte) (values[i + 2] >>> 18);
+    buf[off++] = (byte) (values[i + 2] >>> 10);
+    buf[off++] = (byte) (values[i + 2] >>> 2);
+
+    buf[off] = (byte) (values[i + 2] << 6);
+    buf[off++] |= values[i + 3] >>> 56;
+    buf[off++] = (byte) (values[i + 3] >>> 48);
+    buf[off++] = (byte) (values[i + 3] >>> 40);
+    buf[off++] = (byte) (values[i + 3] >>> 32);
+    buf[off++] = (byte) (values[i + 3] >>> 24);
+    buf[off++] = (byte) (values[i + 3] >>> 16);
+    buf[off++] = (byte) (values[i + 3] >>> 8);
+    buf[off++] = (byte) (values[i + 3]);
+
+    buf[off++] = (byte) (values[i + 4] >>> 54);
+    buf[off++] = (byte) (values[i + 4] >>> 46);
+    buf[off++] = (byte) (values[i + 4] >>> 38);
+    buf[off++] = (byte) (values[i + 4] >>> 30);
+    buf[off++] = (byte) (values[i + 4] >>> 22);
+    buf[off++] = (byte) (values[i + 4] >>> 14);
+    buf[off++] = (byte) (values[i + 4] >>> 6);
+
+    buf[off] = (byte) (values[i + 4] << 2);
+    buf[off++] |= values[i + 5] >>> 60;
+    buf[off++] = (byte) (values[i + 5] >>> 52);
+    buf[off++] = (byte) (values[i + 5] >>> 44);
+    buf[off++] = (byte) (values[i + 5] >>> 36);
+    buf[off++] = (byte) (values[i + 5] >>> 28);
+    buf[off++] = (byte) (values[i + 5] >>> 20);
+    buf[off++] = (byte) (values[i + 5] >>> 12);
+    buf[off++] = (byte) (values[i + 5] >>> 4);
+
+    buf[off] = (byte) (values[i + 5] << 4);
+    buf[off++] |= values[i + 6] >>> 58;
+    buf[off++] = (byte) (values[i + 6] >>> 50);
+    buf[off++] = (byte) (values[i + 6] >>> 42);
+    buf[off++] = (byte) (values[i + 6] >>> 34);
+    buf[off++] = (byte) (values[i + 6] >>> 26);
+    buf[off++] = (byte) (values[i + 6] >>> 18);
+    buf[off++] = (byte) (values[i + 6] >>> 10);
+    buf[off++] = (byte) (values[i + 6] >>> 2);
+
+    buf[off] = (byte) (values[i + 6] << 6);
+    buf[off++] |= values[i + 7] >>> 56;
+    buf[off++] = (byte) (values[i + 7] >>> 48);
+    buf[off++] = (byte) (values[i + 7] >>> 40);
+    buf[off++] = (byte) (values[i + 7] >>> 32);
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) (values[i + 7]);
+  }
+
+  static void packBits63(final long[] values, final int i, final byte[] buf, int off) {
+    buf[off++] = (byte) (values[i + 0] >>> 55);
+    buf[off++] = (byte) (values[i + 0] >>> 47);
+    buf[off++] = (byte) (values[i + 0] >>> 39);
+    buf[off++] = (byte) (values[i + 0] >>> 31);
+    buf[off++] = (byte) (values[i + 0] >>> 23);
+    buf[off++] = (byte) (values[i + 0] >>> 15);
+    buf[off++] = (byte) (values[i + 0] >>> 7);
+
+    buf[off] = (byte) (values[i + 0] << 1);
+    buf[off++] |= values[i + 1] >>> 62;
+    buf[off++] = (byte) (values[i + 1] >>> 54);
+    buf[off++] = (byte) (values[i + 1] >>> 46);
+    buf[off++] = (byte) (values[i + 1] >>> 38);
+    buf[off++] = (byte) (values[i + 1] >>> 30);
+    buf[off++] = (byte) (values[i + 1] >>> 22);
+    buf[off++] = (byte) (values[i + 1] >>> 14);
+    buf[off++] = (byte) (values[i + 1] >>> 6);
+
+    buf[off] = (byte) (values[i + 1] << 2);
+    buf[off++] |= values[i + 2] >>> 61;
+    buf[off++] = (byte) (values[i + 2] >>> 53);
+    buf[off++] = (byte) (values[i + 2] >>> 45);
+    buf[off++] = (byte) (values[i + 2] >>> 37);
+    buf[off++] = (byte) (values[i + 2] >>> 29);
+    buf[off++] = (byte) (values[i + 2] >>> 21);
+    buf[off++] = (byte) (values[i + 2] >>> 13);
+    buf[off++] = (byte) (values[i + 2] >>> 5);
+
+    buf[off] = (byte) (values[i + 2] << 3);
+    buf[off++] |= values[i + 3] >>> 60;
+    buf[off++] = (byte) (values[i + 3] >>> 52);
+    buf[off++] = (byte) (values[i + 3] >>> 44);
+    buf[off++] = (byte) (values[i + 3] >>> 36);
+    buf[off++] = (byte) (values[i + 3] >>> 28);
+    buf[off++] = (byte) (values[i + 3] >>> 20);
+    buf[off++] = (byte) (values[i + 3] >>> 12);
+    buf[off++] = (byte) (values[i + 3] >>> 4);
+
+    buf[off] = (byte) (values[i + 3] << 4);
+    buf[off++] |= values[i + 4] >>> 59;
+    buf[off++] = (byte) (values[i + 4] >>> 51);
+    buf[off++] = (byte) (values[i + 4] >>> 43);
+    buf[off++] = (byte) (values[i + 4] >>> 35);
+    buf[off++] = (byte) (values[i + 4] >>> 27);
+    buf[off++] = (byte) (values[i + 4] >>> 19);
+    buf[off++] = (byte) (values[i + 4] >>> 11);
+    buf[off++] = (byte) (values[i + 4] >>> 3);
+
+    buf[off] = (byte) (values[i + 4] << 5);
+    buf[off++] |= values[i + 5] >>> 58;
+    buf[off++] = (byte) (values[i + 5] >>> 50);
+    buf[off++] = (byte) (values[i + 5] >>> 42);
+    buf[off++] = (byte) (values[i + 5] >>> 34);
+    buf[off++] = (byte) (values[i + 5] >>> 26);
+    buf[off++] = (byte) (values[i + 5] >>> 18);
+    buf[off++] = (byte) (values[i + 5] >>> 10);
+    buf[off++] = (byte) (values[i + 5] >>> 2);
+
+    buf[off] = (byte) (values[i + 5] << 6);
+    buf[off++] |= values[i + 6] >>> 57;
+    buf[off++] = (byte) (values[i + 6] >>> 49);
+    buf[off++] = (byte) (values[i + 6] >>> 41);
+    buf[off++] = (byte) (values[i + 6] >>> 33);
+    buf[off++] = (byte) (values[i + 6] >>> 25);
+    buf[off++] = (byte) (values[i + 6] >>> 17);
+    buf[off++] = (byte) (values[i + 6] >>> 9);
+    buf[off++] = (byte) (values[i + 6] >>> 1);
+
+    buf[off] = (byte) (values[i + 6] << 7);
+    buf[off++] |= values[i + 7] >>> 56;
+    buf[off++] = (byte) (values[i + 7] >>> 48);
+    buf[off++] = (byte) (values[i + 7] >>> 40);
+    buf[off++] = (byte) (values[i + 7] >>> 32);
+    buf[off++] = (byte) (values[i + 7] >>> 24);
+    buf[off++] = (byte) (values[i + 7] >>> 16);
+    buf[off++] = (byte) (values[i + 7] >>> 8);
+    buf[off] = (byte) values[i + 7];
+  }
+
+  static void unpackBits1(final long[] values, final int i, final byte[] buf, final int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off]) >>> 7) & 1;
+    values[i + 1] = (Byte.toUnsignedLong(buf[off]) >>> 6) & 1;
+    values[i + 2] = (Byte.toUnsignedLong(buf[off]) >>> 5) & 1;
+    values[i + 3] = (Byte.toUnsignedLong(buf[off]) >>> 4) & 1;
+    values[i + 4] = (Byte.toUnsignedLong(buf[off]) >>> 3) & 1;
+    values[i + 5] = (Byte.toUnsignedLong(buf[off]) >>> 2) & 1;
+    values[i + 6] = (Byte.toUnsignedLong(buf[off]) >>> 1) & 1;
+    values[i + 7] = Byte.toUnsignedLong(buf[off]) & 1;
+  }
+
+  static void unpackBits2(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off]) >>> 6) & 3;
+    values[i + 1] = (Byte.toUnsignedLong(buf[off]) >>> 4) & 3;
+    values[i + 2] = (Byte.toUnsignedLong(buf[off]) >>> 2) & 3;
+    values[i + 3] = Byte.toUnsignedLong(buf[off++]) & 3;
+    values[i + 4] = (Byte.toUnsignedLong(buf[off]) >>> 6) & 3;
+    values[i + 5] = (Byte.toUnsignedLong(buf[off]) >>> 4) & 3;
+    values[i + 6] = (Byte.toUnsignedLong(buf[off]) >>> 2) & 3;
+    values[i + 7] = Byte.toUnsignedLong(buf[off]) & 3;
+  }
+
+  static void unpackBits3(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off]) >>> 5;
+    values[i + 1] = (Byte.toUnsignedLong(buf[off]) >>> 2) & 7;
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 3) << 1;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+    values[i + 3] = (Byte.toUnsignedLong(buf[off]) >>> 4) & 7;
+    values[i + 4] = (Byte.toUnsignedLong(buf[off]) >>> 1) & 7;
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 1) << 2;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+    values[i + 6] = (Byte.toUnsignedLong(buf[off]) >>> 3) & 7;
+    values[i + 7] = Byte.toUnsignedLong(buf[off]) & 7;
+  }
+
+  static void unpackBits4(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off]) >>> 4;
+    values[i + 1] = Byte.toUnsignedLong(buf[off++]) & 0xf;
+    values[i + 2] = Byte.toUnsignedLong(buf[off]) >>> 4;
+    values[i + 3] = Byte.toUnsignedLong(buf[off++]) & 0xf;
+    values[i + 4] = Byte.toUnsignedLong(buf[off]) >>> 4;
+    values[i + 5] = Byte.toUnsignedLong(buf[off++]) & 0xf;
+    values[i + 6] = Byte.toUnsignedLong(buf[off]) >>> 4;
+    values[i + 7] = Byte.toUnsignedLong(buf[off]) & 0xf;
+  }
+
+  static void unpackBits5(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 7) << 2;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off]) >>> 1) & 0x1f;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 1) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 1;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off]) >>> 2) & 0x1f;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 3) << 3;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 7] = Byte.toUnsignedLong(buf[off]) & 0x1f;
+  }
+
+  static void unpackBits6(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 3) << 4;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 2;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 3] = Byte.toUnsignedLong(buf[off++]) & 0x3f;
+
+    values[i + 4] = Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 3) << 4;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 2;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 7] = Byte.toUnsignedLong(buf[off]) & 0x3f;
+  }
+
+  static void unpackBits7(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 1) << 6;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 3) << 5;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 7) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 3;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 2;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 1;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 7] = Byte.toUnsignedLong(buf[off]) & 0x7f;
+  }
+
+  static void unpackBits8(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off++]);
+    values[i + 1] = Byte.toUnsignedLong(buf[off++]);
+    values[i + 2] = Byte.toUnsignedLong(buf[off++]);
+    values[i + 3] = Byte.toUnsignedLong(buf[off++]);
+    values[i + 4] = Byte.toUnsignedLong(buf[off++]);
+    values[i + 5] = Byte.toUnsignedLong(buf[off++]);
+    values[i + 6] = Byte.toUnsignedLong(buf[off++]);
+    values[i + 7] = Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits9(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off++]) << 1;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 0x7f) << 2;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 3;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 5;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 7) << 6;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 3) << 7;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 1) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits10(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 4;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 6;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 3) << 8;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 4] = Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 4;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 6;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 3) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits11(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off++]) << 3;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 6;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 3) << 9;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 1;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 0x7f) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 7;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 1) << 10;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 5;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 7) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits12(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 8;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 2] = Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 8;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 4] = Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 8;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 6] = Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits13(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off++]) << 5;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 7) << 10;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 7;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 1) << 12;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 9;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 1;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0x7f) << 6;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 3) << 11;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 3;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits14(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 3) << 12;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 10;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 8;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 4] = Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 3) << 12;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 10;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits15(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off++]) << 7;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 1) << 14;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 3) << 13;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 5;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 7) << 12;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 11;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 3;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 10;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 9;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 1;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 0x7f) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits16(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 1] = Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 2] = Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 3] = Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 4] = Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 5] = Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 6] = Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 7] = Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits17(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off++]) << 9;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 1;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 0x7f) << 10;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 11;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 3;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 12;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 13;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 5;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 7) << 14;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 3) << 15;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 7;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 1) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits18(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 12;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 14;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 3) << 16;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 4] = Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 12;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 14;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 3) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits19(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off++]) << 11;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 3;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 14;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 3) << 17;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 9;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 1;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 0x7f) << 12;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 15;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 7;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 1) << 18;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 13;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 5;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 7) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits20(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 16;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 2] = Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 16;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 4] = Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 16;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 6] = Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits21(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off++]) << 13;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 5;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 7) << 18;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 15;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 7;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 1) << 20;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 17;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 9;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 1;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0x7f) << 14;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 3) << 19;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 11;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 3;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits22(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 3) << 20;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 18;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 16;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 4] = Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 3) << 20;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 18;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits23(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off++]) << 15;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 7;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 1) << 22;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 3) << 21;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 13;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 5;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 7) << 20;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 19;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 11;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 3;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 18;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 17;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 9;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 1;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 0x7f) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits24(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 1] = Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 2] = Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 3] = Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 4] = Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 5] = Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 6] = Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 7] = Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits25(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off++]) << 17;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 9;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 1;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 0x7f) << 18;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 19;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 11;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 3;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 20;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 21;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 13;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 5;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 7) << 22;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 3) << 23;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 15;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 7;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 1) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits26(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 20;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 22;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 3) << 24;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 4] = Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 20;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 22;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 3) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits27(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off++]) << 19;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 11;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 3;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 22;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 3) << 25;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 17;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 9;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 1;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 0x7f) << 20;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 23;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 15;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 7;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 1) << 26;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 21;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 13;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 5;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 7) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits28(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 24;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 2] = Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 24;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 4] = Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 24;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 6] = Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits29(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off++]) << 21;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 13;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 5;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 7) << 26;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 23;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 15;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 7;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 1) << 28;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 25;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 17;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 9;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 1;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0x7f) << 22;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 3) << 27;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 19;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 11;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 3;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits30(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 3) << 28;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 26;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 24;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 4] = Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 3) << 28;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 26;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits31(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = Byte.toUnsignedLong(buf[off++]) << 23;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 15;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 7;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 1) << 30;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 3) << 29;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 21;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 13;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 5;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 7) << 28;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 27;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 19;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 11;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 3;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 26;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 25;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 17;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 9;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 1;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 0x7f) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits32(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits33(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 25;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 17;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 9;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 1;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 0x7f) << 26;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 27;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 19;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 11;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 3;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 28;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 29;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 21;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 13;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 5;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 7) << 30;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 3) << 31;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 23;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 15;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 7;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 1) << 32;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits34(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 28;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 30;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 3) << 32;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 28;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 30;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 3) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]);
+  }
+
+  static void unpackBits35(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 27;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 19;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 11;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 3;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 30;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 3) << 33;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 25;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 17;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 9;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 1;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 0x7f) << 28;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 31;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 23;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 15;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 7;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 1) << 34;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 29;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 21;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 13;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 5;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 7) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits36(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 32;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 32;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 32;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits37(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 29;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 21;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 13;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 5;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 7) << 34;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 31;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 23;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 15;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 7;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 1) << 36;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 33;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 25;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 17;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 9;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 1;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0x7f) << 30;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 22;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 3) << 35;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 27;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 19;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 11;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 3;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits38(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 30;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 3) << 36;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 34;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 32;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++])) << 30;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 3) << 36;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 34;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits39(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 31;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 23;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 15;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 7;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 1) << 38;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 30;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 3) << 37;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 29;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 21;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 13;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 5;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 7) << 36;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 35;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 27;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 19;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 11;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 3;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 34;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 33;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 25;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 17;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 9;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 1;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 0x7f) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits40(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits41(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 33;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 25;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 17;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 9;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 1;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 0x7f) << 34;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 35;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 27;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 19;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 11;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 3;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 36;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 37;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 29;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 21;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 13;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 5;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 7) << 38;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 30;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 3) << 39;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 31;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 23;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 15;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 7;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 1) << 40;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits42(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 34;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 36;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 38;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 30;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 3) << 40;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++])) << 34;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 36;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 38;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 30;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 3) << 40;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits43(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 35;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 27;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 19;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 11;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 3;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 38;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 30;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 3) << 41;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 33;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 25;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 17;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 9;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 1;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 0x7f) << 36;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 39;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 31;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 23;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 15;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 7;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 1) << 42;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 34;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 37;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 29;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 21;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 13;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 5;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 7) << 40;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits44(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 40;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 40;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 40;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 40;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits45(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 37;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 29;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 21;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 13;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 5;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 7) << 42;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 34;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 39;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 31;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 23;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 15;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 7;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 1) << 44;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 41;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 33;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 25;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 17;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 9;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 1;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0x7f) << 38;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 30;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 22;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 3) << 43;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 35;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 27;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 19;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 11;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 3;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 40;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits46(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 38;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 30;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 3) << 44;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 42;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 34;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 40;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++])) << 38;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 30;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 3) << 44;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 42;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 34;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 40;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits47(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 39;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 31;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 23;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 15;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 7;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 1) << 46;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 38;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 30;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 3) << 45;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 37;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 29;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 21;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 13;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 5;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 7) << 44;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 43;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 35;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 27;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 19;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 11;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 3;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 42;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 34;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 41;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 33;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 25;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 17;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 9;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 1;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 0x7f) << 40;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits48(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits49(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 41;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 33;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 25;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 17;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 9;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 1;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 0x7f) << 42;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 34;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 43;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 35;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 27;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 19;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 11;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 3;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 44;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 45;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 37;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 29;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 21;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 13;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 5;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 7) << 46;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 38;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 30;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 3) << 47;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 39;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 31;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 23;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 15;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 7;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 1) << 48;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits50(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 42;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 34;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 44;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 46;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 38;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 30;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 3) << 48;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++])) << 42;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 34;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 44;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 46;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 38;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 30;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 3) << 48;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits51(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 43;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 35;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 27;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 19;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 11;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 3;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 46;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 38;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 30;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 3) << 49;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 41;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 33;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 25;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 17;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 9;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 1;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 0x7f) << 44;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 47;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 39;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 31;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 23;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 15;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 7;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 1) << 50;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 42;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 34;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 45;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 37;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 29;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 21;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 13;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 5;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 7) << 48;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits52(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 44;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 48;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++])) << 44;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 48;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++])) << 44;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 48;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++])) << 44;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 48;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits53(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 45;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 37;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 29;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 21;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 13;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 5;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 7) << 50;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 42;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 34;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 47;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 39;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 31;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 23;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 15;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 7;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 1) << 52;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 44;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 49;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 41;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 33;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 25;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 17;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 9;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 1;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0x7f) << 46;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 38;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 30;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 3) << 51;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 43;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 35;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 27;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 19;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 11;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 3;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 48;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits54(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 46;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 38;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 30;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 3) << 52;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 44;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 50;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 42;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 34;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 48;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++])) << 46;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 38;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 30;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 3) << 52;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 44;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 50;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 42;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 34;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 48;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]);
+  }
+
+  static void unpackBits55(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 47;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 39;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 31;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 23;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 15;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 7;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 1) << 54;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 46;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 38;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 30;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 3) << 53;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 45;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 37;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 29;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 21;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 13;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 5;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 7) << 52;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 44;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 51;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 43;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 35;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 27;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 19;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 11;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 3;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 50;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 42;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 34;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 49;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 41;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 33;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 25;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 17;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 9;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 1;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 0x7f) << 48;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits56(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 48;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++])) << 48;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++])) << 48;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++])) << 48;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++])) << 48;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++])) << 48;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++])) << 48;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]);
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++])) << 48;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits57(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 49;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 41;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 33;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 25;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 17;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 9;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 1;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 0x7f) << 50;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 42;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 34;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 51;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 43;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 35;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 27;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 19;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 11;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 3;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 52;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 44;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 53;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 45;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 37;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 29;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 21;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 13;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 5;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 7) << 54;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 46;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 38;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 30;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 3) << 55;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 47;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 39;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 31;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 23;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 15;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 7;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 1) << 56;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 48;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits58(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 50;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 42;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 34;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 52;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 44;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 54;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 46;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 38;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 30;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 3) << 56;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 48;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++])) << 50;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 42;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 34;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 52;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 44;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 54;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 46;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 38;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 30;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 3) << 56;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 48;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]);
+  }
+
+  static void unpackBits59(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 51;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 43;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 35;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 27;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 19;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 11;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 3;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 54;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 46;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 38;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 30;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 3) << 57;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 49;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 41;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 33;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 25;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 17;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 9;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 1;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 0x7f) << 52;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 44;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 55;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 47;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 39;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 31;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 23;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 15;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 7;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 1) << 58;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 50;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 42;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 34;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 53;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 45;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 37;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 29;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 21;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 13;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 5;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 7) << 56;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 48;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits60(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 52;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 44;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 56;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 48;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++])) << 52;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 44;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 56;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 48;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++])) << 52;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 44;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 56;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 48;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++])) << 52;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 44;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 56;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 48;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits61(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 53;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 45;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 37;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 29;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 21;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 13;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 5;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 7) << 58;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 50;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 42;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 34;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 55;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 47;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 39;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 31;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 23;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 15;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 7;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 1) << 60;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 52;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 44;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 57;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 49;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 41;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 33;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 25;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 17;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 9;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 1;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0x7f) << 54;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 46;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 38;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 30;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 3) << 59;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 51;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 43;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 35;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 27;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 19;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 11;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 3;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 56;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 48;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits62(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 54;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 46;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 38;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 30;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 3) << 60;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 52;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 44;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 58;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 50;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 42;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 34;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 56;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 48;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]);
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++])) << 54;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 46;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 38;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 30;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 3) << 60;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 52;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 44;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 58;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 50;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 42;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 34;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 56;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 48;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+  static void unpackBits63(final long[] values, final int i, final byte[] buf, int off) {
+    values[i + 0] = (Byte.toUnsignedLong(buf[off++])) << 55;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 47;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 39;
+    values[i + 0] |= (Byte.toUnsignedLong(buf[off++])) << 31;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 23;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 15;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off++]) << 7;
+    values[i + 0] |= Byte.toUnsignedLong(buf[off]) >>> 1;
+
+    values[i + 1] = (Byte.toUnsignedLong(buf[off++]) & 1) << 62;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 54;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 46;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 38;
+    values[i + 1] |= (Byte.toUnsignedLong(buf[off++])) << 30;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 22;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 14;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off++]) << 6;
+    values[i + 1] |= Byte.toUnsignedLong(buf[off]) >>> 2;
+
+    values[i + 2] = (Byte.toUnsignedLong(buf[off++]) & 3) << 61;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 53;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 45;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 37;
+    values[i + 2] |= (Byte.toUnsignedLong(buf[off++])) << 29;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 21;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 13;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off++]) << 5;
+    values[i + 2] |= Byte.toUnsignedLong(buf[off]) >>> 3;
+
+    values[i + 3] = (Byte.toUnsignedLong(buf[off++]) & 7) << 60;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 52;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 44;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 36;
+    values[i + 3] |= (Byte.toUnsignedLong(buf[off++])) << 28;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 20;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 12;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off++]) << 4;
+    values[i + 3] |= Byte.toUnsignedLong(buf[off]) >>> 4;
+
+    values[i + 4] = (Byte.toUnsignedLong(buf[off++]) & 0xf) << 59;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 51;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 43;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 35;
+    values[i + 4] |= (Byte.toUnsignedLong(buf[off++])) << 27;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 19;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 11;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off++]) << 3;
+    values[i + 4] |= Byte.toUnsignedLong(buf[off]) >>> 5;
+
+    values[i + 5] = (Byte.toUnsignedLong(buf[off++]) & 0x1f) << 58;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 50;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 42;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 34;
+    values[i + 5] |= (Byte.toUnsignedLong(buf[off++])) << 26;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 18;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 10;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off++]) << 2;
+    values[i + 5] |= Byte.toUnsignedLong(buf[off]) >>> 6;
+
+    values[i + 6] = (Byte.toUnsignedLong(buf[off++]) & 0x3f) << 57;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 49;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 41;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 33;
+    values[i + 6] |= (Byte.toUnsignedLong(buf[off++])) << 25;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 17;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 9;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off++]) << 1;
+    values[i + 6] |= Byte.toUnsignedLong(buf[off]) >>> 7;
+
+    values[i + 7] = (Byte.toUnsignedLong(buf[off++]) & 0x7f) << 56;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 48;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 40;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 32;
+    values[i + 7] |= (Byte.toUnsignedLong(buf[off++])) << 24;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 16;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off++]) << 8;
+    values[i + 7] |= Byte.toUnsignedLong(buf[off]);
+  }
+
+}
diff --git a/src/main/java/org/apache/datasketches/theta2/BytesCompactCompressedHashIterator.java b/src/main/java/org/apache/datasketches/theta2/BytesCompactCompressedHashIterator.java
new file mode 100644
index 00000000..81a98592
--- /dev/null
+++ b/src/main/java/org/apache/datasketches/theta2/BytesCompactCompressedHashIterator.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.datasketches.theta2;
+
+/*
+ * This is to uncompress serial version 4 sketch incrementally
+ */
+class BytesCompactCompressedHashIterator implements HashIterator {
+  private byte[] bytes;
+  private int offset;
+  private int entryBits;
+  private int numEntries;
+  private int index;
+  private long previous;
+  private int offsetBits;
+  private long[] buffer;
+  private boolean isBlockMode;
+
+  BytesCompactCompressedHashIterator(
+      final byte[] bytes,
+      final int offset,
+      final int entryBits,
+      final int numEntries
+  ) {
+    this.bytes = bytes;
+    this.offset = offset;
+    this.entryBits = entryBits;
+    this.numEntries = numEntries;
+    index = -1;
+    previous = 0;
+    offsetBits = 0;
+    buffer = new long[8];
+    isBlockMode = numEntries >= 8;
+  }
+
+  @Override
+  public long get() {
+    return buffer[index & 7];
+  }
+
+  @Override
+  public boolean next() {
+    if (++index == numEntries) { return false; }
+    if (isBlockMode) {
+      if ((index & 7) == 0) {
+        if (numEntries - index >= 8) {
+          unpack8();
+        } else {
+          isBlockMode = false;
+          unpack1();
+        }
+      }
+    } else {
+      unpack1();
+    }
+    return true;
+  }
+
+  private void unpack1() {
+    final int i = index & 7;
+    BitPacking.unpackBits(buffer, i, entryBits, bytes, offset, offsetBits);
+    offset += (offsetBits + entryBits) >>> 3;
+    offsetBits = (offsetBits + entryBits) & 7;
+    buffer[i] += previous;
+    previous = buffer[i];
+  }
+
+  private void unpack8() {
+    BitPacking.unpackBitsBlock8(buffer, 0, bytes, offset, entryBits);
+    offset += entryBits;
+    for (int i = 0; i < 8; i++) {
+      buffer[i] += previous;
+      previous = buffer[i];
+    }
+  }
+}
diff --git a/src/main/java/org/apache/datasketches/theta2/BytesCompactHashIterator.java b/src/main/java/org/apache/datasketches/theta2/BytesCompactHashIterator.java
new file mode 100644
index 00000000..9a475457
--- /dev/null
+++ b/src/main/java/org/apache/datasketches/theta2/BytesCompactHashIterator.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.datasketches.theta2;
+
+import org.apache.datasketches.common.ByteArrayUtil;
+
+/*
+ * This is to iterate over serial version 3 sketch representation
+ */
+class BytesCompactHashIterator implements HashIterator {
+  final private byte[] bytes;
+  final private int offset;
+  final private int numEntries;
+  private int index;
+
+  BytesCompactHashIterator(
+      final byte[] bytes,
+      final int offset,
+      final int numEntries
+  ) {
+    this.bytes = bytes;
+    this.offset = offset;
+    this.numEntries = numEntries;
+    index = -1;
+  }
+
+  @Override
+  public long get() {
+    return ByteArrayUtil.getLongLE(bytes, offset + index * Long.BYTES);
+  }
+
+  @Override
+  public boolean next() {
+    return ++index < numEntries;
+ }
+}
diff --git a/src/main/java/org/apache/datasketches/theta2/CompactOperations.java b/src/main/java/org/apache/datasketches/theta2/CompactOperations.java
new file mode 100644
index 00000000..ab342a1f
--- /dev/null
+++ b/src/main/java/org/apache/datasketches/theta2/CompactOperations.java
@@ -0,0 +1,388 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.datasketches.theta2;
+
+import static java.lang.foreign.ValueLayout.JAVA_BYTE;
+import static java.lang.foreign.ValueLayout.JAVA_LONG_UNALIGNED;
+import static java.lang.foreign.ValueLayout.JAVA_SHORT_UNALIGNED;
+import static org.apache.datasketches.theta2.PreambleUtil.COMPACT_FLAG_MASK;
+import static org.apache.datasketches.theta2.PreambleUtil.EMPTY_FLAG_MASK;
+import static org.apache.datasketches.theta2.PreambleUtil.LG_NOM_LONGS_BYTE;
+import static org.apache.datasketches.theta2.PreambleUtil.ORDERED_FLAG_MASK;
+import static org.apache.datasketches.theta2.PreambleUtil.READ_ONLY_FLAG_MASK;
+import static org.apache.datasketches.theta2.PreambleUtil.SER_VER;
+import static org.apache.datasketches.theta2.PreambleUtil.SINGLEITEM_FLAG_MASK;
+import static org.apache.datasketches.theta2.PreambleUtil.extractCurCount;
+import static org.apache.datasketches.theta2.PreambleUtil.extractFamilyID;
+import static org.apache.datasketches.theta2.PreambleUtil.extractFlags;
+import static org.apache.datasketches.theta2.PreambleUtil.extractLgArrLongs;
+import static org.apache.datasketches.theta2.PreambleUtil.extractPreLongs;
+import static org.apache.datasketches.theta2.PreambleUtil.extractSeedHash;
+import static org.apache.datasketches.theta2.PreambleUtil.extractSerVer;
+import static org.apache.datasketches.theta2.PreambleUtil.extractThetaLong;
+import static org.apache.datasketches.theta2.PreambleUtil.insertCurCount;
+import static org.apache.datasketches.theta2.PreambleUtil.insertFamilyID;
+import static org.apache.datasketches.theta2.PreambleUtil.insertFlags;
+import static org.apache.datasketches.theta2.PreambleUtil.insertP;
+import static org.apache.datasketches.theta2.PreambleUtil.insertPreLongs;
+import static org.apache.datasketches.theta2.PreambleUtil.insertSeedHash;
+import static org.apache.datasketches.theta2.PreambleUtil.insertSerVer;
+import static org.apache.datasketches.theta2.PreambleUtil.insertThetaLong;
+
+import java.lang.foreign.MemorySegment;
+import java.util.Arrays;
+
+import org.apache.datasketches.common.Family;
+import org.apache.datasketches.common.SketchesArgumentException;
+import org.apache.datasketches.common.SketchesStateException;
+
+/**
+ * @author Lee Rhodes
+ */
+final class CompactOperations {
+
+  private CompactOperations() {}
+
+  static CompactSketch componentsToCompact( //No error checking
+      final long thetaLong,
+      final int curCount,
+      final short seedHash,
+      final boolean srcEmpty,
+      final boolean srcCompact,
+      final boolean srcOrdered,
+      final boolean dstOrdered,
+      final MemorySegment dstWSeg,
+      final long[] hashArr) //may not be compacted, ordered or unordered, may be null
+  {
+    final boolean direct = dstWSeg != null;
+    final boolean empty = srcEmpty || ((curCount == 0) && (thetaLong == Long.MAX_VALUE));
+    final boolean single = (curCount == 1) && (thetaLong == Long.MAX_VALUE);
+    final long[] hashArrOut;
+    if (!srcCompact) {
+      hashArrOut = CompactOperations.compactCache(hashArr, curCount, thetaLong, dstOrdered);
+    } else {
+      hashArrOut = hashArr;
+    }
+    if (!srcOrdered && dstOrdered && !empty && !single) {
+      Arrays.sort(hashArrOut);
+    }
+    //Note: for empty or single we always output the ordered form.
+    final boolean dstOrderedOut = (empty || single) ? true : dstOrdered;
+    if (direct) {
+      final int preLongs = computeCompactPreLongs(empty, curCount, thetaLong);
+      int flags = READ_ONLY_FLAG_MASK | COMPACT_FLAG_MASK; //always LE
+      flags |=  empty ? EMPTY_FLAG_MASK : 0;
+      flags |= dstOrderedOut ? ORDERED_FLAG_MASK : 0;
+      flags |= single ? SINGLEITEM_FLAG_MASK : 0;
+
+      final MemorySegment seg =
+          loadCompactMemory(hashArrOut, seedHash, curCount, thetaLong, dstWSeg, (byte)flags, preLongs);
+      return new DirectCompactSketch(seg);
+
+    } else { //Heap
+      if (empty) {
+        return EmptyCompactSketch.getInstance();
+      }
+      if (single) {
+        return new SingleItemSketch(hashArrOut[0], seedHash);
+      }
+      return new HeapCompactSketch(hashArrOut, empty, seedHash, curCount, thetaLong, dstOrderedOut);
+    }
+  }
+
+  /**
+   * Heapify or convert a source Theta Sketch MemorySegment image into a heap or target MemorySegment CompactSketch.
+   * This assumes hashSeed is OK; serVer = 3.
+   * @param srcSeg the given input source MemorySegment image. Can be Read Only.
+   * @param dstOrdered the desired ordering of the resulting CompactSketch
+   * @param dstWSeg Used for the target CompactSketch if it is MemorySegment-based. Must be Writable.
+   * @return a CompactSketch of the correct form.
+   */
+  @SuppressWarnings("unused")
+  static CompactSketch memoryToCompact(
+      final MemorySegment srcSeg,
+      final boolean dstOrdered,
+      final MemorySegment dstWSeg)
+  {
+    //extract Pre0 fields and Flags from srcMem
+    final int srcPreLongs = extractPreLongs(srcSeg);
+    final int srcSerVer = extractSerVer(srcSeg); //not used
+    final int srcFamId = extractFamilyID(srcSeg);
+    final int srcLgArrLongs = extractLgArrLongs(srcSeg);
+    final int srcFlags = extractFlags(srcSeg);
+    final short srcSeedHash = (short) extractSeedHash(srcSeg);
+
+    //srcFlags
+    final boolean srcReadOnlyFlag = (srcFlags & READ_ONLY_FLAG_MASK) > 0;
+    final boolean srcEmptyFlag = (srcFlags & EMPTY_FLAG_MASK) > 0;
+    final boolean srcCompactFlag = (srcFlags & COMPACT_FLAG_MASK) > 0;
+    final boolean srcOrderedFlag = (srcFlags & ORDERED_FLAG_MASK) > 0;
+    final boolean srcSingleFlag = (srcFlags & SINGLEITEM_FLAG_MASK) > 0;
+
+    final boolean single = srcSingleFlag
+        || SingleItemSketch.otherCheckForSingleItem(srcPreLongs, srcSerVer, srcFamId, srcFlags);
+
+    //extract pre1 and pre2 fields
+    final int curCount = single ? 1 : (srcPreLongs > 1) ? extractCurCount(srcSeg) : 0;
+    final long thetaLong = (srcPreLongs > 2) ? extractThetaLong(srcSeg) : Long.MAX_VALUE;
+
+    //do some basic checks ...
+    if (srcEmptyFlag)  { assert (curCount == 0) && (thetaLong == Long.MAX_VALUE); }
+    if (single) { assert (curCount == 1) && (thetaLong == Long.MAX_VALUE); }
+    checkFamilyAndFlags(srcFamId, srcCompactFlag, srcReadOnlyFlag);
+
+    //dispatch empty and single cases
+    //Note: for empty and single we always output the ordered form.
+    final boolean dstOrderedOut = (srcEmptyFlag || single) ? true : dstOrdered;
+    if (srcEmptyFlag) {
+      if (dstWSeg != null) {
+        MemorySegment.copy(EmptyCompactSketch.EMPTY_COMPACT_SKETCH_ARR, 0, dstWSeg, JAVA_BYTE, 0, 8);
+        return new DirectCompactSketch(dstWSeg);
+      } else {
+        return EmptyCompactSketch.getInstance();
+      }
+    }
+    if (single) {
+      final long hash = srcSeg.get(JAVA_LONG_UNALIGNED, srcPreLongs << 3);
+      final SingleItemSketch sis = new SingleItemSketch(hash, srcSeedHash);
+      if (dstWSeg != null) {
+        MemorySegment.copy(sis.toByteArray(), 0, dstWSeg, JAVA_BYTE, 0, 16);
+        return new DirectCompactSketch(dstWSeg);
+      } else { //heap
+        return sis;
+      }
+    }
+
+    //extract hashArr > 1
+    final long[] hashArr;
+    if (srcCompactFlag) {
+      hashArr = new long[curCount];
+      MemorySegment.copy(srcSeg, JAVA_LONG_UNALIGNED, srcPreLongs << 3, hashArr, 0, curCount);
+    } else { //update sketch, thus hashTable form
+      final int srcCacheLen = 1 << srcLgArrLongs;
+      final long[] tempHashArr = new long[srcCacheLen];
+      MemorySegment.copy(srcSeg, JAVA_LONG_UNALIGNED, srcPreLongs << 3, tempHashArr, 0, srcCacheLen);
+      hashArr = compactCache(tempHashArr, curCount, thetaLong, dstOrderedOut);
+    }
+
+    final int flagsOut = READ_ONLY_FLAG_MASK | COMPACT_FLAG_MASK
+                         | ((dstOrderedOut) ? ORDERED_FLAG_MASK : 0);
+
+    //load the destination.
+    if (dstWSeg != null) {
+      final MemorySegment tgtSeg = loadCompactMemory(hashArr, srcSeedHash, curCount, thetaLong, dstWSeg,
+          (byte)flagsOut, srcPreLongs);
+      return new DirectCompactSketch(tgtSeg);
+    } else { //heap
+      return new HeapCompactSketch(hashArr, srcEmptyFlag, srcSeedHash, curCount, thetaLong,
+          dstOrderedOut);
+    }
+  }
+
+  private static final void checkFamilyAndFlags(
+      final int srcFamId,
+      final boolean srcCompactFlag,
+      final boolean srcReadOnlyFlag) {
+    final Family srcFamily = Family.idToFamily(srcFamId);
+    if (srcCompactFlag) {
+      if ((srcFamily == Family.COMPACT) && srcReadOnlyFlag) { return; }
+    } else {
+      if (srcFamily == Family.ALPHA) { return; }
+      if (srcFamily == Family.QUICKSELECT) { return; }
+    }
+    throw new SketchesArgumentException(
+        "Possible Corruption: Family does not match flags: Family: "
+            + srcFamily.toString()
+            + ", Compact Flag: " + srcCompactFlag
+            + ", ReadOnly Flag: " + srcReadOnlyFlag);
+  }
+
+  //All arguments must be valid and correct including flags.
+  // Used as helper to create byte arrays as well as loading Memory for direct compact sketches
+  //Input must be writable, return can be Read Only
+  static final MemorySegment loadCompactMemory(
+      final long[] compactHashArr,
+      final short seedHash,
+      final int curCount,
+      final long thetaLong,
+      final MemorySegment dstWSeg,
+      final byte flags,
+      final int preLongs)
+  {
+    assert (dstWSeg != null) && (compactHashArr != null);
+    final int outLongs = preLongs + curCount;
+    final int outBytes = outLongs << 3;
+    final int dstBytes = (int) dstWSeg.byteSize();
+    if (outBytes > dstBytes) {
+      throw new SketchesArgumentException("Insufficient Memory: " + dstBytes
+        + ", Need: " + outBytes);
+    }
+    final byte famID = (byte) Family.COMPACT.getID();
+
+    //Caution: The following loads directly into Memory without creating a heap byte[] first,
+    // which would act as a pre-clearing, initialization mechanism. So it is important to make sure
+    // that all fields are initialized, even those that are not used by the CompactSketch.
+    // Otherwise, uninitialized fields could be filled with off-heap garbage, which could cause
+    // other problems downstream if those fields are not filtered out first.
+    // As written below, all fields are initialized avoiding an extra copy.
+
+    //The first 8 bytes (pre0)
+    insertPreLongs(dstWSeg, preLongs); //RF not used = 0
+    insertSerVer(dstWSeg, SER_VER);
+    insertFamilyID(dstWSeg, famID);
+    //The following initializes the lgNomLongs and lgArrLongs to 0.
+    //They are not used in CompactSketches.
+    dstWSeg.set(JAVA_SHORT_UNALIGNED, LG_NOM_LONGS_BYTE, (short)0);
+    insertFlags(dstWSeg, flags);
+    insertSeedHash(dstWSeg, seedHash);
+
+    if ((preLongs == 1) && (curCount == 1)) { //singleItem, theta = 1.0
+      dstWSeg.set(JAVA_LONG_UNALIGNED, 8, compactHashArr[0]);
+      return dstWSeg;
+    }
+    if (preLongs > 1) {
+      insertCurCount(dstWSeg, curCount);
+      insertP(dstWSeg, (float) 1.0);
+    }
+    if (preLongs > 2) {
+      insertThetaLong(dstWSeg, thetaLong);
+    }
+    if (curCount > 0) { //theta could be < 1.0.
+      //dstWSeg.putLongArray(preLongs << 3, compactHashArr, 0, curCount);
+      MemorySegment.copy(compactHashArr, 0, dstWSeg, JAVA_LONG_UNALIGNED, preLongs << 3, curCount);
+    }
+    return dstWSeg; //if prelongs == 3 & curCount == 0, theta could be < 1.0. This can be RO
+  }
+
+  /**
+   * Copies then compacts, cleans, and may sort the resulting array.
+   * The source cache can be a hash table with interstitial zeros or
+   * "dirty" values, which are hash values greater than theta.
+   * These can be generated by the Alpha sketch.
+   * @param srcCache anything
+   * @param curCount must be correct
+   * @param thetaLong The correct
+   * <a href="{@docRoot}/resources/dictionary.html#thetaLong">thetaLong</a>.
+   * @param dstOrdered true if output array must be sorted
+   * @return the compacted array.
+   */
+  static final long[] compactCache(final long[] srcCache, final int curCount,
+      final long thetaLong, final boolean dstOrdered) {
+    if (curCount == 0) {
+      return new long[0];
+    }
+    final long[] cacheOut = new long[curCount];
+    final int len = srcCache.length;
+    int j = 0;
+    for (int i = 0; i < len; i++) { //scan the full srcCache
+      final long v = srcCache[i];
+      if ((v <= 0L) || (v >= thetaLong) ) { continue; } //ignoring zeros or dirty values
+      cacheOut[j++] = v;
+    }
+    if (j < curCount) {
+      throw new SketchesStateException(
+          "Possible Corruption: curCount parameter is incorrect.");
+    }
+    if (dstOrdered && (curCount > 1)) {
+      Arrays.sort(cacheOut);
+    }
+    return cacheOut;
+  }
+
+  /*
+   * The truth table for empty, curCount and theta when compacting is as follows:
+   * <pre>
+   * Num Theta CurCount Empty State    Name, Comments
+   *  0    1.0     0      T     OK     EMPTY: The Normal Empty State
+   *  1    1.0     0      F   Internal This can occur internally as the result of an intersection of two exact,
+   *                                   disjoint sets, or AnotB of two exact, identical sets. There is no probability
+   *                                   distribution, so this is converted internally to EMPTY {1.0, 0, T}.
+   *                                   This is handled in SetOperation.createCompactSketch().
+   *  2    1.0    !0      T   Error    Empty=T and curCount !0 should never coexist.
+   *                                   This is checked in all compacting operations.
+   *  3    1.0    !0      F     OK     EXACT: This corresponds to a sketch in exact mode
+   *  4   <1.0     0      T   Internal This can be an initial UpdateSketch state if p < 1.0,
+   *                                   so change theta to 1.0. Return {Th = 1.0, 0, T}.
+   *                                   This is handled in UpdateSketch.compact() and toByteArray().
+   *  5   <1.0     0      F     OK     This can result from set operations
+   *  6   <1.0    !0      T   Error    Empty=T and curCount !0 should never coexist.
+   *                                   This is checked in all compacting operations.
+   *  7   <1.0    !0      F     OK     This corresponds to a sketch in estimation mode
+   * </pre>
+   * #4 is handled by <i>correctThetaOnCompat(boolean, int)</i> (below).
+   * #2 & #6 handled by <i>checkIllegalCurCountAndEmpty(boolean, int)</i>
+   */
+
+  /**
+   * This corrects a temporary anomalous condition where compact() is called on an UpdateSketch
+   * that was initialized with p < 1.0 and update() was never called.  In this case Theta < 1.0,
+   * curCount = 0, and empty = true.  The correction is to change Theta to 1.0, which makes the
+   * returning sketch empty. This should only be used in the compaction or serialization of an
+   * UpdateSketch.
+   * @param empty the given empty state
+   * @param curCount the given curCount
+   * @param thetaLong the given thetaLong
+   * @return thetaLong
+   */
+  static final long correctThetaOnCompact(final boolean empty, final int curCount,
+      final long thetaLong) { //handles #4 above
+    return (empty && (curCount == 0)) ? Long.MAX_VALUE : thetaLong;
+  }
+
+  /**
+   * This checks for the illegal condition where curCount > 0 and the state of
+   * empty = true.  This check can be used anywhere a sketch is returned or a sketch is created
+   * from complete arguments.
+   * @param empty the given empty state
+   * @param curCount the given current count
+   */ //This handles #2 and #6 above
+  static final void checkIllegalCurCountAndEmpty(final boolean empty, final int curCount) {
+    if (empty && (curCount != 0)) { //this handles #2 and #6 above
+      throw new SketchesStateException("Illegal State: Empty=true and Current Count != 0.");
+    }
+  }
+
+  /**
+   * This compute number of preamble longs for a compact sketch based on <i>empty</i>,
+   * <i>curCount</i> and <i>thetaLong</i>.
+   * This also accommodates for EmptyCompactSketch and SingleItemSketch.
+   * @param empty The given empty state
+   * @param curCount The given current count (retained entries)
+   * @param thetaLong the current thetaLong
+   * @return the number of preamble longs
+   */
+  static final int computeCompactPreLongs(final boolean empty, final int curCount,
+      final long thetaLong) {
+    return (thetaLong < Long.MAX_VALUE) ? 3 : empty ? 1 : (curCount > 1) ? 2 : 1;
+  }
+
+  /**
+   * This checks for the singleItem Compact Sketch.
+   * @param empty the given empty state
+   * @param curCount the given curCount
+   * @param thetaLong the given thetaLong
+   * @return true if notEmpty, curCount = 1 and theta = 1.0;
+   */
+  static final boolean isSingleItem(final boolean empty, final int curCount,
+      final long thetaLong) {
+    return !empty && (curCount == 1) && (thetaLong == Long.MAX_VALUE);
+  }
+}
+
diff --git a/src/main/java/org/apache/datasketches/theta2/CompactSketch.java b/src/main/java/org/apache/datasketches/theta2/CompactSketch.java
new file mode 100644
index 00000000..0498eed3
--- /dev/null
+++ b/src/main/java/org/apache/datasketches/theta2/CompactSketch.java
@@ -0,0 +1,478 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.datasketches.theta2;
+
+import static java.lang.foreign.ValueLayout.JAVA_BYTE;
+import static java.lang.foreign.ValueLayout.JAVA_LONG_UNALIGNED;
+import static java.lang.foreign.ValueLayout.JAVA_SHORT_UNALIGNED;
+import static org.apache.datasketches.common.ByteArrayUtil.getShortLE;
+import static org.apache.datasketches.common.Family.idToFamily;
+import static org.apache.datasketches.theta2.PreambleUtil.COMPACT_FLAG_MASK;
+import static org.apache.datasketches.theta2.PreambleUtil.EMPTY_FLAG_MASK;
+import static org.apache.datasketches.theta2.PreambleUtil.FLAGS_BYTE;
+import static org.apache.datasketches.theta2.PreambleUtil.ORDERED_FLAG_MASK;
+import static org.apache.datasketches.theta2.PreambleUtil.PREAMBLE_LONGS_BYTE;
+import static org.apache.datasketches.theta2.PreambleUtil.READ_ONLY_FLAG_MASK;
+import static org.apache.datasketches.theta2.PreambleUtil.SEED_HASH_SHORT;
+import static org.apache.datasketches.theta2.PreambleUtil.extractEntryBitsV4;
+import static org.apache.datasketches.theta2.PreambleUtil.extractFamilyID;
+import static org.apache.datasketches.theta2.PreambleUtil.extractFlags;
+import static org.apache.datasketches.theta2.PreambleUtil.extractNumEntriesBytesV4;
+import static org.apache.datasketches.theta2.PreambleUtil.extractPreLongs;
+import static org.apache.datasketches.theta2.PreambleUtil.extractSeedHash;
+import static org.apache.datasketches.theta2.PreambleUtil.extractSerVer;
+import static org.apache.datasketches.theta2.PreambleUtil.extractThetaLongV4;
+import static org.apache.datasketches.theta2.PreambleUtil.wholeBytesToHoldBits;
+import static org.apache.datasketches.theta2.SingleItemSketch.otherCheckForSingleItem;
+
+import java.lang.foreign.MemorySegment;
+
+import org.apache.datasketches.common.Family;
+import org.apache.datasketches.common.SketchesArgumentException;
+import org.apache.datasketches.thetacommon.ThetaUtil;
+
+/**
+ * The parent class of all the CompactSketches. CompactSketches are never created directly.
+ * They are created as a result of the compact() method of an UpdateSketch, a result of a
+ * getResult() of a SetOperation, or from a heapify method.
+ *
+ * <p>A CompactSketch is the simplest form of a Theta Sketch. It consists of a compact list
+ * (i.e., no intervening spaces) of hash values, which may be ordered or not, a value for theta
+ * and a seed hash.  A CompactSketch is immutable (read-only),
+ * and the space required when stored is only the space required for the hash values and 8 to 24
+ * bytes of preamble. An empty CompactSketch consumes only 8 bytes.</p>
+ *
+ * @author Lee Rhodes
+ */
+public abstract class CompactSketch extends Sketch {
+
+  /**
+   * Heapify takes a CompactSketch image in a MemorySegment and instantiates an on-heap CompactSketch.
+   *
+   * <p>The resulting sketch will not retain any link to the source MemorySegment and all of its data will be
+   * copied to the heap CompactSketch.</p>
+   *
+   * <p>This method assumes that the sketch image was created with the correct hash seed, so it is not checked.
+   * The resulting on-heap CompactSketch will be given the seedHash derived from the given sketch image.
+   * However, Serial Version 1 sketch images do not have a seedHash field,
+   * so the resulting heapified CompactSketch will be given the hash of the DEFAULT_UPDATE_SEED.</p>
+   *
+   * @param srcSeg an image of a CompactSketch.
+   * @return a CompactSketch on the heap.
+   */
+  public static CompactSketch heapify(final MemorySegment srcSeg) {
+    return heapify(srcSeg, ThetaUtil.DEFAULT_UPDATE_SEED, false);
+  }
+
+  /**
+   * Heapify takes a CompactSketch image in Memory and instantiates an on-heap CompactSketch.
+   *
+   * <p>The resulting sketch will not retain any link to the source Memory and all of its data will be
+   * copied to the heap CompactSketch.</p>
+   *
+   * <p>This method checks if the given expectedSeed was used to create the source Memory image.
+   * However, SerialVersion 1 sketch images cannot be checked as they don't have a seedHash field,
+   * so the resulting heapified CompactSketch will be given the hash of the expectedSeed.</p>
+   *
+   * @param srcSeg an image of a CompactSketch that was created using the given expectedSeed.
+   * <a href="{@docRoot}/resources/dictionary.html#mem">See Memory</a>.
+   * @param expectedSeed the seed used to validate the given Memory image.
+   * <a href="{@docRoot}/resources/dictionary.html#seed">See Update Hash Seed</a>.
+   * @return a CompactSketch on the heap.
+   */
+  public static CompactSketch heapify(final MemorySegment srcSeg, final long expectedSeed) {
+    return heapify(srcSeg, expectedSeed, true);
+  }
+
+  private static CompactSketch heapify(final MemorySegment srcSeg, final long seed, final boolean enforceSeed) {
+    final int serVer = extractSerVer(srcSeg);
+    final int familyID = extractFamilyID(srcSeg);
+    final Family family = idToFamily(familyID);
+    if (family != Family.COMPACT) {
+      throw new IllegalArgumentException("Corrupted: " + family + " is not Compact!");
+    }
+    if (serVer == 4) {
+       return heapifyV4(srcSeg, seed, enforceSeed);
+    }
+    if (serVer == 3) {
+      final int flags = extractFlags(srcSeg);
+      final boolean srcOrdered = (flags & ORDERED_FLAG_MASK) != 0;
+      final boolean empty = (flags & EMPTY_FLAG_MASK) != 0;
+      if (enforceSeed && !empty) { PreambleUtil.checkMemorySeedHash(srcSeg, seed); }
+      return CompactOperations.memoryToCompact(srcSeg, srcOrdered, null);
+    }
+    //not SerVer 3, assume compact stored form
+    final short seedHash = ThetaUtil.computeSeedHash(seed);
+    if (serVer == 1) {
+      return ForwardCompatibility.heapify1to3(srcSeg, seedHash);
+    }
+    if (serVer == 2) {
+      return ForwardCompatibility.heapify2to3(srcSeg,
+          enforceSeed ? seedHash : (short) extractSeedHash(srcSeg));
+    }
+    throw new SketchesArgumentException("Unknown Serialization Version: " + serVer);
+  }
+
+  /**
+   * Wrap takes the CompactSketch image in given MemorySegment and refers to it directly.
+   * There is no data copying onto the java heap.
+   * The wrap operation enables fast read-only merging and access to all the public read-only API.
+   *
+   * <p>Only "Direct" Serialization Version 3 (i.e, OpenSource) sketches that have
+   * been explicitly stored as direct sketches can be wrapped.
+   * Wrapping earlier serial version sketches will result in a heapify operation.
+   * These early versions were never designed to "wrap".</p>
+   *
+   * <p>Wrapping any subclass of this class that is empty or contains only a single item will
+   * result in heapified forms of empty and single item sketch respectively.
+   * This is actually faster and consumes less overall memory.</p>
+   *
+   * <p>This method assumes that the sketch image was created with the correct hash seed, so it is not checked.
+   * However, Serial Version 1 sketch images do not have a seedHash field,
+   * so the resulting on-heap CompactSketch will be given the hash of the DEFAULT_UPDATE_SEED.</p>
+   *
+   * @param srcSeg an image of a Sketch.
+   * @return a CompactSketch backed by the given MemorySegment except as above.
+   */
+  public static CompactSketch wrap(final MemorySegment srcSeg) {
+    return wrap(srcSeg, ThetaUtil.DEFAULT_UPDATE_SEED, false);
+  }
+
+  /**
+   * Wrap takes the sketch image in the given MemorySegment and refers to it directly.
+   * There is no data copying onto the java heap.
+   * The wrap operation enables fast read-only merging and access to all the public read-only API.
+   *
+   * <p>Only "Direct" Serialization Version 3 (i.e, OpenSource) sketches that have
+   * been explicitly stored as direct sketches can be wrapped.
+   * Wrapping earlier serial version sketches will result in a heapify operation.
+   * These early versions were never designed to "wrap".</p>
+   *
+   * <p>Wrapping any subclass of this class that is empty or contains only a single item will
+   * result in heapified forms of empty and single item sketch respectively.
+   * This is actually faster and consumes less overall memory.</p>
+   *
+   * <p>This method checks if the given expectedSeed was used to create the source MemorySegment image.
+   * However, SerialVersion 1 sketches cannot be checked as they don't have a seedHash field,
+   * so the resulting heapified CompactSketch will be given the hash of the expectedSeed.</p>
+   *
+   * @param srcSeg an image of a Sketch that was created using the given expectedSeed.
+   * @param expectedSeed the seed used to validate the given MemorySegment image.
+   * <a href="{@docRoot}/resources/dictionary.html#seed">See Update Hash Seed</a>.
+   * @return a CompactSketch backed by the given MemorySegment except as above.
+   */
+  public static CompactSketch wrap(final MemorySegment srcSeg, final long expectedSeed) {
+    return wrap(srcSeg, expectedSeed, true);
+  }
+
+  private static CompactSketch wrap(final MemorySegment srcSeg, final long seed, final boolean enforceSeed) {
+    final int serVer = extractSerVer(srcSeg);
+    final int familyID = extractFamilyID(srcSeg);
+    final Family family = Family.idToFamily(familyID);
+    if (family != Family.COMPACT) {
+      throw new IllegalArgumentException("Corrupted: " + family + " is not Compact!");
+    }
+    final short seedHash = ThetaUtil.computeSeedHash(seed);
+
+    if (serVer == 4) {
+      return DirectCompactCompressedSketch.wrapInstance(srcSeg,
+          enforceSeed ? seedHash : (short) extractSeedHash(srcSeg));
+    }
+    else if (serVer == 3) {
+      if (PreambleUtil.isEmptyFlag(srcSeg)) {
+        return EmptyCompactSketch.getHeapInstance(srcSeg);
+      }
+      if (otherCheckForSingleItem(srcSeg)) {
+        return SingleItemSketch.heapify(srcSeg, enforceSeed ? seedHash : (short) extractSeedHash(srcSeg));
+      }
+      //not empty & not singleItem
+      final int flags = extractFlags(srcSeg);
+      final boolean compactFlag = (flags & COMPACT_FLAG_MASK) > 0;
+      if (!compactFlag) {
+        throw new SketchesArgumentException(
+            "Corrupted: COMPACT family sketch image must have compact flag set");
+      }
+      final boolean readOnly = (flags & READ_ONLY_FLAG_MASK) > 0;
+      if (!readOnly) {
+        throw new SketchesArgumentException(
+            "Corrupted: COMPACT family sketch image must have Read-Only flag set");
+      }
+      return DirectCompactSketch.wrapInstance(srcSeg,
+          enforceSeed ? seedHash : (short) extractSeedHash(srcSeg));
+    } //end of serVer 3
+    else if (serVer == 1) {
+      return ForwardCompatibility.heapify1to3(srcSeg, seedHash);
+    }
+    else if (serVer == 2) {
+      return ForwardCompatibility.heapify2to3(srcSeg,
+          enforceSeed ? seedHash : (short) extractSeedHash(srcSeg));
+    }
+    throw new SketchesArgumentException(
+        "Corrupted: Serialization Version " + serVer + " not recognized.");
+  }
+
+  /**
+   * Wrap takes the sketch image in the given MemorySegment and refers to it directly.
+   * There is no data copying onto the java heap.
+   * The wrap operation enables fast read-only merging and access to all the public read-only API.
+   *
+   * <p>Only "Direct" Serialization Version 3 (i.e, OpenSource) sketches that have
+   * been explicitly stored as direct sketches can be wrapped.
+   * Wrapping earlier serial version sketches will result in a heapify operation.
+   * These early versions were never designed to "wrap".</p>
+   *
+   * <p>Wrapping any subclass of this class that is empty or contains only a single item will
+   * result in heapified forms of empty and single item sketch respectively.
+   * This is actually faster and consumes less overall memory.</p>
+   *
+   * <p>This method checks if the DEFAULT_UPDATE_SEED was used to create the source MemorySegment image.
+   * Note that SerialVersion 1 sketches cannot be checked as they don't have a seedHash field,
+   * so the resulting heapified CompactSketch will be given the hash of DEFAULT_UPDATE_SEED.</p>
+   *
+   * @param bytes a byte array image of a Sketch that was created using the DEFAULT_UPDATE_SEED.
+   *
+   * @return a CompactSketch backed by the given MemorySegment except as above.
+   */
+  public static CompactSketch wrap(final byte[] bytes) {
+    return wrap(bytes, ThetaUtil.DEFAULT_UPDATE_SEED, false);
+  }
+
+  /**
+   * Wrap takes the sketch image in the given MemorySegment and refers to it directly.
+   * There is no data copying onto the java heap.
+   * The wrap operation enables fast read-only merging and access to all the public read-only API.
+   *
+   * <p>Only "Direct" Serialization Version 3 (i.e, OpenSource) sketches that have
+   * been explicitly stored as direct sketches can be wrapped.
+   * Wrapping earlier serial version sketches will result in a heapify operation.
+   * These early versions were never designed to "wrap".</p>
+   *
+   * <p>Wrapping any subclass of this class that is empty or contains only a single item will
+   * result in heapified forms of empty and single item sketch respectively.
+   * This is actually faster and consumes less overall memory.</p>
+   *
+   * <p>This method checks if the given expectedSeed was used to create the source MemorySegment image.
+   * Note that SerialVersion 1 sketches cannot be checked as they don't have a seedHash field,
+   * so the resulting heapified CompactSketch will be given the hash of the expectedSeed.</p>
+   *
+   * @param bytes a byte array image of a Sketch that was created using the given expectedSeed.
+   * @param expectedSeed the seed used to validate the given MemorySegment image.
+   * <a href="{@docRoot}/resources/dictionary.html#seed">See Update Hash Seed</a>.
+   * @return a CompactSketch backed by the given MemorySegment except as above.
+   */
+  public static CompactSketch wrap(final byte[] bytes, final long expectedSeed) {
+    return wrap(bytes, expectedSeed, true);
+  }
+
+  private static CompactSketch wrap(final byte[] bytes, final long seed, final boolean enforceSeed) {
+    final int serVer = bytes[PreambleUtil.SER_VER_BYTE];
+    final int familyId = bytes[PreambleUtil.FAMILY_BYTE];
+    final Family family = Family.idToFamily(familyId);
+    if (family != Family.COMPACT) {
+      throw new IllegalArgumentException("Corrupted: " + family + " is not Compact!");
+    }
+    final short seedHash = ThetaUtil.computeSeedHash(seed);
+    if (serVer == 4) {
+      return WrappedCompactCompressedSketch.wrapInstance(bytes, seedHash);
+    } else if (serVer == 3) {
+      final int flags = bytes[FLAGS_BYTE];
+      if ((flags & EMPTY_FLAG_MASK) > 0) {
+        return EmptyCompactSketch.getHeapInstance(MemorySegment.ofArray(bytes));
+      }
+      final int preLongs = bytes[PREAMBLE_LONGS_BYTE];
+      if (otherCheckForSingleItem(preLongs, serVer, familyId, flags)) {
+        return SingleItemSketch.heapify(MemorySegment.ofArray(bytes), enforceSeed ? seedHash : getShortLE(bytes, SEED_HASH_SHORT));
+      }
+      //not empty & not singleItem
+      final boolean compactFlag = (flags & COMPACT_FLAG_MASK) > 0;
+      if (!compactFlag) {
+        throw new SketchesArgumentException(
+            "Corrupted: COMPACT family sketch image must have compact flag set");
+      }
+      final boolean readOnly = (flags & READ_ONLY_FLAG_MASK) > 0;
+      if (!readOnly) {
+        throw new SketchesArgumentException(
+            "Corrupted: COMPACT family sketch image must have Read-Only flag set");
+      }
+      return WrappedCompactSketch.wrapInstance(bytes,
+          enforceSeed ? seedHash : getShortLE(bytes, SEED_HASH_SHORT));
+    } else if (serVer == 1) {
+      return ForwardCompatibility.heapify1to3(MemorySegment.ofArray(bytes), seedHash);
+    } else if (serVer == 2) {
+      return ForwardCompatibility.heapify2to3(MemorySegment.ofArray(bytes),
+          enforceSeed ? seedHash : getShortLE(bytes, SEED_HASH_SHORT));
+    }
+    throw new SketchesArgumentException(
+        "Corrupted: Serialization Version " + serVer + " not recognized.");
+  }
+
+  //Sketch Overrides
+
+  @Override
+  public abstract CompactSketch compact(final boolean dstOrdered, final MemorySegment dstMem);
+
+  @Override
+  public int getCompactBytes() {
+    return getCurrentBytes();
+  }
+
+  @Override
+  int getCurrentDataLongs() {
+    return getRetainedEntries(true);
+  }
+
+  @Override
+  public Family getFamily() {
+    return Family.COMPACT;
+  }
+
+  @Override
+  public boolean isCompact() {
+    return true;
+  }
+
+  @Override
+  public double getEstimate() {
+    return Sketch.estimate(getThetaLong(), getRetainedEntries());
+  }
+
+  /**
+   * gets the sketch as a compressed byte array
+   * @return the sketch as a compressed byte array
+   */
+  public byte[] toByteArrayCompressed() {
+    if (!isOrdered() || getRetainedEntries() == 0 || (getRetainedEntries() == 1 && !isEstimationMode())) {
+      return toByteArray();
+    }
+    return toByteArrayV4();
+  }
+
+  private int computeMinLeadingZeros() {
+    // compression is based on leading zeros in deltas between ordered hash values
+    // assumes ordered sketch
+    long previous = 0;
+    long ored = 0;
+    final HashIterator it = iterator();
+    while (it.next()) {
+      final long delta = it.get() - previous;
+      ored |= delta;
+      previous = it.get();
+    }
+    return Long.numberOfLeadingZeros(ored);
+  }
+
+  private byte[] toByteArrayV4() {
+    final int preambleLongs = isEstimationMode() ? 2 : 1;
+    final int entryBits = 64 - computeMinLeadingZeros();
+    final int compressedBits = entryBits * getRetainedEntries();
+
+    // store num_entries as whole bytes since whole-byte blocks will follow (most probably)
+    final int numEntriesBytes = wholeBytesToHoldBits(32 - Integer.numberOfLeadingZeros(getRetainedEntries()));
+
+    final int sizeBytes = preambleLongs * Long.BYTES + numEntriesBytes + wholeBytesToHoldBits(compressedBits);
+    final byte[] bytes = new byte[sizeBytes];
+    final MemorySegment wseg = MemorySegment.ofArray(bytes);
+    int offsetBytes = 0;
+    wseg.set(JAVA_BYTE, offsetBytes++, (byte) preambleLongs);
+    wseg.set(JAVA_BYTE, offsetBytes++, (byte) 4); // to do: add constant
+    wseg.set(JAVA_BYTE, offsetBytes++, (byte) Family.COMPACT.getID());
+    wseg.set(JAVA_BYTE, offsetBytes++, (byte) entryBits);
+    wseg.set(JAVA_BYTE, offsetBytes++, (byte) numEntriesBytes);
+    wseg.set(JAVA_BYTE, offsetBytes++, (byte) (COMPACT_FLAG_MASK | READ_ONLY_FLAG_MASK | ORDERED_FLAG_MASK));
+    wseg.set(JAVA_SHORT_UNALIGNED, offsetBytes, getSeedHash());
+    offsetBytes += Short.BYTES;
+    if (isEstimationMode()) {
+      wseg.set(JAVA_LONG_UNALIGNED, offsetBytes, getThetaLong());
+      offsetBytes += Long.BYTES;
+    }
+    int numEntries = getRetainedEntries();
+    for (int i = 0; i < numEntriesBytes; i++) {
+      wseg.set(JAVA_BYTE, offsetBytes++, (byte) (numEntries & 0xff));
+      numEntries >>>= 8;
+    }
+    long previous = 0;
+    final long[] deltas = new long[8];
+    final HashIterator it = iterator();
+    int i;
+    for (i = 0; i + 7 < getRetainedEntries(); i += 8) {
+      for (int j = 0; j < 8; j++) {
+        it.next();
+        deltas[j] = it.get() - previous;
+        previous = it.get();
+      }
+      BitPacking.packBitsBlock8(deltas, 0, bytes, offsetBytes, entryBits);
+      offsetBytes += entryBits;
+    }
+    int offsetBits = 0;
+    for (; i < getRetainedEntries(); i++) {
+      it.next();
+      final long delta = it.get() - previous;
+      previous = it.get();
+      BitPacking.packBits(delta, entryBits, bytes, offsetBytes, offsetBits);
+      offsetBytes += (offsetBits + entryBits) >>> 3;
+      offsetBits = (offsetBits + entryBits) & 7;
+    }
+    return bytes;
+  }
+
+  private static CompactSketch heapifyV4(final MemorySegment srcSeg, final long seed, final boolean enforceSeed) {
+    final int preLongs = extractPreLongs(srcSeg);
+    final int entryBits = extractEntryBitsV4(srcSeg);
+    final int numEntriesBytes = extractNumEntriesBytesV4(srcSeg);
+    final short seedHash = (short) extractSeedHash(srcSeg);
+    if (enforceSeed) { PreambleUtil.checkMemorySeedHash(srcSeg, seed); }
+    int offsetBytes = 8;
+    long theta = Long.MAX_VALUE;
+    if (preLongs > 1) {
+      theta = extractThetaLongV4(srcSeg);
+      offsetBytes += Long.BYTES;
+    }
+    int numEntries = 0;
+    for (int i = 0; i < numEntriesBytes; i++) {
+      numEntries |= Byte.toUnsignedInt(srcSeg.get(JAVA_BYTE, offsetBytes++)) << (i << 3);
+    }
+    final long[] entries = new long[numEntries];
+    final byte[] bytes = new byte[entryBits]; // temporary buffer for unpacking
+    int i;
+    for (i = 0; i + 7 < numEntries; i += 8) {
+      MemorySegment.copy(srcSeg, JAVA_BYTE, offsetBytes, bytes, 0, entryBits);
+      BitPacking.unpackBitsBlock8(entries, i, bytes, 0, entryBits);
+      offsetBytes += entryBits;
+    }
+    if (i < numEntries) {
+      MemorySegment.copy(srcSeg, JAVA_BYTE, offsetBytes, bytes, 0, wholeBytesToHoldBits((numEntries - i) * entryBits));
+      int offsetBits = 0;
+      offsetBytes = 0;
+      for (; i < numEntries; i++) {
+        BitPacking.unpackBits(entries, i, entryBits, bytes, offsetBytes, offsetBits);
+        offsetBytes += (offsetBits + entryBits) >>> 3;
+        offsetBits = (offsetBits + entryBits) & 7;
+      }
+    }
+    // undo deltas
+    long previous = 0;
+    for (i = 0; i < numEntries; i++) {
+      entries[i] += previous;
+      previous = entries[i];
+    }
+    return new HeapCompactSketch(entries, false, seedHash, numEntries, theta, true);
+  }
+
+}
diff --git a/src/main/java/org/apache/datasketches/theta2/ConcurrentBackgroundThetaPropagation.java b/src/main/java/org/apache/datasketches/theta2/ConcurrentBackgroundThetaPropagation.java
new file mode 100644
index 00000000..2d529c4c
--- /dev/null
+++ b/src/main/java/org/apache/datasketches/theta2/ConcurrentBackgroundThetaPropagation.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.datasketches.theta2;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Background propagation thread. Propagates a given sketch or a hash value from local threads
+ * buffers into the shared sketch which stores the most up-to-date estimation of number of unique
+ * items. This propagation is done at the background by dedicated threads, which allows
+ * application threads to continue updating their local buffer.
+ *
+ * @author eshcar
+ */
+class ConcurrentBackgroundThetaPropagation implements Runnable {
+
+  // Shared sketch to absorb the data
+  private final ConcurrentSharedThetaSketch sharedThetaSketch;
+
+  // Propagation flag of local buffer that is being processed.
+  // It is the synchronization primitive to coordinate the work of the propagation with the
+  // local buffer.  Updated when the propagation completes.
+  private final AtomicBoolean localPropagationInProgress;
+
+  // Sketch to be propagated to shared sketch. Can be null if only a single hash is propagated
+  private final Sketch sketchIn;
+
+  // Hash of the datum to be propagated to shared sketch. Can be ConcurrentSharedThetaSketch.NOT_SINGLE_HASH
+  // if the data is propagated through a sketch.
+  private final long singleHash;
+
+  // The propagation epoch. The data can be propagated only within the context of this epoch.
+  // The data should not be propagated if this epoch is not equal to the
+  // shared sketch epoch.
+  private final long epoch;
+
+  ConcurrentBackgroundThetaPropagation(final ConcurrentSharedThetaSketch sharedThetaSketch,
+      final AtomicBoolean localPropagationInProgress, final Sketch sketchIn, final long singleHash,
+      final long epoch) {
+    this.sharedThetaSketch = sharedThetaSketch;
+    this.localPropagationInProgress = localPropagationInProgress;
+    this.sketchIn = sketchIn;
+    this.singleHash = singleHash;
+    this.epoch = epoch;
+  }
+
+  /**
+   * Propagation protocol:
+   * 1) validate propagation is executed at the context of the right epoch, otherwise abort
+   * 2) handle propagation: either of a single hash or of a sketch
+   * 3) complete propagation: ping local buffer
+   */
+  @Override
+  public void run() {
+    // 1) validate propagation is executed at the context of the right epoch, otherwise abort
+    if (!sharedThetaSketch.validateEpoch(epoch)) {
+      // invalid epoch - should not propagate
+      sharedThetaSketch.endPropagation(null, false);
+      return;
+    }
+
+    // 2) handle propagation: either of a single hash or of a sketch
+    if (singleHash != ConcurrentSharedThetaSketch.NOT_SINGLE_HASH) {
+      sharedThetaSketch.propagate(singleHash);
+    } else if (sketchIn != null) {
+      final long volTheta = sharedThetaSketch.getVolatileTheta();
+      assert volTheta <= sketchIn.getThetaLong() :
+          "volTheta = " + volTheta + ", bufTheta = " + sketchIn.getThetaLong();
+
+      // propagate values from input sketch one by one
+      final long[] cacheIn = sketchIn.getCache();
+
+      if (sketchIn.isOrdered()) { //Ordered compact, Use early stop
+        for (final long hashIn : cacheIn) {
+          if (hashIn >= volTheta) {
+            break; //early stop
+          }
+          sharedThetaSketch.propagate(hashIn);
+        }
+      } else { //not ordered, also may have zeros (gaps) in the array.
+        for (final long hashIn : cacheIn) {
+          if (hashIn > 0) {
+            sharedThetaSketch.propagate(hashIn);
+          }
+        }
+      }
+    }
+
+    // 3) complete propagation: ping local buffer
+    sharedThetaSketch.endPropagation(localPropagationInProgress, false);
+  }
+
+}
diff --git a/src/main/java/org/apache/datasketches/theta2/ConcurrentDirectQuickSelectSketch.java b/src/main/java/org/apache/datasketches/theta2/ConcurrentDirectQuickSelectSketch.java
new file mode 100644
index 00000000..af591712
--- /dev/null
+++ b/src/main/java/org/apache/datasketches/theta2/ConcurrentDirectQuickSelectSketch.java
@@ -0,0 +1,270 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.datasketches.theta2;
+
+import static java.lang.foreign.ValueLayout.JAVA_LONG_UNALIGNED;
+import static org.apache.datasketches.theta2.PreambleUtil.THETA_LONG;
+
+import java.lang.foreign.MemorySegment;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.datasketches.common.ResizeFactor;
+import org.apache.datasketches.common.SuppressFBWarnings;
+
+/**
+ * A concurrent shared sketch that is based on DirectQuickSelectSketch.
+ * It reflects all data processed by a single or multiple update threads, and can serve queries at
+ * any time.
+ * Background propagation threads are used to propagate data from thread local buffers into this
+ * sketch which stores the most up-to-date estimation of number of unique items.
+ *
+ * @author eshcar
+ * @author Lee Rhodes
+ */
+final class ConcurrentDirectQuickSelectSketch extends DirectQuickSelectSketch
+    implements ConcurrentSharedThetaSketch {
+
+  // The propagation thread
+  private ExecutorService executorService_;
+
+  // A flag to coordinate between several eager propagation threads
+  private final AtomicBoolean sharedPropagationInProgress_;
+
+  // Theta value of concurrent sketch
+  private volatile long volatileThetaLong_;
+
+  // A snapshot of the estimated number of unique entries
+  private volatile double volatileEstimate_;
+
+  // Num of retained entries in which the sketch toggles from sync (exact) mode to async
+  //  propagation mode
+  private final long exactLimit_;
+
+  // An epoch defines an interval between two resets. A propagation invoked at epoch i cannot
+  // affect the sketch at epoch j > i.
+  private volatile long epoch_;
+
+  /**
+   * Construct a new sketch instance and initialize the given Memory as its backing store.
+   *
+   * @param lgNomLongs <a href="{@docRoot}/resources/dictionary.html#lgNomLongs">See lgNomLongs</a>.
+   * @param seed       <a href="{@docRoot}/resources/dictionary.html#seed">See Update Hash Seed</a>.
+   * @param maxConcurrencyError the max error value including error induced by concurrency.
+   * @param dstSeg     the given MemorySegment object destination. It cannot be null.
+   */
+  ConcurrentDirectQuickSelectSketch(final int lgNomLongs, final long seed,
+      final double maxConcurrencyError, final MemorySegment dstSeg) {
+    super(lgNomLongs, seed, 1.0F, //p
+      ResizeFactor.X1, //rf,
+      dstSeg, false); //unionGadget
+
+    volatileThetaLong_ = Long.MAX_VALUE;
+    volatileEstimate_ = 0;
+    exactLimit_ = ConcurrentSharedThetaSketch.computeExactLimit(1L << getLgNomLongs(),
+        maxConcurrencyError);
+    sharedPropagationInProgress_ = new AtomicBoolean(false);
+    epoch_ = 0;
+    initBgPropagationService();
+  }
+
+  ConcurrentDirectQuickSelectSketch(final UpdateSketch sketch, final long seed,
+      final double maxConcurrencyError, final MemorySegment dstSeg) {
+    super(sketch.getLgNomLongs(), seed, 1.0F, //p
+        ResizeFactor.X1, //rf,
+        dstSeg,
+        false); //unionGadget
+
+    exactLimit_ = ConcurrentSharedThetaSketch.computeExactLimit(1L << getLgNomLongs(),
+        maxConcurrencyError);
+    sharedPropagationInProgress_ = new AtomicBoolean(false);
+    epoch_ = 0;
+    initBgPropagationService();
+    for (final long hashIn : sketch.getCache()) {
+      propagate(hashIn);
+    }
+    wseg_.set(JAVA_LONG_UNALIGNED, THETA_LONG, sketch.getThetaLong());
+    updateVolatileTheta();
+    updateEstimationSnapshot();
+  }
+
+  //Sketch overrides
+
+  @Override
+  public double getEstimate() {
+    return volatileEstimate_;
+  }
+
+  @Override
+  public boolean isEstimationMode() {
+    return (getRetainedEntries(false) > exactLimit_) || super.isEstimationMode();
+  }
+
+  @Override
+  public byte[] toByteArray() {
+    while (!sharedPropagationInProgress_.compareAndSet(false, true)) { } //busy wait till free
+    final byte[] res = super.toByteArray();
+    sharedPropagationInProgress_.set(false);
+    return res;
+  }
+
+  //UpdateSketch overrides
+
+  @Override
+  public UpdateSketch rebuild() {
+    super.rebuild();
+    updateEstimationSnapshot();
+    return this;
+  }
+
+  /**
+   * {@inheritDoc}
+   * Takes care of mutual exclusion with propagation thread.
+   */
+  @Override
+  public void reset() {
+    advanceEpoch();
+    super.reset();
+    volatileThetaLong_ = Long.MAX_VALUE;
+    volatileEstimate_ = 0;
+  }
+
+  @Override
+  UpdateReturnState hashUpdate(final long hash) {
+    final String msg = "No update method should be called directly to a shared theta sketch."
+        + " Updating the shared sketch is only permitted through propagation from local sketches.";
+    throw new UnsupportedOperationException(msg);
+  }
+
+  //ConcurrentSharedThetaSketch declarations
+
+  @Override
+  public long getExactLimit() {
+    return exactLimit_;
+  }
+
+  @Override
+  public boolean startEagerPropagation() {
+    while (!sharedPropagationInProgress_.compareAndSet(false, true)) { } //busy wait till free
+    return (!isEstimationMode());// no eager propagation is allowed in estimation mode
+  }
+
+  @Override
+  public void endPropagation(final AtomicBoolean localPropagationInProgress, final boolean isEager) {
+    //update volatile theta, uniques estimate and propagation flag
+    updateVolatileTheta();
+    updateEstimationSnapshot();
+    if (isEager) {
+      sharedPropagationInProgress_.set(false);
+    }
+    if (localPropagationInProgress != null) {
+      localPropagationInProgress.set(false); //clear local propagation flag
+    }
+  }
+
+  @Override
+  public long getVolatileTheta() {
+    return volatileThetaLong_;
+  }
+
+  @Override
+  public void awaitBgPropagationTermination() {
+    try {
+      executorService_.shutdown();
+      while (!executorService_.awaitTermination(1, TimeUnit.MILLISECONDS)) {
+        Thread.sleep(1);
+      }
+    } catch (final InterruptedException e) {
+      e.printStackTrace();
+    }
+  }
+
+  @Override
+  public final void initBgPropagationService() {
+    executorService_ = ConcurrentPropagationService.getExecutorService(Thread.currentThread().threadId());
+  }
+
+  @Override
+  public boolean propagate(final AtomicBoolean localPropagationInProgress,
+                           final Sketch sketchIn, final long singleHash) {
+    final long epoch = epoch_;
+    if ((singleHash != NOT_SINGLE_HASH)                   // namely, is a single hash and
+        && (getRetainedEntries(false) < exactLimit_)) {   // a small sketch then propagate myself (blocking)
+      if (!startEagerPropagation()) {
+        endPropagation(localPropagationInProgress, true);
+        return false;
+      }
+      if (!validateEpoch(epoch)) {
+        endPropagation(null, true); // do not change local flag
+        return true;
+      }
+      propagate(singleHash);
+      endPropagation(localPropagationInProgress, true);
+      return true;
+    }
+    // otherwise, be nonblocking, let background thread do the work
+    final ConcurrentBackgroundThetaPropagation job = new ConcurrentBackgroundThetaPropagation(
+        this, localPropagationInProgress, sketchIn, singleHash, epoch);
+    executorService_.execute(job);
+    return true;
+  }
+
+  @Override
+  public void propagate(final long singleHash) {
+    super.hashUpdate(singleHash);
+  }
+
+  @Override
+  public void updateEstimationSnapshot() {
+    volatileEstimate_ = super.getEstimate();
+  }
+
+  @Override
+  public void updateVolatileTheta() {
+    volatileThetaLong_ = getThetaLong();
+  }
+
+  @Override
+  public boolean validateEpoch(final long epoch) {
+    return epoch_ == epoch;
+  }
+
+  //Restricted
+
+  /**
+   * Advances the epoch while there is no background propagation
+   * This ensures a propagation invoked before the reset cannot affect the sketch after the reset
+   * is completed. Ignore VO_VOLATILE_INCREMENT findbugs warning, it is False Positive.
+   */
+  @SuppressFBWarnings(value = "VO_VOLATILE_INCREMENT", justification = "Likely False Positive, Fix Later")
+  private void advanceEpoch() {
+    awaitBgPropagationTermination();
+    startEagerPropagation();
+    ConcurrentPropagationService.resetExecutorService(Thread.currentThread().threadId());
+    //no inspection NonAtomicOperationOnVolatileField
+    // this increment of a volatile field is done within the scope of the propagation
+    // synchronization and hence is done by a single thread.
+    epoch_++;
+    endPropagation(null, true);
+    initBgPropagationService();
+  }
+
+}
diff --git a/src/main/java/org/apache/datasketches/theta2/ConcurrentHeapQuickSelectSketch.java b/src/main/java/org/apache/datasketches/theta2/ConcurrentHeapQuickSelectSketch.java
new file mode 100644
index 00000000..56e254b5
--- /dev/null
+++ b/src/main/java/org/apache/datasketches/theta2/ConcurrentHeapQuickSelectSketch.java
@@ -0,0 +1,266 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.datasketches.theta2;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.datasketches.common.ResizeFactor;
+import org.apache.datasketches.common.SuppressFBWarnings;
+
+/**
+ * A concurrent shared sketch that is based on HeapQuickSelectSketch.
+ * It reflects all data processed by a single or multiple update threads, and can serve queries at
+ * any time.
+ * Background propagation threads are used to propagate data from thread local buffers into this
+ * sketch which stores the most up-to-date estimation of number of unique items.
+ *
+ * @author eshcar
+ * @author Lee Rhodes
+ */
+final class ConcurrentHeapQuickSelectSketch extends HeapQuickSelectSketch
+    implements ConcurrentSharedThetaSketch {
+
+  // The propagation thread
+  private volatile ExecutorService executorService_;
+
+  //A flag to coordinate between several eager propagation threads
+  private final AtomicBoolean sharedPropagationInProgress_;
+
+  // Theta value of concurrent sketch
+  private volatile long volatileThetaLong_;
+
+  // A snapshot of the estimated number of unique entries
+  private volatile double volatileEstimate_;
+
+  // Num of retained entries in which the sketch toggles from sync (exact) mode to async
+  //  propagation mode
+  private final long exactLimit_;
+
+  // An epoch defines an interval between two resets. A propagation invoked at epoch i cannot
+  // affect the sketch at epoch j > i.
+  private volatile long epoch_;
+
+  /**
+   * Construct a new sketch instance on the java heap.
+   *
+   * @param lgNomLongs <a href="{@docRoot}/resources/dictionary.html#lgNomLogs">See lgNomLongs</a>.
+   * @param seed       <a href="{@docRoot}/resources/dictionary.html#seed">See seed</a>
+   * @param maxConcurrencyError the max error value including error induced by concurrency
+   *
+   */
+  ConcurrentHeapQuickSelectSketch(final int lgNomLongs, final long seed,
+      final double maxConcurrencyError) {
+    super(lgNomLongs, seed, 1.0F, //p
+        ResizeFactor.X1, //rf,
+        false); //unionGadget
+
+    volatileThetaLong_ = Long.MAX_VALUE;
+    volatileEstimate_ = 0;
+    exactLimit_ = ConcurrentSharedThetaSketch.computeExactLimit(1L << getLgNomLongs(),
+        maxConcurrencyError);
+    sharedPropagationInProgress_ = new AtomicBoolean(false);
+    epoch_ = 0;
+    initBgPropagationService();
+  }
+
+  ConcurrentHeapQuickSelectSketch(final UpdateSketch sketch, final long seed,
+      final double maxConcurrencyError) {
+    super(sketch.getLgNomLongs(), seed, 1.0F, //p
+        ResizeFactor.X1, //rf,
+        false); //unionGadget
+
+    exactLimit_ = ConcurrentSharedThetaSketch.computeExactLimit(1L << getLgNomLongs(),
+        maxConcurrencyError);
+    sharedPropagationInProgress_ = new AtomicBoolean(false);
+    epoch_ = 0;
+    initBgPropagationService();
+    for (final long hashIn : sketch.getCache()) {
+      propagate(hashIn);
+    }
+    thetaLong_ = sketch.getThetaLong();
+    updateVolatileTheta();
+    updateEstimationSnapshot();
+  }
+
+  //Sketch overrides
+
+  @Override
+  public double getEstimate() {
+    return volatileEstimate_;
+  }
+
+  @Override
+  public boolean isEstimationMode() {
+    return (getRetainedEntries(false) > exactLimit_) || super.isEstimationMode();
+  }
+
+  @Override
+  public byte[] toByteArray() {
+    while (!sharedPropagationInProgress_.compareAndSet(false, true)) { } //busy wait till free
+    final byte[] res = super.toByteArray();
+    sharedPropagationInProgress_.set(false);
+    return res;
+  }
+
+  //UpdateSketch overrides
+
+  @Override
+  public UpdateSketch rebuild() {
+    super.rebuild();
+    updateEstimationSnapshot();
+    return this;
+  }
+
+  /**
+   * {@inheritDoc}
+   * Takes care of mutual exclusion with propagation thread.
+   */
+  @Override
+  public void reset() {
+    advanceEpoch();
+    super.reset();
+    volatileThetaLong_ = Long.MAX_VALUE;
+    volatileEstimate_ = 0;
+  }
+
+  @Override
+  UpdateReturnState hashUpdate(final long hash) {
+    final String msg = "No update method should be called directly to a shared theta sketch."
+        + " Updating the shared sketch is only permitted through propagation from local sketches.";
+    throw new UnsupportedOperationException(msg);
+  }
+
+  //ConcurrentSharedThetaSketch declarations
+
+  @Override
+  public long getExactLimit() {
+    return exactLimit_;
+  }
+
+  @Override
+  public boolean startEagerPropagation() {
+    while (!sharedPropagationInProgress_.compareAndSet(false, true)) { } //busy wait till free
+    return (!isEstimationMode());// no eager propagation is allowed in estimation mode
+  }
+
+  @Override
+  public void endPropagation(final AtomicBoolean localPropagationInProgress, final boolean isEager) {
+    //update volatile theta, uniques estimate and propagation flag
+    updateVolatileTheta();
+    updateEstimationSnapshot();
+    if (isEager) {
+      sharedPropagationInProgress_.set(false);
+    }
+    if (localPropagationInProgress != null) {
+      localPropagationInProgress.set(false); //clear local propagation flag
+    }
+  }
+
+  @Override
+  public long getVolatileTheta() {
+    return volatileThetaLong_;
+  }
+
+  @Override
+  public void awaitBgPropagationTermination() {
+    try {
+      executorService_.shutdown();
+      while (!executorService_.awaitTermination(1, TimeUnit.MILLISECONDS)) {
+        Thread.sleep(1);
+      }
+    } catch (final InterruptedException e) {
+      e.printStackTrace();
+    }
+  }
+
+  @Override
+  public void initBgPropagationService() {
+    executorService_ = ConcurrentPropagationService.getExecutorService(Thread.currentThread().threadId());
+  }
+
+  @Override
+  public boolean propagate(final AtomicBoolean localPropagationInProgress,
+                           final Sketch sketchIn, final long singleHash) {
+    final long epoch = epoch_;
+    if ((singleHash != NOT_SINGLE_HASH)                 //namely, is a single hash and
+        && (getRetainedEntries(false) < exactLimit_)) { //a small sketch then propagate myself (blocking)
+      if (!startEagerPropagation()) {
+        endPropagation(localPropagationInProgress, true);
+        return false;
+      }
+      if (!validateEpoch(epoch)) {
+        endPropagation(null, true); // do not change local flag
+        return true;
+      }
+      propagate(singleHash);
+      endPropagation(localPropagationInProgress, true);
+      return true;
+    }
+    // otherwise, be nonblocking, let background thread do the work
+    final ConcurrentBackgroundThetaPropagation job = new ConcurrentBackgroundThetaPropagation(
+        this, localPropagationInProgress, sketchIn, singleHash, epoch);
+    executorService_.execute(job);
+    return true;
+  }
+
+  @Override
+  public void propagate(final long singleHash) {
+    super.hashUpdate(singleHash);
+  }
+
+  @Override
+  public void updateEstimationSnapshot() {
+    volatileEstimate_ = super.getEstimate();
+  }
+
+  @Override
+  public void updateVolatileTheta() {
+    volatileThetaLong_ = getThetaLong();
+  }
+
+  @Override
+  public boolean validateEpoch(final long epoch) {
+    return epoch_ == epoch;
+  }
+
+  //Restricted
+
+  /**
+   * Advances the epoch while there is no background propagation
+   * This ensures a propagation invoked before the reset cannot affect the sketch after the reset
+   * is completed.
+   */
+  @SuppressFBWarnings(value = "VO_VOLATILE_INCREMENT", justification = "Likely False Positive, Fix Later")
+  private void advanceEpoch() {
+    awaitBgPropagationTermination();
+    startEagerPropagation();
+    ConcurrentPropagationService.resetExecutorService(Thread.currentThread().threadId());
+    //no inspection NonAtomicOperationOnVolatileField
+    // this increment of a volatile field is done within the scope of the propagation
+    // synchronization and hence is done by a single thread
+    // Ignore a FindBugs warning
+    epoch_++;
+    endPropagation(null, true);
+    initBgPropagationService();
+  }
+
+}
diff --git a/src/main/java/org/apache/datasketches/theta2/ConcurrentHeapThetaBuffer.java b/src/main/java/org/apache/datasketches/theta2/ConcurrentHeapThetaBuffer.java
new file mode 100644
index 00000000..ab1d41a6
--- /dev/null
+++ b/src/main/java/org/apache/datasketches/theta2/ConcurrentHeapThetaBuffer.java
@@ -0,0 +1,223 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.datasketches.theta2;
+
+import static org.apache.datasketches.theta2.UpdateReturnState.ConcurrentBufferInserted;
+import static org.apache.datasketches.theta2.UpdateReturnState.ConcurrentPropagated;
+import static org.apache.datasketches.theta2.UpdateReturnState.RejectedOverTheta;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.datasketches.common.ResizeFactor;
+import org.apache.datasketches.thetacommon.HashOperations;
+
+/**
+ * This is a theta filtering, bounded size buffer that operates in the context of a single writing
+ * thread.  When the buffer becomes full its content is propagated into the shared sketch, which
+ * may be on a different thread. The limit on the buffer size is configurable. A bound of size 1
+ * allows the combination of buffers and shared sketch to maintain an error bound in real-time
+ * that is close to the error bound of a sequential theta sketch.  Allowing larger buffer sizes
+ * enables amortization of the cost propagations and substantially improves overall system throughput.
+ * The error caused by the buffering is essentially a perspective of time and synchronization
+ * and not really a true error.  At the end of a stream, after all the buffers have synchronized with
+ * the shared sketch, there is no additional error.
+ * Propagation is done either synchronously by the updating thread, or asynchronously by a
+ * background propagation thread.
+ *
+ * <p>This is a buffer, not a sketch, and it extends the <i>HeapQuickSelectSketch</i>
+ * in order to leverage some of the sketch machinery to make its work simple. However, if this
+ * buffer receives a query, like <i>getEstimate()</i>, the correct answer does not come from the super
+ * <i>HeapQuickSelectSketch</i>, which knows nothing about the concurrency relationship to the
+ * shared concurrent sketch, it must come from the shared concurrent sketch. As a result nearly all
+ * of the inherited sketch methods are redirected to the shared concurrent sketch.
+ *
+ * @author eshcar
+ * @author Lee Rhodes
+ */
+final class ConcurrentHeapThetaBuffer extends HeapQuickSelectSketch {
+
+  // Shared sketch consisting of the global sample set and theta value.
+  private final ConcurrentSharedThetaSketch shared;
+
+  // A flag indicating whether the shared sketch is in shared mode and requires eager propagation
+  // Initially this is true. Once it is set to false (estimation mode) it never flips back.
+  private boolean isExactMode;
+
+  // A flag to indicate if we expect the propagated data to be ordered
+  private final boolean propagateOrderedCompact;
+
+  // Propagation flag is set to true while propagation is in progress (or pending).
+  // It is the synchronization primitive to coordinate the work with the propagation thread.
+  private final AtomicBoolean localPropagationInProgress;
+
+  ConcurrentHeapThetaBuffer(final int lgNomLongs, final long seed,
+      final ConcurrentSharedThetaSketch shared, final boolean propagateOrderedCompact,
+      final int maxNumLocalThreads) {
+    super(computeLogBufferSize(lgNomLongs, shared.getExactLimit(), maxNumLocalThreads),
+      seed, 1.0F, //p
+      ResizeFactor.X1, //rf
+      false); //not a union gadget
+
+    this.shared = shared;
+    isExactMode = true;
+    this.propagateOrderedCompact = propagateOrderedCompact;
+    localPropagationInProgress = new AtomicBoolean(false);
+  }
+
+  private static int computeLogBufferSize(final int lgNomLongs, final long exactSize,
+      final int maxNumLocalBuffers) {
+    return Math.min(lgNomLongs, (int)Math.log(Math.sqrt(exactSize) / (2 * maxNumLocalBuffers)));
+  }
+
+  //concurrent restricted methods
+
+  /**
+   * Propagates a single hash value to the shared sketch
+   *
+   * @param hash to be propagated
+   */
+  private boolean propagateToSharedSketch(final long hash) {
+    //no inspection StatementWithEmptyBody
+    while (localPropagationInProgress.get()) {
+    } //busy wait until previous propagation completed
+    localPropagationInProgress.set(true);
+    final boolean res = shared.propagate(localPropagationInProgress, null, hash);
+    //in this case the parent empty_ and curCount_ were not touched
+    thetaLong_ = shared.getVolatileTheta();
+    return res;
+  }
+
+  /**
+   * Propagates the content of the buffer as a sketch to the shared sketch
+   */
+  private void propagateToSharedSketch() {
+    //no inspection StatementWithEmptyBody
+    while (localPropagationInProgress.get()) {
+    } //busy wait until previous propagation completed
+
+    final CompactSketch compactSketch = compact(propagateOrderedCompact, null);
+    localPropagationInProgress.set(true);
+    shared.propagate(localPropagationInProgress, compactSketch,
+        ConcurrentSharedThetaSketch.NOT_SINGLE_HASH);
+    super.reset();
+    thetaLong_ = shared.getVolatileTheta();
+  }
+
+  //Public Sketch overrides proxies to shared concurrent sketch
+
+  @Override
+  public int getCompactBytes() {
+    return shared.getCompactBytes();
+  }
+
+  @Override
+  public int getCurrentBytes() {
+    return shared.getCurrentBytes();
+  }
+
+  @Override
+  public double getEstimate() {
+    return shared.getEstimate();
+  }
+
+  @Override
+  public double getLowerBound(final int numStdDev) {
+    return shared.getLowerBound(numStdDev);
+  }
+
+  @Override
+  public double getUpperBound(final int numStdDev) {
+    return shared.getUpperBound(numStdDev);
+  }
+
+  @Override
+  public boolean hasMemorySegment() {
+    return false;
+  }
+
+  @Override
+  public boolean isDirect() {
+    return false;
+  }
+
+  @Override
+  public boolean isEmpty() {
+    return shared.isEmpty();
+  }
+
+  @Override
+  public boolean isEstimationMode() {
+    return shared.isEstimationMode();
+  }
+
+  //End of proxies
+
+  @Override
+  public byte[] toByteArray() {
+    throw new UnsupportedOperationException("Local theta buffer need not be serialized");
+  }
+
+  //Public UpdateSketch overrides
+
+  @Override
+  public void reset() {
+    super.reset();
+    isExactMode = true;
+    localPropagationInProgress.set(false);
+  }
+
+  //Restricted UpdateSketch overrides
+
+  /**
+   * Updates buffer with given hash value.
+   * Triggers propagation to shared sketch if buffer is full.
+   *
+   * @param hash the given input hash value.  A hash of zero or Long.MAX_VALUE is ignored.
+   * A negative hash value will throw an exception.
+   * @return
+   * <a href="{@docRoot}/resources/dictionary.html#updateReturnState">See Update Return State</a>
+   */
+  @Override
+  UpdateReturnState hashUpdate(final long hash) {
+    if (isExactMode) {
+      isExactMode = !shared.isEstimationMode();
+    }
+    HashOperations.checkHashCorruption(hash);
+    if ((getHashTableThreshold() == 0) || isExactMode ) {
+      //The over-theta and zero test
+      if (HashOperations.continueCondition(getThetaLong(), hash)) {
+        return RejectedOverTheta; //signal that hash was rejected due to theta or zero.
+      }
+      if (propagateToSharedSketch(hash)) {
+        return ConcurrentPropagated;
+      }
+    }
+    final UpdateReturnState state = super.hashUpdate(hash);
+    if (isOutOfSpace(getRetainedEntries(true) + 1)) {
+      propagateToSharedSketch();
+      return ConcurrentPropagated;
+    }
+    if (state == UpdateReturnState.InsertedCountIncremented) {
+      return ConcurrentBufferInserted;
+    }
+    return state;
+  }
+
+}
diff --git a/src/main/java/org/apache/datasketches/theta2/ConcurrentPropagationService.java b/src/main/java/org/apache/datasketches/theta2/ConcurrentPropagationService.java
new file mode 100644
index 00000000..92ca954f
--- /dev/null
+++ b/src/main/java/org/apache/datasketches/theta2/ConcurrentPropagationService.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.datasketches.theta2;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.apache.datasketches.common.SuppressFBWarnings;
+
+/**
+ * Pool of threads to serve <i>all</i> propagation tasks in the system.
+ *
+ * @author Eshcar Hillel
+ */
+final class ConcurrentPropagationService {
+
+  static int NUM_POOL_THREADS = 3; // Default: 3 threads
+  private static volatile ConcurrentPropagationService instance = null; // Singleton
+  private static ExecutorService[] propagationExecutorService = null;
+
+  @SuppressFBWarnings(value = "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD", justification = "Fix later")
+  private ConcurrentPropagationService() {
+    propagationExecutorService = new ExecutorService[NUM_POOL_THREADS];
+  }
+
+  //Factory: Get the singleton
+  @SuppressFBWarnings(value = "SSD_DO_NOT_USE_INSTANCE_LOCK_ON_SHARED_STATIC_DATA", justification = "Fix later")
+  private static ConcurrentPropagationService getInstance() {
+    if (instance == null) {
+      synchronized (ConcurrentPropagationService.class) {
+        if (instance == null) {
+          instance = new ConcurrentPropagationService(); //SpotBugs: SSD_DO_NOT_USE_INSTANCE_LOCK_ON_SHARED_STATIC_DATA
+        }
+      }
+    }
+    return instance;
+  }
+
+  public static ExecutorService getExecutorService(final long id) {
+    return getInstance().initExecutorService((int) id % NUM_POOL_THREADS);
+  }
+
+  @SuppressWarnings("static-access")
+  public static ExecutorService resetExecutorService(final long id) {
+    return getInstance().propagationExecutorService[(int) id % NUM_POOL_THREADS] = null;
+  }
+
+  @SuppressWarnings("static-method")
+  private ExecutorService initExecutorService(final int i) {
+    if (propagationExecutorService[i] == null) {
+      propagationExecutorService[i] = Executors.newSingleThreadExecutor();
+    }
+    return propagationExecutorService[i];
+  }
+}
diff --git a/src/main/java/org/apache/datasketches/theta2/ConcurrentSharedThetaSketch.java b/src/main/java/org/apache/datasketches/theta2/ConcurrentSharedThetaSketch.java
new file mode 100644
index 00000000..5bf14704
--- /dev/null
+++ b/src/main/java/org/apache/datasketches/theta2/ConcurrentSharedThetaSketch.java
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.datasketches.theta2;
+
+import java.lang.foreign.MemorySegment;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.datasketches.common.Util;
+
+/**
+ * An internal interface to define the API of a concurrent shared theta sketch.
+ * It reflects all data processed by a single or multiple update threads, and can serve queries at
+ * any time.
+ *
+ * @author eshcar
+ */
+interface ConcurrentSharedThetaSketch {
+
+  long NOT_SINGLE_HASH = -1L;
+  double MIN_ERROR = 0.0000001;
+
+  static long computeExactLimit(final long k, final double error) {
+    return 2 * Math.min(k, (long) Math.ceil(1.0 / Math.pow(Math.max(error,MIN_ERROR), 2.0)));
+  }
+
+  /**
+   * Returns flip point (number of updates) from exact to estimate mode.
+   * @return flip point from exact to estimate mode
+   */
+  long getExactLimit();
+
+  /**
+   * Ensures mutual exclusion. No other thread can update the shared sketch while propagation is
+   * in progress
+   * @return true if eager propagation was started
+   */
+  boolean startEagerPropagation();
+
+  /**
+   * Completes the propagation: end mutual exclusion block.
+   * Notifies the local thread the propagation is completed
+   *
+   * @param localPropagationInProgress the synchronization primitive through which propagator
+   *                                   notifies local thread the propagation is completed
+   * @param isEager true if the propagation is in eager mode
+   */
+  void endPropagation(AtomicBoolean localPropagationInProgress, boolean isEager);
+
+  /**
+   * Returns the value of the volatile theta managed by the shared sketch
+   * @return the value of the volatile theta managed by the shared sketch
+   */
+  long getVolatileTheta();
+
+  /**
+   * Awaits termination of background (lazy) propagation tasks
+   */
+  void awaitBgPropagationTermination();
+
+  /**
+   * Init background (lazy) propagation service
+   */
+  void initBgPropagationService();
+
+  /**
+   * (Eager) Propagates the given sketch or hash value into this sketch
+   * @param localPropagationInProgress the flag to be updated when propagation is done
+   * @param sketchIn any Theta sketch with the data
+   * @param singleHash a single hash value
+   * @return true if propagation successfully started
+   */
+  boolean propagate(final AtomicBoolean localPropagationInProgress, final Sketch sketchIn,
+    final long singleHash);
+
+  /**
+   * (Lazy/Eager) Propagates the given hash value into this sketch
+   * @param singleHash a single hash value
+   */
+  void propagate(final long singleHash);
+
+  /**
+   * Updates the estimation of the number of unique entries by capturing a snapshot of the sketch
+   * data, namely, volatile theta and the num of valid entries in the sketch
+   */
+  void updateEstimationSnapshot();
+
+  /**
+   * Updates the value of the volatile theta by extracting it from the underlying sketch managed
+   * by the shared sketch
+   */
+  void updateVolatileTheta();
+
+  /**
+   * Validates the shared sketch is in the context of the given epoch
+   *
+   * @param epoch the epoch number to be validates
+   * @return true iff the shared sketch is in the context of the given epoch
+   */
+  boolean validateEpoch(long epoch);
+
+  //The following mirrors are public methods that already exist on the "extends" side of the dual
+  // inheritance. They are provided here to allow casts to this interface access
+  // to these methods without having to cast back to the extended parent class.
+  //
+  //This allows an internal class to cast either the Concurrent Direct or Concurrent Heap
+  //shared class to this interface and have access to the above special concurrent methods as
+  //well as the methods below.
+  //
+  //For the external user all of the below methods can be obtained by casting the shared
+  //sketch to UpdateSketch.  However, these methods here also act as an alias so that an
+  //attempt to access these methods from the local buffer will be diverted to the shared
+  //sketch.
+
+  //From Sketch and MemoryStatus
+
+  int getCompactBytes();
+
+  int getCurrentBytes();
+
+  double getEstimate();
+
+  double getLowerBound(int numStdDev);
+
+  double getUpperBound(int numStdDev);
+
+  /**
+   * Returns true if this object's internal data is backed by a Memory object,
+   * which may be on-heap or off-heap.
+   * @return true if this object's internal data is backed by a Memory object.
+   */
+  boolean hasMemorySegment();
+
+  /**
+   * Returns true if this object's internal data is backed by direct (off-heap) Memory.
+   * @return true if this object's internal data is backed by direct (off-heap) Memory.
+   */
+  boolean isDirect();
+
+  /**
+   * Returns true if the two given MemorySegments refer to the same backing resource,
+   * which is either an off-heap memory location and size, or the same on-heap array object.
+   *
+   * <p>This is a convenient delegate of
+   * {@link org.apache.datasketches.common.Util#isSameResource(MemorySegment, MemorySegment) isSameResource()}</p>
+   *
+   * @param seg1 The first given MemorySegment
+   * @param seg2 The second given MemorySegment
+   * @return true if both MemorySegments are determined to be the same backing memory.
+   */
+  default boolean isSameResource(final MemorySegment seg1, final MemorySegment seg2) {
+    return Util.isSameResource(seg1, seg2);
+  }
+
+  boolean isEmpty();
+
+  boolean isEstimationMode();
+
+  byte[] toByteArray();
+
+  int getRetainedEntries(boolean valid);
+
+  CompactSketch compact();
+
+  CompactSketch compact(boolean ordered, MemorySegment wseg);
+
+  UpdateSketch rebuild();
+
+  void reset();
+}
+
diff --git a/src/main/java/org/apache/datasketches/theta2/DirectCompactCompressedSketch.java b/src/main/java/org/apache/datasketches/theta2/DirectCompactCompressedSketch.java
new file mode 100644
index 00000000..9be51c37
--- /dev/null
+++ b/src/main/java/org/apache/datasketches/theta2/DirectCompactCompressedSketch.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.datasketches.theta2;
+
+import static java.lang.foreign.ValueLayout.JAVA_BYTE;
+import static org.apache.datasketches.theta2.PreambleUtil.extractEntryBitsV4;
+import static org.apache.datasketches.theta2.PreambleUtil.extractNumEntriesBytesV4;
+import static org.apache.datasketches.theta2.PreambleUtil.extractPreLongs;
+import static org.apache.datasketches.theta2.PreambleUtil.extractSeedHash;
+import static org.apache.datasketches.theta2.PreambleUtil.extractThetaLongV4;
+import static org.apache.datasketches.theta2.PreambleUtil.wholeBytesToHoldBits;
+
+import java.lang.foreign.MemorySegment;
+
+import org.apache.datasketches.thetacommon.ThetaUtil;
+
+/**
+ * An off-heap (Direct), compact, compressed, read-only sketch. It is not empty, not a single item and ordered.
+ *
+ * <p>This sketch can only be associated with a Serialization Version 4 format binary image.</p>
+ *
+ * <p>This implementation uses data in a given MemorySegment that is owned and managed by the caller.
+ * This MemorySegment can be off-heap, which if managed properly will greatly reduce the need for
+ * the JVM to perform garbage collection.</p>
+ */
+class DirectCompactCompressedSketch extends DirectCompactSketch {
+  /**
+   * Construct this sketch with the given MemorySegment.
+   * @param seg Read-only MemorySegment object.
+   */
+  DirectCompactCompressedSketch(final MemorySegment seg) {
+    super(seg);
+  }
+
+  /**
+   * Wraps the given MemorySegment, which must be a SerVer 4 compressed CompactSketch image.
+   * Must check the validity of the Memory before calling.
+   * @param srcSeg The source MemorySegment
+   * @param seedHash The update seedHash.
+   * <a href="{@docRoot}/resources/dictionary.html#seedHash">See Seed Hash</a>.
+   * @return this sketch
+   */
+  static DirectCompactCompressedSketch wrapInstance(final MemorySegment srcSeg, final short seedHash) {
+    ThetaUtil.checkSeedHashes((short) extractSeedHash(srcSeg), seedHash);
+    return new DirectCompactCompressedSketch(srcSeg);
+  }
+
+  //Sketch Overrides
+
+  @Override
+  public CompactSketch compact(final boolean dstOrdered, final MemorySegment dstSeg) {
+    if (dstSeg != null) {
+      MemorySegment.copy(seg_, 0, dstSeg, 0, getCurrentBytes());
+      return new DirectCompactSketch(dstSeg);
+    }
+    return CompactSketch.heapify(seg_);
+  }
+
+  @Override
+  public int getCurrentBytes() {
+    final int preLongs = extractPreLongs(seg_);
+    final int entryBits = extractEntryBitsV4(seg_);
+    final int numEntriesBytes = extractNumEntriesBytesV4(seg_);
+    return preLongs * Long.BYTES + numEntriesBytes + wholeBytesToHoldBits(getRetainedEntries() * entryBits);
+  }
+
+  private static final int START_PACKED_DATA_EXACT_MODE = 8;
+  private static final int START_PACKED_DATA_ESTIMATION_MODE = 16;
+
+  @Override
+  public int getRetainedEntries(final boolean valid) { //compact is always valid
+    // number of entries is stored using variable length encoding
+    // most significant bytes with all zeros are not stored
+    // one byte in the preamble has the number of non-zero bytes used
+    final int preLongs = extractPreLongs(seg_); // if > 1 then the second long has theta
+    final int numEntriesBytes = extractNumEntriesBytesV4(seg_);
+    int offsetBytes = preLongs > 1 ? START_PACKED_DATA_ESTIMATION_MODE : START_PACKED_DATA_EXACT_MODE;
+    int numEntries = 0;
+    for (int i = 0; i < numEntriesBytes; i++) {
+      numEntries |= Byte.toUnsignedInt(seg_.get(JAVA_BYTE, offsetBytes++)) << (i << 3);
+    }
+    return numEntries;
+  }
+
+  @Override
+  public long getThetaLong() {
+    final int preLongs = extractPreLongs(seg_);
+    return (preLongs > 1) ? extractThetaLongV4(seg_) : Long.MAX_VALUE;
+  }
+
+  @Override
+  public boolean isEmpty() {
+    return false;
+  }
+
+  @Override
+  public boolean isOrdered() {
+    return true;
+  }
+
+  @Override
+  public HashIterator iterator() {
+    return new MemoryCompactCompressedHashIterator(
+      seg_,
+      (extractPreLongs(seg_) > 1 ? START_PACKED_DATA_ESTIMATION_MODE : START_PACKED_DATA_EXACT_MODE)
+        + extractNumEntriesBytesV4(seg_),
+      extractEntryBitsV4(seg_),
+      getRetainedEntries()
+    );
+  }
+
+  //restricted methods
+
+  @Override
+  long[] getCache() {
+    final int numEntries = getRetainedEntries();
+    final long[] cache = new long[numEntries];
+    int i = 0;
+    final HashIterator it = iterator();
+    while (it.next()) {
+      cache[i++] = it.get();
+    }
+    return cache;
+  }
+}
diff --git a/src/main/java/org/apache/datasketches/theta2/DirectCompactSketch.java b/src/main/java/org/apache/datasketches/theta2/DirectCompactSketch.java
new file mode 100644
index 00000000..188f2cd7
--- /dev/null
+++ b/src/main/java/org/apache/datasketches/theta2/DirectCompactSketch.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.datasketches.theta2;
+
+import static java.lang.foreign.ValueLayout.JAVA_BYTE;
+import static java.lang.foreign.ValueLayout.JAVA_LONG_UNALIGNED;
+import static org.apache.datasketches.theta2.CompactOperations.checkIllegalCurCountAndEmpty;
+import static org.apache.datasketches.theta2.CompactOperations.memoryToCompact;
+import static org.apache.datasketches.theta2.PreambleUtil.ORDERED_FLAG_MASK;
+import static org.apache.datasketches.theta2.PreambleUtil.extractCurCount;
+import static org.apache.datasketches.theta2.PreambleUtil.extractFlags;
+import static org.apache.datasketches.theta2.PreambleUtil.extractPreLongs;
+import static org.apache.datasketches.theta2.PreambleUtil.extractSeedHash;
+import static org.apache.datasketches.theta2.PreambleUtil.extractThetaLong;
+import static org.apache.datasketches.theta2.SingleItemSketch.otherCheckForSingleItem;
+
+import java.lang.foreign.MemorySegment;
+
+import org.apache.datasketches.thetacommon.ThetaUtil;
+
+/**
+ * An off-heap (Direct), compact, read-only sketch. The internal hash array can be either ordered
+ * or unordered. It is not empty, not a single item.
+ *
+ * <p>This sketch can only be associated with a Serialization Version 3 format binary image.</p>
+ *
+ * <p>This implementation uses data in a given MemorySegment that is owned and managed by the caller.
+ * This MemorySegment can be off-heap, which if managed properly will greatly reduce the need for
+ * the JVM to perform garbage collection.</p>
+ *
+ * @author Lee Rhodes
+ */
+class DirectCompactSketch extends CompactSketch {
+  final MemorySegment seg_;
+
+  /**
+   * Construct this sketch with the given MemorySegment.
+   * @param seg Read-only MemorySegment object with the order bit properly set.
+   */
+  DirectCompactSketch(final MemorySegment seg) {
+    seg_ = seg;
+  }
+
+  /**
+   * Wraps the given MemorySegment, which must be a SerVer 3, CompactSketch image.
+   * Must check the validity of the MemorySegment before calling. The order bit must be set properly.
+   * @param srcSeg the given MemorySegment
+   * @param seedHash The update seedHash.
+   * <a href="{@docRoot}/resources/dictionary.html#seedHash">See Seed Hash</a>.
+   * @return this sketch
+   */
+  static DirectCompactSketch wrapInstance(final MemorySegment srcSeg, final short seedHash) {
+    ThetaUtil.checkSeedHashes((short) extractSeedHash(srcSeg), seedHash);
+    return new DirectCompactSketch(srcSeg);
+  }
+
+  //Sketch Overrides
+
+  @Override
+  public CompactSketch compact(final boolean dstOrdered, final MemorySegment dstSeg) {
+    return memoryToCompact(seg_, dstOrdered, dstSeg);
+  }
+
+  @Override
+  public int getCurrentBytes() {
+    if (otherCheckForSingleItem(seg_)) { return 16; }
+    final int preLongs = extractPreLongs(seg_);
+    final int curCount = (preLongs == 1) ? 0 : extractCurCount(seg_);
+    return (preLongs + curCount) << 3;
+  }
+
+  @Override
+  public int getRetainedEntries(final boolean valid) { //compact is always valid
+    if (otherCheckForSingleItem(seg_)) { return 1; }
+    final int preLongs = extractPreLongs(seg_);
+    final int curCount = (preLongs == 1) ? 0 : extractCurCount(seg_);
+    return curCount;
+  }
+
+  @Override
+  public long getThetaLong() {
+    final int preLongs = extractPreLongs(seg_);
+    return (preLongs > 2) ? extractThetaLong(seg_) : Long.MAX_VALUE;
+  }
+
+  @Override
+  public boolean hasMemorySegment() {
+    return seg_ != null;
+  }
+
+  @Override
+  public boolean isDirect() {
+    return hasMemorySegment() ? seg_.isNative() : false;
+  }
+
+  @Override
+  public boolean isEmpty() {
+    final boolean emptyFlag = PreambleUtil.isEmptyFlag(seg_);
+    final long thetaLong = getThetaLong();
+    final int curCount = getRetainedEntries(true);
+    return emptyFlag || ((curCount == 0) && (thetaLong == Long.MAX_VALUE));
+  }
+
+  @Override
+  public boolean isOrdered() {
+    return (extractFlags(seg_) & ORDERED_FLAG_MASK) > 0;
+  }
+
+  @Override
+  public HashIterator iterator() {
+    return new MemoryHashIterator(seg_, getRetainedEntries(true), getThetaLong());
+  }
+
+  @Override
+  public byte[] toByteArray() {
+    checkIllegalCurCountAndEmpty(isEmpty(), getRetainedEntries());
+    final int outBytes = getCurrentBytes();
+    final byte[] byteArrOut = new byte[outBytes];
+    MemorySegment.copy(seg_, JAVA_BYTE, 0, byteArrOut, 0, outBytes);
+    return byteArrOut;
+  }
+
+  //restricted methods
+
+  @Override
+  long[] getCache() {
+    if (otherCheckForSingleItem(seg_)) { return new long[] { seg_.get(JAVA_LONG_UNALIGNED, 8) }; }
+    final int preLongs = extractPreLongs(seg_);
+    final int curCount = (preLongs == 1) ? 0 : extractCurCount(seg_);
+    if (curCount > 0) {
+      final long[] cache = new long[curCount];
+      MemorySegment.copy(seg_, JAVA_LONG_UNALIGNED, preLongs << 3, cache, 0, curCount);
+      return cache;
+    }
+    return new long[0];
+  }
+
+  @Override
+  int getCompactPreambleLongs() {
+    return extractPreLongs(seg_);
+  }
+
+  @Override
+  int getCurrentPreambleLongs() {
+    return extractPreLongs(seg_);
+  }
+
+  @Override
+  MemorySegment getMemorySegment() {
+    return seg_;
+  }
+
+  @Override
+  short getSeedHash() {
+    return (short) extractSeedHash(seg_);
+  }
+}
diff --git a/src/main/java/org/apache/datasketches/theta2/DirectQuickSelectSketch.java b/src/main/java/org/apache/datasketches/theta2/DirectQuickSelectSketch.java
new file mode 100644
index 00000000..32ae0d14
--- /dev/null
+++ b/src/main/java/org/apache/datasketches/theta2/DirectQuickSelectSketch.java
@@ -0,0 +1,339 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.datasketches.theta2;
+
+import static java.lang.foreign.ValueLayout.JAVA_BYTE;
+import static java.lang.foreign.ValueLayout.JAVA_FLOAT_UNALIGNED;
+import static java.lang.foreign.ValueLayout.JAVA_INT_UNALIGNED;
+import static java.lang.foreign.ValueLayout.JAVA_LONG_UNALIGNED;
+import static org.apache.datasketches.common.Util.LONG_MAX_VALUE_AS_DOUBLE;
+import static org.apache.datasketches.common.Util.newHeapSegment;
+import static org.apache.datasketches.theta2.PreambleUtil.EMPTY_FLAG_MASK;
+import static org.apache.datasketches.theta2.PreambleUtil.FLAGS_BYTE;
+import static org.apache.datasketches.theta2.PreambleUtil.PREAMBLE_LONGS_BYTE;
+import static org.apache.datasketches.theta2.PreambleUtil.P_FLOAT;
+import static org.apache.datasketches.theta2.PreambleUtil.RETAINED_ENTRIES_INT;
+import static org.apache.datasketches.theta2.PreambleUtil.SER_VER;
+import static org.apache.datasketches.theta2.PreambleUtil.THETA_LONG;
+import static org.apache.datasketches.theta2.PreambleUtil.extractLgArrLongs;
+import static org.apache.datasketches.theta2.PreambleUtil.extractLgNomLongs;
+import static org.apache.datasketches.theta2.PreambleUtil.extractPreLongs;
+import static org.apache.datasketches.theta2.PreambleUtil.getMemBytes;
+import static org.apache.datasketches.theta2.PreambleUtil.insertCurCount;
+import static org.apache.datasketches.theta2.PreambleUtil.insertFamilyID;
+import static org.apache.datasketches.theta2.PreambleUtil.insertFlags;
+import static org.apache.datasketches.theta2.PreambleUtil.insertLgArrLongs;
+import static org.apache.datasketches.theta2.PreambleUtil.insertLgNomLongs;
+import static org.apache.datasketches.theta2.PreambleUtil.insertLgResizeFactor;
+import static org.apache.datasketches.theta2.PreambleUtil.insertP;
+import static org.apache.datasketches.theta2.PreambleUtil.insertPreLongs;
+import static org.apache.datasketches.theta2.PreambleUtil.insertSeedHash;
+import static org.apache.datasketches.theta2.PreambleUtil.insertSerVer;
+import static org.apache.datasketches.theta2.PreambleUtil.insertThetaLong;
+import static org.apache.datasketches.theta2.PreambleUtil.insertUnionThetaLong;
+import static org.apache.datasketches.theta2.Rebuilder.actLgResizeFactor;
+import static org.apache.datasketches.theta2.Rebuilder.moveAndResize;
+import static org.apache.datasketches.theta2.Rebuilder.quickSelectAndRebuild;
+import static org.apache.datasketches.theta2.Rebuilder.resize;
+import static org.apache.datasketches.theta2.UpdateReturnState.InsertedCountIncremented;
+import static org.apache.datasketches.theta2.UpdateReturnState.InsertedCountIncrementedRebuilt;
+import static org.apache.datasketches.theta2.UpdateReturnState.InsertedCountIncrementedResized;
+import static org.apache.datasketches.theta2.UpdateReturnState.RejectedDuplicate;
+import static org.apache.datasketches.theta2.UpdateReturnState.RejectedOverTheta;
+
+import java.lang.foreign.MemorySegment;
+
+import org.apache.datasketches.common.Family;
+import org.apache.datasketches.common.ResizeFactor;
+import org.apache.datasketches.common.SketchesArgumentException;
+import org.apache.datasketches.thetacommon.HashOperations;
+import org.apache.datasketches.thetacommon.ThetaUtil;
+
+/**
+ * The default Theta Sketch using the QuickSelect algorithm.
+ * This subclass implements methods, which affect the state (update, rebuild, reset)
+ *
+ * <p>This implementation uses data in a given Memory that is owned and managed by the caller.
+ * This Memory can be off-heap, which if managed properly will greatly reduce the need for
+ * the JVM to perform garbage collection.</p>
+ *
+ * @author Lee Rhodes
+ * @author Kevin Lang
+ */
+class DirectQuickSelectSketch extends DirectQuickSelectSketchR {
+
+  private DirectQuickSelectSketch(
+      final long seed,
+      final MemorySegment wseg) {
+    super(seed, wseg);
+  }
+
+  /**
+   * Construct a new sketch instance and initialize the given MemorySegment as its backing store.
+   *
+   * @param lgNomLongs <a href="{@docRoot}/resources/dictionary.html#lgNomLongs">See lgNomLongs</a>.
+   * @param seed <a href="{@docRoot}/resources/dictionary.html#seed">See Update Hash Seed</a>.
+   * @param p
+   * <a href="{@docRoot}/resources/dictionary.html#p">See Sampling Probability, <i>p</i></a>
+   * @param rf Currently internally fixed at 2. Unless dstMem is not configured with a valid
+   * MemoryRequest, in which case the rf is effectively 1, which is no resizing at all and the
+   * dstMem must be large enough for a full sketch.
+   * <a href="{@docRoot}/resources/dictionary.html#resizeFactor">See Resize Factor</a>
+   * @param memReqSvr the given MemoryRequestServer
+   * @param dstSeg the given Memory object destination. It cannot be null.
+   * It will be cleared prior to use.
+   * @param unionGadget true if this sketch is implementing the Union gadget function.
+   * Otherwise, it is behaving as a normal QuickSelectSketch.
+   */
+  DirectQuickSelectSketch(
+      final int lgNomLongs,
+      final long seed,
+      final float p,
+      final ResizeFactor rf,
+      final MemorySegment dstSeg,
+      final boolean unionGadget) {
+    this(
+        checkMemSize(lgNomLongs, rf, dstSeg, unionGadget),
+        //SpotBugs CT_CONSTRUCTOR_THROW is false positive.
+        //this construction scheme is compliant with SEI CERT Oracle Coding Standard for Java / OBJ11-J
+        lgNomLongs,
+        seed,
+        p,
+        rf,
+        dstSeg,
+        unionGadget);
+  }
+
+  private DirectQuickSelectSketch(
+      final boolean secure, //required part of Finalizer Attack prevention
+      final int lgNomLongs,
+      final long seed,
+      final float p,
+      final ResizeFactor rf,
+      final MemorySegment dstSeg,
+      final boolean unionGadget) {
+    super(seed, dstSeg);
+    //Choose family, preambleLongs
+    final Family family;
+    final int preambleLongs;
+    if (unionGadget) {
+      preambleLongs = Family.UNION.getMinPreLongs();
+      family = Family.UNION;
+    }
+    else {
+      preambleLongs = Family.QUICKSELECT.getMinPreLongs();
+      family = Family.QUICKSELECT;
+    }
+
+    //Choose RF, minReqBytes, lgArrLongs.
+    final int lgRF = rf.lg();
+    final int lgArrLongs = (lgRF == 0) ? lgNomLongs + 1 : ThetaUtil.MIN_LG_ARR_LONGS;
+
+    //@formatter:off
+    //Build preamble
+    insertPreLongs(dstSeg, preambleLongs);                 //byte 0
+    insertLgResizeFactor(dstSeg, lgRF);                    //byte 0
+    insertSerVer(dstSeg, SER_VER);                         //byte 1
+    insertFamilyID(dstSeg, family.getID());                //byte 2
+    insertLgNomLongs(dstSeg, lgNomLongs);                  //byte 3
+    insertLgArrLongs(dstSeg, lgArrLongs);                  //byte 4
+    //flags: bigEndian = readOnly = compact = ordered = false; empty = true : 00100 = 4
+    insertFlags(dstSeg, EMPTY_FLAG_MASK);                  //byte 5
+    insertSeedHash(dstSeg, ThetaUtil.computeSeedHash(seed));    //bytes 6,7
+    insertCurCount(dstSeg, 0);                             //bytes 8-11
+    insertP(dstSeg, p);                                    //bytes 12-15
+    final long thetaLong = (long)(p * LONG_MAX_VALUE_AS_DOUBLE);
+    insertThetaLong(dstSeg, thetaLong);                    //bytes 16-23
+    if (unionGadget) {
+      insertUnionThetaLong(dstSeg, thetaLong);
+    }
+    //@formatter:on
+
+    //clear hash table area
+    dstSeg.asSlice(preambleLongs << 3, Long.BYTES << lgArrLongs).fill((byte)0);
+
+    hashTableThreshold_ = getOffHeapHashTableThreshold(lgNomLongs, lgArrLongs);
+  }
+
+  private static final boolean checkMemSize(
+      final int lgNomLongs, final ResizeFactor rf, final MemorySegment dstSeg, final boolean unionGadget) {
+    final int preambleLongs = (unionGadget) ? Family.UNION.getMinPreLongs() : Family.QUICKSELECT.getMinPreLongs();
+    final int lgRF = rf.lg();
+    final int lgArrLongs = (lgRF == 0) ? lgNomLongs + 1 : ThetaUtil.MIN_LG_ARR_LONGS;
+    final int minReqBytes = getMemBytes(lgArrLongs, preambleLongs);
+    final long curMemCapBytes = dstSeg.byteSize();
+    if (curMemCapBytes < minReqBytes) {
+      throw new SketchesArgumentException(
+        "Memory capacity is too small: " + curMemCapBytes + " < " + minReqBytes);
+    }
+    return true;
+  }
+
+  /**
+   * Wrap a sketch around the given source MemorySegment containing sketch data that originated from
+   * this sketch.
+   * @param srcSeg The given MemorySegment object must be in hash table form and not read only.
+   * @param seed <a href="{@docRoot}/resources/dictionary.html#seed">See Update Hash Seed</a>
+   * @return instance of this sketch
+   */
+  static DirectQuickSelectSketch writableWrap(final MemorySegment srcSeg, final long seed) {
+    final int preambleLongs = extractPreLongs(srcSeg);                  //byte 0
+    final int lgNomLongs = extractLgNomLongs(srcSeg);                   //byte 3
+    final int lgArrLongs = extractLgArrLongs(srcSeg);                   //byte 4
+
+    UpdateSketch.checkUnionQuickSelectFamily(srcSeg, preambleLongs, lgNomLongs);
+    checkMemIntegrity(srcSeg, seed, preambleLongs, lgNomLongs, lgArrLongs);
+
+    if (isResizeFactorIncorrect(srcSeg, lgNomLongs, lgArrLongs)) {
+      //If incorrect it sets it to X2 which always works.
+      insertLgResizeFactor(srcSeg, ResizeFactor.X2.lg());
+    }
+
+    final DirectQuickSelectSketch dqss =
+        new DirectQuickSelectSketch(seed, srcSeg);
+    dqss.hashTableThreshold_ = getOffHeapHashTableThreshold(lgNomLongs, lgArrLongs);
+    return dqss;
+  }
+
+  /**
+   * Fast-wrap a sketch around the given source MemorySegment containing sketch data that originated from
+   * this sketch.  This does NO validity checking of the given Memory.
+   * @param srcSeg The given MemorySegment must be in hash table form and not read only.
+   * @param seed <a href="{@docRoot}/resources/dictionary.html#seed">See Update Hash Seed</a>
+   * @return instance of this sketch
+   */
+  static DirectQuickSelectSketch fastWritableWrap(final MemorySegment srcSeg, final long seed) {
+    final int lgNomLongs = extractLgNomLongs(srcSeg);                   //byte 3
+    final int lgArrLongs = extractLgArrLongs(srcSeg);                   //byte 4
+
+    final DirectQuickSelectSketch dqss =
+        new DirectQuickSelectSketch(seed, srcSeg);
+    dqss.hashTableThreshold_ = getOffHeapHashTableThreshold(lgNomLongs, lgArrLongs);
+    return dqss;
+  }
+
+  //Sketch
+
+  //UpdateSketch
+
+  @Override
+  public UpdateSketch rebuild() {
+    final int lgNomLongs = getLgNomLongs();
+    final int preambleLongs = wseg_.get(JAVA_BYTE, PREAMBLE_LONGS_BYTE) & 0X3F;
+    if (getRetainedEntries(true) > (1 << lgNomLongs)) {
+      quickSelectAndRebuild(wseg_, preambleLongs, lgNomLongs);
+    }
+    return this;
+  }
+
+  @Override
+  public void reset() {
+    //clear hash table
+    //hash table size and hashTableThreshold stays the same
+    //lgArrLongs stays the same
+    //thetaLongs resets to p
+    final int arrLongs = 1 << getLgArrLongs();
+    final int preambleLongs = wseg_.get(JAVA_BYTE, PREAMBLE_LONGS_BYTE) & 0X3F;
+    final int preBytes = preambleLongs << 3;
+    wseg_.asSlice(preBytes, arrLongs * 8L).fill((byte)0);
+    //flags: bigEndian = readOnly = compact = ordered = false; empty = true.
+    wseg_.set(JAVA_BYTE, FLAGS_BYTE, (byte) EMPTY_FLAG_MASK);
+    wseg_.set(JAVA_INT_UNALIGNED, RETAINED_ENTRIES_INT, 0);
+    final float p = wseg_.get(JAVA_FLOAT_UNALIGNED, P_FLOAT);
+    final long thetaLong = (long) (p * LONG_MAX_VALUE_AS_DOUBLE);
+    wseg_.set(JAVA_LONG_UNALIGNED, THETA_LONG, thetaLong);
+  }
+
+  //restricted methods
+
+  @Override
+  UpdateReturnState hashUpdate(final long hash) {
+    HashOperations.checkHashCorruption(hash);
+
+    wseg_.set(JAVA_BYTE, FLAGS_BYTE, (byte) (wseg_.get(JAVA_BYTE, FLAGS_BYTE) & ~EMPTY_FLAG_MASK));
+    final long thetaLong = getThetaLong();
+    final int lgNomLongs = getLgNomLongs();
+    //The over-theta test
+    if (HashOperations.continueCondition(thetaLong, hash)) {
+      return RejectedOverTheta; //signal that hash was rejected due to theta or zero.
+    }
+
+    final int lgArrLongs = getLgArrLongs();
+    final int preambleLongs = wseg_.get(JAVA_BYTE, PREAMBLE_LONGS_BYTE) & 0X3F;
+
+    //The duplicate test
+    final int index =
+        HashOperations.hashSearchOrInsertMemory(wseg_, lgArrLongs, hash, preambleLongs << 3);
+    if (index >= 0) {
+      return RejectedDuplicate; //Duplicate, not inserted
+    }
+    //insertion occurred, increment curCount
+    final int curCount = getRetainedEntries(true) + 1;
+    wseg_.set(JAVA_INT_UNALIGNED, RETAINED_ENTRIES_INT, curCount); //update curCount
+
+    if (isOutOfSpace(curCount)) { //we need to do something, we are out of space
+
+      if (lgArrLongs > lgNomLongs) { //at full size, rebuild
+        //Assumes no dirty values, changes thetaLong, curCount_
+        assert (lgArrLongs == (lgNomLongs + 1)) : "lgArr: " + lgArrLongs + ", lgNom: " + lgNomLongs;
+        //rebuild, refresh curCount based on # values in the hashtable.
+        quickSelectAndRebuild(wseg_, preambleLongs, lgNomLongs);
+        return InsertedCountIncrementedRebuilt;
+      } //end of rebuild, exit
+
+      else { //Not at full size, resize. Should not get here if lgRF = 0 and memCap is too small.
+        final int lgRF = getLgRF();
+        final int actLgRF = actLgResizeFactor(wseg_.byteSize(), lgArrLongs, preambleLongs, lgRF);
+        int tgtLgArrLongs = Math.min(lgArrLongs + actLgRF, lgNomLongs + 1);
+
+        if (actLgRF > 0) { //Expand in current Memory
+          //lgArrLongs will change; thetaLong, curCount will not
+          resize(wseg_, preambleLongs, lgArrLongs, tgtLgArrLongs);
+          hashTableThreshold_ = getOffHeapHashTableThreshold(lgNomLongs, tgtLgArrLongs);
+          return InsertedCountIncrementedResized;
+        } //end of Expand in current memory, exit.
+
+        else {
+          //Request more memory, then resize. lgArrLongs will change; thetaLong, curCount will not
+          final int preBytes = preambleLongs << 3;
+          tgtLgArrLongs = Math.min(lgArrLongs + lgRF, lgNomLongs + 1);
+          final int tgtArrBytes = 8 << tgtLgArrLongs;
+          final int reqBytes = tgtArrBytes + preBytes;
+
+          //memReqSvr_ = (memReqSvr_ == null) ? wseg_.getMemoryRequestServer() : memReqSvr_;
+          //if (memReqSvr_ == null) { //in case the MRS is not enabled or null.
+          //  throw new SketchesArgumentException("Out of Memory, MemoryRequestServer is null, cannot expand.");
+          //}
+          //final MemorySegment newDstSeg = memReqSvr_.request(wseg_, reqBytes);
+
+          final MemorySegment newDstSeg = newHeapSegment(reqBytes);
+
+          moveAndResize(wseg_, preambleLongs, lgArrLongs, newDstSeg, tgtLgArrLongs, thetaLong);
+          wseg_ = newDstSeg;
+
+          hashTableThreshold_ = getOffHeapHashTableThreshold(lgNomLongs, tgtLgArrLongs);
+          return InsertedCountIncrementedResized;
+        } //end of Request more memory to resize
+      } //end of resize
+    } //end of isOutOfSpace
+    return InsertedCountIncremented;
+  }
+
+}
diff --git a/src/main/java/org/apache/datasketches/theta2/DirectQuickSelectSketchR.java b/src/main/java/org/apache/datasketches/theta2/DirectQuickSelectSketchR.java
new file mode 100644
index 00000000..33d37155
--- /dev/null
+++ b/src/main/java/org/apache/datasketches/theta2/DirectQuickSelectSketchR.java
@@ -0,0 +1,284 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.datasketches.theta2;
+
+import static java.lang.foreign.ValueLayout.JAVA_BYTE;
+import static java.lang.foreign.ValueLayout.JAVA_FLOAT_UNALIGNED;
+import static java.lang.foreign.ValueLayout.JAVA_INT_UNALIGNED;
+import static java.lang.foreign.ValueLayout.JAVA_LONG_UNALIGNED;
+import static org.apache.datasketches.theta2.CompactOperations.checkIllegalCurCountAndEmpty;
+import static org.apache.datasketches.theta2.CompactOperations.computeCompactPreLongs;
+import static org.apache.datasketches.theta2.CompactOperations.correctThetaOnCompact;
+import static org.apache.datasketches.theta2.PreambleUtil.FAMILY_BYTE;
+import static org.apache.datasketches.theta2.PreambleUtil.LG_ARR_LONGS_BYTE;
+import static org.apache.datasketches.theta2.PreambleUtil.LG_NOM_LONGS_BYTE;
+import static org.apache.datasketches.theta2.PreambleUtil.LG_RESIZE_FACTOR_BIT;
+import static org.apache.datasketches.theta2.PreambleUtil.PREAMBLE_LONGS_BYTE;
+import static org.apache.datasketches.theta2.PreambleUtil.P_FLOAT;
+import static org.apache.datasketches.theta2.PreambleUtil.RETAINED_ENTRIES_INT;
+import static org.apache.datasketches.theta2.PreambleUtil.THETA_LONG;
+import static org.apache.datasketches.theta2.PreambleUtil.extractCurCount;
+import static org.apache.datasketches.theta2.PreambleUtil.extractLgArrLongs;
+import static org.apache.datasketches.theta2.PreambleUtil.extractLgNomLongs;
+import static org.apache.datasketches.theta2.PreambleUtil.extractPreLongs;
+import static org.apache.datasketches.theta2.PreambleUtil.extractThetaLong;
+import static org.apache.datasketches.theta2.PreambleUtil.insertThetaLong;
+
+import java.lang.foreign.MemorySegment;
+
+import org.apache.datasketches.common.Family;
+import org.apache.datasketches.common.ResizeFactor;
+import org.apache.datasketches.common.SketchesReadOnlyException;
+import org.apache.datasketches.common.SuppressFBWarnings;
+import org.apache.datasketches.thetacommon.ThetaUtil;
+
+/**
+ * The default Theta Sketch using the QuickSelect algorithm.
+ * This is the read-only implementation with non-functional methods, which affect the state.
+ *
+ * <p>This implementation uses data in a given Memory that is owned and managed by the caller.
+ * This Memory can be off-heap, which if managed properly will greatly reduce the need for
+ * the JVM to perform garbage collection.</p>
+ *
+ * @author Lee Rhodes
+ * @author Kevin Lang
+ */
+class DirectQuickSelectSketchR extends UpdateSketch {
+  static final double DQS_RESIZE_THRESHOLD  = 15.0 / 16.0; //tuned for space
+  final long seed_; //provided, kept only on heap, never serialized.
+  int hashTableThreshold_; //computed, kept only on heap, never serialized.
+  MemorySegment wseg_; //A WritableMemory for child class, but no write methods here
+
+  //only called by DirectQuickSelectSketch and below
+  DirectQuickSelectSketchR(final long seed, final MemorySegment wseg) {
+    seed_ = seed;
+    wseg_ = wseg;
+  }
+
+  /**
+   * Wrap a sketch around the given source MemorySegment containing sketch data that originated from
+   * this sketch.
+   * @param srcSeg the source MemorySegment.
+   * The given MemorySegment object must be in hash table form and not read only.
+   * @param seed <a href="{@docRoot}/resources/dictionary.html#seed">See Update Hash Seed</a>
+   * @return instance of this sketch
+   */
+  static DirectQuickSelectSketchR readOnlyWrap(final MemorySegment srcSeg, final long seed) {
+    final int preambleLongs = extractPreLongs(srcSeg);                  //byte 0
+    final int lgNomLongs = extractLgNomLongs(srcSeg);                   //byte 3
+    final int lgArrLongs = extractLgArrLongs(srcSeg);                   //byte 4
+
+    UpdateSketch.checkUnionQuickSelectFamily(srcSeg, preambleLongs, lgNomLongs);
+    checkMemIntegrity(srcSeg, seed, preambleLongs, lgNomLongs, lgArrLongs);
+
+    final DirectQuickSelectSketchR dqssr =
+        new DirectQuickSelectSketchR(seed, srcSeg);
+    dqssr.hashTableThreshold_ = getOffHeapHashTableThreshold(lgNomLongs, lgArrLongs);
+    return dqssr;
+  }
+
+  /**
+   * Fast-wrap a sketch around the given source MemorySegment containing sketch data that originated from
+   * this sketch.  This does NO validity checking of the given MemorySegment.
+   * @param srcSeg The given MemorySegment object must be in hash table form and not read only.
+   * @param seed <a href="{@docRoot}/resources/dictionary.html#seed">See Update Hash Seed</a>
+   * @return instance of this sketch
+   */
+  static DirectQuickSelectSketchR fastReadOnlyWrap(final MemorySegment srcSeg, final long seed) {
+    final int lgNomLongs = srcSeg.get(JAVA_BYTE, LG_NOM_LONGS_BYTE) & 0XFF;
+    final int lgArrLongs = srcSeg.get(JAVA_BYTE, LG_ARR_LONGS_BYTE) & 0XFF;
+
+    final DirectQuickSelectSketchR dqss =
+        new DirectQuickSelectSketchR(seed, srcSeg);
+    dqss.hashTableThreshold_ = getOffHeapHashTableThreshold(lgNomLongs, lgArrLongs);
+    return dqss;
+  }
+
+  //Sketch
+
+  @Override
+  public int getCurrentBytes() {
+    //not compact
+    final byte lgArrLongs = wseg_.get(JAVA_BYTE, LG_ARR_LONGS_BYTE);
+    final int preLongs = wseg_.get(JAVA_BYTE, PREAMBLE_LONGS_BYTE) & 0X3F;
+    final int lengthBytes = (preLongs + (1 << lgArrLongs)) << 3;
+    return lengthBytes;
+  }
+
+  @Override
+  public double getEstimate() {
+    final int curCount = extractCurCount(wseg_);
+    final long thetaLong = extractThetaLong(wseg_);
+    return Sketch.estimate(thetaLong, curCount);
+  }
+
+  @Override
+  public Family getFamily() {
+    final int familyID = wseg_.get(JAVA_BYTE, FAMILY_BYTE) & 0XFF;
+    return Family.idToFamily(familyID);
+  }
+
+  @Override
+  public int getRetainedEntries(final boolean valid) { //always valid
+    return wseg_.get(JAVA_INT_UNALIGNED, RETAINED_ENTRIES_INT);
+  }
+
+  @Override
+  public long getThetaLong() {
+    return isEmpty() ? Long.MAX_VALUE : wseg_.get(JAVA_LONG_UNALIGNED, THETA_LONG);
+  }
+
+  @Override
+  public boolean hasMemorySegment() {
+    return wseg_ != null;
+  }
+
+  @Override
+  public boolean isDirect() {
+    return hasMemorySegment() ? wseg_.isNative() : false;
+  }
+
+  @Override
+  public boolean isEmpty() {
+    return PreambleUtil.isEmptyFlag(wseg_);
+  }
+
+  @Override
+  public HashIterator iterator() {
+    return new MemoryHashIterator(wseg_, 1 << getLgArrLongs(), getThetaLong());
+  }
+
+  @Override
+  public byte[] toByteArray() { //MY_FAMILY is stored in wmem_
+    checkIllegalCurCountAndEmpty(isEmpty(), extractCurCount(wseg_));
... 7304 lines suppressed ...


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datasketches.apache.org
For additional commands, e-mail: commits-help@datasketches.apache.org