//module RedBlackTree;

import  tango.util.collection.model.Comparator,
        tango.util.collection.model.GuardIterator,
        tango.util.collection.model.Iterator;

// TODO should these be in Basic Node?

// The two children of a node
enum {Left = 0, Right}

// Tags the links of a node
enum Tag {Thread = 0, Child}





/*******************************************************************************
    The basic structure of a node needed for the RedBlackTree.
    
    Remarks:
    This class is meant to be overriden using the Curriosly Reoccuring Template
    Pattern (CRTP). Each subclass must expose a default constructor.
    
    Examples:
    ---
    class Node(V) : BasicNode!(V, Node!(V)) {
        alias V KeyType;
        this() {}
        
    }
    ---
*******************************************************************************/
abstract class BasicNode(KeyType, NodeType) {
    alias KeyType KeyTypeT;
protected:
    // the data used in comparison
    protected KeyType key;
    
    /****************************************************************
        Construct a new node.
    
        Params:
        value = the value to store in the node
     ****************************************************************/
    this(KeyType value) {
        this();
        key = value;
    }


    /****************************************************************
        Construct a new node without any data.
     ****************************************************************/
    this() {
        red = true;
        link[Left] = null;
        link[Right] = null;
        tag[Left] = Tag.Thread;
        tag[Right] = Tag.Thread;
    }


protected:
    // is the node red (as opposed to black)
    bool red;
    
    // the two children/thread nodes
    NodeType[2] link;
    
    // tag the links as children or threads
    Tag[2] tag;
    

    /****************************************************************
        Get either the first or last descendant of the sub-tree.
        
        Params:
        dir = which direction to go, use Left or Right.
        
        Remarks:
        Traverse down the tree, returning the the node containing
        either the smallest or the largest value, depending on dir
        and the comparotor in use.        
        
        Returns:
        The right- or left-most node of the sub-tree
     ****************************************************************/
    NodeType end(int dir) {
        NodeType n = cast(NodeType)this;
        while (n.tag[dir])
            n = n.link[dir];
        return n;
    }
    

    /****************************************************************
        Produce a deep copy of the node

        Returns the copied node.

     ****************************************************************/
    protected NodeType copyOf() {
        NodeType newNode = new NodeType();
        newNode.key = key;
        newNode.link[0] = link[0].copyOf();
        newNode.link[1] = link[1].copyOf();
        newNode.tag[] = tag;
        return newNode;
    }
}





template RBIterMixins(KeyType, Node, ParentIterator) {

    // the current node 
    protected Node mNode;

    // how many items left to enumerate
    uint countLeft;

    public Node node() {
        return mNode;
    }

    // the direction we are iterating
    private const int mDir;

    static if (is(ParentIterator : GuardIterator!(KeyType))) {
        // the latest tree version
        private const uint* mLatestVersion;
        // the tree version we are used to
        private const uint mVersion;

        /***************************************************************
            Construct a new iterator.
            
            Params:
            ver = the tree version number
            node = the start node of the iteration
            dir = the direction the iteration is going (default: Right)
         ***************************************************************/
        this(uint* ver, Node node, int dir = Right) {
            mNode = node;
            mDir = dir;
            mVersion = *ver;
            mLatestVersion = ver;
        }

        bool corrupted() {
            return mVersion != *mLatestVersion;
        }
        
        uint remaining() {
            return countLeft;
        }
    } else {
        this (Node node, int dir = Right) {
            mNode = node;
            mDir = dir;
        }
    }

    /****************************************************************
        Ask whether the enumeration can continue.
        
        Remarks:
        This iterator has more elements if the base tree has more
        elements and hasn't changed since the iterator's construction.
        
        Returns:
        True if the enumeration can continue, false otherwise
     ****************************************************************/
    bool more() {
        static if (is(ParentIterator : GuardIterator!(KeyType))) {
            if (corrupted())
                return false;
        }
        return mNode !is null;
    }

    /****************************************************************
        Get the next element from the iteration.
        
        Remarks:
        Returns the next element of the collection and advances the
        iterator. O(log n).
        
        Results are undefined if !more().
        
        Returns:
        The next element of the collection.
     ****************************************************************/
    KeyType get() {
        assert(more());
        KeyType data = mNode.value();
        advanceNode();
        return data;
    }
    
    protected void advanceNode() {        
        // if the next element is a thread, follow it
        if (!mNode.tag[mDir]) {
            mNode = mNode.link[mDir];
        
        // get the next descendant
        } else {
            mNode = mNode.link[mDir].end(!mDir);
        }
        countLeft--;
    }
}





/******************************************************************************
    A genericized RedBlackTree

    Params:
    TODO

    Remarks:
    TODO
 ******************************************************************************/
abstract class RedBlackTree(alias Traits) : Traits.ParentCollectionT {
    alias Traits.NodeT Node;
    alias Traits.KeyTypeT KeyType;
    alias Traits.ValueTypeT ValueType;
    alias Traits.IteratorT IteratorT;
    alias Traits.GuardIteratorT GuardIteratorT;
    
    alias Traits.SINGLETON SINGLETON;
    alias Traits.DUAL_ITERATION DUAL_ITERATION;
    
    alias Comparator!(KeyType) ComparatorT;


    // the root of the tree
    protected Node mRoot;
    
    // the comparing function
    protected ComparatorT mComparator;

    
    /***************************************************************
         Construct an empty RedBlackTree.
         
         Remarks:
         The objects natural ordering will be used.
     ***************************************************************/
    this() {
        this(null, null, 0);
    }
    

    /***************************************************************
         Construct a RedBlackTree using this comparator
         
         Params:
         c = 
     ***************************************************************/
    this(ComparatorT c, Node root, uint size) {
        //mRoot = root;
        vershion = 0;
        count = size;
        mComparator = c is null ? &compare : c;
    }
    
    /*
     */
    override void clear() {
        vershion = 0;
        mRoot = null;
        count = 0;
    }
    

    /*
     */
    protected Node first() {
        if (mRoot is null)
            return null;
        return mRoot.end(Left);
    }


    /*
     */
    protected Node last() {
        if (mRoot is null)
            return null;
        return mRoot.end(Right);
    }
    

    /*
     */
    GuardIteratorT elements() {
        return new GuardIteratorT(&vershion, first());
    }


    /*
     */
    GuardIteratorT reverseElements() {
        return new GuardIteratorT(&vershion, last(), Left);
    }
    

    /*
     */
    IteratorT iterator() {
        return new IteratorT(first());
    }


    /*
     */
    IteratorT reverseIterator() {
        return new IteratorT(last(), Left);
    }
    

    /*
     */
    protected bool contains_(KeyType obj) {
        return getFirstElement(obj) !is null;
    }
    

    /*
     */
    protected Node insert_(KeyType obj, lazy Node newNode, bool singletons=true) {
        if (mRoot is null) {
            mRoot = newNode;
            mRoot.red = false;
            setCount(1);
            return mRoot;
        }
        
        bool isInserted = false;
        
        auto fakeHead = new Node();
        int dir = Left, last = Left;
        
        Node great = fakeHead;
        Node grand = null;
        Node parent = null;
        Node current = fakeHead.link[Right] = mRoot;
        
        Tag grandTag = Tag.Thread;
        Tag parentTag = Tag.Child;
        
        for (;;) {
            if (!parentTag) {
                current = newNode;
                current.link[dir] = parent.link[dir];
                current.link[!dir] = parent;
                
                parent.link[dir] = current;
                parent.tag[dir] = parentTag = Tag.Child;
                
                isInserted = true;
                incCount();
            }
            
            // color flip
            else if (isRed(current, Left) && isRed(current, Right)) {
                current.red = true;
                current.link[Left].red = false;
                current.link[Right].red = false;
            }
            
            // fix red violation
            if (isRed(current, parentTag) && isRed(parent, grandTag)) {
                int dir2 = great.link[Right] is grand;
                
                if (current is parent.link[last])
                    great.link[dir2] = singleRotate(grand, !last);
                else
                    great.link[dir2] = doubleRotate(grand, !last);
            }
            
            // Stop if found
            int cmp = mComparator(current.key, obj);
            static if (SINGLETON) {
                if (!cmp)
                    break;
            } else {
                if (!cmp && (singletons || isInserted)) {
                    break;
                }
            }
            
            last = dir;
            dir = cmp < 0;
            
            // update helpers
            if (grand !is null) {
                great = grand;
            }
            
            grand = parent;
            parent = current;
            current = current.link[dir];
            
            grandTag = parentTag;
            parentTag = parent.tag[dir];
        }
        
        // update the root and make it black
        mRoot = fakeHead.link[Right];
        mRoot.red = false;
        
        return current;
    }
    

    /*
     */
    void remove(KeyType obj) {
        if (mRoot is null)
            return;// false;
        
        Node fakeHead = new Node();
        fakeHead.link[Right] = mRoot;
        fakeHead.tag[Right] = Tag.Child;
        
        Node current = fakeHead;
        Node parent = null;
        Node grand = null;
        Node found = null;
        
        int dir = Right;
        
        // search and push down red node
        while (current.tag[dir]) {
            int last = dir;
            
            // update helpers
            grand = parent;
            parent = current;
            current = current.link[dir];
            
            int cmp = mComparator(current.key, obj);
            dir = cmp < 0;
            
            // save the found node
            if (!cmp) {
                found = current;
            }
            
            // push the red node down
            if (!isRed(current, parent.tag[last]) && !isRed(current, dir)) {
                if (isRed(current, !dir)) {
                    parent = parent.link[last] = singleRotate(current, dir);
                } else {
                    Node save = parent.link[!last];
                    
                    if (parent.tag[!last]) {
                        if (!isRed(save, Left) && !isRed(save, Right)) {
                            // Color flip
                            parent.red = false;
                            save.red = true;
                            current.red = true;
                        } else {
                            int dir2 = grand.link[Right] is parent;
                            
                            if (isRed(save, last))
                                grand.link[dir2] = doubleRotate(parent, last);
                            else
                                grand.link[dir2] = singleRotate(parent, last);
                            
                            // ensure correct coloring
                            Node uncle = grand.link[dir2];
                            current.red = uncle.red = true;
                            uncle.link[Left].red = false;
                            uncle.link[Right].red = false;
                        }
                    }
                }
            }
        } // end while
        
        // replace and remove if found
        if (found !is null) {
            found.key = current.key;
            decCount();
            
            int replacementDir = parent.link[Right] is current;
            int otherDir = !replacementDir;
            
            //assert(!current.tag[otherDir]);
            //assert(current.link[otherDir] is parent || parent is fakeHead);
            
            parent.link[replacementDir] = current.link[replacementDir];
            parent.tag[replacementDir] = Tag.Thread;
        }
        
        // update the root and make it black
        mRoot = fakeHead.link[Right];
        if (mRoot !is null) {
            mRoot.red = false;
        }
        
        // return found !is null;
    }
    

    /*
     */
    void comparator(ComparatorT c) {
        if (c == null)
            mComparator = &compare;
        if (mComparator is c || !count)
            return;
        scope auto iter = iterator();

        auto node = iter.node;
        iter.get();
        while (iter.more()) {
            node.link[0] = node.link[1] = null;
            node.tag[0] = node.tag[1] = Tag.Thread;
            insert_(node.key, node);
            iter.get();
            node = iter.node;
        }
        node.link[0] = node.link[1] = null;
        node.tag[0] = node.tag[1] = Tag.Thread;
        insert_(node.key, node);

    }
    

    /*
     */
    int opApply(int delegate(inout ValueType) dg) {
        auto scope iter = elements();
        return iter.opApply(dg);
    }
    

    /*
     */
    int opApplyReverse(int delegate(inout ValueType) dg) {
        auto scope iter = reverseElements();
        return iter.opApply(dg);
    }
    

    /*
     */
    static if (DUAL_ITERATION) {
        int opApply(int delegate(inout KeyType, inout ValueType) dg) {
            scope auto iter = elements();
            return iter.opApply(dg);
        }
        int opApplyReverse(int delegate(inout KeyType, inout ValueType) dg) {
            scope auto iter = reverseElements();
            return iter.opApply(dg);
        }
    }


    // default comparison function
    private int compare(KeyType a, KeyType b) {
        static if (is(KeyType : Object)) {
            if (a is b)
                return 0;
            return a.opCmp(b);
        } else {
            KeyType cmp = a - b;
            return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
        }
    }
    

    /*
     */
    protected Node getFirstElement(KeyType obj) {
        if (mRoot is null) return null;
        
        Node cur = mRoot;
        auto parentTag = Tag.Child; 
        while (parentTag) {
            int cmp = mComparator(cur.key, obj);
            if (!cmp)
                return cur;
            
            int dir = cmp < 0;
            parentTag = cur.tag[dir];
            cur = cur.link[dir];
        }
        return null;
    }
    
    static if (false) {
    invariant {
        int checkImpl(Node n) {
            int lh, rh;
            
            if (n is null)
                return 1;
            
            auto ln = n.link[0];
            auto rn = n.link[1];
            
            /* Consecutive red */
            if (isRed(n, Tag.Child)) {
                if (isRed(ln, n.tag[0]) || isRed(rn, n.tag[1])) {
                    //Stdout("Red violation");
                    assert(false);
                }
            }
            
            lh = n.tag[0] ? checkImpl(ln) : 0;
            rh = n.tag[1] ? checkImpl(rn) : 0;
            
            /* invalid bst */
            if ((n.tag[0] && mComparator(ln.key, n.key) > 0) ||
                    n.tag[1] && mComparator(rn.key, n.key) < 0) {
                //Stdout("BST Violation");
                assert(false);
            }
            
            // black height mismatch
            if (lh != 0 && rh != 0 && lh != rh) {
                //Stdout("Black Violation");
                assert(false);
            }
            
            if (lh != 0 && rh != 0) {
                return isRed(n, Tag.Child) ? lh : lh + 1;
            }
            return 0;
        }
        
        checkImpl(mRoot);
    }
    }
    

    
    
private static:

    // helper methods
    
    Node singleRotate(Node node, int dir) {
        int otherDir = !dir;
        Node save = node.link[otherDir];
        // If save's thread points back to node, then just fix the tags
        if (!save.tag[dir]) {
            save.tag[dir] = Tag.Child;
            node.tag[otherDir] = Tag.Thread;
        
        // otherwise swap children
        } else {
            node.link[otherDir] = save.link[dir];
            save.link[dir] = node;
        }
        
        node.red = true;
        save.red = false;
        return save;
    }

    Node doubleRotate(Node node, int dir) {
        int otherDir = !dir;
        node.link[otherDir] = singleRotate(node.link[otherDir], otherDir);
        return singleRotate(node, dir);
    }

    bool isRed(Node node, Tag tag) {
        return tag && node.red;
    }

    bool isRed(Node node, int dir) {
        return isRed(node.link[dir], node.tag[dir]);
    }
}

/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/
/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/
/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/
/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/
/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/
/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/
/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/
/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/

//module SingleRedBlackTree;

//import RedBlackTree;


import tango.util.collection.model.SortedValues;





/******************************************************************************
    An Iterator that iterates over the elements of a RedBlackTree.

    Params:
    Node = the node type of the collection.

    Remarks:
    Iterates over the elements of a RedBlackTree in the direction specified in
    the constructor. If the tree changes, iteration will stop even if there are
    more elements to enumerate.
 ******************************************************************************/
class RBIterator(KeyType, Node, ParentIterator) : ParentIterator {
    mixin RBIterMixins!(KeyType, Node, ParentIterator);

    /****************************************************************
        Apply a delegate to the rest of the elements in the enumeration.
        
        Params:
        dg = a delegate to apply to each element of the iteration.
        
        Remarks:
        Although the paramater to the delegate is an inout, changing
        the value will not change the value in the underlying collection.
        This is to maintain order in the collection and speed in the
        enumeration.
        
        Returns:
        If the delegate returns a non-zero value, that value is returned.
        Otherwise, when all elements have been enumerated, 0 is returned.
     ****************************************************************/
    int opApply(int delegate(inout KeyType value) dg) {
        while (more()) {
            KeyType t = get();
            int ret = dg(t);
            if (ret) return ret;
        }
        return 0;
    }
}







/*
 */
class Node(V) : public BasicNode!(V, Node!(V)) {
    

    /*
     */
    this(V v) {
        super(v);
    }
    

    /*
     */
    this () {
    }
    

    /*
     */
    V value() {
        return key;
    }
}






/*
 */
template SingleRedBlackTreeTraitsMixin(V) {
    alias V KeyTypeT;
    alias V ValueTypeT;
    alias Node!(V) NodeT;
    alias RBIterator!(V, NodeT, Iterator!(V)) IteratorT;
    alias RBIterator!(V, NodeT, GuardIterator!(V)) GuardIteratorT;
    const bool DUAL_ITERATION = false;
}
    




/*
 */
abstract class SingleRedBlackTree(alias Traits) :
    RedBlackTree!(Traits), SortedValues!(Traits.KeyTypeT) {
    

    /*
     */
    this() {}
    

    /*
     */
    this(ComparatorT cmp, Node tree, uint size) {
        super(cmp, tree, size);
    }
    
    /*
     */
    bool contains(ValueType v) {
        return contains_(v);
    }
    

    /*
     */
    ComparatorT comparator() {
        return mComparator;
    }


    /*
     */
    void add(KeyType obj) {
        insert_(obj, new Node(obj), SINGLETON);
    }
    

    /*
     */
    void replace(KeyType oldElement, KeyType newElement) {
        uint size = count;
        remove(oldElement);
        if (count != size) {
            add(newElement);
        }
    }
    

    /*
     */
    void replaceAll(KeyType oldElement, KeyType newElement) {
        scope auto iter = new IteratorT(getFirstElement(oldElement));
        while (iter.more()) {
            KeyType v = iter.get();
            if (v != oldElement)
                break;
            remove(v);
            add(newElement);
        }
    }
    

    /*
     */
    void removeAll(KeyType obj) {
        int startSize = 0;
        while (startSize != count) {
            startSize = count;
            remove(obj);
        }
    }
    

    /*
     */
    ValueType take() {
        checkIndex(0);
        KeyType value = mRoot.key;
        remove(value);
        return value;
    }
    

    /*
     */
    uint instances(ValueType obj) {
        scope auto iter = new IteratorT(getFirstElement(obj));
        uint count = 0;
        while (iter.more()) {
            ValueType t = iter.get();
            if (mComparator(t, obj))
                break;
            count++;
        }
        return count;
    }
}

/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/
/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/
/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/
/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/
/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/
/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/
/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/
/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/
/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/
/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/







    

//import  SingleRedBlackTree;

import  tango.util.collection.impl.BagCollection;

    

/*
 */
template BagTraits(V) {
    mixin SingleRedBlackTreeTraitsMixin!(V);
    alias BagCollection!(V) ParentCollectionT;
    const bool SINGLETON = false;
}






    

/*
 */
class BagTree(V) : SingleRedBlackTree!(BagTraits!(V)) {
    

    /*
     */
    BagTree!(KeyType) duplicate() {
        if (drained()) {
            return new BagTree(mComparator, null, 0);
        }
        return new BagTree(mComparator, mRoot.copyOf(), count);
    }
    

    /*
     */
    this() {}
    

    /*
     */
    protected this(ComparatorT c, Node tree, uint size) {
        super(c, tree, size);
    }
    

    /*
     */
    void addIf(KeyType obj) {
        insert_(obj, new Node(obj), true);
    }
}

/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/
/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/
/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/
/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/
/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/
/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/
/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/

//import SingleRedBlackTree;

import tango.util.collection.impl.SetCollection;



    

/*
 */
template SetTraits(V) {
    mixin SingleRedBlackTreeTraits!(V);
    alias SetCollection!(V) ParentCollectionT;
    const bool SINGLETON = true;
}

    

/*
 */
class SetTree(V) : SingleRedBlackTree!(SetTraits!(V)) {

    

    /*
     */
    this(ComparatorT c) {
        super(c, null, 0);
    }
    

    /*
     */
    private this(ComparatorT cmp, Node tree, uint size) {
        super(cmp, tree, size);
    }
    

    /*
     */
    this() { }
    

    /*
     */
    SetTree!(KeyType) duplicate() {
        if (drained()) {
            return new SetTree(mComparator, null, 0);
        }
        return new SetTree(mComparator, mRoot.copyOf(), count);
    }
}
/+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/

/*
module  MapTree;
import  RedBlackTree;
*/

import  tango.util.collection.impl.MapCollection,
        tango.util.collection.model.SortedKeys,
        tango.util.collection.model.Iterator,
        tango.util.collection.model.GuardIterator;


class PairNode(K, V) : public BasicNode!(K, PairNode!(K, V)) {
    V mValue;
    this(K k, V v) {
        super(k);
        mValue = v;
    }
    this() {
        super();
    }
    V value() {
        return mValue;
    }
}



class PairRedBlackIterator(KeyType, ValueType, Node, ParentIterator) : ParentIterator {
    mixin RBIterMixins!(ValueType, Node, ParentIterator);

    ValueType get() {
        KeyType k;
        return get(k);
    }
    ValueType get(inout KeyType key) {
        assert(more());
        ValueType data = mNode.mValue;
        key = mNode.key;
        advanceNode();
        return data;
    }

    int opApply(int delegate(inout KeyType, inout ValueType) dg) {
        while (more()) {
            auto curNode = mNode;
            KeyType t = curNode.key;
            advanceNode();
            int ret = dg(t, curNode.mValue);
            if (ret) return ret;
        }
        return 0;
    }
    int opApply(int delegate(inout ValueType) dg) {
        while (more()) {
            auto curNode = mNode;
            advanceNode();
            int ret = dg(curNode.mValue);
            if (ret) return ret;
        }
        return 0;
    }
}



template MapTraits(K, V) {
    alias K KeyTypeT;
    alias V ValueTypeT;
    alias PairNode!(K, V) NodeT;
    alias PairRedBlackIterator!(K, V, NodeT, Iterator!(V)) IteratorT;
    alias PairRedBlackIterator!(K, V, NodeT, PairIterator!(K, V)) GuardIteratorT;
    
    alias MapCollection!(K, V) ParentCollectionT;
    const bool SINGLETON = true;
    const bool DUAL_ITERATION = true;
}

class MapTree(KeyType, V) : RedBlackTree!(MapTraits!(KeyType, V)), SortedKeys!(KeyType, V) {
    this() {}
    protected this(ComparatorT cmp, Node n, int size) {
        super(cmp, n, size);
    }
 
    void add(KeyType k, ValueType v) {
        uint startCount = count;
        Node n = insert_(k, new Node(k, v));
        if (count == startCount) {
            n.mValue = v;
        }
    }

    ComparatorT comparator() {
        return mComparator;
    }
    

    void removeKey(KeyType k) {
        return super.remove(k);
    }

    bool containsKey(KeyType k) {
        return contains_(k);
    }


    GuardIteratorT keys() {
        return elements;
    }
    
    uint instances(ValueType obj) {
        scope auto iter = iterator();
        uint count = 0;
        while (iter.more()) {
            ValueType v = iter.get();
            if (v == obj) {
                count++;
            }
        }
        return count;
    }
    
    bool contains(ValueType obj) {
        scope auto iter = iterator();
        while (iter.more()) {
            ValueType v = iter.get();
            if (v == obj)
                return true;
        }
        return false;
    }

    bool get(KeyType k, inout ValueType v) {
        auto n = getFirstElement(k);
        if (n is null)
            return false;
        v = n.mValue;
        return true;
    }

    ValueType get(KeyType k) {
        ValueType v;
        if (!get(k, v))
            checkIndex(count);
        return v;
    }

    bool containsPair(KeyType k, ValueType v) {
        auto n = getFirstElement(k);
        return !(n is null || v == n.mValue) ;
    }
    
    bool keyOf(inout KeyType k, ValueType v) {
        scope auto iter = iterator();
        while (iter.more()) {
            ValueType ov = iter.get(k);
            if (ov == v)
                return true;
        }
        return false;
    }
    
    MapTree!(KeyType, ValueType) duplicate() {
        if (drained()) {
            return new MapTree(mComparator, null, 0);
        }
        return new MapTree(mComparator, mRoot.copyOf(), count);
    }
    
    void remove(ValueType v) {
        foreach (k, ov; this) {
            if (ov == v) {
                removeKey(k);
                break;
            }
        }
    }
    
    void replacePair(KeyType key, ValueType oldValue, ValueType newValue) {
        Node n = getFirstElement(key);
        if (n is null)
            return;
        if (n.mValue == oldValue) {
            n.mValue = newValue;
        }
    }
    
    ValueType take() {
        checkIndex(0);
        ValueType v = mRoot.mValue;
        removeKey(mRoot.key);
        return v;
    }
    
    void removeAll(ValueType v) {
        foreach (k, ov; iterator()) {
            if (ov == v) {
                removeKey(k);
            }
        }
    }
    
    void replace(ValueType oldElement, ValueType newElement) {
        foreach (k, ov; this) {
            if (ov == oldElement) {
                ov = newElement;
                break;
            }
        }
    }
    
    void replaceAll(ValueType oldElement, ValueType newElement) {
        foreach (k, ov; this) {
            if (ov == oldElement) {
                ov = newElement;
            }
        }
    }
}


import tango.io.Stdout;
int main() {
    auto t = new MapTree!(int, double);
    t.add(1, 4.1);
    t.add(2, 4.1);
    t.add(2, 5.1);
    t[4] = 5;
    t.removeAll(4.1);
    double v;
    foreach_reverse (i, j; t)
        Stdout(i)(' ')(j).newline;
    foreach (i; t)
        Stdout(i).newline;
    Stdout("Size =")(t.size).newline;
    try {
        foreach (j;t) {
            t.remove(j);
            Stdout("removing! Size=")(t.size).newline;
        }
    } catch {
        Stdout("Caught!");
    }
    return 0;
}


