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

io.takamaka.code.util.StorageTreeSet Maven / Gradle / Ivy

The newest version!
/*
Copyright 2021 Fausto Spoto

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 io.takamaka.code.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import io.takamaka.code.lang.Exported;
import io.takamaka.code.lang.Storage;
import io.takamaka.code.lang.View;

/**
 * A sorted set of (non-{@code null}) storage values,
 * that can be kept in storage. By iterating on this object, one gets
 * the values in the set, in increasing order.
 *
 * This code is derived from Sedgewick and Wayne's code for
 * red-black trees, with some adaptation. It implements an associative
 * map from keys to a present/missing mark. The map can be kept in storage.
 * Values must have types allowed in storage. They are kept in
 * comparable order, if they implement {@link java.lang.Comparable}.
 * Otherwise, they must extend {@link io.takamaka.code.lang.Storage} and
 * are kept in storage reference order. This implementation does not call
 * {@code equals()} nor {@code hashCode()}.
 *
 * This class represents an ordered set of values.
 * It supports the usual add, contains,
 * remove, size, and is-empty methods.
 * It also provides ordered methods for finding the minimum,
 * maximum, floor, and ceiling.
 * 

* The add, contains, delete, minimum, * maximum, ceiling, and floor operations each take * logarithmic time in the worst case, if the tree becomes unbalanced. * The size, and is-empty operations take constant time. * Construction takes constant time. *

* For additional documentation, see Section 3.3 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne * @param the type of the values */ public class StorageTreeSet extends Storage implements StorageSet { /** * The root of the tree. */ private Node root; /** * Builds an empty set. */ public StorageTreeSet() {} /** * Creates a set initialized to the same elements as the given parent collection. * * @param parent the parent collection */ public StorageTreeSet(Collection parent) { parent.forEach(this::add); } /** * Yields a snapshot of the given set. * * @param parent the map */ private StorageTreeSet(StorageTreeSet parent) { this.root = parent.root; } private void mkRootBlack() { if (isRed(root)) root = Node.mkBlack(root.value, root.size, root.left, root.right); } private void mkRootRed() { if (isBlack(root)) root = Node.mkRed(root.value, root.size, root.left, root.right); } /** * A node of the binary search tree that implements the set. */ private abstract static class Node extends Storage { protected final V value; // never null protected final Node left, right; /** * Count of the subtree nodes. */ protected final int size; private Node(V value, int size, Node left, Node right) { this.value = value; this.size = size; this.left = left; this.right = right; } protected static Node mkBlack(V value, int size, Node left, Node right) { return new BlackNode<>(value, size, left, right); } protected static Node mkRed(V value, int size, Node left, Node right) { return new RedNode<>(value, size, left, right); } protected static Node mkRed(V value) { return new RedNode<>(value, 1, null, null); } @Override public int hashCode() { // unused, but needed to satisfy white-listing for addition of Nodes inside Java collections return 42; } protected abstract Node setValue(V value); protected abstract Node setLeft(Node left); protected abstract Node setRight(Node right); protected abstract Node rotateRight(); protected abstract Node rotateLeft(); protected abstract Node flipColors(); protected abstract Node fixSize(); protected abstract Node flipColor(); private Node moveRedLeft() { Node h = flipColors(); return isRed(h.right.left) ? h.setRight(h.right.rotateRight()).rotateLeft().flipColors() : h; } private Node moveRedRight() { Node h = flipColors(); return isRed(h.left.left) ? h.rotateRight().flipColors() : h; } // restore red-black tree invariant private Node balance() { Node h = this; if (isRed(h.right)) h = h.rotateLeft(); if (isRed(h.left) && isRed(h.left.left)) h = h.rotateRight(); if (isRed(h.left) && isRed(h.right)) h = h.flipColors(); return h.fixSize(); } } private static class RedNode extends Node { private RedNode(V value, int size, Node left, Node right) { super(value, size, left, right); } @Override protected Node fixSize() { return mkRed(value, size(left) + size(right) + 1, left, right); } @Override protected Node flipColor() { return mkBlack(value, size, left, right); } @Override protected Node rotateLeft() { final Node x = right; Node newThis = mkRed(value, size(x.left) + size(left) + 1, left, x.left); return mkRed(x.value, size, newThis, x.right); } @Override protected Node rotateRight() { // assert isRed(left); final Node x = left; Node newThis = mkRed(value, size(x.right) + size(right) + 1, x.right, right); return mkRed(x.value, size, x.left, newThis); } @Override protected Node setValue(V value) { return mkRed(value, size, left, right); } @Override protected Node setLeft(Node left) { return mkRed(value, size, left, right); } @Override protected Node setRight(Node right) { return mkRed(value, size, left, right); } @Override protected Node flipColors() { return mkBlack(value, size, left.flipColor(), right.flipColor()); } } private static class BlackNode extends Node { private BlackNode(V value, int size, Node left, Node right) { super(value, size, left, right); } @Override protected Node fixSize() { return mkBlack(value, size(left) + size(right) + 1, left, right); } @Override protected Node flipColor() { return mkRed(value, size, left, right); } @Override protected Node rotateLeft() { final Node x = right; Node newThis = mkRed(value, size(x.left) + size(left) + 1, left, x.left); return mkBlack(x.value, size, newThis, x.right); } @Override protected Node rotateRight() { final Node x = left; Node newThis = mkRed(value, size(x.right) + size(right) + 1, x.right, right); return mkBlack(x.value, size, x.left, newThis); } @Override protected Node setValue(V value) { return mkBlack(value, size, left, right); } @Override protected Node setLeft(Node left) { return mkBlack(value, size, left, right); } @Override protected Node setRight(Node right) { return mkBlack(value, size, left, right); } @Override protected Node flipColors() { return mkRed(value, size, left.flipColor(), right.flipColor()); } } /** * Determines if the given node is red. * * @param x the node * @return true if and only if {@code x} is red */ private static boolean isRed(Node x) { return x instanceof RedNode; } /** * Determines if the given node is black. * * @param x the node * @return true if and only if {@code x} is black */ private static boolean isBlack(Node x) { return x == null || x instanceof BlackNode; } /** * Yields the number of nodes in the subtree rooted at x. * * @param x the root of the subtree * @return the number of nodes. Yields 0 if {@code x} is {@code null} */ private static int size(Node x) { return x == null ? 0 : x.size; } @Override public @View int size() { return size(root); } @Override public @View boolean isEmpty() { return root == null; } @SuppressWarnings("unchecked") private static int compareTo(K key1, K key2) { if (key1 instanceof Comparable) return ((Comparable) key1).compareTo(key2); else return ((Storage) key1).compareByStorageReference((Storage) key2); } /** * Determines if the given subtree contains the given value. * * @param x the root of the subtree * @param value the value * @return true if and only if that condition holds */ private static boolean contains(Node x, Object value) { while (x != null) { int cmp = compareTo(value, x.value); if (cmp < 0) x = x.left; else if (cmp > 0) x = x.right; else return true; } return false; } @Override public @View boolean contains(Object value) { if (value == null) throw new IllegalArgumentException("value is null"); return contains(root, value); } @Override public void add(V value) { if (value == null) throw new IllegalArgumentException("value is null"); root = put(root, value); mkRootBlack(); } // insert the value in the subtree rooted at h private static Node put(Node h, V value) { if (h == null) return Node.mkRed(value); int cmp = compareTo(value, h.value); if (cmp < 0) h = h.setLeft(put(h.left, value)); else if (cmp > 0) h = h.setRight(put(h.right, value)); else h = h.setValue(value); // fix-up any right-leaning links if (isRed(h.right) && isBlack(h.left)) h = h.rotateLeft(); if (isRed(h.left) && isRed(h.left.left)) h = h.rotateRight(); if (isRed(h.left) && isRed(h.right)) h = h.flipColors(); return h.fixSize(); } @Override public void removeMin() { if (isEmpty()) throw new NoSuchElementException(); if (isBlack(root.left) && isBlack(root.right)) mkRootRed(); root = removeMin(root); if (!isEmpty()) mkRootBlack(); } // removes the minimum value in the tree rooted at h private static Node removeMin(Node h) { if (h.left == null) return null; if (isBlack(h.left) && isBlack(h.left.left)) h = h.moveRedLeft(); return h.setLeft(removeMin(h.left)).balance(); } @Override public void removeMax() { if (isEmpty()) throw new NoSuchElementException(); if (isBlack(root.left) && isBlack(root.right)) mkRootRed(); root = removeMax(root); if (!isEmpty()) mkRootBlack(); } // delete the maximum value in the tree rooted at h private static Node removeMax(Node h) { if (isRed(h.left)) h = h.rotateRight(); if (h.right == null) return null; if (isBlack(h.right) && isBlack(h.right.left)) h = h.moveRedRight(); return h.setRight(removeMax(h.right)).balance(); } @Override public void remove(Object value) { if (value == null) throw new IllegalArgumentException("value is null"); if (contains(value)) { if (isBlack(root.left) && isBlack(root.right)) mkRootRed(); root = remove(root, value); if (!isEmpty()) mkRootBlack(); } } // delete the given value from the tree rooted at h private static Node remove(Node h, Object value) { if (compareTo(value, h.value) < 0) { if (isBlack(h.left) && isBlack(h.left.left)) h = h.moveRedLeft(); h = h.setLeft(remove(h.left, value)); } else { if (isRed(h.left)) h = h.rotateRight(); if (compareTo(value, h.value) == 0 && (h.right == null)) return null; if (isBlack(h.right) && isBlack(h.right.left)) h = h.moveRedRight(); if (compareTo(value, h.value) == 0) { Node x = min(h.right); if (isRed(h)) h = Node.mkRed(x.value, h.size, h.left, removeMin(h.right)); else h = Node.mkBlack(x.value, h.size, h.left, removeMin(h.right)); } else h = h.setRight(remove(h.right, value)); } return h.balance(); } @Override public @View V min() { if (isEmpty()) throw new NoSuchElementException("call to min() on an empty set"); return min(root).value; } // the smallest value in subtree rooted at x private static Node min(Node x) { if (x.left == null) return x; else return min(x.left); } @Override public @View V max() { if (isEmpty()) throw new NoSuchElementException("call to max() on an empty set"); return max(root).value; } // the largest value in the subtree rooted at x private static Node max(Node x) { if (x.right == null) return x; else return max(x.right); } @Override public @View V floorKey(Object value) { if (value == null) throw new IllegalArgumentException("value is null"); if (isEmpty()) throw new NoSuchElementException(); Node x = floorKey(root, value); if (x == null) throw new NoSuchElementException(); else return x.value; } // the largest value in the subtree rooted at x less than or equal to the given value private static Node floorKey(Node x, Object value) { if (x == null) return null; int cmp = compareTo(value, x.value); if (cmp == 0) return x; if (cmp < 0) return floorKey(x.left, value); Node t = floorKey(x.right, value); if (t != null) return t; else return x; } @Override public @View V ceilingKey(Object value) { if (value == null) throw new IllegalArgumentException("value is null"); if (isEmpty()) throw new NoSuchElementException(); Node x = ceilingKey(root, value); if (x == null) throw new NoSuchElementException(); else return x.value; } // the smallest value in the subtree rooted at x greater than or equal to the given value private static Node ceilingKey(Node x, Object value) { if (x == null) return null; int cmp = compareTo(value, x.value); if (cmp == 0) return x; if (cmp > 0) return ceilingKey(x.right, value); Node t = ceilingKey(x.left, value); if (t != null) return t; else return x; } @Override public @View V select(int k) { if (k < 0 || k >= size()) throw new IllegalArgumentException("argument to select() is invalid: " + k); return select(root, k).value; } // yield the value of rank k in the subtree rooted at x private static Node select(Node x, int k) { int t = size(x.left); if (t > k) return select(x.left, k); else if (t < k) return select(x.right, k - t - 1); else return x; } @Override public @View int rank(Object value) { if (value == null) throw new IllegalArgumentException("value is null"); return rank(value, root); } // number of values less than value in the subtree rooted at x private static int rank(Object value, Node x) { if (x == null) return 0; int cmp = compareTo(value, x.value); if (cmp < 0) return rank(value, x.left); else if (cmp > 0) return 1 + size(x.left) + rank(value, x.right); else return size(x.left); } @Override public Iterator iterator() { return new StorageSetIterator<>(root); } private static class StorageSetIterator implements Iterator { // the path under enumeration; it is always true that the left children // have already been enumerated private final List> stack = new ArrayList<>(); private StorageSetIterator(Node root) { // initially, the stack contains the leftmost path of the tree for (Node cursor = root; cursor != null; cursor = cursor.left) stack.add(cursor); } @Override public boolean hasNext() { return !stack.isEmpty(); } @Override public V next() { Node topmost = stack.remove(stack.size() - 1); // we add the leftmost path of the right child of topmost for (Node cursor = topmost.right; cursor != null; cursor = cursor.left) stack.add(cursor); return topmost.value; } } @Override public Stream stream() { return StreamSupport.stream(spliterator(), false); } @Override public String toString() { return stream().map(Objects::toString).collect(Collectors.joining(",", "[", "]")); } @Override public StorageSetView view() { /** * A read-only view of a parent storage set. A view contains the same elements * as the parent storage set, but does not include modification methods. * Moreover, a view is exported, so that it can be safely divulged * outside the store of a node. Calls to the view are simply forwarded to * the parent set. */ @Exported class StorageSetViewImpl extends Storage implements StorageSetView { @Override public @View int size() { return StorageTreeSet.this.size(); } @Override public @View boolean isEmpty() { return StorageTreeSet.this.isEmpty(); } @Override public @View boolean contains(Object value) { return StorageTreeSet.this.contains(value); } @Override public @View V min() { return StorageTreeSet.this.min(); } @Override public @View V max() { return StorageTreeSet.this.max(); } @Override public @View V floorKey(Object value) { return StorageTreeSet.this.floorKey(value); } @Override public @View V ceilingKey(Object value) { return StorageTreeSet.this.ceilingKey(value); } @Override public @View V select(int k) { return StorageTreeSet.this.select(k); } @Override public @View int rank(Object value) { return StorageTreeSet.this.rank(value); } @Override public String toString() { return StorageTreeSet.this.toString(); } @Override public Iterator iterator() { return StorageTreeSet.this.iterator(); } @Override public Stream stream() { return StorageTreeSet.this.stream(); } @Override public StorageSetView snapshot() { return StorageTreeSet.this.snapshot(); } } return new StorageSetViewImpl(); } @Override public StorageSetView snapshot() { return new StorageTreeSet<>(this).view(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy