/// <summary>
 /// Returns a new stack with the top element popped.
 /// </summary>
 /// <returns>The new stack.</returns>
 public override IPersistentStack pop()
 {
     if ( _cnt == 0 )
         throw new InvalidOperationException("Can't pop empty vector");
     if ( _cnt == 1)
         return (IPersistentStack)EMPTY.withMeta(meta());
     if ( _tail.Length > 1 )
     {
         object[] newTail = new object[_tail.Length-1];
         Array.Copy(_tail,newTail,newTail.Length);
         return new PersistentVector(meta(),_cnt-1,_shift,_root,newTail);
     }
     Box ptail = new Box(null);
     object[] newroot = popTail(_shift-5,_root,ptail);
     int newshift = _shift;
     if ( newroot == null )
         newroot = RT.EMPTY_OBJECT_ARRAY;
     if ( _shift > 5 && newroot.Length == 1 )
     {
         newroot = (Object[])newroot[0];
         newshift -= 5;
     }
     return new PersistentVector(meta(),_cnt-1,newshift,newroot,(object[])ptail.Val);
 }
 public INode assoc(int shift, int hash, object key, object val, Box addedLeaf)
 {
     if (_hash == hash)
     {
         int idx = findIndex(hash, key);
         if (idx != -1)
         {
             if (_leaves[idx].val() == val)
                 return this;
             LeafNode[] newLeaves1 = (LeafNode[])_leaves.Clone();
             // Note: do not set addedLeaf, since we are replacing
             newLeaves1[idx] = new LeafNode(hash, key, val);
             return new HashCollisionNode(hash, newLeaves1);
         }
         LeafNode[] newLeaves = new LeafNode[_leaves.Length + 1];
         Array.Copy(_leaves, 0, newLeaves, 0, _leaves.Length);
         addedLeaf.Val = newLeaves[_leaves.Length] = new LeafNode(hash, key, val);
         return new HashCollisionNode(hash, newLeaves);
     }
     return BitmapIndexedNode.create(shift, this, hash, key, val, addedLeaf);
 }
 public INode without(AtomicReference<Thread> edit, int shift, int hash, object key, Box removedLeaf)
 {
     int idx = Util.Mask(hash, shift);
     INode node = _array[idx];
     if (node == null)
         return this;
     INode n = node.without(edit, shift + 5, hash, key, removedLeaf);
     if (n == node)
         return this;
     if (n == null)
     {
         if (_count <= 8) // shrink
             return pack(edit, idx);
         ArrayNode editable = EditAndSet(edit, idx, n);
         editable._count--;
         return editable;
     }
     return EditAndSet(edit, idx, n);
 }
 public INode assoc(AtomicReference<Thread> edit, int shift, int hash, object key, object val, Box addedLeaf)
 {
     int bit = Bitpos(hash, shift);
     int idx = Index(bit);
     if ((_bitmap & bit) != 0)
     {
         object keyOrNull = _array[2 * idx];
         object valOrNode = _array[2 * idx + 1];
         if (keyOrNull == null)
         {
             INode n = ((INode)valOrNode).assoc(edit, shift + 5, hash, key, val, addedLeaf);
             if (n == valOrNode)
                 return this;
             return EditAndSet(edit, 2 * idx + 1, n);
         }
         if (Util.equiv(key, keyOrNull))
         {
             if (val == valOrNode)
                 return this;
             return EditAndSet(edit, 2 * idx + 1, val);
         }
         addedLeaf.Val = addedLeaf;
         return EditAndSet(edit,
             2*idx,null,
             2*idx+1,CreateNode(edit,shift+5,keyOrNull,valOrNode,hash,key,val));
     }
     else
     {int n = Util.BitCount(_bitmap);
         if ( n*2 < _array.Length )
         {
             addedLeaf.Val = addedLeaf;
             BitmapIndexedNode editable = EnsureEditable(edit);
             Array.Copy(editable._array,2*idx,editable._array,2*(idx+1),2*(n-idx));
             editable._array[2*idx] = key;
             editable._array[2*idx+1] = val;
             editable._bitmap |= bit;
             return editable;
         }
         if ( n >= 16 )
         {
             INode[] nodes = new INode[32];
             int jdx = Util.Mask(hash,shift);
             nodes[jdx] = EMPTY.assoc(edit,shift+5,hash,key,val,addedLeaf);
             int j=0;
             for ( int i=0; i<32; i++ )
                 if (((_bitmap>>i) & 1) != 0 )
                 {
                     if ( _array[j] == null )
                         nodes[i] = (INode)_array[j+1];
                     else
                         nodes[i] = EMPTY.assoc(edit,shift+5,Util.hash(_array[j]), _array[j], _array[j+1], addedLeaf);
                     j += 2;
                 }
             return new ArrayNode(edit,n+1,nodes);
         }
         else
         {
             object[] newArray = new object[2*(n+4)];
             Array.Copy(_array,0,newArray,0,2*idx);
             newArray[2*idx] = key;
             addedLeaf.Val = addedLeaf;
             newArray[2 * idx + 1] = val;
             Array.Copy(_array,2*idx,newArray,2*(idx+1),2*(n-idx));
             BitmapIndexedNode editable = EnsureEditable(edit);
             editable._array = newArray;
             editable._bitmap |= bit;
             return editable;
         }
     }
 }
 /// <summary>
 /// Add a new key/value pair.
 /// </summary>
 /// <param name="key">The key</param>
 /// <param name="val">The value</param>
 /// <returns>A new map with key+value added.</returns>
 /// <remarks>Overwrites an exising value for the <paramref name="key"/>, if present.</remarks>
 public override IPersistentMap assoc(object key, object val)
 {
     if (key == null)
     {
         if (_hasNull && val == _nullValue)
             return this;
         return new PersistentHashMap(meta(), _hasNull ? _count : _count + 1, _root, true, val);
     }
     Box addedLeaf = new Box(null);
     INode newroot = (_root == null ? BitmapIndexedNode.EMPTY : _root)
         .assoc(0, Util.Hash(key), key, val, addedLeaf);
     return newroot == _root
         ? this
         : new PersistentHashMap(meta(), addedLeaf.Val == null ? _count : _count + 1, newroot, _hasNull, _nullValue);
 }
 public INode assoc(int shift, int hash, object key, object val, Box addedLeaf)
 {
     int idx = Util.Mask(hash, shift);
     INode node = _array[idx];
     if (node == null)
         return new ArrayNode(null, _count + 1, CloneAndSet(_array, idx, BitmapIndexedNode.EMPTY.assoc(shift + 5, hash, key, val, addedLeaf)));
     INode n = node.assoc(shift + 5, hash, key, val, addedLeaf);
     if (n == node)
         return this;
     return new ArrayNode(null, _count, CloneAndSet(_array, idx, n));
 }
 /// <summary>
 /// Add a node for a key
 /// </summary>
 /// <param name="t"></param>
 /// <param name="key"></param>
 /// <param name="val"></param>
 /// <param name="found"></param>
 /// <returns></returns>
 Node Add(Node t, object key, object val, Box found)
 {
     if (t == null)
         return val == null
             ? new Red(key)
             : new RedVal(key, val);
     int c = DoCompare(key, t.Key);
     if (c == 0)
     {
         found.Val = t;
         return null;
     }
     Node ins = c < 0 ? Add(t.Left, key, val, found) : Add(t.Right, key, val, found);
     if (ins == null)
         return null;
     return c < 0
         ? t.AddLeft(ins)
         : t.AddRight(ins);
 }
 public INode assoc(AtomicReference<Thread> edit, int shift, int hash, Object key, Object val, Box addedLeaf)
 {
     if (hash == _hash)
     {
         int idx = FindIndex(key);
         if (idx != -1)
         {
             if (_array[idx + 1] == val)
                 return this;
             return EditAndSet(edit, idx + 1, val);
         }
         if (_array.Length > 2 * _count)
         {
             addedLeaf.Val = addedLeaf;
             HashCollisionNode editable = EditAndSet(edit, 2 * _count, key, 2 * _count + 1, val);
             editable._count++;
             return editable;
         }
         object[] newArray = new object[_array.Length + 2];
         Array.Copy(_array, 0, newArray, 0, _array.Length);
         newArray[_array.Length] = key;
         newArray[_array.Length + 1] = val;
         addedLeaf.Val = addedLeaf;
         return EnsureEditable(edit, _count + 1, newArray);
     }
     // nest it in a bitmap node
     return new BitmapIndexedNode(edit, Bitpos(_hash, shift), new object[] { null, this, null, null })
         .assoc(edit, shift, hash, key, val, addedLeaf);
 }
 /// <summary>
 /// Add a new key/value pair.
 /// </summary>
 /// <param name="key">The key</param>
 /// <param name="val">The value</param>
 /// <returns>A new map with key+value added.</returns>
 /// <remarks>Throws an exception if <paramref name="key"/> has a value already.</remarks>
 public override IPersistentMap assocEx(object key, object val)
 {
     Box found = new Box(null);
     Node t = Add(_tree, key, val, found);
     if (t == null)
         throw new Exception("Key already present");
     return new PersistentTreeMap(_comp, t.Blacken(), _count + 1, meta());
 }
 /// <summary>
 /// Remove a key entry.
 /// </summary>
 /// <param name="key">The key to remove</param>
 /// <returns>A new map with the key removed (or the same map if the key is not contained).</returns>
 public override IPersistentMap without(object key)
 {
     Box found = new Box(null);
     Node t = Remove(_tree, key, found);
     if (t == null)
     {
         if (found.Val == null)
             return this;
         return new PersistentTreeMap(meta(), _comp);
     }
     return new PersistentTreeMap(_comp, t.Blacken(), _count - 1, meta());
 }
        /// <summary>
        /// Add a new key/value pair.
        /// </summary>
        /// <param name="key">The key</param>
        /// <param name="val">The value</param>
        /// <returns>A new map with key+value added.</returns>
        /// <remarks>Overwrites an exising value for the <paramref name="key"/>, if present.</remarks>
        public override IPersistentMap assoc(object key, object val)
        {
            Box found = new Box(null);
            Node t = Add(_tree, key, val, found);
            if (t == null)
            {
                Node foundNode = (Node)found.Val;
                if (foundNode.Val == val)
                    return this;
                return new PersistentTreeMap(_comp, Replace(_tree, key, val), _count, meta());
            }

            return new PersistentTreeMap(_comp, t.Blacken(), _count + 1, meta());
        }
 private object[] pushTail(int level, object[] arr, object[] tailNode, Box expansion)
 {
     object newchild;
     if (level == 0)
         newchild = tailNode;
     else
     {
         newchild = pushTail(level - 5, (object[])arr[arr.Length - 1], tailNode, expansion);
         if (expansion.Val == null)
         {
             object[] ret1 = (object[])arr.Clone();
             ret1[arr.Length - 1] = newchild;
             return ret1;
         }
         else
             newchild = expansion.Val;
     }
     //expansion
     if ( arr.Length == 32 )
     {
         expansion.Val = new object[]{newchild};
         return arr;
     }
     object[] ret = new object[arr.Length + 1];
     Array.Copy(arr, ret, arr.Length);
     ret[arr.Length] = newchild;
     expansion.Val = null;
     return ret;
 }
        private object[] popTail(int shift, object[] arr, Box ptail)
        {
            if ( shift > 0 )
            {
                object[] newchild = popTail(shift-5,(object[])arr[arr.Length-1],ptail);
                if ( newchild != null )
                {
                    object[] ret1 = (object[])arr.Clone();
                    ret1[arr.Length-1] = newchild;
                    return ret1;
                }
            }
            if ( shift == 0 )
                ptail.Val = arr[arr.Length-1];
            //contaction
            if( arr.Length == 1 )
                return null;

            object[] ret = new Object[arr.Length-1];
            Array.Copy(arr,ret,ret.Length);
            return ret;
        }
 public INode assoc(int shift, int hash, object key, object val, Box addedLeaf)
 {
     if (hash == _hash)
     {
         if (Util.equals(key, _key))
         {
             if (val == _val)
                 return this;
             // note - do not set AddedLeaf, since we are replacing
             else
                 return new LeafNode(hash, key, val);
         }
         else
         {
             // hash collision, same hash, different keys
             LeafNode newLeaf = new LeafNode(hash, key, val);
             addedLeaf.Val = newLeaf;
             return new HashCollisionNode(hash, this, newLeaf);
         }
     }
     else
         return BitmapIndexedNode.create(shift, this, hash, key, val, addedLeaf);
 }
 Node Remove(Node t, object key, Box found)
 {
     if (t == null)
         return null;
     int c = DoCompare(key, t.Key);
     if (c == 0)
     {
         found.Val = t;
         return Append(t.Left, t.Right);
     }
     Node del = c < 0 ? Remove(t.Left, key, found) : Remove(t.Right, key, found);
     if (del == null && found.Val == null)
         return null;
     if (c < 0)
         return (t.Left is Black)
             ? BalanceLeftDel(t.Key, t.Val, del, t.Right)
             : MakeRed(t.Key, t.Val, del, t.Right);
     return (t.Right is Black)
         ? BalanceRightDel(t.Key, t.Val, t.Left, del)
         : MakeRed(t.Key, t.Val, t.Left, del);
 }
 public INode assoc(int shift, int hash, object key, object val, Box addedLeaf)
 {
     if (_hash == hash)
     {
         int idx = FindIndex(key);
         if (idx != -1)
         {
             if (_array[idx + 1] == val)
                 return this;
             return new HashCollisionNode(null, hash, _count, CloneAndSet(_array, idx + 1, val));
         }
         object[] newArray = new object[_array.Length + 2];
         Array.Copy(_array, 0, newArray, 0, _array.Length);
         newArray[_array.Length] = key;
         newArray[_array.Length + 1] = val;
         addedLeaf.Val = addedLeaf;
         return new HashCollisionNode(_edit, hash, _count + 1, newArray);
     }
     // nest it in a bitmap node
     return new BitmapIndexedNode(null, Bitpos(_hash, shift), new object[] { null, this })
         .assoc(shift, hash, key, val, addedLeaf);
 }
 /// <summary>
 /// Add a new key/value pair.
 /// </summary>
 /// <param name="key">The key</param>
 /// <param name="val">The value</param>
 /// <returns>A new map with key+value added.</returns>
 /// <remarks>Overwrites an exising value for the <paramref name="key"/>, if present.</remarks>
 public override IPersistentMap assoc(object key, object val)
 {
     Box addedLeaf = new Box(null);
     INode newroot = _root.assoc(0, Util.Hash(key), key, val, addedLeaf);
     return newroot == _root
         ? this
         : new PersistentHashMap(meta(), addedLeaf.Val == null ? _count : _count + 1, newroot);
 }
 public INode without(AtomicReference<Thread> edit, int shift, int hash, Object key, Box removedLeaf)
 {
     int idx = FindIndex(key);
     if (idx == -1)
         return this;
     if (_count == 1)
         return null;
     HashCollisionNode editable = EnsureEditable(edit);
     editable._array[idx] = editable._array[2 * _count - 2];
     editable._array[idx + 1] = editable._array[2 * _count - 1];
     editable._array[2 * _count - 2] = editable._array[2 * _count - 1] = null;
     editable._count--;
     return editable;
 }
 public INode assoc(int shift, int hash, object key, object val, Box addedLeaf)
 {
     int bit = bitpos(hash, shift);
     int idx = index(bit);
     if ((_bitmap & bit) != 0)
     {
         INode n = _nodes[idx].assoc(shift + 5, hash, key, val, addedLeaf);
         if (n == _nodes[idx])
             return this;
         else
         {
             INode[] newnodes = (INode[])_nodes.Clone();
             newnodes[idx] = n;
             return new BitmapIndexedNode(_bitmap, newnodes, shift);
         }
     }
     else
     {
         INode[] newnodes = new INode[_nodes.Length + 1];
         Array.Copy(_nodes, 0, newnodes, 0, idx);
         addedLeaf.Val = newnodes[idx] = new LeafNode(hash, key, val);
         Array.Copy(_nodes, idx, newnodes, idx + 1, _nodes.Length - idx);
         return create(_bitmap | bit, newnodes, shift);
     }
 }
 private static INode CreateNode(AtomicReference<Thread> edit, int shift, Object key1, Object val1, int key2hash, Object key2, Object val2)
 {
     int key1hash = Util.hash(key1);
     if (key1hash == key2hash)
         return new HashCollisionNode(null, key1hash, 2, new Object[] { key1, val1, key2, val2 });
     Box _ = new Box(null);
     return BitmapIndexedNode.EMPTY
         .assoc(edit, shift, key1hash, key1, val1, _)
         .assoc(edit, shift, key2hash, key2, val2, _);
 }
 internal static INode create(int shift, INode branch, int hash, object key, object val, Box addedLeaf)
 {
     return (new BitmapIndexedNode(bitpos(branch.getHash(), shift), new INode[] { branch }, shift))
             .assoc(shift, hash, key, val, addedLeaf);
 }
 public INode assoc(AtomicReference<Thread> edit, int shift, int hash, object key, object val, Box addedLeaf)
 {
     int idx = Util.Mask(hash, shift);
     INode node = _array[idx];
     if (node == null)
     {
         ArrayNode editable = EditAndSet(edit, idx, BitmapIndexedNode.EMPTY.assoc(edit, shift + 5, hash, key, val, addedLeaf));
         editable._count++;
         return editable;
     }
     INode n = node.assoc(edit, shift + 5, hash, key, val, addedLeaf);
     if (n == node)
         return this;
     return EditAndSet(edit, idx, n);
 }
 public INode assoc(int shift, int hash, object key, object val, Box addedLeaf)
 {
     INode ret = new LeafNode(hash, key, val);
     addedLeaf.Val = ret;
     return ret;
 }
 public INode assoc(int shift, int hash, object key, object val, Box addedLeaf)
 {
     int bit = Bitpos(hash, shift);
     int idx = Index(bit);
     if ((_bitmap & bit) != 0)
     {
         object keyOrNull = _array[2 * idx];
         object valOrNode = _array[2 * idx + 1];
         if (keyOrNull == null)
         {
             INode n = ((INode)valOrNode).assoc(shift + 5, hash, key, val, addedLeaf);
             if (n == valOrNode)
                 return this;
             return new BitmapIndexedNode(null, _bitmap, CloneAndSet(_array, 2 * idx + 1, n));
         }
         if ( Util.equiv(key,keyOrNull))
         {
             if ( val == valOrNode)
                 return this;
             return new BitmapIndexedNode(null,_bitmap,CloneAndSet(_array,2*idx+1,val));
         }
         addedLeaf.Val = addedLeaf;
         return new BitmapIndexedNode(null,_bitmap,
             CloneAndSet(_array,
                         2*idx,
                         null,
                         2*idx+1,
                         CreateNode(shift+5,keyOrNull,valOrNode,hash,key,val)));
     }
     else
     {
         int n = Util.BitCount(_bitmap);
         if ( n >= 16 )
         {
             INode [] nodes = new INode[32];
             int jdx = Util.Mask(hash,shift);
             nodes[jdx] = EMPTY.assoc(shift+5,hash,key,val,addedLeaf);
             int j=0;
             for ( int i=0; i < 32; i++ )
                 if ( ( (_bitmap >>i) & 1) != 0 )
                 {
                     if ( _array[j] ==  null )
                        nodes[i] = (INode) _array[j+1];
                     else
                         nodes[i] = EMPTY.assoc(shift+5,Util.hash(_array[j]),_array[j],_array[j+1], addedLeaf);
                     j += 2;
                 }
             return new ArrayNode(null,n+1,nodes);
         }
         else
         {
             object[] newArray = new object[2*(n+1)];
             Array.Copy(_array, 0, newArray, 0, 2*idx);
             newArray[2*idx] = key;
             addedLeaf.Val = addedLeaf;
             newArray[2*idx+1] = val;
             Array.Copy(_array, 2*idx, newArray, 2*(idx + 1), 2*(n - idx));
             return new BitmapIndexedNode(null, _bitmap | bit, newArray);
         }
     }
 }
            public INode assoc(int shift, int hash, object key, object val, Box addedLeaf)
            {
                int idx = Util.Mask(hash, shift);

                INode n = _nodes[idx].assoc(shift + 5, hash, key, val, addedLeaf);
                if (n == _nodes[idx])
                    return this;
                else
                {
                    INode[] newNodes = (INode[])_nodes.Clone();
                    newNodes[idx] = n;
                    return new FullNode(newNodes, shift);
                }
            }
 public INode without(AtomicReference<Thread> edit, int shift, int hash, object key, Box removedLeaf)
 {
     int bit = Bitpos(hash, shift);
     if ((_bitmap & bit) == 0)
         return this;
     int idx = Index(bit);
     Object keyOrNull = _array[2 * idx];
     Object valOrNode = _array[2 * idx + 1];
     if (keyOrNull == null)
     {
         INode n = ((INode)valOrNode).without(edit, shift + 5, hash, key, removedLeaf);
         if (n == valOrNode)
             return this;
         if (n != null)
             return EditAndSet(edit, 2 * idx + 1, n);
         if (_bitmap == bit)
             return null;
         removedLeaf.Val = removedLeaf;
         return EditAndRemovePair(edit, bit, idx);
     }
     if (Util.equiv(key, keyOrNull))
     {
         removedLeaf.Val = removedLeaf;
         // TODO: collapse
         return EditAndRemovePair(edit, bit, idx);
     }
     return this;
 }
        /// <summary>
        /// Creates a new vector with a new item at the end.
        /// </summary>
        /// <param name="o">The item to add to the vector.</param>
        /// <returns>A new (immutable) vector with the objected added at the end.</returns>
        /// <remarks>Overrides <c>cons</c> in <see cref="IPersistentCollection">IPersistentCollection</see> to specialize the return value.</remarks>
        public override IPersistentVector cons(object val)
        {
            if (_tail.Length < 32)
            {
                object[] newTail = new object[_tail.Length + 1];
                Array.Copy(_tail, newTail, _tail.Length);
                newTail[_tail.Length] = val;
                return new PersistentVector(meta(), _cnt + 1, _shift, _root, newTail);
            }
            Box expansion = new Box(null);

            object[] newroot = pushTail(_shift - 5, _root, _tail, expansion);
            int newshift = _shift;
            if (expansion.Val != null)
            {
                newroot = new object[] { newroot, expansion.Val };
                newshift += 5;
            }

            return new PersistentVector(meta(), _cnt + 1, newshift, newroot, new object[] { val });
        }