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

org.javersion.util.AbstractRedBlackTree Maven / Gradle / Ivy

There is a newer version: 0.15.3
Show newest version
/*
 *  Copyright 2013 Samppa Saarela
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.javersion.util;

import static java.util.Objects.requireNonNull;
import static org.javersion.util.AbstractRedBlackTree.Color.BLACK;
import static org.javersion.util.AbstractRedBlackTree.Color.RED;
import static org.javersion.util.AbstractRedBlackTree.Mirror.LEFT;
import static org.javersion.util.AbstractRedBlackTree.Mirror.RIGHT;

import java.util.*;
import java.util.function.Consumer;

import org.javersion.util.AbstractRedBlackTree.Node;

import com.google.common.collect.UnmodifiableIterator;

public abstract class AbstractRedBlackTree, This extends AbstractRedBlackTree> {

    @SuppressWarnings("rawtypes")
    private final static Comparator NATURAL = new Comparator() {
        @SuppressWarnings("unchecked")
        @Override
        public int compare(Comparable left, Comparable right) {
            return left.compareTo(right);
        }
    };

    protected final Comparator comparator;

    @SuppressWarnings("unchecked")
    public AbstractRedBlackTree() {
        this((Comparator) NATURAL);
    }

    public AbstractRedBlackTree(Comparator comparator) {
        this.comparator = requireNonNull(comparator, "comparator");
    }

    public abstract int size();

    public boolean isEmpty() {
        return size() == 0;
    }

    protected abstract This doReturn(Comparator comparator, N newRoot, int newSize);

    private This commitAndReturn(UpdateContext context, Comparator comparator, N newRoot, int newSize) {
        commit(context);
        return doReturn(comparator, newRoot, newSize);
    }

    protected void commit(UpdateContext context) {
        context.commit();
    }

    protected final N find(N root, Object keyObj) {
        @SuppressWarnings("unchecked")
        K key = (K) keyObj;
        N node = root;
        while (node != null) {
            int cmp;
            cmp = comparator.compare(key, node.key);
            if (cmp < 0) {
                node = node.left;
            } else if (cmp > 0) {
                node = node.right;
            } else {
                return node;
            }
        }
        return null;
    }

    protected final N findMin(N node) {
        while (node != null) {
            if (node.left == null) {
                return node;
            } else {
                node = node.left;
            }
        }
        return null;
    }

    protected final N findMax(N node) {
        while (node != null) {
            if (node.right == null) {
                return node;
            } else {
                node = node.right;
            }
        }
        return null;
    }

    protected final N floorNode(N node, K key) {
        N current = node;
        N floor = null;
        while (current != null) {
            int cmp = comparator.compare(current.key, key);
            if (cmp < 0) {
                floor = current;
                current = current.right;
            } else if (cmp > 0) {
                current = current.left;
            } else {
                return current;
            }
        }
        return floor;
    }

    protected final N lowerNode(N node, K key) {
        N current = node;
        N lower = null;
        while (current != null) {
            int cmp = comparator.compare(current.key, key);
            if (cmp < 0) {
                lower = current;
                current = current.right;
            } else {
                current = current.left;
            }
        }
        return lower;
    }

    protected final N ceilingNode(N node, K key) {
        N current = node;
        N ceiling = null;
        while (current != null) {
            int cmp = comparator.compare(current.key, key);
            if (cmp > 0) {
                ceiling = current;
                current = current.left;
            } else if (cmp < 0) {
                current = current.right;
            } else {
                return current;
            }
        }
        return ceiling;
    }

    protected final N higherNode(N node, K key) {
        N current = node;
        N ceiling = null;
        while (current != null) {
            int cmp = comparator.compare(current.key, key);
            if (cmp > 0) {
                ceiling = current;
                current = current.left;
            } else {
                current = current.right;
            }
        }
        return ceiling;
    }

    protected final This doAdd(UpdateContext context, N root, N node) {
        if (root == null) {
            context.insert(node);
            return commitAndReturn(context, comparator, node.edit(context, BLACK, null, null), 1);
        } else {
            N newRoot = root.add(context, node.edit(context, RED, null, null), comparator);
            if (newRoot == null) {
                return self();
            } else {
                return commitAndReturn(context, comparator, newRoot.blacken(context), size() + context.getChangeAndReset());
            }
        }
    }

    @SuppressWarnings("rawtypes")
    protected final This doAddAll(UpdateContext context, final N root, Iterable nodes) {
        N newRoot = root;
        N rootCandidate = null;
        int newSize = size();
        for (N node : nodes) {
            if (newRoot == null) {
                newSize++;
                rootCandidate = node.edit(context, BLACK, null, null);
            } else {
                rootCandidate = newRoot.add(context, node.edit(context, RED, null, null), comparator);
            }
            if (rootCandidate != null) {
                newRoot = rootCandidate.blacken(context);
                newSize += context.getChangeAndReset();
            }
        }
        return commitAndReturn(context, comparator, newRoot, newSize);
    }

    protected Iterator doIterator(N root, boolean asc) {
        return new RBIterator(root, asc);
    }

    protected Iterator doRangeIterator(N root, boolean asc, K from, boolean fromInclusive, K to, boolean toInclusive) {
        return new RangeIterator(root, asc, comparator, from, fromInclusive, to, toInclusive);
    }

    protected final This doRemove(UpdateContext context, N root, Object keyObj) {
        @SuppressWarnings("unchecked")
        K key = (K) keyObj;
        if (root == null) {
            return self();
        } else {
            N newRoot = root.remove(context, key, comparator);
            if (!context.hasChanged()) {
                return self();
            } else if (newRoot != null) {
                return commitAndReturn(context, comparator, newRoot.blacken(context), size() + context.getChangeAndReset());
            } else {
                return commitAndReturn(context, comparator, null, 0);
            }
        }
    }

    @SuppressWarnings("unchecked")
    protected This self() {
        return (This) this;
    }

    static abstract class Node> implements Cloneable {
        final UpdateContext context;
        final K key;
        Color color;
        This left;
        This right;

        protected Node(UpdateContext context, K key, Color color, This left, This right) {
            this.context = context;
            this.key = key;
            this.color = color;
            this.left = left;
            this.right = right;
        }

        @SuppressWarnings("unchecked")
        protected This self() {
            return (This) this;
        }

        This blacken(UpdateContext currentContext) {
            return changeColor(currentContext, BLACK);
        }

        This redden(UpdateContext currentContext) {
            return changeColor(currentContext, RED);
        }

        protected This add(UpdateContext currentContext, final This node, Comparator comparator) {
            This self = self();
            int cmpr = comparator.compare(node.key, key);
            if (cmpr == 0) {
                if (currentContext.merge(self, node)) {
                    return replaceWith(currentContext, node);
                } else {
                    return null;
                }
            } else if (cmpr < 0) {
                return LEFT.add(currentContext, self, node, comparator);
            } else {
                return RIGHT.add(currentContext, self, node, comparator);
            }
        }

        protected This remove(UpdateContext currentContext, final K key, Comparator comparator) {
            This self = self();
            int cmpr = comparator.compare(key, self.key);
            if (cmpr == 0) {
                currentContext.delete(self());
                return append(currentContext, left, right);
            } else if (cmpr < 0) {
                return LEFT.remove(currentContext, self, key, comparator);
            } else {
                return RIGHT.remove(currentContext, self, key, comparator);
            }
        }

        private This append(UpdateContext currentContext, This left, This right) {
            if (left == null) {
                return right;
            }
            else if (right == null) {
                return left;
            }
            else if (isRed(left)) {
                if (isRed(right)) {
                    This app = append(currentContext, left.right, right.left);
                    if (isRed(app)) {
                        This newLeft = left.edit(currentContext, RED, left.left, app.left);
                        This newRight = right.edit(currentContext, RED, app.right, right.right);
                        return app.edit(currentContext, RED, newLeft, newRight);
                    }
                    else {
                        This newRight = right.edit(currentContext, RED, app, right.right);
                        return left.edit(currentContext, RED, left.left, newRight);
                    }
                }
                else {
                    This newRight = append(currentContext, left.right, right);
                    return left.edit(currentContext, RED, left.left, newRight);
                }
            }
            else if (isRed(right)) {
                This newLeft = append(currentContext, left, right.left);
                return right.edit(currentContext, RED, newLeft, right.right);
            }
            else { // black/black
                This app = append(currentContext, left.right, right.left);
                if (isRed(app)) {
                    This newLeft = left.edit(currentContext, BLACK, left.left, app.left);
                    This newRight = right.edit(currentContext, BLACK, app.right, right.right);
                    return app.edit(currentContext, RED, newLeft, newRight);
                }
                else {
                    This newRight = right.edit(currentContext, BLACK, app, right.right);
                    return balanceLeftDel(currentContext, left, left.left, newRight);
                }
            }
        }

        This changeColor(UpdateContext currentContext, Color newColor) {
            This node = toEditable(currentContext);
            node.color = newColor;
            return node;
        }

        This edit(UpdateContext currentContext, Color newColor, This newLeft, This newRight) {
            This node = toEditable(currentContext);
            node.color = newColor;
            node.left = newLeft;
            node.right = newRight;
            return node;
        }

        This toEditable(UpdateContext currentContext) {
            if (this.context.isSameAs(currentContext)) {
                return self();
            } else {
                return cloneWith(currentContext);
            }
        }

        abstract This cloneWith(UpdateContext currentContext);

        abstract This replaceWith(UpdateContext currentContext, This node);

    }

    static enum Color {
        // TODO: Abstract methods for contract
        RED {
            @Override
            > N balanceInsert(UpdateContext currentContext, N parent, N child, Mirror mirror) {
                N result;
                N left = mirror.leftOf(child);
                N right = mirror.rightOf(child);
                if (isRed(left)) {
                    N newRight = parent.toEditable(currentContext);
                    newRight.color = BLACK;
                    mirror.children(newRight, right, mirror.rightOf(parent));

                    result = child.toEditable(currentContext);
                    result.color = RED;
                    mirror.children(result, left.blacken(currentContext), newRight);
                }
                else if (isRed(right)) {
                    N newLeft = child.toEditable(currentContext);
                    newLeft.color = BLACK;
                    mirror.children(newLeft, left, mirror.leftOf(right));

                    N newRight = parent.toEditable(currentContext);
                    newRight.color = BLACK;
                    mirror.children(newRight, mirror.rightOf(right), mirror.rightOf(parent));

                    result = right.toEditable(currentContext);
                    result.color = RED;
                    mirror.children(result, newLeft, newRight);
                }
                else {
                    result = BLACK.balanceInsert(currentContext, parent, child, mirror);
                }
                return result;
            }
            @Override
            > N add(UpdateContext currentContext, N node, N newChild, Mirror mirror) {
                N editable = node.toEditable(currentContext);
                mirror.setLeftOf(editable, newChild);
                editable.color = RED;
                return editable;
            }
        },
        BLACK;
        > N balanceInsert(UpdateContext currentContext, N parent, N child, Mirror mirror) {
            N result = parent.toEditable(currentContext);
            result.color = BLACK;
            mirror.children(result, child, mirror.rightOf(parent));
            return result;
        }
        > N add(UpdateContext currentContext, N node, N newChild, Mirror mirror) {
            N editable = node.toEditable(currentContext);
            mirror.setLeftOf(editable, newChild);
            return newChild.color.balanceInsert(currentContext, editable, newChild, mirror);
        }
    }

    static enum Mirror {
        // TODO: Abstract methods for contract
        RIGHT {
            @Override
            > N leftOf(N node) {
                return node.right;
            }
            @Override
            > N rightOf(N node) {
                return node.left;
            }
            @Override
            > void setLeftOf(N node, N left) {
                node.right = left;
            }
            @Override
            > void setRightOf(N node, N right) {
                node.left = right;
            }
            @Override
            > N balanceDelete(UpdateContext currentContext, N node, N newChild) {
                return balanceRightDel(currentContext, node, node.left, newChild);
            }
            @Override
            > N delete(UpdateContext currentContext, N node, N newChild) {
                return node.edit(currentContext, RED, node.left, newChild);
            }
        },
        LEFT;
        > N leftOf(N node) {
            return node.left;
        }
        > N rightOf(N node) {
            return node.right;
        }
        > void setLeftOf(N node, N left) {
            node.left = left;
        }
        > void setRightOf(N node, N right) {
            node.right = right;
        }
        > void children(N node, N left, N right) {
            setLeftOf(node, left);
            setRightOf(node, right);
        }
        > N add(UpdateContext currentContext, N self, N node, Comparator comparator) {
            N left = leftOf(self);
            N newChild;
            if (left == null) {
                currentContext.insert(node);
                newChild = node;
            } else {
                newChild = left.add(currentContext, node, comparator);
            }
            if (newChild == null) {
                return null;
            }
            return self.color.add(currentContext, self, newChild, this);
        }
        > N balanceDelete(UpdateContext currentContext, N node, N newChild) {
            return balanceLeftDel(currentContext, node, newChild, node.right);
        }

        > N delete(UpdateContext currentContext, N node, N newChild) {
            return node.edit(currentContext, RED, newChild, node.right);
        }
        > N remove(UpdateContext currentContext, N self, final K key, Comparator comparator) {
            N child = leftOf(self);
            if (child == null) {
                // key not found
                return self;
            }
            boolean balance = isBlack(leftOf(self));
            N newChild = child.remove(currentContext, key, comparator);
            if (!currentContext.hasChanged()) {
                return self;
            } else if (balance) {
                return balanceDelete(currentContext, self, newChild);
            } else {
                return delete(currentContext, self, newChild);
            }
        }
    }

    private static boolean isBlack(Node node) {
        return node != null && node.color == BLACK;
    }

    private static boolean isRed(Node node) {
        return node != null && node.color == RED;
    }

    // TODO: Refactor into LEFT.balanceDelete -method
    private static > N balanceLeftDel(UpdateContext currentContext, N node, N left, N right) {
        if (isRed(left)) {
            return node.edit(currentContext, RED, left.blacken(currentContext), right);
        }
        else if (isBlack(right)) {
            return balanceRight(currentContext, node, left, right.redden(currentContext));
        }
        else if (isRed(right) && isBlack(right.left)) {
            N rightLeft = right.left;
            N newLeft = node.edit(currentContext, BLACK, left, rightLeft.left);
            N newRight = balanceRight(currentContext, right, rightLeft.right, right.right.redden(currentContext));
            return rightLeft.edit(currentContext, RED, newLeft, newRight);
        }
        else {
            throw new IllegalStateException("Illegal invariant");
        }
    }

    // TODO: Refactor into RIGHT.balanceDelete -method
    private static > N balanceRightDel(UpdateContext currentContext, N node, N left, N right) {
        if (isRed(right)) {
            return node.edit(currentContext, RED, left, right.blacken(currentContext));
        }
        else if (isBlack(left)) {
            return balanceLeft(currentContext, node, left.redden(currentContext), right);
        }
        else if (isRed(left) && isBlack(left.right)) {
            N leftRight = left.right;
            N newLeft = balanceLeft(currentContext, left, left.left.redden(currentContext), leftRight.left);
            N newRight = node.edit(currentContext, BLACK, leftRight.right, right);
            return leftRight.edit(currentContext, RED, newLeft, newRight);
        }
        else {
            throw new IllegalStateException("Illegal invariant");
        }
    }

    // TODO: Refactor into LEFT.balance -method
    private static > N balanceLeft(UpdateContext currentContext, N node, N left, N right) {
        if (isRed(left) && isRed(left.left)) {
            N newRight = node.edit(currentContext, BLACK, left.right, right);
            return left.edit(currentContext, RED, left.left.blacken(currentContext), newRight);
        }
        else if (isRed(left) && isRed(left.right)) {
            N leftRight = left.right;
            N newLeft = left.edit(currentContext, BLACK, left.left, leftRight.left);
            N newRight = node.edit(currentContext, BLACK, leftRight.right, right);
            return leftRight.edit(currentContext, RED, newLeft, newRight);
        }
        else {
            return node.edit(currentContext, BLACK, left, right);
        }
    }

    // TODO: Refactor into RIGHT.balance -method
    private static > N balanceRight(UpdateContext currentContext, N node, N left, N right) {
        if (isRed(right) && isRed(right.right)) {
            N newLeft= node.edit(currentContext, BLACK, left, right.left);
            return right.edit(currentContext, RED, newLeft, right.right.blacken(currentContext));
        }
        else if (isRed(right) && isRed(right.left)) {
            N rightLeft = right.left;
            N newLeft = node.edit(currentContext, BLACK, left, rightLeft.left);
            N newRight = right.edit(currentContext, BLACK, rightLeft.right, right.right);
            return rightLeft.edit(currentContext, RED, newLeft, newRight);
        }
        else {
            return node.edit(currentContext, BLACK, left, right);
        }
    }

    static abstract class AbstractRBIterator> extends UnmodifiableIterator {

        final Deque stack = new ArrayDeque();

        final boolean asc;

        public AbstractRBIterator(boolean asc) {
            this.asc = asc;
        }

        protected abstract void pushAll(N node);

        @Override
        public boolean hasNext() {
            return !stack.isEmpty();
        }

        @Override
        public N next() {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            N result = stack.pop();
            pushAll(asc ? result.right : result.left);
            return result;
        }

    }

    static class RBIterator> extends AbstractRBIterator {

        public RBIterator(N root, boolean asc) {
            super(asc);
            pushAll(root);
        }

        protected void pushAll(N node) {
            while (node != null) {
                stack.push(node);
                node = (asc ? node.left : node.right);
            }
        }
    }

    static class RangeIterator> extends AbstractRBIterator {

        final Comparator comparator;
        final K from;
        final boolean fromInclusive;
        final K to;
        final boolean toInclusive;

        public RangeIterator(N root, boolean asc, Comparator comparator, K from, boolean fromInclusive, K to, boolean toInclusive) {
            super(asc);
            this.comparator = comparator;
            this.from = from;
            this.fromInclusive = fromInclusive;
            this.to = to;
            this.toInclusive = toInclusive;
            pushAll(root);
        }

        @Override
        protected void pushAll(N node) {
            while (node != null) {
                boolean fromIncluded = fromIncluded(node.key);
                boolean toIncluded = toIncluded(node.key);
                if (fromIncluded && toIncluded) {
                    stack.push(node);
                }
                if (asc) {
                    node = fromIncluded ? node.left : node.right;
                } else {
                    node = toIncluded ? node.right : node.left;
                }
            }
        }

        private boolean fromIncluded(K key) {
            return from == null || isIncluded(from, key, fromInclusive);
        }

        private boolean toIncluded(K key) {
            return to == null || isIncluded(key, to, toInclusive);
        }

        private boolean isIncluded(K key1, K key2, boolean inclusive) {
            int cmpr = comparator.compare(key1, key2);
            return cmpr < 0 || inclusive && cmpr == 0;
        }
    }

    static abstract class RBSpliterator> implements Spliterator {

        private N root;
        private int sizeEstimate;
        private Deque stack;
        private final int characteristics;

        protected RBSpliterator(N root, int size, int additionalCharacteristics) {
            this.root = requireNonNull(root, "root");
            stack = new ArrayDeque();
            this.sizeEstimate = size;
            this.characteristics = ordered(sized(additionalCharacteristics));
        }

        protected RBSpliterator(int sizeEstimate, int additionalCharacteristics) {
            this.sizeEstimate = sizeEstimate;
            this.characteristics = ordered(nonSized(additionalCharacteristics));
        }

        private static int sized(int characteristics) {
            return characteristics | SIZED;
        }

        private static int ordered(int characteristics) {
            return characteristics | ORDERED;
        }

        private static int nonSized(int characteristics) {
            return characteristics & ~SIZED;
        }

        private void init() {
            if (root != null) {
                pushAll(root);
                root = null;
            }
        }

        protected void pushAll(N node) {
            while (node != null) {
                stack.addFirst(node);
                node = node.left;
            }
        }

        @Override
        public boolean tryAdvance(Consumer action) {
            init();
            if (stack.isEmpty()) {
                return false;
            }
            N result = stack.removeFirst();
            action.accept(apply(result));
            pushAll(result.right);
            return true;
        }

        @Override
        public void forEachRemaining(Consumer action) {
            if (root != null) {
                forEach(root, action);
                root = null;
            } else {
                while (!stack.isEmpty()) {
                    N next = stack.removeFirst();
                    action.accept(apply(next));
                    if (next.right != null) {
                        forEach(next.right, action);
                    }
                }
            }
        }

        private void forEach(N node, Consumer action) {
            if (node.left != null) {
                forEach(node.left, action);
            }
            action.accept(apply(node));
            if (node.right != null) {
                forEach(node.right, action);
            }
        }

        @Override
        public Spliterator trySplit() {
            if (root != null) {
                return splitRoot();
            } else {
                return splitStack();
            }
        }

        private Spliterator splitRoot() {
            if (root.left == null || root.right == null) {
                return null;
            }
            RBSpliterator prefix = split();
            prefix.stack = new ArrayDeque<>();
            prefix.root = root.left;

            this.stack.push(root);
            this.root = null;

            return prefix;
        }

        private Spliterator splitStack() {
            if (stack.size() < 2) {
                return null;
            }
            N mid = stack.removeLast();
            RBSpliterator prefix = split();
            prefix.stack = this.stack;

            this.stack = new ArrayDeque<>();
            this.stack.push(mid);

            return prefix;
        }

        private RBSpliterator split() {
            return newSpliterator(sizeEstimate >>>= 1);

        }

        protected abstract RBSpliterator newSpliterator(int sizeEstimate);

        protected abstract T apply(N node);

        @Override
        public long estimateSize() {
            return sizeEstimate;
        }

        @Override
        public int characteristics() {
            return characteristics;
        }

    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy