]> gerrit.simantics Code Review - simantics/sysdyn.git/commitdiff
Add new sysdyn functions details in issue 16 linked below 66/1966/1
authorMiro Richard Eklund <miro.eklund@semantum.fi>
Fri, 27 Jul 2018 11:45:33 +0000 (14:45 +0300)
committerMiro Richard Eklund <miro.eklund@semantum.fi>
Fri, 27 Jul 2018 11:45:33 +0000 (14:45 +0300)
Multiple functions said to be available in simantics sysdyn
documentation were missing. I added those that I could add, leaving
functions that "cannot be called from functions" unimplemented, such as
"der, initial, terminal, sample, edge, change, reinit, cardinality,
inStream, actualStream"

gitlab #16

Change-Id: Ibd2932683f046e2ab11830fb6dd9f4f6b58c29cb

bundles/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Environment.java

index baf91ac64413628f2867a93a15fdb87585676ac9..74e2ffb7e6283c90625a23ddecea43a3516e8bf1 100644 (file)
-/*******************************************************************************\r
- * Copyright (c) 2013 Semantum Oy.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- *     Semantum Oy - initial API and implementation\r
- *     VTT Technical Research Centre of Finland - #4488\r
- *******************************************************************************/\r
-package fi.semantum.sysdyn.solver;\r
-\r
-import fi.semantum.sysdyn.solver.Array.Modifier;\r
-import gnu.trove.list.array.TIntArrayList;\r
-import gnu.trove.map.hash.TObjectIntHashMap;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.HashMap;\r
-import java.util.HashSet;\r
-import java.util.Iterator;\r
-import java.util.Map;\r
-import java.util.Random;\r
-import java.util.Set;\r
-import java.util.TreeMap;\r
-\r
-interface Fn {\r
-       public Object evaluate(IEnvironment environment, int argc);\r
-       public Object evaluateInput(IEnvironment environment, int argPosition);\r
-       public void setLocals(IEnvironment environment);\r
-       public int offset();\r
-       public Variable[] parameters(int args);\r
-}\r
-\r
-abstract class Fn1 implements Fn {\r
-\r
-       Variable[] parameters;\r
-       \r
-       public Fn1(int pCount) {\r
-               if(pCount > 0) {\r
-                       parameters = new Variable[pCount];\r
-                       for(int i=0;i<pCount;i++)\r
-                               parameters[i] = new Variable(new VariableBase("", i));\r
-               }\r
-       }\r
-       \r
-       public int offset() {\r
-               return 0;\r
-       }\r
-       \r
-       public Variable[] parameters(int argc) {\r
-               if(parameters != null) return parameters;\r
-               else {\r
-                       Variable[] ret = new Variable[argc];\r
-                       for(int i=0;i<argc;i++)\r
-                               ret[i] = new Variable(new VariableBase("", i));\r
-                       return ret;\r
-               }\r
-       }\r
-       \r
-       @Override\r
-       public void setLocals(IEnvironment environment) {\r
-       }\r
-       \r
-       @Override\r
-       public Object evaluateInput(IEnvironment environment, int argPosition) {\r
-               // Function frame size is constant - this is called when padding is needed\r
-               return null;\r
-       }\r
-       \r
-}\r
-\r
-abstract class FnRandom extends Fn1 {\r
-       \r
-       protected Random random = null;\r
-\r
-       public FnRandom(int pCount) {\r
-               super(pCount);\r
-       }\r
-       \r
-}\r
-\r
-final public class Environment implements IEnvironment, ISystem {\r
-       \r
-       final Map<String,Object> named = new HashMap<String,Object>();\r
-\r
-       public Model model;\r
-       public double step;\r
-       public double time;\r
-       public boolean initial = true;\r
-       public int size;\r
-       \r
-       public Object[] valueTable;\r
-       \r
-       public TObjectIntHashMap<String> keyIndexMap = new TObjectIntHashMap<String>(); \r
-\r
-       HashMap<String,TreeMap<Double,Double>> history = new HashMap<String,TreeMap<Double,Double>>();\r
-\r
-       public Environment(Model model, double step, double start) {\r
-               this.model = model;\r
-               this.step = step;\r
-               this.time = start;\r
-               \r
-               model.functions.put("sin", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               return Math.sin(x);\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("cos", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               return Math.cos(x);\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("tan", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               return Math.tan(x);\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("asin", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               return Math.asin(x);\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("acos", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               return Math.acos(x);\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("atan", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               return Math.atan(x);\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("toRadians", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               return Math.toRadians(x);\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("toDegrees", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               return Math.toDegrees(x);\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("exp", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               return Math.exp(x);\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("log", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               return Math.log(x);\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("log10", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               return Math.log10(x);\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("sqrt", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               return Math.sqrt(x);\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("cbrt", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               return Math.cbrt(x);\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("mod", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               Double y = (Double)environment.getValue(1);\r
-                               return mod(x, y);\r
-                       }\r
-\r
-                       double mod(double x, double y) {\r
-                               return x - Math.floor(x/y)*y;\r
-                       }\r
-\r
-               });\r
-               model.functions.put("rem", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               Double y = (Double)environment.getValue(1);\r
-                               return Math.IEEEremainder(x, y);\r
-                       }\r
-\r
-               });\r
-               model.functions.put("ceil", new Fn1(1) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Object value = environment.getValue(0);\r
-                               if(value instanceof Double) {\r
-                                       return Math.ceil((Double)value);\r
-                               } else if(value instanceof Array) {\r
-                                       return ((Array)value).copy(new Modifier() {\r
-\r
-                                               @Override\r
-                                               public Object modify(Object o) {\r
-                                                       if(o instanceof Double) {\r
-                                                               return Math.ceil((Double)o);\r
-                                                       } else {\r
-                                                               throw new IllegalStateException();\r
-                                                       }\r
-                                               }\r
-                                               \r
-                                       });\r
-                               } else {\r
-                                       throw new IllegalStateException();\r
-                               }\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("floor", new Fn1(1) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Object value = environment.getValue(0);\r
-                               if(value instanceof Double) {\r
-                                       return Math.floor((Double)value);\r
-                               } else if(value instanceof Array) {\r
-                                       return ((Array)value).copy(new Modifier() {\r
-\r
-                                               @Override\r
-                                               public Object modify(Object o) {\r
-                                                       if(o instanceof Double) {\r
-                                                               return Math.floor((Double)o);\r
-                                                       } else {\r
-                                                               throw new IllegalStateException();\r
-                                                       }\r
-                                               }\r
-                                               \r
-                                       });\r
-                               } else {\r
-                                       throw new IllegalStateException();\r
-                               }\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("rint", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               return Math.rint(x);\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("atan2", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               Double y = (Double)environment.getValue(1);\r
-                               return Math.atan2(x, y);\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("pow", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               Double y = (Double)environment.getValue(1);\r
-                               return Math.pow(x, y);\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("round", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               long result = Math.round(x); \r
-                               return (int)result;\r
-                       }\r
-                       \r
-               });\r
-\r
-               model.functions.put("abs", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Object o = environment.getValue(0);\r
-                               if(o instanceof Double) {\r
-                                       return Math.abs((Double)o);\r
-                               } else if (o instanceof Integer) {\r
-                                       return Math.abs((Integer)o);\r
-                               } else {\r
-                                       throw new IllegalStateException("Unsupported argument to abs: " + o);\r
-                               }\r
-                       }\r
-                       \r
-               });\r
-\r
-               model.functions.put("min", new Fn1(5) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Object first = environment.getValue(0);\r
-                               if(first instanceof Double) {\r
-                                       Double result = (Double)first;\r
-                                       for(int i=1;i<argc;i++) {\r
-                                               Double d = (Double)environment.getValue(i);\r
-                                               if(d < result) result = d;\r
-                                       }\r
-                                       return result;\r
-                               } else if (first instanceof Integer) {\r
-                                       Integer result = (Integer )first;\r
-                                       for(int i=1;i<argc;i++) {\r
-                                               Integer  d = (Integer )environment.getValue(i);\r
-                                               if(d < result) result = d;\r
-                                       }\r
-                                       return result;\r
-                               } else {\r
-                                       throw new IllegalStateException("Unsupported argument to min: " + first);\r
-                               }\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("max", new Fn1(5) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Object first = environment.getValue(0);\r
-                               if(first instanceof Double) {\r
-                                       Double result = (Double)first;\r
-                                       for(int i=1;i<argc;i++) {\r
-                                               Double d = (Double)environment.getValue(i);\r
-                                               if(d > result) result = d;\r
-                                       }\r
-                                       return result;\r
-                               } else if (first instanceof Integer) {\r
-                                       Integer result = (Integer )first;\r
-                                       for(int i=1;i<argc;i++) {\r
-                                               Integer  d = (Integer )environment.getValue(i);\r
-                                               if(d > result) result = d;\r
-                                       }\r
-                                       return result;\r
-                               } else {\r
-                                       throw new IllegalStateException("Unsupported argument to max: " + first);\r
-                               }\r
-                       }\r
-                       \r
-               });\r
-\r
-               model.functions.put("ulp", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               return Math.ulp(x);\r
-                       }\r
-                       \r
-               });\r
-\r
-               model.functions.put("signum", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               return Math.signum(x);\r
-                       }\r
-                       \r
-               });\r
-\r
-               model.functions.put("sinh", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               return Math.sinh(x);\r
-                       }\r
-                       \r
-               });\r
-\r
-               model.functions.put("cosh", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               return Math.cosh(x);\r
-                       }\r
-                       \r
-               });\r
-               \r
-               model.functions.put("tanh", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               return Math.tanh(x);\r
-                       }\r
-                       \r
-               });\r
-\r
-               model.functions.put("hypot", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               Double y = (Double)environment.getValue(1);\r
-                               return Math.hypot(x, y);\r
-                       }\r
-                       \r
-               });\r
-\r
-               model.functions.put("expm1", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               return Math.expm1(x);\r
-                       }\r
-                       \r
-               });\r
-\r
-               model.functions.put("log1p", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               return Math.log1p(x);\r
-                       }\r
-                       \r
-               });\r
-\r
-               model.functions.put("copySign", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               Double y = (Double)environment.getValue(1);\r
-                               return Math.copySign(x, y);\r
-                       }\r
-                       \r
-               });\r
-\r
-               model.functions.put("getExponent", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               return Math.getExponent(x);\r
-                       }\r
-                       \r
-               });\r
-\r
-               model.functions.put("nextAfter", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               Double y = (Double)environment.getValue(1);\r
-                               return Math.nextAfter(x, y);\r
-                       }\r
-                       \r
-               });\r
-\r
-               model.functions.put("nextUp", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               return Math.nextUp(x);\r
-                       }\r
-                       \r
-               });\r
-\r
-               model.functions.put("scalb", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double x = (Double)environment.getValue(0);\r
-                               Integer y = (Integer)environment.getValue(1);\r
-                               return Math.scalb(x, y);\r
-                       }\r
-                       \r
-               });\r
-\r
-               model.functions.put("size", new Fn1(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Array array = (Array)environment.getValue(0);\r
-                               Double col = (Double)environment.getValue(1);\r
-                               return Double.valueOf(array.size(col.intValue()));\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("zidz", new Fn1(2) {\r
-\r
-                       private Object evaluate(Double p1, Double p2) {\r
-                               if(Math.abs(p2) < 1e-12) return 0.0;\r
-                               else return p1 / p2;\r
-                       }\r
-                       \r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Object o1 = environment.getValue(0);\r
-                               Object o2 = environment.getValue(1);\r
-                               \r
-                               if(o1 instanceof Double && o2 instanceof Double) {\r
-                                       return evaluate((Double)o1, (Double)o2);\r
-                               }\r
-                               if(o1 instanceof Array && o2 instanceof Array) {\r
-                                       Array la = (Array)o1;\r
-                                       Array ra = (Array)o2;\r
-                                       Collection<Object> lae = la.elements();\r
-                                       Collection<Object> rae = ra.elements();\r
-                                       if(lae.size() != rae.size()) throw new UnsupportedOperationException();\r
-                                       Iterator<Object> li = lae.iterator();\r
-                                       Iterator<Object> ri = rae.iterator();\r
-                                       Array result = new Array();\r
-                                       for(int i=0;i<lae.size();i++) {\r
-                                               double ld = (Double)li.next();\r
-                                               double rd = (Double)ri.next();\r
-                                               result.addElement(evaluate(ld,rd));\r
-                                       }\r
-                                       return result;\r
-                               }\r
-                               throw new IllegalStateException();\r
-                               \r
-                               \r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("xidz", new Fn1(3) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double p1 = (Double)environment.getValue(0);\r
-                               Double p2 = (Double)environment.getValue(1);\r
-                               Double x = (Double)environment.getValue(2);\r
-                               if(Math.abs(p2) < 1e-12) return x;\r
-                               else return p1 / p2;\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("availabilityExternal", new Fn1(4) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double numberOfDays = (Double)environment.getValue(0);\r
-                               Double numberOfDesigners = (Double)environment.getValue(1);\r
-                               @SuppressWarnings("unused")\r
-                               Double rseed = (Double)environment.getValue(2);\r
-                               @SuppressWarnings("unused")\r
-                               Array availability = (Array)environment.getValue(3);\r
-                               Array result = new Array();\r
-                               for(int i=0;i<numberOfDays.intValue()*numberOfDesigners.intValue();i++) {\r
-                                       result.addElement(Math.random());\r
-                               }\r
-                               return result;\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("pre", new Fn1(1) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               \r
-                               return (Double)environment.getValue(0);\r
-                               \r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("fill", new Fn1(3) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double value = (Double)environment.getValue(0);\r
-                               Double dim1 = (Double)environment.getValue(1);\r
-                               if(argc == 2) {\r
-                                       Array result = new Array();\r
-                                       for(int i=0;i<dim1.intValue();i++) {\r
-                                               result.addElement(value);\r
-                                       }\r
-                                       return result;\r
-                               } else if(argc == 3) {\r
-                                       Double dim2 = (Double)environment.getValue(2);\r
-                                       Array result = new Array();\r
-                                       for(int i=0;i<dim1.intValue();i++) {\r
-                                               Array array = new Array();\r
-                                               for(int j=0;j<dim2.intValue();j++) {\r
-                                                       array.addElement(value);\r
-                                               }\r
-                                               result.addElement(array);\r
-                                       }\r
-                                       return result;\r
-                               } else throw new UnsupportedOperationException();\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("randomNumber", new FnRandom(2) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double seed = (Double)environment.getValue(0);\r
-                               \r
-                               if (random == null) {\r
-                                       random = new Random(Double.doubleToLongBits(seed));\r
-                               }\r
-                               \r
-                               return random.nextDouble();\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("randomUniform", new FnRandom(4) {\r
-                       \r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double min = (Double)environment.getValue(0);\r
-                               Double max = (Double)environment.getValue(1);\r
-                               Double seed = (Double)environment.getValue(2);\r
-                               \r
-                               // the fourth parameter should be "time" and is only necessary\r
-                               // because sysdyn treats all functions as side-effect-free and\r
-                               // does not evaluate a function "needlessly" if the parameters\r
-                               // do not change. the parameter does not actually do anything\r
-                               // though so we can safely ignore it here\r
-                               \r
-                               if (random == null) {\r
-                                       random = new Random(Double.doubleToLongBits(seed));\r
-                               }\r
-                               return random.nextDouble() * (max - min) + min;\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("randomNormal", new FnRandom(6) {\r
-                       \r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Double min = (Double)environment.getValue(0);\r
-                               Double max = (Double)environment.getValue(1);\r
-                               Double mean = (Double)environment.getValue(2);\r
-                               Double stdev = (Double)environment.getValue(3);\r
-                               Double seed = (Double)environment.getValue(4);\r
-                               \r
-                               if (random == null) {\r
-                                       random = new Random(Double.doubleToLongBits(seed));\r
-                               }\r
-                               \r
-                               // TODO: use an actual library for this stuff\r
-                               \r
-                               return NormalDistribution.uniformVariableToNormal(min, max, mean, stdev, random.nextDouble());\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("initial", new Fn1(0) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment _environment, int argc) {\r
-                               return _environment.getSystem().initial();\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("noEvent", new Fn1(1) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               \r
-                               return environment.getValue(0);\r
-                               \r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("integer", new Fn1(1) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Object value = environment.getValue(0);\r
-                               if(value instanceof Double) {\r
-                                       double d = ((Double)value).intValue(); \r
-                                       return d;\r
-                               } else if(value instanceof Array) {\r
-                                       return ((Array)value).copy(new Modifier() {\r
-\r
-                                               @Override\r
-                                               public Object modify(Object o) {\r
-                                                       if(o instanceof Double) {\r
-                                                               double d = ((Double)o).intValue(); \r
-                                                               return d;\r
-                                                       } else {\r
-                                                               throw new IllegalStateException();\r
-                                                       }\r
-                                               }\r
-                                               \r
-                                       });\r
-                               } else {\r
-                                       throw new IllegalStateException();\r
-                               }\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("sum", new Fn1(1) {\r
-\r
-                       public double sum(Array a) {\r
-                               double res = 0;\r
-                               for(Object element : a.elements()) {\r
-                                       if(element instanceof Array) {\r
-                                               res += sum((Array)element);\r
-                                       } else if(element instanceof Double) {\r
-                                               res += (Double)element;\r
-                                       } else {\r
-                                               throw new IllegalStateException("Tried to sum a non-numerical array");\r
-                                       }\r
-                               }\r
-                               return res;\r
-                       }\r
-                       \r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               Object value = environment.getValue(0);\r
-                               if(value instanceof Double) return (Double)value;\r
-                               Array arr = (Array)value;\r
-                               return sum(arr);\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("ones", new Fn1(-1) {\r
-\r
-                       private Array make(int[] dims, int pos) {\r
-                               Array result = new Array();\r
-                               int d = dims[pos];\r
-                               if(pos == dims.length - 1) {\r
-                                       for(int i=0;i<d;i++) result.addElement(1.0);\r
-                               } else {\r
-                                       for(int i=0;i<d;i++) result.addElement(make(dims, pos+1));\r
-                               }\r
-                               return result;\r
-                       }\r
-                       \r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               int[] dims = new int[argc];\r
-                               for(int i=0;i<argc;i++) {\r
-                                       Double d = (Double)environment.getValue(i);\r
-                                       dims[i] = d.intValue();\r
-                               }\r
-                               return make(dims, 0);\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("zeros", new Fn1(-1) {\r
-\r
-                       private Array make(int[] dims, int pos) {\r
-                               Array result = new Array();\r
-                               int d = dims[pos];\r
-                               if(pos == dims.length - 1) {\r
-                                       for(int i=0;i<d;i++) result.addElement(0.0);\r
-                               } else {\r
-                                       for(int i=0;i<d;i++) result.addElement(make(dims, pos+1));\r
-                               }\r
-                               return result;\r
-                       }\r
-                       \r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               int[] dims = new int[argc];\r
-                               for(int i=0;i<argc;i++) {\r
-                                       Double d = (Double)environment.getValue(i);\r
-                                       dims[i] = d.intValue();\r
-                               }\r
-                               return make(dims, 0);\r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("transpose", new Fn1(1) {\r
-\r
-                       @Override\r
-                       public Object evaluate(IEnvironment environment, int argc) {\r
-                               \r
-                               Array array = (Array)environment.getValue(0);\r
-                               return array.transposed();\r
-                               \r
-                       }\r
-                       \r
-               });\r
-               model.functions.put("delay", new Fn1(4) {\r
-                       \r
-                       @Override\r
-                       public Object evaluate(IEnvironment _environment, int argc) {\r
-                       \r
-                               String ident = (String)_environment.getValue(0);\r
-                               Double value = (Double)_environment.getValue(1);\r
-                               Double p1 = (Double)_environment.getValue(2);\r
-                               \r
-                               ISystem system = _environment.getSystem();\r
-                               \r
-                               TreeMap<Double,Double> history = system.getHistory(ident);\r
-                               double time = system.time();\r
-                               \r
-                               history.put(time, value);\r
-                               \r
-                               Double key = history.ceilingKey(time-p1);\r
-                               \r
-                               return history.get(key);\r
-                               \r
-                       }\r
-                       \r
-               });             \r
-               model.functions.put("cat", new Fn1(10) {\r
-                       \r
-                       @Override\r
-                       public Object evaluate(IEnvironment _environment, int argc) {\r
-                       \r
-                               Double dim = (Double)_environment.getValue(0);\r
-                               \r
-                               Array array = new Array();\r
-                               for(int i=1;i<argc;i++) {\r
-                                       Object ar = _environment.getValue(i);\r
-                                       if (ar instanceof Array) {\r
-                                               Array a = (Array)ar;\r
-                                               for(Object o : a.elements())\r
-                                                       array.addElement(o);\r
-                                       } else {\r
-                                               throw new IllegalStateException();\r
-                                       }\r
-                               }\r
-                               \r
-                               return array;\r
-                               \r
-                       }\r
-                       \r
-               });             \r
-       }\r
-       \r
-       public TreeMap<Double,Double> getHistory(String ident) {\r
-               TreeMap<Double,Double> result = history.get(ident);\r
-               if(result == null) {\r
-                       result = new TreeMap<Double,Double>();\r
-                       history.put(ident, result);\r
-               }\r
-               return result;\r
-       }\r
-\r
-       @Override\r
-       public Object getValue(int key) {\r
-               return valueTable[key];\r
-       }\r
-       \r
-       @Override\r
-       public void put(int key, Object value) {\r
-               valueTable[key] = value;\r
-       }\r
-       \r
-       public void put(String key, Object value) {\r
-               named.put(key, value);\r
-       }\r
-\r
-       public void setSize(int size) {\r
-               this.size = size;\r
-       }\r
-       \r
-       @Override\r
-       public int offset() {\r
-               return size;\r
-       }\r
-       \r
-       @Override\r
-       public ISystem getSystem() {\r
-               return this;\r
-       }\r
-       \r
-       @Override\r
-       public boolean initial() {\r
-               return initial;\r
-       }\r
-       \r
-       public Object evaluateFunction(IEnvironment environment, String name, ArgumentList args) {\r
-               return model.evaluateFunction(environment, name, args);\r
-       }\r
-       \r
-       @Override\r
-       public double time() {\r
-               return time;\r
-       }\r
-\r
-       double[] valueArray;\r
-       int[] valueIndices;\r
-       \r
-       public void addIndexed(int dimensions[], ArrayList<String> keys, String prefix, int d) {\r
-\r
-               if(d == dimensions.length) {\r
-                       keys.add(prefix);\r
-                       return;\r
-               }\r
-\r
-               String prefix2 = prefix;\r
-               if(d== 0) prefix2 += "[";\r
-               if(d>0) prefix2 += ",";\r
-\r
-               for(int i=0;i<dimensions[d];i++) {\r
-                       String prefix3 = prefix2 + (i+1);\r
-                       if(d+1 == dimensions.length) prefix3 += "]";\r
-                       addIndexed(dimensions, keys, prefix3, d+1);\r
-               }\r
-               \r
-       }\r
-       \r
-       public int length(int[] dimension) {\r
-               int result = 1;\r
-               for(int d : dimension) result *=d;\r
-               return result;\r
-       }\r
-       \r
-       public void addVariable(String name, int index, int[] dimensions, ArrayList<String> keys, TIntArrayList indices, Set<String> used) {\r
-\r
-               if(!used.add(name)) return;\r
-               \r
-               if(dimensions == null) {\r
-                       keys.add(name);\r
-                       indices.add(index);\r
-               } else {\r
-                       addIndexed(dimensions, keys, name, 0);\r
-                       for(int i=0;i<length(dimensions);i++)\r
-                               indices.add(index+i);\r
-               }\r
-\r
-       }\r
-       \r
-       public void addVariable(Variable var, ArrayList<String> keys, TIntArrayList indices, Set<String> used) {\r
-               addVariable(var.base.name, var.base.index, var.base.dimensions, keys, indices, used);\r
-       }\r
-\r
-       public String[] getValueKeyArray() {\r
-               \r
-               ArrayList<String> keys = new ArrayList<String>();\r
-               TIntArrayList indices = new TIntArrayList();\r
-               Set<String> used = new HashSet<String>();\r
-               \r
-               for (int i = 0; i < model.assignmentArray.length; i++) {\r
-                       addVariable(model.assignmentArray[i].target, keys, indices, used);\r
-               }\r
-               \r
-               for (int i = 0; i < model.derivativeArray.length; i++) {\r
-                       addVariable(model.derivativeArray[i].target, keys, indices, used);\r
-               }\r
-               \r
-               // NOTE: there is room for optimization as parameter values that do not\r
-               // change should only be obtained once (and parameter values that do\r
-               // change are (possibly) included in assignments or derivatives)\r
-               for(int i = 0; i < model.parameterArray.length; i++) {\r
-                       addVariable(model.parameterArray[i].variable, keys, indices, used);\r
-               }\r
-               \r
-               for(Map.Entry<String,VariableBase> entry : model.copies.entrySet()) {\r
-                       addVariable(entry.getKey(), entry.getValue().index, entry.getValue().dimensions, keys, indices, used);\r
-               }\r
-               \r
-               valueArray = new double[keys.size()];\r
-               \r
-               keyIndexMap.clear();\r
-               for(int i=0;i<keys.size();i++) {\r
-                       String key = keys.get(i);\r
-                       int index = indices.get(i);\r
-                       keyIndexMap.put(key, index);\r
-               }\r
-               \r
-               valueIndices = new int[keys.size()];\r
-               getValueIndices();\r
-               \r
-               return keys.toArray(new String[keys.size()]);\r
-               \r
-       }\r
-       \r
-       public int[] getValueIndices() {\r
-\r
-               int offset = 0;\r
-\r
-               Set<String> used = new HashSet<String>();\r
-\r
-               for (int i = 0; i < model.assignmentArray.length; i++) {\r
-                       Variable v = model.assignmentArray[i].target;\r
-                       if(!used.add(v.base.name)) continue;\r
-                       for(int index=0;index<v.base.dimension();index++)\r
-                               valueIndices[offset++] = v.base.index(this, null)+index;\r
-               }\r
-               \r
-               for (int i = 0; i < model.derivativeArray.length; i++) {\r
-                       Variable v = model.derivativeArray[i].target;\r
-                       if(!used.add(v.base.name)) continue;\r
-                       for(int index=0;index<v.base.dimension();index++) {\r
-                               valueIndices[offset++] = v.base.index(this, null)+index;\r
-                       }\r
-               }\r
-               \r
-               // NOTE: there is room for optimization as parameter values that do not\r
-               // change should only be obtained once (and parameter values that do\r
-               // change are (possibly) included in assignments or derivatives)\r
-               for(int i = 0; i < model.parameterArray.length; i++) {\r
-                       Variable v = model.parameterArray[i].variable;\r
-                       if(!used.add(v.base.name)) continue;\r
-                       for(int index=0;index<v.base.dimension();index++)\r
-                               valueIndices[offset++] = v.base.index(this, null)+index;\r
-               }\r
-\r
-               for(Map.Entry<String,VariableBase> entry : model.copies.entrySet()) {\r
-                       VariableBase base = entry.getValue();\r
-                       if(!used.add(entry.getKey())) continue;\r
-                       for(int index=0;index<base.dimension();index++)\r
-                               valueIndices[offset++] = base.index(this, null)+index;\r
-               }\r
-               \r
-               return valueIndices;\r
-               \r
-       }\r
-\r
-       // TODO: this is probably not smart at all, figure out a better way to obtain results\r
-       public double[] getValueArray() {\r
-\r
-               for(int i=0;i<valueArray.length;i++) {\r
-                       Double value = (Double)getValue(valueIndices[i]);\r
-                       // FIXME: should not be fixed like this\r
-                       if(value == null) value = 0.0;\r
-                       valueArray[i] = value;\r
-               }\r
-               \r
-               return valueArray;\r
-               \r
-//             int offset = 0;\r
-//\r
-//             Set<String> used = new HashSet<String>();\r
-//\r
-//             for (int i = 0; i < model.assignmentArray.length; i++) {\r
-//                     Variable v = model.assignmentArray[i].target;\r
-//                     if(!used.add(v.base.name)) continue;\r
-//                     for(int index=0;index<v.base.dimension();index++)\r
-//                             valueArray[offset++] = (Double)getValue(v.base.index(this, null)+index);\r
-//             }\r
-//             \r
-//             for (int i = 0; i < model.derivativeArray.length; i++) {\r
-//                     Variable v = model.derivativeArray[i].target;\r
-//                     if(!used.add(v.base.name)) continue;\r
-//                     for(int index=0;index<v.base.dimension();index++) {\r
-//                             Double value = (Double)getValue(v.base.index(this, null)+index);\r
-//                             // FIXME: should not be fixed like this\r
-//                             if(value == null) value = 0.0;\r
-//                             valueArray[offset++] = value;\r
-//                     }\r
-//             }\r
-//             \r
-//             // NOTE: there is room for optimization as parameter values that do not\r
-//             // change should only be obtained once (and parameter values that do\r
-//             // change are (possibly) included in assignments or derivatives)\r
-//             for(int i = 0; i < model.parameterArray.length; i++) {\r
-//                     Variable v = model.parameterArray[i].variable;\r
-//                     if(!used.add(v.base.name)) continue;\r
-//                     for(int index=0;index<v.base.dimension();index++)\r
-//                             valueArray[offset++] = (Double)getValue(v.base.index(this, null)+index);\r
-//             }\r
-//\r
-//             for(Map.Entry<String,VariableBase> entry : model.copies.entrySet()) {\r
-//                     VariableBase base = entry.getValue();\r
-//                     if(!used.add(entry.getKey())) continue;\r
-//                     for(int index=0;index<base.dimension();index++)\r
-//                             valueArray[offset++] = (Double)getValue(base.index(this, null)+index);\r
-//             }\r
-//             \r
-//             return valueArray;\r
-               \r
-       }\r
-\r
-       @Override\r
-       public Object getNamedValue(String key) {\r
-               return named.get(key);\r
-       }\r
-\r
-       public void setValue(String key, double value) {\r
-               int index = keyIndexMap.get(key);\r
-               put(index, value);\r
-       }\r
-       \r
-       public void setAssigned(Object ass) {\r
-               assigned.add(ass);\r
-       }\r
-       \r
-       public boolean isAssigned(Object o) {\r
-               return assigned.contains(o);\r
-       }\r
-       \r
-       private Set<Object> assigned = new HashSet<Object>();\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2013 Semantum Oy.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Semantum Oy - initial API and implementation
+ *     VTT Technical Research Centre of Finland - #4488
+ *******************************************************************************/
+package fi.semantum.sysdyn.solver;
+
+import fi.semantum.sysdyn.solver.Array.Modifier;
+import gnu.trove.list.array.TIntArrayList;
+import gnu.trove.map.hash.TObjectIntHashMap;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.TreeMap;
+
+interface Fn {
+       public Object evaluate(IEnvironment environment, int argc);
+       public Object evaluateInput(IEnvironment environment, int argPosition);
+       public void setLocals(IEnvironment environment);
+       public int offset();
+       public Variable[] parameters(int args);
+}
+
+abstract class Fn1 implements Fn {
+
+       Variable[] parameters;
+       
+       public Fn1(int pCount) {
+               if(pCount > 0) {
+                       parameters = new Variable[pCount];
+                       for(int i=0;i<pCount;i++)
+                               parameters[i] = new Variable(new VariableBase("", i));
+               }
+       }
+       
+       public int offset() {
+               return 0;
+       }
+       
+       public Variable[] parameters(int argc) {
+               if(parameters != null) return parameters;
+               else {
+                       Variable[] ret = new Variable[argc];
+                       for(int i=0;i<argc;i++)
+                               ret[i] = new Variable(new VariableBase("", i));
+                       return ret;
+               }
+       }
+       
+       @Override
+       public void setLocals(IEnvironment environment) {
+       }
+       
+       @Override
+       public Object evaluateInput(IEnvironment environment, int argPosition) {
+               // Function frame size is constant - this is called when padding is needed
+               return null;
+       }
+       
+}
+
+abstract class FnRandom extends Fn1 {
+       
+       protected Random random = null;
+
+       public FnRandom(int pCount) {
+               super(pCount);
+       }
+       
+}
+
+final public class Environment implements IEnvironment, ISystem {
+       
+       final Map<String,Object> named = new HashMap<String,Object>();
+
+       public Model model;
+       public double step;
+       public double time;
+       public boolean initial = true;
+       public int size;
+       
+       public Object[] valueTable;
+       
+       public TObjectIntHashMap<String> keyIndexMap = new TObjectIntHashMap<String>(); 
+
+       HashMap<String,TreeMap<Double,Double>> history = new HashMap<String,TreeMap<Double,Double>>();
+
+       public Environment(Model model, double step, double start) {
+               this.model = model;
+               this.step = step;
+               this.time = start;
+               
+               model.functions.put("sin", new Fn1(2) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               return Math.sin(x);
+                       }
+                       
+               });
+               model.functions.put("cos", new Fn1(2) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               return Math.cos(x);
+                       }
+                       
+               });
+               model.functions.put("tan", new Fn1(2) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               return Math.tan(x);
+                       }
+                       
+               });
+               model.functions.put("asin", new Fn1(2) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               return Math.asin(x);
+                       }
+                       
+               });
+               model.functions.put("acos", new Fn1(2) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               return Math.acos(x);
+                       }
+                       
+               });
+               model.functions.put("atan", new Fn1(2) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               return Math.atan(x);
+                       }
+                       
+               });
+               model.functions.put("toRadians", new Fn1(1) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               return Math.toRadians(x);
+                       }
+                       
+               });
+               model.functions.put("toDegrees", new Fn1(1) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               return Math.toDegrees(x);
+                       }
+                       
+               });
+               model.functions.put("exp", new Fn1(1) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               return Math.exp(x);
+                       }
+                       
+               });
+               model.functions.put("log", new Fn1(1) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               return Math.log(x);
+                       }
+                       
+               });
+               model.functions.put("log10", new Fn1(1) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               return Math.log10(x);
+                       }
+                       
+               });
+               model.functions.put("sqrt", new Fn1(1) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               return Math.sqrt(x);
+                       }
+                       
+               });
+               model.functions.put("cbrt", new Fn1(1) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               return Math.cbrt(x);
+                       }
+                       
+               });
+               model.functions.put("mod", new Fn1(2) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               Double y = (Double)environment.getValue(1);
+                               return mod(x, y);
+                       }
+
+                       double mod(double x, double y) {
+                               return x - Math.floor(x/y)*y;
+                       }
+
+               });
+               model.functions.put("rem", new Fn1(2) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               Double y = (Double)environment.getValue(1);
+                               return Math.IEEEremainder(x, y);
+                       }
+
+               });
+               model.functions.put("ceil", new Fn1(1) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Object value = environment.getValue(0);
+                               if(value instanceof Double) {
+                                       return Math.ceil((Double)value);
+                               } else if(value instanceof Array) {
+                                       return ((Array)value).copy(new Modifier() {
+
+                                               @Override
+                                               public Object modify(Object o) {
+                                                       if(o instanceof Double) {
+                                                               return Math.ceil((Double)o);
+                                                       } else {
+                                                               throw new IllegalStateException();
+                                                       }
+                                               }
+                                               
+                                       });
+                               } else {
+                                       throw new IllegalStateException();
+                               }
+                       }
+                       
+               });
+               model.functions.put("floor", new Fn1(1) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Object value = environment.getValue(0);
+                               if(value instanceof Double) {
+                                       return Math.floor((Double)value);
+                               } else if(value instanceof Array) {
+                                       return ((Array)value).copy(new Modifier() {
+
+                                               @Override
+                                               public Object modify(Object o) {
+                                                       if(o instanceof Double) {
+                                                               return Math.floor((Double)o);
+                                                       } else {
+                                                               throw new IllegalStateException();
+                                                       }
+                                               }
+                                               
+                                       });
+                               } else {
+                                       throw new IllegalStateException();
+                               }
+                       }
+                       
+               });
+               model.functions.put("rint", new Fn1(2) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               return Math.rint(x);
+                       }
+                       
+               });
+               model.functions.put("atan2", new Fn1(2) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               Double y = (Double)environment.getValue(1);
+                               return Math.atan2(x, y);
+                       }
+                       
+               });
+               model.functions.put("pow", new Fn1(2) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               Double y = (Double)environment.getValue(1);
+                               return Math.pow(x, y);
+                       }
+                       
+               });
+               model.functions.put("round", new Fn1(2) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               long result = Math.round(x); 
+                               return (int)result;
+                       }
+                       
+               });
+
+               model.functions.put("abs", new Fn1(2) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Object o = environment.getValue(0);
+                               if(o instanceof Double) {
+                                       return Math.abs((Double)o);
+                               } else if (o instanceof Integer) {
+                                       return Math.abs((Integer)o);
+                               } else {
+                                       throw new IllegalStateException("Unsupported argument to abs: " + o);
+                               }
+                       }
+                       
+               });
+
+               model.functions.put("min", new Fn1(5) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Object first = environment.getValue(0);
+                               if(first instanceof Double) {
+                                       Double result = (Double)first;
+                                       for(int i=1;i<argc;i++) {
+                                               Double d = (Double)environment.getValue(i);
+                                               if(d < result) result = d;
+                                       }
+                                       return result;
+                               } else if (first instanceof Integer) {
+                                       Integer result = (Integer )first;
+                                       for(int i=1;i<argc;i++) {
+                                               Integer  d = (Integer )environment.getValue(i);
+                                               if(d < result) result = d;
+                                       }
+                                       return result;
+                               } else {
+                                       throw new IllegalStateException("Unsupported argument to min: " + first);
+                               }
+                       }
+                       
+               });
+               model.functions.put("max", new Fn1(5) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Object first = environment.getValue(0);
+                               if(first instanceof Double) {
+                                       Double result = (Double)first;
+                                       for(int i=1;i<argc;i++) {
+                                               Double d = (Double)environment.getValue(i);
+                                               if(d > result) result = d;
+                                       }
+                                       return result;
+                               } else if (first instanceof Integer) {
+                                       Integer result = (Integer )first;
+                                       for(int i=1;i<argc;i++) {
+                                               Integer  d = (Integer )environment.getValue(i);
+                                               if(d > result) result = d;
+                                       }
+                                       return result;
+                               } else {
+                                       throw new IllegalStateException("Unsupported argument to max: " + first);
+                               }
+                       }
+                       
+               });
+
+               model.functions.put("ulp", new Fn1(2) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               return Math.ulp(x);
+                       }
+                       
+               });
+
+               model.functions.put("signum", new Fn1(2) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               return Math.signum(x);
+                       }
+                       
+               });
+
+               model.functions.put("sinh", new Fn1(2) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               return Math.sinh(x);
+                       }
+                       
+               });
+
+               model.functions.put("cosh", new Fn1(2) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               return Math.cosh(x);
+                       }
+                       
+               });
+               
+               model.functions.put("cross", new Fn1(2) {
+
+                       //Returns index 0 with input 1 as a Double
+                       //index 1 with input 2 as a Double, etc.
+                       private Double get(Array a, int crossIndex) {
+                               return (Double)a.element(crossIndex - 1);
+                       }
+                       
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Array X = (Array)environment.getValue(0);
+                               Array Y = (Array)environment.getValue(1);
+                               if(X.dimension() != 3 && Y.dimension() != 3) {
+                                       return null;
+                               } else {
+                                       Array result = new Array(3);
+                                       Double a = (get( X,2 )*get( Y,3 )) - (get( X,3 )*get( Y,2 ));
+                                       Double b = (get( X,3 )*get( Y,1 )) - (get( X,1 )*get( Y,3 ));
+                                       Double c = (get( X,1 )*get( Y,2 )) - (get( X,2 )*get( Y,1 ));
+                                       result.addElement(a);
+                                       result.addElement(b);
+                                       result.addElement(c);
+                                       return result;
+                               }
+                       }
+                       
+               });
+               model.functions.put("tanh", new Fn1(2) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               return Math.tanh(x);
+                       }
+                       
+               });
+
+               model.functions.put("hypot", new Fn1(2) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               Double y = (Double)environment.getValue(1);
+                               return Math.hypot(x, y);
+                       }
+                       
+               });
+
+               model.functions.put("expm1", new Fn1(2) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               return Math.expm1(x);
+                       }
+                       
+               });
+
+               model.functions.put("log1p", new Fn1(2) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               return Math.log1p(x);
+                       }
+                       
+               });
+
+               model.functions.put("copySign", new Fn1(2) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               Double y = (Double)environment.getValue(1);
+                               return Math.copySign(x, y);
+                       }
+                       
+               });
+
+               model.functions.put("getExponent", new Fn1(2) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               return Math.getExponent(x);
+                       }
+                       
+               });
+
+               model.functions.put("nextAfter", new Fn1(2) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               Double y = (Double)environment.getValue(1);
+                               return Math.nextAfter(x, y);
+                       }
+                       
+               });
+
+               model.functions.put("nextUp", new Fn1(2) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               return Math.nextUp(x);
+                       }
+                       
+               });
+
+               model.functions.put("scalb", new Fn1(2) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               Integer y = (Integer)environment.getValue(1);
+                               return Math.scalb(x, y);
+                       }
+                       
+               });
+
+               model.functions.put("ndims", new Fn1(1) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Array array = (Array)environment.getValue(0);
+                               return Double.valueOf(array.elements().size());
+                       }
+                       
+               });
+               
+               model.functions.put("diagonal", new Fn1(1) {
+                       
+                       private Array make(int[] dims, int pos) {
+                               Array result = new Array();
+                               int d = dims[pos];
+                               if(pos == dims.length - 1) {
+                                       for(int i=0;i<d;i++) result.addElement(0.0);
+                               } else {
+                                       for(int i=0;i<d;i++) result.addElement(make(dims, pos+1));
+                               }
+                               return result;
+                       }
+                       
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Array diagonalInputValues = (Array)environment.getValue(0);
+                               int s = diagonalInputValues.dimension();
+                               Array result = make(new int[] {s, s}, 0);
+                               for(int i = 0; i < s; i++) {
+                                       Array sub = (Array)result.element(i);
+                                       sub.setElement(i, diagonalInputValues.element(i));
+                                       result.setElement(i, sub);
+                               }
+                               return result;
+                       }
+               });
+               
+               model.functions.put("identity", new Fn1(1) {
+                       
+                       private Array make(int[] dims, int pos) {
+                               Array result = new Array();
+                               int d = dims[pos];
+                               if(pos == dims.length - 1) {
+                                       for(int i=0;i<d;i++) result.addElement(0.0);
+                               } else {
+                                       for(int i=0;i<d;i++) result.addElement(make(dims, pos+1));
+                               }
+                               return result;
+                       }
+                       
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double size = (Double)environment.getValue(0);
+                               int s = size.intValue();
+                               Array result = make(new int[] {s, s}, 0);
+                               for(int i = 0; i < s; i++) {
+                                       Array sub = (Array)result.element(i);
+                                       sub.setElement(i, 1.0);
+                                       result.setElement(i, sub);
+                               }
+                               return result;
+                       }
+               });
+               
+               model.functions.put("linspace", new Fn1(3){
+                       
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double startValue = (Double)environment.getValue(0);
+                               Double endValue = (Double)environment.getValue(1);
+                               Double n = (Double)environment.getValue(2);
+                               
+                               if(n <= 2) {
+                                       Array res = new Array(2);
+                                       res.addElement(startValue);
+                                       res.addElement(endValue);
+                                       return res;
+                               }
+
+                               Array result = new Array(n.intValue());
+                               Double stepSize = (endValue - startValue) / (n - 1);
+                               for(int i = 0; i < (n-1); i++) {
+                                       result.addElement(startValue + (stepSize*i));
+                               }
+                               result.addElement(endValue);
+                               return result;
+                       }
+                       
+               });
+               
+               model.functions.put("size", new Fn1(-1){
+                       
+                       private List<Integer> findRecursive(Array array) {
+                               int s = array.elements().size();
+                               List<Integer> result = new ArrayList<>();
+                               result.add(s);
+                               if(s > 0) {
+                                       Object e = array.element(0);
+                                       if(e instanceof Array) {
+                                               List<Integer> found = findRecursive((Array)e);
+                                               result.addAll(found);
+                                       }
+                               }
+                               return result;
+                       }
+                       
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               if(argc==1) {
+                                       Array array = (Array)environment.getValue(0);
+                                       List<Integer> resultL = findRecursive(array);
+                                       Array result = new Array();
+                                       for(Integer i : resultL) {
+                                               result.addElement(i);
+                                       }
+                                       return result;
+                               } else if(argc==2) {
+                                       Array array = (Array)environment.getValue(0);
+                                       Double col = (Double)environment.getValue(1);
+                                       return Double.valueOf(array.size(col.intValue()));
+                               } else {
+                                       return null;
+                               }
+                       }
+                       
+               });
+               
+               model.functions.put("zidz", new Fn1(2) {
+
+                       private Object evaluate(Double p1, Double p2) {
+                               if(Math.abs(p2) < 1e-12) return 0.0;
+                               else return p1 / p2;
+                       }
+                       
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Object o1 = environment.getValue(0);
+                               Object o2 = environment.getValue(1);
+                               
+                               if(o1 instanceof Double && o2 instanceof Double) {
+                                       return evaluate((Double)o1, (Double)o2);
+                               }
+                               if(o1 instanceof Array && o2 instanceof Array) {
+                                       Array la = (Array)o1;
+                                       Array ra = (Array)o2;
+                                       Collection<Object> lae = la.elements();
+                                       Collection<Object> rae = ra.elements();
+                                       if(lae.size() != rae.size()) throw new UnsupportedOperationException();
+                                       Iterator<Object> li = lae.iterator();
+                                       Iterator<Object> ri = rae.iterator();
+                                       Array result = new Array();
+                                       for(int i=0;i<lae.size();i++) {
+                                               double ld = (Double)li.next();
+                                               double rd = (Double)ri.next();
+                                               result.addElement(evaluate(ld,rd));
+                                       }
+                                       return result;
+                               }
+                               throw new IllegalStateException();
+                               
+                               
+                       }
+                       
+               });
+               model.functions.put("xidz", new Fn1(3) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double p1 = (Double)environment.getValue(0);
+                               Double p2 = (Double)environment.getValue(1);
+                               Double x = (Double)environment.getValue(2);
+                               if(Math.abs(p2) < 1e-12) return x;
+                               else return p1 / p2;
+                       }
+                       
+               });
+               model.functions.put("availabilityExternal", new Fn1(4) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double numberOfDays = (Double)environment.getValue(0);
+                               Double numberOfDesigners = (Double)environment.getValue(1);
+                               @SuppressWarnings("unused")
+                               Double rseed = (Double)environment.getValue(2);
+                               @SuppressWarnings("unused")
+                               Array availability = (Array)environment.getValue(3);
+                               Array result = new Array();
+                               for(int i=0;i<numberOfDays.intValue()*numberOfDesigners.intValue();i++) {
+                                       result.addElement(Math.random());
+                               }
+                               return result;
+                       }
+                       
+               });
+               model.functions.put("pre", new Fn1(1) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               
+                               return (Double)environment.getValue(0);
+                               
+                       }
+                       
+               });
+               model.functions.put("fill", new Fn1(3) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double value = (Double)environment.getValue(0);
+                               Double dim1 = (Double)environment.getValue(1);
+                               if(argc == 2) {
+                                       Array result = new Array();
+                                       for(int i=0;i<dim1.intValue();i++) {
+                                               result.addElement(value);
+                                       }
+                                       return result;
+                               } else if(argc == 3) {
+                                       Double dim2 = (Double)environment.getValue(2);
+                                       Array result = new Array();
+                                       for(int i=0;i<dim1.intValue();i++) {
+                                               Array array = new Array();
+                                               for(int j=0;j<dim2.intValue();j++) {
+                                                       array.addElement(value);
+                                               }
+                                               result.addElement(array);
+                                       }
+                                       return result;
+                               } else throw new UnsupportedOperationException();
+                       }
+                       
+               });
+               model.functions.put("randomNumber", new FnRandom(2) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double seed = (Double)environment.getValue(0);
+                               
+                               if (random == null) {
+                                       random = new Random(Double.doubleToLongBits(seed));
+                               }
+                               
+                               return random.nextDouble();
+                       }
+                       
+               });
+               model.functions.put("randomUniform", new FnRandom(4) {
+                       
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double min = (Double)environment.getValue(0);
+                               Double max = (Double)environment.getValue(1);
+                               Double seed = (Double)environment.getValue(2);
+                               
+                               // the fourth parameter should be "time" and is only necessary
+                               // because sysdyn treats all functions as side-effect-free and
+                               // does not evaluate a function "needlessly" if the parameters
+                               // do not change. the parameter does not actually do anything
+                               // though so we can safely ignore it here
+                               
+                               if (random == null) {
+                                       random = new Random(Double.doubleToLongBits(seed));
+                               }
+                               return random.nextDouble() * (max - min) + min;
+                       }
+                       
+               });
+               model.functions.put("randomNormal", new FnRandom(6) {
+                       
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double min = (Double)environment.getValue(0);
+                               Double max = (Double)environment.getValue(1);
+                               Double mean = (Double)environment.getValue(2);
+                               Double stdev = (Double)environment.getValue(3);
+                               Double seed = (Double)environment.getValue(4);
+                               
+                               if (random == null) {
+                                       random = new Random(Double.doubleToLongBits(seed));
+                               }
+                               
+                               // TODO: use an actual library for this stuff
+                               
+                               return NormalDistribution.uniformVariableToNormal(min, max, mean, stdev, random.nextDouble());
+                       }
+                       
+               });
+               model.functions.put("initial", new Fn1(0) {
+
+                       @Override
+                       public Object evaluate(IEnvironment _environment, int argc) {
+                               return _environment.getSystem().initial();
+                       }
+                       
+               });
+               model.functions.put("noEvent", new Fn1(1) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               
+                               return environment.getValue(0);
+                               
+                       }
+                       
+               });
+               model.functions.put("integer", new Fn1(1) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Object value = environment.getValue(0);
+                               if(value instanceof Double) {
+                                       double d = ((Double)value).intValue(); 
+                                       return d;
+                               } else if(value instanceof Array) {
+                                       return ((Array)value).copy(new Modifier() {
+
+                                               @Override
+                                               public Object modify(Object o) {
+                                                       if(o instanceof Double) {
+                                                               double d = ((Double)o).intValue(); 
+                                                               return d;
+                                                       } else {
+                                                               throw new IllegalStateException();
+                                                       }
+                                               }
+                                               
+                                       });
+                               } else {
+                                       throw new IllegalStateException();
+                               }
+                       }
+                       
+               });
+               model.functions.put("sum", new Fn1(1) {
+
+                       public double sum(Array a) {
+                               double res = 0;
+                               for(Object element : a.elements()) {
+                                       if(element instanceof Array) {
+                                               res += sum((Array)element);
+                                       } else if(element instanceof Double) {
+                                               res += (Double)element;
+                                       } else {
+                                               throw new IllegalStateException("Tried to sum a non-numerical array");
+                                       }
+                               }
+                               return res;
+                       }
+                       
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Object value = environment.getValue(0);
+                               if(value instanceof Double) return (Double)value;
+                               Array arr = (Array)value;
+                               return sum(arr);
+                       }
+                       
+               });
+               
+               model.functions.put("product", new Fn1(1) {
+                       
+                       public Double productRecursive(Array a) {
+                               List<Object> elements = a.elements();
+                               Double result = 1.0;
+                               if(elements.size() > 0) {
+                                       if(elements.get(0) instanceof Array) {
+                                               for(Object e : elements) {
+                                                       result = result * productRecursive((Array)e);
+                                               }
+                                       } else {
+                                               for(Object e : elements) {
+                                                       result = result * (Double)e;
+                                               }
+                                       }
+                               }
+                               return result;
+                       }
+                       
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Array array = (Array)environment.getValue(0);
+                               Double result = productRecursive(array);
+                               return result;
+                       }
+               });
+               
+               model.functions.put("ones", new Fn1(-1) {
+
+                       private Array make(int[] dims, int pos) {
+                               Array result = new Array();
+                               int d = dims[pos];
+                               if(pos == dims.length - 1) {
+                                       for(int i=0;i<d;i++) result.addElement(1.0);
+                               } else {
+                                       for(int i=0;i<d;i++) result.addElement(make(dims, pos+1));
+                               }
+                               return result;
+                       }
+                       
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               int[] dims = new int[argc];
+                               for(int i=0;i<argc;i++) {
+                                       Double d = (Double)environment.getValue(i);
+                                       dims[i] = d.intValue();
+                               }
+                               return make(dims, 0);
+                       }
+                       
+               });
+               model.functions.put("zeros", new Fn1(-1) {
+
+                       private Array make(int[] dims, int pos) {
+                               Array result = new Array();
+                               int d = dims[pos];
+                               if(pos == dims.length - 1) {
+                                       for(int i=0;i<d;i++) result.addElement(0.0);
+                               } else {
+                                       for(int i=0;i<d;i++) result.addElement(make(dims, pos+1));
+                               }
+                               return result;
+                       }
+                       
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               int[] dims = new int[argc];
+                               for(int i=0;i<argc;i++) {
+                                       Double d = (Double)environment.getValue(i);
+                                       dims[i] = d.intValue();
+                               }
+                               return make(dims, 0);
+                       }
+                       
+               });
+               model.functions.put("transpose", new Fn1(1) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               
+                               Array array = (Array)environment.getValue(0);
+                               return array.transposed();
+                               
+                       }
+                       
+               });
+               
+               model.functions.put("div", new Fn1(2) {
+
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               int x = ((Double)environment.getValue(0)).intValue();
+                               int y = ((Double)environment.getValue(1)).intValue();
+                               return (double)(x/y);
+                       }
+                       
+               });
+               
+               
+               model.functions.put("sign", new Fn1(1) {
+                       @Override
+                       public Object evaluate(IEnvironment environment, int argc) {
+                               Double x = (Double)environment.getValue(0);
+                               if (x > 0.0) return 1.0;
+                               else if (x < 0.0) return -1.0;
+                               else return 0.0;
+                       }
+               });
+               
+               model.functions.put("delay", new Fn1(4) {
+                       
+                       @Override
+                       public Object evaluate(IEnvironment _environment, int argc) {
+                       
+                               String ident = (String)_environment.getValue(0);
+                               Double value = (Double)_environment.getValue(1);
+                               Double p1 = (Double)_environment.getValue(2);
+                               
+                               ISystem system = _environment.getSystem();
+                               
+                               TreeMap<Double,Double> history = system.getHistory(ident);
+                               double time = system.time();
+                               
+                               history.put(time, value);
+                               
+                               Double key = history.ceilingKey(time-p1);
+                               
+                               return history.get(key);
+                               
+                       }
+                       
+               });             
+               model.functions.put("cat", new Fn1(10) {
+                       
+                       @Override
+                       public Object evaluate(IEnvironment _environment, int argc) {
+                       
+                               Double dim = (Double)_environment.getValue(0);
+                               
+                               Array array = new Array();
+                               for(int i=1;i<argc;i++) {
+                                       Object ar = _environment.getValue(i);
+                                       if (ar instanceof Array) {
+                                               Array a = (Array)ar;
+                                               for(Object o : a.elements())
+                                                       array.addElement(o);
+                                       } else {
+                                               throw new IllegalStateException();
+                                       }
+                               }
+                               
+                               return array;
+                               
+                       }
+                       
+               });             
+       }
+       
+       public TreeMap<Double,Double> getHistory(String ident) {
+               TreeMap<Double,Double> result = history.get(ident);
+               if(result == null) {
+                       result = new TreeMap<Double,Double>();
+                       history.put(ident, result);
+               }
+               return result;
+       }
+
+       @Override
+       public Object getValue(int key) {
+               return valueTable[key];
+       }
+       
+       @Override
+       public void put(int key, Object value) {
+               valueTable[key] = value;
+       }
+       
+       public void put(String key, Object value) {
+               named.put(key, value);
+       }
+
+       public void setSize(int size) {
+               this.size = size;
+       }
+       
+       @Override
+       public int offset() {
+               return size;
+       }
+       
+       @Override
+       public ISystem getSystem() {
+               return this;
+       }
+       
+       @Override
+       public boolean initial() {
+               return initial;
+       }
+       
+       public Object evaluateFunction(IEnvironment environment, String name, ArgumentList args) {
+               return model.evaluateFunction(environment, name, args);
+       }
+       
+       @Override
+       public double time() {
+               return time;
+       }
+
+       double[] valueArray;
+       int[] valueIndices;
+       
+       public void addIndexed(int dimensions[], ArrayList<String> keys, String prefix, int d) {
+
+               if(d == dimensions.length) {
+                       keys.add(prefix);
+                       return;
+               }
+
+               String prefix2 = prefix;
+               if(d== 0) prefix2 += "[";
+               if(d>0) prefix2 += ",";
+
+               for(int i=0;i<dimensions[d];i++) {
+                       String prefix3 = prefix2 + (i+1);
+                       if(d+1 == dimensions.length) prefix3 += "]";
+                       addIndexed(dimensions, keys, prefix3, d+1);
+               }
+               
+       }
+       
+       public int length(int[] dimension) {
+               int result = 1;
+               for(int d : dimension) result *=d;
+               return result;
+       }
+       
+       public void addVariable(String name, int index, int[] dimensions, ArrayList<String> keys, TIntArrayList indices, Set<String> used) {
+
+               if(!used.add(name)) return;
+               
+               if(dimensions == null) {
+                       keys.add(name);
+                       indices.add(index);
+               } else {
+                       addIndexed(dimensions, keys, name, 0);
+                       for(int i=0;i<length(dimensions);i++)
+                               indices.add(index+i);
+               }
+
+       }
+       
+       public void addVariable(Variable var, ArrayList<String> keys, TIntArrayList indices, Set<String> used) {
+               addVariable(var.base.name, var.base.index, var.base.dimensions, keys, indices, used);
+       }
+
+       public String[] getValueKeyArray() {
+               
+               ArrayList<String> keys = new ArrayList<String>();
+               TIntArrayList indices = new TIntArrayList();
+               Set<String> used = new HashSet<String>();
+               
+               for (int i = 0; i < model.assignmentArray.length; i++) {
+                       addVariable(model.assignmentArray[i].target, keys, indices, used);
+               }
+               
+               for (int i = 0; i < model.derivativeArray.length; i++) {
+                       addVariable(model.derivativeArray[i].target, keys, indices, used);
+               }
+               
+               // NOTE: there is room for optimization as parameter values that do not
+               // change should only be obtained once (and parameter values that do
+               // change are (possibly) included in assignments or derivatives)
+               for(int i = 0; i < model.parameterArray.length; i++) {
+                       addVariable(model.parameterArray[i].variable, keys, indices, used);
+               }
+               
+               for(Map.Entry<String,VariableBase> entry : model.copies.entrySet()) {
+                       addVariable(entry.getKey(), entry.getValue().index, entry.getValue().dimensions, keys, indices, used);
+               }
+               
+               valueArray = new double[keys.size()];
+               
+               keyIndexMap.clear();
+               for(int i=0;i<keys.size();i++) {
+                       String key = keys.get(i);
+                       int index = indices.get(i);
+                       keyIndexMap.put(key, index);
+               }
+               
+               valueIndices = new int[keys.size()];
+               getValueIndices();
+               
+               return keys.toArray(new String[keys.size()]);
+               
+       }
+       
+       public int[] getValueIndices() {
+
+               int offset = 0;
+
+               Set<String> used = new HashSet<String>();
+
+               for (int i = 0; i < model.assignmentArray.length; i++) {
+                       Variable v = model.assignmentArray[i].target;
+                       if(!used.add(v.base.name)) continue;
+                       for(int index=0;index<v.base.dimension();index++)
+                               valueIndices[offset++] = v.base.index(this, null)+index;
+               }
+               
+               for (int i = 0; i < model.derivativeArray.length; i++) {
+                       Variable v = model.derivativeArray[i].target;
+                       if(!used.add(v.base.name)) continue;
+                       for(int index=0;index<v.base.dimension();index++) {
+                               valueIndices[offset++] = v.base.index(this, null)+index;
+                       }
+               }
+               
+               // NOTE: there is room for optimization as parameter values that do not
+               // change should only be obtained once (and parameter values that do
+               // change are (possibly) included in assignments or derivatives)
+               for(int i = 0; i < model.parameterArray.length; i++) {
+                       Variable v = model.parameterArray[i].variable;
+                       if(!used.add(v.base.name)) continue;
+                       for(int index=0;index<v.base.dimension();index++)
+                               valueIndices[offset++] = v.base.index(this, null)+index;
+               }
+
+               for(Map.Entry<String,VariableBase> entry : model.copies.entrySet()) {
+                       VariableBase base = entry.getValue();
+                       if(!used.add(entry.getKey())) continue;
+                       for(int index=0;index<base.dimension();index++)
+                               valueIndices[offset++] = base.index(this, null)+index;
+               }
+               
+               return valueIndices;
+               
+       }
+
+       // TODO: this is probably not smart at all, figure out a better way to obtain results
+       public double[] getValueArray() {
+
+               for(int i=0;i<valueArray.length;i++) {
+                       Double value = (Double)getValue(valueIndices[i]);
+                       // FIXME: should not be fixed like this
+                       if(value == null) value = 0.0;
+                       valueArray[i] = value;
+               }
+               
+               return valueArray;
+               
+//             int offset = 0;
+//
+//             Set<String> used = new HashSet<String>();
+//
+//             for (int i = 0; i < model.assignmentArray.length; i++) {
+//                     Variable v = model.assignmentArray[i].target;
+//                     if(!used.add(v.base.name)) continue;
+//                     for(int index=0;index<v.base.dimension();index++)
+//                             valueArray[offset++] = (Double)getValue(v.base.index(this, null)+index);
+//             }
+//             
+//             for (int i = 0; i < model.derivativeArray.length; i++) {
+//                     Variable v = model.derivativeArray[i].target;
+//                     if(!used.add(v.base.name)) continue;
+//                     for(int index=0;index<v.base.dimension();index++) {
+//                             Double value = (Double)getValue(v.base.index(this, null)+index);
+//                             // FIXME: should not be fixed like this
+//                             if(value == null) value = 0.0;
+//                             valueArray[offset++] = value;
+//                     }
+//             }
+//             
+//             // NOTE: there is room for optimization as parameter values that do not
+//             // change should only be obtained once (and parameter values that do
+//             // change are (possibly) included in assignments or derivatives)
+//             for(int i = 0; i < model.parameterArray.length; i++) {
+//                     Variable v = model.parameterArray[i].variable;
+//                     if(!used.add(v.base.name)) continue;
+//                     for(int index=0;index<v.base.dimension();index++)
+//                             valueArray[offset++] = (Double)getValue(v.base.index(this, null)+index);
+//             }
+//
+//             for(Map.Entry<String,VariableBase> entry : model.copies.entrySet()) {
+//                     VariableBase base = entry.getValue();
+//                     if(!used.add(entry.getKey())) continue;
+//                     for(int index=0;index<base.dimension();index++)
+//                             valueArray[offset++] = (Double)getValue(base.index(this, null)+index);
+//             }
+//             
+//             return valueArray;
+               
+       }
+
+       @Override
+       public Object getNamedValue(String key) {
+               return named.get(key);
+       }
+
+       public void setValue(String key, double value) {
+               int index = keyIndexMap.get(key);
+               put(index, value);
+       }
+       
+       public void setAssigned(Object ass) {
+               assigned.add(ass);
+       }
+       
+       public boolean isAssigned(Object o) {
+               return assigned.contains(o);
+       }
+       
+       private Set<Object> assigned = new HashSet<Object>();
+
+}