public Expr GenerateZ3ExprFromExpr(FExp expr, FastTransducerInstance fti)
 {
     switch (expr.kind)
     {
         case (FExpKind.App):
             return GenerateZ3Expr((AppExp)expr, fti);
         case (FExpKind.Value):
             return GenerateZ3Expr((Value)expr, fti);
         case (FExpKind.Var):
             return GenerateZ3Expr((Variable)expr, fti);
     }
     return null;
 }
 public Const(ConstDef def, FastTransducerInstance fti, Z3Provider z3p)
 {
     this.z3p = z3p;
     this.name = def.id.text;
     switch (def.sort.kind)
     {
         case (FastSortKind.Real):
             {
                 sort = z3p.RealSort;
                 break;
             }
         case (FastSortKind.Bool):
             {
                 sort = z3p.BoolSort;
                 break;
             }
         case (FastSortKind.Int):
             {
                 sort = z3p.IntSort;
                 break;
             }
         case (FastSortKind.String):
             {
                 sort = z3p.MkListSort(z3p.CharSort);
                 break;
             }
         case (FastSortKind.Tree):
             {
                 foreach (var enumSort in fti.enums)
                 {
                     if (enumSort.name == def.sort.name.text)
                     {
                         sort = enumSort.sort;
                         break;
                     }
                 }
                 break;
             }
     }
     this.value = GenerateZ3ExprFromExpr(def.expr, fti).Simplify();
 }
 //Generates all the languages and transductions of a particular tree definitions class
 private static bool GenerateDefinitions(Dictionary<string, Def> defs, FastTransducerInstance fti)
 {
     foreach (var def in defs.Values)
     {
         if (def.kind == DefKind.Def)
         {
             switch (((DefDef)def).ddkind)
             {
                 case DefDefKind.Trans:
                     {
                         TransDefDef transDef = def as TransDefDef;
                         if (!(fti.treeDefinitions[transDef.domain.name.text].AddTransducer(transDef, fti, defs)))
                             return false;
                         else
                             fti.fastLog.WriteLog(LogLevel.Maximal,
                                 string.Format("transformation '{0}: {1} -> {2}' correctly added",
                                     transDef.func.name.text,
                                     transDef.domain.name.text,
                                     transDef.range.name.text)
                                 );
                         break;
                     }
                 case DefDefKind.Lang:
                     {
                         LangDefDef langDef = def as LangDefDef;
                         if (!(fti.treeDefinitions[langDef.domain.name.text].AddAcceptor(langDef, fti, defs)))
                             return false;
                         else
                             fti.fastLog.WriteLog(LogLevel.Maximal, string.Format("language '{0}:{1}' correctly added", langDef.func.name.text, langDef.domain.name.text));
                         break;
                     }
                 case DefDefKind.Tree:
                     {
                         TreeDef treeDef = def as TreeDef;
                         if (!fti.treeDefinitions[treeDef.domain.name.text].AddTree(treeDef, fti, defs))
                             return false;
                         else
                             fti.fastLog.WriteLog(LogLevel.Maximal, string.Format("tree '{0}:{1}' correctly added", treeDef.func.name.text, treeDef.domain.name.text));
                         break;
                     }
             }
         }
     }
     return true;
 }
 internal static TreeTransducer getTreeAutomatonFromExpr(BuiltinLangExp expr, FastTransducerInstance fti, Dictionary<string, Def> defs)
 {
     Stopwatch stopwatch = new Stopwatch();
     switch (expr.kind)
     {
         case (BuiltinLangExpKind.Intersection):
             {
                 var castExpr = expr as IntersectionExp;
                 var a = getTreeAutomatonFromExpr(castExpr.arg1, fti, defs);
                 var b = getTreeAutomatonFromExpr(castExpr.arg2, fti, defs);
                 stopwatch.Start();
                 var inters = a.Intersect(b).RemoveMultipleInitialStates();
                 stopwatch.Stop();
                 fti.fastLog.WriteLog(LogLevel.Normal, string.Format("intersection of {0} and {1}: {2} ms", castExpr.arg1.ToString(), castExpr.arg2.ToString(), stopwatch.ElapsedMilliseconds));
                 return inters;
             }
         case (BuiltinLangExpKind.Difference):
             {
                 var castExpr = expr as DifferenceExp;
                 var a = getTreeAutomatonFromExpr(castExpr.arg1, fti, defs);
                 var b = getTreeAutomatonFromExpr(castExpr.arg2, fti, defs);
                 stopwatch.Start();
                 var inters = a.Intersect(b.Complement()).RemoveMultipleInitialStates();
                 stopwatch.Stop();
                 fti.fastLog.WriteLog(LogLevel.Normal, string.Format("difference of {0} and {1}: {2} ms", castExpr.arg1.ToString(), castExpr.arg2.ToString(), stopwatch.ElapsedMilliseconds));
                 return inters;
             }
         case (BuiltinLangExpKind.Union):
             {
                 var castExpr = expr as UnionExp;
                 var a = getTreeAutomatonFromExpr(castExpr.arg1, fti, defs);
                 var b = getTreeAutomatonFromExpr(castExpr.arg2, fti, defs);
                 stopwatch.Start();
                 var res = a.Union(b).RemoveMultipleInitialStates();
                 stopwatch.Stop();
                 fti.fastLog.WriteLog(LogLevel.Normal, string.Format("union of {0} and {1}: {2} ms", castExpr.arg1.ToString(), castExpr.arg2.ToString(), stopwatch.ElapsedMilliseconds));
                 return res;
             }
         case (BuiltinLangExpKind.Domain):
             {
                 var castExpr = expr as DomainExp;
                 var a = getTreeAutomatonFromExpr(castExpr.arg1, fti, defs);
                 stopwatch.Start();
                 var res = a.ComputeDomainAcceptor().RemoveMultipleInitialStates();
                 stopwatch.Stop();
                 fti.fastLog.WriteLog(LogLevel.Normal, string.Format("domain of {0}: {1} ms", castExpr.arg1.ToString(), stopwatch.ElapsedMilliseconds));
                 return res;
             }
         case (BuiltinLangExpKind.Preimage):
             {
                 var castExpr = expr as PreimageExp;
                 var t = getTreeAutomatonFromExpr(castExpr.arg1, fti, defs);
                 var output = getTreeAutomatonFromExpr(castExpr.arg2, fti, defs);
                 stopwatch.Start();
                 var res = t.RestrictRange(output).ComputeDomainAcceptor().RemoveMultipleInitialStates();
                 stopwatch.Stop();
                 fti.fastLog.WriteLog(LogLevel.Normal, string.Format("preimage of {0}: {1} ms", castExpr.arg1.ToString(), stopwatch.ElapsedMilliseconds));
                 return res;
             }
         case (BuiltinLangExpKind.Complement):
             {
                 var castExpr = expr as ComplementExp;
                 var a =  getTreeAutomatonFromExpr(castExpr.arg1, fti, defs);
                 stopwatch.Start();
                 var res = a.Complement().RemoveMultipleInitialStates();
                 stopwatch.Stop();
                 fti.fastLog.WriteLog(LogLevel.Normal, string.Format("complement of {0}: {1} ms", castExpr.arg1.ToString(), stopwatch.ElapsedMilliseconds));
                 return res;
             }
         case (BuiltinLangExpKind.Minimization):
             {
                 var castExpr = expr as MinimizeExp;
                 var a = getTreeAutomatonFromExpr(castExpr.arg1, fti, defs);
                 stopwatch.Start();
                 var res = a.Minimize().RemoveMultipleInitialStates();
                 stopwatch.Stop();
                 fti.fastLog.WriteLog(LogLevel.Normal, string.Format("minimization of {0}: {1} ms", castExpr.arg1.ToString(), stopwatch.ElapsedMilliseconds));
                 return res;
             }
         case (BuiltinLangExpKind.Var):
             {
                 var castExpr = expr as LangNameExp;
                 return fti.treeDefinitions[castExpr.domain.name.text].acceptors[castExpr.func.name.text];
             }
         default:
             {
                 throw new FastException(FastExceptionKind.NotImplemented);
             }
     }
     throw new FastException(FastExceptionKind.InternalError);
 }
 //Generate Z3 enums from enum definitions
 private static bool GenerateConstsAndFunctions(List<Def> constFunDefs, FastTransducerInstance fti)
 {
     foreach (var def in constFunDefs)
     {
         switch (def.kind)
         {
             case (DefKind.Const):
                 {
                     if (!GenerateConst((ConstDef)def, fti))
                         return false;
                     break;
                 }
             case (DefKind.Function):
                 {
                     if (!GenerateFunction((FunctionDef)def, fti))
                         return false;
                     break;
                 }
         }
     }
     return true;
 }
        /// <summary>
        /// Generate a transducer rooted in def from a transducer definition
        /// </summary>
        public bool AddTransducer(Def def, FastTransducerInstance fti, Dictionary<string, Def> defs)
        {
            #region Accessory variables
            int ruleNumb;
            int[][] givenExpr;
            Expr whereExpr;
            Expr toExpr;
            int currentState = 0;
            Def untdef;
            TransDef currentDef;
            LangDef currentLangDef;
            bool isTrans = true;

            //The alphabet of the transducer
            List<GuardedExp> cases = new List<GuardedExp>();
            RankedAlphabet currentAlphabet = alphabet.alph;
            List<TreeRule> transducerRules = new List<TreeRule>();
            List<string> exploredStates = new List<string>();
            List<string> reachedStates = new List<string>();

            RankedAlphabetSort outputAlphabet = null;

            int childPosition;
            String name = "";
            String defName = "";
            List<FastToken> children;

            int[][] nextStates;
            List<int>[] nextStatesL;
            #endregion

            List<Def> definitionQueue = new List<Def>();

            //Find the output alphabet

            #region pick proper outputAlph
            if (def.kind == DefKind.Trans)
            {
                TransDef tdef = def as TransDef;
                defName = tdef.func.name.text;
                outputAlphabet = fti.alphabets[tdef.range.name.text];
            }
            if (def.kind == DefKind.Lang)
            {
                LangDef ldef = def as LangDef;
                defName = ldef.func.name.text;
                outputAlphabet = fti.alphabets[ldef.domain.name.text];
            }
            #endregion

            reachedStates.Add(defName);
            definitionQueue.Add(def);

            while (definitionQueue.Count > 0)
            {
                //remove state from the queue and mark it as explored
                untdef = definitionQueue.ElementAt<Def>(0);
                definitionQueue.RemoveAt(0);
                #region pick proper type for def
                if (untdef.kind == DefKind.Trans)
                {
                    isTrans = true;
                    currentDef = (TransDef)untdef;
                    exploredStates.Add(currentDef.func.name.text);
                    cases = currentDef.cases;
                }
                if (untdef.kind == DefKind.Lang)
                {
                    isTrans = false;
                    currentLangDef = (LangDef)untdef;
                    exploredStates.Add(currentLangDef.func.name.text);
                    cases = currentLangDef.cases;
                }
                #endregion

                ruleNumb = 0;
                List<int> unsatRules = new List<int>();
                foreach (var defCase in cases)
                {
                    ruleNumb++;
                    children = defCase.pat.children;

                    //Compute the Z3 term for the where condition
                    whereExpr = GenerateZ3ExprFromWhereExpr(defCase.where, fti).Simplify();

                    if (z3p.IsSatisfiable(whereExpr))
                    {
                        //Compute the termSet array corresponding to the given
                        givenExpr = new int[children.Count][];

                        #region Find next states and add them to the queue and add the corresponding number to next states
                        nextStates = new int[children.Count][];
                        nextStatesL = new List<int>[children.Count];
                        for (int i = 0; i < children.Count; i++)
                            nextStatesL[i] = new List<int>();
                        foreach (var expr in defCase.given)
                        {
                            if (expr.kind == FExpKind.App)
                            {
                                name = ((AppExp)expr).func.name.text;
                                //Add the state to the queue if it has not been reached yet
                                if (!reachedStates.Contains(name))
                                {
                                    reachedStates.Add(name);
                                    var langDef = defs[name] as LangDef;
                                    definitionQueue.Add(langDef);

                                }
                                childPosition = 0;
                                foreach (var child in children)
                                {
                                    if (child.text == ((AppExp)expr).args[0].token.text)
                                    {
                                        break;
                                    }
                                    childPosition++;
                                }
                                nextStatesL[childPosition].Add(reachedStates.IndexOf(name));
                            }
                        }

                        #endregion

                        //Compute the Z3 term for the to condition
                        if (isTrans)
                            toExpr = GenerateZ3ExprFromToExpr(defCase.to, outputAlphabet, children, currentState, reachedStates, definitionQueue, defs, fti, nextStatesL).Simplify();
                        else
                            toExpr = null;

                        //Next states will contain the outgoing states of the automata
                        for (int i = 0; i < children.Count; i++)
                            nextStates[i] = nextStatesL[i].ToArray();
                        transducerRules.Add(z3p.TT.MkTreeRule(currentAlphabet, outputAlphabet.alph, currentState, defCase.pat.symbol.text, whereExpr, toExpr, nextStates));
                    }
                    else
                    {
                        unsatRules.Add(ruleNumb - 1);
                        //Console.WriteLine("Rule " + ruleNumb + " is unsat in " + untdef.id.text);
                        fti.fastLog.WriteLog(LogLevel.Normal, string.Format("Rule {0} in '{1}' is not satisfiable", +ruleNumb, untdef.id.text));
                    }

                }
                int removed = 0;
                foreach (int ind in unsatRules)
                {
                    cases.RemoveAt(ind-removed);
                    removed++;
                }
                currentState++;
            }

            //Generate transducer
            try
            {
                var automaton = z3p.TT.MkTreeAutomaton(0, currentAlphabet, outputAlphabet.alph, transducerRules);
                automaton = automaton.RemoveMultipleInitialStates();
                if (def.kind == DefKind.Trans)
                {
                    transducers.Add(defName, automaton);
                    if (automaton.IsEmpty)
                        fti.fastLog.WriteLog(LogLevel.Normal, string.Format("the transducer '{0}' is empty", defName));
                }
                else
                {
                    acceptors.Add(defName, automaton);
                    if (automaton.IsEmpty)
                        fti.fastLog.WriteLog(LogLevel.Normal, string.Format("the language '{0}' is empty", defName));
                }
            }
            catch (AutomataException e)
            {
                Console.WriteLine(e);
                throw e;
            }
            return true;
        }
        public Function(FunctionDef def, FastTransducerInstance fti, Z3Provider z3p)
        {
            this.z3p = z3p;
            this.arity = def.inputVariables.Count;
            this.name = def.id.text;
            this.outputSort = getSort(def.outputSort, fti);
            this.inputSorts = new Dictionary<string, Sort>();
            this.variableExprs = new Dictionary<string, Expr>(); //contains the terms for the variables of the function

            #region Compute input and outputs sort
            Sort[] inputs = new Sort[this.arity];
            uint i = 0;
            foreach (var va in def.inputVariables)
            {
                inputs[i] = getSort(va.Value, fti);
                inputSorts.Add(va.Key.text, inputs[i]);
                variableExprs.Add(va.Key.text, z3p.MkVar(i, inputs[i]));
                i++;
            }
            Expr[] varExprs = variableExprs.Values.ToArray();
            #endregion

            this.funcDecl = z3p.MkFreshFuncDecl(this.name, inputs, this.outputSort);
            var e = GenerateZ3ExprFromExpr(def.expr, fti);
            this.functionDef = e.Simplify(); //z3p.MkEqForall(z3p.MkApp(funcDecl, varExprs), GenerateZ3ExprFromExpr(def.expr, fti), varExprs);
        }
 //Generate Z3 term for fun def
 private static bool GenerateFunction(FunctionDef def, FastTransducerInstance fti)
 {
     fti.functions.Add(new Function(def, fti, z3p));
     return true;
 }
        //Generate result from query
        private static bool GenerateQueryResult(QueryDef query, Dictionary<string, Def> defs, FastTransducerInstance fti)
        {
            string msg = "";
            switch (query.id.Kind)
            {
                case (Tokens.CONTAINS):
                    {
                        ContainsQueryDef ct = query as ContainsQueryDef;

                        IEnumerable<Expr> t = null;
                        if (ct.expr.kind == FExpKind.Var)
                        {
                            foreach (var treeDef in fti.treeDefinitions)
                                if (treeDef.Value.trees.TryGetValue(ct.expr.token.text, out t))
                                    break;
                        }
                        else
                        {
                            throw new FastException(FastExceptionKind.InternalError);
                        }

                        TreeTransducer lang = OperationTranGen.getTreeAutomatonFromExpr(ct.language, fti, defs);

                        var results = lang.Apply(t).ToList();
                        var containsRes = results.Count>0;

                        msg = string.Format("'{0}' is{1} a member of the language '{2}'", ct.expr, containsRes ? "" : " not", ct.language);
                        if (ct.isAssert)
                        {
                            if (containsRes == ct.assertTrue)
                                return true;
                            else
                                throw new FastAssertException(msg,ct.func.name.line, ct.func.name.position);
                        }
                        break;
                    }
                case (Tokens.TYPECHECK):
                    {
                        TypecheckQueryDef ct = query as TypecheckQueryDef;

                        TreeTransducer input = OperationTranGen.getTreeAutomatonFromExpr(ct.input, fti, defs);
                        TreeTransducer output = OperationTranGen.getTreeAutomatonFromExpr(ct.output, fti, defs);
                        TreeTransducer trans = OperationTranGen.getTreeAutomatonFromExpr(ct.trans, fti, defs);

                        //Preimage of outputcomplement doesn't interesect input
                        var ocomp = output.Complement();
                        var preim = trans.RestrictRange(ocomp).ComputeDomainAcceptor();
                        var badinp = preim.Intersect(input);
                        bool typechecks = badinp.IsEmpty;

                        //For assertions
                        var sb = new StringBuilder();
                        if (!typechecks)
                        {
                            var badInput = badinp.GenerateWitness();
                            FastGen fg = new FastGen(z3p);
                            sb.Append("\n ---> input '");
                            fg.ToFastExpr(badInput, sb, false);
                            sb.Append("' produces output '");
                            // take first bad output
                            foreach (var v in trans.Apply(new Expr[] { badInput }))
                                if(output.Apply(new Expr[] { v })!=null)
                                {
                                    fg.ToFastExpr(v, sb, false);
                                    break;
                                }
                            sb.Append("'");
                        }
                        msg = string.Format("'{0}' has{3} type '{1}' -> '{2}'{4}",
                            ct.trans, ct.input, ct.output, typechecks ? "" : " not",sb.ToString());
                        if (ct.isAssert)
                        {
                            if (typechecks == ct.assertTrue)
                                return true;
                            else
                                throw new FastAssertException(msg, ct.func.name.line, ct.func.name.position);
                        }
                        break;
                    }
                case (Tokens.ID):
                case (Tokens.PRINT):
                    {
                        DisplayQueryDef dt = query as DisplayQueryDef;
                        StringBuilder sb = new StringBuilder();
                        FastGen fastgen = new FastGen(z3p);

                        var toPrintDef = defs[dt.toPrintVar];
                        switch (toPrintDef.kind)
                        {
                            case DefKind.Alphabet:
                            case DefKind.Const:
                            case DefKind.Function:
                            case DefKind.Enum:
                                {
                                    toPrintDef.PrettyPrint(sb);
                                    msg = string.Format("'{0}': {1}", dt.toPrintVar, sb);
                                    break;
                                }
                            case DefKind.Lang:
                                {
                                    LangDef td = toPrintDef as LangDef;
                                    var trans = fti.treeDefinitions[td.domain.name.text].acceptors[dt.toPrintVar];
                                    fastgen.ToFast(dt.toPrintVar,trans, sb,true);
                                    msg = string.Format("{0}", sb);
                                    break;
                                }
                            case DefKind.Trans:
                                {
                                    TransDef td = toPrintDef as TransDef;
                                    var trans = fti.treeDefinitions[td.domain.name.text].transducers[dt.toPrintVar];
                                    fastgen.ToFast(dt.toPrintVar,trans, sb,false);
                                    msg = string.Format("{0}", sb);
                                    break;
                                }
                            case DefKind.Def:
                                {
                                    DefDef df = toPrintDef as DefDef;
                                    switch(df.ddkind){
                                        case DefDefKind.Lang:
                                            {
                                                LangDefDef td = df as LangDefDef;
                                                var trans = fti.treeDefinitions[td.domain.name.text].acceptors[dt.toPrintVar];
                                                fastgen.ToFast(dt.toPrintVar, trans, sb, true);
                                                msg = string.Format("{0}", sb);
                                                break;
                                            }
                                        case DefDefKind.Trans:
                                            {
                                                TransDefDef td = df as TransDefDef;
                                                var trans = fti.treeDefinitions[td.domain.name.text].transducers[dt.toPrintVar];
                                                fastgen.ToFast(dt.toPrintVar, trans, sb, false);
                                                msg = string.Format("{0}", sb);
                                                break;
                                            }
                                        case DefDefKind.Tree:
                                            {
                                                TreeDef td = df as TreeDef;
                                                var trees = new List<Expr>(fti.treeDefinitions[td.domain.name.text].trees[dt.toPrintVar]);
                                                var counter = 1;
                                                if(trees.Count==0){
                                                    msg = string.Format("'{0}' does not contain any tree", dt.toPrintVar);
                                                    break;
                                                }
                                                sb.AppendLine(string.Format("'{0}': [", dt.toPrintVar));
                                                foreach (var tree in trees)
                                                {
                                                    sb.AppendFormat("\t{0}) ",counter);
                                                    fastgen.ToFastExpr(tree, sb, false);
                                                    sb.AppendLine();
                                                    counter++;
                                                }
                                                sb.Append("]");
                                                msg += sb.ToString();
                                                break;
                                            }
                                    }
                                    break;
                                }
                        }
                        break;
                    }
                case (Tokens.STRING):
                    {
                        StringQueryDef ie = query as StringQueryDef;
                        msg = ie.message;
                        break;
                    }
                case (Tokens.IS_EMPTY_TRANS):
                case (Tokens.IS_EMPTY_LANG):
                    {
                        IsEmptyQueryDef ie = query as IsEmptyQueryDef;

                        if (ie.isTrans)
                        {
                            IsEmptyTransQueryDef te = ie as IsEmptyTransQueryDef;
                            TreeTransducer trans = OperationTranGen.getTreeAutomatonFromExpr(te.trans, fti, defs);

                            if (trans.IsEmpty)
                                msg = string.Format("The transformation '{0}' is empty", te.trans);
                            else
                            {
                                FastGen fg = new FastGen(z3p);
                                StringBuilder sb = new StringBuilder();
                                var tre = trans.ComputeDomainAcceptor().GenerateWitness();
                                fg.ToFastExpr(tre, sb, false);
                                msg = string.Format("The transformation '{0}' is not empty\n ---> '{0}' accepts the tree '{1}'", te.trans, sb.ToString());
                            }
                            //For assertions
                            if (te.isAssert)
                            {
                                if (trans.IsEmpty == te.assertTrue)
                                    return true;
                                else
                                    throw new FastAssertException(msg, te.func.name.line, te.func.name.position);
                            }

                        }
                        else
                        {
                            IsEmptyLangQueryDef te = ie as IsEmptyLangQueryDef;
                            TreeTransducer lang = OperationTranGen.getTreeAutomatonFromExpr(te.lang, fti, defs);

                            if (lang.IsEmpty)
                            {
                                msg = string.Format("The transformation '{0}' is empty", te.lang);
                            }
                            else
                            {
                                FastGen fg = new FastGen(z3p);
                                StringBuilder sb = new StringBuilder();
                                var tre = lang.GenerateWitness();
                                fg.ToFastExpr(tre, sb, false);
                                msg = string.Format("The language '{0}' is not empty\n ---> '{0}' accepts the tree '{1}'", te.lang, sb.ToString());
                            }

                            if (te.isAssert)
                            {
                                if (lang.IsEmpty == te.assertTrue)
                                    return true;
                                else
                                    throw new FastAssertException(msg, te.func.name.line, te.func.name.position);
                            }
                        }
                        break;
                    }
                case (Tokens.EQ_TRANS):
                case (Tokens.EQ_LANG):
                    {
                        EquivQueryDef ie = query as EquivQueryDef;

                        if (ie.isTransEquiv)
                        {
                            TransEquivQueryDef te = ie as TransEquivQueryDef;
                            TreeTransducer trans1 = OperationTranGen.getTreeAutomatonFromExpr(te.trans1, fti, defs);
                            TreeTransducer trans2 = OperationTranGen.getTreeAutomatonFromExpr(te.trans2, fti, defs);

                            throw new FastException(FastExceptionKind.NotImplemented);

                            //if (te.isAssert)
                            //{
                            //    if (lang.IsEmpty == te.assertTrue)
                            //        return true;
                            //    else
                            //        throw new FastAssertException(ct.func.name.line, ct.func.name.position);
                            //}
                            //msg = string.Format("The transformation '{0}' is{1} empty", te.trans, trans.IsEmpty ? "" : " not");
                        }
                        else
                        {
                            StringBuilder sb = new StringBuilder();
                            FastGen fg = new FastGen(z3p);
                            StringBuilder msgBd = new StringBuilder();
                            LangEquivQueryDef te = ie as LangEquivQueryDef;
                            TreeTransducer lang1 = OperationTranGen.getTreeAutomatonFromExpr(te.lang1, fti, defs);
                            TreeTransducer lang2 = OperationTranGen.getTreeAutomatonFromExpr(te.lang2, fti, defs);

                            var areEquiv = false;
                            string counterex ="";
                            //var lang1m = lang1.MinimizeMoore();
                            TreeTransducer lang1_compl = lang1.Complement();
                            TreeTransducer lang = lang1_compl.Intersect(lang2);
                            if (lang.IsEmpty)
                            {
                                lang = lang2.Complement().Intersect(lang1);
                                areEquiv = lang.IsEmpty;
                                if (!areEquiv)
                                {
                                    fg.ToFastExpr(lang.GenerateWitness(), sb, false);
                                    counterex = string.Format("---> the language '{0}' contains the tree '{1}' while '{2}' doesn't.", te.lang1, sb.ToString(), te.lang2);
                                }
                            }
                            else
                            {
                                var tre = lang.GenerateWitness();
                                fg.ToFastExpr(tre, sb, false);
                                counterex = string.Format("---> the language '{0}' contains the tree '{1}' while '{2}' doesn't.", te.lang2, sb.ToString(),te.lang1);
                            }

                            msgBd.AppendFormat("The language '{0}' is{1} equivalent to language '{2}':", te.lang1, lang.IsEmpty ? "" : " not",te.lang2);
                            if (!areEquiv)
                            {
                                msgBd.AppendLine();
                                msgBd.Append(counterex);
                            }
                            msg = msgBd.ToString();

                            //Assertion case
                            if (te.isAssert)
                            {
                                if (areEquiv == te.assertTrue)
                                    return true;
                                else
                                    throw new FastAssertException(msg, te.func.name.line, te.func.name.position);
                            }
                        }
                        break;
                    }
                case (Tokens.GEN_CSHARP):
                    {
                        GenCodeQueryDef cge = query as GenCodeQueryDef;
                        StringBuilder sb = new StringBuilder();

                        switch (cge.language)
                        {
                            case PLang.CSharp:
                                fti.ToFast(sb);
                                FastPgm pgm = Parser.ParseFromString(sb.ToString());
                                sb = new StringBuilder();
                                sb.AppendLine("generated C# [");
                                CsharpGenerator.GenerateCode(pgm, sb);
                                sb.AppendLine("]");
                                msg = sb.ToString();
                                break;
                            case PLang.Javascript:
                                throw new FastException(FastExceptionKind.NotImplemented);
                        }
                        break;
                    }
                default:
                    throw new FastException(FastExceptionKind.InternalError);
            }
            fti.fastLog.WriteLog(LogLevel.Minimal, msg);
            fti.queryRes.Add(new QueryResult(msg));
            return true;
        }
 private Expr GenerateZ3WhereExpr(Value expr, FastTransducerInstance fti)
 {
     switch (expr.sort.kind)
     {
         case (FastSortKind.Char):
             {
                 return z3p.MkNumeral(expr.token.ToInt(), z3p.CharSort);
             }
         case (FastSortKind.Bool):
             {
                 if (expr.token.Kind == Tokens.TRUE)
                     return z3p.True;
                 if (expr.token.Kind == Tokens.FALSE)
                     return z3p.False;
                 break;
             }
         case (FastSortKind.Int):
             {
                 return z3p.MkInt(expr.token.ToInt());
             }
         case (FastSortKind.Real):
             {
                 String[] parts = expr.token.text.Split('.');
                 if (parts.Length == 2 && Regex.IsMatch(parts[1],"[0]+"))
                 {
                     return z3p.Z3.MkReal(parts[0]);
                 }
                 return z3p.Z3.MkReal(expr.token.text);
             }
         case (FastSortKind.String):
             {
                 return z3p.MkListFromString(expr.token.text.Substring(1, expr.token.text.Length - 2), z3p.CharSort);
             }
         case (FastSortKind.Tree):
             {
                 String[] lr = expr.token.text.Split('.');
                 return z3p.GetEnumElement(lr[0], lr[1]);
             }
     }
     throw new Exception("Unexpected Sort");
 }
 private Expr GenerateZ3WhereExpr(Variable expr, FastTransducerInstance fti)
 {
     foreach (var c in fti.consts)
     {
         if (c.name == expr.token.text)
             return c.value;
     }
     return z3p.MkProj(alphabet.tupleKeys.IndexOf(expr.token.text), alphabet.alph.AttrVar);
 }
        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);

                    }
            }
        }
 private Expr GenerateZ3ToExpr(RecordExp expr, RankedAlphabetSort outputAlph, FastTransducerInstance fti)
 {
     Expr[] terms = new Expr[expr.args.Count];
     for (int i = 0; i < expr.args.Count; i++)
     {
         terms[i] = GenerateZ3ExprFromWhereExpr(expr.args.ElementAt<FExp>(i), fti);
     }
     return z3p.MkApp(outputAlph.tupleFuncDec, terms);
 }
 public Expr GenerateZ3ExprFromToExpr(FExp expr, RankedAlphabetSort outputAlph, List<FastToken> children, int from, List<string> reachedStates, List<Def> queue, Dictionary<string, Def> defs, FastTransducerInstance fti, List<int>[] nextStatesL)
 {
     switch (expr.kind)
     {
         case (FExpKind.App):
             return GenerateZ3ToExpr((AppExp)expr, outputAlph, children, from, reachedStates, queue, defs, fti, nextStatesL);
         case (FExpKind.Value):
             return GenerateZ3ToExpr((Value)expr, outputAlph);
         case (FExpKind.Var):
             return GenerateZ3ToExpr((Variable)expr, outputAlph, children);
     }
     return null;
 }
        /// <summary>
        /// Generate a tree from a tree definition
        /// </summary>
        public bool AddTree(TreeDef def, FastTransducerInstance fti, Dictionary<string, Def> defs)
        {
            switch (def.tdkind)
            {
                case TreeDefKind.Witness:
                    {
                        TreeWitnessDef ce = def as TreeWitnessDef;
                        TreeTransducer language = OperationTranGen.getTreeAutomatonFromExpr(ce.language, fti, defs);
                        var res = language.GenerateWitness();
                        if(res!=null)
                            this.trees.Add(def.func.name.text, new Expr[] { res });
                        else
                            this.trees.Add(def.func.name.text, new Expr[] { });
                        break;
                    }
                case TreeDefKind.Apply:
                    {
                        TreeAppDef ce = def as TreeAppDef;
                        IEnumerable<Expr> t = null;
                        if (ce.expr.kind == FExpKind.Var)
                        {
                            foreach (var treeDef in fti.treeDefinitions)
                                if (treeDef.Value.trees.TryGetValue(ce.expr.token.text, out t))
                                    break;
                            if (t == null)
                            {
                                this.trees.Add(def.func.name.text, null);
                                return true;
                            }
                        }
                        else
                        {
                            Expr t1 = GenerateZ3ExprFromToExpr(ce.expr, alphabet, null, 0, null, null, defs, fti, null).Simplify();
                            List<Expr> tl = new List<Expr>();
                            tl.Add(t1);
                            t = tl;
                        }

                        TreeTransducer a = OperationTranGen.getTreeAutomatonFromExpr(ce.transducer, fti, defs);

                        var results = a.Apply(t);
                        this.trees.Add(def.func.name.text, results.ToList());
                        break;
                    }
                case TreeDefKind.Tree:
                    {
                        TreeExpDef ce = def as TreeExpDef;
                        IEnumerable<Expr> t = null;
                        if (ce.expr.kind == FExpKind.Var)
                        {
                            foreach (var treeDef in fti.treeDefinitions)
                                if (treeDef.Value.trees.TryGetValue(ce.expr.token.text, out t))
                                    break;
                            if (t == null)
                            {
                                this.trees.Add(def.func.name.text, null);
                                return true;
                            }
                        }
                        else
                        {
                            Expr t1 = GenerateZ3ExprFromToExpr(ce.expr, alphabet, null, 0, null, null, defs, fti, null).Simplify();
                            List<Expr> tl = new List<Expr>();
                            tl.Add(t1);
                            t = tl;
                        }
                        this.trees.Add(def.func.name.text, t);
                        break;
                    }
            }
            return true;
        }
 /// <summary>
 /// Generate a transducer rooted in def from a compound transducer definition and adds it to the list of transducers
 /// </summary>
 public bool AddTransducer(TransDefDef def, FastTransducerInstance fti, Dictionary<string, Def> defs)
 {
     var transducer = OperationTranGen.getTreeAutomatonFromExpr(def.expr, fti, defs);
     transducers.Add(def.func.name.text, transducer);
     if (transducer.IsEmpty)
         fti.fastLog.WriteLog(LogLevel.Normal, string.Format("the transducer '{0}' is empty", def.func.name.text));
     return true;
 }
 //Generate Z3 sorts from enum definition
 private static bool GenerateEnumSort(EnumDef def, FastTransducerInstance fti)
 {
     fti.enums.Add(new EnumSort(def, z3p));
     return true;
 }
 private Expr GenerateZ3Expr(Variable expr, FastTransducerInstance fti)
 {
     foreach (var c in fti.consts)
     {
         if (c.name == expr.token.text)
             return c.value;
     }
     throw new Exception("The constant " + expr.token.text + " wasn't defined before");
 }
 //Generate Z3 enums from enum definitions
 private static bool GenerateEnumSorts(List<EnumDef> enumDefs, FastTransducerInstance fti)
 {
     foreach (var def in enumDefs)
     {
         if (!GenerateEnumSort(def, fti))
              return false;
         break;
     }
     return true;
 }
 //Generate resultsfor queries
 private static bool GenerateQueryResults(List<QueryDef> queryDefs, Dictionary<string, Def> defs, FastTransducerInstance fti)
 {
     for (int i = 0; i < queryDefs.Count; i++ )
     {
         var def = queryDefs[i];
         if (!GenerateQueryResult(def, defs, fti))
             return false;
     }
     return true;
 }
        //Generates all the languages and transductions of a particular tree definitions class
        private static bool GenerateLanguagesAndTransductions(Dictionary<string, Def> defs, FastTransducerInstance fti)
        {
            foreach (var def in defs.Values)
            {
                if (def.kind == DefKind.Lang){
                    LangDef langDef= def as LangDef;
                    if (langDef.isPublic)
                        if (!fti.treeDefinitions[langDef.domain.name.text].AddTransducer(def, fti, defs))
                            return false;
                        else
                            fti.fastLog.WriteLog(LogLevel.Maximal, string.Format("language '{0}:{1}' correctly added", langDef.func.name.text, langDef.domain.name.text));
                }
                if (def.kind == DefKind.Trans)
                {
                    TransDef transDef = def as TransDef;
                    if (transDef.isPublic)
                        if (!fti.treeDefinitions[transDef.domain.name.text].AddTransducer(def, fti, defs))
                            return false;
                        else
                            fti.fastLog.WriteLog(LogLevel.Maximal,
                                string.Format("transformation '{0}: {1} -> {2}' correctly added",
                                    transDef.func.name.text,
                                    transDef.domain.name.text,
                                    transDef.range.name.text)
                                );
                }
            }

            return true;
        }
 //Generate the trees definition for each alphabet
 private static bool GenerateTreeClasses(Dictionary<string, Def> defs, FastTransducerInstance fti)
 {
     //Generate one tree with the corresponding transductions and languages for each ranked alphabet
     foreach (var ras in fti.alphabets.Values)
     {
         if (!GenerateTreeClass(defs, fti, ras))
             return false;
     }
     //Generate Languages And Transductions
     if (!GenerateLanguagesAndTransductions(defs, fti))
         return false;
     return GenerateDefinitions(defs, fti);
 }
        /// <summary>
        /// Generate a FastTransducerInstance from a Fast program 
        /// </summary>
        /// <param name="fpg">the fast program</param>
        public static FastTransducerInstance MkFastTransducerInstance(FastPgm fpg,TextWriter tw, LogLevel lv)
        {
            FastTransducerInstance fti = new FastTransducerInstance(tw);
            fti.fastLog.setLogLevel(lv);
            z3p = new Z3Provider();

            Dictionary<string, Def> definitions = new Dictionary<string, Def>();
            List<EnumDef> enumDefs = new List<EnumDef>();
            List<Def> constFunDefs = new List<Def>();
            List<AlphabetDef> alphabetDefs = new List<AlphabetDef>();
            List<QueryDef> queryDefs = new List<QueryDef>();

            foreach (var def in fpg.defs)
            {
                switch(def.kind){
                    case DefKind.Query: {
                        queryDefs.Add(def as QueryDef);
                        break;
                    }
                    case DefKind.Alphabet: {
                        alphabetDefs.Add(def as AlphabetDef);
                        break;
                    }
                    case DefKind.Enum: {
                        enumDefs.Add(def as EnumDef);
                        break;
                    }
                    case DefKind.Const: case DefKind.Function:{
                        constFunDefs.Add(def);
                        break;
                    }
                }
                if(def.kind!=DefKind.Query)
                    definitions[def.id.text] = def;
            }

            if (!GenerateEnumSorts(enumDefs, fti))
                return null;
            if (!GenerateConstsAndFunctions(constFunDefs, fti))
                return null;
            if (!GenerateAlphabetSorts(alphabetDefs, fti))
                return null;
            if (!GenerateTreeClasses(definitions, fti))
                return null;
            if (!GenerateQueryResults(queryDefs, definitions, fti))
                return null;

            return fti;
        }
        public RankedAlphabetSort(AlphabetDef def, Z3Provider z3p, FastTransducerInstance fti)
        {
            this.z3p = z3p;

            List<string> alphSymbols = new List<string>();
            List<int> alphArities = new List<int>();

            // Create list of symbols with corresponding arities
            foreach (var sym in def.symbols)
            {
                alphSymbols.Add(sym.name.text);
                alphArities.Add(sym.arity - 1);
            }

            alphFieldsSorts = new List<Sort>();
            tupleKeys = new List<String>();

            foreach (var field in def.attrSort.fieldSorts)
            {
                tupleKeys.Add(field.Key);
                switch (field.Value.kind)
                {
                    case (FastSortKind.Char):
                        {
                            alphFieldsSorts.Add(z3p.CharSort);
                            break;
                        }
                    case (FastSortKind.Real):
                        {
                            alphFieldsSorts.Add(z3p.RealSort);
                            break;
                        }
                    case (FastSortKind.Bool):
                        {
                            alphFieldsSorts.Add(z3p.BoolSort);
                            break;
                        }
                    case (FastSortKind.Int):
                        {
                            alphFieldsSorts.Add(z3p.IntSort);
                            break;
                        }
                    case (FastSortKind.String):
                        {
                            alphFieldsSorts.Add(z3p.MkListSort(z3p.CharSort));
                            break;
                        }
                    case (FastSortKind.Tree):
                        {
                            foreach (var enumSort in fti.enums)
                            {
                                if (enumSort.name == field.Value.name.text)
                                {
                                    alphFieldsSorts.Add(enumSort.sort);
                                    break;
                                }
                            }
                            break;
                        }
                }
            }

            this.tupleName = "$" + def.id.text;
            this.tupleTests = new FuncDecl[alphFieldsSorts.Count];
            var tupsymbs = new Symbol[tupleKeys.Count];
            int j = 0;
            foreach(var v in tupleKeys){
                tupsymbs[j]= z3p.Z3.MkSymbol(v);
                j++;
            }
            var tup = z3p.Z3.MkTupleSort(z3p.Z3.MkSymbol(tupleName), tupsymbs, alphFieldsSorts.ToArray());
            this.tupleSort = tup;
            this.tupleFuncDec = tup.MkDecl;
            this.tupleTests = tup.FieldDecls;

            this.alph = z3p.TT.MkRankedAlphabet(def.id.text, this.tupleSort, alphSymbols.ToArray(), alphArities.ToArray());
            this.alphName = def.id.text;
            this.alphSort = this.alph.AlphabetSort;
        }
 //Generate the tree definition corresponding to a ranked alphabet
 private static bool GenerateTreeClass(Dictionary<string, Def> defs, FastTransducerInstance fti, RankedAlphabetSort ras)
 {
     //Create a new tree definition
     TreeClassDef treeDef = new TreeClassDef(ras, z3p);
     fti.treeDefinitions.Add(ras.alphName, treeDef);
     return true;
 }
 //Generate Z3 sorts from alphabet definitions
 private static bool GenerateAlphabetSorts(List<AlphabetDef> defs, FastTransducerInstance fti)
 {
     //Generate the set of alphabets in fti
     foreach (var def in defs)
     {
         AlphabetDef alphDef = def as AlphabetDef;
         fti.alphabets[alphDef.id.text] = new RankedAlphabetSort(alphDef, z3p, fti);
         fti.fastLog.WriteLog(LogLevel.Maximal, string.Format("alphabet '{0}' correctly added", alphDef.id));
     }
     return true;
 }
        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");
                    }
            }
        }
 //Generate Z3 term for const def
 private static bool GenerateConst(ConstDef def, FastTransducerInstance fti)
 {
     fti.consts.Add(new Const(def, fti, z3p));
     return true;
 }
        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);
            }
        }
 internal static TreeTransducer getTreeAutomatonFromExpr(BuiltinTransExp expr, FastTransducerInstance fti, Dictionary<string, Def> defs)
 {
     Stopwatch stopwatch = new Stopwatch();
     switch (expr.kind)
     {
         case (BuiltinTransExpKind.Composition):
             {
                 var castExpr = expr as CompositionExp;
                 var a = getTreeAutomatonFromExpr(castExpr.arg1, fti, defs);
                 var b = getTreeAutomatonFromExpr(castExpr.arg2, fti, defs);
                 stopwatch.Start();
                 var comp = TreeTransducer.ComposeR(a, b).RemoveMultipleInitialStates();
                 stopwatch.Stop();
                 fti.fastLog.WriteLog(LogLevel.Normal, string.Format("compose of {0} and {1}: {2} ms", castExpr.arg1.ToString(), castExpr.arg2.ToString(), stopwatch.ElapsedMilliseconds));
                 return comp;
             }
         case (BuiltinTransExpKind.RestrictionInp):
             {
                 var castExpr = expr as RestrictionInpExp;
                 var a = getTreeAutomatonFromExpr(castExpr.arg1, fti, defs);
                 var b = getTreeAutomatonFromExpr(castExpr.arg2, fti, defs);
                 stopwatch.Start();
                 var res = a.RestrictDomain(b).RemoveMultipleInitialStates();
                 stopwatch.Stop();
                 fti.fastLog.WriteLog(LogLevel.Normal, string.Format("compose of {0} and {1}: {2} ms", castExpr.arg1.ToString(), castExpr.arg2.ToString(), stopwatch.ElapsedMilliseconds));
                 return res;
             }
         case (BuiltinTransExpKind.RestrictionOut):
             {
                 var castExpr = expr as RestrictionOutExp;
                 var a = getTreeAutomatonFromExpr(castExpr.arg1, fti, defs);
                 var b = getTreeAutomatonFromExpr(castExpr.arg2, fti, defs);
                 stopwatch.Start();
                 var res = a.RestrictRange(b).RemoveMultipleInitialStates();
                 stopwatch.Stop();
                 fti.fastLog.WriteLog(LogLevel.Normal, string.Format("compose of {0} and {1}: {2} ms", castExpr.arg1.ToString(), castExpr.arg2.ToString(), stopwatch.ElapsedMilliseconds));
                 return res;
             }
         case (BuiltinTransExpKind.Var):
             {
                 var castExpr = expr as TransNameExp;
                 return fti.treeDefinitions[castExpr.domain.name.text].transducers[castExpr.func.name.text];
             }
         default:
             {
                 throw new FastException(FastExceptionKind.NotImplemented);
             }
     }
     throw new FastException(FastExceptionKind.InternalError);
 }