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