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

drv.AVLTreeSet.drv Maven / Gradle / Ivy

Go to download

fastutil extends the Java Collections Framework by providing type-specific maps, sets, lists, and queues with a small memory footprint and fast operations; it provides also big (64-bit) arrays, sets, and lists, sorting algorithms, fast, practical I/O classes for binary and text files, and facilities for memory mapping large files. This jar (fastutil-core.jar) contains data structures based on integers, longs, doubles, and objects, only; fastutil.jar contains all classes. If you have both jars in your dependencies, this jar should be excluded.

The newest version!
/*
 * Copyright (C) 2002-2024 Sebastiano Vigna
 *
 * 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 PACKAGE;

import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.SortedSet;
import java.util.NoSuchElementException;

/** A type-specific AVL tree set with a fast, small-footprint implementation.
 *
 * 

The iterators provided by this class are type-specific {@link * it.unimi.dsi.fastutil.BidirectionalIterator bidirectional iterators}. * Moreover, the iterator returned by {@code iterator()} can be safely cast * to a type-specific {@linkplain java.util.ListIterator list iterator}. */ public class AVL_TREE_SET KEY_GENERIC extends ABSTRACT_SORTED_SET KEY_GENERIC implements java.io.Serializable, Cloneable, SORTED_SET KEY_GENERIC { /** A reference to the root entry. */ protected transient Entry KEY_GENERIC tree; /** Number of elements in this set. */ protected int count; /** The entry of the first element of this set. */ protected transient Entry KEY_GENERIC firstEntry; /** The entry of the last element of this set. */ protected transient Entry KEY_GENERIC lastEntry; /** This set's comparator, as provided in the constructor. */ protected Comparator storedComparator; /** This set's actual comparator; it may differ from {@link #storedComparator} because it is always a type-specific comparator, so it could be derived from the former by wrapping. */ protected transient KEY_COMPARATOR KEY_SUPER_GENERIC actualComparator; private static final long serialVersionUID = -7046029254386353130L; { allocatePaths(); } /** Creates a new empty tree set. */ public AVL_TREE_SET() { tree = null; count = 0; } /** Generates the comparator that will be actually used. * *

When a given {@link Comparator} is specified and stored in {@link * #storedComparator}, we must check whether it is type-specific. If it is * so, we can used directly, and we store it in {@link #actualComparator}. Otherwise, * we adapt it using a helper static method. */ private void setActualComparator() { #if KEY_CLASS_Object actualComparator = storedComparator; #else actualComparator = COMPARATORS.AS_KEY_COMPARATOR(storedComparator); #endif } /** Creates a new empty tree set with the given comparator. * * @param c a {@link Comparator} (even better, a type-specific comparator). */ public AVL_TREE_SET(final Comparator c) { this(); storedComparator = c; setActualComparator(); } /** Creates a new tree set copying a given set. * * @param c a collection to be copied into the new tree set. */ public AVL_TREE_SET(final Collection c) { this(); addAll(c); } /** Creates a new tree set copying a given sorted set (and its {@link Comparator}). * * @param s a {@link SortedSet} to be copied into the new tree set. */ public AVL_TREE_SET(final SortedSet s) { this(s.comparator()); addAll(s); } /** Creates a new tree set copying a given type-specific collection. * * @param c a type-specific collection to be copied into the new tree set. */ public AVL_TREE_SET(final COLLECTION KEY_EXTENDS_GENERIC c) { this(); addAll(c); } /** Creates a new tree set copying a given type-specific sorted set (and its {@link Comparator}). * * @param s a type-specific sorted set to be copied into the new tree set. */ public AVL_TREE_SET(final SORTED_SET KEY_GENERIC s) { this(s.comparator()); addAll(s); } /** Creates a new tree set using elements provided by a type-specific iterator. * * @param i a type-specific iterator whose elements will fill the set. */ public AVL_TREE_SET(final STD_KEY_ITERATOR KEY_EXTENDS_GENERIC i) { while(i.hasNext()) add(i.NEXT_KEY()); } #if KEYS_PRIMITIVE /** Creates a new tree set using elements provided by an iterator. * * @param i an iterator whose elements will fill the set. */ SUPPRESS_WARNINGS_KEY_UNCHECKED public AVL_TREE_SET(final Iterator i) { this(ITERATORS.AS_KEY_ITERATOR(i)); } #endif /** Creates a new tree set and fills it with the elements of a given array using a given {@link Comparator}. * * @param a an array whose elements will be used to fill the set. * @param offset the first element to use. * @param length the number of elements to use. * @param c a {@link Comparator} (even better, a type-specific comparator). */ public AVL_TREE_SET(final KEY_GENERIC_TYPE[] a, final int offset, final int length, final Comparator c) { this(c); ARRAYS.ensureOffsetLength(a, offset, length); for(int i = 0; i < length; i++) add(a[offset + i]); } /** Creates a new tree set and fills it with the elements of a given array. * * @param a an array whose elements will be used to fill the set. * @param offset the first element to use. * @param length the number of elements to use. */ public AVL_TREE_SET(final KEY_GENERIC_TYPE[] a, final int offset, final int length) { this(a, offset, length, null); } /** Creates a new tree set copying the elements of an array. * * @param a an array to be copied into the new tree set. */ public AVL_TREE_SET(final KEY_GENERIC_TYPE[] a) { this(); int i = a.length; while(i-- != 0) add(a[i]); } /** Creates a new tree set copying the elements of an array using a given {@link Comparator}. * * @param a an array to be copied into the new tree set. * @param c a {@link Comparator} (even better, a type-specific comparator). */ public AVL_TREE_SET(final KEY_GENERIC_TYPE[] a, final Comparator c) { this(c); int i = a.length; while(i-- != 0) add(a[i]); } /* * The following methods implements some basic building blocks used by * all accessors. They are (and should be maintained) identical to those used in AVLTreeMap.drv. * * The add()/remove() code is derived from Ben Pfaff's GNU libavl * (https://adtinfo.org/). If you want to understand what's * going on, you should have a look at the literate code contained therein * first. */ /** Compares two keys in the right way. * *

This method uses the {@link #actualComparator} if it is non-{@code null}. * Otherwise, it resorts to primitive type comparisons or to {@link Comparable#compareTo(Object) compareTo()}. * * @param k1 the first key. * @param k2 the second key. * @return a number smaller than, equal to or greater than 0, as usual * (i.e., when k1 < k2, k1 = k2 or k1 > k2, respectively). */ SUPPRESS_WARNINGS_KEY_UNCHECKED final int compare(final KEY_GENERIC_TYPE k1, final KEY_GENERIC_TYPE k2) { return actualComparator == null ? KEY_CMP(k1, k2) : actualComparator.compare(k1, k2); } /** Returns the entry corresponding to the given key, if it is in the tree; {@code null}, otherwise. * * @param k the key to search for. * @return the corresponding entry, or {@code null} if no entry with the given key exists. */ private Entry KEY_GENERIC findKey(final KEY_GENERIC_TYPE k) { Entry KEY_GENERIC e = tree; int cmp; while (e != null && (cmp = compare(k, e.key)) != 0) e = cmp < 0 ? e.left() : e.right(); return e; } /** Locates a key. * * @param k a key. * @return the last entry on a search for the given key; this will be * the given key, if it present; otherwise, it will be either the smallest greater key or the greatest smaller key. */ final Entry KEY_GENERIC locateKey(final KEY_GENERIC_TYPE k) { Entry KEY_GENERIC e = tree, last = tree; int cmp = 0; while (e != null && (cmp = compare(k, e.key)) != 0) { last = e; e = cmp < 0 ? e.left() : e.right(); } return cmp == 0 ? e : last; } /** This vector remembers the path followed during the current insertion. It suffices for about 232 entries. */ private transient boolean dirPath[]; private void allocatePaths() { dirPath = new boolean[48]; } @Override public boolean add(final KEY_GENERIC_TYPE k) { REQUIRE_KEY_NON_NULL(k) if (tree == null) { // The case of the empty tree is treated separately. count++; tree = lastEntry = firstEntry = new Entry KEY_GENERIC_DIAMOND(k); } else { Entry KEY_GENERIC p = tree, q = null, y = tree, z = null, e = null, w = null; int cmp, i = 0; while(true) { if ((cmp = compare(k, p.key)) == 0) return false; if (p.balance() != 0) { i = 0; z = q; y = p; } if (dirPath[i++] = cmp > 0) { if (p.succ()) { count++; e = new Entry KEY_GENERIC_DIAMOND(k); if (p.right == null) lastEntry = e; e.left = p; e.right = p.right; p.right(e); break; } q = p; p = p.right; } else { if (p.pred()) { count++; e = new Entry KEY_GENERIC_DIAMOND(k); if (p.left == null) firstEntry = e; e.right = p; e.left = p.left; p.left(e); break; } q = p; p = p.left; } } p = y; i = 0; while(p != e) { if (dirPath[i]) p.incBalance(); else p.decBalance(); p = dirPath[i++] ? p.right : p.left; } if (y.balance() == -2) { Entry KEY_GENERIC x = y.left; if (x.balance() == -1) { w = x; if (x.succ()) { x.succ(false); y.pred(x); } else y.left = x.right; x.right = y; x.balance(0); y.balance(0); } else { assert x.balance() == 1; w = x.right; x.right = w.left; w.left = x; y.left = w.right; w.right = y; if (w.balance() == -1) { x.balance(0); y.balance(1); } else if (w.balance() == 0) { x.balance(0); y.balance(0); } else { x.balance(-1); y.balance(0); } w.balance(0); if (w.pred()) { x.succ(w); w.pred(false); } if (w.succ()) { y.pred(w); w.succ(false); } } } else if (y.balance() == +2) { Entry KEY_GENERIC x = y.right; if (x.balance() == 1) { w = x; if (x.pred()) { x.pred(false); y.succ(x); } else y.right = x.left; x.left = y; x.balance(0); y.balance(0); } else { assert x.balance() == -1; w = x.left; x.left = w.right; w.right = x; y.right = w.left; w.left = y; if (w.balance() == 1) { x.balance(0); y.balance(-1); } else if (w.balance() == 0) { x.balance(0); y.balance(0); } else { x.balance(1); y.balance(0); } w.balance(0); if (w.pred()) { y.succ(w); w.pred(false); } if (w.succ()) { x.pred(w); w.succ(false); } } } else return true; if (z == null) tree = w; else { if (z.left == y) z.left = w; else z.right = w; } } return true; } /** Finds the parent of an entry. * * @param e a node of the tree. * @return the parent of the given node, or {@code null} for the root. */ private Entry KEY_GENERIC parent(final Entry KEY_GENERIC e) { if (e == tree) return null; Entry KEY_GENERIC x, y, p; x = y = e; while(true) { if (y.succ()) { p = y.right; if (p == null || p.left != e) { while(! x.pred()) x = x.left; p = x.left; } return p; } else if (x.pred()) { p = x.left; if (p == null || p.right != e) { while(! y.succ()) y = y.right; p = y.right; } return p; } x = x.left; y = y.right; } } SUPPRESS_WARNINGS_KEY_UNCHECKED @Override public boolean remove(final KEY_TYPE k) { if (tree == null) return false; int cmp; Entry KEY_GENERIC p = tree, q = null; boolean dir = false; final KEY_GENERIC_TYPE kk = KEY_GENERIC_CAST k; while(true) { if ((cmp = compare(kk, p.key)) == 0) break; else if (dir = cmp > 0) { q = p; if ((p = p.right()) == null) return false; } else { q = p; if ((p = p.left()) == null) return false; } } if (p.left == null) firstEntry = p.next(); if (p.right == null) lastEntry = p.prev(); if (p.succ()) { if (p.pred()) { if (q != null) { if (dir) q.succ(p.right); else q.pred(p.left); } else tree = dir ? p.right : p.left; } else { p.prev().right = p.right; if (q != null) { if (dir) q.right = p.left; else q.left = p.left; } else tree = p.left; } } else { Entry KEY_GENERIC r = p.right; if (r.pred()) { r.left = p.left; r.pred(p.pred()); if (! r.pred()) r.prev().right = r; if (q != null) { if (dir) q.right = r; else q.left = r; } else tree = r; r.balance(p.balance()); q = r; dir = true; } else { Entry KEY_GENERIC s; while(true) { s = r.left; if (s.pred()) break; r = s; } if (s.succ()) r.pred(s); else r.left = s.right; s.left = p.left; if (! p.pred()) { p.prev().right = s; s.pred(false); } s.right = p.right; s.succ(false); if (q != null) { if (dir) q.right = s; else q.left = s; } else tree = s; s.balance(p.balance()); q = r; dir = false; } } Entry KEY_GENERIC y; while(q != null) { y = q; q = parent(y); if (! dir) { dir = q != null && q.left != y; y.incBalance(); if (y.balance() == 1) break; else if (y.balance() == 2) { Entry KEY_GENERIC x = y.right; assert x != null; if (x.balance() == -1) { Entry KEY_GENERIC w; assert x.balance() == -1; w = x.left; x.left = w.right; w.right = x; y.right = w.left; w.left = y; if (w.balance() == 1) { x.balance(0); y.balance(-1); } else if (w.balance() == 0) { x.balance(0); y.balance(0); } else { assert w.balance() == -1; x.balance(1); y.balance(0); } w.balance(0); if (w.pred()) { y.succ(w); w.pred(false); } if (w.succ()) { x.pred(w); w.succ(false); } if (q != null) { if (dir) q.right = w; else q.left = w; } else tree = w; } else { if (q != null) { if (dir) q.right = x; else q.left = x; } else tree = x; if (x.balance() == 0) { y.right = x.left; x.left = y; x.balance(-1); y.balance(+1); break; } assert x.balance() == 1; if (x.pred()) { y.succ(true); x.pred(false); } else y.right = x.left; x.left = y; y.balance(0); x.balance(0); } } } else { dir = q != null && q.left != y; y.decBalance(); if (y.balance() == -1) break; else if (y.balance() == -2) { Entry KEY_GENERIC x = y.left; assert x != null; if (x.balance() == 1) { Entry KEY_GENERIC w; assert x.balance() == 1; w = x.right; x.right = w.left; w.left = x; y.left = w.right; w.right = y; if (w.balance() == -1) { x.balance(0); y.balance(1); } else if (w.balance() == 0) { x.balance(0); y.balance(0); } else { assert w.balance() == 1; x.balance(-1); y.balance(0); } w.balance(0); if (w.pred()) { x.succ(w); w.pred(false); } if (w.succ()) { y.pred(w); w.succ(false); } if (q != null) { if (dir) q.right = w; else q.left = w; } else tree = w; } else { if (q != null) { if (dir) q.right = x; else q.left = x; } else tree = x; if (x.balance() == 0) { y.left = x.right; x.right = y; x.balance(+1); y.balance(-1); break; } assert x.balance() == -1; if (x.succ()) { y.pred(true); x.succ(false); } else y.left = x.right; x.right = y; y.balance(0); x.balance(0); } } } } count--; return true; } SUPPRESS_WARNINGS_KEY_UNCHECKED @Override public boolean contains(final KEY_TYPE k) { return findKey(KEY_GENERIC_CAST k) != null; } #if KEY_CLASS_Object SUPPRESS_WARNINGS_KEY_UNCHECKED public K get(final KEY_TYPE k) { final Entry KEY_GENERIC entry = findKey(KEY_GENERIC_CAST k); return entry == null ? null : entry.key; } #endif @Override public void clear() { count = 0; tree = null; firstEntry = lastEntry = null; } /** This class represent an entry in a tree set. * *

We use the only "metadata", i.e., {@link Entry#info}, to store * information about balance, predecessor status and successor status. * *

Note that since the class is recursive, it can be * considered equivalently a tree. */ private static final class Entry KEY_GENERIC implements Cloneable { /** If the bit in this mask is true, {@link #right} points to a successor. */ private static final int SUCC_MASK = 1 << 31; /** If the bit in this mask is true, {@link #left} points to a predecessor. */ private static final int PRED_MASK = 1 << 30; /** The bits in this mask hold the node balance info. You can get it just by casting to byte. */ private static final int BALANCE_MASK = 0xFF; /** The key of this entry. */ KEY_GENERIC_TYPE key; /** The pointers to the left and right subtrees. */ Entry KEY_GENERIC left, right; /** This integers holds different information in different bits (see {@link #SUCC_MASK}, {@link #PRED_MASK} and {@link #BALANCE_MASK}). */ int info; Entry() {} /** Creates a new entry with the given key. * * @param k a key. */ Entry(final KEY_GENERIC_TYPE k) { this.key = k; info = SUCC_MASK | PRED_MASK; } /** Returns the left subtree. * * @return the left subtree ({@code null} if the left * subtree is empty). */ Entry KEY_GENERIC left() { return (info & PRED_MASK) != 0 ? null : left; } /** Returns the right subtree. * * @return the right subtree ({@code null} if the right * subtree is empty). */ Entry KEY_GENERIC right() { return (info & SUCC_MASK) != 0 ? null : right; } /** Checks whether the left pointer is really a predecessor. * @return true if the left pointer is a predecessor. */ boolean pred() { return (info & PRED_MASK) != 0; } /** Checks whether the right pointer is really a successor. * @return true if the right pointer is a successor. */ boolean succ() { return (info & SUCC_MASK) != 0; } /** Sets whether the left pointer is really a predecessor. * @param pred if true then the left pointer will be considered a predecessor. */ void pred(final boolean pred) { if (pred) info |= PRED_MASK; else info &= ~PRED_MASK; } /** Sets whether the right pointer is really a successor. * @param succ if true then the right pointer will be considered a successor. */ void succ(final boolean succ) { if (succ) info |= SUCC_MASK; else info &= ~SUCC_MASK; } /** Sets the left pointer to a predecessor. * @param pred the predecessr. */ void pred(final Entry KEY_GENERIC pred) { info |= PRED_MASK; left = pred; } /** Sets the right pointer to a successor. * @param succ the successor. */ void succ(final Entry KEY_GENERIC succ) { info |= SUCC_MASK; right = succ; } /** Sets the left pointer to the given subtree. * @param left the new left subtree. */ void left(final Entry KEY_GENERIC left) { info &= ~PRED_MASK; this.left = left; } /** Sets the right pointer to the given subtree. * @param right the new right subtree. */ void right(final Entry KEY_GENERIC right) { info &= ~SUCC_MASK; this.right = right; } /** Returns the current level of the node. * @return the current level of this node. */ int balance() { return (byte)info; } /** Sets the level of this node. * @param level the new level of this node. */ void balance(int level) { info &= ~BALANCE_MASK; info |= (level & BALANCE_MASK); } /** Increments the level of this node. */ void incBalance() { info = info & ~BALANCE_MASK | ((byte)info + 1) & 0xFF; } /** Decrements the level of this node. */ protected void decBalance() { info = info & ~BALANCE_MASK | ((byte)info - 1) & 0xFF; } /** Computes the next entry in the set order. * * @return the next entry ({@code null}) if this is the last entry). */ Entry KEY_GENERIC next() { Entry KEY_GENERIC next = this.right; if ((info & SUCC_MASK) == 0) while ((next.info & PRED_MASK) == 0) next = next.left; return next; } /** Computes the previous entry in the set order. * * @return the previous entry ({@code null}) if this is the first entry). */ Entry KEY_GENERIC prev() { Entry KEY_GENERIC prev = this.left; if ((info & PRED_MASK) == 0) while ((prev.info & SUCC_MASK) == 0) prev = prev.right; return prev; } @Override SUPPRESS_WARNINGS_KEY_UNCHECKED public Entry KEY_GENERIC clone() { Entry KEY_GENERIC c; try { c = (Entry KEY_GENERIC)super.clone(); } catch(CloneNotSupportedException cantHappen) { throw new InternalError(); } c.key = key; c.info = info; return c; } @Override public boolean equals(final Object o) { if (!(o instanceof Entry)) return false; Entry KEY_GENERIC_WILDCARD e = (Entry KEY_GENERIC_WILDCARD)o; return KEY_EQUALS(key, e.key); } @Override public int hashCode() { return KEY2JAVAHASH_NOT_NULL(key); } @Override public String toString() { return String.valueOf(key); } /* public void prettyPrint() { prettyPrint(0); } public void prettyPrint(int level) { if (pred()) { for (int i = 0; i < level; i++) System.err.print(" "); System.err.println("pred: " + left); } else if (left != null) left.prettyPrint(level +1); for (int i = 0; i < level; i++) System.err.print(" "); System.err.println(key + " (" + level() + ")"); if (succ()) { for (int i = 0; i < level; i++) System.err.print(" "); System.err.println("succ: " + right); } else if (right != null) right.prettyPrint(level + 1); } */ } /* public void prettyPrint() { System.err.println("size: " + count); if (tree != null) tree.prettyPrint(); } */ @Override public int size() { return count; } @Override public boolean isEmpty() { return count == 0; } @Override public KEY_GENERIC_TYPE FIRST() { if (tree == null) throw new NoSuchElementException(); return firstEntry.key; } @Override public KEY_GENERIC_TYPE LAST() { if (tree == null) throw new NoSuchElementException(); return lastEntry.key; } /** An iterator on the whole range. * *

This class can iterate in both directions on a threaded tree. */ private class SetIterator implements KEY_LIST_ITERATOR KEY_GENERIC { /** The entry that will be returned by the next call to {@link java.util.ListIterator#previous()} (or {@code null} if no previous entry exists). */ Entry KEY_GENERIC prev; /** The entry that will be returned by the next call to {@link java.util.ListIterator#next()} (or {@code null} if no next entry exists). */ Entry KEY_GENERIC next; /** The last entry that was returned (or {@code null} if we did not iterate or used {@link #remove()}). */ Entry KEY_GENERIC curr; /** The current index (in the sense of a {@link java.util.ListIterator}). Note that this value is not meaningful when this {@link SetIterator} has been created using the nonempty constructor.*/ int index = 0; SetIterator() { next = firstEntry; } SetIterator(final KEY_GENERIC_TYPE k) { if ((next = locateKey(k)) != null) { if (compare(next.key, k) <= 0) { prev = next; next = next.next(); } else prev = next.prev(); } } @Override public boolean hasNext() { return next != null; } @Override public boolean hasPrevious() { return prev != null; } void updateNext() { next = next.next(); } Entry KEY_GENERIC nextEntry() { if (! hasNext()) throw new NoSuchElementException(); curr = prev = next; index++; updateNext(); return curr; } @Override public KEY_GENERIC_TYPE NEXT_KEY() { return nextEntry().key; } @Override public KEY_GENERIC_TYPE PREV_KEY() { return previousEntry().key; } void updatePrevious() { prev = prev.prev(); } Entry KEY_GENERIC previousEntry() { if (! hasPrevious()) throw new NoSuchElementException(); curr = next = prev; index--; updatePrevious(); return curr; } @Override public int nextIndex() { return index; } @Override public int previousIndex() { return index - 1; } @Override public void remove() { if (curr == null) throw new IllegalStateException(); /* If the last operation was a next(), we are removing an entry that preceeds the current index, and thus we must decrement it. */ if (curr == prev) index--; next = prev = curr; updatePrevious(); updateNext(); AVL_TREE_SET.this.remove(curr.key); curr = null; } } @Override public KEY_BIDI_ITERATOR KEY_GENERIC iterator() { return new SetIterator(); } @Override public KEY_BIDI_ITERATOR KEY_GENERIC iterator(final KEY_GENERIC_TYPE from) { return new SetIterator(from); } @Override public KEY_COMPARATOR KEY_SUPER_GENERIC comparator() { return actualComparator; } @Override public SORTED_SET KEY_GENERIC headSet(final KEY_GENERIC_TYPE to) { return new Subset(KEY_NULL, true, to, false); } @Override public SORTED_SET KEY_GENERIC tailSet(final KEY_GENERIC_TYPE from) { return new Subset(from, false, KEY_NULL, true); } @Override public SORTED_SET KEY_GENERIC subSet(final KEY_GENERIC_TYPE from, final KEY_GENERIC_TYPE to) { return new Subset(from, false, to, false); } /** A subset with given range. * *

This class represents a subset. One has to specify the left/right * limits (which can be set to -∞ or ∞). Since the subset is a * view on the set, at a given moment it could happen that the limits of * the range are not any longer in the main set. Thus, things such as * {@link java.util.SortedSet#first()} or {@link java.util.SortedSet#size()} must be always computed * on-the-fly. */ private final class Subset extends ABSTRACT_SORTED_SET KEY_GENERIC implements java.io.Serializable, SORTED_SET KEY_GENERIC { private static final long serialVersionUID = -7046029254386353129L; /** The start of the subset range, unless {@link #bottom} is true. */ KEY_GENERIC_TYPE from; /** The end of the subset range, unless {@link #top} is true. */ KEY_GENERIC_TYPE to; /** If true, the subset range starts from -∞. */ boolean bottom; /** If true, the subset range goes to ∞. */ boolean top; /** Creates a new subset with given key range. * * @param from the start of the subset range. * @param bottom if true, the first parameter is ignored and the range starts from -∞. * @param to the end of the subset range. * @param top if true, the third parameter is ignored and the range goes to ∞. */ public Subset(final KEY_GENERIC_TYPE from, final boolean bottom, final KEY_GENERIC_TYPE to, final boolean top) { if (! bottom && ! top && AVL_TREE_SET.this.compare(from, to) > 0) throw new IllegalArgumentException("Start element (" + from + ") is larger than end element (" + to + ")"); this.from = from; this.bottom = bottom; this.to = to; this.top = top; } @Override public void clear() { final SubsetIterator i = new SubsetIterator(); while(i.hasNext()) { i.NEXT_KEY(); i.remove(); } } /** Checks whether a key is in the subset range. * @param k a key. * @return true if is the key is in the subset range. */ final boolean in(final KEY_GENERIC_TYPE k) { return (bottom || AVL_TREE_SET.this.compare(k, from) >= 0) && (top || AVL_TREE_SET.this.compare(k, to) < 0); } SUPPRESS_WARNINGS_KEY_UNCHECKED @Override public boolean contains(final KEY_TYPE k) { return in(KEY_GENERIC_CAST k) && AVL_TREE_SET.this.contains(k); } @Override public boolean add(final KEY_GENERIC_TYPE k) { if (! in(k)) throw new IllegalArgumentException("Element (" + k + ") out of range [" + (bottom ? "-" : String.valueOf(from)) + ", " + (top ? "-" : String.valueOf(to)) + ")"); return AVL_TREE_SET.this.add(k); } SUPPRESS_WARNINGS_KEY_UNCHECKED @Override public boolean remove(final KEY_TYPE k) { if (! in(KEY_GENERIC_CAST k)) return false; return AVL_TREE_SET.this.remove(k); } @Override public int size() { final SubsetIterator i = new SubsetIterator(); int n = 0; while(i.hasNext()) { n++; i.NEXT_KEY(); } return n; } @Override public boolean isEmpty() { return ! new SubsetIterator().hasNext(); } @Override public KEY_COMPARATOR KEY_SUPER_GENERIC comparator() { return actualComparator; } @Override public KEY_BIDI_ITERATOR KEY_GENERIC iterator() { return new SubsetIterator(); } @Override public KEY_BIDI_ITERATOR KEY_GENERIC iterator(final KEY_GENERIC_TYPE from) { return new SubsetIterator(from); } @Override public SORTED_SET KEY_GENERIC headSet(final KEY_GENERIC_TYPE to) { if (top) return new Subset(from, bottom, to, false); return compare(to, this.to) < 0 ? new Subset(from, bottom, to, false) : this; } @Override public SORTED_SET KEY_GENERIC tailSet(final KEY_GENERIC_TYPE from) { if (bottom) return new Subset(from, false, to, top); return compare(from, this.from) > 0 ? new Subset(from, false, to, top) : this; } @Override public SORTED_SET KEY_GENERIC subSet(KEY_GENERIC_TYPE from, KEY_GENERIC_TYPE to) { if (top && bottom) return new Subset(from, false, to, false); if (! top) to = compare(to, this.to) < 0 ? to : this.to; if (! bottom) from = compare(from, this.from) > 0 ? from : this.from; if (! top && ! bottom && from == this.from && to == this.to) return this; return new Subset(from, false, to, false); } /** Locates the first entry. * * @return the first entry of this subset, or {@code null} if the subset is empty. */ public AVL_TREE_SET.Entry KEY_GENERIC firstEntry() { if (tree == null) return null; // If this subset goes to -infinity, we return the main set first entry; otherwise, we locate the start of the set. AVL_TREE_SET.Entry KEY_GENERIC e; if (bottom) e = firstEntry; else { e = locateKey(from); // If we find either the start or something greater we're OK. if (compare(e.key, from) < 0) e = e.next(); } // Finally, if this subset doesn't go to infinity, we check that the resulting key isn't greater than the end. if (e == null || ! top && compare(e.key, to) >= 0) return null; return e; } /** Locates the last entry. * * @return the last entry of this subset, or {@code null} if the subset is empty. */ public AVL_TREE_SET.Entry KEY_GENERIC lastEntry() { if (tree == null) return null; // If this subset goes to infinity, we return the main set last entry; otherwise, we locate the end of the set. AVL_TREE_SET.Entry KEY_GENERIC e; if (top) e = lastEntry; else { e = locateKey(to); // If we find something smaller than the end we're OK. if (compare(e.key, to) >= 0) e = e.prev(); } // Finally, if this subset doesn't go to -infinity, we check that the resulting key isn't smaller than the start. if (e == null || ! bottom && compare(e.key, from) < 0) return null; return e; } @Override public KEY_GENERIC_TYPE FIRST() { AVL_TREE_SET.Entry KEY_GENERIC e = firstEntry(); if (e == null) throw new NoSuchElementException(); return e.key; } @Override public KEY_GENERIC_TYPE LAST() { AVL_TREE_SET.Entry KEY_GENERIC e = lastEntry(); if (e == null) throw new NoSuchElementException(); return e.key; } /** An iterator for subranges. * *

This class inherits from {@link SetIterator}, but overrides the methods that * update the pointer after a {@link java.util.ListIterator#next()} or {@link java.util.ListIterator#previous()}. If we would * move out of the range of the subset we just overwrite the next or previous * entry with {@code null}. */ private final class SubsetIterator extends SetIterator { SubsetIterator() { next = firstEntry(); } SubsetIterator(final KEY_GENERIC_TYPE k) { this(); if (next != null) { if (! bottom && compare(k, next.key) < 0) prev = null; else if (! top && compare(k, (prev = lastEntry()).key) >= 0) next = null; else { next = locateKey(k); if (compare(next.key, k) <= 0) { prev = next; next = next.next(); } else prev = next.prev(); } } } @Override void updatePrevious() { prev = prev.prev(); if (! bottom && prev != null && AVL_TREE_SET.this.compare(prev.key, from) < 0) prev = null; } @Override void updateNext() { next = next.next(); if (! top && next != null && AVL_TREE_SET.this.compare(next.key, to) >= 0) next = null; } } } /** Returns a deep copy of this tree set. * *

This method performs a deep copy of this tree set; the data stored in the * set, however, is not cloned. Note that this makes a difference only for object keys. * * @return a deep copy of this tree set. */ @Override SUPPRESS_WARNINGS_KEY_UNCHECKED public Object clone() { AVL_TREE_SET KEY_GENERIC c; try { c = (AVL_TREE_SET KEY_GENERIC)super.clone(); } catch(CloneNotSupportedException cantHappen) { throw new InternalError(); } c.allocatePaths(); if (count != 0) { // Also this apparently unfathomable code is derived from GNU libavl. Entry KEY_GENERIC e, p, q, rp = new Entry KEY_GENERIC_DIAMOND(), rq = new Entry KEY_GENERIC_DIAMOND(); p = rp; rp.left(tree); q = rq; rq.pred(null); while(true) { if (! p.pred()) { e = p.left.clone(); e.pred(q.left); e.succ(q); q.left(e); p = p.left; q = q.left; } else { while(p.succ()) { p = p.right; if (p == null) { q.right = null; c.tree = rq.left; c.firstEntry = c.tree; while(c.firstEntry.left != null) c.firstEntry = c.firstEntry.left; c.lastEntry = c.tree; while(c.lastEntry.right != null) c.lastEntry = c.lastEntry.right; return c; } q = q.right; } p = p.right; q = q.right; } if (! p.succ()) { e = p.right.clone(); e.succ(q.right); e.pred(q); q.right(e); } } } return c; } private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { int n = count; SetIterator i = new SetIterator(); s.defaultWriteObject(); while(n-- != 0) s.WRITE_KEY(i.NEXT_KEY()); } /** Reads the given number of entries from the input stream, returning the corresponding tree. * * @param s the input stream. * @param n the (positive) number of entries to read. * @param pred the entry containing the key that preceeds the first key in the tree. * @param succ the entry containing the key that follows the last key in the tree. */ SUPPRESS_WARNINGS_KEY_UNCHECKED private Entry KEY_GENERIC readTree(final java.io.ObjectInputStream s, final int n, final Entry KEY_GENERIC pred, final Entry KEY_GENERIC succ) throws java.io.IOException, ClassNotFoundException { if (n == 1) { final Entry KEY_GENERIC top = new Entry KEY_GENERIC_DIAMOND(KEY_GENERIC_CAST s.READ_KEY()); top.pred(pred); top.succ(succ); return top; } if (n == 2) { /* We handle separately this case so that recursion will *always* be on nonempty subtrees. */ final Entry KEY_GENERIC top = new Entry KEY_GENERIC_DIAMOND(KEY_GENERIC_CAST s.READ_KEY()); top.right(new Entry KEY_GENERIC_DIAMOND(KEY_GENERIC_CAST s.READ_KEY())); top.right.pred(top); top.balance(1); top.pred(pred); top.right.succ(succ); return top; } // The right subtree is the largest one. final int rightN = n / 2, leftN = n - rightN - 1; final Entry KEY_GENERIC top = new Entry KEY_GENERIC_DIAMOND(); top.left(readTree(s, leftN, pred, top)); top.key = KEY_GENERIC_CAST s.READ_KEY(); top.right(readTree(s, rightN, top, succ)); if (n == (n & -n)) top.balance(1); // Quick test for determining whether n is a power of 2. return top; } private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); /* The storedComparator is now correctly set, but we must restore on-the-fly the actualComparator. */ setActualComparator(); allocatePaths(); if (count != 0) { tree = readTree(s, count, null, null); Entry KEY_GENERIC e; e = tree; while(e.left() != null) e = e.left(); firstEntry = e; e = tree; while(e.right() != null) e = e.right(); lastEntry = e; } } #ifdef ASSERTS_CODE private static KEY_GENERIC int checkTree(Entry KEY_GENERIC e) { if (e == null) return 0; final int leftN = checkTree(e.left()), rightN = checkTree(e.right()); if (leftN + e.balance() != rightN) throw new AssertionError("Mismatch between left tree size (" + leftN + "), right tree size (" + rightN + ") and balance (" + e.balance() + ")"); return Math.max(leftN , rightN) + 1; } #endif #ifdef TEST private static long seed = System.currentTimeMillis(); private static java.util.Random r = new java.util.Random(seed); private static KEY_TYPE genKey() { #if KEY_CLASS_Byte || KEY_CLASS_Short || KEY_CLASS_Character return (KEY_TYPE)(r.nextInt()); #elif KEYS_PRIMITIVE return r.NEXT_KEY(); #else return Integer.toBinaryString(r.nextInt()); #endif } private static java.text.NumberFormat format = new java.text.DecimalFormat("#,###.00"); private static java.text.FieldPosition p = new java.text.FieldPosition(0); private static String format(double d) { StringBuffer s = new StringBuffer(); return format.format(d, s, p).toString(); } private static void speedTest(int n, boolean comp) { int i, j; AVL_TREE_SET m; java.util.TreeSet t; KEY_TYPE k[] = new KEY_TYPE[n]; KEY_TYPE nk[] = new KEY_TYPE[n]; long ms; for(i = 0; i < n; i++) { k[i] = genKey(); nk[i] = genKey(); } double totAdd = 0, totYes = 0, totNo = 0, totIterFor = 0, totIterBack = 0, totRemYes = 0, d, dd; if (comp) { for(j = 0; j < 20; j++) { t = new java.util.TreeSet(); /* We first add all pairs to t. */ for(i = 0; i < n; i++) t.add(KEY2OBJ(k[i])); /* Then we remove the first half and put it back. */ for(i = 0; i < n/2; i++) t.remove(KEY2OBJ(k[i])); ms = System.currentTimeMillis(); for(i = 0; i < n/2; i++) t.add(KEY2OBJ(k[i])); d = System.currentTimeMillis() - ms; /* Then we remove the other half and put it back again. */ ms = System.currentTimeMillis(); for(i = n/2; i < n; i++) t.remove(KEY2OBJ(k[i])); dd = System.currentTimeMillis() - ms ; ms = System.currentTimeMillis(); for(i = n/2; i < n; i++) t.add(KEY2OBJ(k[i])); d += System.currentTimeMillis() - ms; if (j > 2) totAdd += n/d; System.out.print("Add: " + format(n/d) +" K/s "); /* Then we remove again the first half. */ ms = System.currentTimeMillis(); for(i = 0; i < n/2; i++) t.remove(KEY2OBJ(k[i])); dd += System.currentTimeMillis() - ms ; if (j > 2) totRemYes += n/dd; System.out.print("RemYes: " + format(n/dd) +" K/s "); /* And then we put it back. */ for(i = 0; i < n/2; i++) t.add(KEY2OBJ(k[i])); /* We check for pairs in t. */ ms = System.currentTimeMillis(); for(i = 0; i < n; i++) t.contains(KEY2OBJ(k[i])); d = 1.0 * n / (System.currentTimeMillis() - ms); if (j > 2) totYes += d; System.out.print("Yes: " + format(d) +" K/s "); /* We check for pairs not in t. */ ms = System.currentTimeMillis(); for(i = 0; i < n; i++) t.contains(KEY2OBJ(nk[i])); d = 1.0 * n / (System.currentTimeMillis() - ms); if (j > 2) totNo += d; System.out.print("No: " + format(d) +" K/s "); /* We iterate on t. */ ms = System.currentTimeMillis(); for(Iterator it = t.iterator(); it.hasNext(); it.next()); d = 1.0 * n / (System.currentTimeMillis() - ms); if (j > 2) totIterFor += d; System.out.print("IterFor: " + format(d) +" K/s "); System.out.println(); } System.out.println(); System.out.println("java.util Add: " + format(totAdd/(j-3)) + " K/s RemYes: " + format(totRemYes/(j-3)) + " K/s Yes: " + format(totYes/(j-3)) + " K/s No: " + format(totNo/(j-3)) + " K/s IterFor: " + format(totIterFor/(j-3)) + " K/s"); System.out.println(); totAdd = totYes = totNo = totIterFor = totIterBack = totRemYes = 0; } for(j = 0; j < 20; j++) { m = new AVL_TREE_SET(); /* We first add all pairs to m. */ for(i = 0; i < n; i++) m.add(k[i]); /* Then we remove the first half and put it back. */ for(i = 0; i < n/2; i++) m.remove(k[i]); ms = System.currentTimeMillis(); for(i = 0; i < n/2; i++) m.add(k[i]); d = System.currentTimeMillis() - ms; /* Then we remove the other half and put it back again. */ ms = System.currentTimeMillis(); for(i = n/2; i < n; i++) m.remove(k[i]); dd = System.currentTimeMillis() - ms ; ms = System.currentTimeMillis(); for(i = n/2; i < n; i++) m.add(k[i]); d += System.currentTimeMillis() - ms; if (j > 2) totAdd += n/d; System.out.print("Add: " + format(n/d) +" K/s "); /* Then we remove again the first half. */ ms = System.currentTimeMillis(); for(i = 0; i < n/2; i++) m.remove(k[i]); dd += System.currentTimeMillis() - ms ; if (j > 2) totRemYes += n/dd; System.out.print("RemYes: " + format(n/dd) +" K/s "); /* And then we put it back. */ for(i = 0; i < n/2; i++) m.add(k[i]); /* We check for pairs in m. */ ms = System.currentTimeMillis(); for(i = 0; i < n; i++) m.contains(k[i]); d = 1.0 * n / (System.currentTimeMillis() - ms); if (j > 2) totYes += d; System.out.print("Yes: " + format(d) +" K/s "); /* We check for pairs not in m. */ ms = System.currentTimeMillis(); for(i = 0; i < n; i++) m.contains(nk[i]); d = 1.0 * n / (System.currentTimeMillis() - ms); if (j > 2) totNo += d; System.out.print("No: " + format(d) +" K/s "); /* We iterate on m. */ KEY_LIST_ITERATOR it = (KEY_LIST_ITERATOR)m.iterator(); ms = System.currentTimeMillis(); for(; it.hasNext(); it.NEXT_KEY()); d = 1.0 * n / (System.currentTimeMillis() - ms); if (j > 2) totIterFor += d; System.out.print("IterFor: " + format(d) +" K/s "); /* We iterate back on m. */ ms = System.currentTimeMillis(); for(; it.hasPrevious(); it.PREV_KEY()); d = 1.0 * n / (System.currentTimeMillis() - ms); if (j > 2) totIterBack += d; System.out.print("IterBack: " + format(d) +" K/s "); System.out.println(); } System.out.println(); System.out.println("fastutil Add: " + format(totAdd/(j-3)) + " K/s RemYes: " + format(totRemYes/(j-3)) + " K/s Yes: " + format(totYes/(j-3)) + " K/s No: " + format(totNo/(j-3)) + " K/s IterFor: " + format(totIterFor/(j-3)) + " K/s IterBack: " + format(totIterBack/(j-3)) + "K/s"); System.out.println(); } private static boolean valEquals(Object o1, Object o2) { return o1 == null ? o2 == null : o1.equals(o2); } private static void fatal(String msg) { throw new AssertionError(msg); } private static void ensure(boolean cond, String msg) { if (cond) return; fatal(msg); } private static Object[] k, v, nk; private static KEY_TYPE kt[]; private static KEY_TYPE nkt[]; private static AVL_TREE_SET topSet; protected static void testSets(SORTED_SET m, SortedSet t, int n, int level) throws Exception { long ms; boolean mThrowsIllegal, tThrowsIllegal, mThrowsNoElement, tThrowsNoElement; boolean rt = false, rm = false; if (level > 4) return; /* Now we check that both sets agree on first/last keys. */ mThrowsNoElement = mThrowsIllegal = tThrowsNoElement = tThrowsIllegal = false; try { m.first(); } catch (NoSuchElementException e) { mThrowsNoElement = true; } try { t.first(); } catch (NoSuchElementException e) { tThrowsNoElement = true; } ensure(mThrowsNoElement == tThrowsNoElement, "Error (" + level + ", " + seed + "): first() divergence at start in NoSuchElementException (" + mThrowsNoElement + ", " + tThrowsNoElement + ")"); if (! mThrowsNoElement) ensure(t.first().equals(m.first()), "Error (" + level + ", " + seed + "): m and t differ at start on their first key (" + m.first() + ", " + t.first() +")"); mThrowsNoElement = mThrowsIllegal = tThrowsNoElement = tThrowsIllegal = false; try { m.last(); } catch (NoSuchElementException e) { mThrowsNoElement = true; } try { t.last(); } catch (NoSuchElementException e) { tThrowsNoElement = true; } ensure(mThrowsNoElement == tThrowsNoElement, "Error (" + level + ", " + seed + "): last() divergence at start in NoSuchElementException (" + mThrowsNoElement + ", " + tThrowsNoElement + ")"); if (! mThrowsNoElement) ensure(t.last().equals(m.last()), "Error (" + level + ", " + seed + "): m and t differ at start on their last key (" + m.last() + ", " + t.last() +")"); /* Now we check that m and t are equal. */ if (!m.equals(t) || ! t.equals(m)) System.err.println("m: " + m + " t: " + t); ensure(m.equals(t), "Error (" + level + ", " + seed + "): ! m.equals(t) at start"); ensure(t.equals(m), "Error (" + level + ", " + seed + "): ! t.equals(m) at start"); /* Now we check that m actually holds that data. */ for(Iterator i=t.iterator(); i.hasNext();) { ensure(m.contains(i.next()), "Error (" + level + ", " + seed + "): m and t differ on an entry after insertion (iterating on t)"); } /* Now we check that m actually holds that data, but iterating on m. */ for(Iterator i=m.iterator(); i.hasNext();) { ensure(t.contains(i.next()), "Error (" + level + ", " + seed + "): m and t differ on an entry after insertion (iterating on m)"); } /* Now we check that inquiries about random data give the same answer in m and t. For m we use the polymorphic method. */ for(int i=0; i 0) { badPrevious = true; j.previous(); break; } previous = k; } i = (it.unimi.dsi.fastutil.BidirectionalIterator)m.iterator(from); for(int k = 0; k < 2*n; k++) { ensure(i.hasNext() == j.hasNext(), "Error (" + level + ", " + seed + "): divergence in hasNext() (iterator with starting point " + from + ")"); ensure(i.hasPrevious() == j.hasPrevious() || badPrevious && (i.hasPrevious() == (previous != null)), "Error (" + level + ", " + seed + "): divergence in hasPrevious() (iterator with starting point " + from + ")"); if (r.nextFloat() < .8 && i.hasNext()) { ensure((I = i.next()).equals(J = j.next()), "Error (" + level + ", " + seed + "): divergence in next() (" + I + ", " + J + ", iterator with starting point " + from + ")"); //System.err.println("Done next " + I + " " + J + " " + badPrevious); badPrevious = false; if (r.nextFloat() < 0.5) { //System.err.println("Removing in next"); i.remove(); j.remove(); t.remove(J); } } else if (!badPrevious && r.nextFloat() < .2 && i.hasPrevious()) { ensure((I = i.previous()).equals(J = j.previous()), "Error (" + level + ", " + seed + "): divergence in previous() (" + I + ", " + J + ", iterator with starting point " + from + ")"); if (r.nextFloat() < 0.5) { //System.err.println("Removing in prev"); i.remove(); j.remove(); t.remove(J); } } } } /* Now we check that m actually holds that data. */ ensure(m.equals(t), "Error (" + level + ", " + seed + "): ! m.equals(t) after iteration"); ensure(t.equals(m), "Error (" + level + ", " + seed + "): ! t.equals(m) after iteration"); /* Now we select a pair of keys and create a subset. */ if (! m.isEmpty()) { java.util.ListIterator i; Object start = m.first(), end = m.first(); for(i = (java.util.ListIterator)m.iterator(); i.hasNext() && r.nextFloat() < .3; start = end = i.next()); for(; i.hasNext() && r.nextFloat() < .95; end = i.next()); //System.err.println("Checking subSet from " + start + " to " + end + " (level=" + (level+1) + ")..."); testSets((SORTED_SET)m.subSet((KEY_CLASS)start, (KEY_CLASS)end), t.subSet(start, end), n, level + 1); ensure(m.equals(t), "Error (" + level + ", " + seed + "): ! m.equals(t) after subSet"); ensure(t.equals(m), "Error (" + level + ", " + seed + "): ! t.equals(m) after subSet"); //System.err.println("Checking headSet to " + end + " (level=" + (level+1) + ")..."); testSets((SORTED_SET)m.headSet((KEY_CLASS)end), t.headSet(end), n, level + 1); ensure(m.equals(t), "Error (" + level + ", " + seed + "): ! m.equals(t) after headSet"); ensure(t.equals(m), "Error (" + level + ", " + seed + "): ! t.equals(m) after headSet"); //System.err.println("Checking tailSet from " + start + " (level=" + (level+1) + ")..."); testSets((SORTED_SET)m.tailSet((KEY_CLASS)start), t.tailSet(start), n, level + 1); ensure(m.equals(t), "Error (" + level + ", " + seed + "): ! m.equals(t) after tailSet"); ensure(t.equals(m), "Error (" + level + ", " + seed + "): ! t.equals(m) after tailSet"); } } private static void runTest(int n) throws Exception { AVL_TREE_SET m = new AVL_TREE_SET(); SortedSet t = new java.util.TreeSet(); topSet = m; k = new Object[n]; nk = new Object[n]; kt = new KEY_TYPE[n]; nkt = new KEY_TYPE[n]; for(int i = 0; i < n; i++) { #if KEY_CLASS_Object k[i] = kt[i] = genKey(); nk[i] = nkt[i] = genKey(); #else k[i] = new KEY_CLASS(kt[i] = genKey()); nk[i] = new KEY_CLASS(nkt[i] = genKey()); #endif } /* We add pairs to t. */ for(int i = 0; i < n; i++) t.add(k[i]); /* We add to m the same data */ m.addAll(t); testSets(m, t, n, 0); System.out.println("Test OK"); return; } public static void main(String args[]) throws Exception { int n = Integer.parseInt(args[1]); if (args.length > 2) r = new java.util.Random(seed = Long.parseLong(args[2])); try { if ("speedTest".equals(args[0]) || "speedComp".equals(args[0])) speedTest(n, "speedComp".equals(args[0])); else if ("test".equals(args[0])) runTest(n); } catch(Throwable e) { e.printStackTrace(System.err); System.err.println("seed: " + seed); throw e; } } #endif }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy