internal void getByteArray(Page pg, int i) 
 { 
     int len = BtreePage.getKeyStrSize(pg, i);
     int offs = BtreePage.firstKeyOffs + BtreePage.getKeyStrOffs(pg, i);
     byte[] bval = new byte[len];
     Array.Copy(pg.data, offs, bval, 0, len);
     key = new Key(bval);
 }
 internal void  getStr(Page pg, int i)
 {
     int len = BtreePage.getKeyStrSize(pg, i);
     int offs = BtreePage.firstKeyOffs + BtreePage.getKeyStrOffs(pg, i);
     char[] sval = new char[len];
     for (int j = 0; j < len; j++)
     {
         sval[j] = (char) Bytes.unpack2(pg.data, offs);
         offs += 2;
     }
     key = new Key(sval);
 }
 /// <summary>
 /// Remove all associations with this name
 /// </summary>
 /// <param name="item">source item</param>
 /// <param name="name">attribute name (verb in terms of associative database model)</param>
 /// <returns>number of removed associations</returns>
 public int Unlink(Item item, String name)
 {
     checkIfActive();
     int id;
     if (!db.name2id.TryGetValue(name, out id))
     {
         return 0;
     }
     int nFields = item.fieldIds.Length;
     int nStrings = item.stringFields.Length;
     int nNumbers = item.numericFields.Length;
     int l, r;
     for (l = 0; l < nFields && item.fieldIds[l] != id; l++) ;
     for (r = l; r < nFields && item.fieldIds[r] == id; r++) ;
     if (l < r)
     {
         Index index = (Index)db.storage.GetObjectByOID(id);
         if (l < nStrings)
         {
             for (int i = l; i < r; i++)
             {
                 index.Remove(new Key(item.stringFields[i].ToLower()), item);
             }
             item.stringFields = Arrays.Remove(item.stringFields, l, r - l);
         }
         else if (l < nStrings + nNumbers)
         {
             for (int i = l; i < r; i++)
             {
                 index.Remove(new Key(item.numericFields[i - nStrings]), item);
             }
             item.numericFields = Arrays.Remove(item.numericFields, l - nStrings, r - l);
         }
         else
         {
             checkIfNotInverseLink(name);
             int j = l - nStrings - nNumbers;
             for (int i = l; i < r; i++)
             {
                 removeLink(item, (Item)item.relations[j], id);
                 item.relations.Remove(j);
             }
         }
         item.fieldIds = Arrays.Remove(item.fieldIds, l, r - l);
         modify(item);
         return r - l;
     }
     checkIfNotInverseLink(name);
     Key key = new Key(((long)item.Oid << 32) | (uint)id);
     int nUnlinked = 0;
     foreach (Item target in db.root.relations.Range(key, key))
     {
         db.root.relations.Remove(key, target);
         removeLink(item, target, id);
         nUnlinked += 1;
     }
     item.fieldNames = null;
     return nUnlinked;
 }
        internal void  extract(Page pg, int offs, ClassDescriptor.FieldType type)
        {
            byte[] data = pg.data;
			
            switch (type)
            {
                case ClassDescriptor.FieldType.tpBoolean: 
                    key = new Key(data[offs] != 0);
                    break;
				
                case ClassDescriptor.FieldType.tpSByte: 
                    key = new Key((sbyte)data[offs]);
                    break;
                case ClassDescriptor.FieldType.tpByte: 
                    key = new Key(data[offs]);
                    break;
				
                case ClassDescriptor.FieldType.tpShort: 
                    key = new Key(Bytes.unpack2(data, offs));
                    break;
                case ClassDescriptor.FieldType.tpUShort: 
                    key = new Key((ushort)Bytes.unpack2(data, offs));
                    break;
				
				
                case ClassDescriptor.FieldType.tpChar: 
                    key = new Key((char) Bytes.unpack2(data, offs));
                    break;
				
                case ClassDescriptor.FieldType.tpInt: 
                    key = new Key(Bytes.unpack4(data, offs));
                    break;
                case ClassDescriptor.FieldType.tpEnum: 
                case ClassDescriptor.FieldType.tpUInt: 
                case ClassDescriptor.FieldType.tpObject: 
                case ClassDescriptor.FieldType.tpOid: 
                    key = new Key((uint)Bytes.unpack4(data, offs));
                    break;
				
                case ClassDescriptor.FieldType.tpLong: 
                    key = new Key(Bytes.unpack8(data, offs));
                    break;
                case ClassDescriptor.FieldType.tpDate: 
                case ClassDescriptor.FieldType.tpULong: 
                    key = new Key((ulong)Bytes.unpack8(data, offs));
                    break;
				
                case ClassDescriptor.FieldType.tpFloat: 
                    key = new Key(Bytes.unpackF4(data, offs));
                    break;
				
                case ClassDescriptor.FieldType.tpDouble: 
                    key = new Key(Bytes.unpackF8(data, offs));
                    break;

                case ClassDescriptor.FieldType.tpGuid:
                    key = new Key(Bytes.unpackGuid(data, offs));
                    break;
                
                case ClassDescriptor.FieldType.tpDecimal:
                    key = new Key(Bytes.unpackDecimal(data, offs));
                    break;

                default: 
                    Debug.Assert(false, "Invalid type");
                    break;
				
            }
        }
 internal BtreeKey(Key key, int oid)
 {
     this.key = key;
     this.oid = oid;
 }
 internal static bool find(StorageImpl db, int pageId, Key firstKey, Key lastKey, Btree tree, int height, ArrayList result)
 {
     Page pg = db.getPage(pageId);
     int l = 0, n = getnItems(pg), r = n;
     int oid;
     height -= 1;
     try
     {
         if (tree.FieldType == ClassDescriptor.FieldType.tpString)
         {
             if (firstKey != null)
             {
                 while (l < r)
                 {
                     int i = (l + r) >> 1;
                     if (compareStr(firstKey, pg, i) >= firstKey.inclusion)
                     {
                         l = i + 1;
                     }
                     else
                     {
                         r = i;
                     }
                 }
                 Debug.Assert(r == l);
             }
             if (lastKey != null)
             {
                 if (height == 0)
                 {
                     while (l < n)
                     {
                         if (- compareStr(lastKey, pg, l) >= lastKey.inclusion)
                         {
                             return false;
                         }
                         oid = getKeyStrOid(pg, l);
                         result.Add(db.lookupObject(oid, null));
                         l += 1;
                     }
                 }
                 else
                 {
                     do 
                     {
                         if (!find(db, getKeyStrOid(pg, l), firstKey, lastKey, tree, height, result))
                         {
                             return false;
                         }
                         if (l == n)
                         {
                             return true;
                         }
                     }
                     while (compareStr(lastKey, pg, l++) >= 0);
                     return false;
                 }
             }
             else
             {
                 if (height == 0)
                 {
                     while (l < n)
                     {
                         oid = getKeyStrOid(pg, l);
                         result.Add(db.lookupObject(oid, null));
                         l += 1;
                     }
                 }
                 else
                 {
                     do 
                     {
                         if (!find(db, getKeyStrOid(pg, l), firstKey, lastKey, tree, height, result))
                         {
                             return false;
                         }
                     }
                     while (++l <= n);
                 }
             }
         }
         else if (tree.FieldType == ClassDescriptor.FieldType.tpArrayOfByte)
         {
             if (firstKey != null)
             {
                 while (l < r)
                 {
                     int i = (l + r) >> 1;
                     if (tree.compareByteArrays(firstKey, pg, i) >= firstKey.inclusion)
                     {
                         l = i + 1;
                     }
                     else
                     {
                         r = i;
                     }
                 }
                 Debug.Assert(r == l);
             }
             if (lastKey != null)
             {
                 if (height == 0)
                 {
                     while (l < n)
                     {
                         if (-tree.compareByteArrays(lastKey, pg, l) >= lastKey.inclusion)
                         {
                             return false;
                         }
                         oid = getKeyStrOid(pg, l);
                         result.Add(db.lookupObject(oid, null));
                         l += 1;
                     }
                 }
                 else
                 {
                     do 
                     {
                         if (!find(db, getKeyStrOid(pg, l), firstKey, lastKey, tree, height, result))
                         {
                             return false;
                         }
                         if (l == n)
                         {
                             return true;
                         }
                     }
                     while (tree.compareByteArrays(lastKey, pg, l++) >= 0);
                     return false;
                 }
             }
             else
             {
                 if (height == 0)
                 {
                     while (l < n)
                     {
                         oid = getKeyStrOid(pg, l);
                         result.Add(db.lookupObject(oid, null));
                         l += 1;
                     }
                 }
                 else
                 {
                     do 
                     {
                         if (!find(db, getKeyStrOid(pg, l), firstKey, lastKey, tree, height, result))
                         {
                             return false;
                         }
                     }
                     while (++l <= n);
                 }
             }
         }
         else
         {
             if (firstKey != null)
             {
                 while (l < r)
                 {
                     int i = (l + r) >> 1;
                     if (compare(firstKey, pg, i) >= firstKey.inclusion)
                     {
                         l = i + 1;
                     }
                     else
                     {
                         r = i;
                     }
                 }
                 Debug.Assert(r == l);
             }
             if (lastKey != null)
             {
                 if (height == 0)
                 {
                     while (l < n)
                     {
                         if (- compare(lastKey, pg, l) >= lastKey.inclusion)
                         {
                             return false;
                         }
                         oid = getReference(pg, maxItems - 1 - l);
                         result.Add(db.lookupObject(oid, null));
                         l += 1;
                     }
                     return true;
                 }
                 else
                 {
                     do 
                     {
                         if (!find(db, getReference(pg, maxItems - 1 - l), firstKey, lastKey, tree, height, result))
                         {
                             return false;
                         }
                         if (l == n)
                         {
                             return true;
                         }
                     }
                     while (compare(lastKey, pg, l++) >= 0);
                     return false;
                 }
             }
             if (height == 0)
             {
                 while (l < n)
                 {
                     oid = getReference(pg, maxItems - 1 - l);
                     result.Add(db.lookupObject(oid, null));
                     l += 1;
                 }
             }
             else
             {
                 do 
                 {
                     if (!find(db, getReference(pg, maxItems - 1 - l), firstKey, lastKey, tree, height, result))
                     {
                         return false;
                     }
                 }
                 while (++l <= n);
             }
         }
     }
     finally
     {
         db.pool.unfix(pg);
     }
     return true;
 }
        internal static int compare(Key key, Page pg, int i)
        {
            long i8;
            ulong u8;
            int i4;
            uint u4;
            float r4;
            double r8;
            switch (key.type)
            {
                case ClassDescriptor.FieldType.tpSByte: 
                    return (sbyte) key.ival - (sbyte)pg.data[BtreePage.firstKeyOffs + i];
                
                case ClassDescriptor.FieldType.tpBoolean: 
                case ClassDescriptor.FieldType.tpByte: 
                    return (byte)key.ival - pg.data[BtreePage.firstKeyOffs + i];
				
                case ClassDescriptor.FieldType.tpShort: 
                    return (short) key.ival - Bytes.unpack2(pg.data, BtreePage.firstKeyOffs + i * 2);
                case ClassDescriptor.FieldType.tpUShort: 
                    return (ushort) key.ival - (ushort)Bytes.unpack2(pg.data, BtreePage.firstKeyOffs + i * 2);
				
                case ClassDescriptor.FieldType.tpChar: 
                    return (char) key.ival - (char) Bytes.unpack2(pg.data, BtreePage.firstKeyOffs + i * 2);
				
                case ClassDescriptor.FieldType.tpObject: 
                case ClassDescriptor.FieldType.tpUInt: 
                case ClassDescriptor.FieldType.tpOid: 
                case ClassDescriptor.FieldType.tpEnum: 
                    u4 = (uint)Bytes.unpack4(pg.data, BtreePage.firstKeyOffs + i * 4);
                    return (uint)key.ival < u4 ? -1 : (uint)key.ival == u4 ? 0 : 1;

                case ClassDescriptor.FieldType.tpInt: 
                    i4 = Bytes.unpack4(pg.data, BtreePage.firstKeyOffs + i * 4);
                    return key.ival < i4 ? -1 : key.ival == i4 ? 0 : 1;
				
                case ClassDescriptor.FieldType.tpLong: 
                    i8 = Bytes.unpack8(pg.data, BtreePage.firstKeyOffs + i * 8);
                    return key.lval < i8 ? -1 : key.lval == i8 ? 0 : 1;

                case ClassDescriptor.FieldType.tpDate: 
                case ClassDescriptor.FieldType.tpULong: 
                    u8 = (ulong)Bytes.unpack8(pg.data, BtreePage.firstKeyOffs + i * 8);
                    return (ulong)key.lval < u8 ? -1 : (ulong)key.lval == u8 ? 0 : 1;
				
                case ClassDescriptor.FieldType.tpFloat: 
                    r4 = Bytes.unpackF4(pg.data, BtreePage.firstKeyOffs + i * 4);
                    return key.dval < r4 ? -1 : key.dval == r4 ? 0 : 1;
				
                case ClassDescriptor.FieldType.tpDouble: 
                    r8 = Bytes.unpackF8(pg.data, BtreePage.firstKeyOffs + i * 8);
                    return key.dval < r8 ? -1 : key.dval == r8 ? 0 : 1;

                case ClassDescriptor.FieldType.tpDecimal:
                   return key.dec.CompareTo(Bytes.unpackDecimal(pg.data, BtreePage.firstKeyOffs + i*16));

                case ClassDescriptor.FieldType.tpGuid:
                    return key.guid.CompareTo(Bytes.unpackGuid(pg.data, BtreePage.firstKeyOffs + i*16));
            }
            Debug.Assert(false, "Invalid type");
            return 0;
        }
 internal static int compareStr(Key key, Page pg, int i)
 {
     char[] chars = (char[])key.oval;
     int alen = chars.Length;
     int blen = BtreePage.getKeyStrSize(pg, i);
     int minlen = alen < blen?alen:blen;
     int offs = BtreePage.getKeyStrOffs(pg, i) + BtreePage.firstKeyOffs;
     byte[] b = pg.data;
     for (int j = 0; j < minlen; j++)
     {
         int diff = chars[j] - (char) Bytes.unpack2(b, offs);
         if (diff != 0)
         {
             return diff;
         }
         offs += 2;
     }
     return alen - blen;
 }
        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;
        }
 public bool MoveNext() 
 {
     while (true) 
     {
         while (pos < iterators.Length && !iterators[pos].MoveNext()) 
         {
             pos += 1;
         }
         if (pos == iterators.Length) 
         { 
             currThing = null;
             return false;
         } 
         Thing thing = (Thing)iterators[pos].Current;
         switch (kind) 
         {
             case SearchKind.LatestVersion:
                 if (!thing.IsLatest()) 
                 {
                     continue;
                 }
                 break;
             case SearchKind.LatestBefore:
                 if (thing.timestamp > timestamp) 
                 { 
                     continue;
                 }
                 break;
             case SearchKind.OldestAfter:
                 if (thing.timestamp < timestamp) 
                 { 
                     continue;
                 }
                 break;
         }
         if (pos == 0) 
         { 
             if (visited.ContainsKey(thing.Oid)) 
             {
                 continue;
             } 
             else 
             {
                 visited[thing.Oid] = true;
             }
             currThing = thing;
             return true;
         }
         pos -= 1;
         Key key = new Key(new object[]{defs[pos], thing.vh});
         iterators[pos] = root.refPropIndex.GetEnumerator(key, key);
     }
 }
        /// <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();
            }
        }
 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;
 }