001/* An actor that computes a specified rounded 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.conversions; 029 030import ptolemy.actor.lib.Transformer; 031import ptolemy.data.DoubleToken; 032import ptolemy.data.IntToken; 033import ptolemy.data.Token; 034import ptolemy.data.type.BaseType; 035import ptolemy.kernel.CompositeEntity; 036import ptolemy.kernel.util.Attribute; 037import ptolemy.kernel.util.IllegalActionException; 038import ptolemy.kernel.util.InvalidStateException; 039import ptolemy.kernel.util.NameDuplicationException; 040import ptolemy.kernel.util.StringAttribute; 041import ptolemy.kernel.util.Workspace; 042 043// NOTE: If you update the list of functions, then you will want 044// to update the list in actor/lib/math.xml. 045/////////////////////////////////////////////////////////////////// 046//// Round 047 048/** 049 Produce an output token on each firing with a value that is 050 equal to the specified rounded value of the input. 051 The input type is DoubleToken. The output type is IntToken. 052 The functions are a subset of those in the java.lang.Math class. 053 They are: 054 <ul> 055 <li> <b>ceil</b>: Round towards positive infinity. 056 <li> <b>floor</b>: Round towards negative infinity. 057 <li> <b>round</b>: Round towards nearest integer. This is the 058 default behavior. 059 <li> <b>truncate</b>: Round towards zero. 060 </ul> 061 062 If the input is NaN, then an exception is thrown. 063 The reason for this is that there is no way to represent a NaN 064 as an integer. Thus, even though java.lang.Math.round(Double.NaN) 065 returns 0, ceil(Double.NaN), floor(Double.NaN) and truncate(DoubleNaN) all 066 return a Double.NaN. However, this actor has an integer output, 067 so there is no way to represent the Double.NaN as an integer, so 068 we throw an exception. 069 070 071 @author C. Fong, Contributor: Christopher Brooks 072 @version $Id$ 073 @since Ptolemy II 1.0 074 @Pt.ProposedRating Green (chf) 075 @Pt.AcceptedRating Green (janneck) 076 */ 077public class Round extends Transformer { 078 /** Construct an actor with the given container and name. 079 * @param container The container. 080 * @param name The name of this actor. 081 * @exception NameDuplicationException If the container already has an 082 * actor with this name. 083 * @exception IllegalActionException If the actor cannot be contained 084 * by the proposed container. 085 */ 086 public Round(CompositeEntity container, String name) 087 throws NameDuplicationException, IllegalActionException { 088 super(container, name); 089 090 // Parameters 091 function = new StringAttribute(this, "function"); 092 function.setExpression("round"); 093 _function = _ROUND; 094 095 // Ports 096 input.setTypeEquals(BaseType.DOUBLE); 097 output.setTypeEquals(BaseType.INT); 098 099 _attachText("_iconDescription", 100 "<svg>\n" + "<circle cx=\"0\" cy=\"0\" r=\"17\"" 101 + "style=\"fill:white\"/>\n" + "</svg>\n"); 102 } 103 104 /////////////////////////////////////////////////////////////////// 105 //// ports and parameters //// 106 107 /** The rounding strategy to use. This is a string-valued parameter 108 * that defaults to "round". 109 */ 110 public StringAttribute function; 111 112 /////////////////////////////////////////////////////////////////// 113 //// public methods //// 114 115 /** Override the base class to determine which function is being 116 * specified. 117 * @param attribute The attribute that changed. 118 * @exception IllegalActionException If the function is not recognized. 119 */ 120 @Override 121 public void attributeChanged(Attribute attribute) 122 throws IllegalActionException { 123 if (attribute == function) { 124 String functionName = function.getExpression(); 125 126 if (functionName.equals("ceil")) { 127 _function = _CEIL; 128 } else if (functionName.equals("floor")) { 129 _function = _FLOOR; 130 } else if (functionName.equals("round")) { 131 _function = _ROUND; 132 } else if (functionName.equals("truncate")) { 133 _function = _TRUNCATE; 134 } else { 135 throw new IllegalActionException(this, 136 "Unrecognized rounding function: " + functionName); 137 } 138 } else { 139 super.attributeChanged(attribute); 140 } 141 } 142 143 /** Clone the actor into the specified workspace. 144 * @param workspace The workspace for the new object. 145 * @return A new actor. 146 * @exception CloneNotSupportedException If a derived class contains 147 * an attribute that cannot be cloned. 148 */ 149 @Override 150 public Object clone(Workspace workspace) throws CloneNotSupportedException { 151 Round newObject = (Round) super.clone(workspace); 152 153 // The non-primitive fields of the clone must refer to objects 154 // distinct from the objects of the same name in the class. 155 // If this is not done, then there may be problems with actor 156 // oriented classes. 157 newObject._resultArray = new IntToken[0]; 158 return newObject; 159 } 160 161 /** This computes the specified rounded value of the input. 162 * This consumes and produces at most one token for each firing. 163 * If there is no input, then produce no output. 164 * @exception IllegalActionException If there is no director, 165 * or if the input is NaN. 166 */ 167 @Override 168 public void fire() throws IllegalActionException { 169 super.fire(); 170 double in = ((DoubleToken) input.get(0)).doubleValue(); 171 if (Double.isNaN(in)) { 172 throw new IllegalActionException("Input is Double.NaN, " 173 + "there is no way to represent a NaN as an integer."); 174 } 175 output.send(0, new IntToken(_doFunction(in))); 176 } 177 178 /** Invoke a specified number of iterations of this actor. Each 179 * iteration computes the rounding function specified by the 180 * <i>function</i> attribute on a single token. An invocation 181 * of this method therefore applies the function to <i>count</i> 182 * successive input tokens. 183 * <p> 184 * This method should be called instead of the usual prefire(), 185 * fire(), postfire() methods when this actor is used in a 186 * domain that supports vectorized actors. This leads to more 187 * efficient execution. 188 * @param count The number of iterations to perform. 189 * @return COMPLETED if the actor was successfully iterated the 190 * specified number of times. Otherwise, return NOT_READY, and do 191 * not consume any input tokens. 192 * @exception IllegalActionException Not thrown in this base class. 193 */ 194 @Override 195 public int iterate(int count) throws IllegalActionException { 196 // Check whether we need to reallocate the output token array. 197 if (count > _resultArray.length) { 198 _resultArray = new IntToken[count]; 199 } 200 201 if (input.hasToken(0, count)) { 202 // NOTE: inArray.length may be > count, in which case 203 // only the first count tokens are valid. 204 Token[] inArray = input.get(0, count); 205 206 for (int i = 0; i < count; i++) { 207 double value = ((DoubleToken) inArray[i]).doubleValue(); 208 if (Double.isNaN(value)) { 209 throw new IllegalActionException("Input is Double.NaN, " 210 + "there is no way to represent a NaN as an integer."); 211 } 212 213 _resultArray[i] = new IntToken(_doFunction(value)); 214 } 215 216 output.send(0, _resultArray, count); 217 return COMPLETED; 218 } else { 219 return NOT_READY; 220 } 221 } 222 223 /** Return false if there is no available input token, and otherwise 224 * return whatever the superclass returns (presumably true). 225 * @exception IllegalActionException If there is no director. 226 */ 227 @Override 228 public boolean prefire() throws IllegalActionException { 229 if (!input.hasToken(0)) { 230 return false; 231 } 232 233 return super.prefire(); 234 } 235 236 /////////////////////////////////////////////////////////////////// 237 //// private methods //// 238 239 /** Calculate the function on the given argument. 240 * @param in The input value. 241 * @return The result of applying the function. 242 */ 243 private int _doFunction(double in) { 244 int result; 245 246 switch (_function) { 247 case _CEIL: 248 result = (int) Math.ceil(in); 249 break; 250 251 case _FLOOR: 252 result = (int) Math.floor(in); 253 break; 254 255 case _ROUND: 256 result = (int) Math.round(in); 257 break; 258 259 case _TRUNCATE: 260 261 if (in > 0) { 262 result = (int) Math.floor(in); 263 } else { 264 result = (int) Math.ceil(in); 265 } 266 267 break; 268 269 default: 270 throw new InvalidStateException( 271 "Invalid value for _function private variable. " 272 + "Round actor (" + getFullName() + ")" 273 + " on function type " + _function); 274 } 275 276 return result; 277 } 278 279 /////////////////////////////////////////////////////////////////// 280 //// private variables //// 281 private IntToken[] _resultArray = new IntToken[0]; 282 283 // An indicator for the function to compute. 284 // This variable has values specified in the constants below 285 private int _function; 286 287 // Constants used for more efficient execution. 288 private static final int _CEIL = 0; 289 290 private static final int _FLOOR = 1; 291 292 private static final int _ROUND = 2; 293 294 private static final int _TRUNCATE = 3; 295}