package LBJ2.util; import java.util.Arrays; /** * This class implements an expandable two dimensional array of doubles that * should be faster than java's <code>Vector</code>. * * @author Nick Rizzolo **/ public class DVector2D implements Cloneable, java.io.Serializable { /** The default capacity of the first dimension of this 2D vector. */ protected static final int defaultCapacity1 = 8; /** The default capacity of the second dimension of this 2D vector. */ protected static final int defaultDefaultCapacity2 = 8; /** The elements of the vector. */ protected double[][] vector; /** The sizes of each vector in the second dimension. */ protected IVector sizes; /** The capacity of new vectors created in the second dimension. */ protected int defaultCapacity2; /** * Constructs a new vector with default capacities * {@link #defaultCapacity1} and {@link #defaultCapacity2}. **/ public DVector2D() { this(defaultCapacity1, defaultDefaultCapacity2); } /** * Constructs a new vector with the specified capacities. * * @param c1 The initial capacity for the first dimension of the new * vector. * @param c2 The initial capacity for the second dimension of the new * vector. **/ public DVector2D(int c1, int c2) { defaultCapacity2 = Math.max(defaultDefaultCapacity2, c2); vector = new double[Math.max(defaultCapacity1, c1)][defaultCapacity2]; sizes = new IVector(c1); } /** * Constructs a new vector using the specified array as a starting point. * * @param v The initial array. **/ public DVector2D(double[][] v) { defaultCapacity2 = defaultDefaultCapacity2; if (v.length == 0) { vector = new double[defaultCapacity1][defaultCapacity2]; sizes = new IVector(defaultCapacity1); } else { vector = v; sizes = new IVector(v.length); for (int i = 0; i < v.length; ++i) { sizes.set(i, v[i].length); defaultCapacity2 = Math.max(defaultCapacity2, v[i].length); } for (int i = 0; i < v.length; ++i) if (v[i].length == 0) v[i] = new double[defaultCapacity2]; } } /** * Throws an exception when either of the specified indexes are negative. * * @param i1 The index in the first dimension. * @param i2 The index in the second dimension. * @throws ArrayIndexOutOfBoundsException * When <code>i1</code> or <code>i2</code> < 0. **/ protected void boundsCheck(int i1, int i2) { if (i1 < 0 || i2 < 0) throw new ArrayIndexOutOfBoundsException( "Attempted to access negative index of DVector2D."); } /** * Retrieves the value stored at the specified index of the vector. * * @param i1 The index in the first dimension. * @param i2 The index in the second dimension. * @return The retrieved value. * @throws ArrayIndexOutOfBoundsException * When <code>i1</code> or <code>i2</code> < 0. **/ public double get(int i1, int i2) { return get(i1, i2, 0); } /** * Retrieves the value stored at the specified index of the vector. * * @param i1 The index in the first dimension. * @param i2 The index in the second dimension. * @param d The default value. * @return The retrieved value. * @throws ArrayIndexOutOfBoundsException * When <code>i1</code> or <code>i2</code> < 0. **/ public double get(int i1, int i2, double d) { boundsCheck(i1, i2); // Because of the way IVector works, the only way i2 < sizes.get(i1) will // be true is if i1 is in fact a valid index for the first dimension. return i2 < sizes.get(i1) ? vector[i1][i2] : d; } /** * Sets the value at the specified index to the given value. * * @param i1 The index in the first dimension. * @param i2 The index in the second dimension. * @param v The new value at that index. * @return The value that used to be at index <code>i</code>. * @throws ArrayIndexOutOfBoundsException * When <code>i1</code> or <code>i2</code> < 0. **/ public double set(int i1, int i2, double v) { return set(i1, i2, v, 0); } /** * Sets the value at the specified index to the given value. * * @param i1 The index in the first dimension. * @param i2 The index in the second dimension. * @param v The new value at that index. * @param d The default value for other new indexes that might get * created. * @return The value that used to be at index <code>i</code>. * @throws ArrayIndexOutOfBoundsException * When <code>i1</code> or <code>i2</code> < 0. **/ public double set(int i1, int i2, double v, double d) { boundsCheck(i1, i2); expandFor(i1, i2, d); double result = vector[i1][i2]; vector[i1][i2] = v; return result; } /** * Removes the row at the specified index. * * @param i The index of the row to remove. * @return The removed row. **/ public double[] remove(int i) { boundsCheck(i, 0); int rows = sizes.size(); if (i >= rows) throw new ArrayIndexOutOfBoundsException( "LBJ: DVector2D: Can't remove row at index " + i + " as it is larger than the size (" + rows + ")"); double[] result = vector[i]; for (int j = i + 1; j < rows; ++j) vector[j - 1] = vector[j]; vector[rows - 1] = null; sizes.remove(i); return result; } /** * Removes the element at the specified index of the vector. * * @param i1 The row index of the element to remove. * @param i2 The column index of the element to remove. * @return The removed element. **/ public double remove(int i1, int i2) { boundsCheck(i1, i2); int rows = sizes.size(), columns = sizes.get(i1); if (i1 >= rows || i2 >= columns) throw new ArrayIndexOutOfBoundsException( "LBJ: DVector2D: Can't remove index [" + i1 + ", " + i2 + "] as it is out of bounds (" + rows + ", " + columns + ")"); double result = vector[i1][i2]; for (int j = i2 + 1; j < columns; ++j) vector[i1][j - 1] = vector[i1][j]; sizes.set(i1, columns - 1); return result; } /** Returns the size of the first dimension of this vector.. */ public int size() { return sizes.size(); } /** * Returns the size associated with the specified vector. * * @param i The index of the vector whose size will be returned. **/ public int size(int i) { return sizes.get(i); } /** * Returns the value of the maximum element in the * <code>i</code><sup>th</sup> vector. * * @param i An index into the first dimension of this vector. **/ public double max(int i) { if (i < 0 || i >= sizes.size()) throw new ArrayIndexOutOfBoundsException( "Attempted to access negative index of DVector2D."); double result = -Double.MAX_VALUE; int size = sizes.get(i); for (int j = 0; j < size; ++j) if (vector[i][j] > result) result = vector[i][j]; return result; } /** * Sorts the selected row in increasing order. * * @param i The row to sort. **/ public void sort(int i) { Arrays.sort(vector[i], 0, sizes.get(i)); } /** * Searches the selected row vector for the specified value using the * binary search algorithm. The vector <strong>must</strong> be sorted (as * by the {@link #sort(int)} method) prior to making this call. If it is * not sorted, the results are undefined. If the vector contains multiple * elements with the specified value, there is no guarantee which one will * be found. * * @param i The selected row. * @param v The value to be searched for. * @return The index of <code>v</code>, if it is contained in the vector; * otherwise, <code>(-(<i>insertion point</i>) - 1)</code>. The * <i>insertion point</i> is defined as the point at which * <code>v</code> would be inserted into the vector: the index of * the first element greater than <code>v</code>, or the size of * the vector if all elements in the vector are less than * <code>v</code>. Note that this guarantees that the return value * will be >= 0 if and only if <code>v</code> is found. **/ public int binarySearch(int i, int v) { int a = 0, b = sizes.get(i); while (b != a) { int m = (a + b) >> 1; if (vector[i][m] > v) b = m; else if (vector[i][m] < v) a = m + 1; else return m; } return -a - 1; } /** * Makes sure the capacities and sizes of the vectors can accomodate the * given indexes. The capacities of the vectors are simply doubled until * they can accomodate their sizes. * * @param i1 The index in the first dimension. * @param i2 The index in the second dimension. * @param d The default value for other new indexes that might get * created. **/ protected void expandFor(int i1, int i2, double d) { if (i1 >= sizes.size()) { int oldSize = sizes.size(), capacity = vector.length; sizes.set(i1, 0); if (capacity < sizes.size()) { while (capacity < sizes.size()) capacity *= 2; double[][] t = new double[capacity][]; System.arraycopy(vector, 0, t, 0, oldSize); vector = t; } for (int i = oldSize; i < sizes.size(); ++i) vector[i] = new double[defaultCapacity2]; } if (i2 < sizes.get(i1)) return; int oldSize = sizes.get(i1), capacity = vector[i1].length; sizes.set(i1, i2 + 1); if (capacity >= sizes.get(i1)) return; while (capacity < sizes.get(i1)) capacity *= 2; double[] t = new double[capacity]; System.arraycopy(vector[i1], 0, t, 0, oldSize); if (d != 0) Arrays.fill(t, oldSize, sizes.get(i1), d); vector[i1] = t; } /** * Returns a new 2D array of <code>double</code>s containing the same data * as this vector. **/ public double[][] toArray() { double[][] result = new double[sizes.size()][]; for (int i = 0; i < result.length; ++i) { result[i] = new double[sizes.get(i)]; System.arraycopy(vector[i], 0, result[i], 0, result[i].length); } return result; } /** * Two <code>DVector2D</code>s are considered equal if they contain all the * same elements, sizes, and capacities. **/ public boolean equals(Object o) { if (!(o instanceof DVector2D)) return false; DVector2D v = (DVector2D) o; if (vector.length != v.vector.length || !sizes.equals(v.sizes)) return false; for (int i = 0; i < vector.length; ++i) if (!Arrays.equals(vector[i], v.vector[i])) return false; return true; } /** * A hash code based on the hash codes of the constituents of * {@link #vector}. **/ public int hashCode() { int result = vector.hashCode(); for (int i = 0; i < vector.length; ++i) result = 17 * result + vector[i].hashCode(); return result; } /** * Returns a clone of this vector that is one level deep; in particular, * the objects in the vector themselves are not cloned, but the underlying * array is. **/ public Object clone() { DVector2D clone = null; try { clone = (DVector2D) super.clone(); } catch (Exception e) { System.err.println("Error cloning " + getClass().getName() + ":"); e.printStackTrace(); System.exit(1); } clone.vector = (double[][]) vector.clone(); for (int i = 0; i < vector.length; ++i) if (clone.vector[i] != null) clone.vector[i] = (double[]) vector[i].clone(); return clone; } /** Returns a text representation of this vector. */ public String toString() { StringBuffer result = new StringBuffer(); result.append("["); int s1 = sizes.size(); for (int i = 0; i < s1; ++i) { int s2 = sizes.get(i); result.append("["); for (int j = 0; j < s2; ++j) { result.append(vector[i][j]); if (j + 1 < s2) result.append(", "); } result.append("]"); if (i + 1 < s1) result.append(",\n "); } result.append("]"); return result.toString(); } /** * Writes a binary representation of the vector. * * @param out The output stream. **/ public void write(ExceptionlessOutputStream out) { sizes.write(out); for (int i = 0; i < sizes.size(); ++i) for (int j = 0; j < sizes.get(i); ++j) out.writeDouble(vector[i][j]); out.writeInt(defaultCapacity2); } /** * Reads the binary representation of a vector from the specified stream, * overwriting the data in this object. * * @param in The input stream. **/ public void read(ExceptionlessInputStream in) { sizes = new IVector(); sizes.read(in); if (sizes.size() == 0) { defaultCapacity2 = defaultDefaultCapacity2; vector = new double[defaultCapacity1][defaultCapacity2]; } else { vector = new double[sizes.size()][]; for (int i = 0; i < vector.length; ++i) { vector[i] = new double[sizes.get(i)]; for (int j = 0; j < vector[i].length; ++j) vector[i][j] = in.readDouble(); } defaultCapacity2 = in.readInt(); } } }