001/* Class providing additional functions in the Ptolemy II expression language.
002
003 Copyright (c) 1998-2016 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION_2
025 COPYRIGHTENDKEY
026
027
028 */
029package ptolemy.data.expr;
030
031import java.io.BufferedReader;
032import java.io.File;
033import java.io.FileInputStream;
034import java.io.FileNotFoundException;
035import java.io.IOException;
036import java.io.InputStream;
037import java.io.InputStreamReader;
038import java.net.URL;
039import java.util.Arrays;
040import java.util.Comparator;
041import java.util.HashMap;
042import java.util.HashSet;
043import java.util.Iterator;
044import java.util.LinkedList;
045import java.util.List;
046import java.util.Map;
047import java.util.Random;
048import java.util.Set;
049import java.util.StringTokenizer;
050import java.util.Vector;
051
052import ptolemy.data.ActorToken;
053import ptolemy.data.ArrayToken;
054import ptolemy.data.BooleanToken;
055import ptolemy.data.ComplexMatrixToken;
056import ptolemy.data.ComplexToken;
057import ptolemy.data.DoubleMatrixToken;
058import ptolemy.data.DoubleToken;
059import ptolemy.data.FunctionToken;
060import ptolemy.data.IntMatrixToken;
061import ptolemy.data.IntToken;
062import ptolemy.data.LongMatrixToken;
063import ptolemy.data.LongToken;
064import ptolemy.data.MatrixToken;
065import ptolemy.data.ObjectToken;
066import ptolemy.data.RecordToken;
067import ptolemy.data.ScalarToken;
068import ptolemy.data.StringToken;
069import ptolemy.data.Token;
070import ptolemy.data.UnsignedByteToken;
071import ptolemy.data.UnsizedArrayToken;
072import ptolemy.data.type.ArrayType;
073import ptolemy.data.type.BaseType;
074import ptolemy.data.type.FunctionType;
075import ptolemy.data.type.Type;
076import ptolemy.data.type.TypeLattice;
077import ptolemy.graph.CPO;
078import ptolemy.kernel.util.IllegalActionException;
079import ptolemy.kernel.util.InternalErrorException;
080import ptolemy.math.ComplexMatrixMath;
081import ptolemy.util.MessageHandler;
082import ptolemy.util.StringUtilities;
083
084///////////////////////////////////////////////////////////////////
085//// UtilityFunctions
086
087/**
088 This class provides additional functions for use in the Ptolemy II
089 expression language.  All of the methods in this class are static
090 and return an instance of Token.  The expression language identifies
091 the appropriate method to use by using reflection, matching the
092 types of the arguments.
093
094 @author Christopher Hylands Brooks, Tobin Fricke, Bart Kienhuis, Edward A. Lee,
095 Steve Neuendorffer, Neil Smyth, Yang Zhao.  Contributor: Daniel Crawl
096 @version $Id$
097 @since Ptolemy II 0.2
098 @Pt.ProposedRating Yellow (eal)
099 @Pt.AcceptedRating Red (cxh)
100 */
101public class UtilityFunctions {
102    ///////////////////////////////////////////////////////////////////
103    ////                         public methods                    ////
104
105    /** Return a new UnsizedArrayToken whose element type is the same
106     * as the given type.
107     * @return a new UnsizedArrayToken.
108     */
109    public static ArrayToken arrayType(Token t) {
110        // Note that this method is deliberately not listed in the Expression
111        // chapter because it is very specialized.
112        return new UnsizedArrayToken(t.getType());
113    }
114
115    /** Return a new ArrayType whose element type is the same as the given
116     *  type.
117     *  @param type The type of the array.
118     *  @return a new ArrayType.
119     */
120    public static Type arrayTypeReturnType(Type type) {
121        // Note that this method is deliberately not listed in the Expression
122        // chapter because it is very specialized.
123        return new ArrayType(type);
124    }
125
126    /** Return a new UnsizedArrayToken whose element type is the same
127     *  as the given type, and whose length is the given length.
128     *  @param t The element type of the array.
129     *  @param numberOfTimes The array length.
130     *  @return a new ArrayToken.
131     */
132    public static ArrayToken arrayType(Token t, IntToken numberOfTimes) {
133        // Note that this method is deliberately not listed in the Expression
134        // chapter because it is very specialized.
135        return repeat(numberOfTimes, t);
136    }
137
138    /** Return the (not quite exact) return type of the arrayType
139     *  function above.  This function always returns an ArrayType
140     *  whose element type is the first argument, with length equal to
141     *  the second argument.  The result type is reported as being
142     *  unsized, which may result in an inexact type being inferred
143     *  for expressions including the arrayType function.
144     *  @param type1 The type of the first argument to the
145     *  corresponding function.
146     *  @param type2 The type of the second argument to the
147     *  corresponding function.
148     *  @return The type of the value returned from the corresponding function.
149     */
150    public static Type arrayTypeReturnType(Type type1, Type type2) {
151        // FIXME: The method comment makes no sense, the type1
152        // argument is not used.
153
154        // Note that this method is deliberately not listed in the Expression
155        // chapter because it is very specialized.
156        return new ArrayType(type2);
157    }
158
159    /** Convert the argument from a fileName to a URL that begins with
160     *  "file:".
161     *  @param fileName The name of the file to be converted.
162     *  @return the URL that is equivalent to the file
163     */
164    public static String asURL(String fileName) {
165        File file = new File(fileName);
166
167        try {
168            URL url = file.toURI().toURL();
169            return url.toString();
170        } catch (java.net.MalformedURLException malformed) {
171            throw new RuntimeException(
172                    "could not convert '" + file + "' to a URL", malformed);
173        }
174    }
175
176    /** Convert the second token to the type of the first.
177     *  @exception IllegalActionException If the token cannot be converted.
178     */
179    public static Token cast(Token token1, Token token2)
180            throws IllegalActionException {
181        return token1.getType().convert(token2);
182    }
183
184    /** Return the type of the first argument.
185     *  @param type1 The type being cast to.
186     *  @param type2 The type being cast.
187     *  @return The type being cast to.
188     */
189    public static Type castReturnType(Type type1, Type type2)
190            throws IllegalActionException {
191        return type1;
192    }
193
194    /**
195     * Returns the lower triangular matrix L that satisfies LL*=A
196     * @param A a positive definite matrix (implies also symmetric)
197     * @return lower triangular matrix L such that A=LL*
198     * @exception IllegalActionException
199     */
200    public static double[][] choleskyDecomposition(double[][] A)
201            throws IllegalActionException {
202
203        if (A == null || A[0] == null || A.length != A[0].length) {
204            throw new IllegalArgumentException(
205                    "The input must be a square matrix.");
206        }
207        int N = A.length;
208
209        //A should also be positive-definite. If found not to be so, an error will be thrown.
210        double[][] L = new double[N][N]; // the lower triangular Cholesky factor s.t. L*L^A
211        for (int i = 0; i < N; i++) {
212            for (int j = 0; j <= i; j++) {
213                double lowerSum = 0;
214                for (int k = 0; k < j; k++) {
215                    lowerSum += L[i][k] * L[j][k];
216                }
217                if (i != j) {
218                    L[i][j] = 1.0 / L[j][j] * (A[i][j] - lowerSum);
219                } else {
220                    if (A[i][i] - lowerSum < 0.0) {
221                        throw new IllegalActionException(
222                                "The input matrix must be positive-definite.");
223                    }
224                    L[i][j] = Math.sqrt(A[i][i] - lowerSum);
225                }
226            }
227        }
228        return L;
229    }
230
231    /** Concatenate two arrays.
232     *  The arrays must have the same type.
233     *  Example: concatenate({1,2,3},{4,5,6}) == {1,2,3,4,5,6}.
234     *  @param token1 First array from which tokens are copied to the result.
235     *  @param token2 Second array from which tokens are copied to the result.
236     *  @return An array containing all of the elements of the first argument
237     *   (in order), followed by all of the arguments of the second argument
238     *   (in order).
239     *  @exception IllegalActionException If the arrays do not have
240     *   compatible types.
241     *  @since Ptolemy II 4.1
242     */
243    public static ArrayToken concatenate(ArrayToken token1, ArrayToken token2)
244            throws IllegalActionException {
245        Token[] array1 = token1.arrayValue();
246        Token[] array2 = token2.arrayValue();
247
248        int nElements = array1.length + array2.length;
249        Token[] resultArray = new Token[nElements];
250
251        System.arraycopy(array1, 0, resultArray, 0, array1.length);
252        System.arraycopy(array2, 0, resultArray, array1.length, array2.length);
253
254        return new ArrayToken(resultArray);
255    }
256
257    /** Concatenate an array of arrays into a single array.
258     *  Example: concatenate({{1,2,3},{4,5},{6,7}}) == {1,2,3,4,5,6,7}.
259     *  @param token Array of arrays which are to be concatenated.
260     *  @exception IllegalActionException If the argument is not an array of
261     *   arrays.
262     *  @since Ptolemy II 4.1
263     */
264    public static ArrayToken concatenate(ArrayToken token)
265            throws IllegalActionException {
266        if (!(token.getElementType() instanceof ArrayType)) {
267            throw new IllegalActionException(
268                    "The argument to concatenate(ArrayToken) "
269                            + "must be an array of arrays.");
270        }
271
272        int nElements = 0;
273
274        for (int i = 0; i < token.length(); i++) {
275            nElements += ((ArrayToken) token.getElement(i)).length();
276        }
277
278        Token[] result = new Token[nElements];
279        int cursor = 0;
280
281        for (int i = 0; i < token.length(); i++) {
282            Token[] array = ((ArrayToken) token.getElement(i)).arrayValue();
283            System.arraycopy(array, 0, result, cursor, array.length);
284            cursor += array.length;
285        }
286
287        return new ArrayToken(result);
288    }
289
290    /** Return the return type of the two argument concatenate method,
291     *  given the types of the arguments.  This specialized method is
292     *  called by the CachedMethod class and used to provide a better
293     *  return type.
294     *  @param firstArrayType The type of the predicate function.
295     *  @param secondArrayType The type of the array to be filtered.
296     *  @return The type of the result, which is an array of the least
297     *   upper bound of the array arguments.  The length of the array type
298     *   is the sum of the length of the arguments.
299     *  @exception IllegalActionException If the types of the array arguments
300     *   are not Arrays.
301     */
302    public static Type concatenateReturnType(Type firstArrayType,
303            Type secondArrayType) throws IllegalActionException {
304        // Note that this method is deliberately not listed in the Expression
305        // chapter because it is very specialized.
306        if (!(firstArrayType instanceof ArrayType)
307                || !(secondArrayType instanceof ArrayType)) {
308            throw new IllegalActionException("Type " + firstArrayType + " or "
309                    + secondArrayType + " is not an ArrayType?");
310        } else {
311            return new ArrayType(
312                    ((ArrayType) TypeLattice.lattice()
313                            .leastUpperBound(firstArrayType, secondArrayType))
314                                    .getElementType(),
315                    ((ArrayType) firstArrayType).length()
316                            + ((ArrayType) secondArrayType).length());
317        }
318    }
319
320    /** Return the return type of the one argument concatenate method, given the type
321     *  of the argument.
322     *  This specialized method is called by the CachedMethod class and used
323     *  to provide a better return type.
324     *  @param firstArrayType The type of the predicate function.
325     *  @return The type of the result, which is an array of the
326     *   firstArrayType arguments.  The length of the array type
327     *   may be unknown.
328     *  @exception IllegalActionException If the types of the array argument
329     *   is not an Array.
330     */
331    public static Type concatenateReturnType(Type firstArrayType)
332            throws IllegalActionException {
333        // Note that this method is deliberately not listed in the Expression
334        // chapter because it is very specialized.
335        if (!(firstArrayType instanceof ArrayType)) {
336            throw new IllegalActionException(
337                    "Type " + firstArrayType + " is not an ArrayType?");
338        } else {
339            int nElements = 0;
340            ArrayType arrayType = (ArrayType) firstArrayType;
341            if (arrayType.getElementType() instanceof ArrayType) {
342                ArrayType arrayArrayType = (ArrayType) arrayType
343                        .getElementType();
344                if (arrayArrayType.hasKnownLength()) {
345                    nElements += ((ArrayType) arrayType.getElementType())
346                            .length();
347                }
348            }
349            if (nElements > 0) {
350                return new ArrayType(((ArrayType) arrayType.getElementType())
351                        .getElementType(), nElements);
352            } else {
353                return new ArrayType(((ArrayType) arrayType.getElementType())
354                        .getElementType());
355            }
356        }
357    }
358
359    /** Return a record token that contains the names of all the
360     *  constants and their values.
361     *  @return A token containing the names of all the constants
362     *   and their values.
363     *  @since Ptolemy II 2.1
364     */
365    public static RecordToken constants() {
366        return Constants.constants();
367    }
368
369    /** Return an empty record.
370     *  @return An empty record.
371     */
372    public static RecordToken emptyRecord() throws IllegalActionException {
373        return new RecordToken(new String[0], new Token[0]);
374    }
375
376    /** Return an empty array with its element type matching
377     *  the specified token.
378     *  @param prototype A token specifying the element type.
379     *  @return An empty array.
380     */
381    public static ArrayToken emptyArray(Token prototype) {
382        return new ArrayToken(prototype.getType());
383    }
384
385    /** Return the type of an empty array of unspecified length.
386     *  @param prototype A token specifying the element type.
387     *  @return An empty array.
388     */
389    public static Type emptyArrayReturnType(Type prototype) {
390        return new ArrayType(prototype);
391    }
392
393    /** Extract a sub-array consisting of all of the elements of an
394     *  array for which the given predicate function returns true.
395     *  If the given array has type {X}, then the given function should
396     *  have type function(x:X)(boolean).  Example usage:
397     *  <p><code>even = function(x:int)(x%2==0)
398     *  filter(even,[1:1:20].toArray)</code></p>
399     *  @param predicate A function that takes exactly one parameter (of the
400     *   same type as the elements of the given array) and returns
401     *   a boolean value.
402     *  @param array An array, on whose elements the given function
403     *   will operate.
404     *  @return The subarray of this array containing exactly those
405     *   elements, in order, for which the given function returns
406     *   a BooleanToken with value true.  Any other return value
407     *   results in the element being omitted. If the given function
408     *   never returns true, then return an empty array with an
409     *   element type that matches the specified array.
410     *  @exception IllegalActionException If applying the function
411     *   triggers an exception.
412     *  @since Ptolemy II 4.1
413     */
414    public static ArrayToken filter(FunctionToken predicate, ArrayToken array)
415            throws IllegalActionException {
416        return filter(predicate, array, new IntToken(-1));
417    }
418
419    /** Extract a sub-array consisting of all of the elements of an
420     *  array for which the given predicate function returns true.
421     *  If the given array has type {X}, then the given function should
422     *  have type function(x:X)(boolean).  Example usage:
423     *  <p><code>even = function(x:int)(x%2==0)
424     *  filter(even,[1:1:20].toArray)</code></p>
425     *  @param predicate A function that takes exactly one parameter (of the
426     *   same type as the elements of the given array) and returns
427     *   a boolean value.
428     *  @param array An array, on whose elements the given function
429     *   will operate.
430     *  @param sizeLimit The maximum size of the resulting array,
431     *   or a negative number to specify no limit.
432     *  @return The subarray of this array containing exactly those
433     *   elements, in order, for which the given function returns
434     *   a BooleanToken with value true.  Any other return value
435     *   results in the element being omitted. If the given function
436     *   never returns true, then return an empty array with an
437     *   element type that matches the specified array.
438     *  @exception IllegalActionException If applying the function
439     *   triggers an exception.
440     *  @since Ptolemy II 4.1
441     */
442    public static ArrayToken filter(FunctionToken predicate, ArrayToken array,
443            IntToken sizeLimit) throws IllegalActionException {
444        List result = new LinkedList();
445        int arity = predicate.getNumberOfArguments();
446
447        if (arity != 1) {
448            throw new IllegalActionException(
449                    "The predicate argument of filter() must be a function"
450                            + " that takes one argument.");
451        }
452
453        int sizeLimitValue = sizeLimit.intValue();
454
455        for (int i = 0; i < array.length(); i++) {
456            Token element = array.getElement(i);
457            Token[] elementList = { element };
458            Token include = predicate.apply(elementList);
459
460            if (include instanceof BooleanToken
461                    && ((BooleanToken) include).booleanValue()) {
462                result.add(element);
463            }
464
465            if (sizeLimitValue >= 0 && result.size() >= sizeLimitValue) {
466                break;
467            }
468        }
469
470        if (result.size() > 0) {
471            Token[] resultArray = new Token[result.size()];
472            resultArray = (Token[]) result.toArray(resultArray);
473            return new ArrayToken(resultArray);
474        } else {
475            return new ArrayToken(array.getElementType());
476        }
477    }
478
479    /** Return the return type of the filter method, given the types
480     *  of the argument.
481     *  @param predicateType The type of the predicate function.
482     *  @param arrayTokenType The type of the array to be filtered.
483     *  @return The type of the result, which is the same as the array
484     *   type to be filtered.
485     *  @exception IllegalActionException If the specified function does not
486     *   take exactly one argument, or if the type signature of the function
487     *   is not compatible with the other array argument.
488     */
489    public static Type filterReturnType(Type predicateType, Type arrayTokenType)
490            throws IllegalActionException {
491        // Note that this method is deliberately not listed in the Expression
492        // chapter because it is very specialized.
493        return filterReturnType(predicateType, arrayTokenType, null);
494    }
495
496    /** Return the return type of the filter method, given the types
497     *  of the argument.
498     *  @param predicateType The type of the predicate function.
499     *  @param arrayTokenType The type of the array to be filtered.
500     *  @param sizeLimitType The type of the sizeLimit argument, or
501     *   null if there is no specified size limit.
502     *  @return The type of the result, which is the same as the array
503     *   type to be filtered.
504     *  @exception IllegalActionException If the specified function does not
505     *   take exactly one argument, or if the type signature of the function
506     *   is not compatible with the other array argument.
507     */
508    public static Type filterReturnType(Type predicateType, Type arrayTokenType,
509            Type sizeLimitType) throws IllegalActionException {
510        // Note that this method is deliberately not listed in the Expression
511        // chapter because it is very specialized.
512        if (predicateType instanceof FunctionType) {
513            FunctionType castPredicateType = (FunctionType) predicateType;
514
515            if (castPredicateType.getArgCount() != 1) {
516                throw new IllegalActionException(
517                        "filter() can only be used on functions that take "
518                                + "one argument.");
519            } else {
520                Type argType = castPredicateType.getArgType(0);
521                int comparison = TypeLattice.compare(
522                        ((ArrayType) arrayTokenType).getElementType(), argType);
523
524                if (comparison != CPO.LOWER && comparison != CPO.SAME) {
525                    throw new IllegalActionException(
526                            "filter(): specified array element is not "
527                                    + "compatible with function argument type.");
528                }
529
530                // Force size to be unknown
531                return new ArrayType(
532                        ((ArrayType) arrayTokenType).getElementType());
533            }
534        } else {
535            return BaseType.UNKNOWN;
536        }
537    }
538
539    /** Find all true-valued elements in an array of boolean values,
540     *  returning an array containing the indices (in ascending order)
541     *  of all occurrences of the value 'true'.
542     *  @param array An array of boolean tokens.
543     *  @return An array of integers giving the indices of 'true' elements
544     *   in the array given as an argument.
545     *  @since Ptolemy II 4.1
546     *  @see #find(ArrayToken, Token)
547     *  @exception IllegalActionException If the specified array is not
548     *   a boolean array.
549     */
550    public static ArrayToken find(ArrayToken array)
551            throws IllegalActionException {
552        if (!(array.getElementType() == BaseType.BOOLEAN)) {
553            throw new IllegalActionException(
554                    "The argument must be an array of boolean tokens.");
555        }
556
557        return find(array, BooleanToken.TRUE);
558    }
559
560    /** Find all elements in an array that match the specified token
561     *  and return an array containing their indices (in ascending order).
562     *  @param array An array.
563     *  @return An array of integers giving the indices of elements
564     *   in the specified array that match the specified token.
565     *  @since Ptolemy II 4.1
566     *  @see #find(ArrayToken)
567     */
568    public static ArrayToken find(ArrayToken array, Token match) {
569        List result = new LinkedList();
570
571        for (int i = 0; i < array.length(); i++) {
572            if (array.getElement(i).equals(match)) {
573                result.add(new IntToken(i));
574            }
575        }
576
577        if (result.size() > 0) {
578            Token[] resultArray = new Token[result.size()];
579            resultArray = (Token[]) result.toArray(resultArray);
580
581            try {
582                return new ArrayToken(BaseType.INT, resultArray);
583            } catch (IllegalActionException ex) {
584                throw new InternalErrorException(null, ex,
585                        "UtilityFunctions.find: "
586                                + "cannot create an an Array of Integers");
587            }
588        } else {
589            return new ArrayToken(array.getElementType());
590        }
591    }
592
593    /** Find a file or directory. If the file does not exist as is, then
594     *  search the current working directory, the user's home directory,
595     *  and finally, the classpath.
596     *  @param name Path of a file or directory to find.
597     *  @return Canonical absolute path if the file or directory is found,
598     *   otherwise the argument is returned unchanged.
599     */
600    public static String findFile(String name) {
601        File file = new File(name);
602
603        // File has problems if we change user.dir, which we do in
604        // ptolemy/actor/gui/jnlp/MenuApplication.java if we are running
605        // under Web Start or InstallAnywhere.  What happens is that
606        // File ignores changes to user.dir, so findFile("ptmatlab.dll")
607        // will look in the old value of user.dir instead of the new
608        // value of user.dir.  The hack is to get the canonical path
609        // and use that instead.
610        if (file.exists()) {
611            try {
612                file = new File(file.getCanonicalPath());
613            } catch (IOException ex) {
614                file = file.getAbsoluteFile();
615            }
616        }
617
618        if (!file.exists()) {
619            String curDir = StringUtilities.getProperty("user.dir");
620            file = new File(curDir, name);
621        }
622
623        if (!file.exists()) {
624            String curDir = StringUtilities.getProperty("user.home");
625            file = new File(curDir, name);
626        }
627
628        if (!file.exists()) {
629            String cp = System.getProperty("java.class.path");
630            StringTokenizer tokens = new StringTokenizer(cp,
631                    System.getProperty("path.separator"));
632
633            while (tokens.hasMoreTokens()) {
634                String token = tokens.nextToken();
635                file = new File(token, name);
636
637                if (file.exists()) {
638                    break;
639                }
640            }
641        }
642
643        if (file.exists()) {
644            try {
645                return file.getCanonicalPath();
646            } catch (java.io.IOException ex) {
647                return file.getAbsolutePath();
648            }
649        } else {
650            return name;
651        }
652    }
653
654    /** Return the approximate number of bytes available for future
655     *  object allocation.  Note that requesting a garbage collection
656     *  may change this value.
657     *  @return The approximate number of bytes available.
658     *  @see #totalMemory()
659     */
660    public static LongToken freeMemory() {
661        return new LongToken(Runtime.getRuntime().freeMemory());
662    }
663
664    /** Return a Gaussian random number.
665     *  @param mean The mean.
666     *  @param standardDeviation The standard deviation.
667     *  @return An observation of a Gaussian random variable.
668     */
669    public static DoubleToken gaussian(double mean, double standardDeviation) {
670        if (_random == null) {
671            _random = new Random();
672        }
673
674        double raw = _random.nextGaussian();
675        double result = raw * standardDeviation + mean;
676        return new DoubleToken(result);
677    }
678
679    /** Return an array of Gaussian random numbers.
680     *  @param mean The mean.
681     *  @param standardDeviation The standard deviation.
682     *  @param length The length of the array.
683     *  @return An array of doubles with IID Gaussian random variables.
684     */
685    public static ArrayToken gaussian(double mean, double standardDeviation,
686            int length) {
687        if (_random == null) {
688            _random = new Random();
689        }
690
691        DoubleToken[] result = new DoubleToken[length];
692
693        for (int i = 0; i < length; i++) {
694            double raw = _random.nextGaussian();
695            result[i] = new DoubleToken(raw * standardDeviation + mean);
696        }
697
698        try {
699            return new ArrayToken(BaseType.DOUBLE, result);
700        } catch (IllegalActionException ex) {
701            // This should not happen since result should not be null.
702            throw new InternalErrorException(null, ex,
703                    "UtilityFunction.gaussian: "
704                            + "Cannot create the array that contains "
705                            + "Gaussian random numbers.");
706        }
707    }
708
709    /** Return a matrix of Gaussian random numbers.
710     *  @param mean The mean.
711     *  @param standardDeviation The standard deviation.
712     *  @param rows The number of rows.
713     *  @param columns The number of columns.
714     *  @return A matrix of observations of a Gaussian random variable.
715     */
716    public static DoubleMatrixToken gaussian(double mean,
717            double standardDeviation, int rows, int columns) {
718        if (_random == null) {
719            _random = new Random();
720        }
721
722        double[][] result = new double[rows][columns];
723
724        for (int i = 0; i < rows; i++) {
725            for (int j = 0; j < columns; j++) {
726                double raw = _random.nextGaussian();
727                result[i][j] = raw * standardDeviation + mean;
728            }
729        }
730
731        try {
732            return new DoubleMatrixToken(result);
733        } catch (IllegalActionException ex) {
734            // This should not happen since result should not be null.
735            throw new InternalErrorException(null, ex,
736                    "UtilityFunction.gaussian: "
737                            + "Cannot create the DoubleMatrixToken that contains "
738                            + "Gaussian random numbers.");
739        }
740    }
741
742    /** Get a variable from the environment.
743     *  @param variableName The name of the environment variable to get
744     *  @return the environment variable, or null if the variable is not defined.
745     */
746    public static StringToken getenv(String variableName) {
747        return new StringToken(System.getenv(variableName));
748    }
749
750    /** Get a variable from the environment.
751     *  @return the environment variable, or null if the variable is not defined.
752     */
753    public static RecordToken getenv() throws IllegalActionException {
754
755        Map<String, Token> environmentMap = new HashMap<String, Token>();
756
757        Map<String, String> environment = System.getenv();
758
759        Iterator environmentVariables = environment.entrySet().iterator();
760        while (environmentVariables.hasNext()) {
761            Map.Entry pairs = (Map.Entry) environmentVariables.next();
762            environmentMap.put((String) pairs.getKey(),
763                    new StringToken((String) pairs.getValue()));
764        }
765        return new RecordToken(environmentMap);
766    }
767
768    /** Get the specified property from the environment. An empty string
769     *  is returned if the argument environment variable does not exist,
770     *  though if certain properties are not defined, then we
771     *  make various attempts to determine them and then set them.
772     *  See the javadoc page for java.util.System.getProperties() for
773     *  a list of system properties.
774     *  <p>The following properties are handled specially
775     *  <dl>
776     *  <dt> "ptolemy.ptII.dir"
777     *  <dd> vergil usually sets the ptolemy.ptII.dir property to the
778     *  value of $PTII.  However, if we are running under Web Start,
779     *  then this property might not be set, in which case we look
780     *  for "ptolemy/kernel/util/NamedObj.class" and set the
781     *  property accordingly.
782     *  <dt> "ptolemy.ptII.dirAsURL"
783     *  <dd> Return $PTII as a URL.  For example, if $PTII was c:\ptII,
784     *  then return file:/c:/ptII/.
785     *  <dt> "user.dir"
786     *  <dd> Return the canonical path name to the current working directory.
787     *  This is necessary because under JDK1.4.1 System.getProperty()
788     *  returns <code><b>c</b>:/<i>foo</i></code>
789     *  whereas most of the other methods that operate
790     *  on path names return <code><b>C</b>:/<i>foo</i></code>.
791     *  </dl>
792     *  @param propertyName The name of property.
793     *  @return A String containing the string value of the property.
794     *  @deprecated Use
795     *  {@link ptolemy.util.StringUtilities#getProperty(String)}
796     *  instead
797     */
798    @Deprecated
799    public static String getProperty(String propertyName) {
800        return StringUtilities.getProperty(propertyName);
801    }
802
803    /** Infer the type of the given string as an expression in the
804     *  expression language. Return a string representation of the
805     *  given type.
806     *  @param string The string to be parsed and evaluated.
807     *  @return A string representing an inferred type.
808     */
809    public static String inferType(String string)
810            throws IllegalActionException {
811        // Note that this method is deliberately not listed in the Expression
812        // chapter because it is very specialized.
813        PtParser parser = new PtParser();
814        ASTPtRootNode parseTree = parser.generateParseTree(string);
815        ParseTreeTypeInference typeInference = new ParseTreeTypeInference();
816        return typeInference.inferTypes(parseTree).toString();
817    }
818
819    /** Find the intersection of two records. The field names are
820     *  intersected, and the values are taken from the first
821     *  record.
822     *  <p>Example: intersect({a = 1, b = 2}, {a = 3, c = 4}) returns {a = 1}.
823     *  @param record1 The first record to intersect. The result gets
824     *  its values from this record.
825     *  @param record2 The second record to intersect.
826     *  @return A RecordToken containing the result.
827     */
828    public static Token intersect(RecordToken record1, RecordToken record2)
829            throws IllegalActionException {
830        Set commonNames = new HashSet(record1.labelSet());
831        commonNames.retainAll(record2.labelSet());
832
833        Token[] values = new Token[commonNames.size()];
834        String[] names = new String[values.length];
835        int i = 0;
836
837        for (Iterator iterator = commonNames.iterator(); iterator
838                .hasNext(); i++) {
839            String name = (String) iterator.next();
840            values[i] = record1.get(name);
841            names[i] = name;
842        }
843
844        return new RecordToken(names, values);
845    }
846
847    /** Iterate the specified function to produce an array of the specified
848     *  length.  The first element of the output array is the <i>initial</i>
849     *  argument, the second is the result of applying the function to
850     *  <i>initial</i>, the third is the result of applying the function to
851     *  that result, etc.  The <i>length</i> argument is required to be
852     *  greater than 1.
853     *  @param function A single-argument function to iterate.
854     *  @param length The length of the resulting array.
855     *  @param initial The first element of the result.
856     *  @return A new array that is the result of applying the function
857     *   repeatedly.
858     *  @exception IllegalActionException If the specified function does not
859     *  take exactly one argument, or if an error occurs applying the function.
860     */
861    public static ArrayToken iterate(FunctionToken function, int length,
862            Token initial) throws IllegalActionException {
863        int arity = function.getNumberOfArguments();
864
865        if (arity != 1) {
866            throw new IllegalActionException(
867                    "iterate() can only be used on functions that take "
868                            + "one argument.");
869        } else if (length < 2) {
870            throw new IllegalActionException(
871                    "iterate() requires the length argument to be greater "
872                            + "than 1.");
873        } else {
874            Token[] result = new Token[length];
875            Token iterate = initial;
876            result[0] = initial;
877
878            for (int i = 1; i < length; i++) {
879                Token[] args = new Token[1];
880                args[0] = iterate;
881                iterate = function.apply(args);
882                result[i] = iterate;
883            }
884
885            return new ArrayToken(result);
886        }
887    }
888
889    /** Return the return type of the iterate method, given the types
890     *  of the argument.
891     *  @param functionType The type of the function to iterate.
892     *  @param lengthType The type of the length argument.
893     *  @param initialType The type of the first element of the result.
894     *  @return The type of the result.
895     *  @exception IllegalActionException If the specified function does not
896     *   take exactly one argument, or if the type signature of the function
897     *   is not compatible with the other arguments.
898     */
899    public static Type iterateReturnType(Type functionType, Type lengthType,
900            Type initialType) throws IllegalActionException {
901        if (functionType instanceof FunctionType) {
902            FunctionType castFunctionType = (FunctionType) functionType;
903
904            if (castFunctionType.getArgCount() != 1) {
905                throw new IllegalActionException(
906                        "iterate() can only be used on functions that take "
907                                + "one argument.");
908            } else {
909                Type argType = castFunctionType.getArgType(0);
910                int comparison = TypeLattice.compare(initialType, argType);
911
912                if (comparison != CPO.LOWER && comparison != CPO.SAME) {
913                    throw new IllegalActionException(
914                            "iterate(): specified initial value is not "
915                                    + "compatible with function argument type.");
916                }
917
918                Type resultType = castFunctionType.getReturnType();
919                int comparison2 = TypeLattice.compare(resultType, argType);
920
921                if (comparison2 != CPO.LOWER && comparison2 != CPO.SAME) {
922                    throw new IllegalActionException(
923                            "iterate(): invalid function: function return "
924                                    + "type is not "
925                                    + "compatible with function argument type.");
926                }
927
928                return new ArrayType(
929                        TypeLattice.leastUpperBound(resultType, initialType));
930            }
931        } else {
932            return BaseType.UNKNOWN;
933        }
934    }
935
936    /** Load a library by first using the default platform dependent
937     *  System.loadLibrary() method.  If the library cannot be loaded
938     *  using System.loadLibrary(), then search for the library using
939     *  {@link #findFile(String)} and if the library is found,
940     *  load it using System.load().  If the library is not found
941     *  by findFile() and the pathname contains a /
942     *  then we call System.loadLibrary() the filename after the last
943     *  /.  This step is necessary to support native methods under
944     *  Webstart, which looks for libraries at the top level.
945     *  If all of the above fails, we throw the initial exception.
946     *
947     *  @param library the name of the library to be loaded.  The name
948     *  should not include the platform dependent suffix.
949     */
950    public static void loadLibrary(String library) {
951        final boolean debug = false;
952        if (debug) {
953            System.out.println("UtilityFunctions.loadLibrary(" + library + ")");
954        }
955        try {
956            if (library.indexOf("/") == -1 && library.indexOf("\\") == -1) {
957                // loadLibrary does not work if the library has a \ or / in it.
958                // Unfortunately, pathnames tend to get specified with
959                // a forward slash, even under windows
960                if (debug) {
961                    System.out.println("UtilityFunctions.loadLibrary(" + library
962                            + "): System.loadLibrary()");
963                }
964                System.loadLibrary(library);
965            } else {
966                // load() does not work with relative paths.
967                if (debug) {
968                    System.out.println("UtilityFunctions.loadLibrary(" + library
969                            + "): System.load()");
970                }
971                System.load(library);
972            }
973        } catch (UnsatisfiedLinkError ex) {
974            if (debug) {
975                System.out.println(
976                        "UtilityFunctions.loadLibrary(" + library + "): " + ex);
977            }
978            String sharedLibrarySuffix = "dll";
979
980            String osName = StringUtilities.getProperty("os.name");
981
982            if (osName.startsWith("SunOS") || osName.startsWith("Linux")
983                    || osName.startsWith("Mac OS X")) {
984
985                if (osName.startsWith("Mac OS X")) {
986                    // FIXME: we should also look for dylib?
987                    sharedLibrarySuffix = "jnilib";
988                    try {
989                        _loadLibrary(debug, library, "jnilib", ex);
990                    } catch (Exception exMac) {
991                        _loadLibrary(debug, library, "dylib", ex);
992                    }
993                    return;
994                } else {
995                    sharedLibrarySuffix = "so";
996                }
997            }
998            _loadLibrary(debug, library, sharedLibrarySuffix, ex);
999        }
1000    }
1001
1002    /** Apply the specified function to the specified array and return
1003     *  an array with the results. The function must take at least one
1004     *  argument. If the function takes more than one argument, then
1005     *  the specified array should be an array of arrays, where each
1006     *  subarray is a set of arguments.  Since arrays in the expression
1007     *  language can only contain elements of the same type, this method
1008     *  will only work for functions whose arguments are all of the same
1009     *  type.
1010     *  @param function A function with at least one argument.
1011     *  @param array The array to which to apply the function.
1012     *  @return A new array that is the result of applying the function
1013     *   to the specified array.
1014     *  @exception IllegalActionException If the specified function does not
1015     *          take at least one argument, or if an error occurs applying the
1016     *   function, or if the number of arguments does not match the subarray
1017     *   lengths.
1018     */
1019    public static ArrayToken map(FunctionToken function, ArrayToken array)
1020            throws IllegalActionException {
1021        int arity = function.getNumberOfArguments();
1022        Token[] result = new Token[array.length()];
1023
1024        if (arity == 1) {
1025            for (int i = 0; i < array.length(); i++) {
1026                Token arg = array.getElement(i);
1027                Token[] args = new Token[1];
1028                args[0] = arg;
1029                result[i] = function.apply(args);
1030            }
1031        } else if (arity > 1) {
1032            for (int i = 0; i < array.length(); i++) {
1033                Token args = array.getElement(i);
1034
1035                if (!(args instanceof ArrayToken)) {
1036                    throw new IllegalActionException(
1037                            "Invalid arguments to map(): mismatched arity.");
1038                }
1039
1040                Token[] invokeArgs = new Token[arity];
1041                ArrayToken castArgs = (ArrayToken) args;
1042
1043                if (castArgs.length() != arity) {
1044                    throw new IllegalActionException(
1045                            "Invalid arguments to map(): mismatched arity.");
1046                } else {
1047                    for (int j = 0; j < arity; j++) {
1048                        invokeArgs[j] = castArgs.getElement(j);
1049                    }
1050
1051                    result[i] = function.apply(invokeArgs);
1052                }
1053            }
1054        } else {
1055            throw new IllegalActionException(
1056                    "map() can only be used on functions that take at least "
1057                            + "one argument.");
1058        }
1059
1060        return new ArrayToken(result);
1061    }
1062
1063    /** Return the return type of the map method, given the types
1064     *  of the argument.
1065     *  @param functionType The type of the function to map.
1066     *  @param arrayTokenType The type of array to apply the
1067     *  specified function on.
1068     *  @return The type of the result.
1069     *  @exception IllegalActionException If the specified function does not
1070     *   take at least one argument, or if the type signature of the function
1071     *   is not compatible with the array elements, or if the specified
1072     *   function has different argument type.
1073     */
1074    public static Type mapReturnType(Type functionType, Type arrayTokenType)
1075            throws IllegalActionException {
1076        // Note that this method is deliberately not listed in the Expression
1077        // chapter because it is very specialized.
1078        if (functionType instanceof FunctionType) {
1079            FunctionType castFunctionType = (FunctionType) functionType;
1080
1081            if (castFunctionType.getArgCount() == 1) {
1082                Type argType = castFunctionType.getArgType(0);
1083                int comparison = TypeLattice.compare(
1084                        ((ArrayType) arrayTokenType).getElementType(), argType);
1085
1086                if (comparison != CPO.LOWER && comparison != CPO.SAME) {
1087                    throw new IllegalActionException(
1088                            "map(): specified array token is not compatible "
1089                                    + "with function argument type.");
1090                }
1091            } else if (castFunctionType.getArgCount() > 1) {
1092                Type firstArgType = castFunctionType.getArgType(0);
1093                boolean flag = true;
1094
1095                for (int i = 1; i < castFunctionType.getArgCount(); i++) {
1096                    Type argType = castFunctionType.getArgType(i);
1097
1098                    if (argType != firstArgType) {
1099                        i = castFunctionType.getArgCount();
1100                        flag = false;
1101                        throw new IllegalActionException("map() can only work "
1102                                + "for functions whose arguments are all of "
1103                                + "the same type.");
1104                    }
1105                }
1106
1107                if (flag) {
1108                    Type argType = castFunctionType.getArgType(0);
1109                    Type elementType = ((ArrayType) arrayTokenType)
1110                            .getElementType();
1111
1112                    if (!(elementType instanceof ArrayType)) {
1113                        throw new IllegalActionException(
1114                                "map(): specified array token is not "
1115                                        + "compatible with function arity.");
1116                    } else {
1117                        int comparison = TypeLattice.compare(
1118                                ((ArrayType) elementType).getElementType(),
1119                                argType);
1120
1121                        if (comparison != CPO.LOWER && comparison != CPO.SAME) {
1122                            throw new IllegalActionException(
1123                                    "map(): specified array token is not "
1124                                            + "compatible with function "
1125                                            + "argument type.");
1126                        }
1127                    }
1128                }
1129            }
1130
1131            Type resultType = castFunctionType.getReturnType();
1132            return new ArrayType(resultType);
1133        } else {
1134            return BaseType.UNKNOWN;
1135        }
1136    }
1137
1138    /** Return the maximum of two unsigned bytes.
1139     *  @param x An unsigned byte.
1140     *  @param y An unsigned byte.
1141     *  @return The maximum of x and y.
1142     */
1143    public static UnsignedByteToken max(UnsignedByteToken x,
1144            UnsignedByteToken y) {
1145        if (x.intValue() > y.intValue()) {
1146            return x;
1147        } else {
1148            return y;
1149        }
1150    }
1151
1152    /** Return the maximum of the contents of the array.
1153     *  @param array An array of scalar tokens.
1154     *  @return The largest element of the array.
1155     *  @exception IllegalActionException If the array is empty or
1156     *   it contains tokens that are not scalar or it contains complex tokens.
1157     */
1158    public static ScalarToken max(ArrayToken array)
1159            throws IllegalActionException {
1160        if (array.length() == 0
1161                || !BaseType.SCALAR.isCompatible(array.getElementType())) {
1162            throw new IllegalActionException(
1163                    "max function can only be applied to arrays of scalars.");
1164        }
1165
1166        ScalarToken result = (ScalarToken) array.getElement(0);
1167
1168        for (int i = 1; i < array.length(); i++) {
1169            ScalarToken element = (ScalarToken) array.getElement(i);
1170
1171            if (element.isGreaterThan(result).booleanValue()) {
1172                result = element;
1173            }
1174        }
1175
1176        return result;
1177    }
1178
1179    /** Return the (exact) return type of the max function above.
1180     *  If the argument is an array type, then return its element type,
1181     *  otherwise return BaseType.UNKNOWN.
1182     *  @param type The type of the argument to the corresponding function.
1183     *  @return The type of the value returned from the corresponding
1184     *  function.
1185     */
1186    public static Type maxReturnType(Type type) {
1187        // Note that this method is deliberately not listed in the Expression
1188        // chapter because it is very specialized.
1189        if (type instanceof ArrayType) {
1190            ArrayType arrayType = (ArrayType) type;
1191            return arrayType.getElementType();
1192        } else {
1193            return BaseType.UNKNOWN;
1194        }
1195    }
1196
1197    /** Return the minimum of two unsigned bytes.
1198     *  @param x An unsigned byte.
1199     *  @param y An unsigned byte.
1200     *  @return The minimum of x and y.
1201     */
1202    public static UnsignedByteToken min(UnsignedByteToken x,
1203            UnsignedByteToken y) {
1204        if (x.intValue() < y.intValue()) {
1205            return x;
1206        } else {
1207            return y;
1208        }
1209    }
1210
1211    /** Return the minimum of the contents of the array.
1212     *  @param array An array of scalar tokens.
1213     *  @return The largest element of the array.
1214     *  @exception IllegalActionException If the array is empty or
1215     *   it contains tokens that are not scalar or it contains complex tokens.
1216     */
1217    public static ScalarToken min(ArrayToken array)
1218            throws IllegalActionException {
1219        if (array.length() == 0
1220                || !BaseType.SCALAR.isCompatible(array.getElementType())) {
1221            throw new IllegalActionException(
1222                    "min function can only be applied to arrays of scalars.");
1223        }
1224
1225        ScalarToken result = (ScalarToken) array.getElement(0);
1226
1227        for (int i = 1; i < array.length(); i++) {
1228            ScalarToken element = (ScalarToken) array.getElement(i);
1229
1230            if (element.isLessThan(result).booleanValue()) {
1231                result = element;
1232            }
1233        }
1234
1235        return result;
1236    }
1237
1238    /** Return the (exact) return type of the min function above.
1239     *  If the argument is an array type, then return its element type,
1240     *  otherwise return BaseType.UNKNOWN.
1241     *  @param type The type of the argument to the corresponding function.
1242     *  @return The type of the value returned from the corresponding function.
1243     */
1244    public static Type minReturnType(Type type) {
1245        // Note that this method is deliberately not listed in the Expression
1246        // chapter because it is very specialized.
1247        if (type instanceof ArrayType) {
1248            ArrayType arrayType = (ArrayType) type;
1249            return arrayType.getElementType();
1250        } else {
1251            return BaseType.UNKNOWN;
1252        }
1253    }
1254
1255    /** FIXME. Placeholder for a function that will return a model.
1256     */
1257    public static ObjectToken model(String classname)
1258            throws IllegalActionException {
1259        return new ObjectToken(classname);
1260    }
1261
1262    /** Generate a sample from a multivariate Gaussian distribution.
1263     *  @param mean The mean.
1264     *  @param covariance The covariance.
1265     *  @return a sample from a multivariate Gaussian distribution
1266     */
1267    public static ArrayToken multivariateGaussian(ArrayToken mean,
1268            DoubleMatrixToken covariance) throws IllegalActionException {
1269
1270        // Check dimensions.
1271        int N = mean.length(); // size of array
1272        if ((covariance.getColumnCount() != N)
1273                || (covariance.getRowCount() != covariance.getColumnCount())) {
1274            throw new IllegalActionException(
1275                    "Covariance must be a square matrix and its dimension must "
1276                            + "match the mean array length");
1277        } else if (!BaseType.DOUBLE.isCompatible(mean.getElementType())) {
1278            throw new IllegalActionException(
1279                    "Mean vector must consist of scalar type elements");
1280        }
1281        double[] meanArray = new double[N];
1282        for (int i = 0; i < N; i++) {
1283            meanArray[i] = ((DoubleToken) mean.getElement(i)).doubleValue();
1284        }
1285
1286        double[][] sigma = covariance.doubleMatrix();
1287
1288        return multivariateGaussian(meanArray, sigma);
1289    }
1290
1291    /** Generate a sample from a multivariate Gaussian distribution.
1292     *  @param mean The mean array.
1293     *  @param S The covariance matrix, whose length must match
1294     *  that of the mean array and be square.
1295     *  @return a sample from a multivariate Gaussian distribution
1296     */
1297    public static ArrayToken multivariateGaussian(double[] mean, double[][] S)
1298            throws IllegalActionException {
1299
1300        // Check dimensions.
1301        int N = mean.length; // size of array
1302        if ((S.length != N) || (S[0].length != N)) {
1303            throw new IllegalActionException(
1304                    "Covariance must be a square matrix and its dimension must "
1305                            + "match the mean array length");
1306        }
1307        // Check if the covariance matrix is symmetric.
1308        for (int i = 0; i < N; i++) {
1309            for (int j = 0; j < i; j++) {
1310                if (Math.abs(S[i][j] - S[j][i]) > 1E-6) {
1311                    throw new IllegalActionException(
1312                            "Covariance must be a symmetric matrix.");
1313                }
1314            }
1315        }
1316
1317        double[][] L = choleskyDecomposition(S);
1318
1319        // Draw uncorrelated samples from a standard Gaussian.
1320        ArrayToken uncorrelated = gaussian(0, 1, N);
1321        Token[] uncorrelatedTokens = uncorrelated.arrayValue();
1322        Token[] correlatedTokens = new Token[N];
1323        double[] correlatedSamples = new double[N];
1324        for (int i = 0; i < N; i++) {
1325            for (int j = 0; j < N; j++) {
1326                double uncorr = ((DoubleToken) uncorrelatedTokens[j])
1327                        .doubleValue();
1328                correlatedSamples[i] += L[i][j] * uncorr;
1329            }
1330            correlatedTokens[i] = new DoubleToken(
1331                    correlatedSamples[i] + mean[i]);
1332        }
1333        return new ArrayToken(BaseType.DOUBLE, correlatedTokens);
1334    }
1335
1336    /** Parse the string provided and return the result wrapped in a token.
1337     *  @param moml The MoML string.
1338     *  @return The result of parsing the MoML.
1339     *  @exception Exception If the MoML is invalid or the results is not an
1340     *   instance of Entity.
1341     */
1342    public static ActorToken parseMoML(String moml) throws Exception {
1343        return MoMLUtilities.parseMoML(moml);
1344    }
1345
1346    /** Get the specified property from the environment. An empty string
1347     *  is returned if the argument environment variable does not exist.
1348     *  See the javadoc page for java.util.System.getProperties() for
1349     *  a list of system properties.  Example properties include:
1350     *  <dl>
1351     *  <dt> "java.version"
1352     *  <dd> the version of the JDK.
1353     *  <dt> "ptolemy.ptII.dir"
1354     *  <dd> The value of $PTII, which is the name of the directory in
1355     *       which Ptolemy II is installed.
1356     *  <dt> "ptolemy.ptII.dirAsURL"
1357     *  <dd> The value of $PTII as a URL, which is the name of the directory in
1358     *       which Ptolemy II is installed.
1359     *  <dt> "user.dir"
1360     *  <dd> The canonical path name to the current working directory.
1361     *  </dl>
1362     *
1363     *  @param propertyName The name of property.
1364     *  @return A token containing the string value of the property.
1365     *  @see ptolemy.util.StringUtilities#getProperty(String)
1366     */
1367    public static StringToken property(String propertyName) {
1368        return new StringToken(StringUtilities.getProperty(propertyName));
1369    }
1370
1371    /** Return an array of IID random numbers with value greater than
1372     *  or equal to 0.0 and less than 1.0.
1373     *  @param length The length of the array.
1374     *  @return An array of doubles with IID random variables.
1375     */
1376    public static ArrayToken random(int length) {
1377        DoubleToken[] result = new DoubleToken[length];
1378
1379        for (int i = 0; i < length; i++) {
1380            result[i] = new DoubleToken(Math.random());
1381        }
1382
1383        try {
1384            return new ArrayToken(BaseType.DOUBLE, result);
1385        } catch (IllegalActionException ex) {
1386            // This should not happen since result should not be null.
1387            throw new InternalErrorException(null, ex,
1388                    "UtilityFunction.random: "
1389                            + "Cannot create the array that contains "
1390                            + "random numbers.");
1391        }
1392    }
1393
1394    /** Return a matrix of IID random numbers with value greater than
1395     *  or equal to 0.0 and less than 1.0.
1396     *  @param rows The number of rows.
1397     *  @param columns The number of columns.
1398     *  @return A matrix of IID random variables.
1399     */
1400    public static DoubleMatrixToken random(int rows, int columns) {
1401        double[][] result = new double[rows][columns];
1402
1403        for (int i = 0; i < rows; i++) {
1404            for (int j = 0; j < columns; j++) {
1405                result[i][j] = Math.random();
1406            }
1407        }
1408
1409        try {
1410            return new DoubleMatrixToken(result);
1411        } catch (IllegalActionException ex) {
1412            // This should not happen since result should not be null.
1413            throw new InternalErrorException(null, ex,
1414                    "UtilityFunction.random: "
1415                            + "Cannot create the DoubleMatrixToken that contains "
1416                            + "random numbers.");
1417        }
1418    }
1419
1420    /** Return the (exact) return type of the random function above.
1421     *  If the argument is BaseType.INT, then return and ArrayType of
1422     *  BaseType.DOUBLE, otherwise return BaseType.UNKNOWN.
1423     *  @param type The type of the argument to the corresponding function.
1424     *  @return The type of the value returned from the corresponding function.
1425     */
1426    public static Type randomReturnType(Type type) {
1427        // Note that this method is deliberately not listed in the Expression
1428        // chapter because it is very specialized.
1429        if (type.equals(BaseType.INT)) {
1430            return new ArrayType(BaseType.DOUBLE);
1431        } else {
1432            return BaseType.UNKNOWN;
1433        }
1434    }
1435
1436    /** Get the string text contained in the specified file. The argument
1437     *  is first interpreted using findFile(), so file names relative to
1438     *  the current working directory, the user's home directory, or the
1439     *  classpath are understood. If the file contains text that is a
1440     *  valid expression in the expression language, then that text can
1441     *  interpreted using the eval() function in
1442     *  ptolemy.data.expr.ASTPtFunctionApplicationNode.
1443     *  For example: <code>eval(readFile("<i>filename</i>"))</code><p>
1444     *  Note that readFile() does not work inside applets, use
1445     *  {@link #readResource(String)} instead.
1446     *
1447     *  @param filename The name of the file to read from.
1448     *  @return A StringToken containing the text contained in
1449     *   the specified file.
1450     *  @exception IllegalActionException If the file cannot be opened.
1451     *  @see ptolemy.data.expr.ASTPtFunctionApplicationNode
1452     *  @see #readResource(String)
1453     */
1454    public static StringToken readFile(String filename)
1455            throws IllegalActionException {
1456        File file = new File(findFile(filename));
1457
1458        //System.out.println("Trying to open file: " + file.toString());
1459        BufferedReader fin = null;
1460        String line;
1461        StringBuffer result = new StringBuffer("");
1462        String newline = System.getProperty("line.separator");
1463
1464        try {
1465            fin = new BufferedReader(new java.io.InputStreamReader(
1466                    new java.io.FileInputStream(file),
1467                    java.nio.charset.Charset.defaultCharset()));
1468
1469            while (true) {
1470                try {
1471                    line = fin.readLine();
1472                } catch (IOException e) {
1473                    break;
1474                }
1475
1476                if (line == null) {
1477                    break;
1478                }
1479
1480                result.append(line + newline);
1481            }
1482        } catch (FileNotFoundException ex) {
1483            throw new IllegalActionException(null, ex, "File not found");
1484        } finally {
1485            if (fin != null) {
1486                try {
1487                    fin.close();
1488                } catch (IOException ex) {
1489                    throw new IllegalActionException(null, ex,
1490                            "Problem closing '" + file + "'");
1491                }
1492            }
1493        }
1494
1495        return new StringToken(result.toString());
1496    }
1497
1498    /** Read a file that contains a matrix of reals in Matlab notation.
1499     *
1500     *  @param filename The filename.
1501     *  @return The matrix defined in the file.
1502     *  @exception IllegalActionException If the file cannot be opened.
1503     *  @deprecated Use eval(readFile()) instead.
1504     */
1505    @Deprecated
1506    public static DoubleMatrixToken readMatrix(String filename)
1507            throws IllegalActionException {
1508        // Note that this method is deliberately not listed in the Expression
1509        // chapter because it is deprecated.
1510
1511        DoubleMatrixToken returnMatrix = null;
1512
1513        File file = new File(findFile(filename));
1514        if (!file.exists()) {
1515            throw new IllegalActionException(
1516                    "readMatrix: File " + filename + " not found.");
1517        }
1518
1519        // Vector containing the matrix
1520        Vector k = null;
1521
1522        // Parameters for the Matrix
1523        int row = -1;
1524        int column = -1;
1525
1526        int rowPosition = 0;
1527        int columnPosition = 0;
1528        double[][] mtr = null;
1529
1530        // At one point MatrixParser.ReInit() took a Reader.  With
1531        // JavaCC-5.0, it takes an InputStream.
1532        // FileReader fin = null;
1533        FileInputStream fin = null;
1534        try {
1535            try {
1536                // Open the matrix file.
1537                // fin = new FileReader(file);
1538                fin = new FileInputStream(file);
1539            } catch (FileNotFoundException e) {
1540                throw new IllegalActionException("readMatrix: file \""
1541                        + file.getName() + "\" not found");
1542            }
1543
1544            // Read the file and convert it into a matrix.
1545            if (_matrixParser == null) {
1546                _matrixParser = new MatrixParser(System.in);
1547            }
1548
1549            //MatrixParser.ReInit(fin);
1550            _matrixParser.ReInit(fin);
1551
1552            k = _matrixParser.readMatrix();
1553
1554            if (column == -1) {
1555                // The column size of the matrix
1556                column = k.size();
1557            }
1558
1559            Iterator i = k.iterator();
1560
1561            while (i.hasNext()) {
1562                Vector l = (Vector) i.next();
1563
1564                if (row == -1) {
1565                    // the row size.
1566                    row = l.size();
1567
1568                    // create a new matrix definition
1569                    mtr = new double[column][row];
1570                } else {
1571                    if (row != l.size()) {
1572                        throw new IllegalActionException(" The Row"
1573                                + " size needs to be the same for all"
1574                                + " rows");
1575                    }
1576                }
1577
1578                Iterator j = l.iterator();
1579
1580                while (j.hasNext()) {
1581                    Double s = (Double) j.next();
1582                    mtr[columnPosition][rowPosition++] = s.doubleValue();
1583                }
1584
1585                rowPosition = 0;
1586                columnPosition++;
1587            }
1588
1589            // Vectors have now become obsolete, data is stored
1590            // in double[][].
1591            k.clear();
1592            returnMatrix = new DoubleMatrixToken(mtr);
1593        } finally {
1594            if (fin != null) {
1595                try {
1596                    fin.close();
1597                } catch (IOException ex) {
1598                    throw new IllegalActionException(null, ex,
1599                            "Problem closing '" + file + "'");
1600                }
1601            }
1602        }
1603        return returnMatrix;
1604    }
1605
1606    /** Get the string text contained in the specified resource, which
1607     *  is a file that is specified relative to the Java classpath.
1608     *  Resource strings look like filenames without a leading slash.
1609     *  If the file contains text that is a
1610     *  valid expression in the expression language, then that text can
1611     *  interpreted using the eval() function in
1612     *  ptolemy.data.expr.ASTPtFunctionApplicationNode.
1613     *  For example: <code>eval(readFile("<i>filename</i>"))</code><p>
1614     *
1615     *  @param name The name of the resource to read from.
1616     *  @return A StringToken containing the text contained in
1617     *   the specified resource.
1618     *  @exception IllegalActionException If the resource cannot be opened.
1619     *  @see ptolemy.data.expr.ASTPtFunctionApplicationNode
1620     *  @see #readFile(String)
1621     */
1622    public static StringToken readResource(String name)
1623            throws IllegalActionException {
1624        URL url = ClassLoader.getSystemResource(name);
1625        StringBuffer result = new StringBuffer("");
1626        BufferedReader fin = null;
1627
1628        try {
1629            InputStream stream = url.openStream();
1630            String line;
1631            String newline = System.getProperty("line.separator");
1632            fin = new BufferedReader(new InputStreamReader(stream));
1633
1634            while (true) {
1635                try {
1636                    line = fin.readLine();
1637                } catch (IOException e) {
1638                    break;
1639                }
1640
1641                if (line == null) {
1642                    break;
1643                }
1644
1645                result.append(line + newline);
1646            }
1647        } catch (IOException ex) {
1648            throw new IllegalActionException(null, ex, "File not found");
1649        } finally {
1650            if (fin != null) {
1651                try {
1652                    fin.close();
1653                } catch (IOException ex) {
1654                    throw new IllegalActionException(null, ex,
1655                            "Failed to close '" + name + "'");
1656                }
1657            }
1658        }
1659
1660        return new StringToken(result.toString());
1661    }
1662
1663    /** Create an array that contains the specified element
1664     *  repeated the specified number of times.
1665     *  @param numberOfTimes The number of times to repeat the element.
1666     *  @param element The element to repeat.
1667     *  @return A new array containing the specified element repeated the
1668     *   specified number of times.
1669     */
1670    public static ArrayToken repeat(IntToken numberOfTimes, Token element) {
1671        int length = numberOfTimes.intValue();
1672        Token[] result = new Token[length];
1673
1674        for (int i = 0; i < length; i++) {
1675            result[i] = element;
1676        }
1677
1678        ArrayToken arrayToken;
1679
1680        try {
1681            arrayToken = new ArrayToken(element.getType(), result);
1682        } catch (IllegalActionException ex) {
1683            // This should not happen since the elements of the array always
1684            // have the same type.
1685            throw new InternalErrorException(null, ex,
1686                    "UtilityFunctions.repeat: "
1687                            + "Cannot construct an ArrayToken.");
1688        } catch (IllegalArgumentException ex2) {
1689            // This should not happen since the elements of the array always
1690            // have the same type.
1691            throw new InternalErrorException(null, ex2,
1692                    "UtilityFunctions.repeat: "
1693                            + "Cannot construct an ArrayToken.");
1694        }
1695
1696        return arrayToken;
1697    }
1698
1699    /** Return the (exact) return type of the repeat function above.
1700     *  This function always returns an ArrayType whose element type
1701     *  is the second argument.
1702     *  @param type1 The type of the first argument to the
1703     *  corresponding function.
1704     *  @param type2 The type of the second argument to the
1705     *  corresponding function.
1706     *  @return The type of the value returned from the corresponding function.
1707     */
1708    public static Type repeatReturnType(Type type1, Type type2) {
1709        // Note that this method is deliberately not listed in the Expression
1710        // chapter because it is very specialized.
1711        return new ArrayType(type2);
1712    }
1713
1714    /** Return a new array that is the sorted contents of a specified
1715     *  array, in ascending order. The specified array can contain
1716     *  scalar tokens (except complex) or string tokens. If the
1717     *  specified array is empty, then it is returned.
1718     *  @param array An array of scalar tokens.
1719     *  @return A new sorted array.
1720     *  @exception IllegalActionException If the array contains
1721     *   tokens that are complex or are not scalar or string tokens.
1722     */
1723    public static ArrayToken sort(ArrayToken array)
1724            throws IllegalActionException {
1725        if (array.length() == 0) {
1726            return array;
1727        }
1728
1729        // NOTE: The following method returns a copy, so we can modify it.
1730        Token[] value = array.arrayValue();
1731        Arrays.sort(value, _ASCENDING);
1732        return new ArrayToken(array.getElementType(), value);
1733    }
1734
1735    /** Return the (exact) return type of the sort function above.
1736     *  If the argument is an array type with elements that are strings
1737     *  or scalars other than complex, then return the argument;
1738     *  otherwise return BaseType.UNKNOWN.
1739     *  @param type The type of the argument to the corresponding function.
1740     *  @return The type of the value returned from the corresponding function.
1741     */
1742    public static Type sortReturnType(Type type) {
1743        // Note that this method is deliberately not listed in the Expression
1744        // chapter because it is very specialized.
1745
1746        // NOTE: This logic indicates which tokens are comparable
1747        // by the TokenComparator inner class. If that class is changed,
1748        // then this logic needs to be changed too.
1749        if (type instanceof ArrayType) {
1750            ArrayType arrayType = (ArrayType) type;
1751            Type elementType = arrayType.getElementType();
1752
1753            if (elementType.equals(BaseType.COMPLEX)) {
1754                return BaseType.UNKNOWN;
1755            } else if (elementType.equals(BaseType.STRING)
1756                    || BaseType.SCALAR.isCompatible(elementType)) {
1757                return type;
1758            }
1759        }
1760
1761        return BaseType.UNKNOWN;
1762    }
1763
1764    /** Return a new array that is the sorted contents of a specified
1765     *  array, in ascending order. The specified array can contain
1766     *  scalar tokens (except complex) or string tokens. If the
1767     *  specified array is empty, then it is returned.
1768     *  This method is identical to sort(), and is provided only
1769     *  for naming uniformity.
1770     *  @see #sort(ArrayToken)
1771     *  @see #sortDescending(ArrayToken)
1772     *  @param array An array of scalar tokens.
1773     *  @return A new sorted array.
1774     *  @exception IllegalActionException If the array contains
1775     *   tokens that are complex or are not scalar or string tokens.
1776     */
1777    public static ArrayToken sortAscending(ArrayToken array)
1778            throws IllegalActionException {
1779        return sort(array);
1780    }
1781
1782    /** Return the (exact) return type of the sortAscending function above.
1783     *  If the argument is an array type with elements that are strings
1784     *  or scalars other than complex, then return the argument;
1785     *  otherwise return BaseType.UNKNOWN.
1786     *  @param type The type of the argument to the corresponding function.
1787     *  @return The type of the value returned from the corresponding function.
1788     */
1789    public static Type sortAscendingReturnType(Type type) {
1790        // Note that this method is deliberately not listed in the Expression
1791        // chapter because it is very specialized.
1792        return sortReturnType(type);
1793    }
1794
1795    /** Return a new array that is the sorted contents of a specified
1796     *  array, in descending order. The specified array can contain
1797     *  scalar tokens (except complex) or string tokens. If the
1798     *  specified array is empty, then it is returned.
1799     *  @param array An array of scalar tokens.
1800     *  @return A new sorted array.
1801     *  @exception IllegalActionException If the array contains
1802     *   tokens that are complex or are not scalar or string tokens.
1803     */
1804    public static ArrayToken sortDescending(ArrayToken array)
1805            throws IllegalActionException {
1806        if (array.length() == 0) {
1807            return array;
1808        }
1809
1810        // NOTE: The following method returns a copy, so we can modify it.
1811        Token[] value = array.arrayValue();
1812        Arrays.sort(value, _DESCENDING);
1813        return new ArrayToken(array.getElementType(), value);
1814    }
1815
1816    /** Return the (exact) return type of the sortDescending function above.
1817     *  If the argument is an array type with elements that are strings
1818     *  or scalars other than complex, then return the argument;
1819     *  otherwise return BaseType.UNKNOWN.
1820     *  @param type The type of the argument to the corresponding function.
1821     *  @return The type of the value returned from the corresponding function.
1822     */
1823    public static Type sortDescendingReturnType(Type type) {
1824        // Note that this method is deliberately not listed in the Expression
1825        // chapter because it is very specialized.
1826        return sortReturnType(type);
1827    }
1828
1829    /** Return the contiguous subarray of the specified array
1830     *  starting at the specified index and of the specified length.
1831     *  If the specified index is out of range,
1832     *  or if the specified length extends beyond the end of the array,
1833     *  then return an empty array with the same type as the specified array.
1834     *  @param array The array.
1835     *  @param index The index of the beginning of the subarray.
1836     *  @param count The length of the subarray.
1837     *  @return The extracted subarray.
1838     *  @exception IllegalActionException If the index argument is less
1839     *   than zero.
1840     *  @since Ptolemy II 4.1
1841     */
1842    public static ArrayToken subarray(ArrayToken array, IntToken index,
1843            IntToken count) throws IllegalActionException {
1844        return array.subarray(index.intValue(), count.intValue());
1845    }
1846
1847    /** Return the return type of the subarray() method, which is the
1848     *  same as the array type.
1849     *  @param arrayType The type of the array argument.
1850     *  @param indexType The type of the index argument.
1851     *  @param countType The type of the count argument.
1852     *  @return The extracted subarray.
1853     *  @since Ptolemy II 4.1
1854     */
1855    public static Type subarrayReturnType(Type arrayType, Type indexType,
1856            Type countType) {
1857        // Note that this method is deliberately not listed in the Expression
1858        // chapter because it is very specialized.
1859        return arrayType;
1860    }
1861
1862    /** Return the sum of the elements in the specified array.
1863     *  This method is polymorphic in that it can sum any array
1864     *  whose elements support addition.
1865     *  There is special support for strings, which are added
1866     *  not via their add method (which concatenates two strings),
1867     *  but are accumulated in a StringBuffer, which is much more
1868     *  efficient.
1869     *  @param array An array.
1870     *  @return The sum of the elements of the array.
1871     *  @exception IllegalActionException If the length of the
1872     * array is zero, or if the array elements do not support
1873     * addition.
1874     */
1875    public static final Token sum(ArrayToken array)
1876            throws IllegalActionException {
1877        if (array == null || array.length() < 1) {
1878            throw new IllegalActionException(
1879                    "sum() function cannot be applied to an empty array");
1880        }
1881
1882        if (array.getElement(0) instanceof StringToken) {
1883            int length = 0;
1884
1885            for (int i = 0; i < array.length(); i++) {
1886                length += ((StringToken) array.getElement(i)).stringValue()
1887                        .length();
1888            }
1889
1890            StringBuffer buffer = new StringBuffer(length);
1891
1892            for (int i = 0; i < array.length(); i++) {
1893                buffer.append(
1894                        ((StringToken) array.getElement(i)).stringValue());
1895            }
1896
1897            return new StringToken(buffer.toString());
1898        }
1899
1900        Token result = array.getElement(0);
1901
1902        for (int i = 1; i < array.length(); i++) {
1903            result = result.add(array.getElement(i));
1904        }
1905
1906        return result;
1907    }
1908
1909    /** Return the (exact) return type of the sum function above.
1910     *  If the argument is an array type, then return its element type,
1911     *  otherwise return BaseType.UNKNOWN.
1912     *  @param type The type of the argument to the corresponding function.
1913     *  @return The type of the value returned from the corresponding function.
1914     */
1915    public static Type sumReturnType(Type type) {
1916        // Note that this method is deliberately not listed in the Expression
1917        // chapter because it is very specialized.
1918        if (type instanceof ArrayType) {
1919            ArrayType arrayType = (ArrayType) type;
1920            return arrayType.getElementType();
1921        } else {
1922            return BaseType.UNKNOWN;
1923        }
1924    }
1925
1926    /** Return the approximate number of bytes used by current objects
1927     *  and available for future object allocation.
1928     *  @return The total number of bytes used by the JVM.
1929     *  @see #freeMemory()
1930     */
1931    public static LongToken totalMemory() {
1932        return new LongToken(Runtime.getRuntime().totalMemory());
1933    }
1934
1935    /** Evaluate the given string as an expression in the expression
1936     *  language.  Instead of returning the resulting value, return a
1937     *  trace of the evaluation, including such useful information as
1938     *  what registered method is actually invoked.
1939     *  @param string The string to be parsed and evaluated.
1940     *  @return A string representing an evaluation trace.
1941     */
1942    public static String traceEvaluation(String string)
1943            throws IllegalActionException {
1944        PtParser parser = new PtParser();
1945        ASTPtRootNode parseTree = parser.generateParseTree(string);
1946        ParseTreeEvaluator evaluator = new ParseTreeEvaluator();
1947        return evaluator.traceParseTreeEvaluation(parseTree, null);
1948    }
1949
1950    /** Return true if the first argument is close in value to the second,
1951     *  where "close" means that it is within the distance given by the
1952     *  third argument. Exactly what this means depends on the data type.
1953     *  This method uses the isCloseTo() method of the first token.
1954     *  @param token1 The first token.
1955     *  @param token2 The second token.
1956     *         @param distance The distance criterion.
1957     *  @return a true-valued token if the first two arguments are close
1958     *   enough.
1959     *  @exception IllegalActionException If the first two arguments cannot
1960     *   be compared.
1961     */
1962    public static BooleanToken within(Token token1, Token token2,
1963            double distance) throws IllegalActionException {
1964        return token1.isCloseTo(token2, distance);
1965    }
1966
1967    /** Query the user for a yes-no answer and return a boolean.
1968     *  This function will open a dialog if a GUI is available,
1969     *  and otherwise will use standard input and output.
1970     */
1971    public static BooleanToken yesNoQuestion(StringToken prompt) {
1972        if (MessageHandler.yesNoQuestion(prompt.stringValue())) {
1973            return BooleanToken.TRUE;
1974        } else {
1975            return BooleanToken.FALSE;
1976        }
1977    }
1978
1979    /** Return a double zero matrix with the given number of rows and
1980     *  columns.
1981     *  @return The zero matrix with the given number of rows and
1982     *   columns.
1983     *  @deprecated Use zeroMatrixDouble instead.
1984     */
1985    @Deprecated
1986    public static DoubleMatrixToken zeroMatrix(int rows, int columns) {
1987        return zeroMatrixDouble(rows, columns);
1988    }
1989
1990    /** Return a complex zero matrix with the given number of rows and
1991     *  columns.
1992     *  @return The zero matrix with the given number of rows and
1993     *   columns.
1994     */
1995    public static ComplexMatrixToken zeroMatrixComplex(int rows, int columns) {
1996        try {
1997            return new ComplexMatrixToken(
1998                    ComplexMatrixMath.zero(rows, columns));
1999        } catch (IllegalActionException ex) {
2000            throw new InternalErrorException(null, ex,
2001                    "UtilityFunctions.zeroMatrixComplex: "
2002                            + "Cannot create a ComplexMatrixToken.");
2003        }
2004    }
2005
2006    /** Return a double zero matrix with the given number of rows and
2007     *  columns.
2008     *  @return The zero matrix with the given number of rows and
2009     *   columns.
2010     */
2011    public static DoubleMatrixToken zeroMatrixDouble(int rows, int columns) {
2012        double[][] mtr = new double[rows][columns];
2013        DoubleMatrixToken result = null;
2014
2015        try {
2016            result = new DoubleMatrixToken(mtr, MatrixToken.DO_NOT_COPY);
2017        } catch (IllegalActionException ex) {
2018            throw new InternalErrorException(null, ex,
2019                    "UtilityFunctions.zeroMatrixDouble: "
2020                            + "Cannot create a DoubleMatrixToken.");
2021        }
2022
2023        return result;
2024    }
2025
2026    /** Return a int zero matrix with the given number of rows and
2027     *  columns.
2028     *  @return The zero matrix with the given number of rows and
2029     *   columns.
2030     */
2031    public static IntMatrixToken zeroMatrixInt(int rows, int columns) {
2032        int[][] mtr = new int[rows][columns];
2033        IntMatrixToken result = null;
2034
2035        try {
2036            result = new IntMatrixToken(mtr, MatrixToken.DO_NOT_COPY);
2037        } catch (IllegalActionException ex) {
2038            throw new InternalErrorException(null, ex,
2039                    "UtilityFunctions.zeroMatrixInt: "
2040                            + "Cannot create a IntMatrixToken.");
2041        }
2042
2043        return result;
2044    }
2045
2046    /** Return a long zero matrix with the given number of rows and
2047     *  columns.
2048     *  @return The zero matrix with the given number of rows and
2049     *   columns.
2050     */
2051    public static LongMatrixToken zeroMatrixLong(int rows, int columns) {
2052        long[][] mtr = new long[rows][columns];
2053        LongMatrixToken result = null;
2054
2055        try {
2056            result = new LongMatrixToken(mtr, MatrixToken.DO_NOT_COPY);
2057        } catch (IllegalActionException ex) {
2058            throw new InternalErrorException(null, ex,
2059                    "UtilityFunctions.zeroMatrixLong: "
2060                            + "Cannot create a LongMatrixToken.");
2061        }
2062
2063        return result;
2064    }
2065
2066    ///////////////////////////////////////////////////////////////////
2067    ////                         private methods                   ////
2068
2069    /** Load a shared library.
2070     *  @param library the name of the library to be loaded.  The name
2071     *  should not include the platform dependent suffix.
2072     *  @param sharedLibrarySuffix The suffix for the shared library, without the dot.
2073     *  Under Windows, this would be ".dll", under Mac OS X, "jnilib" or "dylib",
2074     *  under Solaris or Linux, ".o".
2075     *  @param throwable The throwable that occurred when System.load() or System.loadLibrary()
2076     *  was called.
2077     */
2078    private static void _loadLibrary(final boolean debug, String library,
2079            String sharedLibrarySuffix, Throwable throwable) {
2080        // We have a separate method here so that we can call it with
2081        // jnilib and then with dylib because under Web Start on Mac OS X 10.5
2082        // with Java 10.5, shared libraries need to have a .jnilib extension.  Sigh.
2083
2084        // The name of the library (everything after the last /)
2085        String shortLibraryName = null;
2086
2087        String osName = StringUtilities.getProperty("os.name");
2088        if (debug) {
2089            System.out.println("UtilityFunctions._loadLibrary(" + library + ", "
2090                    + sharedLibrarySuffix + "): osName: " + osName);
2091        }
2092        if (osName.startsWith("SunOS") || osName.startsWith("Linux")
2093                || osName.startsWith("Mac OS X")) {
2094            // Under Solaris, libraries start with lib, so
2095            // we find the last /, and if the next chars are not "lib"
2096            // then we insert "lib".
2097
2098            int index = library.lastIndexOf("/");
2099
2100            if (index == -1) {
2101                if (!library.startsWith("lib")) {
2102                    library = "lib" + library;
2103                }
2104
2105                shortLibraryName = library;
2106            } else {
2107                if (!library.substring(index, index + 4).equals("/lib")) {
2108                    if (osName.startsWith("Linux")) {
2109                        library = library.substring(index + 1);
2110                        shortLibraryName = library;
2111                    } else {
2112                        // Under SunOS, we add lib to
2113                        // the path.  If we don't do this on the Mac,
2114                        // then libptymatlab.dynlib will not be found.
2115                        shortLibraryName = "/lib"
2116                                + library.substring(index + 1);
2117                        library = library.substring(0, index)
2118                                + shortLibraryName;
2119                    }
2120                } else {
2121                    // Needed to find $PTII/lib/libJNIFMU.jnilib if
2122                    // actor/lib/fmi/jni/libJNIFMU.jnilib does not exist
2123                    // $PTII/bin/vergil $PTII/ptolemy/actor/lib/fmi/jni/test/auto/EightFourInFourOutsJNI.xml
2124                    library = library.substring(index + 1);
2125                    shortLibraryName = library;
2126                }
2127            }
2128        } else {
2129            // Windows
2130            int index = library.lastIndexOf("/");
2131
2132            if (index != -1) {
2133                // Everything after the trailing /
2134                shortLibraryName = library.substring(index + 1);
2135            }
2136        }
2137
2138        String libraryWithSuffix = library + "." + sharedLibrarySuffix;
2139        String libraryPath = UtilityFunctions.findFile(libraryWithSuffix);
2140        boolean libraryPathExists = false;
2141
2142        try {
2143            // It turns out that when looking for libraries under
2144            // InstallAnywhere, findFile() can somehow end up returning
2145            // a bogus value in C:/Documents and Settings
2146            File libraryPathFile = new File(libraryPath);
2147            if (libraryPathFile.exists()) {
2148                libraryPathExists = true;
2149            }
2150        } catch (Throwable throwable2) {
2151            // Ignore, the file can't be found
2152        }
2153
2154        if (debug) {
2155            System.out.println("UtilityFunctions._loadLibrary(" + library + ", "
2156                    + sharedLibrarySuffix + "): libraryWithSuffix: "
2157                    + libraryWithSuffix + " libraryPathExists"
2158                    + libraryPathExists);
2159        }
2160
2161        if (libraryPath.equals(libraryWithSuffix) || !libraryPathExists) {
2162            if (debug) {
2163                System.out.println("UtilityFunctions._loadLibrary(" + library
2164                        + ", " + sharedLibrarySuffix
2165                        + "): findFile did not find the library try the short name: "
2166                        + shortLibraryName);
2167            }
2168
2169            try {
2170                // findFile did not find the library, so we try using
2171                // just the short library name.  This is necessary
2172                // for Web Start because Web Start requires that
2173                // native code be in the top level of a jar file
2174                // that is specially marked.  Matlab under Web Start
2175                // requires this.
2176                if (shortLibraryName != null) {
2177                    if (debug) {
2178                        System.out.println("UtilityFunctions._loadLibrary("
2179                                + library + ", " + sharedLibrarySuffix
2180                                + "): System.loadLibrary(" + shortLibraryName
2181                                + ")");
2182                    }
2183                    System.loadLibrary(shortLibraryName);
2184                    if (debug) {
2185                        System.out.println("UtilityFunctions._loadLibrary("
2186                                + library + ", " + sharedLibrarySuffix
2187                                + "): loaded " + shortLibraryName);
2188                    }
2189                }
2190                return;
2191            } catch (UnsatisfiedLinkError ex2) {
2192                String shortLibraryName2 = shortLibraryName;
2193                try {
2194                    int index = shortLibraryName2.lastIndexOf("/");
2195                    if (index != -1) {
2196                        shortLibraryName2 = shortLibraryName2
2197                                .substring(index + 1);
2198                    }
2199                    index = shortLibraryName2.lastIndexOf("lib");
2200                    if (index != -1) {
2201                        shortLibraryName2 = shortLibraryName2
2202                                .substring(index + 3);
2203                    }
2204                    if (debug) {
2205                        System.out.println("UtilityFunctions._loadLibrary("
2206                                + library + ", " + sharedLibrarySuffix
2207                                + "): System.loadLibrary(" + shortLibraryName2
2208                                + ")");
2209                    }
2210                    System.loadLibrary(shortLibraryName2);
2211                    return;
2212                } catch (UnsatisfiedLinkError ex3) {
2213                    // UnsatisfiedLinkError does not have a (String, Throwable)
2214                    // constructor, so we call initCause().
2215                    String userDir = "<<user.dir unknown>>";
2216                    try {
2217                        userDir = System.getProperty("user.dir");
2218                    } catch (Throwable throwable3) {
2219                        // Ignore.
2220                    }
2221
2222                    String userHome = "<<user.home unknown>>";
2223                    try {
2224                        userHome = System.getProperty("user.home");
2225                    } catch (Throwable throwable3) {
2226                        // Ignore.
2227                    }
2228
2229                    String javaLibraryPath = "<<java.library.path unknown>>";
2230                    try {
2231                        javaLibraryPath = System
2232                                .getProperty("java.library.path");
2233                    } catch (Throwable throwable3) {
2234                        // Ignore.
2235                    }
2236
2237                    String classpath = "<<classpath unknown>>";
2238                    try {
2239                        classpath = System.getProperty("java.class.path");
2240                    } catch (Throwable throwable3) {
2241                        // Ignore.
2242                    }
2243
2244                    Error error = new UnsatisfiedLinkError("Did not find '"
2245                            + library + "' in path, searched " + "user.home ("
2246                            + userDir + ") user.dir (" + userHome
2247                            + ") and the classpath for '" + libraryPath
2248                            + "', but that was not found either.\n"
2249                            + "The java.library.path property was: "
2250                            + javaLibraryPath + "\nThe Java classpath was: "
2251                            + classpath + "\nIn addition, loadLibrary(\""
2252                            + shortLibraryName
2253                            + "\") was called, the exception for the "
2254                            + "loadLibrary() call was: " + ex2
2255                            + (shortLibraryName.equals(shortLibraryName2) ? ""
2256                                    : "\nAlso, loadlibrary(\""
2257                                            + shortLibraryName2
2258                                            + "\") was called, "
2259                                            + "the exception  for the loadLibrary call was: "
2260                                            + ex3));
2261                    error.initCause(throwable);
2262                    error.printStackTrace();
2263                    throw error;
2264                }
2265            }
2266        }
2267
2268        // System.loadLibrary() does not handle pathnames with separators.
2269        // If we get to here and load a library that includes references
2270        // to libraries not in the PATH or LD_LIBRARY_PATH, then we will
2271        // get and UnsatisfiedLinkError on the file we depend on.
2272        // For example, if liba.so uses libb.so and we call this
2273        // method on a, then libb.so will not be found.
2274        if (debug) {
2275            System.out.println("UtilityFunctions._loadLibrary(" + library + ", "
2276                    + sharedLibrarySuffix + "): System.load(" + libraryPath
2277                    + ")");
2278        }
2279        System.load(libraryPath);
2280    }
2281
2282    ///////////////////////////////////////////////////////////////////
2283    ////                         private variables                 ////
2284
2285    /** Comparator for ascending sort. */
2286    private static TokenComparator _ASCENDING = new TokenComparator(true);
2287
2288    /** Comparator for descending sort. */
2289    private static TokenComparator _DESCENDING = new TokenComparator(false);
2290
2291    /** The Matrix Parser. The Matrix parser is recreated for the standard
2292     *  in. However, we use ReInit for the specific matrix files.
2293     */
2294    private static MatrixParser _matrixParser;
2295
2296    /** The random number generator.
2297     */
2298    private static Random _random;
2299
2300    ///////////////////////////////////////////////////////////////////
2301    ////                         inner classes                     ////
2302
2303    /** Comparator for tokens.
2304     */
2305    private static class TokenComparator implements Comparator {
2306        /** Construct a new comparator.
2307         *  @param ascending True for ascending comparisons.
2308         */
2309        public TokenComparator(boolean ascending) {
2310            super();
2311            _ascending = ascending;
2312        }
2313
2314        /** Return -1, 0, or 1 depending on whether the first
2315         *  argument is less than, equal to, or greater
2316         *  than the second. If the argument to the constructor
2317         *  was false, then return the converse (to get descending
2318         *  sorts). The arguments are expected to be instances of
2319         *  Token, and in particular, to be instances of ScalarToken
2320         *  (except ComplexToken) or StringToken; otherwise a
2321         *  ClassCastException will be thrown.
2322         *  @param arg0 The first argument to compare.
2323         *  @param arg1 The second argument to compare.
2324         *  @return The result of comparison.
2325         *  @exception ClassCastException If the arguments cannot
2326         *   be compared.
2327         */
2328        @Override
2329        public int compare(Object arg0, Object arg1) throws ClassCastException {
2330            // NOTE: This logic indicates which tokens are comparable
2331            // and is replicated in the sortReturnType() method. If
2332            // this changes, then that method should also be changed.
2333            if (arg0 instanceof StringToken && arg1 instanceof StringToken) {
2334                String string0 = ((StringToken) arg0).stringValue();
2335                String string1 = ((StringToken) arg1).stringValue();
2336                int result = string0.compareTo(string1);
2337
2338                if (_ascending) {
2339                    return result;
2340                } else {
2341                    return -1 * result;
2342                }
2343            } else if (arg0 instanceof ScalarToken
2344                    && arg1 instanceof ScalarToken
2345                    && !(arg0 instanceof ComplexToken)
2346                    && !(arg1 instanceof ComplexToken)) {
2347                ScalarToken cast0 = (ScalarToken) arg0;
2348                ScalarToken cast1 = (ScalarToken) arg1;
2349
2350                try {
2351                    if (cast0.isEqualTo(cast1).booleanValue()) {
2352                        return 0;
2353                    } else {
2354                        if (cast0.isLessThan(cast1).booleanValue()) {
2355                            if (_ascending) {
2356                                return -1;
2357                            } else {
2358                                return 1;
2359                            }
2360                        } else {
2361                            if (_ascending) {
2362                                return 1;
2363                            } else {
2364                                return -1;
2365                            }
2366                        }
2367                    }
2368                } catch (IllegalActionException ex) {
2369                    // Fall through to exception below.
2370                }
2371            }
2372
2373            // If we get here, then arguments are not of the correct type.
2374            // NOTE: The error message is appropriate only for the use of
2375            // this inside the sort() methods.
2376            throw new ClassCastException(
2377                    "Sorting only works on arrays of strings"
2378                            + " or non-complex scalars.");
2379        }
2380
2381        private boolean _ascending;
2382    }
2383}