All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ajjpj.afoundation.collection.immutable.ALongRedBlackTreeMap Maven / Gradle / Ivy

The newest version!
package com.ajjpj.afoundation.collection.immutable;

import com.ajjpj.afoundation.collection.AEquality;

import java.util.Iterator;
import java.util.NoSuchElementException;


/**
 * This is a {@link ARedBlackTreeMap} specialization for keys of primitive 'long' values.
 *
 * @author arno
 */
public class ALongRedBlackTreeMap extends AbstractAMap {
    final Tree root;

    @SuppressWarnings ("unchecked")
    private static final ALongRedBlackTreeMap EMPTY = new ALongRedBlackTreeMap (null);

    @SuppressWarnings ("unchecked")
    public static  ALongRedBlackTreeMap empty () {
        return EMPTY;
    }

    private ALongRedBlackTreeMap (Tree root) {
        this.root = root;
    }

    @Override public int size () {
        return root == null ? 0 : root.count;
    }

    @Override public boolean containsKey (Long key) {
        return containsKey (key.longValue ());
    }
    public boolean containsKey (long key) {
        return lookup (root, key) != null;
    }

    @Override public AOption get (Long key) {
        return get (key.longValue ());
    }
    public AOption get (long key) {
        final Tree raw = lookup (root, key);
        if (raw == null) {
            return AOption.none ();
        }
        return AOption.some (raw.value);
    }

    public V getRequired (long key) {
        return get (key).get ();
    }

    @Override public ASet keys () {
        return ALongRedBlackTreeSet.create (this);
    }

    @Override public AEquality keyEquality () {
        return AEquality.EQUALS;
    }

    @Override public AMap clear () {
        return empty ();
    }

    @Override public AMap updated (Long key, V value) {
        return updated (key.longValue (), value);
    }
    public ALongRedBlackTreeMap updated (long key, V value) {
        return new ALongRedBlackTreeMap<> (blacken (upd (root, key, value)));
    }

    @Override public AMap removed (Long key) {
        return removed (key.longValue ());
    }
    public ALongRedBlackTreeMap removed (long key) {
        return new ALongRedBlackTreeMap<> (blacken (del (root, key)));
    }

    @Override public Iterator> iterator () {
        return new TreeIterator> () {
            @Override AMapEntry nextResult (Tree tree) {
                return tree;
            }
        };
    }

    @SuppressWarnings ("unused")
    static void validate(Tree tree) {
        if (tree == null) {
            return;
        }

        validate (tree.left);
        validate (tree.right);

        // rule 4: every redden node has two blacken children
        if (isRedTree (tree)) {
            if (tree.left != null && isRedTree (tree.left)) {
                throw new IllegalStateException ("tree " + tree.key + " is redden and has a left child that is redden");
            }
            if (tree.right != null && isRedTree (tree.right)) {
                throw new IllegalStateException ("tree " + tree.key + " is redden and has a right child that is redden");
            }
        }

        checkBlackDepth (tree);
    }

    private static int checkBlackDepth (Tree tree) {
        if (tree == null) {
            return 1;
        }

        final int own = isBlackTree (tree) ? 1 : 0;
        final int left  = checkBlackDepth (tree.left);
        final int right = checkBlackDepth (tree.right);

        // rule 5: every path to 'leaf' nodes must have the same number of blacken nodes
        if (left != right) {
            throw new IllegalStateException ("left and right side have paths to leaf nodes with different numbers of blacken nodes: " + tree.key);
        }
        return own + left;
    }


    private abstract class TreeIterator implements Iterator {
        /*
         * According to "Ralf Hinze. Constructing redden-blacken trees" [http://www.cs.ox.ac.uk/ralf.hinze/publications/#P5]
         * the maximum height of a redden-blacken tree is 2*log_2(n + 2) - 2.
         *
         * According to {@see Integer#numberOfLeadingZeros} ceil(log_2(n)) = (32 - Integer.numberOfLeadingZeros(n - 1))
         *
         * We also don't store the deepest nodes in the pathStack so the maximum pathStack length is further reduced by one.
         */
        @SuppressWarnings ("unchecked")
        private final Tree[] pathStack;
        private int stackIndex = 0;
        private Tree next;

        abstract R nextResult (Tree tree); //TODO rename this

        @SuppressWarnings ("unchecked")
        private TreeIterator() {
            // initialize 'next' with the leftmost element
            if (root == null) {
                pathStack = null;
                next = null;
            }
            else {
                pathStack = new Tree [2 * (32 - Integer.numberOfLeadingZeros(root.count + 2 - 1)) - 2 - 1];
                next = root;
                while (next.left != null) {
                    pushPath (next);
                    next = next.left;
                }
            }
        }

        @Override public boolean hasNext() {
            return next != null;
        }

        @Override public R next() {
            if (next == null) {
                throw new NoSuchElementException ();
            }

            final Tree cur = next;
            next = findNext (next.right);
            return nextResult (cur);
        }

        @Override public void remove () {
            throw new UnsupportedOperationException ();
        }

        private Tree findNext (Tree tree) {
            while (true) {
                if (tree == null) {
                    return popPath ();
                }
                if (tree.left == null) {
                    return tree;
                }
                pushPath (tree);
                tree = tree.left;
            }
        }

        private void pushPath (Tree tree) {
            pathStack[stackIndex] = tree;
            stackIndex += 1;
        }

        private Tree popPath() {
            if (stackIndex == 0) {
                // convenience for handling the end of iteration
                return null;
            }
            stackIndex -= 1;
            return pathStack[stackIndex];
        }
    }





    static  Tree lookup(Tree tree, long key) {
        while (tree != null) {
            if (key == tree.key) return tree;

            tree = (key < tree.key) ? tree.left : tree.right;
        }
        return null;
    }

    static boolean isRedTree (Tree tree) {
        return tree != null && tree.isRed ();
    }
    static boolean isBlackTree (Tree tree) {
        return tree != null && tree.isBlack ();
    }

    static  Tree blacken (Tree tree) {
        if (tree == null) {
            return null;
        }
        return tree.blacken ();
    }

    static  Tree balanceLeft (TreeFactory treeFactory, long key, V value, Tree l, Tree d) {
        if (isRedTree (l) && isRedTree (l.left)) {
            return new RedTree<> (l.key, l.value,
                    new BlackTree<> (l.left.key, l.left.value, l.left.left, l.left.right),
                    new BlackTree<> (key, value, l.right, d));
        }
        if (isRedTree (l) && isRedTree (l.right)) {
            return new RedTree<> (l.right.key, l.right.value,
                    new BlackTree<> (l.key, l.value, l.left, l.right.left),
                    new BlackTree<> (key, value, l.right.right, d));
        }
        return treeFactory.create (l, d);
    }

    static  Tree balanceRight (TreeFactory treeFactory, long key, V value, Tree a, Tree r) {
        if (isRedTree (r) && isRedTree (r.left)) {
            return new RedTree<> (r.left.key, r.left.value,
                    new BlackTree<> (key, value, a, r.left.left),
                    new BlackTree<> (r.key, r.value, r.left.right, r.right));
        }
        if (isRedTree (r) && isRedTree (r.right)) {
            return new RedTree<> (r.key, r.value,
                    new BlackTree<> (key, value, a, r.left),
                    new BlackTree<> (r.right.key, r.right.value, r.right.left, r.right.right));
        }
        return treeFactory.create (a, r);
    }

    static  Tree upd (Tree tree, long key, V value) {
        if (tree == null) {
            return new RedTree<> (key, value, null, null);
        }
        if (key < tree.key) {
            return balanceLeft (tree, tree.key, tree.value, upd (tree.left, key, value), tree.right);
        }
        if (key > tree.key) {
            return balanceRight (tree, tree.key, tree.value, tree.left, upd (tree.right, key, value));
        }
        return tree.withNewValue (key, value);
    }

    static  Tree del (Tree tree, long key) {
        if (tree == null) {
            return null;
        }

        if (key < tree.key) {
            // the node that must be deleted is to the left
            return isBlackTree (tree.left) ?
                    balanceLeft (tree.key, tree.value, del (tree.left, key), tree.right) :

                // tree.left is 'redden', so its children are guaranteed to be blacken.
                new RedTree<> (tree.key, tree.value, del (tree.left, key), tree.right);
        }
        else if (key > tree.key) {
            // the node that must be deleted is to the right
            return isBlackTree (tree.right) ?
                    balanceRight (tree.key, tree.value, tree.left, del (tree.right, key)) :
                new RedTree<> (tree.key, tree.value, tree.left, del (tree.right, key));
        }

        // delete this node and we are finished
        return append (tree.left, tree.right);

    }

    static  Tree balance (long key, V value, Tree tl, Tree tr) {
        if (isRedTree (tl) && isRedTree (tr)) return new RedTree<> (key, value, tl.blacken (), tr.blacken ());

        if (isRedTree (tl)) {
            // left is redden, right is blacken
            if (isRedTree (tl.left)) return new RedTree<> (tl.key, tl.value, tl.left.blacken (), new BlackTree<> (key, value, tl.right, tr));
            if (isRedTree (tl.right)) {
                return new RedTree<> (tl.right.key, tl.right.value,
                        new BlackTree<> (tl.key, tl.value, tl.left, tl.right.left),
                        new BlackTree<> (key, value, tl.right.right, tr));
            }
            return new BlackTree<> (key, value, tl, tr);
        }

        if (isRedTree (tr)) {
            // left is blacken, right is redden
            if (isRedTree (tr.right)) return new RedTree<> (tr.key, tr.value, new BlackTree<> (key, value, tl, tr.left), tr.right.blacken ());
            if (isRedTree (tr.left))  return new RedTree<> (tr.left.key, tr.left.value, new BlackTree<> (key, value, tl, tr.left.left), new BlackTree<> (tr.key, tr.value, tr.left.right, tr.right));
            return new BlackTree<> (key, value, tl, tr);
        }

        // tl and tr are both blacken
        return new BlackTree<> (key, value, tl, tr);
    }

    private static  Tree balanceLeft (long key, V value, Tree tl, Tree tr) { //TODO merge with other 'balanceLeft' method?
        if (isRedTree (tl)) {
            return new RedTree<> (key, value, tl.blacken (), tr);
        }
        if (isBlackTree (tr)) {
            return balance (key, value, tl, tr.redden ());
        }
        if (isRedTree (tr) && isBlackTree (tr.left)) {
            return new RedTree<> (tr.left.key, tr.left.value, new BlackTree<> (key, value, tl, tr.left.left), balance (tr.key, tr.value, tr.left.right, tr.right.blackToRed ()));
        }
        throw new IllegalStateException ("invariant violation");
    }

    static  Tree balanceRight (long key, V value, Tree tl, Tree tr) {
        if (isRedTree (tr)) {
            return new RedTree<> (key, value, tl, tr.blacken ());
        }
        if (isBlackTree (tl)) {
            return balance (key, value, tl.redden (), tr);
        }
        if (isRedTree (tl) && isBlackTree (tl.right)) {
            return new RedTree<> (tl.right.key, tl.right.value, balance (tl.key, tl.value, tl.left.blackToRed (), tl.right.left), new BlackTree <> (key, value, tl.right.right, tr));
        }
        throw new IllegalStateException ("invariant violation");
    }

    /**
     * This method combines two separate sub-trees into a single (balanced) tree. It assumes that both subtrees are
     *  balanced and that all elements in 'tl' are smaller than all elements in 'tr'. This situation occurs when a
     *  node is deleted and its child nodes must be combined into a resulting tree.
     */
    private static  Tree append (Tree tl, Tree tr) {
        if (tl == null) return tr;
        if (tr == null) return tl;

        if (isRedTree (tl) && isRedTree (tr)) {
            final Tree bc = append (tl.right, tr.left);
            return isRedTree (bc) ?
                    new RedTree<> (bc.key, bc.value, new RedTree<> (tl.key, tl.value, tl.left, bc.left), new RedTree<> (tr.key, tr.value, bc.right, tr.right)) :
                    new RedTree<> (tl.key, tl.value, tl.left, new RedTree<> (tr.key, tr.value, bc, tr.right));
        }
        if (isBlackTree (tl) && isBlackTree (tr)) {
            final Tree bc = append (tl.right, tr.left);
            return isRedTree (bc) ?
                    new RedTree<> (bc.key, bc.value, new BlackTree<> (tl.key, tl.value, tl.left, bc.left), new BlackTree<> (tr.key, tr.value, bc.right, tr.right)) :
                    balanceLeft (tl.key, tl.value, tl.left, new BlackTree<> (tr.key, tr.value, bc, tr.right));
        }
        if (isRedTree (tr)) {
            return new RedTree<> (tr.key, tr.value, append (tl, tr.left), tr.right);
        }
        if (isRedTree (tl)) {
            return new RedTree<> (tl.key, tl.value, tl.left, append (tl.right, tr));
        }
        throw new IllegalStateException ("invariant violation: unmatched tree on append: " + tl + ", " + tr);
    }


    /**
     * encapsulates tree creation for a given colour
     */
    interface TreeFactory {
        Tree create (Tree left, Tree right);
    }

    static abstract class Tree implements TreeFactory, AMapEntry {
        final long key;
        final V value;
        final int count;

        final Tree left;
        final Tree right;

        public Tree (long key, V value, Tree left, Tree right) {
            this.key = key;
            this.value = value;
            this.left = left;
            this.right = right;

            this.count = 1 +
                    (left == null ? 0 : left.count) +
                    (right == null ? 0 : right.count);
        }

        @Override public Long getKey () {
            return key;
        }
        @Override public V getValue () {
            return value;
        }

        abstract Tree withNewValue (long key, V value);

        abstract Tree blackToRed();

        abstract boolean isRed();
        abstract boolean isBlack();

        abstract Tree redden ();
        abstract Tree blacken ();
    }

    static class BlackTree extends Tree {
        public BlackTree (long key, V value, Tree left, Tree right) {
            super(key, value, left, right);
        }

        @Override Tree withNewValue (long key, V value) {
            return new BlackTree<> (key, value, left, right);
        }
        @Override public Tree create (Tree left, Tree right) {
            return new BlackTree<> (key, value, left, right);
        }

        @Override Tree blackToRed () {
            return redden ();
        }

        @Override boolean isRed () {
            return false;
        }
        @Override boolean isBlack () {
            return true;
        }

        @Override Tree redden () {
            return new RedTree<> (key, value, left, right);
        }
        @Override Tree blacken () {
            return this;
        }
    }

    static class RedTree extends Tree {
        public RedTree (long key, V value, Tree left, Tree right) {
            super (key, value, left, right);
        }

        @Override Tree withNewValue (long key, V value) {
            return new RedTree<> (key, value, left, right);
        }
        @Override public Tree create (Tree left, Tree right) {
            return new RedTree<> (key, value, left, right);
        }

        @Override Tree blackToRed () {
            throw new IllegalStateException ();
        }

        @Override boolean isRed () {
            return true;
        }
        @Override boolean isBlack () {
            return false;
        }

        @Override Tree redden () {
            return this;
        }
        @Override Tree blacken () {
            return new BlackTree<> (key, value, left, right);
        }
    }
}







© 2015 - 2024 Weber Informatics LLC | Privacy Policy