/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_finite_product_relation.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-09-14. Revision History: --*/ #include #include"dl_context.h" #include"dl_relation_manager.h" #include"dl_table_relation.h" #include"dl_finite_product_relation.h" #include"bool_rewriter.h" namespace datalog { //static variables const table_sort finite_product_relation::s_rel_idx_sort = INT_MAX; void universal_delete(finite_product_relation* ptr) { ptr->deallocate(); } // ----------------------------------- // // finite_product_relation_plugin // // ----------------------------------- finite_product_relation & finite_product_relation_plugin::get(relation_base & r) { SASSERT(r.get_plugin().is_finite_product_relation()); return static_cast(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(r); } finite_product_relation * finite_product_relation_plugin::get(relation_base * r) { SASSERT(!r || r->get_plugin().is_finite_product_relation()); return static_cast(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(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(); svector 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, svector & table_columns) { SASSERT(table_columns.empty()); unsigned s_sz = s.size(); for(unsigned i=0; i 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; icomplement_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); //now all rows in the table will correspond to rows in the resulting table_relation const table_base & t = r.get_table(); unsigned removed_col = t.get_signature().size()-1; scoped_ptr 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(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()) { //can be converted by mk_from_inner_relation return true; } if(r.from_table()) { //We can convert directly from table plugin only if the inner plugin can handle empty signatures. //TODO: If the inner plugin cannot handle empty signatures, we may try to move some of the //table columns into the inner relation signature. 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 = 0; 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; //empty signature for the inner relation if(!get_inner_plugin().can_handle_signature(inner_sig)) { return 0; } 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 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 join_fun = get_manager().mk_join_fn(t, *idx_singleton, 0, 0, 0); SASSERT(join_fun); scoped_rel res_table = (*join_fun)(t, *idx_singleton); svector table_cols(sig.size(), true); finite_product_relation * res = mk_empty(sig, table_cols.c_ptr()); //this one does not need to be deleted -- it will be taken over by \c res in the \c init function 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 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); svector table_cols(sig.size(), false); finite_product_relation * res = mk_empty(sig, table_cols.c_ptr()); 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 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(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) {} virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) { scoped_rel r1_conv; if(&r1.get_plugin()!=&m_plugin) { r1_conv = convert(r1); } scoped_rel 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 m_tjoin_fn; scoped_ptr 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; //Column equalities betweet table and inner relations. //The columns numbers correspont to the columns of the table/inner relation //in the result of the join operation unsigned_vector m_tr_table_joined_cols; unsigned_vector m_tr_rel_joined_cols; scoped_ptr m_filter_tr_identities; scoped_ptr m_tjoined_second_rel_remover; //determines which columns of the result are table columns and which are in the inner relation svector 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) {} virtual bool operator()(table_element * func_columns) { const relation_base & or1 = m_r1.get_inner_rel(func_columns[0]); const relation_base & or2 = m_r2.get_inner_rel(func_columns[1]); SASSERT(&or1); SASSERT(&or2); 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 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); //dealocated in inner_join_mapper scoped_ptr 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); } //remove the second functional column from tjoined to get a table that corresponds //to the table signature of the resulting relation scoped_rel res_table = (*m_tjoined_second_rel_remover)(*tjoined); relation_plugin & res_oplugin = joined_orelations.empty() ? r1.m_other_plugin : joined_orelations.back()->get_plugin(); //TODO: Maybe we might want to specify a particular relation kind, instead of just null_family_id. //It would however need to be somehow inferred for the new signature. finite_product_relation * res = alloc(finite_product_relation, r1.get_plugin(), get_result_signature(), m_res_table_columns.c_ptr(), res_table->get_plugin(), res_oplugin, null_family_id); res->init(*res_table, joined_orelations, true); if(m_tr_table_joined_cols.size()) { //There were some shared variables between the table and the relation part. //We enforce those equalities here. 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.c_ptr(), m_tr_rel_joined_cols.c_ptr()); 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 0; } 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 m_rel_projector; scoped_ptr m_inner_rel_union; //determines which columns of the result are table columns and which are in the inner relation svector 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; ii); 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) {} virtual void operator()(table_element * func_columns, const table_element * merged_func_columns) { relation_base * tgt = m_relations[static_cast(func_columns[0])]->clone(); relation_base & src = *m_relations[static_cast(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; } }; virtual relation_base * operator()(const relation_base & rb) { 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; iclone() : 0); } 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 tproject = rmgr.mk_project_with_reduce_fn(rtable, m_removed_table_cols.size(), m_removed_table_cols.c_ptr(), preducer); res_table = (*tproject)(rtable); } relation_plugin * res_oplugin = 0; if(!m_removed_rel_cols.empty()) { unsigned res_rel_cnt = res_relations.size(); for(unsigned i=0; ideallocate(); if(!res_oplugin) { res_oplugin = &res_relations[i]->get_plugin(); } } } if(!res_oplugin) { res_oplugin = &r.m_other_plugin; } //TODO: Maybe we might want to specify a particular relation kind, instead of just null_family_id. //It would however need to be somehow inferred for the new signature. finite_product_relation * res = alloc(finite_product_relation, r.get_plugin(), get_result_signature(), m_res_table_columns.c_ptr(), res_table->get_plugin(), *res_oplugin, null_family_id); res->init(*res_table, res_relations, false); if(!shared_res_table) { const_cast(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 0; } return alloc(project_fn, get(rb), col_cnt, removed_cols); } class finite_product_relation_plugin::rename_fn : public convenient_relation_rename_fn { scoped_ptr m_table_renamer; scoped_ptr m_rel_renamer; bool m_rel_identity; unsigned_vector m_rel_permutation; //determines which columns of the result are table columns and which are in the inner relation svector 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_iclone() : 0); } if(!m_rel_identity) { unsigned res_rel_cnt = res_relations.size(); for(unsigned i=0; i 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 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(); } //TODO: Maybe we might want to specify a particular relation kind, instead of just null_family_id. //It would however need to be somehow inferred for the new signature. finite_product_relation * res = alloc(finite_product_relation, r.get_plugin(), get_result_signature(), m_res_table_columns.c_ptr(), 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 0; } 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;//non-functional columns in the product-relation table (useful for creating operations) scoped_ptr m_common_join; //result of the join contains (data columns), tgt_rel_idx, src_rel_idx scoped_ptr m_rel_union; scoped_ptr m_table_union; scoped_ptr m_remove_overlaps; scoped_ptr m_remove_src_column_from_overlap; //this one is populated only if we're doing union with delta scoped_ptr m_delta_merging_union; scoped_ptr 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 : 0); } 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; //table with signature (updated tgt rel index, delta_index in m_delta_rels) relation_vector * m_delta_rels; table_fact m_di_fact; //auxiliary fact for inserting into \c m_delta_indexes 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) {} virtual ~union_mapper() {} virtual bool operator()(table_element * func_columns) { 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) {} virtual bool operator()(table_element * func_columns) { 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; } }; virtual void operator()(relation_base & tgtb, const relation_base & srcb, relation_base * deltab) { 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 src_aux_copy; //copy of src in case its specification needs to be modified 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 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 table_overlap = (*m_common_join)(tgt.get_table(), src.get_table()); scoped_rel 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 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); } //transform table_overlap into the signature of tgt.get_table(), so that the functional //column contains indexes of the united relations scoped_rel 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); } //in tgt keep only the rows that are in tgt only (*m_remove_overlaps)(tgt.get_table(), *regular_overlap); //add rows in which tgt and src overlapped 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 src_only = src.get_table().clone(); (*m_remove_overlaps)(*src_only, *regular_overlap); scoped_rel src_only2; //a copy of src_only for use in building the delta if(m_use_delta) { src_only2 = src_only->clone(); } { src_copying_mapper * cpmapper = alloc(src_copying_mapper, tgt, src); scoped_ptr mapping_fn = rmgr.mk_map_fn(*src_only, cpmapper); (*mapping_fn)(*src_only); } //add rows that were only in src (*m_table_union)(tgt.get_table(), *src_only); if(m_use_delta) { bool extending_delta = !delta->empty(); //current delta, we will add it to the deltab argument later if it was not given to us 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 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 mapping_fn = rmgr.mk_map_fn(*src_only2, cpmapper); (*mapping_fn)(*src_only2); } //add rows that were only in src (*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(); } } } }; /** 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(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 m_t_union_fun; offset_row_mapper * m_offset_mapper_obj; //initialized together with m_offset_mapper_fun, and deallocated by it scoped_ptr 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(); //we want only non-empty inner relations to remain 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 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); //if we need to generate a delta, we get collect it into an empty relation and then union //it with the delta passed in as argument. scoped_rel loc_delta = delta ? get(plugin.mk_empty(*delta)) : 0; //even if we don't need to generate the delta for the caller, we still want to have a delta //table to know which relations to copy. scoped_rel 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); //TODO: copy the relations into tgt and (possibly) delta using inner_relation_copier //TODO: unite the local delta with the delta passed in as an argument NOT_IMPLEMENTED_YET(); } }; class finite_product_relation_plugin::converting_union_fn : public relation_union_fn { scoped_ptr m_tr_union_fun; public: virtual void operator()(relation_base & tgtb, const relation_base & srcb, relation_base * deltab) { 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 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 0; } 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 0; } const finite_product_relation * delta = get(deltab); return alloc(union_fn, get(tgtb), delta!=0); } class finite_product_relation_plugin::filter_identical_fn : public relation_mutator_fn { //the table and relation columns that should be identical //the column numbering is local to the table or inner relation unsigned_vector m_table_cols; unsigned_vector m_rel_cols; scoped_ptr m_table_filter; scoped_ptr m_rel_filter; scoped_ptr m_tr_filter; public: filter_identical_fn(const finite_product_relation & r, unsigned col_cnt, const unsigned * identical_cols) : m_table_filter(0), m_rel_filter(0), m_tr_filter(0) { finite_product_relation_plugin & plugin = r.get_plugin(); for(unsigned i=0; i1) { m_table_filter = r.get_manager().mk_filter_identical_fn(r.get_table(), m_table_cols.size(), m_table_cols.c_ptr()); 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.c_ptr()); SASSERT(m_rel_filter); } virtual void operator()(relation_base & rb) { 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 m_table_filter; scoped_ptr 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]); } } virtual void operator()(relation_base & rb) { 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 m_table_filter; scoped_ptr m_rel_filter; app_ref m_cond; idx_set m_table_cond_columns; idx_set m_rel_cond_columns; //like m_table_cond_columns and m_rel_cond_columns, only the indexes are local to the //table/relation, not to the signature of the whole relation 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 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 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(); idx_set cond_columns; collect_vars(m_manager, m_cond, cond_columns); unsigned sig_sz = r.get_signature().size(); for(unsigned i=0; i 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 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(f.back()); const relation_base & old_rel = r.get_inner_rel(old_rel_idx); //put the table values into the substitution for(unsigned i=0; i filter = rmgr.mk_filter_interpreted_fn(*new_rel, to_app(inner_cond)); (*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 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 0; } 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 m_table_neg_filter; scoped_ptr m_table_neg_complement_selector; scoped_ptr m_neg_intersection_join; scoped_ptr m_table_intersection_join; scoped_ptr m_table_overlap_union; scoped_ptr m_table_subtract; scoped_ptr m_inner_subtract; scoped_ptr m_overlap_table_last_column_remover; scoped_ptr 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; iget_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; } }; virtual void operator()(relation_base & rb, const relation_base & negb) { 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; } finite_product_relation_plugin & plugin = r.get_plugin(); if(!m_neg_intersection_join) { } scoped_rel intersection = get((*m_neg_intersection_join)(r, neg)); SASSERT(&intersection->get_plugin()==&plugin); //the result of join is also finite product 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(); //we need to do this before we perform the \c m_table_subtract //the sigrature of the \c table_overlap0 relation is //(data_columns)(original rel idx)(subtracted rel idx) scoped_rel table_overlap0 = (*m_table_intersection_join)(r_table, intersection->get_table()); //the rows that don't appear in the table of the intersection are safe to stay (*m_table_subtract)(r_table, intersection->get_table()); //now we will examine the rows that do appear in the intersection //There are no functional columns in the \c table_overlap0 relation (because of //the project we did). We need to make both rel idx columns functional. //We do not lose any rows, since the data columns by themselves are unique. table_signature table_overlap_sig(table_overlap0->get_signature()); table_overlap_sig.set_functional_columns(2); scoped_rel 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 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 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 0; } 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 m_tproject_fn; //if zero, no columns need to be projected away unsigned m_col_cnt; unsigned_vector m_table_cols; unsigned_vector m_rel_cols; scoped_ptr m_assembling_join_project; scoped_ptr 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() 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 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(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 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 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.c_ptr()); //filtered table cols rtable_joined_cols.push_back(rtable_sig_sz-1); //unfiltered relation indexes unsigned_vector filtered_joined_cols; add_sequence(0, selected_col_cnt, filtered_joined_cols); //filtered table cols filtered_joined_cols.push_back(selected_col_cnt); //unfiltered relation indexes SASSERT(rtable_joined_cols.size()==filtered_joined_cols.size()); //the signature after join: //(all relation table columns)(all filtered relation table columns)(unfiltered rel idx non-func from 'filtered') // (unfiltered rel idx func from 'rtable')(filtered rel idx) unsigned_vector removed_cols; unsigned filtered_nonfunc_ofs = rtable_sig_sz-1; add_sequence(filtered_nonfunc_ofs, selected_col_cnt, removed_cols); //data columns from 'filtered' unsigned idx_ofs = filtered_nonfunc_ofs+selected_col_cnt; removed_cols.push_back(idx_ofs); //unfiltered relation indexes from 'filtered' removed_cols.push_back(idx_ofs+1); //unfiltered relation indexes from rtable 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(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; iclone()), 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() { //m_others is now just a shallow copy, we need use clone of the relations that in it now unsigned other_sz = m_others.size(); for(unsigned i=0; ideallocate(); 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(0); 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 = 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; isuggest_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; icomplement(p); std::swap(m_others[i],r); r->deallocate(); } table_element full_rel_idx = get_full_rel_idx(); scoped_rel complement_table = m_table->complement(p, &full_rel_idx); scoped_ptr u_fn = get_manager().mk_union_fn(*m_table, *complement_table, 0); SASSERT(u_fn); (*u_fn)(*m_table, *complement_table, 0); } 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) {} virtual void operator()(table_element * func_columns, const table_element * merged_func_columns) { m_accumulator.insert(static_cast(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((*iit)[0])); SASSERT((++iit)==iend); } return; } if(!m_live_rel_collection_project) { buffer removed_cols; removed_cols.resize(table_data_col_cnt); for(unsigned i=0; i 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((*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 empty_rel_indexes; //populated only if \c remove_empty==true 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_idxempty()) { 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 & rels) { if(rels.empty()) { return true; } unsigned sig_sz = rels.back()->get_signature().size(); svector table_cols(sig_sz, true); ptr_vector::iterator it = rels.begin(); ptr_vector::iterator end = rels.end(); for(; it!=end; ++it) { finite_product_relation & rel = **it; for(unsigned i=0; i pr_fun = get_manager().mk_project_fn(get_table(), to_project_away); table_base * moved_cols_table = (*pr_fun)(get_table()); //gets destroyed inside moved_cols_trel scoped_rel moved_cols_trel = rmgr.get_table_relation_plugin(moved_cols_table->get_plugin()).mk_from_table(moved_cols_sig, moved_cols_table); svector moved_cols_table_flags(moved_cols_sig.size(), false); scoped_rel moved_cols_rel = get_plugin().mk_empty(moved_cols_sig, moved_cols_table_flags.c_ptr()); scoped_ptr union_fun = get_manager().mk_union_fn(*moved_cols_rel, *moved_cols_trel); SASSERT(union_fun); //the table_relation should be able to be 'unioned into' any relation (*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 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 unordered_rel = (*join_fun)(*this, *moved_cols_rel); SASSERT(unordered_rel->get_signature().size()==sig_sz); //the signature size should be the same as original //now we need to reorder the columns in the \c new_rel to match the original table 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 perm_fun = get_manager().mk_rename_fn(*unordered_rel, cycle); //the scoped_rel wrapper does the destruction of the old object unordered_rel = (*perm_fun)(*unordered_rel); cycle.reset(); } finite_product_relation & new_rel = finite_product_relation_plugin::get(*unordered_rel); //Swap the content of the current object and new_rel. On exitting the function new_rel will be destroyed //since it points to the content of scoped_rel 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; iget_fact(tfact); const table_relation & orel = static_cast(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; iget_fact(fact); conjs.reset(); SASSERT(fact.size() == fact_sz); unsigned rel_idx = static_cast(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.c_ptr())); } bool_rewriter(m).mk_or(disjs.size(), disjs.c_ptr(), fml); } };