Coverage Report - org.apache.commons.math.util.ResizableDoubleArray

Classes in this File Line Coverage Branch Coverage Complexity
ResizableDoubleArray
99% 
100% 
2.138

 1  
 /*
 2  
  * Copyright 2003-2004 The Apache Software Foundation.
 3  
  *
 4  
  * Licensed under the Apache License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  *      http://www.apache.org/licenses/LICENSE-2.0
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.apache.commons.math.util;
 17  
 
 18  
 import java.io.Serializable;
 19  
 
 20  
 /**
 21  
  * <p>
 22  
  * A variable length {@link DoubleArray} implementation that automatically 
 23  
  * handles expanding and contracting its internal storage array as elements 
 24  
  * are added and removed.
 25  
  * </p>
 26  
  * <p>
 27  
  *  The internal storage array starts with capacity determined by the
 28  
  * <code>initialCapacity</code> property, which can be set by the constructor.
 29  
  * The default initial capacity is 16.  Adding elements using 
 30  
  * {@link #addElement(double)} appends elements to the end of the array.  When 
 31  
  * there are no open entries at the end of the internal storage array, the 
 32  
  * array is expanded.  The size of the expanded array depends on the 
 33  
  * <code>expansionMode</code> and <code>expansionFactor</code> properties.  
 34  
  * The <code>expansionMode</code> determines whether the size of the array is 
 35  
  * multiplied by the <code>expansionFactor</code> (MULTIPLICATIVE_MODE) or if 
 36  
  * the expansion is additive (ADDITIVE_MODE -- <code>expansionFactor</code>
 37  
  * storage locations added).  The default <code>expansionMode</code> is 
 38  
  * MULTIPLICATIVE_MODE and the default <code>expansionFactor</code>
 39  
  * is 2.0.
 40  
  * </p>
 41  
  * <p>
 42  
  * The {@link #addElementRolling(double)} method adds a new element to the end
 43  
  * of the internal storage array and adjusts the "usable window" of the 
 44  
  * internal array forward by one position (effectively making what was the 
 45  
  * second element the first, and so on).  Repeated activations of this method
 46  
  * (or activation of {@link #discardFrontElements(int)}) will effectively orphan
 47  
  * the storage locations at the beginning of the internal storage array.  To
 48  
  * reclaim this storage, each time one of these methods is activated, the size
 49  
  * of the internal storage array is compared to the number of addressable 
 50  
  * elements (the <code>numElements</code> property) and if the difference
 51  
  * is too large, the internal array is contracted to size 
 52  
  * <code>numElements + 1.</code>  The determination of when the internal
 53  
  * storage array is "too large" depends on the <code>expansionMode</code> and
 54  
  * <code>contractionFactor</code> properties.  If  the <code>expansionMode</code>
 55  
  * is <code>MULTIPLICATIVE_MODE</code>, contraction is triggered when the
 56  
  * ratio between storage array length and <code>numElements</code> exceeds
 57  
  * <code>contractionFactor.</code>  If the <code>expansionMode</code>
 58  
  * is <code>ADDITIVE_MODE,</code> the number of excess storage locations
 59  
  * is compared to <code>contractionFactor.</code>  
 60  
  * </p>
 61  
  * <p>
 62  
  * To avoid cycles of expansions and contractions, the 
 63  
  * <code>expansionFactor</code> must not exceed the 
 64  
  * <code>contractionFactor.</code> Constructors and mutators for both of these
 65  
  * properties enforce this requirement, throwing IllegalArgumentException if it
 66  
  * is violated.
 67  
  * </p>
 68  
  * <p>
 69  
  * @version $Revision$ $Date: 2005-02-26 05:11:52 -0800 (Sat, 26 Feb 2005) $
 70  
  */
 71  
 public class ResizableDoubleArray implements DoubleArray, Serializable {
 72  
     
 73  
     /** Serializable version identifier */
 74  
     static final long serialVersionUID = -3485529955529426875L; 
 75  
     
 76  
     /** additive expansion mode */
 77  
     public static final int ADDITIVE_MODE = 1;
 78  
     
 79  
     /** multiplicative expansion mode */
 80  
     public static final int MULTIPLICATIVE_MODE = 0;
 81  
    
 82  
     /** 
 83  
      * The contraction criteria determines when the internal array will be 
 84  
      * contracted to fit the number of elements contained in the element
 85  
      *  array + 1.
 86  
      */
 87  202
     protected float contractionCriteria = 2.5f;
 88  
 
 89  
     /** 
 90  
      * The expansion factor of the array.  When the array needs to be expanded, 
 91  
      * the new array size will be 
 92  
      * <code>internalArray.length * expansionFactor</code>
 93  
      * if <code>expansionMode</code> is set to MULTIPLICATIVE_MODE, or
 94  
      * <code>internalArray.length + expansionFactor</code> if 
 95  
      * <code>expansionMode</code> is set to ADDITIVE_MODE.
 96  
      */
 97  202
     protected float expansionFactor = 2.0f;
 98  
     
 99  
     /**
 100  
      * Determines whether array expansion by <code>expansionFactor</code>
 101  
      * is additive or multiplicative.
 102  
      */
 103  202
     protected int expansionMode = MULTIPLICATIVE_MODE;
 104  
 
 105  
     /**
 106  
      * The initial capacity of the array.  Initial capacity is not exposed as a
 107  
      * property as it is only meaningful when passed to a constructor.
 108  
      */
 109  202
     protected int initialCapacity = 16;
 110  
     
 111  
     /** 
 112  
      * The internal storage array.
 113  
      */
 114  
     protected double[] internalArray;
 115  
 
 116  
     /** 
 117  
      * The number of addressable elements in the array.  Note that this
 118  
      * has nothing to do with the length of the internal storage array.
 119  
      */
 120  202
     protected int numElements = 0;
 121  
 
 122  
     /** 
 123  
      * The position of the first addressable element in the internal storage
 124  
      * array.  The addressable elements in the array are <code>
 125  
      * internalArray[startIndex],...,internalArray[startIndex + numElements -1]
 126  
      * </code>
 127  
      */
 128  202
     protected int startIndex = 0;
 129  
 
 130  
     /**
 131  
      * Create a ResizableArray with default properties.
 132  
      * <ul>
 133  
      * <li><code>initialCapacity = 16</code></li>
 134  
      * <li><code>expansionMode = MULTIPLICATIVE_MODE</code></li>
 135  
      * <li><code>expansionFactor = 2.5</code></li>
 136  
      * <li><code>contractionFactor = 2.0</code></li>
 137  
      * </ul>
 138  
      */
 139  176
     public ResizableDoubleArray() {
 140  176
         internalArray = new double[initialCapacity];
 141  176
     }
 142  
 
 143  
     /**
 144  
      * Create a ResizableArray with the specified initial capacity.  Other
 145  
      * properties take default values:
 146  
       * <ul>
 147  
      * <li><code>expansionMode = MULTIPLICATIVE_MODE</code></li>
 148  
      * <li><code>expansionFactor = 2.5</code></li>
 149  
      * <li><code>contractionFactor = 2.0</code></li>
 150  
      * </ul>
 151  
      * @param initialCapacity The initial size of the internal storage array
 152  
      * @throws IllegalArgumentException if initialCapacity is not > 0
 153  
      */
 154  6
     public ResizableDoubleArray(int initialCapacity) {
 155  6
         setInitialCapacity(initialCapacity);
 156  4
         internalArray = new double[this.initialCapacity];
 157  4
     }
 158  
 
 159  
     /**
 160  
      * <p>
 161  
      * Create a ResizableArray with the specified initial capacity 
 162  
      * and expansion factor.  The remaining properties take default
 163  
      * values:
 164  
      * <ul>
 165  
      * <li><code>expansionMode = MULTIPLICATIVE_MODE</code></li>
 166  
      * <li><code>contractionFactor = 0.5 + expansionFactor</code></li>
 167  
      * </ul></p>
 168  
      * <p>
 169  
      * Throws IllegalArgumentException if the following conditions are
 170  
      * not met:
 171  
      * <ul>
 172  
      * <li><code>initialCapacity > 0</code></li>
 173  
      * <li><code>expansionFactor > 1</code></li>
 174  
      * </ul></p>
 175  
      * 
 176  
      * @param initialCapacity The initial size of the internal storage array
 177  
      * @param expansionFactor the array will be expanded based on this 
 178  
      *                        parameter
 179  
      * @throws IllegalArgumentException if parameters are not valid
 180  
      */
 181  6
     public ResizableDoubleArray(int initialCapacity, float expansionFactor) {
 182  6
         this.expansionFactor = expansionFactor;
 183  6
         setInitialCapacity(initialCapacity);
 184  6
         internalArray = new double[initialCapacity];
 185  6
         setContractionCriteria(expansionFactor +0.5f);
 186  4
     }
 187  
 
 188  
     /**
 189  
      * <p>
 190  
      * Create a ResizableArray with the specified initialCapacity, 
 191  
      * expansionFactor, and contractionCriteria. The <code>expansionMode</code>
 192  
      * will default to <code>MULTIPLICATIVE_MODE.</code></p>
 193  
      * <p>
 194  
      * Throws IllegalArgumentException if the following conditions are
 195  
      * not met:
 196  
      * <ul>
 197  
      * <li><code>initialCapacity > 0</code></li>
 198  
      * <li><code>expansionFactor > 1</code></li>
 199  
      * <li><code>contractionFactor >= expansionFactor</code></li>
 200  
      * </ul></p>
 201  
      * @param initialCapacity The initial size of the internal storage array
 202  
      * @param expansionFactor the array will be expanded based on this 
 203  
      *                        parameter
 204  
      * @param contractionCriteria The contraction Criteria.
 205  
      * @throws IllegalArgumentException if parameters are not valid
 206  
      */
 207  
     public ResizableDoubleArray(int initialCapacity, float expansionFactor,
 208  6
         float contractionCriteria) {
 209  6
         this.expansionFactor = expansionFactor;
 210  6
         setContractionCriteria(contractionCriteria);
 211  4
         setInitialCapacity(initialCapacity);
 212  4
         internalArray = new double[initialCapacity];
 213  4
     }
 214  
     
 215  
     /**
 216  
      * <p>
 217  
      * Create a ResizableArray with the specified properties.</p>
 218  
     * <p>
 219  
      * Throws IllegalArgumentException if the following conditions are
 220  
      * not met:
 221  
      * <ul>
 222  
      * <li><code>initialCapacity > 0</code></li>
 223  
      * <li><code>expansionFactor > 1</code></li>
 224  
      * <li><code>contractionFactor >= expansionFactor</code></li>
 225  
      * <li><code>expansionMode in {MULTIPLICATIVE_MODE, ADDITIVE_MODE}</code>
 226  
      * </li>
 227  
      * </ul></p>
 228  
      * 
 229  
      * @param initialCapacity the initial size of the internal storage array
 230  
      * @param expansionFactor the array will be expanded based on this 
 231  
      *                        parameter
 232  
      * @param contractionCriteria the contraction Criteria
 233  
      * @param expansionMode  the expansion mode
 234  
      * @throws IllegalArgumentException if parameters are not valid
 235  
      */
 236  
     public ResizableDoubleArray(int initialCapacity, float expansionFactor,
 237  8
             float contractionCriteria, int expansionMode) {
 238  8
         this.expansionFactor = expansionFactor;
 239  8
         setContractionCriteria(contractionCriteria);
 240  8
         setInitialCapacity(initialCapacity);
 241  8
         setExpansionMode(expansionMode);
 242  6
         internalArray = new double[initialCapacity];
 243  6
     }
 244  
 
 245  
     /**
 246  
      * Adds an element to the end of this expandable array.
 247  
      * 
 248  
      * @param value to be added to end of array
 249  
      */
 250  
     public synchronized void addElement(double value) {
 251  18735
         numElements++;
 252  18735
         if ((startIndex + numElements) > internalArray.length) {
 253  424
             expand();
 254  
         }
 255  18735
         internalArray[startIndex + (numElements - 1)] = value;
 256  18735
         if (shouldContract()) {
 257  168
             contract();
 258  
         }
 259  18735
     }
 260  
 
 261  
     /**
 262  
      * <p>
 263  
      * Adds an element to the end of the array and removes the first
 264  
      * element in the array.  Returns the discarded first element.
 265  
      * The effect is similar to a push operation in a FIFO queue.
 266  
      * </p>
 267  
      * <p>
 268  
      * Example: If the array contains the elements 1, 2, 3, 4 (in that order)
 269  
      * and addElementRolling(5) is invoked, the result is an array containing
 270  
      * the entries 2, 3, 4, 5 and the value returned is 1.
 271  
      * </p>
 272  
      * 
 273  
      * @param value the value to be added to the array
 274  
      * @return the value which has been discarded or "pushed" out of the array
 275  
      *         by this rolling insert
 276  
      */
 277  
     public synchronized double addElementRolling(double value) {
 278  2090
         double discarded = internalArray[startIndex];
 279  
 
 280  2090
         if ((startIndex + (numElements + 1)) > internalArray.length) {
 281  462
             expand();
 282  
         }
 283  
         // Increment the start index
 284  2090
         startIndex += 1;
 285  
 
 286  
         // Add the new value
 287  2090
         internalArray[startIndex + (numElements - 1)] = value;
 288  
 
 289  
         // Check the contraction criteria
 290  2090
         if (shouldContract()) {
 291  228
             contract();
 292  
         }
 293  2090
         return discarded;
 294  
     }
 295  
 
 296  
     /**
 297  
      * Checks the expansion factor and the contraction criteria and throws an 
 298  
      * IllegalArgumentException if the contractionCriteria is less than the 
 299  
      * expansionCriteria
 300  
      * 
 301  
      * @param expansionFactor factor to be checked
 302  
      * @param contractionCritera critera to be checked
 303  
      * @throws IllegalArgumentException if the contractionCriteria is less than
 304  
      *         the expansionCriteria.
 305  
      */
 306  
     protected void checkContractExpand(
 307  
         float contractionCritera,
 308  
         float expansionFactor) {
 309  
 
 310  26
         if (contractionCritera < expansionFactor) {
 311  4
             String msg =
 312  
                 "Contraction criteria can never be smaller than " +
 313  
                 "the expansion factor.  This would lead to a never " +
 314  
                 "ending loop of expansion and contraction as a newly " +
 315  
                 "expanded internal storage array would immediately " +
 316  
                 "satisfy the criteria for contraction";
 317  4
             throw new IllegalArgumentException(msg);
 318  
         }
 319  
 
 320  22
         if (contractionCriteria <= 1.0) {
 321  0
             String msg =
 322  
                 "The contraction criteria must be a number larger " +
 323  
                 "than one.  If the contractionCriteria is less than or " +
 324  
                 "equal to one an endless loop of contraction and " +
 325  
                 "expansion would ensue as an internalArray.length " +
 326  
                 "== numElements would satisfy the contraction criteria";
 327  0
             throw new IllegalArgumentException(msg);
 328  
         }
 329  
 
 330  22
         if (expansionFactor <= 1.0) {
 331  2
             String msg =
 332  
                 "The expansion factor must be a number greater than 1.0";
 333  2
             throw new IllegalArgumentException(msg);
 334  
         }
 335  20
     }
 336  
     
 337  
     /**
 338  
      * Clear the array, reset the size to the initialCapacity and the number 
 339  
      * of elements to zero.
 340  
      */
 341  
     public synchronized void clear() {
 342  40
         numElements = 0;
 343  40
         internalArray = new double[initialCapacity];
 344  40
     }
 345  
     
 346  
     /**
 347  
      * Contracts the storage array to the (size of the element set) + 1 - to 
 348  
      * avoid a zero length array. This function also resets the startIndex to 
 349  
      * zero. 
 350  
      */
 351  
     public synchronized void contract() {
 352  398
         double[] tempArray = new double[numElements + 1];
 353  
 
 354  
         // Copy and swap - copy only the element array from the src array.
 355  398
         System.arraycopy(internalArray, startIndex, tempArray, 0, numElements);
 356  398
         internalArray = tempArray;
 357  
 
 358  
         // Reset the start index to zero
 359  398
         startIndex = 0;
 360  398
     }
 361  
 
 362  
     /**
 363  
      * Discards the <code>i<code> initial elements of the array.  For example,
 364  
      * if the array contains the elements 1,2,3,4, invoking 
 365  
      * <code>discardFrontElements(2)</code> will cause the first two elements 
 366  
      * to be discarded, leaving 3,4 in the array.  Throws illegalArgumentException
 367  
      * if i exceeds numElements.
 368  
      * 
 369  
      * @param i  the number of elements to discard from the front of the array
 370  
      * @throws IllegalArgumentException if i is greater than numElements.
 371  
      */
 372  
     public synchronized void discardFrontElements(int i) {
 373  8
         if (i > numElements) {
 374  2
             String msg = "Cannot discard more elements than are" +
 375  
             "contained in this array.";
 376  2
             throw new IllegalArgumentException(msg);
 377  6
         } else if (i < 0) {
 378  2
             String msg = "Cannot discard a negative number of elements.";
 379  2
             throw new IllegalArgumentException(msg);
 380  
         } else {
 381  
             // "Subtract" this number of discarded from numElements 
 382  4
             numElements -= i;
 383  4
             startIndex += i;
 384  
         }
 385  4
         if (shouldContract()) {
 386  2
             contract();
 387  
         }
 388  4
     }
 389  
 
 390  
     /**
 391  
      * Expands the internal storage array using the expansion factor.
 392  
      * <p>
 393  
      * if <code>expansionMode</code> is set to MULTIPLICATIVE_MODE,
 394  
      * the new array size will be <code>internalArray.length * expansionFactor.</code>
 395  
      * If <code>expansionMode</code> is set to ADDITIVE_MODE,  the length
 396  
      * after expansion will be <code>internalArray.length + expansionFactor</code>
 397  
      */
 398  
     protected synchronized void expand() {
 399  
 
 400  
         // notice the use of Math.ceil(), this gaurantees that we will always 
 401  
         // have an array of at least currentSize + 1.   Assume that the 
 402  
         // current initial capacity is 1 and the expansion factor
 403  
         // is 1.000000000000000001.  The newly calculated size will be 
 404  
         // rounded up to 2 after the multiplication is performed.
 405  886
         int newSize = 0;
 406  886
         if (expansionMode == MULTIPLICATIVE_MODE) {
 407  880
             newSize = (int) Math.ceil(internalArray.length * expansionFactor);
 408  
         } else {
 409  6
             newSize = internalArray.length + Math.round(expansionFactor);
 410  
         }
 411  886
         double[] tempArray = new double[newSize];
 412  
 
 413  
         // Copy and swap
 414  886
         System.arraycopy(internalArray, 0, tempArray, 0, internalArray.length);
 415  886
         internalArray = tempArray;
 416  886
     }
 417  
     
 418  
     /**
 419  
      * Expands the internal storage array to the specified size.
 420  
      * 
 421  
      * @param size Size of the new internal storage array
 422  
      */
 423  
     private synchronized void expandTo(int size) {
 424  6
         double[] tempArray = new double[size];
 425  
         // Copy and swap
 426  6
         System.arraycopy(internalArray, 0, tempArray, 0, internalArray.length);
 427  6
         internalArray = tempArray;
 428  6
     }
 429  
 
 430  
     /**
 431  
      * The contraction criteria defines when the internal array will contract 
 432  
      * to store only the number of elements in the element array.   
 433  
      * If  the <code>expansionMode</code> is <code>MULTIPLICATIVE_MODE</code>,
 434  
      * contraction is triggered when the ratio between storage array length 
 435  
      * and <code>numElements</code> exceeds <code>contractionFactor</code>.
 436  
      * If the <code>expansionMode</code> is <code>ADDITIVE_MODE</code>, the
 437  
      * number of excess storage locations is compared to 
 438  
      * <code>contractionFactor.</code>   
 439  
      * 
 440  
      * @return the contraction criteria used to reclaim memory.
 441  
      */
 442  
     public float getContractionCriteria() {
 443  16
         return contractionCriteria;
 444  
     }
 445  
     
 446  
     /**
 447  
      * Returns the element at the specified index
 448  
      * 
 449  
      * @param index index to fetch a value from
 450  
      * @return value stored at the specified index
 451  
      * @throws ArrayIndexOutOfBoundsException if <code>index</code> is less than
 452  
      *         zero or is greater than <code>getNumElements() - 1</code>.
 453  
      */
 454  
     public double getElement(int index) {
 455  46
         double value = Double.NaN;
 456  46
         if (index >= numElements) {
 457  2
             String msg =
 458  
                 "The index specified: " + index +
 459  
                 " is larger than the current number of elements";
 460  2
             throw new ArrayIndexOutOfBoundsException(msg);
 461  44
         } else if (index >= 0) {
 462  42
             value = internalArray[startIndex + index];
 463  
         } else {
 464  2
             String msg =
 465  
                 "Elements cannot be retrieved from a negative array index";
 466  2
             throw new ArrayIndexOutOfBoundsException(msg);
 467  
         }
 468  42
         return value;
 469  
     }
 470  
     
 471  
      /**
 472  
      * Returns a double array containing the elements of this 
 473  
      * <code>ResizableArray</code>.  This method returns a copy, not a
 474  
      * reference to the underlying array, so that changes made to the returned
 475  
      *  array have no effect on this <code>ResizableArray.</code>
 476  
      * @return the double array.
 477  
      */
 478  
     public double[] getElements() {
 479  108
         double[] elementArray = new double[numElements];
 480  108
         System.arraycopy( internalArray, startIndex, elementArray, 0,
 481  
                 numElements);
 482  108
         return elementArray;
 483  
     }
 484  
     
 485  
     /**
 486  
      * The expansion factor controls the size of a new aray when an array 
 487  
      * needs to be expanded.  The <code>expansionMode</code>
 488  
      * determines whether the size of the array is multiplied by the 
 489  
      * <code>expansionFactor</code> (MULTIPLICATIVE_MODE) or if 
 490  
      * the expansion is additive (ADDITIVE_MODE -- <code>expansionFactor</code>
 491  
      * storage locations added).  The default <code>expansionMode</code> is 
 492  
      * MULTIPLICATIVE_MODE and the default <code>expansionFactor</code>
 493  
      * is 2.0.
 494  
      * 
 495  
      * @return the expansion factor of this expandable double array
 496  
      */
 497  
     public float getExpansionFactor() {
 498  36
         return expansionFactor;
 499  
     }
 500  
     
 501  
     /**
 502  
      * The <code>expansionMode</code> determines whether the internal storage 
 503  
      * array grows additively (ADDITIVE_MODE) or multiplicatively 
 504  
      * (MULTIPLICATIVE_MODE) when it is expanded.
 505  
      * 
 506  
      * @return Returns the expansionMode.
 507  
      */
 508  
     public int getExpansionMode() {
 509  10
         return expansionMode;
 510  
     }
 511  
     
 512  
     /**
 513  
      * Notice the package scope on this method.   This method is simply here 
 514  
      * for the JUnit test, it allows us check if the expansion is working 
 515  
      * properly after a number of expansions.  This is not meant to be a part 
 516  
      * of the public interface of this class.
 517  
      * 
 518  
      * @return the length of the internal storage array.
 519  
      */
 520  
     int getInternalLength() {
 521  32
         return (internalArray.length);
 522  
     }
 523  
 
 524  
     /**
 525  
      * Returns the number of elements currently in the array.  Please note
 526  
      * that this is different from the length of the internal storage array.  
 527  
      *
 528  
      * @return number of elements
 529  
      */
 530  
     public int getNumElements() {
 531  2168
         return (numElements);
 532  
     }
 533  
     
 534  
     /**
 535  
      * Returns the internal storage array.  Note that this method returns
 536  
      * a reference to the internal storage array, not a copy, and to correctly
 537  
      * address elements of the array, the <code>startIndex</code> is
 538  
      * required (available via the {@link #start} method).  This method should
 539  
      * only be used in cases where copying the internal array is not practical.
 540  
      * The {@link #getElements} method should be used in all other cases.
 541  
      *
 542  
      * 
 543  
      * @return the internal storage array used by this object
 544  
      */
 545  
     public double[] getValues() {
 546  618
         return (internalArray);
 547  
     }
 548  
 
 549  
     /**
 550  
      * Sets the contraction criteria for this ExpandContractDoubleArray. 
 551  
      * 
 552  
      * @param contractionCriteria contraction criteria
 553  
      */
 554  
     public void setContractionCriteria(float contractionCriteria) {
 555  22
         checkContractExpand(contractionCriteria, getExpansionFactor());
 556  18
         this.contractionCriteria = contractionCriteria;
 557  18
     }
 558  
     
 559  
 
 560  
     /**
 561  
      * Sets the element at the specified index.  If the specified index is greater than
 562  
      * <code>getNumElements() - 1</code>, the <code>numElements</code> property
 563  
      * is increased to <code>index +1</code> and additional storage is allocated 
 564  
      * (if necessary) for the new element and all  (uninitialized) elements 
 565  
      * between the new element and the previous end of the array).
 566  
      * 
 567  
      * @param index index to store a value in
 568  
      * @param value value to store at the specified index
 569  
      * @throws ArrayIndexOutOfBoundsException if <code>index</code> is less than
 570  
      *         zero.
 571  
      */
 572  
     public synchronized void setElement(int index, double value) {
 573  12
         if (index < 0) {
 574  2
             String msg = "Cannot set an element at a negative index";
 575  2
             throw new ArrayIndexOutOfBoundsException(msg);
 576  
         }
 577  10
         if (index + 1 > numElements) {
 578  6
             numElements = index + 1;
 579  
         }       
 580  10
         if ((startIndex + index) >= internalArray.length) {
 581  4
             expandTo(startIndex + (index + 1));
 582  
         }    
 583  10
         internalArray[startIndex + index] = value;
 584  10
     }
 585  
 
 586  
     /**
 587  
      * Sets the expansionFactor.  Throws IllegalArgumentException if the 
 588  
      * the following conditions are not met:
 589  
      * <ul>
 590  
      * <li><code>expansionFactor > 1</code></li>
 591  
      * <li><code>contractionFactor >= expansionFactor</code></li>
 592  
      * </ul>
 593  
      * @param expansionFactor the new expansion factor value.
 594  
      * @throws IllegalArgumentException if expansionFactor is <= 1 or greater
 595  
      * than contractionFactor
 596  
      */
 597  
     public void setExpansionFactor(float expansionFactor) {
 598  4
         checkContractExpand(getContractionCriteria(), expansionFactor);
 599  
         // The check above verifies that the expansion factor is > 1.0;
 600  2
         this.expansionFactor = expansionFactor;
 601  2
     }
 602  
 
 603  
     /**
 604  
      * Sets the <code>expansionMode</code>. The specified value must be one of
 605  
      * ADDITIVE_MODE, MULTIPLICATIVE_MODE.
 606  
      * 
 607  
      * @param expansionMode The expansionMode to set.
 608  
      * @throws IllegalArgumentException if the specified mode value is not valid
 609  
      */
 610  
     public void setExpansionMode(int expansionMode) {
 611  12
         if (expansionMode != MULTIPLICATIVE_MODE && 
 612  
                 expansionMode != ADDITIVE_MODE) {
 613  4
             throw new IllegalArgumentException("Illegal expansionMode setting.");  
 614  
         }
 615  8
         this.expansionMode = expansionMode;
 616  8
     }
 617  
     
 618  
     /**
 619  
      * Sets the initial capacity.  Should only be invoked by constructors.
 620  
      * 
 621  
      * @param initialCapacity of the array
 622  
      * @throws IllegalArgumentException if <code>initialCapacity</code> is not
 623  
      *         positive.
 624  
      */
 625  
     protected void setInitialCapacity(int initialCapacity) {
 626  24
         if (initialCapacity > 0) {
 627  22
             this.initialCapacity = initialCapacity;
 628  
         } else {
 629  2
             String msg =
 630  
                 "The initial capacity supplied: " + initialCapacity +
 631  
                 "must be a positive integer";
 632  2
             throw new IllegalArgumentException(msg);
 633  
         }
 634  22
     }
 635  
     
 636  
     /**
 637  
      * This function allows you to control the number of elements contained 
 638  
      * in this array, and can be used to "throw out" the last n values in an 
 639  
      * array. This function will also expand the internal array as needed.
 640  
      * 
 641  
      * @param i a new number of elements
 642  
      * @throws IllegalArgumentException if <code>i</code> is negative.
 643  
      */
 644  
     public synchronized void setNumElements(int i) {
 645  
 
 646  
         // If index is negative thrown an error
 647  6
         if (i < 0) {
 648  2
             String msg =
 649  
                 "Number of elements must be zero or a positive " + "integer";
 650  2
             throw new IllegalArgumentException(msg);
 651  
         }
 652  
 
 653  
         // Test the new num elements, check to see if the array needs to be 
 654  
         // expanded to accomodate this new number of elements
 655  4
         if ((startIndex + i) > internalArray.length) {
 656  2
             expandTo(startIndex + i);
 657  
         }
 658  
 
 659  
         // Set the new number of elements to new value
 660  4
         numElements = i;
 661  4
     }
 662  
 
 663  
     /**
 664  
      * Returns true if the internal storage array has too many unused 
 665  
      * storage positions.  
 666  
      * 
 667  
      * @return true if array satisfies the contraction criteria
 668  
      */
 669  
     private synchronized boolean shouldContract() {
 670  20829
         if (expansionMode == MULTIPLICATIVE_MODE) { 
 671  20813
             return (internalArray.length / numElements) > contractionCriteria;
 672  
         } else {
 673  16
             return (internalArray.length - numElements) > contractionCriteria;
 674  
         }
 675  
     }
 676  
 
 677  
     /**
 678  
      * Returns the starting index of the internal array.  The starting index is
 679  
      * the position of the first addressable element in the internal storage
 680  
      * array.  The addressable elements in the array are <code>
 681  
      * internalArray[startIndex],...,internalArray[startIndex + numElements -1]
 682  
      * </code>
 683  
      *
 684  
      * @return starting index
 685  
      */
 686  
     public int start() {
 687  618
         return startIndex;
 688  
     }
 689  
 
 690  
 }