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 */ 017package org.apache.commons.math3.util; 018 019import java.io.Serializable; 020import java.util.Arrays; 021 022import org.apache.commons.math3.exception.MathIllegalArgumentException; 023import org.apache.commons.math3.exception.MathIllegalStateException; 024import org.apache.commons.math3.exception.MathInternalError; 025import org.apache.commons.math3.exception.NullArgumentException; 026import org.apache.commons.math3.exception.NotStrictlyPositiveException; 027import org.apache.commons.math3.exception.NumberIsTooSmallException; 028import org.apache.commons.math3.exception.util.LocalizedFormats; 029 030/** 031 * <p> 032 * A variable length {@link DoubleArray} implementation that automatically 033 * handles expanding and contracting its internal storage array as elements 034 * are added and removed. 035 * </p> 036 * <h3>Important note: Usage should not assume that this class is thread-safe 037 * even though some of the methods are {@code synchronized}. 038 * This qualifier will be dropped in the next major release (4.0).</h3> 039 * <p> 040 * The internal storage array starts with capacity determined by the 041 * {@code initialCapacity} property, which can be set by the constructor. 042 * The default initial capacity is 16. Adding elements using 043 * {@link #addElement(double)} appends elements to the end of the array. 044 * When there are no open entries at the end of the internal storage array, 045 * the array is expanded. The size of the expanded array depends on the 046 * {@code expansionMode} and {@code expansionFactor} properties. 047 * The {@code expansionMode} determines whether the size of the array is 048 * multiplied by the {@code expansionFactor} 049 * ({@link ExpansionMode#MULTIPLICATIVE}) or if the expansion is additive 050 * ({@link ExpansionMode#ADDITIVE} -- {@code expansionFactor} storage 051 * locations added). 052 * The default {@code expansionMode} is {@code MULTIPLICATIVE} and the default 053 * {@code expansionFactor} is 2. 054 * </p> 055 * <p> 056 * The {@link #addElementRolling(double)} method adds a new element to the end 057 * of the internal storage array and adjusts the "usable window" of the 058 * internal array forward by one position (effectively making what was the 059 * second element the first, and so on). Repeated activations of this method 060 * (or activation of {@link #discardFrontElements(int)}) will effectively orphan 061 * the storage locations at the beginning of the internal storage array. To 062 * reclaim this storage, each time one of these methods is activated, the size 063 * of the internal storage array is compared to the number of addressable 064 * elements (the {@code numElements} property) and if the difference 065 * is too large, the internal array is contracted to size 066 * {@code numElements + 1}. The determination of when the internal 067 * storage array is "too large" depends on the {@code expansionMode} and 068 * {@code contractionFactor} properties. If the {@code expansionMode} 069 * is {@code MULTIPLICATIVE}, contraction is triggered when the 070 * ratio between storage array length and {@code numElements} exceeds 071 * {@code contractionFactor.} If the {@code expansionMode} 072 * is {@code ADDITIVE}, the number of excess storage locations 073 * is compared to {@code contractionFactor}. 074 * </p> 075 * <p> 076 * To avoid cycles of expansions and contractions, the 077 * {@code expansionFactor} must not exceed the {@code contractionFactor}. 078 * Constructors and mutators for both of these properties enforce this 079 * requirement, throwing a {@code MathIllegalArgumentException} if it is 080 * violated. 081 * </p> 082 */ 083public class ResizableDoubleArray implements DoubleArray, Serializable { 084 /** Additive expansion mode. 085 * @deprecated As of 3.1. Please use {@link ExpansionMode#ADDITIVE} instead. 086 */ 087 @Deprecated 088 public static final int ADDITIVE_MODE = 1; 089 /** Multiplicative expansion mode. 090 * @deprecated As of 3.1. Please use {@link ExpansionMode#MULTIPLICATIVE} instead. 091 */ 092 @Deprecated 093 public static final int MULTIPLICATIVE_MODE = 0; 094 /** Serializable version identifier. */ 095 private static final long serialVersionUID = -3485529955529426875L; 096 097 /** Default value for initial capacity. */ 098 private static final int DEFAULT_INITIAL_CAPACITY = 16; 099 /** Default value for array size modifier. */ 100 private static final double DEFAULT_EXPANSION_FACTOR = 2.0; 101 /** 102 * Default value for the difference between {@link #contractionCriterion} 103 * and {@link #expansionFactor}. 104 */ 105 private static final double DEFAULT_CONTRACTION_DELTA = 0.5; 106 107 /** 108 * The contraction criteria determines when the internal array will be 109 * contracted to fit the number of elements contained in the element 110 * array + 1. 111 */ 112 private double contractionCriterion = 2.5; 113 114 /** 115 * The expansion factor of the array. When the array needs to be expanded, 116 * the new array size will be 117 * {@code internalArray.length * expansionFactor} 118 * if {@code expansionMode} is set to MULTIPLICATIVE_MODE, or 119 * {@code internalArray.length + expansionFactor} if 120 * {@code expansionMode} is set to ADDITIVE_MODE. 121 */ 122 private double expansionFactor = 2.0; 123 124 /** 125 * Determines whether array expansion by {@code expansionFactor} 126 * is additive or multiplicative. 127 */ 128 private ExpansionMode expansionMode = ExpansionMode.MULTIPLICATIVE; 129 130 /** 131 * The internal storage array. 132 */ 133 private double[] internalArray; 134 135 /** 136 * The number of addressable elements in the array. Note that this 137 * has nothing to do with the length of the internal storage array. 138 */ 139 private int numElements = 0; 140 141 /** 142 * The position of the first addressable element in the internal storage 143 * array. The addressable elements in the array are 144 * {@code internalArray[startIndex],...,internalArray[startIndex + numElements - 1]}. 145 */ 146 private int startIndex = 0; 147 148 /** 149 * Specification of expansion algorithm. 150 * @since 3.1 151 */ 152 public enum ExpansionMode { 153 /** Multiplicative expansion mode. */ 154 MULTIPLICATIVE, 155 /** Additive expansion mode. */ 156 ADDITIVE 157 } 158 159 /** 160 * Creates an instance with default properties. 161 * <ul> 162 * <li>{@code initialCapacity = 16}</li> 163 * <li>{@code expansionMode = MULTIPLICATIVE}</li> 164 * <li>{@code expansionFactor = 2.0}</li> 165 * <li>{@code contractionCriterion = 2.5}</li> 166 * </ul> 167 */ 168 public ResizableDoubleArray() { 169 this(DEFAULT_INITIAL_CAPACITY); 170 } 171 172 /** 173 * Creates an instance with the specified initial capacity. 174 * Other properties take default values: 175 * <ul> 176 * <li>{@code expansionMode = MULTIPLICATIVE}</li> 177 * <li>{@code expansionFactor = 2.0}</li> 178 * <li>{@code contractionCriterion = 2.5}</li> 179 * </ul> 180 * @param initialCapacity Initial size of the internal storage array. 181 * @throws MathIllegalArgumentException if {@code initialCapacity <= 0}. 182 */ 183 public ResizableDoubleArray(int initialCapacity) 184 throws MathIllegalArgumentException { 185 this(initialCapacity, DEFAULT_EXPANSION_FACTOR); 186 } 187 188 /** 189 * Creates an instance from an existing {@code double[]} with the 190 * initial capacity and numElements corresponding to the size of 191 * the supplied {@code double[]} array. 192 * If the supplied array is null, a new empty array with the default 193 * initial capacity will be created. 194 * The input array is copied, not referenced. 195 * Other properties take default values: 196 * <ul> 197 * <li>{@code initialCapacity = 16}</li> 198 * <li>{@code expansionMode = MULTIPLICATIVE}</li> 199 * <li>{@code expansionFactor = 2.0}</li> 200 * <li>{@code contractionCriterion = 2.5}</li> 201 * </ul> 202 * 203 * @param initialArray initial array 204 * @since 2.2 205 */ 206 public ResizableDoubleArray(double[] initialArray) { 207 this(DEFAULT_INITIAL_CAPACITY, 208 DEFAULT_EXPANSION_FACTOR, 209 DEFAULT_CONTRACTION_DELTA + DEFAULT_EXPANSION_FACTOR, 210 ExpansionMode.MULTIPLICATIVE, 211 initialArray); 212 } 213 214 /** 215 * Creates an instance with the specified initial capacity 216 * and expansion factor. 217 * The remaining properties take default values: 218 * <ul> 219 * <li>{@code expansionMode = MULTIPLICATIVE}</li> 220 * <li>{@code contractionCriterion = 0.5 + expansionFactor}</li> 221 * </ul> 222 * <br/> 223 * Throws IllegalArgumentException if the following conditions are 224 * not met: 225 * <ul> 226 * <li>{@code initialCapacity > 0}</li> 227 * <li>{@code expansionFactor > 1}</li> 228 * </ul> 229 * 230 * @param initialCapacity Initial size of the internal storage array. 231 * @param expansionFactor The array will be expanded based on this 232 * parameter. 233 * @throws MathIllegalArgumentException if parameters are not valid. 234 * @deprecated As of 3.1. Please use 235 * {@link #ResizableDoubleArray(int,double)} instead. 236 */ 237 @Deprecated 238 public ResizableDoubleArray(int initialCapacity, 239 float expansionFactor) 240 throws MathIllegalArgumentException { 241 this(initialCapacity, 242 (double) expansionFactor); 243 } 244 245 /** 246 * Creates an instance with the specified initial capacity 247 * and expansion factor. 248 * The remaining properties take default values: 249 * <ul> 250 * <li>{@code expansionMode = MULTIPLICATIVE}</li> 251 * <li>{@code contractionCriterion = 0.5 + expansionFactor}</li> 252 * </ul> 253 * <br/> 254 * Throws IllegalArgumentException if the following conditions are 255 * not met: 256 * <ul> 257 * <li>{@code initialCapacity > 0}</li> 258 * <li>{@code expansionFactor > 1}</li> 259 * </ul> 260 * 261 * @param initialCapacity Initial size of the internal storage array. 262 * @param expansionFactor The array will be expanded based on this 263 * parameter. 264 * @throws MathIllegalArgumentException if parameters are not valid. 265 * @since 3.1 266 */ 267 public ResizableDoubleArray(int initialCapacity, 268 double expansionFactor) 269 throws MathIllegalArgumentException { 270 this(initialCapacity, 271 expansionFactor, 272 DEFAULT_CONTRACTION_DELTA + expansionFactor); 273 } 274 275 /** 276 * Creates an instance with the specified initialCapacity, 277 * expansionFactor, and contractionCriterion. 278 * The expansion mode will default to {@code MULTIPLICATIVE}. 279 * <br/> 280 * Throws IllegalArgumentException if the following conditions are 281 * not met: 282 * <ul> 283 * <li>{@code initialCapacity > 0}</li> 284 * <li>{@code expansionFactor > 1}</li> 285 * <li>{@code contractionCriterion >= expansionFactor}</li> 286 * </ul> 287 * 288 * @param initialCapacity Initial size of the internal storage array.. 289 * @param expansionFactor The array will be expanded based on this 290 * parameter. 291 * @param contractionCriteria Contraction criteria. 292 * @throws MathIllegalArgumentException if parameters are not valid. 293 * @deprecated As of 3.1. Please use 294 * {@link #ResizableDoubleArray(int,double,double)} instead. 295 */ 296 @Deprecated 297 public ResizableDoubleArray(int initialCapacity, 298 float expansionFactor, 299 float contractionCriteria) 300 throws MathIllegalArgumentException { 301 this(initialCapacity, 302 (double) expansionFactor, 303 (double) contractionCriteria); 304 } 305 306 /** 307 * Creates an instance with the specified initial capacity, 308 * expansion factor, and contraction criteria. 309 * The expansion mode will default to {@code MULTIPLICATIVE}. 310 * <br/> 311 * Throws IllegalArgumentException if the following conditions are 312 * not met: 313 * <ul> 314 * <li>{@code initialCapacity > 0}</li> 315 * <li>{@code expansionFactor > 1}</li> 316 * <li>{@code contractionCriterion >= expansionFactor}</li> 317 * </ul> 318 * 319 * @param initialCapacity Initial size of the internal storage array.. 320 * @param expansionFactor The array will be expanded based on this 321 * parameter. 322 * @param contractionCriterion Contraction criterion. 323 * @throws MathIllegalArgumentException if the parameters are not valid. 324 * @since 3.1 325 */ 326 public ResizableDoubleArray(int initialCapacity, 327 double expansionFactor, 328 double contractionCriterion) 329 throws MathIllegalArgumentException { 330 this(initialCapacity, 331 expansionFactor, 332 contractionCriterion, 333 ExpansionMode.MULTIPLICATIVE, 334 null); 335 } 336 337 /** 338 * <p> 339 * Create a ResizableArray with the specified properties.</p> 340 * <p> 341 * Throws IllegalArgumentException if the following conditions are 342 * not met: 343 * <ul> 344 * <li><code>initialCapacity > 0</code></li> 345 * <li><code>expansionFactor > 1</code></li> 346 * <li><code>contractionFactor >= expansionFactor</code></li> 347 * <li><code>expansionMode in {MULTIPLICATIVE_MODE, ADDITIVE_MODE}</code> 348 * </li> 349 * </ul></p> 350 * 351 * @param initialCapacity the initial size of the internal storage array 352 * @param expansionFactor the array will be expanded based on this 353 * parameter 354 * @param contractionCriteria the contraction Criteria 355 * @param expansionMode the expansion mode 356 * @throws MathIllegalArgumentException if parameters are not valid 357 * @deprecated As of 3.1. Please use 358 * {@link #ResizableDoubleArray(int,double,double,ExpansionMode,double[])} 359 * instead. 360 */ 361 @Deprecated 362 public ResizableDoubleArray(int initialCapacity, float expansionFactor, 363 float contractionCriteria, int expansionMode) throws MathIllegalArgumentException { 364 this(initialCapacity, 365 expansionFactor, 366 contractionCriteria, 367 expansionMode == ADDITIVE_MODE ? 368 ExpansionMode.ADDITIVE : 369 ExpansionMode.MULTIPLICATIVE, 370 null); 371 // XXX Just ot retain the expected failure in a unit test. 372 // With the new "enum", that test will become obsolete. 373 setExpansionMode(expansionMode); 374 } 375 376 /** 377 * Creates an instance with the specified properties. 378 * <br/> 379 * Throws MathIllegalArgumentException if the following conditions are 380 * not met: 381 * <ul> 382 * <li>{@code initialCapacity > 0}</li> 383 * <li>{@code expansionFactor > 1}</li> 384 * <li>{@code contractionCriterion >= expansionFactor}</li> 385 * </ul> 386 * 387 * @param initialCapacity Initial size of the internal storage array. 388 * @param expansionFactor The array will be expanded based on this 389 * parameter. 390 * @param contractionCriterion Contraction criteria. 391 * @param expansionMode Expansion mode. 392 * @param data Initial contents of the array. 393 * @throws MathIllegalArgumentException if the parameters are not valid. 394 */ 395 public ResizableDoubleArray(int initialCapacity, 396 double expansionFactor, 397 double contractionCriterion, 398 ExpansionMode expansionMode, 399 double ... data) 400 throws MathIllegalArgumentException { 401 if (initialCapacity <= 0) { 402 throw new NotStrictlyPositiveException(LocalizedFormats.INITIAL_CAPACITY_NOT_POSITIVE, 403 initialCapacity); 404 } 405 checkContractExpand(contractionCriterion, expansionFactor); 406 407 this.expansionFactor = expansionFactor; 408 this.contractionCriterion = contractionCriterion; 409 this.expansionMode = expansionMode; 410 internalArray = new double[initialCapacity]; 411 numElements = 0; 412 startIndex = 0; 413 414 if (data != null && data.length > 0) { 415 addElements(data); 416 } 417 } 418 419 /** 420 * Copy constructor. Creates a new ResizableDoubleArray that is a deep, 421 * fresh copy of the original. Needs to acquire synchronization lock 422 * on original. Original may not be null; otherwise a {@link NullArgumentException} 423 * is thrown. 424 * 425 * @param original array to copy 426 * @exception NullArgumentException if original is null 427 * @since 2.0 428 */ 429 public ResizableDoubleArray(ResizableDoubleArray original) 430 throws NullArgumentException { 431 MathUtils.checkNotNull(original); 432 copy(original, this); 433 } 434 435 /** 436 * Adds an element to the end of this expandable array. 437 * 438 * @param value Value to be added to end of array. 439 */ 440 public synchronized void addElement(double value) { 441 if (internalArray.length <= startIndex + numElements) { 442 expand(); 443 } 444 internalArray[startIndex + numElements++] = value; 445 } 446 447 /** 448 * Adds several element to the end of this expandable array. 449 * 450 * @param values Values to be added to end of array. 451 * @since 2.2 452 */ 453 public synchronized void addElements(double[] values) { 454 final double[] tempArray = new double[numElements + values.length + 1]; 455 System.arraycopy(internalArray, startIndex, tempArray, 0, numElements); 456 System.arraycopy(values, 0, tempArray, numElements, values.length); 457 internalArray = tempArray; 458 startIndex = 0; 459 numElements += values.length; 460 } 461 462 /** 463 * <p> 464 * Adds an element to the end of the array and removes the first 465 * element in the array. Returns the discarded first element. 466 * The effect is similar to a push operation in a FIFO queue. 467 * </p> 468 * <p> 469 * Example: If the array contains the elements 1, 2, 3, 4 (in that order) 470 * and addElementRolling(5) is invoked, the result is an array containing 471 * the entries 2, 3, 4, 5 and the value returned is 1. 472 * </p> 473 * 474 * @param value Value to be added to the array. 475 * @return the value which has been discarded or "pushed" out of the array 476 * by this rolling insert. 477 */ 478 public synchronized double addElementRolling(double value) { 479 double discarded = internalArray[startIndex]; 480 481 if ((startIndex + (numElements + 1)) > internalArray.length) { 482 expand(); 483 } 484 // Increment the start index 485 startIndex += 1; 486 487 // Add the new value 488 internalArray[startIndex + (numElements - 1)] = value; 489 490 // Check the contraction criterion. 491 if (shouldContract()) { 492 contract(); 493 } 494 return discarded; 495 } 496 497 /** 498 * Substitutes <code>value</code> for the most recently added value. 499 * Returns the value that has been replaced. If the array is empty (i.e. 500 * if {@link #numElements} is zero), an IllegalStateException is thrown. 501 * 502 * @param value New value to substitute for the most recently added value 503 * @return the value that has been replaced in the array. 504 * @throws MathIllegalStateException if the array is empty 505 * @since 2.0 506 */ 507 public synchronized double substituteMostRecentElement(double value) 508 throws MathIllegalStateException { 509 if (numElements < 1) { 510 throw new MathIllegalStateException( 511 LocalizedFormats.CANNOT_SUBSTITUTE_ELEMENT_FROM_EMPTY_ARRAY); 512 } 513 514 final int substIndex = startIndex + (numElements - 1); 515 final double discarded = internalArray[substIndex]; 516 517 internalArray[substIndex] = value; 518 519 return discarded; 520 } 521 522 /** 523 * Checks the expansion factor and the contraction criterion and throws an 524 * IllegalArgumentException if the contractionCriteria is less than the 525 * expansionCriteria 526 * 527 * @param expansion factor to be checked 528 * @param contraction criteria to be checked 529 * @throws MathIllegalArgumentException if the contractionCriteria is less than 530 * the expansionCriteria. 531 * @deprecated As of 3.1. Please use 532 * {@link #checkContractExpand(double,double)} instead. 533 */ 534 @Deprecated 535 protected void checkContractExpand(float contraction, float expansion) 536 throws MathIllegalArgumentException { 537 checkContractExpand((double) contraction, 538 (double) expansion); 539 } 540 541 /** 542 * Checks the expansion factor and the contraction criterion and raises 543 * an exception if the contraction criterion is smaller than the 544 * expansion criterion. 545 * 546 * @param contraction Criterion to be checked. 547 * @param expansion Factor to be checked. 548 * @throws NumberIsTooSmallException if {@code contraction < expansion}. 549 * @throws NumberIsTooSmallException if {@code contraction <= 1}. 550 * @throws NumberIsTooSmallException if {@code expansion <= 1 }. 551 * @since 3.1 552 */ 553 protected void checkContractExpand(double contraction, 554 double expansion) 555 throws NumberIsTooSmallException { 556 if (contraction < expansion) { 557 final NumberIsTooSmallException e = new NumberIsTooSmallException(contraction, 1, true); 558 e.getContext().addMessage(LocalizedFormats.CONTRACTION_CRITERIA_SMALLER_THAN_EXPANSION_FACTOR, 559 contraction, expansion); 560 throw e; 561 } 562 563 if (contraction <= 1) { 564 final NumberIsTooSmallException e = new NumberIsTooSmallException(contraction, 1, false); 565 e.getContext().addMessage(LocalizedFormats.CONTRACTION_CRITERIA_SMALLER_THAN_ONE, 566 contraction); 567 throw e; 568 } 569 570 if (expansion <= 1) { 571 final NumberIsTooSmallException e = new NumberIsTooSmallException(contraction, 1, false); 572 e.getContext().addMessage(LocalizedFormats.EXPANSION_FACTOR_SMALLER_THAN_ONE, 573 expansion); 574 throw e; 575 } 576 } 577 578 /** 579 * Clear the array contents, resetting the number of elements to zero. 580 */ 581 public synchronized void clear() { 582 numElements = 0; 583 startIndex = 0; 584 } 585 586 /** 587 * Contracts the storage array to the (size of the element set) + 1 - to 588 * avoid a zero length array. This function also resets the startIndex to 589 * zero. 590 */ 591 public synchronized void contract() { 592 final double[] tempArray = new double[numElements + 1]; 593 594 // Copy and swap - copy only the element array from the src array. 595 System.arraycopy(internalArray, startIndex, tempArray, 0, numElements); 596 internalArray = tempArray; 597 598 // Reset the start index to zero 599 startIndex = 0; 600 } 601 602 /** 603 * Discards the <code>i</code> initial elements of the array. For example, 604 * if the array contains the elements 1,2,3,4, invoking 605 * <code>discardFrontElements(2)</code> will cause the first two elements 606 * to be discarded, leaving 3,4 in the array. Throws illegalArgumentException 607 * if i exceeds numElements. 608 * 609 * @param i the number of elements to discard from the front of the array 610 * @throws MathIllegalArgumentException if i is greater than numElements. 611 * @since 2.0 612 */ 613 public synchronized void discardFrontElements(int i) 614 throws MathIllegalArgumentException { 615 discardExtremeElements(i,true); 616 } 617 618 /** 619 * Discards the <code>i</code> last elements of the array. For example, 620 * if the array contains the elements 1,2,3,4, invoking 621 * <code>discardMostRecentElements(2)</code> will cause the last two elements 622 * to be discarded, leaving 1,2 in the array. Throws illegalArgumentException 623 * if i exceeds numElements. 624 * 625 * @param i the number of elements to discard from the end of the array 626 * @throws MathIllegalArgumentException if i is greater than numElements. 627 * @since 2.0 628 */ 629 public synchronized void discardMostRecentElements(int i) 630 throws MathIllegalArgumentException { 631 discardExtremeElements(i,false); 632 } 633 634 /** 635 * Discards the <code>i</code> first or last elements of the array, 636 * depending on the value of <code>front</code>. 637 * For example, if the array contains the elements 1,2,3,4, invoking 638 * <code>discardExtremeElements(2,false)</code> will cause the last two elements 639 * to be discarded, leaving 1,2 in the array. 640 * For example, if the array contains the elements 1,2,3,4, invoking 641 * <code>discardExtremeElements(2,true)</code> will cause the first two elements 642 * to be discarded, leaving 3,4 in the array. 643 * Throws illegalArgumentException 644 * if i exceeds numElements. 645 * 646 * @param i the number of elements to discard from the front/end of the array 647 * @param front true if elements are to be discarded from the front 648 * of the array, false if elements are to be discarded from the end 649 * of the array 650 * @throws MathIllegalArgumentException if i is greater than numElements. 651 * @since 2.0 652 */ 653 private synchronized void discardExtremeElements(int i, 654 boolean front) 655 throws MathIllegalArgumentException { 656 if (i > numElements) { 657 throw new MathIllegalArgumentException( 658 LocalizedFormats.TOO_MANY_ELEMENTS_TO_DISCARD_FROM_ARRAY, 659 i, numElements); 660 } else if (i < 0) { 661 throw new MathIllegalArgumentException( 662 LocalizedFormats.CANNOT_DISCARD_NEGATIVE_NUMBER_OF_ELEMENTS, 663 i); 664 } else { 665 // "Subtract" this number of discarded from numElements 666 numElements -= i; 667 if (front) { 668 startIndex += i; 669 } 670 } 671 if (shouldContract()) { 672 contract(); 673 } 674 } 675 676 /** 677 * Expands the internal storage array using the expansion factor. 678 * <p> 679 * if <code>expansionMode</code> is set to MULTIPLICATIVE_MODE, 680 * the new array size will be <code>internalArray.length * expansionFactor.</code> 681 * If <code>expansionMode</code> is set to ADDITIVE_MODE, the length 682 * after expansion will be <code>internalArray.length + expansionFactor</code> 683 * </p> 684 */ 685 protected synchronized void expand() { 686 // notice the use of FastMath.ceil(), this guarantees that we will always 687 // have an array of at least currentSize + 1. Assume that the 688 // current initial capacity is 1 and the expansion factor 689 // is 1.000000000000000001. The newly calculated size will be 690 // rounded up to 2 after the multiplication is performed. 691 int newSize = 0; 692 if (expansionMode == ExpansionMode.MULTIPLICATIVE) { 693 newSize = (int) FastMath.ceil(internalArray.length * expansionFactor); 694 } else { 695 newSize = (int) (internalArray.length + FastMath.round(expansionFactor)); 696 } 697 final double[] tempArray = new double[newSize]; 698 699 // Copy and swap 700 System.arraycopy(internalArray, 0, tempArray, 0, internalArray.length); 701 internalArray = tempArray; 702 } 703 704 /** 705 * Expands the internal storage array to the specified size. 706 * 707 * @param size Size of the new internal storage array. 708 */ 709 private synchronized void expandTo(int size) { 710 final double[] tempArray = new double[size]; 711 // Copy and swap 712 System.arraycopy(internalArray, 0, tempArray, 0, internalArray.length); 713 internalArray = tempArray; 714 } 715 716 /** 717 * The contraction criteria defines when the internal array will contract 718 * to store only the number of elements in the element array. 719 * If the <code>expansionMode</code> is <code>MULTIPLICATIVE_MODE</code>, 720 * contraction is triggered when the ratio between storage array length 721 * and <code>numElements</code> exceeds <code>contractionFactor</code>. 722 * If the <code>expansionMode</code> is <code>ADDITIVE_MODE</code>, the 723 * number of excess storage locations is compared to 724 * <code>contractionFactor.</code> 725 * 726 * @return the contraction criteria used to reclaim memory. 727 * @deprecated As of 3.1. Please use {@link #getContractionCriterion()} 728 * instead. 729 */ 730 @Deprecated 731 public float getContractionCriteria() { 732 return (float) getContractionCriterion(); 733 } 734 735 /** 736 * The contraction criterion defines when the internal array will contract 737 * to store only the number of elements in the element array. 738 * If the <code>expansionMode</code> is <code>MULTIPLICATIVE_MODE</code>, 739 * contraction is triggered when the ratio between storage array length 740 * and <code>numElements</code> exceeds <code>contractionFactor</code>. 741 * If the <code>expansionMode</code> is <code>ADDITIVE_MODE</code>, the 742 * number of excess storage locations is compared to 743 * <code>contractionFactor.</code> 744 * 745 * @return the contraction criterion used to reclaim memory. 746 * @since 3.1 747 */ 748 public double getContractionCriterion() { 749 return contractionCriterion; 750 } 751 752 /** 753 * Returns the element at the specified index 754 * 755 * @param index index to fetch a value from 756 * @return value stored at the specified index 757 * @throws ArrayIndexOutOfBoundsException if <code>index</code> is less than 758 * zero or is greater than <code>getNumElements() - 1</code>. 759 */ 760 public synchronized double getElement(int index) { 761 if (index >= numElements) { 762 throw new ArrayIndexOutOfBoundsException(index); 763 } else if (index >= 0) { 764 return internalArray[startIndex + index]; 765 } else { 766 throw new ArrayIndexOutOfBoundsException(index); 767 } 768 } 769 770 /** 771 * Returns a double array containing the elements of this 772 * <code>ResizableArray</code>. This method returns a copy, not a 773 * reference to the underlying array, so that changes made to the returned 774 * array have no effect on this <code>ResizableArray.</code> 775 * @return the double array. 776 */ 777 public synchronized double[] getElements() { 778 final double[] elementArray = new double[numElements]; 779 System.arraycopy(internalArray, startIndex, elementArray, 0, numElements); 780 return elementArray; 781 } 782 783 /** 784 * The expansion factor controls the size of a new array when an array 785 * needs to be expanded. The <code>expansionMode</code> 786 * determines whether the size of the array is multiplied by the 787 * <code>expansionFactor</code> (MULTIPLICATIVE_MODE) or if 788 * the expansion is additive (ADDITIVE_MODE -- <code>expansionFactor</code> 789 * storage locations added). The default <code>expansionMode</code> is 790 * MULTIPLICATIVE_MODE and the default <code>expansionFactor</code> 791 * is 2.0. 792 * 793 * @return the expansion factor of this expandable double array 794 * @deprecated As of 3.1. Return type will be changed to "double" in 4.0. 795 */ 796 @Deprecated 797 public float getExpansionFactor() { 798 return (float) expansionFactor; 799 } 800 801 /** 802 * The expansion mode determines whether the internal storage 803 * array grows additively or multiplicatively when it is expanded. 804 * 805 * @return the expansion mode. 806 * @deprecated As of 3.1. Return value to be changed to 807 * {@link ExpansionMode} in 4.0. 808 */ 809 @Deprecated 810 public int getExpansionMode() { 811 synchronized (this) { 812 switch (expansionMode) { 813 case MULTIPLICATIVE: 814 return MULTIPLICATIVE_MODE; 815 case ADDITIVE: 816 return ADDITIVE_MODE; 817 default: 818 throw new MathInternalError(); // Should never happen. 819 } 820 } 821 } 822 823 /** 824 * Notice the package scope on this method. This method is simply here 825 * for the JUnit test, it allows us check if the expansion is working 826 * properly after a number of expansions. This is not meant to be a part 827 * of the public interface of this class. 828 * 829 * @return the length of the internal storage array. 830 * @deprecated As of 3.1. Please use {@link #getCapacity()} instead. 831 */ 832 @Deprecated 833 synchronized int getInternalLength() { 834 return internalArray.length; 835 } 836 837 /** 838 * Gets the currently allocated size of the internal data structure used 839 * for storing elements. 840 * This is not to be confused with {@link #getNumElements() the number of 841 * elements actually stored}. 842 * 843 * @return the length of the internal array. 844 * @since 3.1 845 */ 846 public int getCapacity() { 847 return internalArray.length; 848 } 849 850 /** 851 * Returns the number of elements currently in the array. Please note 852 * that this is different from the length of the internal storage array. 853 * 854 * @return the number of elements. 855 */ 856 public synchronized int getNumElements() { 857 return numElements; 858 } 859 860 /** 861 * Returns the internal storage array. Note that this method returns 862 * a reference to the internal storage array, not a copy, and to correctly 863 * address elements of the array, the <code>startIndex</code> is 864 * required (available via the {@link #start} method). This method should 865 * only be used in cases where copying the internal array is not practical. 866 * The {@link #getElements} method should be used in all other cases. 867 * 868 * 869 * @return the internal storage array used by this object 870 * @since 2.0 871 * @deprecated As of 3.1. 872 */ 873 @Deprecated 874 public synchronized double[] getInternalValues() { 875 return internalArray; 876 } 877 878 /** 879 * Provides <em>direct</em> access to the internal storage array. 880 * Please note that this method returns a reference to this object's 881 * storage array, not a copy. 882 * <br/> 883 * To correctly address elements of the array, the "start index" is 884 * required (available via the {@link #getStartIndex() getStartIndex} 885 * method. 886 * <br/> 887 * This method should only be used to avoid copying the internal array. 888 * The returned value <em>must</em> be used for reading only; other 889 * uses could lead to this object becoming inconsistent. 890 * <br/> 891 * The {@link #getElements} method has no such limitation since it 892 * returns a copy of this array's addressable elements. 893 * 894 * @return the internal storage array used by this object. 895 * @since 3.1 896 */ 897 protected double[] getArrayRef() { 898 return internalArray; 899 } 900 901 /** 902 * Returns the "start index" of the internal array. 903 * This index is the position of the first addressable element in the 904 * internal storage array. 905 * The addressable elements in the array are at indices contained in 906 * the interval [{@link #getStartIndex()}, 907 * {@link #getStartIndex()} + {@link #getNumElements()} - 1]. 908 * 909 * @return the start index. 910 * @since 3.1 911 */ 912 protected int getStartIndex() { 913 return startIndex; 914 } 915 916 /** 917 * Sets the contraction criteria. 918 * 919 * @param contractionCriteria contraction criteria 920 * @throws MathIllegalArgumentException if the contractionCriteria is less than 921 * the expansionCriteria. 922 * @deprecated As of 3.1 (to be removed in 4.0 as field will become "final"). 923 */ 924 @Deprecated 925 public void setContractionCriteria(float contractionCriteria) 926 throws MathIllegalArgumentException { 927 checkContractExpand(contractionCriteria, getExpansionFactor()); 928 synchronized(this) { 929 this.contractionCriterion = contractionCriteria; 930 } 931 } 932 933 /** 934 * Performs an operation on the addressable elements of the array. 935 * 936 * @param f Function to be applied on this array. 937 * @return the result. 938 * @since 3.1 939 */ 940 public double compute(MathArrays.Function f) { 941 final double[] array; 942 final int start; 943 final int num; 944 synchronized(this) { 945 array = internalArray; 946 start = startIndex; 947 num = numElements; 948 } 949 return f.evaluate(array, start, num); 950 } 951 952 /** 953 * Sets the element at the specified index. If the specified index is greater than 954 * <code>getNumElements() - 1</code>, the <code>numElements</code> property 955 * is increased to <code>index +1</code> and additional storage is allocated 956 * (if necessary) for the new element and all (uninitialized) elements 957 * between the new element and the previous end of the array). 958 * 959 * @param index index to store a value in 960 * @param value value to store at the specified index 961 * @throws ArrayIndexOutOfBoundsException if {@code index < 0}. 962 */ 963 public synchronized void setElement(int index, double value) { 964 if (index < 0) { 965 throw new ArrayIndexOutOfBoundsException(index); 966 } 967 if (index + 1 > numElements) { 968 numElements = index + 1; 969 } 970 if ((startIndex + index) >= internalArray.length) { 971 expandTo(startIndex + (index + 1)); 972 } 973 internalArray[startIndex + index] = value; 974 } 975 976 /** 977 * Sets the expansionFactor. Throws IllegalArgumentException if the 978 * the following conditions are not met: 979 * <ul> 980 * <li><code>expansionFactor > 1</code></li> 981 * <li><code>contractionFactor >= expansionFactor</code></li> 982 * </ul> 983 * @param expansionFactor the new expansion factor value. 984 * @throws MathIllegalArgumentException if expansionFactor is <= 1 or greater 985 * than contractionFactor 986 * @deprecated As of 3.1 (to be removed in 4.0 as field will become "final"). 987 */ 988 @Deprecated 989 public void setExpansionFactor(float expansionFactor) throws MathIllegalArgumentException { 990 checkContractExpand(getContractionCriterion(), expansionFactor); 991 // The check above verifies that the expansion factor is > 1.0; 992 synchronized(this) { 993 this.expansionFactor = expansionFactor; 994 } 995 } 996 997 /** 998 * Sets the <code>expansionMode</code>. The specified value must be one of 999 * ADDITIVE_MODE, MULTIPLICATIVE_MODE. 1000 * 1001 * @param expansionMode The expansionMode to set. 1002 * @throws MathIllegalArgumentException if the specified mode value is not valid. 1003 * @deprecated As of 3.1. Please use {@link #setExpansionMode(ExpansionMode)} instead. 1004 */ 1005 @Deprecated 1006 public void setExpansionMode(int expansionMode) 1007 throws MathIllegalArgumentException { 1008 if (expansionMode != MULTIPLICATIVE_MODE && 1009 expansionMode != ADDITIVE_MODE) { 1010 throw new MathIllegalArgumentException(LocalizedFormats.UNSUPPORTED_EXPANSION_MODE, expansionMode, 1011 MULTIPLICATIVE_MODE, "MULTIPLICATIVE_MODE", 1012 ADDITIVE_MODE, "ADDITIVE_MODE"); 1013 } 1014 synchronized(this) { 1015 if (expansionMode == MULTIPLICATIVE_MODE) { 1016 setExpansionMode(ExpansionMode.MULTIPLICATIVE); 1017 } else if (expansionMode == ADDITIVE_MODE) { 1018 setExpansionMode(ExpansionMode.ADDITIVE); 1019 } 1020 } 1021 } 1022 1023 /** 1024 * Sets the {@link ExpansionMode expansion mode}. 1025 * 1026 * @param expansionMode Expansion mode to use for resizing the array. 1027 * @deprecated As of 3.1 (to be removed in 4.0 as field will become "final"). 1028 */ 1029 @Deprecated 1030 public void setExpansionMode(ExpansionMode expansionMode) { 1031 synchronized(this) { 1032 this.expansionMode = expansionMode; 1033 } 1034 } 1035 1036 /** 1037 * Sets the initial capacity. Should only be invoked by constructors. 1038 * 1039 * @param initialCapacity of the array 1040 * @throws MathIllegalArgumentException if <code>initialCapacity</code> is not 1041 * positive. 1042 * @deprecated As of 3.1, this is a no-op. 1043 */ 1044 @Deprecated 1045 protected void setInitialCapacity(int initialCapacity) 1046 throws MathIllegalArgumentException { 1047 // Body removed in 3.1. 1048 } 1049 1050 /** 1051 * This function allows you to control the number of elements contained 1052 * in this array, and can be used to "throw out" the last n values in an 1053 * array. This function will also expand the internal array as needed. 1054 * 1055 * @param i a new number of elements 1056 * @throws MathIllegalArgumentException if <code>i</code> is negative. 1057 */ 1058 public synchronized void setNumElements(int i) 1059 throws MathIllegalArgumentException { 1060 // If index is negative thrown an error. 1061 if (i < 0) { 1062 throw new MathIllegalArgumentException( 1063 LocalizedFormats.INDEX_NOT_POSITIVE, 1064 i); 1065 } 1066 1067 // Test the new num elements, check to see if the array needs to be 1068 // expanded to accommodate this new number of elements. 1069 final int newSize = startIndex + i; 1070 if (newSize > internalArray.length) { 1071 expandTo(newSize); 1072 } 1073 1074 // Set the new number of elements to new value. 1075 numElements = i; 1076 } 1077 1078 /** 1079 * Returns true if the internal storage array has too many unused 1080 * storage positions. 1081 * 1082 * @return true if array satisfies the contraction criteria 1083 */ 1084 private synchronized boolean shouldContract() { 1085 if (expansionMode == ExpansionMode.MULTIPLICATIVE) { 1086 return (internalArray.length / ((float) numElements)) > contractionCriterion; 1087 } else { 1088 return (internalArray.length - numElements) > contractionCriterion; 1089 } 1090 } 1091 1092 /** 1093 * Returns the starting index of the internal array. The starting index is 1094 * the position of the first addressable element in the internal storage 1095 * array. The addressable elements in the array are <code> 1096 * internalArray[startIndex],...,internalArray[startIndex + numElements -1] 1097 * </code> 1098 * 1099 * @return the starting index. 1100 * @deprecated As of 3.1. 1101 */ 1102 @Deprecated 1103 public synchronized int start() { 1104 return startIndex; 1105 } 1106 1107 /** 1108 * <p>Copies source to dest, copying the underlying data, so dest is 1109 * a new, independent copy of source. Does not contract before 1110 * the copy.</p> 1111 * 1112 * <p>Obtains synchronization locks on both source and dest 1113 * (in that order) before performing the copy.</p> 1114 * 1115 * <p>Neither source nor dest may be null; otherwise a {@link NullArgumentException} 1116 * is thrown</p> 1117 * 1118 * @param source ResizableDoubleArray to copy 1119 * @param dest ResizableArray to replace with a copy of the source array 1120 * @exception NullArgumentException if either source or dest is null 1121 * @since 2.0 1122 * 1123 */ 1124 public static void copy(ResizableDoubleArray source, 1125 ResizableDoubleArray dest) 1126 throws NullArgumentException { 1127 MathUtils.checkNotNull(source); 1128 MathUtils.checkNotNull(dest); 1129 synchronized(source) { 1130 synchronized(dest) { 1131 dest.contractionCriterion = source.contractionCriterion; 1132 dest.expansionFactor = source.expansionFactor; 1133 dest.expansionMode = source.expansionMode; 1134 dest.internalArray = new double[source.internalArray.length]; 1135 System.arraycopy(source.internalArray, 0, dest.internalArray, 1136 0, dest.internalArray.length); 1137 dest.numElements = source.numElements; 1138 dest.startIndex = source.startIndex; 1139 } 1140 } 1141 } 1142 1143 /** 1144 * Returns a copy of the ResizableDoubleArray. Does not contract before 1145 * the copy, so the returned object is an exact copy of this. 1146 * 1147 * @return a new ResizableDoubleArray with the same data and configuration 1148 * properties as this 1149 * @since 2.0 1150 */ 1151 public synchronized ResizableDoubleArray copy() { 1152 final ResizableDoubleArray result = new ResizableDoubleArray(); 1153 copy(this, result); 1154 return result; 1155 } 1156 1157 /** 1158 * Returns true iff object is a ResizableDoubleArray with the same properties 1159 * as this and an identical internal storage array. 1160 * 1161 * @param object object to be compared for equality with this 1162 * @return true iff object is a ResizableDoubleArray with the same data and 1163 * properties as this 1164 * @since 2.0 1165 */ 1166 @Override 1167 public boolean equals(Object object) { 1168 if (object == this ) { 1169 return true; 1170 } 1171 if (object instanceof ResizableDoubleArray == false) { 1172 return false; 1173 } 1174 synchronized(this) { 1175 synchronized(object) { 1176 boolean result = true; 1177 final ResizableDoubleArray other = (ResizableDoubleArray) object; 1178 result = result && (other.contractionCriterion == contractionCriterion); 1179 result = result && (other.expansionFactor == expansionFactor); 1180 result = result && (other.expansionMode == expansionMode); 1181 result = result && (other.numElements == numElements); 1182 result = result && (other.startIndex == startIndex); 1183 if (!result) { 1184 return false; 1185 } else { 1186 return Arrays.equals(internalArray, other.internalArray); 1187 } 1188 } 1189 } 1190 } 1191 1192 /** 1193 * Returns a hash code consistent with equals. 1194 * 1195 * @return the hash code representing this {@code ResizableDoubleArray}. 1196 * @since 2.0 1197 */ 1198 @Override 1199 public synchronized int hashCode() { 1200 final int[] hashData = new int[6]; 1201 hashData[0] = Double.valueOf(expansionFactor).hashCode(); 1202 hashData[1] = Double.valueOf(contractionCriterion).hashCode(); 1203 hashData[2] = expansionMode.hashCode(); 1204 hashData[3] = Arrays.hashCode(internalArray); 1205 hashData[4] = numElements; 1206 hashData[5] = startIndex; 1207 return Arrays.hashCode(hashData); 1208 } 1209 1210}