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}