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