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: 811833 $ $Date: 2009-09-06 12:27:50 -0400 (Sun, 06 Sep 2009) $ 073 */ 074 public class ResizableDoubleArray implements DoubleArray, Serializable { 075 076 /** additive expansion mode */ 077 public static final int ADDITIVE_MODE = 1; 078 079 /** multiplicative expansion mode */ 080 public static final int MULTIPLICATIVE_MODE = 0; 081 082 /** Serializable version identifier */ 083 private static final long serialVersionUID = -3485529955529426875L; 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 expansion factor to be checked 341 * @param contraction criteria to be checked 342 * @throws IllegalArgumentException if the contractionCriteria is less than 343 * the expansionCriteria. 344 */ 345 protected void checkContractExpand(float contraction, float expansion) { 346 347 if (contraction < expansion) { 348 throw MathRuntimeException.createIllegalArgumentException( 349 "contraction criteria ({0}) smaller than the expansion factor ({1}). This would " + 350 "lead to a never ending loop of expansion and contraction as a newly expanded " + 351 "internal storage array would immediately satisfy the criteria for contraction", 352 contraction, expansion); 353 } 354 355 if (contraction <= 1.0) { 356 throw MathRuntimeException.createIllegalArgumentException( 357 "contraction criteria smaller than one ({0}). This would lead to a never ending " + 358 "loop of expansion and contraction as an internal storage array length equal " + 359 "to the number of elements would satisfy the contraction criteria.", 360 contraction); 361 } 362 363 if (expansion <= 1.0) { 364 throw MathRuntimeException.createIllegalArgumentException( 365 "expansion factor smaller than one ({0})", 366 expansion); 367 } 368 } 369 370 /** 371 * Clear the array, reset the size to the initialCapacity and the number 372 * of elements to zero. 373 */ 374 public synchronized void clear() { 375 numElements = 0; 376 startIndex = 0; 377 internalArray = new double[initialCapacity]; 378 } 379 380 /** 381 * Contracts the storage array to the (size of the element set) + 1 - to 382 * avoid a zero length array. This function also resets the startIndex to 383 * zero. 384 */ 385 public synchronized void contract() { 386 double[] tempArray = new double[numElements + 1]; 387 388 // Copy and swap - copy only the element array from the src array. 389 System.arraycopy(internalArray, startIndex, tempArray, 0, numElements); 390 internalArray = tempArray; 391 392 // Reset the start index to zero 393 startIndex = 0; 394 } 395 396 /** 397 * Discards the <code>i<code> initial elements of the array. For example, 398 * if the array contains the elements 1,2,3,4, invoking 399 * <code>discardFrontElements(2)</code> will cause the first two elements 400 * to be discarded, leaving 3,4 in the array. Throws illegalArgumentException 401 * if i exceeds numElements. 402 * 403 * @param i the number of elements to discard from the front of the array 404 * @throws IllegalArgumentException if i is greater than numElements. 405 * @since 2.0 406 */ 407 public synchronized void discardFrontElements(int i) { 408 409 discardExtremeElements(i,true); 410 411 } 412 413 /** 414 * Discards the <code>i<code> last elements of the array. For example, 415 * if the array contains the elements 1,2,3,4, invoking 416 * <code>discardMostRecentElements(2)</code> will cause the last two elements 417 * to be discarded, leaving 1,2 in the array. Throws illegalArgumentException 418 * if i exceeds numElements. 419 * 420 * @param i the number of elements to discard from the end of the array 421 * @throws IllegalArgumentException if i is greater than numElements. 422 * @since 2.0 423 */ 424 public synchronized void discardMostRecentElements(int i) { 425 426 discardExtremeElements(i,false); 427 428 } 429 430 /** 431 * Discards the <code>i<code> first or last elements of the array, 432 * depending on the value of <code>front</code>. 433 * For example, if the array contains the elements 1,2,3,4, invoking 434 * <code>discardExtremeElements(2,false)</code> will cause the last two elements 435 * to be discarded, leaving 1,2 in the array. 436 * For example, if the array contains the elements 1,2,3,4, invoking 437 * <code>discardExtremeElements(2,true)</code> will cause the first two elements 438 * to be discarded, leaving 3,4 in the array. 439 * Throws illegalArgumentException 440 * if i exceeds numElements. 441 * 442 * @param i the number of elements to discard from the front/end of the array 443 * @param front true if elements are to be discarded from the front 444 * of the array, false if elements are to be discarded from the end 445 * of the array 446 * @throws IllegalArgumentException if i is greater than numElements. 447 * @since 2.0 448 */ 449 private synchronized void discardExtremeElements(int i,boolean front) { 450 if (i > numElements) { 451 throw MathRuntimeException.createIllegalArgumentException( 452 "cannot discard {0} elements from a {1} elements array", 453 i, numElements); 454 } else if (i < 0) { 455 throw MathRuntimeException.createIllegalArgumentException( 456 "cannot discard a negative number of elements ({0})", 457 i); 458 } else { 459 // "Subtract" this number of discarded from numElements 460 numElements -= i; 461 if (front) startIndex += i; 462 } 463 if (shouldContract()) { 464 contract(); 465 } 466 } 467 468 /** 469 * Expands the internal storage array using the expansion factor. 470 * <p> 471 * if <code>expansionMode</code> is set to MULTIPLICATIVE_MODE, 472 * the new array size will be <code>internalArray.length * expansionFactor.</code> 473 * If <code>expansionMode</code> is set to ADDITIVE_MODE, the length 474 * after expansion will be <code>internalArray.length + expansionFactor</code> 475 * </p> 476 */ 477 protected synchronized void expand() { 478 479 // notice the use of Math.ceil(), this guarantees that we will always 480 // have an array of at least currentSize + 1. Assume that the 481 // current initial capacity is 1 and the expansion factor 482 // is 1.000000000000000001. The newly calculated size will be 483 // rounded up to 2 after the multiplication is performed. 484 int newSize = 0; 485 if (expansionMode == MULTIPLICATIVE_MODE) { 486 newSize = (int) Math.ceil(internalArray.length * expansionFactor); 487 } else { 488 newSize = internalArray.length + Math.round(expansionFactor); 489 } 490 double[] tempArray = new double[newSize]; 491 492 // Copy and swap 493 System.arraycopy(internalArray, 0, tempArray, 0, internalArray.length); 494 internalArray = tempArray; 495 } 496 497 /** 498 * Expands the internal storage array to the specified size. 499 * 500 * @param size Size of the new internal storage array 501 */ 502 private synchronized void expandTo(int size) { 503 double[] tempArray = new double[size]; 504 // Copy and swap 505 System.arraycopy(internalArray, 0, tempArray, 0, internalArray.length); 506 internalArray = tempArray; 507 } 508 509 /** 510 * The contraction criteria defines when the internal array will contract 511 * to store only the number of elements in the element array. 512 * If the <code>expansionMode</code> is <code>MULTIPLICATIVE_MODE</code>, 513 * contraction is triggered when the ratio between storage array length 514 * and <code>numElements</code> exceeds <code>contractionFactor</code>. 515 * If the <code>expansionMode</code> is <code>ADDITIVE_MODE</code>, the 516 * number of excess storage locations is compared to 517 * <code>contractionFactor.</code> 518 * 519 * @return the contraction criteria used to reclaim memory. 520 */ 521 public float getContractionCriteria() { 522 return contractionCriteria; 523 } 524 525 /** 526 * Returns the element at the specified index 527 * 528 * @param index index to fetch a value from 529 * @return value stored at the specified index 530 * @throws ArrayIndexOutOfBoundsException if <code>index</code> is less than 531 * zero or is greater than <code>getNumElements() - 1</code>. 532 */ 533 public synchronized double getElement(int index) { 534 if (index >= numElements) { 535 throw MathRuntimeException.createArrayIndexOutOfBoundsException( 536 "the index specified: {0} is larger than the current maximal index {1}", 537 index, numElements - 1); 538 } else if (index >= 0) { 539 return internalArray[startIndex + index]; 540 } else { 541 throw MathRuntimeException.createArrayIndexOutOfBoundsException( 542 "elements cannot be retrieved from a negative array index {0}", 543 index); 544 } 545 } 546 547 /** 548 * Returns a double array containing the elements of this 549 * <code>ResizableArray</code>. This method returns a copy, not a 550 * reference to the underlying array, so that changes made to the returned 551 * array have no effect on this <code>ResizableArray.</code> 552 * @return the double array. 553 */ 554 public synchronized double[] getElements() { 555 double[] elementArray = new double[numElements]; 556 System.arraycopy( internalArray, startIndex, elementArray, 0, 557 numElements); 558 return elementArray; 559 } 560 561 /** 562 * The expansion factor controls the size of a new array when an array 563 * needs to be expanded. The <code>expansionMode</code> 564 * determines whether the size of the array is multiplied by the 565 * <code>expansionFactor</code> (MULTIPLICATIVE_MODE) or if 566 * the expansion is additive (ADDITIVE_MODE -- <code>expansionFactor</code> 567 * storage locations added). The default <code>expansionMode</code> is 568 * MULTIPLICATIVE_MODE and the default <code>expansionFactor</code> 569 * is 2.0. 570 * 571 * @return the expansion factor of this expandable double array 572 */ 573 public float getExpansionFactor() { 574 return expansionFactor; 575 } 576 577 /** 578 * The <code>expansionMode</code> determines whether the internal storage 579 * array grows additively (ADDITIVE_MODE) or multiplicatively 580 * (MULTIPLICATIVE_MODE) when it is expanded. 581 * 582 * @return Returns the expansionMode. 583 */ 584 public int getExpansionMode() { 585 return expansionMode; 586 } 587 588 /** 589 * Notice the package scope on this method. This method is simply here 590 * for the JUnit test, it allows us check if the expansion is working 591 * properly after a number of expansions. This is not meant to be a part 592 * of the public interface of this class. 593 * 594 * @return the length of the internal storage array. 595 */ 596 synchronized int getInternalLength() { 597 return internalArray.length; 598 } 599 600 /** 601 * Returns the number of elements currently in the array. Please note 602 * that this is different from the length of the internal storage array. 603 * 604 * @return number of elements 605 */ 606 public synchronized int getNumElements() { 607 return numElements; 608 } 609 610 /** 611 * Returns the internal storage array. Note that this method returns 612 * a reference to the internal storage array, not a copy, and to correctly 613 * address elements of the array, the <code>startIndex</code> is 614 * required (available via the {@link #start} method). This method should 615 * only be used in cases where copying the internal array is not practical. 616 * The {@link #getElements} method should be used in all other cases. 617 * 618 * 619 * @return the internal storage array used by this object 620 * @deprecated replaced by {@link #getInternalValues()} as of 2.0 621 */ 622 @Deprecated 623 public synchronized double[] getValues() { 624 return internalArray; 625 } 626 627 /** 628 * Returns the internal storage array. Note that this method returns 629 * a reference to the internal storage array, not a copy, and to correctly 630 * address elements of the array, the <code>startIndex</code> is 631 * required (available via the {@link #start} method). This method should 632 * only be used in cases where copying the internal array is not practical. 633 * The {@link #getElements} method should be used in all other cases. 634 * 635 * 636 * @return the internal storage array used by this object 637 * @since 2.0 638 */ 639 public synchronized double[] getInternalValues() { 640 return internalArray; 641 } 642 643 /** 644 * Sets the contraction criteria for this ExpandContractDoubleArray. 645 * 646 * @param contractionCriteria contraction criteria 647 */ 648 public void setContractionCriteria(float contractionCriteria) { 649 checkContractExpand(contractionCriteria, getExpansionFactor()); 650 synchronized(this) { 651 this.contractionCriteria = contractionCriteria; 652 } 653 } 654 655 656 /** 657 * Sets the element at the specified index. If the specified index is greater than 658 * <code>getNumElements() - 1</code>, the <code>numElements</code> property 659 * is increased to <code>index +1</code> and additional storage is allocated 660 * (if necessary) for the new element and all (uninitialized) elements 661 * between the new element and the previous end of the array). 662 * 663 * @param index index to store a value in 664 * @param value value to store at the specified index 665 * @throws ArrayIndexOutOfBoundsException if <code>index</code> is less than 666 * zero. 667 */ 668 public synchronized void setElement(int index, double value) { 669 if (index < 0) { 670 throw MathRuntimeException.createArrayIndexOutOfBoundsException( 671 "cannot set an element at a negative index {0}", 672 index); 673 } 674 if (index + 1 > numElements) { 675 numElements = index + 1; 676 } 677 if ((startIndex + index) >= internalArray.length) { 678 expandTo(startIndex + (index + 1)); 679 } 680 internalArray[startIndex + index] = value; 681 } 682 683 /** 684 * Sets the expansionFactor. Throws IllegalArgumentException if the 685 * the following conditions are not met: 686 * <ul> 687 * <li><code>expansionFactor > 1</code></li> 688 * <li><code>contractionFactor >= expansionFactor</code></li> 689 * </ul> 690 * @param expansionFactor the new expansion factor value. 691 * @throws IllegalArgumentException if expansionFactor is <= 1 or greater 692 * than contractionFactor 693 */ 694 public void setExpansionFactor(float expansionFactor) { 695 checkContractExpand(getContractionCriteria(), expansionFactor); 696 // The check above verifies that the expansion factor is > 1.0; 697 synchronized(this) { 698 this.expansionFactor = expansionFactor; 699 } 700 } 701 702 /** 703 * Sets the <code>expansionMode</code>. The specified value must be one of 704 * ADDITIVE_MODE, MULTIPLICATIVE_MODE. 705 * 706 * @param expansionMode The expansionMode to set. 707 * @throws IllegalArgumentException if the specified mode value is not valid 708 */ 709 public void setExpansionMode(int expansionMode) { 710 if (expansionMode != MULTIPLICATIVE_MODE && 711 expansionMode != ADDITIVE_MODE) { 712 throw MathRuntimeException.createIllegalArgumentException( 713 "unsupported expansion mode {0}, supported modes are {1} ({2}) and {3} ({4})", 714 expansionMode, MULTIPLICATIVE_MODE, "MULTIPLICATIVE_MODE", 715 ADDITIVE_MODE, "ADDITIVE_MODE"); 716 } 717 synchronized(this) { 718 this.expansionMode = expansionMode; 719 } 720 } 721 722 /** 723 * Sets the initial capacity. Should only be invoked by constructors. 724 * 725 * @param initialCapacity of the array 726 * @throws IllegalArgumentException if <code>initialCapacity</code> is not 727 * positive. 728 */ 729 protected void setInitialCapacity(int initialCapacity) { 730 if (initialCapacity > 0) { 731 synchronized(this) { 732 this.initialCapacity = initialCapacity; 733 } 734 } else { 735 throw MathRuntimeException.createIllegalArgumentException( 736 "initial capacity ({0}) is not positive", 737 initialCapacity); 738 } 739 } 740 741 /** 742 * This function allows you to control the number of elements contained 743 * in this array, and can be used to "throw out" the last n values in an 744 * array. This function will also expand the internal array as needed. 745 * 746 * @param i a new number of elements 747 * @throws IllegalArgumentException if <code>i</code> is negative. 748 */ 749 public synchronized void setNumElements(int i) { 750 751 // If index is negative thrown an error 752 if (i < 0) { 753 throw MathRuntimeException.createIllegalArgumentException( 754 "index ({0}) is not positive", 755 i); 756 } 757 758 // Test the new num elements, check to see if the array needs to be 759 // expanded to accommodate this new number of elements 760 if ((startIndex + i) > internalArray.length) { 761 expandTo(startIndex + i); 762 } 763 764 // Set the new number of elements to new value 765 numElements = i; 766 } 767 768 /** 769 * Returns true if the internal storage array has too many unused 770 * storage positions. 771 * 772 * @return true if array satisfies the contraction criteria 773 */ 774 private synchronized boolean shouldContract() { 775 if (expansionMode == MULTIPLICATIVE_MODE) { 776 return (internalArray.length / ((float) numElements)) > contractionCriteria; 777 } else { 778 return (internalArray.length - numElements) > contractionCriteria; 779 } 780 } 781 782 /** 783 * Returns the starting index of the internal array. The starting index is 784 * the position of the first addressable element in the internal storage 785 * array. The addressable elements in the array are <code> 786 * internalArray[startIndex],...,internalArray[startIndex + numElements -1] 787 * </code> 788 * 789 * @return starting index 790 */ 791 public synchronized int start() { 792 return startIndex; 793 } 794 795 /** 796 * <p>Copies source to dest, copying the underlying data, so dest is 797 * a new, independent copy of source. Does not contract before 798 * the copy.</p> 799 * 800 * <p>Obtains synchronization locks on both source and dest 801 * (in that order) before performing the copy.</p> 802 * 803 * <p>Neither source nor dest may be null; otherwise a NullPointerException 804 * is thrown</p> 805 * 806 * @param source ResizableDoubleArray to copy 807 * @param dest ResizableArray to replace with a copy of the source array 808 * @since 2.0 809 * 810 */ 811 public static void copy(ResizableDoubleArray source, ResizableDoubleArray dest) { 812 synchronized(source) { 813 synchronized(dest) { 814 dest.initialCapacity = source.initialCapacity; 815 dest.contractionCriteria = source.contractionCriteria; 816 dest.expansionFactor = source.expansionFactor; 817 dest.expansionMode = source.expansionMode; 818 dest.internalArray = new double[source.internalArray.length]; 819 System.arraycopy(source.internalArray, 0, dest.internalArray, 820 0, dest.internalArray.length); 821 dest.numElements = source.numElements; 822 dest.startIndex = source.startIndex; 823 } 824 } 825 } 826 827 /** 828 * Returns a copy of the ResizableDoubleArray. Does not contract before 829 * the copy, so the returned object is an exact copy of this. 830 * 831 * @return a new ResizableDoubleArray with the same data and configuration 832 * properties as this 833 * @since 2.0 834 */ 835 public synchronized ResizableDoubleArray copy() { 836 ResizableDoubleArray result = new ResizableDoubleArray(); 837 copy(this, result); 838 return result; 839 } 840 841 /** 842 * Returns true iff object is a ResizableDoubleArray with the same properties 843 * as this and an identical internal storage array. 844 * 845 * @param object object to be compared for equality with this 846 * @return true iff object is a ResizableDoubleArray with the same data and 847 * properties as this 848 * @since 2.0 849 */ 850 @Override 851 public boolean equals(Object object) { 852 if (object == this ) { 853 return true; 854 } 855 if (object instanceof ResizableDoubleArray == false) { 856 return false; 857 } 858 synchronized(this) { 859 synchronized(object) { 860 boolean result = true; 861 ResizableDoubleArray other = (ResizableDoubleArray) object; 862 result = result && (other.initialCapacity == initialCapacity); 863 result = result && (other.contractionCriteria == contractionCriteria); 864 result = result && (other.expansionFactor == expansionFactor); 865 result = result && (other.expansionMode == expansionMode); 866 result = result && (other.numElements == numElements); 867 result = result && (other.startIndex == startIndex); 868 if (!result) { 869 return false; 870 } else { 871 return Arrays.equals(internalArray, other.internalArray); 872 } 873 } 874 } 875 } 876 877 /** 878 * Returns a hash code consistent with equals. 879 * 880 * @return hash code representing this ResizableDoubleArray 881 * @since 2.0 882 */ 883 @Override 884 public synchronized int hashCode() { 885 int[] hashData = new int[7]; 886 hashData[0] = new Float(expansionFactor).hashCode(); 887 hashData[1] = new Float(contractionCriteria).hashCode(); 888 hashData[2] = expansionMode; 889 hashData[3] = Arrays.hashCode(internalArray); 890 hashData[4] = initialCapacity; 891 hashData[5] = numElements; 892 hashData[6] = startIndex; 893 return Arrays.hashCode(hashData); 894 } 895 896 }