private Expr GenerateZ3Expr(AppExp expr, FastTransducerInstance fti)
        {
            List<Expr> termList = new List<Expr>();
            switch (expr.func.name.Kind)
            {
                case (Tokens.EQ):
                    {
                        return z3p.MkEq(GenerateZ3ExprFromExpr(expr.args[0], fti), GenerateZ3ExprFromExpr(expr.args[1], fti));
                    }
                case (Tokens.AND):
                    {
                        foreach (var arg in expr.args)
                            termList.Add(GenerateZ3ExprFromExpr(arg, fti));
                        return z3p.MkAnd(termList.ToArray());
                    }
                //case ("xor"):
                //    {
                //        return z3p.Z3.MkXor((BoolExpr)GenerateZ3ExprFromExpr(expr.args[0], fti), (BoolExpr)GenerateZ3ExprFromExpr(expr.args[1], fti));
                //    }
                case (Tokens.NOT):
                    {
                        return z3p.MkNot(GenerateZ3ExprFromExpr(expr.args[0], fti));
                    }
                case (Tokens.OR):
                    {
                        foreach (var arg in expr.args)
                            termList.Add(GenerateZ3ExprFromExpr(arg, fti));
                        return z3p.MkOr(termList.ToArray());
                    }
                case (Tokens.IMPLIES):
                    {
                        return z3p.MkOr(z3p.MkNot(GenerateZ3ExprFromExpr(expr.args[0], fti)), GenerateZ3ExprFromExpr(expr.args[1], fti));
                    }
                case (Tokens.PLUS):
                    {
                        foreach (var arg in expr.args)
                            termList.Add(GenerateZ3ExprFromExpr(arg, fti));
                        if (z3p.GetSort(termList[0]).SortKind == Z3_sort_kind.Z3_BV_SORT)
                            return z3p.MkBvAddMany(termList.ToArray());
                        return z3p.MkAdd(termList.ToArray());
                    }
                case (Tokens.DIV):
                    {
                        return z3p.MkCharDiv(GenerateZ3ExprFromExpr(expr.args[0], fti), GenerateZ3ExprFromExpr(expr.args[1], fti));
                    }
                case (Tokens.MINUS):
                    {
                        return z3p.MkCharSub(GenerateZ3ExprFromExpr(expr.args[0], fti), GenerateZ3ExprFromExpr(expr.args[1], fti));
                    }
                case (Tokens.TIMES):
                    {
                        return z3p.MkCharMul(GenerateZ3ExprFromExpr(expr.args[0], fti), GenerateZ3ExprFromExpr(expr.args[1], fti));
                    }
                case (Tokens.LT):
                    {
                        return z3p.MkCharLt(GenerateZ3ExprFromExpr(expr.args[0], fti), GenerateZ3ExprFromExpr(expr.args[1], fti));
                    }
                case (Tokens.LE):
                    {
                        return z3p.MkCharLe(GenerateZ3ExprFromExpr(expr.args[0], fti), GenerateZ3ExprFromExpr(expr.args[1], fti));
                    }
                case (Tokens.GT):
                    {
                        return z3p.MkCharGt(GenerateZ3ExprFromExpr(expr.args[0], fti), GenerateZ3ExprFromExpr(expr.args[1], fti));
                    }
                case (Tokens.GE):
                    {
                        return z3p.MkCharGe(GenerateZ3ExprFromExpr(expr.args[0], fti), GenerateZ3ExprFromExpr(expr.args[1], fti));
                    }
                case (Tokens.MOD):
                    {
                        return z3p.MkMod(GenerateZ3ExprFromExpr(expr.args[0], fti), GenerateZ3ExprFromExpr(expr.args[1], fti));
                    }
                //case ("concat"):
                //    {
                //        throw new Exception("concat not defined yet");
                //        //return z3p.MkL(GenerateZ3ExprFromExpr(expr.args[0], fti), GenerateZ3ExprFromExpr(expr.args[1], fti));
                //    }
                case (Tokens.ITE):
                    {
                        return z3p.MkIte(GenerateZ3ExprFromExpr(expr.args[0], fti), GenerateZ3ExprFromExpr(expr.args[1], fti), GenerateZ3ExprFromExpr(expr.args[2], fti));
                    }
                case (Tokens.ID):
                    {
                        foreach (var f in fti.functions)
                        {
                            if (this.name == f.name)
                                throw new Exception("cannot define recursive functions");

                            if (expr.func.name.text == f.name)
                            {
                                List<Expr> argsExprs = new List<Expr>();
                                foreach (var a in expr.args)
                                {
                                    argsExprs.Add(GenerateZ3ExprFromExpr(a, fti));
                                }
                                return z3p.ApplySubstitution(f.functionDef, f.variableExprs.Values.ToArray(), argsExprs.ToArray<Expr>());
                                //return z3p.MkApp(f.funcDecl, argsExprs.ToArray<Expr>());
                            }
                        }
                        throw new FastException(FastExceptionKind.UnknownFunction);
                    }
                default:
                    throw new FastException(FastExceptionKind.UnknownFunction);
            }
        }
        private Expr GenerateZ3Expr(AppExp expr, FastTransducerInstance fti)
        {
            List<Expr> termList = new List<Expr>();
            switch (expr.func.name.text)
            {
                case ("="):
                    {
                        return z3p.MkEq(GenerateZ3ExprFromExpr(expr.args[0], fti), GenerateZ3ExprFromExpr(expr.args[1], fti));
                    }
                case ("and"):
                    {
                        foreach (var arg in expr.args)
                            termList.Add(GenerateZ3ExprFromExpr(arg, fti));
                        return z3p.MkAnd(termList.ToArray());
                    }
                case ("xor"):
                    {
                        if (expr.args.Count > 2)
                            throw new Exception("Too many arguments");
                        return z3p.Z3.MkXor((BoolExpr)GenerateZ3ExprFromExpr(expr.args[0], fti), (BoolExpr)GenerateZ3ExprFromExpr(expr.args[1], fti));
                    }
                case ("not"):
                    {
                        return z3p.MkNot(GenerateZ3ExprFromExpr(expr.args[0], fti));
                    }
                case ("or"):
                    {
                        foreach (var arg in expr.args)
                            termList.Add(GenerateZ3ExprFromExpr(arg, fti));
                        return z3p.MkOr(termList.ToArray());
                    }
                case ("=>"):
                    {
                        return z3p.MkOr(z3p.MkNot(GenerateZ3ExprFromExpr(expr.args[0], fti)), GenerateZ3ExprFromExpr(expr.args[1], fti));
                    }
                case ("+"):
                    {
                        foreach (var arg in expr.args)
                            termList.Add(GenerateZ3ExprFromExpr(arg, fti));
                        if (z3p.GetSort(termList[0]).SortKind == Z3_sort_kind.Z3_BV_SORT)
                            return z3p.MkBvAddMany(termList.ToArray());
                        return z3p.MkAdd(termList.ToArray());
                    }
                case ("/"):
                    {
                        return z3p.MkDiv(GenerateZ3ExprFromExpr(expr.args[0], fti), GenerateZ3ExprFromExpr(expr.args[1], fti));
                    }
                case ("-"):
                    {
                        return z3p.MkSub(GenerateZ3ExprFromExpr(expr.args[0], fti), GenerateZ3ExprFromExpr(expr.args[1], fti));
                    }
                case ("*"):
                    {
                        return z3p.MkMul(GenerateZ3ExprFromExpr(expr.args[0], fti), GenerateZ3ExprFromExpr(expr.args[1], fti));
                    }
                case ("<"):
                    {
                        return z3p.MkLt(GenerateZ3ExprFromExpr(expr.args[0], fti), GenerateZ3ExprFromExpr(expr.args[1], fti));
                    }
                case ("<="):
                    {
                        return z3p.MkLe(GenerateZ3ExprFromExpr(expr.args[0], fti), GenerateZ3ExprFromExpr(expr.args[1], fti));
                    }
                case (">"):
                    {
                        return z3p.MkGt(GenerateZ3ExprFromExpr(expr.args[0], fti), GenerateZ3ExprFromExpr(expr.args[1], fti));
                    }

                case (">="):
                    {
                        return z3p.MkGe(GenerateZ3ExprFromExpr(expr.args[0], fti), GenerateZ3ExprFromExpr(expr.args[1], fti));
                    }
                case ("if"):
                    {
                        return z3p.MkIte(GenerateZ3ExprFromExpr(expr.args[0], fti), GenerateZ3ExprFromExpr(expr.args[1], fti), GenerateZ3ExprFromExpr(expr.args[2], fti));
                    }
                default:
                    {
                        foreach (var f in fti.functions)
                        {
                            if (expr.func.name.text == f.name)
                            {
                                List<Expr> argsExprs = new List<Expr>();
                                foreach (var a in expr.args)
                                {
                                    argsExprs.Add(GenerateZ3ExprFromExpr(a, fti));
                                }
                                return z3p.ApplySubstitution(f.functionDef, f.variableExprs.Values.ToArray(), argsExprs.ToArray<Expr>());
                                //return z3p.MkApp(f.funcDecl, argsExprs.ToArray<Expr>());
                            }
                        }
                        throw new Exception("Function not defined");
                    }
            }
        }
        private Expr GenerateZ3ToExpr(AppExp expr, RankedAlphabetSort outputAlph, List<FastToken> children, int from, List<string> reachedStates, List<Def> queue, Dictionary<string, Def> defs, FastTransducerInstance fti, List<int>[] nextStatesL)
        {
            List<Expr> termList = new List<Expr>();
            switch (expr.func.name.Kind)
            {
                #region predefined functions
                case (Tokens.EQ):
                    {
                        return z3p.MkEq(GenerateZ3ExprFromToExpr(expr.args[0], outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL), GenerateZ3ExprFromToExpr(expr.args[1], outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL));
                    }
                case (Tokens.AND):
                    {
                        foreach (var arg in expr.args)
                            termList.Add(GenerateZ3ExprFromToExpr(arg, outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL));
                        return z3p.MkAnd(termList.ToArray());
                    }
                //case ("xor"):
                //    {
                //        if (expr.args.Count > 2)
                //            throw new Exception("Too many arguments");
                //        return z3p.Z3.MkXor((BoolExpr)GenerateZ3ExprFromToExpr(expr.args[0], outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL), (BoolExpr)GenerateZ3ExprFromToExpr(expr.args[1], outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL));
                //    }
                case (Tokens.NOT):
                    {
                        return z3p.MkNot(GenerateZ3ExprFromToExpr(expr.args[0], outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL));
                    }
                case (Tokens.OR):
                    {
                        foreach (var arg in expr.args)
                            termList.Add(GenerateZ3ExprFromToExpr(arg, outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL));
                        return z3p.MkOr(termList.ToArray());
                    }
                case (Tokens.IMPLIES):
                    {
                        return z3p.MkOr(z3p.MkNot(GenerateZ3ExprFromToExpr(expr.args[0], outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL)), GenerateZ3ExprFromToExpr(expr.args[1], outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL));
                    }
                case (Tokens.PLUS):
                    {
                        foreach (var arg in expr.args)
                            termList.Add(GenerateZ3ExprFromToExpr(arg, outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL));
                        if (z3p.GetSort(termList[0]).SortKind == Z3_sort_kind.Z3_BV_SORT)
                            return z3p.MkBvAddMany(termList.ToArray());
                        return z3p.MkAdd(termList.ToArray());
                    }
                case (Tokens.DIV):
                    {
                        return z3p.MkCharDiv(GenerateZ3ExprFromToExpr(expr.args[0], outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL), GenerateZ3ExprFromToExpr(expr.args[1], outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL));
                    }
                case (Tokens.MINUS):
                    {
                        return z3p.MkCharSub(GenerateZ3ExprFromToExpr(expr.args[0], outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL), GenerateZ3ExprFromToExpr(expr.args[1], outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL));
                    }
                case (Tokens.TIMES):
                    {
                        return z3p.MkCharMul(GenerateZ3ExprFromToExpr(expr.args[0], outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL), GenerateZ3ExprFromToExpr(expr.args[1], outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL));
                    }
                case (Tokens.LT):
                    {
                        return z3p.MkCharLt(GenerateZ3ExprFromToExpr(expr.args[0], outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL), GenerateZ3ExprFromToExpr(expr.args[1], outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL));
                    }
                case (Tokens.LE):
                    {
                        return z3p.MkCharLe(GenerateZ3ExprFromToExpr(expr.args[0], outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL), GenerateZ3ExprFromToExpr(expr.args[1], outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL));
                    }
                case (Tokens.GT):
                    {
                        return z3p.MkCharGt(GenerateZ3ExprFromToExpr(expr.args[0], outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL), GenerateZ3ExprFromToExpr(expr.args[1], outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL));
                    }
                case (Tokens.GE):
                    {
                        return z3p.MkCharGe(GenerateZ3ExprFromToExpr(expr.args[0], outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL), GenerateZ3ExprFromToExpr(expr.args[1], outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL));
                    }
                case (Tokens.MOD):
                    {
                        return z3p.MkMod(GenerateZ3ExprFromToExpr(expr.args[0], outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL), GenerateZ3ExprFromToExpr(expr.args[1], outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL));
                    }
                case (Tokens.ITE):
                    {
                        return z3p.MkIte(GenerateZ3ExprFromToExpr(expr.args[0], outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL), GenerateZ3ExprFromToExpr(expr.args[1], outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL), GenerateZ3ExprFromToExpr(expr.args[2], outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL));
                    }
                #endregion
                default:
                    {
                        if (expr.IsTranDef)
                        {
                            //The application is a transduction applied to a subtree
                            //Check if the current trans has been processed, if not add it to the queue
                            String name = ((AppExp)expr).func.name.text;
                            int pos = reachedStates.IndexOf(name);
                            if (pos == -1)
                            {
                                reachedStates.Add(name);
                                var transDef = defs[name];
                                queue.Add((TransDef)transDef);
                                pos = reachedStates.Count-1;
                            }

                            //Find the child number and return the transition
                            int childPosition = 1;
                            foreach (var child in children)
                            {
                                if (child.text == ((AppExp)expr).args[0].token.text)
                                {
                                    break;
                                }
                                childPosition++;
                            }
                            if (!nextStatesL[childPosition - 1].Contains(pos))
                                nextStatesL[childPosition - 1].Add(pos);

                            return alphabet.alph.MkTrans(outputAlph.alph, pos, childPosition);
                        }

                        //It means the app is a constructor
                        Expr[] terms = new Expr[expr.args.Count-1];
                        var terms_0 = GenerateZ3ToExpr((RecordExp)expr.args.ElementAt<FExp>(0), outputAlph, fti);
                        for (int i = 1; i < expr.args.Count; i++)
                        {
                            terms[i-1] = GenerateZ3ExprFromToExpr(expr.args.ElementAt<FExp>(i), outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL);
                        }
                        return outputAlph.alph.MkTree(expr.func.name.text, terms_0, terms);

                    }
            }
        }
        //Generates the code for an output of a transduction when the expression is an Expr
        private static bool PrintOutputTree(List<FastToken> children, AppExp ex, String range, StringBuilder sb, bool isapplied)
        {
            if (ex.IsTranDef)
            {
                //We hit the new function invocation
                sb.Append(ex.func.name);
                PrintOutputTree(children, ex.args[0], range, sb, true);
                return true;
            }
            else
            {
                //The function symbol is a constructor in the output alphabet
                //Create a new Tree of the result kind based on the current function arity
                sb.Append("Tree" + range + ".MakeTree(" + range + "." + ex.func.name);

                foreach (var att in ((RecordExp)ex.args[0]).args)
                {
                    sb.Append(", ");
                    PrintExpr(children, att, sb);
                }

                sb.Append(", new Tree" + range + "[" + (ex.func.arity - 1) + "]{");

                int i = 0;
                foreach (var node in ex.args)
                {
                    if (i == 0) { }
                    else
                    {
                        if (i == 1)
                        {
                            PrintOutputTree(children, node, range, sb, false);
                        }
                        else
                        {
                            sb.Append(", ");
                            PrintOutputTree(children, node, range, sb, false);
                        }
                    }
                    i++;
                }
                sb.Append("}");
                sb.Append(")");

                return true;
            }
        }
        //Generates the code for an AppExpr expression
        private static bool PrintExpr(List<FastToken> children, AppExp ex, StringBuilder sb)
        {
            sb.Append("(");

            switch (ex.func.name.text)
            {
                case ("="):
                    {
                        PrintExpr(children, ex.args[0], sb);
                        sb.Append(" == ");
                        PrintExpr(children, ex.args[1], sb);
                        break;
                    }
                case ("and"):
                    {
                        for (int i = 0; i < ex.func.arity; i++)
                        {
                            if (i == 0)
                            {
                                PrintExpr(children, ex.args[i], sb);
                            }
                            else
                            {
                                sb.Append(" && ");
                                PrintExpr(children, ex.args[i], sb);
                            }
                        }
                        break;
                    }
                case ("xor"):
                    {
                        for (int i = 0; i < ex.func.arity; i++)
                        {
                            if (i == 0)
                            {
                                PrintExpr(children, ex.args[i], sb);
                            }
                            else
                            {
                                sb.Append(" ^ ");
                                PrintExpr(children, ex.args[i], sb);
                            }
                        }
                        break;
                    }
                case ("not"):
                    {
                        sb.Append("!");
                        PrintExpr(children, ex.args[0], sb);
                        break;
                    }
                case ("or"):
                    {
                        for (int i = 0; i < ex.func.arity; i++)
                        {
                            if (i == 0)
                            {
                                PrintExpr(children, ex.args[i], sb);
                            }
                            else
                            {
                                sb.Append(" || ");
                                PrintExpr(children, ex.args[i], sb);
                            }
                        }
                        break;
                    }
                case ("=>"):
                    {
                        sb.Append("(!");
                        PrintExpr(children, ex.args[0], sb);
                        sb.Append(")");
                        sb.Append(" || ");
                        PrintExpr(children, ex.args[1], sb);
                        break;
                    }
                case ("+"):
                    {
                        for (int i = 0; i < ex.func.arity; i++)
                        {
                            if (i == 0)
                            {
                                PrintExpr(children, ex.args[i], sb);
                            }
                            else
                            {
                                sb.Append(" + ");
                                PrintExpr(children, ex.args[i], sb);
                            }
                        }
                        break;
                    }
                case ("/"):
                    {
                        for (int i = 0; i < ex.func.arity; i++)
                        {
                            if (i == 0)
                            {
                                PrintExpr(children, ex.args[i], sb);
                            }
                            else
                            {
                                sb.Append(" / ");
                                PrintExpr(children, ex.args[i], sb);
                            }
                        }
                        break;
                    }
                case ("-"):
                    {
                        for (int i = 0; i < ex.func.arity; i++)
                        {
                            if (i == 0)
                            {
                                PrintExpr(children, ex.args[i], sb);
                            }
                            else
                            {
                                sb.Append(" - ");
                                PrintExpr(children, ex.args[i], sb);
                            }
                        }
                        break;
                    }
                case ("*"):
                    {
                        for (int i = 0; i < ex.func.arity; i++)
                        {
                            if (i == 0)
                            {
                                PrintExpr(children, ex.args[i], sb);
                            }
                            else
                            {
                                sb.Append(" * ");
                                PrintExpr(children, ex.args[i], sb);
                            }
                        }
                        break;
                    }
                case ("<"):
                    {
                        PrintExpr(children, ex.args[0], sb);
                        sb.Append(" < ");
                        PrintExpr(children, ex.args[1], sb);
                        break;
                    }
                case ("<="):
                    {
                        PrintExpr(children, ex.args[0], sb);
                        sb.Append(" <= ");
                        PrintExpr(children, ex.args[1], sb);
                        break;
                    }
                case (">"):
                    {
                        PrintExpr(children, ex.args[0], sb);
                        sb.Append(" > ");
                        PrintExpr(children, ex.args[1], sb);
                        break;
                    }
                case (">="):
                    {
                        PrintExpr(children, ex.args[0], sb);
                        sb.Append(" >= ");
                        PrintExpr(children, ex.args[1], sb);
                        break;
                    }
                case ("mod"):
                    {
                        PrintExpr(children, ex.args[0], sb);
                        sb.Append(" % ");
                        PrintExpr(children, ex.args[1], sb);
                        break;
                    }
                case ("if"):
                    {
                        PrintExpr(children, ex.args[0], sb);
                        sb.Append(" ? ");
                        PrintExpr(children, ex.args[1], sb);
                        sb.Append(" : ");
                        PrintExpr(children, ex.args[2], sb);
                        break;
                    }

                default:
                    {
                        if (ex.IsLangDef)
                        {
                            PrintExpr(children, ex.args[0], sb);
                            sb.Append("." + ex.func.name.text + "()");
                        }
                        else{
                            sb.Append(ex.func.name.text + "(");
                            PrintExpr(children, ex.args[0], sb);
                            sb.Append(")");
                        }
                        break;
                    }

            }
            sb.Append(")");
            return true;
        }
 //Compute all the iterators necessary to generate a particular output of a transduction when expr is AppExpr
 private static bool ComputeIterators(AppExp ex, String range, StringBuilder sb, bool isApplied)
 {
     if (ex.IsTranDef)
     {
         List<String> t = new List<String>();
         t.Add(ex.func.name.text);
         iterCases.Insert(0, t);
         return ComputeIterators((Variable)ex.args[0], range, sb, true);
     }
     else
     {
         int i = 0;
         foreach (var node in ex.args)
         {
             if (i != 0)
                 ComputeIterators(node, range, sb, false);
             i++;
         }
         return true;
     }
 }