Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_finite_product_relation.cpp
Abstract:
<abstract>
Author:
Krystof Hoder (t-khoder) 2010-09-14.
Revision History:
--*/
#include<string>
#include "muz/base/dl_context.h"
#include "muz/rel/dl_relation_manager.h"
#include "muz/rel/dl_table_relation.h"
#include "muz/rel/dl_finite_product_relation.h"
#include "ast/rewriter/bool_rewriter.h"
#include "ast/rewriter/th_rewriter.h"
namespace datalog {
const table_sort finite_product_relation::s_rel_idx_sort = INT_MAX;
void universal_delete(finite_product_relation* ptr) {
ptr->deallocate();
}
finite_product_relation & finite_product_relation_plugin::get(relation_base & r) {
SASSERT(r.get_plugin().is_finite_product_relation());
return static_cast<finite_product_relation &>(r);
}
const finite_product_relation & finite_product_relation_plugin::get(const relation_base & r) {
SASSERT(r.get_plugin().is_finite_product_relation());
return static_cast<const finite_product_relation &>(r);
}
finite_product_relation * finite_product_relation_plugin::get(relation_base * r) {
SASSERT(!r || r->get_plugin().is_finite_product_relation());
return static_cast<finite_product_relation *>(r);
}
const finite_product_relation * finite_product_relation_plugin::get(const relation_base * r) {
SASSERT(!r || r->get_plugin().is_finite_product_relation());
return static_cast<const finite_product_relation *>(r);
}
symbol finite_product_relation_plugin::get_name(relation_plugin & inner_plugin) {
std::string str = std::string("fpr_")+inner_plugin.get_name().bare_str();
return symbol(str.c_str());
}
finite_product_relation_plugin & finite_product_relation_plugin::get_plugin(relation_manager & rmgr,
relation_plugin & inner) {
finite_product_relation_plugin * res;
if(!rmgr.try_get_finite_product_relation_plugin(inner, res)) {
res = alloc(finite_product_relation_plugin, inner, rmgr);
rmgr.register_plugin(res);
}
return *res;
}
finite_product_relation_plugin::finite_product_relation_plugin(relation_plugin & inner_plugin,
relation_manager & manager)
: relation_plugin(get_name(inner_plugin), manager, ST_FINITE_PRODUCT_RELATION),
m_inner_plugin(inner_plugin), m_spec_store(*this) {
}
void finite_product_relation_plugin::initialize(family_id fid) {
relation_plugin::initialize(fid);
m_spec_store.add_available_kind(get_kind());
}
family_id finite_product_relation_plugin::get_relation_kind(finite_product_relation & r,
const bool * table_columns) {
const relation_signature & sig = r.get_signature();
bool_vector table_cols_vect(sig.size(), table_columns);
return m_spec_store.get_relation_kind(sig, rel_spec(table_cols_vect));
}
void finite_product_relation_plugin::get_all_possible_table_columns(relation_manager & rmgr,
const relation_signature & s, bool_vector & table_columns) {
SASSERT(table_columns.empty());
unsigned s_sz = s.size();
for(unsigned i=0; i<s_sz; i++) {
table_sort t_sort;
bool can_be_table_column = rmgr.relation_sort_to_table(s[i], t_sort);
table_columns.push_back(can_be_table_column);
}
}
void finite_product_relation_plugin::split_signatures(const relation_signature & s,
table_signature & table_sig, relation_signature & remaining_sig) {
relation_manager & rmgr = get_manager();
unsigned n = s.size();
for(unsigned i=0; i<n; i++) {
table_sort t_sort;
if(rmgr.relation_sort_to_table(s[i], t_sort)) {
table_sig.push_back(t_sort);
}
else {
remaining_sig.push_back(s[i]);
}
}
}
void finite_product_relation_plugin::split_signatures(const relation_signature & s, const bool * table_columns,
table_signature & table_sig, relation_signature & remaining_sig) {
relation_manager & rmgr = get_manager();
unsigned n = s.size();
for(unsigned i=0; i<n; i++) {
if(table_columns[i]) {
table_sort t_sort;
VERIFY( rmgr.relation_sort_to_table(s[i], t_sort) );
table_sig.push_back(t_sort);
}
else {
remaining_sig.push_back(s[i]);
}
}
}
bool finite_product_relation_plugin::can_handle_signature(const relation_signature & s) {
table_signature table_sig;
relation_signature remaining_sig;
split_signatures(s, table_sig, remaining_sig);
return m_inner_plugin.can_handle_signature(remaining_sig)
&& get_manager().try_get_appropriate_plugin(table_sig);
}
relation_base * finite_product_relation_plugin::mk_empty(const relation_signature & s) {
bool_vector table_columns;
get_all_possible_table_columns(s, table_columns);
#ifndef _EXTERNAL_RELEASE
unsigned s_sz = s.size();
unsigned rel_col_cnt = 0;
for(unsigned i=0; i<s_sz; i++) {
if(!table_columns[i]) {
rel_col_cnt++;
}
}
if(get_manager().get_context().dbg_fpr_nonempty_relation_signature() && rel_col_cnt==0) {
relation_signature candidate_rel_sig;
unsigned rel_sig_ofs = s.size()/2;
unsigned rel_sig_sz = s.size()-rel_sig_ofs;
candidate_rel_sig.append(rel_sig_sz, s.data()+rel_sig_ofs);
if(m_inner_plugin.can_handle_signature(candidate_rel_sig)) {
for(unsigned i=rel_sig_ofs; i<s_sz; i++) {
table_columns[i] = false;
}
}
}
#endif
return mk_empty(s, table_columns.data());
}
finite_product_relation * finite_product_relation_plugin::mk_empty(const relation_signature & s,
const bool * table_columns, family_id inner_kind) {
table_signature table_sig;
relation_signature remaining_sig;
split_signatures(s, table_columns, table_sig, remaining_sig);
table_sig.push_back(finite_product_relation::s_rel_idx_sort);
table_sig.set_functional_columns(1);
table_plugin & tplugin = get_manager().get_appropriate_plugin(table_sig);
return alloc(finite_product_relation, *this, s, table_columns, tplugin, m_inner_plugin, inner_kind);
}
finite_product_relation * finite_product_relation_plugin::mk_empty(const finite_product_relation & original) {
return get(mk_empty(original.get_signature(), original.get_kind()));
}
relation_base * finite_product_relation_plugin::mk_empty(const relation_base & original) {
SASSERT(&original.get_plugin()==this);
return mk_empty(get(original));
}
relation_base * finite_product_relation_plugin::mk_empty(const relation_signature & s, family_id kind) {
rel_spec spec;
m_spec_store.get_relation_spec(s, kind, spec);
return mk_empty(s, spec.m_table_cols.data(), spec.m_inner_kind);
}
relation_base * finite_product_relation_plugin::mk_full(func_decl* p, const relation_signature & s) {
finite_product_relation * res = get(mk_empty(s));
res->complement_self(p);
return res;
}
bool finite_product_relation_plugin::can_convert_to_table_relation(const finite_product_relation & r) {
return r.m_other_sig.empty();
}
table_relation * finite_product_relation_plugin::to_table_relation(const finite_product_relation & r) {
SASSERT(can_convert_to_table_relation(r));
r.garbage_collect(true);
const table_base & t = r.get_table();
unsigned removed_col = t.get_signature().size()-1;
scoped_ptr<table_transformer_fn> project_fun =
get_manager().mk_project_fn(r.get_table(), 1, &removed_col);
table_base * res_table = (*project_fun)(t);
SASSERT(res_table->get_signature().functional_columns()==0);
return static_cast<table_relation *>(get_manager().mk_table_relation(r.get_signature(), res_table));
}
bool finite_product_relation_plugin::can_be_converted(const relation_base & r) {
if(&r.get_plugin()==&get_inner_plugin()) {
return true;
}
if(r.from_table()) {
return get_inner_plugin().can_handle_signature(relation_signature());
}
return false;
}
finite_product_relation * finite_product_relation_plugin::mk_from_table_relation(const table_relation & r) {
func_decl* pred = nullptr;
const relation_signature & sig = r.get_signature();
const table_base & t = r.get_table();
table_plugin & tplugin = r.get_table().get_plugin();
relation_signature inner_sig;
if(!get_inner_plugin().can_handle_signature(inner_sig)) {
return nullptr;
}
table_signature idx_singleton_sig;
idx_singleton_sig.push_back(finite_product_relation::s_rel_idx_sort);
idx_singleton_sig.set_functional_columns(1);
scoped_rel<table_base> idx_singleton;
if(tplugin.can_handle_signature(idx_singleton_sig)) {
idx_singleton = tplugin.mk_empty(idx_singleton_sig);
}
else {
idx_singleton = get_manager().mk_empty_table(idx_singleton_sig);
}
table_fact idx_singleton_fact;
idx_singleton_fact.push_back(0);
idx_singleton->add_fact(idx_singleton_fact);
scoped_ptr<table_join_fn> join_fun = get_manager().mk_join_fn(t, *idx_singleton, 0, nullptr, nullptr);
SASSERT(join_fun);
scoped_rel<table_base> res_table = (*join_fun)(t, *idx_singleton);
bool_vector table_cols(sig.size(), true);
finite_product_relation * res = mk_empty(sig, table_cols.data());
relation_base * inner_rel = get_inner_plugin().mk_full(pred, inner_sig, get_inner_plugin().get_kind());
relation_vector rels;
rels.push_back(inner_rel);
res->init(*res_table, rels, true);
return res;
}
finite_product_relation * finite_product_relation_plugin::mk_from_inner_relation(const relation_base & r) {
SASSERT(&r.get_plugin()==&get_inner_plugin());
const relation_signature & sig = r.get_signature();
table_signature idx_singleton_sig;
idx_singleton_sig.push_back(finite_product_relation::s_rel_idx_sort);
idx_singleton_sig.set_functional_columns(1);
scoped_rel<table_base> idx_singleton = get_manager().mk_empty_table(idx_singleton_sig);
table_fact idx_singleton_fact;
idx_singleton_fact.push_back(0);
idx_singleton->add_fact(idx_singleton_fact);
bool_vector table_cols(sig.size(), false);
finite_product_relation * res = mk_empty(sig, table_cols.data());
relation_vector rels;
rels.push_back(r.clone());
res->init(*idx_singleton, rels, true);
return res;
}
class finite_product_relation_plugin::converting_join_fn : public convenient_relation_join_fn {
finite_product_relation_plugin & m_plugin;
scoped_ptr<relation_join_fn> m_native_join;
finite_product_relation * convert(const relation_base & r) {
SASSERT(&r.get_plugin()!=&m_plugin);
if(&r.get_plugin()==&m_plugin.get_inner_plugin()) {
return m_plugin.mk_from_inner_relation(r);
}
SASSERT(r.from_table());
const table_relation & tr = static_cast<const table_relation &>(r);
finite_product_relation * res = m_plugin.mk_from_table_relation(tr);
SASSERT(res);
return res;
}
public:
converting_join_fn(finite_product_relation_plugin & plugin, const relation_signature & sig1,
const relation_signature & sig2, unsigned col_cnt, const unsigned * cols1,
const unsigned * cols2)
: convenient_relation_join_fn(sig1, sig2, col_cnt, cols1, cols2),
m_plugin(plugin) {}
relation_base * operator()(const relation_base & r1, const relation_base & r2) override {
scoped_rel<finite_product_relation> r1_conv;
if(&r1.get_plugin()!=&m_plugin) {
r1_conv = convert(r1);
}
scoped_rel<finite_product_relation> r2_conv;
if(&r2.get_plugin()!=&m_plugin) {
r2_conv = convert(r2);
}
const finite_product_relation & fpr1 = r1_conv ? *r1_conv : get(r1);
const finite_product_relation & fpr2 = r2_conv ? *r2_conv : get(r2);
SASSERT(&fpr1.get_plugin()==&m_plugin);
SASSERT(&fpr2.get_plugin()==&m_plugin);
if(!m_native_join) {
m_native_join = m_plugin.get_manager().mk_join_fn(fpr1, fpr2, m_cols1, m_cols2, false);
}
return (*m_native_join)(fpr1, fpr2);
}
};
class finite_product_relation_plugin::join_fn : public convenient_relation_join_fn {
scoped_ptr<table_join_fn> m_tjoin_fn;
scoped_ptr<relation_join_fn> m_rjoin_fn;
unsigned_vector m_t_joined_cols1;
unsigned_vector m_t_joined_cols2;
unsigned_vector m_r_joined_cols1;
unsigned_vector m_r_joined_cols2;
unsigned_vector m_tr_table_joined_cols;
unsigned_vector m_tr_rel_joined_cols;
scoped_ptr<relation_mutator_fn> m_filter_tr_identities;
scoped_ptr<table_transformer_fn> m_tjoined_second_rel_remover;
bool_vector m_res_table_columns;
public:
class join_maker : public table_row_mutator_fn {
join_fn & m_parent;
const finite_product_relation & m_r1;
const finite_product_relation & m_r2;
relation_vector & m_rjoins;
public:
join_maker(join_fn & parent, const finite_product_relation & r1, const finite_product_relation & r2,
relation_vector & rjoins)
: m_parent(parent), m_r1(r1), m_r2(r2), m_rjoins(rjoins) {}
bool operator()(table_element * func_columns) override {
const relation_base & or1 = m_r1.get_inner_rel(func_columns[0]);
const relation_base & or2 = m_r2.get_inner_rel(func_columns[1]);
unsigned new_rel_num = m_rjoins.size();
m_rjoins.push_back(m_parent.do_rjoin(or1, or2));
func_columns[0]=new_rel_num;
return true;
}
};
join_fn(const finite_product_relation & r1, const finite_product_relation & r2, unsigned col_cnt,
const unsigned * cols1, const unsigned * cols2)
: convenient_relation_join_fn(r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2) {
unsigned second_table_after_join_ofs = r1.m_table2sig.size();
unsigned second_inner_rel_after_join_ofs = r1.m_other2sig.size();
for(unsigned i=0;i<col_cnt; i++) {
if(!r1.is_table_column(cols1[i]) && !r2.is_table_column(cols2[i])) {
m_r_joined_cols1.push_back(r1.m_sig2other[cols1[i]]);
m_r_joined_cols2.push_back(r2.m_sig2other[cols2[i]]);
}
else if(r1.is_table_column(cols1[i]) && r2.is_table_column(cols2[i])) {
m_t_joined_cols1.push_back(r1.m_sig2table[cols1[i]]);
m_t_joined_cols2.push_back(r2.m_sig2table[cols2[i]]);
}
else if(!r1.is_table_column(cols1[i]) && r2.is_table_column(cols2[i])) {
m_tr_rel_joined_cols.push_back(r1.m_sig2other[cols1[i]]);
m_tr_table_joined_cols.push_back(second_table_after_join_ofs + r2.m_sig2table[cols2[i]]);
}
else {
SASSERT(r1.is_table_column(cols1[i]) && !r2.is_table_column(cols2[i]));
m_tr_table_joined_cols.push_back(r1.m_sig2table[cols1[i]]);
m_tr_rel_joined_cols.push_back(second_inner_rel_after_join_ofs + r2.m_sig2other[cols2[i]]);
}
}
m_tjoin_fn = r1.get_manager().mk_join_fn(r1.get_table(), r2.get_table(), m_t_joined_cols1.size(),
m_t_joined_cols1.data(), m_t_joined_cols2.data());
SASSERT(m_tjoin_fn);
unsigned r1_sig_sz = r1.get_signature().size();
unsigned r2_sig_sz = r2.get_signature().size();
for(unsigned i=0; i<r1_sig_sz; i++) {
m_res_table_columns.push_back(r1.is_table_column(i));
}
for(unsigned i=0; i<r2_sig_sz; i++) {
m_res_table_columns.push_back(r2.is_table_column(i));
}
}
relation_base * do_rjoin(const relation_base & r1, const relation_base & r2) {
if(!m_rjoin_fn) {
m_rjoin_fn = r1.get_manager().mk_join_fn(r1, r2, m_r_joined_cols1, m_r_joined_cols2, false);
}
SASSERT(m_rjoin_fn);
return (*m_rjoin_fn)(r1, r2);
}
relation_base * operator()(const relation_base & rb1, const relation_base & rb2) override {
finite_product_relation_plugin & plugin = get(rb1).get_plugin();
relation_manager & rmgr = plugin.get_manager();
const finite_product_relation & r1 = get(rb1);
const finite_product_relation & r2 = get(rb2);
scoped_rel<table_base> tjoined = (*m_tjoin_fn)(r1.get_table(), r2.get_table());
relation_vector joined_orelations;
{
join_maker * mutator = alloc(join_maker, *this, r1, r2, joined_orelations);
scoped_ptr<table_mutator_fn> inner_join_mapper = rmgr.mk_map_fn(*tjoined, mutator);
(*inner_join_mapper)(*tjoined);
}
if(!m_tjoined_second_rel_remover) {
unsigned removed_col = tjoined->get_signature().size()-1;
m_tjoined_second_rel_remover = rmgr.mk_project_fn(*tjoined, 1, &removed_col);
}
scoped_rel<table_base> res_table = (*m_tjoined_second_rel_remover)(*tjoined);
relation_plugin & res_oplugin =
joined_orelations.empty() ? r1.m_other_plugin : joined_orelations.back()->get_plugin();
finite_product_relation * res = alloc(finite_product_relation, r1.get_plugin(), get_result_signature(),
m_res_table_columns.data(), res_table->get_plugin(), res_oplugin, null_family_id);
res->init(*res_table, joined_orelations, true);
if(!m_tr_table_joined_cols.empty()) {
if(!m_filter_tr_identities) {
m_filter_tr_identities = plugin.mk_filter_identical_pairs(*res, m_tr_table_joined_cols.size(),
m_tr_table_joined_cols.data(), m_tr_rel_joined_cols.data());
SASSERT(m_filter_tr_identities);
}
(*m_filter_tr_identities)(*res);
}
return res;
}
};
relation_join_fn * finite_product_relation_plugin::mk_join_fn(const relation_base & rb1, const relation_base & rb2,
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) {
if(!check_kind(rb1) || !check_kind(rb2)) {
bool r1foreign = &rb1.get_plugin()!=this;
bool r2foreign = &rb2.get_plugin()!=this;
if( (!r1foreign || can_be_converted(rb1)) && (!r2foreign || can_be_converted(rb2))) {
return alloc(converting_join_fn, *this, rb1.get_signature(), rb2.get_signature(), col_cnt, cols1,
cols2);
}
return nullptr;
}
const finite_product_relation & r1 = get(rb1);
const finite_product_relation & r2 = get(rb2);
return alloc(join_fn, r1, r2, col_cnt, cols1, cols2);
}
class finite_product_relation_plugin::project_fn : public convenient_relation_project_fn {
unsigned_vector m_removed_table_cols;
unsigned_vector m_removed_rel_cols;
scoped_ptr<relation_transformer_fn> m_rel_projector;
scoped_ptr<relation_union_fn> m_inner_rel_union;
bool_vector m_res_table_columns;
public:
project_fn(const finite_product_relation & r, unsigned col_cnt, const unsigned * removed_cols)
: convenient_relation_project_fn(r.get_signature(), col_cnt, removed_cols) {
SASSERT(col_cnt>0);
for(unsigned i=0; i<col_cnt; i++) {
unsigned col = removed_cols[i];
if(r.is_table_column(col)) {
m_removed_table_cols.push_back(r.m_sig2table[col]);
}
else {
m_removed_rel_cols.push_back(r.m_sig2other[col]);
}
}
unsigned sig_sz = r.get_signature().size();
unsigned removed_idx = 0;
for(unsigned i=0; i<sig_sz; i++) {
if(removed_idx<col_cnt && removed_cols[removed_idx]==i) {
removed_idx++;
continue;
}
SASSERT(removed_idx==col_cnt || removed_cols[removed_idx]>i);
m_res_table_columns.push_back(r.is_table_column(i));
}
}
class project_reducer : public table_row_pair_reduce_fn {
project_fn & m_parent;
relation_vector & m_relations;
public:
project_reducer(project_fn & parent, relation_vector & relations)
: m_parent(parent), m_relations(relations) {}
void operator()(table_element * func_columns, const table_element * merged_func_columns) override {
relation_base * tgt = m_relations[static_cast<unsigned>(func_columns[0])]->clone();
relation_base & src = *m_relations[static_cast<unsigned>(merged_func_columns[0])];
if(!m_parent.m_inner_rel_union) {
m_parent.m_inner_rel_union = tgt->get_manager().mk_union_fn(*tgt, src);
}
(*m_parent.m_inner_rel_union)(*tgt, src);
unsigned new_idx = m_relations.size();
m_relations.push_back(tgt);
func_columns[0] = new_idx;
}
};
relation_base * operator()(const relation_base & rb) override {
const finite_product_relation & r = get(rb);
finite_product_relation_plugin & plugin = r.get_plugin();
const table_base & rtable = r.get_table();
relation_manager & rmgr = plugin.get_manager();
r.garbage_collect(false);
relation_vector res_relations;
unsigned orig_rel_cnt = r.m_others.size();
for(unsigned i=0; i<orig_rel_cnt; i++) {
relation_base * orig_rel = r.m_others[i];
res_relations.push_back(orig_rel ? orig_rel->clone() : nullptr);
}
SASSERT(res_relations.size()==orig_rel_cnt);
bool shared_res_table = false;
const table_base * res_table;
if(m_removed_table_cols.empty()) {
shared_res_table = true;
res_table = &rtable;
}
else {
project_reducer * preducer = alloc(project_reducer, *this, res_relations);
scoped_ptr<table_transformer_fn> tproject =
rmgr.mk_project_with_reduce_fn(rtable, m_removed_table_cols.size(), m_removed_table_cols.data(), preducer);
res_table = (*tproject)(rtable);
}
relation_plugin * res_oplugin = nullptr;
if(!m_removed_rel_cols.empty()) {
unsigned res_rel_cnt = res_relations.size();
for(unsigned i=0; i<res_rel_cnt; i++) {
if(res_relations[i]==0) {
continue;
}
relation_base * inner_rel = res_relations[i];
if(!m_rel_projector) {
m_rel_projector = rmgr.mk_project_fn(*inner_rel, m_removed_rel_cols);
}
res_relations[i] = (*m_rel_projector)(*inner_rel);
inner_rel->deallocate();
if(!res_oplugin) {
res_oplugin = &res_relations[i]->get_plugin();
}
}
}
if(!res_oplugin) {
res_oplugin = &r.m_other_plugin;
}
finite_product_relation * res = alloc(finite_product_relation, r.get_plugin(), get_result_signature(),
m_res_table_columns.data(), res_table->get_plugin(), *res_oplugin, null_family_id);
res->init(*res_table, res_relations, false);
if(!shared_res_table) {
const_cast<table_base *>(res_table)->deallocate();
}
return res;
}
};
relation_transformer_fn * finite_product_relation_plugin::mk_project_fn(const relation_base & rb, unsigned col_cnt,
const unsigned * removed_cols) {
if(&rb.get_plugin()!=this) {
return nullptr;
}
return alloc(project_fn, get(rb), col_cnt, removed_cols);
}
class finite_product_relation_plugin::rename_fn : public convenient_relation_rename_fn {
scoped_ptr<table_transformer_fn> m_table_renamer;
scoped_ptr<relation_transformer_fn> m_rel_renamer;
bool m_rel_identity;
unsigned_vector m_rel_permutation;
bool_vector m_res_table_columns;
public:
rename_fn(const finite_product_relation & r, unsigned cycle_len, const unsigned * permutation_cycle)
: convenient_relation_rename_fn(r.get_signature(), cycle_len, permutation_cycle) {
SASSERT(cycle_len>1);
unsigned sig_sz = r.get_signature().size();
unsigned_vector permutation;
add_sequence(0, sig_sz, permutation);
permutate_by_cycle(permutation, cycle_len, permutation_cycle);
unsigned_vector table_permutation;
bool table_identity = true;
m_rel_identity = true;
for(unsigned new_i=0; new_i<sig_sz; new_i++) {
unsigned idx = permutation[new_i];
bool is_orig_table = r.is_table_column(idx);
m_res_table_columns.push_back(is_orig_table);
}
collect_sub_permutation(permutation, r.m_sig2table, table_permutation, table_identity);
table_permutation.push_back(table_permutation.size());
collect_sub_permutation(permutation, r.m_sig2other, m_rel_permutation, m_rel_identity);
if(!table_identity) {
m_table_renamer = r.get_manager().mk_permutation_rename_fn(r.get_table(), table_permutation);
}
}
relation_base * operator()(const relation_base & rb) override {
const finite_product_relation & r = get(rb);
const table_base & rtable = r.get_table();
r.garbage_collect(false);
relation_vector res_relations;
unsigned orig_rel_cnt = r.m_others.size();
for(unsigned i=0; i<orig_rel_cnt; i++) {
relation_base * orig_rel = r.m_others[i];
res_relations.push_back(orig_rel ? orig_rel->clone() : nullptr);
}
if(!m_rel_identity) {
unsigned res_rel_cnt = res_relations.size();
for(unsigned i=0; i<res_rel_cnt; i++) {
if(res_relations[i]==0) {
continue;
}
scoped_rel<relation_base> inner_rel = res_relations[i];
if(!m_rel_renamer) {
m_rel_renamer = r.get_manager().mk_permutation_rename_fn(*inner_rel, m_rel_permutation);
}
res_relations[i] = (*m_rel_renamer)(*inner_rel);
}
}
scoped_rel<table_base> res_table_scoped;
const table_base * res_table = &rtable;
if(m_table_renamer) {
res_table_scoped = (*m_table_renamer)(*res_table);
res_table = res_table_scoped.get();
}
finite_product_relation * res = alloc(finite_product_relation, r.get_plugin(), get_result_signature(),
m_res_table_columns.data(), res_table->get_plugin(), r.m_other_plugin, null_family_id);
res->init(*res_table, res_relations, false);
return res;
}
};
relation_transformer_fn * finite_product_relation_plugin::mk_rename_fn(const relation_base & rb,
unsigned permutation_cycle_len, const unsigned * permutation_cycle) {
if(&rb.get_plugin()!=this) {
return nullptr;
}
const finite_product_relation & r = get(rb);
return alloc(rename_fn, r, permutation_cycle_len, permutation_cycle);
}
class finite_product_relation_plugin::union_fn : public relation_union_fn {
bool m_use_delta;
unsigned_vector m_data_cols;
scoped_ptr<table_join_fn> m_common_join;
scoped_ptr<relation_union_fn> m_rel_union;
scoped_ptr<table_union_fn> m_table_union;
scoped_ptr<table_intersection_filter_fn> m_remove_overlaps;
scoped_ptr<table_transformer_fn> m_remove_src_column_from_overlap;
scoped_ptr<relation_union_fn> m_delta_merging_union;
scoped_ptr<table_join_fn> m_overlap_delta_table_builder;
public:
union_fn(const finite_product_relation & tgt, bool use_delta) : m_use_delta(use_delta) {}
relation_union_fn & get_inner_rel_union_op(relation_base & r) {
if(!m_rel_union) {
m_rel_union = r.get_manager().mk_union_fn(r, r, m_use_delta ? &r : nullptr);
}
return *m_rel_union;
}
class union_mapper : public table_row_mutator_fn {
union_fn & m_parent;
finite_product_relation & m_tgt;
const finite_product_relation & m_src;
table_base * m_delta_indexes;
relation_vector * m_delta_rels;
table_fact m_di_fact;
public:
If \c delta_indexes is 0, it means we are not collecting indexes.
*/
union_mapper(union_fn & parent, finite_product_relation & tgt, const finite_product_relation & src,
table_base * delta_indexes, relation_vector * delta_rels)
: m_parent(parent),
m_tgt(tgt),
m_src(src),
m_delta_indexes(delta_indexes),
m_delta_rels(delta_rels) {}
~union_mapper() override {}
bool operator()(table_element * func_columns) override {
relation_base & otgt_orig = m_tgt.get_inner_rel(func_columns[0]);
const relation_base & osrc = m_src.get_inner_rel(func_columns[1]);
relation_base * otgt = otgt_orig.clone();
unsigned new_tgt_idx = m_tgt.get_next_rel_idx();
m_tgt.set_inner_rel(new_tgt_idx, otgt);
if(m_delta_indexes) {
relation_base * odelta = otgt->get_plugin().mk_empty(otgt->get_signature());
m_parent.get_inner_rel_union_op(*otgt)(*otgt, osrc, odelta);
unsigned delta_idx = m_delta_rels->size();
m_delta_rels->push_back(odelta);
m_di_fact.reset();
m_di_fact.push_back(new_tgt_idx);
m_di_fact.push_back(delta_idx);
m_delta_indexes->add_fact(m_di_fact);
}
else {
m_parent.get_inner_rel_union_op(*otgt)(*otgt, osrc);
}
func_columns[0]=new_tgt_idx;
return true;
}
};
Makes a table whose last column has indexes to relations in \c src into a table
with indexes to relation \c tgt.
*/
class src_copying_mapper : public table_row_mutator_fn {
finite_product_relation & m_tgt;
const finite_product_relation & m_src;
public:
If \c delta_indexes is 0, it means we are not collecting indexes.
*/
src_copying_mapper(finite_product_relation & tgt, const finite_product_relation & src)
: m_tgt(tgt), m_src(src) {}
bool operator()(table_element * func_columns) override {
const relation_base & osrc = m_src.get_inner_rel(func_columns[0]);
unsigned new_tgt_idx = m_tgt.get_next_rel_idx();
m_tgt.set_inner_rel(new_tgt_idx, osrc.clone());
func_columns[0]=new_tgt_idx;
return true;
}
};
void operator()(relation_base & tgtb, const relation_base & srcb, relation_base * deltab) override {
finite_product_relation & tgt = get(tgtb);
const finite_product_relation & src0 = get(srcb);
finite_product_relation * delta = get(deltab);
relation_manager & rmgr = tgt.get_manager();
scoped_rel<finite_product_relation> src_aux_copy;
if(!vectors_equal(tgt.m_table2sig, src0.m_table2sig)
|| (delta && !vectors_equal(tgt.m_table2sig, delta->m_table2sig)) ) {
src_aux_copy = src0.clone();
ptr_vector<finite_product_relation> orig_rels;
orig_rels.push_back(src_aux_copy.get());
orig_rels.push_back(&tgt);
if(delta) {
orig_rels.push_back(delta);
}
if(!finite_product_relation::try_unify_specifications(orig_rels)) {
throw default_exception("finite_product_relation union: cannot convert relations to common specification");
}
}
const finite_product_relation & src = src_aux_copy ? *src_aux_copy : src0;
table_plugin & tplugin = tgt.get_table_plugin();
if(!m_common_join) {
unsigned data_cols_cnt = tgt.m_table_sig.size()-1;
for(unsigned i=0; i<data_cols_cnt; i++) {
m_data_cols.push_back(i);
}
m_common_join = rmgr.mk_join_project_fn(tgt.get_table(), tgt.get_table(), m_data_cols, m_data_cols,
m_data_cols);
}
scoped_rel<table_base> table_overlap = (*m_common_join)(tgt.get_table(), src.get_table());
scoped_rel<table_base> delta_indexes;
relation_vector delta_rels;
if(m_use_delta) {
table_signature di_sig;
di_sig.push_back(finite_product_relation::s_rel_idx_sort);
di_sig.push_back(finite_product_relation::s_rel_idx_sort);
di_sig.set_functional_columns(1);
delta_indexes = tplugin.mk_empty(di_sig);
}
{
union_mapper * umapper = alloc(union_mapper, *this, tgt, src, delta_indexes.get(), &delta_rels);
scoped_ptr<table_mutator_fn> mapping_fn = rmgr.mk_map_fn(*table_overlap, umapper);
(*mapping_fn)(*table_overlap);
}
if(!m_remove_src_column_from_overlap) {
unsigned removed_cols[] = { table_overlap->get_signature().size()-1 };
m_remove_src_column_from_overlap = rmgr.mk_project_fn(*table_overlap, 1, removed_cols);
}
scoped_rel<table_base> regular_overlap = (*m_remove_src_column_from_overlap)(*table_overlap);
if(!m_remove_overlaps) {
m_remove_overlaps = rmgr.mk_filter_by_negation_fn(tgt.get_table(), *regular_overlap, m_data_cols,
m_data_cols);
}
(*m_remove_overlaps)(tgt.get_table(), *regular_overlap);
if(!m_table_union) {
m_table_union = rmgr.mk_union_fn(tgt.get_table(), tgt.get_table());
}
(*m_table_union)(tgt.get_table(), *regular_overlap);
scoped_rel<table_base> src_only = src.get_table().clone();
(*m_remove_overlaps)(*src_only, *regular_overlap);
scoped_rel<table_base> src_only2;
if(m_use_delta) {
src_only2 = src_only->clone();
}
{
src_copying_mapper * cpmapper = alloc(src_copying_mapper, tgt, src);
scoped_ptr<table_mutator_fn> mapping_fn = rmgr.mk_map_fn(*src_only, cpmapper);
(*mapping_fn)(*src_only);
}
(*m_table_union)(tgt.get_table(), *src_only);
if(m_use_delta) {
bool extending_delta = !delta->empty();
finite_product_relation * cdelta;
if(extending_delta) {
cdelta = delta->get_plugin().mk_empty(*delta);
}
else {
cdelta = delta;
}
if(!m_overlap_delta_table_builder) {
unsigned table_fn_col = regular_overlap->get_signature().size()-1;
unsigned first_col = 0;
unsigned removed_cols[] = { table_fn_col, table_fn_col+1 };
m_overlap_delta_table_builder = rmgr.mk_join_project_fn(*regular_overlap, *delta_indexes, 1,
&table_fn_col, &first_col, 2, removed_cols);
}
scoped_rel<table_base> overlap_delta_table =
(*m_overlap_delta_table_builder)(*regular_overlap, *delta_indexes);
cdelta->init(*overlap_delta_table, delta_rels, true);
{
src_copying_mapper * cpmapper = alloc(src_copying_mapper, *cdelta, src);
scoped_ptr<table_mutator_fn> mapping_fn = rmgr.mk_map_fn(*src_only2, cpmapper);
(*mapping_fn)(*src_only2);
}
(*m_table_union)(cdelta->get_table(), *src_only2);
if(extending_delta) {
if(!m_delta_merging_union) {
m_delta_merging_union = rmgr.mk_union_fn(*delta, *cdelta);
}
(*m_delta_merging_union)(*delta, *cdelta);
cdelta->deallocate();
}
}
}
};
#if 0
Union operation taking advantage of the fact that the inner relation of all the arguments
is a singleton relation.
*/
class finite_product_relation_plugin::inner_singleton_union_fn : public relation_union_fn {
class offset_row_mapper : public table_row_mutator_fn {
public:
unsigned m_ofs;
virtual bool operator()(table_element * func_columns) {
func_columns[0] += m_ofs;
return true;
}
};
class inner_relation_copier : public table_row_mutator_fn {
finite_product_relation & m_tgt;
const finite_product_relation & m_src;
finite_product_relation * m_delta;
unsigned m_tgt_ofs;
unsigned m_delta_ofs;
public:
virtual bool operator()(table_element * func_columns) {
unsigned src_idx = static_cast<unsigned>(func_columns[0]);
unsigned tgt_idx = src_idx + m_tgt_ofs;
unsigned delta_idx = m_delta ? (src_idx + m_delta_ofs) : 0;
SASSERT(!m_delta || m_tgt.m_others[tgt_idx]==m_delta->m_others[delta_idx]);
SASSERT(m_tgt.m_others[tgt_idx]==0 || m_tgt.m_others[tgt_idx]==m_src.m_others[src_idx]);
if(m_tgt.m_others[tgt_idx]==0) {
m_tgt.m_others[tgt_idx] = m_src.m_others[src_idx]->clone();
if(m_delta) {
m_delta->m_others[delta_idx] = m_src.m_others[src_idx]->clone();
}
}
return true;
}
};
scoped_ptr<table_union_fn> m_t_union_fun;
offset_row_mapper * m_offset_mapper_obj;
scoped_ptr<table_mutator_fn> m_offset_mapper_fun;
public:
virtual void operator()(relation_base & tgtb, const relation_base & srcb, relation_base * deltab) {
finite_product_relation & tgt = get(tgtb);
const finite_product_relation & src = get(srcb);
finite_product_relation * delta = get(deltab);
finite_product_relation_plugin & plugin = tgt.get_plugin();
relation_manager & rmgr = plugin.get_manager();
tgt.garbage_collect(true);
src.garbage_collect(true);
table_base & tgt_table = tgt.get_table();
const table_base & src_table = src.get_table();
scoped_rel<table_base> offsetted_src = src_table.clone();
if(!m_offset_mapper_fun) {
m_offset_mapper_obj = alloc(offset_row_mapper);
m_offset_mapper_fun = rmgr.mk_map_fn(*offsetted_src, m_offset_mapper_obj);
}
unsigned src_rel_offset = tgt.m_others.size();
m_offset_mapper_obj->m_ofs = src_rel_offset;
(*m_offset_mapper_fun)(*offsetted_src);
scoped_rel<finite_product_relation> loc_delta = delta ? get(plugin.mk_empty(*delta)) : 0;
scoped_rel<table_base> loc_delta_table_scoped;
if(!loc_delta) {
loc_delta_table_scoped = tgt_table.get_plugin().mk_empty(tgt_table);
}
table_base * loc_delta_table = loc_delta ? &loc_delta->get_table() : loc_delta_table_scoped.get();
if(!m_t_union_fun) {
m_t_union_fun = rmgr.mk_union_fn(tgt_table, *offsetted_src, loc_delta_table);
}
(*m_t_union_fun)(tgt_table, *offsetted_src, loc_delta_table);
NOT_IMPLEMENTED_YET();
}
};
#endif
class finite_product_relation_plugin::converting_union_fn : public relation_union_fn {
scoped_ptr<relation_union_fn> m_tr_union_fun;
public:
void operator()(relation_base & tgtb, const relation_base & srcb, relation_base * deltab) override {
SASSERT(srcb.get_plugin().is_finite_product_relation());
const finite_product_relation & src = get(srcb);
finite_product_relation_plugin & plugin = src.get_plugin();
scoped_rel<table_relation> tr_src = plugin.to_table_relation(src);
if(!m_tr_union_fun) {
m_tr_union_fun = plugin.get_manager().mk_union_fn(tgtb, *tr_src, deltab);
SASSERT(m_tr_union_fun);
}
(*m_tr_union_fun)(tgtb, *tr_src, deltab);
}
};
relation_union_fn * finite_product_relation_plugin::mk_union_fn(const relation_base & tgtb, const relation_base & srcb,
const relation_base * deltab) {
if(&srcb.get_plugin()!=this) {
return nullptr;
}
const finite_product_relation & src = get(srcb);
if(&tgtb.get_plugin()!=this || (deltab && &deltab->get_plugin()!=this) ) {
if(can_convert_to_table_relation(src)) {
return alloc(converting_union_fn);
}
return nullptr;
}
const finite_product_relation * delta = get(deltab);
return alloc(union_fn, get(tgtb), delta!=nullptr);
}
class finite_product_relation_plugin::filter_identical_fn : public relation_mutator_fn {
unsigned_vector m_table_cols;
unsigned_vector m_rel_cols;
scoped_ptr<table_mutator_fn> m_table_filter;
scoped_ptr<relation_mutator_fn> m_rel_filter;
scoped_ptr<relation_mutator_fn> m_tr_filter;
public:
filter_identical_fn(const finite_product_relation & r, unsigned col_cnt, const unsigned * identical_cols)
: m_table_filter(nullptr), m_rel_filter(nullptr), m_tr_filter(nullptr) {
finite_product_relation_plugin & plugin = r.get_plugin();
for(unsigned i=0; i<col_cnt; i++) {
unsigned col = identical_cols[i];
if(r.is_table_column(col)) {
m_table_cols.push_back(r.m_sig2table[col]);
}
else {
m_rel_cols.push_back(r.m_sig2other[col]);
}
}
if(m_table_cols.size()>1) {
m_table_filter = r.get_manager().mk_filter_identical_fn(r.get_table(), m_table_cols.size(),
m_table_cols.data());
SASSERT(m_table_filter);
}
if(!m_table_cols.empty() && !m_rel_cols.empty()) {
unsigned tr_filter_table_cols[] = { m_table_cols[0] };
unsigned tr_filter_rel_cols[] = { m_rel_cols[0] };
m_tr_filter = plugin.mk_filter_identical_pairs(r, 1, tr_filter_table_cols, tr_filter_rel_cols);
SASSERT(m_tr_filter);
}
}
void ensure_rel_filter(const relation_base & orel) {
SASSERT(m_rel_cols.size()>1);
if(m_rel_filter) {
return;
}
m_rel_filter = orel.get_manager().mk_filter_identical_fn(orel, m_rel_cols.size(), m_rel_cols.data());
SASSERT(m_rel_filter);
}
void operator()(relation_base & rb) override {
finite_product_relation & r = get(rb);
if(m_table_cols.size()>1) {
(*m_table_filter)(r.get_table());
}
if(m_rel_cols.size()>1) {
r.garbage_collect(true);
unsigned rel_cnt = r.m_others.size();
for(unsigned rel_idx=0; rel_idx<rel_cnt; rel_idx++) {
if(r.m_others[rel_idx]==0) {
continue;
}
ensure_rel_filter(*r.m_others[rel_idx]);
(*m_rel_filter)(*r.m_others[rel_idx]);
}
}
if(!m_table_cols.empty() && !m_rel_cols.empty()) {
(*m_tr_filter)(r);
}
}
};
relation_mutator_fn * finite_product_relation_plugin::mk_filter_identical_fn(const relation_base & rb,
unsigned col_cnt, const unsigned * identical_cols) {
if(&rb.get_plugin()!=this) {
return nullptr;
}
return alloc(filter_identical_fn, get(rb), col_cnt, identical_cols);
}
class finite_product_relation_plugin::filter_equal_fn : public relation_mutator_fn {
scoped_ptr<table_mutator_fn> m_table_filter;
scoped_ptr<relation_mutator_fn> m_rel_filter;
unsigned m_col;
app_ref m_value;
public:
filter_equal_fn(const finite_product_relation & r, const relation_element & value, unsigned col)
: m_col(col), m_value(value, r.get_context().get_manager()) {
if(r.is_table_column(col)) {
table_element tval;
r.get_manager().relation_to_table(r.get_signature()[col], value, tval);
m_table_filter = r.get_manager().mk_filter_equal_fn(r.get_table(), tval, r.m_sig2table[col]);
}
}
void operator()(relation_base & rb) override {
finite_product_relation & r = get(rb);
if(m_table_filter) {
(*m_table_filter)(r.get_table());
return;
}
r.garbage_collect(false);
relation_vector & inner_rels = r.m_others;
unsigned rel_cnt = inner_rels.size();
for(unsigned i=0; i<rel_cnt; i++) {
if(inner_rels[i]==0) {
continue;
}
if(!m_rel_filter) {
m_rel_filter = r.get_manager().mk_filter_equal_fn(*inner_rels[i], m_value, r.m_sig2other[m_col]);
}
(*m_rel_filter)(*inner_rels[i]);
}
}
};
relation_mutator_fn * finite_product_relation_plugin::mk_filter_equal_fn(const relation_base & rb,
const relation_element & value, unsigned col) {
if(&rb.get_plugin()!=this) {
return nullptr;
}
return alloc(filter_equal_fn, get(rb), value, col);
}
class finite_product_relation_plugin::filter_interpreted_fn : public relation_mutator_fn {
ast_manager & m_manager;
var_subst & m_subst;
scoped_ptr<table_mutator_fn> m_table_filter;
scoped_ptr<relation_mutator_fn> m_rel_filter;
app_ref m_cond;
idx_set m_table_cond_columns;
idx_set m_rel_cond_columns;
idx_set m_table_local_cond_columns;
idx_set m_rel_local_cond_columns;
If equal to 0, it means the interpreted condition uses all table columns. Then the original
table is used instead of the result of the projection.
*/
scoped_ptr<table_transformer_fn> m_table_cond_columns_project;
\brief Column indexes of the global relations to which correspond the data columns in the table
that is result of applying the \c m_table_cond_columns_project functor.
*/
unsigned_vector m_global_origins_of_projected_columns;
scoped_ptr<table_join_fn> m_assembling_join_project;
\brief Renaming that transforms the variable numbers pointing to the global relation into
variables that point to the inner relation variables.
The elements that do not correspond to columns of the inner relation (but rather to the table
columns) is modified in \c operator() when evaluating the condition for all the relevant
combinations of table values.
*/
expr_ref_vector m_renaming_for_inner_rel;
public:
filter_interpreted_fn(const finite_product_relation & r, app * condition)
: m_manager(r.get_context().get_manager()),
m_subst(r.get_context().get_var_subst()),
m_cond(condition, m_manager),
m_renaming_for_inner_rel(m_manager) {
relation_manager & rmgr = r.get_manager();
rule_manager& rm = r.get_context().get_rule_manager();
idx_set& cond_columns = rm.collect_vars(m_cond);
unsigned sig_sz = r.get_signature().size();
for(unsigned i=0; i<sig_sz; i++) {
if(r.is_table_column(i)) {
m_table_cond_columns.insert(i);
}
else {
m_rel_cond_columns.insert(i);
}
}
set_intersection(m_table_cond_columns, cond_columns);
set_intersection(m_rel_cond_columns, cond_columns);
transform_set(r.m_sig2table, m_table_cond_columns, m_table_local_cond_columns);
transform_set(r.m_sig2other, m_rel_cond_columns, m_rel_local_cond_columns);
if(m_rel_cond_columns.empty()) {
expr_ref_vector renaming(m_manager);
get_renaming_args(r.m_sig2table, r.get_signature(), renaming);
expr_ref table_cond = m_subst(condition, renaming.size(), renaming.data());
m_table_filter = rmgr.mk_filter_interpreted_fn(r.get_table(), to_app(table_cond));
}
else {
get_renaming_args(r.m_sig2other, r.get_signature(), m_renaming_for_inner_rel);
if(!m_table_cond_columns.empty()) {
unsigned_vector removed_cols;
unsigned table_data_col_cnt = r.m_table_sig.size()-1;
for(unsigned i=0; i<table_data_col_cnt; i++) {
if(m_table_local_cond_columns.contains(i)) {
m_global_origins_of_projected_columns.push_back(r.m_table2sig[i]);
}
else {
removed_cols.push_back(i);
}
}
if(!removed_cols.empty()) {
m_table_cond_columns_project = rmgr.mk_project_fn(r.get_table(), removed_cols);
}
}
}
}
void operator()(relation_base & rb) override {
finite_product_relation & r = get(rb);
table_base & rtable = r.get_table();
table_plugin & tplugin = r.get_table_plugin();
const relation_signature & osig = r.get_signature();
relation_manager & rmgr = r.get_manager();
unsigned rsig_sz = r.get_signature().size();
if(m_rel_cond_columns.empty()) {
SASSERT(m_table_filter);
(*m_table_filter)(rtable);
return;
}
if(m_table_cond_columns.empty()) {
r.garbage_collect(false);
unsigned rel_cnt = r.m_others.size();
for(unsigned i=0; i<rel_cnt; i++) {
relation_base * inner = r.m_others[i];
if(inner==nullptr) {
continue;
}
if(!m_rel_filter) {
expr_ref inner_cond = m_subst(m_cond, m_renaming_for_inner_rel.size(), m_renaming_for_inner_rel.data());
m_rel_filter = rmgr.mk_filter_interpreted_fn(*inner, to_app(inner_cond));
}
(*m_rel_filter)(*inner);
}
return;
}
scoped_rel<table_base> tproj_scope;
const table_base * tproj;
if(m_table_cond_columns_project) {
tproj_scope = (*m_table_cond_columns_project)(rtable);
tproj = tproj_scope.get();
}
else {
tproj = &rtable;
}
unsigned projected_data_cols = tproj->get_signature().size()-1;
SASSERT(m_table_cond_columns.num_elems()==projected_data_cols);
table_signature filtered_sig = tproj->get_signature();
filtered_sig.push_back(finite_product_relation::s_rel_idx_sort);
filtered_sig.set_functional_columns(1);
relation_vector new_rels;
scoped_rel<table_base> filtered_table = tplugin.mk_empty(filtered_sig);
table_fact f;
unsigned renaming_ofs = m_renaming_for_inner_rel.size()-1;
table_base::iterator pit = tproj->begin();
table_base::iterator pend = tproj->end();
for(; pit!=pend; ++pit) {
pit->get_fact(f);
unsigned old_rel_idx = static_cast<unsigned>(f.back());
const relation_base & old_rel = r.get_inner_rel(old_rel_idx);
for(unsigned i=0; i<projected_data_cols; i++) {
unsigned orig_col_idx = m_global_origins_of_projected_columns[i];
relation_element r_el;
rmgr.table_to_relation(osig[orig_col_idx], f[i], r_el);
m_renaming_for_inner_rel.set(renaming_ofs-orig_col_idx, r_el);
}
expr_ref inner_cond(m_manager);
inner_cond = m_subst(m_cond, m_renaming_for_inner_rel.size(), m_renaming_for_inner_rel.data());
th_rewriter rw(m_manager);
rw(inner_cond);
if (m_manager.is_false(inner_cond)) {
continue;
}
relation_base * new_rel = old_rel.clone();
scoped_ptr<relation_mutator_fn> filter = rmgr.mk_filter_interpreted_fn(*new_rel, to_app(inner_cond));
if (!filter)
throw default_exception("rules do not belong to supported finite domain fragment");
(*filter)(*new_rel);
if(new_rel->empty()) {
new_rel->deallocate();
continue;
}
unsigned new_rel_idx = new_rels.size();
new_rels.push_back(new_rel);
f.push_back(new_rel_idx);
filtered_table->add_fact(f);
}
if(!m_assembling_join_project) {
unsigned_vector table_cond_columns_vect;
for(unsigned i=0; i<rsig_sz; i++) {
if(m_table_local_cond_columns.contains(i)) {
table_cond_columns_vect.push_back(i);
}
}
m_assembling_join_project = mk_assembler_of_filter_result(rtable, *filtered_table,
table_cond_columns_vect);
}
scoped_rel<table_base> new_table = (*m_assembling_join_project)(rtable, *filtered_table);
r.reset();
r.init(*new_table, new_rels, true);
}
};
relation_mutator_fn * finite_product_relation_plugin::mk_filter_interpreted_fn(const relation_base & rb,
app * condition) {
if(&rb.get_plugin()!=this) {
return nullptr;
}
return alloc(filter_interpreted_fn, get(rb), condition);
}
class finite_product_relation_plugin::negation_filter_fn : public relation_intersection_filter_fn {
unsigned_vector m_r_cols;
unsigned_vector m_neg_cols;
scoped_ptr<table_intersection_filter_fn> m_table_neg_filter;
scoped_ptr<table_join_fn> m_table_neg_complement_selector;
scoped_ptr<relation_join_fn> m_neg_intersection_join;
scoped_ptr<table_join_fn> m_table_intersection_join;
scoped_ptr<table_union_fn> m_table_overlap_union;
scoped_ptr<table_intersection_filter_fn> m_table_subtract;
scoped_ptr<relation_intersection_filter_fn> m_inner_subtract;
scoped_ptr<table_transformer_fn> m_overlap_table_last_column_remover;
scoped_ptr<table_union_fn> m_r_table_union;
bool m_table_overlaps_only;
unsigned_vector m_r_shared_table_cols;
unsigned_vector m_neg_shared_table_cols;
unsigned_vector m_r_shared_rel_cols;
unsigned_vector m_neg_shared_rel_cols;
public:
negation_filter_fn(const finite_product_relation & r, const finite_product_relation & neg,
unsigned joined_col_cnt, const unsigned * r_cols, const unsigned * neg_cols)
: m_r_cols(joined_col_cnt, r_cols),
m_neg_cols(joined_col_cnt, neg_cols),
m_table_overlaps_only(true) {
const table_signature & tsig = r.m_table_sig;
const table_base & rtable = r.get_table();
relation_manager & rmgr = r.get_manager();
for(unsigned i=0; i<joined_col_cnt; i++) {
if(r.is_table_column(r_cols[i]) && neg.is_table_column(neg_cols[i])) {
m_r_shared_table_cols.push_back(r.m_sig2table[r_cols[i]]);
m_neg_shared_table_cols.push_back(neg.m_sig2table[neg_cols[i]]);
}
else {
m_table_overlaps_only = false;
}
}
if(m_table_overlaps_only) {
m_table_neg_filter = rmgr.mk_filter_by_negation_fn(rtable, neg.get_table(),
m_r_shared_table_cols, m_neg_shared_table_cols);
SASSERT(m_table_neg_filter);
}
else {
unsigned_vector removed_cols;
add_sequence(r.get_signature().size(), neg.get_signature().size(), removed_cols);
m_neg_intersection_join = rmgr.mk_join_project_fn(r, neg, m_r_cols, m_neg_cols, removed_cols, false);
SASSERT(m_neg_intersection_join);
unsigned_vector data_cols;
add_sequence(0, tsig.size()-1, data_cols);
removed_cols.reset();
add_sequence(tsig.size()-1, tsig.size()-1, removed_cols);
m_table_intersection_join = rmgr.mk_join_project_fn(rtable, rtable, data_cols, data_cols,
removed_cols);
SASSERT(m_table_intersection_join);
m_table_subtract = rmgr.mk_filter_by_negation_fn(rtable, rtable, data_cols, data_cols);
SASSERT(m_table_subtract);
}
}
void handle_only_tables_overlap_case(finite_product_relation & r, const finite_product_relation & neg) {
SASSERT(m_table_overlaps_only);
(*m_table_neg_filter)(r.get_table(), neg.get_table());
}
class rel_subtractor : public table_row_mutator_fn {
negation_filter_fn & m_parent;
finite_product_relation & m_r;
const finite_product_relation & m_inters;
public:
rel_subtractor(negation_filter_fn & parent, finite_product_relation & r,
const finite_product_relation & inters)
: m_parent(parent), m_r(r), m_inters(inters) {}
bool operator()(table_element * func_columns) override {
relation_base * r_inner = m_r.get_inner_rel(func_columns[0]).clone();
const relation_base & inters_inner = m_inters.get_inner_rel(func_columns[1]);
if(!m_parent.m_inner_subtract) {
unsigned_vector all_rel_cols;
add_sequence(0, r_inner->get_signature().size() , all_rel_cols);
m_parent.m_inner_subtract = m_r.get_manager().mk_filter_by_negation_fn(*r_inner,
inters_inner, all_rel_cols, all_rel_cols);
}
(*m_parent.m_inner_subtract)(*r_inner, inters_inner);
unsigned new_rel_num = m_r.get_next_rel_idx();
m_r.set_inner_rel(new_rel_num, r_inner);
func_columns[0]=new_rel_num;
return true;
}
};
void operator()(relation_base & rb, const relation_base & negb) override {
finite_product_relation & r = get(rb);
const finite_product_relation & neg = get(negb);
if(m_table_overlaps_only) {
handle_only_tables_overlap_case(r, neg);
return;
}
scoped_rel<finite_product_relation> intersection = get((*m_neg_intersection_join)(r, neg));
DEBUG_CODE(
finite_product_relation_plugin & plugin = r.get_plugin();
SASSERT(&intersection->get_plugin()==&plugin);
SASSERT(r.get_signature()==intersection->get_signature()););
table_base & r_table = r.get_table();
table_plugin & tplugin = r_table.get_plugin();
relation_manager & rmgr = r.get_manager();
scoped_rel<table_base> table_overlap0 = (*m_table_intersection_join)(r_table,
intersection->get_table());
(*m_table_subtract)(r_table, intersection->get_table());
table_signature table_overlap_sig(table_overlap0->get_signature());
table_overlap_sig.set_functional_columns(2);
scoped_rel<table_base> table_overlap = tplugin.mk_empty(table_overlap_sig);
if(!m_table_overlap_union) {
m_table_overlap_union = rmgr.mk_union_fn(*table_overlap, *table_overlap0);
SASSERT(m_table_overlap_union);
}
(*m_table_overlap_union)(*table_overlap, *table_overlap0);
{
rel_subtractor * mutator = alloc(rel_subtractor, *this, r, *intersection);
scoped_ptr<table_mutator_fn> mapper = rmgr.mk_map_fn(*table_overlap, mutator);
(*mapper)(*table_overlap);
}
if(!m_overlap_table_last_column_remover) {
unsigned removed_col = table_overlap->get_signature().size()-1;
m_overlap_table_last_column_remover = rmgr.mk_project_fn(*table_overlap, 1, &removed_col);
}
scoped_rel<table_base> final_overlapping_rows_table =
(*m_overlap_table_last_column_remover)(*table_overlap);
if(!m_r_table_union) {
m_r_table_union = rmgr.mk_union_fn(r_table, *final_overlapping_rows_table);
}
(*m_r_table_union)(r_table, *final_overlapping_rows_table);
}
};
relation_intersection_filter_fn * finite_product_relation_plugin::mk_filter_by_negation_fn(const relation_base & rb,
const relation_base & negb, unsigned joined_col_cnt,
const unsigned * r_cols, const unsigned * negated_cols) {
if(&rb.get_plugin()!=this || &negb.get_plugin()!=this) {
return nullptr;
}
return alloc(negation_filter_fn, get(rb), get(negb), joined_col_cnt, r_cols, negated_cols);
}
class finite_product_relation_plugin::filter_identical_pairs_fn : public relation_mutator_fn {
scoped_ptr<table_transformer_fn> m_tproject_fn;
unsigned m_col_cnt;
unsigned_vector m_table_cols;
unsigned_vector m_rel_cols;
scoped_ptr<table_join_fn> m_assembling_join_project;
scoped_ptr<table_union_fn> m_updating_union;
public:
filter_identical_pairs_fn(const finite_product_relation & r, unsigned col_cnt, const unsigned * table_cols,
const unsigned * rel_cols) :
m_col_cnt(col_cnt),
m_table_cols(col_cnt, table_cols),
m_rel_cols(col_cnt, rel_cols) {
SASSERT(col_cnt>0);
const table_signature & tsig = r.m_table_sig;
unsigned t_sz = tsig.size();
sort_two_arrays(col_cnt, m_table_cols.begin(), m_rel_cols.begin());
SASSERT(m_table_cols.back()<t_sz-1);
SASSERT(*std::max_element(m_rel_cols.begin(), m_rel_cols.end())<r.m_other_sig.size());
unsigned_vector removed_cols;
add_sequence_without_set(0, t_sz-1, m_table_cols, removed_cols);
if(!removed_cols.empty()) {
m_tproject_fn = r.get_manager().mk_project_fn(r.get_table(), removed_cols.size(), removed_cols.data());
}
}
void operator()(relation_base & rb) override {
finite_product_relation & r = get(rb);
finite_product_relation_plugin & plugin = r.get_plugin();
table_plugin & tplugin = r.get_table_plugin();
relation_signature & osig = r.m_other_sig;
relation_manager & rmgr = tplugin.get_manager();
table_base & rtable = r.get_table();
ast_manager & m = plugin.get_ast_manager();
scoped_rel<table_base> tproj;
if(m_tproject_fn) {
tproj = (*m_tproject_fn)(r.get_table());
}
else {
tproj = r.get_table().clone();
}
SASSERT(m_col_cnt+1==tproj->get_signature().size());
table_signature filtered_sig = tproj->get_signature();
filtered_sig.push_back(finite_product_relation::s_rel_idx_sort);
filtered_sig.set_functional_columns(1);
relation_vector new_rels;
scoped_rel<table_base> filtered_table = tplugin.mk_empty(filtered_sig);
table_fact f;
table_base::iterator pit = tproj->begin();
table_base::iterator pend = tproj->end();
for(; pit!=pend; ++pit) {
pit->get_fact(f);
unsigned old_rel_idx = static_cast<unsigned>(f.back());
const relation_base & old_rel = r.get_inner_rel(old_rel_idx);
relation_base * new_rel = old_rel.clone();
for(unsigned i=0; i<m_col_cnt; i++) {
relation_element_ref r_el(m);
rmgr.table_to_relation(osig[m_rel_cols[i]], f[i], r_el);
scoped_ptr<relation_mutator_fn> filter = rmgr.mk_filter_equal_fn(*new_rel, r_el, m_rel_cols[i]);
(*filter)(*new_rel);
}
if(new_rel->empty()) {
new_rel->deallocate();
continue;
}
unsigned new_rel_idx = new_rels.size();
new_rels.push_back(new_rel);
f.push_back(new_rel_idx);
filtered_table->add_fact(f);
}
if(!m_assembling_join_project) {
m_assembling_join_project = mk_assembler_of_filter_result(rtable, *filtered_table, m_table_cols);
}
scoped_rel<table_base> new_table = (*m_assembling_join_project)(r.get_table(), *filtered_table);
r.reset();
r.init(*new_table, new_rels, true);
}
};
relation_mutator_fn * finite_product_relation_plugin::mk_filter_identical_pairs(const finite_product_relation & r,
unsigned col_cnt, const unsigned * table_cols, const unsigned * rel_cols) {
return alloc(filter_identical_pairs_fn, r, col_cnt, table_cols, rel_cols);
}
table_join_fn * finite_product_relation_plugin::mk_assembler_of_filter_result(const table_base & relation_table,
const table_base & filtered_table, const unsigned_vector & selected_columns) {
table_plugin & tplugin = relation_table.get_plugin();
const table_signature & rtable_sig = relation_table.get_signature();
unsigned rtable_sig_sz = rtable_sig.size();
unsigned selected_col_cnt = selected_columns.size();
SASSERT(selected_col_cnt+2==filtered_table.get_signature().size());
SASSERT(rtable_sig.functional_columns()==1);
SASSERT(filtered_table.get_signature().functional_columns()==1);
unsigned_vector rtable_joined_cols;
rtable_joined_cols.append(selected_col_cnt, selected_columns.data());
rtable_joined_cols.push_back(rtable_sig_sz-1);
unsigned_vector filtered_joined_cols;
add_sequence(0, selected_col_cnt, filtered_joined_cols);
filtered_joined_cols.push_back(selected_col_cnt);
SASSERT(rtable_joined_cols.size()==filtered_joined_cols.size());
unsigned_vector removed_cols;
unsigned filtered_nonfunc_ofs = rtable_sig_sz-1;
add_sequence(filtered_nonfunc_ofs, selected_col_cnt, removed_cols);
unsigned idx_ofs = filtered_nonfunc_ofs+selected_col_cnt;
removed_cols.push_back(idx_ofs);
removed_cols.push_back(idx_ofs+1);
table_join_fn * res = tplugin.get_manager().mk_join_project_fn(relation_table, filtered_table,
rtable_joined_cols, filtered_joined_cols, removed_cols);
SASSERT(res);
return res;
}
finite_product_relation::finite_product_relation(finite_product_relation_plugin & p,
const relation_signature & s, const bool * table_columns, table_plugin & tplugin,
relation_plugin & oplugin, family_id other_kind)
: relation_base(p, s),
m_other_plugin(oplugin),
m_other_kind(other_kind),
m_full_rel_idx(UINT_MAX) {
const relation_signature & rel_sig = get_signature();
unsigned sz = rel_sig.size();
m_sig2table.resize(sz, UINT_MAX);
m_sig2other.resize(sz, UINT_MAX);
for(unsigned i=0; i<sz; i++) {
if(table_columns[i]) {
m_sig2table[i]=m_table_sig.size();
table_sort srt;
VERIFY( get_manager().relation_sort_to_table(rel_sig[i], srt) );
m_table_sig.push_back(srt);
m_table2sig.push_back(i);
}
else {
m_sig2other[i]=m_other_sig.size();
m_other_sig.push_back(rel_sig[i]);
m_other2sig.push_back(i);
}
}
SASSERT(m_table_sig.size()+m_other_sig.size()==sz);
m_table_sig.push_back(s_rel_idx_sort);
m_table_sig.set_functional_columns(1);
m_table = tplugin.mk_empty(m_table_sig);
set_kind(p.get_relation_kind(*this, table_columns));
}
finite_product_relation::finite_product_relation(const finite_product_relation & r)
: relation_base(r),
m_table_sig(r.m_table_sig),
m_table2sig(r.m_table2sig),
m_sig2table(r.m_sig2table),
m_other_sig(r.m_other_sig),
m_other2sig(r.m_other2sig),
m_sig2other(r.m_sig2other),
m_other_plugin(r.m_other_plugin),
m_other_kind(r.m_other_kind),
m_table(r.m_table->clone()),
m_others(r.m_others),
m_available_rel_indexes(r.m_available_rel_indexes),
m_full_rel_idx(r.m_full_rel_idx),
m_live_rel_collection_project(),
m_empty_rel_removal_filter() {
unsigned other_sz = m_others.size();
for(unsigned i=0; i<other_sz; i++) {
if(m_others[i]==0) {
continue;
}
m_others[i] = get_inner_rel(i).clone();
}
}
void finite_product_relation::swap(relation_base & r0) {
SASSERT(can_swap(r0));
finite_product_relation & r = finite_product_relation_plugin::get(r0);
SASSERT(get_signature()==r.get_signature());
SASSERT(&get_plugin()==&r.get_plugin());
SASSERT(&m_other_plugin==&r.m_other_plugin);
m_table_sig.swap(r.m_table_sig);
m_table2sig.swap(r.m_table2sig);
m_sig2table.swap(r.m_sig2table);
m_other_sig.swap(r.m_other_sig);
m_other2sig.swap(r.m_other2sig);
m_sig2other.swap(r.m_sig2other);
std::swap(m_table, r.m_table);
m_others.swap(r.m_others);
m_available_rel_indexes.swap(r.m_available_rel_indexes);
std::swap(m_full_rel_idx,r.m_full_rel_idx);
m_live_rel_collection_project=nullptr;
m_empty_rel_removal_filter=nullptr;
relation_base::swap(r0);
}
finite_product_relation::~finite_product_relation() {
m_table->deallocate();
relation_vector::iterator it = m_others.begin();
relation_vector::iterator end = m_others.end();
for(; it!=end; ++it) {
relation_base * rel= *it;
if(rel) {
rel->deallocate();
}
}
}
context & finite_product_relation::get_context() const {
return get_manager().get_context();
}
unsigned finite_product_relation::get_next_rel_idx() const {
unsigned res;
if(!m_available_rel_indexes.empty()) {
res = m_available_rel_indexes.back();
m_available_rel_indexes.pop_back();
}
else {
res = m_others.size();
m_others.push_back(0);
}
SASSERT(m_others[res]==0);
return res;
}
void finite_product_relation::recycle_rel_idx(unsigned idx) const {
SASSERT(m_others[idx]==0);
m_available_rel_indexes.push_back(idx);
}
unsigned finite_product_relation::get_full_rel_idx() {
if(m_full_rel_idx==UINT_MAX) {
m_full_rel_idx = get_next_rel_idx();
relation_base * full_other = mk_full_inner(nullptr);
m_others[m_full_rel_idx] = full_other;
}
return m_full_rel_idx;
}
void finite_product_relation::init(const table_base & table_vals, const relation_vector & others, bool contiguous) {
SASSERT(m_table_sig.equal_up_to_fn_mark(table_vals.get_signature()));
SASSERT(empty());
if(!m_others.empty()) {
garbage_collect(false);
}
SASSERT(m_others.empty());
m_others = others;
scoped_ptr<table_union_fn> table_union = get_manager().mk_union_fn(get_table(), table_vals);
(*table_union)(get_table(), table_vals);
if(!contiguous) {
unsigned rel_cnt = m_others.size();
for(unsigned i=0; i<rel_cnt; i++) {
if(m_others[i]==0) {
m_available_rel_indexes.push_back(i);
}
}
}
}
void finite_product_relation::extract_table_fact(const relation_fact & rf, table_fact & tf) const {
const relation_signature & sig = get_signature();
relation_manager & rmgr = get_manager();
tf.reset();
unsigned t_rel_sz = m_table2sig.size();
for(unsigned i=0; i<t_rel_sz; i++) {
table_element el;
unsigned sig_idx = m_table2sig[i];
rmgr.relation_to_table(sig[sig_idx], rf[sig_idx], el);
tf.push_back(el);
}
tf.push_back(0);
}
void finite_product_relation::extract_other_fact(const relation_fact & rf, relation_fact & of) const {
of.reset();
unsigned o_sz = m_other_sig.size();
for(unsigned i=0; i<o_sz; i++) {
unsigned sig_idx = m_other2sig[i];
of.push_back(rf[sig_idx]);
}
}
relation_base * finite_product_relation::mk_empty_inner() {
if(m_other_kind==null_family_id) {
return m_other_plugin.mk_empty(m_other_sig);
}
else {
return m_other_plugin.mk_empty(m_other_sig, m_other_kind);
}
}
relation_base * finite_product_relation::mk_full_inner(func_decl* pred) {
return m_other_plugin.mk_full(pred, m_other_sig, m_other_kind);
}
void finite_product_relation::add_fact(const relation_fact & f) {
SASSERT(f.size()==get_signature().size());
table_fact t_f;
extract_table_fact(f, t_f);
relation_fact o_f(get_manager().get_context());
extract_other_fact(f, o_f);
unsigned new_rel_idx = get_next_rel_idx();
t_f.back()=new_rel_idx;
relation_base * new_rel;
if(m_table->suggest_fact(t_f)) {
SASSERT(t_f.back()==new_rel_idx);
new_rel = mk_empty_inner();
} else {
new_rel = get_inner_rel(t_f.back()).clone();
t_f[t_f.size()-1]=new_rel_idx;
m_table->ensure_fact(t_f);
}
new_rel->add_fact(o_f);
set_inner_rel(new_rel_idx, new_rel);
}
bool finite_product_relation::contains_fact(const relation_fact & f) const {
table_fact t_f;
extract_table_fact(f, t_f);
if(!m_table->fetch_fact(t_f)) {
return false;
}
relation_fact o_f(get_context());
extract_other_fact(f, o_f);
const relation_base & other = get_inner_rel(t_f.back());
return other.contains_fact(o_f);
}
bool finite_product_relation::empty() const {
garbage_collect(true);
return m_table->empty();
}
finite_product_relation * finite_product_relation::clone() const {
return alloc(finite_product_relation, *this);
}
void finite_product_relation::complement_self(func_decl* p) {
unsigned other_sz = m_others.size();
for(unsigned i=0; i<other_sz; i++) {
if(m_others[i]==0) {
continue;
}
relation_base * r = m_others[i]->complement(p);
std::swap(m_others[i],r);
r->deallocate();
}
table_element full_rel_idx = get_full_rel_idx();
scoped_rel<table_base> complement_table = m_table->complement(p, &full_rel_idx);
scoped_ptr<table_union_fn> u_fn = get_manager().mk_union_fn(*m_table, *complement_table, nullptr);
SASSERT(u_fn);
(*u_fn)(*m_table, *complement_table, nullptr);
}
finite_product_relation * finite_product_relation::complement(func_decl* p) const {
finite_product_relation * res = clone();
res->complement_self(p);
return res;
}
class finite_product_relation::live_rel_collection_reducer : public table_row_pair_reduce_fn {
idx_set & m_accumulator;
public:
live_rel_collection_reducer(idx_set & accumulator) : m_accumulator(accumulator) {}
void operator()(table_element * func_columns, const table_element * merged_func_columns) override {
m_accumulator.insert(static_cast<unsigned>(merged_func_columns[0]));
}
};
void finite_product_relation::collect_live_relation_indexes(idx_set & res) const {
SASSERT(res.empty());
unsigned table_data_col_cnt = m_table_sig.size()-1;
if(table_data_col_cnt==0) {
if(!get_table().empty()) {
table_base::iterator iit = get_table().begin();
table_base::iterator iend = get_table().end();
SASSERT(iit!=iend);
res.insert(static_cast<unsigned>((*iit)[0]));
SASSERT((++iit)==iend);
}
return;
}
if(!m_live_rel_collection_project) {
buffer<unsigned, false> removed_cols;
removed_cols.resize(table_data_col_cnt);
for(unsigned i=0; i<table_data_col_cnt; i++) {
removed_cols[i] = i;
}
live_rel_collection_reducer * reducer = alloc(live_rel_collection_reducer, m_live_rel_collection_acc);
m_live_rel_collection_project = get_manager().mk_project_with_reduce_fn(get_table(), removed_cols.size(), removed_cols.data(), reducer);
SASSERT(m_live_rel_collection_project);
}
m_live_rel_collection_acc.reset();
scoped_rel<table_base> live_indexes_table = (*m_live_rel_collection_project)(get_table());
res.swap(m_live_rel_collection_acc);
SASSERT(live_indexes_table->get_signature().size()==1);
SASSERT(live_indexes_table->get_signature().functional_columns()==1);
if(!live_indexes_table->empty()) {
table_base::iterator iit = live_indexes_table->begin();
table_base::iterator iend = live_indexes_table->end();
SASSERT(iit!=iend);
res.insert(static_cast<unsigned>((*iit)[0]));
SASSERT((++iit)==iend);
}
}
void finite_product_relation::garbage_collect(bool remove_empty) const {
idx_set live_indexes;
collect_live_relation_indexes(live_indexes);
scoped_rel<table_base> empty_rel_indexes;
table_fact empty_rel_fact;
unsigned rel_cnt = m_others.size();
#if Z3DEBUG
unsigned encountered_live_indexes = 0;
#endif
for(unsigned rel_idx=0; rel_idx<rel_cnt; rel_idx++) {
if(m_others[rel_idx]==0) {
continue;
}
if(live_indexes.contains(rel_idx)) {
DEBUG_CODE( encountered_live_indexes++; );
if(!remove_empty) {
continue;
}
if(!m_others[rel_idx]->empty()) {
continue;
}
if(empty_rel_indexes==0) {
table_signature empty_rel_indexes_sig;
empty_rel_indexes_sig.push_back(s_rel_idx_sort);
empty_rel_indexes = get_table_plugin().mk_empty(empty_rel_indexes_sig);
}
empty_rel_fact.reset();
empty_rel_fact.push_back(rel_idx);
empty_rel_indexes->add_fact(empty_rel_fact);
}
m_others[rel_idx]->deallocate();
m_others[rel_idx] = 0;
if(rel_idx==m_full_rel_idx) {
m_full_rel_idx = UINT_MAX;
}
recycle_rel_idx(rel_idx);
}
SASSERT(encountered_live_indexes==live_indexes.num_elems());
if(m_available_rel_indexes.size()==m_others.size()) {
m_available_rel_indexes.reset();
m_others.reset();
}
if(empty_rel_indexes) {
SASSERT(remove_empty);
if(!m_empty_rel_removal_filter) {
unsigned t_joined_cols[] = { m_table_sig.size()-1 };
unsigned ei_joined_cols[] = { 0 };
m_empty_rel_removal_filter = get_manager().mk_filter_by_negation_fn(get_table(), *empty_rel_indexes,
1, t_joined_cols, ei_joined_cols);
}
(*m_empty_rel_removal_filter)(*m_table, *empty_rel_indexes);
}
}
bool finite_product_relation::try_unify_specifications(ptr_vector<finite_product_relation> & rels) {
if(rels.empty()) {
return true;
}
unsigned sig_sz = rels.back()->get_signature().size();
bool_vector table_cols(sig_sz, true);
ptr_vector<finite_product_relation>::iterator it = rels.begin();
ptr_vector<finite_product_relation>::iterator end = rels.end();
for(; it!=end; ++it) {
finite_product_relation & rel = **it;
for(unsigned i=0; i<sig_sz; i++) {
table_cols[i] &= rel.is_table_column(i);
}
}
it = rels.begin();
for(; it!=end; ++it) {
finite_product_relation & rel = **it;
if(!rel.try_modify_specification(table_cols.data())) {
return false;
}
}
return true;
}
bool finite_product_relation::try_modify_specification(const bool * table_cols) {
relation_manager & rmgr = get_manager();
const relation_signature & sig = get_signature();
unsigned_vector new_rel_columns;
unsigned_vector to_project_away;
relation_signature moved_cols_sig;
unsigned sig_sz = get_signature().size();
for(unsigned i=0; i<sig_sz; i++) {
if(table_cols[i] && !is_table_column(i)) {
return false;
}
if(is_table_column(i)) {
if(!table_cols[i]) {
new_rel_columns.push_back(i);
moved_cols_sig.push_back(sig[i]);
}
else {
to_project_away.push_back(m_sig2table[i]);
}
}
}
to_project_away.push_back(get_table().get_signature().size()-1);
if(new_rel_columns.empty()) {
return true;
}
if(!m_other_plugin.can_handle_signature(moved_cols_sig)) {
return false;
}
scoped_ptr<table_transformer_fn> pr_fun = get_manager().mk_project_fn(get_table(), to_project_away);
table_base * moved_cols_table = (*pr_fun)(get_table());
scoped_rel<relation_base> moved_cols_trel =
rmgr.get_table_relation_plugin(moved_cols_table->get_plugin()).mk_from_table(moved_cols_sig, moved_cols_table);
bool_vector moved_cols_table_flags(moved_cols_sig.size(), false);
scoped_rel<finite_product_relation> moved_cols_rel = get_plugin().mk_empty(moved_cols_sig,
moved_cols_table_flags.data());
scoped_ptr<relation_union_fn> union_fun =
get_manager().mk_union_fn(*moved_cols_rel, *moved_cols_trel);
SASSERT(union_fun);
(*union_fun)(*moved_cols_rel, *moved_cols_trel);
unsigned_vector all_moved_cols_indexes;
add_sequence(0, moved_cols_sig.size(), all_moved_cols_indexes);
scoped_ptr<relation_join_fn> join_fun = get_manager().mk_join_project_fn(*this, *moved_cols_rel, new_rel_columns,
all_moved_cols_indexes, new_rel_columns, false);
scoped_rel<relation_base> unordered_rel = (*join_fun)(*this, *moved_cols_rel);
SASSERT(unordered_rel->get_signature().size()==sig_sz);
unsigned_vector permutation;
unsigned moved_cols_cnt = new_rel_columns.size();
unsigned next_replaced_idx = 0;
unsigned next_orig_idx = 0;
for(unsigned i=0; i<sig_sz; i++) {
if(next_replaced_idx<moved_cols_cnt && new_rel_columns[next_replaced_idx]==i) {
permutation.push_back(sig_sz-moved_cols_cnt+next_replaced_idx);
next_replaced_idx++;
}
else {
permutation.push_back(next_orig_idx++);
}
}
unsigned_vector cycle;
while(try_remove_cycle_from_permutation(permutation, cycle)) {
scoped_ptr<relation_transformer_fn> perm_fun = get_manager().mk_rename_fn(*unordered_rel, cycle);
unordered_rel = (*perm_fun)(*unordered_rel);
cycle.reset();
}
finite_product_relation & new_rel = finite_product_relation_plugin::get(*unordered_rel);
swap(new_rel);
return true;
}
void finite_product_relation::display(std::ostream & out) const {
garbage_collect(true);
out << "finite_product_relation:\n";
out << " table:\n";
get_table().display(out);
unsigned other_sz = m_others.size();
for(unsigned i=0; i<other_sz; i++) {
if(m_others[i]==0) {
continue;
}
out << " inner relation " << i << ":\n";
get_inner_rel(i).display(out);
}
}
void finite_product_relation::display_tuples(func_decl & pred, std::ostream & out) const {
out << "Tuples in " << pred.get_name() << ": \n";
if(!m_other_plugin.from_table()) {
display(out);
return;
}
context & ctx = get_context();
table_fact tfact;
table_fact ofact;
unsigned sig_sz = get_signature().size();
unsigned rel_idx_col = m_table_sig.size()-1;
table_base::iterator it = get_table().begin();
table_base::iterator end = get_table().end();
for(; it!=end ; ++it) {
it->get_fact(tfact);
const table_relation & orel = static_cast<const table_relation &>(get_inner_rel(tfact[rel_idx_col]));
const table_base & otable = orel.get_table();
table_base::iterator oit = otable.begin();
table_base::iterator oend = otable.end();
for(; oit!=oend; ++oit) {
oit->get_fact(ofact);
out << "\t(";
for(unsigned i=0; i<sig_sz; i++) {
if(i!=0) {
out << ',';
}
table_element sym_num;
if(is_table_column(i)) {
sym_num = tfact[m_sig2table[i]];
}
else {
sym_num = ofact[m_sig2other[i]];
}
relation_sort sort = pred.get_domain(i);
out << ctx.get_argument_name(&pred, i) << '=';
ctx.print_constant_name(sort, sym_num, out);
out << '(' << sym_num << ')';
}
out << ")\n";
}
}
}
void finite_product_relation::to_formula(expr_ref& fml) const {
relation_signature const& sig = get_signature();
ast_manager& m = fml.get_manager();
expr_ref_vector disjs(m), conjs(m);
expr_ref tmp(m);
dl_decl_util util(m);
shift_vars sh(m);
table_fact fact;
table_base::iterator it = get_table().begin();
table_base::iterator end = get_table().end();
unsigned fact_sz = m_table_sig.size();
for(; it!=end ; ++it) {
it->get_fact(fact);
conjs.reset();
SASSERT(fact.size() == fact_sz);
unsigned rel_idx = static_cast<unsigned>(fact[fact_sz-1]);
m_others[rel_idx]->to_formula(tmp);
for (unsigned i = 0; i + 1 < fact_sz; ++i) {
conjs.push_back(m.mk_eq(m.mk_var(i, sig[i]), util.mk_numeral(fact[i], sig[i])));
}
sh(tmp, fact_sz-1, tmp);
conjs.push_back(tmp);
disjs.push_back(m.mk_and(conjs.size(), conjs.data()));
}
bool_rewriter(m).mk_or(disjs.size(), disjs.data(), fml);
}
};