/// <summary>
 /// Area of covered rectangle for two sepcified rectangles
 /// </summary>
 public static double JoinArea(RectangleR2 a, RectangleR2 b) 
 {
     double _left = (a._left < b._left) ? a._left : b._left;
     double _right = (a._right > b._right) ? a._right : b._right;
     double _top = (a._top < b._top) ? a._top : b._top;
     double _bottom = (a._bottom > b._bottom) ? a._bottom : b._bottom;
     return (_bottom-_top)*(_right-_left);
 }
 internal RtreeR2Page(Storage storage, object obj, RectangleR2 r)
 {
     branch = storage.CreateLink(card);
     branch.Length = card;
     b = new RectangleR2[card];
     setBranch(0, new RectangleR2(r), obj);
     n = 1;
     for (int i = 1; i < card; i++)
     {
         b[i] = new RectangleR2();
     }
 }
 internal RtreeR2Page(Storage storage, RtreeR2Page root, RtreeR2Page p)
 {
     branch = storage.CreateLink(card);
     branch.Length = card;
     b = new RectangleR2[card];
     n = 2;
     setBranch(0, root.cover(), root);
     setBranch(1, p.cover(), p);
     for (int i = 2; i < card; i++)
     {
         b[i] = new RectangleR2();
     }
 }
    public static void Main(String[] args) 
    { 
        Storage db = StorageFactory.Instance.CreateStorage();
        db.Open("testleak.dbs");
#if USE_GENERICS
        SpatialIndexR2<SpatialObject> root = db.CreateSpatialIndexR2<SpatialObject>();
#else
        SpatialIndexR2 root = db.CreateSpatialIndexR2();
#endif
        RectangleR2[] rectangles = new RectangleR2[nObjects];
        Random rnd = new Random(2014);
        db.Root = root;
        for (int i = 0; i < nObjects; i++) { 
            SpatialObject so = new SpatialObject();
            double lat = rnd.NextDouble()*180;
            double lng = rnd.NextDouble()*180;
            so.rect = rectangles[i] = new RectangleR2(lat, lng, lat+10, lng+10);
            so.body = new byte[minObjectSize + rnd.Next(maxObjectSize - minObjectSize)];
            root.Put(so.rect, so);
        } 
        db.Commit();

        for (int i = 0; i < nIterations; i++) {
            if (i % 1000 == 0) { 
                Console.WriteLine("Iteration " + i);
            }
            for (int j = 0; j < batchSize; j++) { 
                int k = rnd.Next(nObjects);
                bool found = false;
                foreach (SpatialObject oldObj in root.Overlaps(rectangles[k])) { 
                    if (oldObj.rect.Equals(rectangles[k])) {
                        root.Remove(oldObj.rect, oldObj);
                        SpatialObject newObj = new SpatialObject();
                        newObj.rect = oldObj.rect;
                        newObj.body = new byte[minObjectSize + rnd.Next(maxObjectSize - minObjectSize)];
                        root.Put(newObj.rect, newObj);
                        oldObj.Deallocate();
                        found = true;
                        break;
                    }
                }
                Debug.Assert(found);
            }
            db.Commit();
        }
        db.Close();
    }
 internal RtreeR2Page insert(Storage storage, RectangleR2 r, object obj, int level)
 {
     Modify();
     if (--level != 0)
     {
         // not leaf page
         int i, mini = 0;
         double minIncr = Double.MaxValue;
         double minArea = Double.MaxValue;
         for (i = 0; i < n; i++)
         {
             double area = b[i].Area();
             double incr = RectangleR2.JoinArea(b[i], r) - area;
             if (incr < minIncr)
             {
                 minIncr = incr;
                 minArea = area;
                 mini = i;
             }
             else if (incr == minIncr && area < minArea)
             {
                 minArea = area;
                 mini = i;
             }
         }
         RtreeR2Page p = (RtreeR2Page)branch[mini];
         RtreeR2Page q = p.insert(storage, r, obj, level);
         if (q == null)
         {
             // child was not split
             b[mini].Join(r);
             return null;
         }
         else
         {
             // child was split
             setBranch(mini, p.cover(),  p);
             return addBranch(storage, q.cover(), q);
         }
     }
     else
     {
         return addBranch(storage, new RectangleR2(r), obj);
     }
 }
 /// <summary>
 /// Join two rectangles. This rectangle is updates to contain cover of this and specified rectangle.
 /// </summary>
 /// <param name="r">rectangle to be joined with this rectangle
 /// </param>
 public void Join(RectangleR2 r) 
 { 
     if (_left > r._left) 
     { 
         _left = r._left;
     }
     if (_right < r._right) 
     { 
         _right = r._right;
     }
     if (_top > r._top) 
     { 
         _top = r._top;
     }
     if (_bottom < r._bottom) 
     { 
         _bottom = r._bottom;
     }
 }
 /// <summary>
 /// Create copy of the rectangle
 /// </summary>
 public RectangleR2(RectangleR2 r) 
 {
     this._top = r._top;
     this._left = r._left;
     this._bottom = r._bottom;
     this._right = r._right;
 }
 internal int remove(RectangleR2 r, object obj, int level, ArrayList reinsertList)
 {
     if (--level != 0)
     {
         for (int i = 0; i < n; i++)
         {
             if (r.Intersects(b[i]))
             {
                 RtreeR2Page pg = (RtreeR2Page)branch[i];
                 int reinsertLevel = pg.remove(r, obj, level, reinsertList);
                 if (reinsertLevel >= 0)
                 {
                     if (pg.n >= minFill)
                     {
                         setBranch(i, pg.cover(), pg);
                         Modify();
                     }
                     else
                     {
                         // not enough entries in child
                         reinsertList.Add(pg);
                         reinsertLevel = level - 1;
                         removeBranch(i);
                     }
                     return reinsertLevel;
                 }
             }
         }
     }
     else
     {
         for (int i = 0; i < n; i++)
         {
             if (branch.ContainsElement(i, obj))
             {
                 removeBranch(i);
                 return 0;
             }
         }
     }
     return -1;
 }
 internal RectangleR2 cover()
 {
     RectangleR2 r = new RectangleR2(b[0]);
     for (int i = 1; i < n; i++)
     {
         r.Join(b[i]);
     }
     return r;
 }
 private bool MatchProperty(NameVal prop, Thing thing) 
 {
     switch (prop.name) 
     {
         case Symbols.Point:
             if (prop.val is NameVal[]) 
             {
                 NameVal[] coord = (NameVal[])prop.val;
                 if (coord.Length == 2) 
                 { 
                     double x = (double)coord[0].val;
                     double y = (double)coord[1].val;
                     RectangleR2 r = new RectangleR2(x, y, x, y);
                     foreach (Thing t in root.spatialIndex.Overlaps(r)) 
                     {
                         if (t == thing) 
                         {
                             return true;   
                         }
                     }
                     return false;
                 }
             }
             break;
         
         case Symbols.Rectangle:
             if (prop.val is NameVal[]) 
             {
                 NameVal[] coord = (NameVal[])prop.val;
                 if (coord.Length == 4) 
                 { 
                     RectangleR2 r = new RectangleR2((double)coord[0].val,
                             (double)coord[1].val,
                             (double)coord[2].val,
                             (double)coord[3].val);
                     foreach (Thing t in root.spatialIndex.Overlaps(r)) 
                     {
                         if (t == thing) 
                         {
                             return true;   
                         }
                     }
                     return false;
                 }
             }
             break;
         
         case Symbols.Keyword:
             if (prop.val is string) 
             {
                 Hashtable keywords = new Hashtable();
                 foreach (PropVal pv in thing.props) 
                 { 
                     object val = pv.val;
                     if (val is string) 
                     {
                         foreach (string keyword in ((string)val).ToLower().Split(keywordSeparators)) 
                         {
                             if (keyword.Length > 0 && !keywordStopList.ContainsKey(keyword)) 
                             {
                                 keywords[keyword] = this;
                             }
                         }
                     }
                 }
                 foreach (string keyword in ((string)prop.val).ToLower().Split(keywordSeparators)) 
                 {
                     if (keyword.Length > 0 && !keywordStopList.ContainsKey(keyword) && !keywords.ContainsKey(keyword)) 
                     {
                         return false;
                     }
                 }
                 return true;
             }
             break;                 
     }
                                                       
 NextItem:
     foreach (object val in thing[prop.name]) 
     {
         object pattern = prop.val;
         if (val is string && pattern is string) 
         { 
             if (MatchString((string)val, (string)pattern)) 
             {
                 return true;
             }
         } 
         else if (pattern is NameVal) 
         { 
             if (FollowReference((NameVal)pattern, val as VersionHistory)) 
             { 
                 return true;
             }
         }
         else if (pattern is NameVal[]) 
         { 
             foreach (NameVal p in (NameVal[])prop.val) 
             {
                 if (!FollowReference(p, val as VersionHistory))
                 {
                     goto NextItem;
                 }
             }
             return true;
         } 
         else if (pattern is Range && val is IComparable) 
         {
             try 
             {
                 Range range = (Range)pattern;
                 IComparable cmp = (IComparable)val;
                 return cmp.CompareTo(range.from) >= (range.fromInclusive ? 0 : 1) &&
                     cmp.CompareTo(range.till) <= (range.tillInclusive ? 0 : -1);
             } 
             catch (ArgumentException) {}
         }
         else if (pattern != null && pattern.Equals(val))
         {
             return true;
         }
     }
     return false;
 }
 /// <summary>
 /// Checks if this rectangle contains the specified rectangle
 /// </summary>
 public bool Contains(RectangleR2 r) 
 { 
     return _left <= r._left && _top <= r._top && _right >= r._right && _bottom >= r._bottom;
 }
 RtreeR2Page addBranch(Storage storage, RectangleR2 r, object obj)
 {
     if (n < card)
     {
         setBranch(n++, r, obj);
         return null;
     }
     else
     {
         return splitPage(storage, r, obj);
     }
 }
 void setBranch(int i, RectangleR2 r, object obj)
 {
     b[i] = r;
     branch[i] = obj;
 }
 internal void find(RectangleR2 r, ArrayList result, int level)
 {
     if (--level != 0)
     { /* this is an internal node in the tree */
         for (int i = 0; i < n; i++)
         {
             if (r.Intersects(b[i]))
             {
                 ((RtreeR2Page)branch[i]).find(r, result, level);
             }
         }
     }
     else
     { /* this is a leaf node */
         for (int i = 0; i < n; i++)
         {
             if (r.Intersects(b[i]))
             {
                 result.Add(branch[i]);
             }
         }
     }
 }
 public  void Unpack(ObjectReader reader)
 {
     rect = new RectangleR2(reader.ReadDouble(), reader.ReadDouble(), reader.ReadDouble(), reader.ReadDouble());
     body = reader.ReadBytes(reader.ReadInt32());
 }
 /// <summary>
 ///  Non destructive join of two rectangles. 
 /// </summary>
 /// <param name="a">first joined rectangle
 /// </param>
 /// <param name="b">second joined rectangle
 /// </param>
 /// <returns>rectangle containing cover of these two rectangles
 /// </returns>
 public static RectangleR2 Join(RectangleR2 a, RectangleR2 b) 
 {
     RectangleR2 r = new RectangleR2(a);
     r.Join(b);
     return r;
 }
        /// <summary>Get iterator through object matching specified search parameters</summary>
        /// <param name="type">String representing type of the object (direct or indirect - IsInstanceOf
        /// method will be used to check if object belongs to the specified type). It may be null, 
        /// in this case type criteria is skipped.</param>
        /// <param name="uri">Object URI pattern. It may be null, in this case URI is not inspected.</param>
        /// <param name="patterns">array of name:value pairs specifying search condition for object properties</param>
        /// <param name="kind">search kind used to select inspected versions</param>
        /// <param name="timestamp">timestamp used to select versions, if kind is SearchKind.LatestVersion
        /// or SearchKind.AllVersions this parameter is ignored</param>
        /// <returns>Enumerator through object meet search criteria.</returns>
        public IEnumerable Search(string type, string uri, NameVal[] patterns, SearchKind kind, DateTime timestamp) 
        {
            VersionHistory typeVh = null;
            root.SharedLock();
            try 
            {
                if (type != null) 
                { 
                    typeVh = GetObject(type);
                    if (typeVh == null) 
                    { 
                        return new object[0]; // empty selection
                    }
                }
                if (uri != null) 
                {
                    int wc = uri.IndexOf('*');
                    if (wc < 0) 
                    { 
                        return new SearchResult(root, typeVh, null, patterns, kind, timestamp, root.prefixUriIndex.GetEnumerator(uri, uri));
                    } 
                    else if (wc > 0) 
                    { 
                        String prefix = uri.Substring(0, wc);
                        return new SearchResult(root, typeVh, uri, patterns, kind, timestamp, root.prefixUriIndex.GetEnumerator(prefix));
                    } 
                    else if ((wc = uri.LastIndexOf('*')) < uri.Length-1) 
                    {
                        String suffix = ReverseString(uri.Substring(wc+1, uri.Length-wc-1));
                        return new SearchResult(root, typeVh, uri, patterns, kind, timestamp, root.suffixUriIndex.GetEnumerator(suffix));
                    }
                }
                if (patterns.Length > 0) 
                { 
                    NameVal prop = patterns[0];
                    object val = prop.val;
                    NameVal[] restOfPatterns = SubArray(patterns);

                    switch (prop.name) 
                    {
                        case Symbols.Timestamp: 
                  
                            if (val is Range) 
                            { 
                                Range range = (Range)val;
                                if (range.from is DateTime) 
                                {
                                    Key fromKey = new Key((DateTime)range.from, range.fromInclusive);
                                    Key tillKey = new Key((DateTime)range.till, range.tillInclusive);
                                    return new SearchResult(root, typeVh, uri, restOfPatterns, kind, timestamp, 
                                        root.timeIndex.GetEnumerator(fromKey, tillKey));
                                    
                                }
                            } 
                            else if (val is DateTime) 
                            {
                                Key key = new Key((DateTime)val);
                                return new SearchResult(root, typeVh, uri, restOfPatterns, kind, timestamp, 
                                    root.timeIndex.GetEnumerator(key, key));                            
                            } 
                            return new object[0]; // empty selection
                        case Symbols.Rectangle:
                            if (val is NameVal[]) 
                            {
                                NameVal[] coord = (NameVal[])val;
                                if (coord.Length == 4) 
                                {
                                    RectangleR2 r = new RectangleR2((double)coord[0].val, 
                                        (double)coord[1].val, 
                                        (double)coord[2].val, 
                                        (double)coord[3].val);
                                    return new SearchResult(root, typeVh, uri, restOfPatterns, kind, timestamp, 
                                        root.spatialIndex.Overlaps(r).GetEnumerator());
                                }
                            }
                            break;
                        case Symbols.Point:
                            if (val is NameVal[]) 
                            {
                                NameVal[] coord = (NameVal[])val;
                                if (coord.Length == 2) 
                                {
                                    double x = (double)coord[0].val;
                                    double y = (double)coord[1].val;
                                    RectangleR2 r = new RectangleR2(x, y, x, y);
                                    return new SearchResult(root, typeVh, uri, restOfPatterns, kind, timestamp, 
                                        root.spatialIndex.Overlaps(r).GetEnumerator());
                                }
                            }
                            break;
                        case Symbols.Keyword:
                            if (val is string) 
                            {
                                ArrayList keywords = new ArrayList();
                                foreach (string keyword in ((string)val).ToLower().Split(keywordSeparators)) 
                                {
                                    if (keyword.Length > 0 && !keywordStopList.ContainsKey(keyword))
                                    {
                                        keywords.Add(keyword);
                                    }
                                }
                                IEnumerator[] occurences = new IEnumerator[keywords.Count];
                                for (int i = 0; i < occurences.Length; i++) 
                                { 
                                    Key key = new Key((string)keywords[i]);
                                    occurences[i] = root.inverseIndex.GetEnumerator(key, key);
                                }
                                return new SearchResult(root, typeVh, uri, restOfPatterns, kind, timestamp, db.Merge(occurences));
                            }
                            break;
                    }

                    PropDef def = (PropDef)root.propDefIndex[prop.name];
                    if (def == null) 
                    { 
                        return new object[0]; // empty selection
                    }
                    if (val is Range) 
                    { 
                        Range range = (Range)val;
                        if (range.from is double) 
                        {
                            Key fromKey = new Key(new object[]{def, range.from}, range.fromInclusive);
                            Key tillKey = new Key(new object[]{def, range.till}, range.tillInclusive);
                            return new SearchResult(root, typeVh, uri, restOfPatterns, kind, timestamp, 
                                root.numPropIndex.GetEnumerator(fromKey, tillKey));
                        } 
                        else if (range.from is DateTime) 
                        {
                            Key fromKey = new Key(new object[]{def, range.from}, range.fromInclusive);
                            Key tillKey = new Key(new object[]{def, range.till}, range.tillInclusive);
                            return new SearchResult(root, typeVh, uri, restOfPatterns, kind, timestamp, 
                                root.timePropIndex.GetEnumerator(fromKey, tillKey));
                        } 
                        else 
                        { 
                            Key fromKey = new Key(new object[]{def, range.from}, range.fromInclusive);
                            Key tillKey = new Key(new object[]{def, range.till}, range.tillInclusive);
                            return new SearchResult(root, typeVh, uri, restOfPatterns, kind, timestamp, 
                                root.strPropIndex.GetEnumerator(fromKey, tillKey));
                        }
                    } 
                    else if (val is string) 
                    {
                        string str = (string)val;
                        int wc = str.IndexOf('*');
                        if (wc < 0) 
                        { 
                            Key key = new Key(new object[]{def, str});
                            return new SearchResult(root, typeVh, uri, restOfPatterns, kind, timestamp, 
                                root.strPropIndex.GetEnumerator(key, key));
                                
                        } 
                        else if (wc > 0) 
                        { 
                            string prefix = str.Substring(0, wc);
                            Key fromKey = new Key(new object[]{def, prefix});
                            Key tillKey = new Key(new object[]{def, prefix + Char.MaxValue}, false);                        
                            return new SearchResult(root, typeVh, uri, wc == str.Length-1 ? restOfPatterns : patterns, kind, timestamp, 
                                root.strPropIndex.GetEnumerator(fromKey, tillKey));
                        }                             
                    }
                    else if (val is double)
                    {
                        Key key = new Key(new object[]{def, val});
                        return new SearchResult(root, typeVh, uri, restOfPatterns, kind, timestamp, 
                            root.numPropIndex.GetEnumerator(key, key));
                    } 
                    else if (val is DateTime) 
                    {
                        Key key = new Key(new object[]{def, val});
                        return new SearchResult(root, typeVh, uri, restOfPatterns, kind, timestamp, 
                            root.timePropIndex.GetEnumerator(key, key));
                    }
                    else if (val is NameVal) 
                    { 
                        IEnumerable iterator = SearchReferenceProperty(typeVh, uri, patterns, kind, timestamp, (NameVal)val, false, def, new ArrayList());
                        if (iterator != null) 
                        {
                            return iterator;
                        }
                    }
                    else if (val is NameVal[]) 
                    { 
                        NameVal[] props = (NameVal[])val;
                        if (props.Length > 0) 
                        {
                            IEnumerable iterator = SearchReferenceProperty(typeVh, uri, patterns, kind, timestamp, props[0], props.Length > 1, def, new ArrayList());
                            if (iterator != null) 
                            {
                                return iterator;
                            }
                        }
                    }
                    
                }
                if (kind == SearchKind.LatestVersion) 
                { 
                    return new SearchResult(root, typeVh, uri, patterns, kind, timestamp, root.latest.GetEnumerator());   
                }
                return new SearchResult(root, typeVh, uri, patterns, kind, timestamp, root.timeIndex.GetEnumerator());           
            } 
            finally 
            { 
                root.Unlock();
            }
        }
 /// <summary>
 /// Checks if this rectangle intersects with specified rectangle
 /// </summary>
 public bool Intersects(RectangleR2 r) 
 { 
     return _left <= r._right && _top <= r._bottom && _right >= r._left && _bottom >= r._top;
 }
        private IEnumerable SearchReferenceProperty(VersionHistory type, string uri, NameVal[] patterns, SearchKind kind, DateTime timestamp, NameVal prop, bool compound, PropDef def, ArrayList refs)
        {
            refs.Add(def);

            NameVal[] restOfPatterns = compound ? patterns : SubArray(patterns);

            object val = prop.val;
            switch (prop.name) 
            {
                case Symbols.Timestamp: 
            
                    if (val is Range) 
                    { 
                        Range range = (Range)val;
                        if (range.from is DateTime) 
                        {
                            Key fromKey = new Key((DateTime)range.from, range.fromInclusive);
                            Key tillKey = new Key((DateTime)range.till, range.tillInclusive);
                            return new SearchResult(root, type, uri, restOfPatterns, kind, timestamp, 
                                new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)), 
                                root.timeIndex.GetEnumerator(fromKey, tillKey), kind, timestamp));
                        }
                    } 
                    else if (val is DateTime) 
                    {
                        Key key = new Key((DateTime)val);
                        return new SearchResult(root, type, uri, restOfPatterns, kind, timestamp, 
                            new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)), 
                            root.timeIndex.GetEnumerator(key, key), kind, timestamp));                            
                    } 
                    return new object[0]; // empty selection
                case Symbols.Rectangle:
                    if (val is NameVal[]) 
                    {
                        NameVal[] coord = (NameVal[])val;
                        if (coord.Length == 4) 
                        {
                            RectangleR2 r = new RectangleR2((double)coord[0].val, 
                                (double)coord[1].val, 
                                (double)coord[2].val, 
                                (double)coord[3].val);
                            return new SearchResult(root, type, uri, restOfPatterns, kind, timestamp, 
                                new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)), 
                                root.spatialIndex.Overlaps(r).GetEnumerator(), kind, timestamp));
                        }
                    }
                    break;
                case Symbols.Point:
                    if (val is NameVal[]) 
                    {
                        NameVal[] coord = (NameVal[])val;
                        if (coord.Length == 2) 
                        {
                            double x = (double)coord[0].val;
                            double y = (double)coord[1].val;
                            RectangleR2 r = new RectangleR2(x, y, x, y);
                            return new SearchResult(root, type, uri, restOfPatterns, kind, timestamp, 
                                new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)), 
                                root.spatialIndex.Overlaps(r).GetEnumerator(), kind, timestamp));
                        }
                    }
                    break;
                case Symbols.Keyword:
                    if (val is string) 
                    {
                        ArrayList keywords = new ArrayList();
                        foreach (string keyword in ((string)val).ToLower().Split(keywordSeparators)) 
                        {
                            if (keyword.Length > 0 && !keywordStopList.ContainsKey(keyword))
                            {
                                keywords.Add(keyword);
                            }
                        }
                        IEnumerator[] occurences = new IEnumerator[keywords.Count];
                        for (int i = 0; i < occurences.Length; i++) 
                        { 
                            Key key = new Key((string)keywords[i]);
                            occurences[i] = root.inverseIndex.GetEnumerator(key, key);
                        }
                        return new SearchResult(root, type, uri, restOfPatterns, kind, timestamp, 
                            new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)), 
                            db.Merge(occurences), kind, timestamp));
                    }
                    break;
            }

            def = (PropDef)root.propDefIndex[prop.name];
            if (def == null) 
            { 
                return new object[0]; // empty selection
            }
            if (val is Range) 
            { 
                Range range = (Range)val;
                if (range.from is double) 
                {
                    Key fromKey = new Key(new object[]{def, range.from}, range.fromInclusive);
                    Key tillKey = new Key(new object[]{def, range.till}, range.tillInclusive);
                    return new SearchResult(root, type, uri, restOfPatterns, kind, timestamp, 
                        new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)), 
                        root.numPropIndex.GetEnumerator(fromKey, tillKey), kind, timestamp));
                } 
                else if (range.from is DateTime) 
                {
                    Key fromKey = new Key(new object[]{def, range.from}, range.fromInclusive);
                    Key tillKey = new Key(new object[]{def, range.till}, range.tillInclusive);
                    return new SearchResult(root, type, uri, restOfPatterns, kind, timestamp, 
                        new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)), 
                        root.timePropIndex.GetEnumerator(fromKey, tillKey), kind, timestamp));
                } 
                else 
                { 
                    Key fromKey = new Key(new object[]{def, range.from}, range.fromInclusive);
                    Key tillKey = new Key(new object[]{def, range.till}, range.tillInclusive);
                    return new SearchResult(root, type, uri, restOfPatterns, kind, timestamp, 
                        new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)), 
                        root.strPropIndex.GetEnumerator(fromKey, tillKey), kind, timestamp));
                }
            } 
            if (val is string) 
            {
                string str = (string)prop.val;
                int wc = str.IndexOf('*');
                if (wc < 0) 
                { 
                    Key key = new Key(new object[]{def, str});
                    return new SearchResult(root, type, uri, restOfPatterns, kind, timestamp, 
                        new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)), 
                        root.strPropIndex.GetEnumerator(key, key), kind, timestamp));
                } 
                else if (wc > 0) 
                { 
                    string prefix = str.Substring(0, wc);
                    Key fromKey = new Key(new object[]{def, prefix});
                    Key tillKey = new Key(new object[]{def, prefix + Char.MaxValue}, false);                        
                    return new SearchResult(root, type, uri, wc == str.Length-1 ? restOfPatterns : patterns, kind, timestamp, 
                        new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)), 
                        root.strPropIndex.GetEnumerator(fromKey, tillKey), kind, timestamp));
                } 
            } 
            else if (val is double) 
            {
                Key key = new Key(new object[]{def, val});
                return new SearchResult(root, type, uri, restOfPatterns, kind, timestamp, 
                    new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)), 
                    root.numPropIndex.GetEnumerator(key, key), kind, timestamp));
            } 
            else if (val is DateTime) 
            {
                Key key = new Key(new object[]{def, (DateTime)val});
                return new SearchResult(root, type, uri, restOfPatterns, kind, timestamp, 
                    new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)), 
                    root.timePropIndex.GetEnumerator(key, key), kind, timestamp));
            } 
            else if (val is NameVal) 
            {
                return SearchReferenceProperty(type, uri, patterns, kind, timestamp, (NameVal)val, compound, def, refs);
            }
            else if (val is NameVal[]) 
            {
                NameVal[] props = (NameVal[])val;
                if (props.Length > 0) 
                {
                    return SearchReferenceProperty(type, uri, patterns, kind, timestamp, props[0], true, def, refs);
                }
            }
            return null;
        }
        RtreeR2Page splitPage(Storage storage, RectangleR2 r, object obj)
        {
            int i, j, seed0 = 0, seed1 = 0;
            double[] rectArea = new double[card+1];
            double   waste;
            double   worstWaste = Double.MinValue;
            //
            // As the seeds for the two groups, find two rectangles which waste
            // the most area if covered by a single rectangle.
            //
            rectArea[0] = r.Area();
            for (i = 0; i < card; i++)
            {
                rectArea[i+1] = b[i].Area();
            }
            RectangleR2 bp = r;
            for (i = 0; i < card; i++)
            {
                for (j = i+1; j <= card; j++)
                {
                    waste = RectangleR2.JoinArea(bp, b[j-1]) - rectArea[i] - rectArea[j];
                    if (waste > worstWaste)
                    {
                        worstWaste = waste;
                        seed0 = i;
                        seed1 = j;
                    }
                }
                bp = b[i];
            }
            byte[] taken = new byte[card];
            RectangleR2 group0, group1;
            double      groupArea0, groupArea1;
            int         groupCard0, groupCard1;
            RtreeR2Page pg;

            taken[seed1-1] = 2;
            group1 = new RectangleR2(b[seed1-1]);

            if (seed0 == 0)
            {
                group0 = new RectangleR2(r);
                pg = new RtreeR2Page(storage, obj, r);
            }
            else
            {
                group0 = new RectangleR2(b[seed0-1]);
                pg = new RtreeR2Page(storage, branch.GetRaw(seed0-1), group0);
                setBranch(seed0-1, r, obj);
            }
            groupCard0 = groupCard1 = 1;
            groupArea0 = rectArea[seed0];
            groupArea1 = rectArea[seed1];
            //
            // Split remaining rectangles between two groups.
            // The one chosen is the one with the greatest difference in area
            // expansion depending on which group - the rect most strongly
            // attracted to one group and repelled from the other.
            //
            while (groupCard0 + groupCard1 < card + 1
                && groupCard0 < card + 1 - minFill
                && groupCard1 < card + 1 - minFill)
            {
                int betterGroup = -1, chosen = -1;
                double biggestDiff = -1;
                for (i = 0; i < card; i++)
                {
                    if (taken[i] == 0)
                    {
                        double diff = (RectangleR2.JoinArea(group0, b[i]) - groupArea0)
                            - (RectangleR2.JoinArea(group1, b[i]) - groupArea1);
                        if (diff > biggestDiff || -diff > biggestDiff)
                        {
                            chosen = i;
                            if (diff < 0)
                            {
                                betterGroup = 0;
                                biggestDiff = -diff;
                            }
                            else
                            {
                                betterGroup = 1;
                                biggestDiff = diff;
                            }
                        }
                    }
                }
                Debug.Assert(chosen >= 0);
                if (betterGroup == 0)
                {
                    group0.Join(b[chosen]);
                    groupArea0 = group0.Area();
                    taken[chosen] = 1;
                    pg.setBranch(groupCard0++, b[chosen], branch.GetRaw(chosen));
                }
                else
                {
                    groupCard1 += 1;
                    group1.Join(b[chosen]);
                    groupArea1 = group1.Area();
                    taken[chosen] = 2;
                }
            }
            //
            // If one group gets too full, then remaining rectangle are
            // split between two groups in such way to balance cards of two groups.
            //
            if (groupCard0 + groupCard1 < card + 1)
            {
                for (i = 0; i < card; i++)
                {
                    if (taken[i] == 0)
                    {
                        if (groupCard0 >= groupCard1)
                        {
                            taken[i] = 2;
                            groupCard1 += 1;
                        }
                        else
                        {
                            taken[i] = 1;
                            pg.setBranch(groupCard0++, b[i], branch.GetRaw(i));
                        }
                    }
                }
            }
            pg.n = groupCard0;
            n = groupCard1;
            for (i = 0, j = 0; i < groupCard1; j++)
            {
                if (taken[j] == 2)
                {
                    setBranch(i++, b[j], branch.GetRaw(j));
                }
            }
            // truncate rest of link
            branch.Length = groupCard1;
            branch.Length = card;
            return pg;
        }
 private Thing CreateObject(Thing type, VersionHistory vh, NameVal[] props) 
 {
     Thing thing = new Thing();
     thing.vh = vh;
     thing.type = type;
     thing.timestamp = DateTime.Now;
     thing.props = new PropVal[props.Length];
     for (int i = 0; i < props.Length; i++) 
     { 
         NameVal prop = props[i];
         PropDef def = (PropDef)root.propDefIndex[prop.name];
         if (def == null) 
         {
             def = new PropDef();
             def.name = prop.name;
             root.propDefIndex.Put(def);
         }
         object val = prop.val;
         PropVal pv = new PropVal(def, val);
         Key key = new Key(new object[]{def, val});
         if (val is string) 
         { 
             root.strPropIndex.Put(key, thing);
             foreach (string keyword in ((string)val).ToLower().Split(keywordSeparators)) 
             {
                 if (keyword.Length > 0 && !keywordStopList.ContainsKey(keyword)) 
                 {
                     root.inverseIndex.Put(keyword, thing);
                 }
             }
         } 
         else if (val is double) 
         { 
             root.numPropIndex.Put(key, thing);
         } 
         else if (val is DateTime) 
         { 
             root.timePropIndex.Put(key, thing);
         } 
         else if (val is VersionHistory || val == null) 
         { 
             root.refPropIndex.Put(key, thing);
             if (prop.name == Symbols.Rectangle) 
             {
                 PropVal[] coord = ((VersionHistory)val).Latest.props;
                 RectangleR2 r = new RectangleR2((double)coord[0].val, 
                     (double)coord[1].val, 
                     (double)coord[2].val, 
                     (double)coord[3].val);
                 root.spatialIndex.Put(r, thing);   
             }
             else if (prop.name == Symbols.Point) 
             {
                 PropVal[] coord = ((VersionHistory)val).Latest.props;
                 double x = (double)coord[0].val;
                 double y = (double)coord[1].val;
                 RectangleR2 r = new RectangleR2(x, y, x, y);
                 root.spatialIndex.Put(r, thing);   
             }
         } 
         else 
         { 
             throw new InvalidOperationException("Invalid propery value type " + prop.val.GetType());
         }
         thing.props[i] = pv;                  
     }
     thing.Modify();
     vh.versions.Add(thing);
     root.timeIndex.Put(thing);
     root.latest.Add(thing);
     return thing;
 }