001/* A polymorphic adder/subtractor.
002
003 Copyright (c) 1997-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 java.util.HashSet;
031import java.util.Set;
032
033import ptolemy.actor.TypedAtomicActor;
034import ptolemy.actor.TypedIOPort;
035import ptolemy.data.Token;
036import ptolemy.graph.Inequality;
037import ptolemy.kernel.CompositeEntity;
038import ptolemy.kernel.util.IllegalActionException;
039import ptolemy.kernel.util.NameDuplicationException;
040import ptolemy.kernel.util.Workspace;
041
042///////////////////////////////////////////////////////////////////
043//// AddSubtract
044
045/**
046 <p>
047 A polymorphic adder/subtractor.
048 This adder has two input ports, both of which are multiports,
049 and one output port, which is not.
050 The types on the ports are undeclared and will be resolved by
051 the type resolution mechanism. Data that arrives on the
052 input port named <i>plus</i> will be added, and data that arrives
053 on the input port named <i>minus</i> will be subtracted.
054 Any token type supporting addition and subtraction can be used.
055 In most domains, either input port can be left unconnected.
056 Thus, to get a simple adder (with no subtractor), just leave the
057 <i>minus</i> input unconnected.</p>
058 <p>
059 The <i>plus</i> input port will typically resolve to the least upper bound
060 of the types presented to it.  Thus, for example, if one input channel
061 comes from a source of type BooleanToken and another comes from a source
062 of type IntToken, the resolved type will be StringToken, and addition
063 will be that implemented in StringToken (which concatenates strings).
064 Notice that StringToken does not support subtraction, so if any
065 inputs are presented to the <i>minus</i> port, an exception will
066 be thrown at run time.</p>
067 <p>
068 Currently, the type system is quite liberal about the resolved
069 types it will permit at the inputs. In particular, it may permit the
070 <i>plus</i> and <i>minus</i> inputs to resolve to types that cannot in fact
071 be subtracted.  In these cases, a run-time error will occur.
072 In the future, we hope that the type system will intercept such errors
073 before run time.</p>
074 <p>
075 This actor does not require that each input
076 channel have a token upon firing. It will add or subtract available
077 tokens at the inputs and ignore the channels that do not have tokens.
078 It consumes at most one input token from each port.
079 If no input tokens are available at all, then no output is produced.</p>
080
081 @author Yuhong Xiong and Edward A. Lee
082 @version $Id$
083 @since Ptolemy II 0.3
084 @Pt.ProposedRating Green (eal)
085 @Pt.AcceptedRating Green (bilung)
086 */
087public class AddSubtract extends TypedAtomicActor {
088    /** Construct an actor in the specified container with the specified
089     *  name.
090     *  @param container The container.
091     *  @param name The name of this adder within the container.
092     *  @exception IllegalActionException If the actor cannot be contained
093     *   by the proposed container.
094     *  @exception NameDuplicationException If the name coincides with
095     *   an actor already in the container.
096     */
097    public AddSubtract(CompositeEntity container, String name)
098            throws IllegalActionException, NameDuplicationException {
099        super(container, name);
100        plus = new TypedIOPort(this, "plus", true, false);
101        plus.setMultiport(true);
102        plus.setAutomaticTypeConversion(false);
103        minus = new TypedIOPort(this, "minus", true, false);
104        minus.setMultiport(true);
105        minus.setAutomaticTypeConversion(false);
106        output = new TypedIOPort(this, "output", false, true);
107
108        output.setTypeAtLeast(plus);
109        output.setTypeAtLeast(minus);
110
111        // To better support type inference, the two input
112        // ports should have the same type, just as their
113        // input channels all have the same type.
114        plus.setTypeSameAs(minus);
115
116        _attachText("_iconDescription", "<svg>\n" + "<rect x=\"-20\" y=\"-20\" "
117                + "width=\"40\" height=\"40\" " + "style=\"fill:white\"/>\n"
118                + "<text x=\"-13\" y=\"-5\" " + "style=\"font-size:18\">\n"
119                + "+ \n" + "</text>\n" + "<text x=\"-13\" y=\"7\" "
120                + "style=\"font-size:18\">\n" + "_ \n" + "</text>\n"
121                + "</svg>\n");
122    }
123
124    ///////////////////////////////////////////////////////////////////
125    ////                     ports and parameters                  ////
126
127    /** Input for tokens to be subtracted.  This is a multiport, and its
128     *  type is inferred from the connections.
129     */
130    public TypedIOPort minus;
131
132    /** Output port.  The type is inferred from the connections.
133     */
134    public TypedIOPort output;
135
136    /** Input for tokens to be added.  This is a multiport, and its
137     *  type is inferred from the connections.
138     */
139    public TypedIOPort plus;
140
141    ///////////////////////////////////////////////////////////////////
142    ////                         public methods                    ////
143
144    /** Override the base class to set type constraints on the ports.
145     *  @param workspace The workspace into which to clone.
146     *  @return A new instance of AddSubtract.
147     *  @exception CloneNotSupportedException If a derived class contains
148     *   an attribute that cannot be cloned.
149     */
150    @Override
151    public Object clone(Workspace workspace) throws CloneNotSupportedException {
152        AddSubtract newObject = (AddSubtract) super.clone(workspace);
153        newObject.output.setTypeAtLeast(newObject.plus);
154        newObject.output.setTypeAtLeast(newObject.minus);
155        newObject.plus.setTypeSameAs(newObject.minus);
156        return newObject;
157    }
158
159    /** If there is at least one token on the input ports, add
160     *  tokens from the <i>plus</i> port, subtract tokens from the
161     *  <i>minus</i> port, and send the result to the
162     *  <i>output</i> port. At most one token is read
163     *  from each channel, so if more than one token is pending, the
164     *  rest are left for future firings.  If none of the input
165     *  channels has a token, do nothing.  If none of the plus channels
166     *  have tokens, then the tokens on the minus channels are subtracted
167     *  from a zero token of the same type as the first token encountered
168     *  on the minus channels.
169     *
170     *  @exception IllegalActionException If there is no director,
171     *   or if addition and subtraction are not supported by the
172     *   available tokens.
173     */
174    @Override
175    public void fire() throws IllegalActionException {
176        super.fire();
177        Token sum = null;
178
179        for (int i = 0; i < plus.getWidth(); i++) {
180            if (plus.hasToken(i)) {
181                if (sum == null) {
182                    sum = plus.get(i);
183                } else {
184                    sum = sum.add(plus.get(i));
185                }
186            }
187        }
188
189        for (int i = 0; i < minus.getWidth(); i++) {
190            if (minus.hasToken(i)) {
191                Token in = minus.get(i);
192
193                if (sum == null) {
194                    sum = in.zero();
195                }
196
197                sum = sum.subtract(in);
198            }
199        }
200
201        if (sum != null) {
202            output.send(0, sum);
203        }
204    }
205
206    /** Set the plus port to be greater than or equal to the output port
207     *  if backward type inference is enabled.
208     *  @return A set of Inequalities
209     */
210    @Override
211    protected Set<Inequality> _customTypeConstraints() {
212        Set<Inequality> result = new HashSet<Inequality>();
213        if (isBackwardTypeInferenceEnabled()) {
214            result.add(
215                    new Inequality(output.getTypeTerm(), plus.getTypeTerm()));
216        }
217        return result;
218    }
219
220}