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