3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-13 12:28:44 +00:00
z3/lib/poly_simplifier_plugin.cpp
Leonardo de Moura e9eab22e5c Z3 sources
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2012-10-02 11:35:25 -07:00

813 lines
24 KiB
C++

/*++
Copyright (c) 2007 Microsoft Corporation
Module Name:
poly_simplifier_plugin.cpp
Abstract:
Abstract class for families that have polynomials.
Author:
Leonardo (leonardo) 2008-01-08
--*/
#include"poly_simplifier_plugin.h"
#include"ast_pp.h"
#include"ast_util.h"
#include"ast_smt2_pp.h"
poly_simplifier_plugin::poly_simplifier_plugin(symbol const & fname, ast_manager & m, decl_kind add, decl_kind mul, decl_kind uminus, decl_kind sub,
decl_kind num):
simplifier_plugin(fname, m),
m_ADD(add),
m_MUL(mul),
m_SUB(sub),
m_UMINUS(uminus),
m_NUM(num),
m_curr_sort(0),
m_curr_sort_zero(0) {
}
expr * poly_simplifier_plugin::mk_add(unsigned num_args, expr * const * args) {
SASSERT(num_args > 0);
#ifdef Z3DEBUG
// check for incorrect use of mk_add
for (unsigned i = 0; i < num_args; i++) {
SASSERT(!is_zero(args[i]));
}
#endif
if (num_args == 1)
return args[0];
else
return m_manager.mk_app(m_fid, m_ADD, num_args, args);
}
expr * poly_simplifier_plugin::mk_mul(unsigned num_args, expr * const * args) {
SASSERT(num_args > 0);
#ifdef Z3DEBUG
// check for incorrect use of mk_mul
set_curr_sort(args[0]);
SASSERT(!is_zero(args[0]));
numeral k;
for (unsigned i = 0; i < num_args; i++) {
SASSERT(!is_numeral(args[i], k) || !k.is_one());
SASSERT(i == 0 || !is_numeral(args[i]));
}
#endif
if (num_args == 1)
return args[0];
else if (num_args == 2)
return m_manager.mk_app(m_fid, m_MUL, args[0], args[1]);
else if (is_numeral(args[0]))
return m_manager.mk_app(m_fid, m_MUL, args[0], m_manager.mk_app(m_fid, m_MUL, num_args - 1, args+1));
else
return m_manager.mk_app(m_fid, m_MUL, num_args, args);
}
expr * poly_simplifier_plugin::mk_mul(numeral const & c, expr * body) {
numeral c_prime;
c_prime = norm(c);
if (c_prime.is_zero())
return 0;
if (body == 0)
return mk_numeral(c_prime);
if (c_prime.is_one())
return body;
set_curr_sort(body);
expr * args[2] = { mk_numeral(c_prime), body };
return mk_mul(2, args);
}
/**
\brief Traverse args, and copy the non-numeral exprs to result, and accumulate the
value of the numerals in k.
*/
void poly_simplifier_plugin::process_monomial(unsigned num_args, expr * const * args, numeral & k, ptr_buffer<expr> & result) {
rational v;
for (unsigned i = 0; i < num_args; i++) {
expr * arg = args[i];
if (is_numeral(arg, v))
k *= v;
else
result.push_back(arg);
}
}
#ifdef Z3DEBUG
/**
\brief Return true if m is a wellformed monomial.
*/
bool poly_simplifier_plugin::wf_monomial(expr * m) const {
SASSERT(!is_add(m));
if (is_mul(m)) {
app * curr = to_app(m);
expr * pp = 0;
if (is_numeral(curr->get_arg(0)))
pp = curr->get_arg(1);
else
pp = curr;
if (is_mul(pp)) {
for (unsigned i = 0; i < to_app(pp)->get_num_args(); i++) {
expr * arg = to_app(pp)->get_arg(i);
CTRACE("wf_monomial_bug", is_mul(arg),
tout << "m: " << mk_ismt2_pp(m, m_manager) << "\n";
tout << "pp: " << mk_ismt2_pp(pp, m_manager) << "\n";
tout << "arg: " << mk_ismt2_pp(arg, m_manager) << "\n";
tout << "i: " << i << "\n";
);
SASSERT(!is_mul(arg));
SASSERT(!is_numeral(arg));
}
}
}
return true;
}
/**
\brief Return true if m is a wellformed polynomial.
*/
bool poly_simplifier_plugin::wf_polynomial(expr * m) const {
if (is_add(m)) {
for (unsigned i = 0; i < to_app(m)->get_num_args(); i++) {
expr * arg = to_app(m)->get_arg(i);
SASSERT(!is_add(arg));
SASSERT(wf_monomial(arg));
}
}
else if (is_mul(m)) {
SASSERT(wf_monomial(m));
}
return true;
}
#endif
/**
\brief Functor used to sort the elements of a monomial.
Force numeric constants to be in the beginning.
*/
struct monomial_element_lt_proc {
poly_simplifier_plugin & m_plugin;
monomial_element_lt_proc(poly_simplifier_plugin & p):m_plugin(p) {}
bool operator()(expr * m1, expr * m2) const {
SASSERT(!m_plugin.is_numeral(m1) || !m_plugin.is_numeral(m2));
if (m_plugin.is_numeral(m1))
return true;
if (m_plugin.is_numeral(m2))
return false;
return m1->get_id() < m2->get_id();
}
};
/**
\brief Create a monomial (* args).
*/
void poly_simplifier_plugin::mk_monomial(unsigned num_args, expr * * args, expr_ref & result) {
switch(num_args) {
case 0:
result = mk_one();
break;
case 1:
result = args[0];
break;
default:
std::sort(args, args + num_args, monomial_element_lt_proc(*this));
result = mk_mul(num_args, args);
SASSERT(wf_monomial(result));
break;
}
}
/**
\brief Return the body of the monomial. That is, the monomial without a coefficient.
Examples: (* 2 (* x y)) ==> (* x y)
(* x x) ==> (* x x)
x ==> x
10 ==> 10
*/
expr * poly_simplifier_plugin::get_monomial_body(expr * m) {
TRACE("get_monomial_body_bug", tout << mk_pp(m, m_manager) << "\n";);
SASSERT(wf_monomial(m));
if (!is_mul(m))
return m;
if (is_numeral(to_app(m)->get_arg(0)))
return to_app(m)->get_arg(1);
return m;
}
inline bool is_essentially_var(expr * n, family_id fid) {
SASSERT(is_var(n) || is_app(n));
return is_var(n) || to_app(n)->get_family_id() != fid;
}
/**
\brief Hack for ordering monomials.
We want an order << where
- (* c1 m1) << (* c2 m2) when m1->get_id() < m2->get_id(), and c1 and c2 are numerals.
- c << m when c is a numeral, and m is not.
So, this method returns -1 for numerals, and the id of the body of the monomial
*/
int poly_simplifier_plugin::get_monomial_body_order(expr * m) {
if (is_essentially_var(m, m_fid)) {
return m->get_id();
}
else if (is_mul(m)) {
if (is_numeral(to_app(m)->get_arg(0)))
return to_app(m)->get_arg(1)->get_id();
else
return m->get_id();
}
else if (is_numeral(m)) {
return -1;
}
else {
return m->get_id();
}
}
void poly_simplifier_plugin::get_monomial_coeff(expr * m, numeral & result) {
SASSERT(!is_numeral(m));
SASSERT(wf_monomial(m));
if (!is_mul(m))
result = numeral::one();
else if (is_numeral(to_app(m)->get_arg(0), result))
return;
else
result = numeral::one();
}
/**
\brief Return true if n1 and n2 can be written as k1 * t and k2 * t, where k1 and
k2 are numerals, or n1 and n2 are both numerals.
*/
bool poly_simplifier_plugin::eq_monomials_modulo_k(expr * n1, expr * n2) {
bool is_num1 = is_numeral(n1);
bool is_num2 = is_numeral(n2);
if (is_num1 != is_num2)
return false;
if (is_num1 && is_num2)
return true;
return get_monomial_body(n1) == get_monomial_body(n2);
}
/**
\brief Return (k1 + k2) * t (or (k1 - k2) * t when inv = true), where n1 = k1 * t, and n2 = k2 * t
Return false if the monomials cancel each other.
*/
bool poly_simplifier_plugin::merge_monomials(bool inv, expr * n1, expr * n2, expr_ref & result) {
numeral k1;
numeral k2;
bool is_num1 = is_numeral(n1, k1);
bool is_num2 = is_numeral(n2, k2);
SASSERT(is_num1 == is_num2);
if (!is_num1 && !is_num2) {
get_monomial_coeff(n1, k1);
get_monomial_coeff(n2, k2);
SASSERT(eq_monomials_modulo_k(n1, n2));
}
if (inv)
k1 -= k2;
else
k1 += k2;
if (k1.is_zero())
return false;
if (is_num1 && is_num2) {
result = mk_numeral(k1);
}
else {
expr * b = get_monomial_body(n1);
if (k1.is_one())
result = b;
else
result = m_manager.mk_app(m_fid, m_MUL, mk_numeral(k1), b);
}
return true;
}
/**
\brief Return a monomial equivalent to -n.
*/
void poly_simplifier_plugin::inv_monomial(expr * n, expr_ref & result) {
set_curr_sort(n);
SASSERT(wf_monomial(n));
rational v;
SASSERT(n != 0);
TRACE("inv_monomial_bug", tout << "n:\n" << mk_ismt2_pp(n, m_manager) << "\n";);
if (is_numeral(n, v)) {
TRACE("inv_monomial_bug", tout << "is numeral\n";);
v.neg();
result = mk_numeral(v);
}
else {
TRACE("inv_monomial_bug", tout << "is not numeral\n";);
numeral k;
get_monomial_coeff(n, k);
expr * b = get_monomial_body(n);
k.neg();
if (k.is_one())
result = b;
else
result = m_manager.mk_app(m_fid, m_MUL, mk_numeral(k), b);
}
}
/**
\brief Add a monomial n to result.
*/
template<bool Inv>
void poly_simplifier_plugin::add_monomial_core(expr * n, expr_ref_vector & result) {
if (is_zero(n))
return;
if (Inv) {
expr_ref n_prime(m_manager);
inv_monomial(n, n_prime);
result.push_back(n_prime);
}
else {
result.push_back(n);
}
}
void poly_simplifier_plugin::add_monomial(bool inv, expr * n, expr_ref_vector & result) {
if (inv)
add_monomial_core<true>(n, result);
else
add_monomial_core<false>(n, result);
}
/**
\brief Copy the monomials in n to result. The monomials are inverted if inv is true.
Equivalent monomials are merged.
*/
template<bool Inv>
void poly_simplifier_plugin::process_sum_of_monomials_core(expr * n, expr_ref_vector & result) {
SASSERT(wf_polynomial(n));
if (is_add(n)) {
for (unsigned i = 0; i < to_app(n)->get_num_args(); i++)
add_monomial_core<Inv>(to_app(n)->get_arg(i), result);
}
else {
add_monomial_core<Inv>(n, result);
}
}
void poly_simplifier_plugin::process_sum_of_monomials(bool inv, expr * n, expr_ref_vector & result) {
if (inv)
process_sum_of_monomials_core<true>(n, result);
else
process_sum_of_monomials_core<false>(n, result);
}
/**
\brief Copy the (non-numeral) monomials in n to result. The monomials are inverted if inv is true.
Equivalent monomials are merged. The constant (numeral) monomials are accumulated in k.
*/
void poly_simplifier_plugin::process_sum_of_monomials(bool inv, expr * n, expr_ref_vector & result, numeral & k) {
SASSERT(wf_polynomial(n));
numeral val;
if (is_add(n)) {
for (unsigned i = 0; i < to_app(n)->get_num_args(); i++) {
expr * arg = to_app(n)->get_arg(i);
if (is_numeral(arg, val)) {
k += inv ? -val : val;
}
else {
add_monomial(inv, arg, result);
}
}
}
else if (is_numeral(n, val)) {
k += inv ? -val : val;
}
else {
add_monomial(inv, n, result);
}
}
/**
\brief Functor used to sort monomials.
Force numeric constants to be in the beginning of a polynomial.
*/
struct monomial_lt_proc {
poly_simplifier_plugin & m_plugin;
monomial_lt_proc(poly_simplifier_plugin & p):m_plugin(p) {}
bool operator()(expr * m1, expr * m2) const {
return m_plugin.get_monomial_body_order(m1) < m_plugin.get_monomial_body_order(m2);
}
};
void poly_simplifier_plugin::mk_sum_of_monomials_core(unsigned sz, expr ** ms, expr_ref & result) {
switch (sz) {
case 0:
result = mk_zero();
break;
case 1:
result = ms[0];
break;
default:
result = mk_add(sz, ms);
break;
}
}
/**
\brief Return true if m is essentially a variable, or is of the form (* c x),
where c is a numeral and x is essentially a variable.
Store the "variable" in x.
*/
bool poly_simplifier_plugin::is_simple_monomial(expr * m, expr * & x) {
if (is_essentially_var(m, m_fid)) {
x = m;
return true;
}
if (is_app(m) && to_app(m)->get_num_args() == 2) {
expr * arg1 = to_app(m)->get_arg(0);
expr * arg2 = to_app(m)->get_arg(1);
if (is_numeral(arg1) && is_essentially_var(arg2, m_fid)) {
x = arg2;
return true;
}
}
return false;
}
/**
\brief Return true if all monomials are simple, and each "variable" occurs only once.
The method assumes the monomials were sorted using monomial_lt_proc.
*/
bool poly_simplifier_plugin::is_simple_sum_of_monomials(expr_ref_vector & monomials) {
expr * last_var = 0;
expr * curr_var = 0;
unsigned size = monomials.size();
for (unsigned i = 0; i < size; i++) {
expr * m = monomials.get(i);
if (!is_simple_monomial(m, curr_var))
return false;
if (curr_var == last_var)
return false;
last_var = curr_var;
}
return true;
}
/**
\brief Store in result the sum of the given monomials.
*/
void poly_simplifier_plugin::mk_sum_of_monomials(expr_ref_vector & monomials, expr_ref & result) {
switch (monomials.size()) {
case 0:
result = mk_zero();
break;
case 1:
result = monomials.get(0);
break;
default: {
std::sort(monomials.c_ptr(), monomials.c_ptr() + monomials.size(), monomial_lt_proc(*this));
if (is_simple_sum_of_monomials(monomials)) {
mk_sum_of_monomials_core(monomials.size(), monomials.c_ptr(), result);
return;
}
ptr_buffer<expr> new_monomials;
expr * last_body = 0;
numeral last_coeff;
numeral coeff;
unsigned sz = monomials.size();
for (unsigned i = 0; i < sz; i++) {
expr * m = monomials.get(i);
expr * body = 0;
if (!is_numeral(m, coeff)) {
body = get_monomial_body(m);
get_monomial_coeff(m, coeff);
}
if (last_body == body) {
last_coeff += coeff;
continue;
}
expr * new_m = mk_mul(last_coeff, last_body);
if (new_m)
new_monomials.push_back(new_m);
last_body = body;
last_coeff = coeff;
}
expr * new_m = mk_mul(last_coeff, last_body);
if (new_m)
new_monomials.push_back(new_m);
TRACE("mk_sum", for (unsigned i = 0; i < monomials.size(); i++) tout << mk_pp(monomials.get(i), m_manager) << "\n";
tout << "======>\n";
for (unsigned i = 0; i < new_monomials.size(); i++) tout << mk_pp(new_monomials.get(i), m_manager) << "\n";);
mk_sum_of_monomials_core(new_monomials.size(), new_monomials.c_ptr(), result);
break;
} }
}
/**
\brief Auxiliary template for mk_add_core
*/
template<bool Inv>
void poly_simplifier_plugin::mk_add_core_core(unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(num_args >= 2);
expr_ref_vector monomials(m_manager);
process_sum_of_monomials_core<false>(args[0], monomials);
for (unsigned i = 1; i < num_args; i++) {
process_sum_of_monomials_core<Inv>(args[i], monomials);
}
TRACE("mk_add_core_bug",
for (unsigned i = 0; i < monomials.size(); i++) {
SASSERT(monomials.get(i) != 0);
tout << mk_ismt2_pp(monomials.get(i), m_manager) << "\n";
});
mk_sum_of_monomials(monomials, result);
}
/**
\brief Return a sum of monomials. The method assume that each arg in args is a sum of monomials.
If inv is true, then all but the first argument in args are inverted.
*/
void poly_simplifier_plugin::mk_add_core(bool inv, unsigned num_args, expr * const * args, expr_ref & result) {
TRACE("mk_add_core_bug",
for (unsigned i = 0; i < num_args; i++) {
SASSERT(args[i] != 0);
tout << mk_ismt2_pp(args[i], m_manager) << "\n";
});
switch (num_args) {
case 0:
result = mk_zero();
break;
case 1:
result = args[0];
break;
default:
if (inv)
mk_add_core_core<true>(num_args, args, result);
else
mk_add_core_core<false>(num_args, args, result);
break;
}
}
void poly_simplifier_plugin::mk_add(unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(num_args > 0);
set_curr_sort(args[0]);
mk_add_core(false, num_args, args, result);
}
void poly_simplifier_plugin::mk_add(expr * arg1, expr * arg2, expr_ref & result) {
expr * args[2] = { arg1, arg2 };
mk_add(2, args, result);
}
void poly_simplifier_plugin::mk_sub(unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(num_args > 0);
set_curr_sort(args[0]);
mk_add_core(true, num_args, args, result);
}
void poly_simplifier_plugin::mk_sub(expr * arg1, expr * arg2, expr_ref & result) {
expr * args[2] = { arg1, arg2 };
mk_sub(2, args, result);
}
void poly_simplifier_plugin::mk_uminus(expr * arg, expr_ref & result) {
set_curr_sort(arg);
rational v;
if (is_numeral(arg, v)) {
v.neg();
result = mk_numeral(v);
}
else {
expr_ref zero(mk_zero(), m_manager);
mk_sub(zero.get(), arg, result);
}
}
/**
\brief Add monomial n to result, the coeff of n is stored in k.
*/
void poly_simplifier_plugin::append_to_monomial(expr * n, numeral & k, ptr_buffer<expr> & result) {
SASSERT(wf_monomial(n));
rational val;
if (is_numeral(n, val)) {
k *= val;
return;
}
get_monomial_coeff(n, val);
k *= val;
n = get_monomial_body(n);
if (is_mul(n)) {
for (unsigned i = 0; i < to_app(n)->get_num_args(); i++)
result.push_back(to_app(n)->get_arg(i));
}
else {
result.push_back(n);
}
}
/**
\brief Return a sum of monomials that is equivalent to (* args[0] ... args[num_args-1]).
This method assumes that each arg[i] is a sum of monomials.
*/
void poly_simplifier_plugin::mk_mul(unsigned num_args, expr * const * args, expr_ref & result) {
if (num_args == 1) {
result = args[0];
return;
}
rational val;
if (num_args == 2 && is_numeral(args[0], val) && is_essentially_var(args[1], m_fid)) {
if (val.is_one())
result = args[1];
else if (val.is_zero())
result = args[0];
else
result = mk_mul(num_args, args);
return;
}
if (num_args == 2 && is_essentially_var(args[0], m_fid) && is_numeral(args[1], val)) {
if (val.is_one())
result = args[0];
else if (val.is_zero())
result = args[1];
else {
expr * inv_args[2] = { args[1], args[0] };
result = mk_mul(2, inv_args);
}
return;
}
TRACE("mk_mul_bug",
for (unsigned i = 0; i < num_args; i++) {
tout << mk_pp(args[i], m_manager) << "\n";
});
set_curr_sort(args[0]);
buffer<unsigned> szs;
buffer<unsigned> it;
vector<ptr_vector<expr> > sums;
for (unsigned i = 0; i < num_args; i ++) {
it.push_back(0);
expr * arg = args[i];
SASSERT(wf_polynomial(arg));
sums.push_back(ptr_vector<expr>());
ptr_vector<expr> & v = sums.back();
if (is_add(arg)) {
v.append(to_app(arg)->get_num_args(), to_app(arg)->get_args());
}
else {
v.push_back(arg);
}
szs.push_back(v.size());
}
expr_ref_vector monomials(m_manager);
do {
rational k(1);
ptr_buffer<expr> m;
for (unsigned i = 0; i < num_args; i++) {
ptr_vector<expr> & v = sums[i];
expr * arg = v[it[i]];
TRACE("mk_mul_bug", tout << "k: " << k << " arg: " << mk_pp(arg, m_manager) << "\n";);
append_to_monomial(arg, k, m);
TRACE("mk_mul_bug", tout << "after k: " << k << "\n";);
}
expr_ref num(m_manager);
if (!k.is_zero() && !k.is_one()) {
num = mk_numeral(k);
m.push_back(num);
// bit-vectors can normalize
// to 1 during
// internalization.
if (is_numeral(num, k) && k.is_one()) {
m.pop_back();
}
}
if (!k.is_zero()) {
expr_ref new_monomial(m_manager);
TRACE("mk_mul_bug",
for (unsigned i = 0; i < m.size(); i++) {
tout << mk_pp(m[i], m_manager) << "\n";
});
mk_monomial(m.size(), m.c_ptr(), new_monomial);
TRACE("mk_mul_bug", tout << "new_monomial:\n" << mk_pp(new_monomial, m_manager) << "\n";);
add_monomial_core<false>(new_monomial, monomials);
}
}
while (product_iterator_next(szs.size(), szs.c_ptr(), it.c_ptr()));
mk_sum_of_monomials(monomials, result);
}
void poly_simplifier_plugin::mk_mul(expr * arg1, expr * arg2, expr_ref & result) {
expr * args[2] = { arg1, arg2 };
mk_mul(2, args, result);
}
bool poly_simplifier_plugin::reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result) {
set_reduce_invoked();
unsigned i = 0;
for (; i < num_args; i++)
if (!is_numeral(args[i]))
break;
if (i == num_args) {
// all arguments are numerals
// check if arguments are different...
ptr_buffer<expr> buffer;
buffer.append(num_args, args);
std::sort(buffer.begin(), buffer.end(), ast_lt_proc());
for (unsigned i = 0; i < num_args; i++) {
if (i > 0 && buffer[i] == buffer[i-1]) {
result = m_manager.mk_false();
return true;
}
}
result = m_manager.mk_true();
return true;
}
return false;
}
bool poly_simplifier_plugin::reduce(func_decl * f, unsigned num_args, rational const * mults, expr * const * args, expr_ref & result) {
set_reduce_invoked();
if (is_decl_of(f, m_fid, m_ADD)) {
SASSERT(num_args > 0);
set_curr_sort(args[0]);
expr_ref_buffer args1(m_manager);
for (unsigned i = 0; i < num_args; ++i) {
expr * arg = args[i];
rational m = norm(mults[i]);
if (m.is_zero()) {
// skip
}
else if (m.is_one()) {
args1.push_back(arg);
}
else {
expr_ref k(m_manager);
k = mk_numeral(m);
expr_ref new_arg(m_manager);
mk_mul(k, args[i], new_arg);
args1.push_back(new_arg);
}
}
if (args1.empty()) {
result = mk_zero();
}
else {
mk_add(args1.size(), args1.c_ptr(), result);
}
return true;
}
else {
return simplifier_plugin::reduce(f, num_args, mults, args, result);
}
}
/**
\brief Return true if n is can be put into the form (+ v t) or (+ (- v) t)
\c inv = true will contain true if (- v) is found, and false otherwise.
*/
bool poly_simplifier_plugin::is_var_plus_ground(expr * n, bool & inv, var * & v, expr_ref & t) {
if (!is_add(n) || is_ground(n))
return false;
ptr_buffer<expr> args;
v = 0;
expr * curr = to_app(n);
bool stop = false;
inv = false;
while (!stop) {
expr * arg;
expr * neg_arg;
if (is_add(curr)) {
arg = to_app(curr)->get_arg(0);
curr = to_app(curr)->get_arg(1);
}
else {
arg = curr;
stop = true;
}
if (is_ground(arg)) {
TRACE("model_checker_bug", tout << "pushing:\n" << mk_pp(arg, m_manager) << "\n";);
args.push_back(arg);
}
else if (is_var(arg)) {
if (v != 0)
return false; // already found variable
v = to_var(arg);
}
else if (is_times_minus_one(arg, neg_arg) && is_var(neg_arg)) {
if (v != 0)
return false; // already found variable
v = to_var(neg_arg);
inv = true;
}
else {
return false; // non ground term.
}
}
if (v == 0)
return false; // did not find variable
SASSERT(!args.empty());
mk_add(args.size(), args.c_ptr(), t);
return true;
}