diff options
Diffstat (limited to 'omegalib/omega/src/Relations.cc')
-rw-r--r-- | omegalib/omega/src/Relations.cc | 2882 |
1 files changed, 2882 insertions, 0 deletions
diff --git a/omegalib/omega/src/Relations.cc b/omegalib/omega/src/Relations.cc new file mode 100644 index 0000000..d7dbe86 --- /dev/null +++ b/omegalib/omega/src/Relations.cc @@ -0,0 +1,2882 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + All Rights Reserved. + + Purpose: + Integer set and relation operations. + + Notes: + + History: + 04/22/09 merge_rels, Chun Chen +*****************************************************************************/ + +#include <omega/Relation.h> +#include <omega/Rel_map.h> +#include <omega/pres_tree.h> +#include <omega/pres_dnf.h> +#include <omega/pres_conj.h> +#include <omega/hull.h> +#include <basic/Tuple.h> +#include <basic/Map.h> +#include <basic/util.h> +#include <omega/omega_i.h> +#if defined STUDY_EVACUATIONS +#include <omega/evac.h> +#endif +#include <assert.h> + +namespace omega { + +#define CHECK_MAYBE_SUBSET 1 + +int relation_debug=0; + +namespace { + int leave_pufs_untouched = 0; + Variable_ID_Tuple exists_ids; + List<int> exists_numbers; + F_And * and_below_exists; +} + +/* The following allows us to avoid warnings about passing + temporaries as non-const references. This is useful but + has suddenly become illegal. */ + +Relation consume_and_regurgitate(NOT_CONST Relation &R) { + if(!R.is_null()) + ((Relation &) R).finalize(); + Relation S = (Relation &) R; + (Relation &) R = Relation::Null(); + return S; +} + + +// +// r1 Union r2. +// align the input tuples (if any) for F and G +// align the output tuples (if any) for F and G +// match named variables in F and G +// formula is f | g +// +Relation Union(NOT_CONST Relation &input_r1, + NOT_CONST Relation &input_r2) { + Relation r1 = consume_and_regurgitate(input_r1); + Relation r2 = consume_and_regurgitate(input_r2); + if (r1.is_null()) + return r2; + else if (r2.is_null()) + return r1; + if (r1.n_inp() != r2.n_inp() || r1.n_out() != r2.n_out()) + throw std::invalid_argument("relation arity does not match"); + + // skip_set_checks++; + // assert(r1.n_inp() == r2.n_inp()); + // assert(r1.n_out() == r2.n_out()); + // assert(!r1.is_null() && !r2.is_null()); + int in = r1.n_inp(), out = r1.n_out(); + // skip_set_checks--; + + return MapAndCombineRel2(r1, r2, Mapping::Identity(in, out), + Mapping::Identity(in,out), Comb_Or); +} + + +// +// F intersection G +// align the input tuples (if any) for F and G +// align the output tuples (if any) for F and G +// match named variables in F and G +// formula is f & g +// +Relation Intersection(NOT_CONST Relation &input_r1, + NOT_CONST Relation &input_r2) { + Relation r1 = consume_and_regurgitate(input_r1); + Relation r2 = consume_and_regurgitate(input_r2); + if (r1.is_null()) + return r2; + else if (r2.is_null()) + return r1; + if (r1.n_inp() != r2.n_inp() || r1.n_out() != r2.n_out()) + throw std::invalid_argument("relation arity does not match"); + + // skip_set_checks++; + // assert(r1.n_inp() == r2.n_inp()); + // assert(r1.n_out() == r2.n_out()); + // assert(!r1.is_null() && !r2.is_null()); + int in = r1.n_inp(), out = r1.n_out(); + // skip_set_checks--; + + return MapAndCombineRel2(r1, r2, Mapping::Identity(in,out), + Mapping::Identity(in,out), Comb_And); +} + + +// +// F \ G (the relation F restricted to domain G) +// align the input tuples for F and G +// match named variables in F and G +// formula is f & g +// +Relation Restrict_Domain(NOT_CONST Relation &input_r1, + NOT_CONST Relation &input_r2) { + Relation r1 = consume_and_regurgitate(input_r1); + Relation r2 = consume_and_regurgitate(input_r2); + if (r1.is_null()) + return r1; + else if (r2.is_null()) + return r1; + if (r1.n_inp() != r2.n_set()) + throw std::invalid_argument("relation arity does not match"); + + // assert(!r1.is_null() && !r2.is_null()); + // skip_set_checks++; + // assert(r1.n_inp() == r2.n_set()); + // assert(r2.is_set()); + int in = r1.n_inp(), out = r1.n_out(); + // skip_set_checks--; + + int i; + Mapping m2(r2.n_set()); + for(i=1; i<=r2.n_set(); i++) m2.set_map_set(i, Input_Var,i); + + // skip_set_checks++; + assert(r2.query_guaranteed_leading_0s() == -1 && + r2.query_possible_leading_0s() == -1); + // skip_set_checks--; + + Relation result = MapAndCombineRel2(r1, r2, Mapping::Identity(in,out), + m2, Comb_And); + // FERD -- update leading 0's - the may close up? + //result.invalidate_leading_info(); // could do better + return result; +} + +// +// +// F / G (the relation F restricted to range G) +// align the output tuples for F and G +// match named variables in F and G +// formula is f & g +// +Relation Restrict_Range(NOT_CONST Relation &input_r1, + NOT_CONST Relation &input_r2) { + Relation r1 = consume_and_regurgitate(input_r1); + Relation r2 = consume_and_regurgitate(input_r2); + if (r1.is_null()) + return r1; + else if (r2.is_null()) + return r1; + if (r1.n_out() != r2.n_set()) + throw std::invalid_argument("relation arity does not match"); + + // skip_set_checks++; + // assert(r1.n_out() == r2.n_set()); + // assert(r2.is_set()); + // assert(!r1.is_null() && !r2.is_null()); + int in = r1.n_inp(), out = r1.n_out(); + // skip_set_checks--; + + int i; + Mapping m2(r2.n_set()); + for(i=1; i<=r2.n_set(); i++) m2.set_map_set(i, Output_Var,i); + + // skip_set_checks++; + assert(r2.query_guaranteed_leading_0s() == -1 && + r2.query_possible_leading_0s() == -1); + // skip_set_checks--; + + Relation result = MapAndCombineRel2(r1, r2, Mapping::Identity(in, out), + m2, Comb_And); + // FERD -- update leading 0's - the may close up? + // result.invalidate_leading_info(); // could do better + return result; +} + + +// +// Add input variable to relation. +// +Relation Extend_Domain(NOT_CONST Relation &S) { + Relation R = consume_and_regurgitate(S); + if (R.is_null()) + throw std::invalid_argument("cannot extend domain on null relation"); + + // assert(!R.is_null() && (skip_set_checks || !R.is_set())); + // assert(!R.is_null()); + Rel_Body *r = R.split(); + r->In_Names.append(Const_String()); + r->number_input++; + assert(!r->is_null()); + + if (r->number_input <= r->number_output) + R.invalidate_leading_info(r->number_input); + + return R; +} + +// +// Add more input variables to relation. +// +Relation Extend_Domain(NOT_CONST Relation &S, int more) { + Relation R = consume_and_regurgitate(S); + if (R.is_null()) + throw std::invalid_argument("cannot extend domain on null relation"); + + // assert(!R.is_null()); + R.split(); + for (int i=1; i<=more; i++) R = Extend_Domain(R); + return R; +} + + +// +// Add output variable to relation. +// +Relation Extend_Range(NOT_CONST Relation &S) { + Relation R = consume_and_regurgitate(S); + if (R.is_null()) + throw std::invalid_argument("cannot extend range on null relation"); + + // assert(!R.is_null() && !R.is_set()); + // assert(!R.is_null()); + Rel_Body *r = R.split(); + r->Out_Names.append(Const_String()); + r->number_output++; + assert(!r->is_null()); + + if (r->number_output <= r->number_input) + R.invalidate_leading_info(r->number_output); + + return R; +} + +// +// Add more output variables to relation. +// +Relation Extend_Range(NOT_CONST Relation &S, int more) { + Relation R = consume_and_regurgitate(S); + + // assert(!R.is_null()); + R.split(); + for (int i=1; i<=more; i++) R = Extend_Range(R); + return R; +} + + +// +// Add set variable to set. +// +Relation Extend_Set(NOT_CONST Relation &S) { + Relation R = consume_and_regurgitate(S); + if (R.is_null()) + throw std::invalid_argument("cannot extend set on null relation"); + if (R.n_out() > 0) + throw std::invalid_argument("relation must be a set"); + + // assert(!R.is_null() && R.is_set()); + Rel_Body *r = R.split(); + r->In_Names.append(Const_String()); + r->number_input++; + assert(!r->is_null()); + return R; +} + +// +// Add more variables to set +// +Relation Extend_Set(NOT_CONST Relation &S, int more) { + Relation R = consume_and_regurgitate(S); + R.split(); + for (int i=1; i<=more; i++) R = Extend_Set(R); + return R; +} + + + +// +// Domain and Range. +// Make output (input) variables wildcards and simplify. +// Move all UFS's to have have the remaining tuple as an argument, +// and maprel will move them to the set tuple +// RESET all leading 0's +// +Relation Domain(NOT_CONST Relation &S) { + Relation r = consume_and_regurgitate(S); + if (r.is_null()) + return r; + + // assert(!S.is_null()); + // assert(!r.is_set()); + // skip_set_checks++; + int i; + Mapping m1(r.n_inp(), r.n_out()); + for(i=1; i<=r.n_inp(); i++) m1.set_map_in (i, Set_Var,i); + for(i=1; i<=r.n_out(); i++) m1.set_map_out(i, Exists_Var,i); + // skip_set_checks--; + + int a = r.max_ufs_arity_of_out(); + if (a > 0) { + // UFS's must evacuate from the output tuple + Variable_ID_Tuple remapped; + + r.simplify(); + DNF *d = r.split()->DNFize(); + d->count_leading_0s(); + // Any conjucts with leading_0s == -1 must have >= "a" leading 0s + // What a gross way to do this. Ferd + + for (DNF_Iterator conj(d); conj; conj++) { +#if defined STUDY_EVACUATIONS + study_evacuation(*conj, out_to_in, a); +#endif + + int cL0 = (*conj)->guaranteed_leading_0s; + + for (Variable_ID_Iterator func((*conj)->mappedVars); func; func++) + if ((*func)->kind() == Global_Var) { + Global_Var_ID f = (*func)->get_global_var(); + if (f->arity() > 0 && (*func)->function_of()==Output_Tuple) { + if (cL0 >= f->arity()) { + (*func)->remap = r.get_local(f, Input_Tuple); + } + else { + (*func)->remap = (*conj)->declare(); + (*conj)->make_inexact(); + } + remapped.append(*func); + } + } + (*conj)->remap(); + reset_remap_field(remapped); + remapped.clear(); + + (*conj)->guaranteed_leading_0s = (*conj)->possible_leading_0s = -1; + (*conj)->leading_dir = 0; + } + } + + MapRel1(r, m1, Comb_Id); // this invalidates leading0s + assert(r.is_set() || m1.n_in() == 0); // MapRel can't tell to make a set + r.markAsSet(); // if there were no inputs. + + // skip_set_checks++; + assert(r.query_guaranteed_leading_0s() == -1 && r.query_possible_leading_0s() == -1); + // skip_set_checks--; + + return r; +} + + +Relation Range(NOT_CONST Relation &S) { + Relation r = consume_and_regurgitate(S); + if (r.is_null()) + return r; + + //assert(!r.is_null()); + // skip_set_checks++; + + int i; + Mapping m1(r.n_inp(), r.n_out()); + for(i=1; i<=r.n_inp(); i++) m1.set_map_in (i, Exists_Var,i); + for(i=1; i<=r.n_out(); i++) m1.set_map_out(i, Set_Var,i); + // skip_set_checks--; + + int a = r.max_ufs_arity_of_in(); + if (a > 0) { + // UFS's must evacuate from the input tuple + Variable_ID_Tuple remapped; + + r.simplify(); + DNF *d = r.split()->DNFize(); + d->count_leading_0s(); + // Any conjucts with leading_0s == -1 must have >= "a" leading 0s + // What a gross way to do this. Ferd + + for (DNF_Iterator conj(d); conj; conj++) { +#if defined STUDY_EVACUATIONS + study_evacuation(*conj, in_to_out, a); +#endif + + int cL0 = (*conj)->guaranteed_leading_0s; + for (Variable_ID_Iterator func((*conj)->mappedVars); func; func++) + if ((*func)->kind() == Global_Var) { + Global_Var_ID f = (*func)->get_global_var(); + if (f->arity() > 0 && (*func)->function_of()==Input_Tuple) { + if (cL0 >= f->arity()) { + (*func)->remap = r.get_local(f, Output_Tuple); + } + else { + (*func)->remap = (*conj)->declare(); + (*conj)->make_inexact(); + } + remapped.append(*func); + } + } + (*conj)->remap(); + reset_remap_field(remapped); + remapped.clear(); + + (*conj)->guaranteed_leading_0s = (*conj)->possible_leading_0s = -1; + (*conj)->leading_dir = 0; + } + } + + MapRel1(r, m1, Comb_Id); // this invalidates leading0s + assert(r.is_set() || m1.n_out() == 0); // MapRel can't tell to make a set + r.markAsSet(); // if there were no outputs. + + // skip_set_checks++; + assert(r.query_guaranteed_leading_0s() == -1 && r.query_possible_leading_0s() == -1); + // skip_set_checks--; + + return r; +} + + +// +// Cross Product. Give two sets, A and B, create a relation whose +// domain is A and whose range is B. +// +Relation Cross_Product(NOT_CONST Relation &input_A, + NOT_CONST Relation &input_B) { + Relation A = consume_and_regurgitate(input_A); + Relation B = consume_and_regurgitate(input_B); + if (A.is_null() || B.is_null()) + throw std::invalid_argument("null relation"); + if (!A.is_set() || !B.is_set()) + throw std::invalid_argument("cross product must be on two set"); + + // assert(A.is_set()); + // assert(B.is_set()); + + // skip_set_checks++; + assert(A.query_guaranteed_leading_0s() == -1 && + A.query_possible_leading_0s() == -1); + assert(B.query_guaranteed_leading_0s() == -1 && + B.query_possible_leading_0s() == -1); + // skip_set_checks--; + + Mapping mA(A.n_set()); + Mapping mB(B.n_set()); + int i; + for(i = 1; i <= B.n_set(); i++) mB.set_map_set(i, Output_Var,i); + for(i = 1; i <= A.n_set(); i++) mA.set_map_set(i, Input_Var,i); + return MapAndCombineRel2(A, B, mA, mB, Comb_And); +} + + +// +// inverse F +// reverse the input and output tuples +// +Relation Inverse(NOT_CONST Relation &S) { + Relation r = consume_and_regurgitate(S); + if (r.is_null()) + return r; + + // assert(!r.is_null()); + // assert(!r.is_set()); + int i; + + Mapping m1(r.n_inp(), r.n_out()); + for(i=1; i<=r.n_inp(); i++) m1.set_map_in (i, Output_Var,i); + for(i=1; i<=r.n_out(); i++) m1.set_map_out(i, Input_Var,i); + + MapRel1(r, m1, Comb_Id, -1, -1, false); + + r.reverse_leading_dir_info(); + + return r; +} + +Relation After(NOT_CONST Relation &input_S, + int carried_by, int new_output,int dir) { + Relation S = consume_and_regurgitate(input_S); + assert(!S.is_null()); + assert(!S.is_set()); + int i; + Relation r(*S.split(),42); + + int a = r.max_ufs_arity_of_out(); + int preserved_positions = min(carried_by-1,new_output); + if (a >= preserved_positions) { + // UFS's must evacuate from the output tuple + Variable_ID_Tuple remapped; + + r.simplify(); + DNF *d = r.split()->DNFize(); + d->count_leading_0s(); + // Any conjucts with leading_0s == -1 must have >= "a" leading 0s + // What a gross way to do this. Ferd + + for (DNF_Iterator conj(d); conj; conj++) { + int cL0 = (*conj)->guaranteed_leading_0s; + + for (Variable_ID_Iterator func((*conj)->mappedVars); func; func++) + if ((*func)->kind() == Global_Var) { + Global_Var_ID f = (*func)->get_global_var(); + if (f->arity() > preserved_positions + && (*func)->function_of()==Output_Tuple) { + if (cL0 >= f->arity()) { + (*func)->remap = r.get_local(f, Input_Tuple); + } + else { + (*func)->remap = (*conj)->declare(); + (*conj)->make_inexact(); + } + remapped.append(*func); + } + } + (*conj)->remap(); + reset_remap_field(remapped); + remapped.clear(); + + (*conj)->guaranteed_leading_0s = + (*conj)->possible_leading_0s = -1; + (*conj)->leading_dir = 0; + } + } + + Mapping m1(r.n_inp(), r.n_out()); + for(i=1; i<=r.n_inp(); i++) m1.set_map_in (i, Input_Var,i); + if (carried_by > new_output) { + int preserve = min(new_output,r.n_out()); + for(i=1; i<=preserve; i++) m1.set_map_out(i, Output_Var,i); + for(i=preserve+1; i<=r.n_out(); i++) m1.set_map_out(i, Exists_Var,-1); + MapRel1(r, m1, Comb_Id, -1, -1, true); + if (new_output > preserve) + r = Extend_Range(r,new_output-r.n_out()); + return r; + } + + for(i=1; i<carried_by; i++) m1.set_map_out(i, Output_Var,i); + m1.set_map_out(carried_by, Exists_Var,1); + for(i=carried_by+1; i<=r.n_out(); i++) m1.set_map_out(i, Exists_Var,-1); + + MapRel1(r, m1, Comb_Id, -1, -1, true,false); + + Rel_Body *body = r.split(); + body->Out_Names.append(Const_String()); + body->number_output++; + assert(body->n_out() <= input_vars.size()); + + + GEQ_Handle h = and_below_exists->add_GEQ(0); + assert(carried_by < 128); + h.update_coef(exists_ids[1],-dir); + h.update_coef(r.output_var(carried_by),dir); + h.update_const(-1); + h.finalize(); + r.finalize(); + if (new_output > r.n_out()) + r = Extend_Range(r,new_output-r.n_out()); + return r; +} + +// +// Identity. +// +Relation Identity(int n_inp) { + Relation rr(n_inp, n_inp); + F_And *f = rr.add_and(); + for(int i=1; i<=n_inp; i++) { + EQ_Handle e = f->add_EQ(); + e.update_coef(rr.input_var(i), -1); + e.update_coef(rr.output_var(i), 1); + e.finalize(); + } + rr.finalize(); + assert(!rr.is_null()); + return rr; +} + +Relation Identity(NOT_CONST Relation &input_r) { + Relation r = consume_and_regurgitate(input_r); + return Restrict_Domain(Identity(r.n_set()),r); +} + +// +// Deltas(F) +// Return a set such that the ith variable is old Out_i - In_i +// Delta variables are created as input variables. +// Then input and output variables are projected out. +// +Relation Deltas(NOT_CONST Relation &S) { + Relation R = consume_and_regurgitate(S); + assert(!R.is_null()); + // skip_set_checks++; + assert(R.n_inp()==R.n_out()); + int in = R.n_inp(); + // skip_set_checks--; + return Deltas(R,in); +} + +Relation Deltas(NOT_CONST Relation &S, int eq_no) { + Relation R = consume_and_regurgitate(S); + // skip_set_checks++; + assert(!R.is_null()); + assert(eq_no<=R.n_inp()); + assert(eq_no<=R.n_out()); + // R.split(); + + int no_inp = R.n_inp(); + int no_out = R.n_out(); + + if(relation_debug) { + fprintf(DebugFile,"Computing Deltas:\n"); + R.prefix_print(DebugFile); + } + int a = R.max_ufs_arity(); + if (a > 0) { + Variable_ID_Tuple remapped; + + // UFS's must evacuate from all tuples - we need to go to DNF + // to enumerate the variables, I think... + R.simplify(); + if(relation_debug) { + fprintf(DebugFile,"Relation simplified:\n"); + R.prefix_print(DebugFile); + } + DNF *d = R.split()->DNFize(); + + for (DNF_Iterator conj(d); conj; conj++) { + for (Variable_ID_Iterator func((*conj)->mappedVars); func; func++) + if ((*func)->kind() == Global_Var) { + Global_Var_ID f = (*func)->get_global_var(); + if (f->arity() > 0) { + (*func)->remap = (*conj)->declare(); + (*conj)->make_inexact(); + remapped.append(*func); + } + } + (*conj)->remap(); + reset_remap_field(remapped); + remapped.clear(); + } + } + + R = Extend_Domain(R, eq_no); // add eq_no Delta vars + Mapping M(no_inp+eq_no, no_out); + int i; + for(i=1; i<=eq_no; i++) { // Set up Deltas equalities + EQ_Handle E = R.and_with_EQ(); + /* delta_i - w_i + r_i = 0 */ + E.update_coef(R.input_var(i), 1); + E.update_coef(R.output_var(i), -1); + E.update_coef(R.input_var(no_inp+i), 1); + E.finalize(); + M.set_map(Input_Var, no_inp+i, Set_Var, i); // Result will be a set + } + for(i=1; i<=no_inp; i++) { // project out input variables + M.set_map(Input_Var, i, Exists_Var, i); + } + for(i=1; i<=no_out; i++) { // project out output variables + M.set_map(Output_Var, i, Exists_Var, no_inp+i); + } + MapRel1(R, M, Comb_Id, eq_no, 0); + + if(relation_debug) { + fprintf(DebugFile,"Computing deltas:\n"); + R.prefix_print(DebugFile); + }; + R.finalize(); + assert(R.is_set()); // Should be since we map things to Set_Var + assert(R.n_set() == eq_no); + // skip_set_checks--; + return R; +} + + + + +Relation DeltasToRelation(NOT_CONST Relation &D, int n_inputs, int n_outputs) { + Relation R = consume_and_regurgitate(D); + + // skip_set_checks++; + assert(!R.is_null()); + R.markAsRelation(); + int common = R.n_inp(); + assert(common <= n_inputs); + assert(common <= n_outputs); + R.split(); + + if (R.max_ufs_arity() > 0) { + assert(R.max_ufs_arity() == 0 && + "'Deltas' not ready for UFS yet"); // FERD + fprintf(stderr, "'Deltas' not ready for UFS yet"); + exit(1); + } + + R = Extend_Domain(R, n_inputs); + R = Extend_Range(R, n_outputs); + Mapping M(common+n_inputs, n_outputs); + int i; + for(i=1; i<=common; i++) { // Set up Deltas equalities + EQ_Handle E = R.and_with_EQ(); + /* delta_i - w_i + r_i = 0 */ + E.update_coef(R.input_var(i), 1); + E.update_coef(R.output_var(i), -1); + E.update_coef(R.input_var(common+i), 1); + E.finalize(); + M.set_map(Input_Var, i, Exists_Var, i); // Result will be a set + } + for(i=1; i<=n_inputs; i++) { // project out input variables + M.set_map(Input_Var, common+i, Input_Var, i); + } + for(i=1; i<=n_outputs; i++) { // project out output variables + M.set_map(Output_Var, i, Output_Var, i); + } + MapRel1(R, M, Comb_Id, n_inputs, n_outputs); + + if(relation_debug) { + fprintf(DebugFile,"Computed DeltasToRelation:\n"); + R.prefix_print(DebugFile); + } + R.finalize(); + assert(!R.is_set()); + // skip_set_checks--; + return R; +} + + + +Relation Join(NOT_CONST Relation &G, NOT_CONST Relation &F) { + return Composition(F, G); +} + +bool prepare_relations_for_composition(Relation &r1,Relation &r2) { + assert(!r2.is_null() && !r1.is_null()); + + if(r2.is_set()) { + int a1 = r1.max_ufs_arity_of_in(), a2 = r2.max_ufs_arity_of_set(); + + if (a1 == 0 && a2 == 0) + return true; + else { + assert(0 && "Can't compose relation and set with function symbols"); + fprintf(stderr, "Can't compose relation and set with function symbols"); + exit(1); + return false; // make compiler shut up + } + } + + assert(r2.n_out() == r1.n_inp()); + + int zeros = max(r1.query_guaranteed_leading_0s(), + r2.query_guaranteed_leading_0s()); + return (zeros >= r1.max_ufs_arity_of_in() + && zeros >= r2.max_ufs_arity_of_out()); +} + +// +// Composition(F, G) = F o G, where F o G (x) = F(G(x)) +// That is, if F = { [i] -> [j] : ... } +// and G = { [x] -> [y] : ... } +// then Composition(F, G) = { [x] -> [j] : ... } +// +// align the output tuple for G and the input tuple for F, +// these become existensially quantified variables +// use the output tuple from F and the input tuple from G for the result +// match named variables in G and F +// formula is g & f +// +// If there are function symbols of arity > 0, we call special case +// code to handle them. This is not set up for the r2.is_set case yet. +// + +Relation Composition(NOT_CONST Relation &input_r1, NOT_CONST Relation &input_r2) { + Relation r1 = consume_and_regurgitate(input_r1); + Relation r2 = consume_and_regurgitate(input_r2); + assert(!r2.is_null() && !r1.is_null()); + + if(r2.is_set()) { + int a1 = r1.max_ufs_arity_of_in(), a2 = r2.max_ufs_arity_of_set(); + if (r2.n_set() != r1.n_inp()) { + fprintf(stderr,"Illegal composition/application, arities don't match\n"); + fprintf(stderr,"Trying to compute r1(r2)\n"); + fprintf(stderr,"arity of r2 must match input arity of r1\n"); + fprintf(stderr,"r1: "); + r1.print_with_subs(stderr); + fprintf(stderr,"r2: "); + r2.print_with_subs(stderr); + fprintf(stderr,"\n"); + assert(r2.n_set() == r1.n_inp()); + exit(1); + } + // skip_set_checks++; + int i; + if (a1 == 0 && a2 == 0) { + int x = r1.n_out(); + Mapping m1(r1.n_inp(), r1.n_out()); + for(i=1; i<=r1.n_out(); i++) m1.set_map_out(i, Set_Var,i); + for(i=1; i<=r1.n_inp(); i++) m1.set_map_in (i, Exists_Var,i); + Mapping m2(r2.n_set()); + for(i=1; i<=r2.n_set(); i++) m2.set_map_set(i, Exists_Var,i); + Relation R3 = MapAndCombineRel2(r2, r1, m2, m1, Comb_And); + // skip_set_checks--; + if (x == 0) + R3.markAsSet(); + return R3; + } + else { + assert(0 && + "Can't compose relation and set with function symbols"); + fprintf(stderr, + "Can't compose relation and set with function symbols"); + exit(1); + return Identity(0); // make compiler shut up + } + } + + if (r2.n_out() != r1.n_inp()) { + fprintf(stderr,"Illegal composition, arities don't match\n"); + fprintf(stderr,"Trying to compute r1 compose r2\n"); + fprintf(stderr,"Output arity of r2 must match input arity of r1\n"); + fprintf(stderr,"r1: "); + r1.print_with_subs(stderr); + fprintf(stderr,"r2: "); + r2.print_with_subs(stderr); + fprintf(stderr,"\n"); + assert(r2.n_out() == r1.n_inp()); + exit(1); + } + + int a1 = r1.max_ufs_arity_of_in(), a2 = r2.max_ufs_arity_of_out(); + + if (a1 == 0 && a2 == 0 && 0 /* FERD - leading 0's go wrong here */ ) { + // If no real UFS's, we can just use the general code: + int i; + Mapping m1(r1.n_inp(), r1.n_out()); + for(i=1; i<=r1.n_inp(); i++) m1.set_map_in (i, Exists_Var,i); + for(i=1; i<=r1.n_out(); i++) m1.set_map_out(i, Output_Var,i); + Mapping m2(r2.n_inp(), r2.n_out()); + for(i=1; i<=r2.n_inp(); i++) m2.set_map_in (i, Input_Var,i); + for(i=1; i<=r2.n_out(); i++) m2.set_map_out(i, Exists_Var,i); + + return MapAndCombineRel2(r2, r1, m2, m1, Comb_And); + } + else { + Relation result(r2.n_inp(), r1.n_out()); + int mid_size = r2.n_out(); + int i; + for(i =1; i<=r2.n_inp(); i++) + result.name_input_var(i,r2.input_var(i)->base_name); + for(i =1; i<=r1.n_out(); i++) + result.name_output_var(i,r1.output_var(i)->base_name); + + r1.simplify(); + r2.simplify(); + + Rel_Body *b1 = r1.split(), *b2 = r2.split(); + + if (b1 == b2) { + assert(0 && "Compose: not ready to handle b1 == b2 yet."); + fprintf(stderr, "Compose: not ready to handle b1 == b2 yet.\n"); + exit(1); + } + + DNF *d1 = b1->DNFize(); + DNF *d2 = b2->DNFize(); + + d1->count_leading_0s(); + d2->count_leading_0s(); + // Any conjucts with leading_0s == -1 must have >= max_arity leading 0s + // What a gross way to do this. Ferd + + F_Exists *exists = result.add_exists(); + Section<Variable_ID> middle_tuple = exists->declare_tuple(mid_size); + Map<Global_Var_ID, Variable_ID> lost_functions((Variable_ID)0); + + F_Or *result_conjs = exists->add_or(); + + for (DNF_Iterator conj1(d1); conj1; conj1++) + for (DNF_Iterator conj2(d2); conj2; conj2++) { + // combine conj1 and conj2: + // conj2's in becomes result's in; conj1's out becomes out + // conj2's out and conj1's in get merged and exist. quant. + // conj2's f(in) and conj1's f(out) become f(in) and f(out) + // conj2's f(out) and conj1's f(in) get merged, evacuate: + // if conj1 has f.arity leading 0s, they become f(out), + // if conj2 has f.arity leading 0s, they become f(in) + // if neither has enough 0s, they become a wildcard + // and the result is inexact + // old wildcards stay wildcards + +#if defined STUDY_EVACUATIONS + study_evacuation(*conj1, *conj2, max(a1, a2)); +#endif + + Conjunct *copy1, *copy2; + copy2 = (*conj2)->copy_conj_same_relation(); + copy1 = (*conj1)->copy_conj_same_relation(); + + Variable_ID_Tuple remapped; + + int c1L0 = copy1->guaranteed_leading_0s; + int c2L0 = copy2->guaranteed_leading_0s; + + int inexact = 0; + + // get rid of conj2's f(out) + { + for (Variable_ID_Iterator func(copy2->mappedVars); func; func++) + if ((*func)->kind() == Global_Var) { + Global_Var_ID f = (*func)->get_global_var(); + if (f->arity() > 0 && (*func)->function_of()==Output_Tuple) { + if (c2L0 >= f->arity()) { + (*func)->remap = r2.get_local(f, Input_Tuple); + remapped.append(*func); + } + else if (c1L0 >= f->arity()) { + // f->remap = copy1->get_local(f, Output_Tuple); + // this should work with the current impl. + // SHOULD BE A NO-OP? + assert((*func)==r1.get_local(f,Output_Tuple)); + } + else { + Variable_ID f_quantified = lost_functions[f]; + if (!f_quantified) { + f_quantified = exists->declare(); + lost_functions[f] = f_quantified; + } + inexact = 1; + (*func)->remap = f_quantified; + remapped.append(*func); + } + } + } + } + + // remap copy2's out + for (i=1; i<=mid_size; i++) { + r2.output_var(i)->remap = middle_tuple[i]; + } + + // do remapping for conj2, then reset everything so + // we can go on with conj1 + + copy2->remap(); + reset_remap_field(remapped); + reset_remap_field(output_vars,mid_size); + + + remapped.clear(); + + // get rid of conj1's f(in) + { + for (Variable_ID_Iterator func(copy1->mappedVars); func; func++) + if ((*func)->kind() == Global_Var) { + Global_Var_ID f = (*func)->get_global_var(); + if (f->arity() > 0 && (*func)->function_of()==Input_Tuple) { + if (c1L0 >= f->arity()) { + (*func)->remap = r1.get_local(f,Output_Tuple); + remapped.append(*func); + } + else if (c2L0 >= f->arity()) { + // f->remap = copy2->get_local(f, Input_Tuple); + // this should work with the current impl. + // SHOULD BE A NO-OP? + assert((*func)==r2.get_local(f,Input_Tuple)); + } + else { + Variable_ID f_quantified = lost_functions[f]; + if (!f_quantified) { + f_quantified = exists->declare(); + lost_functions[f] = f_quantified; + } + inexact = 1; + (*func)->remap = f_quantified; + remapped.append(*func); + } + } + } + } + + // merge copy1's in with the already remapped copy2's out + for (i=1; i<=mid_size; i++) { + r1.input_var(i)->remap = middle_tuple[i]; + } + + copy1->remap(); + reset_remap_field(remapped); + reset_remap_field(input_vars,mid_size); + + Conjunct *conj3 = merge_conjs(copy1, copy2, MERGE_COMPOSE, exists->relation()); + result_conjs->add_child(conj3); + delete copy1; + delete copy2; + + // make sure all variables used in the conjunct + // are listed in the "result" relation + + for (Variable_ID_Iterator func(conj3->mappedVars); func; func++) + if ((*func)->kind() == Global_Var) { + Global_Var_ID f = (*func)->get_global_var(); + if (f->arity() > 0) + result.get_local(f, (*func)->function_of()); + else + result.get_local(f); + } + + if (inexact) + conj3->make_inexact(); + } + + // result.simplify(2, 4); // can't really do that now, will cause failure in chill + result.finalize(); + r1 = r2 = Relation(); + return result; + } +} + + + +bool Is_Obvious_Subset(NOT_CONST Relation &input_r1, NOT_CONST Relation &input_r2) { + Relation r1 = consume_and_regurgitate(input_r1); + Relation r2 = consume_and_regurgitate(input_r2); + + assert(!r1.is_null() && !r2.is_null()); + Rel_Body *rr1 = r1.split(); + Rel_Body *rr2 = r2.split(); + rr1->simplify(); + rr2->simplify(); + use_ugly_names++; + + remap_DNF_vars(rr2, rr1); + + for(DNF_Iterator pd1(rr1->query_DNF()); pd1.live(); pd1.next()) { + Conjunct *conj1 = pd1.curr(); + int found = false; + for(DNF_Iterator pd2(rr2->query_DNF()); pd2.live(); pd2.next()) { + Conjunct *conj2 = pd2.curr(); + if (!conj2->is_exact()) continue; + + Conjunct *cgist = merge_conjs(conj1, conj2, MERGE_GIST, conj2->relation()); +#ifndef NDEBUG + cgist->setup_names(); +#endif + if (cgist->redSimplifyProblem(2, 0) == noRed) { + delete cgist; + found = true; + break; + } + delete cgist; + } + if (! found) { + use_ugly_names--; + r1 = r2 = Relation(); + return false; + } + } + use_ugly_names--; + r1 = r2 = Relation(); + return true; +} /* Is_Obvious_Subset */ + + +bool do_subset_check(NOT_CONST Relation &input_r1, + NOT_CONST Relation &input_r2); + +// do_subset_check really implements Must_Be_Subset anyway (due to +// correct handling of inexactness in the negation code), but +// still take upper and lower bounds here +bool Must_Be_Subset(NOT_CONST Relation &r1, NOT_CONST Relation &r2) { + Relation s1 = Upper_Bound(consume_and_regurgitate(r1)); + Relation s2 = Lower_Bound(consume_and_regurgitate(r2)); + return do_subset_check(s1,s2); +} + +bool Might_Be_Subset(NOT_CONST Relation &r1, NOT_CONST Relation &r2) { + Relation s1 = Lower_Bound(consume_and_regurgitate(r1)); + Relation s2 = Upper_Bound(consume_and_regurgitate(r2)); + return do_subset_check(s1,s2); +} + +bool May_Be_Subset(NOT_CONST Relation &r1, NOT_CONST Relation &r2){ + return Might_Be_Subset(r1,r2); +} + + + + +// +// F Must_Be_Subset G +// Test that (f => g) === (~f | g) is a Tautology +// or that (f & ~g) is unsatisfiable: +// align the input tuples (if any) for F and G +// align the output tuples (if any) for F and G +// Special case: if r2 has a single conjunct then use HasRedQeuations. +// + +bool do_subset_check(NOT_CONST Relation &input_r1, + NOT_CONST Relation &input_r2) { + Relation r1 = consume_and_regurgitate(input_r1); + Relation r2 = consume_and_regurgitate(input_r2); + if (r1.is_null() || r2.is_null()) + throw std::invalid_argument("null relation"); + if (r1.n_inp() != r2.n_inp() || r1.n_out() != r2.n_out()) + throw std::invalid_argument("relation arity does not match"); + + // assert(!r1.is_null() && !r2.is_null()); + // skip_set_checks++; + // assert(r1.n_inp() == r2.n_inp()); + // assert(r1.n_out() == r2.n_out()); + // skip_set_checks--; + r1.simplify(1,0); + r2.simplify(2,2); + Rel_Body *rr1 = r1.split(); + + if(relation_debug) { + fprintf(DebugFile, "\n$$$ Must_Be_Subset IN $$$\n"); + } + + bool c = true; + + // Check each conjunct separately + for(DNF_Iterator pd(rr1->query_DNF()); c && pd.live(); ) { + Relation tmp(r1,pd.curr()); + pd.next(); +#ifndef CHECK_MAYBE_SUBSET + if (pd.live()) + c = !Difference(tmp,copy(r2)).is_upper_bound_satisfiable(); + else + c = !Difference(tmp,r2).is_upper_bound_satisfiable(); +#else + Relation d=Difference(copy(tmp), copy(r2)); + c=!d.is_upper_bound_satisfiable(); + if (!c && !d.is_exact()) { // negation-induced inexactness + static int OMEGA_WHINGE = -1; + if (OMEGA_WHINGE < 0) { + OMEGA_WHINGE = getenv("OMEGA_WHINGE") ? atoi(getenv("OMEGA_WHINGE")) : 0; + } + if (OMEGA_WHINGE) { + fprintf(DebugFile,"\n===== r1 is maybe a Must_Be_Subset of r2 ========\n"); + fprintf(DebugFile,"-------> r1:\n"); + tmp.print_with_subs(DebugFile); + fprintf(DebugFile,"-------> r2:\n"); + r2.print_with_subs(DebugFile); + fprintf(DebugFile,"-------> r1-r2:\n"); + d.print_with_subs(DebugFile); + } + } +#endif + } + + if(relation_debug) { + fprintf(DebugFile, "$$$ Must_Be_Subset OUT $$$\n"); + } + r1 = r2 = Relation(); + return c; +} + + +// +// F minus G +// +Relation Difference(NOT_CONST Relation &input_r1, + NOT_CONST Relation &input_r2) { + Relation r1 = consume_and_regurgitate(input_r1); + Relation r2 = consume_and_regurgitate(input_r2); + if (r1.is_null() || r2.is_null()) + return r1; + if (r1.n_inp() != r2.n_inp() || r1.n_out() != r2.n_out()) + throw std::invalid_argument("relation arity does not match"); + + //assert(!r1.is_null() && !r2.is_null()); + // skip_set_checks++; + // assert(r1.n_inp() == r2.n_inp()); + // assert(r1.n_out() == r2.n_out()); + + int i; + Mapping m1(r1.n_inp(), r1.n_out()); + for(i=1; i<=r1.n_inp(); i++) m1.set_map_in (i, Input_Var,i); + for(i=1; i<=r1.n_out(); i++) m1.set_map_out(i, Output_Var,i); + Mapping m2(r2.n_inp(), r2.n_out()); + for(i=1; i<=r2.n_inp(); i++) m2.set_map_in (i, Input_Var,i); + for(i=1; i<=r2.n_out(); i++) m2.set_map_out(i, Output_Var,i); + // skip_set_checks--; + + return MapAndCombineRel2(r1, r2, m1, m2, Comb_AndNot); +} + +// +// complement F +// not F +// +Relation Complement(NOT_CONST Relation &S) { + Relation r = consume_and_regurgitate(S); + if (r.is_null()) + return r; + + // assert(!r.is_null()); + // skip_set_checks++; + int i; + Mapping m(r.n_inp(), r.n_out()); + for(i=1; i<=r.n_inp(); i++) m.set_map_in (i, Input_Var,i); + for(i=1; i<=r.n_out(); i++) m.set_map_out(i, Output_Var,i); + // skip_set_checks--; + + MapRel1(r, m, Comb_AndNot, -1, -1, false); + return r; +} + + +// +// Compute (gist r1 given r2). +// Currently we assume that r2 has only one conjunct. +// r2 may have zero input and output OR may have # in/out vars equal to r1. +// +Relation GistSingleConjunct(NOT_CONST Relation &input_R1, + NOT_CONST Relation &input_R2, int effort) { + Relation R1 = consume_and_regurgitate(input_R1); + Relation R2 = consume_and_regurgitate(input_R2); + + // skip_set_checks++; + assert(!R1.is_null() && !R2.is_null()); + assert((R1.n_inp() == R2.n_inp() && R1.n_out() == R2.n_out()) || + (R2.n_inp() == 0 && R2.n_out() == 0)); + R1.simplify(); + R2.simplify(); + Rel_Body *r1 = R1.split(); + Rel_Body *r2 = R2.split(); + + if(relation_debug) { + fprintf(DebugFile, "\n### GIST computation start ### [\n"); + R1.prefix_print(DebugFile); + R2.prefix_print(DebugFile); + fprintf(DebugFile, "### ###\n"); + } + + +// The merged conjunct has to have the variables of either r1 or r2, but +// not both. Use r1's, since it'll be cheaper to remap r2's single conj. + remap_DNF_vars(r2, r1); + assert(r2->is_upper_bound_satisfiable() && "Gist: second operand is FALSE"); + // skip_set_checks--; + + Conjunct *known = r2->single_conjunct(); + assert(known != NULL && "Gist: second operand has more than 1 conjunct"); + + DNF *new_dnf = new DNF(); + for(DNF_Iterator pd(r1->simplified_DNF); pd.live(); pd.next()) { + Conjunct *conj = pd.curr(); + Conjunct *cgist = merge_conjs(known, conj, MERGE_GIST, conj->relation()); // Uses r1's vars + cgist->set_relation(r1); // Thinks it's part of r1 now, for var. purposes + if(simplify_conj(cgist, true, effort+1, EQ_RED)) { + /* Throw out black constraints, turn red constraints into black */ + cgist->rm_color_constrs(); + if(cgist->is_true()) { + delete new_dnf; + delete cgist; + // skip_set_checks++; + Relation retval = Relation::True(r1->n_inp(), r2->n_out()); + // retval.finalize(); + retval.simplify(); + if(R1.is_set() && R2.is_set()) retval.markAsSet(); + // skip_set_checks--; + return retval; + } + else { + // since modular equations might be changed, simplify again! + simplify_conj(cgist, true, effort+1, EQ_BLACK); + + new_dnf->add_conjunct(cgist); + } + } + } + delete r1->simplified_DNF; + r1->simplified_DNF = new_dnf; + assert(!r1->is_null()); + R1.finalize(); + if(relation_debug) { + fprintf(DebugFile, "] ### GIST computation end ###\n"); + R1.prefix_print(DebugFile); + fprintf(DebugFile, "### ###\n"); + } + return(R1); +} + + +// +// Compute gist r1 given r2. r2 can have multiple conjuncts, +// return result is always simplified. +// +Relation Gist(NOT_CONST Relation &input_R1, + NOT_CONST Relation &input_R2, int effort) { + Relation R1 = consume_and_regurgitate(input_R1); + Relation R2 = consume_and_regurgitate(input_R2); + if (R1.is_null()) + return R1; + // change the Gist semantics to allow r2 be null -- by chun 07/30/2007 + if (R2.is_null()) { + R1.simplify(); + return R1; + } + if (!(R1.n_inp() == 0 && R2.n_out() == 0) && + (R1.n_inp() != R2.n_inp() || R1.n_out() != R2.n_out())) + throw std::invalid_argument("relation arity does not match"); + + // skip_set_checks++; + // assert(!R1.is_null()); + // assert(R2.is_null() || + // (R1.n_inp() == R2.n_inp() && R1.n_out() == R2.n_out()) || + // (R2.n_inp() == 0 && R2.n_out() == 0)); + // skip_set_checks--; + R2.simplify(); + + if(relation_debug) { + fprintf(DebugFile, "\n### multi-GIST computation start ### [\n"); + R1.prefix_print(DebugFile); + R2.prefix_print(DebugFile); + fprintf(DebugFile, "### ###\n"); + } + + if (!R2.is_upper_bound_satisfiable()) + return Relation::True(R1); + if (R2.is_obvious_tautology()) { + R1.simplify(); + return R1; + } + R1.simplify(); + + if (!Intersection(copy(R1), copy(R2)).is_upper_bound_satisfiable()) + return Relation::False(R1); + + int nconj1=0; + for (DNF_Iterator di(R1.simplified_DNF()); di.live(); di.next()) + nconj1++; + int nconj2=0; + for (DNF_Iterator di2(R2.simplified_DNF()); di2.live(); di2.next()) + nconj2++; + + { + static int OMEGA_WHINGE = -1; + if (OMEGA_WHINGE < 0) { + OMEGA_WHINGE = getenv("OMEGA_WHINGE") ? atoi(getenv("OMEGA_WHINGE")) : 0; + } + if (OMEGA_WHINGE && (nconj1 + nconj2 > 50)) { + fprintf(DebugFile,"WOW!!!! - Gist (%d conjuncts, %d conjuncts)!!!\n", + nconj1,nconj2); + fprintf(DebugFile,"Base:\n"); + R1.prefix_print(DebugFile); + fprintf(DebugFile,"Context:\n"); + R2.prefix_print(DebugFile); + } + } + + if (nconj2==1) + return GistSingleConjunct(R1,R2, effort); + else { + R1.simplify(0,1); + R2.simplify(0,1); + Relation G = Relation::True(R1); + for (DNF_Iterator di2(R2.simplified_DNF()); di2.live(); di2.next()) { + Conjunct * c2 = di2.curr(); + Relation G2 = Relation::False(R1); + for (DNF_Iterator di1(R1.simplified_DNF()); di1.live(); di1.next()) { + Conjunct * c1 = di1.curr(); + Relation G1=GistSingleConjunct(Relation(R1,c1), Relation(R2,c2),effort); + + if (G1.is_obvious_tautology()) { + G2 = G1; + break; + } + else if (!G1.is_upper_bound_satisfiable() || !G1.is_exact()) { + if(relation_debug) { + fprintf(DebugFile, "gist A given B is unsatisfiable\n"); + fprintf(DebugFile, "A:\n"); + Relation(R1,c1).prefix_print(DebugFile); + fprintf(DebugFile, "B:\n"); + Relation(R2,c2).prefix_print(DebugFile); + fprintf(DebugFile, "\n"); + } + //G1 = Relation(R1,c1); + return R1; + } + else if(0 && G1.is_exact() && !Must_Be_Subset(Relation(R1,c1),copy(G1))) { + fprintf(DebugFile,"Unexpected non-Must_Be_Subset gist result!\n"); + fprintf(DebugFile,"base: \n"); + Relation(R1,c1).prefix_print(DebugFile); + fprintf(DebugFile,"context: \n"); + Relation(R2,c2).prefix_print(DebugFile); + fprintf(DebugFile,"result: \n"); + G1.prefix_print(DebugFile); + fprintf(DebugFile,"base not subseteq result: \n"); + assert(!G1.is_exact() || Must_Be_Subset(Relation(R1,c1),copy(G1))); + } + G2=Union(G2,G1); + } + G2.simplify(0,1); + G = Intersection(G,G2); + G.simplify(0,1); + if(relation_debug) { + fprintf(DebugFile, "result so far is:\n"); + G.prefix_print(DebugFile); + } + } + + if(relation_debug) { + fprintf(DebugFile, "\n### end multi-GIST computation ### ]\n"); + fprintf(DebugFile, "G is:\n"); + G.prefix_print(DebugFile); + fprintf(DebugFile, "### ###\n"); + } +#if ! defined NDEBUG + Relation S1 = Intersection(copy(R1), copy(R2)); + Relation S2 = Intersection(copy(G), copy(R2)); + + + if(relation_debug) { + fprintf(DebugFile, "\n---->[Checking validity of the GIST result\n"); + fprintf(DebugFile, "for G=gist R1 given R2:\n"); + fprintf(DebugFile, "R1 intersect R2 is:\n"); + S1.print_with_subs(DebugFile); + fprintf(DebugFile, "\nG intersect R2 is:\n"); + S2.print_with_subs(DebugFile); + fprintf(DebugFile, "---->]\n"); + } + assert (!S1.is_exact() || !S2.is_exact() || (Must_Be_Subset(copy(S1),copy(S2)) && Must_Be_Subset(copy(S2),copy(S1)))); +#endif + return G; + } +} + + +// Project away all input and output variables. +Relation Project_On_Sym(NOT_CONST Relation &S, + NOT_CONST Relation &input_context) { + Relation R = consume_and_regurgitate(S); + Relation context = consume_and_regurgitate(input_context); + int i; + + // skip_set_checks++; + leave_pufs_untouched++; + int in_arity = R.max_ufs_arity_of_in(); + int out_arity = R.max_ufs_arity_of_out(); + assert(!R.is_null()); + R.split(); + + int no_inp = R.n_inp(); + int no_out = R.n_out(); + Mapping M(no_inp, no_out); + + for(i=1; i<=no_inp; i++) { // project out input variables + M.set_map(Input_Var, i, Exists_Var, i); + } + for(i=1; i<=no_out; i++) { // project out output variables + M.set_map(Output_Var, i, Exists_Var, no_inp+i); + } + MapRel1(R, M, Comb_Id, 0, 0); + + R.finalize(); + if (in_arity) R = Extend_Domain(R,in_arity); + if (out_arity) R = Extend_Range(R,out_arity); + + int d = min(in_arity,out_arity); + if (d && !context.is_null()) { + int g = min(d,context.query_guaranteed_leading_0s()); + int p = min(d,context.query_possible_leading_0s()); + int dir = context.query_leading_dir(); + R.enforce_leading_info(g,p,dir); + } + + leave_pufs_untouched--; + // skip_set_checks--; + if(relation_debug) { + fprintf(DebugFile,"\nProjecting onto symbolic (%d,%d):\n",in_arity,out_arity); + R.prefix_print(DebugFile); + } + return R; +} + + +// +// Project out global variable g from relation r +// +Relation Project(NOT_CONST Relation &S, Global_Var_ID g) { + Relation R = consume_and_regurgitate(S); + assert(!R.is_null()); + + skip_finalization_check++; + + Rel_Body *r = R.split(); + r->DNF_to_formula(); + Formula *f = r->rm_formula(); + F_Exists *ex = r->add_exists(); + ex->add_child(f); + + if (g->arity() == 0) { + assert(R.has_local(g) && "Project: Relation doesn't contain variable to be projected"); + Variable_ID v = R.get_local(g); + + bool rmd = rm_variable(r->Symbolic,v); + assert(rmd && "Project: Variable to be projected doesn't exist"); + + v->remap = ex->declare(v->base_name); + f->remap(); + v->remap = v; + } + else { + assert((R.has_local(g, Input_Tuple) || R.has_local(g, Output_Tuple)) && "Project: Relation doesn't contain variable to be projected"); + + if (R.has_local(g, Input_Tuple)) { + Variable_ID v = R.get_local(g, Input_Tuple); + + bool rmd = rm_variable(r->Symbolic,v); + assert(rmd && "Project: Variable to be projected doesn't exist"); + + v->remap = ex->declare(v->base_name); + f->remap(); + v->remap = v; + } + if (R.has_local(g, Output_Tuple)) { + Variable_ID v = R.get_local(g, Output_Tuple); + + bool rmd = rm_variable(r->Symbolic,v); + assert(rmd && "Project: Variable to be projected doesn't exist"); + + v->remap = ex->declare(v->base_name); + f->remap(); + v->remap = v; + } + } + + skip_finalization_check--; + + R.finalize(); + return R; +} + + +// +// Project all symbolic variables from relation r +// +Relation Project_Sym(NOT_CONST Relation &S) { + Relation R = consume_and_regurgitate(S); + assert(!R.is_null()); + + Rel_Body *r = R.split(); + r->DNF_to_formula(); + + Formula *f = r->rm_formula(); + + skip_finalization_check++; + F_Exists *ex = r->add_exists(); + for(Variable_ID_Iterator R_Sym(r->Symbolic); R_Sym; R_Sym++) { + Variable_ID v = *R_Sym; + v->remap = ex->declare(v->base_name); + } + ex->add_child(f); + skip_finalization_check--; + + f->remap(); + + reset_remap_field(r->Symbolic); + r->Symbolic.clear(); + + R.finalize(); + return R; +} + +// +// Project specified variables, leaving those variables with no constraints. +// +Relation Project(NOT_CONST Relation &S, Sequence<Variable_ID> &s) { + // This is difficult to do with mappings. This cheats, since it is + // much easier and more straightforward. + + Relation R = consume_and_regurgitate(S); + assert(!R.is_null()); + + Rel_Body *r = R.split(); + r->DNF_to_formula(); + Formula *f = r->rm_formula(); + bool need_symbolic_clear = false; + + skip_finalization_check++; + F_Exists *ex = r->add_exists(); + for(int i = 1; i <= s.size(); i++) { + if (s[i]->kind() == Global_Var) + need_symbolic_clear = true; + s[i]->remap = ex->declare(s[i]->base_name); + } + ex->add_child(f); + skip_finalization_check--; + + f->remap(); + + reset_remap_field(s); + if (need_symbolic_clear) + r->Symbolic.clear(); + + R.finalize(); + return R; +} + +Relation Project(NOT_CONST Relation &S, int pos, Var_Kind vkind) { + Variable_ID v = 0; // shut the compiler up + switch (vkind) { + case Input_Var: + v = input_vars[pos]; + break; + case Output_Var: + v = output_vars[pos]; + break; + // case Set_Var: + // v = set_vars[pos]; + // break; + default: + assert(0); + } + + return Project(S, v); +} + +Relation Project(NOT_CONST Relation &S, Variable_ID v) { + Tuple<Variable_ID> s; + s.append(v); + return Project(S, s); +} + +// +// Variables in DNF of map_rel reference declarations of map_rel (or not). +// remap_DNF_vars makes them to reference declarations of ref_rel. +// Ref_rel can get new global variable declarations in the process. +// +void remap_DNF_vars(Rel_Body *map_rel, Rel_Body *ref_rel) { + // skip_set_checks++; + assert (map_rel->simplified_DNF); + assert (ref_rel->simplified_DNF); + + // skip_set_checks++; + + for(DNF_Iterator pd(map_rel->simplified_DNF); pd.live(); pd.next()) { + Conjunct *cc = pd.curr(); + Variable_ID_Tuple &mvars = cc->mappedVars; + for(Variable_Iterator mvarsIter=mvars; mvarsIter; mvarsIter++) { + Variable_ID v = *mvarsIter; + switch(v->kind()) { + case Input_Var: + assert(ref_rel->n_inp() >= v->get_position()); + break; + case Output_Var: + assert(ref_rel->n_out() >= v->get_position()); + break; + case Global_Var: + // The assignment is a noop, but tells ref_rel that the global may be + // used inside it, which is required. + *mvarsIter = ref_rel->get_local(v->get_global_var(),v->function_of()); + break; + case Wildcard_Var: + break; + default: + assert(0 && "bad variable kind"); + } + } + } + // skip_set_checks--; +} + + +Relation projectOntoJust(Relation R, Variable_ID v) { + // skip_set_checks++; + + int ivars = R.n_inp(), ovars = R.n_out(); + int ex_ivars= 0, ex_ovars = 0; + + assert(v->kind() == Input_Var || v->kind() == Output_Var); + if (v->kind() == Input_Var) { + ex_ivars = 1; + R = Extend_Domain(R,1); + } + else { + ex_ovars = 1; + R = Extend_Range(R,1); + } + + // Project everything except v + Mapping m(ivars+ex_ivars,ovars+ex_ovars); + int j; + for(j = 1; j <=ivars+ex_ivars; j++) m.set_map_in(j, Exists_Var, j); + for(j = 1; j <=ovars+ex_ovars; j++) m.set_map_out(j, Exists_Var, j+ivars+ex_ivars); + m.set_map(v->kind(), v->get_position(), v->kind(), v->get_position()); + + MapRel1(R, m, Comb_Id,-1,-1); + R.finalize(); + // skip_set_checks--; + return R; +} + +//static +//void copyEQtoGEQ(GEQ_Handle &g, const EQ_Handle &e, bool negate) { +//extern void copy_constraint(Constraint_Handle H, Constraint_Handle initial); +// copy_constraint(g, e); +//} + + +Relation EQs_to_GEQs(NOT_CONST Relation &S, bool excludeStrides) { + Relation R = consume_and_regurgitate(S); + assert(R.is_simplified()); + use_ugly_names++; + for (DNF_Iterator s(R.query_DNF()); s.live(); s.next()) + s.curr()->convertEQstoGEQs(excludeStrides); + use_ugly_names--; + return R; +} + + +// Tuple to find values for is input+output +Relation Symbolic_Solution(NOT_CONST Relation &R) { + Relation S = consume_and_regurgitate(R); + Tuple<Variable_ID> vee; + // skip_set_checks++; + int i; + for(i = 1; i <= S.n_inp(); i++) vee.append(input_var(i)); + for(i = 1; i <= S.n_out(); i++) vee.append(output_var(i)); + // skip_set_checks--; + + return Solution(S, vee); +} + + +// Tuple to find values for is given as arg, plus input and output +Relation Symbolic_Solution(NOT_CONST Relation &R, Sequence<Variable_ID> &for_these){ + Relation S = consume_and_regurgitate(R); + Tuple<Variable_ID> vee; + // skip_set_checks++; + int i; + for(Any_Iterator<Variable_ID> it(for_these); it; it++) + vee.append(*it); + for(i = 1; i <= S.n_inp(); i++) vee.append(input_var(i)); + for(i = 1; i <= S.n_out(); i++) vee.append(output_var(i)); + // skip_set_checks--; + + return Solution(S, vee); +} + + +// Tuple to find values for is input+output+global_decls +Relation Sample_Solution(NOT_CONST Relation &R) { + Relation S = consume_and_regurgitate(R); + + Tuple<Variable_ID> vee; + + // skip_set_checks++; + int i; + for(i = 1; i <= S.global_decls()->size(); i++) + vee.append((*S.global_decls())[i]); + for(i = 1; i <= S.n_inp(); i++) vee.append(input_var(i)); + for(i = 1; i <= S.n_out(); i++) vee.append(output_var(i)); + // skip_set_checks--; + + return Solution(S,vee); +} + + +// Tuple to find values is given as arg +Relation Solution(NOT_CONST Relation &S, Sequence<Variable_ID> &for_these ) { + Relation R = consume_and_regurgitate(S); + if (R.is_null()) + return R; + + //assert(!R.is_null()); + + if(!R.is_upper_bound_satisfiable()) { + return Relation::False(R); + } + + bool inexactAnswer=false; + if(R.is_inexact()) { + if(R.is_lower_bound_satisfiable()) + R = Lower_Bound(R); // a solution to LB is a solution to the relation + else { + // A solution to the UB may not be a solution to the relation: + // There may be a solution which satisfies all known constraints, but + // we have no way of knowing if it satisifies the unknown constraints. + inexactAnswer = true; + R = Upper_Bound(R); + } + } + + Sequence<Variable_ID> &vee = for_these; + for (DNF_Iterator di(R.query_DNF()); di; di++) { + Relation current(R, *di); + int i; + for(i = vee.size()-1; i >= 0; i--) { + bool some_constraints = false, one_stride = false; + + int current_var = vee.size()-i; + Section<Variable_ID> s(&vee,current_var+1,i); + + // Query variable in vee[current_var] + Relation projected = Project(copy(current), s); + + retry_solution: + assert(projected.has_single_conjunct()); + DNF_Iterator one = projected.query_DNF(); + + // Look for candidate EQ's + EQ_Handle stride; + EQ_Iterator ei(*one); + for(; ei; ei++) { + if((*ei).get_coef(vee[current_var]) != 0) { + if(!Constr_Vars_Iter(*ei,true).live()) { // no wildcards + some_constraints = true; + // Add this constraint to the current as an EQ + current.and_with_EQ(*ei); + break; + } + else { + one_stride = !one_stride && !some_constraints; + stride = *ei; + } + } + } + if(ei) + continue; // Found an EQ, skip to next variable + else if (one_stride && !some_constraints) { + // if unconstrained except for a stride, pick stride as value + Constr_Vars_Iter cvi(stride,true); + assert(cvi.live()); + cvi++; + if(!cvi) { // Just one existentially quantified variable + Relation current_copy = current; + EQ_Handle eh = current_copy.and_with_EQ(); + for(Constr_Vars_Iter si = stride; si; si++) + if((*si).var->kind() != Wildcard_Var){ + // pick "0" for wildcard, don't set its coef + eh.update_coef((*si).var, (*si).coef); + } + eh.update_const(stride.get_const()); + if(current_copy.is_upper_bound_satisfiable()){ + current = current_copy; + continue; // skip to next var + } + } + some_constraints = true; // count the stride as a constraint + } + + // Can we convert a GEQ? + GEQ_Iterator gi(*one); + for(; gi; gi++) { + if((*gi).get_coef(vee[current_var]) != 0) { + some_constraints = true; + if(!Constr_Vars_Iter(*gi,true).live()) { // no wildcards + Relation current_copy = current; + // Add this constraint to the current as an EQ & test + current_copy.and_with_EQ(*gi); + if (current_copy.is_upper_bound_satisfiable()) { + current = current_copy; + break; + } + } + } + } + if (gi) continue; // Turned a GEQ into EQ, skip to next + + // Remove wildcards, try try again + Relation approx = Approximate(copy(projected)); + assert(approx.has_single_conjunct()); + DNF_Iterator d2 = approx.query_DNF(); + + EQ_Iterator ei2(*d2); + for(; ei2; ei2++) { + if((*ei2).get_coef(vee[current_var]) != 0) { + some_constraints = true; + assert(!Constr_Vars_Iter(*ei2,true).live()); // no wildcards + Relation current_copy = current; + // Add this constraint to the current as an EQ & test + current_copy.and_with_EQ(*ei2); + if (current_copy.is_upper_bound_satisfiable()) { + current = current_copy; + break; + } + } + } + if(ei2) continue; // Found an EQ, skip to next variable + + GEQ_Iterator gi2(*d2); + for(; gi2; gi2++) { + if((*gi2).get_coef(vee[current_var]) != 0) { + some_constraints = true; + assert(!Constr_Vars_Iter(*gi2,true).live()); // no wildcards + Relation current_copy = current; + // Add this constraint to the current as an EQ & test + current_copy.and_with_EQ(*gi2); + if (current_copy.is_upper_bound_satisfiable()) { + current = current_copy; + break; + } + } + } + if(gi2) continue; + + if(!some_constraints) { // No constraints on this variable were found + EQ_Handle e = current.and_with_EQ(); + e.update_const(-42); // Be creative + e.update_coef(vee[current_var], 1); + continue; + } + else { // What to do? Find a wildcard to discard + Variable_ID wild = NULL; + + for (GEQ_Iterator gi(*one); gi; gi++) + if ((*gi).get_coef(vee[current_var]) != 0 && (*gi).has_wildcards()) { + Constr_Vars_Iter cvi(*gi, true); + wild = (*cvi).var; + break; + } + if (wild == NULL) + for (EQ_Iterator ei(*one); ei; ei++) + if ((*ei).get_coef(vee[current_var]) != 0 && (*ei).has_wildcards()) { + Constr_Vars_Iter cvi(*ei, true); + wild = (*cvi).var; + break; + } + + if (wild != NULL) { + // skip_set_checks++; + + Relation R2; + { + Tuple<Relation> r(1); + r[1] = projected; + Tuple<std::map<Variable_ID, std::pair<Var_Kind, int> > > mapping(1); + mapping[1][wild] = std::make_pair(vee[current_var]->kind(), vee[current_var]->get_position()); + mapping[1][vee[current_var]] = std::make_pair(Exists_Var, 1); + Tuple<bool> inverse(1); + inverse[1] = false; + R2 = merge_rels(r, mapping, inverse, Comb_And); + } + + Variable_ID R2_v; + switch (vee[current_var]->kind()) { + // case Set_Var: + case Input_Var: { + int pos = vee[current_var]->get_position(); + R2_v = R2.input_var(pos); + break; + } + case Output_Var: { + int pos = vee[current_var]->get_position(); + R2_v = R2.output_var(pos); + break; + } + case Global_Var: { + Global_Var_ID g = vee[current_var]->get_global_var(); + if (g->arity() == 0) + R2_v = R2.get_local(g); + else + R2_v = R2.get_local(g, vee[current_var]->function_of()); + } + default: + assert(0); + } + + Relation S2; + { + Tuple<Variable_ID> vee; + vee.append(R2_v); + S2 = Solution(R2, vee); + } + + Variable_ID S2_v; + switch (vee[current_var]->kind()) { + // case Set_Var: + case Input_Var: { + int pos = vee[current_var]->get_position(); + S2_v = S2.input_var(pos); + break; + } + case Output_Var: { + int pos = vee[current_var]->get_position(); + S2_v = S2.output_var(pos); + break; + } + case Global_Var: { + Global_Var_ID g = vee[current_var]->get_global_var(); + if (g->arity() == 0) + S2_v = S2.get_local(g); + else + S2_v = S2.get_local(g, vee[current_var]->function_of()); + } + default: + assert(0); + } + + Relation R3; + { + Tuple<Relation> r(2); + r[1] = projected; + r[2] = S2; + Tuple<std::map<Variable_ID, std::pair<Var_Kind, int> > > mapping(2); + mapping[1][wild] = std::make_pair(Exists_Var, 1); + mapping[2][S2_v] = std::make_pair(Exists_Var, 1); + Tuple<bool> inverse(2); + inverse[1] = inverse[2] = false; + R3 = merge_rels(r, mapping, inverse, Comb_And); + } + + // skip_set_checks--; + + if (R3.is_upper_bound_satisfiable()) { + projected = R3; + goto retry_solution; + } + } + } + + // If we get here, we failed to find a suitable constraint for + // this variable at this conjunct, look for another conjunct. + break; + } + + if (i < 0) { // solution found + if(inexactAnswer) + current.and_with_and()->add_unknown(); + current.finalize(); + return current; + } + } + + // No solution found for any conjunct, we bail out. + fprintf(stderr,"Couldn't find suitable constraint for variable\n"); + return Relation::Unknown(R); +} + + +Relation Approximate(NOT_CONST Relation &input_R, bool strides_allowed) { + Relation R = consume_and_regurgitate(input_R); + if (R.is_null()) + return R; + + // assert(!R.is_null()); + Rel_Body *r = R.split(); + + // approximate can be used to remove lambda variables from farkas, + // so be careful not to invoke simplification process for integers. + r->simplify(-1,-1); + + if (pres_debug) { + fprintf(DebugFile,"Computing approximation "); + if (strides_allowed) fprintf(DebugFile,"with strides allowed "); + fprintf(DebugFile,"[ \n"); + r->prefix_print(DebugFile); + } + + use_ugly_names++; + for (DNF_Iterator pd(r->simplified_DNF); pd.live(); ) { + Conjunct *C = pd.curr(); + pd.next(); + + for(int i = 0; i < C->problem->nGEQs; i++) + C->problem->GEQs[i].touched = 1; + + C->reorder(); + if(C->problem->simplifyApproximate(strides_allowed)==0) { + r->simplified_DNF->rm_conjunct(C); + delete C; + } + else { + C->simplifyProblem(1,0,1); + + free_var_decls(C->myLocals); C->myLocals.clear(); + + Problem *p = C->problem; + Variable_ID_Tuple new_mapped(0); // This is expanded by "append" + for (int i = 1; i <= p->safeVars; i++) { + // what is now in column i used to be in column p->var[i] + Variable_ID v = C->mappedVars[p->var[i]]; + assert (v->kind() != Wildcard_Var); + new_mapped.append(v); + } + assert(strides_allowed || C->problem->nVars == C->problem->safeVars); + C->mappedVars = new_mapped; + for (int i = p->safeVars+1; i <= p->nVars; i++) { + Variable_ID v = C->declare(); + C->mappedVars.append(v); + } + + + // reset var and forwarding address if desired. + p->variablesInitialized = 0; + for(int i = 1; i < C->problem->nVars; i++) + C->problem->var[i] = C->problem->forwardingAddress[i] = i; + } + } + + if (pres_debug) + fprintf(DebugFile,"] done Computing approximation\n"); + use_ugly_names--; + return R; +} + + +Relation Lower_Bound(NOT_CONST Relation &r) { + Relation s = consume_and_regurgitate(r); + s.interpret_unknown_as_false(); + return s; +} + + +Relation Upper_Bound(NOT_CONST Relation &r) { + Relation s = consume_and_regurgitate(r); + s.interpret_unknown_as_true(); + return s; +} + + +bool operator==(const Relation &, const Relation &) { + assert(0 && "You rilly, rilly don't want to do this.\n"); + abort(); + return false; +} + + +namespace { // supporting stuff for MapRel1 and MapAndCombine2 + // Determine if a mapping requires an f_exists node + bool has_existentials(const Mapping &m) { + for(int i=1;i<=m.n_in(); i++) + if (m.get_map_in_kind(i) == Exists_Var) return true; + for(int j=1;j<=m.n_out(); j++) + if (m.get_map_out_kind(j) == Exists_Var) return true; + return false; + } + + void get_relation_arity_from_one_mapping(const Mapping &m1, + int &in_req, int &out_req) { + int j, i; + in_req = 0; out_req = 0; + for(i = 1; i <= m1.n_in(); i++) { + j = m1.get_map_in_pos(i); + switch(m1.get_map_in_kind(i)) { + case Input_Var: in_req = max(in_req, j); break; + // case Set_Var: in_req = max(in_req, j); break; + case Output_Var: out_req = max(out_req, j); break; + default: break; + } + } + for(i = 1; i <= m1.n_out(); i++) { + j = m1.get_map_out_pos(i); + switch(m1.get_map_out_kind(i)) { + case Input_Var: in_req = max(in_req, j); break; + // case Set_Var: in_req = max(in_req, j); break; + case Output_Var: out_req = max(out_req, j); break; + default: break; + } + } + } + + // Scan mappings to see how many input and output variables they require. + void get_relation_arity_from_mappings(const Mapping &m1, + const Mapping &m2, + int &in_req, int &out_req) { + int inreq1, inreq2, outreq1, outreq2; + get_relation_arity_from_one_mapping(m1, inreq1, outreq1); + get_relation_arity_from_one_mapping(m2, inreq2, outreq2); + in_req = max(inreq1, inreq2); + out_req = max(outreq1, outreq2); + } +} + + +// +// Build lists of variables that need to be replaced in the given +// Formula. Declare globals in new relation. Then call +// map_vars to do the replacements. +// +// Obnoxiously many arguments here: +// Relation arguments contain declarations of symbolic and in/out vars. +// F_Exists argument is where needed existentially quant. vars can be decl. +// +// Mapping specifies how in/out vars are mapped +// Two lists are required to be able to map in/out variables from the first +// and second relations to the same existentially quantified variable. +// +void align(Rel_Body *originalr, Rel_Body *newr, F_Exists *fe, + Formula *f, const Mapping &mapping, bool &newrIsSet, + List<int> &seen_exists, Variable_ID_Tuple &seen_exists_ids) { + int i, cur_ex = 0; // initialize cur_ex to shut up the compiler + + f->set_relation(newr); // Might not need to do this anymore, if bugs were fixed + int input_remapped = 0; + int output_remapped = 0; + int sym_remapped = 0; + // skip_set_checks++; + + Variable_ID new_var; + Const_String new_name; + int new_pos; + + // MAP old input variables by setting their remap fields + for(i = 1; i <= originalr->n_inp(); i++) { + Variable_ID this_var = originalr->input_var(i), New_E; + Const_String this_name = originalr->In_Names[i]; + + switch (mapping.get_map_in_kind(i)) { + case Input_Var: + // case Set_Var: + // if (mapping.get_map_in_kind(i) == Set_Var) + // newrIsSet = true; // Don't mark it just yet; we still need to + // // refer to its "input" vars internally + + // assert((newrIsSet && mapping.get_map_in_kind(i) == Set_Var) + // || ((!newrIsSet &&mapping.get_map_in_kind(i) == Input_Var))); + + new_pos = mapping.get_map_in_pos(i); + new_var = newr->input_var(new_pos); + if (this_var != new_var) { + input_remapped = 1; + this_var->remap = new_var; + } + new_name = newr->In_Names[new_pos]; + if (!this_name.null()) { // should we name this? + if (!new_name.null()) { // already named, anonymize + if (new_name != this_name) + newr->name_input_var(new_pos, Const_String()); + } + else + newr->name_input_var(new_pos, this_name); + } + break; + case Output_Var: + assert(!newr->is_set()); + input_remapped = 1; + new_pos = mapping.get_map_in_pos(i); + this_var->remap = new_var = newr->output_var(new_pos); + new_name = newr->Out_Names[new_pos]; + if (!this_name.null()) { + if (!new_name.null()) { // already named, anonymize + if (new_name != this_name) + newr->name_output_var(new_pos, Const_String()); + } + else + newr->name_output_var(new_pos, this_name); + } + break; + case Exists_Var: + input_remapped = 1; + // check if we have declared it, use that if so. + // create it if not. + if (mapping.get_map_in_pos(i) <= 0 || + (cur_ex = seen_exists.index(mapping.get_map_in_pos(i))) == 0){ + if (!this_name.null()) + New_E = fe->declare(this_name); + else + New_E = fe->declare(); + this_var->remap = New_E; + if (mapping.get_map_in_pos(i) > 0) { + seen_exists.append(mapping.get_map_in_pos(i)); + seen_exists_ids.append(New_E); + } + } + else { + this_var->remap = new_var = seen_exists_ids[cur_ex]; + if (!this_name.null()) { // Have we already assigned a name? + if (!new_var->base_name.null()) { + if (new_var->base_name != this_name) + new_var->base_name = Const_String(); + } + else { + new_var->base_name = this_name; + assert(!this_name.null()); + } + } + } + break; + default: + assert(0 && "Unsupported var type in MapRel2"); + break; + } + } + + // MAP old output variables. + for(i = 1; i <= originalr->n_out(); i++) { + Variable_ID this_var = originalr->output_var(i), New_E; + Const_String this_name = originalr->Out_Names[i]; + + switch (mapping.get_map_out_kind(i)) { + case Input_Var: + // case Set_Var: + // if (mapping.get_map_out_kind(i) == Set_Var) + // newrIsSet = true; // Don't mark it just yet; we still need to refer to its "input" vars internally + + // assert((newrIsSet && mapping.get_map_out_kind(i) == Set_Var) + // ||((!newrIsSet &&mapping.get_map_out_kind(i) == Input_Var))); + + output_remapped = 1; + new_pos = mapping.get_map_out_pos(i); + this_var->remap = new_var = newr->input_var(new_pos); + new_name = newr->In_Names[new_pos]; + if (!this_name.null()) { + if (!new_name.null()) { // already named, anonymize + if (new_name != this_name) + newr->name_input_var(new_pos, Const_String()); + } + else + newr->name_input_var(new_pos, this_name); + } + break; + case Output_Var: + assert(!newr->is_set()); + new_pos = mapping.get_map_out_pos(i); + new_var = newr->output_var(new_pos); + if (new_var != this_var) { + output_remapped = 1; + this_var->remap = new_var; + } + new_name = newr->Out_Names[new_pos]; + if (!this_name.null()) { + if (!new_name.null()) { // already named, anonymize + if (new_name != this_name) + newr->name_output_var(new_pos, Const_String()); + } + else + newr->name_output_var(new_pos, this_name); + } + break; + case Exists_Var: + // check if we have declared it, create it if not. + output_remapped = 1; + if (mapping.get_map_out_pos(i) <= 0 || + (cur_ex = seen_exists.index(mapping.get_map_out_pos(i))) == 0) { // Declare it. + New_E = fe->declare(this_name); + this_var->remap = New_E; + if (mapping.get_map_out_pos(i) > 0) { + seen_exists.append(mapping.get_map_out_pos(i)); + seen_exists_ids.append(New_E); + } + } + else { + this_var->remap = new_var = seen_exists_ids[cur_ex]; + if (!this_name.null()) { + if (!new_var->base_name.null()) { + if (new_var->base_name != this_name) + new_var->base_name = Const_String(); + } + else { + new_var->base_name = this_name; + } + } + } + break; + default: + assert(0 &&"Unsupported var type in MapRel2"); + break; + } + } + + Variable_ID_Tuple *oldSym = originalr->global_decls(); + for(i=1; i<=(*oldSym).size(); i++) { + Variable_ID v = (*oldSym)[i]; + assert(v->kind()==Global_Var); + if (v->get_global_var()->arity() > 0) { + Argument_Tuple new_of = v->function_of(); + if (!leave_pufs_untouched) + new_of = mapping.get_tuple_fate(new_of, v->get_global_var()->arity()); + if (new_of == Unknown_Tuple) { + // hopefully v is not really used + // if we get here, f should have been in DNF, + // now an OR node with conjuncts below + // we just need to check that no conjunct uses v +#if ! defined NDEBUG + if (f->node_type() == Op_Conjunct) { + assert(f->really_conjunct()->mappedVars.index(v)==0 + && "v unused"); + } +#if 0 + else { + // assert(f->node_type() == Op_Or); + for (List_Iterator<Formula *> conj(f->children()); conj; conj++) { + assert((*conj)->really_conjunct()->mappedVars.index(v)==0 + && "v unused"); + } + } +#endif +#endif + // since its not really used, don't bother adding it to + // the the global_vars list of the new relation + continue; + } + if (v->function_of() != new_of) { + Variable_ID new_v=newr->get_local(v->get_global_var(),new_of); + assert(v != new_v); + v->remap = new_v; + sym_remapped = 1; + } + else { + // add symbolic to symbolic list +#if ! defined NDEBUG + Variable_ID new_v = +#endif + newr->get_local(v->get_global_var(), v->function_of()); +#if ! defined NDEBUG + assert(v == new_v); +#endif + } + } + else { + // add symbolic to symbolic list +#if ! defined NDEBUG + Variable_ID new_v = +#endif + newr->get_local(v->get_global_var()); +#if ! defined NDEBUG + assert(v == new_v); +#endif + } + } + + if (sym_remapped || input_remapped || output_remapped) { + f->remap(); + + // If 2 vars mapped to same variable, combine them + //There's a column to combine only when there are two equal remap fields. + Tuple<Variable_ID> vt(0); + bool combine = false; + Tuple_Iterator<Variable_ID> t(input_vars); + for(i=1; !combine && i<=originalr->n_inp(); t++, i++) + if (vt.index((*t)->remap)) + combine = true; + else + vt.append((*t)->remap); + Tuple_Iterator<Variable_ID> t2(output_vars); + for(i=1; !combine && i <= originalr->n_out(); t2++, i++) + if (vt.index((*t2)->remap)) + combine = true; + else + vt.append((*t2)->remap); + if (combine) f->combine_columns(); + + if (sym_remapped) + reset_remap_field(originalr->Symbolic); + if (input_remapped) + reset_remap_field(input_vars,originalr->n_inp()); + if (output_remapped) + reset_remap_field(output_vars,originalr->n_out()); + } + + // skip_set_checks--; + +#ifndef NDEBUG + if (fe) + foreach(v,Variable_ID,fe->myLocals,assert(v == v->remap)); +#endif +} + + +// MapRel1, MapAndCombineRel2 can be replaced by merge_rels +void MapRel1(Relation &R, const Mapping &map, Combine_Type ctype, + int number_input, int number_output, + bool invalidate_resulting_leading_info, + bool finalize) { +#if defined(INCLUDE_COMPRESSION) + assert(!R.is_compressed()); +#endif + assert(!R.is_null()); + + Relation inputRel = R; + R = Relation(); + Rel_Body *inputRelBody = inputRel.split(); + + int in_req=0, out_req=0; + get_relation_arity_from_one_mapping(map, in_req, out_req); + + R = Relation(number_input == -1 ? in_req : number_input, + number_output == -1 ? out_req : number_output); + + Rel_Body *outputRelBody = R.split(); + + inputRelBody->DNF_to_formula(); + Formula *f1 = inputRelBody->rm_formula(); + + F_Exists *fe; + Formula *f; + if (has_existentials(map)) { + f = fe = outputRelBody->add_exists(); + } + else { + fe = NULL; + f = outputRelBody; + } + and_below_exists = NULL; + if (finalize) and_below_exists = NULL; + else f = and_below_exists = f->add_and(); + if(ctype == Comb_AndNot) { + f = f->add_not(); + } + f->add_child(f1); + + exists_ids.clear(); + exists_numbers.clear(); + + bool returnAsSet=false; + align(inputRelBody, outputRelBody, fe, f1, map, returnAsSet, + exists_numbers, exists_ids); + + if (returnAsSet || + (inputRelBody->is_set() && outputRelBody->n_out() == 0)) { + R.markAsSet(); + R.invalidate_leading_info(); // nonsensical for a set + } + + if (finalize) R.finalize(); + inputRel = Relation(); + if (invalidate_resulting_leading_info) + R.invalidate_leading_info(); +} + + +Relation MapAndCombineRel2(Relation &R1, Relation &R2, const Mapping &mapping1, + const Mapping &mapping2, Combine_Type ctype, + int number_input, int number_output) { +#if defined(INCLUDE_COMPRESSION) + assert(!R1.is_compressed()); + assert(!R2.is_compressed()); +#endif + assert(!R1.is_null() && !R2.is_null()); + Rel_Body *r1 = R1.split(); + Rel_Body *r2 = R2.split(); + + int in_req, out_req; // Create the new relation + get_relation_arity_from_mappings(mapping1, mapping2, in_req, out_req); + Relation R3(number_input == -1 ? in_req : number_input, + number_output == -1 ? out_req : number_output); + Rel_Body *r3 = R3.split(); // This is just to get the pointer, it's cheap + + /* permit the add_{exists,and} below, reset after they are done.*/ + skip_finalization_check++; + + F_Exists *fe = NULL; + Formula *f; + if (has_existentials(mapping1) || has_existentials(mapping2)) { + fe = r3->add_exists(); + f = fe; + } + else { + f = r3; + } + + r1->DNF_to_formula(); + Formula *f1 = r1->rm_formula(); + r2->DNF_to_formula(); + Formula *f2 = r2->rm_formula(); + + // align: change r1 vars to r3 vars in formula f1 via map mapping1, + // declaring needed exists vars in F_Exists *fe + // Also maps symbolic variables appropriately, sets relation ptrs in f1. + // In order to map variables of both relations to the same variables, + // we keep a list of new existentially quantified vars between calls. + // returnAsSet means mark r3 as set before return. Don't mark it yet, + // because internally we need to refer to "input_vars" of a set, and that + // would blow assertions. + + bool returnAsSet=false; + exists_ids.clear(); + exists_numbers.clear(); + align(r1, r3, fe, f1, mapping1, returnAsSet, exists_numbers, exists_ids); + // align: change r2 vars to r3 vars in formula f2 via map mapping2 + align(r2, r3, fe, f2, mapping2, returnAsSet, exists_numbers, exists_ids); + + switch (ctype) { + case Comb_Or: + if(f1->node_type() == Op_Or) { + f->add_child(f1); + f = f1; + } + else { + f = f->add_or(); + f->add_child(f1); + } + break; + case Comb_And: + case Comb_AndNot: + if(f1->node_type() == Op_And) { + f->add_child(f1); + f = f1; + } + else { + f = f->add_and(); + f->add_child(f1); + } + break; + default: + assert(0 && "Invalid combine type in MapAndCombineRel2"); + } + + Formula *c2; + if (ctype==Comb_AndNot) { + c2 = f->add_not(); + } + else { + c2 = f; + } + c2->add_child(f2); + + skip_finalization_check--; /* Set this back for return */ + R3.finalize(); + + if (returnAsSet || + (R1.is_set() && R2.is_set() && R3.n_inp() >= 0 && R3.n_out() == 0)){ + R3.markAsSet(); + R3.invalidate_leading_info(); + } + R1 = Relation(); + R2 = Relation(); + return R3; +} + + +// +// Scramble each relation's variables and merge these relations +// together. Support variable mapping to and from existentials. +// Unspecified variables in mapping are mapped to themselves by +// default. It intends to replace MapRel1 and MapAndCombineRel2 +// functions (the time saved by grafting formula tree might be +// neglegible when compared to the simplification cost). +// +Relation merge_rels(Tuple<Relation> &R, const Tuple<std::map<Variable_ID, std::pair<Var_Kind, int> > > &mapping, const Tuple<bool> &inverse, Combine_Type ctype, int number_input, int number_output) { + const int m = R.size(); + assert(mapping.size() == m && inverse.size() == m); + // skip_set_checks++; + + // if new relation's arity is not given, calculate it on demand + if (number_input == -1) { + number_input = 0; + for (int i = 1; i <= m; i++) { + for (int j = R[i].n_inp(); j >= 1; j--) { + Variable_ID v = R[i].input_var(j); + std::map<Variable_ID, std::pair<Var_Kind, int> >::const_iterator p = mapping[i].find(v); + if (p == mapping[i].end()) { + number_input = j; + break; + } + } + + for (std::map<Variable_ID, std::pair<Var_Kind, int> >::const_iterator j = mapping[i].begin(); j != mapping[i].end(); j++) { + if ((*j).second.first == Input_Var || (*j).second.first == Set_Var) + number_input = max(number_input, (*j).second.second); + } + } + } + + if (number_output == -1) { + number_output = 0; + for (int i = 1; i <= m; i++) { + for (int j = R[i].n_out(); j >= 1; j--) { + Variable_ID v = R[i].output_var(j); + std::map<Variable_ID, std::pair<Var_Kind, int> >::const_iterator p = mapping[i].find(v); + if (p == mapping[i].end()) { + number_output = j; + break; + } + } + for (std::map<Variable_ID, std::pair<Var_Kind, int> >::const_iterator j = mapping[i].begin(); j != mapping[i].end(); j++) { + if ((*j).second.first == Output_Var) + number_output = max(number_output, (*j).second.second); + } + } + } + + Relation R2(number_input, number_output); + F_Exists *fe = R2.add_exists(); + Formula *f_root; + switch (ctype) { + case Comb_And: + f_root = fe->add_and(); + break; + case Comb_Or: + f_root = fe->add_or(); + break; + default: + assert(0); // unsupported merge type + } + + std::map<int, Variable_ID> seen_exists_by_num; + std::map<Variable_ID, Variable_ID> seen_exists_by_id; + + for (int i = 1; i <= m; i++) { + F_Or *fo; + if (inverse[i]) + fo = f_root->add_not()->add_or(); + else + fo = f_root->add_or(); + + for (DNF_Iterator di(R[i].query_DNF()); di; di++) { + F_And *f = fo->add_and(); + + for (GEQ_Iterator gi(*di); gi; gi++) { + GEQ_Handle h = f->add_GEQ(); + for (Constr_Vars_Iter cvi(*gi); cvi; cvi++) { + Variable_ID v = cvi.curr_var(); + std::map<Variable_ID, std::pair<Var_Kind, int> >::const_iterator p = mapping[i].find(v); + if (p == mapping[i].end()) { + switch (v->kind()) { + // case Set_Var: + case Input_Var: { + int pos = v->get_position(); + h.update_coef(R2.input_var(pos), cvi.curr_coef()); + break; + } + case Output_Var: { + int pos = v->get_position(); + h.update_coef(R2.output_var(pos), cvi.curr_coef()); + break; + } + case Exists_Var: + case Wildcard_Var: { + std::map<Variable_ID, Variable_ID>::iterator p2 = seen_exists_by_id.find(cvi.curr_var()); + Variable_ID e; + if (p2 == seen_exists_by_id.end()) { + e = fe->declare(); + seen_exists_by_id[cvi.curr_var()] = e; + } + else + e = (*p2).second; + h.update_coef(e, cvi.curr_coef()); + break; + } + case Global_Var: { + Global_Var_ID g = v->get_global_var(); + Variable_ID v2; + if (g->arity() == 0) + v2 = R2.get_local(g); + else + v2 = R2.get_local(g, v->function_of()); + h.update_coef(v2, cvi.curr_coef()); + break; + } + default: + assert(0); // shouldn't happen if input relations are simplified + } + } + else { + switch ((*p).second.first) { + // case Set_Var: + case Input_Var: { + int pos = (*p).second.second; + h.update_coef(R2.input_var(pos), cvi.curr_coef()); + break; + } + case Output_Var: { + int pos = (*p).second.second; + h.update_coef(R2.output_var(pos), cvi.curr_coef()); + break; + } + case Exists_Var: + case Wildcard_Var: { + int pos = (*p).second.second; + std::map<int, Variable_ID>::iterator p2 = seen_exists_by_num.find(pos); + Variable_ID e; + if (p2 == seen_exists_by_num.end()) { + e = fe->declare(); + seen_exists_by_num[pos] = e; + } + else + e = (*p2).second; + h.update_coef(e, cvi.curr_coef()); + break; + } + default: + assert(0); // mapped to unsupported variable type + } + } + } + h.update_const((*gi).get_const()); + } + + for (EQ_Iterator ei(*di); ei; ei++) { + EQ_Handle h = f->add_EQ(); + for (Constr_Vars_Iter cvi(*ei); cvi; cvi++) { + Variable_ID v = cvi.curr_var(); + std::map<Variable_ID, std::pair<Var_Kind, int> >::const_iterator p = mapping[i].find(v); + if (p == mapping[i].end()) { + switch (v->kind()) { + // case Set_Var: + case Input_Var: { + int pos = v->get_position(); + h.update_coef(R2.input_var(pos), cvi.curr_coef()); + break; + } + case Output_Var: { + int pos = v->get_position(); + h.update_coef(R2.output_var(pos), cvi.curr_coef()); + break; + } + case Exists_Var: + case Wildcard_Var: { + std::map<Variable_ID, Variable_ID>::iterator p2 = seen_exists_by_id.find(v); + Variable_ID e; + if (p2 == seen_exists_by_id.end()) { + e = fe->declare(); + seen_exists_by_id[v] = e; + } + else + e = (*p2).second; + h.update_coef(e, cvi.curr_coef()); + break; + } + case Global_Var: { + Global_Var_ID g = v->get_global_var(); + Variable_ID v2; + if (g->arity() == 0) + v2 = R2.get_local(g); + else + v2 = R2.get_local(g, v->function_of()); + h.update_coef(v2, cvi.curr_coef()); + break; + } + default: + assert(0); // shouldn't happen if input relations are simplified + } + } + else { + switch ((*p).second.first) { + // case Set_Var: + case Input_Var: { + int pos = (*p).second.second; + h.update_coef(R2.input_var(pos), cvi.curr_coef()); + break; + } + case Output_Var: { + int pos = (*p).second.second; + h.update_coef(R2.output_var(pos), cvi.curr_coef()); + break; + } + case Exists_Var: + case Wildcard_Var: { + int pos = (*p).second.second; + std::map<int, Variable_ID>::iterator p2 = seen_exists_by_num.find(pos); + Variable_ID e; + if (p2 == seen_exists_by_num.end()) { + e = fe->declare(); + seen_exists_by_num[pos] = e; + } + else + e = (*p2).second; + h.update_coef(e, cvi.curr_coef()); + break; + } + default: + assert(0); // mapped to unsupported variable type + } + } + } + h.update_const((*ei).get_const()); + } + } + } + + // skip_set_checks--; + + if (number_output == 0) { + R2.markAsSet(); + // R2.invalidate_leading_info(); + } + + return R2; +} + +} // namespace |