lucenenet-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From synhers...@apache.org
Subject [4/6] lucenenet git commit: Moving the 3.0 spatial module to serve as a basis to Lucene.Net.Spatial
Date Mon, 09 Feb 2015 01:27:54 GMT
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/c9f901b2/src/Lucene.Net.Spatial/Util/CachingDoubleValueSource.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/CachingDoubleValueSource.cs b/src/Lucene.Net.Spatial/Util/CachingDoubleValueSource.cs
new file mode 100644
index 0000000..ef7a174
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Util/CachingDoubleValueSource.cs
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+using System.Collections.Generic;
+using Lucene.Net.Index;
+using Lucene.Net.Search.Function;
+
+namespace Lucene.Net.Spatial.Util
+{
+    public class CachingDoubleValueSource : ValueSource
+    {
+        protected readonly ValueSource source;
+        protected readonly Dictionary<int, double> cache;
+
+        public CachingDoubleValueSource(ValueSource source)
+        {
+            this.source = source;
+            cache = new Dictionary<int, double>();
+        }
+
+        public class CachingDoubleDocValue : DocValues
+        {
+            private readonly int docBase;
+            private readonly DocValues values;
+            private readonly Dictionary<int, double> cache;
+
+            public CachingDoubleDocValue(int docBase, DocValues vals, Dictionary<int, double> cache)
+            {
+                this.docBase = docBase;
+                this.values = vals;
+                this.cache = cache;
+            }
+
+            public override double DoubleVal(int doc)
+            {
+                var key = docBase + doc;
+                double v;
+                if (!cache.TryGetValue(key, out v))
+                {
+                    v = values.DoubleVal(doc);
+                    cache[key] = v;
+                }
+                return v;
+            }
+
+            public override float FloatVal(int doc)
+            {
+                return (float)DoubleVal(doc);
+            }
+
+            public override string ToString(int doc)
+            {
+                return DoubleVal(doc) + string.Empty;
+            }
+        }
+
+        public override DocValues GetValues(IndexReader reader)
+        {
+            var @base = 0; //reader.DocBase;
+            var vals = source.GetValues(reader);
+            return new CachingDoubleDocValue(@base, vals, cache);
+
+        }
+
+        public override string Description()
+        {
+            return "Cached[" + source.Description() + "]";
+        }
+
+        public override bool Equals(object o)
+        {
+            if (this == o) return true;
+
+            var that = o as CachingDoubleValueSource;
+
+            if (that == null) return false;
+            if (source != null ? !source.Equals(that.source) : that.source != null) return false;
+
+            return true;
+        }
+
+        public override int GetHashCode()
+        {
+            return source != null ? source.GetHashCode() : 0;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/c9f901b2/src/Lucene.Net.Spatial/Util/CompatibilityExtensions.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/CompatibilityExtensions.cs b/src/Lucene.Net.Spatial/Util/CompatibilityExtensions.cs
new file mode 100644
index 0000000..26a1462
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Util/CompatibilityExtensions.cs
@@ -0,0 +1,296 @@
+/*
+ * 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.
+ */
+
+using System;
+#if NET35
+using Lucene.Net.Support.Compatibility;
+#else
+using System.Collections.Concurrent;
+#endif
+using System.Collections.Generic;
+using System.Diagnostics;
+using Lucene.Net.Analysis.Tokenattributes;
+using Lucene.Net.Index;
+using Lucene.Net.Search;
+
+namespace Lucene.Net.Spatial.Util
+{
+    public static class CompatibilityExtensions
+    {
+        public static void Append(this ITermAttribute termAtt, string str)
+        {
+            termAtt.SetTermBuffer(termAtt.Term + str); // TODO: Not optimal, but works
+        }
+
+        public static void Append(this ITermAttribute termAtt, char ch)
+        {
+            termAtt.SetTermBuffer(termAtt.Term + new string(new[] {ch})); // TODO: Not optimal, but works
+        }
+
+        private static readonly ConcurrentDictionary<Key<string, IndexReader>, IBits> _docsWithFieldCache =
+            new ConcurrentDictionary<Key<string, IndexReader>, IBits>();
+
+        internal static IBits GetDocsWithField(this FieldCache fc, IndexReader reader, String field)
+        {
+            return _docsWithFieldCache.GetOrAdd(new Key<string, IndexReader>(field, reader),
+                                                key =>
+                                                DocsWithFieldCacheEntry_CreateValue(key.Item2,
+                                                                                    new Entry(key.Item1, null), false));
+        }
+
+        /// <summary> <p/>
+        /// EXPERT: Instructs the FieldCache to forcibly expunge all entries 
+        /// from the underlying caches.  This is intended only to be used for 
+        /// test methods as a way to ensure a known base state of the Cache 
+        /// (with out needing to rely on GC to free WeakReferences).  
+        /// It should not be relied on for "Cache maintenance" in general 
+        /// application code.
+        /// <p/>
+        /// <p/>
+        /// <b>EXPERIMENTAL API:</b> This API is considered extremely advanced 
+        /// and experimental.  It may be removed or altered w/o warning in future 
+        /// releases 
+        /// of Lucene.
+        /// <p/>
+        /// </summary>
+        public static void PurgeSpatialCaches(this FieldCache fc)
+        {
+            _docsWithFieldCache.Clear();
+        }
+
+        private static IBits DocsWithFieldCacheEntry_CreateValue(IndexReader reader, Entry entryKey,
+                                                                 bool setDocsWithField /* ignored */)
+        {
+            var field = entryKey.field;
+            FixedBitSet res = null;
+            var terms = new TermsEnumCompatibility(reader, field);
+            var maxDoc = reader.MaxDoc;
+
+            var term = terms.Next();
+            if (term != null)
+            {
+                int termsDocCount = terms.GetDocCount();
+                Debug.Assert(termsDocCount <= maxDoc);
+                if (termsDocCount == maxDoc)
+                {
+                    // Fast case: all docs have this field:
+                    return new MatchAllBits(maxDoc);
+                }
+
+                while (true)
+                {
+                    if (res == null)
+                    {
+                        // lazy init
+                        res = new FixedBitSet(maxDoc);
+                    }
+
+                    var termDocs = reader.TermDocs(term);
+                    while (termDocs.Next())
+                    {
+                        res.Set(termDocs.Doc);
+                    }
+
+                    term = terms.Next();
+                    if (term == null)
+                    {
+                        break;
+                    }
+                }
+            }
+            if (res == null)
+            {
+                return new MatchNoBits(maxDoc);
+            }
+            int numSet = res.Cardinality();
+            if (numSet >= maxDoc)
+            {
+                // The cardinality of the BitSet is maxDoc if all documents have a value.
+                Debug.Assert(numSet == maxDoc);
+                return new MatchAllBits(maxDoc);
+            }
+            return res;
+        }
+
+        /* table of number of leading zeros in a byte */
+
+        public static readonly byte[] nlzTable =
+            {
+                8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3
+                , 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+                2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1
+                , 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+                , 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+                , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+                , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+            };
+
+        /// <summary>
+        /// Returns the number of leading zero bits.
+        /// </summary>
+        /// <param name="x"></param>
+        /// <returns></returns>
+        public static int BitUtilNlz(long x)
+        {
+            int n = 0;
+            // do the first step as a long
+            var y = (int) ((ulong) x >> 32);
+            if (y == 0)
+            {
+                n += 32;
+                y = (int) (x);
+            }
+            if ((y & 0xFFFF0000) == 0)
+            {
+                n += 16;
+                y <<= 16;
+            }
+            if ((y & 0xFF000000) == 0)
+            {
+                n += 8;
+                y <<= 8;
+            }
+            return n + nlzTable[(uint) y >> 24];
+            /* implementation without table:
+              if ((y & 0xF0000000) == 0) { n+=4; y<<=4; }
+              if ((y & 0xC0000000) == 0) { n+=2; y<<=2; }
+              if ((y & 0x80000000) == 0) { n+=1; y<<=1; }
+              if ((y & 0x80000000) == 0) { n+=1;}
+              return n;
+             */
+        }
+    }
+
+    public static class Arrays
+    {
+        public static void Fill<T>(T[] array, int fromIndex, int toIndex, T value)
+        {
+            if (array == null)
+            {
+                throw new ArgumentNullException("array");
+            }
+            if (fromIndex < 0 || fromIndex > toIndex)
+            {
+                throw new ArgumentOutOfRangeException("fromIndex");
+            }
+            if (toIndex > array.Length)
+            {
+                throw new ArgumentOutOfRangeException("toIndex");
+            }
+            for (var i = fromIndex; i < toIndex; i++)
+            {
+                array[i] = value;
+            }
+        }
+    }
+
+    /// <summary>
+    /// Expert: Every composite-key in the internal cache is of this type.
+    /// </summary>
+    internal class Entry
+    {
+        internal readonly String field; // which Fieldable
+        internal readonly Object custom; // which custom comparator or parser
+
+        /* Creates one of these objects for a custom comparator/parser. */
+
+        public Entry(String field, Object custom)
+        {
+            this.field = field;
+            this.custom = custom;
+        }
+
+        /* Two of these are equal iff they reference the same field and type. */
+
+        public override bool Equals(Object o)
+        {
+            var other = o as Entry;
+            if (other != null)
+            {
+                if (other.field.Equals(field))
+                {
+                    if (other.custom == null)
+                    {
+                        if (custom == null) return true;
+                    }
+                    else if (other.custom.Equals(custom))
+                    {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+
+        /* Composes a hashcode based on the field and type. */
+
+        public override int GetHashCode()
+        {
+            return field.GetHashCode() ^ (custom == null ? 0 : custom.GetHashCode());
+        }
+    }
+
+    internal struct Key<T1, T2> : IEquatable<Key<T1, T2>>
+    {
+        public bool Equals(Key<T1, T2> other)
+        {
+            return EqualityComparer<T1>.Default.Equals(Item1, other.Item1) &&
+                   EqualityComparer<T2>.Default.Equals(Item2, other.Item2);
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (ReferenceEquals(null, obj))
+            {
+                return false;
+            }
+            return obj is Key<T1, T2> && Equals((Key<T1, T2>) obj);
+        }
+
+        public override int GetHashCode()
+        {
+            unchecked
+            {
+                return (EqualityComparer<T1>.Default.GetHashCode(Item1)*397) ^
+                       EqualityComparer<T2>.Default.GetHashCode(Item2);
+            }
+        }
+
+        public static bool operator ==(Key<T1, T2> left, Key<T1, T2> right)
+        {
+            return left.Equals(right);
+        }
+
+        public static bool operator !=(Key<T1, T2> left, Key<T1, T2> right)
+        {
+            return !left.Equals(right);
+        }
+
+        public readonly T1 Item1;
+        public readonly T2 Item2;
+
+        public Key(T1 item1, T2 item2)
+        {
+            Item1 = item1;
+            Item2 = item2;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/c9f901b2/src/Lucene.Net.Spatial/Util/FixedBitSet.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/FixedBitSet.cs b/src/Lucene.Net.Spatial/Util/FixedBitSet.cs
new file mode 100644
index 0000000..bfc5c45
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Util/FixedBitSet.cs
@@ -0,0 +1,454 @@
+/*
+ * 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.
+ */
+
+using System;
+using System.Collections;
+using Lucene.Net.Search;
+using Lucene.Net.Util;
+
+namespace Lucene.Net.Spatial.Util
+{
+    /* BitSet of fixed length (numBits), backed by accessible
+ *  ({@link #getBits}) long[], accessed with an int index,
+ *  implementing Bits and DocIdSet.  Unlike {@link
+ *  OpenBitSet} this bit set does not auto-expand, cannot
+ *  handle long index, and does not have fastXX/XX variants
+ *  (just X).
+ *
+ * @lucene.internal
+ **/
+    public class FixedBitSet : DocIdSet, IBits
+    {
+        private readonly BitArray bits;
+
+        /// <summary>
+        /// returns the number of 64 bit words it would take to hold numBits
+        /// </summary>
+        /// <param name="numBits"></param>
+        /// <returns></returns>
+        public static int bits2words(int numBits)
+        {
+            var numLong = (int)((uint)numBits >> 6);
+            if ((numBits & 63) != 0)
+            {
+                numLong++;
+            }
+            return numLong;
+        }
+
+        public FixedBitSet(int numBits)
+        {
+            bits = new BitArray(numBits);
+        }
+
+        /// <summary>
+        /// Makes full copy.
+        /// </summary>
+        /// <param name="other"></param>
+        public FixedBitSet(FixedBitSet other)
+        {
+            bits = new BitArray(other.bits);
+        }
+
+        public IBits Bits()
+        {
+            return this;
+        }
+
+        public int Length()
+        {
+            return bits.Length;
+        }
+
+        public override bool IsCacheable
+        {
+            get { return true; }
+        }
+
+        /// <summary>
+        /// Returns number of set bits.  NOTE: this visits every
+        /// long in the backing bits array, and the result is not
+        /// internally cached!
+        /// </summary>
+        /// <returns></returns>
+        public int Cardinality()
+        {
+            int ret = 0;
+            for (var i = 0; i < bits.Length; i++)
+            {
+                if (bits[i]) ret++;
+            }
+            return ret;
+        }
+
+        public bool Get(int index)
+        {
+            return bits[index];
+        }
+
+        public void Set(int index)
+        {
+            bits.Set(index, true);
+        }
+
+        public bool GetAndSet(int index)
+        {
+            var ret = bits[index];
+            bits.Set(index, true);
+            return ret;
+        }
+
+        public void Clear(int index)
+        {
+            bits.Set(index, false);
+        }
+
+        public bool GetAndClear(int index)
+        {
+            var ret = bits[index];
+            bits.Set(index, false);
+            return ret;
+        }
+
+        /// <summary>
+        /// Returns the index of the first set bit starting at the index specified.
+        /// -1 is returned if there are no more set bits.
+        /// </summary>
+        /// <param name="index"></param>
+        /// <returns></returns>
+        public int NextSetBit(int index)
+        {
+            if (index >= bits.Length || index < 0)
+                throw new ArgumentException("Invalid index", "index");
+
+            for (var i = index; i < bits.Length; i++)
+            {
+                if (bits[i]) return i;
+            }
+
+            return -1;
+        }
+
+        /* Returns the index of the last set bit before or on the index specified.
+         *  -1 is returned if there are no more set bits.
+         */
+        public int PrevSetBit(int index)
+        {
+            if (index >= bits.Length || index < 0)
+                throw new ArgumentException("Invalid index", "index");
+
+            for (var i = index; i >= 0; i--)
+            {
+                if (bits[i]) return i;
+            }
+
+            return -1;
+        }
+
+        /* Does in-place OR of the bits provided by the
+         *  iterator. */
+        //public void Or(DocIdSetIterator iter)
+        //{
+        //    if (iter is OpenBitSetIterator && iter.DocID() == -1)
+        //    {
+        //        var obs = (OpenBitSetIterator)iter;
+        //        Or(obs.arr, obs.words);
+        //        // advance after last doc that would be accepted if standard
+        //        // iteration is used (to exhaust it):
+        //        obs.Advance(bits.Length);
+        //    }
+        //    else
+        //    {
+        //        int doc;
+        //        while ((doc = iter.NextDoc()) < bits.Length)
+        //        {
+        //            Set(doc);
+        //        }
+        //    }
+        //}
+
+        /* this = this OR other */
+        public void Or(FixedBitSet other)
+        {
+            Or(other.bits, other.bits.Length);
+        }
+
+        private void Or(BitArray otherArr, int otherLen)
+        {
+            var thisArr = this.bits;
+            int pos = Math.Min(thisArr.Length, otherLen);
+            while (--pos >= 0)
+            {
+                thisArr[pos] |= otherArr[pos];
+            }
+        }
+
+        /* Does in-place AND of the bits provided by the
+         *  iterator. */
+        //public void And(DocIdSetIterator iter)
+        //{
+        //    if (iter is OpenBitSetIterator && iter.DocID() == -1)
+        //    {
+        //        var obs = (OpenBitSetIterator)iter;
+        //        And(obs.arr, obs.words);
+        //        // advance after last doc that would be accepted if standard
+        //        // iteration is used (to exhaust it):
+        //        obs.Advance(bits.Length);
+        //    }
+        //    else
+        //    {
+        //        if (bits.Length == 0) return;
+        //        int disiDoc, bitSetDoc = NextSetBit(0);
+        //        while (bitSetDoc != -1 && (disiDoc = iter.Advance(bitSetDoc)) < bits.Length)
+        //        {
+        //            Clear(bitSetDoc, disiDoc);
+        //            disiDoc++;
+        //            bitSetDoc = (disiDoc < bits.Length) ? NextSetBit(disiDoc) : -1;
+        //        }
+        //        if (bitSetDoc != -1)
+        //        {
+        //            Clear(bitSetDoc, bits.Length);
+        //        }
+        //    }
+        //}
+
+        /* this = this AND other */
+        public void And(FixedBitSet other)
+        {
+            And(other.bits, other.bits.Length);
+        }
+
+        private void And(BitArray otherArr, int otherLen)
+        {
+            var thisArr = this.bits;
+            int pos = Math.Min(thisArr.Length, otherLen);
+            while (--pos >= 0)
+            {
+                thisArr[pos] &= otherArr[pos];
+            }
+            if (thisArr.Length > otherLen)
+            {
+                for (var i = otherLen; i < thisArr.Length; i++)
+                {
+                    thisArr[i] = false;
+                }
+            }
+        }
+
+        /* Does in-place AND NOT of the bits provided by the
+         *  iterator. */
+        //public void AndNot(DocIdSetIterator iter)
+        //{
+        //    var obs = iter as OpenBitSetIterator;
+        //    if (obs != null && iter.DocID() == -1)
+        //    {
+        //        AndNot(obs.arr, obs.words);
+        //        // advance after last doc that would be accepted if standard
+        //        // iteration is used (to exhaust it):
+        //        obs.Advance(bits.Length);
+        //    }
+        //    else
+        //    {
+        //        int doc;
+        //        while ((doc = iter.NextDoc()) < bits.Length)
+        //        {
+        //            Clear(doc);
+        //        }
+        //    }
+        //}
+
+        /* this = this AND NOT other */
+        public void AndNot(FixedBitSet other)
+        {
+            AndNot(other.bits, other.bits.Length);
+        }
+
+        private void AndNot(BitArray otherArr, int otherLen)
+        {
+            var thisArr = this.bits;
+            int pos = Math.Min(thisArr.Length, otherLen);
+            while (--pos >= 0)
+            {
+                thisArr[pos] &= !otherArr[pos];
+            }
+        }
+
+        // NOTE: no .isEmpty() here because that's trappy (ie,
+        // typically isEmpty is low cost, but this one wouldn't
+        // be)
+
+        /* Flips a range of bits
+         *
+         * @param startIndex lower index
+         * @param endIndex one-past the last bit to flip
+         */
+        //      public void Flip(int startIndex, int endIndex) {
+        //  Debug.Assert(startIndex >= 0 && startIndex < numBits);
+        //  Debug.Assert(endIndex >= 0 && endIndex <= numBits);
+        //  if (endIndex <= startIndex) {
+        //    return;
+        //  }
+
+        //  int startWord = startIndex >> 6;
+        //  int endWord = (endIndex-1) >> 6;
+
+        //  /* Grrr, java shifting wraps around so -1L>>>64 == -1
+        //   * for that reason, make sure not to use endmask if the bits to flip will
+        //   * be zero in the last word (redefine endWord to be the last changed...)
+        //  long startmask = -1L << (startIndex & 0x3f);     // example: 11111...111000
+        //  long endmask = -1L >>> (64-(endIndex & 0x3f));   // example: 00111...111111
+        //  ***/
+
+        //  long startmask = -1L << startIndex;
+        //  long endmask =  -1L >>> -endIndex;  // 64-(endIndex&0x3f) is the same as -endIndex due to wrap
+
+        //  if (startWord == endWord) {
+        //    bits[startWord] ^= (startmask & endmask);
+        //    return;
+        //  }
+
+        //  bits[startWord] ^= startmask;
+
+        //  for (var i=startWord+1; i<endWord; i++) {
+        //    bits[i] = ~bits[i];
+        //  }
+
+        //  bits[endWord] ^= endmask;
+        //}
+
+        /* Sets a range of bits
+         *
+         * @param startIndex lower index
+         * @param endIndex one-past the last bit to set
+         */
+        public void Set(int startIndex, int endIndex)
+        {
+            // Naive implementation
+            for (int i = startIndex; i < endIndex; i++)
+            {
+                Set(i);
+            }
+        }
+
+        //      public void Set(int startIndex, int endIndex) {
+        //  Debug.Assert(startIndex >= 0 && startIndex < numBits);
+        //  Debug.Assert(endIndex >= 0 && endIndex <= numBits);
+        //  if (endIndex <= startIndex) {
+        //    return;
+        //  }
+
+        //  int startWord = startIndex >> 6;
+        //  int endWord = (endIndex-1) >> 6;
+
+        //  long startmask = -1L << startIndex;
+        //  long endmask = -1L >>> -endIndex;  // 64-(endIndex&0x3f) is the same as -endIndex due to wrap
+
+        //  if (startWord == endWord) {
+        //    bits[startWord] |= (startmask & endmask);
+        //    return;
+        //  }
+
+        //  bits[startWord] |= startmask;
+        //  Arrays.Fill(bits, startWord+1, endWord, -1L);
+        //  bits[endWord] |= endmask;
+        //}
+
+        /* Clears a range of bits.
+         *
+         * @param startIndex lower index
+         * @param endIndex one-past the last bit to clear
+         */
+        public void Clear(int startIndex, int endIndex)
+        {
+            for (int i = startIndex; i < endIndex; i++)
+            {
+                Clear(i);
+            }
+        }
+
+        //@Override
+        public FixedBitSet Clone()
+        {
+            return new FixedBitSet(this);
+        }
+
+        /* returns true if both sets have the same bits set */
+        public override bool Equals(Object o)
+        {
+            if (this == o)
+            {
+                return true;
+            }
+
+            var other = o as FixedBitSet;
+            if (other == null)
+            {
+                return false;
+            }
+
+            return bits.Equals(other.bits);
+        }
+
+        public override int GetHashCode()
+        {
+            return bits.GetHashCode();
+        }
+
+        public override DocIdSetIterator Iterator()
+        {
+            return new FixedBitSetIterator(this);
+        }
+
+        /// <summary>
+        /// A FixedBitSet Iterator implementation
+        /// </summary>
+        public class FixedBitSetIterator : DocIdSetIterator
+        {
+            private int curDocId = -1;
+            private readonly IEnumerator enumerator;
+
+            public FixedBitSetIterator(FixedBitSet bitset)
+            {
+                enumerator = bitset.bits.GetEnumerator();
+            }
+
+            public override int DocID()
+            {
+                return curDocId;
+            }
+
+            public override int NextDoc()
+            {
+                while (enumerator.MoveNext())
+                {
+                    ++curDocId;
+                    if ((bool)enumerator.Current) return curDocId;
+                }
+                return curDocId = NO_MORE_DOCS;
+            }
+
+            public override int Advance(int target)
+            {
+                int doc;
+                while ((doc = NextDoc()) < target)
+                {
+                }
+                return doc;
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/c9f901b2/src/Lucene.Net.Spatial/Util/FunctionQuery.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/FunctionQuery.cs b/src/Lucene.Net.Spatial/Util/FunctionQuery.cs
new file mode 100644
index 0000000..64eda65
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Util/FunctionQuery.cs
@@ -0,0 +1,215 @@
+/*
+ * 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.
+ */
+
+
+using System;
+using Lucene.Net.Index;
+using Lucene.Net.Search;
+using Lucene.Net.Search.Function;
+
+namespace Lucene.Net.Spatial.Util
+{
+    /// <summary>
+    /// Port of Solr's FunctionQuery (v1.4)
+    /// 
+    /// Returns a score for each document based on a ValueSource,
+    /// often some function of the value of a field.
+    /// 
+    /// <b>Note: This API is experimental and may change in non backward-compatible ways in the future</b>
+    /// </summary>
+    public class FunctionQuery : Query
+    {
+        protected readonly ValueSource func;
+
+        public FunctionQuery(ValueSource func)
+        {
+            this.func = func;
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <returns>The associated ValueSource</returns>
+        public ValueSource GetValueSource()
+        {
+            return func;
+        }
+
+        public override Query Rewrite(Index.IndexReader reader)
+        {
+            return this;
+        }
+
+        public override void ExtractTerms(System.Collections.Generic.ISet<Term> terms)
+        {
+            //base.ExtractTerms(terms);
+        }
+
+        protected class FunctionWeight : Weight
+        {
+            protected Searcher searcher;
+            protected float queryNorm;
+            protected float queryWeight;
+            protected readonly FunctionQuery enclosingInstance;
+
+            public FunctionWeight(Searcher searcher, FunctionQuery q)
+            {
+                enclosingInstance = q;
+                this.searcher = searcher;
+                //q.func.CreateWeight(searcher);
+            }
+
+            internal float GetQueryNorm()
+            {
+                return queryNorm;
+            }
+
+            public override Query Query
+            {
+                get { return enclosingInstance; }
+            }
+
+            public override float Value
+            {
+                get { return queryWeight; }
+            }
+
+            public override float GetSumOfSquaredWeights()
+            {
+                queryWeight = enclosingInstance.Boost;
+                return queryWeight * queryWeight;
+            }
+
+            public override void Normalize(float norm)
+            {
+                this.queryNorm = norm;
+                queryWeight *= this.queryNorm;
+            }
+
+            public override Scorer Scorer(IndexReader reader, bool scoreDocsInOrder, bool topScorer)
+            {
+                return new AllScorer(enclosingInstance.GetSimilarity(searcher), reader, this);
+            }
+
+            public override Explanation Explain(IndexReader reader, int doc)
+            {
+                return ((AllScorer)Scorer(reader, true, true)).Explain(doc);
+            }
+        }
+
+        protected class AllScorer : Scorer
+        {
+            readonly IndexReader reader;
+            readonly FunctionWeight weight;
+            readonly int maxDoc;
+            readonly float qWeight;
+            int doc = -1;
+            readonly DocValues vals;
+            readonly bool hasDeletions;
+
+            public AllScorer(Similarity similarity, IndexReader reader, FunctionWeight w)
+                : base(similarity)
+            {
+                this.weight = w;
+                this.qWeight = w.Value;
+                this.reader = reader;
+                this.maxDoc = reader.MaxDoc;
+                this.hasDeletions = reader.HasDeletions;
+                vals = ((FunctionQuery)w.Query).func.GetValues(reader);
+            }
+
+            public override int DocID()
+            {
+                return doc;
+            }
+
+            // instead of matching all docs, we could also embed a query.
+            // the score could either ignore the subscore, or boost it.
+            // Containment:  floatline(foo:myTerm, "myFloatField", 1.0, 0.0f)
+            // Boost:        foo:myTerm^floatline("myFloatField",1.0,0.0f)
+            public override int NextDoc()
+            {
+                for (; ; )
+                {
+                    ++doc;
+                    if (doc >= maxDoc)
+                    {
+                        return doc = NO_MORE_DOCS;
+                    }
+                    if (hasDeletions && reader.IsDeleted(doc)) continue;
+                    return doc;
+                }
+            }
+
+            public override int Advance(int target)
+            {
+                // this will work even if target==NO_MORE_DOCS
+                doc = target - 1;
+                return NextDoc();
+            }
+
+            public override float Score()
+            {
+                float score = qWeight * vals.FloatVal(doc);
+
+                // Current Lucene priority queues can't handle NaN and -Infinity, so
+                // map to -Float.MAX_VALUE. This conditional handles both -infinity
+                // and NaN since comparisons with NaN are always false.
+                return score > float.NegativeInfinity ? score : -float.MaxValue;
+            }
+
+            public /*override*/ Explanation Explain(int doc)
+            {
+                float sc = qWeight * vals.FloatVal(doc);
+
+                Explanation result = new ComplexExplanation
+                  (true, sc, "FunctionQuery(" + ((FunctionQuery)weight.Query).func + "), product of:");
+
+                result.AddDetail(vals.Explain(doc));
+                result.AddDetail(new Explanation(weight.Query.Boost, "boost"));
+                result.AddDetail(new Explanation(weight.GetQueryNorm(), "queryNorm"));
+                return result;
+            }
+        }
+
+        public override Weight CreateWeight(Searcher searcher)
+        {
+            return new FunctionQuery.FunctionWeight(searcher, this);
+        }
+
+        public override string ToString(string field)
+        {
+            float boost = Boost;
+            return (boost != 1.0 ? "(" : "") + func.ToString()
+                    + (boost == 1.0 ? "" : ")^" + boost);
+        }
+
+        public override bool Equals(object o)
+        {
+            var other = o as FunctionQuery;
+
+            if (other == null) return false;
+
+            return this.Boost == other.Boost && this.func.Equals(other.func);
+        }
+
+        public override int GetHashCode()
+        {
+            return (int) (func.GetHashCode() * 31 + BitConverter.DoubleToInt64Bits(Boost));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/c9f901b2/src/Lucene.Net.Spatial/Util/ReciprocalFloatFunction.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/ReciprocalFloatFunction.cs b/src/Lucene.Net.Spatial/Util/ReciprocalFloatFunction.cs
new file mode 100644
index 0000000..5789df4
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Util/ReciprocalFloatFunction.cs
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+
+using System;
+using Lucene.Net.Index;
+using Lucene.Net.Search.Function;
+
+namespace Lucene.Net.Spatial.Util
+{
+    public class ReciprocalFloatFunction : ValueSource
+    {
+        protected readonly ValueSource source;
+        protected readonly float m;
+        protected readonly float a;
+        protected readonly float b;
+
+        /// <summary>
+        /// f(source) = a/(m*float(source)+b)
+        /// </summary>
+        /// <param name="source"></param>
+        /// <param name="m"></param>
+        /// <param name="a"></param>
+        /// <param name="b"></param>
+        public ReciprocalFloatFunction(ValueSource source, float m, float a, float b)
+        {
+            this.source = source;
+            this.m = m;
+            this.a = a;
+            this.b = b;
+        }
+
+        public class FloatDocValues : DocValues
+        {
+            private readonly ReciprocalFloatFunction _enclosingInstance;
+            private readonly DocValues vals;
+
+            public FloatDocValues(ReciprocalFloatFunction enclosingInstance, DocValues vals)
+            {
+                _enclosingInstance = enclosingInstance;
+                this.vals = vals;
+            }
+
+            public override float FloatVal(int doc)
+            {
+                return _enclosingInstance.a / (_enclosingInstance.m * vals.FloatVal(doc) + _enclosingInstance.b);
+            }
+
+            public override string ToString(int doc)
+            {
+                return _enclosingInstance.a + "/("
+                       + _enclosingInstance.m + "*float(" + vals.ToString(doc) + ')'
+                       + '+' + _enclosingInstance.b + ')';
+            }
+        }
+
+        public override DocValues GetValues(IndexReader reader)
+        {
+            var vals = source.GetValues(reader);
+            return new FloatDocValues(this, vals);
+        }
+
+        public override string Description()
+        {
+            return a + "/("
+                   + m + "*float(" + source.Description() + ")"
+                   + "+" + b + ')';
+        }
+
+        public override bool Equals(object o)
+        {
+            if (typeof(ReciprocalFloatFunction) != o.GetType()) return false;
+            var other = (ReciprocalFloatFunction)o;
+            return this.m == other.m
+                   && this.a == other.a
+                   && this.b == other.b
+                   && this.source.Equals(other.source);
+        }
+
+        public override int GetHashCode()
+        {
+            int h = (int) BitConverter.DoubleToInt64Bits(a) + (int) BitConverter.DoubleToInt64Bits(m);
+            h ^= (h << 13) | (int)((uint)h >> 20);
+            return h + ((int) BitConverter.DoubleToInt64Bits(b)) + source.GetHashCode();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/c9f901b2/src/Lucene.Net.Spatial/Util/ShapeFieldCache.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/ShapeFieldCache.cs b/src/Lucene.Net.Spatial/Util/ShapeFieldCache.cs
new file mode 100644
index 0000000..59db379
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Util/ShapeFieldCache.cs
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+using System.Collections.Generic;
+using Spatial4n.Core.Shapes;
+
+namespace Lucene.Net.Spatial.Util
+{
+    /// <summary>
+    /// Bounded Cache of Shapes associated with docIds.  Note, multiple Shapes can be
+    /// associated with a given docId
+    /// </summary>
+    /// <typeparam name="T"></typeparam>
+    public class ShapeFieldCache<T> where T : Shape
+    {
+        private readonly IList<T>[] cache;
+        public int defaultLength;
+
+        public ShapeFieldCache(int length, int defaultLength)
+        {
+            cache = new IList<T>[length];
+            this.defaultLength = defaultLength;
+        }
+
+        public void Add(int docid, T s)
+        {
+            IList<T> list = cache[docid];
+            if (list == null)
+            {
+                list = cache[docid] = new List<T>(defaultLength);
+            }
+            list.Add(s);
+        }
+
+        public IList<T> GetShapes(int docid)
+        {
+            return cache[docid];
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/c9f901b2/src/Lucene.Net.Spatial/Util/ShapeFieldCacheDistanceValueSource.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/ShapeFieldCacheDistanceValueSource.cs b/src/Lucene.Net.Spatial/Util/ShapeFieldCacheDistanceValueSource.cs
new file mode 100644
index 0000000..d806767
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Util/ShapeFieldCacheDistanceValueSource.cs
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+using System;
+using Lucene.Net.Index;
+using Lucene.Net.Search.Function;
+using Spatial4n.Core.Context;
+using Spatial4n.Core.Distance;
+using Spatial4n.Core.Shapes;
+
+namespace Lucene.Net.Spatial.Util
+{
+    /// <summary>
+    /// An implementation of the Lucene ValueSource model to support spatial relevance ranking.
+    /// </summary>
+    public class ShapeFieldCacheDistanceValueSource : ValueSource
+    {
+        private readonly ShapeFieldCacheProvider<Point> provider;
+        private readonly SpatialContext ctx;
+        private readonly Point from;
+
+        public ShapeFieldCacheDistanceValueSource(SpatialContext ctx, ShapeFieldCacheProvider<Point> provider, Point from)
+        {
+            this.ctx = ctx;
+            this.from = from;
+            this.provider = provider;
+        }
+
+        public class CachedDistanceDocValues : DocValues
+        {
+            private readonly ShapeFieldCacheDistanceValueSource enclosingInstance;
+            private readonly ShapeFieldCache<Point> cache;
+            private readonly Point from;
+            private readonly DistanceCalculator calculator;
+            private readonly double nullValue;
+
+            public CachedDistanceDocValues(IndexReader reader, ShapeFieldCacheDistanceValueSource enclosingInstance)
+            {
+                cache = enclosingInstance.provider.GetCache(reader);
+                this.enclosingInstance = enclosingInstance;
+                
+                from = enclosingInstance.from;
+                calculator = enclosingInstance.ctx.GetDistCalc();
+                nullValue = (enclosingInstance.ctx.IsGeo() ? 180 : double.MaxValue);
+            }
+
+            public override float FloatVal(int doc)
+            {
+                return (float)DoubleVal(doc);
+            }
+
+            public override double DoubleVal(int doc)
+            {
+                var vals = cache.GetShapes(doc);
+                if (vals != null)
+                {
+                    double v = calculator.Distance(from, vals[0]);
+                    for (int i = 1; i < vals.Count; i++)
+                    {
+                        v = Math.Min(v, calculator.Distance(from, vals[i]));
+                    }
+                    return v;
+                }
+                return nullValue;
+            }
+
+            public override string ToString(int doc)
+            {
+                return enclosingInstance.Description() + "=" + FloatVal(doc);
+            }
+        }
+
+        public override DocValues GetValues(IndexReader reader)
+        {
+            return new CachedDistanceDocValues(reader, this);
+        }
+
+        public override string Description()
+        {
+            return GetType().Name + "(" + provider + ", " + from + ")";
+        }
+
+        public override bool Equals(object o)
+        {
+            if (this == o) return true;
+
+            var that = o as ShapeFieldCacheDistanceValueSource;
+
+            if (that == null) return false;
+            if (!ctx.Equals(that.ctx)) return false;
+            if (!from.Equals(that.from)) return false;
+            if (!provider.Equals(that.provider)) return false;
+
+            return true;
+        }
+
+        public override int GetHashCode()
+        {
+            return from.GetHashCode();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/c9f901b2/src/Lucene.Net.Spatial/Util/ShapeFieldCacheProvider.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/ShapeFieldCacheProvider.cs b/src/Lucene.Net.Spatial/Util/ShapeFieldCacheProvider.cs
new file mode 100644
index 0000000..782ebc9
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Util/ShapeFieldCacheProvider.cs
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+using System;
+using System.Runtime.CompilerServices;
+using Lucene.Net.Index;
+using Spatial4n.Core.Shapes;
+#if NET35
+using Lucene.Net.Support;
+#endif
+
+namespace Lucene.Net.Spatial.Util
+{
+    /// <summary>
+    /// Provides access to a {@link ShapeFieldCache} for a given {@link AtomicReader}.
+    /// 
+    /// If a Cache does not exist for the Reader, then it is built by iterating over
+    /// the all terms for a given field, reconstructing the Shape from them, and adding
+    /// them to the Cache.
+    /// </summary>
+    /// <typeparam name="T"></typeparam>
+    public abstract class ShapeFieldCacheProvider<T> where T : Shape
+    {
+        //private Logger log = Logger.getLogger(getClass().getName());
+
+        // it may be a List<T> or T
+#if !NET35
+        private readonly ConditionalWeakTable<IndexReader, ShapeFieldCache<T>> sidx =
+            new ConditionalWeakTable<IndexReader, ShapeFieldCache<T>>(); // WeakHashMap
+#else
+        private readonly WeakDictionary<IndexReader, ShapeFieldCache<T>> sidx =
+            new WeakDictionary<IndexReader, ShapeFieldCache<T>>();
+#endif
+
+
+        protected readonly int defaultSize;
+        protected readonly String shapeField;
+
+        protected ShapeFieldCacheProvider(String shapeField, int defaultSize)
+        {
+            this.shapeField = shapeField;
+            this.defaultSize = defaultSize;
+        }
+
+        protected abstract T ReadShape(/*BytesRef*/ Term term);
+
+        private readonly object locker = new object();
+
+        public ShapeFieldCache<T> GetCache(IndexReader reader)
+        {
+            lock (locker)
+            {
+                ShapeFieldCache<T> idx;
+                if (sidx.TryGetValue(reader, out idx) && idx != null)
+                {
+                    return idx;
+                }
+
+                //long startTime = System.CurrentTimeMillis();
+                //log.fine("Building Cache [" + reader.MaxDoc() + "]");
+
+                idx = new ShapeFieldCache<T>(reader.MaxDoc, defaultSize);
+                var count = 0;
+                var tec = new TermsEnumCompatibility(reader, shapeField);
+
+                var term = tec.Next();
+                while (term != null)
+                {
+                    var shape = ReadShape(term);
+                    if (shape != null)
+                    {
+                        var docs = reader.TermDocs(new Term(shapeField, tec.Term().Text));
+                        while (docs.Next())
+                        {
+                            idx.Add(docs.Doc, shape);
+                            count++;
+                        }
+                    }
+                    term = tec.Next();
+                }
+
+                sidx.Add(reader, idx);
+                tec.Close();
+
+                //long elapsed = System.CurrentTimeMillis() - startTime;
+                //log.fine("Cached: [" + count + " in " + elapsed + "ms] " + idx);
+                return idx;
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/c9f901b2/src/Lucene.Net.Spatial/Util/TermsEnumCompatibility.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/TermsEnumCompatibility.cs b/src/Lucene.Net.Spatial/Util/TermsEnumCompatibility.cs
new file mode 100644
index 0000000..7b6809e
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Util/TermsEnumCompatibility.cs
@@ -0,0 +1,140 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+using System;
+using System.Collections.Generic;
+using Lucene.Net.Index;
+using Lucene.Net.Util;
+
+namespace Lucene.Net.Spatial.Util
+{
+    /// <summary>
+    /// Wraps Lucene 3 TermEnum to make it look like a Lucene 4 TermsEnum
+    /// SOLR-2155
+    /// @author dsmiley
+    /// </summary>
+    public class TermsEnumCompatibility
+    {
+        private readonly IndexReader reader;
+        private readonly String fieldName;
+        private TermEnum termEnum;
+        private bool initialState = true;
+
+        public TermsEnumCompatibility(IndexReader reader, String fieldName)
+        {
+            this.reader = reader;
+            this.fieldName = string.Intern(fieldName);
+            this.termEnum = reader.Terms(new Term(this.fieldName));
+        }
+
+        public TermEnum GetTermEnum()
+        {
+            return termEnum;
+        }
+
+        public Term Term()
+        {
+            Term t = termEnum.Term;
+            return t != null && t.Field == fieldName ? t : null;
+        }
+
+        public Term Next()
+        {
+            //in Lucene 3, a call to reader.terms(term) is already pre-positioned, you don't call next first
+            if (initialState)
+            {
+                initialState = false;
+                return Term();
+            }
+            else
+            {
+                return termEnum.Next() ? Term() : null;
+            }
+        }
+
+        public void Close()
+        {
+            termEnum.Close();
+        }
+
+        public enum SeekStatus
+        {
+            END,
+            FOUND,
+            NOT_FOUND
+        }
+
+        public SeekStatus Seek(String value)
+        {
+            termEnum = reader.Terms(new Term(this.fieldName, value));
+            Term t = Term();
+            if (t == null)
+                return SeekStatus.END;
+            return (t.Text.Equals(value)) ? SeekStatus.FOUND : SeekStatus.NOT_FOUND;
+        }
+
+        /// <summary>
+        /// Seeks to the specified term, if it exists, or to the
+        /// next (ceiling) term.  Returns SeekStatus to
+        /// indicate whether exact term was found, a different
+        /// term was found, or EOF was hit.  The target term may
+        /// be before or after the current term.  If this returns
+        /// SeekStatus.END, the enum is unpositioned.
+        /// </summary>
+        /// <param name="value"></param>
+        /// <returns></returns>
+        public SeekStatus SeekCeil(String value)
+        {
+            return Seek(value);
+        }
+
+        /// <summary>
+        /// Returns the number of documents that have at least one
+        /// term for this field, or -1 if this measure isn't
+        /// stored by the codec.  Note that, just like other term
+        /// measures, this measure does not take deleted documents
+        /// into account.
+        /// </summary>
+        /// <returns></returns>
+        public int GetDocCount()
+        {
+            return -1; // TODO find a way to efficiently determine this
+        }
+
+        public void Docs(OpenBitSet bits)
+        {
+            var termDocs = reader.TermDocs(new Term(fieldName, Term().Text));
+            while (termDocs.Next())
+            {
+                bits.FastSet(termDocs.Doc);
+            }
+        }
+
+        public void Docs(FixedBitSet bits)
+        {
+            var termDocs = reader.TermDocs(new Term(fieldName, Term().Text));
+            while (termDocs.Next())
+            {
+                bits.Set(termDocs.Doc);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/c9f901b2/src/Lucene.Net.Spatial/Util/TermsFilter.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/TermsFilter.cs b/src/Lucene.Net.Spatial/Util/TermsFilter.cs
new file mode 100644
index 0000000..4acfcef
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Util/TermsFilter.cs
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Lucene.Net.Index;
+using Lucene.Net.Search;
+
+namespace Lucene.Net.Spatial.Util
+{
+    /// <summary>
+    /// Constructs a filter for docs matching any of the terms added to this class.
+    /// Unlike a RangeFilter this can be used for filtering on multiple terms that are not necessarily in
+    /// a sequence. An example might be a collection of primary keys from a database query result or perhaps
+    /// a choice of "category" labels picked by the end user. As a filter, this is much faster than the
+    /// equivalent query (a BooleanQuery with many "should" TermQueries)
+    /// </summary>
+    public class TermsFilter : Filter
+    {
+        private readonly SortedSet<Term> terms = new SortedSet<Term>();
+
+        /// <summary>
+        /// Adds a term to the list of acceptable terms
+        /// </summary>
+        /// <param name="term"></param>
+        public void AddTerm(Term term)
+        {
+            terms.Add(term);
+        }
+
+        public override DocIdSet GetDocIdSet(IndexReader reader)
+        {
+            var result = new FixedBitSet(reader.MaxDoc);
+            var fields = reader.GetFieldNames(IndexReader.FieldOption.ALL);
+
+            if (fields == null || fields.Count == 0)
+            {
+                return result;
+            }
+
+            String lastField = null;
+            TermsEnumCompatibility termsEnum = null;
+            foreach (Term term in terms)
+            {
+                if (!term.Field.Equals(lastField))
+                {
+                    var termsC = new TermsEnumCompatibility(reader, term.Field);
+                    if (termsC.Term() == null)
+                    {
+                        return result;
+                    }
+                    termsEnum = termsC;
+                    lastField = term.Field;
+                }
+
+                if (terms != null)
+                {
+                    // TODO this check doesn't make sense, decide which variable its supposed to be for
+                    Debug.Assert(termsEnum != null);
+                    if (termsEnum.SeekCeil(term.Text) == TermsEnumCompatibility.SeekStatus.FOUND)
+                    {
+                        termsEnum.Docs(result);
+                    }
+                }
+            }
+            return result;
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (this == obj)
+                return true;
+
+            if ((obj == null) || (obj.GetType() != this.GetType()))
+                return false;
+
+            var test = (TermsFilter)obj;
+            if (terms == test.terms)
+                return true;
+            if (terms == null || terms.Count != test.terms.Count)
+                return false;
+
+            var e1 = terms.GetEnumerator();
+            var e2 = test.terms.GetEnumerator();
+            while (e1.MoveNext() && e2.MoveNext())
+            {
+                if (!e1.Current.Equals(e2.Current)) return false;
+            }
+            return true;
+        }
+
+        public override int GetHashCode()
+        {
+            int hash = 9;
+            foreach (Term term in terms)
+            {
+                hash = 31 * hash + term.GetHashCode();
+            }
+            return hash;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/c9f901b2/src/Lucene.Net.Spatial/Util/ValueSourceFilter.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/ValueSourceFilter.cs b/src/Lucene.Net.Spatial/Util/ValueSourceFilter.cs
new file mode 100644
index 0000000..e92c120
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Util/ValueSourceFilter.cs
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using Lucene.Net.Search;
+using Lucene.Net.Search.Function;
+
+namespace Lucene.Net.Spatial.Util
+{
+    /// <summary>
+    /// Filter that matches all documents where a valuesource is
+    /// in between a range of <c>min</c> and <c>max</c> inclusive.
+    /// </summary>
+    public class ValueSourceFilter : Filter
+    {
+        readonly Filter startingFilter;
+        readonly ValueSource source;
+        public readonly double min;
+        public readonly double max;
+
+        public ValueSourceFilter(Filter startingFilter, ValueSource source, double min, double max)
+        {
+            if (startingFilter == null)
+            {
+                throw new ArgumentException("please provide a non-null startingFilter; you can use QueryWrapperFilter(MatchAllDocsQuery) as a no-op filter", "startingFilter");
+            }
+            this.startingFilter = startingFilter;
+            this.source = source;
+            this.min = min;
+            this.max = max;
+        }
+
+        public override DocIdSet GetDocIdSet(Index.IndexReader reader)
+        {
+            var values = source.GetValues(reader);
+            return new ValueSourceFilteredDocIdSet(startingFilter.GetDocIdSet(reader), values, this);
+        }
+
+        public class ValueSourceFilteredDocIdSet : FilteredDocIdSet
+        {
+            private readonly ValueSourceFilter enclosingFilter;
+            private readonly DocValues values;
+
+            public ValueSourceFilteredDocIdSet(DocIdSet innerSet, DocValues values, ValueSourceFilter caller)
+                : base(innerSet)
+            {
+                this.enclosingFilter = caller;
+                this.values = values;
+            }
+
+            public override bool Match(int doc)
+            {
+                double val = values.DoubleVal(doc);
+                return val >= enclosingFilter.min && val <= enclosingFilter.max;
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/c9f901b2/src/Lucene.Net.Spatial/Vector/DistanceValueSource.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Vector/DistanceValueSource.cs b/src/Lucene.Net.Spatial/Vector/DistanceValueSource.cs
new file mode 100644
index 0000000..19e2ea0
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Vector/DistanceValueSource.cs
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+using System.Diagnostics;
+using Lucene.Net.Index;
+using Lucene.Net.Search;
+using Lucene.Net.Search.Function;
+using Lucene.Net.Spatial.Util;
+using Spatial4n.Core.Distance;
+using Spatial4n.Core.Shapes;
+using Spatial4n.Core.Shapes.Impl;
+
+namespace Lucene.Net.Spatial.Vector
+{
+    /// <summary>
+    /// An implementation of the Lucene ValueSource model that returns the distance.
+    /// </summary>
+    public class DistanceValueSource : ValueSource
+    {
+        private readonly PointVectorStrategy strategy;
+        private readonly Point from;
+
+        public DistanceValueSource(PointVectorStrategy strategy, Point from)
+        {
+            this.strategy = strategy;
+            this.from = from;
+        }
+
+        public class DistanceDocValues : DocValues
+        {
+            private readonly DistanceValueSource enclosingInstance;
+
+            private readonly double[] ptX, ptY;
+            private readonly IBits validX, validY;
+
+            private readonly Point from;
+            private readonly DistanceCalculator calculator;
+            private readonly double nullValue;
+
+            public DistanceDocValues(DistanceValueSource enclosingInstance, IndexReader reader)
+            {
+                this.enclosingInstance = enclosingInstance;
+
+                ptX = FieldCache_Fields.DEFAULT.GetDoubles(reader, enclosingInstance.strategy.GetFieldNameX()/*, true*/);
+                ptY = FieldCache_Fields.DEFAULT.GetDoubles(reader, enclosingInstance.strategy.GetFieldNameY()/*, true*/);
+                validX = FieldCache_Fields.DEFAULT.GetDocsWithField(reader, enclosingInstance.strategy.GetFieldNameX());
+                validY = FieldCache_Fields.DEFAULT.GetDocsWithField(reader, enclosingInstance.strategy.GetFieldNameY());
+
+                from = enclosingInstance.from;
+                calculator = enclosingInstance.strategy.GetSpatialContext().GetDistCalc();
+                nullValue = (enclosingInstance.strategy.GetSpatialContext().IsGeo() ? 180 : double.MaxValue);
+            }
+
+            public override float FloatVal(int doc)
+            {
+                return (float)DoubleVal(doc);
+            }
+
+            public override double DoubleVal(int doc)
+            {
+                // make sure it has minX and area
+                if (validX.Get(doc))
+                {
+                    Debug.Assert(validY.Get(doc));
+                    return calculator.Distance(from, ptX[doc], ptY[doc]);
+                }
+                return nullValue;
+            }
+
+            public override string ToString(int doc)
+            {
+                return enclosingInstance.Description() + "=" + FloatVal(doc);
+            }
+        }
+
+        public override DocValues GetValues(IndexReader reader)
+        {
+            return new DistanceDocValues(this, reader);
+        }
+
+        public override string Description()
+        {
+            return "DistanceValueSource(" + strategy + ", " + from + ")";
+        }
+
+        public override bool Equals(object o)
+        {
+            if (this == o) return true;
+
+            var that = o as DistanceValueSource;
+            if (that == null) return false;
+
+            if (!from.Equals(that.from)) return false;
+            if (!strategy.Equals(that.strategy)) return false;
+
+            return true;
+        }
+
+        public override int GetHashCode()
+        {
+            return from.GetHashCode();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/c9f901b2/src/Lucene.Net.Spatial/Vector/PointVectorStrategy.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Vector/PointVectorStrategy.cs b/src/Lucene.Net.Spatial/Vector/PointVectorStrategy.cs
new file mode 100644
index 0000000..f3ef815
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Vector/PointVectorStrategy.cs
@@ -0,0 +1,260 @@
+/*
+ * 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.
+ */
+
+using System;
+using Lucene.Net.Documents;
+using Lucene.Net.Search;
+using Lucene.Net.Search.Function;
+using Lucene.Net.Spatial.Queries;
+using Lucene.Net.Spatial.Util;
+using Spatial4n.Core.Context;
+using Spatial4n.Core.Shapes;
+
+namespace Lucene.Net.Spatial.Vector
+{
+    /// <summary>
+    /// Simple {@link SpatialStrategy} which represents Points in two numeric {@link DoubleField}s.
+    /// 
+    /// Note, currently only Points can be indexed by this Strategy.  At query time, the bounding
+    /// box of the given Shape is used to create {@link NumericRangeQuery}s to efficiently
+    /// find Points within the Shape.
+    /// 
+    /// Due to the simple use of numeric fields, this Strategy provides support for sorting by
+    /// distance through {@link DistanceValueSource}
+    /// </summary>
+    public class PointVectorStrategy : SpatialStrategy
+    {
+        public static String SUFFIX_X = "__x";
+        public static String SUFFIX_Y = "__y";
+
+        private readonly String fieldNameX;
+        private readonly String fieldNameY;
+
+        public int precisionStep = 8; // same as solr default
+
+        public PointVectorStrategy(SpatialContext ctx, String fieldNamePrefix)
+            : base(ctx, fieldNamePrefix)
+        {
+            this.fieldNameX = fieldNamePrefix + SUFFIX_X;
+            this.fieldNameY = fieldNamePrefix + SUFFIX_Y;
+        }
+
+        public void SetPrecisionStep(int p)
+        {
+            precisionStep = p;
+            if (precisionStep <= 0 || precisionStep >= 64)
+                precisionStep = int.MaxValue;
+        }
+
+        public string GetFieldNameX()
+        {
+            return fieldNameX;
+        }
+
+        public string GetFieldNameY()
+        {
+            return fieldNameY;
+        }
+
+        public override AbstractField[] CreateIndexableFields(Shape shape)
+        {
+            var point = shape as Point;
+            if (point != null)
+                return CreateIndexableFields(point);
+
+            throw new InvalidOperationException("Can only index Point, not " + shape);
+        }
+
+        public AbstractField[] CreateIndexableFields(Point point)
+        {
+                var f = new AbstractField[2];
+
+                var f0 = new NumericField(fieldNameX, precisionStep, Field.Store.NO, true)
+                             {OmitNorms = true, OmitTermFreqAndPositions = true};
+                f0.SetDoubleValue(point.GetX());
+                f[0] = f0;
+
+                var f1 = new NumericField(fieldNameY, precisionStep, Field.Store.NO, true)
+                             {OmitNorms = true, OmitTermFreqAndPositions = true};
+                f1.SetDoubleValue(point.GetY());
+                f[1] = f1;
+
+                return f;
+        }
+
+        public override ValueSource MakeDistanceValueSource(Point queryPoint)
+        {
+            return new DistanceValueSource(this, queryPoint);
+        }
+
+        public override ConstantScoreQuery MakeQuery(SpatialArgs args)
+        {
+            if (!SpatialOperation.Is(args.Operation,
+                                     SpatialOperation.Intersects,
+                                     SpatialOperation.IsWithin))
+                throw new UnsupportedSpatialOperation(args.Operation);
+
+            Shape shape = args.Shape;
+            var bbox = shape as Rectangle;
+            if (bbox != null)
+                return new ConstantScoreQuery(new QueryWrapperFilter(MakeWithin(bbox)));
+
+            var circle = shape as Circle;
+            if (circle != null)
+            {
+                bbox = circle.GetBoundingBox();
+                var vsf = new ValueSourceFilter(
+                    new QueryWrapperFilter(MakeWithin(bbox)),
+                    MakeDistanceValueSource(circle.GetCenter()),
+                    0,
+                    circle.GetRadius());
+                return new ConstantScoreQuery(vsf);
+            }
+            
+            throw new InvalidOperationException("Only Rectangles and Circles are currently supported, " +
+                                            "found [" + shape.GetType().Name + "]"); //TODO
+        }
+
+        //TODO this is basically old code that hasn't been verified well and should probably be removed
+        public Query MakeQueryDistanceScore(SpatialArgs args)
+        {
+            // For starters, just limit the bbox
+            var shape = args.Shape;
+            if (!(shape is Rectangle || shape is Circle))
+                throw new InvalidOperationException("Only Rectangles and Circles are currently supported, found ["
+                    + shape.GetType().Name + "]");//TODO
+
+            Rectangle bbox = shape.GetBoundingBox();
+            if (bbox.GetCrossesDateLine())
+            {
+                throw new InvalidOperationException("Crossing dateline not yet supported");
+            }
+
+            ValueSource valueSource = null;
+
+            Query spatial = null;
+            SpatialOperation op = args.Operation;
+
+            if (SpatialOperation.Is(op,
+                SpatialOperation.BBoxWithin,
+                SpatialOperation.BBoxIntersects))
+            {
+                spatial = MakeWithin(bbox);
+            }
+            else if (SpatialOperation.Is(op,
+              SpatialOperation.Intersects,
+              SpatialOperation.IsWithin))
+            {
+                spatial = MakeWithin(bbox);
+                var circle = args.Shape as Circle;
+                if (circle != null)
+                {
+                    // Make the ValueSource
+                    valueSource = MakeDistanceValueSource(shape.GetCenter());
+
+                    var vsf = new ValueSourceFilter(
+                        new QueryWrapperFilter(spatial), valueSource, 0, circle.GetRadius());
+
+                    spatial = new FilteredQuery(new MatchAllDocsQuery(), vsf);
+                }
+            }
+            else if (op == SpatialOperation.IsDisjointTo)
+            {
+                spatial = MakeDisjoint(bbox);
+            }
+
+            if (spatial == null)
+            {
+                throw new UnsupportedSpatialOperation(args.Operation);
+            }
+
+            if (valueSource != null)
+            {
+                valueSource = new CachingDoubleValueSource(valueSource);
+            }
+            else
+            {
+                valueSource = MakeDistanceValueSource(shape.GetCenter());
+            }
+            Query spatialRankingQuery = new FunctionQuery(valueSource);
+            var bq = new BooleanQuery();
+            bq.Add(spatial, Occur.MUST);
+            bq.Add(spatialRankingQuery, Occur.MUST);
+            return bq;
+
+        }
+
+        public override Filter MakeFilter(SpatialArgs args)
+        {
+            //unwrap the CSQ from makeQuery
+            ConstantScoreQuery csq = MakeQuery(args);
+            Filter filter = csq.Filter;
+            if (filter != null)
+                return filter;
+            else
+                return new QueryWrapperFilter(csq);
+        }
+
+        /// <summary>
+        /// Constructs a query to retrieve documents that fully contain the input envelope.
+        /// </summary>
+        /// <param name="bbox"></param>
+        private Query MakeWithin(Rectangle bbox)
+        {
+            var bq = new BooleanQuery();
+            const Occur MUST = Occur.MUST;
+            if (bbox.GetCrossesDateLine())
+            {
+                //use null as performance trick since no data will be beyond the world bounds
+                bq.Add(RangeQuery(fieldNameX, null /*-180*/, bbox.GetMaxX()), Occur.SHOULD);
+                bq.Add(RangeQuery(fieldNameX, bbox.GetMinX(), null /*+180*/), Occur.SHOULD);
+                bq.MinimumNumberShouldMatch = 1; //must match at least one of the SHOULD
+            }
+            else
+            {
+                bq.Add(RangeQuery(fieldNameX, bbox.GetMinX(), bbox.GetMaxX()), MUST);
+            }
+            bq.Add(RangeQuery(fieldNameY, bbox.GetMinY(), bbox.GetMaxY()), MUST);
+            return bq;
+        }
+
+        private NumericRangeQuery<Double> RangeQuery(String fieldName, double? min, double? max)
+        {
+            return NumericRangeQuery.NewDoubleRange(
+                fieldName,
+                precisionStep,
+                min,
+                max,
+                true,
+                true); //inclusive
+        }
+
+        /// <summary>
+        /// Constructs a query to retrieve documents that fully contain the input envelope.
+        /// </summary>
+        /// <param name="bbox"></param>
+        private Query MakeDisjoint(Rectangle bbox)
+        {
+            if (bbox.GetCrossesDateLine())
+                throw new InvalidOperationException("MakeDisjoint doesn't handle dateline cross");
+            Query qX = RangeQuery(fieldNameX, bbox.GetMinX(), bbox.GetMaxX());
+            Query qY = RangeQuery(fieldNameY, bbox.GetMinY(), bbox.GetMaxY());
+            var bq = new BooleanQuery {{qX, Occur.MUST_NOT}, {qY, Occur.MUST_NOT}};
+            return bq;
+        }
+    }
+}


Mime
View raw message