Пример #1
0
 void context::display_subexprs_info(std::ostream & out, expr * n) const {
     ptr_buffer<expr> todo;
     todo.push_back(n);
     while (!todo.empty()) {
         expr * n = todo.back();
         todo.pop_back();
         out << "#";
         out.width(6);
         out << std::left << n->get_id();
         out << ", relevant: " << is_relevant(n);
         if (m_manager.is_bool(n)) {
             out << ", val: ";
             out.width(7);
             out << std::right;
             if (lit_internalized(n))
                 out << get_assignment(n);
             else
                 out << "l_undef";
         }
         if (e_internalized(n)) {
             enode * e = get_enode(n);
             out << ", root: #" << e->get_root()->get_owner_id();
         }
         out << "\n";
         if (is_app(n)) {
             for (unsigned i = 0; i < to_app(n)->get_num_args(); i++)
                 todo.push_back(to_app(n)->get_arg(i));
         }
     }
 }
Пример #2
0
bool quasi_macros::depends_on(expr * e, func_decl * f) const {
    ptr_vector<expr> todo;
    expr_mark visited;
    todo.push_back(e);
    while(!todo.empty()) {
        expr * cur = todo.back();
        todo.pop_back();

        if (visited.is_marked(cur))
            continue;

        if (is_app(cur)) {
            app * a = to_app(cur);
            if (a->get_decl() == f)
                return true;

            unsigned j = a->get_num_args();
            while (j>0)
                todo.push_back(a->get_arg(--j));
        }

        visited.mark(cur, true);
    }
    return false;
}
Пример #3
0
 void save_candidate(expr * t, bool form_ctx) {
     if (!form_ctx)
         return;
     if (!m.is_bool(t))
         return;
     if (!m_has_term_ite.is_marked(t))
         return;
     if (!is_app(t))
         return;
     if (to_app(t)->get_family_id() == m.get_basic_family_id()) {
         switch (to_app(t)->get_decl_kind()) {
         case OP_OR:
         case OP_AND:
         case OP_NOT:
         case OP_XOR:
         case OP_IMPLIES:
         case OP_TRUE:
         case OP_FALSE:
         case OP_ITE:
             return;
         case OP_EQ:
         case OP_DISTINCT:
             if (m.is_bool(to_app(t)->get_arg(0)))
                 return;
             break;
         default:
             break;
         }
     }
     // it is an atom in a formula context (i.e., it is not nested inside a term),
     // and it contains a term if-then-else.
     m_candidates.insert(t);
 }
Пример #4
0
 void mk_interp_tail_simplifier::rule_substitution::apply(app * a, app_ref& res) {
     SASSERT(m_rule);
     expr_ref res_e(m);
     m_subst.apply(a, res_e);
     SASSERT(is_app(res_e.get()));
     res = to_app(res_e.get());
 }
Пример #5
0
 void process(expr * f) {
     if (fvisited.is_marked(f))
         return;
     fvisited.mark(f);
     todo.push_back(f);
     while (!todo.empty()) {
         expr * t = todo.back();
         todo.pop_back();
         if (is_uninterp_const(t))
             continue;
         if (is_app(t) && to_app(t)->get_family_id() == m.get_basic_family_id() && to_app(t)->get_num_args() > 0) {
             decl_kind k = to_app(t)->get_decl_kind();
             if (k == OP_OR || k == OP_NOT || k == OP_IFF || ((k == OP_EQ || k == OP_ITE) && m.is_bool(to_app(t)->get_arg(1)))) {
                 unsigned num = to_app(t)->get_num_args();
                 for (unsigned i = 0; i < num; i++) {
                     expr * arg = to_app(t)->get_arg(i);
                     if (fvisited.is_marked(arg))
                         continue;
                     fvisited.mark(arg);
                     todo.push_back(arg);
                 }
             }
         }
         else {
             quick_for_each_expr(proc, tvisited, t);
         }
     }
 }
Пример #6
0
    void variable_intersection::populate_self(const app * a)
    {
        SASSERT(is_uninterp(a));

        //TODO: optimize quadratic complexity
        //TODO: optimize number of checks when variable occurs multiple times
        unsigned arity = a->get_num_args();
        for(unsigned i1=0; i1<arity; i1++) {
            expr * e1=a->get_arg(i1);
            if(is_var(e1)) {
                var* v1=to_var(e1);
                for(unsigned i2=i1+1; i2<arity; i2++) {
                    expr * e2=a->get_arg(i2);
                    if(!is_var(e2)) {
                        continue;
                    }
                    var* v2=to_var(e2);
                    if(v1->get_idx()==v2->get_idx()) {
                        add_pair(i1, i2);
                    }
                }
            }
            else {
                SASSERT(is_app(e1));
                app * c1 = to_app(e1);
                SASSERT(c1->get_num_args()==0); //c1 must be a constant

                m_const_indexes.push_back(i1);
                m_consts.push_back(c1);

                SASSERT(m_const_indexes.size()==m_consts.size());
            }
        }
    }
Пример #7
0
 void mk_array_instantiation::retrieve_selects(expr* e)
 {
   //If the expression is not a function application, we ignore it
   if (!is_app(e)) {
     return;
   }
   app*f=to_app(e);
   //Call the function recursively on all arguments
   unsigned nbargs = f->get_num_args();
   for(unsigned i=0;i<nbargs;i++)
   {
     retrieve_selects(f->get_arg(i));
   }
   //If it is a select, then add it to selects
   if(m_a.is_select(f))
   {
     SASSERT(!m_a.is_array(get_sort(e)));
     selects.insert_if_not_there(f->get_arg(0), ptr_vector<expr>());
     selects[f->get_arg(0)].push_back(e);
   }
   //If it is a condition between arrays, for example the result of a store, then add it to the equiv_classes
   if(m_a.is_store(f))
   {
     eq_classes.merge(e, f->get_arg(0));
   }
   else if(m.is_eq(f) && m_a.is_array(get_sort(f->get_arg(0))))
   {
     eq_classes.merge(f->get_arg(0), f->get_arg(1));
   }
 }
Пример #8
0
bool expr_delta::delta_dfs(unsigned& n, expr* e, expr_ref& result) {
    ast_manager& m = m_manager;
    if (m.is_true(e) || m.is_false(e)) {
        return false;
    }
    if (n == 0 && m.is_bool(e)) {
        result = m.mk_true();
        return true;
    }
    else if (n == 1 && m.is_bool(e)) {
        result = m.mk_false();
        return true;
    }
    else if (is_app(e)) {        
        if (m.is_bool(e)) {
            SASSERT(n >= 2);
            n -= 2;
        }
        return delta_dfs(n, to_app(e), result);
    }
    else if (is_quantifier(e)) {
        SASSERT(n >= 2);
        n -= 2;
        quantifier* q = to_quantifier(e);
        if (delta_dfs(n, q->get_expr(), result)) {
            result = m.update_quantifier(q, result.get());
            return true;
        }
        else {
            return false;
        }
    }
    return false;
}
void expr_context_simplifier::reduce_rec(expr * m, expr_ref & result) {
    //
    // reduce expr in context evaluation.
    //
    bool polarity;
    if (m_context.find(m, polarity)) {
        result = polarity ? m_manager.mk_true() : m_manager.mk_false();
    }
    else if (m_mark.is_marked(m) && !m_manager.is_not(m)) {
        result = m;
    }
    else if (is_quantifier(m)) {
        reduce_rec(to_quantifier(m), result);
        m_mark.mark(m, true);
    }
    else if (is_app(m)) {
        reduce_rec(to_app(m), result);
        m_mark.mark(m, true);
    }
    else if (is_var(m)) {
        result = m;
        m_mark.mark(m, true);
    }
    else {
        UNREACHABLE();
        result = m;
    }
}
Пример #10
0
expr const & get_app_rev_args(expr const & e, buffer<expr> & args) {
    expr const * it = &e;
    while (is_app(*it)) {
        args.push_back(app_arg(*it));
        it = &(app_fn(*it));
    }
    return *it;
}
Пример #11
0
 void visit_args(expr * t, expr_fast_mark1 & visited) {
     if (is_app(t)) {
         for (expr * arg : *to_app(t)) {
             save_degree(arg, m_one);
             visit(arg, visited);
         }
     }
 }
Пример #12
0
unsigned get_app_num_args(expr const & e) {
    expr const * it = &e;
    unsigned n = 0;
    while (is_app(*it)) {
        it = &(app_fn(*it));
        n++;
    }
    return n;
}
Пример #13
0
 static bool to_apps(unsigned n, Z3_app const es[], app_ref_vector& result) {
     for (unsigned i = 0; i < n; ++i) {
         if (!is_app(to_app(es[i]))) {
             return false;
         }
         result.push_back (to_app (es [i]));
     }
     return true;
 }
Пример #14
0
 br_status pull_ite(expr_ref & result) {
     expr * t = result.get();
     if (is_app(t)) {
         br_status st = pull_ite(to_app(t)->get_decl(), to_app(t)->get_num_args(), to_app(t)->get_args(), result);
         if (st != BR_FAILED)
             return st;
     }
     return BR_DONE;
 }
Пример #15
0
 void visit_args(expr * t, expr_fast_mark1 & visited) {
     if (is_app(t)) {
         unsigned num_args = to_app(t)->get_num_args();
         for (unsigned i = 0; i < num_args; i++) {
             expr * arg = to_app(t)->get_arg(i);
             save_degree(arg, m_one);
             visit(arg, visited);
         }
     }
 }
Пример #16
0
expr const & get_app_args(expr const & e, buffer<expr> & args) {
    unsigned sz = args.size();
    expr const * it = &e;
    while (is_app(*it)) {
        args.push_back(app_arg(*it));
        it = &(app_fn(*it));
    }
    std::reverse(args.begin() + sz, args.end());
    return *it;
}
Пример #17
0
br_status datatype_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) {
    if (!is_app(lhs) || !is_app(rhs) || !m_util.is_constructor(to_app(lhs)) || !m_util.is_constructor(to_app(rhs)))
        return BR_FAILED;
    if (to_app(lhs)->get_decl() != to_app(rhs)->get_decl()) {
        result = m().mk_false();
        return BR_DONE;
    }

    // Remark: In datatype_simplifier_plugin, we used
    // m_basic_simplifier to create '=' and 'and' applications in the
    // following code. This trick not guarantee that the final expression
    // will be fully simplified.
    //
    // Example:
    // The assertion
    // (assert (= (cons a1 (cons a2 (cons a3 (cons (+ a4 1) (cons (+ a5 c5) (cons a6 nil))))))
    //         (cons b1 (cons b2 (cons b3 (cons b4 (cons b5 (cons b6 nil))))))))
    //
    // After applying asserted_formulas::reduce(), the following formula was generated.
    //
    //   (= a1 b1)
    //   (= a2 b2)
    //   (= a3 b3)
    //   (= (+ a4 (* (- 1) b4)) (- 1))
    //   (= (+ c5 a5) b5)                    <<< NOT SIMPLIFIED WITH RESPECT TO ARITHMETIC
    //   (= (cons a6 nil) (cons b6 nil)))    <<< NOT SIMPLIFIED WITH RESPECT TO DATATYPE theory
    //
    // Note that asserted_formulas::reduce() applied the simplier many times.
    // After the first simplification step we had:
    //  (= a1 b1)
    //  (= (cons a2 (cons a3 (cons (+ a4 1) (cons (+ a5 c5) (cons a6 nil))))))
    //     (cons b2 (cons b3 (cons b4 (cons b5 (cons b6 nil))))))

    ptr_buffer<expr> eqs;
    unsigned num = to_app(lhs)->get_num_args();
    SASSERT(num == to_app(rhs)->get_num_args());
    for (unsigned i = 0; i < num; ++i) {
        eqs.push_back(m().mk_eq(to_app(lhs)->get_arg(i), to_app(rhs)->get_arg(i)));
    }
    result = m().mk_and(eqs.size(), eqs.c_ptr());
    return BR_REWRITE2;
}
Пример #18
0
/**
   \brief Return true if s >_{lpo} t_i forall children t_i of t.
*/
bool lpo::dominates_args(expr_offset s, expr_offset t, unsigned depth) {
    SASSERT(is_app(t.get_expr()));
    unsigned num_args = to_app(t.get_expr())->get_num_args();
    unsigned off      = t.get_offset();
    for (unsigned i = 0; i < num_args; i++) {
        expr * t_i = to_app(t.get_expr())->get_arg(i);
        if (!greater(s, expr_offset(t_i, off), depth+1))
            return false;
    }
    return true;
}
Пример #19
0
 expr* apply_accessor(
     ptr_vector<func_decl> const& acc,
     unsigned j,
     func_decl* f,
     expr* c) {
     if (is_app(c) && to_app(c)->get_decl() == f) {
         return to_app(c)->get_arg(j);
     }
     else {
         return m.mk_app(acc[j], c);
     }
 }
Пример #20
0
bool is_atom(ast_manager & m, expr * n) {
    if (is_quantifier(n) || !m.is_bool(n))
        return false;
    if (is_var(n))
        return true;
    SASSERT(is_app(n));
    if (to_app(n)->get_family_id() != m.get_basic_family_id()) {
        return true;        
    }
    // the other operators of the basic family are not considered atomic: distinct, ite, and, or, iff, xor, not, implies.
    return (m.is_eq(n) && !m.is_bool(to_app(n)->get_arg(0))) || m.is_true(n) || m.is_false(n);
}
Пример #21
0
static optional<pair<expr, unsigned>> find_hyp_core(expr const & meta, F && pred) {
    expr const * it = &meta;
    unsigned i = 0;
    while (is_app(*it)) {
        expr const & h = app_arg(*it);
        if (pred(h))
            return some(mk_pair(h, i));
        i++;
        it = &app_fn(*it);
    }
    return optional<pair<expr, unsigned>>();
}
Пример #22
0
/**
   \brief Return true if s_i >=_{lpo} t for some arg s_i of s.
 */
bool lpo::arg_dominates_expr(expr_offset s, expr_offset t, unsigned depth) {
    SASSERT(is_app(s.get_expr()));
    unsigned num_args = to_app(s.get_expr())->get_num_args();
    unsigned off      = s.get_offset();
    for (unsigned i = 0; i < num_args; i++) {
        expr * s_i = to_app(s.get_expr())->get_arg(i);
        result r   = compare(expr_offset(s_i, off), t, depth+1);
        if (r == EQUAL || r == GREATER)
            return true;
    }
    return false;
}
Пример #23
0
        bool reduce_arg(expr* a, expr_ref& result) {

            sort* s = get_sort(a);
            if (!m_imp.is_fd(s)) {
                return false;
            }
            unsigned bv_size = get_bv_size(s);

            if (is_var(a)) {
                result = m.mk_var(to_var(a)->get_idx(), m_bv.mk_sort(bv_size));
                return true;
            }
            SASSERT(is_app(a));
            func_decl* f = to_app(a)->get_decl();
            if (m_dt.is_constructor(f)) {
                unsigned idx = m_dt.get_constructor_idx(f);
                result = m_bv.mk_numeral(idx, bv_size);
            }
            else if (is_uninterp_const(a)) {
                func_decl* f_fresh;
                if (m_imp.m_enum2bv.find(f, f_fresh)) {
                    result = m.mk_const(f_fresh);
                    return true;
                }

                // create a fresh variable, add bounds constraints for it.
                unsigned nc = m_dt.get_datatype_num_constructors(s);
                result = m.mk_fresh_const(f->get_name().str().c_str(), m_bv.mk_sort(bv_size));
                f_fresh = to_app(result)->get_decl();
                if (!is_power_of_two(nc) || nc == 1) {
                    m_imp.m_bounds.push_back(m_bv.mk_ule(result, m_bv.mk_numeral(nc-1, bv_size)));
                }                
                expr_ref f_def(m);
                ptr_vector<func_decl> const& cs = *m_dt.get_datatype_constructors(s);
                f_def = m.mk_const(cs[nc-1]);
                for (unsigned i = nc - 1; i > 0; ) {
                    --i;
                    f_def = m.mk_ite(m.mk_eq(result, m_bv.mk_numeral(i,bv_size)), m.mk_const(cs[i]), f_def);
                }
                m_imp.m_enum2def.insert(f, f_def);
                m_imp.m_enum2bv.insert(f, f_fresh);
                m_imp.m_bv2enum.insert(f_fresh, f);
                m_imp.m_enum_consts.push_back(f);
                m_imp.m_enum_bvs.push_back(f_fresh);
                m_imp.m_enum_defs.push_back(f_def);
            }
            else {
                throw_non_fd(a);
            }
            ++m_imp.m_num_translated;
            return true;
        }
Пример #24
0
        void operator()(expr * t) {
            SASSERT(m.is_bool(t));
            push_frame(t, true);
            SASSERT(!m_frame_stack.empty());
            while (!m_frame_stack.empty()) {
                frame & fr    = m_frame_stack.back();
                expr * t      = fr.m_t;
                bool form_ctx = fr.m_form_ctx;
                TRACE("cofactor", tout << "processing, form_ctx: " << form_ctx << "\n" << mk_bounded_pp(t, m) << "\n";);

                m_owner.checkpoint();
                
                if (m_processed.is_marked(t)) {
                    save_candidate(t, form_ctx);
                    m_frame_stack.pop_back();
                    continue;
                }

                if (m.is_term_ite(t)) {
                    m_has_term_ite.mark(t);
                    m_processed.mark(t);
                    m_frame_stack.pop_back();
                    continue;
                }
                
                if (fr.m_first) {
                    fr.m_first   = false;
                    bool visited = true;
                    if (is_app(t)) {
                        unsigned num_args = to_app(t)->get_num_args();
                        for (unsigned i = 0; i < num_args; i++)
                            visit(to_app(t)->get_arg(i), form_ctx, visited);
                    }
                    // ignoring quantifiers
                    if (!visited)
                        continue;
                }
                
                if (is_app(t)) {
                    unsigned num_args = to_app(t)->get_num_args();
                    unsigned i;
                    for (i = 0; i < num_args; i++) {
                        if (m_has_term_ite.is_marked(to_app(t)->get_arg(i)))
                            break;
                    }
                    if (i < num_args) {
                        m_has_term_ite.mark(t);
                        TRACE("cofactor", tout << "saving candidate: " << form_ctx << "\n" << mk_bounded_pp(t, m) << "\n";);
                        save_candidate(t, form_ctx);
                    }
Пример #25
0
expr const & get_app_args_at_most(expr const & e, unsigned num, buffer<expr> & args) {
    unsigned sz = args.size();
    expr const * it = &e;
    unsigned i = 0;
    while (is_app(*it)) {
        if (i == num)
            break;
        args.push_back(app_arg(*it));
        it = &(app_fn(*it));
        i++;
    }
    std::reverse(args.begin() + sz, args.end());
    return *it;
}
Пример #26
0
 void fixedpoint_context::simplify_rules(
     unsigned num_rules, expr* const* rules, 
     unsigned num_outputs,  func_decl* const* outputs, expr_ref_vector& result) {
     ast_manager& m = m_context.get_manager();
     
     datalog::context ctx(m, m_context.get_fparams());
     datalog::rule_manager& rm = ctx.get_rule_manager();
     for (unsigned i = 0; i < num_rules; ++i) {
         expr* rule = rules[i], *body, *head;
         while (true) {
             if (is_quantifier(rule)) {
                 rule = to_quantifier(rule)->get_expr();
             }
             else if (m.is_implies(rule, body, head)) {
                 rule = head;
             }
             else {
                 break;
             }
         }
         if (is_app(rule)) {
             func_decl* r = to_app(rule)->get_decl();
             if (!ctx.is_predicate(r)) {
                 ctx.register_predicate(r);
                 if (num_outputs == 0) {
                     ctx.set_output_predicate(r);
                 }
             }
         }
     }
     for (unsigned i = 0; i < num_outputs; ++i) {
         ctx.set_output_predicate(outputs[i]);
     }
     for (unsigned i = 0; i < num_rules; ++i) {
         expr* rule = rules[i];
         ctx.add_rule(rule, symbol::null);
     }
     model_converter_ref mc; // not exposed.
     proof_converter_ref pc; // not exposed.
     ctx.apply_default_transformation(mc, pc);
     datalog::rule_set const& new_rules = ctx.get_rules();
     datalog::rule_set::iterator it = new_rules.begin(), end = new_rules.end();
     for (; it != end; ++it) {
         datalog::rule* r = *it;
         expr_ref fml(m);
         r->to_formula(fml);
         result.push_back(fml);
     }
 }
Пример #27
0
    void mk_coalesce::merge_rules(rule_ref& tgt, rule const& src) {
        SASSERT(same_body(*tgt.get(), src));
        m_sub1.reset();
        m_sub2.reset();
        m_idx = 0;
        app_ref pred(m), head(m);
        expr_ref fml1(m), fml2(m), fml(m);
        app_ref_vector tail(m);
        ptr_vector<sort> sorts1, sorts2;
        expr_ref_vector conjs1(m), conjs(m);
        rule_ref res(rm);
        bool_rewriter bwr(m);
        svector<bool> is_neg;
        tgt->get_vars(sorts1);
        src.get_vars(sorts2);

        mk_pred(head, src.get_head(), tgt->get_head()); 
        for (unsigned i = 0; i < src.get_uninterpreted_tail_size(); ++i) {
            mk_pred(pred, src.get_tail(i), tgt->get_tail(i));
            tail.push_back(pred);
            is_neg.push_back(src.is_neg_tail(i));
        }           
        extract_conjs(m_sub1, src, fml1);
        extract_conjs(m_sub2, *tgt.get(),  fml2);
        bwr.mk_or(fml1, fml2, fml);
        SASSERT(is_app(fml));
        tail.push_back(to_app(fml));
        is_neg.push_back(false);
        res = rm.mk(head, tail.size(), tail.c_ptr(), is_neg.c_ptr(), tgt->name());
        if (m_ctx.generate_proof_trace()) {
            src.to_formula(fml1);
            tgt->to_formula(fml2);
            res->to_formula(fml);
#if 0
            sort* ps = m.mk_proof_sort();
            sort* domain[3] = { ps, ps, m.mk_bool_sort() };
            func_decl* merge = m.mk_func_decl(symbol("merge-clauses"), 3, domain, ps);  // TBD: ad-hoc proof rule
            expr* args[3] = { m.mk_asserted(fml1), m.mk_asserted(fml2), fml };
            // ...m_pc->insert(m.mk_app(merge, 3, args));
#else
            svector<std::pair<unsigned, unsigned> > pos;
            vector<expr_ref_vector> substs;
            proof* p = src.get_proof();
            p = m.mk_hyper_resolve(1, &p, fml, pos, substs);
            res->set_proof(m, p);
#endif
        }
        tgt = res;
    }
Пример #28
0
order::result lpo::lex_compare(expr_offset s, expr_offset t, unsigned depth) {
    SASSERT(is_app(s.get_expr()));
    SASSERT(is_app(t.get_expr()));
    app * _s = to_app(s.get_expr());
    app * _t = to_app(t.get_expr());
    unsigned num_args1 = _s->get_num_args();
    unsigned num_args2 = _t->get_num_args();
    unsigned num_args  = std::min(num_args1, num_args2);
    unsigned off1      = s.get_offset();
    unsigned off2      = t.get_offset();
    result r = EQUAL;
    for (unsigned i = 0; i < num_args; i++) {
        r = compare(expr_offset(_s->get_arg(i), off1), expr_offset(_t->get_arg(i), off2), depth+1);
        if (r != EQUAL)
            break;
    }
    if (r == EQUAL) {
        if (num_args1 > num_args2)
            return GREATER;
        if (num_args1 < num_args2)
            return NOT_GTEQ;
    }
    return r;
}
Пример #29
0
 void superposition::insert_r(clause * cls, expr * n, unsigned i, bool lhs) {
     if (is_app(n)) {
         unsigned idx = (i << 1) | static_cast<unsigned>(lhs);
         
         clause_pos_pair new_pair(cls, idx);
         SASSERT(m_todo.empty());
         m_todo.push_back(to_app(n));
         while (!m_todo.empty()) {
             app * n = m_todo.back();
             m_todo.pop_back();
             clause_pos_set * s = m_r2clause_set.get_parents(n);
             if (s == 0 || !s->contains(new_pair)) {
                 m_r.insert(n);
                 m_r2clause_set.insert(new_pair, n);
                 unsigned num_args = n->get_num_args();
                 for (unsigned i = 0; i < num_args; i++) {
                     expr * c = n->get_arg(i);
                     if (is_app(c))
                         m_todo.push_back(to_app(c));
                 }
             }
         }
     }
 }
Пример #30
0
 Z3_pattern Z3_API Z3_mk_pattern(Z3_context c, unsigned num_patterns, Z3_ast const terms[]) {
     Z3_TRY;
     LOG_Z3_mk_pattern(c, num_patterns, terms);
     RESET_ERROR_CODE();
     for (unsigned i = 0; i < num_patterns; ++i) {
         if (!is_app(to_expr(terms[i]))) {
             SET_ERROR_CODE(Z3_INVALID_ARG);
             RETURN_Z3(0);
         }
     }
     app* a = mk_c(c)->m().mk_pattern(num_patterns, reinterpret_cast<app*const*>(to_exprs(terms)));
     mk_c(c)->save_ast_trail(a);
     RETURN_Z3(of_pattern(a));
     Z3_CATCH_RETURN(0);
 }