001/* An actor that outputs the absolute value of the input.
002
003 Copyright (c) 1998-2014 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 */
028package ptolemy.actor.lib;
029
030import ptolemy.actor.TypedIOPort;
031import ptolemy.data.ArrayToken;
032import ptolemy.data.ScalarToken;
033import ptolemy.data.Token;
034import ptolemy.data.type.ArrayType;
035import ptolemy.data.type.BaseType;
036import ptolemy.data.type.MonotonicFunction;
037import ptolemy.data.type.Type;
038import ptolemy.graph.InequalityTerm;
039import ptolemy.kernel.CompositeEntity;
040import ptolemy.kernel.util.IllegalActionException;
041import ptolemy.kernel.util.NameDuplicationException;
042import ptolemy.kernel.util.Workspace;
043
044///////////////////////////////////////////////////////////////////
045//// AbsoluteValue
046
047/**
048 Produce an output token on each firing with a value that is
049 equal to the absolute value of the input. The input can have any
050 scalar type, or it can be an array of scalars (or an array of arrays
051 of scalars, etc.). If the input type is not Complex, the output has the
052 same type as the input. If the input type is Complex, the output
053 type is Double, in which case, the output value is the magnitude
054 of the input complex.
055
056 @author Edward A. Lee
057 @version $Id$
058 @since Ptolemy II 0.3
059 @Pt.ProposedRating Yellow (eal)
060 @Pt.AcceptedRating Yellow (yuhong)
061 */
062public class AbsoluteValue extends Transformer {
063    /** Construct an actor with the given container and name.
064     *  @param container The container.
065     *  @param name The name of this actor.
066     *  @exception IllegalActionException If the actor cannot be contained
067     *   by the proposed container.
068     *  @exception NameDuplicationException If the container already has an
069     *   actor with this name.
070     */
071    public AbsoluteValue(CompositeEntity container, String name)
072            throws NameDuplicationException, IllegalActionException {
073        super(container, name);
074
075        output.setTypeAtLeast(new TypeOfAbsoluteValue(input));
076        // FIXME: This actor accepts a rather complicated set
077        // of input types. Is there a way to express the constraints?
078        // input.setTypeAtMost(BaseType.SCALAR);
079        // Also, type constraints do not propagate backwards from
080        // the output to the input.
081    }
082
083    ///////////////////////////////////////////////////////////////////
084    ////                         public methods                    ////
085
086    /** Clone the actor into the specified workspace. This calls the
087     *  base class and then sets the type constraints.
088     *  @param workspace The workspace for the new object.
089     *  @return A new actor.
090     *  @exception CloneNotSupportedException If a derived class has
091     *   an attribute that cannot be cloned.
092     */
093    @Override
094    public Object clone(Workspace workspace) throws CloneNotSupportedException {
095        AbsoluteValue newObject = (AbsoluteValue) super.clone(workspace);
096        newObject.output
097                .setTypeAtLeast(new TypeOfAbsoluteValue(newObject.input));
098        return newObject;
099    }
100
101    /** Compute the absolute value of the input.  If there is no input, then
102     *  produce no output.
103     *  @exception IllegalActionException If there is no director.
104     */
105    @Override
106    public void fire() throws IllegalActionException {
107        super.fire();
108        if (input.hasToken(0)) {
109            output.send(0, _absoluteValue(input.get(0)));
110        }
111    }
112
113    ///////////////////////////////////////////////////////////////////
114    ////                         private methods                   ////
115
116    /** If the argument type is an array, then return an array
117     *  type with element types recursively defined by this method.
118     *  Otherwise, if the argument type is complex, then return
119     *  double. Otherwise, return the argument type.
120     *  @return The absolute value of the input.
121     *  @exception IllegalActionException If there is no absolute value
122     *   operation for the specified token.
123     */
124    private Token _absoluteValue(Token input) throws IllegalActionException {
125        if (input instanceof ArrayToken) {
126            int length = ((ArrayToken) input).length();
127            Token[] result = new Token[length];
128            for (int i = 0; i < length; i++) {
129                result[i] = _absoluteValue(((ArrayToken) input).getElement(i));
130            }
131            return new ArrayToken(result);
132        } else if (input instanceof ScalarToken) {
133            return ((ScalarToken) input).absolute();
134        } else {
135            throw new IllegalActionException(this,
136                    "AbsoluteValue only accepts scalar inputs or arrays of scalars.");
137        }
138    }
139
140    ///////////////////////////////////////////////////////////////////
141    ////                         inner classes                     ////
142
143    /** This class implements a monotonic function of the input port
144     *  type. It returns the type of the absolute value of the input
145     *  port type. If the input type is an array, then the function
146     *  returns an array type with element types recursively defined
147     *  by this same function.
148     *  Otherwise, if the port type is complex, then return
149     *  double. Otherwise, return the port
150     *  type.
151     */
152    private static class TypeOfAbsoluteValue extends MonotonicFunction {
153
154        // FindBugs suggested making this class a static inner class:
155        //
156        // "This class is an inner class, but does not use its embedded
157        // reference to the object which created it. This reference makes
158        // the instances of the class larger, and may keep the reference
159        // to the creator object alive longer than necessary. If
160        // possible, the class should be made into a static inner class."
161
162        // The constructor takes a port argument so that the clone()
163        // method can construct an instance of this class for the
164        // input port on the clone.
165        private TypeOfAbsoluteValue(TypedIOPort port) {
166            _port = port;
167        }
168
169        ///////////////////////////////////////////////////////////////
170        ////                       public inner methods            ////
171
172        /** Return the function result.
173         *  @return A Type.
174         */
175        @Override
176        public Object getValue() {
177            return _outputType(_port.getType());
178        }
179
180        /** Return the variables in this term. If the type of the input port
181         *  is a variable, return a one element array containing the
182         *  InequalityTerm of that port; otherwise, return an array of zero
183         *  length.
184         *  @return An array of InequalityTerm.
185         */
186        @Override
187        public InequalityTerm[] getVariables() {
188            if (_port.getTypeTerm().isSettable()) {
189                InequalityTerm[] variable = new InequalityTerm[1];
190                variable[0] = _port.getTypeTerm();
191                return variable;
192            } else {
193                return new InequalityTerm[0];
194            }
195        }
196
197        ///////////////////////////////////////////////////////////////
198        ////                      private inner methods            ////
199
200        /** If the argument type is an array, then return an array
201         *  type with element types recursively defined by this method.
202         *  Otherwise, if the argument type is complex, then return
203         *  double. Otherwise, return the argument type.
204         *  @return A Type.
205         */
206        private Type _outputType(Type inputType) {
207            if (inputType == BaseType.COMPLEX) {
208                return BaseType.DOUBLE;
209            } else if (inputType instanceof ArrayType) {
210                Type elementType = ((ArrayType) inputType).getElementType();
211                return new ArrayType(_outputType(elementType));
212            } else {
213                return inputType;
214            }
215        }
216
217        ///////////////////////////////////////////////////////////////
218        ////                       private inner variable          ////
219        private TypedIOPort _port;
220    }
221}