/** * Copyright (C) 2010 Hal Hildebrand. All rights reserved. * * This file is part of the Prime Mover Event Driven Simulation Framework. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Affero General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more * details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.hellblazer.primeMover.runtime; import java.lang.reflect.Array; import java.util.ArrayDeque; import java.util.Collection; import java.util.Deque; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Queue; import com.hellblazer.primeMover.runtime.SplayQueue.BinaryNode.duplicate; /** * Implements a priority queue using a splay tree. This queue implementation * allows duplicates. * * Original splay tree implementation by Danny Sleator <sleator@cs.cmu.edu> * * @author <a href="mailto:hal.hildebrand@gmail.com">Hal Hildebrand</a> */ public class SplayQueue<K extends Comparable<K>> implements Queue<K> { static class BinaryNode<K extends Comparable<K>> { static class duplicate<K extends Comparable<K>> { final K value; final duplicate<K> next; duplicate(K value, duplicate<K> next) { this.value = value; this.next = next; } } K value; duplicate<K> duplicates; BinaryNode<K> left; BinaryNode<K> right; BinaryNode(K theKey) { value = theKey; left = right = null; } public int fill(int i, Object[] result) { result[i++] = value; duplicate<K> current = duplicates; while (current != null) { result[i++] = current.value; current = current.next; } return i; } K getValue() { return value; } void insert(K newValue) { if (value == null) { value = newValue; } else { duplicates = new duplicate<K>(newValue, duplicates); } } boolean removeValue() { if (duplicates == null) { return true; } value = duplicates.value; duplicates = duplicates.next; return false; } } // test code stolen from Weiss public static void main(String[] args) { SplayQueue<Integer> t = new SplayQueue<Integer>(); final int NUMS = 40000; final int GAP = 307; System.out.println("Checking... (no bad output means success)"); for (int i = GAP; i != 0; i = (i + GAP) % NUMS) { t.add(new Integer(i)); } System.out.println("Inserts complete"); for (int i = 1; i < NUMS; i += 2) { t.remove(new Integer(i)); } System.out.println("Removes complete"); /* if (t.findMin().intValue() != 2 || t.findMax().intValue() != NUMS - 2) { System.out.println("FindMin or FindMax error!"); } */ for (int i = 2; i < NUMS; i += 2) { if (!t.contains(new Integer(i))) { System.out.println("Error: find fails for " + i); } } for (int i = 1; i < NUMS; i += 2) { if (t.contains(new Integer(i))) { System.out.println("Error: Found deleted item " + i); } } } private BinaryNode<K> root; private int size = 0; public SplayQueue() { root = null; } @Override public boolean add(K e) { if (e == null) { throw new NullPointerException("Cannot add null elements"); } BinaryNode<K> n; int c; if (root == null) { root = new BinaryNode<K>(e); } else { splay(e); if ((c = e.compareTo(root.value)) == 0) { root.insert(e); } else { n = new BinaryNode<K>(e); if (c < 0) { n.left = root.left; n.right = root; root.left = null; } else { n.right = root.right; n.left = root; root.right = null; } root = n; } } size++; return true; } @Override public boolean addAll(Collection<? extends K> c) { boolean changed = false; for (K e : c) { changed |= add(e); } return changed; } @Override public void clear() { size = 0; root = null; } @Override public boolean contains(Object o) { if (o == null) { throw new NullPointerException("Does not allow null elements"); } if (!(o instanceof Comparable<?>)) { return false; } @SuppressWarnings("unchecked") K k = (K) o; if (root == null) { return false; } splay(k); if (root.value.compareTo(k) != 0) { return false; } return true; } @Override public boolean containsAll(Collection<?> c) { for (Object o : c) { if (!contains(o)) { return false; } } return true; } private void deleteRoot(K result) { BinaryNode<K> x; // Now delete the root if (root.left == null) { root = root.right; } else { x = root.right; root = root.left; splay(result); root.right = x; } } @Override public K element() { BinaryNode<K> x = root; if (root == null) { throw new NoSuchElementException(); } while (x.left != null) { x = x.left; } splay(x.value); return x.value; } private void fill(Object[] result) { BinaryNode<K> node = root; int i = 0; while (null != node) { Deque<BinaryNode<K>> nodes = new ArrayDeque<BinaryNode<K>>(); while (!nodes.isEmpty() || null != node) { if (null != node) { nodes.push(node); node = node.left; } else { node = nodes.pop(); i = node.fill(i, result); node = node.right; } } } } /** * Test if the tree is logically empty. * * @return true if empty, false otherwise. */ @Override public boolean isEmpty() { return root == null; } @Override public Iterator<K> iterator() { return new Iterator<K>() { Deque<BinaryNode<K>> visiting = new ArrayDeque<BinaryNode<K>>(); BinaryNode<K> currentRoot = root; duplicate<K> duplicates; @Override public boolean hasNext() { return duplicates == null && currentRoot != null; } @Override public K next() { if (!hasNext()) { throw new NoSuchElementException(); } K result; if (duplicates != null) { result = duplicates.value; duplicates = duplicates.next; return result; } if (visiting.isEmpty()) { pushLeft(currentRoot); } BinaryNode<K> node = visiting.pop(); result = node.value; duplicates = node.duplicates; if (node.right != null) { BinaryNode<K> right = node.right; pushLeft(right); } if (visiting.isEmpty()) { currentRoot = null; } return result; } private void pushLeft(BinaryNode<K> node) { if (node != null) { visiting.push(node); pushLeft(node.left); } } @Override public void remove() { throw new UnsupportedOperationException(); } }; } @Override public boolean offer(K e) { return add(e); } @Override public K peek() { BinaryNode<K> x = root; if (root == null) { return null; } while (x.left != null) { x = x.left; } splay(x.value); return x.value; } @Override public K poll() { BinaryNode<K> x = root; if (root == null) { return null; } while (x.left != null) { x = x.left; } splay(x.value); K result = x.value; deleteRoot(result); return result; } @Override public K remove() { BinaryNode<K> x = root; if (root == null) { throw new NoSuchElementException(); } while (x.left != null) { x = x.left; } splay(x.value); K result = x.value; if (x.removeValue()) { deleteRoot(result); } size--; return result; } @Override public boolean remove(Object o) { if (o == null) { throw new NullPointerException("null elements are not allowed"); } @SuppressWarnings("unchecked") K key = (K) o; splay(key); if (key.compareTo(root.value) != 0) { return false; } if (root.removeValue()) { deleteRoot(key); } size--; return true; } @Override public boolean removeAll(Collection<?> c) { boolean changed = false; for (Object o : c) { changed |= remove(o); } return changed; } @Override public boolean retainAll(Collection<?> c) { throw new UnsupportedOperationException(); } @Override public int size() { return size; } /** * splay(key) does the splay operation on the given key. If key is in the * tree, then the BinaryNode containing that key becomes the root. If key is * not in the tree, then after the splay, key.root is either the greatest * key < key in the tree, or the lest key > key in the tree. * * This means, among other things, that if you splay with a key that's * larger than any in the tree, the rightmost node of the tree becomes the * root. This property is used in the delete() method. */ private void splay(K key) { BinaryNode<K> l, r, t, y, header; header = new BinaryNode<K>(null); l = r = header; t = root; header.left = header.right = null; for (;;) { if (key.compareTo(t.value) < 0) { if (t.left == null) { break; } if (key.compareTo(t.left.value) < 0) { y = t.left; /* rotate right */ t.left = y.right; y.right = t; t = y; if (t.left == null) { break; } } r.left = t; /* link right */ r = t; t = t.left; } else if (key.compareTo(t.value) > 0) { if (t.right == null) { break; } if (key.compareTo(t.right.value) > 0) { y = t.right; /* rotate left */ t.right = y.left; y.left = t; t = y; if (t.right == null) { break; } } l.right = t; /* link left */ l = t; t = t.right; } else { break; } } l.right = t.left; /* assemble */ r.left = t.right; t.left = header.right; t.right = header.left; root = t; } @Override public Object[] toArray() { Object[] result = new Object[size]; fill(result); return result; } @Override public <T> T[] toArray(T[] a) { if (a.length < size) { @SuppressWarnings("unchecked") T[] newInstance = (T[]) Array.newInstance(a.getClass().getComponentType(), size); a = newInstance; } fill(a); return a; } /** * Returns a string representation of this collection. The string * representation consists of a list of the collection's elements in the * order they are returned by its iterator, enclosed in square brackets ( * <tt>"[]"</tt>). Adjacent elements are separated by the characters * <tt>", "</tt> (comma and space). Elements are converted to strings as by * {@link String#valueOf(Object)}. * * @return a string representation of this collection */ @Override public String toString() { if (isEmpty()) { return "[]"; } StringBuilder sb = new StringBuilder(); sb.append('['); BinaryNode<K> node = root; int i = 0; while (null != node) { Deque<BinaryNode<K>> nodes = new ArrayDeque<BinaryNode<K>>(); while (!nodes.isEmpty() || null != node) { if (null != node) { nodes.push(node); node = node.left; } else { node = nodes.pop(); K e = node.value; sb.append(e == this ? "(this Collection)" : e); i++; duplicate<K> current = node.duplicates; while (current != null) { e = current.value; sb.append(", "); sb.append(e == this ? "(this Collection)" : e); i++; current = current.next; } node = node.right; if (i != size) { sb.append(", "); } } } } sb.append(']'); return sb.toString(); } }