1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.math.util; 18 19 import java.io.Serializable; 20 import java.util.Arrays; 21 22 import org.apache.commons.math.MathRuntimeException; 23 import org.apache.commons.math.exception.util.LocalizedFormats; 24 25 /** 26 * <p> 27 * A variable length {@link DoubleArray} implementation that automatically 28 * handles expanding and contracting its internal storage array as elements 29 * are added and removed. 30 * </p> 31 * <p> 32 * The internal storage array starts with capacity determined by the 33 * <code>initialCapacity</code> property, which can be set by the constructor. 34 * The default initial capacity is 16. Adding elements using 35 * {@link #addElement(double)} appends elements to the end of the array. When 36 * there are no open entries at the end of the internal storage array, the 37 * array is expanded. The size of the expanded array depends on the 38 * <code>expansionMode</code> and <code>expansionFactor</code> properties. 39 * The <code>expansionMode</code> determines whether the size of the array is 40 * multiplied by the <code>expansionFactor</code> (MULTIPLICATIVE_MODE) or if 41 * the expansion is additive (ADDITIVE_MODE -- <code>expansionFactor</code> 42 * storage locations added). The default <code>expansionMode</code> is 43 * MULTIPLICATIVE_MODE and the default <code>expansionFactor</code> 44 * is 2.0. 45 * </p> 46 * <p> 47 * The {@link #addElementRolling(double)} method adds a new element to the end 48 * of the internal storage array and adjusts the "usable window" of the 49 * internal array forward by one position (effectively making what was the 50 * second element the first, and so on). Repeated activations of this method 51 * (or activation of {@link #discardFrontElements(int)}) will effectively orphan 52 * the storage locations at the beginning of the internal storage array. To 53 * reclaim this storage, each time one of these methods is activated, the size 54 * of the internal storage array is compared to the number of addressable 55 * elements (the <code>numElements</code> property) and if the difference 56 * is too large, the internal array is contracted to size 57 * <code>numElements + 1.</code> The determination of when the internal 58 * storage array is "too large" depends on the <code>expansionMode</code> and 59 * <code>contractionFactor</code> properties. If the <code>expansionMode</code> 60 * is <code>MULTIPLICATIVE_MODE</code>, contraction is triggered when the 61 * ratio between storage array length and <code>numElements</code> exceeds 62 * <code>contractionFactor.</code> If the <code>expansionMode</code> 63 * is <code>ADDITIVE_MODE,</code> the number of excess storage locations 64 * is compared to <code>contractionFactor.</code> 65 * </p> 66 * <p> 67 * To avoid cycles of expansions and contractions, the 68 * <code>expansionFactor</code> must not exceed the 69 * <code>contractionFactor.</code> Constructors and mutators for both of these 70 * properties enforce this requirement, throwing IllegalArgumentException if it 71 * is violated. 72 * </p> 73 * @version $Revision: 1073474 $ $Date: 2011-02-22 20:52:17 +0100 (mar. 22 fvr. 2011) $ 74 */ 75 public class ResizableDoubleArray implements DoubleArray, Serializable { 76 77 /** additive expansion mode */ 78 public static final int ADDITIVE_MODE = 1; 79 80 /** multiplicative expansion mode */ 81 public static final int MULTIPLICATIVE_MODE = 0; 82 83 /** Serializable version identifier */ 84 private static final long serialVersionUID = -3485529955529426875L; 85 86 /** 87 * The contraction criteria determines when the internal array will be 88 * contracted to fit the number of elements contained in the element 89 * array + 1. 90 */ 91 protected float contractionCriteria = 2.5f; 92 93 /** 94 * The expansion factor of the array. When the array needs to be expanded, 95 * the new array size will be 96 * <code>internalArray.length * expansionFactor</code> 97 * if <code>expansionMode</code> is set to MULTIPLICATIVE_MODE, or 98 * <code>internalArray.length + expansionFactor</code> if 99 * <code>expansionMode</code> is set to ADDITIVE_MODE. 100 */ 101 protected float expansionFactor = 2.0f; 102 103 /** 104 * Determines whether array expansion by <code>expansionFactor</code> 105 * is additive or multiplicative. 106 */ 107 protected int expansionMode = MULTIPLICATIVE_MODE; 108 109 /** 110 * The initial capacity of the array. Initial capacity is not exposed as a 111 * property as it is only meaningful when passed to a constructor. 112 */ 113 protected int initialCapacity = 16; 114 115 /** 116 * The internal storage array. 117 */ 118 protected double[] internalArray; 119 120 /** 121 * The number of addressable elements in the array. Note that this 122 * has nothing to do with the length of the internal storage array. 123 */ 124 protected int numElements = 0; 125 126 /** 127 * The position of the first addressable element in the internal storage 128 * array. The addressable elements in the array are <code> 129 * internalArray[startIndex],...,internalArray[startIndex + numElements -1] 130 * </code> 131 */ 132 protected int startIndex = 0; 133 134 /** 135 * Create a ResizableArray with default properties. 136 * <ul> 137 * <li><code>initialCapacity = 16</code></li> 138 * <li><code>expansionMode = MULTIPLICATIVE_MODE</code></li> 139 * <li><code>expansionFactor = 2.5</code></li> 140 * <li><code>contractionFactor = 2.0</code></li> 141 * </ul> 142 */ 143 public ResizableDoubleArray() { 144 internalArray = new double[initialCapacity]; 145 } 146 147 /** 148 * Create a ResizableArray with the specified initial capacity. Other 149 * properties take default values: 150 * <ul> 151 * <li><code>expansionMode = MULTIPLICATIVE_MODE</code></li> 152 * <li><code>expansionFactor = 2.5</code></li> 153 * <li><code>contractionFactor = 2.0</code></li> 154 * </ul> 155 * @param initialCapacity The initial size of the internal storage array 156 * @throws IllegalArgumentException if initialCapacity is not > 0 157 */ 158 public ResizableDoubleArray(int initialCapacity) { 159 setInitialCapacity(initialCapacity); 160 internalArray = new double[this.initialCapacity]; 161 } 162 163 /** 164 * Create a ResizableArray from an existing double[] with the 165 * initial capacity and numElements corresponding to the size of 166 * the supplied double[] array. If the supplied array is null, a 167 * new empty array with the default initial capacity will be created. 168 * The input array is copied, not referenced. 169 * Other properties take default values: 170 * <ul> 171 * <li><code>initialCapacity = 16</code></li> 172 * <li><code>expansionMode = MULTIPLICATIVE_MODE</code></li> 173 * <li><code>expansionFactor = 2.5</code></li> 174 * <li><code>contractionFactor = 2.0</code></li> 175 * </ul> 176 * 177 * @param initialArray initial array 178 * @since 2.2 179 */ 180 public ResizableDoubleArray(double[] initialArray) { 181 if (initialArray == null) { 182 this.internalArray = new double[initialCapacity]; 183 } else { 184 this.internalArray = new double[initialArray.length]; 185 System.arraycopy(initialArray, 0, this.internalArray, 0, initialArray.length); 186 initialCapacity = initialArray.length; 187 numElements = initialArray.length; 188 } 189 } 190 191 /** 192 * <p> 193 * Create a ResizableArray with the specified initial capacity 194 * and expansion factor. The remaining properties take default 195 * values: 196 * <ul> 197 * <li><code>expansionMode = MULTIPLICATIVE_MODE</code></li> 198 * <li><code>contractionFactor = 0.5 + expansionFactor</code></li> 199 * </ul></p> 200 * <p> 201 * Throws IllegalArgumentException if the following conditions are 202 * not met: 203 * <ul> 204 * <li><code>initialCapacity > 0</code></li> 205 * <li><code>expansionFactor > 1</code></li> 206 * </ul></p> 207 * 208 * @param initialCapacity The initial size of the internal storage array 209 * @param expansionFactor the array will be expanded based on this 210 * parameter 211 * @throws IllegalArgumentException if parameters are not valid 212 */ 213 public ResizableDoubleArray(int initialCapacity, float expansionFactor) { 214 this.expansionFactor = expansionFactor; 215 setInitialCapacity(initialCapacity); 216 internalArray = new double[initialCapacity]; 217 setContractionCriteria(expansionFactor +0.5f); 218 } 219 220 /** 221 * <p> 222 * Create a ResizableArray with the specified initialCapacity, 223 * expansionFactor, and contractionCriteria. The <code>expansionMode</code> 224 * will default to <code>MULTIPLICATIVE_MODE.</code></p> 225 * <p> 226 * Throws IllegalArgumentException if the following conditions are 227 * not met: 228 * <ul> 229 * <li><code>initialCapacity > 0</code></li> 230 * <li><code>expansionFactor > 1</code></li> 231 * <li><code>contractionFactor >= expansionFactor</code></li> 232 * </ul></p> 233 * @param initialCapacity The initial size of the internal storage array 234 * @param expansionFactor the array will be expanded based on this 235 * parameter 236 * @param contractionCriteria The contraction Criteria. 237 * @throws IllegalArgumentException if parameters are not valid 238 */ 239 public ResizableDoubleArray(int initialCapacity, float expansionFactor, 240 float contractionCriteria) { 241 this.expansionFactor = expansionFactor; 242 setContractionCriteria(contractionCriteria); 243 setInitialCapacity(initialCapacity); 244 internalArray = new double[initialCapacity]; 245 } 246 247 /** 248 * <p> 249 * Create a ResizableArray with the specified properties.</p> 250 * <p> 251 * Throws IllegalArgumentException if the following conditions are 252 * not met: 253 * <ul> 254 * <li><code>initialCapacity > 0</code></li> 255 * <li><code>expansionFactor > 1</code></li> 256 * <li><code>contractionFactor >= expansionFactor</code></li> 257 * <li><code>expansionMode in {MULTIPLICATIVE_MODE, ADDITIVE_MODE}</code> 258 * </li> 259 * </ul></p> 260 * 261 * @param initialCapacity the initial size of the internal storage array 262 * @param expansionFactor the array will be expanded based on this 263 * parameter 264 * @param contractionCriteria the contraction Criteria 265 * @param expansionMode the expansion mode 266 * @throws IllegalArgumentException if parameters are not valid 267 */ 268 public ResizableDoubleArray(int initialCapacity, float expansionFactor, 269 float contractionCriteria, int expansionMode) { 270 this.expansionFactor = expansionFactor; 271 setContractionCriteria(contractionCriteria); 272 setInitialCapacity(initialCapacity); 273 setExpansionMode(expansionMode); 274 internalArray = new double[initialCapacity]; 275 } 276 277 /** 278 * Copy constructor. Creates a new ResizableDoubleArray that is a deep, 279 * fresh copy of the original. Needs to acquire synchronization lock 280 * on original. Original may not be null; otherwise a NullPointerException 281 * is thrown. 282 * 283 * @param original array to copy 284 * @since 2.0 285 */ 286 public ResizableDoubleArray(ResizableDoubleArray original) { 287 copy(original, this); 288 } 289 290 /** 291 * Adds an element to the end of this expandable array. 292 * 293 * @param value to be added to end of array 294 */ 295 public synchronized void addElement(double value) { 296 numElements++; 297 if ((startIndex + numElements) > internalArray.length) { 298 expand(); 299 } 300 internalArray[startIndex + (numElements - 1)] = value; 301 if (shouldContract()) { 302 contract(); 303 } 304 } 305 306 /** 307 * Adds several element to the end of this expandable array. 308 * 309 * @param values to be added to end of array 310 * @since 2.2 311 */ 312 public synchronized void addElements(double[] values) { 313 final double[] tempArray = new double[numElements + values.length + 1]; 314 System.arraycopy(internalArray, startIndex, tempArray, 0, numElements); 315 System.arraycopy(values, 0, tempArray, numElements, values.length); 316 internalArray = tempArray; 317 startIndex = 0; 318 numElements += values.length; 319 } 320 321 /** 322 * <p> 323 * Adds an element to the end of the array and removes the first 324 * element in the array. Returns the discarded first element. 325 * The effect is similar to a push operation in a FIFO queue. 326 * </p> 327 * <p> 328 * Example: If the array contains the elements 1, 2, 3, 4 (in that order) 329 * and addElementRolling(5) is invoked, the result is an array containing 330 * the entries 2, 3, 4, 5 and the value returned is 1. 331 * </p> 332 * 333 * @param value the value to be added to the array 334 * @return the value which has been discarded or "pushed" out of the array 335 * by this rolling insert 336 */ 337 public synchronized double addElementRolling(double value) { 338 double discarded = internalArray[startIndex]; 339 340 if ((startIndex + (numElements + 1)) > internalArray.length) { 341 expand(); 342 } 343 // Increment the start index 344 startIndex += 1; 345 346 // Add the new value 347 internalArray[startIndex + (numElements - 1)] = value; 348 349 // Check the contraction criteria 350 if (shouldContract()) { 351 contract(); 352 } 353 return discarded; 354 } 355 356 /** 357 * Substitutes <code>value</code> for the most recently added value. 358 * Returns the value that has been replaced. If the array is empty (i.e. 359 * if {@link #numElements} is zero), a MathRuntimeException is thrown. 360 * 361 * @param value new value to substitute for the most recently added value 362 * @return value that has been replaced in the array 363 * @since 2.0 364 */ 365 public synchronized double substituteMostRecentElement(double value) { 366 if (numElements < 1) { 367 throw MathRuntimeException.createArrayIndexOutOfBoundsException( 368 LocalizedFormats.CANNOT_SUBSTITUTE_ELEMENT_FROM_EMPTY_ARRAY); 369 } 370 371 double discarded = internalArray[startIndex + (numElements - 1)]; 372 373 internalArray[startIndex + (numElements - 1)] = value; 374 375 return discarded; 376 } 377 378 379 /** 380 * Checks the expansion factor and the contraction criteria and throws an 381 * IllegalArgumentException if the contractionCriteria is less than the 382 * expansionCriteria 383 * 384 * @param expansion factor to be checked 385 * @param contraction criteria to be checked 386 * @throws IllegalArgumentException if the contractionCriteria is less than 387 * the expansionCriteria. 388 */ 389 protected void checkContractExpand(float contraction, float expansion) { 390 391 if (contraction < expansion) { 392 throw MathRuntimeException.createIllegalArgumentException( 393 LocalizedFormats.CONTRACTION_CRITERIA_SMALLER_THAN_EXPANSION_FACTOR, 394 contraction, expansion); 395 } 396 397 if (contraction <= 1.0) { 398 throw MathRuntimeException.createIllegalArgumentException( 399 LocalizedFormats.CONTRACTION_CRITERIA_SMALLER_THAN_ONE, 400 contraction); 401 } 402 403 if (expansion <= 1.0) { 404 throw MathRuntimeException.createIllegalArgumentException( 405 LocalizedFormats.EXPANSION_FACTOR_SMALLER_THAN_ONE, 406 expansion); 407 } 408 } 409 410 /** 411 * Clear the array, reset the size to the initialCapacity and the number 412 * of elements to zero. 413 */ 414 public synchronized void clear() { 415 numElements = 0; 416 startIndex = 0; 417 internalArray = new double[initialCapacity]; 418 } 419 420 /** 421 * Contracts the storage array to the (size of the element set) + 1 - to 422 * avoid a zero length array. This function also resets the startIndex to 423 * zero. 424 */ 425 public synchronized void contract() { 426 double[] tempArray = new double[numElements + 1]; 427 428 // Copy and swap - copy only the element array from the src array. 429 System.arraycopy(internalArray, startIndex, tempArray, 0, numElements); 430 internalArray = tempArray; 431 432 // Reset the start index to zero 433 startIndex = 0; 434 } 435 436 /** 437 * Discards the <code>i<code> initial elements of the array. For example, 438 * if the array contains the elements 1,2,3,4, invoking 439 * <code>discardFrontElements(2)</code> will cause the first two elements 440 * to be discarded, leaving 3,4 in the array. Throws illegalArgumentException 441 * if i exceeds numElements. 442 * 443 * @param i the number of elements to discard from the front of the array 444 * @throws IllegalArgumentException if i is greater than numElements. 445 * @since 2.0 446 */ 447 public synchronized void discardFrontElements(int i) { 448 449 discardExtremeElements(i,true); 450 451 } 452 453 /** 454 * Discards the <code>i<code> last elements of the array. For example, 455 * if the array contains the elements 1,2,3,4, invoking 456 * <code>discardMostRecentElements(2)</code> will cause the last two elements 457 * to be discarded, leaving 1,2 in the array. Throws illegalArgumentException 458 * if i exceeds numElements. 459 * 460 * @param i the number of elements to discard from the end of the array 461 * @throws IllegalArgumentException if i is greater than numElements. 462 * @since 2.0 463 */ 464 public synchronized void discardMostRecentElements(int i) { 465 466 discardExtremeElements(i,false); 467 468 } 469 470 /** 471 * Discards the <code>i<code> first or last elements of the array, 472 * depending on the value of <code>front</code>. 473 * For example, if the array contains the elements 1,2,3,4, invoking 474 * <code>discardExtremeElements(2,false)</code> will cause the last two elements 475 * to be discarded, leaving 1,2 in the array. 476 * For example, if the array contains the elements 1,2,3,4, invoking 477 * <code>discardExtremeElements(2,true)</code> will cause the first two elements 478 * to be discarded, leaving 3,4 in the array. 479 * Throws illegalArgumentException 480 * if i exceeds numElements. 481 * 482 * @param i the number of elements to discard from the front/end of the array 483 * @param front true if elements are to be discarded from the front 484 * of the array, false if elements are to be discarded from the end 485 * of the array 486 * @throws IllegalArgumentException if i is greater than numElements. 487 * @since 2.0 488 */ 489 private synchronized void discardExtremeElements(int i,boolean front) { 490 if (i > numElements) { 491 throw MathRuntimeException.createIllegalArgumentException( 492 LocalizedFormats.TOO_MANY_ELEMENTS_TO_DISCARD_FROM_ARRAY, 493 i, numElements); 494 } else if (i < 0) { 495 throw MathRuntimeException.createIllegalArgumentException( 496 LocalizedFormats.CANNOT_DISCARD_NEGATIVE_NUMBER_OF_ELEMENTS, 497 i); 498 } else { 499 // "Subtract" this number of discarded from numElements 500 numElements -= i; 501 if (front) startIndex += i; 502 } 503 if (shouldContract()) { 504 contract(); 505 } 506 } 507 508 /** 509 * Expands the internal storage array using the expansion factor. 510 * <p> 511 * if <code>expansionMode</code> is set to MULTIPLICATIVE_MODE, 512 * the new array size will be <code>internalArray.length * expansionFactor.</code> 513 * If <code>expansionMode</code> is set to ADDITIVE_MODE, the length 514 * after expansion will be <code>internalArray.length + expansionFactor</code> 515 * </p> 516 */ 517 protected synchronized void expand() { 518 519 // notice the use of FastMath.ceil(), this guarantees that we will always 520 // have an array of at least currentSize + 1. Assume that the 521 // current initial capacity is 1 and the expansion factor 522 // is 1.000000000000000001. The newly calculated size will be 523 // rounded up to 2 after the multiplication is performed. 524 int newSize = 0; 525 if (expansionMode == MULTIPLICATIVE_MODE) { 526 newSize = (int) FastMath.ceil(internalArray.length * expansionFactor); 527 } else { 528 newSize = internalArray.length + FastMath.round(expansionFactor); 529 } 530 double[] tempArray = new double[newSize]; 531 532 // Copy and swap 533 System.arraycopy(internalArray, 0, tempArray, 0, internalArray.length); 534 internalArray = tempArray; 535 } 536 537 /** 538 * Expands the internal storage array to the specified size. 539 * 540 * @param size Size of the new internal storage array 541 */ 542 private synchronized void expandTo(int size) { 543 double[] tempArray = new double[size]; 544 // Copy and swap 545 System.arraycopy(internalArray, 0, tempArray, 0, internalArray.length); 546 internalArray = tempArray; 547 } 548 549 /** 550 * The contraction criteria defines when the internal array will contract 551 * to store only the number of elements in the element array. 552 * If the <code>expansionMode</code> is <code>MULTIPLICATIVE_MODE</code>, 553 * contraction is triggered when the ratio between storage array length 554 * and <code>numElements</code> exceeds <code>contractionFactor</code>. 555 * If the <code>expansionMode</code> is <code>ADDITIVE_MODE</code>, the 556 * number of excess storage locations is compared to 557 * <code>contractionFactor.</code> 558 * 559 * @return the contraction criteria used to reclaim memory. 560 */ 561 public float getContractionCriteria() { 562 return contractionCriteria; 563 } 564 565 /** 566 * Returns the element at the specified index 567 * 568 * @param index index to fetch a value from 569 * @return value stored at the specified index 570 * @throws ArrayIndexOutOfBoundsException if <code>index</code> is less than 571 * zero or is greater than <code>getNumElements() - 1</code>. 572 */ 573 public synchronized double getElement(int index) { 574 if (index >= numElements) { 575 throw MathRuntimeException.createArrayIndexOutOfBoundsException( 576 LocalizedFormats.INDEX_LARGER_THAN_MAX, 577 index, numElements - 1); 578 } else if (index >= 0) { 579 return internalArray[startIndex + index]; 580 } else { 581 throw MathRuntimeException.createArrayIndexOutOfBoundsException( 582 LocalizedFormats.CANNOT_RETRIEVE_AT_NEGATIVE_INDEX, 583 index); 584 } 585 } 586 587 /** 588 * Returns a double array containing the elements of this 589 * <code>ResizableArray</code>. This method returns a copy, not a 590 * reference to the underlying array, so that changes made to the returned 591 * array have no effect on this <code>ResizableArray.</code> 592 * @return the double array. 593 */ 594 public synchronized double[] getElements() { 595 double[] elementArray = new double[numElements]; 596 System.arraycopy( internalArray, startIndex, elementArray, 0, 597 numElements); 598 return elementArray; 599 } 600 601 /** 602 * The expansion factor controls the size of a new array when an array 603 * needs to be expanded. The <code>expansionMode</code> 604 * determines whether the size of the array is multiplied by the 605 * <code>expansionFactor</code> (MULTIPLICATIVE_MODE) or if 606 * the expansion is additive (ADDITIVE_MODE -- <code>expansionFactor</code> 607 * storage locations added). The default <code>expansionMode</code> is 608 * MULTIPLICATIVE_MODE and the default <code>expansionFactor</code> 609 * is 2.0. 610 * 611 * @return the expansion factor of this expandable double array 612 */ 613 public float getExpansionFactor() { 614 return expansionFactor; 615 } 616 617 /** 618 * The <code>expansionMode</code> determines whether the internal storage 619 * array grows additively (ADDITIVE_MODE) or multiplicatively 620 * (MULTIPLICATIVE_MODE) when it is expanded. 621 * 622 * @return Returns the expansionMode. 623 */ 624 public int getExpansionMode() { 625 return expansionMode; 626 } 627 628 /** 629 * Notice the package scope on this method. This method is simply here 630 * for the JUnit test, it allows us check if the expansion is working 631 * properly after a number of expansions. This is not meant to be a part 632 * of the public interface of this class. 633 * 634 * @return the length of the internal storage array. 635 */ 636 synchronized int getInternalLength() { 637 return internalArray.length; 638 } 639 640 /** 641 * Returns the number of elements currently in the array. Please note 642 * that this is different from the length of the internal storage array. 643 * 644 * @return number of elements 645 */ 646 public synchronized int getNumElements() { 647 return numElements; 648 } 649 650 /** 651 * Returns the internal storage array. Note that this method returns 652 * a reference to the internal storage array, not a copy, and to correctly 653 * address elements of the array, the <code>startIndex</code> is 654 * required (available via the {@link #start} method). This method should 655 * only be used in cases where copying the internal array is not practical. 656 * The {@link #getElements} method should be used in all other cases. 657 * 658 * 659 * @return the internal storage array used by this object 660 * @deprecated replaced by {@link #getInternalValues()} as of 2.0 661 */ 662 @Deprecated 663 public synchronized double[] getValues() { 664 return internalArray; 665 } 666 667 /** 668 * Returns the internal storage array. Note that this method returns 669 * a reference to the internal storage array, not a copy, and to correctly 670 * address elements of the array, the <code>startIndex</code> is 671 * required (available via the {@link #start} method). This method should 672 * only be used in cases where copying the internal array is not practical. 673 * The {@link #getElements} method should be used in all other cases. 674 * 675 * 676 * @return the internal storage array used by this object 677 * @since 2.0 678 */ 679 public synchronized double[] getInternalValues() { 680 return internalArray; 681 } 682 683 /** 684 * Sets the contraction criteria for this ExpandContractDoubleArray. 685 * 686 * @param contractionCriteria contraction criteria 687 */ 688 public void setContractionCriteria(float contractionCriteria) { 689 checkContractExpand(contractionCriteria, getExpansionFactor()); 690 synchronized(this) { 691 this.contractionCriteria = contractionCriteria; 692 } 693 } 694 695 696 /** 697 * Sets the element at the specified index. If the specified index is greater than 698 * <code>getNumElements() - 1</code>, the <code>numElements</code> property 699 * is increased to <code>index +1</code> and additional storage is allocated 700 * (if necessary) for the new element and all (uninitialized) elements 701 * between the new element and the previous end of the array). 702 * 703 * @param index index to store a value in 704 * @param value value to store at the specified index 705 * @throws ArrayIndexOutOfBoundsException if <code>index</code> is less than 706 * zero. 707 */ 708 public synchronized void setElement(int index, double value) { 709 if (index < 0) { 710 throw MathRuntimeException.createArrayIndexOutOfBoundsException( 711 LocalizedFormats.CANNOT_SET_AT_NEGATIVE_INDEX, 712 index); 713 } 714 if (index + 1 > numElements) { 715 numElements = index + 1; 716 } 717 if ((startIndex + index) >= internalArray.length) { 718 expandTo(startIndex + (index + 1)); 719 } 720 internalArray[startIndex + index] = value; 721 } 722 723 /** 724 * Sets the expansionFactor. Throws IllegalArgumentException if the 725 * the following conditions are not met: 726 * <ul> 727 * <li><code>expansionFactor > 1</code></li> 728 * <li><code>contractionFactor >= expansionFactor</code></li> 729 * </ul> 730 * @param expansionFactor the new expansion factor value. 731 * @throws IllegalArgumentException if expansionFactor is <= 1 or greater 732 * than contractionFactor 733 */ 734 public void setExpansionFactor(float expansionFactor) { 735 checkContractExpand(getContractionCriteria(), expansionFactor); 736 // The check above verifies that the expansion factor is > 1.0; 737 synchronized(this) { 738 this.expansionFactor = expansionFactor; 739 } 740 } 741 742 /** 743 * Sets the <code>expansionMode</code>. The specified value must be one of 744 * ADDITIVE_MODE, MULTIPLICATIVE_MODE. 745 * 746 * @param expansionMode The expansionMode to set. 747 * @throws IllegalArgumentException if the specified mode value is not valid 748 */ 749 public void setExpansionMode(int expansionMode) { 750 if (expansionMode != MULTIPLICATIVE_MODE && 751 expansionMode != ADDITIVE_MODE) { 752 throw MathRuntimeException.createIllegalArgumentException( 753 LocalizedFormats.UNSUPPORTED_EXPANSION_MODE, 754 expansionMode, MULTIPLICATIVE_MODE, "MULTIPLICATIVE_MODE", 755 ADDITIVE_MODE, "ADDITIVE_MODE"); 756 } 757 synchronized(this) { 758 this.expansionMode = expansionMode; 759 } 760 } 761 762 /** 763 * Sets the initial capacity. Should only be invoked by constructors. 764 * 765 * @param initialCapacity of the array 766 * @throws IllegalArgumentException if <code>initialCapacity</code> is not 767 * positive. 768 */ 769 protected void setInitialCapacity(int initialCapacity) { 770 if (initialCapacity > 0) { 771 synchronized(this) { 772 this.initialCapacity = initialCapacity; 773 } 774 } else { 775 throw MathRuntimeException.createIllegalArgumentException( 776 LocalizedFormats.INITIAL_CAPACITY_NOT_POSITIVE, 777 initialCapacity); 778 } 779 } 780 781 /** 782 * This function allows you to control the number of elements contained 783 * in this array, and can be used to "throw out" the last n values in an 784 * array. This function will also expand the internal array as needed. 785 * 786 * @param i a new number of elements 787 * @throws IllegalArgumentException if <code>i</code> is negative. 788 */ 789 public synchronized void setNumElements(int i) { 790 791 // If index is negative thrown an error 792 if (i < 0) { 793 throw MathRuntimeException.createIllegalArgumentException( 794 LocalizedFormats.INDEX_NOT_POSITIVE, 795 i); 796 } 797 798 // Test the new num elements, check to see if the array needs to be 799 // expanded to accommodate this new number of elements 800 if ((startIndex + i) > internalArray.length) { 801 expandTo(startIndex + i); 802 } 803 804 // Set the new number of elements to new value 805 numElements = i; 806 } 807 808 /** 809 * Returns true if the internal storage array has too many unused 810 * storage positions. 811 * 812 * @return true if array satisfies the contraction criteria 813 */ 814 private synchronized boolean shouldContract() { 815 if (expansionMode == MULTIPLICATIVE_MODE) { 816 return (internalArray.length / ((float) numElements)) > contractionCriteria; 817 } else { 818 return (internalArray.length - numElements) > contractionCriteria; 819 } 820 } 821 822 /** 823 * Returns the starting index of the internal array. The starting index is 824 * the position of the first addressable element in the internal storage 825 * array. The addressable elements in the array are <code> 826 * internalArray[startIndex],...,internalArray[startIndex + numElements -1] 827 * </code> 828 * 829 * @return starting index 830 */ 831 public synchronized int start() { 832 return startIndex; 833 } 834 835 /** 836 * <p>Copies source to dest, copying the underlying data, so dest is 837 * a new, independent copy of source. Does not contract before 838 * the copy.</p> 839 * 840 * <p>Obtains synchronization locks on both source and dest 841 * (in that order) before performing the copy.</p> 842 * 843 * <p>Neither source nor dest may be null; otherwise a NullPointerException 844 * is thrown</p> 845 * 846 * @param source ResizableDoubleArray to copy 847 * @param dest ResizableArray to replace with a copy of the source array 848 * @since 2.0 849 * 850 */ 851 public static void copy(ResizableDoubleArray source, ResizableDoubleArray dest) { 852 synchronized(source) { 853 synchronized(dest) { 854 dest.initialCapacity = source.initialCapacity; 855 dest.contractionCriteria = source.contractionCriteria; 856 dest.expansionFactor = source.expansionFactor; 857 dest.expansionMode = source.expansionMode; 858 dest.internalArray = new double[source.internalArray.length]; 859 System.arraycopy(source.internalArray, 0, dest.internalArray, 860 0, dest.internalArray.length); 861 dest.numElements = source.numElements; 862 dest.startIndex = source.startIndex; 863 } 864 } 865 } 866 867 /** 868 * Returns a copy of the ResizableDoubleArray. Does not contract before 869 * the copy, so the returned object is an exact copy of this. 870 * 871 * @return a new ResizableDoubleArray with the same data and configuration 872 * properties as this 873 * @since 2.0 874 */ 875 public synchronized ResizableDoubleArray copy() { 876 ResizableDoubleArray result = new ResizableDoubleArray(); 877 copy(this, result); 878 return result; 879 } 880 881 /** 882 * Returns true iff object is a ResizableDoubleArray with the same properties 883 * as this and an identical internal storage array. 884 * 885 * @param object object to be compared for equality with this 886 * @return true iff object is a ResizableDoubleArray with the same data and 887 * properties as this 888 * @since 2.0 889 */ 890 @Override 891 public boolean equals(Object object) { 892 if (object == this ) { 893 return true; 894 } 895 if (object instanceof ResizableDoubleArray == false) { 896 return false; 897 } 898 synchronized(this) { 899 synchronized(object) { 900 boolean result = true; 901 ResizableDoubleArray other = (ResizableDoubleArray) object; 902 result = result && (other.initialCapacity == initialCapacity); 903 result = result && (other.contractionCriteria == contractionCriteria); 904 result = result && (other.expansionFactor == expansionFactor); 905 result = result && (other.expansionMode == expansionMode); 906 result = result && (other.numElements == numElements); 907 result = result && (other.startIndex == startIndex); 908 if (!result) { 909 return false; 910 } else { 911 return Arrays.equals(internalArray, other.internalArray); 912 } 913 } 914 } 915 } 916 917 /** 918 * Returns a hash code consistent with equals. 919 * 920 * @return hash code representing this ResizableDoubleArray 921 * @since 2.0 922 */ 923 @Override 924 public synchronized int hashCode() { 925 int[] hashData = new int[7]; 926 hashData[0] = new Float(expansionFactor).hashCode(); 927 hashData[1] = new Float(contractionCriteria).hashCode(); 928 hashData[2] = expansionMode; 929 hashData[3] = Arrays.hashCode(internalArray); 930 hashData[4] = initialCapacity; 931 hashData[5] = numElements; 932 hashData[6] = startIndex; 933 return Arrays.hashCode(hashData); 934 } 935 936 } 937