diff --git a/src/java/com/samskivert/util/RandomUtil.java b/src/java/com/samskivert/util/RandomUtil.java
new file mode 100644
index 00000000..4ca99698
--- /dev/null
+++ b/src/java/com/samskivert/util/RandomUtil.java
@@ -0,0 +1,298 @@
+//
+// $Id$
+//
+// samskivert library - useful routines for java programs
+// Copyright (C) 2001-2006 Michael Bayne
+//
+// This library is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This library 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
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package com.samskivert.util;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Random;
+
+import com.samskivert.Log;
+
+/**
+ * Provides miscellaneous utility routines to simplify obtaining useful random
+ * number values and to centralize seeding and proper care and feeding of the
+ * pseudo-random number generator.
+ */
+public class RandomUtil
+{
+ /** The random number generator used by the methods in this class. */
+ public static final Random rand = new Random();
+
+ /**
+ * Returns a pseudorandom, uniformly distributed int
+ * value between 0 (inclusive) and the specified value (exclusive).
+ *
+ * @param high the high value limiting the random number sought.
+ */
+ public static int getInt (int high)
+ {
+ return rand.nextInt(high);
+ }
+
+ /**
+ * Returns a pseudorandom, uniformly distributed int
+ * value between high and low, exclusive of each.
+ */
+ public static int getInt (int high, int low)
+ {
+ if (high - low - 1 <= 0) {
+ throw new IllegalArgumentException(
+ "Invalid range [high=" + high + ", low=" + low + "]");
+ }
+ return low + 1 + rand.nextInt(high - low - 1);
+ }
+
+ /**
+ * Returns a pseudorandom, uniformly distributed float value between
+ * 0.0 (inclusive) and the specified value (exclusive).
+ *
+ * @param high the high value limiting the random number sought.
+ */
+ public static float getFloat (float high)
+ {
+ return rand.nextFloat() * high;
+ }
+
+ /**
+ * Pick a random index from the array, weighted by the value of the
+ * corresponding array element.
+ *
+ * @param weights an array of non-negative integers.
+ * @return an index into the array, or -1 if the sum of the weights
+ * is less than 1.
+ *
+ * For example, passing in {1, 0, 3, 4} will return
+ *
| 0 | 1/8th of the time |
+ * | 1 | never |
+ * | 2 | 3/8th of the time |
+ * | 3 | half of the time |
+ */
+ public static int getWeightedIndex (int[] weights)
+ {
+ int sum = IntListUtil.sum(weights);
+ if (sum < 1) {
+ return -1;
+ }
+ int pick = getInt(sum);
+ for (int ii=0, nn=weights.length; ii < nn; ii++) {
+ pick -= weights[ii];
+ if (pick < 0) {
+ return ii;
+ }
+ }
+
+ // Impossible!
+ Log.logStackTrace(new Throwable());
+ return 0;
+ }
+
+ /**
+ * Pick a random index from the array, weighted by the value of the
+ * corresponding array element.
+ *
+ * @param weights an array of non-negative floats.
+ * @return an index into the array, or -1 if the sum of the weights
+ * is less than or equal to 0.0 or any individual element is negative.
+ *
+ * For example, passing in {0.2, 0.0, 0.6, 0.8} will return
+ * | 0 | 1/8th of the time |
+ * | 1 | never |
+ * | 2 | 3/8th of the time |
+ * | 3 | half of the time |
+ */
+ public static int getWeightedIndex (float[] weights)
+ {
+ float sum = 0.0f;
+ for (int ii = 0; ii < weights.length; ii++) {
+ if (weights[ii] < 0.0f) {
+ return -1;
+ }
+ sum += weights[ii];
+ }
+
+ if (sum <= 0.0) {
+ return -1;
+ }
+ float pick = getFloat(sum);
+ for (int ii=0, nn=weights.length; ii < nn; ii++) {
+ pick -= weights[ii];
+ if (pick < 0.0) {
+ return ii;
+ }
+ }
+
+ // Impossible!
+ Log.logStackTrace(new Throwable());
+ return 0;
+ }
+
+ /**
+ * Picks a random object from the supplied array of values. Even
+ * weight is given to all elements of the array.
+ *
+ * @return a randomly selected item or null if the array is null or of
+ * length zero.
+ */
+ public static T pickRandom (T[] values)
+ {
+ return (values == null || values.length == 0) ? null :
+ values[getInt(values.length)];
+ }
+
+ /**
+ * Picks a random object from the supplied array of values, not
+ * including the specified skip object as a possible selection
+ * (equality with the skipped object is referential rather than via
+ * {@link Object#equals}). The element to be skipped must exist in the
+ * array exactly once. Even weight is given to all elements of the
+ * array except the skipped element.
+ *
+ * @return a randomly selected item or null if the array is null, of
+ * length zero or contains only the skip item.
+ */
+ public static T pickRandom (T[] values, T skip)
+ {
+ if (values == null || values.length < 2) {
+ return null;
+ }
+ int index = getInt(values.length-1);
+ for (int ii = 0; ii <= index; ii++) {
+ if (values[ii] == skip) {
+ index++;
+ }
+ }
+ return (index >= values.length) ? null : values[index];
+ }
+
+ /**
+ * Picks a random object from the supplied {@link Collection}.
+ */
+ public static T pickRandom (Collection values)
+ {
+ return pickRandom(values.iterator(), values.size());
+ }
+
+ /**
+ * Picks a random object from the supplied List
+ *
+ * @return a randomly selected item.
+ */
+ public static T pickRandom (List values)
+ {
+ int size = values.size();
+ if (size == 0) {
+ throw new IllegalArgumentException(
+ "Must have at least one element [size=" + size + "]");
+ }
+ return values.get(getInt(size));
+ }
+
+ /**
+ * Picks a random object from the supplied List. The specified skip
+ * object will be skipped when selecting a random value. The skipped
+ * object must exist exactly once in the List.
+ *
+ * @return a randomly selected item.
+ */
+ public static T pickRandom (List values, T skip)
+ {
+ return pickRandom(values, skip, rand);
+ }
+
+ /**
+ * Picks a random object from the supplied List. The specified skip
+ * object will be skipped when selecting a random value. The skipped
+ * object must exist exactly once in the List.
+ *
+ * @return a randomly selected item.
+ */
+ public static T pickRandom (List values, T skip, Random r)
+ {
+ int size = values.size();
+ if (size < 2) {
+ throw new IllegalArgumentException(
+ "Must have at least two elements [size=" + size + "]");
+ }
+
+ int pick = r.nextInt(size - 1);
+ for (int ii = 0; ii < size; ii++) {
+ T val = values.get(ii);
+ if ((val != skip) && (pick-- == 0)) {
+ return val;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Picks a random object from the supplied iterator (which must
+ * iterate over exactly count objects.
+ *
+ * @return a randomly selected item.
+ *
+ * @exception NoSuchElementException thrown if the iterator provides
+ * fewer than count elements.
+ */
+ public static T pickRandom (Iterator iter, int count)
+ {
+ if (count < 1) {
+ throw new IllegalArgumentException(
+ "Must have at least one element [count=" + count + "]");
+ }
+
+ T value = iter.next();
+ for (int ii = 0, ll = getInt(count); ii < ll; ii++) {
+ value = iter.next();
+ }
+ return value;
+ }
+
+ /**
+ * Picks a random object from the supplied iterator (which must
+ * iterate over exactly count objects. The specified skip
+ * object will be skipped when selecting a random value. The skipped
+ * object must exist exactly once in the set of objects returned by
+ * the iterator.
+ *
+ * @return a randomly selected item.
+ *
+ * @exception NoSuchElementException thrown if the iterator provides
+ * fewer than count elements.
+ */
+ public static T pickRandom (Iterator iter, int count, T skip)
+ {
+ if (count < 2) {
+ throw new IllegalArgumentException(
+ "Must have at least two elements [count=" + count + "]");
+ }
+
+ int index = getInt(count-1);
+ T value = null;
+ do {
+ value = iter.next();
+ if (value == skip) {
+ value = iter.next();
+ }
+ } while (index-- > 0);
+ return value;
+ }
+}