From 839879b95c19c1db3786ed23ef9bc525e460efbe Mon Sep 17 00:00:00 2001
From: Kevin Wang <kevin807359@gmail.com>
Date: Sun, 13 Apr 2014 22:58:47 +1000
Subject: [PATCH] LUCENE-5596

---
 .../simpletext/SimpleTextStoredFieldsReader.java   |   6 +
 .../simpletext/SimpleTextStoredFieldsWriter.java   |   9 +
 .../lucene/analysis/BigNumericTokenStream.java     | 190 +++++++
 .../compressing/CompressingStoredFieldsReader.java |   9 +-
 .../compressing/CompressingStoredFieldsWriter.java |   8 +-
 .../lucene40/Lucene40StoredFieldsReader.java       |   6 +
 .../lucene40/Lucene40StoredFieldsWriter.java       |   6 +
 .../apache/lucene/document/BigIntegerField.java    |  89 ++++
 .../lucene/document/BigNumericDocValuesField.java  |  49 ++
 .../document/DocumentStoredFieldVisitor.java       |   6 +
 .../src/java/org/apache/lucene/document/Field.java |  76 ++-
 .../java/org/apache/lucene/document/FieldType.java |   4 +-
 .../org/apache/lucene/document/StoredField.java    |  13 +
 .../apache/lucene/index/StoredFieldVisitor.java    |   5 +
 .../java/org/apache/lucene/search/FieldCache.java  |  55 ++
 .../org/apache/lucene/search/FieldCacheImpl.java   | 106 +++-
 .../org/apache/lucene/search/FieldComparator.java  |  82 +++
 .../apache/lucene/search/NumericRangeFilter.java   |  13 +
 .../apache/lucene/search/NumericRangeQuery.java    |  48 +-
 .../java/org/apache/lucene/search/SortField.java   |  10 +-
 .../java/org/apache/lucene/store/DataInput.java    |  13 +
 .../java/org/apache/lucene/store/DataOutput.java   |  12 +
 .../org/apache/lucene/util/BigNumericUtils.java    | 189 +++++++
 .../lucene/analysis/TestBigNumericTokenStream.java |  98 ++++
 .../search/TestNumericRangeQueryBigInteger.java    | 579 +++++++++++++++++++++
 .../apache/lucene/search/TestSortDocValues.java    | 134 +++++
 .../apache/lucene/util/TestBigNumericUtils.java    | 149 ++++++
 .../builders/NumericRangeQueryNodeBuilder.java     |  12 +-
 .../flexible/standard/config/NumericConfig.java    |  27 +-
 .../standard/nodes/NumericRangeQueryNode.java      |   4 +
 .../processors/NumericQueryNodeProcessor.java      |  14 +-
 .../processors/NumericRangeQueryNodeProcessor.java |  29 +-
 .../xml/builders/NumericRangeFilterBuilder.java    |  10 +-
 .../xml/builders/NumericRangeQueryBuilder.java     |  10 +-
 .../flexible/standard/TestNumericQueryParser.java  |  25 +-
 .../builders/TestNumericRangeFilterBuilder.java    |  30 ++
 .../xml/builders/TestNumericRangeQueryBuilder.java |  29 ++
 .../index/BaseStoredFieldsFormatTestCase.java      |  37 +-
 .../lucene/index/FieldFilterAtomicReader.java      |   6 +
 39 files changed, 2131 insertions(+), 66 deletions(-)
 create mode 100644 lucene/core/src/java/org/apache/lucene/analysis/BigNumericTokenStream.java
 create mode 100644 lucene/core/src/java/org/apache/lucene/document/BigIntegerField.java
 create mode 100644 lucene/core/src/java/org/apache/lucene/document/BigNumericDocValuesField.java
 create mode 100644 lucene/core/src/java/org/apache/lucene/util/BigNumericUtils.java
 create mode 100644 lucene/core/src/test/org/apache/lucene/analysis/TestBigNumericTokenStream.java
 create mode 100644 lucene/core/src/test/org/apache/lucene/search/TestNumericRangeQueryBigInteger.java
 create mode 100644 lucene/core/src/test/org/apache/lucene/util/TestBigNumericUtils.java

diff --git a/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextStoredFieldsReader.java b/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextStoredFieldsReader.java
index 15c7f51..2cb6e5f 100644
--- a/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextStoredFieldsReader.java
+++ b/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextStoredFieldsReader.java
@@ -18,6 +18,7 @@ package org.apache.lucene.codecs.simpletext;
  */
 
 import java.io.IOException;
+import java.math.BigInteger;
 import java.nio.charset.StandardCharsets;
 
 import org.apache.lucene.codecs.StoredFieldsReader;
@@ -125,6 +126,8 @@ public class SimpleTextStoredFieldsReader extends StoredFieldsReader {
         type = TYPE_FLOAT;
       } else if (equalsAt(TYPE_DOUBLE, scratch, TYPE.length)) {
         type = TYPE_DOUBLE;
+      } else if (equalsAt(TYPE_BIG_INTEGER, scratch, TYPE.length)) {
+        type = TYPE_BIG_INTEGER;
       } else {
         throw new RuntimeException("unknown field type");
       }
@@ -163,6 +166,9 @@ public class SimpleTextStoredFieldsReader extends StoredFieldsReader {
     } else if (type == TYPE_DOUBLE) {
       UnicodeUtil.UTF8toUTF16(scratch.bytes, scratch.offset+VALUE.length, scratch.length-VALUE.length, scratchUTF16);
       visitor.doubleField(fieldInfo, Double.parseDouble(scratchUTF16.toString()));
+    } else if (type == TYPE_BIG_INTEGER) {
+      UnicodeUtil.UTF8toUTF16(scratch.bytes, scratch.offset+VALUE.length, scratch.length-VALUE.length, scratchUTF16);
+      visitor.bigIntegerField(fieldInfo, new BigInteger(scratchUTF16.toString()));
     }
   }
 
diff --git a/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextStoredFieldsWriter.java b/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextStoredFieldsWriter.java
index a31d791..dd02f94 100644
--- a/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextStoredFieldsWriter.java
+++ b/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextStoredFieldsWriter.java
@@ -18,6 +18,7 @@ package org.apache.lucene.codecs.simpletext;
  */
 
 import java.io.IOException;
+import java.math.BigInteger;
 
 import org.apache.lucene.codecs.StoredFieldsWriter;
 import org.apache.lucene.index.FieldInfo;
@@ -50,6 +51,7 @@ public class SimpleTextStoredFieldsWriter extends StoredFieldsWriter {
   final static BytesRef TYPE_LONG   = new BytesRef("long");
   final static BytesRef TYPE_FLOAT  = new BytesRef("float");
   final static BytesRef TYPE_DOUBLE = new BytesRef("double");
+  final static BytesRef TYPE_BIG_INTEGER = new BytesRef("biginteger");
 
   final static BytesRef END      = new BytesRef("END");
   final static BytesRef DOC      = new BytesRef("doc ");
@@ -130,6 +132,13 @@ public class SimpleTextStoredFieldsWriter extends StoredFieldsWriter {
         write(VALUE);
         write(Double.toString(n.doubleValue()));
         newLine();
+      } else if (n instanceof BigInteger) {
+        write(TYPE_BIG_INTEGER);
+        newLine();
+
+        write(VALUE);
+        write(n.toString());
+        newLine();
       } else {
         throw new IllegalArgumentException("cannot store numeric type " + n.getClass());
       }
diff --git a/lucene/core/src/java/org/apache/lucene/analysis/BigNumericTokenStream.java b/lucene/core/src/java/org/apache/lucene/analysis/BigNumericTokenStream.java
new file mode 100644
index 0000000..5f98186
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/analysis/BigNumericTokenStream.java
@@ -0,0 +1,190 @@
+package org.apache.lucene.analysis;
+
+/*
+ * 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.apache.lucene.analysis.tokenattributes.CharTermAttribute;
+import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
+import org.apache.lucene.analysis.tokenattributes.TermToBytesRefAttribute;
+import org.apache.lucene.analysis.tokenattributes.TypeAttribute;
+import org.apache.lucene.util.Attribute;
+import org.apache.lucene.util.AttributeImpl;
+import org.apache.lucene.util.AttributeReflector;
+import org.apache.lucene.util.BigNumericUtils;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.NumericUtils;
+
+import java.math.BigInteger;
+
+
+/**
+ * Modified from {@link NumericTokenStream} to support {@link java.math.BigInteger}
+ */
+public final class BigNumericTokenStream extends TokenStream {
+  public static final String TOKEN_TYPE_FULL_PREC  = NumericTokenStream.TOKEN_TYPE_FULL_PREC;
+
+  public static final String TOKEN_TYPE_LOWER_PREC = NumericTokenStream.TOKEN_TYPE_LOWER_PREC;
+
+  public interface NumericTermAttribute extends Attribute {
+    int getShift();
+    BigInteger getRawValue();
+    int getValueSize();
+    void init(BigInteger value, int valueSize, int precisionStep, int shift);
+    void setShift(int shift);
+    int incShift();
+  }
+
+  // just a wrapper to prevent adding CTA
+  private static final class NumericAttributeFactory extends AttributeFactory {
+    private final AttributeFactory delegate;
+
+    NumericAttributeFactory(AttributeFactory delegate) {
+      this.delegate = delegate;
+    }
+
+    @Override
+    public AttributeImpl createAttributeInstance(Class<? extends Attribute> attClass) {
+      if (CharTermAttribute.class.isAssignableFrom(attClass))
+        throw new IllegalArgumentException("BigNumericTokenStream does not support CharTermAttribute.");
+      return delegate.createAttributeInstance(attClass);
+    }
+  }
+
+  public static final class NumericTermAttributeImpl extends AttributeImpl implements NumericTermAttribute,TermToBytesRefAttribute {
+    private BigInteger value;
+    private int valueSize = 0, shift = 0, precisionStep = 0;
+    private BytesRef bytes = new BytesRef();
+
+    public NumericTermAttributeImpl() {}
+
+    @Override
+    public BytesRef getBytesRef() {
+      return bytes;
+    }
+
+    @Override
+    public int fillBytesRef() {
+      return BigNumericUtils.bigIntegerToPrefixCoded(value, shift, bytes, valueSize);
+    }
+
+    @Override
+    public int getShift() { return shift; }
+    @Override
+    public void setShift(int shift) { this.shift = shift; }
+    @Override
+    public int incShift() {
+      return (shift += precisionStep);
+    }
+
+    @Override
+    public BigInteger getRawValue() {
+      return value.and(BigInteger.valueOf((1L << shift) - 1L).not());
+    }
+    @Override
+    public int getValueSize() { return valueSize; }
+
+    @Override
+    public void init(BigInteger value, int valueSize, int precisionStep, int shift) {
+      this.value = value;
+      this.valueSize = valueSize;
+      this.precisionStep = precisionStep;
+      this.shift = shift;
+    }
+
+    @Override
+    public void clear() {
+    }
+
+    @Override
+    public void reflectWith(AttributeReflector reflector) {
+      fillBytesRef();
+      reflector.reflect(TermToBytesRefAttribute.class, "bytes", BytesRef.deepCopyOf(bytes));
+      reflector.reflect(NumericTermAttribute.class, "shift", shift);
+      reflector.reflect(NumericTermAttribute.class, "rawValue", getRawValue());
+      reflector.reflect(NumericTermAttribute.class, "valueSize", valueSize);
+    }
+
+    @Override
+    public void copyTo(AttributeImpl target) {
+      final NumericTermAttribute a = (NumericTermAttribute) target;
+      a.init(value, valueSize, precisionStep, shift);
+    }
+  }
+
+  public BigNumericTokenStream() {
+    this(AttributeFactory.DEFAULT_ATTRIBUTE_FACTORY, NumericUtils.PRECISION_STEP_DEFAULT, BigNumericUtils.VALUE_SIZE_DEFAULT);
+  }
+
+
+  public BigNumericTokenStream(final int precisionStep) {
+    this(AttributeFactory.DEFAULT_ATTRIBUTE_FACTORY, precisionStep, BigNumericUtils.VALUE_SIZE_DEFAULT);
+  }
+
+  public BigNumericTokenStream(final int precisionStep, final int valSize) {
+    this(AttributeFactory.DEFAULT_ATTRIBUTE_FACTORY, precisionStep, valSize);
+  }
+
+  public BigNumericTokenStream(AttributeFactory factory, final int precisionStep, final int valSize) {
+    super(new NumericAttributeFactory(factory));
+    if (precisionStep < 1)
+      throw new IllegalArgumentException("precisionStep must be >=1");
+    this.precisionStep = precisionStep;
+    this.valSize = valSize;
+    numericAtt.setShift(-precisionStep);
+  }
+
+  public BigNumericTokenStream setBigIntValue(final BigInteger value) {
+    numericAtt.init(value, valSize, precisionStep, -precisionStep);
+    init = true;
+    return this;
+  }
+
+  @Override
+  public void reset() {
+    if (!init)
+      throw new IllegalStateException("call set???Value() before usage");
+    numericAtt.setShift(-precisionStep);
+  }
+
+  @Override
+  public boolean incrementToken() {
+    if (!init)
+      throw new IllegalStateException("call set???Value() before usage");
+
+    // this will only clear all other attributes in this TokenStream
+    clearAttributes();
+
+    final int shift = numericAtt.incShift();
+    typeAtt.setType((shift == 0) ? TOKEN_TYPE_FULL_PREC : TOKEN_TYPE_LOWER_PREC);
+    posIncrAtt.setPositionIncrement((shift == 0) ? 1 : 0);
+    return (shift < valSize);
+  }
+
+  /** Returns the precision step. */
+  public int getPrecisionStep() {
+    return precisionStep;
+  }
+
+  // members
+  private final NumericTermAttribute numericAtt = addAttribute(NumericTermAttribute.class);
+  private final TypeAttribute typeAtt = addAttribute(TypeAttribute.class);
+  private final PositionIncrementAttribute posIncrAtt = addAttribute(PositionIncrementAttribute.class);
+
+  private int valSize = 0;
+  private final int precisionStep;
+  private boolean init = false;
+}
diff --git a/lucene/core/src/java/org/apache/lucene/codecs/compressing/CompressingStoredFieldsReader.java b/lucene/core/src/java/org/apache/lucene/codecs/compressing/CompressingStoredFieldsReader.java
index 08cf56c..2c97456 100644
--- a/lucene/core/src/java/org/apache/lucene/codecs/compressing/CompressingStoredFieldsReader.java
+++ b/lucene/core/src/java/org/apache/lucene/codecs/compressing/CompressingStoredFieldsReader.java
@@ -20,6 +20,7 @@ package org.apache.lucene.codecs.compressing;
 import static org.apache.lucene.codecs.compressing.CompressingStoredFieldsWriter.BYTE_ARR;
 import static org.apache.lucene.codecs.compressing.CompressingStoredFieldsWriter.CODEC_SFX_DAT;
 import static org.apache.lucene.codecs.compressing.CompressingStoredFieldsWriter.CODEC_SFX_IDX;
+import static org.apache.lucene.codecs.compressing.CompressingStoredFieldsWriter.NUMERIC_BIG_INTEGER;
 import static org.apache.lucene.codecs.compressing.CompressingStoredFieldsWriter.NUMERIC_DOUBLE;
 import static org.apache.lucene.codecs.compressing.CompressingStoredFieldsWriter.NUMERIC_FLOAT;
 import static org.apache.lucene.codecs.compressing.CompressingStoredFieldsWriter.NUMERIC_INT;
@@ -201,6 +202,9 @@ public final class CompressingStoredFieldsReader extends StoredFieldsReader {
       case NUMERIC_DOUBLE:
         visitor.doubleField(info, Double.longBitsToDouble(in.readLong()));
         break;
+      case NUMERIC_BIG_INTEGER:
+        visitor.bigIntegerField(info, in.readBigInteger());
+        break;
       default:
         throw new AssertionError("Unknown type flag: " + Integer.toHexString(bits));
     }
@@ -221,6 +225,9 @@ public final class CompressingStoredFieldsReader extends StoredFieldsReader {
       case NUMERIC_DOUBLE:
         in.readLong();
         break;
+      case NUMERIC_BIG_INTEGER:
+        in.readBigInteger();
+        break;
       default:
         throw new AssertionError("Unknown type flag: " + Integer.toHexString(bits));
     }
@@ -347,7 +354,7 @@ public final class CompressingStoredFieldsReader extends StoredFieldsReader {
       final FieldInfo fieldInfo = fieldInfos.fieldInfo(fieldNumber);
 
       final int bits = (int) (infoAndBits & TYPE_MASK);
-      assert bits <= NUMERIC_DOUBLE: "bits=" + Integer.toHexString(bits);
+      assert bits <= NUMERIC_BIG_INTEGER: "bits=" + Integer.toHexString(bits);
 
       switch(visitor.needsField(fieldInfo)) {
         case YES:
diff --git a/lucene/core/src/java/org/apache/lucene/codecs/compressing/CompressingStoredFieldsWriter.java b/lucene/core/src/java/org/apache/lucene/codecs/compressing/CompressingStoredFieldsWriter.java
index 38911d6..d77b605 100644
--- a/lucene/core/src/java/org/apache/lucene/codecs/compressing/CompressingStoredFieldsWriter.java
+++ b/lucene/core/src/java/org/apache/lucene/codecs/compressing/CompressingStoredFieldsWriter.java
@@ -21,6 +21,7 @@ import static org.apache.lucene.codecs.lucene40.Lucene40StoredFieldsWriter.FIELD
 import static org.apache.lucene.codecs.lucene40.Lucene40StoredFieldsWriter.FIELDS_INDEX_EXTENSION;
 
 import java.io.IOException;
+import java.math.BigInteger;
 import java.util.Arrays;
 
 import org.apache.lucene.codecs.CodecUtil;
@@ -63,8 +64,9 @@ public final class CompressingStoredFieldsWriter extends StoredFieldsWriter {
   static final int  NUMERIC_FLOAT = 0x03;
   static final int   NUMERIC_LONG = 0x04;
   static final int NUMERIC_DOUBLE = 0x05;
+  static final int NUMERIC_BIG_INTEGER = 0x06;
 
-  static final int TYPE_BITS = PackedInts.bitsRequired(NUMERIC_DOUBLE);
+  static final int TYPE_BITS = PackedInts.bitsRequired(NUMERIC_BIG_INTEGER);
   static final int TYPE_MASK = (int) PackedInts.maxValue(TYPE_BITS);
 
   static final String CODEC_SFX_IDX = "Index";
@@ -256,6 +258,8 @@ public final class CompressingStoredFieldsWriter extends StoredFieldsWriter {
         bits = NUMERIC_FLOAT;
       } else if (number instanceof Double) {
         bits = NUMERIC_DOUBLE;
+      } else if (number instanceof BigInteger) {
+        bits = NUMERIC_BIG_INTEGER;
       } else {
         throw new IllegalArgumentException("cannot store numeric type " + number.getClass());
       }
@@ -292,6 +296,8 @@ public final class CompressingStoredFieldsWriter extends StoredFieldsWriter {
         bufferedDocs.writeInt(Float.floatToIntBits(number.floatValue()));
       } else if (number instanceof Double) {
         bufferedDocs.writeLong(Double.doubleToLongBits(number.doubleValue()));
+      } else if (number instanceof BigInteger) {
+        bufferedDocs.writeBigInteger((BigInteger) number);
       } else {
         throw new AssertionError("Cannot get here");
       }
diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40StoredFieldsReader.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40StoredFieldsReader.java
index 47dc8e1..c977a4f 100644
--- a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40StoredFieldsReader.java
+++ b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40StoredFieldsReader.java
@@ -184,6 +184,9 @@ public final class Lucene40StoredFieldsReader extends StoredFieldsReader impleme
         case FIELD_IS_NUMERIC_DOUBLE:
           visitor.doubleField(info, Double.longBitsToDouble(fieldsStream.readLong()));
           return;
+        case FIELD_IS_NUMERIC_BIG_INTEGER:
+          visitor.bigIntegerField(info, fieldsStream.readBigInteger());
+          return;
         default:
           throw new CorruptIndexException("Invalid numeric type: " + Integer.toHexString(numeric));
       }
@@ -211,6 +214,9 @@ public final class Lucene40StoredFieldsReader extends StoredFieldsReader impleme
         case FIELD_IS_NUMERIC_DOUBLE:
           fieldsStream.readLong();
           return;
+        case FIELD_IS_NUMERIC_BIG_INTEGER:
+          fieldsStream.readBigInteger();
+          return;
         default: 
           throw new CorruptIndexException("Invalid numeric type: " + Integer.toHexString(numeric));
       }
diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40StoredFieldsWriter.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40StoredFieldsWriter.java
index d3416c9..948de77 100644
--- a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40StoredFieldsWriter.java
+++ b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40StoredFieldsWriter.java
@@ -18,6 +18,7 @@ package org.apache.lucene.codecs.lucene40;
 
 import java.io.Closeable;
 import java.io.IOException;
+import java.math.BigInteger;
 
 import org.apache.lucene.codecs.CodecUtil;
 import org.apache.lucene.codecs.StoredFieldsReader;
@@ -61,6 +62,7 @@ public final class Lucene40StoredFieldsWriter extends StoredFieldsWriter {
   static final int FIELD_IS_NUMERIC_LONG = 2 << _NUMERIC_BIT_SHIFT;
   static final int FIELD_IS_NUMERIC_FLOAT = 3 << _NUMERIC_BIT_SHIFT;
   static final int FIELD_IS_NUMERIC_DOUBLE = 4 << _NUMERIC_BIT_SHIFT;
+  static final int FIELD_IS_NUMERIC_BIG_INTEGER = 5 << _NUMERIC_BIT_SHIFT;
 
   // the next possible bits are: 1 << 6; 1 << 7
   // currently unused: static final int FIELD_IS_NUMERIC_SHORT = 5 << _NUMERIC_BIT_SHIFT;
@@ -159,6 +161,8 @@ public final class Lucene40StoredFieldsWriter extends StoredFieldsWriter {
         bits |= FIELD_IS_NUMERIC_FLOAT;
       } else if (number instanceof Double) {
         bits |= FIELD_IS_NUMERIC_DOUBLE;
+      } else if (number instanceof BigInteger) {
+        bits |= FIELD_IS_NUMERIC_BIG_INTEGER;
       } else {
         throw new IllegalArgumentException("cannot store numeric type " + number.getClass());
       }
@@ -193,6 +197,8 @@ public final class Lucene40StoredFieldsWriter extends StoredFieldsWriter {
         fieldsStream.writeInt(Float.floatToIntBits(number.floatValue()));
       } else if (number instanceof Double) {
         fieldsStream.writeLong(Double.doubleToLongBits(number.doubleValue()));
+      } else if (number instanceof BigInteger) {
+        fieldsStream.writeBigInteger((BigInteger) number);
       } else {
         throw new AssertionError("Cannot get here");
       }
diff --git a/lucene/core/src/java/org/apache/lucene/document/BigIntegerField.java b/lucene/core/src/java/org/apache/lucene/document/BigIntegerField.java
new file mode 100644
index 0000000..9d932c4
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/document/BigIntegerField.java
@@ -0,0 +1,89 @@
+package org.apache.lucene.document;
+
+/*
+ * 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.apache.lucene.index.FieldInfo.IndexOptions;
+
+import java.math.BigInteger;
+
+/**
+ *
+ */
+public final class BigIntegerField extends Field {
+
+  /**
+   * Type for a BigIntegerField that is not stored:
+   * normalization factors, frequencies, and positions are omitted.
+   */
+  public static final FieldType TYPE_NOT_STORED = new FieldType();
+  static {
+    TYPE_NOT_STORED.setIndexed(true);
+    TYPE_NOT_STORED.setTokenized(true);
+    TYPE_NOT_STORED.setOmitNorms(true);
+    TYPE_NOT_STORED.setIndexOptions(IndexOptions.DOCS_ONLY);
+    TYPE_NOT_STORED.setNumericType(FieldType.NumericType.BIG_INTEGER);
+    TYPE_NOT_STORED.freeze();
+  }
+
+  /**
+   * Type for a stored BigIntegerField:
+   * normalization factors, frequencies, and positions are omitted.
+   */
+  public static final FieldType TYPE_STORED = new FieldType();
+  static {
+    TYPE_STORED.setIndexed(true);
+    TYPE_STORED.setTokenized(true);
+    TYPE_STORED.setOmitNorms(true);
+    TYPE_STORED.setIndexOptions(IndexOptions.DOCS_ONLY);
+    TYPE_STORED.setNumericType(FieldType.NumericType.BIG_INTEGER);
+    TYPE_STORED.setStored(true);
+    TYPE_STORED.freeze();
+  }
+
+  /** Creates a stored or un-stored LongField with the provided value
+   *  and default <code>precisionStep</code> {@link
+   *  org.apache.lucene.util.NumericUtils#PRECISION_STEP_DEFAULT} (4).
+   *  @param name field name
+   *  @param value 64-bit long value
+   *  @param stored Store.YES if the content should also be stored
+   *  @throws IllegalArgumentException if the field name is null.
+   */
+  public BigIntegerField(String name, BigInteger value, Store stored, int valueSize) {
+    super(name, stored == Store.YES ? TYPE_STORED : TYPE_NOT_STORED);
+    fieldsData = value;
+    this.valueSize = valueSize;
+  }
+
+  /** Expert: allows you to customize the {@link
+   *  org.apache.lucene.document.FieldType}.
+   *  @param name field name
+   *  @param value 64-bit long value
+   *  @param type customized field type: must have {@link org.apache.lucene.document.FieldType#numericType()}
+   *         of {@link org.apache.lucene.document.FieldType.NumericType#LONG}.
+   *  @throws IllegalArgumentException if the field name or type is null, or
+   *          if the field type does not have a LONG numericType()
+   */
+  public BigIntegerField(String name, BigInteger value, FieldType type, int valueSize) {
+    super(name, type);
+    if (type.numericType() != FieldType.NumericType.BIG_INTEGER) {
+      throw new IllegalArgumentException("type.numericType() must be BIG_INTEGER but got " + type.numericType());
+    }
+    fieldsData = value;
+    this.valueSize = valueSize;
+  }
+}
diff --git a/lucene/core/src/java/org/apache/lucene/document/BigNumericDocValuesField.java b/lucene/core/src/java/org/apache/lucene/document/BigNumericDocValuesField.java
new file mode 100644
index 0000000..5b11f9e
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/document/BigNumericDocValuesField.java
@@ -0,0 +1,49 @@
+package org.apache.lucene.document;
+
+/*
+ * 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.apache.lucene.index.FieldInfo;
+import org.apache.lucene.util.BytesRef;
+
+import java.math.BigInteger;
+
+/**
+ * todo: javadoc
+ */
+public class BigNumericDocValuesField extends Field {
+
+  /**
+   * Type for numeric DocValues.
+   */
+  public static final FieldType TYPE = new FieldType();
+  static {
+    TYPE.setDocValueType(FieldInfo.DocValuesType.BINARY);
+    TYPE.freeze();
+  }
+
+  /**
+   * Creates a new DocValues field with the specified 64-bit long value
+   * @param name field name
+   * @param value 64-bit long value
+   * @throws IllegalArgumentException if the field name is null
+   */
+  public BigNumericDocValuesField(String name, BigInteger value) {
+    super(name, TYPE);
+    fieldsData = new BytesRef(value.toByteArray());
+  }
+}
diff --git a/lucene/core/src/java/org/apache/lucene/document/DocumentStoredFieldVisitor.java b/lucene/core/src/java/org/apache/lucene/document/DocumentStoredFieldVisitor.java
index c01b5ce..828bb36 100644
--- a/lucene/core/src/java/org/apache/lucene/document/DocumentStoredFieldVisitor.java
+++ b/lucene/core/src/java/org/apache/lucene/document/DocumentStoredFieldVisitor.java
@@ -18,6 +18,7 @@ package org.apache.lucene.document;
  */
 
 import java.io.IOException;
+import java.math.BigInteger;
 import java.util.Set;
 import java.util.HashSet;
 
@@ -96,6 +97,11 @@ public class DocumentStoredFieldVisitor extends StoredFieldVisitor {
   }
 
   @Override
+  public void bigIntegerField(FieldInfo fieldInfo, BigInteger value) throws IOException {
+    doc.add(new StoredField(fieldInfo.name, value));
+  }
+
+  @Override
   public Status needsField(FieldInfo fieldInfo) throws IOException {
     return fieldsToAdd == null || fieldsToAdd.contains(fieldInfo.name) ? Status.YES : Status.NO;
   }
diff --git a/lucene/core/src/java/org/apache/lucene/document/Field.java b/lucene/core/src/java/org/apache/lucene/document/Field.java
index b157691..394ffe6 100644
--- a/lucene/core/src/java/org/apache/lucene/document/Field.java
+++ b/lucene/core/src/java/org/apache/lucene/document/Field.java
@@ -19,8 +19,10 @@ package org.apache.lucene.document;
 
 import java.io.IOException;
 import java.io.Reader;
+import java.math.BigInteger;
 
 import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.BigNumericTokenStream;
 import org.apache.lucene.analysis.NumericTokenStream;
 import org.apache.lucene.analysis.TokenStream;
 import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
@@ -83,6 +85,11 @@ public class Field implements IndexableField, StorableField {
   protected float boost = 1.0f;
 
   /**
+   * Value size for BigInteger field
+   */
+  protected int valueSize;
+
+  /**
    * Expert: creates a field with no initial value.
    * Intended only for custom Field subclasses.
    * @param name field name
@@ -390,6 +397,17 @@ public class Field implements IndexableField, StorableField {
   }
 
   /**
+   * Expert: change the value of this field. See
+   * {@link #setStringValue(String)}.
+   */
+  public void setBigIntegerValue(BigInteger value) {
+    if (!(fieldsData instanceof BigInteger)) {
+      throw new IllegalArgumentException("cannot change value type from " + fieldsData.getClass().getSimpleName() + " to BigInteger");
+    }
+    fieldsData = value;
+  }
+
+  /**
    * Expert: change the value of this field. See 
    * {@link #setStringValue(String)}.
    */
@@ -506,30 +524,42 @@ public class Field implements IndexableField, StorableField {
 
     final NumericType numericType = fieldType().numericType();
     if (numericType != null) {
-      if (!(internalTokenStream instanceof NumericTokenStream)) {
-        // lazy init the TokenStream as it is heavy to instantiate
-        // (attributes,...) if not needed (stored field loading)
-        internalTokenStream = new NumericTokenStream(type.numericPrecisionStep());
-      }
-      final NumericTokenStream nts = (NumericTokenStream) internalTokenStream;
-      // initialize value in TokenStream
-      final Number val = (Number) fieldsData;
-      switch (numericType) {
-      case INT:
-        nts.setIntValue(val.intValue());
-        break;
-      case LONG:
-        nts.setLongValue(val.longValue());
-        break;
-      case FLOAT:
-        nts.setFloatValue(val.floatValue());
-        break;
-      case DOUBLE:
-        nts.setDoubleValue(val.doubleValue());
-        break;
-      default:
-        throw new AssertionError("Should never get here");
+      if (numericType == NumericType.BIG_INTEGER) {
+        if (!(internalTokenStream instanceof BigNumericTokenStream)) {
+          // lazy init the TokenStream as it is heavy to instantiate
+          // (attributes,...) if not needed (stored field loading)
+          internalTokenStream = new BigNumericTokenStream(type.numericPrecisionStep(), valueSize);
+        }
+        final BigNumericTokenStream nts = (BigNumericTokenStream) internalTokenStream;
+        final BigInteger val = (BigInteger) fieldsData;
+        nts.setBigIntValue(val);
+      } else {
+        if (!(internalTokenStream instanceof NumericTokenStream)) {
+          // lazy init the TokenStream as it is heavy to instantiate
+          // (attributes,...) if not needed (stored field loading)
+          internalTokenStream = new NumericTokenStream(type.numericPrecisionStep());
+        }
+        final NumericTokenStream nts = (NumericTokenStream) internalTokenStream;
+        // initialize value in TokenStream
+        final Number val = (Number) fieldsData;
+        switch (numericType) {
+          case INT:
+            nts.setIntValue(val.intValue());
+            break;
+          case LONG:
+            nts.setLongValue(val.longValue());
+            break;
+          case FLOAT:
+            nts.setFloatValue(val.floatValue());
+            break;
+          case DOUBLE:
+            nts.setDoubleValue(val.doubleValue());
+            break;
+          default:
+            throw new AssertionError("Should never get here");
+        }
       }
+
       return internalTokenStream;
     }
 
diff --git a/lucene/core/src/java/org/apache/lucene/document/FieldType.java b/lucene/core/src/java/org/apache/lucene/document/FieldType.java
index b171faf..a195c40 100644
--- a/lucene/core/src/java/org/apache/lucene/document/FieldType.java
+++ b/lucene/core/src/java/org/apache/lucene/document/FieldType.java
@@ -40,7 +40,9 @@ public class FieldType implements IndexableFieldType  {
     /** 32-bit float numeric type */
     FLOAT, 
     /** 64-bit double numeric type */
-    DOUBLE
+    DOUBLE,
+    /** BigInteger */
+    BIG_INTEGER
   }
 
   private boolean indexed;
diff --git a/lucene/core/src/java/org/apache/lucene/document/StoredField.java b/lucene/core/src/java/org/apache/lucene/document/StoredField.java
index d0fc08d..49da7fa 100644
--- a/lucene/core/src/java/org/apache/lucene/document/StoredField.java
+++ b/lucene/core/src/java/org/apache/lucene/document/StoredField.java
@@ -5,6 +5,8 @@ import org.apache.lucene.index.StorableField;
 import org.apache.lucene.search.IndexSearcher; // javadocs
 import org.apache.lucene.util.BytesRef;
 
+import java.math.BigInteger;
+
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -166,4 +168,15 @@ public class StoredField extends Field {
     super(name, TYPE);
     fieldsData = value;
   }
+
+  /**
+   * Create a stored-only field with the given BigInteger value.
+   * @param name field name
+   * @param value BigInteger value
+   * @throws IllegalArgumentException if the field name is null.
+   */
+  public StoredField(String name, BigInteger value) {
+    super(name, TYPE);
+    fieldsData = value;
+  }
 }
diff --git a/lucene/core/src/java/org/apache/lucene/index/StoredFieldVisitor.java b/lucene/core/src/java/org/apache/lucene/index/StoredFieldVisitor.java
index 752c245..00562a1 100644
--- a/lucene/core/src/java/org/apache/lucene/index/StoredFieldVisitor.java
+++ b/lucene/core/src/java/org/apache/lucene/index/StoredFieldVisitor.java
@@ -18,6 +18,7 @@ package org.apache.lucene.index;
  */
 
 import java.io.IOException;
+import java.math.BigInteger;
 
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.DocumentStoredFieldVisitor;
@@ -72,6 +73,10 @@ public abstract class StoredFieldVisitor {
   /** Process a double numeric field. */
   public void doubleField(FieldInfo fieldInfo, double value) throws IOException {
   }
+
+  /** Process a BigInteger numeric field. */
+  public void bigIntegerField(FieldInfo fieldInfo, BigInteger value) throws IOException {
+  }
   
   /**
    * Hook before processing a field.
diff --git a/lucene/core/src/java/org/apache/lucene/search/FieldCache.java b/lucene/core/src/java/org/apache/lucene/search/FieldCache.java
index f34b98c..48d6272 100644
--- a/lucene/core/src/java/org/apache/lucene/search/FieldCache.java
+++ b/lucene/core/src/java/org/apache/lucene/search/FieldCache.java
@@ -19,6 +19,7 @@ package org.apache.lucene.search;
 
 import java.io.IOException;
 import java.io.PrintStream;
+import java.math.BigInteger;
 
 import org.apache.lucene.analysis.NumericTokenStream;
 import org.apache.lucene.document.DoubleField;
@@ -34,6 +35,7 @@ import org.apache.lucene.index.SortedDocValues;
 import org.apache.lucene.index.SortedSetDocValues;
 import org.apache.lucene.index.Terms;
 import org.apache.lucene.index.TermsEnum;
+import org.apache.lucene.util.BigNumericUtils;
 import org.apache.lucene.util.Bits;
 import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.NumericUtils;
@@ -107,6 +109,20 @@ public interface FieldCache {
     };
   }
 
+  /** Field values as BigInteger */
+  public static abstract class BigIntegers {
+    /** Return an double representation of this field's value. */
+    public abstract BigInteger get(int docID);
+
+    /** Zero value for every document */
+    public static final BigIntegers EMPTY = new BigIntegers() {
+      @Override
+      public BigInteger get(int docID) {
+        return BigInteger.ZERO;
+      }
+    };
+  }
+
   /**
    * Placeholder indicating creation of this cache is currently in-progress.
    */
@@ -164,6 +180,14 @@ public interface FieldCache {
     public double parseDouble(BytesRef term);
   }
 
+  /** Interface to parse doubles from document fields.
+   * @see FieldCache#getBigIntegers(AtomicReader, String, FieldCache.BigIntegerParser, boolean)
+   */
+  public interface BigIntegerParser extends Parser {
+    /** Return an double representation of this field's value. */
+    public BigInteger parseBigInteger(BytesRef term);
+  }
+
   /** Expert: The cache used internally by sorting and range query classes. */
   public static FieldCache DEFAULT = new FieldCacheImpl();
 
@@ -247,6 +271,25 @@ public interface FieldCache {
       return NumericUtils.filterPrefixCodedLongs(terms.iterator(null));
     }
   };
+
+  /**
+   * todo: javadoc
+   */
+  public static final BigIntegerParser NUMERIC_UTILS_BIG_INTEGER_PARSER = new BigIntegerParser(){
+    @Override
+    public BigInteger parseBigInteger(BytesRef term) {
+      return BigNumericUtils.prefixCodedToBigInteger(term);
+    }
+    @Override
+    public String toString() {
+      return FieldCache.class.getName()+".NUMERIC_UTILS_BIG_INTEGER_PARSER";
+    }
+
+    @Override
+    public TermsEnum termsEnum(Terms terms) throws IOException {
+      return BigNumericUtils.filterPrefixCodedBigIntegers(terms.iterator(null));
+    }
+  };
   
   /** Checks the internal cache for an appropriate entry, and if none is found,
    *  reads the terms in <code>field</code> and returns a bit set at the size of
@@ -391,6 +434,18 @@ public interface FieldCache {
    */
   public Doubles getDoubles(AtomicReader reader, String field, DoubleParser parser, boolean setDocsWithField) throws IOException;
 
+
+  /**
+   * todo: javadoc
+   * @param reader
+   * @param field
+   * @param parser
+   * @param setDocsWithField
+   * @return
+   * @throws IOException
+   */
+  public BigIntegers getBigIntegers(AtomicReader reader, String field, BigIntegerParser parser, boolean setDocsWithField) throws IOException;
+
   /** Checks the internal cache for an appropriate entry, and if none
    * is found, reads the term values in <code>field</code>
    * and returns a {@link BinaryDocValues} instance, providing a
diff --git a/lucene/core/src/java/org/apache/lucene/search/FieldCacheImpl.java b/lucene/core/src/java/org/apache/lucene/search/FieldCacheImpl.java
index 243e42a..a67af12 100644
--- a/lucene/core/src/java/org/apache/lucene/search/FieldCacheImpl.java
+++ b/lucene/core/src/java/org/apache/lucene/search/FieldCacheImpl.java
@@ -19,7 +19,9 @@ package org.apache.lucene.search;
 
 import java.io.IOException;
 import java.io.PrintStream;
+import java.math.BigInteger;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -61,7 +63,7 @@ class FieldCacheImpl implements FieldCache {
   }
 
   private synchronized void init() {
-    caches = new HashMap<>(9);
+    caches = new HashMap<>(10);
     caches.put(Integer.TYPE, new IntCache(this));
     caches.put(Float.TYPE, new FloatCache(this));
     caches.put(Long.TYPE, new LongCache(this));
@@ -70,6 +72,7 @@ class FieldCacheImpl implements FieldCache {
     caches.put(SortedDocValues.class, new SortedDocValuesCache(this));
     caches.put(DocTermOrds.class, new DocTermOrdsCache(this));
     caches.put(DocsWithFieldCache.class, new DocsWithFieldCache(this));
+    caches.put(BigInteger.class, new BigIntegerCache(this));
   }
 
   @Override
@@ -894,6 +897,107 @@ class FieldCacheImpl implements FieldCache {
     }
   }
 
+  @Override
+  public BigIntegers getBigIntegers(AtomicReader reader, String field, BigIntegerParser parser, boolean setDocsWithField) throws IOException {
+    final BinaryDocValues valuesIn = reader.getBinaryDocValues(field);
+    if (valuesIn != null) {
+      // Not cached here by FieldCacheImpl (cached instead
+      // per-thread by SegmentReader):
+      return new BigIntegers() {
+        @Override
+        public BigInteger get(int docID) {
+          BytesRef bytesRef = new BytesRef();
+          valuesIn.get(docID, bytesRef);
+          if (bytesRef.length == 0) return BigInteger.ZERO;
+          return new BigInteger(Arrays.copyOfRange(bytesRef.bytes, bytesRef.offset, bytesRef.offset + bytesRef.length));
+        }
+      };
+    } else {
+      final FieldInfo info = reader.getFieldInfos().fieldInfo(field);
+      if (info == null) {
+        return BigIntegers.EMPTY;
+      } else if (info.hasDocValues()) {
+        throw new IllegalStateException("Type mismatch: " + field + " was indexed as " + info.getDocValuesType());
+      } else if (!info.isIndexed()) {
+        return BigIntegers.EMPTY;
+      }
+      return (BigIntegers) caches.get(BigInteger.class).get(reader, new CacheKey(field, parser), setDocsWithField);
+    }
+  }
+
+  static class BigIntegersFromArray extends BigIntegers {
+    private final BigInteger[] values;
+
+    public BigIntegersFromArray(BigInteger[] values) {
+      this.values = values;
+    }
+
+    @Override
+    public BigInteger get(int docID) {
+      return values[docID];
+    }
+  }
+
+  static final class BigIntegerCache extends Cache {
+    BigIntegerCache(FieldCacheImpl wrapper) {
+      super(wrapper);
+    }
+
+    @Override
+    protected Object createValue(final AtomicReader reader, CacheKey key, boolean setDocsWithField)
+        throws IOException {
+
+      final BigIntegerParser parser = (BigIntegerParser) key.custom;
+      if (parser == null) {
+        // Confusing: must delegate to wrapper (vs simply
+        // setting parser = NUMERIC_UTILS_DOUBLE_PARSER) so
+        // cache key includes NUMERIC_UTILS_DOUBLE_PARSER:
+        return wrapper.getBigIntegers(reader, key.field, NUMERIC_UTILS_BIG_INTEGER_PARSER, setDocsWithField);
+      }
+
+      final HoldsOneThing<BigInteger[]> valuesRef = new HoldsOneThing<>();
+
+      Uninvert u = new Uninvert() {
+        private BigInteger currentValue;
+        private BigInteger[] values;
+
+        @Override
+        public void visitTerm(BytesRef term) {
+          currentValue = parser.parseBigInteger(term);
+          if (values == null) {
+            // Lazy alloc so for the numeric field case
+            // (which will hit a NumberFormatException
+            // when we first try the DEFAULT_INT_PARSER),
+            // we don't double-alloc:
+            values = new BigInteger[reader.maxDoc()];
+            valuesRef.set(values);
+          }
+        }
+
+        @Override
+        public void visitDoc(int docID) {
+          values[docID] = currentValue;
+        }
+
+        @Override
+        protected TermsEnum termsEnum(Terms terms) throws IOException {
+          return parser.termsEnum(terms);
+        }
+      };
+
+      u.uninvert(reader, key.field, setDocsWithField);
+
+      if (setDocsWithField) {
+        wrapper.setDocsWithField(reader, key.field, u.docsWithField);
+      }
+      BigInteger[] values = valuesRef.get();
+      if (values == null) {
+        values = new BigInteger[reader.maxDoc()];
+      }
+      return new BigIntegersFromArray(values);
+    }
+  }
+
   public static class SortedDocValuesImpl extends SortedDocValues {
     private final PagedBytes.Reader bytes;
     private final MonotonicAppendingLongBuffer termOrdToBytesOffset;
diff --git a/lucene/core/src/java/org/apache/lucene/search/FieldComparator.java b/lucene/core/src/java/org/apache/lucene/search/FieldComparator.java
index 5c81ab5..6bea99a 100644
--- a/lucene/core/src/java/org/apache/lucene/search/FieldComparator.java
+++ b/lucene/core/src/java/org/apache/lucene/search/FieldComparator.java
@@ -18,6 +18,7 @@ package org.apache.lucene.search;
  */
 
 import java.io.IOException;
+import java.math.BigInteger;
 
 import org.apache.lucene.index.AtomicReaderContext;
 import org.apache.lucene.index.BinaryDocValues;
@@ -567,6 +568,87 @@ public abstract class FieldComparator<T> {
     }
   }
 
+  /** Parses field's values as BigInteger (using {@link
+   *  FieldCache#getBigIntegers and sorts by ascending value */
+  public static final class BigIntegerComparator extends NumericComparator<BigInteger> {
+    private final BigInteger[] values;
+    private final FieldCache.BigIntegerParser parser;
+    private FieldCache.BigIntegers currentReaderValues;
+    private BigInteger bottom;
+    private BigInteger topValue;
+
+    BigIntegerComparator(int numHits, String field, FieldCache.Parser parser, BigInteger missingValue) {
+      super(field, missingValue);
+      values = new BigInteger[numHits];
+      this.parser = (FieldCache.BigIntegerParser) parser;
+    }
+
+    @Override
+    public int compare(int slot1, int slot2) {
+      return values[slot1].compareTo(values[slot2]);
+    }
+
+    @Override
+    public int compareBottom(int doc) {
+      // TODO: there are sneaky non-branch ways to compute
+      // -1/+1/0 sign
+      BigInteger v2 = currentReaderValues.get(doc);
+      // Test for v2 == 0 to save Bits.get method call for
+      // the common case (doc has value and value is non-zero):
+      if (docsWithField != null && v2.compareTo(BigInteger.ZERO) == 0 && !docsWithField.get(doc)) {
+        v2 = missingValue;
+      }
+
+      return bottom.compareTo(v2);
+    }
+
+    @Override
+    public void copy(int slot, int doc) {
+      BigInteger v2 = currentReaderValues.get(doc);
+      // Test for v2 == 0 to save Bits.get method call for
+      // the common case (doc has value and value is non-zero):
+      if (docsWithField != null && v2.compareTo(BigInteger.ZERO) == 0 && !docsWithField.get(doc)) {
+        v2 = missingValue;
+      }
+
+      values[slot] = v2;
+    }
+
+    @Override
+    public FieldComparator<BigInteger> setNextReader(AtomicReaderContext context) throws IOException {
+      // NOTE: must do this before calling super otherwise
+      // we compute the docsWithField Bits twice!
+      currentReaderValues = FieldCache.DEFAULT.getBigIntegers(context.reader(), field, parser, missingValue != null);
+      return super.setNextReader(context);
+    }
+
+    @Override
+    public void setBottom(final int bottom) {
+      this.bottom = values[bottom];
+    }
+
+    @Override
+    public void setTopValue(BigInteger value) {
+      topValue = value;
+    }
+
+    @Override
+    public BigInteger value(int slot) {
+      return values[slot];
+    }
+
+    @Override
+    public int compareTop(int doc) {
+      BigInteger docValue = currentReaderValues.get(doc);
+      // Test for docValue == 0 to save Bits.get method call for
+      // the common case (doc has value and value is non-zero):
+      if (docsWithField != null && docValue.compareTo(BigInteger.ZERO) == 0 && !docsWithField.get(doc)) {
+        docValue = missingValue;
+      }
+      return topValue.compareTo(docValue);
+    }
+  }
+
   /** Sorts by descending relevance.  NOTE: if you are
    *  sorting only by descending relevance and then
    *  secondarily by ascending docID, performance is faster
diff --git a/lucene/core/src/java/org/apache/lucene/search/NumericRangeFilter.java b/lucene/core/src/java/org/apache/lucene/search/NumericRangeFilter.java
index 96fe8a4..bba7775 100644
--- a/lucene/core/src/java/org/apache/lucene/search/NumericRangeFilter.java
+++ b/lucene/core/src/java/org/apache/lucene/search/NumericRangeFilter.java
@@ -24,6 +24,8 @@ import org.apache.lucene.document.IntField; // for javadocs
 import org.apache.lucene.document.LongField; // for javadocs
 import org.apache.lucene.util.NumericUtils; // for javadocs
 
+import java.math.BigInteger;
+
 /**
  * A {@link Filter} that only accepts numeric values within
  * a specified range. To use this, you must first index the
@@ -179,6 +181,17 @@ public final class NumericRangeFilter<T extends Number> extends MultiTermQueryWr
     );
   }
 
+  /**
+   * todo: javadoc
+   */
+  public static NumericRangeFilter<BigInteger> newBigIntegerRange(final String field, final int precisionStep,
+                                                                     BigInteger min, BigInteger max, final boolean minInclusive, final boolean maxInclusive, final int valueSize
+  ) {
+    return new NumericRangeFilter<>(
+        NumericRangeQuery.newBigIntegerRange(field, precisionStep, min, max, minInclusive, maxInclusive, valueSize)
+    );
+  }
+
   /** Returns <code>true</code> if the lower endpoint is inclusive */
   public boolean includesMin() { return query.includesMin(); }
   
diff --git a/lucene/core/src/java/org/apache/lucene/search/NumericRangeQuery.java b/lucene/core/src/java/org/apache/lucene/search/NumericRangeQuery.java
index 102523e..be84b2d 100644
--- a/lucene/core/src/java/org/apache/lucene/search/NumericRangeQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/NumericRangeQuery.java
@@ -18,6 +18,7 @@ package org.apache.lucene.search;
  */
 
 import java.io.IOException;
+import java.math.BigInteger;
 import java.util.LinkedList;
 
 import org.apache.lucene.analysis.NumericTokenStream; // for javadocs
@@ -30,6 +31,7 @@ import org.apache.lucene.index.FilteredTermsEnum;
 import org.apache.lucene.index.Terms;
 import org.apache.lucene.index.TermsEnum;
 import org.apache.lucene.util.AttributeSource;
+import org.apache.lucene.util.BigNumericUtils;
 import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.NumericUtils;
 import org.apache.lucene.util.ToStringUtils;
@@ -168,7 +170,12 @@ import org.apache.lucene.index.Term; // for javadocs
 public final class NumericRangeQuery<T extends Number> extends MultiTermQuery {
 
   private NumericRangeQuery(final String field, final int precisionStep, final NumericType dataType,
-    T min, T max, final boolean minInclusive, final boolean maxInclusive
+                            T min, T max, final boolean minInclusive, final boolean maxInclusive){
+    this(field, precisionStep, dataType, min, max, minInclusive, maxInclusive, 0);
+  }
+
+  private NumericRangeQuery(final String field, final int precisionStep, final NumericType dataType,
+    T min, T max, final boolean minInclusive, final boolean maxInclusive, final int valueSize
   ) {
     super(field);
     if (precisionStep < 1)
@@ -179,6 +186,7 @@ public final class NumericRangeQuery<T extends Number> extends MultiTermQuery {
     this.max = max;
     this.minInclusive = minInclusive;
     this.maxInclusive = maxInclusive;
+    this.valueSize = valueSize;
   }
   
   /**
@@ -293,6 +301,16 @@ public final class NumericRangeQuery<T extends Number> extends MultiTermQuery {
     return new NumericRangeQuery<>(field, NumericUtils.PRECISION_STEP_DEFAULT, NumericType.FLOAT, min, max, minInclusive, maxInclusive);
   }
 
+  /**
+   * todo : javadoc
+   */
+  public static NumericRangeQuery<BigInteger> newBigIntegerRange(final String field, final int precisionStep,
+                                                                    BigInteger min, BigInteger max, final boolean minInclusive, final boolean maxInclusive, final int valueSize
+  ) {
+    return new NumericRangeQuery<>(field, precisionStep, NumericType.BIG_INTEGER, min, max, minInclusive, maxInclusive, valueSize);
+  }
+
+
   @Override @SuppressWarnings("unchecked")
   protected TermsEnum getTermsEnum(final Terms terms, AttributeSource atts) throws IOException {
     // very strange: java.lang.Number itself is not Comparable, but all subclasses used here are
@@ -343,14 +361,15 @@ public final class NumericRangeQuery<T extends Number> extends MultiTermQuery {
         (q.max == null ? max == null : q.max.equals(max)) &&
         minInclusive == q.minInclusive &&
         maxInclusive == q.maxInclusive &&
-        precisionStep == q.precisionStep
+        precisionStep == q.precisionStep &&
+        valueSize == q.valueSize
       );
     }
     return false;
   }
 
   @Override
-  public final int hashCode() {
+  public final int hashCode() {       // todo: include valueSize
     int hash = super.hashCode();
     hash += precisionStep^0x64365465;
     if (min != null) hash += min.hashCode()^0x14fa55fb;
@@ -365,6 +384,7 @@ public final class NumericRangeQuery<T extends Number> extends MultiTermQuery {
   final NumericType dataType;
   final T min, max;
   final boolean minInclusive,maxInclusive;
+  final int valueSize; // only used for BigInteger
 
   // used to handle float/double infinity correcty
   static final long LONG_NEGATIVE_INFINITY =
@@ -376,6 +396,8 @@ public final class NumericRangeQuery<T extends Number> extends MultiTermQuery {
   static final int INT_POSITIVE_INFINITY =
     NumericUtils.floatToSortableInt(Float.POSITIVE_INFINITY);
 
+  static final BigInteger MINUS_ONE = BigInteger.valueOf(-1);
+
   /**
    * Subclass of FilteredTermsEnum for enumerating all terms that match the
    * sub-ranges for trie range queries, using flex API.
@@ -474,7 +496,25 @@ public final class NumericRangeQuery<T extends Number> extends MultiTermQuery {
           }, precisionStep, minBound, maxBound);
           break;
         }
-          
+
+        case BIG_INTEGER:
+          BigInteger minBound = min == null? MINUS_ONE.shiftLeft(valueSize - 1) : (BigInteger) min;  // min value is -2 ^ (valueSize -1)
+          if (!minInclusive) {
+            minBound = minBound.add(BigInteger.ONE);
+          }
+          BigInteger maxBound = max == null ? BigInteger.ONE.shiftLeft(valueSize - 1).subtract(BigInteger.ONE) : (BigInteger) max;  // max value is 2 ^ (valueSize -1) - 1
+          if (!maxInclusive) {
+            maxBound = maxBound.subtract(BigInteger.ONE);
+          }
+
+          BigNumericUtils.splitRange(new BigNumericUtils.BigIntegerRangeBuilder() {
+            @Override
+            public final void addRange(BytesRef minPrefixCoded, BytesRef maxPrefixCoded) {
+              rangeBounds.add(minPrefixCoded);
+              rangeBounds.add(maxPrefixCoded);
+            }
+          }, valueSize, precisionStep, minBound, maxBound);
+          break;
         default:
           // should never happen
           throw new IllegalArgumentException("Invalid NumericType");
diff --git a/lucene/core/src/java/org/apache/lucene/search/SortField.java b/lucene/core/src/java/org/apache/lucene/search/SortField.java
index 2987f95..3d2fdf0 100644
--- a/lucene/core/src/java/org/apache/lucene/search/SortField.java
+++ b/lucene/core/src/java/org/apache/lucene/search/SortField.java
@@ -18,6 +18,7 @@ package org.apache.lucene.search;
  */
 
 import java.io.IOException;
+import java.math.BigInteger;
 import java.util.Comparator;
 
 import org.apache.lucene.util.BytesRef;
@@ -67,6 +68,10 @@ public class SortField {
      * lower values are at the front. */
     DOUBLE,
 
+    /** Sort using term values as encoded BigIntegers.  Sort values are BigInteger and
+     * lower values are at the front. */
+    BIG_INTEGER,
+
     /** Sort using a custom Comparator.  Sort values are any Comparable and
      * sorting is done according to natural order. */
     CUSTOM,
@@ -185,7 +190,7 @@ public class SortField {
       if (missingValue != STRING_FIRST && missingValue != STRING_LAST) {
         throw new IllegalArgumentException("For STRING type, missing value must be either STRING_FIRST or STRING_LAST");
       }
-    } else if (type != Type.INT && type != Type.FLOAT && type != Type.LONG && type != Type.DOUBLE) {
+    } else if (type != Type.INT && type != Type.FLOAT && type != Type.LONG && type != Type.DOUBLE && type != Type.BIG_INTEGER) {
       throw new IllegalArgumentException("Missing value only works for numeric or STRING types");
     }
     this.missingValue = missingValue;
@@ -392,6 +397,9 @@ public class SortField {
     case DOUBLE:
       return new FieldComparator.DoubleComparator(numHits, field, parser, (Double) missingValue);
 
+    case BIG_INTEGER:
+      return new FieldComparator.BigIntegerComparator(numHits, field, parser, (BigInteger) missingValue);
+
     case CUSTOM:
       assert comparatorSource != null;
       return comparatorSource.newComparator(field, numHits, sortPos, reverse);
diff --git a/lucene/core/src/java/org/apache/lucene/store/DataInput.java b/lucene/core/src/java/org/apache/lucene/store/DataInput.java
index beebef2..eac9397 100644
--- a/lucene/core/src/java/org/apache/lucene/store/DataInput.java
+++ b/lucene/core/src/java/org/apache/lucene/store/DataInput.java
@@ -18,6 +18,7 @@ package org.apache.lucene.store;
  */
 
 import java.io.IOException;
+import java.math.BigInteger;
 import java.nio.charset.StandardCharsets;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -268,4 +269,16 @@ public abstract class DataInput implements Cloneable {
     }
   }
 
+  /**
+   * todo: javadoc
+   * @return
+   * @throws IOException
+   */
+  public BigInteger readBigInteger() throws IOException {
+    int length = readVInt();
+    final byte[] bytes = new byte[length];
+    readBytes(bytes, 0, length);
+    return new BigInteger(bytes);
+  }
+
 }
diff --git a/lucene/core/src/java/org/apache/lucene/store/DataOutput.java b/lucene/core/src/java/org/apache/lucene/store/DataOutput.java
index 2bcd195..2f87d3d 100644
--- a/lucene/core/src/java/org/apache/lucene/store/DataOutput.java
+++ b/lucene/core/src/java/org/apache/lucene/store/DataOutput.java
@@ -18,6 +18,7 @@ package org.apache.lucene.store;
  */
 
 import java.io.IOException;
+import java.math.BigInteger;
 import java.util.Map;
 import java.util.Set;
 
@@ -297,4 +298,15 @@ public abstract class DataOutput {
       }
     }
   }
+
+  /**
+   * todo: javadoc
+   * @param value
+   * @throws IOException
+   */
+  public void writeBigInteger(BigInteger value) throws IOException{
+    byte[] bytes = value.toByteArray();
+    writeVInt(bytes.length);
+    writeBytes(bytes, bytes.length);
+  }
 }
diff --git a/lucene/core/src/java/org/apache/lucene/util/BigNumericUtils.java b/lucene/core/src/java/org/apache/lucene/util/BigNumericUtils.java
new file mode 100644
index 0000000..3175fd1
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/util/BigNumericUtils.java
@@ -0,0 +1,189 @@
+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.apache.lucene.index.FilteredTermsEnum;
+import org.apache.lucene.index.TermsEnum;
+
+import java.math.BigInteger;
+
+/**
+ * Copies from {@link NumericUtils} to support {@link BigInteger}
+ */
+public final class BigNumericUtils {
+
+  public static final int VALUE_SIZE_DEFAULT = 128;
+
+  public static final int PRECISION_STEP_DEFAULT = NumericUtils.PRECISION_STEP_DEFAULT;
+
+  public static enum BigNumericType {
+    BIG_INT
+  }
+
+  public static int getBufferSize(int valueSize) {
+    return (valueSize - 1) / 7 + 2;
+  }
+
+
+  protected static BigInteger parseBigInteger(BigInteger value, final int valueSize) {
+    if (value.signum() < 0)
+      value = value.add(BigInteger.ONE.shiftLeft(valueSize - 1));
+    return value;
+  }
+
+  public static int bigIntegerToPrefixCoded(final BigInteger val, final int shift, final BytesRef bytes, final int valueSize) {
+    bigIntegerToPrefixCodedBytes(val, shift, bytes, valueSize);
+    return bytes.hashCode();
+  }
+
+  public static void bigIntegerToPrefixCodedBytes(BigInteger val, final int shift, final BytesRef bytes, final int valueSize) {
+    if (shift < 0 || shift > valueSize)  // ensure shift is 0..valueSize
+      throw new IllegalArgumentException("Illegal shift value, must be 0.." + valueSize);
+    int nChars = (valueSize - 1 - shift) / 7 + 1;
+    bytes.offset = 0;
+    bytes.length = nChars + 1;   // one extra for the byte that contains the shift info
+    if (bytes.bytes.length < bytes.length) {
+      bytes.bytes = new byte[getBufferSize(valueSize)];  // use the max
+    }
+    bytes.bytes[0] = (byte) (shift);
+    BigInteger sortableBits;
+    if (val.signum() < 0) {
+      sortableBits = val.add(BigInteger.ONE.shiftLeft(valueSize - 1));
+    } else {
+      sortableBits = val.flipBit(valueSize - 1);
+    }
+    sortableBits = sortableBits.shiftRight(shift);
+    while (nChars > 0) {
+      // Store 7 bits per byte for compatibility
+      // with UTF-8 encoding of terms
+      bytes.bytes[nChars--] = sortableBits.and(BigInteger.valueOf(0x7f)).byteValue();
+      sortableBits = sortableBits.shiftRight(7);
+    }
+  }
+
+  public static BigInteger prefixCodedToBigInteger(final BytesRef val) {
+    BigInteger sortableBits = BigInteger.ZERO;
+    for (int i = val.offset + 1, limit = val.offset + val.length; i < limit; i++) {
+      sortableBits = sortableBits.shiftLeft(7);
+      final byte b = val.bytes[i];
+      if (b < 0) {
+        throw new NumberFormatException(
+            "Invalid prefixCoded numerical value representation (byte " +
+                Integer.toHexString(b & 0xff) + " at position " + (i - val.offset) + " is invalid)"
+        );
+      }
+      sortableBits = sortableBits.or(BigInteger.valueOf(b));
+    }
+    sortableBits = sortableBits.shiftLeft(getPrefixCodedBigIntegerShift(val));
+    return sortableBits.flipBit(sortableBits.bitLength() - 1);
+
+  }
+
+  public static int getPrefixCodedBigIntegerShift(final BytesRef val) {
+    return val.bytes[val.offset];
+  }
+
+
+  public static void splitRange(
+      final BigIntegerRangeBuilder builder, final int valSize,
+      final int precisionStep, BigInteger minBound, BigInteger maxBound
+  ) {
+    if (precisionStep < 1)
+      throw new IllegalArgumentException("precisionStep must be >=1");
+    if (minBound.compareTo(maxBound) > 0) return;
+    for (int shift = 0; ; shift += precisionStep) {
+      // calculate new bounds for inner precision
+      final BigInteger diff = BigInteger.valueOf(1L << (shift + precisionStep)),
+          mask = BigInteger.valueOf(((1L << precisionStep) - 1L) << shift);
+      final boolean
+          hasLower = !minBound.and(mask).equals(BigInteger.ZERO),
+          hasUpper = !maxBound.and(mask).equals(mask);
+      final BigInteger
+          nextMinBound = (hasLower ? minBound.add(diff) : minBound).and(mask.not()),
+          nextMaxBound = (hasUpper ? maxBound.subtract(diff) : maxBound).and(mask.not());
+      final boolean
+          lowerWrapped = nextMinBound.compareTo(minBound) < 0,
+          upperWrapped = nextMaxBound.compareTo(maxBound) > 0;
+
+      if (shift + precisionStep >= valSize || nextMinBound.compareTo(nextMaxBound) > 0 || lowerWrapped || upperWrapped) {
+        // We are in the lowest precision or the next precision is not available.
+        addRange(builder, valSize, minBound, maxBound, shift);
+        // exit the split recursion loop
+        break;
+      }
+
+      if (hasLower)
+        addRange(builder, valSize, minBound, minBound.or(mask), shift);
+      if (hasUpper)
+        addRange(builder, valSize, maxBound.and(mask.not()), maxBound, shift);
+
+      // recurse to next precision
+      minBound = nextMinBound;
+      maxBound = nextMaxBound;
+    }
+  }
+
+  private static void addRange(
+      final BigIntegerRangeBuilder builder, final int valSize,
+      BigInteger minBound, BigInteger maxBound,
+      final int shift
+  ) {
+    maxBound = maxBound.or(BigInteger.valueOf((1L << shift) - 1L));
+    builder.addRange(minBound, maxBound, shift, valSize);
+  }
+
+
+  public static abstract class BigIntegerRangeBuilder {
+
+    /**
+     * Overwrite this method, if you like to receive the already prefix encoded range bounds.
+     * You can directly build classical (inclusive) range queries from them.
+     */
+    public void addRange(BytesRef minPrefixCoded, BytesRef maxPrefixCoded) {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Overwrite this method, if you like to receive the raw long range bounds.
+     * You can use this for e.g. debugging purposes (print out range bounds).
+     */
+    public void addRange(final BigInteger min, final BigInteger max, final int shift, final int valueSize) {
+      final BytesRef minBytes = new BytesRef(getBufferSize(valueSize)), maxBytes = new BytesRef(getBufferSize(valueSize));
+      bigIntegerToPrefixCodedBytes(min, shift, minBytes, valueSize);
+      bigIntegerToPrefixCodedBytes(max, shift, maxBytes, valueSize);
+      addRange(minBytes, maxBytes);
+    }
+
+  }
+
+  /**
+   * todo: javadoc
+   * @param termsEnum
+   * @return
+   */
+  public static TermsEnum filterPrefixCodedBigIntegers(TermsEnum termsEnum) {
+    return new FilteredTermsEnum(termsEnum, false) {
+      @Override
+      protected AcceptStatus accept(BytesRef term) {
+        return BigNumericUtils.getPrefixCodedBigIntegerShift(term) == 0 ? AcceptStatus.YES : AcceptStatus.END;
+      }
+    };
+  }
+
+}
+
diff --git a/lucene/core/src/test/org/apache/lucene/analysis/TestBigNumericTokenStream.java b/lucene/core/src/test/org/apache/lucene/analysis/TestBigNumericTokenStream.java
new file mode 100644
index 0000000..47b35fc
--- /dev/null
+++ b/lucene/core/src/test/org/apache/lucene/analysis/TestBigNumericTokenStream.java
@@ -0,0 +1,98 @@
+package org.apache.lucene.analysis;
+
+/*
+ * 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.apache.lucene.analysis.tokenattributes.CharTermAttribute;
+import org.apache.lucene.analysis.tokenattributes.CharTermAttributeImpl;
+import org.apache.lucene.analysis.tokenattributes.TermToBytesRefAttribute;
+import org.apache.lucene.analysis.tokenattributes.TypeAttribute;
+import org.apache.lucene.util.BigNumericUtils;
+import org.apache.lucene.util.BytesRef;
+import org.junit.Test;
+
+import java.math.BigInteger;
+
+public class TestBigNumericTokenStream extends BaseTokenStreamTestCase {
+
+  static final long lvalue = 4573245871874382L;
+
+  @Test
+  public void testLongStream() throws Exception {
+    final BigNumericTokenStream stream=new BigNumericTokenStream(4, 64).setBigIntValue(BigInteger.valueOf(lvalue));
+    // use getAttribute to test if attributes really exist, if not an IAE will be throwed
+    final TermToBytesRefAttribute bytesAtt = stream.getAttribute(TermToBytesRefAttribute.class);
+    final TypeAttribute typeAtt = stream.getAttribute(TypeAttribute.class);
+    final BigNumericTokenStream.NumericTermAttribute numericAtt = stream.getAttribute(BigNumericTokenStream.NumericTermAttribute.class);
+    final BytesRef bytes = bytesAtt.getBytesRef();
+    stream.reset();
+    assertEquals(64, numericAtt.getValueSize());
+    for (int shift=0; shift<64; shift+=BigNumericUtils.PRECISION_STEP_DEFAULT) {
+      assertTrue("New token is available", stream.incrementToken());
+      assertEquals("Shift value wrong", shift, numericAtt.getShift());
+      final int hash = bytesAtt.fillBytesRef();
+      assertEquals("Hash incorrect", bytes.hashCode(), hash);
+      assertEquals("Term is incorrectly encoded", BigInteger.valueOf(lvalue & ~((1L << shift) - 1L)), BigNumericUtils.prefixCodedToBigInteger(bytes));
+      assertEquals("Term raw value is incorrectly encoded", BigInteger.valueOf(lvalue & ~((1L << shift) - 1L)), numericAtt.getRawValue());
+      assertEquals("Type incorrect", (shift == 0) ? BigNumericTokenStream.TOKEN_TYPE_FULL_PREC : BigNumericTokenStream.TOKEN_TYPE_LOWER_PREC, typeAtt.type());
+    }
+    assertFalse("More tokens available", stream.incrementToken());
+    stream.end();
+    stream.close();
+  }
+
+
+  @Test
+  public void testNotInitialized() throws Exception {
+    final BigNumericTokenStream stream=new BigNumericTokenStream();
+    
+    try {
+      stream.reset();
+      fail("reset() should not succeed.");
+    } catch (IllegalStateException e) {
+      // pass
+    }
+
+    try {
+      stream.incrementToken();
+      fail("incrementToken() should not succeed.");
+    } catch (IllegalStateException e) {
+      // pass
+    }
+  }
+  
+  public static interface TestAttribute extends CharTermAttribute {}
+  public static class TestAttributeImpl extends CharTermAttributeImpl implements TestAttribute {}
+
+  @Test
+  public void testCTA() throws Exception {
+    final BigNumericTokenStream stream=new BigNumericTokenStream();
+    try {
+      stream.addAttribute(CharTermAttribute.class);
+      fail("Succeeded to add CharTermAttribute.");
+    } catch (IllegalArgumentException iae) {
+      assertTrue(iae.getMessage().startsWith("BigNumericTokenStream does not support"));
+    }
+    try {
+      stream.addAttribute(TestAttribute.class);
+      fail("Succeeded to add TestAttribute.");
+    } catch (IllegalArgumentException iae) {
+      assertTrue(iae.getMessage().startsWith("BigNumericTokenStream does not support"));
+    }
+  }
+  
+}
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestNumericRangeQueryBigInteger.java b/lucene/core/src/test/org/apache/lucene/search/TestNumericRangeQueryBigInteger.java
new file mode 100644
index 0000000..b83316a
--- /dev/null
+++ b/lucene/core/src/test/org/apache/lucene/search/TestNumericRangeQueryBigInteger.java
@@ -0,0 +1,579 @@
+package org.apache.lucene.search;
+
+/*
+ * 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.apache.lucene.analysis.MockAnalyzer;
+import org.apache.lucene.document.BigIntegerField;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.FieldType;
+import org.apache.lucene.index.AtomicReaderContext;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.MultiFields;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.index.SlowCompositeReaderWrapper;
+import org.apache.lucene.index.StoredDocument;
+import org.apache.lucene.index.Terms;
+import org.apache.lucene.index.TermsEnum;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.BigNumericUtils;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.util.TestUtil;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.math.BigInteger;
+
+public class TestNumericRangeQueryBigInteger extends LuceneTestCase {
+  // distance of entries
+  private static long distance;
+  // shift the starting of the values to the left, to also have negative values:
+  private static final long startOffset = - 1L << 31;
+  // number of docs to generate for testing
+  private static int noDocs;
+  
+  private static Directory directory = null;
+  private static IndexReader reader = null;
+  private static IndexSearcher searcher = null;
+
+  private static int valueSize = 64;
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    noDocs = atLeast(4096);
+    distance = (1L << 60) / noDocs;
+    directory = newDirectory();
+    RandomIndexWriter writer = new RandomIndexWriter(random(), directory,
+        newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random()))
+        .setMaxBufferedDocs(TestUtil.nextInt(random(), 100, 1000))
+        .setMergePolicy(newLogMergePolicy()));
+
+    final FieldType storedLong = new FieldType(BigIntegerField.TYPE_NOT_STORED);
+    storedLong.setStored(true);
+    storedLong.freeze();
+
+    final FieldType storedLong8 = new FieldType(storedLong);
+    storedLong8.setNumericPrecisionStep(8);
+
+    final FieldType storedLong4 = new FieldType(storedLong);
+    storedLong4.setNumericPrecisionStep(4);
+
+    final FieldType storedLong6 = new FieldType(storedLong);
+    storedLong6.setNumericPrecisionStep(6);
+
+    final FieldType storedLong2 = new FieldType(storedLong);
+    storedLong2.setNumericPrecisionStep(2);
+
+    final FieldType storedLongNone = new FieldType(storedLong);
+    storedLongNone.setNumericPrecisionStep(Integer.MAX_VALUE);
+
+    final FieldType unstoredLong = BigIntegerField.TYPE_NOT_STORED;
+
+    final FieldType unstoredLong8 = new FieldType(unstoredLong);
+    unstoredLong8.setNumericPrecisionStep(8);
+
+    final FieldType unstoredLong6 = new FieldType(unstoredLong);
+    unstoredLong6.setNumericPrecisionStep(6);
+
+    final FieldType unstoredLong4 = new FieldType(unstoredLong);
+    unstoredLong4.setNumericPrecisionStep(4);
+
+    final FieldType unstoredLong2 = new FieldType(unstoredLong);
+    unstoredLong2.setNumericPrecisionStep(2);
+
+    BigIntegerField
+      field8 = new BigIntegerField("field8", BigInteger.valueOf(0L), storedLong8, valueSize),
+      field6 = new BigIntegerField("field6", BigInteger.valueOf(0L), storedLong6, valueSize),
+      field4 = new BigIntegerField("field4", BigInteger.valueOf(0L), storedLong4, valueSize),
+      field2 = new BigIntegerField("field2", BigInteger.valueOf(0L), storedLong2, valueSize),
+      fieldNoTrie = new BigIntegerField("field"+Integer.MAX_VALUE, BigInteger.valueOf(0L), storedLongNone, valueSize),
+      ascfield8 = new BigIntegerField("ascfield8", BigInteger.valueOf(0L), unstoredLong8, valueSize),
+      ascfield6 = new BigIntegerField("ascfield6", BigInteger.valueOf(0L), unstoredLong6, valueSize),
+      ascfield4 = new BigIntegerField("ascfield4", BigInteger.valueOf(0L), unstoredLong4, valueSize),
+      ascfield2 = new BigIntegerField("ascfield2", BigInteger.valueOf(0L), unstoredLong2, valueSize);
+
+    Document doc = new Document();
+    // add fields, that have a distance to test general functionality
+    doc.add(field8); doc.add(field6); doc.add(field4); doc.add(field2); doc.add(fieldNoTrie);
+    // add ascending fields with a distance of 1, beginning at -noDocs/2 to test the correct splitting of range and inclusive/exclusive
+    doc.add(ascfield8); doc.add(ascfield6); doc.add(ascfield4); doc.add(ascfield2);
+    
+    // Add a series of noDocs docs with increasing long values, by updating the fields
+    for (int l=0; l<noDocs; l++) {
+      long val=distance*l+startOffset;
+      field8.setBigIntegerValue(BigInteger.valueOf(val));
+      field6.setBigIntegerValue(BigInteger.valueOf(val));
+      field4.setBigIntegerValue(BigInteger.valueOf(val));
+      field2.setBigIntegerValue(BigInteger.valueOf(val));
+      fieldNoTrie.setBigIntegerValue(BigInteger.valueOf(val));
+
+      val=l-(noDocs/2);
+      ascfield8.setBigIntegerValue(BigInteger.valueOf(val));
+      ascfield6.setBigIntegerValue(BigInteger.valueOf(val));
+      ascfield4.setBigIntegerValue(BigInteger.valueOf(val));
+      ascfield2.setBigIntegerValue(BigInteger.valueOf(val));
+      writer.addDocument(doc);
+    }
+    reader = writer.getReader();
+    searcher=newSearcher(reader);
+    writer.shutdown();
+  }
+  
+  @AfterClass
+  public static void afterClass() throws Exception {
+    searcher = null;
+    reader.close();
+    reader = null;
+    directory.close();
+    directory = null;
+  }
+  
+  @Override
+  public void setUp() throws Exception {
+    super.setUp();
+    // set the theoretical maximum term count for 8bit (see docs for the number)
+    // super.tearDown will restore the default
+    BooleanQuery.setMaxClauseCount(7*255*2 + 255);
+  }
+  
+  /** test for constant score + boolean query + filter, the other tests only use the constant score mode */
+  private void testRange(int precisionStep) throws Exception {
+    String field="field"+precisionStep;
+    int count=3000;
+    long lower=(distance*3/2)+startOffset, upper=lower + count*distance + (distance/3);
+    NumericRangeQuery<BigInteger> q = NumericRangeQuery.newBigIntegerRange(field, precisionStep, BigInteger.valueOf(lower), BigInteger.valueOf(upper), true, true, valueSize);
+    NumericRangeFilter<BigInteger> f = NumericRangeFilter.newBigIntegerRange(field, precisionStep, BigInteger.valueOf(lower), BigInteger.valueOf(upper), true, true, valueSize);
+    for (byte i=0; i<3; i++) {
+      TopDocs topDocs;
+      String type;
+      switch (i) {
+        case 0:
+          type = " (constant score filter rewrite)";
+          q.setRewriteMethod(MultiTermQuery.CONSTANT_SCORE_FILTER_REWRITE);
+          topDocs = searcher.search(q, null, noDocs, Sort.INDEXORDER);
+          break;
+        case 1:
+          type = " (constant score boolean rewrite)";
+          q.setRewriteMethod(MultiTermQuery.CONSTANT_SCORE_BOOLEAN_QUERY_REWRITE);
+          topDocs = searcher.search(q, null, noDocs, Sort.INDEXORDER);
+          break;
+        case 2:
+          type = " (filter)";
+          topDocs = searcher.search(new MatchAllDocsQuery(), f, noDocs, Sort.INDEXORDER);
+          break;
+        default:
+          return;
+      }
+      ScoreDoc[] sd = topDocs.scoreDocs;
+      assertNotNull(sd);
+      assertEquals("Score doc count"+type, count, sd.length );
+      StoredDocument doc=searcher.doc(sd[0].doc);
+      assertEquals("First doc"+type, 2*distance+startOffset, new BigInteger(doc.getField(field).stringValue()).longValue());
+      doc=searcher.doc(sd[sd.length-1].doc);
+      assertEquals("Last doc"+type, (1+count)*distance+startOffset, new BigInteger(doc.getField(field).stringValue()).longValue());
+    }
+  }
+
+  @Test
+  public void testRange_8bit() throws Exception {
+    testRange(8);
+  }
+  
+  @Test
+  public void testRange_6bit() throws Exception {
+    testRange(6);
+  }
+  
+  @Test
+  public void testRange_4bit() throws Exception {
+    testRange(4);
+  }
+  
+  @Test
+  public void testRange_2bit() throws Exception {
+    testRange(2);
+  }
+  
+  @Test
+  public void testInverseRange() throws Exception {
+    AtomicReaderContext context = SlowCompositeReaderWrapper.wrap(searcher.getIndexReader()).getContext();
+    NumericRangeFilter<BigInteger> f = NumericRangeFilter.newBigIntegerRange("field8", 8, BigInteger.valueOf(1000L), BigInteger.valueOf(-1000L), true, true, valueSize);
+    assertNull("A inverse range should return the null instance", 
+        f.getDocIdSet(context, context.reader().getLiveDocs()));
+    f = NumericRangeFilter.newBigIntegerRange("field8", 8, BigInteger.valueOf(Long.MAX_VALUE), null, false, false, valueSize);
+    assertNull("A exclusive range starting with Long.MAX_VALUE should return the null instance",
+               f.getDocIdSet(context, context.reader().getLiveDocs()));
+    f = NumericRangeFilter.newBigIntegerRange("field8", 8, null, BigInteger.valueOf(Long.MIN_VALUE), false, false, valueSize);
+    assertNull("A exclusive range ending with Long.MIN_VALUE should return the null instance",
+               f.getDocIdSet(context, context.reader().getLiveDocs()));
+  }
+  
+  @Test
+  public void testOneMatchQuery() throws Exception {
+    NumericRangeQuery<BigInteger> q = NumericRangeQuery.newBigIntegerRange("ascfield8", 8, BigInteger.valueOf(1000L), BigInteger.valueOf(1000L), true, true, valueSize);
+    TopDocs topDocs = searcher.search(q, noDocs);
+    ScoreDoc[] sd = topDocs.scoreDocs;
+    assertNotNull(sd);
+    assertEquals("Score doc count", 1, sd.length );
+  }
+  
+  private void testLeftOpenRange(int precisionStep) throws Exception {
+    String field="field"+precisionStep;
+    int count=3000;
+    long upper=(count-1)*distance + (distance/3) + startOffset;
+    NumericRangeQuery<BigInteger> q = NumericRangeQuery.newBigIntegerRange(field, precisionStep, null, BigInteger.valueOf(upper), true, true, valueSize);
+    TopDocs topDocs = searcher.search(q, null, noDocs, Sort.INDEXORDER);
+    ScoreDoc[] sd = topDocs.scoreDocs;
+    assertNotNull(sd);
+    assertEquals("Score doc count", count, sd.length );
+    StoredDocument doc=searcher.doc(sd[0].doc);
+    assertEquals("First doc", startOffset, new BigInteger(doc.getField(field).stringValue()).longValue());
+    doc=searcher.doc(sd[sd.length-1].doc);
+    assertEquals("Last doc", (count-1)*distance+startOffset, new BigInteger(doc.getField(field).stringValue()).longValue());
+
+    q=NumericRangeQuery.newBigIntegerRange(field, precisionStep, null, BigInteger.valueOf(upper), false, true, valueSize);
+    topDocs = searcher.search(q, null, noDocs, Sort.INDEXORDER);
+    sd = topDocs.scoreDocs;
+    assertNotNull(sd);
+    assertEquals("Score doc count", count, sd.length );
+    doc=searcher.doc(sd[0].doc);
+    assertEquals("First doc", startOffset, new BigInteger(doc.getField(field).stringValue()).longValue());
+    doc=searcher.doc(sd[sd.length-1].doc);
+    assertEquals("Last doc", (count-1)*distance+startOffset, new BigInteger(doc.getField(field).stringValue()).longValue());
+  }
+  
+  @Test
+  public void testLeftOpenRange_8bit() throws Exception {
+    testLeftOpenRange(8);
+  }
+  
+  @Test
+  public void testLeftOpenRange_6bit() throws Exception {
+    testLeftOpenRange(6);
+  }
+  
+  @Test
+  public void testLeftOpenRange_4bit() throws Exception {
+    testLeftOpenRange(4);
+  }
+  
+  @Test
+  public void testLeftOpenRange_2bit() throws Exception {
+    testLeftOpenRange(2);
+  }
+  
+  private void testRightOpenRange(int precisionStep) throws Exception {
+    String field="field"+precisionStep;
+    int count=3000;
+    long lower=(count-1)*distance + (distance/3) +startOffset;
+    NumericRangeQuery<BigInteger> q=NumericRangeQuery.newBigIntegerRange(field, precisionStep, BigInteger.valueOf(lower), null, true, true, valueSize);
+    TopDocs topDocs = searcher.search(q, null, noDocs, Sort.INDEXORDER);
+    ScoreDoc[] sd = topDocs.scoreDocs;
+    assertNotNull(sd);
+    assertEquals("Score doc count", noDocs-count, sd.length );
+    StoredDocument doc=searcher.doc(sd[0].doc);
+    assertEquals("First doc", count*distance+startOffset, new BigInteger(doc.getField(field).stringValue()).longValue());
+    doc=searcher.doc(sd[sd.length-1].doc);
+    assertEquals("Last doc", (noDocs-1)*distance+startOffset, new BigInteger(doc.getField(field).stringValue()).longValue());
+
+    q=NumericRangeQuery.newBigIntegerRange(field, precisionStep, BigInteger.valueOf(lower), null, true, false, valueSize);
+    topDocs = searcher.search(q, null, noDocs, Sort.INDEXORDER);
+    sd = topDocs.scoreDocs;
+    assertNotNull(sd);
+    assertEquals("Score doc count", noDocs-count, sd.length );
+    doc=searcher.doc(sd[0].doc);
+    assertEquals("First doc", count*distance+startOffset, new BigInteger(doc.getField(field).stringValue()).longValue());
+    doc=searcher.doc(sd[sd.length-1].doc);
+    assertEquals("Last doc", (noDocs-1)*distance+startOffset, new BigInteger(doc.getField(field).stringValue()).longValue());
+  }
+  
+  @Test
+  public void testRightOpenRange_8bit() throws Exception {
+    testRightOpenRange(8);
+  }
+  
+  @Test
+  public void testRightOpenRange_6bit() throws Exception {
+    testRightOpenRange(6);
+  }
+  
+  @Test
+  public void testRightOpenRange_4bit() throws Exception {
+    testRightOpenRange(4);
+  }
+  
+  @Test
+  public void testRightOpenRange_2bit() throws Exception {
+    testRightOpenRange(2);
+  }
+
+  
+  private void testRandomTrieAndClassicRangeQuery(int precisionStep) throws Exception {
+    String field="field"+precisionStep;
+    int totalTermCountT=0,totalTermCountC=0,termCountT,termCountC;
+    int num = TestUtil.nextInt(random(), 10, 20);
+    for (int i = 0; i < num; i++) {
+      long lower=(long)(random().nextDouble()*noDocs*distance)+startOffset;
+      long upper=(long)(random().nextDouble()*noDocs*distance)+startOffset;
+      if (lower>upper) {
+        long a=lower; lower=upper; upper=a;
+      }
+      final BytesRef lowerBytes = new BytesRef(BigNumericUtils.getBufferSize(valueSize)), upperBytes = new BytesRef(BigNumericUtils.getBufferSize(valueSize));
+      BigNumericUtils.bigIntegerToPrefixCodedBytes(BigInteger.valueOf(lower), 0, lowerBytes, valueSize);
+      BigNumericUtils.bigIntegerToPrefixCodedBytes(BigInteger.valueOf(upper), 0, upperBytes, valueSize);
+      
+      // test inclusive range
+      NumericRangeQuery<BigInteger> tq=NumericRangeQuery.newBigIntegerRange(field, precisionStep, BigInteger.valueOf(lower), BigInteger.valueOf(upper), true, true, valueSize);
+      TermRangeQuery cq=new TermRangeQuery(field, lowerBytes, upperBytes, true, true);
+      TopDocs tTopDocs = searcher.search(tq, 1);
+      TopDocs cTopDocs = searcher.search(cq, 1);
+      assertEquals("Returned count for NumericRangeQuery and TermRangeQuery must be equal", cTopDocs.totalHits, tTopDocs.totalHits );
+      totalTermCountT += termCountT = countTerms(tq);
+      totalTermCountC += termCountC = countTerms(cq);
+      checkTermCounts(precisionStep, termCountT, termCountC);
+      // test exclusive range
+      tq=NumericRangeQuery.newBigIntegerRange(field, precisionStep, BigInteger.valueOf(lower), BigInteger.valueOf(upper), false, false, valueSize);
+      cq=new TermRangeQuery(field, lowerBytes, upperBytes, false, false);
+      tTopDocs = searcher.search(tq, 1);
+      cTopDocs = searcher.search(cq, 1);
+      assertEquals("Returned count for NumericRangeQuery and TermRangeQuery must be equal", cTopDocs.totalHits, tTopDocs.totalHits );
+      totalTermCountT += termCountT = countTerms(tq);
+      totalTermCountC += termCountC = countTerms(cq);
+      checkTermCounts(precisionStep, termCountT, termCountC);
+      // test left exclusive range
+      tq=NumericRangeQuery.newBigIntegerRange(field, precisionStep, BigInteger.valueOf(lower), BigInteger.valueOf(upper), false, true, valueSize);
+      cq=new TermRangeQuery(field, lowerBytes, upperBytes, false, true);
+      tTopDocs = searcher.search(tq, 1);
+      cTopDocs = searcher.search(cq, 1);
+      assertEquals("Returned count for NumericRangeQuery and TermRangeQuery must be equal", cTopDocs.totalHits, tTopDocs.totalHits );
+      totalTermCountT += termCountT = countTerms(tq);
+      totalTermCountC += termCountC = countTerms(cq);
+      checkTermCounts(precisionStep, termCountT, termCountC);
+      // test right exclusive range
+      tq=NumericRangeQuery.newBigIntegerRange(field, precisionStep, BigInteger.valueOf(lower), BigInteger.valueOf(upper), true, false, valueSize);
+      cq=new TermRangeQuery(field, lowerBytes, upperBytes, true, false);
+      tTopDocs = searcher.search(tq, 1);
+      cTopDocs = searcher.search(cq, 1);
+      assertEquals("Returned count for NumericRangeQuery and TermRangeQuery must be equal", cTopDocs.totalHits, tTopDocs.totalHits );
+      totalTermCountT += termCountT = countTerms(tq);
+      totalTermCountC += termCountC = countTerms(cq);
+      checkTermCounts(precisionStep, termCountT, termCountC);
+    }
+    
+    checkTermCounts(precisionStep, totalTermCountT, totalTermCountC);
+    if (VERBOSE && precisionStep != Integer.MAX_VALUE) {
+      System.out.println("Average number of terms during random search on '" + field + "':");
+      System.out.println(" Numeric query: " + (((double)totalTermCountT)/(num * 4)));
+      System.out.println(" Classical query: " + (((double)totalTermCountC)/(num * 4)));
+    }
+  }
+  
+  @Test
+  public void testEmptyEnums() throws Exception {
+    int count=3000;
+    long lower=(distance*3/2)+startOffset, upper=lower + count*distance + (distance/3);
+    // test empty enum
+    assert lower < upper;
+    assertTrue(0 < countTerms(NumericRangeQuery.newBigIntegerRange("field4", 4, BigInteger.valueOf(lower), BigInteger.valueOf(upper), true, true, valueSize)));
+    assertEquals(0, countTerms(NumericRangeQuery.newBigIntegerRange("field4", 4, BigInteger.valueOf(upper), BigInteger.valueOf(lower), true, true, valueSize)));
+    // test empty enum outside of bounds
+    lower = distance*noDocs+startOffset;
+    upper = 2L * lower;
+    assert lower < upper;
+    assertEquals(0, countTerms(NumericRangeQuery.newBigIntegerRange("field4", 4, BigInteger.valueOf(lower), BigInteger.valueOf(upper), true, true, valueSize)));
+  }
+  
+  private int countTerms(MultiTermQuery q) throws Exception {
+    final Terms terms = MultiFields.getTerms(reader, q.getField());
+    if (terms == null)
+      return 0;
+    final TermsEnum termEnum = q.getTermsEnum(terms);
+    assertNotNull(termEnum);
+    int count = 0;
+    BytesRef cur, last = null;
+    while ((cur = termEnum.next()) != null) {
+      count++;
+      if (last != null) {
+        assertTrue(last.compareTo(cur) < 0);
+      }
+      last = BytesRef.deepCopyOf(cur);
+    } 
+    // LUCENE-3314: the results after next() already returned null are undefined,
+    // assertNull(termEnum.next());
+    return count;
+  }
+  
+  private void checkTermCounts(int precisionStep, int termCountT, int termCountC) {
+    if (precisionStep == Integer.MAX_VALUE) {
+      assertEquals("Number of terms should be equal for unlimited precStep", termCountC, termCountT);
+    } else {
+      assertTrue("Number of terms for NRQ should be <= compared to classical TRQ", termCountT <= termCountC);
+    }
+  }
+
+  @Test
+  public void testRandomTrieAndClassicRangeQuery_8bit() throws Exception {
+    testRandomTrieAndClassicRangeQuery(8);
+  }
+  
+  @Test
+  public void testRandomTrieAndClassicRangeQuery_6bit() throws Exception {
+    testRandomTrieAndClassicRangeQuery(6);
+  }
+  
+  @Test
+  public void testRandomTrieAndClassicRangeQuery_4bit() throws Exception {
+    testRandomTrieAndClassicRangeQuery(4);
+  }
+  
+  @Test
+  public void testRandomTrieAndClassicRangeQuery_2bit() throws Exception {
+    testRandomTrieAndClassicRangeQuery(2);
+  }
+  
+  @Test
+  public void testRandomTrieAndClassicRangeQuery_NoTrie() throws Exception {
+    testRandomTrieAndClassicRangeQuery(Integer.MAX_VALUE);
+  }
+  
+  private void testRangeSplit(int precisionStep) throws Exception {
+    String field="ascfield"+precisionStep;
+    // 10 random tests
+    int num = TestUtil.nextInt(random(), 10, 20);
+    for (int i = 0; i < num; i++) {
+      long lower=(long)(random().nextDouble()*noDocs - noDocs/2);
+      long upper=(long)(random().nextDouble()*noDocs - noDocs/2);
+      if (lower>upper) {
+        long a=lower; lower=upper; upper=a;
+      }
+      // test inclusive range
+      Query tq=NumericRangeQuery.newBigIntegerRange(field, precisionStep, BigInteger.valueOf(lower), BigInteger.valueOf(upper), true, true, valueSize);
+      TopDocs tTopDocs = searcher.search(tq, 1);
+      assertEquals("Returned count of range query must be equal to inclusive range length", upper-lower+1, tTopDocs.totalHits );
+      // test exclusive range
+      tq=NumericRangeQuery.newBigIntegerRange(field, precisionStep, BigInteger.valueOf(lower), BigInteger.valueOf(upper), false, false, valueSize);
+      tTopDocs = searcher.search(tq, 1);
+      assertEquals("Returned count of range query must be equal to exclusive range length", Math.max(upper-lower-1, 0), tTopDocs.totalHits );
+      // test left exclusive range
+      tq=NumericRangeQuery.newBigIntegerRange(field, precisionStep, BigInteger.valueOf(lower), BigInteger.valueOf(upper), false, true, valueSize);
+      tTopDocs = searcher.search(tq, 1);
+      assertEquals("Returned count of range query must be equal to half exclusive range length", upper-lower, tTopDocs.totalHits );
+      // test right exclusive range
+      tq=NumericRangeQuery.newBigIntegerRange(field, precisionStep, BigInteger.valueOf(lower), BigInteger.valueOf(upper), true, false, valueSize);
+      tTopDocs = searcher.search(tq, 1);
+      assertEquals("Returned count of range query must be equal to half exclusive range length", upper-lower, tTopDocs.totalHits );
+    }
+  }
+
+  @Test
+  public void testRangeSplit_8bit() throws Exception {
+    testRangeSplit(8);
+  }
+  
+  @Test
+  public void testRangeSplit_6bit() throws Exception {
+    testRangeSplit(6);
+  }
+  
+  @Test
+  public void testRangeSplit_4bit() throws Exception {
+    testRangeSplit(4);
+  }
+  
+  @Test
+  public void testRangeSplit_2bit() throws Exception {
+    testRangeSplit(2);
+  }
+
+  
+  private void testSorting(int precisionStep) throws Exception {
+    String field="field"+precisionStep;
+    // 10 random tests, the index order is ascending,
+    // so using a reverse sort field should retun descending documents
+    int num = TestUtil.nextInt(random(), 10, 20);
+    for (int i = 0; i < num; i++) {
+      long lower=(long)(random().nextDouble()*noDocs*distance)+startOffset;
+      long upper=(long)(random().nextDouble()*noDocs*distance)+startOffset;
+      if (lower>upper) {
+        long a=lower; lower=upper; upper=a;
+      }
+      Query tq=NumericRangeQuery.newBigIntegerRange(field, precisionStep, BigInteger.valueOf(lower), BigInteger.valueOf(upper), true, true, valueSize);
+      TopDocs topDocs = searcher.search(tq, null, noDocs, new Sort(new SortField(field, SortField.Type.BIG_INTEGER, true)));
+      if (topDocs.totalHits==0) continue;
+      ScoreDoc[] sd = topDocs.scoreDocs;
+      assertNotNull(sd);
+      long last = new BigInteger(searcher.doc(sd[0].doc).getField(field).stringValue()).longValue();
+      for (int j=1; j<sd.length; j++) {
+        long act=new BigInteger(searcher.doc(sd[j].doc).getField(field).stringValue()).longValue();
+        assertTrue("Docs should be sorted backwards", last>act );
+        last=act;
+      }
+    }
+  }
+
+  @Test
+  public void testSorting_8bit() throws Exception {
+    testSorting(8);
+  }
+  
+  @Test
+  public void testSorting_6bit() throws Exception {
+    testSorting(6);
+  }
+  
+  @Test
+  public void testSorting_4bit() throws Exception {
+    testSorting(4);
+  }
+  
+  @Test
+  public void testSorting_2bit() throws Exception {
+    testSorting(2);
+  }
+  
+  @Test
+  public void testEqualsAndHash() throws Exception {
+    QueryUtils.checkHashEquals(NumericRangeQuery.newBigIntegerRange("test1", 4, BigInteger.valueOf(10L), BigInteger.valueOf(20L), true, true, valueSize));
+    QueryUtils.checkHashEquals(NumericRangeQuery.newBigIntegerRange("test2", 4, BigInteger.valueOf(10L), BigInteger.valueOf(20L), false, true, valueSize));
+    QueryUtils.checkHashEquals(NumericRangeQuery.newBigIntegerRange("test3", 4, BigInteger.valueOf(10L), BigInteger.valueOf(20L), true, false, valueSize));
+    QueryUtils.checkHashEquals(NumericRangeQuery.newBigIntegerRange("test4", 4, BigInteger.valueOf(10L), BigInteger.valueOf(20L), false, false, valueSize));
+    QueryUtils.checkHashEquals(NumericRangeQuery.newBigIntegerRange("test5", 4, BigInteger.valueOf(10L), null, true, true, valueSize));
+    QueryUtils.checkHashEquals(NumericRangeQuery.newBigIntegerRange("test6", 4, null, BigInteger.valueOf(20L), true, true, valueSize));
+    QueryUtils.checkHashEquals(NumericRangeQuery.newBigIntegerRange("test7", 4, null, null, true, true, valueSize));
+    QueryUtils.checkEqual(
+        NumericRangeQuery.newBigIntegerRange("test8", 4, BigInteger.valueOf(10L), BigInteger.valueOf(20L), true, true, valueSize),
+        NumericRangeQuery.newBigIntegerRange("test8", 4, BigInteger.valueOf(10L), BigInteger.valueOf(20L), true, true, valueSize)
+    );
+    QueryUtils.checkUnequal(
+        NumericRangeQuery.newBigIntegerRange("test9", 4, BigInteger.valueOf(10L), BigInteger.valueOf(20L), true, true, valueSize),
+        NumericRangeQuery.newBigIntegerRange("test9", 8, BigInteger.valueOf(10L), BigInteger.valueOf(20L), true, true, valueSize)
+    );
+    QueryUtils.checkUnequal(
+        NumericRangeQuery.newBigIntegerRange("test10a", 4, BigInteger.valueOf(10L), BigInteger.valueOf(20L), true, true, valueSize),
+        NumericRangeQuery.newBigIntegerRange("test10b", 4, BigInteger.valueOf(10L), BigInteger.valueOf(20L), true, true, valueSize)
+    );
+    QueryUtils.checkUnequal(
+        NumericRangeQuery.newBigIntegerRange("test11", 4, BigInteger.valueOf(10L), BigInteger.valueOf(20L), true, true, valueSize),
+        NumericRangeQuery.newBigIntegerRange("test11", 4, BigInteger.valueOf(20L), BigInteger.valueOf(10L), true, true, valueSize)
+    );
+    QueryUtils.checkUnequal(
+        NumericRangeQuery.newBigIntegerRange("test12", 4, BigInteger.valueOf(10L), BigInteger.valueOf(20L), true, true, valueSize),
+        NumericRangeQuery.newBigIntegerRange("test12", 4, BigInteger.valueOf(10L), BigInteger.valueOf(20L), false, true, valueSize)
+    );
+  }
+}
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestSortDocValues.java b/lucene/core/src/test/org/apache/lucene/search/TestSortDocValues.java
index f4e46bd..314f601 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestSortDocValues.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestSortDocValues.java
@@ -18,7 +18,9 @@ package org.apache.lucene.search;
  */
 
 import java.io.IOException;
+import java.math.BigInteger;
 
+import org.apache.lucene.document.BigNumericDocValuesField;
 import org.apache.lucene.document.BinaryDocValuesField;
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.DoubleDocValuesField;
@@ -801,4 +803,136 @@ public class TestSortDocValues extends LuceneTestCase {
     ir.close();
     dir.close();
   }
+
+  /** Tests sorting on type BigInteger */
+  public void testBigInteger() throws IOException {
+    Directory dir = newDirectory();
+    RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
+    Document doc = new Document();
+    doc.add(new BigNumericDocValuesField("value", BigInteger.valueOf(300000)));
+    doc.add(newStringField("value", "300000", Field.Store.YES));
+    writer.addDocument(doc);
+    doc = new Document();
+    doc.add(new BigNumericDocValuesField("value", BigInteger.valueOf(-1)));
+    doc.add(newStringField("value", "-1", Field.Store.YES));
+    writer.addDocument(doc);
+    doc = new Document();
+    doc.add(new BigNumericDocValuesField("value", BigInteger.valueOf(4)));
+    doc.add(newStringField("value", "4", Field.Store.YES));
+    writer.addDocument(doc);
+    IndexReader ir = writer.getReader();
+    writer.shutdown();
+
+    IndexSearcher searcher = newSearcher(ir);
+    Sort sort = new Sort(new SortField("value", SortField.Type.BIG_INTEGER));
+
+    TopDocs td = searcher.search(new MatchAllDocsQuery(), 10, sort);
+    assertEquals(3, td.totalHits);
+    // numeric order
+    assertEquals("-1", searcher.doc(td.scoreDocs[0].doc).get("value"));
+    assertEquals("4", searcher.doc(td.scoreDocs[1].doc).get("value"));
+    assertEquals("300000", searcher.doc(td.scoreDocs[2].doc).get("value"));
+    assertNoFieldCaches();
+
+    ir.close();
+    dir.close();
+  }
+
+  /** Tests sorting on type BigInteger in reverse */
+  public void testBigIntegerReverse() throws IOException {
+    Directory dir = newDirectory();
+    RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
+    Document doc = new Document();
+    doc.add(new BigNumericDocValuesField("value", BigInteger.valueOf(300000)));
+    doc.add(newStringField("value", "300000", Field.Store.YES));
+    writer.addDocument(doc);
+    doc = new Document();
+    doc.add(new BigNumericDocValuesField("value", BigInteger.valueOf(-1)));
+    doc.add(newStringField("value", "-1", Field.Store.YES));
+    writer.addDocument(doc);
+    doc = new Document();
+    doc.add(new BigNumericDocValuesField("value", BigInteger.valueOf(4)));
+    doc.add(newStringField("value", "4", Field.Store.YES));
+    writer.addDocument(doc);
+    IndexReader ir = writer.getReader();
+    writer.shutdown();
+
+    IndexSearcher searcher = newSearcher(ir);
+    Sort sort = new Sort(new SortField("value", SortField.Type.BIG_INTEGER, true));
+
+    TopDocs td = searcher.search(new MatchAllDocsQuery(), 10, sort);
+    assertEquals(3, td.totalHits);
+    // reverse numeric order
+    assertEquals("300000", searcher.doc(td.scoreDocs[0].doc).get("value"));
+    assertEquals("4", searcher.doc(td.scoreDocs[1].doc).get("value"));
+    assertEquals("-1", searcher.doc(td.scoreDocs[2].doc).get("value"));
+    assertNoFieldCaches();
+
+    ir.close();
+    dir.close();
+  }
+
+  /** Tests sorting on type BigInteger with a missing value */
+  public void testBigIntegerMissing() throws IOException {
+    Directory dir = newDirectory();
+    RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
+    Document doc = new Document();
+    writer.addDocument(doc);
+    doc = new Document();
+    doc.add(new BigNumericDocValuesField("value", BigInteger.valueOf(-1)));
+    doc.add(newStringField("value", "-1", Field.Store.YES));
+    writer.addDocument(doc);
+    doc = new Document();
+    doc.add(new BigNumericDocValuesField("value", BigInteger.valueOf(4)));
+    doc.add(newStringField("value", "4", Field.Store.YES));
+    writer.addDocument(doc);
+    IndexReader ir = writer.getReader();
+    writer.shutdown();
+
+    IndexSearcher searcher = newSearcher(ir);
+    Sort sort = new Sort(new SortField("value", SortField.Type.BIG_INTEGER));
+
+    TopDocs td = searcher.search(new MatchAllDocsQuery(), 10, sort);
+    assertEquals(3, td.totalHits);
+    // null is treated as a 0
+    assertEquals("-1", searcher.doc(td.scoreDocs[0].doc).get("value"));
+    assertNull(searcher.doc(td.scoreDocs[1].doc).get("value"));
+    assertEquals("4", searcher.doc(td.scoreDocs[2].doc).get("value"));
+
+    ir.close();
+    dir.close();
+  }
+
+  /** Tests sorting on type BigInteger, specifying the missing value should be treated as Integer.MAX_VALUE */
+  public void testBigIntegerMissingLast() throws IOException {
+    Directory dir = newDirectory();
+    RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
+    Document doc = new Document();
+    writer.addDocument(doc);
+    doc = new Document();
+    doc.add(new BigNumericDocValuesField("value", BigInteger.valueOf(-1)));
+    doc.add(newStringField("value", "-1", Field.Store.YES));
+    writer.addDocument(doc);
+    doc = new Document();
+    doc.add(new BigNumericDocValuesField("value", BigInteger.valueOf(4)));
+    doc.add(newStringField("value", "4", Field.Store.YES));
+    writer.addDocument(doc);
+    IndexReader ir = writer.getReader();
+    writer.shutdown();
+
+    IndexSearcher searcher = newSearcher(ir);
+    SortField sortField = new SortField("value", SortField.Type.BIG_INTEGER);
+    sortField.setMissingValue(BigInteger.valueOf(Integer.MAX_VALUE));
+    Sort sort = new Sort(sortField);
+
+    TopDocs td = searcher.search(new MatchAllDocsQuery(), 10, sort);
+    assertEquals(3, td.totalHits);
+    // null is treated as a Integer.MAX_VALUE
+    assertEquals("-1", searcher.doc(td.scoreDocs[0].doc).get("value"));
+    assertEquals("4", searcher.doc(td.scoreDocs[1].doc).get("value"));
+    assertNull(searcher.doc(td.scoreDocs[2].doc).get("value"));
+
+    ir.close();
+    dir.close();
+  }
 }
diff --git a/lucene/core/src/test/org/apache/lucene/util/TestBigNumericUtils.java b/lucene/core/src/test/org/apache/lucene/util/TestBigNumericUtils.java
new file mode 100644
index 0000000..a994761
--- /dev/null
+++ b/lucene/core/src/test/org/apache/lucene/util/TestBigNumericUtils.java
@@ -0,0 +1,149 @@
+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 java.math.BigInteger;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Random;
+
+
+public class TestBigNumericUtils extends LuceneTestCase {
+
+
+  @Test
+  public void testBigIntegerConversionAndOrdering() throws Exception {
+    // generate a series of encoded longs, each numerical one bigger than the one before
+    BytesRef last=null, act=new BytesRef(BigNumericUtils.getBufferSize(128));
+    for (long l=0; l<100000L; l++) {
+      BigNumericUtils.bigIntegerToPrefixCoded(BigInteger.valueOf(l), 0, act, 128);
+      if (last!=null) {
+        // test if smaller
+        assertTrue("actual bigger than last (BytesRef)", last.compareTo(act) < 0 );
+        assertTrue("actual bigger than last (as String)", last.utf8ToString().compareTo(act.utf8ToString()) < 0 );
+      }
+      // test is back and forward conversion works
+      assertEquals("forward and back conversion should generate same long", BigInteger.valueOf(l), BigNumericUtils.prefixCodedToBigInteger(act));
+      // next step
+      last = act;
+      act = new BytesRef(BigNumericUtils.getBufferSize(128));
+    }
+  }
+
+  private void assertBigIntegerRangeSplit(final BigInteger lower, final BigInteger upper, int precisionStep,
+                                    final Iterable<BigInteger> expectedBounds, final Iterable<Integer> expectedShifts,
+                                    final int valueSize
+  ) {
+    final Iterator<BigInteger> neededBounds = (expectedBounds == null) ? null : expectedBounds.iterator();
+    final Iterator<Integer> neededShifts = (expectedShifts == null) ? null : expectedShifts.iterator();
+
+    BigNumericUtils.splitRange(new BigNumericUtils.BigIntegerRangeBuilder() {
+      @Override
+      public void addRange(BigInteger min, BigInteger max, int shift, final int valueSize) {
+        assertTrue("min, max should be inside bounds", min.compareTo(lower) >= 0 && min.compareTo(upper) <= 0
+            && max.compareTo(lower) >= 0 && max.compareTo(upper) <= 0);
+        if (neededBounds == null || neededShifts == null)
+          return;
+        assertEquals("shift", neededShifts.next().intValue(), shift);
+        assertEquals("inner min bound", neededBounds.next(), BigNumericUtils.parseBigInteger(min, valueSize).shiftRight(shift));
+        assertEquals("inner max bound", neededBounds.next(), BigNumericUtils.parseBigInteger(max, valueSize).shiftRight(shift));
+      }
+    }, valueSize, precisionStep, lower, upper);
+
+  }
+
+
+  @Test
+  public void testRandomSplit() throws Exception {
+    long num = (long) atLeast(10);
+    for (long i=0; i < num; i++) {
+      executeOneRandomSplit(random());
+    }
+  }
+
+  private void executeOneRandomSplit(final Random random) throws Exception {
+    long lower = randomLong(random);
+    long len = random.nextInt(16384*1024); // not too large bitsets, else OOME!
+    while (lower + len < lower) { // overflow
+      lower >>= 1;
+    }
+    assertBigIntegerRangeSplit(BigInteger.valueOf(lower), BigInteger.valueOf(lower + len), random.nextInt(64) + 1, null, null, 128);
+  }
+
+  @Test
+  public void testSplitBigIntegerRange() throws Exception {
+    // a hard-coded "standard" range
+    assertBigIntegerRangeSplit(BigInteger.valueOf(-5000L), BigInteger.valueOf(9500L), 4, Arrays.asList(
+        BigInteger.valueOf(0x7fffffffffffec78L), BigInteger.valueOf(0x7fffffffffffec7fL),
+        BigInteger.valueOf(0x2510L), BigInteger.valueOf(0x251cL),
+        BigInteger.valueOf(0x7fffffffffffec8L), BigInteger.valueOf(0x7fffffffffffecfL),
+        BigInteger.valueOf(0x250L), BigInteger.valueOf(0x250L),
+        BigInteger.valueOf(0x7fffffffffffedL), BigInteger.valueOf(0x7fffffffffffefL),
+        BigInteger.valueOf(0x20L), BigInteger.valueOf(0x24L),
+        BigInteger.valueOf(0x7ffffffffffffL), BigInteger.valueOf(0x1L)
+    ), Arrays.asList(
+        0, 0,
+        4, 4,
+        8, 8,
+        12
+    ), 64);
+
+    assertBigIntegerRangeSplit(BigInteger.valueOf(-5000L), BigInteger.valueOf(9500L), 64, Arrays.asList(
+        BigInteger.valueOf(0x7fffffffffffec78L),BigInteger.valueOf(0x251cL)
+    ), Arrays.asList(
+        0
+    ), 64);
+
+    assertBigIntegerRangeSplit(BigInteger.ZERO, BigInteger.valueOf(1024L+63L), 4, Arrays.asList(
+        BigInteger.valueOf(0x40L), BigInteger.valueOf(0x43L),
+        BigInteger.valueOf(0x0L),  BigInteger.valueOf(0x3L)
+    ), Arrays.asList(
+        4, 8
+    ), 64);
+  }
+
+  private long randomLong(final Random random) {
+    long val;
+    switch(random.nextInt(4)) {
+      case 0:
+        val = 1L << (random.nextInt(63)); //  patterns like 0x000000100000 (-1 yields patterns like 0x0000fff)
+        break;
+      case 1:
+        val = -1L << (random.nextInt(63)); // patterns like 0xfffff00000
+        break;
+      default:
+        val = random.nextLong();
+    }
+
+    val += random.nextInt(5)-2;
+
+    if (random.nextBoolean()) {
+      if (random.nextBoolean()) val += random.nextInt(100)-50;
+      if (random.nextBoolean()) val = ~val;
+      if (random.nextBoolean()) val = val<<1;
+      if (random.nextBoolean()) val = val>>>1;
+    }
+
+    return val;
+  }
+
+
+}
diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/builders/NumericRangeQueryNodeBuilder.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/builders/NumericRangeQueryNodeBuilder.java
index 7452679..902630c 100644
--- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/builders/NumericRangeQueryNodeBuilder.java
+++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/builders/NumericRangeQueryNodeBuilder.java
@@ -28,6 +28,8 @@ import org.apache.lucene.queryparser.flexible.standard.nodes.NumericQueryNode;
 import org.apache.lucene.queryparser.flexible.standard.nodes.NumericRangeQueryNode;
 import org.apache.lucene.search.NumericRangeQuery;
 
+import java.math.BigInteger;
+
 /**
  * Builds {@link NumericRangeQuery}s out of {@link NumericRangeQueryNode}s.
  *
@@ -81,9 +83,15 @@ public class NumericRangeQueryNodeBuilder implements StandardQueryBuilder {
         return NumericRangeQuery.newDoubleRange(field, precisionStep,
             (Double) lowerNumber, (Double) upperNumber, minInclusive,
             maxInclusive);
+
+      case BIG_INTEGER:
+        int valueSize = numericConfig.getValueSize();
+        return NumericRangeQuery.newBigIntegerRange(field, precisionStep,
+            (BigInteger) lowerNumber, (BigInteger) upperNumber, minInclusive,
+            maxInclusive, valueSize);
         
-        default :
-          throw new QueryNodeException(new MessageImpl(
+      default :
+        throw new QueryNodeException(new MessageImpl(
             QueryParserMessages.UNSUPPORTED_NUMERIC_DATA_TYPE, numberType));
         
     }
diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/config/NumericConfig.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/config/NumericConfig.java
index 75ea68e..c27a10e 100644
--- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/config/NumericConfig.java
+++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/config/NumericConfig.java
@@ -36,6 +36,8 @@ public class NumericConfig {
   private NumberFormat format;
   
   private NumericType type;
+
+  private int valueSize; // value size for BigInteger
   
   /**
    * Constructs a {@link NumericConfig} object.
@@ -54,10 +56,19 @@ public class NumericConfig {
    */
   public NumericConfig(int precisionStep, NumberFormat format,
       NumericType type) {
+    this(precisionStep, format, type, 0);
+    
+  }
+
+  /**
+   * todo: javadoc
+   */
+  public NumericConfig(int precisionStep, NumberFormat format,
+                       NumericType type, int valueSize) {
     setPrecisionStep(precisionStep);
     setNumberFormat(format);
     setType(type);
-    
+    setValueSize(valueSize);
   }
   
   /**
@@ -135,7 +146,19 @@ public class NumericConfig {
     this.format = format;
     
   }
-  
+
+  /**
+   * todo: javadoc
+   * @param valueSize
+   */
+  public void setValueSize(int valueSize) {
+    this.valueSize = valueSize;
+  }
+
+  public int getValueSize() {
+    return valueSize;
+  }
+
   @Override
   public boolean equals(Object obj) {
     
diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/NumericRangeQueryNode.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/NumericRangeQueryNode.java
index ee154aa..a7d1aab 100644
--- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/NumericRangeQueryNode.java
+++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/NumericRangeQueryNode.java
@@ -24,6 +24,8 @@ import org.apache.lucene.queryparser.flexible.core.nodes.FieldQueryNode;
 import org.apache.lucene.queryparser.flexible.messages.MessageImpl;
 import org.apache.lucene.queryparser.flexible.standard.config.NumericConfig;
 
+import java.math.BigInteger;
+
 /**
  * This query node represents a range query composed by {@link NumericQueryNode}
  * bounds, which means the bound values are {@link Number}s.
@@ -63,6 +65,8 @@ public class NumericRangeQueryNode extends
       return NumericType.DOUBLE;
     } else if (number instanceof Float) {
       return NumericType.FLOAT;
+    } else if (number instanceof BigInteger) {
+      return NumericType.BIG_INTEGER;
     } else {
       throw new QueryNodeException(
           new MessageImpl(
diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/processors/NumericQueryNodeProcessor.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/processors/NumericQueryNodeProcessor.java
index 7c18eb1..a23e864 100644
--- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/processors/NumericQueryNodeProcessor.java
+++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/processors/NumericQueryNodeProcessor.java
@@ -17,6 +17,7 @@ package org.apache.lucene.queryparser.flexible.standard.processors;
  * limitations under the License.
  */
 
+import java.math.BigDecimal;
 import java.text.NumberFormat;
 import java.text.ParseException;
 import java.util.List;
@@ -89,17 +90,17 @@ public class NumericQueryNodeProcessor extends QueryNodeProcessorImpl {
             Number number = null;
             
             if (text.length() > 0) {
-              
+
               try {
                 number = numberFormat.parse(text);
-                
+
               } catch (ParseException e) {
                 throw new QueryNodeParseException(new MessageImpl(
                     QueryParserMessages.COULD_NOT_PARSE_NUMBER, fieldNode
                         .getTextAsString(), numberFormat.getClass()
                         .getCanonicalName()), e);
               }
-              
+
               switch (numericConfig.getType()) {
                 case LONG:
                   number = number.longValue();
@@ -112,8 +113,13 @@ public class NumericQueryNodeProcessor extends QueryNodeProcessorImpl {
                   break;
                 case FLOAT:
                   number = number.floatValue();
+                  break;
+                case BIG_INTEGER:
+                  assert number instanceof BigDecimal;
+                  number = ((BigDecimal) number).toBigInteger();
+                  break;
               }
-              
+
             } else {
               throw new QueryNodeParseException(new MessageImpl(
                   QueryParserMessages.NUMERIC_CANNOT_BE_EMPTY, fieldNode.getFieldAsString()));
diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/processors/NumericRangeQueryNodeProcessor.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/processors/NumericRangeQueryNodeProcessor.java
index 7710fbc..acc4d98 100644
--- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/processors/NumericRangeQueryNodeProcessor.java
+++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/processors/NumericRangeQueryNodeProcessor.java
@@ -17,6 +17,8 @@ package org.apache.lucene.queryparser.flexible.standard.processors;
  * limitations under the License.
  */
 
+import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.text.NumberFormat;
 import java.text.ParseException;
 import java.util.List;
@@ -87,33 +89,33 @@ public class NumericRangeQueryNodeProcessor extends QueryNodeProcessorImpl {
             Number lowerNumber = null, upperNumber = null;
             
              if (lowerText.length() > 0) {
-              
+
               try {
                 lowerNumber = numberFormat.parse(lowerText);
-                
+
               } catch (ParseException e) {
                 throw new QueryNodeParseException(new MessageImpl(
                     QueryParserMessages.COULD_NOT_PARSE_NUMBER, lower
                         .getTextAsString(), numberFormat.getClass()
                         .getCanonicalName()), e);
               }
-              
+
             }
-            
+
              if (upperText.length() > 0) {
-            
+
               try {
                 upperNumber = numberFormat.parse(upperText);
-                
+
               } catch (ParseException e) {
                 throw new QueryNodeParseException(new MessageImpl(
                     QueryParserMessages.COULD_NOT_PARSE_NUMBER, upper
                         .getTextAsString(), numberFormat.getClass()
                         .getCanonicalName()), e);
               }
-            
+
             }
-            
+
             switch (numericConfig.getType()) {
               case LONG:
                 if (upperNumber != null) upperNumber = upperNumber.longValue();
@@ -130,6 +132,17 @@ public class NumericRangeQueryNodeProcessor extends QueryNodeProcessorImpl {
               case FLOAT:
                 if (upperNumber != null) upperNumber = upperNumber.floatValue();
                 if (lowerNumber != null) lowerNumber = lowerNumber.floatValue();
+                break;
+              case BIG_INTEGER:
+                if (upperNumber != null) {
+                  assert upperNumber instanceof BigDecimal;
+                  upperNumber = ((BigDecimal) upperNumber).toBigInteger();
+                }
+                if (lowerNumber != null) {
+                  assert lowerNumber instanceof BigDecimal;
+                  lowerNumber = ((BigDecimal) lowerNumber).toBigInteger();
+                }
+                break;
             }
             
             NumericQueryNode lowerNode = new NumericQueryNode(
diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/xml/builders/NumericRangeFilterBuilder.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/xml/builders/NumericRangeFilterBuilder.java
index e7fba72..9430985 100644
--- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/xml/builders/NumericRangeFilterBuilder.java
+++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/xml/builders/NumericRangeFilterBuilder.java
@@ -21,6 +21,8 @@ import org.apache.lucene.index.AtomicReaderContext;
 import org.apache.lucene.search.DocIdSet;
 import org.apache.lucene.search.Filter;
 import org.apache.lucene.search.NumericRangeFilter;
+import org.apache.lucene.search.NumericRangeQuery;
+import org.apache.lucene.util.BigNumericUtils;
 import org.apache.lucene.util.Bits;
 import org.apache.lucene.util.NumericUtils;
 import org.apache.lucene.queryparser.xml.DOMUtils;
@@ -29,6 +31,7 @@ import org.apache.lucene.queryparser.xml.ParserException;
 import org.w3c.dom.Element;
 
 import java.io.IOException;
+import java.math.BigInteger;
 
 /**
  * Creates a {@link NumericRangeFilter}. The table below specifies the required
@@ -140,8 +143,13 @@ public class NumericRangeFilterBuilder implements FilterBuilder {
         filter = NumericRangeFilter.newFloatRange(field, precisionStep, Float
             .valueOf(lowerTerm), Float.valueOf(upperTerm), lowerInclusive,
             upperInclusive);
+      } else if (type.equalsIgnoreCase("big_integer")) {
+          int valueSize = DOMUtils.getAttribute(e, "valueSize", BigNumericUtils.VALUE_SIZE_DEFAULT);
+          filter = NumericRangeFilter.newBigIntegerRange(field, precisionStep, new BigInteger(lowerTerm),
+              new BigInteger(upperTerm), lowerInclusive,
+              upperInclusive, valueSize);
       } else {
-        throw new ParserException("type attribute must be one of: [long, int, double, float]");
+        throw new ParserException("type attribute must be one of: [long, int, double, float, big_integer]");
       }
       return filter;
     } catch (NumberFormatException nfe) {
diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/xml/builders/NumericRangeQueryBuilder.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/xml/builders/NumericRangeQueryBuilder.java
index f4909d1..a9f9d41 100644
--- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/xml/builders/NumericRangeQueryBuilder.java
+++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/xml/builders/NumericRangeQueryBuilder.java
@@ -19,12 +19,15 @@ package org.apache.lucene.queryparser.xml.builders;
 
 import org.apache.lucene.search.NumericRangeQuery;
 import org.apache.lucene.search.Query;
+import org.apache.lucene.util.BigNumericUtils;
 import org.apache.lucene.util.NumericUtils;
 import org.apache.lucene.queryparser.xml.DOMUtils;
 import org.apache.lucene.queryparser.xml.ParserException;
 import org.apache.lucene.queryparser.xml.QueryBuilder;
 import org.w3c.dom.Element;
 
+import java.math.BigInteger;
+
 /**
  * Creates a {@link NumericRangeQuery}. The table below specifies the required
  * attributes and the defaults if optional attributes are omitted. For more
@@ -115,8 +118,13 @@ public class NumericRangeQueryBuilder implements QueryBuilder {
         filter = NumericRangeQuery.newFloatRange(field, precisionStep, Float
             .valueOf(lowerTerm), Float.valueOf(upperTerm), lowerInclusive,
             upperInclusive);
+      } else if (type.equalsIgnoreCase("big_integer")) {
+        int valueSize = DOMUtils.getAttribute(e, "valueSize", BigNumericUtils.VALUE_SIZE_DEFAULT);
+        filter = NumericRangeQuery.newBigIntegerRange(field, precisionStep, new BigInteger(lowerTerm),
+            new BigInteger(upperTerm), lowerInclusive,
+            upperInclusive, valueSize);
       } else {
-        throw new ParserException("type attribute must be one of: [long, int, double, float]");
+        throw new ParserException("type attribute must be one of: [long, int, double, float, big_integer]");
       }
       return filter;
     } catch (NumberFormatException nfe) {
diff --git a/lucene/queryparser/src/test/org/apache/lucene/queryparser/flexible/standard/TestNumericQueryParser.java b/lucene/queryparser/src/test/org/apache/lucene/queryparser/flexible/standard/TestNumericQueryParser.java
index 6f88c28..a01caeb 100644
--- a/lucene/queryparser/src/test/org/apache/lucene/queryparser/flexible/standard/TestNumericQueryParser.java
+++ b/lucene/queryparser/src/test/org/apache/lucene/queryparser/flexible/standard/TestNumericQueryParser.java
@@ -18,7 +18,9 @@ package org.apache.lucene.queryparser.flexible.standard;
  */
 
 import java.io.IOException;
+import java.math.BigInteger;
 import java.text.DateFormat;
+import java.text.DecimalFormat;
 import java.text.NumberFormat;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
@@ -32,6 +34,7 @@ import java.util.TimeZone;
 
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.analysis.MockAnalyzer;
+import org.apache.lucene.document.BigIntegerField;
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.DoubleField;
 import org.apache.lucene.document.Field;
@@ -161,6 +164,7 @@ public class TestNumericQueryParser extends LuceneTestCase {
     NUMBER_FORMAT.setMinimumFractionDigits((random().nextInt() & 20) + 1);
     NUMBER_FORMAT.setMaximumIntegerDigits((random().nextInt() & 20) + 1);
     NUMBER_FORMAT.setMinimumIntegerDigits((random().nextInt() & 20) + 1);
+    ((DecimalFormat)NUMBER_FORMAT).setParseBigDecimal(true);
     
     double randomDouble;
     long randomLong;
@@ -183,6 +187,7 @@ public class TestNumericQueryParser extends LuceneTestCase {
     randomNumberMap.put(NumericType.INT.name(), randomInt);
     randomNumberMap.put(NumericType.FLOAT.name(), randomFloat);
     randomNumberMap.put(NumericType.DOUBLE.name(), randomDouble);
+    randomNumberMap.put(NumericType.BIG_INTEGER.name(), BigInteger.valueOf(randomLong));
     randomNumberMap.put(DATE_FIELD_NAME, randomDate);
     
     RANDOM_NUMBER_MAP = Collections.unmodifiableMap(randomNumberMap);
@@ -200,7 +205,7 @@ public class TestNumericQueryParser extends LuceneTestCase {
     
     for (NumericType type : NumericType.values()) {
       numericConfigMap.put(type.name(), new NumericConfig(PRECISION_STEP,
-          NUMBER_FORMAT, type));
+          NUMBER_FORMAT, type, 64));
 
       FieldType ft = new FieldType(IntField.TYPE_NOT_STORED);
       ft.setNumericType(type);
@@ -222,6 +227,9 @@ public class TestNumericQueryParser extends LuceneTestCase {
       case DOUBLE:
         field = new DoubleField(type.name(), 0.0, ft);
         break;
+      case BIG_INTEGER:
+        field = new BigIntegerField(type.name(), BigInteger.valueOf(0), ft, 64);
+        break;
       default:
         fail();
         field = null;
@@ -277,7 +285,10 @@ public class TestNumericQueryParser extends LuceneTestCase {
           
         } else if (NumericType.INT.name().equals(fieldName)) {
           number = -number.intValue();
-          
+
+        } else if (NumericType.BIG_INTEGER.name().equals(fieldName)) {
+          number = ((BigInteger)number).negate();
+
         } else {
           throw new IllegalArgumentException("field name not found: "
               + fieldName);
@@ -286,8 +297,11 @@ public class TestNumericQueryParser extends LuceneTestCase {
         return number;
         
       default:
-        return 0;
-        
+        if (NumericType.BIG_INTEGER.name().equals(fieldName)) {
+          return BigInteger.ZERO;
+        } else {
+          return 0;
+        }
     }
     
   }
@@ -311,6 +325,9 @@ public class TestNumericQueryParser extends LuceneTestCase {
     number = getNumberType(numberType, NumericType.FLOAT.name());
     numericFieldMap.get(NumericType.FLOAT.name()).setFloatValue(
         number.floatValue());
+
+    number = getNumberType(numberType, NumericType.BIG_INTEGER.name());
+    numericFieldMap.get(NumericType.BIG_INTEGER.name()).setBigIntegerValue((BigInteger) number);
     
     number = getNumberType(numberType, DATE_FIELD_NAME);
     numericFieldMap.get(DATE_FIELD_NAME).setLongValue(number.longValue());
diff --git a/lucene/queryparser/src/test/org/apache/lucene/queryparser/xml/builders/TestNumericRangeFilterBuilder.java b/lucene/queryparser/src/test/org/apache/lucene/queryparser/xml/builders/TestNumericRangeFilterBuilder.java
index af94ede..5dd64c3 100644
--- a/lucene/queryparser/src/test/org/apache/lucene/queryparser/xml/builders/TestNumericRangeFilterBuilder.java
+++ b/lucene/queryparser/src/test/org/apache/lucene/queryparser/xml/builders/TestNumericRangeFilterBuilder.java
@@ -37,6 +37,7 @@ import javax.xml.parsers.ParserConfigurationException;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.math.BigInteger;
 import java.nio.charset.StandardCharsets;
 
 public class TestNumericRangeFilterBuilder extends LuceneTestCase {
@@ -203,6 +204,35 @@ public class TestNumericRangeFilterBuilder extends LuceneTestCase {
     assertFalse(numRangeFilter2.includesMax());
   }
 
+  @SuppressWarnings({"unchecked","rawtypes"})
+  public void testGetFilterBigInteger() throws Exception {
+    NumericRangeFilterBuilder filterBuilder = new NumericRangeFilterBuilder();
+    filterBuilder.setStrictMode(true);
+
+    String xml = "<NumericRangeFilter fieldName='AGE' type='big_integer' lowerTerm='-2321' upperTerm='60000000' valueSize='64'/>";
+    Document doc = getDocumentFromString(xml);
+    Filter filter = filterBuilder.getFilter(doc.getDocumentElement());
+    assertTrue(filter instanceof NumericRangeFilter<?>);
+
+    NumericRangeFilter<BigInteger> numRangeFilter = (NumericRangeFilter) filter;
+    assertEquals(BigInteger.valueOf(-2321L), numRangeFilter.getMin());
+    assertEquals(BigInteger.valueOf(60000000L), numRangeFilter.getMax());
+    assertEquals("AGE", numRangeFilter.getField());
+    assertTrue(numRangeFilter.includesMin());
+    assertTrue(numRangeFilter.includesMax());
+
+    String xml2 = "<NumericRangeFilter fieldName='AGE' type='big_integer' lowerTerm='-2321' upperTerm='60000000' includeUpper='false' valueSize='64'/>";
+    Document doc2 = getDocumentFromString(xml2);
+    Filter filter2 = filterBuilder.getFilter(doc2.getDocumentElement());
+    assertTrue(filter2 instanceof NumericRangeFilter<?>);
+    NumericRangeFilter<BigInteger> numRangeFilter2 = (NumericRangeFilter) filter2;
+    assertEquals(BigInteger.valueOf(-2321L), numRangeFilter2.getMin());
+    assertEquals(BigInteger.valueOf(60000000L), numRangeFilter2.getMax());
+    assertEquals("AGE", numRangeFilter2.getField());
+    assertTrue(numRangeFilter2.includesMin());
+    assertFalse(numRangeFilter2.includesMax());
+  }
+
   private static Document getDocumentFromString(String str)
       throws SAXException, IOException, ParserConfigurationException {
     InputStream is = new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8));
diff --git a/lucene/queryparser/src/test/org/apache/lucene/queryparser/xml/builders/TestNumericRangeQueryBuilder.java b/lucene/queryparser/src/test/org/apache/lucene/queryparser/xml/builders/TestNumericRangeQueryBuilder.java
index 53a7f09..3e8ad70 100644
--- a/lucene/queryparser/src/test/org/apache/lucene/queryparser/xml/builders/TestNumericRangeQueryBuilder.java
+++ b/lucene/queryparser/src/test/org/apache/lucene/queryparser/xml/builders/TestNumericRangeQueryBuilder.java
@@ -31,6 +31,7 @@ import javax.xml.parsers.ParserConfigurationException;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.math.BigInteger;
 import java.nio.charset.StandardCharsets;
 
 public class TestNumericRangeQueryBuilder extends LuceneTestCase {
@@ -166,6 +167,34 @@ public class TestNumericRangeQueryBuilder extends LuceneTestCase {
     assertFalse(numRangeFilter2.includesMax());
   }
 
+  @SuppressWarnings({"unchecked","rawtypes"})
+  public void testGetFilterBigInteger() throws Exception {
+    NumericRangeQueryBuilder filterBuilder = new NumericRangeQueryBuilder();
+
+    String xml = "<NumericRangeQuery fieldName='AGE' type='big_integer' lowerTerm='-2321' upperTerm='60000000' valueSize='64'/>";
+    Document doc = getDocumentFromString(xml);
+    Query filter = filterBuilder.getQuery(doc.getDocumentElement());
+    assertTrue(filter instanceof NumericRangeQuery<?>);
+    NumericRangeQuery<BigInteger> numRangeFilter = (NumericRangeQuery) filter;
+    assertEquals(BigInteger.valueOf(-2321L), numRangeFilter.getMin());
+    assertEquals(BigInteger.valueOf(60000000L), numRangeFilter.getMax());
+    assertEquals("AGE", numRangeFilter.getField());
+    assertTrue(numRangeFilter.includesMin());
+    assertTrue(numRangeFilter.includesMax());
+
+    String xml2 = "<NumericRangeQuery fieldName='AGE' type='big_integer' lowerTerm='-2321' upperTerm='60000000' includeUpper='false' valueSize='64'/>";
+    Document doc2 = getDocumentFromString(xml2);
+    Query filter2 = filterBuilder.getQuery(doc2.getDocumentElement());
+    assertTrue(filter2 instanceof NumericRangeQuery<?>);
+
+    NumericRangeQuery<BigInteger> numRangeFilter2 = (NumericRangeQuery) filter2;
+    assertEquals(BigInteger.valueOf(-2321L), numRangeFilter2.getMin());
+    assertEquals(BigInteger.valueOf(60000000L), numRangeFilter2.getMax());
+    assertEquals("AGE", numRangeFilter2.getField());
+    assertTrue(numRangeFilter2.includesMin());
+    assertFalse(numRangeFilter2.includesMax());
+  }
+
   private static Document getDocumentFromString(String str)
       throws SAXException, IOException, ParserConfigurationException {
     InputStream is = new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8));
diff --git a/lucene/test-framework/src/java/org/apache/lucene/index/BaseStoredFieldsFormatTestCase.java b/lucene/test-framework/src/java/org/apache/lucene/index/BaseStoredFieldsFormatTestCase.java
index 445e1db..ea07555 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/index/BaseStoredFieldsFormatTestCase.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/index/BaseStoredFieldsFormatTestCase.java
@@ -18,6 +18,7 @@ package org.apache.lucene.index;
  */
 
 import java.io.IOException;
+import java.math.BigInteger;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -34,6 +35,7 @@ import org.apache.lucene.codecs.Codec;
 import org.apache.lucene.codecs.StoredFieldsFormat;
 import org.apache.lucene.codecs.lucene46.Lucene46Codec;
 import org.apache.lucene.codecs.simpletext.SimpleTextCodec;
+import org.apache.lucene.document.BigIntegerField;
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.DoubleField;
 import org.apache.lucene.document.Field;
@@ -267,19 +269,28 @@ public abstract class BaseStoredFieldsFormatTestCase extends BaseIndexFileFormat
           typeAnswer = NumericType.DOUBLE;
         }
       } else {
-        // int/long
+        // biginteger/int/long
         if (random().nextBoolean()) {
-          final int i = random().nextInt();
-          answer = Integer.valueOf(i);
-          nf = new IntField("nf", i, Field.Store.NO);
-          sf = new StoredField("nf", i);
-          typeAnswer = NumericType.INT;
-        } else {
           final long l = random().nextLong();
-          answer = Long.valueOf(l);
-          nf = new LongField("nf", l, Field.Store.NO);
-          sf = new StoredField("nf", l);
-          typeAnswer = NumericType.LONG;
+          answer = BigInteger.valueOf(l);
+          nf = new BigIntegerField("nf", BigInteger.valueOf(l), Field.Store.NO, 64);
+          sf = new StoredField("nf", BigInteger.valueOf(l));
+          typeAnswer = NumericType.BIG_INTEGER;
+        } else {
+          // int/long
+          if (random().nextBoolean()) {
+            final int i = random().nextInt();
+            answer = Integer.valueOf(i);
+            nf = new IntField("nf", i, Field.Store.NO);
+            sf = new StoredField("nf", i);
+            typeAnswer = NumericType.INT;
+          } else {
+            final long l = random().nextLong();
+            answer = Long.valueOf(l);
+            nf = new LongField("nf", l, Field.Store.NO);
+            sf = new StoredField("nf", l);
+            typeAnswer = NumericType.LONG;
+          }
         }
       }
       doc.add(nf);
@@ -343,6 +354,7 @@ public abstract class BaseStoredFieldsFormatTestCase extends BaseIndexFileFormat
     final int i = random().nextBoolean() ? random().nextInt(42) : random().nextInt();
     final float f = random().nextFloat();
     final double d = random().nextDouble();
+    final BigInteger bigInteger = BigInteger.valueOf(l);
 
     List<Field> fields = Arrays.asList(
         new Field("bytes", bytes, ft),
@@ -350,7 +362,8 @@ public abstract class BaseStoredFieldsFormatTestCase extends BaseIndexFileFormat
         new LongField("long", l, Store.YES),
         new IntField("int", i, Store.YES),
         new FloatField("float", f, Store.YES),
-        new DoubleField("double", d, Store.YES)
+        new DoubleField("double", d, Store.YES),
+        new BigIntegerField("biginteger", bigInteger, Store.YES, 64)
     );
 
     for (int k = 0; k < 100; ++k) {
diff --git a/lucene/test-framework/src/java/org/apache/lucene/index/FieldFilterAtomicReader.java b/lucene/test-framework/src/java/org/apache/lucene/index/FieldFilterAtomicReader.java
index 5f073e8..f902433 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/index/FieldFilterAtomicReader.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/index/FieldFilterAtomicReader.java
@@ -18,6 +18,7 @@ package org.apache.lucene.index;
  */
 
 import java.io.IOException;
+import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.Set;
@@ -103,6 +104,11 @@ public final class FieldFilterAtomicReader extends FilterAtomicReader {
       }
 
       @Override
+      public void bigIntegerField(FieldInfo fieldInfo, BigInteger value) throws IOException {
+        visitor.bigIntegerField(fieldInfo, value);
+      }
+
+      @Override
       public Status needsField(FieldInfo fieldInfo) throws IOException {
         return hasField(fieldInfo.name) ? visitor.needsField(fieldInfo) : Status.NO;
       }
-- 
1.8.5.2.msysgit.0

