public static void Run()
        {
            AbstractEnvironment env1 = new AbstractEnvironment();
            env1.SetValue("A", new int[] {0,0,0});
            env1.SetValue("B", false);
            env1.SetValue("I", 32);
            AbstractEnvironment env2 = new AbstractEnvironment();
            env2.SetValue("A", new int[] {0,0,0});
            env2.SetValue("B", false);
            env2.SetValue("I", 32);

            SDDebug.Assert(env1.Equals(env2));

            env2.SetValue("A", new int[] {0,0,1});

            SDDebug.Assert(env1.Equals(env2) == false);
        }
        private static void SpecializeFunctionCall(ILFunction context, 
		                                           ILExpression fcall, 
		                                           string resultVar, 
		                                           StringBuilder source, 
		                                           AbstractEnvironment env, 
		                                           List<FunctionCallContainer> calls)
        {
            var f = fcall.Function;
            //Stdlib function
            if (f.EmbeddedBody != null) {
                if (f.CanBeCalculatedWithoutRun && !fcall.VAList.Any(pexpr => env.IsDynamic(pexpr.Const.ToString()))) {

                    var args = fcall.VAList.Select(ilexpr => ilexpr.Eval(env))
                        .Select(o => (o is Int32) ? o : ( (o is Boolean) ? o : L1Runtime.L1Runtime.GetArrayFromObjectArray((object[])o))).ToArray();
                    var result = f.EmbeddedBody.Invoke(null, args);

                    if (env.IsDynamic(resultVar)) {
                        source.Append("\t"); source.Append(resultVar); source.Append(" := "); RenderConst(result); source.Append(");"); source.Append(System.Environment.NewLine);
                    } else {
                        if (result is L1Runtime.L1Array<int>) {
                            var l1arr = result as L1Runtime.L1Array<int>;
                            var a = new object[l1arr.GetLength()];
                            for (int i = 0; i < a.Length; ++i) {
                                a[i] = l1arr.GetValue(i);
                            }
                            result = a;
                        }
                        env.SetValue(resultVar, result);
                    }
                } else {
                    source.Append("\t"); source.Append(resultVar); source.Append(" := "); source.Append(f.Name); source.Append("(");
                    for (int i = 0; i < fcall.VAList.Count; ++i) {
                        var p = fcall.VAList[i].Const.ToString();
                        if (env.IsDynamic(p)) {
                            source.Append(p);
                        } else {
                            source.Append(RenderConst(env.GetValue(p)));
                        }
                        if (i != fcall.VAList.Count - 1)
                            source.Append(", ");
                    }
                    source.Append(");"); source.Append(System.Environment.NewLine);
                }
            } else {
                if (f.CanBeCalculatedWithoutRun && !fcall.VAList.Any(pexpr => env.IsDynamic(pexpr.Const.ToString()))) {
                    var args = fcall.VAList.Select(ilexpr => ilexpr.Eval(env)).ToArray();
                    var res = f.Call(args);

                    if (env.IsDynamic(resultVar)) {
                        source.Append("\t").Append(resultVar).Append(" := ").Append(RenderConst(res)).Append(";").AppendLine();
                    } else {
                        env.SetValue(resultVar, res);
                    }
                }
                else {
                    var functionCallRef = new FunctionCallContainer {
                        Function = fcall.Function,
                        SourceFunction = context,
                        ResultVariable = resultVar,
                        Parameters = fcall.VAList.Select(ilexpr => env.IsDynamic(ilexpr.Const.ToString()) ? ilexpr.Const.ToString() : env.GetValue(ilexpr.Const.ToString())).ToList()
                    };
                    calls.Add(functionCallRef);
                    source.Append("\t").Append(functionCallRef.ToString()).AppendLine();
                }
            }
        }
        private static FunctionSpecializationResult SpecializeFunction(
		                                                               ILFunction function, 
		                                                               Dictionary<string, object> pars, 
		                                                               List<string> forceDynamic)
        {
            var source = new StringBuilder(1024 * 1024);
            int LabelId = 1;

            var visited = new SpecPointContainer();
            var q = new Queue<SpecPoint>();
            var initEnv = new AbstractEnvironment();
            foreach (var kvp in pars) {
                initEnv.SetValue(kvp.Key, kvp.Value);
            }
            foreach (var kvp in function.LocalTypes) {
                if (pars.ContainsKey(kvp.Key))
                    continue;

                object val = null;
                if (kvp.Value.TypeEnum == L1Runtime.SyntaxTree.VariableTypeEnum.Integer)
                    val = 0;
                if (kvp.Value.TypeEnum == L1Runtime.SyntaxTree.VariableTypeEnum.Bool)
                    val = false;
                if (kvp.Value.TypeEnum == L1Runtime.SyntaxTree.VariableTypeEnum.Char)
                    val = (char)0;
                initEnv.SetValue(kvp.Key, val);
            }
            foreach (var d in forceDynamic) {
                initEnv.SetValue(d, Dynamic.Value);
            }

            initEnv = _bindTimeAnalyze(function, initEnv);
            initEnv.Trace();

            Dictionary<VariableType, List<string>> localVarsInfo = new Dictionary<VariableType, List<string>>();
            foreach (var kvp in function.LocalTypes) {
                if (initEnv.IsDynamic(kvp.Key) && !function.Parameters.Contains(kvp.Key)) {
                    if (localVarsInfo.ContainsKey(kvp.Value) == false) {
                        localVarsInfo.Add(kvp.Value, new List<string>());
                    }
                    localVarsInfo[kvp.Value].Add(kvp.Key);
                }
            }
            foreach (var kvp in localVarsInfo) {
                source.Append("\t"); source.Append(kvp.Key.ToCompileableString()); source.Append(" ");
                for (int i = 0; i < kvp.Value.Count; ++i) {
                    source.Append(kvp.Value[i]);
                    if (i != kvp.Value.Count - 1)
                        source.Append(", ");
                }
                source.Append(";"); source.Append(System.Environment.NewLine);
            }

            foreach (var kvp in pars) {
                if (kvp.Value != Dynamic.Value && initEnv.IsDynamic(kvp.Key)) {
                    source.Append("\t");
                    source.Append(function.LocalTypes[kvp.Key].ToCompileableString());
                    source.Append(" ");
                    source.Append(kvp.Key); source.Append(" := "); source.Append(RenderConst(kvp.Value));
                    source.Append(";"); source.AppendLine();
                }
            }

            source.Append("* Begin of function specialized body"); source.Append(System.Environment.NewLine);

            var initialSp = new SpecPoint { Env = initEnv, P = 1, L = LabelId++ } ;
            q.Enqueue(initialSp);
            initialSp.Env.Freeze();
            visited.AddSpecPoint(initialSp);

            int operations = 0;
            var calls = new List<FunctionCallContainer>();
            while (q.Count != 0) {
                operations++;

                var sp = q.Dequeue();
                var env = sp.Env.Clone();

                source.Append("L_"); source.Append(sp.L); source.Append(":"); source.Append(System.Environment.NewLine);

                bool stopped = false;
                int p = sp.P;

                while (!stopped) {
                    var instr = function.Body[p - 1];

                    if (instr is ILExpression) {
                        var expr = instr as ILExpression;

                        stopped = SpecializeExpression(function, expr, env, source, calls);

                        p++;
                    }
                    else if (instr is ILBranch) {
                        var br = (instr as ILBranch);

                        var condVar = br.Condition.Const.ToString();
                        if (env.IsDynamic(condVar)) {

                            env.Freeze();
                            var succSp = visited.GetSpecPoint(br.SuccessJump, env);
                            var failSp = visited.GetSpecPoint(br.FailJump, env);

                            if (succSp == null) {
                                succSp = new SpecPoint { Env = env, P = br.SuccessJump, L = LabelId++ };
                                q.Enqueue(succSp);
                                visited.AddSpecPoint(succSp);
                            }
                            if (failSp == null) {
                                failSp = new SpecPoint { Env = env, P = br.FailJump, L = LabelId++ };
                                q.Enqueue(failSp);
                                visited.AddSpecPoint(failSp);
                            }

                            source.Append("\tif "); source.Append(condVar); source.Append(" then ");
                            source.Append("goto "); source.Append("L_"); source.Append(succSp.L);
                            source.Append(" else ");
                            source.Append("goto "); source.Append("L_"); source.Append(failSp.L);
                            source.Append(" end;"); source.Append(System.Environment.NewLine);

                            stopped = true;
                        }
                        else {
                            var cond = (bool)env.GetValue(condVar);
                            if (cond)
                                p = br.SuccessJump;
                            else
                                p = br.FailJump;
                        }
                    }
                    else if (instr is ILGoto) {
                        p = (instr as ILGoto).GoTo;
                    }
                    else if (instr is ILReturn) {
                        var ret = (instr as ILReturn);

                        if (ret.Return == null) {
                            source.Append("\treturn;"); source.Append(System.Environment.NewLine);
                        }
                        else {
                            source.Append("\treturn ");
                            var retVar = ret.Return.Const.ToString();
                            if (env.IsDynamic(retVar)) {
                                source.Append(retVar);
                            }
                            else {
                                m_renderConst(source, env.GetValue(retVar));
                            }
                            source.Append(";"); source.Append(System.Environment.NewLine);
                        }

                        stopped = true;
                    }
                }

                //TODO: Stop if too big query and try with more dynamic variables

                if (operations >= MaximimOperations) {
                    var ch = visited.GetMostChangeableVariables();
                    forceDynamic = forceDynamic.Union(ch).ToList();

                    var res1 = SpecializeFunction(function, pars, forceDynamic);
                    return res1;
                }
            }
            source.Append("0").AppendLine();

            //System.Console.WriteLine(source.ToString());

            var r = new FunctionSpecializationResult { FunctionCallsNeedToResolve = calls, Source = source, Function = function, SpecializationRules = pars };
            return r;
        }
        private static bool SpecializeExpression(ILFunction f, ILExpression expr, AbstractEnvironment env, StringBuilder source, List<FunctionCallContainer> calls)
        {
            if (expr.Type == ILExpressionType.Assign) {

                var left = expr.LeftNode;
                var right = expr.RightNode;
                if (left.Type == ILExpressionType.VariableAccess) {
                    var varName = left.Const.ToString();
                    try {
                        var rightRed = right.AbstactReduce(varName, env, f.LocalTypes);
                        if (rightRed is ILExpression) {
                                var fcall = rightRed as ILExpression;

                                SpecializeFunctionCall(f, fcall, varName, source, env, calls);
                                //source.Append("<<FUNCTION_CALL>>");
                        }
                        else if (env.IsDynamic(varName)) {

                            //source.Append(varName); source.Append(" := ");

                            if (rightRed is string) {
                                source.Append("\t"); source.Append(varName); source.Append(" := ");
                                source.Append((string)rightRed);
                                source.Append(";"); source.Append(System.Environment.NewLine);
                            }
                            else {
                                source.Append("\t"); source.Append(varName); source.Append(" := ");
                                source.Append(RenderConst(rightRed));
                                source.Append(";"); source.Append(System.Environment.NewLine);
                            }
                        }
                        else {
                            object val = rightRed;

                            System.Diagnostics.Debug.Assert(val is string == false);
                            System.Diagnostics.Debug.Assert(val is ILExpression == false);

                            env.SetValue(varName, val);
                        }
                    }
                    catch (NullReferenceException) {
                        source.Append("\t__spec_raise_null_ref_exception();"); source.Append(System.Environment.NewLine);
                    }
                    catch (IndexOutOfRangeException) {
                        source.Append("\t__spec_raise_index_out_of_range_exception();"); source.Append(System.Environment.NewLine);
                    }
                }
                else {
                    System.Diagnostics.Debug.Assert(left.Type == ILExpressionType.ArrayAccess);

                    try {
                        var arrayName = left.LeftNode.Const.ToString();
                        object index = null;
                        if (env.IsDynamic(left.RightNode.Const.ToString())) {
                            index = left.RightNode.Const.ToString();
                        } else {
                            index = env.GetValue(left.RightNode.Const.ToString());
                        }

                        object rightVar = null;
                        if (env.IsDynamic(right.Const.ToString())) {
                            rightVar = right.Const.ToString();
                        } else {
                            rightVar = env.GetValue(right.Const.ToString());
                        }

                        if (env.IsDynamic(arrayName)) {
                            source.Append("\t"); source.Append(arrayName); source.Append("["); source.Append(index); source.Append("] := ");
                            source.Append(rightVar); source.Append(";"); source.Append(System.Environment.NewLine);
                        }
                        else {
                            object[] a = (object[])env.GetValue(arrayName);
                            a[Convert.ToInt32(index)] = rightVar;
                        }
                    }
                    catch (NullReferenceException) {
                        source.Append("\t__spec_raise_null_ref_exception();"); source.Append(System.Environment.NewLine);
                    }
                    catch (IndexOutOfRangeException) {
                        source.Append("\t__spec_raise_index_out_of_range_exception();"); source.Append(System.Environment.NewLine);
                    }
                }

            }
            else if (expr.Type == ILExpressionType.FunctionCall) {
                source.Append("\t"); source.Append(expr.Const.ToString()); source.Append("(");
                for (int i = 0; i < expr.VAList.Count; ++i) {
                    string p = null;
                    if (env.IsDynamic(expr.VAList[i].Const.ToString())) {
                        p = expr.VAList[i].Const.ToString();
                    } else {
                        p = RenderConst(env.GetValue(expr.VAList[i].Const.ToString()));
                    }
                    source.Append(p);
                    if (i != expr.VAList.Count - 1)
                        source.Append(", ");
                }
                source.Append(");"); source.Append(System.Environment.NewLine);
            }

            return false;
        }
        public object Eval(AbstractEnvironment state)
        {
            #region +, -, *, /, mod, pow

            if (Type == ILExpressionType.Plus)
            {
                object left = LeftNode.Eval(state);
                object right = RightNode.Eval(state);
                if (left != Dynamic.Value && right != Dynamic.Value)
                    return (int)left + (int)right;
                else
                    return Dynamic.Value;
            }
            if (Type == ILExpressionType.Minus)
            {
                object left = LeftNode.Eval(state);
                object right = RightNode.Eval(state);
                if (left != Dynamic.Value && right != Dynamic.Value)
                    return (int)left - (int)right;
                else
                    return Dynamic.Value;
            }
            if (Type == ILExpressionType.Mul)
            {
                object left = LeftNode.Eval(state);
                object right = RightNode.Eval(state);
                if (left != Dynamic.Value && right != Dynamic.Value)
                    return (int)left * (int)right;
                else
                    return Dynamic.Value;
            }
            if (Type == ILExpressionType.Div)
            {
                object left = LeftNode.Eval(state);
                object right = RightNode.Eval(state);
                if (left != Dynamic.Value && right != Dynamic.Value)
                    return (int)left / (int)right;
                else
                    return Dynamic.Value;
            }
            if (Type == ILExpressionType.Mod)
            {
                object left = LeftNode.Eval(state);
                object right = RightNode.Eval(state);
                if (left != Dynamic.Value && right != Dynamic.Value)
                    return (int)left % (int)right;
                else
                    return Dynamic.Value;
            }
            if (Type == ILExpressionType.Pow)
            {
                object left = LeftNode.Eval(state);
                object right = RightNode.Eval(state);
                if (left != Dynamic.Value && right != Dynamic.Value)
                    return L1Runtime.L1Runtime.Deg((int)left, (int)right);
                else
                    return Dynamic.Value;
            }

            #endregion

            #region []

            if (Type == ILExpressionType.ArrayAccess)
            {
                object left = LeftNode.Eval(state);
                object right = RightNode.Eval(state);
                if (left != Dynamic.Value && right != Dynamic.Value)
                    return ((Array)left).GetValue((int)right);
                else
                    return Dynamic.Value;
            }

            #endregion

            #region >, >=, <, <=, =, <>

            if (Type == ILExpressionType.Gr)
            {
                object left = LeftNode.Eval(state);
                object right = RightNode.Eval(state);
                if (left != Dynamic.Value && right != Dynamic.Value)
                    return (int)left > (int)right;
                else
                    return Dynamic.Value;
            }
            if (Type == ILExpressionType.Greq)
            {
                object left = LeftNode.Eval(state);
                object right = RightNode.Eval(state);
                if (left != Dynamic.Value && right != Dynamic.Value)
                    return (int)left >= (int)right;
                else
                    return Dynamic.Value;
            }
            if (Type == ILExpressionType.Le)
            {
                object left = LeftNode.Eval(state);
                object right = RightNode.Eval(state);
                if (left != Dynamic.Value && right != Dynamic.Value)
                    return (int)left < (int)right;
                else
                    return Dynamic.Value;
            }
            if (Type == ILExpressionType.Leeq)
            {
                object left = LeftNode.Eval(state);
                object right = RightNode.Eval(state);
                if (left != Dynamic.Value && right != Dynamic.Value)
                    return (int)left <= (int)right;
                else
                    return Dynamic.Value;
            }
            if (Type == ILExpressionType.Eq)
            {
                object left = LeftNode.Eval(state);
                object right = RightNode.Eval(state);
                if (left != Dynamic.Value && right != Dynamic.Value)
                    return (int)left == (int)right;
                else
                    return Dynamic.Value;
            }
            if (Type == ILExpressionType.NotEq)
            {
                object left = LeftNode.Eval(state);
                object right = RightNode.Eval(state);
                if (left != Dynamic.Value && right != Dynamic.Value)
                    return (int)left != (int)right;
                else
                    return Dynamic.Value;
            }

            #endregion

            #region Uminus, Unot

            if (Type == ILExpressionType.Uminus)
            {
                object left = LeftNode.Eval(state);
                if (left != Dynamic.Value)
                    return - (int)left;
                else
                    return Dynamic.Value;
            }
            if (Type == ILExpressionType.Unot)
            {
                object left = LeftNode.Eval(state);
                if (left != Dynamic.Value)
                    return !(bool)left;
                else
                    return Dynamic.Value;
            }

            #endregion

            #region And, Or, Xor

            if (Type == ILExpressionType.And)
            {
                throw new InvalidOperationException("And operator is not allowed in IL!");
            }
            if (Type == ILExpressionType.Or)
            {
                throw new InvalidOperationException("Or operator is not allowed in IL!");
            }
            if (Type == ILExpressionType.Xor)
            {
                object left = LeftNode.Eval(state);
                object right = RightNode.Eval(state);
                if (left != Dynamic.Value && right != Dynamic.Value)
                    return (bool)left ^ (bool)right;
                else
                    return Dynamic.Value;
            }

            #endregion

            #region Alloc, ArrayLength, VariableAccess, Const

            if (Type == ILExpressionType.Alloc)
            {
                object left = LeftNode.Eval(state);
                if (left != Dynamic.Value) {
                    int size = (int)left;
                    object[] r = new object[size];
                    if (OriginalType.NestedType == VariableType.IntType) {
                        for (int i = 0; i < size; ++i) {
                            r[i] = 0;
                        }
                    }
                    if (OriginalType.NestedType == VariableType.BoolType) {
                        for (int i = 0; i < size; ++i) {
                            r[i] = false;
                        }
                    }
                    return r;
                }
                else
                    return Dynamic.Value;
            }
            if (Type == ILExpressionType.ArrayLength)
            {
                object left = LeftNode.Eval(state);
                if (left != Dynamic.Value)
                    return ((Array)left).GetLength(0);
                else
                    return Dynamic.Value;
            }
            if (Type == ILExpressionType.VariableAccess)
            {
                return state.GetValue((string)Const);
            }
            if (Type == ILExpressionType.Const)
            {
                if (Const is string) {
                    var s = (string)Const;
                    var a = new object[s.Length];
                    for (int i = 0; i < s.Length; ++i) {
                        a[i] = (int)s[i];
                    }
                    return a;
                }
                return Const;
            }

            #endregion

            #region Assign

            if (Type == ILExpressionType.Assign)
            {
                object right = RightNode.Eval(state);
                if (LeftNode.Type == ILExpressionType.VariableAccess)
                {
                    state.SetValue((string)LeftNode.Const, right);
                    return right;
                }
                else if (LeftNode.Type == ILExpressionType.ArrayAccess)
                {
                    object array = LeftNode.LeftNode.Eval(state);
                    object index = LeftNode.RightNode.Eval(state);
                    if (array == Dynamic.Value)
                    {
                        return right;
                    }
                    else
                    {
                        if (index == Dynamic.Value)
                        {
                            //Set whole array as Dynamic
                            state.SetArrayAsDynamic(array);
                        }
                        else
                        {
                            ((Array)array).SetValue(right, (int)index);
                        }
                        return right;
                    }
                }
                else
                {
                    throw new InvalidOperationException("Bad assign construction!");
                }
            }

            #endregion

            #region FunctionCall

            if (Type == ILExpressionType.FunctionCall)
            {
                string functionName = (string)Const;

                var args = new List<object>();
                bool isDynamic = false;
                foreach (ILExpression expr in VAList)
                {
                    object arg = expr.Eval(state);
                    args.Add(arg);
                    if (arg == Dynamic.Value)
                        isDynamic = true;
                }
                if (isDynamic)
                    return Dynamic.Value;
                else
                    return Function.Call(args.ToArray());;
            }

            #endregion

            throw new InvalidOperationException("Bad interpreter error! =(");
        }
        public virtual object Call(object[] arguments)
        {
            if (arguments.Length != Parameters.Count)
            {
                throw new InvalidOperationException("Interpreter bad error: function call with bad number of arguments =(");
            }

            AbstractEnvironment env = new AbstractEnvironment();
            for (int i = 0; i < Parameters.Count; ++i)
            {
                env.SetValue(Parameters[i], arguments[i]);
            }

            int PC = 1;
            var curr = Body[0];
            while (curr is ILReturn == false)
            {
                curr = Body[PC - 1];
                if (curr is ILExpression)
                {
                    object r = (curr as ILExpression).Eval(env);
                    if (r == Dynamic.Value)
                        throw new InvalidOperationException("Interpreter bad error: try to calculate dynamic function");
                    PC++;
                }
                else if (curr is ILBranch)
                {
                    object r = (curr as ILBranch).Condition.Eval(env);
                    if (r == Dynamic.Value)
                        throw new InvalidOperationException("Interpreter bad error: try to calculate dynamic function");
                    bool br = (bool)r;
                    if (br)
                        PC = (curr as ILBranch).SuccessJump;
                    else
                        PC = (curr as ILBranch).FailJump;
                }
                else if (curr is ILGoto)
                {
                    PC = (curr as ILGoto).GoTo;
                }
            }
            object result = (curr as ILReturn).Return.Eval(env);
            if (result == Dynamic.Value)
                throw new InvalidOperationException("Interpreter bad error: try to calculate dynamic function");

            return result;
        }