package main;

/**
 * RedBlackBST class implements a lightweight version of
 * a standard red-black BST.
 * It supports the methods get, put and delete in O(log N)
 * worst-case time.
 * 
 * The nodes are implemented using an array of childs and a color,
 * there is no such thing as a parent pointer.
 * This is pimarily Julienne Walker's structure on eternallyconfuzzled.com,
 * which I used in order to improve the implementation of deletion.
 * 
 * The get method is implemented using a simple search in a BST.
 * The put method is implemented recursively in the bottom-up way,
 * which is todays's standard for a red-black BST.
 * The delete method is implemented recursively in a top-down way.
 * Its peculiarity is that its core is only 24 lines long, while usual
 * implementations easily go over 100.
 * The basic idea is simply to make the current node or its child a
 * red one, because deleting a red node is easy.
 * This is achieved in only one-pass, that is after deletion of the
 * leaf there is no need to fixup the tree.
 * 
 * I believe this implementation is slightly slower than a usual one because
 * the number of rotations during a delete operation is not bounded, whereas
 * it is limited to 3 in a usual implementation.
 * However, given the gain in simplicity and space, this is a little drawback.
 * 
 * Compared to left-leaning red-black BSTs, it should do slightly better (not tested)
 * because there is no reshaping of the tree while moving up, and the number of
 * compares is also lower (only one compare per level down).
 * 
 * A detailed analysis of the two implementation is given here in terms of LOCs :
 * ******************************************************************************
 * 
 * The LLRB BST version used is the one found at http://algs4.cs.princeton.edu/33balanced/RedBlackBST.java.html,
 * but cleaned in order to keep the exact same statements as this one (no assertion or
 * track of the size of the tree, no exceptions thrown). We will focus only on the put and delete methods
 * 
 * Plus, a method length include its prototype, for example the following would account
 * for 3 LOCs :
 * public String foo() {
 *     return "bar";
 * }
 * 
 * 
 * Left-Leaning Red Black BST
 * **************************
 * 
 * The implementation relies on several helper functions :
 * isRed : 3
 * rotateRight : 8
 * rotateLeft : 8
 * flipColors : 5
 * balance : 6
 * moveRedLeft : 9
 * moveRedRight : 8
 * min(node) : 4
 * deleteMin(node) : 7
 * 
 * insertion :
 * put(key,val) : 4
 * put(node,key,val) : 11
 * 
 * Insertion is 15 lines long and makes use of 4 helper which amount
 * for 24 lines (isRed, rotateRight, rotateLeft, flipColors)
 * 
 * deletion :
 * delete(key) : 6
 * delete(node, key) : 22
 * 
 * Deletion is 28 lines long in total and makes use of 9 helpers,
 * which amount for 58 lines (isRed, rotateRight, rotateLeft, flipColors,
 * balance, moveRedLeft, moveRedRight, min, deleteMin)
 * 
 * This red-black BST
 * ******************
 * 
 * It makes use of several helper functions :
 * isRed : 3
 * cmpToDir : 3
 * rotate : 8
 * flipColors : 5
 * min(node) : 4
 * blacken : 4
 * rotateDel : 7
 * 
 * insertion :
 * put(key, val) : 4
 * put(node, key, val) : 18
 * 
 * Insertion represents a total of 22 LOCs, and use 4 helpers
 * which amount for 19 lines (isRed, cmpToDir, rotate, flipColors)
 * 
 * deletion :
 * delete(key) : 6
 * delete(node, key) : 24
 * 
 * Deletion represents a total of 30 LOCs, and use 7 helpers
 * which amount for 34 lines
 * 
 * ********************
 * The number of lines needed for insertion are roughly the same, however
 * the big difference comes from the deletion operation, which is lower with this
 * implementation.
 * I would finally add that this implementation does not search for the existence of
 * a node before entering the delete operation (the case is handled properly),
 * therefore it saves log N compares.
 * 
 * 
 * @author Tristan Claverie
 *
 * @param <Key>
 * @param <Value>
 */
public class RedBlackBST<Key extends Comparable<Key>, Value> {
    
    // Red and black colors
    private static final boolean RED = true;
    private static final boolean BLACK = false;

    private Node<Key, Value> root; // root of the BST
    
    private class Node<Key, Value> {
        Key key;       // key
        Value val;     // value coupled with key
        Node<Key, Value>[] childs; // links to the childs subtrees
        boolean color; // color of the parent link
        
        @SuppressWarnings("unchecked")
        public Node(Key key, Value val) {
            this.key = key;
            this.val = val;
            this.color = RED;
            this.childs = (Node<Key, Value>[]) new Node[2];
        }
    }
    
    /** 
     * Creates a new symbol table
     */
    public RedBlackBST() {}
    
    /**
     * Get value associated with key
     * @param k the key
     * @return the value associated, null is non-existant
     */
    public Value get(Key k) {
        Node<Key,Value> x = search(root, k);
        if (x == null) return null;
        return x.val;
    }
    
    /**
     * Recursively search among subtrees to find the
     * node containing k
     */
    private Node<Key,Value> search(Node<Key,Value> node, Key k) {
        if (node == null) return null;
        int cmp = k.compareTo(node.key);
        if (cmp == 0) return node;
        int dir = cmpToDir(cmp);
        return search(node.childs[dir], k);
    }
    
    /**
     * Put couple (k,v) in the symbol table, replace old value
     * with v if k is already present
     * @param k key
     * @param v value
     */
    public void put(Key k, Value v) {
        root = put(root, k, v);
        root.color = BLACK;
    }
    
    /**
     * Helper to insert a node in the symbol table
     */
    private Node<Key,Value> put(Node<Key,Value> node, Key k, Value v) {
        if (node == null) return new Node<Key,Value>(k, v);
        int cmp = k.compareTo(node.key);
        // If the key exists, replace the value
        if (cmp == 0) node.val = v;
        
        else {
            int dir = cmpToDir(cmp);
            // Recursive call
            node.childs[dir] = put(node.childs[dir], k, v);
            
            // Fix up the tree on the way up
            if (isRed(node.childs[dir])) {
                if (isRed(node.childs[dir^1]))
                    flipColors(node);
                else {
                    if (isRed(node.childs[dir].childs[dir^1])) node.childs[dir] = rotate(node.childs[dir], dir);
                    if (isRed(node.childs[dir].childs[dir])) node = rotate(node, dir^1);
                }
            }
        }
        return node;
    }
    
    /**
     * Delete node with key k, do not crash if k is non-existent
     * @param k the key
     */
    public void delete(Key k) {
        if (root == null) return;
        if (!isRed(root.childs[0]) && !isRed(root.childs[1])) root.color = RED;
        root = delete(root, k);
        if (root != null) root.color = BLACK;
    }
    
    /**
     * Recursive call for deletion
     */
    private Node<Key,Value> delete(Node<Key,Value> node, Key k) {
        if (node == null) return null;
        int cmp = k.compareTo(node.key);
        // Hit the key
        if (cmp == 0) {
            if (node.childs[1] == null) return blacken(node.childs[0]);
            // If it is not a leaf, replace the node by its successor and deletes
            // the successor
            Node<Key,Value> x = min(node.childs[1]);
            node.key = x.key;
            node.val = x.val;
            k = node.key;
        }
        
        // Fixup the tree on the way down
        int dir = cmpToDir(cmp);
        if (!isRed(node.childs[dir])) {
            if (isRed(node.childs[dir^1])) {
                if (!isRed(node)) node = rotate(node, dir);
            } else if(node.childs[dir] != null && !isRed(node.childs[dir].childs[0]) && !isRed(node.childs[dir].childs[1])) {
                if (node.childs[dir^1] != null && (isRed(node.childs[dir^1].childs[dir^1]) || isRed(node.childs[dir^1].childs[dir])))
                    node = rotateDel(node, dir);
                else
                    flipColors(node);
            }
        }
        
        // Recursive call
        node.childs[dir] = delete(node.childs[dir], k);
        return node;
    }
    
    /***************************
     * Deletion specific helpers
     **************************/
    
    /**
     * Set the color of a node to black if not null
     * and returns it.
     */
    private Node<Key,Value> blacken(Node<Key,Value> n) {
        if (n != null) n.color = BLACK;
        return n;
    }
    
    /**
     * Minimum node of this subtree
     */
    private Node<Key,Value> min(Node<Key,Value> node) {
        if (node.childs[0] == null) return node;
        return min(node.childs[0]);
    }
    
    /**
     * Special rotation in the case of a deletion
     * It makes a simple or double rotation depending
     * of the context
     */
    private Node<Key,Value> rotateDel(Node<Key,Value> node, int dir) {
        flipColors(node);
        if (isRed(node.childs[dir^1].childs[dir])) node.childs[dir^1] = rotate(node.childs[dir^1], dir^1);
        node = rotate(node, dir);
        flipColors(node);
        return node;
    }
    
    /********************
     * Common helpers
     *******************/
    
    /**
     * Rotates a child around his father
     */
    private Node<Key,Value> rotate(Node<Key,Value> x, int dir) {
        Node<Key,Value> y = x.childs[dir^1];
        x.childs[dir^1] = y.childs[dir];
        y.childs[dir] = x;
        y.color = x.color;
        x.color = RED;
        return y;
    }
    
    /**
     * Flip the colors of the node and its childs
     */
    private void flipColors(Node<Key,Value> x) {
        x.color = !x.color;
        x.childs[0].color = !x.childs[0].color;
        x.childs[1].color = !x.childs[1].color;
    }
    
    /*******************************************
     * General helper functions
     ******************************************/

    /**
     * Check the color of the node
     */
    private boolean isRed(Node<Key,Value> x) {
        return x != null && x.color == RED;
    }
    
    /**
     * From the compareTo result (-1,0,1), decides the direction to
     * take : right child or left child
     */
    private int cmpToDir(int cmp) {
        return cmp >= 0 ? 1 : 0;
    }
    
}