Index: src/java/org/apache/lucene/util/Simple9.java
===================================================================
--- src/java/org/apache/lucene/util/Simple9.java	(revision 0)
+++ src/java/org/apache/lucene/util/Simple9.java	(revision 0)
@@ -0,0 +1,424 @@
+package org.apache.lucene.util;
+
+/**
+ * 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.
+ */
+
+
+/** Simple9 methods for compressing integers into a single 32-bit integer
+ * and for the corresponding decompression.
+ * 
+ * The Simple9 compression method is taken from this paper:
+ * <br>
+ * Jiangong Zhang, Xiaohui Long, Torsten Suel,
+ * "Performance of Compressed Inverted List Caching in Search Engines",
+ * WWW 2008 / Refereed Track: Search - Corpus Characterization & Search
+ * Performance, Beijing, China
+ * <p>
+ * Adapted quote from this paper:
+ * <br>
+ * The compressed word contains 4 status bits and 28 data bits.
+ * There are nine different ways of dividing up the 28
+ * data bits: 28 1-bit numbers, 14 2-bit numbers, 9 3-bit numbers (one
+ * bit unused), 7 4-bit numbers, 5 5-numbers (three bits unused), 4
+ * 7-bit numbers, 3 9-bit numbers (one bit unused), 2 14-bit numbers,
+ * or 1 28-bit number. We use the four status bits to store which of the
+ * 9 cases is used. Decompression can be done in a highly efﬁcient
+ * manner by doing a switch operation on the status bits, where each
+ * of the 9 cases applies a ﬁxed bit mask to extract the integers.
+ * <br>
+ * End of quote.
+ * <p>
+ * Simple9 leaves some room to choose the encoding variants, and preference
+ * is given here to cases in which increasing positive
+ * integers are compressed.
+ * <br> The reason for this is that it is expected
+ * that small sets of increasing positive integers will be useful for
+ * encoding of the indexes of exceptions in a variant of PForDelta that is
+ * described in the same paper mentioned above.
+ */
+ 
+ */
+public class Simple9 {
+  /** No instances needed, only static members */
+  private Simple9() {}
+  
+  private static final int NUM_DATA_BITS = 28;
+  private static final int BITS_28_MASK = (1 << 28) - 1;
+  private static final int BITS_14_MASK = (1 << 14) - 1;
+  private static final int BITS_10_MASK = (1 << 10) - 1;
+  private static final int BITS_9_MASK = (1 << 9) - 1;
+  private static final int BITS_7_MASK = (1 << 7) - 1;
+  private static final int BITS_6_MASK = (1 << 6) - 1;
+  private static final int BITS_5_MASK = (1 << 5) - 1;
+  private static final int BITS_4_MASK = (1 << 4) - 1;
+  private static final int BITS_3_MASK = (1 << 3) - 1;
+  private static final int BITS_2_MASK = (1 << 2) - 1;
+  private static final int BITS_1_MASK = (1 << 1) - 1;
+
+  private static final int NUM_STATUS_BITS = 4;
+  private static final int STATUS_MASK = (BITS_4_MASK << NUM_DATA_BITS);
+  private static final int STATUS_1_NUM_28_BITS = (1 << NUM_DATA_BITS); // 1 number of 28 bits
+  private static final int STATUS_2_NUM_14_BITS = (2 << NUM_DATA_BITS); // 2 numbers of 14 bits
+  private static final int STATUS_3_NUM_99_10_BITS = (3 << NUM_DATA_BITS); // 3 numbers of 9, 9 and 10 bits
+  private static final int STATUS_4_NUM_7_BITS = (4 << NUM_DATA_BITS); // 4 numbers of 7 bits
+  private static final int STATUS_5_NUM_55_666_BITS = (5 << NUM_DATA_BITS); // 5 numbers of 5, 5, 6, 6 and 6 bits
+  private static final int STATUS_7_NUM_4_BITS = (6 << NUM_DATA_BITS); // 7 numbers of 4 bits
+  private static final int STATUS_9_NUM_33333333_4_BITS = (7 << NUM_DATA_BITS); // 9 numbers, 8 of 3 bits, 1 of 4 bits
+  private static final int STATUS_14_NUM_2_BITS = (8 << NUM_DATA_BITS); // 14 numbers of 2 bits
+  private static final int STATUS_28_NUM_1_BITS = (9 << NUM_DATA_BITS); // 28 numbers of 1 bits
+
+  /** Encode positive integers from an uncompressed array at a given inOffset
+   * into a single 32 bit integer at a given output offset using a Simple9 method.
+   * The maximum number in the input array should not exceed 2**28 - 1.
+   * The input array should have at least 28 numbers available from the given inOffset.
+   * At most inSize numbers will be compressed. When inSize is smaller than 29
+   * some bits may be taken from at most 13 remaining elements of the given array.
+   * These remaining 13 elements (pads) should be zero.
+   * Return the number of input integers that where compressed. This will
+   * be at least 1 and at most 28, independent of the value of inSize.
+   *
+   * FIXME: encoding should never use more elements than available.
+   */
+  public static int compressSingle(int[] uncompressed, int inOffset, int inSize,
+                                   int[] compressed, int outOffset) {
+    if (inSize < 1) {
+      throw new IllegalArgumentException("Cannot compress input with non positive size " + inSize);
+    }
+    int inputCompressable = 1;
+    int minBits = 1;
+    int maxFitPlus1 = (1 << minBits);
+    int nextData;
+    do {
+      nextData = uncompressed[inOffset+inputCompressable-1];
+      if (nextData < 0) {
+        throw new IllegalArgumentException("Cannot compress negative input " + nextData 
+                                            + " (at index " + (inOffset+inputCompressable-1) + ")");
+      }
+      while ((nextData >= maxFitPlus1) && (minBits < NUM_DATA_BITS))
+      { // Give preference to input cases with increasing numbers:
+        if ((minBits == 3) && (inputCompressable == 9)
+            && (nextData < (maxFitPlus1 << 1))) {
+          /* STATUS_9_NUM_33333333_4_BITS, nextData fits in 4 bits, no need to increase minBits */
+          break;
+        } else if ((minBits == 5) && (inputCompressable >= 3)
+                   && (nextData < (maxFitPlus1 << 1))) {
+          /* STATUS_5_NUM_55_666_BITS, nextData fits in 6 bits, no need to increase minBits */
+          break;
+        } else if ((minBits == 9) && (inputCompressable == 3)
+                   && (nextData < (maxFitPlus1 << 1))) {
+          /* STATUS_3_NUM_99_10_BITS, nextData fits in 10 bits, no need to increase minBits */
+          break;
+        } else {
+          minBits++;
+          maxFitPlus1 <<= 1;
+        }
+      }
+      inputCompressable++;
+    } while ( ((inputCompressable * minBits) <= NUM_DATA_BITS)
+             && (inputCompressable <= inSize) );
+ 
+    inputCompressable--;
+    if ((inputCompressable * minBits) > NUM_DATA_BITS) {
+      throw new IllegalArgumentException("Cannot compress input " + nextData
+                                          + " with more than " + NUM_DATA_BITS
+                                          + " bits (at index " + (inOffset+inputCompressable) + ")");
+    }
+    // Check whether a bigger divisor can be used:
+    while ((inputCompressable * (minBits+1)) <= NUM_DATA_BITS) {
+      minBits++;
+    }
+    // Put compression method in status bits and encode input data accordingly:
+    int s9 = uncompressed[inOffset]; // init with first input value, no masking needed for input values
+    switch (minBits) { // add status and later input values
+      // In some cases below masks would be needed when non zero pads would be allowed.
+      // Non zero pads could be allowed by adding a single mask here after or'ing all shifted input in a single expression.
+      case 28:
+        s9 |= STATUS_1_NUM_28_BITS;
+        compressed[outOffset] = s9;
+        return 1;
+      case 14:
+        s9 |= STATUS_2_NUM_14_BITS;
+        s9 |= uncompressed[inOffset+1] << 14;
+        compressed[outOffset] = s9;
+        return 2;
+      case 9:
+        s9 |= STATUS_3_NUM_99_10_BITS;
+        s9 |= uncompressed[inOffset+1] << 9;
+        s9 |= uncompressed[inOffset+2] << 18;
+        compressed[outOffset] = s9;
+        return 3;
+      case 7:
+        s9 |= STATUS_4_NUM_7_BITS;
+        s9 |= uncompressed[inOffset+1] << 7;
+        s9 |= uncompressed[inOffset+2] << 14;
+        s9 |= uncompressed[inOffset+3] << 21;
+        compressed[outOffset] = s9;
+        return 4;
+      case 5:
+        s9 |= STATUS_5_NUM_55_666_BITS;
+        s9 |= uncompressed[inOffset+1] << 5;
+        s9 |= uncompressed[inOffset+2] << 10;
+        s9 |= uncompressed[inOffset+3] << 16;
+        s9 |= uncompressed[inOffset+4] << 22;
+        compressed[outOffset] = s9;
+        return 5;
+      case 4:
+        s9 |= STATUS_7_NUM_4_BITS;
+        s9 |= uncompressed[inOffset+1] << 4;
+        s9 |= uncompressed[inOffset+2] << 8;
+        s9 |= uncompressed[inOffset+3] << 12;
+        s9 |= uncompressed[inOffset+4] << 16;
+        s9 |= uncompressed[inOffset+5] << 20;
+        s9 |= uncompressed[inOffset+6] << 24; // needs mask 0xf when non zero pad allowed.
+        compressed[outOffset] = s9;
+        return 7;
+      case 3:
+        s9 |= STATUS_9_NUM_33333333_4_BITS;
+        s9 |= uncompressed[inOffset+1] << 3;
+        s9 |= uncompressed[inOffset+2] << 6;
+        s9 |= uncompressed[inOffset+3] << 9;
+        s9 |= uncompressed[inOffset+4] << 12;
+        s9 |= uncompressed[inOffset+5] << 15;
+        s9 |= uncompressed[inOffset+6] << 18;
+        s9 |= uncompressed[inOffset+7] << 21;
+        s9 |= uncompressed[inOffset+8] << 24; // needs mask 0xf when non zero pad allowed.
+        compressed[outOffset] = s9;
+        return 9;
+      case 2:
+        s9 |= STATUS_14_NUM_2_BITS;
+        s9 |= uncompressed[inOffset+1] << 2;
+        s9 |= uncompressed[inOffset+2] << 4;
+        s9 |= uncompressed[inOffset+3] << 6;
+        s9 |= uncompressed[inOffset+4] << 8;
+        s9 |= uncompressed[inOffset+5] << 10;
+        s9 |= uncompressed[inOffset+6] << 12;
+        s9 |= uncompressed[inOffset+7] << 14;
+        s9 |= uncompressed[inOffset+8] << 16;
+        s9 |= uncompressed[inOffset+9] << 18; // needs mask 0x3 when non zero pad allowed.
+        s9 |= uncompressed[inOffset+10] << 20; // needs mask 0x3 when non zero pad allowed.
+        s9 |= uncompressed[inOffset+11] << 22; // needs mask 0x3 when non zero pad allowed.
+        s9 |= uncompressed[inOffset+12] << 24; // needs mask 0x3 when non zero pad allowed.
+        s9 |= uncompressed[inOffset+13] << 26; // needs mask 0x3 when non zero pad allowed.
+        compressed[outOffset] = s9;
+        return 14;
+      case 1:
+        s9 |= STATUS_28_NUM_1_BITS;
+        s9 |= uncompressed[inOffset+1] << 1;
+        s9 |= uncompressed[inOffset+2] << 2;
+        s9 |= uncompressed[inOffset+3] << 3;
+        s9 |= uncompressed[inOffset+4] << 4;
+        s9 |= uncompressed[inOffset+5] << 5;
+        s9 |= uncompressed[inOffset+6] << 6;
+        s9 |= uncompressed[inOffset+7] << 7;
+        s9 |= uncompressed[inOffset+8] << 8;
+        s9 |= uncompressed[inOffset+9] << 9;
+        s9 |= uncompressed[inOffset+10] << 10;
+        s9 |= uncompressed[inOffset+11] << 11;
+        s9 |= uncompressed[inOffset+12] << 12;
+        s9 |= uncompressed[inOffset+13] << 13;
+        s9 |= uncompressed[inOffset+14] << 14;
+        s9 |= uncompressed[inOffset+15] << 15; // needs mask 0x1 when non zero pad allowed.
+        s9 |= uncompressed[inOffset+16] << 16; // needs mask 0x1 when non zero pad allowed.
+        s9 |= uncompressed[inOffset+17] << 17; // needs mask 0x1 when non zero pad allowed.
+        s9 |= uncompressed[inOffset+18] << 18; // needs mask 0x1 when non zero pad allowed.
+        s9 |= uncompressed[inOffset+19] << 19; // needs mask 0x1 when non zero pad allowed.
+        s9 |= uncompressed[inOffset+20] << 20; // needs mask 0x1 when non zero pad allowed.
+        s9 |= uncompressed[inOffset+21] << 21; // needs mask 0x1 when non zero pad allowed.
+        s9 |= uncompressed[inOffset+22] << 22; // needs mask 0x1 when non zero pad allowed.
+        s9 |= uncompressed[inOffset+23] << 23; // needs mask 0x1 when non zero pad allowed.
+        s9 |= uncompressed[inOffset+24] << 24; // needs mask 0x1 when non zero pad allowed.
+        s9 |= uncompressed[inOffset+25] << 25; // needs mask 0x1 when non zero pad allowed.
+        s9 |= uncompressed[inOffset+26] << 26; // needs mask 0x1 when non zero pad allowed.
+        s9 |= uncompressed[inOffset+27] << 27; // needs mask 0x1 when non zero pad allowed.
+        compressed[outOffset] = s9;
+        return 28;
+      default:
+        throw new Error("Unknown minBits: " + minBits);
+    }
+  }
+
+ 
+  /** The decompressed array should have at least 28 elements from the given offset. */
+  public static int decompressSingle(int s9, int[] decompressed, int outOffset) {
+    switch (s9 & STATUS_MASK) {
+      case STATUS_1_NUM_28_BITS:
+        decompressed[outOffset] = s9 & BITS_28_MASK;
+        return 1;
+      case STATUS_2_NUM_14_BITS:
+        decompressed[outOffset] = s9 & BITS_14_MASK;
+        decompressed[++outOffset] = (s9 >>> 14) & BITS_14_MASK;
+        return 2;
+      case STATUS_3_NUM_99_10_BITS:
+        decompressed[outOffset] = s9 & BITS_9_MASK;
+        decompressed[outOffset+1] = (s9 >>> 9) & BITS_9_MASK;
+        decompressed[outOffset+2] = (s9 >>> 18) & BITS_10_MASK;
+        return 3;
+      case STATUS_4_NUM_7_BITS:
+        decompressed[outOffset] = s9 & BITS_7_MASK;
+        decompressed[outOffset+1] = (s9 >>> 7) & BITS_7_MASK;
+        decompressed[outOffset+2] = (s9 >>> 14) & BITS_7_MASK;
+        decompressed[outOffset+3] = (s9 >>> 21) & BITS_7_MASK;
+        return 4;
+      case STATUS_5_NUM_55_666_BITS:
+        decompressed[outOffset] = s9 & BITS_5_MASK;
+        decompressed[outOffset+1] = (s9 >>> 5) & BITS_5_MASK;
+        decompressed[outOffset+2] = (s9 >>> 10) & BITS_6_MASK; 
+        decompressed[outOffset+3] = (s9 >>> 16) & BITS_6_MASK; 
+        decompressed[outOffset+4] = (s9 >>> 22) & BITS_6_MASK;
+        return 5;
+      case STATUS_7_NUM_4_BITS:
+        decompressed[outOffset] = s9 & BITS_4_MASK;
+        decompressed[outOffset+1] = (s9 >>> 4) & BITS_4_MASK;
+        decompressed[outOffset+2] = (s9 >>> 8) & BITS_4_MASK; 
+        decompressed[outOffset+3] = (s9 >>> 12) & BITS_4_MASK; 
+        decompressed[outOffset+4] = (s9 >>> 16) & BITS_4_MASK;
+        decompressed[outOffset+5] = (s9 >>> 20) & BITS_4_MASK;
+        decompressed[outOffset+6] = (s9 >>> 24) & BITS_4_MASK;
+        return 7;
+      case STATUS_9_NUM_33333333_4_BITS:
+        decompressed[outOffset] = s9 & BITS_3_MASK;
+        decompressed[outOffset+1] = (s9 >>> 3) & BITS_3_MASK;
+        decompressed[outOffset+2] = (s9 >>> 6) & BITS_3_MASK; 
+        decompressed[outOffset+3] = (s9 >>> 9) & BITS_3_MASK; 
+        decompressed[outOffset+4] = (s9 >>> 12) & BITS_3_MASK;
+        decompressed[outOffset+5] = (s9 >>> 15) & BITS_3_MASK;
+        decompressed[outOffset+6] = (s9 >>> 18) & BITS_3_MASK;
+        decompressed[outOffset+7] = (s9 >>> 21) & BITS_3_MASK;
+        decompressed[outOffset+8] = (s9 >>> 24) & BITS_4_MASK;
+        return 9;
+      case STATUS_14_NUM_2_BITS:
+        decompressed[outOffset] = s9 & BITS_2_MASK;
+        decompressed[outOffset+1] = (s9 >>> 2) & BITS_2_MASK;
+        decompressed[outOffset+2] = (s9 >>> 4) & BITS_2_MASK;
+        decompressed[outOffset+3] = (s9 >>> 6) & BITS_2_MASK;
+        decompressed[outOffset+4] = (s9 >>> 8) & BITS_2_MASK;
+        decompressed[outOffset+5] = (s9 >>> 10) & BITS_2_MASK;
+        decompressed[outOffset+6] = (s9 >>> 12) & BITS_2_MASK;
+        decompressed[outOffset+7] = (s9 >>> 14) & BITS_2_MASK;
+        decompressed[outOffset+8] = (s9 >>> 16) & BITS_2_MASK;
+        decompressed[outOffset+9] = (s9 >>> 18) & BITS_2_MASK;
+        decompressed[outOffset+10] = (s9 >>> 20) & BITS_2_MASK;
+        decompressed[outOffset+11] = (s9 >>> 22) & BITS_2_MASK;
+        decompressed[outOffset+12] = (s9 >>> 24) & BITS_2_MASK;
+        decompressed[outOffset+13] = (s9 >>> 26) & BITS_2_MASK;
+        return 14;
+      case STATUS_28_NUM_1_BITS:
+        decompressed[outOffset] = s9 & BITS_1_MASK;
+        decompressed[outOffset+1] = (s9 >>> 1) & BITS_1_MASK;
+        decompressed[outOffset+2] = (s9 >>> 2) & BITS_1_MASK;
+        decompressed[outOffset+3] = (s9 >>> 3) & BITS_1_MASK;
+        decompressed[outOffset+4] = (s9 >>> 4) & BITS_1_MASK;
+        decompressed[outOffset+5] = (s9 >>> 5) & BITS_1_MASK;
+        decompressed[outOffset+6] = (s9 >>> 6) & BITS_1_MASK;
+        decompressed[outOffset+7] = (s9 >>> 7) & BITS_1_MASK;
+        decompressed[outOffset+8] = (s9 >>> 8) & BITS_1_MASK;
+        decompressed[outOffset+9] = (s9 >>> 9) & BITS_1_MASK;
+        decompressed[outOffset+10] = (s9 >>> 10) & BITS_1_MASK;
+        decompressed[outOffset+11] = (s9 >>> 11) & BITS_1_MASK;
+        decompressed[outOffset+12] = (s9 >>> 12) & BITS_1_MASK;
+        decompressed[outOffset+13] = (s9 >>> 13) & BITS_1_MASK;
+        decompressed[outOffset+14] = (s9 >>> 14) & BITS_1_MASK;
+        decompressed[outOffset+15] = (s9 >>> 15) & BITS_1_MASK;
+        decompressed[outOffset+16] = (s9 >>> 16) & BITS_1_MASK;
+        decompressed[outOffset+17] = (s9 >>> 17) & BITS_1_MASK;
+        decompressed[outOffset+18] = (s9 >>> 18) & BITS_1_MASK;
+        decompressed[outOffset+19] = (s9 >>> 19) & BITS_1_MASK;
+        decompressed[outOffset+20] = (s9 >>> 20) & BITS_1_MASK;
+        decompressed[outOffset+21] = (s9 >>> 21) & BITS_1_MASK;
+        decompressed[outOffset+22] = (s9 >>> 22) & BITS_1_MASK;
+        decompressed[outOffset+23] = (s9 >>> 23) & BITS_1_MASK;
+        decompressed[outOffset+24] = (s9 >>> 24) & BITS_1_MASK;
+        decompressed[outOffset+25] = (s9 >>> 25) & BITS_1_MASK;
+        decompressed[outOffset+26] = (s9 >>> 26) & BITS_1_MASK;
+        decompressed[outOffset+27] = (s9 >>> 27) & BITS_1_MASK;
+        return 28;
+      default:
+        throw new IllegalArgumentException("Unknown Simple9 status: " + ((s9 >>> 28) & 0xf));
+    }
+  }
+
+    
+  /** Encode positive integers from an uncompressed array at a given inOffset
+   * into an array of 32 bit integers at a given output offset using the
+   * <code>compressSingle</code> method.
+   * The maximum number in the input array should not exceed 2**28 - 1.
+   * The input array should have at least 27 numbers available after inOffset+inSize.
+   * At most inSize numbers will be compressed.
+   * Some bits may be taken from remaining elements of the given array.
+   * Return the number of integers that where added to the compressed array
+   * from the given outOffset.
+   * The output array should be at least as big as the input array,
+   * a smaller array may cause an IndexOutOfBoundsException.
+   */
+  public static int compressArray(int[] uncompressed, int inOffset, int inSize,
+                                  int[] compressed, int outOffset)
+  {
+    int totalOut = 0;
+    while (inSize > 0) {
+      int encoded = compressSingle(uncompressed, inOffset, inSize,
+                                   compressed, outOffset + totalOut);
+      inOffset += encoded;
+      inSize -= encoded;
+      totalOut += 1;
+    }
+    return totalOut;
+  }
+    
+  /** Decompress integers from an array of compressed integers at a given offset and size
+   * into a integers at a given output offset.
+   * The encoding is supposed to have been done by the <code>compressArray</code> method.
+   * Return the number of output integers that where added to the uncompressed array.
+   * The output array should be at 28 times bigger than the input array,
+   * a smaller output array may cause an IndexOutOfBoundsException.
+   */
+  public static int decompressArray(int[] compressed, int inOffset, int inSize,
+                                    int[] decompressed, int outOffset)
+  {
+    int totalOut = 0;
+    while (inSize > 0) {
+      int decoded = decompressSingle(compressed[inOffset],
+                                     decompressed, outOffset + totalOut);
+      inOffset += 1;
+      inSize -= 1;
+      totalOut += decoded;
+    }
+    return totalOut;
+  }
+ 
+  /** Decompress integers from an array of compressed integers at a given offset
+   * into a integers at a given output offset and size.
+   * The encoding is supposed to have been done by the <code>compressArray</code> method.
+   * Return the number of output integers that where actually added to the uncompressed array.
+   * The output array should be at least 27 elements bigger than the given output size,
+   * a smaller output array may cause an IndexOutOfBoundsException.
+   * The input array should contain at least as many compressed integers at the given inOffset
+   * as needed to decompress the given output size, a smaller input array will cause an
+   * IndexOutOfBoundsException.
+   */
+  public static int decompressArray(int[] compressed, int inOffset,
+                                    int[] decompressed, int outOffset, int outSize)
+  {
+    int totalOut = 0;
+    while (totalOut < outSize) {
+      int decoded = decompressSingle(compressed[inOffset],
+                                     decompressed, outOffset + totalOut);
+      inOffset += 1;
+      totalOut += decoded;
+    }
+    return totalOut;
+  }
+}
Index: src/test/org/apache/lucene/util/TestSimple9.java
===================================================================
--- src/test/org/apache/lucene/util/TestSimple9.java	(revision 0)
+++ src/test/org/apache/lucene/util/TestSimple9.java	(revision 0)
@@ -0,0 +1,287 @@
+package org.apache.lucene.util;
+
+/**
+ * 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.
+ */
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test cases for class Simple9. Assumes JUnit 4.0 or later.
+ */
+public class TestSimple9 { // does not need anything from LuceneTestCaseJ4
+  
+  private String dump(int[] data) {
+    StringBuffer r = new StringBuffer();
+    r.append('[');
+    for (int i = 0; i < data.length; i++) {
+      if (i > 0) r.append(',');
+      r.append(data[i]);
+    }
+    r.append(']');
+    return r.toString();
+  }
+  
+  private String dumpDiffBits(int a, int b) {
+    int d = a ^ b;
+    StringBuffer r = new StringBuffer();
+    r.append("0b");
+    for (int i = 0; i < 32; i++) {
+      r.append((d >>> (31-i)) & 1);
+    }
+    return r.toString();
+  }
+
+  private void singleS9Test(int[] data, int simple9, int numCompressed, int inSize) {
+    int[] compressed = new int[1];
+    int actualCompressed = Simple9.compressSingle(data, 0, inSize, compressed, 0);
+    assertEquals("Number of compressed data", numCompressed, actualCompressed);
+    int[] decompressed = new int[numCompressed];
+    int numDecompressed = Simple9.decompressSingle(compressed[0], decompressed, 0);
+    assertEquals("Number of decompressed data", numDecompressed, numCompressed);
+    for (int i = 0; i < numDecompressed; i++) {
+      assertEquals("Decompressed data element " + i, decompressed[i], data[i]);
+    }
+    assertEquals("Simple9 of " + dump(data)
+                  + ", diff bits: " + dumpDiffBits(compressed[0], simple9),
+                  compressed[0], simple9); // delayed, decompression failure message is more useful.
+  }
+
+  private void singleS9Test(int[] data, int simple9, int numCompressed) {
+    singleS9Test(data, simple9, numCompressed, data.length);
+  }
+
+  private void singleS9Test(int[] data, int simple9) {
+    singleS9Test(data, simple9, data.length);
+  }
+
+  @Test public void num01_28bit01() {
+    singleS9Test(new int[] {0x0fffffff}, 0x1fffffff);
+  }
+  @Test public void num01_28bit02() {
+    singleS9Test(new int[] {0x00000004}, 0x10000004);
+  }
+  @Test public void num01_28bit03() {
+    singleS9Test(new int[] {0x00000000}, 0x10000000);
+  }
+  @Test public void num01_28bitx01() {
+    singleS9Test(new int[] {0x0fffffff,0x0}, 0x1fffffff, 1); // compress only first number
+  }
+  @Test public void num01_28bitx02() {
+    singleS9Test(new int[] {0x07fff,0x0,0x0}, 0x10007fff, 1);
+  }
+
+  @Test public void num02_14bit01() {
+    singleS9Test(new int[] {0x3fff,0x3fff}, 0x2fffffff);
+  }
+  @Test public void num02_14bit02() {
+    singleS9Test(new int[] {0x3fff,0x0000}, 0x20003fff);
+  }
+  @Test public void num02_14bit03() {
+    singleS9Test(new int[] {0x0000,0x3fff}, 0x2fffc000);
+  }
+  @Test public void num02_14bit04() {
+    singleS9Test(new int[] {0x0000,0x0000}, 0x20000000);
+  }
+  @Test public void num02_14bitx01() {
+    singleS9Test(new int[] {0x3fff,0x0000,0x0000}, 0x20003fff, 2);
+  }
+  @Test public void num02_14bitx02() {
+    singleS9Test(new int[] {0x3ff,0x0000,0x0000}, 0x200003ff, 2);
+  }
+
+  @Test public void num03_9bit01() {
+    singleS9Test(new int[] {0x1ff,0x1ff,0x1ff}, 0x37ffffff);
+  }
+
+  @Test public void num03_9bit02() {
+    singleS9Test(new int[] {0x000,0x000,0x000}, 0x30000000);
+  }
+
+  @Test public void num03_9bit03() {
+    singleS9Test(new int[] {0x000,0x000,0x0ff}, 0x33fc0000);
+  }
+
+  @Test public void num03_9bit04() {
+    singleS9Test(new int[] {0x000,0x000,0x03f}, 0x30fc0000);
+  }
+
+  @Test public void num03_99_10bit01() {
+    singleS9Test(new int[] {0x1ff,0x1ff,0x3ff}, 0x3fffffff);
+  }
+
+  @Test public void num03_99_10bit02() {
+    singleS9Test(new int[] {0x02f,0x000,0x3ff}, 0x3ffc002f);
+  }
+
+  @Test public void num03_99_10bit03() {
+    singleS9Test(new int[] {0x000,0x000,0x3ff}, 0x3ffc0000);
+  }
+
+  @Test public void num03_9bitx01() {
+    singleS9Test(new int[] {0x1ff,0x000,0x000,0x000}, 0x300001ff, 3);
+  }
+
+  @Test public void num03_9bitx02() {
+    singleS9Test(new int[] {0x0ff,0x000,0x000,0x000}, 0x300000ff, 3);
+  }
+
+  
+  @Test public void num04_7bit01() {
+    singleS9Test(new int[] {0x7f,0x7f,0x7f,0x7f}, 0x4fffffff);
+  }
+
+  @Test public void num04_7bit02() {
+    singleS9Test(new int[] {0x00,0x7f,0x00,0x00}, 0x40003f80);
+  }
+
+  @Test public void num04_7bitx01() {
+    singleS9Test(new int[] {0x7f,0x00,0x00,0x00,0x00}, 0x4000007f, 4);
+  }
+
+  @Test public void num04_7bitx02() {
+    singleS9Test(new int[] {0x1f,0x00,0x7f,0x00,0x00}, 0x401fc01f, 4); // not 55_666
+  }
+
+  @Test public void num05_5bit01() {
+    singleS9Test(new int[] {0x1f,0x1f,0x1f,0x1f,0x1f}, 0x57df7fff);
+  }
+
+  @Test public void num05_5bit02() {
+    singleS9Test(new int[] {0x00,0x00,0x00,0x00,0x1f}, 0x57c00000);
+  }
+
+  @Test public void num05_5bit03() {
+    singleS9Test(new int[] {0x1f,0x00,0x00,0x00,0x00}, 0x5000001f);
+  }
+
+  @Test public void num05_5bitx01() {
+    singleS9Test(new int[] {0x1f,0x1f,0x1f,0x00,0x00,0x00}, 0x50007fff, 5);
+  }
+
+  @Test public void num05_5bitx02() {
+    // just not 7 compressed, 6 available input, one 0 pad, as tested below.
+    singleS9Test(new int[] {0xf,0xf,0xf,0xf,0xf,0xf,0x0}, 0x53cf3def, 5, 5); // 5 compressed, 5 available input, two 0 pads ignored.
+    
+  }
+  @Test public void num05_55_666bit01() {
+    singleS9Test(new int[] {0x1f,0x00,0x3f,0x00,0x00}, 0x5000fc1f);
+  }
+
+  @Test public void num05_55_666bit02() {
+    singleS9Test(new int[] {0x1f,0x00,0x00,0x3f,0x00}, 0x503f001f);
+  }
+
+  @Test public void num05_55_666bit03() {
+    singleS9Test(new int[] {0x1f,0x00,0x00,0x00,0x3f}, 0x5fc0001f);
+  }
+
+  @Test public void num05_55_666bitx03() {
+    singleS9Test(new int[] {0x1f,0x00,0x00,0x00,0x3f,0x00}, 0x5fc0001f, 5);
+  }
+
+
+  @Test public void num07_4bit01() {
+    singleS9Test(new int[] {0xf,0xf,0xf,0xf,0xf,0xf,0xf}, 0x6fffffff);
+  }
+
+  @Test public void num07_4bit02() {
+    singleS9Test(new int[] {0xf,0xf,0xf,0xf,0xf,0xf,0x0}, 0x60ffffff, 7, 6); // 7 compressed, 6 available input, one 0 pad.
+  }
+
+  @Test public void num07_4bit03() {
+    singleS9Test(new int[] {0x0,0xf,0x0,0xf,0x0,0xf,0x0}, 0x60f0f0f0);
+  }
+
+  @Test public void num07_4bit04() {
+    singleS9Test(new int[] {0xf,0x0,0xf,0x0,0xf,0x0,0xf}, 0x6f0f0f0f);
+  }
+
+  @Test public void num09_3bit01() {
+    singleS9Test(new int[] {0x7,0x7,0x7,0x7,0x7,0x7,0x7,0x7,0x7}, 0x77ffffff);
+  }
+
+  @Test public void num09_3_4bit01() {
+    singleS9Test(new int[] {0x7,0x7,0x7,0x7,0x7,0x7,0x7,0x7,0xf}, 0x7fffffff);
+  }
+
+  @Test public void num09_3_4bit02() {
+    singleS9Test(new int[] {0x0,0x7,0x0,0x7,0x0,0x7,0x0,0x7,0x0}, 0x70e38e38);
+  }
+
+  @Test public void num09_3_4bit03() {
+    singleS9Test(new int[] {0x7,0x0,0x7,0x0,0x7,0x0,0x7,0x0,0xf}, 0x7f1c71c7);
+  }
+
+  @Test public void num09_3bitx01() {
+    singleS9Test(new int[] {0x7,0x7,0x7,0x7,0x7,0x7,0x7,0x7,0x0}, 0x70ffffff, 9, 8); // 9 compressed, 8 available input, one 0 pad
+  }
+
+  @Test public void num09_3bitx02() {
+    singleS9Test(new int[] {3,3,3,3,3,3,3,3,3,0,0,0,0,0}, 0x736db6db, 9, 9); // 9 compressed, 9 available input, five ignored.
+  }
+
+  @Test public void num14_2bit01() {
+    singleS9Test(new int[] {3,3,3,3,3,3,3,3,3,3,3,3,3,3}, 0x8fffffff);
+  }
+
+  @Test public void num14_2bit02() {
+    singleS9Test(new int[] {3,0,3,0,3,0,3,0,3,0,3,0,3,0}, 0x83333333);
+  }
+
+  @Test public void num14_2bit03() {
+    singleS9Test(new int[] {0,3,0,3,0,3,0,3,0,3,0,3,0,3}, 0x8ccccccc);
+  }
+
+  @Test public void num14_2bitx01() {
+    singleS9Test(new int[] {3,3,3,3,3,3,3,3,3,3,0,0,0,0}, 0x800fffff, 14, 10); // 14 compressed, 10 available input, four 0 pads
+  }
+
+  @Test public void num14_2bitx02() {
+    singleS9Test(new int[] {1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0x85555555, 14, 14); // 14 compressed, 14 available input, 14 0 pads.
+  }
+
+  @Test public void num28_1bit01() {
+    singleS9Test(new int[] {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, 0x9fffffff);
+  }
+
+  @Test public void num28_1bit02() {
+    singleS9Test(new int[] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0x90000000);
+  }
+
+  @Test public void num28_1bit03() {
+    singleS9Test(new int[] {1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0}, 0x95555555);
+  }
+
+  @Test public void num28_1bit04() {
+    singleS9Test(new int[] {0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1}, 0x9aaaaaaa);
+  }
+
+  @Test public void num28_1bitx01() {
+    singleS9Test(new int[] {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0x90007fff, 28, 15); // 28 compressed, 15 available input, 13 0 pads.
+  }
+
+
+
+/* junit javadoc example: @Test(expected=IndexOutOfBoundsException.class) */
+
+/* Still to be tested: out of bounds offSets, sizes, (un)compressed data.
+  public static int compressSingle(int[] uncompressed, int inOffset, int inSize,
+                                   int[] compressed, int outOffset) {
+  public static int decompressSingle(int s9, int[] decompressed, int outOffset) {
+ */
+}
