You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ab...@apache.org on 2017/03/14 09:20:56 UTC

[18/32] lucene-solr:jira/solr-10247: LUCENE-7740: Refactor Range Fields to remove Field suffix (e.g., DoubleRange), move InetAddressRange and InetAddressPoint from sandbox to misc module, and refactor all other range fields from sandbox to core.

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/sandbox/src/test/org/apache/lucene/search/TestDoubleRangeFieldQueries.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/test/org/apache/lucene/search/TestDoubleRangeFieldQueries.java b/lucene/sandbox/src/test/org/apache/lucene/search/TestDoubleRangeFieldQueries.java
deleted file mode 100644
index 43630e3..0000000
--- a/lucene/sandbox/src/test/org/apache/lucene/search/TestDoubleRangeFieldQueries.java
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.lucene.search;
-
-import java.util.Arrays;
-
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.DoubleRangeField;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.RandomIndexWriter;
-import org.apache.lucene.store.Directory;
-
-/**
- * Random testing for RangeFieldQueries.
- */
-public class TestDoubleRangeFieldQueries extends BaseRangeFieldQueryTestCase {
-  private static final String FIELD_NAME = "doubleRangeField";
-
-  private double nextDoubleInternal() {
-    if (rarely()) {
-      return random().nextBoolean() ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
-    }
-    double max = Double.MAX_VALUE / 2;
-    return (max + max) * random().nextDouble() - max;
-  }
-
-  @Override
-  protected Range nextRange(int dimensions) {
-    double[] min = new double[dimensions];
-    double[] max = new double[dimensions];
-
-    double minV, maxV;
-    for (int d=0; d<dimensions; ++d) {
-      minV = nextDoubleInternal();
-      maxV = nextDoubleInternal();
-      min[d] = Math.min(minV, maxV);
-      max[d] = Math.max(minV, maxV);
-    }
-    return new DoubleRange(min, max);
-  }
-
-  @Override
-  protected DoubleRangeField newRangeField(Range r) {
-    return new DoubleRangeField(FIELD_NAME, ((DoubleRange)r).min, ((DoubleRange)r).max);
-  }
-
-  @Override
-  protected Query newIntersectsQuery(Range r) {
-    return DoubleRangeField.newIntersectsQuery(FIELD_NAME, ((DoubleRange)r).min, ((DoubleRange)r).max);
-  }
-
-  @Override
-  protected Query newContainsQuery(Range r) {
-    return DoubleRangeField.newContainsQuery(FIELD_NAME, ((DoubleRange)r).min, ((DoubleRange)r).max);
-  }
-
-  @Override
-  protected Query newWithinQuery(Range r) {
-    return DoubleRangeField.newWithinQuery(FIELD_NAME, ((DoubleRange)r).min, ((DoubleRange)r).max);
-  }
-
-  @Override
-  protected Query newCrossesQuery(Range r) {
-    return DoubleRangeField.newCrossesQuery(FIELD_NAME, ((DoubleRange)r).min, ((DoubleRange)r).max);
-  }
-
-  /** Basic test */
-  public void testBasics() throws Exception {
-    Directory dir = newDirectory();
-    RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
-
-    // intersects (within)
-    Document document = new Document();
-    document.add(new DoubleRangeField(FIELD_NAME, new double[] {-10.0, -10.0}, new double[] {9.1, 10.1}));
-    writer.addDocument(document);
-
-    // intersects (crosses)
-    document = new Document();
-    document.add(new DoubleRangeField(FIELD_NAME, new double[] {10.0, -10.0}, new double[] {20.0, 10.0}));
-    writer.addDocument(document);
-
-    // intersects (contains, crosses)
-    document = new Document();
-    document.add(new DoubleRangeField(FIELD_NAME, new double[] {-20.0, -20.0}, new double[] {30.0, 30.1}));
-    writer.addDocument(document);
-
-    // intersects (crosses)
-    document = new Document();
-    document.add(new DoubleRangeField(FIELD_NAME, new double[] {-11.1, -11.2}, new double[] {1.23, 11.5}));
-    writer.addDocument(document);
-
-    // intersects (crosses)
-    document = new Document();
-    document.add(new DoubleRangeField(FIELD_NAME, new double[] {12.33, 1.2}, new double[] {15.1, 29.9}));
-    writer.addDocument(document);
-
-    // disjoint
-    document = new Document();
-    document.add(new DoubleRangeField(FIELD_NAME, new double[] {-122.33, 1.2}, new double[] {-115.1, 29.9}));
-    writer.addDocument(document);
-
-    // intersects (crosses)
-    document = new Document();
-    document.add(new DoubleRangeField(FIELD_NAME, new double[] {Double.NEGATIVE_INFINITY, 1.2}, new double[] {-11.0, 29.9}));
-    writer.addDocument(document);
-
-    // equal (within, contains, intersects)
-    document = new Document();
-    document.add(new DoubleRangeField(FIELD_NAME, new double[] {-11, -15}, new double[] {15, 20}));
-    writer.addDocument(document);
-
-    // search
-    IndexReader reader = writer.getReader();
-    IndexSearcher searcher = newSearcher(reader);
-    assertEquals(7, searcher.count(DoubleRangeField.newIntersectsQuery(FIELD_NAME,
-        new double[] {-11.0, -15.0}, new double[] {15.0, 20.0})));
-    assertEquals(2, searcher.count(DoubleRangeField.newWithinQuery(FIELD_NAME,
-        new double[] {-11.0, -15.0}, new double[] {15.0, 20.0})));
-    assertEquals(2, searcher.count(DoubleRangeField.newContainsQuery(FIELD_NAME,
-        new double[] {-11.0, -15.0}, new double[] {15.0, 20.0})));
-    assertEquals(5, searcher.count(DoubleRangeField.newCrossesQuery(FIELD_NAME,
-        new double[] {-11.0, -15.0}, new double[] {15.0, 20.0})));
-
-    reader.close();
-    writer.close();
-    dir.close();
-  }
-
-  /** DoubleRange test class implementation - use to validate DoubleRangeField */
-  private class DoubleRange extends Range {
-    double[] min;
-    double[] max;
-
-    DoubleRange(double[] min, double[] max) {
-      assert min != null && max != null && min.length > 0 && max.length > 0
-          : "test box: min/max cannot be null or empty";
-      assert min.length == max.length : "test box: min/max length do not agree";
-      this.min = min;
-      this.max = max;
-    }
-
-    @Override
-    protected int numDimensions() {
-      return min.length;
-    }
-
-    @Override
-    protected Double getMin(int dim) {
-      return min[dim];
-    }
-
-    @Override
-    protected void setMin(int dim, Object val) {
-      double v = (Double)val;
-      if (min[dim] < v) {
-        max[dim] = v;
-      } else {
-        min[dim] = v;
-      }
-    }
-
-    @Override
-    protected Double getMax(int dim) {
-      return max[dim];
-    }
-
-    @Override
-    protected void setMax(int dim, Object val) {
-      double v = (Double)val;
-      if (max[dim] > v) {
-        min[dim] = v;
-      } else {
-        max[dim] = v;
-      }
-    }
-
-    @Override
-    protected boolean isEqual(Range other) {
-      DoubleRange o = (DoubleRange)other;
-      return Arrays.equals(min, o.min) && Arrays.equals(max, o.max);
-    }
-
-    @Override
-    protected boolean isDisjoint(Range o) {
-      DoubleRange other = (DoubleRange)o;
-      for (int d=0; d<this.min.length; ++d) {
-        if (this.min[d] > other.max[d] || this.max[d] < other.min[d]) {
-          // disjoint:
-          return true;
-        }
-      }
-      return false;
-    }
-
-    @Override
-    protected boolean isWithin(Range o) {
-      DoubleRange other = (DoubleRange)o;
-      for (int d=0; d<this.min.length; ++d) {
-        if ((this.min[d] >= other.min[d] && this.max[d] <= other.max[d]) == false) {
-          // not within:
-          return false;
-        }
-      }
-      return true;
-    }
-
-    @Override
-    protected boolean contains(Range o) {
-      DoubleRange other = (DoubleRange) o;
-      for (int d=0; d<this.min.length; ++d) {
-        if ((this.min[d] <= other.min[d] && this.max[d] >= other.max[d]) == false) {
-          // not contains:
-          return false;
-        }
-      }
-      return true;
-    }
-
-    @Override
-    public String toString() {
-      StringBuilder b = new StringBuilder();
-      b.append("Box(");
-      b.append(min[0]);
-      b.append(" TO ");
-      b.append(max[0]);
-      for (int d=1; d<min.length; ++d) {
-        b.append(", ");
-        b.append(min[d]);
-        b.append(" TO ");
-        b.append(max[d]);
-      }
-      b.append(")");
-
-      return b.toString();
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/sandbox/src/test/org/apache/lucene/search/TestFloatRangeFieldQueries.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/test/org/apache/lucene/search/TestFloatRangeFieldQueries.java b/lucene/sandbox/src/test/org/apache/lucene/search/TestFloatRangeFieldQueries.java
deleted file mode 100644
index 3509e35..0000000
--- a/lucene/sandbox/src/test/org/apache/lucene/search/TestFloatRangeFieldQueries.java
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.lucene.search;
-
-import java.util.Arrays;
-
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.FloatRangeField;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.RandomIndexWriter;
-import org.apache.lucene.store.Directory;
-
-/**
- * Random testing for FloatRangeField Queries.
- */
-public class TestFloatRangeFieldQueries extends BaseRangeFieldQueryTestCase {
-  private static final String FIELD_NAME = "floatRangeField";
-
-  private float nextFloatInternal() {
-    if (rarely()) {
-      return random().nextBoolean() ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY;
-    }
-    float max = Float.MAX_VALUE / 2;
-    return (max + max) * random().nextFloat() - max;
-  }
-
-  @Override
-  protected Range nextRange(int dimensions) {
-    float[] min = new float[dimensions];
-    float[] max = new float[dimensions];
-
-    float minV, maxV;
-    for (int d=0; d<dimensions; ++d) {
-      minV = nextFloatInternal();
-      maxV = nextFloatInternal();
-      min[d] = Math.min(minV, maxV);
-      max[d] = Math.max(minV, maxV);
-    }
-    return new FloatRange(min, max);
-  }
-
-  @Override
-  protected FloatRangeField newRangeField(Range r) {
-    return new FloatRangeField(FIELD_NAME, ((FloatRange)r).min, ((FloatRange)r).max);
-  }
-
-  @Override
-  protected Query newIntersectsQuery(Range r) {
-    return FloatRangeField.newIntersectsQuery(FIELD_NAME, ((FloatRange)r).min, ((FloatRange)r).max);
-  }
-
-  @Override
-  protected Query newContainsQuery(Range r) {
-    return FloatRangeField.newContainsQuery(FIELD_NAME, ((FloatRange)r).min, ((FloatRange)r).max);
-  }
-
-  @Override
-  protected Query newWithinQuery(Range r) {
-    return FloatRangeField.newWithinQuery(FIELD_NAME, ((FloatRange)r).min, ((FloatRange)r).max);
-  }
-
-  @Override
-  protected Query newCrossesQuery(Range r) {
-    return FloatRangeField.newCrossesQuery(FIELD_NAME, ((FloatRange)r).min, ((FloatRange)r).max);
-  }
-
-  /** Basic test */
-  public void testBasics() throws Exception {
-    Directory dir = newDirectory();
-    RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
-
-    // intersects (within)
-    Document document = new Document();
-    document.add(new FloatRangeField(FIELD_NAME, new float[] {-10.0f, -10.0f}, new float[] {9.1f, 10.1f}));
-    writer.addDocument(document);
-
-    // intersects (crosses)
-    document = new Document();
-    document.add(new FloatRangeField(FIELD_NAME, new float[] {10.0f, -10.0f}, new float[] {20.0f, 10.0f}));
-    writer.addDocument(document);
-
-    // intersects (contains, crosses)
-    document = new Document();
-    document.add(new FloatRangeField(FIELD_NAME, new float[] {-20.0f, -20.0f}, new float[] {30.0f, 30.1f}));
-    writer.addDocument(document);
-
-    // intersects (crosses)
-    document = new Document();
-    document.add(new FloatRangeField(FIELD_NAME, new float[] {-11.1f, -11.2f}, new float[] {1.23f, 11.5f}));
-    writer.addDocument(document);
-
-    // intersects (crosses)
-    document = new Document();
-    document.add(new FloatRangeField(FIELD_NAME, new float[] {12.33f, 1.2f}, new float[] {15.1f, 29.9f}));
-    writer.addDocument(document);
-
-    // disjoint
-    document = new Document();
-    document.add(new FloatRangeField(FIELD_NAME, new float[] {-122.33f, 1.2f}, new float[] {-115.1f, 29.9f}));
-    writer.addDocument(document);
-
-    // intersects (crosses)
-    document = new Document();
-    document.add(new FloatRangeField(FIELD_NAME, new float[] {Float.NEGATIVE_INFINITY, 1.2f}, new float[] {-11.0f, 29.9f}));
-    writer.addDocument(document);
-
-    // equal (within, contains, intersects)
-    document = new Document();
-    document.add(new FloatRangeField(FIELD_NAME, new float[] {-11f, -15f}, new float[] {15f, 20f}));
-    writer.addDocument(document);
-
-    // search
-    IndexReader reader = writer.getReader();
-    IndexSearcher searcher = newSearcher(reader);
-    assertEquals(7, searcher.count(FloatRangeField.newIntersectsQuery(FIELD_NAME,
-        new float[] {-11.0f, -15.0f}, new float[] {15.0f, 20.0f})));
-    assertEquals(2, searcher.count(FloatRangeField.newWithinQuery(FIELD_NAME,
-        new float[] {-11.0f, -15.0f}, new float[] {15.0f, 20.0f})));
-    assertEquals(2, searcher.count(FloatRangeField.newContainsQuery(FIELD_NAME,
-        new float[] {-11.0f, -15.0f}, new float[] {15.0f, 20.0f})));
-    assertEquals(5, searcher.count(FloatRangeField.newCrossesQuery(FIELD_NAME,
-        new float[] {-11.0f, -15.0f}, new float[] {15.0f, 20.0f})));
-
-    reader.close();
-    writer.close();
-    dir.close();
-  }
-
-  /** FloatRange test class implementation - use to validate FloatRangeField */
-  private class FloatRange extends Range {
-    float[] min;
-    float[] max;
-
-    FloatRange(float[] min, float[] max) {
-      assert min != null && max != null && min.length > 0 && max.length > 0
-          : "test box: min/max cannot be null or empty";
-      assert min.length == max.length : "test box: min/max length do not agree";
-      this.min = min;
-      this.max = max;
-    }
-
-    @Override
-    protected int numDimensions() {
-      return min.length;
-    }
-
-    @Override
-    protected Float getMin(int dim) {
-      return min[dim];
-    }
-
-    @Override
-    protected void setMin(int dim, Object val) {
-      float v = (Float)val;
-      if (min[dim] < v) {
-        max[dim] = v;
-      } else {
-        min[dim] = v;
-      }
-    }
-
-    @Override
-    protected Float getMax(int dim) {
-      return max[dim];
-    }
-
-    @Override
-    protected void setMax(int dim, Object val) {
-      float v = (Float)val;
-      if (max[dim] > v) {
-        min[dim] = v;
-      } else {
-        max[dim] = v;
-      }
-    }
-
-    @Override
-    protected boolean isEqual(Range other) {
-      FloatRange o = (FloatRange)other;
-      return Arrays.equals(min, o.min) && Arrays.equals(max, o.max);
-    }
-
-    @Override
-    protected boolean isDisjoint(Range o) {
-      FloatRange other = (FloatRange)o;
-      for (int d=0; d<this.min.length; ++d) {
-        if (this.min[d] > other.max[d] || this.max[d] < other.min[d]) {
-          // disjoint:
-          return true;
-        }
-      }
-      return false;
-    }
-
-    @Override
-    protected boolean isWithin(Range o) {
-      FloatRange other = (FloatRange)o;
-      for (int d=0; d<this.min.length; ++d) {
-        if ((this.min[d] >= other.min[d] && this.max[d] <= other.max[d]) == false) {
-          // not within:
-          return false;
-        }
-      }
-      return true;
-    }
-
-    @Override
-    protected boolean contains(Range o) {
-      FloatRange other = (FloatRange) o;
-      for (int d=0; d<this.min.length; ++d) {
-        if ((this.min[d] <= other.min[d] && this.max[d] >= other.max[d]) == false) {
-          // not contains:
-          return false;
-        }
-      }
-      return true;
-    }
-
-    @Override
-    public String toString() {
-      StringBuilder b = new StringBuilder();
-      b.append("Box(");
-      b.append(min[0]);
-      b.append(" TO ");
-      b.append(max[0]);
-      for (int d=1; d<min.length; ++d) {
-        b.append(", ");
-        b.append(min[d]);
-        b.append(" TO ");
-        b.append(max[d]);
-      }
-      b.append(")");
-
-      return b.toString();
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/sandbox/src/test/org/apache/lucene/search/TestIntRangeFieldQueries.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/test/org/apache/lucene/search/TestIntRangeFieldQueries.java b/lucene/sandbox/src/test/org/apache/lucene/search/TestIntRangeFieldQueries.java
deleted file mode 100644
index 0bb782e..0000000
--- a/lucene/sandbox/src/test/org/apache/lucene/search/TestIntRangeFieldQueries.java
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.lucene.search;
-
-import java.util.Arrays;
-
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.IntRangeField;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.RandomIndexWriter;
-import org.apache.lucene.store.Directory;
-
-/**
- * Random testing for IntRangeField Queries.
- */
-public class TestIntRangeFieldQueries extends BaseRangeFieldQueryTestCase {
-  private static final String FIELD_NAME = "intRangeField";
-
-  private int nextIntInternal() {
-    if (rarely()) {
-      return random().nextBoolean() ? Integer.MAX_VALUE : Integer.MIN_VALUE;
-    }
-    int max = Integer.MAX_VALUE / 2;
-    return (max + max) * random().nextInt() - max;
-  }
-
-  @Override
-  protected Range nextRange(int dimensions) {
-    int[] min = new int[dimensions];
-    int[] max = new int[dimensions];
-
-    int minV, maxV;
-    for (int d=0; d<dimensions; ++d) {
-      minV = nextIntInternal();
-      maxV = nextIntInternal();
-      min[d] = Math.min(minV, maxV);
-      max[d] = Math.max(minV, maxV);
-    }
-    return new IntRange(min, max);
-  }
-
-  @Override
-  protected IntRangeField newRangeField(Range r) {
-    return new IntRangeField(FIELD_NAME, ((IntRange)r).min, ((IntRange)r).max);
-  }
-
-  @Override
-  protected Query newIntersectsQuery(Range r) {
-    return IntRangeField.newIntersectsQuery(FIELD_NAME, ((IntRange)r).min, ((IntRange)r).max);
-  }
-
-  @Override
-  protected Query newContainsQuery(Range r) {
-    return IntRangeField.newContainsQuery(FIELD_NAME, ((IntRange)r).min, ((IntRange)r).max);
-  }
-
-  @Override
-  protected Query newWithinQuery(Range r) {
-    return IntRangeField.newWithinQuery(FIELD_NAME, ((IntRange)r).min, ((IntRange)r).max);
-  }
-
-  @Override
-  protected Query newCrossesQuery(Range r) {
-    return IntRangeField.newCrossesQuery(FIELD_NAME, ((IntRange)r).min, ((IntRange)r).max);
-  }
-
-  /** Basic test */
-  public void testBasics() throws Exception {
-    Directory dir = newDirectory();
-    RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
-
-    // intersects (within)
-    Document document = new Document();
-    document.add(new IntRangeField(FIELD_NAME, new int[] {-10, -10}, new int[] {9, 10}));
-    writer.addDocument(document);
-
-    // intersects (crosses)
-    document = new Document();
-    document.add(new IntRangeField(FIELD_NAME, new int[] {10, -10}, new int[] {20, 10}));
-    writer.addDocument(document);
-
-    // intersects (contains / crosses)
-    document = new Document();
-    document.add(new IntRangeField(FIELD_NAME, new int[] {-20, -20}, new int[] {30, 30}));
-    writer.addDocument(document);
-
-    // intersects (within)
-    document = new Document();
-    document.add(new IntRangeField(FIELD_NAME, new int[] {-11, -11}, new int[] {1, 11}));
-    writer.addDocument(document);
-
-    // intersects (crosses)
-    document = new Document();
-    document.add(new IntRangeField(FIELD_NAME, new int[] {12, 1}, new int[] {15, 29}));
-    writer.addDocument(document);
-
-    // disjoint
-    document = new Document();
-    document.add(new IntRangeField(FIELD_NAME, new int[] {-122, 1}, new int[] {-115, 29}));
-    writer.addDocument(document);
-
-    // intersects (crosses)
-    document = new Document();
-    document.add(new IntRangeField(FIELD_NAME, new int[] {Integer.MIN_VALUE, 1}, new int[] {-11, 29}));
-    writer.addDocument(document);
-
-    // equal (within, contains, intersects)
-    document = new Document();
-    document.add(new IntRangeField(FIELD_NAME, new int[] {-11, -15}, new int[] {15, 20}));
-    writer.addDocument(document);
-
-    // search
-    IndexReader reader = writer.getReader();
-    IndexSearcher searcher = newSearcher(reader);
-    assertEquals(7, searcher.count(IntRangeField.newIntersectsQuery(FIELD_NAME,
-        new int[] {-11, -15}, new int[] {15, 20})));
-    assertEquals(3, searcher.count(IntRangeField.newWithinQuery(FIELD_NAME,
-        new int[] {-11, -15}, new int[] {15, 20})));
-    assertEquals(2, searcher.count(IntRangeField.newContainsQuery(FIELD_NAME,
-        new int[] {-11, -15}, new int[] {15, 20})));
-    assertEquals(4, searcher.count(IntRangeField.newCrossesQuery(FIELD_NAME,
-        new int[] {-11, -15}, new int[] {15, 20})));
-
-    reader.close();
-    writer.close();
-    dir.close();
-  }
-
-  /** IntRange test class implementation - use to validate IntRangeField */
-  private class IntRange extends Range {
-    int[] min;
-    int[] max;
-
-    IntRange(int[] min, int[] max) {
-      assert min != null && max != null && min.length > 0 && max.length > 0
-          : "test box: min/max cannot be null or empty";
-      assert min.length == max.length : "test box: min/max length do not agree";
-      this.min = min;
-      this.max = max;
-    }
-
-    @Override
-    protected int numDimensions() {
-      return min.length;
-    }
-
-    @Override
-    protected Integer getMin(int dim) {
-      return min[dim];
-    }
-
-    @Override
-    protected void setMin(int dim, Object val) {
-      int v = (Integer)val;
-      if (min[dim] < v) {
-        max[dim] = v;
-      } else {
-        min[dim] = v;
-      }
-    }
-
-    @Override
-    protected Integer getMax(int dim) {
-      return max[dim];
-    }
-
-    @Override
-    protected void setMax(int dim, Object val) {
-      int v = (Integer)val;
-      if (max[dim] > v) {
-        min[dim] = v;
-      } else {
-        max[dim] = v;
-      }
-    }
-
-    @Override
-    protected boolean isEqual(Range other) {
-      IntRange o = (IntRange)other;
-      return Arrays.equals(min, o.min) && Arrays.equals(max, o.max);
-    }
-
-    @Override
-    protected boolean isDisjoint(Range o) {
-      IntRange other = (IntRange)o;
-      for (int d=0; d<this.min.length; ++d) {
-        if (this.min[d] > other.max[d] || this.max[d] < other.min[d]) {
-          // disjoint:
-          return true;
-        }
-      }
-      return false;
-    }
-
-    @Override
-    protected boolean isWithin(Range o) {
-      IntRange other = (IntRange)o;
-      for (int d=0; d<this.min.length; ++d) {
-        if ((this.min[d] >= other.min[d] && this.max[d] <= other.max[d]) == false) {
-          // not within:
-          return false;
-        }
-      }
-      return true;
-    }
-
-    @Override
-    protected boolean contains(Range o) {
-      IntRange other = (IntRange) o;
-      for (int d=0; d<this.min.length; ++d) {
-        if ((this.min[d] <= other.min[d] && this.max[d] >= other.max[d]) == false) {
-          // not contains:
-          return false;
-        }
-      }
-      return true;
-    }
-
-    @Override
-    public String toString() {
-      StringBuilder b = new StringBuilder();
-      b.append("Box(");
-      b.append(min[0]);
-      b.append(" TO ");
-      b.append(max[0]);
-      for (int d=1; d<min.length; ++d) {
-        b.append(", ");
-        b.append(min[d]);
-        b.append(" TO ");
-        b.append(max[d]);
-      }
-      b.append(")");
-
-      return b.toString();
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/sandbox/src/test/org/apache/lucene/search/TestIpRangeFieldQueries.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/test/org/apache/lucene/search/TestIpRangeFieldQueries.java b/lucene/sandbox/src/test/org/apache/lucene/search/TestIpRangeFieldQueries.java
deleted file mode 100644
index 1563584..0000000
--- a/lucene/sandbox/src/test/org/apache/lucene/search/TestIpRangeFieldQueries.java
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.lucene.search;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-import org.apache.lucene.document.InetAddressRangeField;
-import org.apache.lucene.util.StringHelper;
-
-/**
- * Random testing for {@link InetAddressRangeField}
- */
-public class TestIpRangeFieldQueries extends BaseRangeFieldQueryTestCase {
-  private static final String FIELD_NAME = "ipRangeField";
-
-  private IPVersion ipVersion;
-
-  private enum IPVersion {IPv4, IPv6}
-
-  @Override
-  protected Range nextRange(int dimensions) {
-    try {
-      InetAddress min = nextInetaddress();
-      byte[] bMin = min.getAddress();
-      InetAddress max = nextInetaddress();
-      byte[] bMax = max.getAddress();
-      if (StringHelper.compare(bMin.length, bMin, 0, bMax, 0) > 0) {
-        return new IpRange(max, min);
-      }
-      return new IpRange(min, max);
-    } catch (UnknownHostException e) {
-      e.printStackTrace();
-    }
-    return null;
-  }
-
-  /** return random IPv4 or IPv6 address */
-  private InetAddress nextInetaddress() throws UnknownHostException {
-    byte[] b;
-    switch (ipVersion) {
-      case IPv4:
-        b = new byte[4];
-        break;
-      case IPv6:
-        b = new byte[16];
-        break;
-      default:
-        throw new IllegalArgumentException("incorrect IP version: " + ipVersion);
-    }
-    random().nextBytes(b);
-    return InetAddress.getByAddress(b);
-  }
-
-  /** randomly select version across tests */
-  private IPVersion ipVersion() {
-    return random().nextBoolean() ? IPVersion.IPv4 : IPVersion.IPv6;
-  }
-
-  @Override
-  public void testRandomTiny() throws Exception {
-    ipVersion = ipVersion();
-    super.testRandomTiny();
-  }
-
-  @Override
-  public void testMultiValued() throws Exception {
-    ipVersion = ipVersion();
-    super.testRandomMedium();
-  }
-
-  @Override
-  public void testRandomMedium() throws Exception {
-    ipVersion = ipVersion();
-    super.testMultiValued();
-  }
-
-  @Nightly
-  @Override
-  public void testRandomBig() throws Exception {
-    ipVersion = ipVersion();
-    super.testRandomBig();
-  }
-
-  /** return random range */
-  @Override
-  protected InetAddressRangeField newRangeField(Range r) {
-    return new InetAddressRangeField(FIELD_NAME, ((IpRange)r).min, ((IpRange)r).max);
-  }
-
-  /** return random intersects query */
-  @Override
-  protected Query newIntersectsQuery(Range r) {
-    return InetAddressRangeField.newIntersectsQuery(FIELD_NAME, ((IpRange)r).min, ((IpRange)r).max);
-  }
-
-  /** return random contains query */
-  @Override
-  protected Query newContainsQuery(Range r) {
-    return InetAddressRangeField.newContainsQuery(FIELD_NAME, ((IpRange)r).min, ((IpRange)r).max);
-  }
-
-  /** return random within query */
-  @Override
-  protected Query newWithinQuery(Range r) {
-    return InetAddressRangeField.newWithinQuery(FIELD_NAME, ((IpRange)r).min, ((IpRange)r).max);
-  }
-
-  /** return random crosses query */
-  @Override
-  protected Query newCrossesQuery(Range r) {
-    return InetAddressRangeField.newCrossesQuery(FIELD_NAME, ((IpRange)r).min, ((IpRange)r).max);
-  }
-
-  /** encapsulated IpRange for test validation */
-  private class IpRange extends Range {
-    InetAddress min;
-    InetAddress max;
-
-    IpRange(InetAddress min, InetAddress max) {
-      this.min = min;
-      this.max = max;
-    }
-
-    @Override
-    protected int numDimensions() {
-      return 1;
-    }
-
-    @Override
-    protected InetAddress getMin(int dim) {
-      return min;
-    }
-
-    @Override
-    protected void setMin(int dim, Object val) {
-      byte[] v = ((InetAddress)val).getAddress();
-
-      if (StringHelper.compare(v.length, min.getAddress(), 0, v, 0) < 0) {
-        max = (InetAddress)val;
-      } else {
-        min = (InetAddress) val;
-      }
-    }
-
-    @Override
-    protected InetAddress getMax(int dim) {
-      return max;
-    }
-
-    @Override
-    protected void setMax(int dim, Object val) {
-      byte[] v = ((InetAddress)val).getAddress();
-
-      if (StringHelper.compare(v.length, max.getAddress(), 0, v, 0) > 0) {
-        min = (InetAddress)val;
-      } else {
-        max = (InetAddress) val;
-      }
-    }
-
-    @Override
-    protected boolean isEqual(Range o) {
-      IpRange other = (IpRange)o;
-      return this.min.equals(other.min) && this.max.equals(other.max);
-    }
-
-    @Override
-    protected boolean isDisjoint(Range o) {
-      IpRange other = (IpRange)o;
-      byte[] bMin = min.getAddress();
-      byte[] bMax = max.getAddress();
-      return StringHelper.compare(bMin.length, bMin, 0, other.max.getAddress(), 0) > 0 ||
-          StringHelper.compare(bMax.length, bMax, 0, other.min.getAddress(), 0) < 0;
-    }
-
-    @Override
-    protected boolean isWithin(Range o) {
-      IpRange other = (IpRange)o;
-      byte[] bMin = min.getAddress();
-      byte[] bMax = max.getAddress();
-      return StringHelper.compare(bMin.length, bMin, 0, other.min.getAddress(), 0) >= 0 &&
-          StringHelper.compare(bMax.length, bMax, 0, other.max.getAddress(), 0) <= 0;
-    }
-
-    @Override
-    protected boolean contains(Range o) {
-      IpRange other = (IpRange)o;
-      byte[] bMin = min.getAddress();
-      byte[] bMax = max.getAddress();
-      return StringHelper.compare(bMin.length, bMin, 0, other.min.getAddress(), 0) <= 0 &&
-          StringHelper.compare(bMax.length, bMax, 0, other.max.getAddress(), 0) >= 0;
-    }
-
-    @Override
-    public String toString() {
-      StringBuilder b = new StringBuilder();
-      b.append("Box(");
-      b.append(min.getHostAddress());
-      b.append(" TO ");
-      b.append(max.getHostAddress());
-      b.append(")");
-      return b.toString();
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/sandbox/src/test/org/apache/lucene/search/TestLongRangeFieldQueries.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/test/org/apache/lucene/search/TestLongRangeFieldQueries.java b/lucene/sandbox/src/test/org/apache/lucene/search/TestLongRangeFieldQueries.java
deleted file mode 100644
index fc21a64..0000000
--- a/lucene/sandbox/src/test/org/apache/lucene/search/TestLongRangeFieldQueries.java
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.lucene.search;
-
-import java.util.Arrays;
-
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.LongRangeField;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.RandomIndexWriter;
-import org.apache.lucene.store.Directory;
-
-/**
- * Random testing for LongRangeField Queries.
- */
-public class TestLongRangeFieldQueries extends BaseRangeFieldQueryTestCase {
-  private static final String FIELD_NAME = "longRangeField";
-
-  private long nextLongInternal() {
-    if (rarely()) {
-      return random().nextBoolean() ? Long.MAX_VALUE : Long.MIN_VALUE;
-    }
-    long max = Long.MAX_VALUE / 2;
-    return (max + max) * random().nextLong() - max;
-  }
-
-  @Override
-  protected Range nextRange(int dimensions) {
-    long[] min = new long[dimensions];
-    long[] max = new long[dimensions];
-
-    long minV, maxV;
-    for (int d=0; d<dimensions; ++d) {
-      minV = nextLongInternal();
-      maxV = nextLongInternal();
-      min[d] = Math.min(minV, maxV);
-      max[d] = Math.max(minV, maxV);
-    }
-    return new LongRange(min, max);
-  }
-
-  @Override
-  protected LongRangeField newRangeField(Range r) {
-    return new LongRangeField(FIELD_NAME, ((LongRange)r).min, ((LongRange)r).max);
-  }
-
-  @Override
-  protected Query newIntersectsQuery(Range r) {
-    return LongRangeField.newIntersectsQuery(FIELD_NAME, ((LongRange)r).min, ((LongRange)r).max);
-  }
-
-  @Override
-  protected Query newContainsQuery(Range r) {
-    return LongRangeField.newContainsQuery(FIELD_NAME, ((LongRange)r).min, ((LongRange)r).max);
-  }
-
-  @Override
-  protected Query newWithinQuery(Range r) {
-    return LongRangeField.newWithinQuery(FIELD_NAME, ((LongRange)r).min, ((LongRange)r).max);
-  }
-
-  @Override
-  protected Query newCrossesQuery(Range r) {
-    return LongRangeField.newCrossesQuery(FIELD_NAME, ((LongRange)r).min, ((LongRange)r).max);
-  }
-
-  /** Basic test */
-  public void testBasics() throws Exception {
-    Directory dir = newDirectory();
-    RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
-
-    // intersects (within)
-    Document document = new Document();
-    document.add(new LongRangeField(FIELD_NAME, new long[] {-10, -10}, new long[] {9, 10}));
-    writer.addDocument(document);
-
-    // intersects (crosses)
-    document = new Document();
-    document.add(new LongRangeField(FIELD_NAME, new long[] {10, -10}, new long[] {20, 10}));
-    writer.addDocument(document);
-
-    // intersects (contains, crosses)
-    document = new Document();
-    document.add(new LongRangeField(FIELD_NAME, new long[] {-20, -20}, new long[] {30, 30}));
-    writer.addDocument(document);
-
-    // intersects (within)
-    document = new Document();
-    document.add(new LongRangeField(FIELD_NAME, new long[] {-11, -11}, new long[] {1, 11}));
-    writer.addDocument(document);
-
-    // intersects (crosses)
-    document = new Document();
-    document.add(new LongRangeField(FIELD_NAME, new long[] {12, 1}, new long[] {15, 29}));
-    writer.addDocument(document);
-
-    // disjoint
-    document = new Document();
-    document.add(new LongRangeField(FIELD_NAME, new long[] {-122, 1}, new long[] {-115, 29}));
-    writer.addDocument(document);
-
-    // intersects (crosses)
-    document = new Document();
-    document.add(new LongRangeField(FIELD_NAME, new long[] {Long.MIN_VALUE, 1}, new long[] {-11, 29}));
-    writer.addDocument(document);
-
-    // equal (within, contains, intersects)
-    document = new Document();
-    document.add(new LongRangeField(FIELD_NAME, new long[] {-11, -15}, new long[] {15, 20}));
-    writer.addDocument(document);
-
-    // search
-    IndexReader reader = writer.getReader();
-    IndexSearcher searcher = newSearcher(reader);
-    assertEquals(7, searcher.count(LongRangeField.newIntersectsQuery(FIELD_NAME,
-        new long[] {-11, -15}, new long[] {15, 20})));
-    assertEquals(3, searcher.count(LongRangeField.newWithinQuery(FIELD_NAME,
-        new long[] {-11, -15}, new long[] {15, 20})));
-    assertEquals(2, searcher.count(LongRangeField.newContainsQuery(FIELD_NAME,
-        new long[] {-11, -15}, new long[] {15, 20})));
-    assertEquals(4, searcher.count(LongRangeField.newCrossesQuery(FIELD_NAME,
-        new long[] {-11, -15}, new long[] {15, 20})));
-
-    reader.close();
-    writer.close();
-    dir.close();
-  }
-
-  /** LongRange test class implementation - use to validate LongRangeField */
-  private class LongRange extends Range {
-    long[] min;
-    long[] max;
-
-    LongRange(long[] min, long[] max) {
-      assert min != null && max != null && min.length > 0 && max.length > 0
-          : "test box: min/max cannot be null or empty";
-      assert min.length == max.length : "test box: min/max length do not agree";
-      this.min = min;
-      this.max = max;
-    }
-
-    @Override
-    protected int numDimensions() {
-      return min.length;
-    }
-
-    @Override
-    protected Long getMin(int dim) {
-      return min[dim];
-    }
-
-    @Override
-    protected void setMin(int dim, Object val) {
-      long v = (Long)val;
-      if (min[dim] < v) {
-        max[dim] = v;
-      } else {
-        min[dim] = v;
-      }
-    }
-
-    @Override
-    protected Long getMax(int dim) {
-      return max[dim];
-    }
-
-    @Override
-    protected void setMax(int dim, Object val) {
-      long v = (Long)val;
-      if (max[dim] > v) {
-        min[dim] = v;
-      } else {
-        max[dim] = v;
-      }
-    }
-
-    @Override
-    protected boolean isEqual(Range other) {
-      LongRange o = (LongRange)other;
-      return Arrays.equals(min, o.min) && Arrays.equals(max, o.max);
-    }
-
-    @Override
-    protected boolean isDisjoint(Range o) {
-      LongRange other = (LongRange)o;
-      for (int d=0; d<this.min.length; ++d) {
-        if (this.min[d] > other.max[d] || this.max[d] < other.min[d]) {
-          // disjoint:
-          return true;
-        }
-      }
-      return false;
-    }
-
-    @Override
-    protected boolean isWithin(Range o) {
-      LongRange other = (LongRange)o;
-      for (int d=0; d<this.min.length; ++d) {
-        if ((this.min[d] >= other.min[d] && this.max[d] <= other.max[d]) == false) {
-          // not within:
-          return false;
-        }
-      }
-      return true;
-    }
-
-    @Override
-    protected boolean contains(Range o) {
-      LongRange other = (LongRange) o;
-      for (int d=0; d<this.min.length; ++d) {
-        if ((this.min[d] <= other.min[d] && this.max[d] >= other.max[d]) == false) {
-          // not contains:
-          return false;
-        }
-      }
-      return true;
-    }
-
-    @Override
-    public String toString() {
-      StringBuilder b = new StringBuilder();
-      b.append("Box(");
-      b.append(min[0]);
-      b.append(" TO ");
-      b.append(max[0]);
-      for (int d=1; d<min.length; ++d) {
-        b.append(", ");
-        b.append(min[d]);
-        b.append(" TO ");
-        b.append(max[d]);
-      }
-      b.append(")");
-
-      return b.toString();
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/test-framework/src/java/org/apache/lucene/search/BaseRangeFieldQueryTestCase.java
----------------------------------------------------------------------
diff --git a/lucene/test-framework/src/java/org/apache/lucene/search/BaseRangeFieldQueryTestCase.java b/lucene/test-framework/src/java/org/apache/lucene/search/BaseRangeFieldQueryTestCase.java
new file mode 100644
index 0000000..76de732
--- /dev/null
+++ b/lucene/test-framework/src/java/org/apache/lucene/search/BaseRangeFieldQueryTestCase.java
@@ -0,0 +1,346 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.lucene.search;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.NumericDocValuesField;
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.MultiDocValues;
+import org.apache.lucene.index.MultiFields;
+import org.apache.lucene.index.NumericDocValues;
+import org.apache.lucene.index.SerialMergeScheduler;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.FixedBitSet;
+import org.apache.lucene.util.IOUtils;
+import org.apache.lucene.util.LuceneTestCase;
+
+/**
+ * Abstract class to do basic tests for a RangeField query. Testing rigor inspired by {@code BaseGeoPointTestCase}
+ */
+public abstract class BaseRangeFieldQueryTestCase extends LuceneTestCase {
+  protected abstract Field newRangeField(Range box);
+
+  protected abstract Query newIntersectsQuery(Range box);
+
+  protected abstract Query newContainsQuery(Range box);
+
+  protected abstract Query newWithinQuery(Range box);
+
+  protected abstract Query newCrossesQuery(Range box);
+
+  protected abstract Range nextRange(int dimensions) throws Exception;
+
+  protected int dimension() {
+    return random().nextInt(4) + 1;
+  }
+
+  public void testRandomTiny() throws Exception {
+    // Make sure single-leaf-node case is OK:
+    doTestRandom(10, false);
+  }
+
+  public void testRandomMedium() throws Exception {
+    doTestRandom(10000, false);
+  }
+
+  @Nightly
+  public void testRandomBig() throws Exception {
+    doTestRandom(200000, false);
+  }
+
+  public void testMultiValued() throws Exception {
+    doTestRandom(10000, true);
+  }
+
+  private void doTestRandom(int count, boolean multiValued) throws Exception {
+    int numDocs = atLeast(count);
+    int dimensions = dimension();
+
+    if (VERBOSE) {
+      System.out.println("TEST: numDocs=" + numDocs);
+    }
+
+    Range[][] ranges = new Range[numDocs][];
+
+    boolean haveRealDoc = true;
+
+    nextdoc: for (int id=0; id<numDocs; ++id) {
+      int x = random().nextInt(20);
+      if (ranges[id] == null) {
+        ranges[id] = new Range[] {nextRange(dimensions)};
+      }
+      if (x == 17) {
+        // some docs don't have a box:
+        ranges[id][0].isMissing = true;
+        if (VERBOSE) {
+          System.out.println("  id=" + id + " is missing");
+        }
+        continue;
+      }
+
+      if (multiValued == true && random().nextBoolean()) {
+        // randomly add multi valued documents (up to 2 fields)
+        int n = random().nextInt(2) + 1;
+        ranges[id] = new Range[n];
+        for (int i=0; i<n; ++i) {
+          ranges[id][i] = nextRange(dimensions);
+        }
+      }
+
+      if (id > 0 && x < 9 && haveRealDoc) {
+        int oldID;
+        int i=0;
+        // don't step on missing ranges:
+        while (true) {
+          oldID = random().nextInt(id);
+          if (ranges[oldID][0].isMissing == false) {
+            break;
+          } else if (++i > id) {
+            continue nextdoc;
+          }
+        }
+
+        if (x == dimensions*2) {
+          // Fully identical box (use first box in case current is multivalued but old is not)
+          for (int d=0; d<dimensions; ++d) {
+            ranges[id][0].setMin(d, ranges[oldID][0].getMin(d));
+            ranges[id][0].setMax(d, ranges[oldID][0].getMax(d));
+          }
+          if (VERBOSE) {
+            System.out.println("  id=" + id + " box=" + ranges[id] + " (same box as doc=" + oldID + ")");
+          }
+        } else {
+          for (int m = 0, even = dimensions % 2; m < dimensions * 2; ++m) {
+            if (x == m) {
+              int d = (int)Math.floor(m/2);
+              // current could be multivalue but old may not be, so use first box
+              if (even == 0) {
+                ranges[id][0].setMin(d, ranges[oldID][0].getMin(d));
+                if (VERBOSE) {
+                  System.out.println("  id=" + id + " box=" + ranges[id] + " (same min[" + d + "] as doc=" + oldID + ")");
+                }
+              } else {
+                ranges[id][0].setMax(d, ranges[oldID][0].getMax(d));
+                if (VERBOSE) {
+                  System.out.println("  id=" + id + " box=" + ranges[id] + " (same max[" + d + "] as doc=" + oldID + ")");
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+    verify(ranges);
+  }
+
+  private void verify(Range[][] ranges) throws Exception {
+    IndexWriterConfig iwc = newIndexWriterConfig();
+    // Else seeds may not reproduce:
+    iwc.setMergeScheduler(new SerialMergeScheduler());
+    // Else we can get O(N^2) merging
+    int mbd = iwc.getMaxBufferedDocs();
+    if (mbd != -1 && mbd < ranges.length/100) {
+      iwc.setMaxBufferedDocs(ranges.length/100);
+    }
+    Directory dir;
+    if (ranges.length > 50000) {
+      dir = newFSDirectory(createTempDir(getClass().getSimpleName()));
+    } else {
+      dir = newDirectory();
+    }
+
+    Set<Integer> deleted = new HashSet<>();
+    IndexWriter w = new IndexWriter(dir, iwc);
+    for (int id=0; id < ranges.length; ++id) {
+      Document doc = new Document();
+      doc.add(newStringField("id", ""+id, Field.Store.NO));
+      doc.add(new NumericDocValuesField("id", id));
+      if (ranges[id][0].isMissing == false) {
+        for (int n=0; n<ranges[id].length; ++n) {
+          doc.add(newRangeField(ranges[id][n]));
+        }
+      }
+      w.addDocument(doc);
+      if (id > 0 && random().nextInt(100) == 1) {
+        int idToDelete = random().nextInt(id);
+        w.deleteDocuments(new Term("id", ""+idToDelete));
+        deleted.add(idToDelete);
+        if (VERBOSE) {
+          System.out.println("  delete id=" + idToDelete);
+        }
+      }
+    }
+
+    if (random().nextBoolean()) {
+      w.forceMerge(1);
+    }
+    final IndexReader r = DirectoryReader.open(w);
+    w.close();
+    IndexSearcher s = newSearcher(r);
+
+    int dimensions = ranges[0][0].numDimensions();
+    int iters = atLeast(25);
+    Bits liveDocs = MultiFields.getLiveDocs(s.getIndexReader());
+    int maxDoc = s.getIndexReader().maxDoc();
+
+    for (int iter=0; iter<iters; ++iter) {
+      if (VERBOSE) {
+        System.out.println("\nTEST: iter=" + iter + " s=" + s);
+      }
+
+      // occasionally test open ended bounding ranges
+      Range queryRange = nextRange(dimensions);
+      int rv = random().nextInt(4);
+      Query query;
+      Range.QueryType queryType;
+      if (rv == 0) {
+        queryType = Range.QueryType.INTERSECTS;
+        query = newIntersectsQuery(queryRange);
+      } else if (rv == 1)  {
+        queryType = Range.QueryType.CONTAINS;
+        query = newContainsQuery(queryRange);
+      } else if (rv == 2) {
+        queryType = Range.QueryType.WITHIN;
+        query = newWithinQuery(queryRange);
+      } else {
+        queryType = Range.QueryType.CROSSES;
+        query = newCrossesQuery(queryRange);
+      }
+
+      if (VERBOSE) {
+        System.out.println("  query=" + query);
+      }
+
+      final FixedBitSet hits = new FixedBitSet(maxDoc);
+      s.search(query, new SimpleCollector() {
+        private int docBase;
+
+        @Override
+        public void collect(int doc) {
+          hits.set(docBase + doc);
+        }
+
+        @Override
+        protected void doSetNextReader(LeafReaderContext context) throws IOException {
+          docBase = context.docBase;
+        }
+
+        @Override
+        public boolean needsScores() { return false; }
+      });
+
+      NumericDocValues docIDToID = MultiDocValues.getNumericValues(r, "id");
+      for (int docID=0; docID<maxDoc; ++docID) {
+        assertEquals(docID, docIDToID.nextDoc());
+        int id = (int) docIDToID.longValue();
+        boolean expected;
+        if (liveDocs != null && liveDocs.get(docID) == false) {
+          // document is deleted
+          expected = false;
+        } else if (ranges[id][0].isMissing) {
+          expected = false;
+        } else {
+          expected = expectedResult(queryRange, ranges[id], queryType);
+        }
+
+        if (hits.get(docID) != expected) {
+          StringBuilder b = new StringBuilder();
+          b.append("FAIL (iter " + iter + "): ");
+          if (expected == true) {
+            b.append("id=" + id + (ranges[id].length > 1 ? " (MultiValue) " : " ") + "should match but did not\n");
+          } else {
+            b.append("id=" + id + " should not match but did\n");
+          }
+          b.append(" queryRange=" + queryRange + "\n");
+          b.append(" box" + ((ranges[id].length > 1) ? "es=" : "=" ) + ranges[id][0]);
+          for (int n=1; n<ranges[id].length; ++n) {
+            b.append(", ");
+            b.append(ranges[id][n]);
+          }
+          b.append("\n queryType=" + queryType + "\n");
+          b.append(" deleted?=" + (liveDocs != null && liveDocs.get(docID) == false));
+          fail("wrong hit (first of possibly more):\n\n" + b);
+        }
+      }
+    }
+    IOUtils.close(r, dir);
+  }
+
+  protected boolean expectedResult(Range queryRange, Range[] range, Range.QueryType queryType) {
+    for (int i=0; i<range.length; ++i) {
+      if (expectedBBoxQueryResult(queryRange, range[i], queryType) == true) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  protected boolean expectedBBoxQueryResult(Range queryRange, Range range, Range.QueryType queryType) {
+    if (queryRange.isEqual(range) && queryType != Range.QueryType.CROSSES) {
+      return true;
+    }
+    Range.QueryType relation = range.relate(queryRange);
+    if (queryType == Range.QueryType.INTERSECTS) {
+      return relation != null;
+    } else if (queryType == Range.QueryType.CROSSES) {
+      // by definition, RangeFields that CONTAIN the query are also considered to cross
+      return relation == queryType || relation == Range.QueryType.CONTAINS;
+    }
+    return relation == queryType;
+  }
+
+  /** base class for range verification */
+  protected abstract static class Range {
+    protected boolean isMissing = false;
+
+    /** supported query relations */
+    protected enum QueryType { INTERSECTS, WITHIN, CONTAINS, CROSSES }
+
+    protected abstract int numDimensions();
+    protected abstract Object getMin(int dim);
+    protected abstract void setMin(int dim, Object val);
+    protected abstract Object getMax(int dim);
+    protected abstract void setMax(int dim, Object val);
+    protected abstract boolean isEqual(Range other);
+    protected abstract boolean isDisjoint(Range other);
+    protected abstract boolean isWithin(Range other);
+    protected abstract boolean contains(Range other);
+
+    protected QueryType relate(Range other) {
+      if (isDisjoint(other)) {
+        // if disjoint; return null:
+        return null;
+      } else if (isWithin(other)) {
+        return QueryType.WITHIN;
+      } else if (contains(other)) {
+        return QueryType.CONTAINS;
+      }
+      return QueryType.CROSSES;
+    }
+  }
+}