001/* A library for mathematical operations on arrays of floats. 002 003 Copyright (c) 1998-2013 The Regents of the University of California. 004 All rights reserved. 005 006 Permission is hereby granted, without written agreement and without 007 license or royalty fees, to use, copy, modify, and distribute this 008 software and its documentation for any purpose, provided that the above 009 copyright notice and the following two paragraphs appear in all copies 010 of this software. 011 012 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 013 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 014 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 015 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 016 SUCH DAMAGE. 017 018 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 019 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 020 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 021 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 022 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 023 ENHANCEMENTS, OR MODIFICATIONS. 024 025 PT_COPYRIGHT_VERSION_2 026 COPYRIGHTENDKEY 027 028 */ 029package ptolemy.math; 030 031/////////////////////////////////////////////////////////////////// 032//// FloatArrayMath 033 034/** 035 This class provides a library for mathematical operations on float arrays. 036 Unless explicitly noted otherwise, all array arguments are assumed to be 037 non-null. If a null array is passed to a method, a NullPointerException 038 will be thrown in the method or called methods. 039 040 @author Albert Chen, William Wu, Edward A. Lee, Jeff Tsay 041 @version $Id$ 042 @since Ptolemy II 1.0 043 @Pt.ProposedRating Yellow (ctsay) 044 @Pt.AcceptedRating Yellow (ctsay) 045 */ 046public class FloatArrayMath { 047 // Protected constructor prevents construction of this class. 048 protected FloatArrayMath() { 049 } 050 051 /////////////////////////////////////////////////////////////////// 052 //// public methods //// 053 054 /** Return a new array that is the formed by adding z to each element 055 * of the input array. 056 */ 057 public static final float[] add(final float[] array, final float z) { 058 int length = array.length; 059 float[] returnValue = new float[length]; 060 061 for (int i = 0; i < length; i++) { 062 returnValue[i] = array[i] + z; 063 } 064 065 return returnValue; 066 } 067 068 /** Return a new array that is the element-by-element sum of the two 069 * input arrays. 070 * If the lengths of both arrays are 0, return a new array of length 0. 071 * If the two arrays do not have the same length, throw an 072 * IllegalArgumentException. 073 */ 074 public static final float[] add(final float[] array1, 075 final float[] array2) { 076 int length = _commonLength(array1, array2, "FloatArrayMath.add"); 077 float[] returnValue = new float[length]; 078 079 for (int i = 0; i < length; i++) { 080 returnValue[i] = array1[i] + array2[i]; 081 } 082 083 return returnValue; 084 } 085 086 /** Return a new array that is the result of appending array2 to the end 087 * of array1. This method simply calls 088 * append(array1, 0, array1.length, array2, 0, array2.length) 089 */ 090 public static final float[] append(final float[] array1, 091 final float[] array2) { 092 return append(array1, 0, array1.length, array2, 0, array2.length); 093 } 094 095 /** Return a new array that is the result of appending length2 096 * elements of array2, starting from the array1[idx2] to length1 097 * elements of array1, starting from array1[idx1]. 098 * Appending empty arrays is supported. In that case, the corresponding 099 * idx may be any number. Allow System.arraycopy() to throw array access 100 * exceptions if idx .. idx + length - 1 are not all valid array indices, 101 * for both of the arrays. 102 * @param array1 The first array of floats. 103 * @param idx1 The starting index for array1. 104 * @param length1 The number of elements of array1 to use. 105 * @param array2 The second array of floats, which is appended. 106 * @param idx2 The starting index for array2. 107 * @param length2 The number of elements of array2 to append. 108 * @return A new array of floats. 109 */ 110 public static final float[] append(final float[] array1, final int idx1, 111 final int length1, final float[] array2, final int idx2, 112 final int length2) { 113 float[] returnValue = new float[length1 + length2]; 114 115 if (length1 > 0) { 116 System.arraycopy(array1, idx1, returnValue, 0, length1); 117 } 118 119 if (length2 > 0) { 120 System.arraycopy(array2, idx2, returnValue, length1, length2); 121 } 122 123 return returnValue; 124 } 125 126 /** Return a new array that is formed by applying an instance of a 127 * FloatBinaryOperation to each element in the input array 128 * and z, using the array elements as the left operands and z 129 * as the right operand in all cases. (op.operate(array[i], z)). 130 * If the length of the array is 0, return a new array of length 0. 131 */ 132 public static final float[] applyBinaryOperation(FloatBinaryOperation op, 133 final float[] array, final float z) { 134 int length = array.length; 135 float[] returnValue = new float[length]; 136 137 for (int i = 0; i < length; i++) { 138 returnValue[i] = op.operate(array[i], z); 139 } 140 141 return returnValue; 142 } 143 144 /** Return a new array that is formed by applying an instance of a 145 * FloatBinaryOperation to each element in the input array, 146 * using z as the left operand in all cases and the array elements 147 * as the right operands (op.operate(z, array[i])). 148 * If the length of the array is 0, return a new array of length 0. 149 */ 150 public static final float[] applyBinaryOperation(FloatBinaryOperation op, 151 final float z, final float[] array) { 152 int length = array.length; 153 float[] returnValue = new float[length]; 154 155 for (int i = 0; i < length; i++) { 156 returnValue[i] = op.operate(array[i], z); 157 } 158 159 return returnValue; 160 } 161 162 /** Return a new array that is formed by applying an instance of a 163 * FloatBinaryOperation to the two arrays, element by element, 164 * using the elements of the first array as the left operands and the 165 * elements of the second array as the right operands. 166 * (op.operate(array[i], array2[i])). 167 * If the lengths of both arrays are 0, return a new array of length 0. 168 * If the two arrays do not have the same length, throw an 169 * IllegalArgumentException. 170 */ 171 public static final float[] applyBinaryOperation(FloatBinaryOperation op, 172 final float[] array1, final float[] array2) { 173 int length = _commonLength(array1, array2, 174 "FloatArrayMath.applyBinaryOperation"); 175 float[] returnValue = new float[length]; 176 177 for (int i = 0; i < length; i++) { 178 returnValue[i] = op.operate(array1[i], array2[i]); 179 } 180 181 return returnValue; 182 } 183 184 /** Return a new array that is formed by applying an instance of a 185 * FloatUnaryOperation to each element in the input array 186 * (op.operate(array[i])). 187 * If the length of the array is 0, return a new array of length 0. 188 */ 189 public static final float[] applyUnaryOperation( 190 final FloatUnaryOperation op, final float[] array) { 191 int length = array.length; 192 float[] returnValue = new float[length]; 193 194 for (int i = 0; i < length; i++) { 195 returnValue[i] = op.operate(array[i]); 196 } 197 198 return returnValue; 199 } 200 201 // no need for an element-by-element division, use divide(array, 202 // 1.0 / z) instead 203 204 /** Return a new array that is the element-by-element division of 205 * the first array by the second array (array1[i] / array2[i]). 206 * If the lengths of both arrays are 0, return a new array of length 0. 207 * If the two arrays do not have the same length, throw an 208 * IllegalArgumentException. 209 * @param array1 The first array of floats. 210 * @param array2 The second array of floats. 211 * @return A new array of floats. 212 */ 213 public static final float[] divideElements(final float[] array1, 214 final float[] array2) { 215 int length = _commonLength(array1, array2, 216 "FloatArrayMath.divideElements"); 217 float[] returnValue = new float[length]; 218 219 for (int i = 0; i < length; i++) { 220 returnValue[i] = array1[i] / array2[i]; 221 } 222 223 return returnValue; 224 } 225 226 /** Return a new array that is the element-by-element division of 227 * the first array by the given value. 228 * @param array The array of float numbers. 229 * @param num The float scalar. 230 * @return A new array of float numbers. 231 */ 232 public static final float[] divide(float[] array, float num) { 233 float[] returnValue = new float[array.length]; 234 235 for (int i = 0; i < array.length; i++) { 236 returnValue[i] = array[i] / num; 237 } 238 239 return returnValue; 240 } 241 242 /** Return the dot product of the two arrays. 243 * If the lengths of the array are both 0, return 0.0f. 244 * If the two arrays do not have the same length, throw an 245 * IllegalArgumentException. 246 */ 247 public static final float dotProduct(final float[] array1, 248 final float[] array2) { 249 int length = _commonLength(array1, array2, "FloatArrayMath.dotProduct"); 250 251 float sum = 0.0f; 252 253 for (int i = 0; i < length; i++) { 254 sum += array1[i] * array2[i]; 255 } 256 257 return sum; 258 } 259 260 /** Return the L2-norm of the array, that is, the square root of 261 * the sum of the squares of the elements. 262 */ 263 public static final float l2norm(final float[] array) { 264 return (float) Math.sqrt(sumOfSquares(array)); 265 } 266 267 /** Return a new array that is a copy of the argument except that 268 * the elements are limited to lie within the specified range. 269 * If any value is infinite then it is replaced by either the top 270 * or the bottom, depending on its sign. If any value is 271 * infinite, then it is replaced by either the top or the bottom, 272 * depending on its sign. If any element of the array is NaN, 273 * then the corresponding element in the new array will also be 274 * NaN. To leave either the bottom or the top unconstrained, 275 * specify Float.NEGATIVE_INFINITY or Float.POSITIVE_INFINITY. 276 * 277 * If the length of the array is 0, return a new array of length 0. 278 * @param array An array of floats. 279 * @param bottom The bottom limit. 280 * @param top The top limit. 281 * @return A new array with values in the range [bottom, top]. 282 */ 283 public static final float[] limit(final float[] array, final float bottom, 284 final float top) { 285 float[] returnValue = new float[array.length]; 286 287 for (int i = 0; i < array.length; i++) { 288 if (array[i] > top || array[i] == Float.POSITIVE_INFINITY) { 289 returnValue[i] = top; 290 } else if (array[i] < bottom 291 || array[i] == Float.NEGATIVE_INFINITY) { 292 returnValue[i] = bottom; 293 } else { 294 returnValue[i] = array[i]; 295 } 296 } 297 298 return returnValue; 299 } 300 301 /** Return a new array that is the element-by-element multiplication of 302 * the two input arrays. 303 * If the lengths of both arrays are 0, return a new array of length 0. 304 * If the two arrays do not have the same length, throw an 305 * IllegalArgumentException. 306 */ 307 public static final float[] multiply(final float[] array1, 308 final float[] array2) { 309 int length = _commonLength(array1, array2, "FloatArrayMath.multiply"); 310 float[] returnValue = new float[length]; 311 312 for (int i = 0; i < length; i++) { 313 returnValue[i] = array1[i] * array2[i]; 314 } 315 316 return returnValue; 317 } 318 319 /** Return a new array that is constructed from the argument by 320 * multiplying each element in the array by the second argument, which is 321 * a float. 322 * If the sizes of the array is 0, return a new array of size 0. 323 * @param array An array of floats. 324 * @param factor A float. 325 * @return A new array of floats. 326 */ 327 public static final float[] multiply(float[] array, float factor) { 328 int length = array.length; 329 float[] returnValue = new float[length]; 330 331 for (int i = 0; i < length; i++) { 332 returnValue[i] = array[i] * factor; 333 } 334 335 return returnValue; 336 } 337 338 /** Return a new array that is the formed by the additive inverse of each 339 * element of the input array (-array[i]). 340 */ 341 public static final float[] negative(final float[] array) { 342 int length = array.length; 343 float[] returnValue = new float[length]; 344 345 for (int i = 0; i < length; i++) { 346 returnValue[i] = -array[i]; 347 } 348 349 return returnValue; 350 } 351 352 /** Return a new array that is formed by scaling the array so that 353 * it has a L2-norm of 1. 354 */ 355 public static final float[] normalize(final float[] array) { 356 return scale(array, 1.0f / l2norm(array)); 357 } 358 359 /** Return a new array of floats that is formed by padding the 360 * middle of the array with 0's. If either the length of the 361 * input array is odd, the sample with index ceil(L/2) will be 362 * repeated in the output array, where L is the length of the input array. 363 * If the length of the input and output arrays are equal, return 364 * a copy of the input array. 365 * This method is useful for preparing data for an IFFT. 366 * @param array An array of floats. 367 * @param newLength The desired length of the returned array. 368 * @return A new array of floats. 369 */ 370 public static final float[] padMiddle(final float[] array, 371 final int newLength) { 372 int length = array.length; 373 374 int entriesNeeded = newLength - length; 375 376 if (entriesNeeded < 0) { 377 throw new IllegalArgumentException("ptolemy.math." 378 + "FloatArrayMath.padMiddle() : newLength must be " 379 + ">= length of array."); 380 } else if (entriesNeeded == 0) { 381 return resize(array, newLength); // allocates a new array 382 } 383 384 double halfLength = length * 0.5; 385 int halfLengthFloor = (int) Math.floor(halfLength); 386 int halfLengthCeil = (int) Math.ceil(halfLength); 387 float[] returnValue = new float[newLength]; 388 389 System.arraycopy(array, 0, returnValue, 0, halfLengthCeil); 390 391 System.arraycopy(array, halfLengthFloor, returnValue, 392 newLength - halfLengthCeil, halfLengthCeil); 393 394 return returnValue; 395 } 396 397 /** Return a new array of length newLength that is formed by 398 * either truncating or padding the input array. 399 * This method simply calls : 400 * resize(array, newLength, 0) 401 * @param array An array of floats. 402 * @param newLength The desired length of the output array. 403 * @return A new array of floats of length newLength. 404 */ 405 public static final float[] resize(final float[] array, 406 final int newLength) { 407 return resize(array, newLength, 0); 408 } 409 410 /** Return a new array of length newLength that is formed by 411 * either truncating or padding the input array. 412 * Elements from the input array are copied to the output array, 413 * starting from array[startIdx] until one of the following conditions 414 * is met : 415 * 1) The input array has no more elements to copy. 416 * 2) The output array has been completely filled. 417 * startIdx must index a valid entry in array unless the input array 418 * is of zero length or the output array is of zero length. 419 * If case 1) is met, the remainder of the output array is filled with 420 * zero's, implicitly by Java (padding). 421 * @param array An array of floats. 422 * @param newLength The desired length of the output array. 423 * @param startIdx The starting index for the input array. 424 * @return A new array of floats of length newLength. 425 */ 426 public static final float[] resize(float[] array, final int newLength, 427 final int startIdx) { 428 float[] returnValue = new float[newLength]; 429 int copySize = Math.min(newLength, array.length - startIdx); 430 431 if (startIdx >= array.length && copySize > 0) { 432 throw new IllegalArgumentException("resize(): the start index '" 433 + startIdx + "' is greater than equal to the array length '" 434 + array.length + "' and the number of items to be copied '" 435 + copySize + "' is greater than zero."); 436 } 437 438 if (copySize > 0) { 439 System.arraycopy(array, startIdx, returnValue, 0, copySize); 440 } 441 442 return returnValue; 443 } 444 445 /** Return a new array of floats produced by scaling the input 446 * array elements by scaleFactor. 447 * If the length of the array is 0, return a new array of length 0. 448 */ 449 public static final float[] scale(float[] array, float scaleFactor) { 450 float[] returnValue = new float[array.length]; 451 452 for (int i = 0; i < array.length; i++) { 453 returnValue[i] = scaleFactor * array[i]; 454 } 455 456 return returnValue; 457 } 458 459 /** Return a new array that is the element-by-element difference of the 460 * two input arrays, i.e. the first array minus the second array 461 * (array1[i] - array2[i]). 462 * If the lengths of both arrays are 0, return a new array of length 0. 463 */ 464 public static final float[] subtract(final float[] array1, 465 final float[] array2) { 466 int length = _commonLength(array1, array2, "FloatArrayMath.subtract"); 467 float[] returnValue = new float[length]; 468 469 for (int i = 0; i < length; i++) { 470 returnValue[i] = array1[i] - array2[i]; 471 } 472 473 return returnValue; 474 } 475 476 /** Return the sum of the squares of all of the elements in the array. 477 * This is equivalent to the square of the L2-norm of the array. 478 * Return 0.0f if the length of the array is 0. 479 */ 480 public static final float sumOfSquares(float[] array) { 481 float sum = 0.0f; 482 483 for (float element : array) { 484 sum += element * element; 485 } 486 487 return sum; 488 } 489 490 /** Return a new array that is formed by converting the floats in 491 * the argument array to complex numbers. Each complex number has 492 * real part equal to the value in the argument matrix and a zero 493 * imaginary part. 494 * 495 * @param array An array of floats. 496 * @return A new array of complex numbers. 497 */ 498 public static final Complex[] toComplexArray(final float[] array) { 499 int length = array.length; 500 Complex[] returnValue = new Complex[length]; 501 502 for (int i = 0; i < length; i++) { 503 returnValue[i] = new Complex(array[i], 0.0); 504 } 505 506 return returnValue; 507 } 508 509 /** Return a new array that is formed by converting the floats in 510 * the argument array to doubles. If the length of the argument 511 * array is 0, return a new array of length 0. 512 * @param array An array of float. 513 * @return A new array of doubles. 514 */ 515 public static final double[] toDoubleArray(final float[] array) { 516 int length = array.length; 517 double[] returnValue = new double[length]; 518 519 for (int i = 0; i < length; i++) { 520 returnValue[i] = array[i]; 521 } 522 523 return returnValue; 524 } 525 526 /** Return a new array that is formed by converting the floats in 527 * the argument array to integers. 528 * If the length of the argument array is 0, 529 * return a new array of length 0. 530 * @param array An array of float. 531 * @return A new array of integers. 532 */ 533 public static final int[] toIntegerArray(final float[] array) { 534 int length = array.length; 535 int[] returnValue = new int[length]; 536 537 for (int i = 0; i < length; i++) { 538 returnValue[i] = (int) array[i]; 539 } 540 541 return returnValue; 542 } 543 544 /** Return a new array that is formed by converting the floats in 545 * the argument array to longs. If the length of the argument 546 * array is 0, return a new array of length 0. 547 * @param array An array of float. 548 * @return A new array of longs. 549 */ 550 public static final long[] toLongArray(final float[] array) { 551 int length = array.length; 552 long[] returnValue = new long[length]; 553 554 for (int i = 0; i < length; i++) { 555 returnValue[i] = (long) array[i]; 556 } 557 558 return returnValue; 559 } 560 561 /** Return a new String representing the array, formatted as 562 * in Java array initializers. 563 */ 564 public static final String toString(final float[] array) { 565 return toString(array, ", ", "{", "}"); 566 } 567 568 /** Return a new String representing the array, formatted as 569 * specified by the ArrayStringFormat argument. 570 * To get a String in the Ptolemy expression language format, 571 * call this method with ArrayStringFormat.exprASFormat as the 572 * format argument. 573 */ 574 public static final String toString(final float[] array, 575 String elementDelimiter, String vectorBegin, String vectorEnd) { 576 int length = array.length; 577 StringBuffer sb = new StringBuffer(); 578 579 sb.append(vectorBegin); 580 581 for (int i = 0; i < length; i++) { 582 sb.append(Float.toString(array[i])); 583 584 if (i < length - 1) { 585 sb.append(elementDelimiter); 586 } 587 } 588 589 sb.append(vectorEnd); 590 591 return new String(sb); 592 } 593 594 /** Return true if all the distances between corresponding elements 595 * <i>array1</i> and <i>array2</i> are all less than or equal to 596 * the corresponding elements in <i>maxError</i>. If both arrays 597 * are empty, return true. If <i>maxError</i> is negative, return false. 598 * 599 * @param array1 The first array. 600 * @param array2 The second array. 601 * @param maxError The threshold for the magnitude of the difference. 602 * @return True if all the distances between corresponding elements 603 * <i>array1</i> and <i>array2</i> are all less than or equal to 604 * the corresponding elements in <i>maxError</i>. 605 * @exception IllegalArgumentException If the arrays are not of the same 606 * length. 607 */ 608 public static final boolean within(final float[] array1, 609 final float[] array2, float maxError) { 610 int length = _commonLength(array1, array2, "FloatArrayMath.within"); 611 612 for (int i = 0; i < length; i++) { 613 if (array1[i] > array2[i] + maxError 614 || array1[i] < array2[i] - maxError) { 615 return false; 616 } 617 } 618 619 return true; 620 } 621 622 /** Return true if all the distances between corresponding elements 623 * <i>array1</i> and <i>array2</i> are all less than or equal to 624 * the corresponding elements in <i>maxError</i>. If both arrays 625 * are empty, return true. If any element of <i>maxError</i> is negative 626 * return false. 627 * 628 * @param array1 The first array. 629 * @param array2 The second array. 630 * @param maxError The array of thresholds for the magnitudes of 631 * the difference. 632 * @return True if all the distances between corresponding elements 633 * <i>array1</i> and <i>array2</i> are all less than or equal to 634 * the corresponding elements in <i>maxError</i>. 635 * @exception IllegalArgumentException If the arrays are not of the same 636 * length. 637 */ 638 public static final boolean within(final float[] array1, 639 final float[] array2, float[] maxError) { 640 int length = _commonLength(array1, array2, "FloatArrayMath.within"); 641 642 for (int i = 0; i < length; i++) { 643 if (array1[i] > array2[i] + maxError[i] 644 || array1[i] < array2[i] - maxError[i]) { 645 return false; 646 } 647 } 648 649 return true; 650 } 651 652 /////////////////////////////////////////////////////////////////// 653 //// protected methods //// 654 655 /** Throw an exception if the two arrays are not of the same length, 656 * or if either array is null. An exception is NOT thrown if both 657 * arrays are of length 0. If no exception is thrown, return the common 658 * length of the arrays. 659 * @param array1 The first array of floats. 660 * @param array2 The second array of floats. 661 * @param methodName A String representing the method name of the caller, 662 * without parentheses. 663 * @return The common length of both arrays. 664 */ 665 protected static final int _commonLength(final float[] array1, 666 final float[] array2, String methodName) { 667 if (array1 == null) { 668 throw new IllegalArgumentException("ptolemy.math." + methodName 669 + "() : first input array is null."); 670 } 671 672 if (array2 == null) { 673 throw new IllegalArgumentException("ptolemy.math." + methodName 674 + "() : second input array is null."); 675 } 676 677 if (array1.length != array2.length) { 678 throw new IllegalArgumentException("ptolemy.math." + methodName 679 + "() : input arrays must have the same length, " 680 + "but the first array has length " + array1.length 681 + " and the second array has length " + array2.length 682 + "."); 683 } 684 685 return array1.length; 686 } 687}