diff options
Diffstat (limited to 'omegalib/omega')
89 files changed, 30382 insertions, 0 deletions
diff --git a/omegalib/omega/CMakeLists.txt b/omegalib/omega/CMakeLists.txt new file mode 100644 index 0000000..5bc7e0b --- /dev/null +++ b/omegalib/omega/CMakeLists.txt @@ -0,0 +1,69 @@ +set(BASIC_SRC + src/basic/ConstString.cc + src/basic/Link.cc + ) + +set(OC_SRC + src/omega_core/oc.cc + src/omega_core/oc_eq.cc + src/omega_core/oc_exp_kill.cc + src/omega_core/oc_global.cc + src/omega_core/oc_print.cc + src/omega_core/oc_problems.cc + src/omega_core/oc_simple.cc + src/omega_core/oc_solve.cc + src/omega_core/oc_query.cc + src/omega_core/oc_quick_kill.cc + src/omega_core/oc_util.cc + ) + +set(PRES_SRC + src/pres_beaut.cc + src/pres_cnstr.cc + src/pres_col.cc + src/pres_conj.cc + src/pres_decl.cc + src/pres_dnf.cc + src/pres_form.cc + src/pres_gen.cc + src/pres_logic.cc + src/pres_print.cc + src/pres_rear.cc + src/pres_quant.cc + src/pres_subs.cc + src/pres_var.cc + ) + +set(REL_SRC + src/evac.cc + src/farkas.cc + src/hull_legacy.cc + src/hull_simple.cc + src/Relation.cc + src/Relations.cc + src/RelBody.cc + src/RelVar.cc + ) + +set(FANCY_SRC + src/closure.cc + src/reach.cc + ) + +include_directories( + include + ) + +add_library(omega + ${BASIC_SRC} + ${OC_SRC} + ${PRES_SRC} + ${REL_SRC} + ${FANCY_SRC} + ) + +install(TARGETS omega + ARCHIVE DESTINATION lib) + +install(DIRECTORY include + DESTINATION .) diff --git a/omegalib/omega/doc/interface.pdf b/omegalib/omega/doc/interface.pdf Binary files differnew file mode 100644 index 0000000..7f918ae --- /dev/null +++ b/omegalib/omega/doc/interface.pdf diff --git a/omegalib/omega/include/basic/Bag.c b/omegalib/omega/include/basic/Bag.c new file mode 100644 index 0000000..c3084c1 --- /dev/null +++ b/omegalib/omega/include/basic/Bag.c @@ -0,0 +1,329 @@ +/**************************************************************** + * * + * Collection constructors, desctructors, assignments * + * * + ****************************************************************/ + +#include <assert.h> + +namespace omega { + +template<class T> Bag<T>::Bag() { + contents = new List_Element <T>; + contents->tail = 0; + } +template<class T> Bag<T>::~Bag() { + delete contents; + } + +template<class T> Ordered_Bag<T>::Ordered_Bag() {} + +template<class T> Set<T>::Set() {} + +template<class T> Bag<T>::Bag(const Bag<T> &L) { + contents = new List_Element<T>(*L.contents); + } + +template<class T> Bag<T> & Bag<T>::operator=(const Bag<T> &L) { + if (this != &L) { + delete contents; + contents = new List_Element<T>(*L.contents); + } + return *this; + } + + + +template<class T> Set<T>::Set(T e) { + assert(this->contents); + this->contents->tail = new List_Element<T>(e, 0); + } + + +/**************************************************************** + * * + * Misc. simple Collection operations * + * * + ****************************************************************/ + +template<class T> bool Bag<T>::empty() const { + return contents->tail == 0; + } + +template<class T> Iterator<T> *Bag<T>::new_iterator() + { + return new List_Element_Iterator<T>(contents->tail); + } + + +template<class T> void Bag<T>::clear() { + if (contents->tail) delete contents->tail; + contents->tail = 0; + } + +template<class T> int Bag<T>::size() const { + int i = 0; + List_Element<T> * p = contents->tail; + while (p) { + p = p->tail; + i++; + }; + return i; + } + + +/**************************************************************** + * * + * Collection/Element operations (e.g. insert, contains) * + * * + ****************************************************************/ + +template<class T> void Bag<T>::remove(T e) { + List_Element<T> * p = contents; + while (p->tail && p->tail->head != e) p = p->tail; + if (p->tail && p->tail->head == e) { + List_Element<T> * q = p->tail; + p->tail = q->tail; + q->tail = 0; + delete q; + } + } + +template<class T> T Bag<T>::extract() { + List_Element<T> * p = contents->tail; + T e = p->head; + contents->tail = p->tail; + p->tail = 0; + delete p; + return e; + } + + +template<class T> void Bag<T>::insert(T e) { + List_Element<T> * q = new List_Element<T>(e,contents->tail); + contents->tail = q; + } + +template<class T> void Ordered_Bag<T>::insert(T e) { + List_Element<T> * p = this->contents; + while (p->tail && p->tail->head < e) p = p->tail; + if (!p->tail || p->tail->head != e) { + List_Element<T> * q = new List_Element<T>(e,p->tail); + p->tail = q; + } + } + + +template<class T> bool Bag<T>::contains(T e) const { + List_Element<T> * p = contents; + while (p->tail && p->tail->head != e) p = p->tail; + return (p->tail && p->tail->head == e); + } + +template<class T> bool Ordered_Bag<T>::contains(T e) const { + List_Element<T> * p = this->contents; + while (p->tail && p->tail->head < e) p = p->tail; + return (p->tail && p->tail->head == e); + } + + +template<class T> bool Set<T>::contains (const Set<T>& b) const { + List_Element<T> * p = this->contents; + List_Element<T> * q = b.contents; + do { + /* consume matched elements in p and q */ + p = p->tail; + q = q->tail; + if (!q) return 1; /* no more elements to match */ + if (!p) return 0; /* nothing left in p to match with */ + if (q->head < p->head) { + /* nothing smaller than + p->head left in p, so q->head + can't be matched */ + return 0; + }; + while (p && p->head < q->head) { + /* toss away some elements from p */ + p = p->tail; + } + if (!p || q->head < p->head) return 0; + } while (q); + + return 1; + } + + + +/**************************************************************** + * * + * Collection/Collection operations (e.g. |=) * + * * + ****************************************************************/ + +template<class T> void Bag<T>::operator |= (const Bag<T> & b) { + assert(this != &b); + List_Element<T> * q = b.contents->tail; + + while (q) { + List_Element<T> * r = new List_Element<T>(q->head,contents->tail); + contents->tail = r; + q = q->tail; + } + } + +template<class T> void Ordered_Bag<T>::operator |= (const Ordered_Bag<T> & b) { + if (this == &b) return; + List_Element<T> * p = this->contents; + List_Element<T> * q = b.contents->tail; + + while (q) { + while (p->tail && p->tail->head < q->head) p = p->tail; + List_Element<T> * r = new List_Element<T>(q->head,p->tail); + p->tail = r; + q = q->tail; + } + } + +template<class T> void Ordered_Bag<T>::operator |= (const Bag<T> & b) { + Ordered_Bag<T> tmp; + for (List_Element<T> *p = b.contents; p; p=p->tail) { + tmp.insert(p->head); + } + *this |= tmp; +} + +template<class T> void Set<T>::operator |= (const Set<T> & b) { + if (this == &b) return; + List_Element<T> * p = this->contents; + List_Element<T> * q = b.contents->tail; + + while (q) { + while (p->tail && p->tail->head < q->head) p = p->tail; + if (!p->tail || p->tail->head != q->head) { + List_Element<T> * r = new List_Element<T>(q->head,p->tail); + p->tail = r; + } + q = q->tail; + } + } + +template<class T> void Set<T>::operator |= (const Ordered_Bag<T> & b) { + Set<T> tmp; + for (List_Element<T> *p = b.contents; p; p=p->tail) { + tmp.insert(p->head); + } + *this |= tmp; +} + +template<class T> void Set<T>::operator |= (const Bag<T> & b) { + Set<T> tmp; + for (List_Element<T> *p = b.contents; p; p=p->tail) { + tmp.insert(p->head); + } + *this |= tmp; +} + + + +// delete items also in b +template<class T> void Set<T>::operator -= (const Set<T> & b) { + if (this == &b) { + this->clear(); + return; + } + List_Element<T> * p = this->contents; + List_Element<T> * q = b.contents->tail; + + while (q) { + while (p->tail && p->tail->head < q->head) p = p->tail; + if (p->tail && p->tail->head == q->head) { + List_Element<T> * r = p->tail; + p->tail = r->tail; + r->tail = 0; + delete r; + } + q = q->tail; + } + } + + +// delete items not in b +template<class T> void Set<T>::operator &= (const Set<T> & b) + { + if (this == &b) return; + List_Element<T> * p = this->contents; + List_Element<T> * q = b.contents->tail; + + while (q) { + while (p->tail && p->tail->head < q->head) { + List_Element<T> * r = p->tail; + p->tail = r->tail; + r->tail = 0; + delete r; + }; + if (p->tail && p->tail->head == q->head) { + /* allow p->tail->head into the result */ + p = p->tail; + } + /* q->head has matched anything it is going to match */ + q = q->tail; + } + if (p->tail) { + delete p->tail; + p->tail = 0; + }; + + } + + +template<class T> bool Set<T>::operator & (const Set<T>& b) const { + List_Element<T> * p = this->contents; + List_Element<T> * q = b.contents; + do { + p = p->tail; + q = q->tail; + while (p && q && p->head != q->head) { + while (p && p->head < q->head) p = p->tail; + while (p && q && q->head < p->head) q = q->tail; + }; + if (p && q && p->head == q->head) return 1; + } while (p && q); + + return 0; + } + + +template<class T> bool Ordered_Bag<T>::operator == (const Ordered_Bag<T>& b) const { + List_Element<T> * p = this->contents; + List_Element<T> * q = b.contents; + while (1) { + p = p->tail; + q = q->tail; + if (!p && !q) return 1; + if (!p || !q) return 0; + if (p->head != q->head) return 0; + }; + + } + +template<class T> bool Ordered_Bag<T>::operator != (const Ordered_Bag<T>& b) const { + return !(*this == b); + } + +template<class T> bool Ordered_Bag<T>::operator < (const Ordered_Bag<T>& b) const { + List_Element<T> * p = this->contents; + List_Element<T> * q = b.contents; + while (1) { + p = p->tail; + q = q->tail; + if (!p && !q) return 0; + if (!p) return 1; + if (!q) return 0; + if (p->head < q->head) return 1; + if (q->head < p->head) return 0; + }; + + return 1; + } + +} // namespace diff --git a/omegalib/omega/include/basic/Bag.h b/omegalib/omega/include/basic/Bag.h new file mode 100644 index 0000000..42285d0 --- /dev/null +++ b/omegalib/omega/include/basic/Bag.h @@ -0,0 +1,78 @@ +#if ! defined _Bag_h +#define _Bag_h 1 + +#include <stdio.h> +#include <basic/Iterator.h> +#include <basic/Collection.h> +#include <basic/Link.h> + +namespace omega { + +template<class T> class Bag : public Collection<T> { +public: +virtual ~Bag(); + Bag(); + Bag(const Bag<T>&); + Bag & operator=(const Bag<T>&); +virtual void operator |= (const Bag<T> & b); // add elements in b + Iterator<T> *new_iterator(); + bool empty() const; + void remove(T); +virtual void insert(T); + void clear(); +virtual bool contains(T) const; + int size() const; + T extract(); +// protected: breaks g++ 261 + List_Element<T>* contents; +}; + + +template<class T> class Ordered_Bag : public Bag<T> { +public: + Ordered_Bag(); +// virtual ~Ordered_Bag(); + Ordered_Bag(const Ordered_Bag<T>& B) : Bag<T>(B) {} + void insert(T); +virtual void operator |= (const Ordered_Bag<T> & b); // add elements in b + void operator |= (const Bag<T> & b); + bool contains(T) const; + bool operator == (const Ordered_Bag<T>&) const; + bool operator != (const Ordered_Bag<T>&) const; + bool operator < (const Ordered_Bag<T>&) const; +}; + +template <class T> class Set : public Ordered_Bag <T> { +public: + Set(); +// virtual ~Set(); + Set(T); + Set(const Set<T>& S) : Ordered_Bag<T>(S) {} + + bool contains (const Set<T>& b) const; + bool contains (T t) const { return Ordered_Bag<T>::contains(t); } + // the above makes "standard" C++ happy + +virtual void operator |= (const Set<T> & b); // add elements in b + void operator |= (const Ordered_Bag<T> & b); + void operator |= (const Bag<T> & b); + + void operator -= (const Set<T> & b); // delete items also in b + void operator &= (const Set<T> & b); // delete items not in b + bool operator & (const Set<T> &) const; // check for elements in common +}; + +} // namespace + +#if ! defined DONT_INCLUDE_TEMPLATE_CODE +#include <basic/Bag.c> +#endif + +#define instantiate_Bag(T) template class Bag<T>; \ + instantiate_List_Element(T); +#define instantiate_Ordered_Bag(T) template class Ordered_Bag<T>; \ + instantiate_Bag(T) +#define instantiate_Set(T) template class Set<T>; \ + instantiate_Ordered_Bag(T) + +#endif diff --git a/omegalib/omega/include/basic/BoolSet.h b/omegalib/omega/include/basic/BoolSet.h new file mode 100755 index 0000000..dc9ef83 --- /dev/null +++ b/omegalib/omega/include/basic/BoolSet.h @@ -0,0 +1,637 @@ +/***************************************************************************** + Copyright (C) 2009-2011 Chun Chen + All Rights Reserved. + + Purpose: + BoolSet class, used as a set of integers from 0..n-1 where n is a very + small integer. + + Notes: + Set operands of binary operations can be of different sizes, missing + elements are treated as false. + + History: + 03/30/09 Created by Chun Chen. + 03/26/11 iterator added, -chun +*****************************************************************************/ + +#ifndef _BOOLSET_H +#define _BOOLSET_H + +#include <vector> +#include <iostream> +#include <assert.h> +#include <stdexcept> +#include <iterator> + +namespace omega { + +template<typename T = unsigned int> +class BoolSet { +protected: + unsigned int size_; + std::vector<T> set_; + +public: + BoolSet(unsigned int size = 0); + ~BoolSet() {} + + void set(unsigned int); + void unset(unsigned int); + void set_all(); + void unset_all(); + bool get(unsigned int) const; + unsigned int size() const {return size_;} + unsigned int num_elem() const; + bool imply(const BoolSet<T> &) const; + bool empty() const; + void dump() const; + + BoolSet<T> &operator|=(const BoolSet<T> &); + BoolSet<T> &operator&=(const BoolSet<T> &); + BoolSet<T> &operator-=(const BoolSet<T> &); + + template<typename TT> friend BoolSet<TT> operator|(const BoolSet<TT> &, const BoolSet<TT> &); // union + template<typename TT> friend BoolSet<TT> operator&(const BoolSet<TT> &, const BoolSet<TT> &); // intersection + template<typename TT> friend BoolSet<TT> operator-(const BoolSet<TT> &, const BoolSet<TT> &); // difference + template<typename TT> friend BoolSet<TT> operator~(const BoolSet<TT> &); // complement + template<typename TT> friend bool operator==(const BoolSet<TT> &, const BoolSet<TT> &); + template<typename TT> friend bool operator!=(const BoolSet<TT> &, const BoolSet<TT> &); + template<typename TT> friend std::ostream& operator<<(std::ostream &, const BoolSet<TT> &); + template<typename TT> friend bool operator<(const BoolSet<TT> &, const BoolSet<TT> &); + +// iterator related +public: + class iterator; + class const_iterator; + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; +}; + + +template<typename T> +BoolSet<T>::BoolSet(unsigned int size) { + assert(size >= 0); + size_ = size; + unsigned int n = size / (sizeof(T)*8); + unsigned int r = size % (sizeof(T)*8); + if (r != 0) + n++; + set_ = std::vector<T>(n, static_cast<T>(0)); +} + + +template<typename T> +void BoolSet<T>::set(unsigned int i) { + assert(i < size_ && i >= 0); + unsigned int n = i / (sizeof(T)*8); + unsigned int r = i % (sizeof(T)*8); + + T t = static_cast<T>(1) << r; + set_[n] |= t; +} + + +template<typename T> +void BoolSet<T>::unset(unsigned int i) { + assert(i < size_ && i >= 0); + unsigned int n = i / (sizeof(T)*8); + unsigned int r = i % (sizeof(T)*8); + + T t = static_cast<T>(1) << r; + t = ~t; + set_[n] &= t; +} + + +template<typename T> +void BoolSet<T>::set_all() { + unsigned int r = size_ % (sizeof(T)*8); + if (r == 0) { + for (unsigned int i = 0; i < set_.size(); i++) + set_[i] = ~static_cast<T>(0); + } + else { + for (unsigned int i = 0; i < set_.size()-1; i++) + set_[i] = ~static_cast<T>(0); + set_[set_.size()-1] = static_cast<T>(0); + T t = static_cast<T>(1); + for (unsigned int i = 0; i < r; i++) { + set_[set_.size()-1] |= t; + t = t<<1; + } + } +} + + +template<typename T> +void BoolSet<T>::unset_all() { + for (unsigned int i = 0; i < set_.size(); i++) + set_[i] = static_cast<T>(0); +} + + +template<typename T> +bool BoolSet<T>::get(unsigned int i) const { + assert(i < size_ && i >= 0); + unsigned int n = i / (sizeof(T)*8); + unsigned int r = i % (sizeof(T)*8); + + T t = static_cast<T>(1) << r; + t = set_[n] & t; + if (t) + return true; + else + return false; +} + + +template<typename T> +unsigned int BoolSet<T>::num_elem() const { + unsigned int n = size_; + unsigned int c = 0; + unsigned int p = 0; + while (n != 0) { + unsigned int m; + if (n >= sizeof(T)*8) { + m = sizeof(T)*8; + n -= sizeof(T)*8; + } + else { + m = n; + n = 0; + } + + T v = set_[p++]; + if (v != static_cast<T>(0)) { + for (unsigned int i = 0; i < m; i++) { + if (v & static_cast<T>(1)) + c++; + v >>= 1; + } + } + } + + return c; +} + + +template<typename T> +bool BoolSet<T>::imply(const BoolSet<T> &b) const { + if (size_ >= b.size_) { + for (unsigned int i = 0; i < b.set_.size(); i++) + if ((set_[i] & b.set_[i]) != b.set_[i]) + return false; + } + else { + for (unsigned int i = 0; i < set_.size(); i++) + if ((set_[i] & b.set_[i]) != b.set_[i]) + return false; + for (unsigned int i = set_.size(); i < b.set_.size(); i++) + if (b.set_[i] != static_cast<T>(0)) + return false; + } + + return true; +} + + +template<typename T> +bool BoolSet<T>::empty() const { + for (int i = 0; i < set_.size(); i++) + if (set_[i] != static_cast<T>(0)) + return false; + + return true; +} + + +template<typename T> +void BoolSet<T>::dump() const { + int j = 1; + for (unsigned int i = 0; i < size(); i++) { + if (get(i)) + std::cout << '1'; + else + std::cout << '0'; + if (j%10 == 0 && i != size() - 1) { + std::cout << ' '; + j = 1; + } + else + j++; + } + std::cout << std::endl; + std::cout.flush(); +} + + +template<typename T> +BoolSet<T> operator|(const BoolSet<T> &a, const BoolSet<T> &b) { + if (a.size_ >= b.size_) { + BoolSet<T> c = a; + for (unsigned int i = 0; i < b.set_.size(); i++) + c.set_[i] |= b.set_[i]; + return c; + } + else { + BoolSet<T> c = b; + for (unsigned int i = 0; i < a.set_.size(); i++) + c.set_[i] |= a.set_[i]; + return c; + } +} + + +template<typename T> +BoolSet<T> operator&(const BoolSet<T> &a, const BoolSet<T> &b) { + if (a.size_ >= b.size_) { + BoolSet<T> c = a; + for (unsigned int i = 0; i < b.set_.size(); i++) + c.set_[i] &= b.set_[i]; + for (unsigned int i = b.set_.size(); i < a.set_.size(); i++) + c.set_[i] = static_cast<T>(0); + return c; + } + else { + BoolSet<T> c = b; + for (unsigned int i = 0; i < a.set_.size(); i++) + c.set_[i] &= a.set_[i]; + for (unsigned int i = a.set_.size(); i < b.set_.size(); i++) + c.set_[i] = static_cast<T>(0); + return c; + } +} + + +template<typename T> +BoolSet<T> operator-(const BoolSet<T> &a, const BoolSet<T> &b) { + BoolSet<T> c(a.size_); + + int sz = a.set_.size(); + if (sz > b.set_.size()) + sz = b.set_.size(); + for (int i = 0; i < sz; i++) + c.set_[i] = a.set_[i] ^ (a.set_[i] & b.set_[i]); + for (int i = sz; i < a.set_.size(); i++) + c.set_[i] = a.set_[i]; + + return c; +} + + +template<typename T> +BoolSet<T> operator~(const BoolSet<T> &b) { + unsigned int r = b.size_ % (sizeof(T)*8); + BoolSet<T> a(b.size_); + for (unsigned int i = 0; i < b.set_.size(); i++) + a.set_[i] = ~b.set_[i]; + + if (r != 0) { + T t = static_cast<T>(1); + for (unsigned int i = 1; i < r; i++) + t = (t << 1) | static_cast<T>(1); + a.set_[a.set_.size()-1] &= t; + } + return a; +} + + +template<typename T> +bool operator==(const BoolSet<T> &a, const BoolSet<T> &b) { + return (a.size_ == b.size_) && (a.set_ == b.set_); +} + + +template<typename T> +bool operator!=(const BoolSet<T> &a, const BoolSet<T> &b) { + return !(a == b); +} + + + +template<typename T> +BoolSet<T> & BoolSet<T>::operator|=(const BoolSet<T> &b) { + *this = *this | b; + return *this; +} + + +template<typename T> +BoolSet<T> & BoolSet<T>::operator&=(const BoolSet<T> &b) { + *this = *this & b; + return *this; +} + + +template<typename T> +BoolSet<T> & BoolSet<T>::operator-=(const BoolSet<T> &b) { + *this = *this - b; + return *this; +} + + +template<typename T> +std::ostream& operator<<(std::ostream &os, const BoolSet<T> &b) { + os << '{'; + for (typename BoolSet<T>::const_iterator i = b.begin(); i != b.end(); i++) { + os << *i; + if (i+1 != b.end()) + os << ','; + } + os << '}'; + + return os; +} + + +template<typename T> +bool operator<(const BoolSet<T> &a, const BoolSet<T> &b) { + unsigned int t1, t2; + t1 = a.num_elem(); + t2 = b.num_elem(); + if (t1 < t2) + return true; + else if (t1 > t2) + return false; + else { + t1 = a.size(); + t2 = b.size(); + if (t1 < t2) + return true; + else if (t1 > t2) + return false; + else + for (unsigned int i = 0; i < a.set_.size(); i++) + if (a.set_[i] < b.set_[i]) + return true; + } + return false; +} + + +// +// iterator for BoolSet +// + +template<typename T> +typename BoolSet<T>::iterator BoolSet<T>::begin() { + typename BoolSet<T>::iterator it(this, 0); + if (size_ == 0) + return it; + else if (set_[0] & static_cast<T>(1)) + return it; + else + return ++it; +} + + +template<typename T> +typename BoolSet<T>::iterator BoolSet<T>::end() { + return typename BoolSet<T>::iterator(this, size_); +} + + +template<typename T> +typename BoolSet<T>::const_iterator BoolSet<T>::begin() const { + typename BoolSet<T>::const_iterator it(this, 0); + if (size_ == 0) + return it; + else if (set_[0] & static_cast<T>(1)) + return it; + else + return ++it; +} + + +template<typename T> +typename BoolSet<T>::const_iterator BoolSet<T>::end() const { + return typename BoolSet<T>::const_iterator(this, size_); +} + + +template<typename T> +class BoolSet<T>::iterator: public std::iterator<std::forward_iterator_tag, T> { +protected: + BoolSet<T> *s_; + unsigned int pos_; + +protected: + iterator(BoolSet<T> *s, unsigned int pos) { s_ = s; pos_ = pos; } + +public: + ~iterator() {} + + typename BoolSet<T>::iterator &operator++(); + typename BoolSet<T>::iterator operator++(int); + typename BoolSet<T>::iterator operator+(int) const; + unsigned int operator*() const; + bool operator==(const BoolSet<T>::iterator &) const; + bool operator!=(const BoolSet<T>::iterator &) const; + operator typename BoolSet<T>::const_iterator(); + + friend class BoolSet<T>; +}; + + +template<typename T> +typename BoolSet<T>::iterator &BoolSet<T>::iterator::operator++() { + assert(pos_ < s_->size_); + + pos_++; + unsigned int n = pos_ / (sizeof(T)*8); + unsigned int r = pos_ % (sizeof(T)*8); + while (pos_ < s_->size_) { + if (s_->set_[n] == static_cast<T>(0)) { + pos_ += sizeof(T)*8-r; + n++; + r = 0; + if (pos_ >= s_->size_) + break; + } + + if (r == 0) { + while (pos_ < s_->size_) { + if (s_->set_[n] == static_cast<T>(0)) { + pos_ += sizeof(T)*8; + n++; + } + else + break; + } + if (pos_ >= s_->size_) + break; + } + + for (unsigned int i = r; i < sizeof(T)*8; i++) + if (s_->set_[n] & static_cast<T>(1) << i) { + pos_ = pos_+i-r; + return *this; + } + + pos_ += sizeof(T)*8-r; + n++; + r = 0; + } + + pos_ = s_->size_; + return *this; +} + + +template<typename T> +typename BoolSet<T>::iterator BoolSet<T>::iterator::operator++(int) { + typename BoolSet<T>::iterator it(*this); + ++(*this); + return it; +} + + +template<typename T> +typename BoolSet<T>::iterator BoolSet<T>::iterator::operator+(int n) const { + assert(n >= 0); + typename BoolSet<T>::iterator it(*this); + while (n > 0) { + ++it; + --n; + } + return it; +} + + +template<typename T> +unsigned int BoolSet<T>::iterator::operator*() const { + assert(pos_ < s_->size_); + return pos_; +} + + +template<typename T> +bool BoolSet<T>::iterator::operator==(const BoolSet<T>::iterator &other) const { + return s_ == other.s_ && pos_ == other.pos_; +} + + +template<typename T> +bool BoolSet<T>::iterator::operator!=(const BoolSet<T>::iterator &other) const { + return !((*this) == other); +} + + +template<typename T> +BoolSet<T>::iterator::operator typename BoolSet<T>::const_iterator() { + return BoolSet<T>::const_iterator(s_, pos_); +} + + +template<typename T> +class BoolSet<T>::const_iterator: public std::iterator<std::forward_iterator_tag, T> { +protected: + const BoolSet<T> *s_; + unsigned int pos_; + +protected: + const_iterator(const BoolSet<T> *s, unsigned int pos) { s_ = s; pos_ = pos; } + +public: + ~const_iterator() {} + + typename BoolSet<T>::const_iterator &operator++(); + typename BoolSet<T>::const_iterator operator++(int); + typename BoolSet<T>::const_iterator operator+(int) const; + unsigned int operator*() const; + bool operator==(const BoolSet<T>::const_iterator &) const; + bool operator!=(const BoolSet<T>::const_iterator &) const; + + friend class BoolSet<T>; +}; + + +template<typename T> +typename BoolSet<T>::const_iterator &BoolSet<T>::const_iterator::operator++() { + assert(pos_ < s_->size_); + + pos_++; + unsigned int n = pos_ / (sizeof(T)*8); + unsigned int r = pos_ % (sizeof(T)*8); + while (pos_ < s_->size_) { + if (s_->set_[n] == static_cast<T>(0)) { + pos_ += sizeof(T)*8-r; + n++; + r = 0; + if (pos_ >= s_->size_) + break; + } + + if (r == 0) { + while (pos_ < s_->size_) { + if (s_->set_[n] == static_cast<T>(0)) { + pos_ += sizeof(T)*8; + n++; + } + else + break; + } + if (pos_ >= s_->size_) + break; + } + + for (unsigned int i = r; i < sizeof(T)*8; i++) + if (s_->set_[n] & static_cast<T>(1) << i) { + pos_ = pos_+i-r; + return *this; + } + + pos_ += sizeof(T)*8-r; + n++; + r = 0; + } + + pos_ = s_->size_; + return *this; +} + + +template<typename T> +typename BoolSet<T>::const_iterator BoolSet<T>::const_iterator::operator++(int) { + typename BoolSet<T>::const_iterator it(*this); + ++(*this); + return it; +} + + +template<typename T> +typename BoolSet<T>::const_iterator BoolSet<T>::const_iterator::operator+(int n) const { + assert(n >= 0); + typename BoolSet<T>::const_iterator it(*this); + while (n > 0) { + ++it; + --n; + } + return it; +} + + +template<typename T> +unsigned int BoolSet<T>::const_iterator::operator*() const { + assert(pos_ < s_->size_); + return pos_; +} + + +template<typename T> +bool BoolSet<T>::const_iterator::operator==(const BoolSet<T>::const_iterator &other) const { + return s_ == other.s_ && pos_ == other.pos_; +} + + +template<typename T> +bool BoolSet<T>::const_iterator::operator!=(const BoolSet<T>::const_iterator &other) const { + return !((*this) == other); +} + +} + +#endif diff --git a/omegalib/omega/include/basic/Collection.h b/omegalib/omega/include/basic/Collection.h new file mode 100644 index 0000000..c7e4eef --- /dev/null +++ b/omegalib/omega/include/basic/Collection.h @@ -0,0 +1,47 @@ +#if !defined Already_Included_Collection +#define Already_Included_Collection + +namespace omega { + +template<class T> class Iterator; +template<class T> class Any_Iterator; + + +/* + * protocol for any kind of collection + */ + +template<class T> class Collection { +public: + virtual Iterator<T> *new_iterator() = 0; + virtual Any_Iterator<T> any_iterator() { return Any_Iterator<T>(new_iterator()); } + + virtual int size() const = 0; +}; + + +/* + * protocol for collections whose elements are ordered + * by the way they are entered into the collection, and + * whose elements can be accessed by "index" + * + * note that the implementation need not be a linked list + */ + +template<class T> class Sequence : public Collection<T> { +public: + virtual const T &operator[](int) const = 0; + virtual T &operator[](int) = 0; + + virtual int index(const T &) const = 0; // Y in X --> X[X.index(Y)] == Y +}; + +} // namespace + +#define instantiate_Collection(T) template class Collection<T>; \ + instantiate_Any_Iterator(T) +#define instantiate_Sequence(T) template class Sequence<T>; \ + instantiate_Collection(T) + +#endif + diff --git a/omegalib/omega/include/basic/Collections.h b/omegalib/omega/include/basic/Collections.h new file mode 100644 index 0000000..1e68031 --- /dev/null +++ b/omegalib/omega/include/basic/Collections.h @@ -0,0 +1,12 @@ +#if !defined Already_Included_Collections +#define Already_Included_Collections + +#include <stdio.h> +#include <basic/Collection.h> +#include <basic/Iterator.h> +#include <basic/List.h> +#include <basic/Bag.h> +#include <basic/Map.h> + +#endif + diff --git a/omegalib/omega/include/basic/ConstString.h b/omegalib/omega/include/basic/ConstString.h new file mode 100644 index 0000000..5149e55 --- /dev/null +++ b/omegalib/omega/include/basic/ConstString.h @@ -0,0 +1,58 @@ +#if ! defined _Const_String_h +#define _Const_String_h 1 + +#include <string> + +namespace omega { + +// should be inside Const_String, but I can't get it to +// compile the hashTable when it is: hashTable can't be +// global, but if it and its size are static to Const_String, +// the compiler still doesn't seem to like the definition, +// or the declaration either for that matter. + +class ConstStringRep { +public: + const char *name; + int count; + ConstStringRep *nextInBucket; + ConstStringRep(const char *t); +}; + +class Const_String { +private: + ConstStringRep *rep; + void buildRep(const char *t); + +public: + Const_String(); + Const_String(const char* t); + Const_String(const std::string &s); + Const_String(const Const_String & t) {rep = t.rep;} + + operator int() const; + int null() const; + + operator const char*() const; + operator std::string() const; + int operator++(int); + int operator++(); + int operator--(int); + int operator--(); + friend int operator==(const Const_String &x, const Const_String &y); + friend int operator!=(const Const_String &x, const Const_String &y); + friend int operator<(const Const_String &x, const Const_String &y); + friend int operator >(const Const_String &x, const Const_String &y); + +}; + +#if defined SCREWED_UP_CASTING_RULES +static int operator==(const Const_String &x, const char *y) +{ return x == (Const_String) y; } +static int operator!=(const Const_String &x, const char *y) +{ return x != (Const_String) y; } +#endif + +} // namespace + +#endif diff --git a/omegalib/omega/include/basic/Dynamic_Array.c b/omegalib/omega/include/basic/Dynamic_Array.c new file mode 100644 index 0000000..0300fd8 --- /dev/null +++ b/omegalib/omega/include/basic/Dynamic_Array.c @@ -0,0 +1,219 @@ +#include <assert.h> +#include <basic/Dynamic_Array.h> + +namespace omega { + +template<class T, int d> void Dynamic_Array<T,d>::do_constr() + { +// #if ! defined SHUT_UP_ABOUT_STATEMENT_WITH_NO_EFFECT_IN_DYNAMIC_ARRAY_CREATION +// assert(d > 0); +// #endif + bounds = 0; + elements = 0; + partial = false; + } + + +template<class T> void Dynamic_Array1<T>::do_construct(int d0) + { + this->bounds = new int[1]; + this->bounds[0] = d0; + this->elements = new T [d0]; + this->partial = false; + } + +template<class T> void Dynamic_Array2<T>::do_construct(int d0, int d1) + { + this->bounds = new int[2]; + this->bounds[0] = d0; + this->bounds[1] = d1; + this->elements = new T [d0 * d1]; + this->partial = false; + } + +template<class T> void Dynamic_Array3<T>::do_construct(int d0,int d1,int d2) + { + this->bounds = new int[3]; + this->bounds[0] = d0; + this->bounds[1] = d1; + this->bounds[2] = d2; + this->elements = new T [d0 * d1 * d2]; + this->partial = false; + } + +template<class T> void Dynamic_Array4<T>::do_construct(int d0,int d1,int d2,int d3) + { + this->bounds = new int[4]; + this->bounds[0] = d0; + this->bounds[1] = d1; + this->bounds[2] = d2; + this->bounds[3] = d3; + this->elements = new T [d0 * d1 * d2 * d3]; + this->partial = false; + } + +template<class T, int d> Dynamic_Array<T,d>::Dynamic_Array() + { + do_constr(); + } + +template<class T> Dynamic_Array1<T>::Dynamic_Array1(const char *) + { + this->do_constr(); + } + +template<class T> Dynamic_Array2<T>::Dynamic_Array2(const char *,const char *) + { + this->do_constr(); + } + +template<class T> Dynamic_Array3<T>::Dynamic_Array3(const char *,const char *,const char *) + { + this->do_constr(); + } + +template<class T> Dynamic_Array4<T>::Dynamic_Array4(const char *,const char *,const char *,const char *) + { + this->do_constr(); + } + +template<class T> Dynamic_Array1<T>::Dynamic_Array1(int d0) + { + do_construct(d0); + } + +template<class T> Dynamic_Array2<T>::Dynamic_Array2(int d0, int d1) + { + do_construct(d0, d1); + } + +template<class T> Dynamic_Array3<T>::Dynamic_Array3(int d0,int d1,int d2) + { + do_construct(d0, d1, d2); + } + +template<class T> Dynamic_Array4<T>::Dynamic_Array4(int d0,int d1,int d2,int d3) + { + do_construct(d0, d1, d2, d3); + } + + +template<class T, int d> void Dynamic_Array<T,d>::do_destruct() + { + if (! partial) + { + delete [] bounds; + delete [] elements; + } + } + + +template<class T, int d> Dynamic_Array<T,d>::~Dynamic_Array() + { + do_destruct(); + } + + +template<class T> void Dynamic_Array1<T>::resize(int d0) + { + assert(!this->partial); + this->do_destruct(); + if (d0 == 0) + this->do_constr(); + else + do_construct(d0); + } + +template<class T> void Dynamic_Array2<T>::resize(int d0, int d1) + { + assert(!this->partial); + this->do_destruct(); + if (d0 == 0 && d1 == 0) + this->do_constr(); + else + do_construct(d0, d1); + } + +template<class T> void Dynamic_Array3<T>::resize(int d0, int d1, int d2) + { + assert(!this->partial); + this->do_destruct(); + if (d0 == 0 && d1 == 0 && d2 == 0) + this->do_constr(); + else + do_construct(d0, d1, d2); + } + +template<class T> void Dynamic_Array4<T>::resize(int d0, int d1, int d2, int d3) + { + assert(!this->partial); + this->do_destruct(); + if (d0 == 0 && d1 == 0 && d2 == 0 && d3 == 0) + this->do_constr(); + else + do_construct(d0, d1, d2, d3); + } + + +template<class T> T& Dynamic_Array1<T>::operator[](int d0) + { +#if !defined (NDEBUG) + assert(this->elements != 0 && "Trying to dereference undefined array"); + assert(0 <= d0 && d0 < this->bounds[0] && "Array subscript out of bounds"); +#endif + + return this->elements[d0]; + } + +template<class T> Dynamic_Array1<T> Dynamic_Array2<T>::operator[](int d0) + { +#if !defined (NDEBUG) + assert(this->elements != 0 && "Trying to dereference undefined array"); + assert(0 <= d0 && d0 < this->bounds[0] && "Array subscript out of bounds"); +#endif + + Dynamic_Array1<T> result; + result.bounds = this->bounds+1; + result.elements = this->elements + this->bounds[1] * d0; + result.partial = true; + return result; + } + +template<class T> Dynamic_Array2<T> Dynamic_Array3<T>::operator[](int d0) + { +#if !defined (NDEBUG) + assert(this->elements != 0 && "Trying to dereference undefined array"); + assert(0 <= d0 && d0 < this->bounds[0] && "Array subscript out of bounds"); +#endif + Dynamic_Array2<T> result; + result.bounds = this->bounds+1; + result.elements = this->elements + this->bounds[1] * this->bounds[2] * d0; + result.partial = true; + return result; + } + +template<class T> Dynamic_Array3<T> Dynamic_Array4<T>::operator[](int d0) + { +#if !defined (NDEBUG) + assert(this->elements != 0 && "Trying to dereference undefined array"); + assert(0 <= d0 && d0 < this->bounds[0] && "Array subscript out of bounds"); +#endif + + Dynamic_Array3<T> result; + result.bounds = this->bounds+1; + result.elements = this->elements + this->bounds[1] * this->bounds[2] * this->bounds[3] * d0; + result.partial = true; + return result; + } + + +template<class T, int d> + Dynamic_Array<T,d>::Dynamic_Array(Dynamic_Array<T,d> &D) + { + assert(D.elements != 0 && "Trying to copy an undefined array"); + partial = true; + bounds = D.bounds; + elements = D.elements; + } + +} // namespace diff --git a/omegalib/omega/include/basic/Dynamic_Array.h b/omegalib/omega/include/basic/Dynamic_Array.h new file mode 100644 index 0000000..c0bdf12 --- /dev/null +++ b/omegalib/omega/include/basic/Dynamic_Array.h @@ -0,0 +1,103 @@ +#ifndef Already_Included_Dynamic_Array +#define Already_Included_Dynamic_Array + +namespace omega { + +template <class T> class Dynamic_Array2; +template <class T> class Dynamic_Array3; +template <class T> class Dynamic_Array4; + +template <class T, int d> class Dynamic_Array + { + public: + Dynamic_Array(Dynamic_Array<T,d> &D); + ~Dynamic_Array(); + + protected: + Dynamic_Array(); + bool partial; + int *bounds; + T *elements; + + void do_constr(); + void do_destruct(); + }; + + +template <class T> class Dynamic_Array1 : public Dynamic_Array<T,1> + { + public: + Dynamic_Array1(const char *s0 = 0); + Dynamic_Array1(int d0); + void resize(int d0); + T& operator[](int d); + + friend class Dynamic_Array2<T>; + + private: + void do_construct(int d0); + }; + + +template <class T> class Dynamic_Array2 : public Dynamic_Array<T,2> + { + public: + Dynamic_Array2(const char *s0 = 0, const char *s1 = 0); + Dynamic_Array2(int d0, int d1); + void resize(int d0, int d1); + Dynamic_Array1<T> operator[](int d); + + friend class Dynamic_Array3<T>; + + private: + void do_construct(int d0, int d1); + }; + + +template <class T> class Dynamic_Array3 : public Dynamic_Array<T,3> + { + public: + Dynamic_Array3(const char *s0 = 0, const char *s1 = 0, const char *s2 = 0); + Dynamic_Array3(int d0, int d1, int d2); + void resize(int d0, int d1, int d2); + Dynamic_Array2<T> operator[](int d); + + friend class Dynamic_Array4<T>; + + private: + void do_construct(int d0, int d1, int d2); + }; + +template <class T> class Dynamic_Array4 : public Dynamic_Array<T,4> + { + public: + Dynamic_Array4(const char *s0 = 0, const char *s1 = 0, const char *s2 = 0, const char *s3 = 0); + Dynamic_Array4(int d0, int d1, int d2, int d3); + void resize(int d0, int d1, int d2, int d3); + Dynamic_Array3<T> operator[](int d); + + private: + void do_construct(int d0, int d1, int d2, int d3); + }; + +} // namespace + +#if ! defined DONT_INCLUDE_TEMPLATE_CODE +#include <basic/Dynamic_Array.c> +#endif + +#define instantiate_Dynamic_Array1(T) template class Dynamic_Array1<T>; \ + template class Dynamic_Array<T,1>; + +#define instantiate_Dynamic_Array2(T) template class Dynamic_Array2<T>; \ + template class Dynamic_Array<T,2>; \ + instantiate_Dynamic_Array1(T); + +#define instantiate_Dynamic_Array3(T) template class Dynamic_Array3<T>; \ + template class Dynamic_Array<T,3>; \ + instantiate_Dynamic_Array2(T); + +#define instantiate_Dynamic_Array4(T) template class Dynamic_Array4<T>; \ + template class Dynamic_Array<T,4>; \ + instantiate_Dynamic_Array3(T); +#endif diff --git a/omegalib/omega/include/basic/Iterator.h b/omegalib/omega/include/basic/Iterator.h new file mode 100644 index 0000000..8975d9e --- /dev/null +++ b/omegalib/omega/include/basic/Iterator.h @@ -0,0 +1,131 @@ +/* + * Base classes for iterators, generators + * + * These don't really work yet for constant collections. + * I'm not sure how to make that happen. + */ + +#if ! defined _Iterator_h +#define _Iterator_h 1 + +#include <basic/Collection.h> + +namespace omega { + +#define foreach(x,T,S,A) do {for (omega::Any_Iterator<T> __P_##x = (S).any_iterator();__P_##x;__P_##x++) {T & x = *__P_##x; A;}} while (0) + +#define foreachSeparated(x,T,S,A,B) do {for (omega::Any_Iterator<T> __P_##x = (S).any_iterator();__P_##x;) {T & x = *__P_##x; A; __P_##x++; if (__P_##x) B;}} while (0) + +/* + * Abstract base class Iterator<type> + * Supports two styles of iteration: + * + * for ( ... initialize i (typically i = collection) ... ; i ; i++ ) + * operate_on(*i) + * + * or + * + * for ( ... initialize i ... ; i.live() ; i.next() ) + * operate_on(i.curr()) + * + * >>> IF THE COLLECTION IS CHANGED, THE ITERATOR IS NO LONGER VALID <<< + * + * For collections that are not "Sequence"s, the order in + * which the elements are returned may not be consistent. + */ + +template<class T> class Iterator { +public: + virtual const T & operator*() const = 0; + virtual T & operator*() = 0; + + virtual void operator++(int) = 0; + virtual void operator++() = 0; + + virtual bool live() const = 0; + operator bool() const { return live(); } + + const T & curr() const { return *(*this); } + T & curr() { return *(*this); } + void next() { (*this)++; } + + virtual Iterator<T> *new_copy() const = 0; + virtual ~Iterator() {} +}; + + +// A generator is like an iterator but it gives out values, +// which may or may not exist in some writable collection + +template<class T> class Generator { +public: + virtual T operator*() const = 0; + + virtual void operator++(int) = 0; + virtual void operator++() = 0; + + virtual int live() const = 0; + operator int() const { return live(); } + + const T curr() const { return *(*this); } + T curr() { return *(*this); } + void next() { (*this)++; } +}; + + + +// Delegate to any kind of iterator (on the heap) +// If created via a reference, become a copy of the iterator +// If created via a pointer, manipulate that pointer and free *p when this dies +// +// Mostly useful for Collection::iterator +// Iterator::Iterator(Collection) + + +template<class T> class Any_Iterator : public Iterator<T> { +public: + Any_Iterator(Collection<T> &c); + Any_Iterator(const Iterator<T> &i); // copy of i + + virtual ~Any_Iterator() { delete me; } + + Any_Iterator<T> &operator=(const Any_Iterator<T> &rhs) + { delete me; me = rhs.me->new_copy(); return *this; } + + const T & operator*() const { return *(*me); } + T & operator*() { return *(*me); } + void operator++(int) { (*me)++; } + void operator++() { ++(*me); } + bool live() const { return (*me).live(); } + + Iterator<T> *new_copy() const { return new Any_Iterator<T>((*me).new_copy()); } + +private: + Any_Iterator(Iterator<T> *p) // take over *p, *p MUST BE ON THE HEAP + { me = p; } + friend class Collection<T>; +#if 0 + // Couldn't make this work with g++258 + friend Any_Iterator<T> Collection<T>::any_iterator(); +#endif + Iterator<T> *me; +}; + +template <class T> inline Any_Iterator<T>::Any_Iterator(Collection<T> &c) + { + me = c.new_iterator(); + } + +template <class T> inline Any_Iterator<T>::Any_Iterator(const Iterator<T> &i) + { + me = i.new_copy(); + } + +} // namespace + +#define instantiate_Iterator(T) template class Iterator<T>; +#define instantiate_Generator(T) template class Generator<T>; +#define instantiate_Any_Iterator(T) template class Any_Iterator<T>; \ + instantiate_Iterator(T) + +#endif diff --git a/omegalib/omega/include/basic/Link.h b/omegalib/omega/include/basic/Link.h new file mode 100644 index 0000000..ede7a2b --- /dev/null +++ b/omegalib/omega/include/basic/Link.h @@ -0,0 +1,98 @@ +#if ! defined _Link_h +#define _Link_h 1 + +#include <basic/Iterator.h> +#include <stddef.h> + +namespace omega { + +// By default, if ndebug is not set, do not do free list + +#if ! defined ListElementFreeList +#if ! defined NDEBUG || defined ASSERTIONS_ANYWAY +#define ListElementFreeList 0 +#else +#define ListElementFreeList 1 +#endif +#endif + +/* + List_Element: one item in a list and the pointer to the next. + Each such object should be pointed to by either exactly one + other List_Element or by some other pointer(s), exactly one + of which will delete the List_Element. + ListElements should ONLY be allocated on the heap. + */ + +#if ListElementFreeList + // g++ 2.5.8 does not allow static data in template classes, so... + extern void *kludgy_List_Element_new(size_t size); + extern void kludgy_List_Element_delete(void *ptr, size_t size); +#endif + +template <class T> class List_Element { +public: +#if ListElementFreeList + void *operator new(size_t size) + { + return kludgy_List_Element_new(size); + } + void operator delete(void *ptr, size_t size) + { + kludgy_List_Element_delete(ptr, size); + } +#endif + + T head; + List_Element<T> *tail; + + List_Element() { + tail = 0; + } + List_Element(T h, List_Element<T> * t) { + head = h; + tail = t; + } + List_Element(const List_Element<T> & L) { + head = L.head; + if (L.tail) tail = new List_Element<T>(*L.tail); + else tail = 0; + } + List_Element & operator=(const List_Element<T> &L) { + if (this != &L) { + head = L.head; + if (tail) delete tail; + if (L.tail) tail = new List_Element<T>(*L.tail); + else tail = 0; + } + return *this; + } + virtual ~List_Element() { // virtual ensures 2nd arg of delete is right + delete tail; + } +}; + + + +template<class T> class List_Element_Iterator : public Iterator<T> { +public: + List_Element_Iterator(List_Element<T>* j) { i = j; } + virtual const T & operator*() const { return i->head; } + virtual T & operator*() { return i->head; } + virtual void operator++(int) { i = i->tail; } + virtual void operator++() { i = i->tail; } + virtual bool live() const { return i != 0; } + Iterator<T> * new_copy() const { return new List_Element_Iterator<T>(i);} + +protected: + List_Element<T> *i; +}; + +} // namespace + +#define instantiate_Only_List_Element(T) template class List_Element<T>; \ + template class List_Element_Iterator<T>; +#define instantiate_List_Element(T) instantiate_Only_List_Element(T)\ + instantiate_Collection(T) + +#endif diff --git a/omegalib/omega/include/basic/List.c b/omegalib/omega/include/basic/List.c new file mode 100644 index 0000000..f05e0de --- /dev/null +++ b/omegalib/omega/include/basic/List.c @@ -0,0 +1,149 @@ +#include <assert.h> + +namespace omega { + +template<class T> List_Iterator<T>::List_Iterator(List<T> &l) +: List_Element_Iterator<T>(l.contents) {} + +template<class T> List_Iterator<T>::List_Iterator(const List<T> &l) +: List_Element_Iterator<T>(l.contents) {} + +template<class T> List_Iterator<T>::List_Iterator() +: List_Element_Iterator<T>(0) {} + +template<class T> Iterator<T> *List<T>::new_iterator() +{ + return new List_Iterator<T>(*this); +} + +template<class T> const T &List<T>::operator[](int i) const +{ + assert(i > 0 && "Subscript out of bounds"); + List_Iterator<T> p(*this); + + while(--i > 0 && p) + p++; + + if (p) + return *p; + else + return *((T *)0); +} + +template<class T> T &List<T>::operator[](int i) +{ + assert(i > 0 && "Subscript out of bounds"); + List_Iterator<T> p(*this); + + while(--i > 0 && p) + p++; + + if (p) + return *p; + else + return *((T *)0); +} + +template<class T> int List<T>::index(const T &item) const +{ + List_Iterator<T> p(*this); + int i = 1; + + while(p && *p != item) + { + p++; + i++; + } + + if (p) + return i; + else + return 0; +} + +template<class T> int List<T>::size() const + { + int i = 0; + List_Element<T> * p = contents; + while (p) + { + p = p->tail; + i++; + } + return i; + } + +template<class T> T &List<T>::front() const + { + return contents->head; + } + +template<class T> T List<T>::remove_front() + { + List_Element<T> *frunt = contents; + contents = contents->tail; + T fruntT = frunt->head; + frunt->tail = 0; + delete frunt; + return fruntT; + } + +template<class T> void List<T>::prepend(const T &item) + { + contents = new List_Element<T>(item, contents); + } + + +template<class T> void List<T>::append(const T &item) + { + *(end()) = new List_Element<T>(item, 0); + } + +template<class T> void List<T>::ins_after(List_Iterator<T> i, + const T &item) + { +#if ! defined NDEBUG + for (List_Element<T> *e = contents; e != &(i.element()); e=e->tail) + { + assert(e); + } +#endif + i.element().tail = new List_Element<T>(item, i.element().tail); + } + +template<class T> void List<T>::del_front() + { + List_Element<T> *e = contents; + contents = contents->tail; + e->tail = 0; + delete e; + } + +template<class T> void List<T>::del_after(List_Iterator<T> i) + { +#if ! defined NDEBUG + for (List_Element<T> *e0 = contents; e0 != &(i.element()); e0=e0->tail) + { + assert(e0); + } +#endif + List_Element<T> *e = i.element().tail; + i.element().tail = e->tail; + e->tail = 0; + delete e; + } + +template<class T> void List<T>::clear() + { + delete contents; + contents = 0; + } + +template<class T> void List<T>::join(List<T> &consumed) + { + List_Element<T> *e = consumed.contents; + consumed.contents = 0; + *(end()) = e; + } + +} // namespace diff --git a/omegalib/omega/include/basic/List.h b/omegalib/omega/include/basic/List.h new file mode 100644 index 0000000..c6fc062 --- /dev/null +++ b/omegalib/omega/include/basic/List.h @@ -0,0 +1,95 @@ +#if ! defined _List_h +#define _List_h 1 + +/* + * Linked lists with an interface like a bit of libg++'s SLList class + */ + + +#if 0 +#include <basic/assert.h> /* List requires assert which needs Exit which */ +#endif /* needs List! just include assert in List.c */ +#include <stdio.h> // for NULL +#include <basic/Iterator.h> +#include <basic/Collection.h> +#include <basic/Link.h> + +namespace omega { + +template<class T> class List_Iterator; + +// +// indexing of Lists starts at 1, index == 0 means not there +// + +template<class T> class List : public Sequence<T> { +public: + List(const List<T> &l) + { contents = l.contents ? new List_Element<T>(*l.contents) : 0; } + List() { contents = 0; } + virtual ~List() { delete contents; } + + Iterator<T> *new_iterator(); + const T &operator[](int) const; + T &operator[](int); + + int index(const T &) const; + + int size() const; + int length() const { return size(); } + bool empty() const { return size() == 0; } + + T &front() const; + +// insertion/deletion on a list invalidates any iterators +// that are on/after the element added/removed + + T remove_front(); + + void prepend(const T &item); + void append(const T &item); + void ins_after(List_Iterator<T> i, const T &item); + + void del_front(); + void del_after(List_Iterator<T> i); + void clear(); + + void join(List<T> &consumed); + +private: + friend class List_Iterator<T>; + List_Element<T> **end() + { + List_Element<T> **e = &contents; + while (*e) + e = &((*e)->tail); + return e; + } + + List_Element<T> *contents; +}; + + +template<class T> class List_Iterator : public List_Element_Iterator<T> { +public: + List_Iterator(List<T> &l); + List_Iterator(const List<T> &l); + List_Iterator(); +private: + List_Element<T> &element() { return *List_Element_Iterator<T>::i; } ; + friend class List<T>; +}; + +} // namespace + +#if ! defined DONT_INCLUDE_TEMPLATE_CODE +#include <basic/List.c> +#endif + +#define instantiate_List(T) template class List<T>; \ + template class List_Iterator<T>; \ + instantiate_Only_List_Element(T) \ + instantiate_Sequence(T) + + +#endif diff --git a/omegalib/omega/include/basic/Map.c b/omegalib/omega/include/basic/Map.c new file mode 100644 index 0000000..69cc3f7 --- /dev/null +++ b/omegalib/omega/include/basic/Map.c @@ -0,0 +1,63 @@ +namespace omega { + +template<class K, class V> MapElement<K,V>:: MapElement(const MapElement<K,V>& M) { + if (M.tail) tail = new MapElement<K,V>(*M.tail); + else tail = 0; + k = M.k; + v = M.v; + } + +template<class K, class V> MapElement<K,V> & + MapElement<K,V>:: operator=(const MapElement<K,V>& M) { + if (this != &M) { + if (tail) delete tail; + if (M.tail) tail = new MapElement<K,V>(*M.tail); + else tail = 0; + k = M.k; + v = M.v; + } + return *this; + } + + + + +#if ! defined linux +template <class K, class V> Map <K,V>::Map(const V &default_value) +#else +template <class K, class V> Map <K,V>::Map(V default_value) +#endif + : _default_value(default_value) + { + contents = 0; + } + +template <class K, class V> Map <K,V>::~Map() + { + delete contents; + } + +template <class K, class V> V Map<K,V>::operator()(K k) const { + MapElement <K,V> * P = contents; + while (P) { + if (P->k == k) return P->v; + P = P->tail; + }; + return _default_value; + } + +template <class K, class V> V & Map<K,V>::operator[](K k) { + MapElement <K,V> * P = contents; + while (P) { + if (P->k == k) return P->v; + P = P->tail; + }; + P = new MapElement <K,V>; + P->k = k; + P->v = _default_value; + P->tail = contents; + contents = P; + return P->v; + } + +} // namespace diff --git a/omegalib/omega/include/basic/Map.h b/omegalib/omega/include/basic/Map.h new file mode 100644 index 0000000..f94a10c --- /dev/null +++ b/omegalib/omega/include/basic/Map.h @@ -0,0 +1,68 @@ +#if ! defined _Map_h +#define _Map_h 1 + +#include <basic/Link.h> +#include <stdio.h> // for NULL + +namespace omega { + +#define foreach_map(k,K,v,V,M,A) {for (omega::MapElementIterator<K,V> __M_##k = (M).iterator();__M_##k;__M_##k++) {K & k = *__M_##k; V & v = __M_##k.value(); A;}} + +template <class K, class V> class MapElement { +public: + K k; + V v; + MapElement<K,V> *tail; + MapElement(const MapElement<K,V>&); + MapElement() {} + MapElement & operator=(const MapElement<K,V>&); + ~MapElement() { delete tail; } +}; + +template<class K, class V> class MapElementIterator { +public: + MapElementIterator(MapElement<K,V>* j) { i = j;} + virtual const K & operator*() const { return i->k; } + virtual K & operator*() { return i->k;} + virtual const V & value() const { return i->v; } + virtual V & value() { return i->v; } + virtual void operator++(int) { i = i->tail; } + virtual void operator++() { i = i->tail; } + virtual bool live() const { return i != NULL; } + operator bool() const { return live(); } +protected: +MapElement<K,V> *i; +}; + +template <class K, class V> class Map { +public: +#if ! defined linux + Map(const V &default_value); +#else + // work around for '386 g++ on Linux + Map(V default_value); +#endif + ~Map(); + MapElementIterator<K,V> iterator() + {return MapElementIterator<K,V>(contents);} + int empty() const {return contents == NULL;} + V operator()(K) const; + V& operator[](K); +private: + MapElement<K,V> * contents; + V _default_value; +}; + +} // namespace + +#if ! defined DONT_INCLUDE_TEMPLATE_CODE +#include <basic/Map.c> +#endif + +#define instantiate_Map(T1,T2) template class Map<T1,T2>; \ + template class MapElement<T1,T2>; \ + template class MapElementIterator<T1,T2>; +#define instantiate_MapElement(T1,T2) instantiate_Map(T1,T2) +#define instantiate_MapElementIterator(T1,T2) instantiate_Map(T1,T2) + +#endif diff --git a/omegalib/omega/include/basic/Section.c b/omegalib/omega/include/basic/Section.c new file mode 100644 index 0000000..754e002 --- /dev/null +++ b/omegalib/omega/include/basic/Section.c @@ -0,0 +1,79 @@ +#include <assert.h> + +namespace omega { + +template <class T> Section<T>::Section(Sequence<T> *s, int start, int length) + { + assert(s->size() >= start-1 + length); + it = s; + _start = start; + _length = length; + } + +template <class T> Iterator<T> *Section<T>::new_iterator() + { + return new Section_Iterator<T>(*this); + } + +template <class T> const T &Section<T>::operator[](int i) const + { + assert(1 <= i && i <= size()); + return (*it)[i+(_start-1)]; + } + +template <class T> T &Section<T>::operator[](int i) + { + assert(1 <= i && i <= size()); + return (*it)[i+(_start-1)]; + } + +template <class T> int Section<T>::index(const T &var) const + { + int i; + for (i=1; i<=size(); i++) + if ((*this)[i] == var) + return i; + return 0; + } + +template <class T> int Section<T>::size() const + { + return _length; + } + + +template <class T> Section_Iterator<T>::Section_Iterator(Section<T> &sec) + { + it = sec.it->new_iterator(); + for (int i = 1; i < sec._start; i++) + (*it)++; + remaining = sec.size(); + } + + +template <class T> Section_Iterator<T>::Section_Iterator(const Section_Iterator<T> &si) : it(si.it), remaining(si.remaining) {} + + +template <class T> void Section_Iterator<T>::operator++() + { this->operator++(0); } + +template <class T> void Section_Iterator<T>::operator++(int) + { + if (remaining > 0) + { + (*it)++; + remaining--; + } + } + +template <class T> bool Section_Iterator<T>::live() const + { + return (remaining > 0); + } + +template <class T> Iterator<T> *Section_Iterator<T>::new_copy() const + { + return new Section_Iterator<T>(*this); + } + +} // namespace diff --git a/omegalib/omega/include/basic/Section.h b/omegalib/omega/include/basic/Section.h new file mode 100644 index 0000000..60821d1 --- /dev/null +++ b/omegalib/omega/include/basic/Section.h @@ -0,0 +1,63 @@ +#if ! defined _Section_h +#define _Section_h 1 +/* + Section of an existing collection viewed as a collection + */ + +#include <basic/Collection.h> + +namespace omega { + +template<class T> class Section_Iterator; + +template <class T> class Section : public Sequence<T> { +public: + Section(Sequence<T> *, int start, int length); + + Iterator<T> *new_iterator(); + + const T &operator[](int) const; + T &operator[](int); + + int index(const T &) const; + int size() const; + + friend class Section_Iterator<T>; + +private: + Sequence<T> *it; + int _start, _length; +}; + +template <class T> class Section_Iterator : public Iterator<T> { +public: + Section_Iterator(Section<T> &sec); + virtual ~Section_Iterator() { delete it; } + + const T & operator*() const { return *(*it); } + T & operator*() { return *(*it); } + + void operator++(int); + void operator++(); + + bool live() const; + Iterator<T> *new_copy() const; + +private: + Section_Iterator(const Section_Iterator<T> &si); + Iterator<T> *it; + int remaining; +}; + +} // namespace + +#if ! defined DONT_INCLUDE_TEMPLATE_CODE +#include <basic/Section.c> +#endif + +#define instantiate_Section(T) template class Section<T>; \ + template class Section_Iterator<T>; \ + instantiate_Sequence(T) +#define instantiate_Section_Iterator(T) instantiate_Section(T) + +#endif diff --git a/omegalib/omega/include/basic/SimpleList.c b/omegalib/omega/include/basic/SimpleList.c new file mode 100644 index 0000000..da7de9b --- /dev/null +++ b/omegalib/omega/include/basic/SimpleList.c @@ -0,0 +1,105 @@ +namespace omega { + +template<class T> Simple_List_Iterator<T>::Simple_List_Iterator(Simple_List<T> &l) +: List_Element_Iterator<T>(l.contents) {} + +template<class T> Simple_List_Iterator<T>::Simple_List_Iterator(const Simple_List<T> &l) +: List_Element_Iterator<T>(l.contents) {} + +template<class T> Simple_List_Iterator<T>::Simple_List_Iterator() +: List_Element_Iterator<T>(0) {} + +template<class T> Iterator<T> *Simple_List<T>::new_iterator() +{ + return new Simple_List_Iterator<T>(*this); +} + +template<class T> const T &Simple_List<T>::operator[](int i) const +{ + Simple_List_Iterator<T> p(*this); + + while(--i > 0 && p) + p++; + + if (p) + return *p; + else + return *((T *)0); +} + +template<class T> T &Simple_List<T>::operator[](int i) +{ + Simple_List_Iterator<T> p(*this); + + while(--i > 0 && p) + p++; + + if (p) + return *p; + else + return *((T *)0); +} + + +template<class T> int Simple_List<T>::size() const + { + int i = 0; + List_Element<T> * p = contents; + while (p) + { + p = p->tail; + i++; + } + return i; + } + +template<class T> T &Simple_List<T>::front() const + { + return contents->head; + } + +template<class T> T Simple_List<T>::remove_front() + { + List_Element<T> *frunt = contents; + contents = contents->tail; + T fruntT = frunt->head; + frunt->tail = 0; + delete frunt; + return fruntT; + } + +template<class T> void Simple_List<T>::prepend(const T &item) + { + contents = new List_Element<T>(item, contents); + } + + +template<class T> void Simple_List<T>::append(const T &item) + { + *(end()) = new List_Element<T>(item, 0); + } + + +template<class T> void Simple_List<T>::del_front() + { + List_Element<T> *e = contents; + contents = contents->tail; + e->tail = 0; + delete e; + } + + +template<class T> void Simple_List<T>::clear() + { + delete contents; + contents = 0; + } + +template<class T> void Simple_List<T>::join(Simple_List<T> &consumed) + { + List_Element<T> *e = consumed.contents; + consumed.contents = 0; + *(end()) = e; + } + +} // namespace diff --git a/omegalib/omega/include/basic/SimpleList.h b/omegalib/omega/include/basic/SimpleList.h new file mode 100644 index 0000000..a08b307 --- /dev/null +++ b/omegalib/omega/include/basic/SimpleList.h @@ -0,0 +1,93 @@ +#if ! defined _Simple_List_h +#define _Simple_List_h 1 + +/* + * Linked lists with an interface like a bit of libg++'s SLSimple_List class + */ + +#include <assert.h> +#include <basic/Iterator.h> +#include <basic/Collection.h> +#include <basic/Link.h> + +namespace omega { + +#define Simple_List Omega_Simple_List +#define Simple_List_Iterator Omega_Simple_List_Iterator + +template<class T> class Simple_List_Iterator; + +// A TEMPORARY HACK - ERROR IF YOU TRY TO USE "INDEX" - FERD + +template<class T> class Simple_List : public Sequence<T> { +public: + Simple_List(const Simple_List<T> &l) + { contents = l.contents ? new List_Element<T>(*l.contents) : 0; } + Simple_List() { contents = 0; } + virtual ~Simple_List() { delete contents; } + + Iterator<T> *new_iterator(); + const T &operator[](int) const; + T &operator[](int); + + + int size() const; + int length() const { return size(); } + int empty() const { return size() == 0; } + + T &front() const; + +// insertion/deletion on a list invalidates any iterators +// that are on/after the element added/removed + + T remove_front(); + + void prepend(const T &item); + void append(const T &item); + + void del_front(); + void clear(); + + void join(Simple_List<T> &consumed); + + int index(const T &) const { + assert(0&&"ILLEGAL SimpleList operation\n"); + return -1; + } + +private: + friend class Simple_List_Iterator<T>; + List_Element<T> **end() + { + List_Element<T> **e = &contents; + while (*e) + e = &((*e)->tail); + return e; + } + + List_Element<T> *contents; +}; + + +template<class T> class Simple_List_Iterator : public List_Element_Iterator<T> { +public: + Simple_List_Iterator(Simple_List<T> &l); + Simple_List_Iterator(const Simple_List<T> &l); + Simple_List_Iterator(); +private: + List_Element<T> &element() { return *this->i; } ; + friend class Simple_List<T>; +}; + +} // namespace + +#if ! defined DONT_INCLUDE_TEMPLATE_CODE +#include <basic/SimpleList.c> +#endif + +#define instantiate_Simple_List(T) template class Simple_List<T>; \ + template class Simple_List_Iterator<T>; \ + instantiate_Only_List_Element(T) \ + instantiate_Sequence(T) + +#endif diff --git a/omegalib/omega/include/basic/Tuple.c b/omegalib/omega/include/basic/Tuple.c new file mode 100644 index 0000000..ce99e82 --- /dev/null +++ b/omegalib/omega/include/basic/Tuple.c @@ -0,0 +1,254 @@ +/* class Tuple */ + +// THESE FIRST TWO REALLY SHOULD BE INLINE BUT IT BREAKS CFRONT: + +namespace omega { + +template<class T> T &Tuple<T>::operator[](int index) + { + assert(1 <= index && index <= sz); return data[index-1]; + } + +template<class T> const T &Tuple<T>::operator[](int index) const + { + assert(1 <= index && index <= sz); return data[index-1]; + } + + +template<class T> Tuple<T>::~Tuple() + { + if (data) + delete [] data; + } + +template<class T> Tuple<T>::Tuple() : sz(0), alloc_sz(0), + prealloc_min(20),prealloc_pad(5), data(0) +{ + // nothing needs be done + } + +template<class T> Tuple<T>::Tuple(int size) : sz(size), + prealloc_min(20),prealloc_pad(5) +{ + if (sz > 0) + { + alloc_sz = prealloc_size(sz); + data = new T[alloc_sz]; + assert(alloc_sz >= sz); + //Need some handling for out of memory. + assert (data!=0); + } + else { + alloc_sz = 0; + data = 0; + } +} + + +template<class T> Tuple<T>::Tuple(const Tuple<T>& t) + : sz(t.sz), alloc_sz(t.alloc_sz), prealloc_min(20),prealloc_pad(5) +{ + if (sz > 0) { + data = new T[alloc_sz]; + assert (data!=0); + assert (alloc_sz >= sz); + for (int i=0; i<sz; i++) + data[i] = t.data[i]; + } else { + data = 0; + alloc_sz = 0; // THis might not be 0 if it was a "clear"ed Tuple +// assert(alloc_sz == 0); + } +} + + +template<class T> Tuple<T>& Tuple<T>::operator=(const Tuple<T>& t) +{ + if (this != &t) { // Delete this + if (data) + delete [] data; + sz = t.sz; + alloc_sz = t.alloc_sz; + assert(alloc_sz >= sz); + if (sz > 0) { // Copy old + data = new T[alloc_sz]; + assert (data!=0); + for (int i=0; i<sz; i++) + data[i] = t.data[i]; + } else { + data=0; + alloc_sz = 0; // THis might not be 0 if it was a "clear"ed Tuple +// assert(alloc_sz == 0); + } + } + return *this; +} + + +template<class T> void Tuple<T>::reallocate(const int req_size) +{ + if (alloc_sz >= req_size) { // if (sz >= req_size), does this. + sz = req_size; + return; + } + alloc_sz = prealloc_size(req_size); + T* tmp_data = new T[alloc_sz]; + for(int i=0;i<sz;i++) + tmp_data[i] = data[i]; + delete [] data; + data = tmp_data; + sz = req_size; + assert(alloc_sz >= req_size); +} + +template<class T> void Tuple<T>::delete_last() +{ +assert(sz > 0); +sz --; +} + +template<class T> void Tuple<T>::append(const T &v) +{ + // Check if reallocation is necessary. + if (sz == 0) { // Empty Tuple + assert(alloc_sz >= 0); // May be nonzero for cleared tuple + + if(alloc_sz == 0) { // If it's > 1 no allocation is necessary + alloc_sz = prealloc_size(1); + data = new T[alloc_sz]; + } + assert (alloc_sz > 0 && data != 0); + } else { + if(sz == alloc_sz) { // Requires new allocation + alloc_sz = realloc_size(alloc_sz); + T * data_tmp = new T[alloc_sz]; + assert (data_tmp!=0); + assert (alloc_sz > sz); + for (int i=0; i<sz; i++) + data_tmp[i] = data[i]; + delete [] data; + data=data_tmp; + } // Otherwise big enough, no reallocation necessary + } + // Make assignment + assert(alloc_sz >= sz); + data[sz++] = v; +} + +template<class T> void Tuple<T>::append(const Tuple<T>& t) { + int old_sz = sz; + reallocate(t.size()+size()); + assert(alloc_sz >= sz); + for(int i=0; i<t.sz; i++) + data[i+old_sz] = t.data[i]; +} + +template<class T> void Tuple<T>::join(Tuple<T>& t) { + int old_sz = sz; + reallocate(t.size()+size()); + assert(alloc_sz >= sz); + for(int i=0; i<t.sz; i++) + data[i+old_sz] = t.data[i]; + t.clear(); +} + +template<class T> void Tuple<T>::clear() { if (sz) delete [] data; data = 0; alloc_sz = 0; sz = 0; } + +template<class T> int Tuple<T>::empty() const { return (sz == 0); } + +template<class T> Iterator<T> *Tuple<T>::new_iterator() +{ + return new Tuple_Iterator<T>(*this); +} + +template<class T> int Tuple<T>::index(const T & var) const +/* returns index or 0 if var isn't in the tuple */ +{ + int i; + for (i=0; i<sz; i++) + if (data[i]== var) + return i+1; + return 0; +} + +template<class T> bool Tuple<T>::operator == (const Tuple<T>& b) const +{ + int i; + if (sz != b.size()) return false; + for (i=0; i<sz; i++) + if (!(data[i] == b[i+1])) return false; + return true; +} + +/* class Tuple_Iterator */ + +template<class T> Tuple_Iterator<T>::Tuple_Iterator(const Tuple<T> &tpl) : +current(tpl.data), lastptr(tpl.data+tpl.sz-1), firstptr(tpl.data), sz(tpl.sz) +{ +} + +template<class T> Tuple_Iterator<T>::Tuple_Iterator(T * cr, T *frst, T * lst, + int insz) + : current(cr), lastptr(lst), firstptr(frst), sz(insz) +{ +} + +template<class T> const T & Tuple_Iterator<T>::operator*() const +{ + assert (current<=lastptr && current>=firstptr); + return *current; +} + +template<class T> T & Tuple_Iterator<T>::operator*() +{ + assert (current<=lastptr && current >=firstptr); + return *current; +} + +template<class T> void Tuple_Iterator<T>::operator++(int) +{ + current++; +} + +template<class T> void Tuple_Iterator<T>::operator++() +{ + current++; +} + +template<class T> void Tuple_Iterator<T>::operator--(int) +{ + current--; +} + +template<class T> void Tuple_Iterator<T>::operator--() +{ + current--; +} + +template<class T> void Tuple_Iterator<T>::set_to_last() +{ + current = lastptr; +} + +template<class T> void Tuple_Iterator<T>::set_to_first() +{ + current = firstptr; +} + +template<class T> void Tuple_Iterator<T>::set_position(const int req_pos) +{ + assert(req_pos <= sz && 1 <= req_pos); + current = firstptr + (req_pos - 1); +} + + +template<class T> bool Tuple_Iterator<T>::live() const +{ + return (current !=0 && current<=lastptr && current >= firstptr); +} + +template<class T> Iterator<T> *Tuple_Iterator<T>::new_copy() const { + return new Tuple_Iterator<T>(current, firstptr, lastptr, sz); +} + +} // namespace diff --git a/omegalib/omega/include/basic/Tuple.h b/omegalib/omega/include/basic/Tuple.h new file mode 100644 index 0000000..28e83bd --- /dev/null +++ b/omegalib/omega/include/basic/Tuple.h @@ -0,0 +1,90 @@ +#if !defined _Already_defined_tuple +#define _Already_defined_tuple + +#include <stdio.h> + +#include <basic/Collection.h> +#include <basic/Iterator.h> +#include <basic/util.h> + +namespace omega { + +template<class T> class Tuple_Iterator; + +// TUPLES ARE INDEXED STARTING AT 1 +// index\(i\) == 0 MEANS i IS NOT IN THE TUPLE + +template <class T> class Tuple : public Sequence<T> { +public: + Tuple(); + Tuple(int size); + Tuple (const Tuple<T>& tpl); + virtual ~Tuple(); + Tuple<T>& operator=(const Tuple<T>& tpl); + int size() const { return sz; } + int length() const { return sz; } + bool operator==(const Tuple<T> &b) const; + void reallocate(const int); + void delete_last(); + void append(const Tuple<T> &v); + void append(const T &v); + void join(Tuple<T> &v); + void clear(); + int empty() const; + + Iterator<T> *new_iterator(); + + virtual T &operator[](int index); + virtual const T &operator[](int index) const; + + int index(const T &) const; + + friend class Tuple_Iterator<T>; + +private: + int prealloc_size(const int req_size) + { return max(req_size+prealloc_pad,prealloc_min); } + int realloc_size(const int oldsize) { return 2*oldsize; } + + + int sz, alloc_sz; // Number of elements, size of allocated array + int prealloc_min,prealloc_pad; // These should be static, but that + // causes portability prob. for initialization + +protected: + T * data; +}; + +template <class T> class Tuple_Iterator : public Iterator <T> { +public: + Tuple_Iterator(const Tuple<T> &tpl); + const T & operator*() const; + T & operator*(); + void set_position(const int req_pos); + void operator++(int); + void operator++(); + void operator--(int); + void operator--(); + void set_to_last(); + void set_to_first(); +// void set_position(const int req_pos); Don't do this, compiler bug + bool live() const; + Iterator<T> *new_copy() const; + +private: + Tuple_Iterator(T * cr, T * frst, T *lst, int insz); + T * current, * lastptr, *firstptr; + int sz; +}; + +} // namespace + +#if ! defined DONT_INCLUDE_TEMPLATE_CODE +#include <basic/Tuple.c> +#endif + +#define instantiate_Tuple(T) template class Tuple<T>; \ + template class Tuple_Iterator<T>; \ + instantiate_Sequence(T) + +#endif diff --git a/omegalib/omega/include/basic/boolset-test.cc b/omegalib/omega/include/basic/boolset-test.cc new file mode 100755 index 0000000..5b68220 --- /dev/null +++ b/omegalib/omega/include/basic/boolset-test.cc @@ -0,0 +1,72 @@ +#include "boolset.h" +#include <iostream> + +using namespace omega; + +void foo(const BoolSet<> &B) { + for (BoolSet<>::const_iterator i = B.begin(); i != B.end(); i++) + std::cout << *i << ' '; + std::cout << std::endl; +} + +int main() { + BoolSet<> A(13); + + A.set(2); + std::cout << A << std::endl; + + A.set_all(); + std::cout << A << std::endl; + + A.unset_all(); + std::cout << A << std::endl; + + A.set(2); + A.set(4); + + BoolSet<> B(13); + B.set(2); + + std::cout << "A: " << A << std::endl; + std::cout << "B: " << B << std::endl; + + std::cout << A.imply(B) << std::endl; + std::cout << B.imply(A) << std::endl; + + B.set(10); + std::cout << (A|B) << std::endl; + std::cout << (A&B) << std::endl; + + BoolSet<> C(3); + C.set(0); + std::cout << (A|C) << std::endl; + std::cout << ~(A|C) << std::endl; + + B = BoolSet<>(23); + std::cout << "test iterator\n"; + B.set(12); + B.set(11); + B.set(0); + std::cout << B << std::endl; + for (BoolSet<>::const_iterator i = B.begin(); i != B.end(); i++) { + std::cout << *i << ' '; + if (*i == 11) + B.unset(*i); + } + std::cout << std::endl; + std::cout << B << std::endl; + std::cout << std::endl; + foo(B); + + std::cout << ~BoolSet<>(5) << std::endl; + + std::cout << "------\n"; + B.dump(); + std::cout << std::endl << *(B.begin()+1) << std::endl; + + for (BoolSet<>::iterator i = B.begin(); i != B.end(); i++) + for (BoolSet<>::iterator j = i; j != B.end(); j++) + if (j == i) + std::cout << "ehh-"; + +} diff --git a/omegalib/omega/include/basic/omega_error.h b/omegalib/omega/include/basic/omega_error.h new file mode 100644 index 0000000..e342efb --- /dev/null +++ b/omegalib/omega/include/basic/omega_error.h @@ -0,0 +1,14 @@ +#ifndef OMEGA_ERROR_H +#define OMEGA_ERROR_H + +namespace omega { + +struct presburger_error: public std::runtime_error { + presburger_error(const std::string &msg): std::runtime_error("presburger error: " + msg) {} +}; + + + +} +#endif + diff --git a/omegalib/omega/include/basic/util.h b/omegalib/omega/include/basic/util.h new file mode 100644 index 0000000..4e807cd --- /dev/null +++ b/omegalib/omega/include/basic/util.h @@ -0,0 +1,263 @@ +#if ! defined Already_Included_Util +#define Already_Included_Util + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <string> +#include <sstream> +#include <stdexcept> + +namespace omega { + +#define LONG_LONG_COEF 1 + +#if LONG_LONG_COEF +#if defined BOGUS_LONG_DOUBLE_COEF +typedef long double coef_t; // type of coefficients +#define coef_fmt "%llf" +#define posInfinity (1e+24) +#define negInfinity (-1e+24) +#else +#ifdef WIN32 +typedef _int64 coef_t; // type of coefficients +#else +typedef long long coef_t; +#endif +#define coef_fmt "%lld" +#define posInfinity (0x7ffffffffffffffLL) +#define negInfinity (-0x7ffffffffffffffLL) +#endif +#else +typedef int coef_t; // type of coefficients +#define coef_fmt "%d" +#define posInfinity (0x7ffffff) +#define negInfinity (-0x7ffffff) +#endif + + +template<typename T> inline const T& max(const T &x, const T &y) { + if (x >= y) return x; else return y; +} + + +template<typename T> inline const T& max(const T &x, const T &y, const T &z) { + return max(x, max(y, z)); +} + +template<typename T> inline const T& min(const T &x, const T &y) { + if (x <= y) return x; else return y; +} + +template<typename T> inline const T& min(const T &x, const T &y, const T &z) { + return min(x, min(y, z)); +} + +template<class T> inline void set_max(T &m, const T &x) { + if (m < x) m = x; +} + +template<class T> inline void set_min(T &m, const T &x) { + if (m > x) m = x; +} + +/* template<class T> inline void swap(T &i, T &j) { */ +/* T tmp; */ +/* tmp = i; */ +/* i = j; */ +/* j = tmp; */ +/* } */ + +/* template<class T> inline T copy(const T &t) { return t; } */ + + +/* inline coef_t check_pos_mul(coef_t x, coef_t y) { */ +/* if (y >= 48051280 && y < posInfinity) */ +/* fprintf(stderr, "%d %d\n", x, y); */ +/* /\* #if !defined NDEBUG *\/ */ +/* /\* if (x != 0) *\/ */ +/* /\* assert(((MAXINT)/4) / x > y); *\/ */ +/* /\* #elif defined STILL_CHECK_MULT *\/ */ +/* /\* if (x != 0 && !(((MAXINT)/4) / x > y)) { *\/ */ +/* /\* assert(0&&"Integer overflow during multiplication (util.h)"); *\/ */ +/* /\* } *\/ */ +/* /\* #endif *\/ */ +/* #if !defined NDEBUG */ +/* if (x != 0 && y != 0) */ +/* assert(x*y > 0); */ +/* #elif defined STILL_CHECK_MULT */ +/* if (x != 0 && y != 0 && x*y < 0) */ +/* assert(0&&"Integer overflow during multiplication (util.h)"); */ +/* #endif */ +/* return x * y; */ +/* } */ + + +/* inline int */ +/* check_pos_mul(int x, int y) { */ +/* #if !defined NDEBUG */ +/* if (x != 0) */ +/* assert(((posInfinity)/4) / x > y); */ +/* #elif defined STILL_CHECK_MULT */ +/* if (x != 0 && !(((posInfinity)/4) / x > y)) { */ +/* assert(0&&"Integer overflow during multiplication (util.h)"); */ +/* } */ +/* #endif */ +/* return x * y; */ +/* } */ + +/* inline LONGLONG */ +/* check_pos_mul(LONGLONG x, LONGLONG y) { */ +/* #if !defined NDEBUG */ +/* if (x != 0) */ +/* assert(((posInfinity)/4) / x > y); */ +/* #elif defined STILL_CHECK_MULT */ +/* if (x != 0 && !(((posInfinity)/4) / x > y)) { */ +/* assert(0&&"Integer overflow during multiplication (util.h)"); */ +/* } */ +/* #endif */ +/* return x * y; */ +/* } */ + +/* inline LONGLONG abs(LONGLONG c) { return (c>=0?c:(-c)); } */ + +template<typename T> inline T check_mul(const T &x, const T &y) { +#if defined NDEBUG && ! defined STILL_CHECK_MULT + return x*y; +#else + if (x == 0 || y == 0) + return 0; + + T z = x*y; + int sign_x = (x>0)?1:-1; + int sign_y = (y>0)?1:-1; + int sign_z = (z>0)?1:-1; + + if (sign_x * sign_y != sign_z) + throw std::overflow_error("coefficient multiply overflow"); + + return z; + + /* if (x > 0) { */ + /* if (y > 0) { */ + /* assert(x*y > 0); */ + /* } */ + /* else */ + /* assert(x*y < 0); */ + /* } */ + /* else { */ + /* if (y > 0) */ + /* assert(x*y < 0); */ + /* else */ + /* assert(x*y > 0); */ + /* } */ + /* return x*y; */ +#endif +} + +template<typename T> inline T abs(const T &v) { + return (v >= static_cast<T>(0))?v:-v; +} + +template<class T> inline T int_div(const T &a, const T &b) { + T result; + assert(b > 0); + if (a>0) result = a/b; + else result = -((-a+b-1)/b); + return result; +} + +template<class T> inline T int_mod(const T &a, const T &b) { + return a-b*int_div(a,b); +} + +template<class T> inline T int_mod_hat(const T &a, const T &b) { + T r; + assert(b > 0); + r = a-b*int_div(a,b); + if (r > -(r-b)) r -= b; + return r; +} + +template<typename T> inline T gcd(T b, T a) {/* First argument is non-negative */ + assert(a >= 0); + assert(b >= 0); + if (b == 1) + return (1); + while (b != 0) { + T t = b; + b = a % b; + a = t; + } + return (a); +} + +template<typename T> inline T lcm(T b, T a) { /* First argument is non-negative */ + assert(a >= 0); + assert(b >= 0); + return check_mul(a/gcd(a,b), b); +} + +template<typename T> T square_root(const T &n, T precision = 1) { + T guess = 1; + + while (true) { + T next_guess = 0.5*(guess+n/guess); + if (abs(next_guess-guess) <= precision) + return next_guess; + else + guess = next_guess; + } +} + +template<typename T> T factor(const T &n) { + assert(n >= 0); + if (n == 1) return 1; + + static int prime[30] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113}; + + if (n <= 113*113) { + for (int i = 0; i < 30; i++) + if (n % static_cast<T>(prime[i]) == 0) + return static_cast<T>(prime[i]); + + return n; + } + + T i = 1; + T k = 2; + T x = static_cast<T>(rand())%n; + T y = x; + while(i < square_root<float>(n, 1)) { + i++; + x = (x*x-1) % n; + T d = gcd(abs(y-x), n); + if(d != 1 && d != n) + return factor(d); + if(i == k) { + y = x; + k *= 2; + } + } + return n; +} + +/* #define implies(A,B) (A==(A&B)) */ + +template<typename T> std::string to_string(const T &t) { + std::ostringstream ss; + ss << t; + return ss.str(); +} + +template<typename T> T from_string(const std::string &s) { + std::istringstream ss(s); + ss.exceptions(std::ios::failbit); + T t; + ss >> t; + return t; +} + +} // namespace + +#endif diff --git a/omegalib/omega/include/omega.h b/omegalib/omega/include/omega.h new file mode 100644 index 0000000..8aa2c08 --- /dev/null +++ b/omegalib/omega/include/omega.h @@ -0,0 +1,71 @@ +/********************************************************************* + Old license information from the Omega Project, updated one can be + found in LICENSE file. + + Copyright (C) 1994-1996 by the Omega Project + All rights reserved. + + NOTICE: This software is provided ``as is'', without any + warranty, including any implied warranty for merchantability or + fitness for a particular purpose. Under no circumstances shall + the Omega Project or its agents be liable for any use of, misuse + of, or inability to use this software, including incidental and + consequential damages. + + License is hereby given to use, modify, and redistribute this + software, in whole or in part, for any purpose, commercial or + non-commercial, provided that the user agrees to the terms of this + copyright notice, including disclaimer of warranty, and provided + that this copyright notice, including disclaimer of warranty, is + preserved in the source code and documentation of anything derived + from this software. Any redistributor of this software or + anything derived from this software assumes responsibility for + ensuring that any parties to whom such a redistribution is made + are fully aware of the terms of this license and disclaimer. + + The Omega project can be contacted at omega@cs.umd.edu + or http://www.cs.umd.edu/projects/omega +*********************************************************************/ + +#ifndef Already_Included_Omega +#define Already_Included_Omega + +/* + * The presburger interface is divided into the following parts. + * These parts are all included together, but are in separate + * files to keep things organized a bit. + * + * In many files, you can include just some of the following, + * specifically: if you are building a presburger tree, just + * include "pres_tree.h"; if you are querying it, include + * "pres_dnf.d" and "pres_conj.h"; if you are doing relational + * operations, include "Relation.h" + * + * Most of the function definitions are in the .c files with + * the same name as the .h that declares them, except: + * the remap and push_exists functions are in pres_var.c + * the DNFize functions are in pres_dnf.c + * the functions involving printing are in pres_print.c + * the beautify functions are in pres_beaut.c + * the rearrange functions are in pres_rear.c + * the compression functions are in pres_cmpr.c + */ + +#include <omega/omega_core/debugging.h> +#include <omega/pres_var.h> +#include <omega/pres_cnstr.h> +#include <omega/pres_subs.h> +#include <omega/pres_form.h> +#include <omega/pres_logic.h> +#include <omega/pres_decl.h> +#include <omega/pres_quant.h> +#include <omega/pres_conj.h> +#include <omega/pres_cmpr.h> +#include <omega/Relation.h> + +#include <omega/Rel_map.h> +#include <omega/farkas.h> +#include <omega/hull.h> +#include <omega/closure.h> + +#endif diff --git a/omegalib/omega/include/omega/RelBody.h b/omegalib/omega/include/omega/RelBody.h new file mode 100644 index 0000000..3c11702 --- /dev/null +++ b/omegalib/omega/include/omega/RelBody.h @@ -0,0 +1,165 @@ +#if ! defined _RelBody_h +#define _RelBody_h 1 + +#include <omega/pres_form.h> +#include <omega/pres_dnf.h> + +namespace omega { + +typedef enum {under_construction, compressed, uncompressed} Rel_Body_Status; +typedef unsigned char Rel_Unknown_Uses; +const Rel_Unknown_Uses no_u = 1; +const Rel_Unknown_Uses and_u = 2; +const Rel_Unknown_Uses or_u = 4; + +// +// Relation body. +// Body and representative are separated to do reference counting. +// + +class Rel_Body : public Formula { +public: + bool is_null() const; + + inline Node_Type node_type() { return Op_Relation; } + + inline bool is_set() const { return number_output == 0; } + int n_inp() const; + int n_out() const; + int n_set() const; + + inline Variable_ID_Tuple *global_decls() { return &Symbolic; } + int max_ufs_arity(); + int max_shared_ufs_arity(); + int max_ufs_arity_of_set(); + int max_ufs_arity_of_in(); + int max_ufs_arity_of_out(); + + Variable_ID input_var(int nth); + Variable_ID output_var(int nth); + Variable_ID set_var(int nth); + Variable_ID get_local(const Variable_ID v); + Variable_ID get_local(const Global_Var_ID G); + Variable_ID get_local(const Global_Var_ID G, Argument_Tuple of); + bool has_local(const Global_Var_ID G); + bool has_local(const Global_Var_ID G, Argument_Tuple of); + void name_input_var(int, Const_String); + void name_output_var(int, Const_String); + void name_set_var(int, Const_String); + + F_And *and_with_and(); + EQ_Handle and_with_EQ(); + EQ_Handle and_with_EQ(const Constraint_Handle &c); + GEQ_Handle and_with_GEQ(); + GEQ_Handle and_with_GEQ(const Constraint_Handle &c); + + void print(); + void print(FILE *output_file) { print(output_file, true); } + void print(FILE *output_file, bool printSym); + std::string print_variables_to_string(bool printSym); + void print_with_subs(FILE *output_file, bool printSym, bool newline); + void print_with_subs(); + std::string print_with_subs_to_string(bool printSym, bool newline); + std::string print_outputs_with_subs_to_string(); + std::string print_outputs_with_subs_to_string(int i); + std::string print_formula_to_string(); + void prefix_print(); + void prefix_print(FILE *output_file, int debug = 1); + + bool is_satisfiable(); + bool is_lower_bound_satisfiable(); + bool is_upper_bound_satisfiable(); + bool is_obvious_tautology(); + bool is_definite_tautology(); + bool is_unknown(); + DNF* query_DNF(); + DNF* query_DNF(int rdt_conjs, int rdt_constrs); + void simplify(int rdt_conjs = 0, int rdt_constrs = 0); + void finalize(); + inline bool is_finalized() { return finalized; } + inline bool is_shared() { return ref_count > 1; } + + void query_difference(Variable_ID v1, Variable_ID v2, + coef_t &lowerBound, coef_t &upperBound, + bool &quaranteed); + void query_variable_bounds(Variable_ID, coef_t &lowerBound, coef_t &upperBound); + coef_t query_variable_mod(Variable_ID v, coef_t factor); + + Relation extract_dnf_by_carried_level(int level, int direction); + void make_level_carried_to(int level); + + // these are only public to allow the creation of "null_rel" + Rel_Body(); + ~Rel_Body(); + void setup_names(); + +private: + + // These are manipulated primarily as parts of Relations + friend class Relation; + friend_rel_ops; + + friend void remap_DNF_vars(Rel_Body *new_rel, Rel_Body *old_rel); + + Rel_Unknown_Uses unknown_uses(); + + inline bool is_simplified() const { return (simplified_DNF!=NULL && get_children().empty()); } + bool is_compressed(); + Conjunct *rm_first_conjunct(); + Conjunct *single_conjunct(); + bool has_single_conjunct(); + + void beautify(); + void rearrange(); + + friend class EQ_Handle; // these set up names for printing + friend class GEQ_Handle; // and check if simplified + friend class Constraint_Handle; // and update coefficients + + void compress(); + void uncompress(); + + void interpret_unknown_as_true(); + void interpret_unknown_as_false(); + + Rel_Body(int n_input, int n_output); + /* Rel_Body(Rel_Body *r); */ + Rel_Body(Rel_Body *r, Conjunct *c); + Rel_Body &operator=(Rel_Body &r); + Rel_Body *clone(); + + inline Formula *formula() { return children().front(); } + inline Formula *rm_formula() { return children().remove_front(); } + bool can_add_child(); + + void reverse_leading_dir_info(); + void invalidate_leading_info(int changed = -1) { Formula::invalidate_leading_info(changed); } + void enforce_leading_info(int guaranteed, int possible, int dir) { Formula::enforce_leading_info(guaranteed, possible, dir); } + // re-declare this so that Relation (a friend) can call it + + DNF* DNFize(); + void DNF_to_formula(); + + Conjunct *find_available_conjunct(); + F_And *find_available_And(); + + +/* === data === */ +private: + + int ref_count; + Rel_Body_Status status; + + int number_input, number_output; + Tuple<Const_String> In_Names, Out_Names; + Variable_ID_Tuple Symbolic; + + DNF* simplified_DNF; + short r_conjs; // are redundant conjuncts eliminated? + bool finalized; + bool _is_set; +}; + +} // namespace + +#endif diff --git a/omegalib/omega/include/omega/Rel_map.h b/omegalib/omega/include/omega/Rel_map.h new file mode 100644 index 0000000..5641cb3 --- /dev/null +++ b/omegalib/omega/include/omega/Rel_map.h @@ -0,0 +1,161 @@ +#if ! defined _Rel_map_h +#define _Rel_map_h 1 + +#include <omega/pres_gen.h> +#include <omega/pres_var.h> + +namespace omega { + +// +// Mapping for relations +// When a relation operation needs to re-arrange the variables, +// it describes the re-arragement with a mapping, and then calls +// align to re-arrange them. +// +// In a mapping, map_in (map_out/map_set) gives the new type and +// position of each of the old input (output/set) variables. +// For variables being mapped to Input, Output, or Set variables, +// the position is the new position in the tuple. +// For variables being mapped to Exists_Var, Forall_Var, or +// Wildcard_Var, the positions can be used to map multiple +// variables to the same quantified variable, by providing +// the same position. Each variable with a negative position +// is given a unique quantified variable that is NOT listed +// in the seen_exists_ids list. +// I'm not sure what the positions mean for Global_Vars - perhaps +// they are ignored? +// +// Currently, align seems to support only mapping to Set, Input, +// Output, and Exists vars. +// + +class Mapping { +public: + inline Mapping(int no_in, int no_out): n_input(no_in), n_output(no_out) {} + inline Mapping(int no_set): n_input(no_set), n_output(0){} + inline Mapping(const Mapping &m): n_input(m.n_input), n_output(m.n_output) { + int i; + for(i=1; i<=n_input; i++) map_in_kind[i] = m.map_in_kind[i]; + for(i=1; i<=n_input; i++) map_in_pos[i] = m.map_in_pos[i]; + for(i=1; i<=n_output;i++) map_out_kind[i] = m.map_out_kind[i]; + for(i=1; i<=n_output;i++) map_out_pos[i] = m.map_out_pos[i]; + } + + inline void set_map (Var_Kind in_kind, int pos, Var_Kind type, int map) { + if(in_kind==Input_Var) + set_map_in(pos,type,map); + else if(in_kind==Set_Var) + set_map_in(pos,type,map); + else if(in_kind==Output_Var) + set_map_out(pos,type,map); + else + assert(0); + } + + inline void set_map_in (int pos, Var_Kind type, int map) { + assert(pos>=1 && pos<=n_input); + map_in_kind[pos] = type; + map_in_pos[pos] = map; + } + inline void set_map_set (int pos, Var_Kind type, int map) { + assert(pos>=1 && pos<=n_input); + map_in_kind[pos] = type; + map_in_pos[pos] = map; + } + + inline void set_map_out(int pos, Var_Kind type, int map) { + assert(pos>=1 && pos<=n_output); + map_out_kind[pos] = type; + map_out_pos[pos] = map; + } + + inline Var_Kind get_map_in_kind(int pos) const { + assert(pos>=1 && pos<=n_input); + return map_in_kind[pos]; + } + + inline int get_map_in_pos(int pos) const { + assert(pos>=1 && pos<=n_input); + return map_in_pos[pos]; + } + + inline Var_Kind get_map_out_kind(int pos) const { + assert(pos>=1 && pos<=n_output); + return map_out_kind[pos]; + } + + inline int get_map_out_pos(int pos) const { + assert(pos>=1 && pos<=n_output); + return map_out_pos[pos]; + } + + inline int n_in() const { return n_input; } + inline int n_out() const { return n_output; } + + // If a tuple as a whole becomes the new Input or Output tuple, + // return the Tuple of they will become (Input, Output) + // Return Unknown_Tuple otherwise + + inline Argument_Tuple get_tuple_fate(Argument_Tuple t, int prefix = -1) const { + return t== Input_Tuple ? get_input_fate(prefix) : + (t==Output_Tuple ? get_output_fate(prefix) : Unknown_Tuple); } + + inline Argument_Tuple get_set_fate(int prefix = -1) const { + return get_input_fate(prefix); } + + inline Argument_Tuple get_input_fate(int prefix = -1) const { + if (prefix < 0) prefix = n_input; + assert(n_input >= prefix); + if (n_input < prefix) + return Unknown_Tuple; + Var_Kind vf = map_in_kind[1]; + for (int i = 1; i<=prefix; i++) + if (map_in_pos[i]!=i || map_in_kind[i]!=vf) + return Unknown_Tuple; + + return vf == Input_Var ? Input_Tuple + : vf == Set_Var ? Set_Tuple + : vf == Output_Var ? Output_Tuple + : Unknown_Tuple; + } + + inline Argument_Tuple get_output_fate(int prefix = -1) const { + if (prefix < 0) prefix = n_output; + assert(n_output >= prefix); + if (n_output < 1) + return Unknown_Tuple; + Var_Kind vf = map_out_kind[1]; + for (int i = 1; i<=prefix; i++) + if (map_out_pos[i]!=i || map_out_kind[i]!=vf) + return Unknown_Tuple; + return vf == Input_Var ? Input_Tuple + : vf == Set_Var ? Set_Tuple + : vf == Output_Var ? Output_Tuple + : Unknown_Tuple; + } + + inline static Mapping Identity(int inp, int outp) { + Mapping m(inp, outp); int i; + for(i=1; i<=m.n_input; i++) m.set_map(Input_Var, i, Input_Var, i); + for(i=1; i<=m.n_output;i++) m.set_map(Output_Var, i, Output_Var, i); + return m; + } + + inline static Mapping Identity(int setvars) { + Mapping m(setvars); int i; + for(i=1; i<=setvars; i++) m.set_map(Set_Var, i, Set_Var, i); + return m; + } + +private: + int n_input; + int n_output; + Var_Kind map_in_kind[maxVars]; + int map_in_pos[maxVars]; + Var_Kind map_out_kind[maxVars]; + int map_out_pos[maxVars]; +}; + +} // namespace + +#endif diff --git a/omegalib/omega/include/omega/Relation.h b/omegalib/omega/include/omega/Relation.h new file mode 100644 index 0000000..b41bef5 --- /dev/null +++ b/omegalib/omega/include/omega/Relation.h @@ -0,0 +1,299 @@ +#if ! defined _Relation_h +#define _Relation_h 1 + +#include <omega/RelBody.h> +#include <omega/pres_cnstr.h> +#include <iostream> +#include <limits.h> + +namespace omega { + +// +// Relation representative. +// Body and representative are separated to do reference counting. +// +class Relation { +public: + Relation(); + + Relation(int n_input, int n_output = 0); + Relation(const Relation &r); + Relation(const Relation &r, Conjunct *c); + Relation &operator=(const Relation &r); + Relation(Rel_Body &r, int foo); + + static Relation Null(); + static Relation Empty(const Relation &R); + static Relation True(const Relation &R); + static Relation True(int setvars); + static Relation True(int in, int out); + static Relation False(const Relation &R); + static Relation False(int setvars); + static Relation False(int in, int out); + static Relation Unknown(const Relation &R); + static Relation Unknown(int setvars); + static Relation Unknown(int in, int out); + + + bool is_null() const; + + ~Relation(); + + inline F_Forall *add_forall() + { return rel_body->add_forall(); } + inline F_Exists *add_exists() + { return rel_body->add_exists(); } + inline F_And *add_and() + { return rel_body->add_and(); } + inline F_And *and_with() + { return rel_body->and_with(); } + inline F_Or *add_or() + { return rel_body->add_or(); } + inline F_Not *add_not() + { return rel_body->add_not(); } + inline void finalize() + { rel_body->finalize(); } + inline bool is_finalized() const + { return rel_body->finalized; } + inline bool is_set() const + { return rel_body->is_set(); } + inline int n_inp() const + { return rel_body->n_inp(); } + inline int n_out() const + { return rel_body->n_out(); } + inline int n_set() const + { return rel_body->n_set(); } + + inline const Variable_ID_Tuple *global_decls() const + { return rel_body->global_decls(); } + inline int max_ufs_arity() const + { return rel_body->max_ufs_arity(); } + inline int max_ufs_arity_of_in() const + { return rel_body->max_ufs_arity_of_in(); } + inline int max_ufs_arity_of_set() const + { return rel_body->max_ufs_arity_of_set(); } + inline int max_ufs_arity_of_out() const + { return rel_body->max_ufs_arity_of_out(); } + inline int max_shared_ufs_arity() const + { return rel_body->max_shared_ufs_arity(); } + + inline Variable_ID input_var(int nth) + { return rel_body->input_var(nth); } + inline Variable_ID output_var(int nth) + { return rel_body->output_var(nth); } + inline Variable_ID set_var(int nth) + { return rel_body->set_var(nth); } + inline bool has_local(const Global_Var_ID G) + { return rel_body->has_local(G); } + inline bool has_local(const Global_Var_ID G, Argument_Tuple of) + { return rel_body->has_local(G, of); } + inline Variable_ID get_local(const Variable_ID v) + { return split()->get_local(v); } + inline Variable_ID get_local(const Global_Var_ID G) + { return split()->get_local(G); } + inline Variable_ID get_local(const Global_Var_ID G, Argument_Tuple of) + { return split()->get_local(G, of); } + + inline void name_input_var(int nth, Const_String S) + { split()->name_input_var(nth, S); } + inline void name_output_var(int nth, Const_String S) + { split()->name_output_var(nth, S); } + inline void name_set_var(int nth, Const_String S) + { split()->name_set_var(nth, S); } + + + inline F_And *and_with_and() + { return split()->and_with_and(); } + inline EQ_Handle and_with_EQ() + { return split()->and_with_EQ(); } + inline EQ_Handle and_with_EQ(const Constraint_Handle &c) + { return split()->and_with_EQ(c); } + inline GEQ_Handle and_with_GEQ() + { return split()->and_with_GEQ(); } + inline GEQ_Handle and_with_GEQ(const Constraint_Handle &c) + { return split()->and_with_GEQ(c); } + + inline void print() + { rel_body->print(); } + inline void print(FILE *output_file) + { rel_body->print(output_file); } + inline void print_with_subs() + { rel_body->print_with_subs(); } + inline void print_with_subs(FILE *output_file, bool printSym=false, + bool newline=true) + { rel_body->print_with_subs(output_file, printSym, newline); } + inline std::string print_with_subs_to_string(bool printSym=false, + bool newline=true) + { return rel_body->print_with_subs_to_string(printSym, newline); } + inline std::string print_outputs_with_subs_to_string() + { return rel_body->print_outputs_with_subs_to_string(); } + inline std::string print_outputs_with_subs_to_string(int i) + { return rel_body->print_outputs_with_subs_to_string(i); } + inline void prefix_print() + { rel_body->prefix_print(); } + inline void prefix_print(FILE *output_file, int debug = 1) + { rel_body->prefix_print(output_file, debug); } + inline std::string print_formula_to_string() { + return rel_body->print_formula_to_string(); + } + void dimensions(int & ndim_all, int &ndim_domain); + + inline bool is_lower_bound_satisfiable() + { return rel_body->is_lower_bound_satisfiable(); } + inline bool is_upper_bound_satisfiable() + { return rel_body->is_upper_bound_satisfiable(); } + inline bool is_satisfiable() + { return rel_body->is_satisfiable(); } + + inline bool is_tautology() + { return rel_body->is_obvious_tautology(); } // for compatibility + inline bool is_obvious_tautology() + { return rel_body->is_obvious_tautology(); } + inline bool is_definite_tautology() + { return rel_body->is_definite_tautology(); } + + // return x s.t. forall conjuncts c, c has >= x leading 0s + // for set, return -1 (pass this in, in case there are no conjuncts + inline int number_of_conjuncts() + { return rel_body->query_DNF()->length(); } + + // return x s.t. forall conjuncts c, c has >= x leading 0s + // for set, return -1 (pass this in, in case there are no conjuncts + inline int query_guaranteed_leading_0s() + { return rel_body->query_DNF()->query_guaranteed_leading_0s(this->is_set() ? -1 : 0); } + + // return x s.t. forall conjuncts c, c has <= x leading 0s + // if no conjuncts return min of input and output tuple sizes, or -1 if relation is a set + inline int query_possible_leading_0s() + { return rel_body->query_DNF()->query_possible_leading_0s( + this->is_set()? -1 : min(n_inp(),n_out())); } + + // return +-1 according to sign of leading dir, or 0 if we don't know + inline int query_leading_dir() + { return rel_body->query_DNF()->query_leading_dir(); } + + inline DNF* query_DNF() + { return rel_body->query_DNF(); } + inline DNF* query_DNF(int rdt_conjs, int rdt_constrs) + { return rel_body->query_DNF(rdt_conjs, rdt_constrs); } + inline void simplify(int rdt_conjs = 0, int rdt_constrs = 0) + { rel_body->simplify(rdt_conjs, rdt_constrs); } + inline bool is_simplified() + { return rel_body->is_simplified(); } + inline bool is_compressed() const + { return rel_body->is_compressed(); } + inline Conjunct *rm_first_conjunct() + { return rel_body->rm_first_conjunct(); } + inline Conjunct *single_conjunct() + { return rel_body->single_conjunct(); } + inline bool has_single_conjunct() + { return rel_body->has_single_conjunct(); } + + + void query_difference(Variable_ID v1, Variable_ID v2, coef_t &lowerBound, coef_t &upperBound, bool &guaranteed) { + rel_body->query_difference(v1, v2, lowerBound, upperBound, guaranteed); + } + void query_variable_bounds(Variable_ID v, coef_t &lowerBound, coef_t &upperBound) { + rel_body->query_variable_bounds(v,lowerBound,upperBound); + } + coef_t query_variable_mod(Variable_ID v, coef_t factor) { + assert(factor > 0); + return rel_body->query_variable_mod(v, factor); + } + int query_variable_mod(Variable_ID v, int factor) { + assert(sizeof(int) <= sizeof(coef_t)); + coef_t result = rel_body->query_variable_mod(v, static_cast<coef_t>(factor)); + if (result == posInfinity) + return INT_MAX; + else + return static_cast<int>(result); + } + + + inline void make_level_carried_to(int level) + { + split()->make_level_carried_to(level); + } + + inline Relation extract_dnf_by_carried_level(int level, int direction) + { + return split()->extract_dnf_by_carried_level(level, direction); + } + + inline void compress() + { +#if defined(INCLUDE_COMPRESSION) + split()->compress(); +#endif + } + void uncompress() + { rel_body->uncompress(); } + + inline bool is_exact() const + { return !(rel_body->unknown_uses() & (and_u | or_u)) ; } + inline bool is_inexact() const + { return !is_exact(); } + inline bool is_unknown() const + { return rel_body->is_unknown(); } + inline Rel_Unknown_Uses unknown_uses() const + { return rel_body->unknown_uses(); } + + void setup_names() {rel_body->setup_names();} + void copy_names(const Relation &r) { + copy_names(*r.rel_body); + }; + void copy_names(Rel_Body &r); + +private: + // Functions that have to create sets from relations: + friend class Rel_Body; + friend_rel_ops; + + + Rel_Body *split(); + + DNF* simplified_DNF() { + simplify(); + return rel_body->simplified_DNF; + }; + + inline void invalidate_leading_info(int changed = -1) + { split()->invalidate_leading_info(changed); } + inline void enforce_leading_info(int guaranteed, int possible, int dir) + { + split()->enforce_leading_info(guaranteed, possible, dir); + } + + + void makeSet(); + void markAsSet(); + void markAsRelation(); + + friend bool operator==(const Relation &, const Relation &); + + void reverse_leading_dir_info() + { split()->reverse_leading_dir_info(); } + void interpret_unknown_as_true() + { split()->interpret_unknown_as_true(); } + void interpret_unknown_as_false() + { split()->interpret_unknown_as_false(); } + + + Rel_Body *rel_body; + + + friend 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); +}; + +inline std::ostream & operator<<(std::ostream &o, Relation &R) +{ + return o << R.print_with_subs_to_string(); +} + +Relation copy(const Relation &r); + +} // namespace + +#include <omega/Relations.h> + +#endif diff --git a/omegalib/omega/include/omega/Relations.h b/omegalib/omega/include/omega/Relations.h new file mode 100644 index 0000000..4fd81e6 --- /dev/null +++ b/omegalib/omega/include/omega/Relations.h @@ -0,0 +1,88 @@ +#if ! defined _Relations_h +#define _Relations_h 1 + +#include <map> +#include <omega/Relation.h> + +namespace omega { + +// UPDATE friend_rel_ops IN pres_gen.h WHEN ADDING TO THIS LIST +// REMEMBER TO TAKE OUT DEFAULT ARGUMENTS IN THAT FILE + +/* 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); + +// +// Operations over relations +// +Relation Union(NOT_CONST Relation &r1, NOT_CONST Relation &r2); +Relation Intersection(NOT_CONST Relation &r1, NOT_CONST Relation &r2); +Relation Extend_Domain(NOT_CONST Relation &R); +Relation Extend_Domain(NOT_CONST Relation &R, int more); +Relation Extend_Range(NOT_CONST Relation &R); +Relation Extend_Range(NOT_CONST Relation &R, int more); +Relation Extend_Set(NOT_CONST Relation &R); +Relation Extend_Set(NOT_CONST Relation &R, int more); +Relation Restrict_Domain(NOT_CONST Relation &r1, NOT_CONST Relation &r2); // Takes set as 2nd +Relation Restrict_Range(NOT_CONST Relation &r1, NOT_CONST Relation &r2); // Takes set as 2nd +Relation Domain(NOT_CONST Relation &r); // Returns set +Relation Range(NOT_CONST Relation &r); // Returns set +Relation Cross_Product(NOT_CONST Relation &A, NOT_CONST Relation &B); // Takes two sets +Relation Inverse(NOT_CONST Relation &r); +Relation After(NOT_CONST Relation &r, int carried_by, int new_output,int dir=1); +Relation Deltas(NOT_CONST Relation &R); // Returns set +Relation Deltas(NOT_CONST Relation &R, int eq_no); // Returns set +Relation DeltasToRelation(NOT_CONST Relation &R, int n_input, int n_output); +Relation Complement(NOT_CONST Relation &r); +Relation Project(NOT_CONST Relation &R, Global_Var_ID v); +Relation Project(NOT_CONST Relation &r, int pos, Var_Kind vkind); +Relation Project(NOT_CONST Relation &S, Variable_ID v); +Relation Project(NOT_CONST Relation &S, Sequence<Variable_ID> &s); +Relation Project_Sym(NOT_CONST Relation &R); +Relation Project_On_Sym(NOT_CONST Relation &R, + NOT_CONST Relation &context = Relation::Null()); +Relation GistSingleConjunct(NOT_CONST Relation &R, NOT_CONST Relation &R2, int effort=0); +Relation Gist(NOT_CONST Relation &R1, NOT_CONST Relation &R2, int effort=0); +Relation Difference(NOT_CONST Relation &r1, NOT_CONST Relation &r2); +Relation Approximate(NOT_CONST Relation &R, bool strides_allowed = false); +Relation Identity(int n_inp); +Relation Identity(NOT_CONST Relation &r); +bool Must_Be_Subset(NOT_CONST Relation &r1, NOT_CONST Relation &r2); +bool Might_Be_Subset(NOT_CONST Relation &r1, NOT_CONST Relation &r2); +// May is the same as might, just another name +bool May_Be_Subset(NOT_CONST Relation &r1, NOT_CONST Relation &r2); +bool Is_Obvious_Subset(NOT_CONST Relation &r1, NOT_CONST Relation &r2); +Relation Composition(NOT_CONST Relation &F, NOT_CONST Relation &G); +bool prepare_relations_for_composition(Relation &F, Relation &G); +Relation Join(NOT_CONST Relation &G, NOT_CONST Relation &F); +Relation EQs_to_GEQs(NOT_CONST Relation &, bool excludeStrides=false); +Relation Symbolic_Solution(NOT_CONST Relation &S); +Relation Symbolic_Solution(NOT_CONST Relation &S, Sequence<Variable_ID> &T); +Relation Sample_Solution(NOT_CONST Relation &S); +Relation Solution(NOT_CONST Relation &S, Sequence<Variable_ID> &T); +Relation Upper_Bound(NOT_CONST Relation &r); +Relation Lower_Bound(NOT_CONST Relation &r); + +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 = -1, int number_output = -1); + +// The followings might retire in the futrue!!! +void MapRel1(Relation &inputRel, + const Mapping &map, + Combine_Type ctype, + int number_input=-1, int number_output=-1, + bool invalidate_resulting_leading_info = true, + bool finalize = true); +Relation MapAndCombineRel2(Relation &R1, Relation &R2, + const Mapping &mapping1, const Mapping &mapping2, + Combine_Type ctype, + int number_input=-1, int number_output=-1); +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); + +} // namespace + +#endif diff --git a/omegalib/omega/include/omega/closure.h b/omegalib/omega/include/omega/closure.h new file mode 100644 index 0000000..67088dd --- /dev/null +++ b/omegalib/omega/include/omega/closure.h @@ -0,0 +1,31 @@ +#if ! defined _closure_h +#define _closure_h + +#include <omega/Relation.h> + +namespace omega { + +Relation VennDiagramForm( + Tuple<Relation> &Rs, + NOT_CONST Relation &Context_In); +Relation VennDiagramForm( + NOT_CONST Relation &R_In, + NOT_CONST Relation &Context_In = Relation::Null()); + +// Given a Relation R, returns a relation deltas +// that correspond to the ConicHull of the detlas of R +Relation ConicClosure (NOT_CONST Relation &R); + +Relation TransitiveClosure (NOT_CONST Relation &r, + int maxExpansion = 1, + NOT_CONST Relation &IterationSpace=Relation::Null()); + +/* Tomasz Klimek */ +Relation calculateTransitiveClosure(NOT_CONST Relation &r); + +/* Tomasz Klimek */ +Relation ApproxClosure(NOT_CONST Relation &r); + +} // namespace + +#endif diff --git a/omegalib/omega/include/omega/evac.h b/omegalib/omega/include/omega/evac.h new file mode 100644 index 0000000..a561f8c --- /dev/null +++ b/omegalib/omega/include/omega/evac.h @@ -0,0 +1,15 @@ +#if defined STUDY_EVACUATIONS + +namespace omega { + +// study the evacuation from one side of C to the other for UFS's of +// arity up to max_arity +extern void study_evacuation(Conjunct *C, which_way dir, int max_arity); + +// study the evacuation from the joined C2's output and C1's input to +// either of the other possible tuples +extern void study_evacuation(Conjunct *C1, Conjunct *C2, int max_arity); + +} // namespace + +#endif diff --git a/omegalib/omega/include/omega/farkas.h b/omegalib/omega/include/omega/farkas.h new file mode 100644 index 0000000..e77ed66 --- /dev/null +++ b/omegalib/omega/include/omega/farkas.h @@ -0,0 +1,19 @@ +#ifndef Already_Included_Affine_Closure +#define Already_Included_Affine_Closure + +#include <omega/Relation.h> + +namespace omega { + +enum Farkas_Type {Basic_Farkas, Decoupled_Farkas, + Linear_Combination_Farkas, Positive_Combination_Farkas, + Affine_Combination_Farkas, Convex_Combination_Farkas }; + +Relation Farkas(NOT_CONST Relation &R, Farkas_Type op, bool early_bailout = false); + +extern coef_t farkasDifficulty; +extern Global_Var_ID coefficient_of_constant_term; + +} // namespace + +#endif diff --git a/omegalib/omega/include/omega/hull.h b/omegalib/omega/include/omega/hull.h new file mode 100644 index 0000000..928d0c6 --- /dev/null +++ b/omegalib/omega/include/omega/hull.h @@ -0,0 +1,89 @@ +#ifndef Already_Included_Hull +#define Already_Included_Hull + +#include <omega/farkas.h> + +namespace omega { + +Relation SimpleHull(const Relation &R, bool allow_stride_constraint = false, bool allow_irregular_constraint = false); +Relation SimpleHull(const std::vector<Relation> &Rs, bool allow_stride_constraint = false, bool allow_irregular_constraint = false); + + +// All of the following first call approximate on R to +// eliminate any wildcards and strides. + +// x in Convex Hull of R +// iff +// exist a_i, y_i s.t. +// x = Sum_i a_i y_i s.t. +// forall i, y_i in R +// forall i, a_i >=0 +// sum_i a_i = 1 +Relation ConvexHull(NOT_CONST Relation &R); + +// DecoupledConvexHull is the same as ConvexHull, +// except that it only finds constraints that involve +// both variables x&y if there is a constraint +// that involves both x&y in one of the conjuncts +// of R. +Relation DecoupledConvexHull(NOT_CONST Relation &R); + +// The affine hull just consists of equality constraints +// but is otherwise the tightest hull on R. +// x in Affine Hull of R +// iff +// exist a_i, y_i s.t. +// x = Sum_i a_i y_i s.t. +// forall i, y_i in R +// sum_i a_i = 1 +Relation AffineHull(NOT_CONST Relation &R); + +// x in Linear Hull of R +// iff +// exist a_i, y_i s.t. +// x = Sum_i a_i y_i s.t. +// forall i, y_i in R +Relation LinearHull(NOT_CONST Relation &R); + +// The conic hull is the tighest cone that contains R +// x in Conic Hull of R. +// iff +// exist a_i, y_i s.t. +// x = Sum_i a_i y_i s.t. +// forall i, y_i in R +// forall i, a_i >=0 +Relation ConicHull(NOT_CONST Relation &R); + +// RectHull includes readily-available constraints from relation +// that can be part of hull, plus rectangular bounds calculated +// from input/output/set variables' range. +Relation RectHull(NOT_CONST Relation &Rel); + +// A constraint is in the result of QuickHull only if it appears in one of +// the relations and is directly implied by a single constraint in each of +// the other relations. +Relation QuickHull(Relation &R); // deprecated +Relation QuickHull(Tuple<Relation> &Rs); // deprecated + +Relation FastTightHull(NOT_CONST Relation &input_R, + NOT_CONST Relation &input_H); +Relation Hull(NOT_CONST Relation &R, + bool stridesAllowed = false, + int effort=1, + NOT_CONST Relation &knownHull = Relation::Null() + ); +Relation Hull(Tuple<Relation> &Rs, + const std::vector<bool> &validMask, + int effort = 1, + bool stridesAllowed = false, + NOT_CONST Relation &knownHull = Relation::Null()); + +// If a union of several conjuncts is a convex, their union +// representaition can be simplified by their convex hull. +Relation ConvexRepresentation(NOT_CONST Relation &R); +Relation CheckForConvexPairs(NOT_CONST Relation &S); // deprecated +Relation CheckForConvexRepresentation(NOT_CONST Relation &R_In); // deprecated + +} + +#endif diff --git a/omegalib/omega/include/omega/omega_core/debugging.h b/omegalib/omega/include/omega/omega_core/debugging.h new file mode 100644 index 0000000..e217ae9 --- /dev/null +++ b/omegalib/omega/include/omega/omega_core/debugging.h @@ -0,0 +1,30 @@ +#if !defined(Already_included_debugging) +#define Already_included_debugging + +// Debugging flags. Can set any of these. + +#include <stdio.h> +#include <ctype.h> + +namespace omega { + + + +extern int omega_core_debug; +extern int pres_debug; +extern int relation_debug; +extern int closure_presburger_debug; +extern int hull_debug; +extern int farkas_debug; +extern int code_gen_debug; + +enum negation_control { any_negation, one_geq_or_eq, one_geq_or_stride }; +extern negation_control pres_legal_negations; + +#if defined STUDY_EVACUATIONS +extern int evac_debug; +#endif + +} // namespace + +#endif diff --git a/omegalib/omega/include/omega/omega_core/oc.h b/omegalib/omega/include/omega/omega_core/oc.h new file mode 100644 index 0000000..e4f5444 --- /dev/null +++ b/omegalib/omega/include/omega/omega_core/oc.h @@ -0,0 +1,350 @@ +#ifndef Already_Included_OC +#define Already_Included_OC 1 + +#include <stdio.h> +#include <string> +#include <basic/util.h> +#include <omega/omega_core/debugging.h> +#include <basic/Tuple.h> + +namespace omega { + +// Manu:: commented the line below -- fortran bug workaround +//#define maxVars 256 /* original 56, increased by chun */ +#define maxVars 100 + +extern int maxGEQs; +extern int maxEQs; + +// Manu:: commented the lines below -- fortran bug workaround +//const int maxmaxGEQs = 2048; // original 512, increaded by chun +//const int maxmaxEQs = 512; // original 256, increased by chun +const int maxmaxGEQs = 512; +const int maxmaxEQs = 256; + +/* #if ! defined maxmaxGEQs */ +/* #define maxmaxGEQs 2048 /\* original 512, increaded by chun *\/ */ +/* #endif */ +/* #if ! defined maxmaxEQs */ +/* #define maxmaxEQs 512 /\* original 256, increased by chun *\/ */ +/* #endif */ + + +#if 0 +#if ! defined Already_Included_Portable +typedef unsigned char bool; /* what a gross thing to do */ +#endif +#endif + +typedef int EqnKey; + +enum {EQ_BLACK = 0, EQ_RED = 1}; +enum {OC_SOLVE_UNKNOWN = 2, OC_SOLVE_SIMPLIFY = 3}; + +struct eqn { + EqnKey key; + coef_t touched; // see oc_simple.c + int color; + int essential; + int varCount; + coef_t coef[maxVars + 1]; +}; + +// typedef eqn * Eqn; +enum redType {notRed=0, redEQ, redGEQ, redLEQ, redStride}; +enum redCheck {noRed=0, redFalse, redConstraints}; +enum normalizeReturnType {normalize_false, normalize_uncoupled, + normalize_coupled}; + +extern char wildName[200][20]; + +extern FILE *outputFile; /* printProblem writes its output to this file */ +#define doTrace (trace && TRACE) +#define isRed(e) (desiredResult == OC_SOLVE_SIMPLIFY && (e)->color) +// #define eqnncpy(e1,e2,s) {int *p00,*q00,*r00; p00 = (int *)(e1); q00 = (int *)(e2); r00 = &p00[headerWords+1+s]; while(p00 < r00) *p00++ = *q00++; } +// #define eqncpy(e1,e2) eqnncpy(e1,e2,nVars) +// #define eqnnzero(e,s) {int *p00,*r00; p00 = (int *)(e); r00 = &p00[headerWords+1+(s)]; while(p00 < r00) *p00++ = 0;} +// #define eqnzero(e) eqnnzero(e,nVars) + +//void eqnncpy(eqn *dest, eqn *src, int); +//void eqnnzero(eqn *e, int); + +inline void eqnncpy(eqn *dest, eqn *src, int nVars) { + dest->key = src->key; + dest->touched = src->touched; + dest->color = src->color; + dest->essential = src->essential; + dest->varCount = src->varCount; + for (int i = 0; i <= nVars; i++) + dest->coef[i] = src->coef[i]; +} + + +inline void eqnnzero(eqn *e, int nVars) { + e->key = 0; + e->touched = 0; + e->color = EQ_BLACK; + e->essential = 0; + e->varCount = 0; + for (int i = 0; i <= nVars; i++) + e->coef[i] = 0; +} + +extern int mayBeRed; + +#ifdef SPEED +#define TRACE 0 +#define DBUG 0 +#define DEBUG 0 +#else +#define TRACE (omega_core_debug) +#define DBUG (omega_core_debug > 1) +#define DEBUG (omega_core_debug > 2) +#endif + + +class Memory { +public: + int length; + coef_t stride; + redType kind; + coef_t constantTerm; + coef_t coef[maxVars + 1]; + int var[maxVars + 1]; +}; + +/* #define headerWords ((4*sizeof(int) + sizeof(coef_t))/sizeof(int)) */ + +void check_number_EQs(int); +void check_number_GEQs(int); +extern eqn SUBs[]; +extern Memory redMemory[]; + +class Problem { +public: + short nVars, safeVars; + short nEQs, nGEQs,nSUBs,nMemories,allocEQs,allocGEQs; + short varsOfInterest; + bool variablesInitialized; + bool variablesFreed; + short var[maxVars+2]; + short forwardingAddress[maxVars+2]; + // int variableColor[maxVars+2]; + int hashVersion; + const char *(*get_var_name)(unsigned int var, void *args); + void *getVarNameArgs; + eqn *GEQs; + eqn *EQs; + bool isTemporary; + + Problem(int in_eqs=0, int in_geqs=0); + Problem(const Problem &); + ~Problem(); + Problem & operator=(const Problem &); + +/* Allocation parameters and functions */ + + static const int min_alloc,first_alloc_pad; + int padEQs(int oldalloc, int newreq) { + check_number_EQs(newreq); + return min((newreq < 2*oldalloc ? 2*oldalloc : 2*newreq),maxmaxEQs); + } + int padGEQs(int oldalloc, int newreq) { + check_number_GEQs(newreq); + return min((newreq < 2*oldalloc ? 2*oldalloc : 2*newreq),maxmaxGEQs); + } + int padEQs(int newreq) { + check_number_EQs(newreq); + return min(max(newreq+first_alloc_pad,min_alloc), maxmaxEQs); + } + int padGEQs(int newreq) { + check_number_GEQs(newreq); + return min(max(newreq+first_alloc_pad,min_alloc), maxmaxGEQs); + } + + + void zeroVariable(int i); + + void putVariablesInStandardOrder(); + void noteEssential(int onlyWildcards); + int findDifference(int e, int &v1, int &v2); + int chainKill(int color,int onlyWildcards); + + int newGEQ(); + int newEQ(); + int newSUB(){ + return nSUBs++; + } + + + void initializeProblem(); + void initializeVariables() const; + void printProblem(int debug = 1) const; + void printSub(int v) const; + std::string print_sub_to_string(int v) const; + void clearSubs(); + void printRedEquations() const; + int countRedEquations() const; + int countRedGEQs() const; + int countRedEQs() const; + int countRedSUBs() const; + void difficulty(int &numberNZs, coef_t &maxMinAbsCoef, coef_t &sumMinAbsCoef) const; + int prettyPrintProblem() const; + std::string prettyPrintProblemToString() const; + int prettyPrintRedEquations() const; + int simplifyProblem(int verify, int subs, int redundantElimination); + int simplifyProblem(); + int simplifyAndVerifyProblem(); + int simplifyApproximate(bool strides_allowed); + void coalesce(); + void partialElimination(); + void unprotectVariable(int var); + void negateGEQ(int); + void convertEQstoGEQs(bool excludeStrides); + void convertEQtoGEQs(int eq); + void nameWildcard(int i); + void useWildNames(); + void ordered_elimination(int symbolic); + int eliminateRedundant (bool expensive); + void eliminateRed(bool expensive); + void constrainVariableSign(int color, int var, int sign); + void constrainVariableValue(int color, int var, int value); + void query_difference(int v1, int v2, coef_t &lowerBound, coef_t &upperBound, bool &guaranteed); + int solve(int desiredResult); + std::string print_term_to_string(const eqn *e, int c) const; + void printTerm(const eqn * e, int c) const; + std::string printEqnToString(const eqn * e, int test, int extra) const; + void sprintEqn(char *str, const eqn * e, int is_geq,int extra) const; + void printEqn(const eqn * e, int is_geq, int extra) const; + void printEQ(const eqn *e) const {printEqn(e,0,0); } + std::string print_EQ_to_string(const eqn *e) const {return printEqnToString(e,0,0);} + std::string print_GEQ_to_string(const eqn *e) const {return printEqnToString(e,1,0);} + void printGEQ(const eqn *e) const {printEqn(e,1,0); } + void printGEQextra(const eqn *e) const {printEqn(e,1,1); } + void printSubstitution(int s) const; + void printVars(int debug = 1) const; + void swapVars(int i, int j); + void reverseProtectedVariables(); + redCheck redSimplifyProblem(int effort, int computeGist); + + // calculate value of variable mod integer from set of equations -- by chun 12/14/2006 + coef_t query_variable_mod(int v, coef_t factor, int color=EQ_BLACK, int nModularEQs=0, int nModularVars=0) const; + coef_t query_variable_mod(int v, coef_t factor, int color, int nModularEQs, int nModularVars, Tuple<bool> &working_on) const; // helper function + + int queryVariable(int i, coef_t *lowerBound, coef_t *upperBound); + int query_variable_bounds(int i, coef_t *l, coef_t *u); + void queryCoupledVariable(int i, coef_t *l, coef_t *u, int *couldBeZero, coef_t lowerBound, coef_t upperBound); + int queryVariableSigns(int i, int dd_lt, int dd_eq, int dd_gt, coef_t lowerBound, coef_t upperBound, bool *distKnown, coef_t *dist); + void addingEqualityConstraint(int e); + normalizeReturnType normalize(); + void normalize_ext(); + void cleanoutWildcards(); + void substitute(eqn *sub, int i, coef_t c); + void deleteVariable( int i); + void deleteBlack(); + int addNewProtectedWildcard(); + int addNewUnprotectedWildcard(); + int protectWildcard( int i); + void doMod( coef_t factor, int e, int j); + void freeEliminations( int fv); + int verifyProblem(); + void resurrectSubs(); + int solveEQ(); + int combineToTighten() ; + int quickKill(int onlyWildcards, bool desperate = false); + int expensiveEqualityCheck(); + int expensiveRedKill(); + int expensiveKill(); + int smoothWeirdEquations(); + void quickRedKill(int computeGist); + void chainUnprotect(); + void freeRedEliminations(); + void doElimination( int e, int i); + void analyzeElimination( + int &v, + int &darkConstraints, + int &darkShadowFeasible, + int &unit, + coef_t ¶llelSplinters, + coef_t &disjointSplinters, + coef_t &lbSplinters, + coef_t &ubSplinters, + int ¶llelLB); + int parallelSplinter(int e, int diff, int desiredResult); + int solveGEQ( int desiredResult); + void setInternals(); + void setExternals(); + int reduceProblem(); + void problem_merge(Problem &); + void deleteRed(); + void turnRedBlack(); + void checkGistInvariant() const; + void check() const; + coef_t checkSum() const; + void rememberRedConstraint(eqn *e, redType type, coef_t stride); + void recallRedMemories(); + void simplifyStrideConstraints(); + const char * orgVariable(int i) const { + return ((i == 0) ? // cfront likes this form better + "1" : + ((i < 0) ? + wildName[-i] : + (*get_var_name)(i,getVarNameArgs))); + }; + const char * variable(int i) const { + return orgVariable(var[i]) ; + }; + + void deleteGEQ(int e) { + if (DEBUG) { + fprintf(outputFile,"Deleting %d (last:%d): ",e,nGEQs-1); + printGEQ(&GEQs[e]); + fprintf(outputFile,"\n"); + }; + if (e < nGEQs-1) + eqnncpy (&GEQs[e], &GEQs[nGEQs - 1],(nVars)); + nGEQs--; + }; + void deleteEQ(int e) { + if (DEBUG) { + fprintf(outputFile,"Deleting %d (last:%d): ",e,nEQs-1); + printGEQ(&EQs[e]); + fprintf(outputFile,"\n"); + }; + if (e < nEQs-1) + eqnncpy (&EQs[e], &EQs[nEQs - 1],(nVars)); + nEQs--; + }; + +}; + + + +/* #define UNKNOWN 2 */ +/* #define SIMPLIFY 3 */ +/* #define _red 1 */ +/* #define black 0 */ + + +extern int print_in_code_gen_style; + + +void initializeOmega(void); + + +/* set extra to 0 for normal use */ +int singleVarGEQ(eqn *e); + +void setPrintLevel(int level); + +void printHeader(); + +void setOutputFile(FILE *file); + +extern void check_number_EQs(int nEQs); +extern void check_number_GEQs(int nGEQs); +extern void checkVars(int nVars); + +} // namespace + +#endif diff --git a/omegalib/omega/include/omega/omega_core/oc_i.h b/omegalib/omega/include/omega/omega_core/oc_i.h new file mode 100644 index 0000000..9533a40 --- /dev/null +++ b/omegalib/omega/include/omega/omega_core/oc_i.h @@ -0,0 +1,79 @@ +#if !defined(Already_included_oc_i) +#define Already_included_oc_i + +#include <basic/util.h> +#include <omega/omega_core/oc.h> +#include <stdlib.h> +#include <assert.h> +#include <string> +#include <vector> + +namespace omega { + +#define maxWildcards 18 + +extern int findingImplicitEqualities; +extern int firstCheckForRedundantEquations; +extern int use_ugly_names; +extern int doItAgain; +extern int newVar; +extern int conservative; +extern FILE *outputFile; /* printProblem writes its output to this file */ +extern int nextWildcard; +extern int trace; +extern int depth; +extern int packing[maxVars]; +extern int headerLevel; +extern int inApproximateMode; +extern int inStridesAllowedMode; +extern int addingOuterEqualities; +extern int outerColor; + +const int keyMult = 31; +const int hashTableSize =5*maxmaxGEQs; +const int maxKeys = 8*maxmaxGEQs; +extern int hashVersion; +extern eqn hashMaster[hashTableSize]; +extern int fastLookup[maxKeys*2]; +extern int nextKey; + +extern int reduceWithSubs; +extern int pleaseNoEqualitiesInSimplifiedProblems; + +#define noProblem ((Problem *) 0) + +extern Problem *originalProblem; +int checkIfSingleVar(eqn *e, int i); +/* Solve e = factor alpha for x_j and substitute */ + +void negateCoefficients(eqn * eqn, int nV); + +extern int omegaInitialized; +extern Problem full_answer, context,redProblem; + +#if defined BRAIN_DAMAGED_FREE +static inline void free(const Problem *p) +{ + free((char *)p); +} +#endif + +#if defined NDEBUG +#define CHECK_FOR_DUPLICATE_VARIABLE_NAMES +#else +#define CHECK_FOR_DUPLICATE_VARIABLE_NAMES \ + { \ + std::vector<std::string> name(nVars); \ + for(int i=1; i<=nVars; i++) { \ + name[i-1] = variable(i); \ + assert(!name[i-1].empty()); \ + for(int j=1; j<i; j++) \ + assert(!(name[i-1] == name[j-1])); \ + } \ + } +#endif + + +} // namespace + +#endif diff --git a/omegalib/omega/include/omega/omega_i.h b/omegalib/omega/include/omega/omega_i.h new file mode 100644 index 0000000..e5d9230 --- /dev/null +++ b/omegalib/omega/include/omega/omega_i.h @@ -0,0 +1,30 @@ +#if ! defined _omega_i_h +#define _omega_i_h 1 + +#include <omega/pres_var.h> + +namespace omega { + +/* #define Assert(c,t) if(!(c)) PresErrAssert(t) */ +/* void PresErrAssert(const char *t); */ + +extern Rel_Body null_rel; + +extern int skip_finalization_check; +// extern int skip_set_checks; + +// Global input and output variable tuples. + +extern Global_Input_Output_Tuple input_vars; +extern Global_Input_Output_Tuple output_vars; +extern Global_Input_Output_Tuple &set_vars; + +} // namespace + +#if ! defined DONT_INCLUDE_TEMPLATE_CODE +// with g++258, everything will need to make Tuple<Relation>, as a +// function taking it as an argument is a friend of lots of classes +#include <omega/Relation.h> +#endif + +#endif diff --git a/omegalib/omega/include/omega/pres_cmpr.h b/omegalib/omega/include/omega/pres_cmpr.h new file mode 100644 index 0000000..fb3e6f0 --- /dev/null +++ b/omegalib/omega/include/omega/pres_cmpr.h @@ -0,0 +1,49 @@ +#if ! defined _pres_cmpr_h +#define _pres_cmpr_h 1 + +#include <omega/omega_core/oc.h> + +namespace omega { + +// +// Compressed problem: rectangular non-0 cut from the big problem. +// +class Comp_Constraints { +public: + Comp_Constraints(eqn *constrs, int no_constrs, int no_vars); + void UncompressConstr(eqn *constrs, short &pn_constrs); + ~Comp_Constraints(); + bool no_constraints() const + { return n_constrs == 0; } + int n_constraints() const + { return n_constrs; } + +protected: + inline int coef_index(int e, int v) + {return e*(n_vars+1) + v;} + +private: + int n_constrs; + int n_vars; + coef_t *coefs; +}; + +class Comp_Problem { +public: + Comp_Problem(Problem *problem); + Problem *UncompressProblem(); + bool no_constraints() const + { return eqs.no_constraints() && geqs.no_constraints(); } + +private: +/* === data === */ + int _nVars, _safeVars; + const char *(*_get_var_name)(unsigned int var, void *args); + void *_getVarNameArgs; + Comp_Constraints eqs; + Comp_Constraints geqs; +}; + +} // namespace + +#endif diff --git a/omegalib/omega/include/omega/pres_cnstr.h b/omegalib/omega/include/omega/pres_cnstr.h new file mode 100644 index 0000000..7b2d98d --- /dev/null +++ b/omegalib/omega/include/omega/pres_cnstr.h @@ -0,0 +1,192 @@ +#if ! defined _pres_cnstr_h +#define _pres_cnstr_h 1 + +#include <omega/pres_var.h> +#include <vector> + +namespace omega { + +// +// Constraint handles +// + + + +void copy_constraint(Constraint_Handle H, const Constraint_Handle initial); + +class Constraint_Handle { +public: + Constraint_Handle() {} + virtual ~Constraint_Handle() {} + + void update_coef(Variable_ID, coef_t delta); + void update_const(coef_t delta); + coef_t get_coef(Variable_ID v) const; + coef_t get_const() const; + bool has_wildcards() const; + int max_tuple_pos() const; + int min_tuple_pos() const; + bool is_const(Variable_ID v); + bool is_const_except_for_global(Variable_ID v); + + virtual std::string print_to_string() const; + virtual std::string print_term_to_string() const; + + Variable_ID get_local(const Global_Var_ID G); + Variable_ID get_local(const Global_Var_ID G, Argument_Tuple of); + // not sure that the second one can be used in a meaningful + // way if the conjunct is in multiple relations + + void finalize(); + void multiply(int multiplier); + Rel_Body *relation() const; + + +protected: + Conjunct *c; + eqn **eqns; + int e; + + friend class Constr_Vars_Iter; + friend class Constraint_Iterator; + + Constraint_Handle(Conjunct *, eqn **, int); + +#if defined PROTECTED_DOESNT_WORK + friend class EQ_Handle; + friend class GEQ_Handle; +#endif + + void update_coef_during_simplify(Variable_ID, coef_t delta); + void update_const_during_simplify(coef_t delta); + coef_t get_const_during_simplify() const; + coef_t get_coef_during_simplify(Variable_ID v) const; + + +public: + friend class Conjunct; // assert_leading_info updates coef's + // as does move_UFS_to_input + friend class DNF; // and DNF::make_level_carried_to + + friend void copy_constraint(Constraint_Handle H, + const Constraint_Handle initial); + // copy_constraint does updates and gets at c and e + +}; + +class GEQ_Handle : public Constraint_Handle { +public: + inline GEQ_Handle() {} + + virtual std::string print_to_string() const; + virtual std::string print_term_to_string() const; + bool operator==(const Constraint_Handle &that); + + void negate(); + +private: + friend class Conjunct; + friend class GEQ_Iterator; + + GEQ_Handle(Conjunct *, int); +}; + + +class EQ_Handle : public Constraint_Handle { +public: + inline EQ_Handle() {} + + virtual std::string print_to_string() const; + virtual std::string print_term_to_string() const; + bool operator==(const Constraint_Handle &that); + +private: + friend class Conjunct; + friend class EQ_Iterator; + + EQ_Handle(Conjunct *, int); +}; + + +// +// Conjuct iterators -- for querying resulting DNF. +// +class Constraint_Iterator : public Generator<Constraint_Handle> { +public: + Constraint_Iterator(Conjunct *); + int live() const; + void operator++(int); + void operator++(); + Constraint_Handle operator* (); + Constraint_Handle operator* () const; + +private: + Conjunct *c; + int current,last; + eqn **eqns; +}; + + +class EQ_Iterator : public Generator<EQ_Handle> { +public: + EQ_Iterator(Conjunct *); + int live() const; + void operator++(int); + void operator++(); + EQ_Handle operator* (); + EQ_Handle operator* () const; + +private: + Conjunct *c; + int current, last; +}; + + +class GEQ_Iterator : public Generator<GEQ_Handle> { +public: + GEQ_Iterator(Conjunct *); + int live() const; + void operator++(int); + void operator++(); + GEQ_Handle operator* (); + GEQ_Handle operator* () const; + +private: + Conjunct *c; + int current, last; +}; + + +// +// Variables of constraint iterator. +// +struct Variable_Info { + Variable_ID var; + coef_t coef; + Variable_Info(Variable_ID _var, coef_t _coef) + { var = _var; coef = _coef; } +}; + +class Constr_Vars_Iter : public Generator<Variable_Info> { +public: + Constr_Vars_Iter(const Constraint_Handle &ch, bool _wild_only = false); + int live() const; + void operator++(int); + void operator++(); + Variable_Info operator*() const; + + Variable_ID curr_var() const; + coef_t curr_coef() const; + +private: + eqn **eqns; + int e; + Problem *prob; + Variable_ID_Tuple &vars; + bool wild_only; + int current; +}; + +} // namespace + +#endif diff --git a/omegalib/omega/include/omega/pres_conj.h b/omegalib/omega/include/omega/pres_conj.h new file mode 100644 index 0000000..ea10a2c --- /dev/null +++ b/omegalib/omega/include/omega/pres_conj.h @@ -0,0 +1,299 @@ +#if ! defined _pres_conj_h +#define _pres_conj_h 1 + +#include <limits.h> +#include <omega/pres_decl.h> +#include <omega/pres_logic.h> +#include <omega/pres_cnstr.h> + +namespace omega { + +// +// Conjunct +// +// About variables in Conjunct: +// All varaibles appear in exactly one declaration. +// All variables used in Conjunct are referenced in mappedVars. +// Wildcard variables are referenced both in mappedVars and in myLocals, +// since they are declared in the conjunct. +// All other variables are declared at the levels above. +// Column number is: +// in forwardingAddress in Problem if variablesInitialized is set, +// equal to position of Variable_ID in mappedVars list otherwise. +// + +class Conjunct : public F_Declaration { +public: + Constraint_Iterator constraints(); + Variable_ID_Tuple *variables(); + EQ_Iterator EQs(); + GEQ_Iterator GEQs(); + inline int n_EQs() { return problem->nEQs; } + inline int n_GEQs() { return problem->nGEQs; } + + void promise_that_ub_solutions_exist(Relation &R); + + inline Node_Type node_type() {return Op_Conjunct;} + + inline int is_true() {return problem->nEQs==0 && problem->nGEQs==0 + && exact;} + + void query_difference(Variable_ID v1, Variable_ID v2, + coef_t &lowerBound, coef_t &upperBound, bool &guaranteed); + void query_variable_bounds(Variable_ID v, coef_t &lowerBound, coef_t &upperBound); + coef_t query_variable_mod(Variable_ID v, coef_t factor); + bool query_variable_used(Variable_ID v); + + int countNonzeros() const { + int numberNZs; + coef_t maxCoef, SumAbsCoef; + problem->difficulty(numberNZs,maxCoef,SumAbsCoef); + return numberNZs; + } + + void difficulty(int &numberNZs, coef_t &maxCoef, coef_t &SumAbsCoef) const { + problem->difficulty(numberNZs,maxCoef,SumAbsCoef); + } + + int query_guaranteed_leading_0s() { + count_leading_0s(); + return guaranteed_leading_0s; + } + + int query_possible_leading_0s() { + count_leading_0s(); + return possible_leading_0s; + } + + int query_leading_dir() { + count_leading_0s(); + return leading_dir; + } + + void calculate_dimensions(Relation &R, int &ndim_all, int &ndim_domain); + int max_ufs_arity_of_set(); + int max_ufs_arity_of_in(); + int max_ufs_arity_of_out(); + + int rank(); + + ~Conjunct(); + + bool is_unknown() const; + inline bool is_exact() const { return exact;} + inline bool is_inexact() const { return !exact;} + inline void make_inexact() { exact=false;} + + +#if ! defined NDEBUG + void assert_leading_info(); +#else + void assert_leading_info() {} +#endif + + + // PRINTING FUNCTIONS + void print(FILE *output_file); + void prefix_print(FILE *output_file, int debug = 1); + std::string print_to_string(int true_printed); + std::string print_EQ_to_string(eqn *e) { return problem->print_EQ_to_string(e); } + std::string print_GEQ_to_string(eqn *e) { return problem->print_GEQ_to_string(e); } + std::string print_EQ_to_string(int e) + { return problem->print_EQ_to_string(&(problem->EQs[e])); } + std::string print_GEQ_to_string(int e) + { return problem->print_GEQ_to_string(&(problem->GEQs[e])); } + std::string print_term_to_string(eqn *e) { return problem->print_term_to_string(e,1); } + std::string print_EQ_term_to_string(int e) + { return problem->print_term_to_string(&(problem->EQs[e]),1); } + std::string print_GEQ_term_to_string(int e) + { return problem->print_term_to_string(&(problem->GEQs[e]),1); } + std::string print_sub_to_string(int col) { return problem->print_sub_to_string(col); } + +private: + + inline void interpret_unknown_as_true() { exact=true;} + + friend Relation approx_closure(NOT_CONST Relation &r, int n); + + virtual Conjunct *really_conjunct(); + + + // create new constraints with all co-efficients 0 + // These are public in F_And, use them from there. + EQ_Handle add_stride(int step, int preserves_level = 0); + EQ_Handle add_EQ(int preserves_level = 0); + GEQ_Handle add_GEQ(int preserves_level = 0); + EQ_Handle add_EQ(const Constraint_Handle &c, int preserves_level = 0); + GEQ_Handle add_GEQ(const Constraint_Handle &c, int preserves_level = 0); + + friend class GEQ_Handle; + friend class EQ_Handle; + friend class Sub_Handle; + friend class Constraint_Handle; + friend class Constraint_Iterator; + friend class GEQ_Iterator; + friend class EQ_Iterator; + friend class Sub_Iterator; + friend class Constr_Vars_Iter; + + + // FUNCTIONS HAVING TO DO WITH BUILDING FORMULAS/DNFs + bool can_add_child(); + void remap(); + void beautify(); + DNF* DNFize(); + int priority(); + virtual Conjunct *find_available_conjunct(); + void finalize(); + + friend class DNF; + + + + // CREATING CONJUNCTS + Conjunct(); + Conjunct(Conjunct &); + Conjunct(Formula *, Rel_Body *); + + friend class Formula; // add_conjunct (a private function) creates Conjuncts + friend class F_Not; + friend class F_Or; + // class F_And; is a friend below + + + // VARIOUS FUNCTIONS TO CREATE / WORK WITH VARIABLES + Variable_ID declare(Const_String s); + Variable_ID declare(); + Variable_ID declare(Variable_ID v); + + friend const char *get_var_name(unsigned int, void *); + void push_exists(Variable_ID_Tuple &S); + int get_column(Variable_ID); + int find_column(Variable_ID); + int map_to_column(Variable_ID); + void combine_columns(); + void reorder(); + void reorder_for_print(bool reverseOrder=false, + int first_pass_input=0, + int first_pass_output=0, + bool sort=false); + + friend void remap_DNF_vars(Rel_Body *new_rel, Rel_Body *old_rel); + + void localize_var(Variable_ID D); + + + // this creates variables in conjuncts for us: + friend int new_WC(Conjunct *nc, Problem *np); + + + // UFS/LEADING ZEROS STUFF + + void move_UFS_to_input(); + + void count_leading_0s(); + void invalidate_leading_info(int changed = -1); + void enforce_leading_info(int guaranteed, int possible, int dir); + + void reverse_leading_dir_info(); + + + + // CONJUNCT SPECIFIC STUFF + + void rm_color_constrs(); + inline int N_protected() { return problem->safeVars; } + + + void ordered_elimination(int symLen) { problem->ordered_elimination(symLen);} + void convertEQstoGEQs(bool excludeStrides); + + int cost(); + + inline Formula* copy(Formula *parent, Rel_Body *reln) + { return copy_conj_diff_relation(parent,reln); } + Conjunct* copy_conj_diff_relation(Formula *parent, Rel_Body *reln); + inline Conjunct* copy_conj_same_relation() + { return copy_conj_diff_relation(&(parent()), relation()); } + friend void internal_copy_conjunct(Conjunct* to, Conjunct* fr); + friend void copy_constraint(Constraint_Handle H, + const Constraint_Handle initial); + +#if defined STUDY_EVACUATIONS + // The core function of "evac.c" does lots of work with conjuncts: + friend bool check_subseq_n(Conjunct *c, Sequence<Variable_ID> &evac_from, Sequence<Variable_ID> &evac_to, int n_from, int n_to, int max_arity, int n, bool allow_offset); + friend void assert_subbed_syms(Conjunct *c); + friend bool check_affine(Conjunct *d, Sequence<Variable_ID> &evac_from, Sequence<Variable_ID> &evac_to, int n_from, int n_to, int max_arity); + friend evac study(Conjunct *C, Sequence<Variable_ID> &evac_from, Sequence<Variable_ID> &evac_to, int n_from, int n_to, int max_arity); +#endif + + // The relational ops tend to do lots of demented things to Conjuncts: + friend class Rel_Body; + friend_rel_ops; + + // F_And sometimes absorbs conjuncts + friend class F_And; + + // Various DNFize functions also get a the problem: + + friend DNF* conj_and_not_dnf(Conjunct *pos_conj, DNF *neg_conjs, bool weak); + friend class F_Exists; + + // Substitutions are a wrapper around a low-level Problem operation + friend class Substitutions; + + // private functions to call problem functions + int simplifyProblem(); + int simplifyProblem(int verify, int subs, int redundantElimination); + int redSimplifyProblem(int effort, int computeGist); + + friend int simplify_conj(Conjunct* conj, int ver_sim, int elim_red, int color); + friend DNF* negate_conj(Conjunct* conj); + friend Conjunct* merge_conjs(Conjunct* conj1, Conjunct* conj2, + Merge_Action action, Rel_Body *body = 0); + friend void copy_conj_header(Conjunct* to, Conjunct* fr); + + + // === at last, the data === + + Variable_ID_Tuple mappedVars; + + int n_open_constraints; + bool cols_ordered; + bool simplified; + bool verified; + + int guaranteed_leading_0s; // -1 if unknown + int possible_leading_0s; // -1 if unknown + int leading_dir; // 0 if unknown, else +/- 1 + int leading_dir_valid_and_known(); + + bool exact; + + short r_constrs; // are redundant constraints eliminated? + Problem *problem; + + bool is_compressed(); + void compress(); + void uncompress(); + + friend class Comp_Problem; + Comp_Problem *comp_problem; +}; + + +/* === Misc. problem manipulation utilities === */ + +const int CantBeNegated = INT_MAX-10; +const int AvoidNegating = INT_MAX-11; + +void copy_column(Problem *tp, int to_col, + Problem *fp, int fr_col, + int start_EQ, int start_GEQ); +void zero_column(Problem *tp, int to_col, + int start_EQ, int start_GEQ, + int no_EQs, int no_GEQs); + +} // namespace + +#endif diff --git a/omegalib/omega/include/omega/pres_decl.h b/omegalib/omega/include/omega/pres_decl.h new file mode 100644 index 0000000..7fec0bc --- /dev/null +++ b/omegalib/omega/include/omega/pres_decl.h @@ -0,0 +1,55 @@ +#if ! defined _pres_decl_h +#define _pres_decl_h 1 + +#include <omega/pres_var.h> +#include <omega/pres_form.h> +#include <basic/Section.h> + +namespace omega { + +// +// Base class for presburger formula nodes with variables +// + +class F_Declaration : public Formula { +public: + virtual Variable_ID declare(Const_String s)=0; + virtual Variable_ID declare()=0; + virtual Variable_ID declare(Variable_ID)=0; + virtual Section<Variable_ID> declare_tuple(int size); + + void finalize(); + + inline Variable_ID_Tuple &locals() {return myLocals;} + +protected: + F_Declaration(Formula *, Rel_Body *); + F_Declaration(Formula *, Rel_Body *, Variable_ID_Tuple &); + ~F_Declaration(); + + Variable_ID do_declare(Const_String s, Var_Kind var_kind); + + void prefix_print(FILE *output_file, int debug = 1); + void print(FILE *output_file); + + void setup_names(); + void setup_anonymous_wildcard_names(); + + Variable_ID_Tuple myLocals; + friend class F_Forall; // rearrange needs to access myLocals + friend class F_Or; // push_exists + +private: + virtual bool can_add_child(); + + int priority(); + + friend 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); +}; + +} // namespace + +#endif diff --git a/omegalib/omega/include/omega/pres_dnf.h b/omegalib/omega/include/omega/pres_dnf.h new file mode 100644 index 0000000..93d5942 --- /dev/null +++ b/omegalib/omega/include/omega/pres_dnf.h @@ -0,0 +1,87 @@ +#if ! defined _pres_dnf_h +#define _pres_dnf_h 1 + +#include <omega/pres_gen.h> + +namespace omega { + +// +// Disjunctive Normal Form -- list of Conjuncts +// +class DNF { +public: + void print(FILE *out_file); + void prefix_print(FILE *out_file, int debug = 1, bool parent_names_setup=false); + + bool is_definitely_false() const; + bool is_definitely_true() const; + int length() const; + + Conjunct *single_conjunct() const; + bool has_single_conjunct() const; + Conjunct *rm_first_conjunct(); + void clear(); + int query_guaranteed_leading_0s(int what_to_return_for_empty_dnf); + int query_possible_leading_0s(int what_to_return_for_empty_dnf); + int query_leading_dir(); + +private: + // all DNFize functions need to access the dnf builders: + friend class F_And; + friend class F_Or; + friend class Conjunct; + friend DNF * negate_conj(Conjunct *); + + friend class Rel_Body; + friend_rel_ops; + + DNF(); + ~DNF(); + + DNF* copy(Rel_Body *); + + void simplify(); + void make_level_carried_to(int level); + void count_leading_0s(); + + void add_conjunct(Conjunct*); + void join_DNF(DNF*); + void rm_conjunct(Conjunct *c); + + void rm_redundant_conjs(int effort); + void rm_redundant_inexact_conjs(); + void DNF_to_formula(Formula* root); + + + friend void remap_DNF_vars(Rel_Body *new_rel, Rel_Body *old_rel); + void remap(); + + void setup_names(); + + void remove_inexact_conj(); + + // These may need to get at the conjList itself: + friend DNF* DNF_and_DNF(DNF*, DNF*); + friend DNF* DNF_and_conj(DNF*, Conjunct*); + friend DNF* conj_and_not_dnf(Conjunct *pos_conj, DNF *neg_conjs, bool weak); + + friend class DNF_Iterator; + + List<Conjunct*> conjList; +}; + +DNF* conj_and_not_dnf(Conjunct *pos_conj, DNF *neg_conjs, bool weak=false); + +// +// DNF iterator +// +class DNF_Iterator : public List_Iterator<Conjunct*> { +public: + DNF_Iterator(DNF*dnf) : List_Iterator<Conjunct*>(dnf->conjList) {} + DNF_Iterator() {} + void curr_set(Conjunct *c) { *(*this) = c; } +}; + +} // namespace + +#endif diff --git a/omegalib/omega/include/omega/pres_form.h b/omegalib/omega/include/omega/pres_form.h new file mode 100644 index 0000000..ed3258e --- /dev/null +++ b/omegalib/omega/include/omega/pres_form.h @@ -0,0 +1,112 @@ +#if ! defined _pres_form_h +#define _pres_form_h 1 + +#include <omega/pres_gen.h> + +namespace omega { + +typedef enum {Op_Relation, Op_Not, Op_And, Op_Or, + Op_Conjunct, Op_Forall, Op_Exists} Node_Type; + + +// +// Presburger Formula base class +// + +class Formula { +public: + virtual Node_Type node_type()=0; + + F_Forall *add_forall(); + F_Exists *add_exists(); + virtual F_And *and_with(); + F_And *add_and(); + F_Or *add_or(); + F_Not *add_not(); + void add_unknown(); + + virtual void finalize(); + virtual void print(FILE *output_file); + + Rel_Body *relation() { return myRelation; } + +protected: + virtual ~Formula(); +private: + Formula(Formula *, Rel_Body *); + + // The relational operations need to work with formula trees + friend class Relation; + friend_rel_ops; + // as do the functions that build DNF's + friend class DNF; + // or other parts of the tree + friend class Conjunct; + friend class F_Declaration; + friend class F_Exists; + friend class F_Forall; + friend class F_Or; + friend class F_And; + friend class F_Not; + friend class Rel_Body; + + + // Operations needed for manipulation of formula trees: + + void remove_child(Formula *); + void replace_child(Formula *child, Formula *new_child); + virtual bool can_add_child(); + void add_child(Formula *); + + Conjunct *add_conjunct(); + virtual Conjunct *find_available_conjunct() = 0; + + virtual Formula *copy(Formula *parent, Rel_Body *reln); + F_Exists *add_exists(Variable_ID_Tuple &S); + virtual void push_exists(Variable_ID_Tuple &S); + + // Accessor functions for tree building + + List<Formula*> &children() {return myChildren;} + int n_children() const {return myChildren.length();} + const List<Formula*> &get_children() const {return myChildren;} + Formula &parent() {return *myParent;} + void set_parent(Formula *p) {myParent = p;} + + + virtual int priority(); + + void verify_tree(); // should be const, but iterators are used + + virtual void reverse_leading_dir_info(); + virtual void invalidate_leading_info(int changed = -1); + virtual void enforce_leading_info(int guaranteed, int possible, int dir); + + virtual void remap(); + virtual DNF* DNFize() = 0; + virtual void beautify(); + virtual void rearrange(); + virtual void setup_names(); + + virtual void print_separator(FILE *output_file); + virtual void combine_columns(); + virtual void prefix_print(FILE *output_file, int debug = 1); + void print_head(FILE *output_file); + + void set_relation(Rel_Body *r); + void set_parent(Formula *parent, Rel_Body *reln); + + void assert_not_finalized(); + + virtual Conjunct *really_conjunct(); // until we get RTTI + +private: + List<Formula*> myChildren; + Formula *myParent; + Rel_Body *myRelation; + +}; + +} // namespace + +#endif diff --git a/omegalib/omega/include/omega/pres_gen.h b/omegalib/omega/include/omega/pres_gen.h new file mode 100644 index 0000000..ba6a793 --- /dev/null +++ b/omegalib/omega/include/omega/pres_gen.h @@ -0,0 +1,192 @@ +#if ! defined _pres_gen_h +#define _pres_gen_h 1 + +#include <omega/omega_core/oc.h> +#include <basic/ConstString.h> +#include <basic/Iterator.h> +#include <basic/List.h> +#include <basic/Tuple.h> +#include <assert.h> +#include <stdlib.h> + +namespace omega { + +// +// general presburger stuff thats needed everywhere +// + +/* The following allows us to avoid warnings about passing + temporaries as non-const references. This is useful but + has suddenly become illegal. */ + +#if !defined(LIE_ABOUT_CONST_TO_MAKE_ANSI_COMMITTEE_HAPPY) +#if defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7)) +#define LIE_ABOUT_CONST_TO_MAKE_ANSI_COMMITTEE_HAPPY 1 +#else +#define LIE_ABOUT_CONST_TO_MAKE_ANSI_COMMITTEE_HAPPY 0 +#endif +#endif + +#if LIE_ABOUT_CONST_TO_MAKE_ANSI_COMMITTEE_HAPPY +#define NOT_CONST const +#else +#define NOT_CONST +#endif + +// +// I/O and error processing and control flags (also in omega_core/debugging.h) +// + +extern FILE *DebugFile; +extern int pres_debug; + +extern int mega_total; +extern int use_ugly_names; + +extern negation_control pres_legal_negations; + + +// +// Lots of things refer to each other, +// so we forward declare these classes: +// + +class Var_Decl; +typedef enum {Input_Var, Set_Var = Input_Var, Output_Var, + Global_Var, Forall_Var, Exists_Var, Wildcard_Var} Var_Kind; +class Global_Var_Decl; +typedef enum {Unknown_Tuple = 0, Input_Tuple = 1, Output_Tuple = 2, + Set_Tuple = Input_Tuple } Argument_Tuple; + +class Constraint_Handle; +class EQ_Handle; +class GEQ_Handle; +typedef EQ_Handle Stride_Handle; + +class Formula; +class F_Declaration; +class F_Forall; +class F_Exists; +class F_And; +class F_Or; +class F_Not; +class Conjunct; +class Relation; +class Rel_Body; +class DNF; +class Mapping; +class Omega_Var; +class Coef_Var_Decl; + +typedef Var_Decl *Variable_ID; +typedef Global_Var_Decl *Global_Var_ID; + +typedef Tuple<Variable_ID> Variable_ID_Tuple; +typedef Sequence<Variable_ID> Variable_ID_Sequence; // use only for rvalues +typedef Tuple_Iterator<Variable_ID> Variable_ID_Tuple_Iterator; +typedef Tuple_Iterator<Variable_ID> Variable_ID_Iterator; + +typedef Variable_ID_Iterator Variable_Iterator; + +typedef enum {Comb_Id, Comb_And, Comb_Or, Comb_AndNot} Combine_Type; + + +// things that are (hopefully) used only privately +class Comp_Problem; +class Comp_Constraints; + +// this has to be here rather than in pres_conj.h because +// MergeConj has to be a friend of Constraint_Handle +typedef enum {MERGE_REGULAR, MERGE_COMPOSE, MERGE_GIST} Merge_Action; + + +// Conjunct can be exact or lower or upper bound. +// For lower bound conjunct, the upper bound is assumed to be true; +// For upper bound conjunct, the lower bound is assumed to be false + +typedef enum {EXACT_BOUND, UPPER_BOUND, LOWER_BOUND, UNSET_BOUND} Bound_Type; + + +#if defined STUDY_EVACUATIONS +typedef enum { in_to_out = 0, out_to_in = 1} which_way; + +enum evac { evac_trivial = 0, + evac_offset = 1, + evac_subseq = 2, + evac_offset_subseq = 3, +// evac_permutation = , + evac_affine = 4, + evac_nasty = 5 }; + +extern char *evac_names[]; + +#endif + +// the following list should be updated in sync with Relations.h + +#define friend_rel_ops \ +friend Relation Union(NOT_CONST Relation &r1, NOT_CONST Relation &r2); \ +friend Relation Intersection(NOT_CONST Relation &r1, NOT_CONST Relation &r2); \ +friend Relation After(NOT_CONST Relation &R, int carried_by, int new_output, int dir);\ +friend Relation Extend_Domain(NOT_CONST Relation &R); \ +friend Relation Extend_Domain(NOT_CONST Relation &R, int more); \ +friend Relation Extend_Range(NOT_CONST Relation &R); \ +friend Relation Extend_Range(NOT_CONST Relation &R, int more); \ +friend Relation Extend_Set(NOT_CONST Relation &R); \ +friend Relation Extend_Set(NOT_CONST Relation &R, int more); \ +friend Relation Restrict_Domain(NOT_CONST Relation &r1, NOT_CONST Relation &r2); \ +friend Relation Restrict_Range(NOT_CONST Relation &r1, NOT_CONST Relation &r2); \ +friend Relation Domain(NOT_CONST Relation &r); \ +friend Relation Range(NOT_CONST Relation &r); \ +friend Relation Cross_Product(NOT_CONST Relation &A, NOT_CONST Relation &B); \ +friend Relation Inverse(NOT_CONST Relation &r); \ +friend Relation Deltas(NOT_CONST Relation &R); \ +friend Relation Deltas(NOT_CONST Relation &R, int eq_no); \ +friend Relation DeltasToRelation(NOT_CONST Relation &R, int n_input, int n_output); \ +friend Relation Complement(NOT_CONST Relation &r); \ +friend Relation Project(NOT_CONST Relation &R, Global_Var_ID v); \ +friend Relation Project(NOT_CONST Relation &r, int pos, Var_Kind vkind); \ +friend Relation Project(NOT_CONST Relation &S, Sequence<Variable_ID> &s); \ +friend Relation Project_Sym(NOT_CONST Relation &R); \ +friend Relation Project_On_Sym(NOT_CONST Relation &R, NOT_CONST Relation &context); \ +friend Relation GistSingleConjunct(NOT_CONST Relation &R1, NOT_CONST Relation &R2, int effort); \ +friend Relation Gist(NOT_CONST Relation &R1, NOT_CONST Relation &R2, int effort); \ +friend Relation Difference(NOT_CONST Relation &r1, NOT_CONST Relation &r2); \ +friend Relation Approximate(NOT_CONST Relation &R, bool strides_allowed); \ +friend Relation Identity(int n_inp); \ +friend Relation Identity(NOT_CONST Relation &r); \ +friend bool do_subset_check(NOT_CONST Relation &r1, NOT_CONST Relation &r2); \ +friend bool Must_Be_Subset(NOT_CONST Relation &r1, NOT_CONST Relation &r2); \ +friend bool Might_Be_Subset(NOT_CONST Relation &r1, NOT_CONST Relation &r2); \ +friend bool May_Be_Subset(NOT_CONST Relation &r1, NOT_CONST Relation &r2); \ +friend bool Is_Obvious_Subset(NOT_CONST Relation &r1, NOT_CONST Relation &r2); \ +friend Relation Join(NOT_CONST Relation &G, NOT_CONST Relation &F); \ +friend Relation Composition(NOT_CONST Relation &F, NOT_CONST Relation &G); \ +friend bool can_do_exact_composition(NOT_CONST Relation &F, NOT_CONST Relation &G); \ +friend Relation EQs_to_GEQs(NOT_CONST Relation &, bool excludeStrides); \ +friend Relation Symbolic_Solution(NOT_CONST Relation &S); \ +friend Relation Symbolic_Solution(NOT_CONST Relation &S, Sequence<Variable_ID> &T); \ +friend Relation Sample_Solution(NOT_CONST Relation &S); \ +friend Relation Solution(NOT_CONST Relation &S, Sequence<Variable_ID> &T); \ +friend void MapRel1(Relation &inputRel, const Mapping &map, \ + Combine_Type ctype, int number_input, \ + int number_output, bool, bool); \ +friend Relation MapAndCombineRel2(Relation &R1, Relation &R2, \ + const Mapping &mapping1, \ + const Mapping &mapping2, \ + Combine_Type ctype, \ + int number_input, \ + int number_output); \ +friend void align(Rel_Body *, Rel_Body *, F_Exists *, \ + Formula *, const Mapping &, bool &, \ + List<int> &, Variable_ID_Tuple &); \ +friend Relation Lower_Bound(NOT_CONST Relation &r); \ +friend Relation Upper_Bound(NOT_CONST Relation &r) + + +// REMEMBER - THE LAST LINE OF THE MACRO SHOULD NOT HAVE A ; +/* TransitiveClosure doesn't need to be in friend_rel_ops */ + +} // namespace + +#endif diff --git a/omegalib/omega/include/omega/pres_logic.h b/omegalib/omega/include/omega/pres_logic.h new file mode 100644 index 0000000..27c4553 --- /dev/null +++ b/omegalib/omega/include/omega/pres_logic.h @@ -0,0 +1,90 @@ +#if ! defined _pres_logic_h +#define _pres_logic_h 1 + +#include <omega/pres_form.h> + +namespace omega { +// +// Presburger formula classes for logical operations: and, or not +// + +class F_And : public Formula { +public: + inline Node_Type node_type() {return Op_And;} + + // "preserves level" should be 0 unless we know this will not + // change the "level" of the constraints - ie the number of + // leading corresponding in,out variables known to be equal + GEQ_Handle add_GEQ(int preserves_level = 0); + EQ_Handle add_EQ(int preserves_level = 0); + Stride_Handle add_stride(int step, int preserves_level = 0); + EQ_Handle add_EQ(const Constraint_Handle &c, int preserves_level = 0); + GEQ_Handle add_GEQ(const Constraint_Handle &c, int preserves_level = 0); + + F_And *and_with(); + void add_unknown(); + +private: + friend class Formula; // add_and() + F_And(Formula *p, Rel_Body *r); + +private: + Formula *copy(Formula *parent, Rel_Body *reln); + virtual Conjunct *find_available_conjunct(); + int priority(); + void print_separator(FILE *output_file); + void prefix_print(FILE *output_file, int debug = 1); + void beautify(); + DNF* DNFize(); + + Conjunct *pos_conj; +}; + + +class F_Or : public Formula { +public: + inline Node_Type node_type() {return Op_Or;} + +private: + friend class Formula; // add_or + F_Or(Formula *, Rel_Body *); + +private: + Formula *copy(Formula *parent, Rel_Body *reln); + + virtual Conjunct *find_available_conjunct(); + void print_separator(FILE *output_file); + void prefix_print(FILE *output_file, int debug = 1); + void beautify(); + int priority(); + DNF* DNFize(); + void push_exists(Variable_ID_Tuple &S); +}; + + +class F_Not : public Formula { +public: + inline Node_Type node_type() {return Op_Not;} + void finalize(); + +private: + friend class Formula; + F_Not(Formula *, Rel_Body *); + +private: + Formula *copy(Formula *parent, Rel_Body *reln); + + virtual Conjunct *find_available_conjunct(); + friend class F_Forall; + bool can_add_child(); + void beautify(); + void rearrange(); + int priority(); + DNF* DNFize(); + void print(FILE *output_file); + void prefix_print(FILE *output_file, int debug = 1); +}; + +} // namespace + +#endif diff --git a/omegalib/omega/include/omega/pres_quant.h b/omegalib/omega/include/omega/pres_quant.h new file mode 100644 index 0000000..98c30df --- /dev/null +++ b/omegalib/omega/include/omega/pres_quant.h @@ -0,0 +1,63 @@ +#if ! defined _pres_quant_h +#define _pres_quant_h 1 + +#include <omega/pres_decl.h> + +namespace omega { + +// +// Presburger formula nodes for quantifiers +// + +class F_Exists : public F_Declaration { +public: + inline Node_Type node_type() {return Op_Exists;} + Variable_ID declare(Const_String s); + Variable_ID declare(); + Variable_ID declare(Variable_ID v); + virtual void push_exists(Variable_ID_Tuple &S); + +protected: + friend class Formula; + + F_Exists(Formula *, Rel_Body *); + F_Exists(Formula *, Rel_Body *, Variable_ID_Tuple &); + +private: + Formula *copy(Formula *parent, Rel_Body *reln); + + virtual Conjunct *find_available_conjunct(); + void print(FILE *output_file); + void prefix_print(FILE *output_file, int debug = 1); + void beautify(); + void rearrange(); + DNF* DNFize(); +}; + + +class F_Forall : public F_Declaration { +public: + inline Node_Type node_type() {return Op_Forall;} + Variable_ID declare(Const_String s); + Variable_ID declare(); + Variable_ID declare(Variable_ID v); + +protected: + friend class Formula; + + F_Forall(Formula *, Rel_Body *); + +private: + Formula *copy(Formula *parent, Rel_Body *reln); + + virtual Conjunct *find_available_conjunct(); + void print(FILE *output_file); + void prefix_print(FILE *output_file, int debug = 1); + void beautify(); + void rearrange(); + DNF* DNFize(); +}; + +} // namespace + +#endif diff --git a/omegalib/omega/include/omega/pres_subs.h b/omegalib/omega/include/omega/pres_subs.h new file mode 100644 index 0000000..8a9ee92 --- /dev/null +++ b/omegalib/omega/include/omega/pres_subs.h @@ -0,0 +1,88 @@ +#if !defined(pres_subs_h) +#define pres_subs_h + +/* Interface to omega core's substitutions. + + Creating an object of class Substitutions causes ordered elimination, + i.e. variables in the input and output tuples are substituted for by + functions of earlier variables. Could conceivablely create a more + flexible interface to orderedElimination if we developed a way to + specify the desired variable order. + + This is not an entirely consistent interface, since Sub_Handles + shouldn't really permit update_coef on SUBs. It is not a real + problem since subs are now no longer part of a conjunct, but it is + a slightly odd situation. + + Don't try to simplify r after performing orderedElimination. +*/ + + +#include <omega/pres_gen.h> +#include <omega/Relation.h> +#include <omega/pres_conj.h> +#include <omega/pres_cnstr.h> + +namespace omega { + +class Sub_Handle; +class Sub_Iterator; + +class Substitutions { +public: + Substitutions(Relation &input_R, Conjunct *input_c); + ~Substitutions(); + Sub_Handle get_sub(Variable_ID v); + bool substituted(Variable_ID v); + bool sub_involves(Variable_ID v, Var_Kind kind); +private: + friend class Sub_Iterator; + friend class Sub_Handle; + Relation *r; + Conjunct *c; + eqn *subs; + Variable_ID_Tuple subbed_vars; +}; + + +//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + + +class Sub_Handle: public Constraint_Handle { +public: + inline Sub_Handle() {} + + virtual std::string print_to_string() const; + virtual std::string print_term_to_string() const; + Variable_ID variable() {return v;} + +private: + friend class Substitutions; + friend class Sub_Iterator; + Sub_Handle(Substitutions *, int, Variable_ID); +// Sub_Handle(Substitutions *, int); + + Variable_ID v; +}; + +//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + + +class Sub_Iterator : public Generator<Sub_Handle> { +public: + Sub_Iterator(Substitutions *input_s): s(input_s), current(0), + last(s->c->problem->nSUBs-1) {} + int live() const; + void operator++(int); + void operator++(); + Sub_Handle operator* (); + Sub_Handle operator* () const; + +private: + Substitutions *s; + int current, last; +}; + +} // namespace + +#endif diff --git a/omegalib/omega/include/omega/pres_tree.h b/omegalib/omega/include/omega/pres_tree.h new file mode 100644 index 0000000..ad78ad0 --- /dev/null +++ b/omegalib/omega/include/omega/pres_tree.h @@ -0,0 +1,15 @@ +#if ! defined _pres_tree_h +#define _pres_tree_h 1 + +// +// Header to include if you need all the classes to build +// a Presburger formula: +// variables, constraints, nodes for logical operations & quantifiers +// + +#include <omega/pres_var.h> +#include <omega/pres_cnstr.h> +#include <omega/pres_logic.h> +#include <omega/pres_quant.h> + +#endif diff --git a/omegalib/omega/include/omega/pres_var.h b/omegalib/omega/include/omega/pres_var.h new file mode 100644 index 0000000..bf60dcb --- /dev/null +++ b/omegalib/omega/include/omega/pres_var.h @@ -0,0 +1,230 @@ +#if ! defined _pres_var_h +#define _pres_var_h 1 + +#include <omega/pres_gen.h> +#include <map> + +namespace omega { + +// +// Variable declaration. +// Variables are free or quantified. +// Free variables are classified as input, output and global. +// Quantified variables are classified as forall, exists and wildcard. +// All global variables are functions symbols of (possibly 0) arguments +// Local variables that correspond to >0-ary functions are identified +// as functions of a prefix of the input, output, or both tuples +// +// +// typedef enum {Input_Var, Output_Var, Set_Var, +// Global_Var, Forall_Var, Exists_Var, Wildcard_Var} Var_Kind; + +typedef enum {Free_Var, Coef_Var, Bomega_Var} Global_Kind; + +// NOW IN PRES_GEN.H, as its used as an argument and can't +// be forward declared: +// typedef enum {Unknown_Tuple = 0, Input_Tuple = 1, Output_Tuple = 2, +// Set_Tuple = Input_Tuple } Argument_Tuple; +// Only Input, Output, and Set can be passed to get_local, +// but the values 0 and 3 are also used internally. + + +class Var_Decl { +public: + inline Var_Kind kind() { return var_kind; } + int get_position(); + Global_Var_ID get_global_var(); + Argument_Tuple function_of(); // valid iff kind() == Global_var + + Const_String base_name; + void name_variable(char *newname); + + // The following should be used with care, as they are only valid + // after setup_names has been used on the relation containing this + // variable. + std::string name(); + const char* char_name(); + void set_kind(Var_Kind v) { var_kind = v; } + + // Operation to allow the remap field to be used for + // union-find operations on variables. + // Be sure to reset the remap fields afterward + void UF_union(Variable_ID v); + Variable_ID UF_owner(); + +private: + Var_Decl(Const_String name, Var_Kind vkind, int pos); + Var_Decl(Var_Kind vkind, int pos); + Var_Decl(Variable_ID v); + Var_Decl(Const_String name, Global_Var_ID v); + Var_Decl(Const_String name, Global_Var_ID v, Argument_Tuple function_of); + + friend class F_Declaration; // creates local variables + friend class Global_Var_Decl; // its constructors create Var_Decls. + + friend class Global_Input_Output_Tuple; + friend void copy_var_decls(Variable_ID_Tuple &new_vl, Variable_ID_Tuple &vl); + +private: + int instance; + void setup_name(); + + // these set up the names + friend class Rel_Body; +// friend class F_Declaration; already a friend + +private: + Variable_ID remap; // pointer to new copy of this node + + // lots of things need to get at "remap" - lots of relation ops, + // and functions that move UFS's around: + // dnf::make_level_carried_to and Conjunct::move_UFS_to_input() + // Also of course Conjunct::remap and push_exists + friend_rel_ops; + friend class DNF; + friend class Conjunct; + + // this prints remap to the debugging output + friend void print_var_addrs(std::string &s, Variable_ID v); + + friend void reset_remap_field(Variable_ID v); + friend void reset_remap_field(Sequence<Variable_ID> &S); + friend void reset_remap_field(Sequence<Variable_ID> &S, int var_no); + friend void reset_remap_field(Variable_ID_Tuple &S); + friend void reset_remap_field(Variable_ID_Tuple &S, int var_no); + +private: + + Var_Kind var_kind; + int position; // only for Input_Var, Output_Var + Global_Var_ID global_var; // only for Global_Var + Argument_Tuple of; // only for Global_Var +}; + +bool rm_variable(Variable_ID_Tuple &vl, Variable_ID v); +void reset_remap_field(Sequence<Variable_ID> &S); +void reset_remap_field(Sequence<Variable_ID> &S, int var_no); +void reset_remap_field(Variable_ID v); +void reset_remap_field(Variable_ID_Tuple &S); +void reset_remap_field(Variable_ID_Tuple &S, int var_no); + +class Global_Input_Output_Tuple: public Tuple<Variable_ID> { +public: + Global_Input_Output_Tuple(Var_Kind in_my_kind, int init=-1); + ~Global_Input_Output_Tuple(); + virtual Variable_ID &operator[](int index); + virtual const Variable_ID &operator[](int index) const; +private: + Var_Kind my_kind; + static const int initial_allocation; +}; + +extern Global_Input_Output_Tuple input_vars; +extern Global_Input_Output_Tuple output_vars; +// This allows the user to refer to set_vars to query sets, w/o knowing +// they are really inputs. +extern Global_Input_Output_Tuple &set_vars; + +Variable_ID input_var(int nth); +Variable_ID output_var(int nth); +Variable_ID set_var(int nth); + + + +// +// Global_Var_ID uniquely identifies global var-s through the whole program. +// Global_Var_Decl is an ADT with the following operations: +// - create global variable, +// - find the arity of the variable, (default = 0, for symbolic consts) +// - get the name of global variable, +// - tell if two variables are the same (if they are the same object) +// + +class Global_Var_Decl { +public: + Global_Var_Decl(Const_String baseName); + + virtual Const_String base_name() const + { + return loc_rep1.base_name; + } + + virtual void set_base_name(Const_String newName) + { + loc_rep1.base_name = newName; + loc_rep2.base_name = newName; + } + + virtual int arity() const + { + return 0; // default compatible with old symbolic constant stuff + } + + virtual Omega_Var *really_omega_var(); // until we get RTTI in C++ + virtual Coef_Var_Decl *really_coef_var(); // until we get RTTI in C++ + virtual Global_Kind kind() const; + +private: + + friend class Rel_Body; // Rel_Body::get_local calls this get_local + + Variable_ID get_local() + { + assert(arity() == 0); + return &loc_rep1; + } + Variable_ID get_local(Argument_Tuple of) + { + assert(arity() == 0 || of == Input_Tuple || of == Output_Tuple); + return ((arity() == 0 || of == Input_Tuple) ? &loc_rep1 : &loc_rep2); + } + + // local representative, there is just 1 for every 0-ary global variable + Var_Decl loc_rep1; // arity == 0, or arity > 0 and of == In + Var_Decl loc_rep2; // arity > 0 and of == Out + +public: +// friend class Rel_Body; // Rel_Body::setup_names sets instance + friend class Var_Decl; + int instance; +}; + + +class Coef_Var_Decl : public Global_Var_Decl { +public: + Coef_Var_Decl(int id, int var); + int stmt() const; + int var() const; + virtual Global_Kind kind() const; + virtual Coef_Var_Decl *really_coef_var(); // until we get RTTI in C++ + +private: + int i, v; +}; + + + +// +// Test subclass for Global_Var: named global variable +// +class Free_Var_Decl : public Global_Var_Decl { +public: + Free_Var_Decl(Const_String name); + Free_Var_Decl(Const_String name, int arity); + int arity() const; + virtual Global_Kind kind() const; + +private: + int _arity; +}; + + +/* === implementation functions === */ +void copy_var_decls(Variable_ID_Tuple &new_vl, Variable_ID_Tuple &vl); +void free_var_decls(Variable_ID_Tuple &vl); + +extern int wildCardInstanceNumber; + +} // namespace + +#endif diff --git a/omegalib/omega/include/omega/reach.h b/omegalib/omega/include/omega/reach.h new file mode 100644 index 0000000..ff4bf79 --- /dev/null +++ b/omegalib/omega/include/omega/reach.h @@ -0,0 +1,23 @@ +#if ! defined _reach_h +#define _reach_h 1 + +namespace omega { + +class reachable_information { +public: + Tuple<std::string> node_names; + Tuple<int> node_arity; + Dynamic_Array1<Relation> start_nodes; + Dynamic_Array2<Relation> transitions; +}; + + +Dynamic_Array1<Relation> * +Reachable_Nodes(reachable_information * reachable_info); + +Dynamic_Array1<Relation> * +I_Reachable_Nodes(reachable_information * reachable_info); + +} // namespace + +#endif diff --git a/omegalib/omega/src/RelBody.cc b/omegalib/omega/src/RelBody.cc new file mode 100644 index 0000000..825b153 --- /dev/null +++ b/omegalib/omega/src/RelBody.cc @@ -0,0 +1,906 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + All Rights Reserved. + + Purpose: + class Rel_Body, internal Relation representation + + Notes: + + History: + 11/26/09 Remove unecessary mandatary checking for set or relation, + treat them uniformly for easy coding, by Chun Chen +*****************************************************************************/ + +#include <basic/util.h> +#include <omega/RelBody.h> +#include <omega/Relation.h> +#include <omega/pres_tree.h> +#include <omega/pres_conj.h> +#include <omega/omega_i.h> +#include <assert.h> + +namespace omega { + +Rel_Body null_rel; +bool Rel_Body::is_null() const { + return(this == &null_rel); +} + + +int Rel_Body::max_ufs_arity() { + int ma = 0, a; + for (Variable_ID_Iterator v(*global_decls()); v; v++) { + a = (*v)->get_global_var()->arity(); + if (a > ma) + ma = a; + } + return ma; +} + +int Rel_Body::max_ufs_arity_of_set() { + int ma = 0, a; + for (Variable_ID_Iterator v(*global_decls()); v; v++) + if ((*v)->function_of() == Set_Tuple) { + a = (*v)->get_global_var()->arity(); + if (a > ma) + ma = a; + } + return ma; +} + +int Rel_Body::max_ufs_arity_of_in() { + int ma = 0, a; + for (Variable_ID_Iterator v(*global_decls()); v; v++) + if ((*v)->function_of() == Input_Tuple) { + a = (*v)->get_global_var()->arity(); + if (a > ma) + ma = a; + } + return ma; +} + +int Rel_Body::max_ufs_arity_of_out() { + int ma = 0, a; + for (Variable_ID_Iterator v(*global_decls()); v; v++) + if ((*v)->function_of() == Output_Tuple) { + a = (*v)->get_global_var()->arity(); + if (a > ma) + ma = a; + } + return ma; +} + +int Rel_Body::max_shared_ufs_arity() { + int ma = 0, a; + for (Variable_ID_Iterator v(*global_decls()); v; v++) + for (Variable_ID_Iterator v2(*global_decls()); v2; v2++) + if (*v != *v2 + && (*v)->get_global_var() == (*v2)->get_global_var() + && (*v)->function_of() != (*v2)->function_of()) { + a = (*v)->get_global_var()->arity(); + if (a > ma) + ma = a; + } + return ma; +} + +// +// Input and output variables. +// +void Rel_Body::name_input_var(int nth, Const_String S) { + // assert(1 <= nth && nth <= number_input && (!is_set() || skip_set_checks > 0)); + if (is_null()) + throw std::invalid_argument("empty relation"); + if (nth < 1 || nth > number_input) + throw std::invalid_argument("invalid input var number"); + In_Names[nth] = S; +} + +void Rel_Body::name_output_var(int nth, Const_String S) { + // assert(1<= nth && nth <= number_output && (!is_set() || skip_set_checks > 0)); + if (is_null()) + throw std::invalid_argument("empty relation"); + if (nth < 1 || nth > number_output) + throw std::invalid_argument("invalid output var number"); + Out_Names[nth] = S; +} + +void Rel_Body::name_set_var(int nth, Const_String S) { + if (number_output != 0) + throw std::runtime_error("relation is not a set"); + name_input_var(nth, S); +} + +int Rel_Body::n_inp() const { + // assert(!is_null() && (!is_set()||skip_set_checks>0)); + if (is_null()) + return 0; + else + return number_input; +} + +int Rel_Body::n_out() const { + // assert(!is_null() && (!is_set()||skip_set_checks>0)); + if (is_null()) + return 0; + else + return number_output; +} + +int Rel_Body::n_set() const { + if (number_output != 0) + throw std::runtime_error("relation is not a set"); + return n_inp(); +} + +Variable_ID Rel_Body::input_var(int nth) { + // assert(!is_null()); + // assert(!is_set() || skip_set_checks>0); + // assert(1 <= nth && nth <= number_input); + if (is_null()) + throw std::invalid_argument("empty relation"); + if (nth < 1 || nth > number_input) + throw std::invalid_argument("invalid input var number"); + input_vars[nth]->base_name = In_Names[nth]; + return input_vars[nth]; +} + +Variable_ID Rel_Body::output_var(int nth) { + // assert(!is_null()); + // assert(!is_set() || skip_set_checks>0); + // assert(1<= nth && nth <= number_output); + if (is_null()) + throw std::invalid_argument("empty relation"); + if (nth < 1 || nth > number_output) + throw std::invalid_argument("invalid output var number"); + output_vars[nth]->base_name = Out_Names[nth]; + return output_vars[nth]; +} + +Variable_ID Rel_Body::set_var(int nth) { + if (number_output != 0) + throw std::runtime_error("relation is not a set"); + return input_var(nth); +} + +// +// Add the AND node to the relation. +// Useful for adding restraints. +// +F_And *Rel_Body::and_with_and() { + assert(!is_null()); + if (is_simplified()) + DNF_to_formula(); + relation()->finalized = false; + Formula *f = rm_formula(); + F_And *a = add_and(); + a->add_child(f); + return a; +} + +// +// Add constraint to relation at the upper level. +// +EQ_Handle Rel_Body::and_with_EQ() { + assert(!is_null()); + if (is_simplified()) + DNF_to_formula(); + assert(! is_shared()); // The relation has been split. + relation()->finalized = false; + return find_available_conjunct()->add_EQ(); +} + +EQ_Handle Rel_Body::and_with_EQ(const Constraint_Handle &initial) { + assert(!is_null()); + assert(initial.relation()->is_simplified()); + EQ_Handle H = and_with_EQ(); + copy_constraint(H, initial); + return H; +} + +GEQ_Handle Rel_Body::and_with_GEQ() { + assert(!is_null()); + if (is_simplified()) + DNF_to_formula(); + assert(! is_shared()); // The relation has been split. + relation()->finalized = false; // We are giving out a handle. + // We should evantually implement finalization + // of subtrees, so the existing formula cannot + // be modified. + return find_available_conjunct()->add_GEQ(); +} + +GEQ_Handle Rel_Body::and_with_GEQ(const Constraint_Handle &initial) { + assert(!is_null()); + assert(initial.relation()->is_simplified()); + GEQ_Handle H = and_with_GEQ(); + copy_constraint(H, initial); + return H; +} + + + +Conjunct *Rel_Body::find_available_conjunct() { + Conjunct *c; + assert(!is_null()); + + if (children().empty()) { + c = add_conjunct(); + } + else { + assert(children().length() == 1); + Formula *kid = children().front(); // RelBodies have only one child + c = kid->find_available_conjunct(); + if (c==NULL) { + remove_child(kid); + F_And *a = add_and(); + a->add_child(kid); + c = a->add_conjunct(); + } + } + return c; +} + +void Rel_Body::finalize() { + assert(!is_null()); + if (!is_finalized()) + assert(! is_shared()); // no other pointers into here + finalized = true; + if (! children().empty()) + children().front()->finalize(); // Can have at most one child +} + +// Null Rel_Body +// This is the only rel_body constructor that has ref_count initialized to 1; +// That's because it's used to construct the global Rel_Body "null_rel". +// Unfortunately because we don't know in what order global constructors will +// be called, we could create a global relation with the default relation +// constructor (which would set the null_rel ref count to 1), and *then* +// call Rel_Body::Rel_Body(), which would set it back to 0, leaving a relation +// that points to a rel_body with it's ref_count set to 0! So this is done as +// a special case, in which the ref_count is always 1. +Rel_Body::Rel_Body(): + Formula(0, this), + ref_count(1), + status(under_construction), + number_input(0), number_output(0), + In_Names(0), Out_Names(0), + simplified_DNF(NULL), + r_conjs(0), + finalized(true), + _is_set(false) { +} + + +Rel_Body::Rel_Body(int n_input, int n_output): + Formula(0, this), + ref_count(0), + status(under_construction), + number_input(n_input), number_output(n_output), + In_Names(n_input), Out_Names(n_output), + simplified_DNF(NULL), + r_conjs(0), + finalized(false) { + if (n_output == 0) + _is_set = true; + else + _is_set = false; + if(pres_debug) { + fprintf(DebugFile, "+++ Create Rel_Body::Rel_Body(%d, %d) = 0x%p +++\n", + n_input, n_output, this); + } + int i; + for(i=1; i<=number_input; i++) { + In_Names[i] = Const_String(); + } + for(i=1; i<=number_output; i++) { + Out_Names[i] = Const_String(); + } +} + +// Rel_Body::Rel_Body(Rel_Body *r): +// Formula(0, this), +// ref_count(0), +// status(r->status), +// number_input(r->number_input), number_output(r->number_output), +// In_Names(r->number_input), Out_Names(r->number_output), +// simplified_DNF(NULL), +// r_conjs(r->r_conjs), +// finalized(r->finalized), +// _is_set(r->_is_set) { +// if(pres_debug) { +// fprintf(DebugFile, "+++ Copy Rel_Body::Rel_Body(Rel_Body * 0x%p) = 0x%p +++\n", r, this); +// prefix_print(DebugFile); +// } + +// int i; +// for(i=1;i<=r->number_input;i++) In_Names[i] = r->In_Names[i]; +// for(i=1;i<=r->number_output;i++) Out_Names[i] = r->Out_Names[i]; +// copy_var_decls(Symbolic,r->Symbolic); + +// if(!r->children().empty() && r->simplified_DNF==NULL) { +// Formula *f = r->formula()->copy(this,this); +// f->remap(); +// children().append(f); +// } +// else if(r->children().empty() && r->simplified_DNF!=NULL) { +// simplified_DNF = r->simplified_DNF->copy(this); +// simplified_DNF->remap(); +// } +// else { // copy NULL relation +// } + +// reset_remap_field(r->Symbolic); +// } + +Rel_Body *Rel_Body::clone() { + Rel_Body *b = new Rel_Body(); + + b->ref_count = 0; + b->status = status; + b->number_input = number_input; + b->number_output = number_output; + b->r_conjs = r_conjs; + b->finalized = finalized; + b->_is_set = _is_set; + + b->In_Names = Tuple<Const_String>(number_input); + b->Out_Names = Tuple<Const_String>(number_output); + for(int i = 1; i <= number_input; i++) + b->In_Names[i] = In_Names[i]; + for(int i = 1; i <= number_output; i++) + b->Out_Names[i] = Out_Names[i]; + + copy_var_decls(b->Symbolic, Symbolic); + + if(!children().empty() && simplified_DNF==NULL) { + Formula *f = formula()->copy(b, b); + f->remap(); + b->children().append(f); + } + else if(children().empty() && simplified_DNF!=NULL) { + b->simplified_DNF = simplified_DNF->copy(b); + b->simplified_DNF->remap(); + } + else { // copy NULL relation + } + + reset_remap_field(Symbolic); + return b; +} + + +Rel_Body::Rel_Body(Rel_Body *r, Conjunct *c): + Formula(0, this), + ref_count(0), + status(uncompressed), + number_input(r->number_input), number_output(r->number_output), + In_Names(r->number_input), Out_Names(r->number_output), + r_conjs(0), + finalized(r->finalized), + _is_set(r->_is_set) { + if(pres_debug) { + fprintf(DebugFile, "+++ Copy Rel_Body::Rel_Body(Rel_Body * 0x%p, Conjunct * 0x%p) = 0x%p +++\n",r,c,this); + } + + int i; + for(i=1;i<=r->number_input;i++) In_Names[i] = r->In_Names[i]; + for(i=1;i<=r->number_output;i++) Out_Names[i] = r->Out_Names[i]; + copy_var_decls(Symbolic,r->Symbolic); + + // assert that r has as many variables as c requires, or that c is from r + assert(r == c->relation()); + assert(r->simplified_DNF != NULL); + simplified_DNF = new DNF; + simplified_DNF->add_conjunct(c->copy_conj_diff_relation(this,this)); + single_conjunct()->remap(); + + reset_remap_field(r->Symbolic); +} + +Rel_Body::~Rel_Body() { + if(pres_debug) { + fprintf(DebugFile, "+++ Destroy Rel_Body::~Rel_Body() 0x%p +++\n", this); + } + free_var_decls(Symbolic); + if(simplified_DNF != NULL) { + delete simplified_DNF; + } +} + +// +// Take a relation that has been simplified and convert it +// back to formula form. +// +void Rel_Body::DNF_to_formula() { + assert(!is_null()); + if (simplified_DNF != NULL) { + simplified_DNF->DNF_to_formula(this); + simplified_DNF = NULL; + status = under_construction; + } +} + +bool Rel_Body::can_add_child() { + assert(this != &null_rel); + return n_children() < 1; +} + + + +// ******************** +// Simplify functions +// ******************** + + +extern int s_rdt_constrs; + + +// +// Simplify a given relation. +// Store the resulting DNF in the relation, clean out the formula. +// +void Rel_Body::simplify(int rdt_conjs, int rdt_constrs) { + if(simplified_DNF == NULL) { + finalized = true; + if(children().empty()) { + simplified_DNF = new DNF; + } + else { + if(pres_debug) { + if(DebugFile==NULL) { + DebugFile = fopen("test.out", "w"); + if(DebugFile==NULL) + fprintf(stderr, "Can not open file test.out\n"); + } + } + + assert(children().length()==1); + if(pres_debug) { + fprintf(DebugFile, "=== %p Rel_Body::simplify(%d, %d) Input tree (%d) ===\n", this,rdt_conjs,rdt_constrs,r_conjs); + prefix_print(DebugFile); + } + verify_tree(); + + beautify(); + verify_tree(); + + rearrange(); + verify_tree(); + + beautify(); + verify_tree(); + + s_rdt_constrs = rdt_constrs; + if(pres_debug) { + fprintf(DebugFile, "\n=== In simplify, before DNFize ===\n"); + prefix_print(DebugFile); + } + DNFize(); + if(pres_debug) { + fprintf(DebugFile, "\n=== In simplify, after DNFize ===\n"); + prefix_print(DebugFile); + } + verify_tree(); + + + simplified_DNF->rm_redundant_inexact_conjs(); + verify_tree(); + + if (rdt_conjs > 0 && !simplified_DNF->is_definitely_false() && simplified_DNF->length() > 1) { + simplified_DNF->rm_redundant_conjs(rdt_conjs-1); + verify_tree(); + } + + if(pres_debug) { + fprintf(DebugFile, "\n=== Resulting Relation ===\n"); + prefix_print(DebugFile); + } + } + } + else { + /* Reprocess DNF to get rid of redundant stuff */ + + if (rdt_constrs < 0) return; + simplified_DNF->rm_redundant_inexact_conjs(); + + if (rdt_conjs > r_conjs) { + if(pres_debug) + fprintf(DebugFile,"=== Rel_Body::simplify() redundant CONJUNCTS ===\n"); + simplified_DNF->rm_redundant_conjs(rdt_conjs-1); + } + if (rdt_constrs > 0 ) { + if(pres_debug) + fprintf(DebugFile,"=== Rel_Body::simplify() redundant CONSTR-S ===\n"); + s_rdt_constrs = rdt_constrs; + simplified_DNF->simplify(); + } + } + + r_conjs = rdt_conjs; + + for(DNF_Iterator D(simplified_DNF); D.live(); D.next()) { + D.curr()->set_relation(this); + D.curr()->set_parent(this); + } +} + + +// ****************** +// Query functions +// ****************** + + +// +// Check if relation has a single conjunct formula and return this conjunct. +// +Conjunct *Rel_Body::single_conjunct() { + simplify(); + return simplified_DNF->single_conjunct(); +} + +bool Rel_Body::has_single_conjunct() { + simplify(); + return simplified_DNF->has_single_conjunct(); +} + +// +// Remove and return first conjunct +// +Conjunct *Rel_Body::rm_first_conjunct() { + simplify(); + return simplified_DNF->rm_first_conjunct(); +} + + +void Rel_Body::query_difference(Variable_ID v1, Variable_ID v2, coef_t &lowerBound, coef_t &upperBound, bool &guaranteed) { + simplify(); + + coef_t _lb, _ub; + int first = 1; + bool _g; + lowerBound = negInfinity; // default values if no DNF's + upperBound = posInfinity; + guaranteed = 0; + + for (DNF_Iterator D(simplified_DNF); D.live(); D.next()) { + (*D)->query_difference(v1, v2, _lb, _ub, _g); + if (first) { + lowerBound = _lb; + upperBound = _ub; + guaranteed = _g; + first = 0; + } + else { + guaranteed = guaranteed && _g; + lowerBound = min(lowerBound, _lb); + upperBound = max(upperBound, _ub); + } + } +} + + +void Rel_Body::query_variable_bounds(Variable_ID v, coef_t &lowerBound, coef_t &upperBound) { + simplify(); + + coef_t _lb, _ub; + int first = 1; + lowerBound = negInfinity; // default values if no DNF's + upperBound = posInfinity; + + for (DNF_Iterator D(simplified_DNF); D.live(); D.next()) { + (*D)->query_variable_bounds(v, _lb, _ub); + if (first) { + lowerBound = _lb; + upperBound = _ub; + first = 0; + } + else { + lowerBound = min(lowerBound, _lb); + upperBound = max(upperBound, _ub); + } + } +} + +coef_t Rel_Body::query_variable_mod(Variable_ID v, coef_t factor) { + simplify(); + + bool first = true; + coef_t result; + + for (DNF_Iterator D(simplified_DNF); D.live(); D.next()) { + coef_t t = (*D)->query_variable_mod(v, factor); + if (t == posInfinity) + return posInfinity; + + if (first) { + result = t; + first = false; + } + else { + if (result != t) + return posInfinity; + } + } + + return result; +} + + + +// +// Simplify formula if needed and return the resulting DNF. +// +DNF* Rel_Body::query_DNF() { + return(query_DNF(false,false)); +} + +DNF* Rel_Body::query_DNF(int rdt_conjs, int rdt_constrs) { + simplify(rdt_conjs, rdt_constrs); + return(simplified_DNF); +} + +// +// Other formula queries. +// + +// Interpret UNKNOWN as true, then check satisfiability +// i.e., check if the formula simplifies to FALSE, since the library +// will never say that if the *known* constraints are unsatisfiable by +// themselves. +bool Rel_Body::is_upper_bound_satisfiable() { + int tmp = s_rdt_constrs; + s_rdt_constrs = -1; + simplify(); + s_rdt_constrs = tmp; + return(!simplified_DNF->is_definitely_false()); +} + +// Interpret UNKNOWN as false, then check satisfiability +// i.e., check if there exist any exact conjuncts in the solution +bool Rel_Body::is_lower_bound_satisfiable() { + int tmp = s_rdt_constrs; + s_rdt_constrs = -1; + simplify(); + s_rdt_constrs = tmp; + for(DNF_Iterator d(simplified_DNF); d; d++) + if((*d)->is_exact()) return true; + return false; +} + +bool Rel_Body::is_satisfiable() { + assert(is_lower_bound_satisfiable() == is_upper_bound_satisfiable()); + return is_upper_bound_satisfiable(); +} + +// Check if we can easily determine if the formula evaluates to true. +bool Rel_Body::is_obvious_tautology() { + int tmp = s_rdt_constrs; + s_rdt_constrs = 0; + simplify(); + s_rdt_constrs = tmp; + return(simplified_DNF->is_definitely_true()); +} + +// Expensive check to determine if the formula evaluates to true. +bool Rel_Body::is_definite_tautology() { + if(is_obvious_tautology()) return true; + Relation l = Lower_Bound(Relation(*this,1)); + return !(Complement(l).is_upper_bound_satisfiable()); +} + +bool Rel_Body::is_unknown() { + simplify(); + return(has_single_conjunct() && single_conjunct()->is_unknown()); +} + +// +// Get accuracy status of the relation +// + +Rel_Unknown_Uses Rel_Body::unknown_uses() { + if (!is_simplified()) + simplify(); + + Rel_Unknown_Uses local_status=0; + int n_conj=0; + + for (DNF_Iterator c(simplified_DNF); c; c++) { + n_conj++; + if ((*c)->is_exact()) + local_status |= no_u; + else if ((*c)->is_unknown()) + local_status |= or_u; + else + local_status |= and_u; + } + + if (n_conj == 0) { + assert(local_status == 0); + local_status = no_u; + } + assert(local_status); +#if ! defined NDEBUG + Rel_Unknown_Uses impossible = (and_u | or_u); + assert( (local_status & impossible) != impossible); +#endif + + return local_status; +} + +void Rel_Body::interpret_unknown_as_false() { + simplify(); + simplified_DNF->remove_inexact_conj(); +} + +void Rel_Body::interpret_unknown_as_true() { + simplify(); + for(DNF_Iterator d(simplified_DNF); d; d++) + (*d)->interpret_unknown_as_true(); +} + + +void Rel_Body::reverse_leading_dir_info() { + if (is_simplified()) { + for (DNF_Iterator c(simplified_DNF); c; c++) + (*c)->reverse_leading_dir_info(); + } + else { + assert(!simplified_DNF); + assert(children().size() == 1); + children().front()->reverse_leading_dir_info(); + } +} + +// +// Rel_Body::DNFize just DNF-izes its child node and calls verify +// + +DNF* Rel_Body::DNFize() { +#if defined(INCLUDE_COMPRESSION) + assert(!this->is_compressed()); +#endif + if (! simplified_DNF) { + simplified_DNF = children().remove_front()->DNFize(); + + int mua = max_shared_ufs_arity(); + if (mua > 0) { + if (pres_debug) { + fprintf(DebugFile, "\n=== In DNFize, before LCDNF ===\n"); + prefix_print(DebugFile); + } + + simplified_DNF->make_level_carried_to(mua); + } + + if(pres_debug) { + fprintf(DebugFile, "\n=== In DNFize, before verify ===\n"); + prefix_print(DebugFile); + } + + simplified_DNF->simplify(); + } + + assert(children().length() == 0); + + return simplified_DNF; +} + +void Rel_Body::make_level_carried_to(int level) { + if (!simplified_DNF) { + DNFize(); + } + + assert(simplified_DNF && children().empty()); + + simplified_DNF->make_level_carried_to(level); +} + +// +// if direction==0, move all conjuncts with >= level leading 0's to return +// else move all conjuncts with level-1 0's followed by +// the appropriate signed difference to returned Relation +// + +Relation Rel_Body::extract_dnf_by_carried_level(int level, int direction) { + if (!simplified_DNF) { + DNFize(); + } + + assert(simplified_DNF && children().empty()); + + simplified_DNF->make_level_carried_to(level); + + Relation extracted(n_inp(), n_out()); + extracted.copy_names(*this); + assert(extracted.rel_body->children().empty()); + assert(extracted.rel_body->simplified_DNF == NULL); + extracted.rel_body->simplified_DNF = new DNF; + extracted.rel_body->Symbolic = Symbolic; + + DNF *remaining = new DNF; + Conjunct *curr; + + for (curr = simplified_DNF->rm_first_conjunct(); + curr; + curr = simplified_DNF->rm_first_conjunct()) { + assert(curr->guaranteed_leading_0s >= level || curr->guaranteed_leading_0s == curr->possible_leading_0s); + assert(curr->possible_leading_0s >= 0); + + curr->assert_leading_info(); + + if ((direction == 0 && curr->guaranteed_leading_0s >= level) || + (curr->guaranteed_leading_0s == level-1 && + curr->leading_dir_valid_and_known() && + curr->leading_dir * direction > 0)) { + extracted.rel_body->simplified_DNF->add_conjunct(curr); + } + else { + remaining->add_conjunct(curr); + } + } + delete simplified_DNF; + simplified_DNF = remaining; + +#if ! defined NDEBUG + for (DNF_Iterator rc(simplified_DNF); rc; rc++) + (*rc)->assert_leading_info(); + + for (DNF_Iterator ec(extracted.rel_body->simplified_DNF); ec; ec++) + (*ec)->assert_leading_info(); +#endif + + finalize(); + extracted.finalize(); + return extracted; +} + +//Compress/uncompress functions + +bool Rel_Body::is_compressed() { +#if defined(INCLUDE_COMPRESSION) + if(is_simplified()) { + for(DNF_Iterator p(simplified_DNF); p.live(); p.next()) { + if(p.curr()->is_compressed()) + return true; + } + } + return false; +#else + return true; // This allows is_compressed assertions to work +#endif +} + +void Rel_Body::compress() { +#if !defined(INCLUDE_COMPRESSION) + return; +#else + if (status == compressed) + return; + if (pres_debug) + fprintf(DebugFile,">>> Compressing relation %p\n",this); + simplify(); + for(DNF_Iterator p(simplified_DNF); p.live(); p.next()) { + p.curr()->compress(); + status = compressed; + } +#endif +} + +void Rel_Body::uncompress() { +#if !defined(INCLUDE_COMPRESSION) + return; +#else + if (pres_debug) + fprintf(DebugFile,"<<< Uncompressing relation %p\n",this); + assert(is_simplified()); + for(DNF_Iterator p(simplified_DNF); p.live(); p.next()) { + p.curr()->uncompress(); + status = uncompressed; + } +#endif +} + +} diff --git a/omegalib/omega/src/RelVar.cc b/omegalib/omega/src/RelVar.cc new file mode 100644 index 0000000..d9b977c --- /dev/null +++ b/omegalib/omega/src/RelVar.cc @@ -0,0 +1,71 @@ +#include <omega/RelBody.h> +#include <omega/omega_i.h> + +namespace omega { + +Variable_ID Rel_Body::get_local(const Variable_ID v) { + Global_Var_ID g; + if (v->kind() == Global_Var) { + g = v->get_global_var(); + if (g->arity()) return get_local(g,v->function_of()); + return get_local(g); + } + if (is_set()) return set_var(v->get_position()); + if (v->kind() == Input_Var) return input_var(v->get_position()); + if (v->kind() == Output_Var) return output_var(v->get_position()); + assert(0 && "Can only get local for variable with global scope"); + exit(1); + return 0; +} + +// +// Find or declare global variable. +// If the VarID does not exist, it is created. Otherwise it's returned. +// Note that this version now works only for 0-ary functions. +// +Variable_ID Rel_Body::get_local(const Global_Var_ID G) { + assert(G->arity() == 0); + for(Variable_Iterator i(Symbolic); i; i++) + if ((*i)->get_global_var() == G) + return (*i); + + Variable_ID v = G->get_local(); + Symbolic.append(v); + return v; +} + + +Variable_ID Rel_Body::get_local(const Global_Var_ID G, Argument_Tuple of) { + assert(G->arity() == 0 || of == Input_Tuple || of == Output_Tuple); + + for(Variable_Iterator i = Symbolic; i; i++) + if ((*i)->get_global_var() == G && (G->arity() == 0 || + of == (*i)->function_of())) + return (*i); + + Variable_ID V = G->get_local(of); + Symbolic.append(V); + return V; +} + + +bool Rel_Body::has_local(const Global_Var_ID G) { + assert(G->arity() == 0); + for(Variable_Iterator i = Symbolic; i; i++) + if ((*i)->get_global_var() == G) + return true; + return false; +} + + +bool Rel_Body::has_local(const Global_Var_ID G, Argument_Tuple of) { + assert(G->arity() == 0 || of == Input_Tuple || of == Output_Tuple); + + for(Variable_Iterator i = Symbolic; i; i++) + if ((*i)->get_global_var() == G && (G->arity() == 0 || + of == (*i)->function_of())) + return true; + return false; +} + +} // namespace diff --git a/omegalib/omega/src/Relation.cc b/omegalib/omega/src/Relation.cc new file mode 100644 index 0000000..1cca43a --- /dev/null +++ b/omegalib/omega/src/Relation.cc @@ -0,0 +1,279 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + All Rights Reserved. + + Purpose: + class Relation + + Notes: + + History: +*****************************************************************************/ + +#include <omega/Relation.h> +#include <omega/Relations.h> +#include <omega/pres_dnf.h> +#include <omega/pres_conj.h> +#include <omega/Rel_map.h> +#include <omega/omega_i.h> +#include <omega/omega_core/debugging.h> + +namespace omega { + +// copy function for Relation, will be removed in the future +// in favor of correct C++ copy constructor and const paramater passing +Relation copy(const Relation &t) { + Relation r = t; + return r; +} + +// +// Create null relation. +// +Relation::Relation() : rel_body(&null_rel) { + rel_body->ref_count = 1; +} + +Relation Relation::Null() { + return Relation(); +} + +bool Relation::is_null() const { + return(rel_body == &null_rel); +} + + +// +// Create a relation. Its will be built later. +// +Relation::Relation(int n_input, int n_output) { + rel_body = new Rel_Body(n_input,n_output); + rel_body->ref_count = 1; +} + +Relation::Relation(Rel_Body &r, int) { + rel_body = &r; + r.ref_count++; +} + +Relation Relation::Empty(const Relation &R) { + if (R.is_set()) return Relation(R.n_set()); + else return Relation(R.n_inp(),R.n_out()); +} + +// +// Create relation which is FALSE or TRUE. +// + +Relation Relation::True(const Relation &R) { + if (R.is_set()) return True(R.n_set()); + else return True(R.n_inp(),R.n_out()); +} + +Relation Relation::False(const Relation &R) { + if (R.is_set()) return False(R.n_set()); + else return False(R.n_inp(),R.n_out()); +} + +Relation Relation::Unknown(const Relation &R) { + if (R.is_set()) return Unknown(R.n_set()); + else return Unknown(R.n_inp(), R.n_out()); +} + + +Relation Relation::True(int setvars) { + Relation R(setvars); + R.add_and(); + R.finalize(); + return R; +} + +Relation Relation::True (int in, int out) { + Relation R(in,out); + R.add_and(); + R.finalize(); + return R; +} + +Relation Relation::False (int setvars) { + Relation R(setvars); + R.add_or(); + R.finalize(); + return R; +} + +Relation Relation::False (int in, int out) { + Relation R(in,out); + R.add_or(); + R.finalize(); + return R; +} + + +Relation Relation::Unknown (int setvars) { + Relation R(setvars); + R.add_and(); + R.finalize(); + R.simplify(); + Conjunct * c= R.single_conjunct(); + c->make_inexact(); + return R; +} + +Relation Relation::Unknown (int in, int out) { + Relation R(in,out); + R.add_and(); + R.finalize(); + R.simplify(); + Conjunct * c= R.single_conjunct(); + c->make_inexact(); + return R; +} + + +// +// Copy a relation. +// +Relation::Relation(const Relation &r) { +#if defined(INCLUDE_COMPRESSION) + assert(!r.is_compressed()); +#endif + if (r.is_finalized()) { + rel_body = r.rel_body; + rel_body->ref_count++; + } else { + assert(! r.rel_body->is_shared()); + // rel_body = new Rel_Body(r.rel_body); + rel_body = r.rel_body->clone(); + rel_body->ref_count = 1; + } +} + +// +// Copy relation r and replace formula in it with conjunct c. +// Wayne (TM) function. +// +Relation::Relation(const Relation &r, Conjunct *c) { + rel_body = new Rel_Body(r.rel_body, c); + rel_body->ref_count = 1; +} + + +// +// Assign a relation r to this relation. +// +Relation &Relation::operator=(const Relation &r) { +#if defined(INCLUDE_COMPRESSION) + assert (!r.is_compressed()); +#endif + + /* === Destroy this === */ + assert(rel_body->ref_count >= 1); + if(rel_body!=&null_rel && --(rel_body->ref_count)==0) { + delete rel_body; + } + + /* === Copy r to this === */ + if (r.is_finalized()) { + rel_body = r.rel_body; + rel_body->ref_count++; + } else { + assert(! r.rel_body->is_shared()); + // rel_body = new Rel_Body(r.rel_body); + rel_body = r.rel_body->clone(); + rel_body->ref_count = 1; + } + return *this; +} + +void Relation::copy_names(Rel_Body &r) { + int t; + for(t = 1; t <= r.n_inp(); t++) + name_input_var(t,r.input_var(t)->base_name); + for(t = 1; t <= r.n_out(); t++) + name_output_var(t,r.output_var(t)->base_name); +} + + +// Like makeSet (see Relations.c), but won't invert the relation -- +// fails if it has output instead of input variables. Called in Relation +// functions just after a MapRel, so that we know there are no outputs anyway. + +void Relation::markAsSet() { + assert(!is_null()); + assert(is_set() || (n_inp() >= 0 && n_out() == 0)); + if (!is_set()) split(); // split if we'll modify this + rel_body->_is_set = true; + invalidate_leading_info(); +} + +void Relation::markAsRelation() { + assert(!is_null()); + if (is_set()) split(); // split if we'll modify this + rel_body->_is_set = false; +} + + +Relation::~Relation() { + assert(rel_body->ref_count >= 1); + assert(this->is_null() == (rel_body == &null_rel)); + if(rel_body!=&null_rel && --(rel_body->ref_count)==0) { + if (rel_body == &null_rel) abort(); + delete rel_body; + } +} + + + +// +// One of the representatives using the body wants to be changed. +// Create a separate body for this rep not to damage other reps. +// Return address of the body. Old rep point to new body. +// +Rel_Body *Relation::split() { + assert(rel_body != &null_rel && "Error: Attempt to modify a null relation"); + assert (rel_body->ref_count >= 1); + if(!(rel_body==&null_rel || rel_body->ref_count==1)) { + if(pres_debug) { + fprintf(DebugFile, "+++ SPLIT relation +++\n"); + } + // Rel_Body *new_body = new Rel_Body(rel_body); + Rel_Body *new_body = rel_body->clone(); + new_body->ref_count = 1; + rel_body->ref_count--; + rel_body = new_body; + if(pres_debug>=2) { + fprintf(DebugFile, " copying 0x%p to give 0x%p\n", this, rel_body); + } + } + return (rel_body); +} + + +void Relation::dimensions(int & ndim_all, int &ndim_domain) { + ndim_all = ndim_domain = 0; + int a,d; + simplify(2,2); + for (DNF_Iterator s(query_DNF()); s.live(); s.next()) { + s.curr()->calculate_dimensions(*this, a, d); + if (a > ndim_all) ndim_all = a; + if (d > ndim_domain) ndim_domain = d; + } +} + +// Make a set: assert that it had only input or output variables, make it +// it have only input, set a flag. Called from domain, range, and difference, +// as well as functions that require a set as input. +void Relation::makeSet() { + assert(!is_null()); + // Assert that it is a set... + assert((n_inp() == 0 && n_out() >= 0) || (n_inp() >= 0 && n_out() == 0)); + + if ((n_inp() == 0 && n_out() != 0) || !is_set()) split(); // split if we'll modify this + if (n_inp() == 0 && n_out() != 0) //Inverse the relation + Inverse(*this); // Modifies "this"; also returns this but we ignore it + rel_body->_is_set = true; +} + +} // namespace 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 diff --git a/omegalib/omega/src/basic/ConstString.cc b/omegalib/omega/src/basic/ConstString.cc new file mode 100644 index 0000000..7d2ec1e --- /dev/null +++ b/omegalib/omega/src/basic/ConstString.cc @@ -0,0 +1,134 @@ +#include <basic/ConstString.h> +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include <string> +#include <string.h> + +/* static const int CS_HashTable_Size = 1000; */ +/* static ConstStringRep *hashTable[CS_HashTable_Size] = {0}; */ + +namespace omega { + +const int CS_HashTable_Size = 1000; +class CS_HashTable { +public: + ConstStringRep *p[CS_HashTable_Size]; + CS_HashTable(); + ~CS_HashTable(); +}; + +namespace { + CS_HashTable hashTable; +} + +CS_HashTable::CS_HashTable() { + for (int i = 0; i < CS_HashTable_Size; i++) + p[i] = NULL; + } + +CS_HashTable::~CS_HashTable() { + for (int i = 0; i < CS_HashTable_Size; i++) { + ConstStringRep *t = p[i]; + while (t != NULL) { + ConstStringRep *tt = t->nextInBucket; + delete []t->name; + delete t; + t = tt; + } + } +} + +Const_String::Const_String() { + rep = 0; +} + +void Const_String::buildRep(const char* t) { + int hash = 0; + const char *s = t; + while (*s != '\0') + hash = hash*33 + *s++; + int hashBucket = hash % CS_HashTable_Size; + if (hashBucket < 0) hashBucket += CS_HashTable_Size; + assert(0 <= hashBucket && hashBucket < CS_HashTable_Size); + ConstStringRep **q = &(hashTable.p[hashBucket]); + ConstStringRep *p = *q; + while (p != 0) { + if (strcmp(p->name,t) == 0) break; + q = &p->nextInBucket; + p = *q; + } + if (p!= 0) rep = p; + else { + rep = new ConstStringRep(t); + *q = rep; + } +} + +Const_String::Const_String(const char * t) { + buildRep(t); +} + +Const_String::Const_String(const std::string &s) { + buildRep(s.c_str()); +} + +Const_String::operator const char*() const { + if (!rep) return 0; + return rep->name; +} + +Const_String::operator std::string() const { + if (!rep) return std::string(""); + return std::string(rep->name); +} + +int Const_String::operator++(int) { + return rep->count++; +} + +int Const_String::operator++() { + return ++rep->count; +} + +int Const_String:: operator--(int) { + return rep->count--; +} + +int Const_String:: operator--() { + return --rep->count; +} + +int operator ==(const Const_String &x, const Const_String &y) { + return x.rep == y.rep; +} + +int operator !=(const Const_String &x, const Const_String &y) { + return x.rep != y.rep; +} + +int operator <(const Const_String &x, const Const_String &y) { + return (strcmp(x.rep->name,y.rep->name) < 0); +} + +int operator >(const Const_String &x, const Const_String &y) { + return (strcmp(x.rep->name,y.rep->name) > 0); +} + +Const_String:: operator int() const { + return rep != 0; +} + +int Const_String::null() const { + return rep == 0; +} + +ConstStringRep:: ConstStringRep(const char *t) { + count = 0; + nextInBucket = 0; + char *s = new char[1+strlen(t)]; + strcpy(s,t); + name = s; +} + +} // namespace diff --git a/omegalib/omega/src/basic/Link.cc b/omegalib/omega/src/basic/Link.cc new file mode 100644 index 0000000..50b9441 --- /dev/null +++ b/omegalib/omega/src/basic/Link.cc @@ -0,0 +1,41 @@ +#include <basic/Link.h> + +namespace omega { + +#if ListElementFreeList + static List_Element<void*> *_kludgy_List_Element_free_list_pointer; +// we rely on the fact that that is initialized to 0 before any +// constructor-based initialization that could call List_Element::new. + + void *kludgy_List_Element_new(size_t size) + { + void *mem; + if (size == sizeof(List_Element<void*>) && + _kludgy_List_Element_free_list_pointer) + { + List_Element<void*> *it = _kludgy_List_Element_free_list_pointer; + _kludgy_List_Element_free_list_pointer = it->tail; + mem = it; + } + else + mem = ::operator new(size); + + return mem; + } + + void kludgy_List_Element_delete(void *ptr, size_t size) + { + if (ptr) + if (size == sizeof(List_Element<void*>)) + { + List_Element<void*> *it = (List_Element<void*> *) ptr; + it->tail = _kludgy_List_Element_free_list_pointer; + _kludgy_List_Element_free_list_pointer = it; + } + else + ::operator delete(ptr); + } + +#endif + +} // namespace diff --git a/omegalib/omega/src/closure.cc b/omegalib/omega/src/closure.cc new file mode 100644 index 0000000..416a3e7 --- /dev/null +++ b/omegalib/omega/src/closure.cc @@ -0,0 +1,2100 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + Copyright (C) 2009-2011 West Pomeranian University of Technology, Szczecin + All Rights Reserved. + + Purpose: + All calculations of closure are now here. + + Notes: + Related paper: + - "Transitive closure of infinite graphs and its applications", + Wayne Kelly, William Pugh, Evan Rosser and Tatiana Shpeisman, IJPP 1996. + - "Computing the Transitive Closure of a Union of Affine Integer Tuple + Relations", Anna Beletska, Denis Barthou, Wlodzimierz Bielecki and + Albert Cohen, COCOA 2009. + - "An Iterative Algorithm of Computing the Transitive Closure of a Union + of Parameterized Affine Integer Tuple Relations", Bielecki Wlodzimierz, + Klimek Tomasz, Palkowski Marek and Anna Beletska, COCOA 2010. + + History: + 12/27/09 move ConicClosure here, Chun Chen + 01/19/11 new closure algorithms, Klimek Tomzsz + 02/02/11 move VennDiagramFrom here, Chun Chen +*****************************************************************************/ + +#include <typeinfo> +#include <assert.h> +#include <omega.h> +#include <omega/hull.h> +#include <basic/Iterator.h> +#include <basic/List.h> +#include <basic/SimpleList.h> + +namespace omega { + +void InvestigateClosure(Relation r, Relation r_closure, Relation bounds); +void print_given_bounds(const Relation & R1, NOT_CONST Relation& input_Bounds); +#define printConjunctClosure (closure_presburger_debug & 0x1) +#define detailedClosureDebug (closure_presburger_debug & 0x2) + + +#ifdef TC_STATS +extern int clock_diff(); +extern void start_clock(); +FILE *statsfile; +int singles, totals=0; +#endif + +int closure_presburger_debug = 0; + + +Relation VennDiagramForm(NOT_CONST Relation &Context_In, + Tuple<Relation> &Rs, + int next, + bool anyPositives, + int weight) { + Relation Context = consume_and_regurgitate(Context_In); + if (hull_debug) { + fprintf(DebugFile,"[VennDiagramForm, next = %d, anyPositives = %d, weight = %d \n", next,anyPositives,weight); + fprintf(DebugFile,"context:\n"); + Context.prefix_print(DebugFile); + } + if (anyPositives && weight > 3) { + Context.simplify(); + if (!Context.is_upper_bound_satisfiable()) { + if (hull_debug) + fprintf(DebugFile,"] not satisfiable\n"); + return Context; + } + weight = 0; + } + if (next > Rs.size()) { + if (!anyPositives) { + if (hull_debug) + fprintf(DebugFile,"] no positives\n"); + return Relation::False(Context); + } + Context.simplify(); + if (hull_debug) { + fprintf(DebugFile,"] answer is:\n"); + Context.prefix_print(DebugFile); + } + return Context; + } + Relation Pos = VennDiagramForm(Intersection(copy(Context),copy(Rs[next])), + Rs, + next+1, + true, + weight+2); + Relation Neg = VennDiagramForm(Difference(Context,copy(Rs[next])), + Rs, + next+1, + anyPositives, + weight+1); + if (hull_debug) { + fprintf(DebugFile,"] VennDiagramForm\n"); + fprintf(DebugFile,"pos part:\n"); + Pos.prefix_print(DebugFile); + fprintf(DebugFile,"neg part:\n"); + Neg.prefix_print(DebugFile); + } + return Union(Pos,Neg); +} + + +Relation VennDiagramForm(Tuple<Relation> &Rs, NOT_CONST Relation &Context_In) { + Relation Context = consume_and_regurgitate(Context_In); + if (Context.is_null()) Context = Relation::True(Rs[1]); + if (hull_debug) { + fprintf(DebugFile,"Starting computation of VennDiagramForm\n"); + fprintf(DebugFile,"Context:\n"); + Context.prefix_print(DebugFile); + for(int i = 1; i <= Rs.size(); i++) { + fprintf(DebugFile,"#%d:\n",i); + Rs[i].prefix_print(DebugFile); + } + } + return VennDiagramForm(Context,Rs,1,false,0); +} + +Relation VennDiagramForm(NOT_CONST Relation &R_In, NOT_CONST Relation &Context_In) { + Relation R = consume_and_regurgitate(R_In); + Relation Context = consume_and_regurgitate(Context_In); + Tuple<Relation> Rs; + for (DNF_Iterator c(R.query_DNF()); c.live(); ) { + Rs.append(Relation(R,c.curr())); + c.next(); + } + return VennDiagramForm(Rs,Context); +} + + +Relation ConicClosure (NOT_CONST Relation &R) { + int n = R.n_inp(); + if (n != R.n_out()) + throw std::invalid_argument("conic closure must have the same input arity and output arity"); + + return DeltasToRelation(ConicHull(Deltas(R)), n, n); +} + + +bool is_lex_forward(Relation R) { + if(R.n_inp() != R.n_out()) { + fprintf(stderr, "relation has wrong inputs/outpts\n"); + exit(1); + } + Relation forw(R.n_inp(), R.n_out()); + F_Or * o = forw.add_or(); + for(int a = 1; a <= forw.n_inp(); a++) { + F_And * andd = o->add_and(); + GEQ_Handle g = andd->add_GEQ(); + g.update_coef(input_var(a), -1); + g.update_coef(output_var(a), 1); + g.update_const(1); + for(int b = 1; b < a; b++) { + EQ_Handle e = andd->add_EQ(); + e.update_coef(input_var(a),1); + e.update_coef(output_var(a),-1); + } + } + Relation test = Difference(R, forw); + return !test.is_upper_bound_satisfiable(); +} + + +static Relation compose_n(NOT_CONST Relation &input_r, int n) { + Relation r = consume_and_regurgitate(input_r); + if (n == 1) + return r; + else + return Composition(r, compose_n(copy(r), n-1)); +} /* compose_n */ + + + + +Relation approx_closure(NOT_CONST Relation &input_r, int n) { + Relation r = consume_and_regurgitate(input_r); + Relation r_closure; + + r_closure=r; + int i; + for(i=2; i<=n; i++) + r_closure=Union(r_closure,compose_n(copy(r), n)); + r_closure = Union(r_closure, Relation::Unknown(r_closure)); + + return r_closure; +} /* approx_closure */ + + +static bool is_closure_itself(NOT_CONST Relation &r) { + return Must_Be_Subset(Composition(copy(r),copy(r)),copy(r)); +} + + +/***** + * get a D form of the Relation (single conjunct). + * D = {[ i_1,i_2,...,i_m] -> [j_1, j_2, ..., j_m ] : + * (forall p, 1<= p <= m) L_p <= j_p - i_p <= U_p && + * j_p - i_p == M_p alpha_p}; + * Right now only wildcards that are in stride constraints are treated. + *****/ + +Relation get_D_form (Relation & R) { + Relation D(R.n_inp(), R.n_out()); + + R.make_level_carried_to(R.n_inp()); + assert(R.has_single_conjunct()); + int n_zero=0; + for (DNF_Iterator d(R.query_DNF()); d.live(); d.next()) + n_zero=d.curr()->query_guaranteed_leading_0s(); + + Relation Diff=Deltas(copy(R)); + + if (detailedClosureDebug) { + fprintf(DebugFile, "The relation projected onto differencies is:\n"); + Diff.print_with_subs(DebugFile); + } + + + /* now form D */ + + int i; + coef_t l,u; + F_And * N = D.add_and(); + GEQ_Handle g; + for (i=1; i<=Diff.n_set(); i++) { + Diff.query_variable_bounds(Diff.set_var(i), l,u); +/* if (i== n_zero+1 && l==negInfinity) + l=1; */ + if (l!=negInfinity) { + g=N->add_GEQ(); + g.update_coef(D.input_var(i),-1); + g.update_coef(D.output_var(i),1); + g.update_const(-l); + g.finalize(); + } + if (u!=posInfinity) { + g=N->add_GEQ(); + g.update_coef(D.input_var(i),1); + g.update_coef(D.output_var(i),-1); + g.update_const(u); + g.finalize(); + } + } + + /* add all stride constrains if they do exist */ + + Conjunct *c = Diff.single_conjunct(); + + if (c->locals().size()>0) {// there are local variables + // now go through all the equalities + + coef_t coef=0; + int pos=0; + for (EQ_Iterator eq = c->EQs(); eq.live(); eq.next()) { + // constraint is in stride form if it has 2 vars, + // one of which is wildcard. Count number if vars and wildcard vars + int nwild=0,nvar=0; + + for (Constr_Vars_Iter cvi(*eq, false); cvi; cvi++) { + if ((*cvi).var->kind() == Global_Var) + continue; + else if ((*cvi).var->kind() == Wildcard_Var) { + coef=(*cvi).coef; + nwild++; + } + else + pos=(*cvi).var->get_position(); + nvar++; + } + if (nvar==2 && nwild==1) { //stride constraint + EQ_Handle e=N->add_stride(coef); + e.update_coef(D.input_var(pos),-1); + e.update_coef(D.output_var(pos),1); + e.finalize(); + } + } + } // end search of stride constrains + + D.finalize(); + D.simplify(); + return D; +} /* end get_D_form */ + +/**** + * get relation A x A describing a region of domain and range: + * A=Hull(Domain(R), Range(R)) intersection IterationSpace + * returns cross product A x A + ***/ + +Relation form_region(const Relation &R, const Relation& IterationSpace) { + Relation H=Union(Domain(copy(R)), Range(copy(R))); + H.simplify(1,1); + H = EQs_to_GEQs(H); + H=Hull(H); + Relation A=Intersection(H, copy(IterationSpace)); + Relation A1=A; + return Cross_Product(A,A1); +} + +Relation form_region1(const Relation &R, const Relation& IterationSpace) { + Relation Dom=Intersection(Domain(copy(R)), copy(IterationSpace)); + Relation Ran=Intersection(Range(copy(R)), copy(IterationSpace)); + return Cross_Product(Dom,Ran); +} + + +/**** + * Check if we can use D instead of R + * i.e. D intersection (A cross A) is Must_Be_Subset of R + ***/ + +bool isD_OK(Relation &R, Relation &D, Relation &AxA) { + Relation B=Intersection(copy(D), copy(AxA)); + B.simplify(); + + if (detailedClosureDebug) { + fprintf(DebugFile, "Intersection of D and AxA is:\n"); + B.print_with_subs(DebugFile); + } + assert (Must_Be_Subset(copy(R),copy(B))); + + return Must_Be_Subset(B, copy(R)); +} + + + +/**** + * check if the constraint is a stride one. Here we say that an equality + * constraint is a stride constraint if it has exatly one wildcard. + * The function returns number of the wildcards in the constraint. + * So if we know that constraint is from the relation in D form, then + * it cannot have more than 1 wildcard variables, and the result of + * this functions can be treated as bool. + ***/ + +static int is_stride(const EQ_Handle &eq) { + int n=0; + + for (Constr_Vars_Iter cvi(eq,true); cvi; cvi++) + n++; + + return n; +} + + + +/***** + * check if the constraint is in the form i_k' - i_k comp_op c + * return v - the number of the var and the type of the comp_op: + * 1 - >, -1 - <, 0 - not in the right form + * if this is equality constraint in the right form any 1 or -1 can be + * returned + ******/ + +static coef_t is_constraint_in_D_form(Relation &r, const Constraint_Handle &h, int &v) { + v=-1; + coef_t c_out = 0; + for (int i = 1; i <= r.n_inp(); i++) { + coef_t c_in = h.get_coef(r.input_var(i)); + if (c_in) { + if (v!=-1) + return 0; + v=i; + c_out = h.get_coef(r.output_var(i)); + + // special case for modular constraint -- by chun 04/02/2009 + if (h.has_wildcards() && typeid(h) == typeid(EQ_Handle)) { + coef_t g = 0; + for (Constr_Vars_Iter cvi(h, true); cvi; cvi++) + g = gcd(g, abs(cvi.curr_coef())); + c_in = int_mod_hat(c_in, g); + c_out = int_mod_hat(c_out, g); + + if (g == 2) { + if (c_in * c_out == 1) { + c_out = -1; + } + else + return 0; + } + else if (c_in * c_out != -1) + return 0; + } + // other cases + else if (c_in * c_out != -1) + return 0; + } + } + return c_out; +} + + +/*** + * Check if relation is in the D form + * D = {[ i_1,i_2,...,i_m] -> [j_1, j_2, ..., j_m ] : + * (forall p, 1<= p <= m) L_p <= j_p - i_p <= U_p && + * j_p - i_p == M_p alpha_p}; + * Right now we do not check for multiple stride constraints for one var. + * Probably they cannot exist in simplified conjunct + * This function will be used in assertions + *****/ + +bool is_in_D_form(Relation & D) { + /* check that D has one conjunct */ + + if (! D.has_single_conjunct()) + return false; + + Conjunct * c=D.single_conjunct(); + + if (D.global_decls()->size() != 0) // there are symbolic vars + return false; + + if (D.n_inp() != D.n_out()) + return false; + + int n=D.n_inp(); + + Tuple<int> bl(n), bu(n); + + for (int i=1; i<= n; i++) + bl[i]=bu[i]=0; + + int v; + coef_t res; + + for (EQ_Iterator eq = c->EQs(); eq.live(); eq.next()) { + if ((res=is_constraint_in_D_form(D,*eq,v))==0) + return false; + int n_wild=is_stride(*eq); + if (n_wild>=2) + return false; + if (n_wild==0) { // not stride constraint + if (bl[v] || bu[v]) + return false; + bl[v]=bu[v]=1; + } + } + + for (GEQ_Iterator geq = c->GEQs(); geq.live(); geq.next()) { + if ((res=is_constraint_in_D_form(D,*geq,v))==0) + return false; + if ((res>0 && bl[v]) || (res<0 && bu[v])) + return false; + if (res>0) + bl[v]=1; + else + bu[v]=1; + } + + return true; +} + + +#define get_D_plus_form(R) (get_D_closure(R,1)) +#define get_D_star_form(R) (get_D_closure(R,0)) + +/**** + * Get D+ or D* from the relation that is in D form + * To get D+ calculate: + * D+= {[i1, i2 .. i_m] -> {j1, j2, ..., j_m]: + * exists s s.t. s>=1 and + * (forall p, 1<= p <= m) L_p * s<= j_p - i_p <= U_p*s && + * j_p - i_p == M_p alpha_p}; + * To get D* calculate almost the same relation but s>=0. + * Parameter n is 1 for getting D+ and 0 for D* + ****/ + + +Relation get_D_closure(Relation & D, int n) { + assert (is_in_D_form(D)); + assert(n==0 || n==1); + + Conjunct *c=D.single_conjunct(); + + Relation R(D.n_inp(), D.n_out()); + + F_Exists * ex = R.add_exists(); + Variable_ID s = ex->declare("s"); + F_And * N = ex->add_and(); + + /* add s>=1 or s>=0 */ + + GEQ_Handle geq= N->add_GEQ(); + geq.update_coef(s,1); + geq.update_const(-n); + geq.finalize(); + + + /* copy and modify all the EQs */ + + for (EQ_Iterator j= c->EQs(); j.live(); j.next()) { + EQ_Handle eq=N->add_EQ(); + copy_constraint(eq, *j); + + // if it's stride constraint do not change it + + if (!is_stride(*j)) { + /* eq is j_k -i_k = c, replace c buy s*c */ + + eq.update_coef(s, (*j).get_const()); + eq.update_const(-(*j).get_const()); + } + eq.finalize(); + } + + /* copy and modify all the GEQs */ + + for (GEQ_Iterator gi= c->GEQs(); gi.live(); gi.next()) { + geq=N->add_GEQ(); + copy_constraint(geq, *gi); + + /* geq is j_k -i_k >=c or i_k-j_k >=c, replace c buy s*c */ + + geq.update_coef(s,(*gi).get_const()); + geq.update_const(-(*gi).get_const()); + geq.finalize(); + } + + R.finalize(); + + if (detailedClosureDebug) { + fprintf(DebugFile, "Simplified D%c is:\n", n==1?'+':'*'); + R.print_with_subs(DebugFile); + } + + return R; +} + + +/*** + * Check if we can easily calculate the D* (D* will be convex). + * We can calculate D* if all differences have both lower and upper + * bounds to be non -/+ infinity + ***/ + + +bool can_get_D_star_form(Relation &D) { + assert(is_in_D_form(D)); + Conjunct *c=D.single_conjunct(); + + int n=D.n_inp(); + Tuple<int> bl(n), bu(n); + int i; + + for (i=1; i<=n; i++) + bl[i]=bu[i]=0; + + for (EQ_Iterator eq = c->EQs(); eq.live(); eq.next()) { + // do not check stride constraints + if (!is_stride(*eq)) { + for (i=1; i<=n; i++) { + if ((*eq).get_coef(D.input_var(i)) !=0 ) + bl[i]=bu[i]=1; + } + } + } + + + for (GEQ_Iterator geq = c->GEQs(); geq.live(); geq.next()) { + for (i=1; i<=n; i++) { + coef_t k; + if ((k=(*geq).get_coef(D.input_var(i))) != 0) { + if (k>0) + bu[i]=1; + else + bl[i]=1; + } + } + } + + for (i=1; i<=n; i++) + if (!bl[i] || !bu[i]) + return false; + + return true; +} + + + +/***** + * Check whether the relation intersect with identity or not + ****/ + +bool does_intersect_with_identity(Relation &R) { + assert (R.n_inp() == R.n_out()); + + Relation I=Identity(R.n_inp()); + Relation C=Intersection(I, copy(R)); + return C.is_upper_bound_satisfiable(); +} + +bool does_include_identity(Relation &R) { + Relation I=Identity(R.n_inp()); + return Must_Be_Subset(I, copy(R)); +} + +/***** + * Bill's closure: check if it is possible to calculate transitive closure + * of the relation using the Bill's algorithm. + * Return the transitive closure relation if it is possible and null relation + * otherwise + ****/ + +bool Bill_closure(Relation &R, Relation& IterationSpace, Relation & R_plus, Relation & R_star) { +#ifdef TC_STATS + fprintf(statsfile,"start bill closure\n"); +#endif + + if (does_include_identity(R)) + return false; + + if (detailedClosureDebug) { + fprintf(DebugFile, "\nApplying Bill's method to calculate transitive closure\n"); + } + + // get D and AxA + Relation D=get_D_form(R); + + + if (detailedClosureDebug) { + fprintf(DebugFile,"\n D form for the relation:\n"); + D.print_with_subs(DebugFile); + } + + Relation AxA=form_region1(R, IterationSpace); + + if (detailedClosureDebug) { + fprintf(DebugFile, "\n AxA for the relation:\n"); + AxA.print_with_subs(DebugFile); + } + + // compute R_+ + + R_plus=Intersection(get_D_plus_form(D), copy(AxA)); + + if (detailedClosureDebug) { + fprintf(DebugFile, "\nR_+= D+ intersection AxA is:\n"); + R_plus.print_with_subs(DebugFile); + } + + // compute R_* + R_star=Intersection(get_D_star_form(D), form_region(R,IterationSpace)); + + if (detailedClosureDebug) { + fprintf(DebugFile, "\nR_*= D* intersection AxA is:\n"); + R_star.print_with_subs(DebugFile); + } + +/* Check that R_+ is acyclic. + Given the way we constructed R_+, R_+=(R_+)+. + As a result it's enough to verify that R_+ intersection I = 0, + to prove that R_+ is acyclic. +*/ + + if (does_intersect_with_identity(R_plus)) { + if (detailedClosureDebug) { + fprintf(DebugFile,"R_+ is not acyclic.\n"); + } + return false; + } + + //Check R_+ - R is Must_Be_Subset of R o R_+ + + if (!Must_Be_Subset(Difference(copy(R_plus), copy(R)), Composition(copy(R), copy(R_plus)))) { +#if defined(TC_STATS) + fprintf(statsfile, "R_+ -R is not a Must_Be_Subset of R o R_+\n"); + fprintf(statsfile, "Bill Method is not applicable\n"); +#endif + return false; + } + if (detailedClosureDebug) { + fprintf(DebugFile, "R_+ -R is a Must_Be_Subset of R o R_+ - good\n"); + } + +// if we are here than all tests worked, and R_+ is transitive closure +// of R. + +#if defined(TC_STATS) + fprintf(statsfile,"\nAll three tests succeeded -- exact closure found\n"); + fprintf(statsfile, "Transitive closure is R_+\n"); +#endif +// assert(isD_OK(R,D,AxA)); + return true; +} + + +/********************************************************************** + * print the relation given the bounds on the iteration space + * If the bounds are unknown (Bounds is Null), then just print relation + * itself + ****/ + +void print_given_bounds( const Relation& R1, NOT_CONST Relation& input_Bounds) { + Relation & Bounds = (Relation &)input_Bounds; + Relation r; + if (Bounds.is_null()) + r=R1; + else + r = Gist(copy(R1),copy(Bounds),1); + r.print_with_subs(DebugFile); +} + +/********************************************************************** + * Investigate closure: + * checks if the copmuted approximation on the Transitive closure + * is upper and lower bound. If it's both - it's exact. + * This function doesn't return any value. It's just prints a lot + * of debug output + * INPUT: + * r - relation + * r_closure - approximation on r+. + * F - iteration space + **********************************************************************/ + +void InvestigateClosure(Relation r, Relation r_closure, Relation F) { + Relation r3; + bool LB_res, UB_res; + + if (!F.is_null()) + F=Cross_Product(copy(F),copy(F)); + + fprintf(DebugFile, "\n\n--->investigating the closure of the relation:\n"); + print_given_bounds(r,F); + + fprintf(DebugFile, "\nComputed closure is:\n"); + print_given_bounds(r_closure,F); + + r3=Composition(copy(r),copy(r_closure)); + r3.simplify(1,1); + + r3=Union(r3,Composition(copy(r_closure),copy(r))); + r3.simplify(1,1); + + r3=Union(r3,copy(r)); + r3.simplify(1,1); + + Relation remainder = Difference(copy(r3),copy(r_closure)); + + if (!F.is_null()) { + r3=Gist(r3,F,1); + } + r3.simplify(1,1); + + if (!F.is_null()) { + r_closure=Gist(r_closure,F,1); + } + r_closure.simplify(1,1); + + LB_res= Must_Be_Subset(copy(r_closure),copy(r3)); + + UB_res=Must_Be_Subset(copy(r3),copy(r_closure)); + + fprintf(DebugFile,"\nThe results of checking closure (gist) are:\n"); + fprintf(DebugFile,"LB - %s, UB - %s\n", LB_res?"YES":"NO", UB_res?"YES":"NO"); + + if (!UB_res) { + remainder.simplify(2,2); + fprintf(DebugFile,"Dependences not included include:\n"); + print_given_bounds(remainder,F); + } +} + + + +/**** + * Transitive closure of the relation containing single conjunct + ****/ + +bool ConjunctTransitiveClosure (NOT_CONST Relation & input_R, Relation & IterationSpace, Relation & R_plus, Relation & R_star) { + Relation R = consume_and_regurgitate(input_R); + assert(R.has_single_conjunct()); + + if (printConjunctClosure) { + fprintf(DebugFile,"\nTaking closure of the single conjunct: [\n"); + R.print_with_subs(DebugFile); + } +#ifdef TC_STATS + fprintf(statsfile,"start conjuncttransitiveclosure\n"); + singles++; +#endif + + if (is_closure_itself(copy(R))) { +#ifdef TC_STATS + fprintf(statsfile, "Relation is closure itself\n"); +#endif + int ndim_all, ndim_domain; + R.dimensions(ndim_all,ndim_domain); + if (ndim_all == ndim_domain +1) { + Relation ispace = Cross_Product(Domain(copy(R)),Range(copy(R))); + Relation R_zero = Intersection(copy(ispace),Identity(R.n_inp())); + R_star = Hull(Union(copy(R),R_zero),true,1,ispace); + R_plus=R; + if (printConjunctClosure) { + fprintf(DebugFile, "\n] For this relation R+=R\n"); + fprintf(DebugFile,"R*:\n"); + R_star.print_with_subs(DebugFile); + } + return true; + } + else { + R_star=R; + R_plus=R; + if (printConjunctClosure) { + fprintf(DebugFile, "\n] For this relation R+=R, not appropriate for R*\n"); + } + return false; + } + } + else { + bool done=false; + if (!IterationSpace.is_null()) { +// Bill's closure requires the information about Iteration Space. +// So if IterationSpace is NULL, i.e. unknown( e.g. when calling from parser, +// we do not do Bill's closure + + done = Bill_closure(R, IterationSpace, R_plus, R_star); +#ifdef TC_STATS + fprintf(statsfile,"Bill closure is %sapplicable\n",done?"":"not "); +#endif + if (printConjunctClosure) { + if (!done) + fprintf(DebugFile, "Bill's closure is not applicable\n"); + else { + fprintf(DebugFile, "Bill's closure is applicable\n"); + fprintf (DebugFile, " For R:\n"); + R.print_with_subs(DebugFile); + fprintf(DebugFile, "R+ is:\n"); + R_plus.print_with_subs(DebugFile); + fprintf(DebugFile, "R* is:\n"); + R_star.print_with_subs(DebugFile); + fprintf(DebugFile, "\n"); + InvestigateClosure(R, R_plus, IterationSpace); + } + } + } + if (done) { + if (printConjunctClosure) { + fprintf(DebugFile, "]\n"); + } + return true; + } + else { + // do and check approximate closure (several compositions) + R_plus = approx_closure(copy(R), 2); +#ifdef TC_STATS + fprintf(statsfile,"Approximating closure with 2 compositions\n"); +#endif + if (printConjunctClosure) { + fprintf(DebugFile, "Doing approximate closure\n"); + InvestigateClosure(R, R_plus, IterationSpace); + } + } //end else (!done after Bill Closure or Iteration space is NULL) + + if (printConjunctClosure) { + fprintf(DebugFile, "]\n"); + } + } + return false; +} + + +/********************************************************************* + * try to get conjunct transitive closure. + * it we can get it easy get it, return true. + * if not - return false + ********************************************************************/ + + +bool TryConjunctTransitiveClosure (NOT_CONST Relation & input_R, Relation & IterationSpace, Relation & R_plus) { + Relation R = consume_and_regurgitate(input_R); + assert(R.has_single_conjunct()); +#ifdef TC_STATS + fprintf(statsfile,"start tryconjuncttransitiveclosure\n"); + singles++; +#endif + + if (printConjunctClosure) { + fprintf(DebugFile,"\nTrying to take closure of the single conjunct: [\n"); + R.print_with_subs(DebugFile); + } + + if (is_closure_itself(copy(R))) { +#ifdef TC_STATS + fprintf(statsfile, "Relation is closure itself, leave alone (try)\n"); +#endif + if (printConjunctClosure) + fprintf(DebugFile, "\n ]The relation is closure itself. Leave it alone\n"); + return false; + } + else { + bool done; + assert(!IterationSpace.is_null()); + Relation R_star; + done = Bill_closure(R, IterationSpace, R_plus, R_star); +#ifdef TC_STATS + fprintf(statsfile, "Bill closure is %sapplicable (try)\n", done?"":"NOT "); +#endif + if (printConjunctClosure) { + if (!done) + fprintf(DebugFile, "]Bill's closure is not applicable\n"); + else { + fprintf(DebugFile, "]Bill's closure is applicable\n"); + fprintf (DebugFile, " For R:\n"); + R.print_with_subs(DebugFile); + fprintf(DebugFile, "R+ is:\n"); + R_plus.print_with_subs(DebugFile); + fprintf(DebugFile, "R* is:\n"); + R_star.print_with_subs(DebugFile); + fprintf(DebugFile, "\n"); + InvestigateClosure(R, R_plus, IterationSpace); + } + } + return done; + } + //return false; +} + + +bool Equal (const Relation & r1, const Relation & r2) { + bool res=Must_Be_Subset (copy(r1), copy(r2)); + if (!res) + return false; + return Must_Be_Subset (copy(r2),copy(r1)); +} + + +void appendClausesToList(Simple_List<Relation> &L, Relation &R) { + R.make_level_carried_to(R.n_inp()); + R.simplify(2,2); + for(int depth = R.n_inp(); depth >= -1; depth--) + for (DNF_Iterator d(R.query_DNF()); d.live(); d.next()) + if (d.curr()->query_guaranteed_leading_0s() == depth) { + L.append(Relation(R, d.curr())); + } +} + +void printRelationList(Simple_List<Relation> &L) { + for (Simple_List_Iterator<Relation> li(L); li.live(); li.next()) { + li.curr().print_with_subs(DebugFile); + } +} + +/**** + * Transitive closure of the relation containing multiple conjuncts + * New (Bill's) version + ***/ + +Relation TransitiveClosure0(NOT_CONST Relation &input_r, int maxExpansion, NOT_CONST Relation & input_IterationSpace) { + Relation r = consume_and_regurgitate(input_r); + Relation IterationSpace = consume_and_regurgitate(input_IterationSpace); + + if (closure_presburger_debug) + fprintf(DebugFile, "\n\n[Transitive closure\n\n"); + + Relation result; + +#ifdef TC_STATS +#define TC_RUNS 1 + int in_conj = copy(r).query_DNF()->length(); + totals++; + fprintf(statsfile,"%d closure run\n", totals); + if(is_in_D_form(copy(r))) + fprintf(statsfile, "Relation initially in D form\n"); + else + fprintf(statsfile, "Relation initially NOT in D form\n"); + if(is_lex_forward(copy(r))) + fprintf(statsfile, "Relation is initially lex forw\n"); + else + fprintf(statsfile, "Relation is NOT initially lex forw\n"); + start_clock(); + for(int tc_loop = 1; tc_loop <= TC_RUNS; tc_loop++) { + singles = 0; +#endif + + assert(!r.is_null()); + assert(r.n_inp() == r.n_out()); + + if (r.max_ufs_arity() > 0) { + assert(r.max_ufs_arity() == 0 && "Can't take transitive closure with UFS yet."); + + fprintf(stderr, "Can't take transitive closure with UFS yet."); + exit(1); + } + + r.simplify(2,2); + if (!r.is_upper_bound_satisfiable()) { +#ifdef TC_STATS + int totalTime = clock_diff(); + fprintf(statsfile, "Relation is unsatisfiable\n"); + fprintf(statsfile, "input conj: %d output conj: %d #singe conj closures: %d time: %d\n", + in_conj, copy(result).query_DNF()->length(), + singles, + totalTime/TC_RUNS); +#endif + + + if (closure_presburger_debug) + fprintf(DebugFile, "]TC : relation is false\n"); + return r; + } + + IterationSpace = Hull(Union(Domain(copy(r)),Range(copy(r))), true, 1, IterationSpace); + + if (detailedClosureDebug) { + fprintf(DebugFile, "r is:\n"); + r.print_with_subs(DebugFile); + fprintf(DebugFile, "IS is:\n"); + IterationSpace.print_with_subs(DebugFile); + } + Relation dom = Domain(copy(r)); + dom.simplify(2,1); + Relation rng = Range(copy(r)); + rng.simplify(2,1); + Relation AC = ConicClosure(Restrict_Range(Restrict_Domain(copy(r),copy(rng)),copy(dom))); + Relation UB = Union(copy(r),Join(copy(r),Join(AC,copy(r)))); + UB.simplify(2,1); + + if (detailedClosureDebug) { + fprintf(DebugFile, "UB is:\n"); + UB.print_with_subs(DebugFile); + } + result = Relation::False(r); + Simple_List<Relation> firstChoice,secondChoice; + + r.simplify(2,2); + + Relation test = Difference(copy(r),Composition(copy(r),copy(r))); + test.simplify(2,2); + if (r.number_of_conjuncts() > test.number_of_conjuncts()) { + Relation test2 = Union(copy(test),Composition(copy(test),copy(test))); + test2.simplify(2,2); + if (Must_Be_Subset(copy(r),copy(test2))) r = test; + else if (detailedClosureDebug) { + fprintf(DebugFile, "Transitive reduction not possible:\n"); + fprintf(DebugFile, "R is:\n"); + r.print_with_subs(DebugFile); + fprintf(DebugFile, "test2 is:\n"); + test2.print_with_subs(DebugFile); + } + } + + r.make_level_carried_to(r.n_inp()); + if (detailedClosureDebug) { + fprintf(DebugFile, "r is:\n"); + r.print_with_subs(DebugFile); + } + for(int depth = r.n_inp(); depth >= -1; depth--) + for (DNF_Iterator d(r.query_DNF()); d.live(); d.next()) + if (d.curr()->query_guaranteed_leading_0s() == depth) { + Relation C(r, d.curr()); + firstChoice.append(C); + } + + bool first_conj=true; + for (Simple_List_Iterator<Relation> sli(firstChoice); sli; sli++) { + if (first_conj) + first_conj=false; + else { + Relation C_plus; + bool change=TryConjunctTransitiveClosure( + copy(sli.curr()), IterationSpace, C_plus); + if (change) + sli.curr()=C_plus; + } + } + + //compute closure + int maxClauses = 3+firstChoice.size()*(1+maxExpansion); + + int resultConjuncts = 0; + int numFails = 0; + bool resultInexact = false; + while (!firstChoice.empty() || !secondChoice.empty()) { + Relation R_plus, R_star; + + if (detailedClosureDebug) { + fprintf(DebugFile,"Main loop of TC:\n"); + if (!firstChoice.empty()) { + fprintf(DebugFile,"First choice:\n"); + printRelationList(firstChoice); + } + if (!secondChoice.empty()) { + fprintf(DebugFile,"Second choice:\n"); + printRelationList(secondChoice); + } + } + + Relation R; + if (!firstChoice.empty()) + R = firstChoice.remove_front(); + else R = secondChoice.remove_front(); + + if (detailedClosureDebug) { + fprintf(DebugFile, "Working with conjunct:\n"); + R.print_with_subs(DebugFile); + } + + bool known=ConjunctTransitiveClosure(copy(R),IterationSpace, R_plus, R_star); + + if (!known && numFails < firstChoice.size()) { + numFails++; + firstChoice.append(R); + if (detailedClosureDebug) { + fprintf(DebugFile, "\nTry another conjunct, R is not suitable\n"); + R.print_with_subs(DebugFile); + } + continue; + } + + + if (detailedClosureDebug) { + fprintf(DebugFile,"\nR+ is:\n"); + R_plus.print_with_subs(DebugFile); + if (known) { + fprintf(DebugFile, "Known R? is :\n"); + R_star.print_with_subs(DebugFile); + } + else + fprintf(DebugFile, "The R* for this relation is not calculated\n"); + } + + Relation R_z; + if (known) { + R_z=Difference(copy(R_star),copy(R_plus)); + known = R_z.is_upper_bound_satisfiable(); + if (known) { + int d = R.single_conjunct()->query_guaranteed_leading_0s(); + R_z.make_level_carried_to(min(R.n_inp(),d+1)); + if (R_z.query_DNF()->length() > 1) known = false; + if (detailedClosureDebug) { + fprintf(DebugFile, "\nForced R_Z to be level carried at level %d\n",min(R.n_inp(),d+1)); + } + } + if (detailedClosureDebug) { + if (known) { + fprintf(DebugFile, "\nDifference between R? and R+ is:\n"); + R_z.print_with_subs(DebugFile); + } + else + fprintf(DebugFile, "\nR_z is unusable\n"); + } + } + else R_z = Relation::False(r); + + if (!known) + numFails++; + else numFails = 0; + if (!known && numFails <= firstChoice.size()) { + firstChoice.append(R); + if (detailedClosureDebug) { + fprintf(DebugFile, "\nTry another conjunct, Rz is avaiable for R:\n"); + R.print_with_subs(DebugFile); + } + continue; + } + + //make N empty list + Relation N = Relation::False(r); + + //append R+ to T + result = Union(result, copy(R_plus)); + resultConjuncts++; + + int expansion = maxClauses - (resultConjuncts + 2*firstChoice.size() + secondChoice.size()); + if (expansion < 0) expansion = 0; + if (detailedClosureDebug) { + fprintf(DebugFile,"Max clauses = %d\n",maxClauses); + fprintf(DebugFile,"result conjuncts = %d\n",resultConjuncts); + fprintf(DebugFile,"firstChoice's = %d\n",firstChoice.size()); + fprintf(DebugFile,"secondChoice's = %d\n",secondChoice.size()); + fprintf(DebugFile,"Allowed expansion is %d\n",expansion); + } + + bool firstPart=true; + if (!known && expansion == 0) { + if (detailedClosureDebug) { + fprintf(DebugFile,"Expansion = 0, R? unknown, skipping composition\n"); + } + if (!resultInexact && detailedClosureDebug) fprintf(DebugFile,"RESULT BECOMES INEXACT 1\n"); + resultInexact = true; + } + else + for (Simple_List_Iterator<Relation> s(firstChoice); + firstPart? + (s.live()?true: + (s = Simple_List_Iterator<Relation>(secondChoice), + firstPart = false, + s.live())) + :s.live(); + s.next()) { + assert(s.live()); + Relation C=(s.curr()); + if (detailedClosureDebug) { + fprintf(DebugFile, "\nComposing chosen conjunct with C:\n"); + C.print_with_subs(DebugFile); + } + + if (!known) { + if (detailedClosureDebug) { + fprintf(DebugFile, "\nR? is unknown! No debug info here yet\n"); + } + Relation C1=Composition(copy(C), copy(R_plus)); + if (detailedClosureDebug) { + fprintf(DebugFile, "\nGenerating \n"); + C1.print_with_subs(DebugFile); + } + C1.simplify(); + Relation newStuff = + Difference( + Difference(copy(C1),copy(C)), + copy(R_plus)); + newStuff.simplify(); + if (detailedClosureDebug) { + fprintf(DebugFile, "New Stuff:\n"); + newStuff.print_with_subs(DebugFile); + } + bool C1_contains_new_stuff = newStuff.is_upper_bound_satisfiable(); + if (C1_contains_new_stuff) { + if (newStuff.has_single_conjunct()) + C1 = newStuff; + if (expansion) { + N = Union(N,copy(C1)); + expansion--; + } + else { + if (!resultInexact && detailedClosureDebug) fprintf(DebugFile,"RESULT BECOMES INEXACT 2\n"); + resultInexact = true; + break; + } + } + else C1 = Relation::False(C1); + + Relation C2(Composition(copy(R_plus),copy(C))); + if (detailedClosureDebug) { + fprintf(DebugFile, "\nGenerating \n"); + C2.print_with_subs(DebugFile); + } + newStuff = + Difference( + Difference( + Difference(copy(C2),copy(C)), + copy(C1)), + copy(R_plus)); + newStuff.simplify(); + if (detailedClosureDebug) { + fprintf(DebugFile, "New Stuff:\n"); + newStuff.print_with_subs(DebugFile); + } + if (newStuff.is_upper_bound_satisfiable()) { + if (newStuff.has_single_conjunct()) + C2 = newStuff; + if (expansion) { + N = Union(N,copy(C2)); + expansion--; + } + else { + if (!resultInexact && detailedClosureDebug) fprintf(DebugFile,"RESULT BECOMES INEXACT 3\n"); + resultInexact = true; + break; + } + } + else C2 = Relation::False(C2); + + if (C1_contains_new_stuff) { + Relation C3(Composition(copy(R_plus),copy(C1))); + if (detailedClosureDebug) { + fprintf(DebugFile, "\nGenerating \n"); + C3.print_with_subs(DebugFile); + } + newStuff = + Difference( + Difference( + Difference( + Difference(copy(C3),copy(C)), + copy(C1)), + copy(C2)), + copy(R_plus)); + newStuff.simplify(); + if (detailedClosureDebug) { + fprintf(DebugFile, "New Stuff:\n"); + newStuff.print_with_subs(DebugFile); + } + if (newStuff.is_upper_bound_satisfiable()) { + if (newStuff.has_single_conjunct()) + C3 = newStuff; + if (expansion) { + N = Union(N,C3); + expansion--; + } + else { + if (!resultInexact && detailedClosureDebug) fprintf(DebugFile,"RESULT BECOMES INEXACT 4\n"); + resultInexact = true; + break; + } + } + } + + } + else { + Relation C_Rz(Composition(copy(C),copy(R_z))); + if (detailedClosureDebug) { + fprintf(DebugFile, "C o Rz is:\n"); + C_Rz.print_with_subs(DebugFile); + } + + Relation Rz_C_Rz(Composition(copy(R_z),copy(C_Rz))); + if (detailedClosureDebug) { + fprintf(DebugFile, "\nRz o C o Rz is:\n"); + Rz_C_Rz.print_with_subs(DebugFile); + } + + if (Equal(C,Rz_C_Rz)) { +#if defined(TC_STATS) + fprintf(statsfile,"weak test selects C?\n"); +#endif + Relation tmp = Composition(C,copy(R_star)); + tmp.simplify(); + Relation tmp2 = Composition(copy(R_star),copy(tmp)); + tmp2.simplify(); + if (Must_Be_Subset(copy(tmp2),copy(tmp))) + *s = tmp; + else + *s = tmp2; + if (detailedClosureDebug) { + fprintf(DebugFile,"\nC is equal to Rz o C o Rz so R? o C o R? replaces C\n"); + fprintf(DebugFile, "R? o C o R? is:\n"); + (*s).print_with_subs(DebugFile); + } + } + else { +#if defined(TC_STATS) + fprintf(statsfile,"weak test fails\n"); +#endif + if (Equal(C, C_Rz)) { + *s=Composition(copy(C),copy(R_star)); + Relation p(Composition(copy(R_plus), copy(*s))); + p.simplify(); + if (detailedClosureDebug) { + fprintf(DebugFile, "\nC is equal to C o Rz, so C o Rz replaces C\n"); + fprintf (DebugFile, "C o R? is:\n"); + (*s).print_with_subs(DebugFile); + fprintf (DebugFile, "R+ o C o R? is added to list N. It's :\n"); + p.print_with_subs(DebugFile); + } + if (!Is_Obvious_Subset(copy(p),copy(R_plus)) + && !Is_Obvious_Subset(copy(p),copy(C))) { + if (expansion) { + p.simplify(2,2); + expansion--; + } + else { + if (!resultInexact && detailedClosureDebug) fprintf(DebugFile,"RESULT BECOMES INEXACT 5\n"); + resultInexact = true; + break; + } + } + } + else { + Relation Rz_C(Composition(copy(R_z),copy(C))); + + if (Equal(C,Rz_C)) { + *s=Composition(copy(R_star),copy(C)); + Relation Rstar_C_Rplus(Composition(copy(*s),copy(R_plus))); + Rstar_C_Rplus.simplify(); + if (detailedClosureDebug) { + fprintf(DebugFile, "\nC is equal to Rz o C , so R? o C replaces C\n"); + fprintf (DebugFile, "R? o C is:\n"); + (*s).print_with_subs(DebugFile); + fprintf (DebugFile, "R+ o C is added to list N. It's :\n"); + Rstar_C_Rplus.print_with_subs(DebugFile); + } + if (!Is_Obvious_Subset(copy(Rstar_C_Rplus),copy(R_plus)) + && !Is_Obvious_Subset(copy(Rstar_C_Rplus),copy(C))) { + if (expansion) + N = Union(N,Rstar_C_Rplus); + else { + if (!resultInexact && detailedClosureDebug) fprintf(DebugFile,"RESULT BECOMES INEXACT 6\n"); + resultInexact = true; + break; + } + } + } + else { + if (detailedClosureDebug) { + fprintf(DebugFile, "\nHave to handle it the hard way\n"); + } + Relation C1=Composition(copy(C), copy(R_plus)); + C1.simplify(); + if (!Is_Obvious_Subset(copy(C1),copy(R_plus)) + && !Is_Obvious_Subset(copy(C1),copy(C))) { + if (expansion) { + N = Union(N,copy(C1)); + expansion--; + } + else { + if (!resultInexact && detailedClosureDebug) fprintf(DebugFile,"RESULT BECOMES INEXACT 7\n"); + resultInexact = true; + break; + } + } + + Relation C2(Composition(copy(R_plus),copy(C))); + C2.simplify(); + if (!Is_Obvious_Subset(copy(C2),copy(R_plus)) + && !Is_Obvious_Subset(copy(C2),copy(C))) { + if (expansion) { + N = Union(N,C2); + expansion--; + } + else { + if (!resultInexact && detailedClosureDebug) { + fprintf(DebugFile,"RESULT BECOMES INEXACT 8\n"); + fprintf(DebugFile,"Have to discard:\n"); + C2.print_with_subs(DebugFile); + } + resultInexact = true; + break; + } + } + Relation C3(Composition(copy(R_plus),C1)); + C3.simplify(); + if (!Is_Obvious_Subset(copy(C3),copy(R_plus)) && !Is_Obvious_Subset(copy(C3),copy(C))) { + if (expansion) { + N = Union(N,C3); + expansion--; + } + else { + if (!resultInexact && detailedClosureDebug) + fprintf(DebugFile,"RESULT BECOMES INEXACT 9\n"); + resultInexact = true; + break; + } + } + } + } + } + } + } + + //now we processed the first conjunct. + if (detailedClosureDebug) { + N.simplify(2,2); + fprintf(DebugFile, "\nNew conjuncts:\n"); + N.print_with_subs(DebugFile); + } + + N.simplify(2,2); + appendClausesToList(secondChoice,N); + } + + //Did we do all conjuncts? If not, make T be inexact + result.copy_names(r); + + result.simplify(2,2); + + if (!result.is_exact()) { + result = Lower_Bound(result); + resultInexact = true; + } + if (resultInexact) { + Relation test(Composition(copy(result),copy(result))); + test.simplify(2,2); + if (detailedClosureDebug) { + fprintf(DebugFile, "\nResult is:\n"); + result.print_with_subs(DebugFile); + fprintf(DebugFile, "\nResult composed with itself is:\n"); + test.print_with_subs(DebugFile); + } + if (!Must_Be_Subset(test,copy(result))) { + result = Union(result,Intersection(UB, Relation::Unknown(result))); + } + } + +#ifdef TC_STATS + { + Relation rcopy = result; + Relation test2(Composition(copy(rcopy),copy(rcopy))); + test2.simplify(2,2); + test2.remove_disjunction_with_unknown(); + rcopy.remove_disjunction_with_unknown(); + if (detailedClosureDebug) { + fprintf(DebugFile, "\nResult is:\n"); + rcopy.print_with_subs(DebugFile); + fprintf(DebugFile, "\nResult composed with itself is:\n"); + test2.print_with_subs(DebugFile); + } + if (!Must_Be_Subset(test2,copy(rcopy))) { + fprintf(statsfile,"multi TC result is inexact\n"); + } + else + fprintf(statsfile,"TC result is exact%s\n", (resultInexact || !rcopy.is_exact())?" despite perceived inexactness":""); + } +#endif + +#ifdef TC_STATS + } + int totalTime = clock_diff(); + fprintf(statsfile, "input conj: %d output conj: %d #singe conj closures: %d time: %d\n", + in_conj, copy(result).query_DNF()->length(), + singles, + totalTime/TC_RUNS); +#endif + + if (closure_presburger_debug || detailedClosureDebug) { + if (detailedClosureDebug) { + fprintf(DebugFile, "\nThe transitive closure is :\n"); + result.print_with_subs(DebugFile); + } + fprintf(DebugFile, "\n\n] END Transitive closure\n\n"); + } + return result; +} + + +Relation TransitiveClosure(NOT_CONST Relation &input_r, + int maxExpansion, + NOT_CONST Relation & input_IterationSpace) { + Relation r = consume_and_regurgitate(input_r); + Relation IterationSpace = consume_and_regurgitate(input_IterationSpace); + if (r.is_null()) + return r; + if (r.n_out() == 0) + throw std::invalid_argument("transitive closure does not apply to set"); + if (r.n_inp() != r.n_out()) + throw std::invalid_argument("transitive closure must has the same input and output arity"); + + if (closure_presburger_debug) { + fprintf(DebugFile,"\nComputing Transitive closure of:\n"); + r.print_with_subs(DebugFile); + fprintf(DebugFile,"\nIteration space is:\n"); + IterationSpace.print_with_subs(DebugFile); + } + if (!r.is_upper_bound_satisfiable()) { + if (closure_presburger_debug) + fprintf(DebugFile, "]TC : relation is false\n"); + return r; + } + + Relation UB = DeltasToRelation(ConicHull(Project_Sym(Deltas(copy(r)))), + r.n_inp(),r.n_out()); + if (closure_presburger_debug) { + fprintf(DebugFile,"UB is:\n"); + UB.print_with_subs(DebugFile); + } + + Relation conditions = Restrict_Domain(copy(UB),Domain(copy(r))); + conditions.simplify(); + if (closure_presburger_debug) { + fprintf(DebugFile,"Forward reachable is:\n"); + conditions.print_with_subs(DebugFile); + } + conditions = Composition(Inverse(copy(UB)),conditions); + conditions.simplify(); + if (closure_presburger_debug) { + fprintf(DebugFile,"Backward/forward reachable is:\n"); + conditions.print_with_subs(DebugFile); + } + conditions = Range(conditions); + conditions.simplify(); + // conditions = Approximate(conditions); + // conditions.simplify(); + conditions = VennDiagramForm(conditions); + conditions.simplify(); + + if (closure_presburger_debug) { + fprintf(DebugFile,"Condition regions are:\n"); + conditions.print_with_subs(DebugFile); + } + + if (conditions.is_obvious_tautology()) { + return TransitiveClosure0(r, maxExpansion, IterationSpace); + } + else { + Relation answer = Relation::False(r); + answer.copy_names(r); + answer.setup_names(); + + for (DNF_Iterator c(conditions.query_DNF()); c.live(); c.next()) { + Relation tmp = Relation(conditions, c.curr()); + if (closure_presburger_debug) { + fprintf(DebugFile,"\nComputing Transitive closure:\n"); + fprintf(DebugFile,"\nRegion:\n"); + tmp.prefix_print(DebugFile); + } + + Relation tmp3 = Restrict_Domain(copy(r),tmp); + tmp3.simplify(2,2); + if (closure_presburger_debug) { + fprintf(DebugFile,"\nRelation:\n"); + tmp3.prefix_print(DebugFile); + } + + answer = Union(answer, TransitiveClosure0(tmp3, maxExpansion,copy(IterationSpace))); + } + return answer; + } +} + + +/* ********************************* */ +/* Function check if relation */ +/* belong to d-form or */ +/* uniform relaion class */ +/* ********************************* */ + +Relation is_DForm_or_Uniform(NOT_CONST Relation &r){ + + Relation s = consume_and_regurgitate(r); + Relation Rtmp, Rdelta, delta; + + delta = Deltas(copy(s)); + Rdelta = DeltasToRelation(copy(delta), s.n_inp(), s.n_out()); + Rtmp = DeltasToRelation(Project_Sym(delta), s.n_inp(), s.n_out()); + + Rtmp = Restrict_Domain(Rtmp, Domain(copy(Rdelta))); + Rtmp = Restrict_Range(Rtmp, Range(Rdelta)); + + Rdelta = copy(Rtmp); + + Rtmp = Restrict_Domain(Rtmp, Domain(copy(s))); + Rtmp = Restrict_Range(Rtmp, Range(copy(s))); + + if (Must_Be_Subset( copy(Rtmp), copy(s)) && \ + Must_Be_Subset(copy(s), copy(Rtmp))) { + Rtmp = Relation::Null(); + } + else { + Rtmp = Rdelta = Relation::Null(); + } + + return Rdelta; + } + + + + /* ********************************* */ + /* Get a conjunction for */ + /* a given number from set */ + /* of relations */ + /* ********************************* */ + +Relation getConjunctionNr(NOT_CONST Relation &r, int conjNr) { + + Relation s = consume_and_regurgitate(r); + int i = 1; + + for (DNF_Iterator c(s.query_DNF()); c; c++,i++) { + if ( i == conjNr ) { + return Relation(s, c.curr()); + } + } + + return Relation::False(s.n_inp(), s.n_out()); + + } + + +/* ********************************* */ +/* Get a common region for */ +/* a given set of relations */ +/* ********************************* */ + +Relation getCommonRegion( NOT_CONST Relation &r, const long* relTab, const long relCount) { + + Relation s = consume_and_regurgitate(r); + Relation commonRegion, Rcurr; + long i = 0; + + Rcurr = getConjunctionNr( copy(s), relTab[0]); + commonRegion = Union(Domain(copy(Rcurr)), Range(copy(Rcurr))); + + for( i=1; i < relCount; i++ ){ + Rcurr = getConjunctionNr( copy(s), relTab[i]); + commonRegion = Intersection( commonRegion, Union( Domain(copy(Rcurr)), Range(copy(Rcurr))) ); + } + + return commonRegion; + } + + +/* ********************************* */ +/* Get a set of relations */ +/* ********************************* */ + +Relation getRelationsSet( NOT_CONST Relation &r, const long* relTab, const long relCount) { + + Relation s = consume_and_regurgitate(r); + Relation R = Relation::False(s.n_inp(), s.n_out()); + long i = 0; + + for( i=0; i < relCount; i++ ){ + R = Union( R, getConjunctionNr( copy(s), relTab[i]) ); + } + + return R; + } + + +/* ********************************* */ +/* Get a set of relations */ +/* from a common region */ +/* ********************************* */ + +Relation relationsOnCommonRegion( NOT_CONST Relation &r, NOT_CONST Relation ®ion ) { + + Relation set = consume_and_regurgitate(r); + Relation reg = consume_and_regurgitate(region); + Relation R = Relation::True(set.n_inp(), set.n_out()); + + R = Restrict_Domain(R, copy(reg)); + R.simplify(2,1); + R = Restrict_Range(R, reg); + R.simplify(2,1); + + R = Intersection(R, set); + + return R; + + } + + +Relation compose_N(NOT_CONST Relation &input_r) { + Relation r = consume_and_regurgitate(input_r); + Relation powerR, powerR2; + + r = Union(r, Identity(r.n_inp())); + powerR = copy(r); + + for(;;){ + if (powerR.number_of_conjuncts() > 50) { + powerR = Relation::Null(); + return powerR; + } + + powerR2 = Composition(copy(powerR), copy(r)); + powerR2.simplify(2,1); + + if (Must_Be_Subset( copy(powerR2), copy(powerR))) { + powerR2 = Relation::Null(); + return powerR; + } + + powerR = Relation::Null(); + powerR = copy(powerR2); + powerR2 = Relation::Null(); + } +} + + +/****************************** */ +/* Check exactness of R+ */ +/* */ +/* Tomasz Klimek 05-06-2010 */ +/****************************** */ + +bool checkExactness(NOT_CONST Relation &r, NOT_CONST Relation &rplus){ + + +Relation s1 = consume_and_regurgitate(r); +Relation s2 = consume_and_regurgitate(rplus); +Relation R; + +R = Composition(copy(s1), copy(s2)); +R = Union(s1, R); + + if( Must_Be_Subset(copy(s2), copy(R)) && \ + Must_Be_Subset(copy(R), copy(s2))) { + R = Relation::Null(); + s1 = Relation::Null(); + return true; + } + + R = Relation::Null(); + s1 = Relation::Null(); + + return false; + +} + +/************************************** */ +/* Calculate approximation of R* */ +/* */ +/* Tomasz Klimek 05-06-2010 */ +/************************************** */ + + +Relation ApproxClosure(NOT_CONST Relation &r) { + + Relation s = consume_and_regurgitate(r); + Relation R = Relation::False(s.n_inp(), s.n_out()); + Relation tc = Identity(s.n_inp()); + Relation Rtmp; + + + for (DNF_Iterator c(s.query_DNF()); c; c++) { + Rtmp = Hull(Project_Sym(Deltas(Relation(s, c.curr()))), false, 1, Relation::Null()); + R = Union(R, TransitiveClosure(DeltasToRelation(Rtmp,s.n_inp(),s.n_out()), 1, Relation::Null())); + } + + for (DNF_Iterator c(R.query_DNF()); c; c++) { + Rtmp = Union(Identity(s.n_inp()), Relation(R, c.curr())); + tc = Composition(tc, Rtmp); + tc = Hull(tc, false, 1, Relation::Null()); + } + + tc = Restrict_Domain(tc,Domain(copy(s))); + tc.simplify(2,1); + tc = Restrict_Range(tc,Range(s)); + tc.simplify(2,1); + tc = Intersection(tc, Relation::Unknown(tc)); + + return tc; +} + + +/************************************** */ +/* Calculate R* on unbounded region */ +/* */ +/* Tomasz Klimek 05-06-2010 */ +/************************************** */ + +Relation ClosureOnUnboundedRegion(NOT_CONST Relation &r) { + + Relation s = consume_and_regurgitate(r); + Relation R = Relation::False(s.n_inp(), s.n_out()); + Relation tc = Identity(s.n_inp()); + Relation Rtmp,tcTmp; + + for (DNF_Iterator c(s.query_DNF()); c; c++) { + Rtmp = is_DForm_or_Uniform(Relation(s, c.curr())); + + if (!(Rtmp.is_null())) { + tcTmp = TransitiveClosure(Rtmp, 1, Relation::Null()); + + if (!(tcTmp.is_exact())){ + tcTmp = R = Relation::Null(); + /* fprintf(DebugFile,"\nTC is inexact!"); */ + return tcTmp; + } + } + else { + R = Relation::Null(); + /* fprintf(DebugFile,"\nR is not d-form relation!"); */ + return Relation::Null(); + } + + R = Union(R, tcTmp); + } + + for (DNF_Iterator c(R.query_DNF()); c; c++) { + Rtmp = Union(Identity(s.n_inp()), Relation(R, c.curr())); + tc = Composition(tc, Rtmp); + tc.simplify(2,1); + } + + tc = Difference(tc, Identity(s.n_inp())); + + return tc; + +} + + + + +/******************************* */ +/* Try to select sets of domain */ +/* and range */ +/* */ +/* Tomasz Klimek 05-06-2010 */ +/******************************* */ + +Relation SelectRegionForClosure(NOT_CONST Relation &r){ + + Relation s = consume_and_regurgitate(r); + Relation DR = Union(Domain(copy(s)),Range(copy(s))); + Relation region,tc,tcTmp; + + region = SimpleHull(copy(DR)); + region.simplify(2,1); + + tc = ClosureOnUnboundedRegion(copy(s)); + + if (tc.is_null()) { + return tc; + } + + tcTmp = Restrict_Domain(copy(tc),copy(region)); + tcTmp.simplify(2,1); + tcTmp = Restrict_Range(tcTmp,region); + tcTmp.simplify(2,1); + + if (checkExactness(copy(s), copy(tcTmp))) { + s = tc = Relation::Null(); + return tcTmp; + } + + tcTmp = Relation::Null(); + region = Hull(DR,false,1,Relation::Null()); + + tcTmp = Restrict_Domain(copy(tc),copy(region)); + tcTmp.simplify(2,1); + tcTmp = Restrict_Range(tcTmp,region); + tcTmp.simplify(2,1); + + if (checkExactness(copy(s), copy(tcTmp))) { + s = tc = Relation::Null(); + return tcTmp; + } + + tcTmp = Relation::Null(); + + tc = Restrict_Domain(tc,Domain(copy(s))); + tc.simplify(2,1); + tc = Restrict_Range(tc,Domain(copy(s))); + tc.simplify(2,1); + + if (checkExactness(copy(s), copy(tc))) { + s = Relation::Null(); + return tc; + } + + tc = Relation::Null(); + + return ApproxClosure(s); + +} + + + + +/************************************** */ +/* Calculate R* */ +/* */ +/* Tomasz Klimek 05-06-2010 */ +/************************************** */ + +Relation calculateTransitiveClosure(NOT_CONST Relation &r) { + + Relation s = consume_and_regurgitate(r); + Relation tc = Relation::False(s.n_inp(), s.n_out()); + long* relationsSet = NULL; + Relation commonRegion, regionTmp; + Relation inputRelations; + long i,j=-1; + long N,M; + Relation R; + + + commonRegion = SelectRegionForClosure(copy(s)); + + if (commonRegion.is_null()) { + return ApproxClosure(s); + } + + if (commonRegion.is_exact()) { + return commonRegion; + } + + commonRegion = Relation::Null(); + N = M = s.number_of_conjuncts(); + relationsSet = (long*)calloc(N,sizeof(long)); + + if (relationsSet == NULL) { + return Relation::False(s.n_inp(), s.n_out()); + } + + for (; N > 1;) { + for ( i=0; i<N; i++ ) { + if ( i < j ) { + continue; + } + else if ( j == -1 ) { + relationsSet[i] = 1; + } + else if ( i > j ) { + relationsSet[i] = relationsSet[i-1] + 1; + } + else if ( i == j ) { + relationsSet[i] += 1; + } + if ( relationsSet[i] <= M ) { + j = i; + } + else { + j = i - 1; + break; + } + } + + if ( j+1 == N) { + /* fprintf(DebugFile,"\n"); + for(i=0;i<N;i++){ + fprintf(DebugFile," %ld", relationsSet[i]); + } + fprintf(DebugFile,"\n"); */ + + commonRegion = getCommonRegion( copy(s), relationsSet, N); + commonRegion.simplify(2,1); + inputRelations = getRelationsSet( copy(s), relationsSet, N); + inputRelations.simplify(2,1); + + /* ******************* */ + /* Check on rectangle */ + /* ******************* */ + regionTmp = SimpleHull(copy(commonRegion)); + regionTmp.simplify(2,1); + R = relationsOnCommonRegion( copy(inputRelations), regionTmp); + R.simplify(2,1); + regionTmp = SelectRegionForClosure(R); + + if (regionTmp.is_exact()) { + /* fprintf(DebugFile,"\nDescribed on rectangle region\n"); */ + tc = Union( tc, regionTmp ); + } + else { + /* ******************* */ + /* Check on hull */ + /* ******************* */ + + R = Relation::Null(); + regionTmp = Relation::Null(); + regionTmp = Hull(copy(commonRegion),false,1,Relation::Null()); + regionTmp.simplify(2,1); + R = relationsOnCommonRegion( copy(inputRelations), regionTmp); + R.simplify(2,1); + regionTmp = SelectRegionForClosure(R); + + if (regionTmp.is_exact()) { + /* fprintf(DebugFile,"\nDescribed on Hull\n"); */ + tc = Union( tc, regionTmp); + } + else { + /* ********************************** */ + /* Check on sets of domain and range */ + /* ********************************** */ + + R = Relation::Null(); + regionTmp = Relation::Null(); + R = relationsOnCommonRegion( copy(inputRelations), copy(commonRegion) ); + R.simplify(2,1); + regionTmp = SelectRegionForClosure(R); + + if (regionTmp.is_exact()) { + /* fprintf(DebugFile,"\nDescribed on sets of doamin and range\n"); */ + tc = Union( tc, regionTmp ); + } + else { + commonRegion = Relation::Null(); + inputRelations = Relation::Null(); + regionTmp = Relation::Null(); + R = Relation::Null(); + + return ApproxClosure(s); + } + } + } + + commonRegion = Relation::Null(); + inputRelations = Relation::Null(); + + regionTmp = Relation::Null(); + R = Relation::Null(); + } + + if ( j == -1 ) N--; + + } + + R = Relation::Null(); + + for (DNF_Iterator c(s.query_DNF()); c; c++) { + if (!Must_Be_Subset(Relation(s, c.curr()), copy(tc))) { + /* fprintf(DebugFile,"\nIs not a subset\n"); */ + tc = Union( tc, SelectRegionForClosure(Relation(s, c.curr()))); + } + } + + if (!(tc.is_exact())){ + return ApproxClosure(s); + } + + tc = compose_N(tc); + + if (tc.is_null()) { + return ApproxClosure(s); + } + + return tc; + +} + + + + + +} // namespace diff --git a/omegalib/omega/src/evac.cc b/omegalib/omega/src/evac.cc new file mode 100644 index 0000000..ff872c9 --- /dev/null +++ b/omegalib/omega/src/evac.cc @@ -0,0 +1,339 @@ +#if defined STUDY_EVACUATIONS + +#include <omega/Relations.h> +#include <omega/pres_conj.h> +#include <omega/evac.h> +#include <omega/omega_core/debugging.h> +#include <omega/omega_core/oc_i.h> + +namespace omega { + +int evac_debug = 0; + +char *evac_names[] = { "trivial", + "offset", + "subseq", + "off_sub", +// "perm.", + "affine", + "nasty" }; + +int single_evacs[evac_nasty+1]; +int double_evacs[evac_nasty+1][evac_nasty+1]; + +/* + * We're going to try to describe the equalities among a set of variables + * We want to perform some substitutions to ensure that we don't miss + * v_1 = v_2 due to its expression as v_1 = v_3 && v_2 = v_3 + * We therefore try to substitute out all variables that we don't care + * about (e.g., v_3 in the above example). + */ + +static bool try_to_sub(Problem *p, int col) { + int e, i; + + if (!p->variablesInitialized) { + p->initializeVariables(); + } + + assert(col <= p->nVars); + assert(!inApproximateMode); + + for(e=0;e<p->nEQs;e++) + if (p->EQs[e].coef[col] == 1 || p->EQs[e].coef[col] == -1) { + int var = p->var[col]; + p->doElimination(e, col); + if (col != p->nVars + 1) + p->forwardingAddress[p->var[p->nVars+1]] = col; + assert(p->SUBs[p->nSUBs-1].key = var); + p->forwardingAddress[var] = -p->nSUBs; + break; + } + + if (e == p->nEQs) + return false; + + for (int c=0;c<=p->nVars;c++) { + assert(p->EQs[e].coef[c] == 0); + } + + p->nEQs--; + if (e < p->nEQs) eqnncpy(&p->EQs[e], &p->EQs[p->nEQs], p->nVars); + + for (i = 0; i < p->nSUBs; i++) { + assert(p->forwardingAddress[p->SUBs[i].key] == -i - 1); + } + + return true; +} + + +// should be static, but must be a friend +bool check_subseq_n(Conjunct *c, Sequence<Variable_ID> &evac_from, Sequence<Variable_ID> &evac_to, int n_from, int n_to, int max_arity, int n, bool allow_offset) { + // check each position v to see if from[v] == to[v+n] (+ offset) + + assert(max_arity + n <= n_to); + + for (int v = 1; v <= max_arity; v++){ + // first, get rid of possible interlopers: + int col; + Conjunct *d = c->copy_conj_same_relation(); + for (int tv = 1; tv <= n_to; tv++) + if (tv != v+n) + if ((col = d->find_column(evac_to[tv])) > 0) + try_to_sub(d->problem, col); + for (int fv = 1; fv <= n_from; fv++) + if (fv != v) + if ((col = d->find_column(evac_from[fv])) > 0) + try_to_sub(d->problem, col); + + int c_to = d->find_column(evac_to[v+n]); + int c_from = d->find_column(evac_from[v]); + assert(c_to > 0); + assert(c_from > 0); + assert(c_to != c_from); + + // now, just look for an equality c_to = c_from + offset + + bool found_needed_eq = false; + + for (int e = 0; e < d->problem->nEQs; e++) { + if (d->problem->EQs[e].coef[c_from] != 0) { + for (int k = allow_offset?1:0; k < d->problem->nVars; k++) + if (k!=c_to && k!=c_from && d->problem->EQs[e].coef[k]!=0) + break; // this EQ is not what we need + if (k == d->problem->nVars) { // this EQ is what we need + found_needed_eq = true; + break; + } + } + } + + delete d; + + if (!found_needed_eq) + return false; // no EQ did what we need + } + + return true; +} + +void assert_subbed_syms(Conjunct *c) { + int v, col; + + // where possible, symbolic constants must have been subbed out + for (v = 1; v <= c->relation()->global_decls()->length(); v++) + if ((col = c->find_column((*c->relation()->global_decls())[v]))>0) + assert(!try_to_sub(c->problem, col)); +} + + +static bool check_offset(Conjunct *c, Sequence<Variable_ID> &evac_from, Sequence<Variable_ID> &evac_to, int n_from, int n_to, int max_arity) { + assert_subbed_syms(c); + + return check_subseq_n(c,evac_from,evac_to,n_from,n_to,max_arity,0,true); +} + +static bool check_subseq(Conjunct *c, Sequence<Variable_ID> &evac_from, Sequence<Variable_ID> &evac_to, int n_from, int n_to, int max_arity) { + assert_subbed_syms(c); + + for (int i = 0; i <= n_to - max_arity; i++) + if (check_subseq_n(c,evac_from,evac_to,n_from,n_to,max_arity,i,false)) + return true; + + return false; +} + +static bool check_offset_subseq(Conjunct *c, Sequence<Variable_ID> &evac_from, Sequence<Variable_ID> &evac_to, int n_from, int n_to, int max_arity) { + assert_subbed_syms(c); + + for (int i = 0; i <= n_to - max_arity; i++) + if (check_subseq_n(c,evac_from,evac_to,n_from,n_to,max_arity,i,true)) + return true; + + return false; +} + +bool check_affine(Conjunct *d, Sequence<Variable_ID> &evac_from, Sequence<Variable_ID> &evac_to, int n_from, int n_to, int max_arity) { + int v, col; + Conjunct *c = d->copy_conj_same_relation(); + assert_subbed_syms(c); + + // try to find substitutions for all evac_to variables + for (v = 1; v <= max_arity; v++) + if ((col = c->find_column(evac_to[v])) > 0) + try_to_sub(c->problem, col); + + // any that didn't have substitutions, aren't affine + for (v = 1; v <= max_arity; v++) + if (c->find_column(evac_to[v]) >= 0) { + delete c; + return false; + } + + // FERD - disallow symbolic constants? + delete c; + return true; +} + + +evac study(Conjunct *C, Sequence<Variable_ID> &evac_from, Sequence<Variable_ID> &evac_to, int n_from, int n_to, int max_arity) { + assert(max_arity > 0); + assert(max_arity <= C->relation()->n_inp()); + assert(max_arity <= C->relation()->n_out()); + + assert((&evac_from == &input_vars && &evac_to == &output_vars) || + (&evac_from == &output_vars && &evac_to == &input_vars)); + + evac ret = evac_nasty; + + if (C->query_guaranteed_leading_0s() >= max_arity) + ret = evac_trivial; + else { + Conjunct *c = C->copy_conj_same_relation(); + assert(c->relation() == C->relation()); + + if (evac_debug >= 3) { + fprintf(DebugFile, "About to study %s evacuation for conjunct\n", + &evac_from == &input_vars ? "In-->Out" : "Out-->In"); + use_ugly_names++; + C->prefix_print(DebugFile); + use_ugly_names--; + } + + bool sat = simplify_conj(c, true, 4, black); + assert(sat); // else c is deleted + + int v, col; + + // Substitute out all possible symbolic constants + assert(c->problem->nSUBs == 0); + for (v = 1; v <= c->relation()->global_decls()->length(); v++) + if ((col = c->find_column((*c->relation()->global_decls())[v]))>0) + try_to_sub(c->problem, col); + + if (check_offset(c, evac_from, evac_to, n_from, n_to, max_arity)) + ret = evac_offset; + else if (check_subseq(c, evac_from, evac_to, n_from, n_to, max_arity)) + ret = evac_subseq; + else if (check_offset_subseq(c, evac_from, evac_to, n_from, n_to, max_arity)) + ret = evac_offset_subseq; + else if (check_affine(c, evac_from, evac_to, n_from, n_to, max_arity)) + ret = evac_affine; + + delete c; + } + + if (evac_debug >= 2) { + if ((evac_debug == 2 && ret != evac_trivial && ret != evac_nasty)) { + fprintf(DebugFile, "Studied %s evacuation for conjunct\n", + &evac_from == &input_vars ? "In-->Out" : "Out-->In"); + use_ugly_names++; + C->prefix_print(DebugFile); + use_ugly_names--; + } + + fprintf(DebugFile, "Saw evacuation type %s\n", evac_names[ret]); + } + + return ret; +} + + +void study_evacuation(Conjunct *C, which_way dir, int max_arity) { + if (evac_debug > 0) { + assert(max_arity >= 0); + + if (max_arity > 0) + if (dir == in_to_out) { + assert(max_arity <= C->relation()->n_inp()); + if (max_arity <= C->relation()->n_out()) + single_evacs[study(C, input_vars, output_vars, + C->relation()->n_inp(), + C->relation()->n_out(), + max_arity)]++; + } + else { + assert(max_arity <= C->relation()->n_out()); + if (max_arity <= C->relation()->n_inp()) + single_evacs[study(C, output_vars, input_vars, + C->relation()->n_out(), + C->relation()->n_inp(), + max_arity)]++; + } + } +} + +void study_evacuation(Conjunct *C1, Conjunct *C2, int max_arity) { + if (evac_debug > 0) { + assert(max_arity >= 0); + assert(max_arity <= C1->relation()->n_inp()); + assert(C2->relation()->n_out() == C1->relation()->n_inp()); + + if (max_arity > 0) + if (max_arity <= C1->relation()->n_out() && + max_arity <= C2->relation()->n_inp()) { + double_evacs[study(C1, input_vars, output_vars, + C1->relation()->n_inp(), + C1->relation()->n_out(), + max_arity)] + [study(C2, output_vars, input_vars, + C2->relation()->n_out(), + C2->relation()->n_inp(), + max_arity)]++; + } + else if (max_arity <= C1->relation()->n_out()) { + single_evacs[study(C1, input_vars, output_vars, + C1->relation()->n_inp(), + C1->relation()->n_out(), + max_arity)]++; + } + else if (max_arity <= C2->relation()->n_inp()) { + single_evacs[study(C2, output_vars, input_vars, + C2->relation()->n_out(), + C2->relation()->n_inp(), + max_arity)]++; + } + } +} + +class Evac_info_printer { +public: + ~Evac_info_printer(); +}; + +Evac_info_printer::~Evac_info_printer() { + if (evac_debug > 0) { + int i, j; + + fprintf(DebugFile, "\n"); + + fprintf(DebugFile, "SINGLE"); + for (i = 0; i <= evac_nasty; i++) + fprintf(DebugFile, "\t%s", evac_names[i]); + fprintf(DebugFile, "\n"); + + for (i = 0; i <= evac_nasty; i++) + fprintf(DebugFile, "\t%d", single_evacs[i]); + fprintf(DebugFile, "\n\n"); + + + fprintf(DebugFile, "DOUBLE"); + for (i = 0; i <= evac_nasty; i++) + fprintf(DebugFile, "\t%s", evac_names[i]); + fprintf(DebugFile, "\n"); + + for (i = 0; i <= evac_nasty; i++) { + fprintf(DebugFile, "%s\t", evac_names[i]); + for (j = 0; j <= evac_nasty; j++) + fprintf(DebugFile, "%d\t", double_evacs[i][j]); + fprintf(DebugFile, "\n"); + } + } +} + +static Evac_info_printer print_stats_at_exit; + +} // namespace + +#endif diff --git a/omegalib/omega/src/farkas.cc b/omegalib/omega/src/farkas.cc new file mode 100644 index 0000000..1b3ef87 --- /dev/null +++ b/omegalib/omega/src/farkas.cc @@ -0,0 +1,480 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + All Rights Reserved. + + Purpose: + convert to dual cone for manipulation. + + Notes: + + History: +*****************************************************************************/ + +#include <basic/Bag.h> +#include <basic/Map.h> +#include <omega.h> +#include <omega/farkas.h> + +namespace omega { + +static Global_Var_Decl constant_term("constantTerm"); + +// class Constant_Term { +// public: +// Global_Var_Decl *p; + +// Constant_Term(); +// ~Constant_Term(); +// }; + +// namespace { +// Constant_Term constant_term; +// } + +// Constant_Term::Constant_Term() { +// p = new Global_Var_Decl("constantTerm"); +// } + +// Constant_Term::~Constant_Term() { +// delete p; +// } + +// Global_Var_ID coefficient_of_constant_term = constant_term.p; + +Global_Var_ID coefficient_of_constant_term = &constant_term; + +extern int inApproximateMode; + +int farkas_debug = 0; + +coef_t farkasDifficulty; + +//***************************************************************************** +// +// forall x1,..,xn s.t. a10 + a11 x1 + ... + a1n xn >= 0 and +// ... +// am0 + am1 x1 + ... + amn xn >= 0 +// +// b0 + b1 x1 + ... + bn xn >= 0 +// +// iff +// +// exists lambda_0,...,lambda_m >= 0 s.t. +// forall x1,..,xn +// lambda_0 + +// lambda_1 ( a10 + a11 x1 + ... + a1n xn) + +// ... +// lambda_m ( am0 + am1 x1 + ... + amn xn) = +// +// b0 + b1 x1 + ... + bn xn +// +// iff +// +// exists lambda_0,...,lambda_m >= 0 s.t. +// lambda_0 + sum_i ( lambda_i a_i0 ) = b_0 +// for j in 1..n +// sum_i ( a_ij lambda_i ) = b_j +// +// iff +// +// exists lambda0,...,lambda_m s.t. +// lambda1,...,lambda_m >= 0 +// lambda0 >= 0 +// lambda_0 = b_0 - sum_i ( lambda_i a_i0 ) +// for j in 1..n +// sum_i ( a_ij lambda_i ) = b_j +// iff +// +// exists lambda1,...,lambda_m s.t. +// lambda1,...,lambda_m >= 0 +// b_0 - sum_i ( lambda_i a_i0 ) >= 0 +// for j in 1..n +// sum_i ( a_ij lambda_i ) = b_j +// +// a_ij come from relation rel +// +// x_1,...,x_n are input and output variables from rel. +// +// b_0,...,b_m are input and output arrays of coef_vars +// +//***************************************************************************** + + +// Given a Relation/Set R +// Compute A,B,C such that +// Ax+By + C >= 0 is true for all x,y in R +// iff [A,B] : constantTerm=C is in AffineClosure(R) +// Note: constantTerm is a special global variable +// If constantTerm appears in the incoming relation +// then set it's coefficient to be 1 in the result + + +// # For example, given +// R := {[i,j] : 1 <= i <= 10 && 1 <= j <= n}; +// # the farkas closure of R is: +// # ac := approximate {[i,j] : exists (lambda0, lambda1,lambda2,lambda3,lambda4 : +// # 0 <= lambda1,lambda2,lambda3,lambda4 +// # && constantTerm - (-lambda1+ 10 lambda2 - lambda3) >= 0 +// # && i = lambda1-lambda2 +// # && j = lambda3-lambda4 +// # && n = lambda4)}; +// # +// # ac; +// +// {[i,j]: 0 <= n && 0 <= n+constantTerm+i+j +// && 0 <= n+constantTerm+10i+j && 0 <= n+j} +// +// The ConvexCombination of ac is: +//# +//# approximate {[i,j] : exists (lambda1,lambda2,lambda3,lambda4 : +//# 0 <= lambda1,lambda2,lambda3,lambda4 +//# && 1 = lambda2+lambda3 +//# && i = lambda2+10lambda3 +//# && j = lambda2+lambda3+lambda4 +//# && n = lambda1+lambda2+lambda3+lambda4 +//# )}; +// +//{[i,j]: 1 <= i <= 10 && 1 <= j <= n} +// + +static void handleVariable(Relation &farkas, Conjunct * conj, + F_And* and_node, + Map<GEQ_Handle, Variable_ID> &gMap, + Map<EQ_Handle, Variable_ID> &eMap, + Variable_ID v) { + use_ugly_names++; + if (farkas_debug > 1) { + fprintf(DebugFile,"Building equality for %s\n", v->name().c_str()); + } + + EQ_Handle e = and_node->add_EQ(); + + for (GEQ_Iterator g = conj->GEQs(); g.live(); g.next()) + if (gMap(*g) != (Variable_ID) 0) { + coef_t c = (*g).get_coef(v); + if (c != 0) { + e.update_coef(gMap(*g), c); + } + } + + for (EQ_Iterator eq = conj->EQs(); eq.live(); eq.next()) + if (eMap(*eq) != (Variable_ID) 0) { + coef_t c = (*eq).get_coef(v); + if (c != 0) { + e.update_coef(eMap(*eq), c); + } + } + + if ((v)->kind() == Global_Var && + (v)->get_global_var() == coefficient_of_constant_term) + e.update_const(-1); + else + e.update_coef(farkas.get_local(v), -1); + + e.finalize(); + if (farkas_debug > 1) { + fprintf(DebugFile,"Constraint is %s\n", e.print_to_string().c_str()); + } + use_ugly_names--; +} + + +Relation Farkas(NOT_CONST Relation &input_R, Farkas_Type op, bool early_bailout) { + assert(!input_R.is_null()); + int saved_use_ugly_names = use_ugly_names; + + use_ugly_names++; + farkasDifficulty = 0; + + Relation R = consume_and_regurgitate(input_R); + + if (op == Basic_Farkas || op == Decoupled_Farkas) { + R.simplify(2, 4); + R = Approximate(R, false); + } + + Relation result = Relation::False(R); + + if (farkas_debug) { + fprintf(DebugFile,"Computing farka of: [\n"); + R.prefix_print(DebugFile); + } + + Variable_ID_Tuple vars; + for (Variable_ID_Iterator v(*R.global_decls()); v; v++) vars.append(*v); + if (R.is_set()) + for(int i=1; i <= R.n_set(); i++) vars.append(R.set_var(i)); + else { + int i; + for(i=1; i <= R.n_inp(); i++) vars.append(R.input_var(i)); + for(i=1; i <= R.n_out(); i++) vars.append(R.output_var(i)); + } + + Set<Variable_ID> empty; + Variable_ID_Tuple owners; + Map<Variable_ID, Set<Variable_ID> > connectedVariables(empty); + + if (op == Decoupled_Farkas) { + for (Variable_ID_Iterator v(*R.global_decls()); v; v++) + if ((*v)->kind() == Global_Var) { + Global_Var_ID g = (*v)->get_global_var(); + if (g->arity() > 0) { + if (R.is_set()) + for(int i=1; i <= g->arity(); i++) + (*v)->UF_union(R.set_var(i)); + else if ((*v)->function_of() == Input_Tuple) + for(int i=1; i <= g->arity(); i++) + (*v)->UF_union(R.input_var(i)); + else + for(int i=1; i <= g->arity(); i++) + (*v)->UF_union(R.output_var(i)); + } + } + + for (DNF_Iterator s(R.query_DNF()); s.live(); s.next()) { + for (Variable_ID_Iterator v1(*(*s)->variables()); v1; v1++) { + for (EQ_Iterator eq = (*s)->EQs(); eq.live(); eq.next()) + if ((*eq).get_coef(*v1)) + for (Variable_ID_Iterator v2(*(*s)->variables()); v2; v2++) + if ((*eq).get_coef(*v2)) + (*v1)->UF_union(*v2); + for (GEQ_Iterator g = (*s)->GEQs(); g.live(); g.next()) + if ((*g).get_coef(*v1)) + for (Variable_ID_Iterator v2(*(*s)->variables()); v2; v2++) + if ((*g).get_coef(*v2)) + (*v1)->UF_union(*v2); + } + } + for (Variable_ID_Iterator v3(vars); v3.live(); v3.next()) + connectedVariables[(*v3)->UF_owner()] |= *v3; + + foreach_map(v,Variable_ID,s,Set<Variable_ID>,connectedVariables, + owners.append(v); + if (farkas_debug) { + fprintf(DebugFile,"%s:",v->char_name()); + foreach(v2,Variable_ID,s, + fprintf(DebugFile," %s",v2->char_name()); + ); + fprintf(DebugFile,"\n"); + } + ); + } + + Variable_ID_Iterator varGroup(owners); + int lambda_cnt = 1; + + Relation partialResult; + bool firstGroup = true; + try { + while ((op == Decoupled_Farkas && varGroup.live()) + || (op != Decoupled_Farkas && firstGroup)) { + + if (farkas_debug && op == Decoupled_Farkas) { + fprintf(DebugFile,"[Computing decoupled farkas for:"); + foreach(v2,Variable_ID,connectedVariables(varGroup.curr()), + fprintf(DebugFile," %s",v2->char_name()); + ); + fprintf(DebugFile,"\n"); + } + firstGroup = false; + partialResult = Relation::True(R); + coef_t difficulty = 0; + for (DNF_Iterator s(R.query_DNF()); s.live(); s.next()) { + int nz; + coef_t m,sum; + (*s)->difficulty(nz,m,sum); + difficulty = max((coef_t) nz,2*nz+2*m+sum); + if (farkas_debug) { + fprintf(DebugFile,"Computing farka of conjunct: \n"); + (*s)->prefix_print(DebugFile); + fprintf(DebugFile,"Difficulty is " coef_fmt "(%d," coef_fmt "," coef_fmt ")\n", difficulty,nz,m,sum); + } + if (early_bailout && difficulty >= 500) { + farkasDifficulty = difficulty; + if (farkas_debug) { + fprintf(DebugFile,"Too ugly, returning dull result\n"); + } + use_ugly_names--; + if (op == Basic_Farkas || op == Decoupled_Farkas) + return Relation::False(partialResult); + else return Relation::True(partialResult); + } + Relation farkas = Relation::Empty(R); + farkas.copy_names(R); + F_Exists* exist = farkas.add_exists(); + F_And* and_node = exist->add_and(); + Map<GEQ_Handle, Variable_ID> gMap((Variable_ID)0); + Map<EQ_Handle, Variable_ID> eMap((Variable_ID)0); + for (EQ_Iterator eq = (*s)->EQs(); eq.live(); eq.next()) { + if (op == Decoupled_Farkas) { + bool ShouldConsider = true; + for (Variable_ID_Iterator v(*(*s)->variables()); v; v++) { + if ((*eq).get_coef(*v) != 0 + && (*v)->UF_owner() != varGroup.curr()) { + ShouldConsider = false; + break; + } + } + if (!ShouldConsider) continue; + } + char s[10]; + sprintf(s, "lambda%d", lambda_cnt++); + eMap[*eq] = exist->declare(s); + assert(op == Basic_Farkas || op == Decoupled_Farkas + || (*eq).get_const() == 0); + } + for (GEQ_Iterator g = (*s)->GEQs(); g.live(); g.next()) { + if (op == Decoupled_Farkas) { + bool ShouldConsider = true; + for (Variable_ID_Iterator v(*(*s)->variables()); v; v++) { + if ((*g).get_coef(*v) != 0 + && (*v)->UF_owner() != varGroup.curr()) { + ShouldConsider = false; + break; + } + } + if (!ShouldConsider) continue; + } + char s[10]; + sprintf(s, "lambda%d", lambda_cnt++); + Variable_ID lambda = exist->declare(s); + GEQ_Handle positive; + switch(op) { + case Positive_Combination_Farkas: + case Convex_Combination_Farkas: + case Basic_Farkas: + case Decoupled_Farkas: + positive = and_node->add_GEQ(); + positive.update_coef(lambda, 1); + positive.finalize(); + break; + case Linear_Combination_Farkas: + case Affine_Combination_Farkas: + break; + } + gMap[*g] = lambda; + assert(op == Basic_Farkas || op == Decoupled_Farkas || (*g).get_const() == 0); + } + + for (Variable_ID_Iterator v(vars); v; v++) { + assert((*v)->kind() != Wildcard_Var); + if ((*v)->kind() == Global_Var + && (*v)->get_global_var() == coefficient_of_constant_term) { + assert(op != Basic_Farkas && op != Decoupled_Farkas); + if (op == Linear_Combination_Farkas) continue; + if (op == Positive_Combination_Farkas) continue; + } + if (op == Decoupled_Farkas && (*v)->UF_owner() != varGroup.curr()) { + EQ_Handle e = and_node->add_EQ(); + e.update_coef(farkas.get_local(*v),-1); + continue; + } + handleVariable(farkas, *s, and_node, gMap,eMap, *v); + } + + if (op == Basic_Farkas || op == Decoupled_Farkas) { + GEQ_Handle e = and_node->add_GEQ(); + e.update_coef(farkas.get_local(coefficient_of_constant_term),1); + for (GEQ_Iterator g = s.curr()->GEQs(); g.live(); g.next()) + if (gMap(*g) != (Variable_ID) 0) + e.update_coef( gMap(*g),-(*g).get_const()); + for (EQ_Iterator eq = s.curr()->EQs(); eq.live(); eq.next()) + if (eMap(*eq) != (Variable_ID) 0) + e.update_coef(eMap(*eq),-(*eq).get_const()); + e.finalize(); + } + + // lambda variables are not integers, so disable integer problem solving, + // we just mark it as simplified. + farkas.simplify(-1, -1); + + farkas.single_conjunct()->difficulty(nz,m,sum); + difficulty = max((coef_t) nz,2*nz+2*m+sum); + if (farkas_debug) { + fprintf(DebugFile,"farka has difficulty " coef_fmt "(%d," coef_fmt "," coef_fmt "):\n", difficulty,nz,m,sum); + farkas.prefix_print(DebugFile); + } + if (early_bailout && difficulty >= 500) { + farkasDifficulty = difficulty; + if (farkas_debug) { + fprintf(DebugFile,"Too ugly, returning dull result\n"); + } + use_ugly_names--; + if (op == Basic_Farkas || op == Decoupled_Farkas) + return Relation::False(partialResult); + else return Relation::True(partialResult); + } + farkas = Approximate(farkas); + if (farkas_debug) { + fprintf(DebugFile,"simplified:\n"); + farkas.prefix_print(DebugFile); + } + partialResult = Approximate(Intersection(partialResult,farkas)); + if (farkas_debug) { + fprintf(DebugFile,"combined:\n"); + partialResult.prefix_print(DebugFile); + } + if (partialResult.has_single_conjunct()) { + partialResult.single_conjunct()->difficulty(nz,m,sum); + difficulty = max((coef_t) nz,2*nz+2*m+sum); + } + else + difficulty = 1000; + if (early_bailout && difficulty >= 500) { + farkasDifficulty = difficulty; + if (farkas_debug) { + fprintf(DebugFile,"Too ugly, returning dull result\n"); + } + use_ugly_names--; + if (op == Basic_Farkas || op == Decoupled_Farkas) + return Relation::False(partialResult); + else return Relation::True(partialResult); + } + } + farkasDifficulty += difficulty; + + if (farkas_debug) { + fprintf(DebugFile,"] done computing farkas\n"); + partialResult.prefix_print(DebugFile); + } + + if (op == Decoupled_Farkas) { + result = Union(result,partialResult); + varGroup.next(); + } + } + } + catch (const std::overflow_error &e) { + // clear global variables + inApproximateMode = 0; + use_ugly_names = saved_use_ugly_names; + + if (early_bailout) { + if (farkasDifficulty < 1000) + farkasDifficulty = 1000; + // return dull result + if (op == Basic_Farkas || op == Decoupled_Farkas) + return Relation::False(partialResult); + else + return Relation::True(partialResult); + } + else + throw std::overflow_error("farkas too ugly"); + } + + if (1 || op == Decoupled_Farkas) { + foreach(v,Variable_ID,vars, reset_remap_field(v)); + } + use_ugly_names--; + if (op == Decoupled_Farkas) { + if (farkas_debug) { + fprintf(DebugFile,"] decoupled result:\n"); + result.prefix_print(DebugFile); + } + return result; + } + return partialResult; +} + +} // namespace diff --git a/omegalib/omega/src/hull.cc b/omegalib/omega/src/hull.cc new file mode 100644 index 0000000..f1b0601 --- /dev/null +++ b/omegalib/omega/src/hull.cc @@ -0,0 +1,1489 @@ +/***************************************************************************** + Copyright (C) 1994-2000 University of Maryland + Copyright (C) 2008 University of Southern California + Copyright (C) 2009-2010 University of Utah + All Rights Reserved. + + Purpose: + Various hull calculations. + + Notes: + + History: + 06/15/09 ConvexRepresentation, Chun Chen + 11/25/09 RectHull, Chun Chen +*****************************************************************************/ + +#include <omega.h> +#include <omega/farkas.h> +#include <omega/hull.h> +#include <basic/Bag.h> +#include <basic/Map.h> +#include <basic/omega_error.h> +#include <list> +#include <vector> +#include <set> + +namespace omega { + +int hull_debug = 0; + +Relation ConvexHull(NOT_CONST Relation &R) { + Relation S = Approximate(consume_and_regurgitate(R)); + if (!S.is_upper_bound_satisfiable()) + return S; + if (S.has_single_conjunct()) + return S; + return Farkas(Farkas(S,Basic_Farkas), Convex_Combination_Farkas); +} + +Relation DecoupledConvexHull(NOT_CONST Relation &R) { + Relation S = Approximate(consume_and_regurgitate(R)); + if (!S.is_upper_bound_satisfiable()) + return S; + if (S.has_single_conjunct()) + return S; + return Farkas(Farkas(S,Decoupled_Farkas), Convex_Combination_Farkas); +} + +Relation AffineHull(NOT_CONST Relation &R) { + Relation S = Approximate(consume_and_regurgitate(R)); + if (!S.is_upper_bound_satisfiable()) + return S; + return Farkas(Farkas(S,Basic_Farkas), Affine_Combination_Farkas); +} + +Relation LinearHull(NOT_CONST Relation &R) { + Relation S = Approximate(consume_and_regurgitate(R)); + if (!S.is_upper_bound_satisfiable()) + return S; + return Farkas(Farkas(S,Basic_Farkas), Linear_Combination_Farkas); +} + +Relation ConicHull(NOT_CONST Relation &R) { + Relation S = Approximate(consume_and_regurgitate(R)); + if (!S.is_upper_bound_satisfiable()) + return S; + return Farkas(Farkas(S,Basic_Farkas), Positive_Combination_Farkas); +} + + +Relation FastTightHull(NOT_CONST Relation &input_R, NOT_CONST Relation &input_H) { + Relation R = Approximate(consume_and_regurgitate(input_R)); + Relation H = Approximate(consume_and_regurgitate(input_H)); + + if (hull_debug) { + fprintf(DebugFile,"[ Computing FastTightHull of:\n"); + R.prefix_print(DebugFile); + fprintf(DebugFile,"given known hull of:\n"); + H.prefix_print(DebugFile); + } + + if (!H.has_single_conjunct()) { + if (hull_debug) + fprintf(DebugFile, "] bailing out of FastTightHull, known hull not convex\n"); + return H; + } + + if (!H.is_obvious_tautology()) { + R = Gist(R,copy(H)); + R.simplify(1,0); + } + + if (R.has_single_conjunct()) { + R = Intersection(R,H); + if (hull_debug) { + fprintf(DebugFile, "] quick easy answer to FastTightHull\n"); + R.prefix_print(DebugFile); + } + return R; + } + if (R.has_local(coefficient_of_constant_term)) { + if (hull_debug) { + fprintf(DebugFile, "] Can't handle recursive application of Farkas lemma\n"); + } + return H; + } + + if (hull_debug) { + fprintf(DebugFile,"Gist of R given H is:\n"); + R.prefix_print(DebugFile); + } + + if (1) { + Set<Variable_ID> vars; + int conjuncts = 0; + for (DNF_Iterator s(R.query_DNF()); s.live(); s.next()) { + conjuncts++; + for (Variable_ID_Iterator v(*((*s)->variables())); v.live(); v++) { + bool found = false; + for (EQ_Iterator eq = (*s)->EQs(); eq.live(); eq.next()) + if ((*eq).get_coef(*v) != 0) { + if (!found) vars.insert(*v); + found = true; + break; + } + if (!found) + for (GEQ_Iterator geq = (*s)->GEQs(); geq.live(); geq.next()) + if ((*geq).get_coef(*v) != 0) { + if (!found) vars.insert(*v); + found = true; + break; + } + } + } + + + // We now know which variables appear in R + if (hull_debug) { + fprintf(DebugFile,"Variables we need a better hull on are: "); + foreach(v,Variable_ID,vars, + fprintf(DebugFile," %s",v->char_name())); + fprintf(DebugFile,"\n"); + } + Conjunct *c = H.single_conjunct(); + int total=0; + int copied = 0; + for (EQ_Iterator eq = c->EQs(); eq.live(); eq.next()) { + total++; + foreach(v,Variable_ID,vars, + if ((*eq).get_coef(v) != 0) { + R.and_with_EQ(*eq); + copied++; + break; // out of variable loop + } + ); + } + for (GEQ_Iterator geq = c->GEQs(); geq.live(); geq.next()) { + total++; + foreach(v,Variable_ID,vars, + if ((*geq).get_coef(v) != 0) { + R.and_with_GEQ(*geq); + copied++; + break; // out of variable loop + } + ); + } + if (copied < total) { + R = Approximate(R); + + if (hull_debug) { + fprintf(DebugFile,"Decomposed relation, copied only %d of %d constraints\n",copied,total); + fprintf(DebugFile,"Original R:\n"); + R.prefix_print(DebugFile); + fprintf(DebugFile,"Known hull:\n"); + H.prefix_print(DebugFile); + fprintf(DebugFile,"New R:\n"); + R.prefix_print(DebugFile); + } + } + } + + Relation F = Farkas(copy(R), Basic_Farkas, true); + if (hull_debug) + fprintf(DebugFile,"Farkas Difficulty = " coef_fmt "\n", farkasDifficulty); + if (farkasDifficulty > 260) { + if (hull_debug) { + fprintf(DebugFile, "] bailing out, farkas is way too complex\n"); + fprintf(DebugFile,"Farkas:\n"); + F.prefix_print(DebugFile); + } + return H; + } + else if (farkasDifficulty > 130) { + // Bail out + if (hull_debug) { + fprintf(DebugFile, coef_fmt " non-zeros in original farkas\n", farkasDifficulty); + } + Relation tmp = Farkas(R, Decoupled_Farkas, true); + + if (hull_debug) { + fprintf(DebugFile, coef_fmt " non-zeros in decoupled farkas\n", farkasDifficulty); + } + if (farkasDifficulty > 260) { + if (hull_debug) { + fprintf(DebugFile, "] bailing out, farkas is way too complex\n"); + fprintf(DebugFile,"Farkas:\n"); + F.prefix_print(DebugFile); + } + return H; + } + else { + if (farkasDifficulty > 130) + R = Intersection(H, Farkas(tmp, Affine_Combination_Farkas, true)); + else R = Intersection(H, + Intersection(Farkas(tmp, Convex_Combination_Farkas, true), + Farkas(F, Affine_Combination_Farkas, true))); + if (hull_debug) { + fprintf(DebugFile, "] bailing out, farkas is too complex, using affine hull\n"); + fprintf(DebugFile,"Farkas:\n"); + F.prefix_print(DebugFile); + fprintf(DebugFile,"Affine hull:\n"); + R.prefix_print(DebugFile); + } + return R; + } + } + + R = Intersection(H, Farkas(F, Convex_Combination_Farkas, true)); + if (hull_debug) { + fprintf(DebugFile, "] Result of FastTightHull:\n"); + R.prefix_print(DebugFile); + } + return R; +} + + + +namespace { + bool parallel(const GEQ_Handle &g1, const GEQ_Handle &g2) { + for(Constr_Vars_Iter cvi(g1, false); cvi; cvi++) { + coef_t c1 = (*cvi).coef; + coef_t c2 = g2.get_coef((*cvi).var); + if (c1 != c2) return false; + } + { + for(Constr_Vars_Iter cvi(g2, false); cvi; cvi++) { + coef_t c1 = g1.get_coef((*cvi).var); + coef_t c2 = (*cvi).coef; + if (c1 != c2) return false; + } + } + return true; + } + + + bool hull(const EQ_Handle &e, const GEQ_Handle &g, coef_t &hull) { + int sign = 0; + for(Constr_Vars_Iter cvi(e, false); cvi; cvi++) { + coef_t c1 = (*cvi).coef; + coef_t c2 = g.get_coef((*cvi).var); + if (sign == 0) sign = (c1*c2>=0?1:-1); + if (sign*c1 != c2) return false; + } + assert(sign != 0); + { + for(Constr_Vars_Iter cvi(g, false); cvi; cvi++) { + coef_t c1 = e.get_coef((*cvi).var); + coef_t c2 = (*cvi).coef; + if (sign*c1 != c2) return false; + } + } + hull = max(sign * e.get_const(), g.get_const()); + if (hull_debug) { + fprintf(DebugFile,"Hull of:\n %s\n", e.print_to_string().c_str()); + fprintf(DebugFile," %s\n", g.print_to_string().c_str()); + fprintf(DebugFile,"is " coef_fmt "\n\n",hull); + } + return true; + } + + bool eq(const EQ_Handle &e1, const EQ_Handle &e2) { + int sign = 0; + for(Constr_Vars_Iter cvi(e1, false); cvi; cvi++) { + coef_t c1 = (*cvi).coef; + coef_t c2 = e2.get_coef((*cvi).var); + if (sign == 0) sign = (c1*c2>=0?1:-1); + if (sign*c1 != c2) return false; + } + assert(sign != 0); + { + for(Constr_Vars_Iter cvi(e2, false); cvi; cvi++) { + coef_t c1 = e1.get_coef((*cvi).var); + coef_t c2 = (*cvi).coef; + if (sign*c1 != c2) return false; + } + } + return sign * e1.get_const() == e2.get_const(); + } +} + + +// This function is deprecated!!! +Relation QuickHull(Relation &R) { + Tuple<Relation> Rs(1); + Rs[1] = R; + return QuickHull(Rs); +} + + +// This function is deprecated!!! +Relation QuickHull(Tuple<Relation> &Rs) { + assert(!Rs.empty()); + + // if (Rs.size() == 1) return Rs[1]; + + Tuple<Relation> l_Rs; + for (int i = 1; i <= Rs.size(); i++) + for (DNF_Iterator c(Rs[i].query_DNF()); c; c++) { + Relation r = Relation(Rs[i], c.curr()); + l_Rs.append(Approximate(r)); + } + + if (l_Rs.size() == 1) + return l_Rs[1]; + + Relation result = Relation::True(Rs[1]); + result.copy_names(Rs[1]); + + use_ugly_names++; + + if (hull_debug > 1) + for (int i = 1; i <= l_Rs.size(); i++) { + fprintf(DebugFile,"#%d \n",i); + l_Rs[i].prefix_print(DebugFile); + } + + +// Relation R = copy(Rs[1]); +// for (int i = 2; i <= Rs.size(); i++) +// R = Union(R,copy(Rs[i])); + +// #if 0 +// if (!R.is_set()) { +// if (R.n_inp() == R.n_out()) { +// Relation AC = DeltasToRelation(Hull(Deltas(copy(R), +// min(R.n_inp(),R.n_out()))), +// R.n_inp(),R.n_out()); +// Relation dH = Hull(Domain(copy(R)),false); +// Relation rH = Hull(Range(copy(R)),false); +// result = Intersection(AC,Cross_Product(dH,rH)); +// } +// else { +// Relation dH = Hull(Domain(copy(R)),false); +// Relation rH = Hull(Range(copy(R)),false); +// result = Cross_Product(dH,rH); +// assert(Must_Be_Subset(copy(R),copy(result))); +// } +// } + +// #endif + + Conjunct *first; + l_Rs[1] = EQs_to_GEQs(l_Rs[1]); + first = l_Rs[1].single_conjunct(); + for (GEQ_Iterator candidate(first->GEQs()); candidate.live(); candidate.next()) { + coef_t maxConstantTerm = (*candidate).get_const(); + bool found = true; + if (hull_debug > 1) { + fprintf(DebugFile,"searching for bound on:\n %s\n", (*candidate).print_to_string().c_str()); + } + for (int i = 2; i <= l_Rs.size(); i++) { + Conjunct *other = l_Rs[i].single_conjunct(); + bool found_for_i = false; + for (GEQ_Iterator target(other->GEQs()); target.live(); target.next()) { + if (hull_debug > 2) { + fprintf(DebugFile,"candidate:\n %s\n", (*candidate).print_to_string().c_str()); + fprintf(DebugFile,"target:\n %s\n", (*target).print_to_string().c_str()); + } + if (parallel(*candidate,*target)) { + if (hull_debug > 1) + fprintf(DebugFile,"Found bound:\n %s\n", (*target).print_to_string().c_str()); + maxConstantTerm = max(maxConstantTerm,(*target).get_const()); + found_for_i = true; + break; + } + } + if (!found_for_i) { + for (EQ_Iterator target_e(other->EQs()); target_e.live(); target_e.next()) { + coef_t h; + if (hull(*target_e,*candidate,h)) { + if (hull_debug > 1) + fprintf(DebugFile,"Found bound of " coef_fmt ":\n %s\n", h, (*target_e).print_to_string().c_str()); + maxConstantTerm = max(maxConstantTerm,h); + found_for_i = true; + break; + } + }; + if (!found_for_i) { + if (hull_debug > 1) { + fprintf(DebugFile,"No bound found in:\n"); + fprintf(DebugFile, "%s", l_Rs[i].print_with_subs_to_string().c_str()); + } + //if nothing found + found = false; + break; + } + } + } + + if (found) { + GEQ_Handle h = result.and_with_GEQ(); + copy_constraint(h,*candidate); + if (hull_debug > 1) + fprintf(DebugFile,"Setting constant term to " coef_fmt " in\n %s\n", maxConstantTerm, h.print_to_string().c_str()); + h.update_const(maxConstantTerm - (*candidate).get_const()); + if (hull_debug > 1) + fprintf(DebugFile,"Updated constraint is\n %s\n", h.print_to_string().c_str()); + } + } + + + for (EQ_Iterator candidate_eq(first->EQs()); candidate_eq.live(); candidate_eq.next()) { + bool found = true; + for (int i = 2; i <= l_Rs.size(); i++) { + Conjunct *C = l_Rs[i].single_conjunct(); + bool found_for_i = false; + + for (EQ_Iterator target(C->EQs()); target.live(); target.next()) { + if (eq(*candidate_eq,*target)) { + found_for_i = true; + break; + } + } + if (!found_for_i) { + //if nothing found + found = false; + break; + } + } + + if (found) { + EQ_Handle h = result.and_with_EQ(); + copy_constraint(h,*candidate_eq); + if (hull_debug > 1) + fprintf(DebugFile,"Adding eq constraint: %s\n", h.print_to_string().c_str()); + } + } + + use_ugly_names--; + if (hull_debug > 1) { + fprintf(DebugFile,"quick hull is of:"); + result.print_with_subs(DebugFile); + } + return result; +} + + +// Relation Hull2(Tuple<Relation> &Rs, Tuple<int> &active) { +// assert(Rs.size() == active.size() && Rs.size() > 0); + +// Tuple<Relation> l_Rs; +// for (int i = 1; i <= Rs.size(); i++) +// if (active[i]) +// l_Rs.append(copy(Rs[i])); + +// if (l_Rs.size() == 0) +// return Relation::False(Rs[1]); + +// try { +// Relation r = l_Rs[1]; +// for (int i = 2; i <= l_Rs.size(); i++) { +// r = Union(r, copy(l_Rs[i])); +// r.simplify(); +// } + +// // Relation F = Farkas(r, Basic_Farkas, true); +// // if (farkasDifficulty >= 500) +// // throw std::overflow_error("loop convex hull too complicated."); +// // F = Farkas(F, Convex_Combination_Farkas, true); +// return Farkas(Farkas(r, Basic_Farkas, true), Convex_Combination_Farkas, true); +// } +// catch (std::overflow_error) { +// return QuickHull(l_Rs); +// } +// } + + +namespace { + void printRs(Tuple<Relation> &Rs) { + fprintf(DebugFile,"Rs:\n"); + for (int i = 1; i <= Rs.size(); i++) + fprintf(DebugFile,"#%d : %s\n",i, + Rs[i].print_with_subs_to_string().c_str()); + } +} + +Relation BetterHull(Tuple<Relation> &Rs, bool stridesAllowed, bool checkSubsets, + NOT_CONST Relation &input_knownHull = Relation::Null()) { + Relation knownHull = consume_and_regurgitate(input_knownHull); + static int OMEGA_WHINGE = -1; + if (OMEGA_WHINGE < 0) { + OMEGA_WHINGE = getenv("OMEGA_WHINGE") ? atoi(getenv("OMEGA_WHINGE")) : 0; + } + assert(!Rs.empty()); + if (Rs.size() == 1) { + if (stridesAllowed) return Rs[1]; + else return Approximate(Rs[1]); + } + + if (checkSubsets) { + Tuple<bool> live(Rs.size()); + if (hull_debug) { + fprintf(DebugFile,"Checking subsets in hull computation:\n"); + printRs(Rs); + } + int i; + for(i=1;i <=Rs.size(); i++) live[i] = true; + for(i=1;i <=Rs.size(); i++) + for(int j=1;j <=Rs.size(); j++) if (i != j && live[j]) { + if (hull_debug) fprintf(DebugFile,"checking %d Is_Obvious_Subset %d\n",i,j); + if (Is_Obvious_Subset(copy(Rs[i]),copy(Rs[j]))) { + if (hull_debug) fprintf(DebugFile,"yes...\n"); + live[i] = false; + break; + } + } + for(i=1;i <=Rs.size(); i++) if (!live[i]) { + if (i < Rs.size()) { + Rs[i] = Rs[Rs.size()]; + live[i] = live[Rs.size()]; + } + Rs[Rs.size()] = Relation(); + Rs.delete_last(); + i--; + } + } + Relation hull; + if (hull_debug) { + fprintf(DebugFile,"Better Hull:\n"); + printRs(Rs); + fprintf(DebugFile,"known hull: %s\n", knownHull.print_with_subs_to_string().c_str()); + } + if (knownHull.is_null()) hull = QuickHull(Rs); + else hull = Intersection(QuickHull(Rs),knownHull); + // for (int i = 1; i <= Rs.size(); i++) + // hull = RectHull(Union(hull, copy(Rs[i]))); + // hull = Intersection(hull, knownHull); + hull.simplify(); + if (hull_debug) { + fprintf(DebugFile,"quick hull: %s\n", hull.print_with_subs_to_string().c_str()); + } + + Relation orig = Relation::False(Rs[1]); + int i; + for (i = 1; i <= Rs.size(); i++) + orig = Union(orig,copy(Rs[i])); + + orig.simplify(); + + for (i = 1; i <= Rs.size(); i++) { + if (!hull.is_obvious_tautology()) Rs[i] = Gist(Rs[i],copy(hull)); + Rs[i].simplify(); + if (Rs[i].is_obvious_tautology()) return hull; + if (Rs[i].has_single_conjunct()) { + Rs[i] = EQs_to_GEQs(Rs[i]); + if (hull_debug) { + fprintf(DebugFile,"Checking for hull constraints in:\n %s\n", Rs[i].print_with_subs_to_string().c_str()); + } + Conjunct *c = Rs[i].single_conjunct(); + for (GEQ_Iterator g(c->GEQs()); g.live(); g.next()) { + Relation tmp = Relation::True(Rs[i]); + tmp.and_with_GEQ(*g); + if (!Difference(copy(orig),tmp).is_upper_bound_satisfiable()) + hull.and_with_GEQ(*g); + } + for (EQ_Iterator e(c->EQs()); e.live(); e.next()) { + Relation tmp = Relation::True(Rs[i]); + tmp.and_with_EQ(*e); + if (!Difference(copy(orig),tmp).is_upper_bound_satisfiable()) + hull.and_with_EQ(*e); + } + } + } + + hull = FastTightHull(orig,hull); + assert(hull.has_single_conjunct()); + + if (stridesAllowed) return hull; + else return Approximate(hull); + +} + + + +Relation Hull(NOT_CONST Relation &S, + bool stridesAllowed, + int effort, + NOT_CONST Relation &knownHull) { + Relation R = consume_and_regurgitate(S); + R.simplify(1,0); + if (!R.is_upper_bound_satisfiable()) return R; + Tuple<Relation> Rs; + for (DNF_Iterator c(R.query_DNF()); c.live(); ) { + Rs.append(Relation(R,c.curr())); + c.next(); + } + if (effort == 1) + return BetterHull(Rs,stridesAllowed,false,knownHull); + else + return QuickHull(Rs); +} + + + +Relation Hull(Tuple<Relation> &Rs, + Tuple<int> &validMask, + int effort, + bool stridesAllowed, + NOT_CONST Relation &knownHull) { + // Use relation of index i only when validMask[i] != 0 + Tuple<Relation> Rs2; + for(int i = 1; i <= Rs.size(); i++) { + if (validMask[i]) { + Rs[i].simplify(); + for (DNF_Iterator c(Rs[i].query_DNF()); c.live(); ) { + Rs2.append(Relation(Rs[i],c.curr())); + c.next(); + } + } + } + assert(effort == 0 || effort == 1); + if (effort == 1) + return BetterHull(Rs2,stridesAllowed,true,knownHull); + else + return QuickHull(Rs2); +} + + +// This function is deprecated!!! +Relation CheckForConvexRepresentation(NOT_CONST Relation &R_In) { + Relation R = consume_and_regurgitate(R_In); + Relation h = Hull(copy(R)); + if (!Difference(copy(h),copy(R)).is_upper_bound_satisfiable()) + return h; + else + return R; +} + +// This function is deprecated!!! +Relation CheckForConvexPairs(NOT_CONST Relation &S) { + Relation R = consume_and_regurgitate(S); + Relation hull = FastTightHull(copy(R),Relation::True(R)); + R.simplify(1,0); + if (!R.is_upper_bound_satisfiable() || R.number_of_conjuncts() < 2) return R; + Tuple<Relation> Rs; + for (DNF_Iterator c(R.query_DNF()); c.live(); ) { + Rs.append(Relation(R,c.curr())); + c.next(); + } + + bool *dead = new bool[Rs.size()+1]; + int i; + for(i = 1; i<=Rs.size();i++) dead[i] = false; + + for(i = 1; i<=Rs.size();i++) + if (!dead[i]) + for(int j = i+1; j<=Rs.size();j++) if (!dead[j]) { + if (hull_debug) { + fprintf(DebugFile,"Comparing #%d and %d\n",i,j); + } + Relation U = Union(copy(Rs[i]),copy(Rs[j])); + Relation H_ij = FastTightHull(copy(U),copy(hull)); + if (!Difference(copy(H_ij),U).is_upper_bound_satisfiable()) { + Rs[i] = H_ij; + dead[j] = true; + if (hull_debug) { + fprintf(DebugFile,"Combined them\n"); + } + } + } + i = 1; + while(i<=Rs.size() && dead[i]) i++; + assert(i<=Rs.size()); + R = Rs[i]; + i++; + for(; i<=Rs.size();i++) + if (!dead[i]) + R = Union(R,Rs[i]); + delete []dead; + return R; +} + +// +// Supporting functions for ConvexRepresentation +// +namespace { +struct Interval { + std::list<std::pair<Relation, Relation> >::iterator pos; + coef_t lb; + coef_t ub; + bool change; + coef_t modulo; + Interval(std::list<std::pair<Relation, Relation> >::iterator pos_, coef_t lb_, coef_t ub_): + pos(pos_), lb(lb_), ub(ub_) {} + friend bool operator<(const Interval &a, const Interval &b); +}; + +bool operator<(const Interval &a, const Interval &b) { + return a.lb < b.lb; +} + +struct Modulo_Interval { + coef_t modulo; + coef_t start; + coef_t size; + Modulo_Interval(coef_t modulo_, coef_t start_, coef_t size_): + modulo(modulo_), start(start_), size(size_) {} + friend bool operator<(const Interval &a, const Interval &b); +}; + +bool operator<(const Modulo_Interval &a, const Modulo_Interval &b) { + if (a.modulo == b.modulo) { + if (a.start == b.start) + return a.size < b.size; + else + return a.start < b.start; + } + else + return a.modulo < b.modulo; +} + +void merge_intervals(std::list<Interval> &intervals, coef_t modulo, std::list<std::pair<Relation, Relation> > &Rs, std::list<std::pair<Relation, Relation> >::iterator orig) { + // normalize intervals + for (std::list<Interval>::iterator i = intervals.begin(); i != intervals.end(); i++) { + (*i).modulo = modulo; + (*i).change = false; + if ((*i).ub - (*i).lb + 1>= modulo) { + (*i).lb = 0; + (*i).ub = modulo - 1; + } + else if ((*i).ub < 0 || (*i).lb >= modulo) { + coef_t range = (*i).ub - (*i).lb; + (*i).lb = int_mod((*i).lb, modulo); + (*i).ub = (*i).lb + range; + } + } + + intervals.sort(); + + // merge neighboring intervals + std::list<Interval>::iterator p = intervals.begin(); + while (p != intervals.end()) { + std::list<Interval>::iterator q = p; + q++; + while (q != intervals.end()) { + if ((*p).ub + 1 >= (*q).lb) { + Relation hull = ConvexHull(Union(copy((*(*p).pos).first), copy((*(*q).pos).first))); + Relation remainder = Difference(Difference(copy(hull), copy((*(*p).pos).first)), copy((*(*q).pos).first)); + if (!remainder.is_upper_bound_satisfiable()) { + if ((*q).pos == orig) + std::swap((*p).pos, (*q).pos); + (*(*p).pos).first = hull; + (*p).ub = max((*p).ub, (*q).ub); + (*p).change = true; + Rs.erase((*q).pos); + q = intervals.erase(q); + } + else + break; + } + else + break; + } + + bool p_moved = false; + q = p; + q++; + while (q != intervals.end()) { + if ((*q).ub >= modulo && int_mod((*q).ub, modulo) + 1 >= (*p).lb) { + Relation hull = ConvexHull(Union(copy((*(*p).pos).first), copy((*(*q).pos).first))); + Relation remainder = Difference(Difference(copy(hull), copy((*(*p).pos).first)), copy((*(*q).pos).first)); + if (!remainder.is_upper_bound_satisfiable()) { + if ((*p).pos == orig) + std::swap((*p).pos, (*q).pos); + (*(*q).pos).first = hull; + coef_t t = (*p).ub - int_mod((*q).ub, modulo); + if (t > 0) + (*q).ub = (*q).ub + t; + (*q).change = true; + Rs.erase((*p).pos); + p = intervals.erase(p); + p_moved = true; + break; + } + else + q++; + } + else + q++; + } + + if (!p_moved) + p++; + } + + // merge by reducing the strengh of modulo + std::list<Modulo_Interval> modulo_intervals; + coef_t max_distance = modulo/2; + for (std::list<Interval>::iterator p = intervals.begin(); p != intervals.end(); p++) { + if ((*p).lb >= max_distance) + break; + + coef_t size = (*p).ub - (*p).lb; + + std::list<Interval>::iterator q = p; + q++; + while (q != intervals.end()) { + coef_t distance = (*q).lb - (*p).lb; + if (distance > max_distance) + break; + + if ((*q).ub - (*q).lb != size || int_mod(modulo, distance) != 0) { + q++; + continue; + } + + int num_reduced = 0; + coef_t looking_for = int_mod((*p).lb, distance); + for (std::list<Interval>::iterator k = intervals.begin(); k != intervals.end(); k++) { + if ((*k).lb == looking_for && (*k).ub - (*k).lb == size) { + num_reduced++; + looking_for += distance; + if (looking_for >= modulo) + break; + } + else if ((*k).lb <= looking_for && (*k).ub >= looking_for + size) { + looking_for += distance; + if (looking_for >= modulo) + break; + } + else if ((*k).lb > looking_for) + break; + } + + if (looking_for >= modulo && num_reduced > 1) + modulo_intervals.push_back(Modulo_Interval(distance, int_mod((*p).lb, distance), size)); + + q++; + } + } + + modulo_intervals.sort(); + + // remove redundant reduced-strength intervals + std::list<Modulo_Interval>::iterator p2 = modulo_intervals.begin(); + while (p2 != modulo_intervals.end()) { + std::list<Modulo_Interval>::iterator q2 = p2; + q2++; + while (q2 != modulo_intervals.end()) { + if ((*p2).modulo == (*q2).modulo && (*p2).start == (*q2).start) + q2 = modulo_intervals.erase(q2); + else if (int_mod((*q2).modulo, (*p2).modulo) == 0 && + (*p2).start == int_mod((*q2).start, (*p2).modulo) && + (*p2).size >= (*q2).size) + q2 = modulo_intervals.erase(q2); + else + q2++; + } + p2++; + } + + // replace original intervals with new reduced-strength ones + for (std::list<Modulo_Interval>::iterator i = modulo_intervals.begin(); i != modulo_intervals.end(); i++) { + std::vector<Relation *> candidates; + int num_replaced = 0; + for (std::list<Interval>::iterator j = intervals.begin(); j != intervals.end(); j++) + if (int_mod((*j).modulo, (*i).modulo) == 0 && + (*j).ub - (*j).lb >= (*i).size && + (int_mod((*j).lb, (*i).modulo) == (*i).start || + int_mod((*j).ub, (*i).modulo) == (*i).start + (*i).size)) { + candidates.push_back(&((*(*j).pos).first)); + if (int_mod((*j).lb, (*i).modulo) == (*i).start && + (*j).ub - (*j).lb == (*i).size) + num_replaced++; + } + if (num_replaced <= 1) + continue; + + Relation R = copy(*candidates[0]); + for (size_t k = 1; k < candidates.size(); k++) + R = Union(R, copy(*candidates[k])); + Relation hull = ConvexHull(copy(R)); + Relation remainder = Difference(copy(hull), copy(R)); + if (!remainder.is_upper_bound_satisfiable()) { + std::list<Interval>::iterator replaced_one = intervals.end(); + for (std::list<Interval>::iterator j = intervals.begin(); j != intervals.end();) + if (int_mod((*j).modulo, (*i).modulo) == 0 && + (*j).ub - (*j).lb >= (*i).size && + (int_mod((*j).lb, (*i).modulo) == (*i).start || + int_mod((*j).ub, (*i).modulo) == (*i).start + (*i).size)) { + if (int_mod((*j).lb, (*i).modulo) == (*i).start && + (*j).ub - (*j).lb == (*i).size) { + if (replaced_one == intervals.end()) { + (*(*j).pos).first = hull; + (*j).lb = int_mod((*j).lb, (*i).modulo); + (*j).ub = int_mod((*j).ub, (*i).modulo); + (*j).modulo = (*i).modulo; + (*j).change = true; + replaced_one = j; + j++; + } + else { + if ((*j).pos == orig) { + std::swap((*replaced_one).pos, (*j).pos); + (*(*replaced_one).pos).first = (*(*j).pos).first; + } + Rs.erase((*j).pos); + j = intervals.erase(j); + } + } + else { + if (int_mod((*j).lb, (*i).modulo) == (*i).start) + (*j).lb = (*j).lb + (*i).size + 1; + else + (*j).ub = (*j).ub - (*i).size - 1; + (*j).change = true; + j++; + } + } + else + j++; + } + } +} +} // namespace + + +// +// Simplify a union of sets/relations to a minimal (may not be +// optimal) number of convex regions. It intends to replace +// CheckForConvexRepresentation and CheckForConvexPairs functions. +// +Relation ConvexRepresentation(NOT_CONST Relation &R) { + Relation l_R = copy(R); + if (!l_R.is_upper_bound_satisfiable() || l_R.number_of_conjuncts() < 2) + return R; + + // separate each conjunct into smooth convex region and holes + std::list<std::pair<Relation, Relation> > Rs; // pair(smooth region, hole condition) + for (DNF_Iterator c(l_R.query_DNF()); c.live(); c++) { + Relation r1 = Relation(l_R, c.curr()); + Relation r2 = Approximate(copy(r1)); + r1 = Gist(r1, copy(r2)); + Rs.push_back(std::make_pair(r2, r1)); + } + + try { + bool change = true; + while (change) { + change = false; + + std::list<std::pair<Relation, Relation> >::iterator i = Rs.begin(); + while (i != Rs.end()) { + // find regions with identical hole conditions to merge + { + std::list<std::pair<Relation, Relation> >::iterator j = i; + j++; + while (j != Rs.end()) { + if (!Difference(copy((*i).second), copy((*j).second)).is_upper_bound_satisfiable() && + !Difference(copy((*j).second), copy((*i).second)).is_upper_bound_satisfiable()) { + if (Must_Be_Subset(copy((*j).first), copy((*i).first))) { + j = Rs.erase(j); + } + else if (Must_Be_Subset(copy((*i).first), copy((*j).first))) { + (*i).first = (*j).first; + j = Rs.erase(j); + change = true; + } + else { + Relation r; + bool already_use_recthull = false; + try { + // chun's debug + // throw std::runtime_error("dfdf"); + + r = ConvexHull(Union(copy((*i).first), copy((*j).first))); + } + catch (const std::overflow_error &e) { + r = RectHull(Union(copy((*i).first), copy((*j).first))); + already_use_recthull = true; + } + retry_recthull: + Relation r2 = Difference(Difference(copy(r), copy((*i).first)), copy((*j).first)); + if (!r2.is_upper_bound_satisfiable()) { // convex hull is tight + (*i).first = r; + j = Rs.erase(j); + change = true; + } + else { + if (!already_use_recthull) { + r = RectHull(Union(copy((*i).first), copy((*j).first))); + already_use_recthull = true; + goto retry_recthull; + } + else + j++; + } + } + } + else + j++; + } + } + + // find identical smooth regions as candidates for hole merge + std::list<std::list<std::pair<Relation, Relation> >::iterator> s; + for (std::list<std::pair<Relation, Relation> >::iterator j = Rs.begin(); j != Rs.end(); j++) + if (j != i) { + if (!Intersection(Difference(copy((*i).first), copy((*j).first)), copy((*j).second)).is_upper_bound_satisfiable() && + !Intersection(Difference(copy((*j).first), copy((*i).first)), copy((*i).second)).is_upper_bound_satisfiable()) + s.push_back(j); + } + + if (s.size() != 0) { + // convert hole condition c1*x1+c2*x2+... = c*alpha+d to a pair of inequalities + (*i).second = EQs_to_GEQs((*i).second, false); + + // find potential wildcards that can be used for hole conditions + std::set<Variable_ID> nonsingle_wild; + for (EQ_Iterator ei((*i).second.single_conjunct()); ei; ei++) + if ((*ei).has_wildcards()) + for (Constr_Vars_Iter cvi(*ei, true); cvi; cvi++) + nonsingle_wild.insert(cvi.curr_var()); + for (GEQ_Iterator gei((*i).second.single_conjunct()); gei; gei++) + if ((*gei).has_wildcards()) { + Constr_Vars_Iter cvi(*gei, true); + Constr_Vars_Iter cvi2 = cvi; + cvi2++; + if (cvi2) { + nonsingle_wild.insert(cvi.curr_var()); + for (; cvi2; cvi2++) + nonsingle_wild.insert(cvi2.curr_var()); + } + } + + // find hole condition in c*alpha+d1<=c1*x1+c2*x2+...<=c*alpha+d2 format + for (GEQ_Iterator gei((*i).second.single_conjunct()); gei; gei++) + if ((*gei).has_wildcards()) { + coef_t c; + Variable_ID v; + { + Constr_Vars_Iter cvi(*gei, true); + v = cvi.curr_var(); + c = cvi.curr_coef(); + if (c < 0 || nonsingle_wild.find(v) != nonsingle_wild.end()) + continue; + } + + coef_t lb = posInfinity; + for (GEQ_Iterator gei2((*i).second.single_conjunct()); gei2; gei2++) { + if (!(*gei2 == *gei) && (*gei2).get_coef(v) != 0) { + if (lb != posInfinity) { + nonsingle_wild.insert(v); + break; + } + + bool match = true; + for (Constr_Vars_Iter cvi2(*gei); cvi2; cvi2++) + if (cvi2.curr_coef() != -((*gei2).get_coef(cvi2.curr_var()))) { + match = false; + break; + } + if (match) + for (Constr_Vars_Iter cvi2(*gei2); cvi2; cvi2++) + if (cvi2.curr_coef() != -((*gei).get_coef(cvi2.curr_var()))) { + match = false; + break; + } + if (!match) { + nonsingle_wild.insert(v); + break; + } + + lb = -(*gei2).get_const(); + } + } + + if (nonsingle_wild.find(v) != nonsingle_wild.end()) + continue; + + Relation stride_cond = Relation::True((*i).second); + F_Exists *f_exists = stride_cond.and_with_and()->add_exists(); + Variable_ID e = f_exists->declare(); + F_And *f_root = f_exists->add_and(); + GEQ_Handle h1 = f_root->add_GEQ(); + GEQ_Handle h2 = f_root->add_GEQ(); + for (Constr_Vars_Iter cvi2(*gei); cvi2; cvi2++) { + Variable_ID v = cvi2.curr_var(); + switch (v->kind()) { + case Wildcard_Var: + h1.update_coef(e, cvi2.curr_coef()); + h2.update_coef(e, -cvi2.curr_coef()); + break; + case Global_Var: { + Global_Var_ID g = v->get_global_var(); + Variable_ID v2; + if (g->arity() == 0) + v2 = stride_cond.get_local(g); + else + v2 = stride_cond.get_local(g, v->function_of()); + h1.update_coef(v2, cvi2.curr_coef()); + h2.update_coef(v2, -cvi2.curr_coef()); + break; + } + default: + h1.update_coef(v, cvi2.curr_coef()); + h2.update_coef(v, -cvi2.curr_coef()); + } + } + h1.update_const((*gei).get_const()); + h2.update_const(-lb); + + stride_cond.simplify(); + Relation other_cond = Gist(copy((*i).second), copy(stride_cond)); + + // find regions with potential mergeable stride condition with this one + std::list<Interval> intervals; + intervals.push_back(Interval(i, lb, (*gei).get_const())); + + for (std::list<std::list<std::pair<Relation, Relation> >::iterator>::iterator j = s.begin(); j != s.end(); j++) + if (Must_Be_Subset(copy((**j).second), copy(other_cond))) { + Relation stride_cond2 = Gist(copy((**j).second), copy(other_cond)); + + // interval can be removed + if (stride_cond2.is_obvious_tautology()) { + intervals.push_back(Interval(*j, 0, c-1)); + continue; + } + + stride_cond2 = EQs_to_GEQs(stride_cond2, false); + coef_t lb, ub; + GEQ_Iterator gei2(stride_cond2.single_conjunct()); + coef_t sign = 0; + for (Constr_Vars_Iter cvi(*gei2, true); cvi; cvi++) + if (sign != 0) { + sign = 0; + break; + } + else if (cvi.curr_coef() == c) + sign = 1; + else if (cvi.curr_coef() == -c) + sign = -1; + else { + sign = 0; + break; + } + if (sign == 0) + continue; + + bool match = true; + for (Constr_Vars_Iter cvi(*gei2); cvi; cvi++) { + Variable_ID v = cvi.curr_var(); + if (v->kind() == Wildcard_Var) + continue; + else if (v->kind() == Global_Var) { + Global_Var_ID g = v->get_global_var(); + if (g->arity() == 0) + v = (*i).second.get_local(g); + else + v = (*i).second.get_local(g, v->function_of()); + } + + if (cvi.curr_coef() != sign * (*gei).get_coef(v)) { + match = false; + break; + } + } + if (!match) + continue; + + for (Constr_Vars_Iter cvi(*gei); cvi; cvi++) { + Variable_ID v = cvi.curr_var(); + if (v->kind() == Wildcard_Var) + continue; + else if (v->kind() == Global_Var) { + Global_Var_ID g = v->get_global_var(); + if (g->arity() == 0) + v = stride_cond2.get_local(g); + else + v = stride_cond2.get_local(g, v->function_of()); + } + + if (cvi.curr_coef() != sign * (*gei2).get_coef(v)) { + match = false; + break; + } + } + if (!match) + continue; + if (sign > 0) + ub = (*gei2).get_const(); + else + lb = -(*gei2).get_const(); + + gei2++; + if (!gei2) + continue; + + coef_t sign2 = 0; + for (Constr_Vars_Iter cvi(*gei2, true); cvi; cvi++) + if (sign2 != 0) { + sign2 = 0; + break; + } + else if (cvi.curr_coef() == c) + sign2 = 1; + else if (cvi.curr_coef() == -c) + sign2 = -1; + else { + sign2 = 0; + break; + } + if (sign2 != -sign) + continue; + + for (Constr_Vars_Iter cvi(*gei2); cvi; cvi++) { + Variable_ID v = cvi.curr_var(); + if (v->kind() == Wildcard_Var) + continue; + else if (v->kind() == Global_Var) { + Global_Var_ID g = v->get_global_var(); + if (g->arity() == 0) + v = (*i).second.get_local(g); + else + v = (*i).second.get_local(g, v->function_of()); + } + + if (cvi.curr_coef() != sign2 * (*gei).get_coef(v)) { + match = false; + break; + } + } + if (!match) + continue; + + for (Constr_Vars_Iter cvi(*gei); cvi; cvi++) { + Variable_ID v = cvi.curr_var(); + if (v->kind() == Wildcard_Var) + continue; + else if (v->kind() == Global_Var) { + Global_Var_ID g = v->get_global_var(); + if (g->arity() == 0) + v = stride_cond2.get_local(g); + else + v = stride_cond2.get_local(g, v->function_of()); + } + + if (cvi.curr_coef() != sign2 * (*gei2).get_coef(v)) { + match = false; + break; + } + } + if (!match) + continue; + if (sign2 > 0) + ub = (*gei2).get_const(); + else + lb = -(*gei2).get_const(); + + gei2++; + if (gei2) + continue; + + intervals.push_back(Interval(*j, lb, ub)); + } + + merge_intervals(intervals, c, Rs, i); + + // make current region the last one being updated + bool invalid = false; + for (std::list<Interval>::iterator ii = intervals.begin(); ii != intervals.end(); ii++) + if ((*ii).change && (*ii).pos == i) { + invalid = true; + intervals.push_back(*ii); + intervals.erase(ii); + break; + } + + // update hole condition for each region + for (std::list<Interval>::iterator ii = intervals.begin(); ii != intervals.end(); ii++) + if ((*ii).change) { + change = true; + + if ((*ii).ub - (*ii).lb + 1 >= (*ii).modulo) + (*(*ii).pos).second = copy(other_cond); + else { + Relation stride_cond = Relation::True((*i).second); + F_Exists *f_exists = stride_cond.and_with_and()->add_exists(); + Variable_ID e = f_exists->declare(); + F_And *f_root = f_exists->add_and(); + GEQ_Handle h1 = f_root->add_GEQ(); + GEQ_Handle h2 = f_root->add_GEQ(); + for (Constr_Vars_Iter cvi2(*gei); cvi2; cvi2++) { + Variable_ID v = cvi2.curr_var(); + switch (v->kind()) { + case Wildcard_Var: + h1.update_coef(e, (*ii).modulo); + h2.update_coef(e, -(*ii).modulo); + break; + case Global_Var: { + Global_Var_ID g = v->get_global_var(); + Variable_ID v2; + if (g->arity() == 0) + v2 = stride_cond.get_local(g); + else + v2 = stride_cond.get_local(g, v->function_of()); + h1.update_coef(v2, cvi2.curr_coef()); + h2.update_coef(v2, -cvi2.curr_coef()); + break; + } + default: + h1.update_coef(v, cvi2.curr_coef()); + h2.update_coef(v, -cvi2.curr_coef()); + } + } + h1.update_const((*ii).ub); + h2.update_const(-(*ii).lb); + + (*(*ii).pos).second = Intersection(copy(other_cond), stride_cond); + (*(*ii).pos).second.simplify(); + } + } + + if (invalid) + break; + } + } + i++; + } + } + } + catch (const presburger_error &e) { + throw e; + } + + Relation R2 = Relation::False(l_R); + for (std::list<std::pair<Relation, Relation> >::iterator i = Rs.begin(); i != Rs.end(); i++) + R2 = Union(R2, Intersection((*i).first, (*i).second)); + R2.simplify(0, 1); + + return R2; +} + + +// +// Use gist and value range to calculate a quick rectangular hull. It +// intends to replace all hull calculations (QuickHull, BetterHull, +// FastTightHull) beyond the method of ConvexHull (dual +// representations). In the future, it will support max(...)-like +// upper bound. So RectHull complements ConvexHull in two ways: first +// for relations that ConvexHull gets too complicated, second for +// relations where different conjuncts have different symbolic upper +// bounds. +// +Relation RectHull(NOT_CONST Relation &Rel) { + Relation R = Approximate(consume_and_regurgitate(Rel)); + if (!R.is_upper_bound_satisfiable()) + return R; + if (R.has_single_conjunct()) + return R; + + std::vector<std::string> input_names(R.n_inp()); + for (int i = 1; i <= R.n_inp(); i++) + input_names[i-1] = R.input_var(i)->name(); + std::vector<std::string> output_names(R.n_out()); + for (int i = 1; i <= R.n_out(); i++) + output_names[i-1] = R.output_var(i)->name(); + + DNF_Iterator c(R.query_DNF()); + Relation r = Relation(R, c.curr()); + c++; + std::vector<std::pair<coef_t, coef_t> > bounds1(R.n_inp()); + std::vector<std::pair<coef_t, coef_t> > bounds2(R.n_out()); + { + Relation t = Project_Sym(copy(r)); + t.simplify(); + for (int i = 1; i <= R.n_inp(); i++) { + Tuple<Variable_ID> v; + for (int j = 1; j <= R.n_inp(); j++) + if (j != i) + v.append(r.input_var(j)); + for (int j = 1; j <= R.n_out(); j++) + v.append(r.output_var(j)); + Relation t2 = Project(copy(t), v); + t2.query_variable_bounds(t2.input_var(i), bounds1[i-1].first, bounds1[i-1].second); + } + for (int i = 1; i <= R.n_out(); i++) { + Tuple<Variable_ID> v; + for (int j = 1; j <= R.n_out(); j++) + if (j != i) + v.append(r.output_var(j)); + for (int j = 1; j <= R.n_inp(); j++) + v.append(r.input_var(j)); + Relation t2 = Project(copy(t), v); + t2.query_variable_bounds(t2.output_var(i), bounds2[i-1].first, bounds2[i-1].second); + } + } + + while (c.live()) { + Relation r2 = Relation(R, c.curr()); + c++; + Relation x = Gist(copy(r), Gist(copy(r), copy(r2), 1), 1); + if (Difference(copy(r2), copy(x)).is_upper_bound_satisfiable()) + x = Relation::True(R); + Relation y = Gist(copy(r2), Gist(copy(r2), copy(r), 1), 1); + if (Difference(copy(r), copy(y)).is_upper_bound_satisfiable()) + y = Relation::True(R); + r = Intersection(x, y); + + { + Relation t = Project_Sym(copy(r2)); + t.simplify(); + for (int i = 1; i <= R.n_inp(); i++) { + Tuple<Variable_ID> v; + for (int j = 1; j <= R.n_inp(); j++) + if (j != i) + v.append(r2.input_var(j)); + for (int j = 1; j <= R.n_out(); j++) + v.append(r2.output_var(j)); + Relation t2 = Project(copy(t), v); + coef_t lbound, ubound; + t2.query_variable_bounds(t2.input_var(i), lbound, ubound); + bounds1[i-1].first = min(bounds1[i-1].first, lbound); + bounds1[i-1].second = max(bounds1[i-1].second, ubound); + } + for (int i = 1; i <= R.n_out(); i++) { + Tuple<Variable_ID> v; + for (int j = 1; j <= R.n_out(); j++) + if (j != i) + v.append(r2.output_var(j)); + for (int j = 1; j <= R.n_inp(); j++) + v.append(r2.input_var(j)); + Relation t2 = Project(copy(t), v); + coef_t lbound, ubound; + t2.query_variable_bounds(t2.output_var(i), lbound, ubound); + bounds2[i-1].first = min(bounds2[i-1].first, lbound); + bounds2[i-1].second = max(bounds2[i-1].second, ubound); + } + } + + Relation r3(R.n_inp(), R.n_out()); + F_And *f_root = r3.add_and(); + for (int i = 1; i <= R.n_inp(); i++) { + if (bounds1[i-1].first != -posInfinity) { + GEQ_Handle h = f_root->add_GEQ(); + h.update_coef(r3.input_var(i), 1); + h.update_const(-bounds1[i-1].first); + } + if (bounds1[i-1].second != posInfinity) { + GEQ_Handle h = f_root->add_GEQ(); + h.update_coef(r3.input_var(i), -1); + h.update_const(bounds1[i-1].second); + } + } + for (int i = 1; i <= R.n_out(); i++) { + if (bounds2[i-1].first != -posInfinity) { + GEQ_Handle h = f_root->add_GEQ(); + h.update_coef(r3.output_var(i), 1); + h.update_const(-bounds2[i-1].first); + } + if (bounds2[i-1].second != posInfinity) { + GEQ_Handle h = f_root->add_GEQ(); + h.update_coef(r3.output_var(i), -1); + h.update_const(bounds2[i-1].second); + } + } + r = Intersection(r, r3); + r.simplify(); + } + + for (int i = 1; i <= r.n_inp(); i++) + r.name_input_var(i, input_names[i-1]); + for (int i = 1; i <= r.n_out(); i++) + r.name_output_var(i, output_names[i-1]); + r.setup_names(); + return r; +} + +} // namespace diff --git a/omegalib/omega/src/hull_legacy.cc b/omegalib/omega/src/hull_legacy.cc new file mode 100755 index 0000000..a59d34f --- /dev/null +++ b/omegalib/omega/src/hull_legacy.cc @@ -0,0 +1,1484 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + All Rights Reserved. + + Purpose: + Legacy hull calculations' implementation. + + Notes: + + History: + 06/15/09 ConvexRepresentation, Chun Chen + 11/25/09 RectHull, Chun Chen +*****************************************************************************/ + +#include <omega.h> +#include <omega/farkas.h> +#include <omega/hull.h> +#include <basic/Bag.h> +#include <basic/omega_error.h> +#include <list> +#include <vector> +#include <set> + +namespace omega { + +int hull_debug = 0; + +Relation ConvexHull(NOT_CONST Relation &R) { + Relation S = Approximate(consume_and_regurgitate(R)); + if (!S.is_upper_bound_satisfiable()) + return S; + if (S.has_single_conjunct()) + return S; + return Farkas(Farkas(S,Basic_Farkas), Convex_Combination_Farkas); +} + +Relation DecoupledConvexHull(NOT_CONST Relation &R) { + Relation S = Approximate(consume_and_regurgitate(R)); + if (!S.is_upper_bound_satisfiable()) + return S; + if (S.has_single_conjunct()) + return S; + return Farkas(Farkas(S,Decoupled_Farkas), Convex_Combination_Farkas); +} + +Relation AffineHull(NOT_CONST Relation &R) { + Relation S = Approximate(consume_and_regurgitate(R)); + if (!S.is_upper_bound_satisfiable()) + return S; + return Farkas(Farkas(S,Basic_Farkas), Affine_Combination_Farkas); +} + +Relation LinearHull(NOT_CONST Relation &R) { + Relation S = Approximate(consume_and_regurgitate(R)); + if (!S.is_upper_bound_satisfiable()) + return S; + return Farkas(Farkas(S,Basic_Farkas), Linear_Combination_Farkas); +} + +Relation ConicHull(NOT_CONST Relation &R) { + Relation S = Approximate(consume_and_regurgitate(R)); + if (!S.is_upper_bound_satisfiable()) + return S; + return Farkas(Farkas(S,Basic_Farkas), Positive_Combination_Farkas); +} + + +Relation FastTightHull(NOT_CONST Relation &input_R, NOT_CONST Relation &input_H) { + Relation R = Approximate(consume_and_regurgitate(input_R)); + Relation H = Approximate(consume_and_regurgitate(input_H)); + + if (hull_debug) { + fprintf(DebugFile,"[ Computing FastTightHull of:\n"); + R.prefix_print(DebugFile); + fprintf(DebugFile,"given known hull of:\n"); + H.prefix_print(DebugFile); + } + + if (!H.has_single_conjunct()) { + if (hull_debug) + fprintf(DebugFile, "] bailing out of FastTightHull, known hull not convex\n"); + return H; + } + + if (!H.is_obvious_tautology()) { + R = Gist(R,copy(H)); + R.simplify(1,0); + } + + if (R.has_single_conjunct()) { + R = Intersection(R,H); + if (hull_debug) { + fprintf(DebugFile, "] quick easy answer to FastTightHull\n"); + R.prefix_print(DebugFile); + } + return R; + } + if (R.has_local(coefficient_of_constant_term)) { + if (hull_debug) { + fprintf(DebugFile, "] Can't handle recursive application of Farkas lemma\n"); + } + return H; + } + + if (hull_debug) { + fprintf(DebugFile,"Gist of R given H is:\n"); + R.prefix_print(DebugFile); + } + + if (1) { + Set<Variable_ID> vars; + int conjuncts = 0; + for (DNF_Iterator s(R.query_DNF()); s.live(); s.next()) { + conjuncts++; + for (Variable_ID_Iterator v(*((*s)->variables())); v.live(); v++) { + bool found = false; + for (EQ_Iterator eq = (*s)->EQs(); eq.live(); eq.next()) + if ((*eq).get_coef(*v) != 0) { + if (!found) vars.insert(*v); + found = true; + break; + } + if (!found) + for (GEQ_Iterator geq = (*s)->GEQs(); geq.live(); geq.next()) + if ((*geq).get_coef(*v) != 0) { + if (!found) vars.insert(*v); + found = true; + break; + } + } + } + + + // We now know which variables appear in R + if (hull_debug) { + fprintf(DebugFile,"Variables we need a better hull on are: "); + foreach(v,Variable_ID,vars, + fprintf(DebugFile," %s",v->char_name())); + fprintf(DebugFile,"\n"); + } + Conjunct *c = H.single_conjunct(); + int total=0; + int copied = 0; + for (EQ_Iterator eq = c->EQs(); eq.live(); eq.next()) { + total++; + foreach(v,Variable_ID,vars, + if ((*eq).get_coef(v) != 0) { + R.and_with_EQ(*eq); + copied++; + break; // out of variable loop + } + ); + } + for (GEQ_Iterator geq = c->GEQs(); geq.live(); geq.next()) { + total++; + foreach(v,Variable_ID,vars, + if ((*geq).get_coef(v) != 0) { + R.and_with_GEQ(*geq); + copied++; + break; // out of variable loop + } + ); + } + if (copied < total) { + R = Approximate(R); + + if (hull_debug) { + fprintf(DebugFile,"Decomposed relation, copied only %d of %d constraints\n",copied,total); + fprintf(DebugFile,"Original R:\n"); + R.prefix_print(DebugFile); + fprintf(DebugFile,"Known hull:\n"); + H.prefix_print(DebugFile); + fprintf(DebugFile,"New R:\n"); + R.prefix_print(DebugFile); + } + } + } + + Relation F = Farkas(copy(R), Basic_Farkas, true); + if (hull_debug) + fprintf(DebugFile,"Farkas Difficulty = " coef_fmt "\n", farkasDifficulty); + if (farkasDifficulty > 260) { + if (hull_debug) { + fprintf(DebugFile, "] bailing out, farkas is way too complex\n"); + fprintf(DebugFile,"Farkas:\n"); + F.prefix_print(DebugFile); + } + return H; + } + else if (farkasDifficulty > 130) { + // Bail out + if (hull_debug) { + fprintf(DebugFile, coef_fmt " non-zeros in original farkas\n", farkasDifficulty); + } + Relation tmp = Farkas(R, Decoupled_Farkas, true); + + if (hull_debug) { + fprintf(DebugFile, coef_fmt " non-zeros in decoupled farkas\n", farkasDifficulty); + } + if (farkasDifficulty > 260) { + if (hull_debug) { + fprintf(DebugFile, "] bailing out, farkas is way too complex\n"); + fprintf(DebugFile,"Farkas:\n"); + F.prefix_print(DebugFile); + } + return H; + } + else { + if (farkasDifficulty > 130) + R = Intersection(H, Farkas(tmp, Affine_Combination_Farkas, true)); + else R = Intersection(H, + Intersection(Farkas(tmp, Convex_Combination_Farkas, true), + Farkas(F, Affine_Combination_Farkas, true))); + if (hull_debug) { + fprintf(DebugFile, "] bailing out, farkas is too complex, using affine hull\n"); + fprintf(DebugFile,"Farkas:\n"); + F.prefix_print(DebugFile); + fprintf(DebugFile,"Affine hull:\n"); + R.prefix_print(DebugFile); + } + return R; + } + } + + R = Intersection(H, Farkas(F, Convex_Combination_Farkas, true)); + if (hull_debug) { + fprintf(DebugFile, "] Result of FastTightHull:\n"); + R.prefix_print(DebugFile); + } + return R; +} + + + +namespace { + bool parallel(const GEQ_Handle &g1, const GEQ_Handle &g2) { + for(Constr_Vars_Iter cvi(g1, false); cvi; cvi++) { + coef_t c1 = (*cvi).coef; + coef_t c2 = g2.get_coef((*cvi).var); + if (c1 != c2) return false; + } + { + for(Constr_Vars_Iter cvi(g2, false); cvi; cvi++) { + coef_t c1 = g1.get_coef((*cvi).var); + coef_t c2 = (*cvi).coef; + if (c1 != c2) return false; + } + } + return true; + } + + + bool hull(const EQ_Handle &e, const GEQ_Handle &g, coef_t &hull) { + int sign = 0; + for(Constr_Vars_Iter cvi(e, false); cvi; cvi++) { + coef_t c1 = (*cvi).coef; + coef_t c2 = g.get_coef((*cvi).var); + if (sign == 0) sign = (c1*c2>=0?1:-1); + if (sign*c1 != c2) return false; + } + assert(sign != 0); + { + for(Constr_Vars_Iter cvi(g, false); cvi; cvi++) { + coef_t c1 = e.get_coef((*cvi).var); + coef_t c2 = (*cvi).coef; + if (sign*c1 != c2) return false; + } + } + hull = max(sign * e.get_const(), g.get_const()); + if (hull_debug) { + fprintf(DebugFile,"Hull of:\n %s\n", e.print_to_string().c_str()); + fprintf(DebugFile," %s\n", g.print_to_string().c_str()); + fprintf(DebugFile,"is " coef_fmt "\n\n",hull); + } + return true; + } + + bool eq(const EQ_Handle &e1, const EQ_Handle &e2) { + int sign = 0; + for(Constr_Vars_Iter cvi(e1, false); cvi; cvi++) { + coef_t c1 = (*cvi).coef; + coef_t c2 = e2.get_coef((*cvi).var); + if (sign == 0) sign = (c1*c2>=0?1:-1); + if (sign*c1 != c2) return false; + } + assert(sign != 0); + { + for(Constr_Vars_Iter cvi(e2, false); cvi; cvi++) { + coef_t c1 = e1.get_coef((*cvi).var); + coef_t c2 = (*cvi).coef; + if (sign*c1 != c2) return false; + } + } + return sign * e1.get_const() == e2.get_const(); + } +} + + +// This function is deprecated!!! +Relation QuickHull(Relation &R) { + Tuple<Relation> Rs(1); + Rs[1] = R; + return QuickHull(Rs); +} + + +// This function is deprecated!!! +Relation QuickHull(Tuple<Relation> &Rs) { + assert(!Rs.empty()); + + // if (Rs.size() == 1) return Rs[1]; + + Tuple<Relation> l_Rs; + for (int i = 1; i <= Rs.size(); i++) + for (DNF_Iterator c(Rs[i].query_DNF()); c; c++) { + Relation r = Relation(Rs[i], c.curr()); + l_Rs.append(Approximate(r)); + } + + if (l_Rs.size() == 1) + return l_Rs[1]; + + Relation result = Relation::True(Rs[1]); + result.copy_names(Rs[1]); + + use_ugly_names++; + + if (hull_debug > 1) + for (int i = 1; i <= l_Rs.size(); i++) { + fprintf(DebugFile,"#%d \n",i); + l_Rs[i].prefix_print(DebugFile); + } + + +// Relation R = copy(Rs[1]); +// for (int i = 2; i <= Rs.size(); i++) +// R = Union(R,copy(Rs[i])); + +// #if 0 +// if (!R.is_set()) { +// if (R.n_inp() == R.n_out()) { +// Relation AC = DeltasToRelation(Hull(Deltas(copy(R), +// min(R.n_inp(),R.n_out()))), +// R.n_inp(),R.n_out()); +// Relation dH = Hull(Domain(copy(R)),false); +// Relation rH = Hull(Range(copy(R)),false); +// result = Intersection(AC,Cross_Product(dH,rH)); +// } +// else { +// Relation dH = Hull(Domain(copy(R)),false); +// Relation rH = Hull(Range(copy(R)),false); +// result = Cross_Product(dH,rH); +// assert(Must_Be_Subset(copy(R),copy(result))); +// } +// } + +// #endif + + Conjunct *first; + l_Rs[1] = EQs_to_GEQs(l_Rs[1]); + first = l_Rs[1].single_conjunct(); + for (GEQ_Iterator candidate(first->GEQs()); candidate.live(); candidate.next()) { + coef_t maxConstantTerm = (*candidate).get_const(); + bool found = true; + if (hull_debug > 1) { + fprintf(DebugFile,"searching for bound on:\n %s\n", (*candidate).print_to_string().c_str()); + } + for (int i = 2; i <= l_Rs.size(); i++) { + Conjunct *other = l_Rs[i].single_conjunct(); + bool found_for_i = false; + for (GEQ_Iterator target(other->GEQs()); target.live(); target.next()) { + if (hull_debug > 2) { + fprintf(DebugFile,"candidate:\n %s\n", (*candidate).print_to_string().c_str()); + fprintf(DebugFile,"target:\n %s\n", (*target).print_to_string().c_str()); + } + if (parallel(*candidate,*target)) { + if (hull_debug > 1) + fprintf(DebugFile,"Found bound:\n %s\n", (*target).print_to_string().c_str()); + maxConstantTerm = max(maxConstantTerm,(*target).get_const()); + found_for_i = true; + break; + } + } + if (!found_for_i) { + for (EQ_Iterator target_e(other->EQs()); target_e.live(); target_e.next()) { + coef_t h; + if (hull(*target_e,*candidate,h)) { + if (hull_debug > 1) + fprintf(DebugFile,"Found bound of " coef_fmt ":\n %s\n", h, (*target_e).print_to_string().c_str()); + maxConstantTerm = max(maxConstantTerm,h); + found_for_i = true; + break; + } + }; + if (!found_for_i) { + if (hull_debug > 1) { + fprintf(DebugFile,"No bound found in:\n"); + fprintf(DebugFile, "%s", l_Rs[i].print_with_subs_to_string().c_str()); + } + //if nothing found + found = false; + break; + } + } + } + + if (found) { + GEQ_Handle h = result.and_with_GEQ(); + copy_constraint(h,*candidate); + if (hull_debug > 1) + fprintf(DebugFile,"Setting constant term to " coef_fmt " in\n %s\n", maxConstantTerm, h.print_to_string().c_str()); + h.update_const(maxConstantTerm - (*candidate).get_const()); + if (hull_debug > 1) + fprintf(DebugFile,"Updated constraint is\n %s\n", h.print_to_string().c_str()); + } + } + + + for (EQ_Iterator candidate_eq(first->EQs()); candidate_eq.live(); candidate_eq.next()) { + bool found = true; + for (int i = 2; i <= l_Rs.size(); i++) { + Conjunct *C = l_Rs[i].single_conjunct(); + bool found_for_i = false; + + for (EQ_Iterator target(C->EQs()); target.live(); target.next()) { + if (eq(*candidate_eq,*target)) { + found_for_i = true; + break; + } + } + if (!found_for_i) { + //if nothing found + found = false; + break; + } + } + + if (found) { + EQ_Handle h = result.and_with_EQ(); + copy_constraint(h,*candidate_eq); + if (hull_debug > 1) + fprintf(DebugFile,"Adding eq constraint: %s\n", h.print_to_string().c_str()); + } + } + + use_ugly_names--; + if (hull_debug > 1) { + fprintf(DebugFile,"quick hull is of:"); + result.print_with_subs(DebugFile); + } + return result; +} + + +// Relation Hull2(Tuple<Relation> &Rs, Tuple<int> &active) { +// assert(Rs.size() == active.size() && Rs.size() > 0); + +// Tuple<Relation> l_Rs; +// for (int i = 1; i <= Rs.size(); i++) +// if (active[i]) +// l_Rs.append(copy(Rs[i])); + +// if (l_Rs.size() == 0) +// return Relation::False(Rs[1]); + +// try { +// Relation r = l_Rs[1]; +// for (int i = 2; i <= l_Rs.size(); i++) { +// r = Union(r, copy(l_Rs[i])); +// r.simplify(); +// } + +// // Relation F = Farkas(r, Basic_Farkas, true); +// // if (farkasDifficulty >= 500) +// // throw std::overflow_error("loop convex hull too complicated."); +// // F = Farkas(F, Convex_Combination_Farkas, true); +// return Farkas(Farkas(r, Basic_Farkas, true), Convex_Combination_Farkas, true); +// } +// catch (std::overflow_error) { +// return QuickHull(l_Rs); +// } +// } + + +namespace { + void printRs(Tuple<Relation> &Rs) { + fprintf(DebugFile,"Rs:\n"); + for (int i = 1; i <= Rs.size(); i++) + fprintf(DebugFile,"#%d : %s\n",i, + Rs[i].print_with_subs_to_string().c_str()); + } +} + +Relation BetterHull(Tuple<Relation> &Rs, bool stridesAllowed, bool checkSubsets, + NOT_CONST Relation &input_knownHull = Relation::Null()) { + Relation knownHull = consume_and_regurgitate(input_knownHull); + static int OMEGA_WHINGE = -1; + if (OMEGA_WHINGE < 0) { + OMEGA_WHINGE = getenv("OMEGA_WHINGE") ? atoi(getenv("OMEGA_WHINGE")) : 0; + } + assert(!Rs.empty()); + if (Rs.size() == 1) { + if (stridesAllowed) return Rs[1]; + else return Approximate(Rs[1]); + } + + if (checkSubsets) { + Tuple<bool> live(Rs.size()); + if (hull_debug) { + fprintf(DebugFile,"Checking subsets in hull computation:\n"); + printRs(Rs); + } + int i; + for(i=1;i <=Rs.size(); i++) live[i] = true; + for(i=1;i <=Rs.size(); i++) + for(int j=1;j <=Rs.size(); j++) if (i != j && live[j]) { + if (hull_debug) fprintf(DebugFile,"checking %d Is_Obvious_Subset %d\n",i,j); + if (Is_Obvious_Subset(copy(Rs[i]),copy(Rs[j]))) { + if (hull_debug) fprintf(DebugFile,"yes...\n"); + live[i] = false; + break; + } + } + for(i=1;i <=Rs.size(); i++) if (!live[i]) { + if (i < Rs.size()) { + Rs[i] = Rs[Rs.size()]; + live[i] = live[Rs.size()]; + } + Rs[Rs.size()] = Relation(); + Rs.delete_last(); + i--; + } + } + Relation hull; + if (hull_debug) { + fprintf(DebugFile,"Better Hull:\n"); + printRs(Rs); + fprintf(DebugFile,"known hull: %s\n", knownHull.print_with_subs_to_string().c_str()); + } + if (knownHull.is_null()) hull = QuickHull(Rs); + else hull = Intersection(QuickHull(Rs),knownHull); + // for (int i = 1; i <= Rs.size(); i++) + // hull = RectHull(Union(hull, copy(Rs[i]))); + // hull = Intersection(hull, knownHull); + hull.simplify(); + if (hull_debug) { + fprintf(DebugFile,"quick hull: %s\n", hull.print_with_subs_to_string().c_str()); + } + + Relation orig = Relation::False(Rs[1]); + int i; + for (i = 1; i <= Rs.size(); i++) + orig = Union(orig,copy(Rs[i])); + + orig.simplify(); + + for (i = 1; i <= Rs.size(); i++) { + if (!hull.is_obvious_tautology()) Rs[i] = Gist(Rs[i],copy(hull)); + Rs[i].simplify(); + if (Rs[i].is_obvious_tautology()) return hull; + if (Rs[i].has_single_conjunct()) { + Rs[i] = EQs_to_GEQs(Rs[i]); + if (hull_debug) { + fprintf(DebugFile,"Checking for hull constraints in:\n %s\n", Rs[i].print_with_subs_to_string().c_str()); + } + Conjunct *c = Rs[i].single_conjunct(); + for (GEQ_Iterator g(c->GEQs()); g.live(); g.next()) { + Relation tmp = Relation::True(Rs[i]); + tmp.and_with_GEQ(*g); + if (!Difference(copy(orig),tmp).is_upper_bound_satisfiable()) + hull.and_with_GEQ(*g); + } + for (EQ_Iterator e(c->EQs()); e.live(); e.next()) { + Relation tmp = Relation::True(Rs[i]); + tmp.and_with_EQ(*e); + if (!Difference(copy(orig),tmp).is_upper_bound_satisfiable()) + hull.and_with_EQ(*e); + } + } + } + + hull = FastTightHull(orig,hull); + assert(hull.has_single_conjunct()); + + if (stridesAllowed) return hull; + else return Approximate(hull); + +} + + + +Relation Hull(NOT_CONST Relation &S, + bool stridesAllowed, + int effort, + NOT_CONST Relation &knownHull) { + Relation R = consume_and_regurgitate(S); + R.simplify(1,0); + if (!R.is_upper_bound_satisfiable()) return R; + Tuple<Relation> Rs; + for (DNF_Iterator c(R.query_DNF()); c.live(); ) { + Rs.append(Relation(R,c.curr())); + c.next(); + } + if (effort == 1) + return BetterHull(Rs,stridesAllowed,false,knownHull); + else + return QuickHull(Rs); +} + + + +Relation Hull(Tuple<Relation> &Rs, + const std::vector<bool> &validMask, + int effort, + bool stridesAllowed, + NOT_CONST Relation &knownHull) { + // Use relation of index i only when validMask[i] != 0 + Tuple<Relation> Rs2; + for(int i = 1; i <= Rs.size(); i++) { + if (validMask[i-1]) { + Rs[i].simplify(); + for (DNF_Iterator c(Rs[i].query_DNF()); c.live(); ) { + Rs2.append(Relation(Rs[i],c.curr())); + c.next(); + } + } + } + assert(effort == 0 || effort == 1); + if (effort == 1) + return BetterHull(Rs2,stridesAllowed,true,knownHull); + else + return QuickHull(Rs2); +} + + +// This function is deprecated!!! +Relation CheckForConvexRepresentation(NOT_CONST Relation &R_In) { + Relation R = consume_and_regurgitate(R_In); + Relation h = Hull(copy(R)); + if (!Difference(copy(h),copy(R)).is_upper_bound_satisfiable()) + return h; + else + return R; +} + +// This function is deprecated!!! +Relation CheckForConvexPairs(NOT_CONST Relation &S) { + Relation R = consume_and_regurgitate(S); + Relation hull = FastTightHull(copy(R),Relation::True(R)); + R.simplify(1,0); + if (!R.is_upper_bound_satisfiable() || R.number_of_conjuncts() < 2) return R; + Tuple<Relation> Rs; + for (DNF_Iterator c(R.query_DNF()); c.live(); ) { + Rs.append(Relation(R,c.curr())); + c.next(); + } + + bool *dead = new bool[Rs.size()+1]; + int i; + for(i = 1; i<=Rs.size();i++) dead[i] = false; + + for(i = 1; i<=Rs.size();i++) + if (!dead[i]) + for(int j = i+1; j<=Rs.size();j++) if (!dead[j]) { + if (hull_debug) { + fprintf(DebugFile,"Comparing #%d and %d\n",i,j); + } + Relation U = Union(copy(Rs[i]),copy(Rs[j])); + Relation H_ij = FastTightHull(copy(U),copy(hull)); + if (!Difference(copy(H_ij),U).is_upper_bound_satisfiable()) { + Rs[i] = H_ij; + dead[j] = true; + if (hull_debug) { + fprintf(DebugFile,"Combined them\n"); + } + } + } + i = 1; + while(i<=Rs.size() && dead[i]) i++; + assert(i<=Rs.size()); + R = Rs[i]; + i++; + for(; i<=Rs.size();i++) + if (!dead[i]) + R = Union(R,Rs[i]); + delete []dead; + return R; +} + +// +// Supporting functions for ConvexRepresentation +// +namespace { +struct Interval { + std::list<std::pair<Relation, Relation> >::iterator pos; + coef_t lb; + coef_t ub; + bool change; + coef_t modulo; + Interval(std::list<std::pair<Relation, Relation> >::iterator pos_, coef_t lb_, coef_t ub_): + pos(pos_), lb(lb_), ub(ub_) {} + friend bool operator<(const Interval &a, const Interval &b); +}; + +bool operator<(const Interval &a, const Interval &b) { + return a.lb < b.lb; +} + +struct Modulo_Interval { + coef_t modulo; + coef_t start; + coef_t size; + Modulo_Interval(coef_t modulo_, coef_t start_, coef_t size_): + modulo(modulo_), start(start_), size(size_) {} + friend bool operator<(const Interval &a, const Interval &b); +}; + +bool operator<(const Modulo_Interval &a, const Modulo_Interval &b) { + if (a.modulo == b.modulo) { + if (a.start == b.start) + return a.size < b.size; + else + return a.start < b.start; + } + else + return a.modulo < b.modulo; +} + +void merge_intervals(std::list<Interval> &intervals, coef_t modulo, std::list<std::pair<Relation, Relation> > &Rs, std::list<std::pair<Relation, Relation> >::iterator orig) { + // normalize intervals + for (std::list<Interval>::iterator i = intervals.begin(); i != intervals.end(); i++) { + (*i).modulo = modulo; + (*i).change = false; + if ((*i).ub - (*i).lb + 1>= modulo) { + (*i).lb = 0; + (*i).ub = modulo - 1; + } + else if ((*i).ub < 0 || (*i).lb >= modulo) { + coef_t range = (*i).ub - (*i).lb; + (*i).lb = int_mod((*i).lb, modulo); + (*i).ub = (*i).lb + range; + } + } + + intervals.sort(); + + // merge neighboring intervals + std::list<Interval>::iterator p = intervals.begin(); + while (p != intervals.end()) { + std::list<Interval>::iterator q = p; + q++; + while (q != intervals.end()) { + if ((*p).ub + 1 >= (*q).lb) { + Relation hull = ConvexHull(Union(copy((*(*p).pos).first), copy((*(*q).pos).first))); + Relation remainder = Difference(Difference(copy(hull), copy((*(*p).pos).first)), copy((*(*q).pos).first)); + if (!remainder.is_upper_bound_satisfiable()) { + if ((*q).pos == orig) + std::swap((*p).pos, (*q).pos); + (*(*p).pos).first = hull; + (*p).ub = max((*p).ub, (*q).ub); + (*p).change = true; + Rs.erase((*q).pos); + q = intervals.erase(q); + } + else + break; + } + else + break; + } + + bool p_moved = false; + q = p; + q++; + while (q != intervals.end()) { + if ((*q).ub >= modulo && int_mod((*q).ub, modulo) + 1 >= (*p).lb) { + Relation hull = ConvexHull(Union(copy((*(*p).pos).first), copy((*(*q).pos).first))); + Relation remainder = Difference(Difference(copy(hull), copy((*(*p).pos).first)), copy((*(*q).pos).first)); + if (!remainder.is_upper_bound_satisfiable()) { + if ((*p).pos == orig) + std::swap((*p).pos, (*q).pos); + (*(*q).pos).first = hull; + coef_t t = (*p).ub - int_mod((*q).ub, modulo); + if (t > 0) + (*q).ub = (*q).ub + t; + (*q).change = true; + Rs.erase((*p).pos); + p = intervals.erase(p); + p_moved = true; + break; + } + else + q++; + } + else + q++; + } + + if (!p_moved) + p++; + } + + // merge by reducing the strengh of modulo + std::list<Modulo_Interval> modulo_intervals; + coef_t max_distance = modulo/2; + for (std::list<Interval>::iterator p = intervals.begin(); p != intervals.end(); p++) { + if ((*p).lb >= max_distance) + break; + + coef_t size = (*p).ub - (*p).lb; + + std::list<Interval>::iterator q = p; + q++; + while (q != intervals.end()) { + coef_t distance = (*q).lb - (*p).lb; + if (distance > max_distance) + break; + + if ((*q).ub - (*q).lb != size || int_mod(modulo, distance) != 0) { + q++; + continue; + } + + int num_reduced = 0; + coef_t looking_for = int_mod((*p).lb, distance); + for (std::list<Interval>::iterator k = intervals.begin(); k != intervals.end(); k++) { + if ((*k).lb == looking_for && (*k).ub - (*k).lb == size) { + num_reduced++; + looking_for += distance; + if (looking_for >= modulo) + break; + } + else if ((*k).lb <= looking_for && (*k).ub >= looking_for + size) { + looking_for += distance; + if (looking_for >= modulo) + break; + } + else if ((*k).lb > looking_for) + break; + } + + if (looking_for >= modulo && num_reduced > 1) + modulo_intervals.push_back(Modulo_Interval(distance, int_mod((*p).lb, distance), size)); + + q++; + } + } + + modulo_intervals.sort(); + + // remove redundant reduced-strength intervals + std::list<Modulo_Interval>::iterator p2 = modulo_intervals.begin(); + while (p2 != modulo_intervals.end()) { + std::list<Modulo_Interval>::iterator q2 = p2; + q2++; + while (q2 != modulo_intervals.end()) { + if ((*p2).modulo == (*q2).modulo && (*p2).start == (*q2).start) + q2 = modulo_intervals.erase(q2); + else if (int_mod((*q2).modulo, (*p2).modulo) == 0 && + (*p2).start == int_mod((*q2).start, (*p2).modulo) && + (*p2).size >= (*q2).size) + q2 = modulo_intervals.erase(q2); + else + q2++; + } + p2++; + } + + // replace original intervals with new reduced-strength ones + for (std::list<Modulo_Interval>::iterator i = modulo_intervals.begin(); i != modulo_intervals.end(); i++) { + std::vector<Relation *> candidates; + int num_replaced = 0; + for (std::list<Interval>::iterator j = intervals.begin(); j != intervals.end(); j++) + if (int_mod((*j).modulo, (*i).modulo) == 0 && + (*j).ub - (*j).lb >= (*i).size && + (int_mod((*j).lb, (*i).modulo) == (*i).start || + int_mod((*j).ub, (*i).modulo) == (*i).start + (*i).size)) { + candidates.push_back(&((*(*j).pos).first)); + if (int_mod((*j).lb, (*i).modulo) == (*i).start && + (*j).ub - (*j).lb == (*i).size) + num_replaced++; + } + if (num_replaced <= 1) + continue; + + Relation R = copy(*candidates[0]); + for (size_t k = 1; k < candidates.size(); k++) + R = Union(R, copy(*candidates[k])); + Relation hull = ConvexHull(copy(R)); + Relation remainder = Difference(copy(hull), copy(R)); + if (!remainder.is_upper_bound_satisfiable()) { + std::list<Interval>::iterator replaced_one = intervals.end(); + for (std::list<Interval>::iterator j = intervals.begin(); j != intervals.end();) + if (int_mod((*j).modulo, (*i).modulo) == 0 && + (*j).ub - (*j).lb >= (*i).size && + (int_mod((*j).lb, (*i).modulo) == (*i).start || + int_mod((*j).ub, (*i).modulo) == (*i).start + (*i).size)) { + if (int_mod((*j).lb, (*i).modulo) == (*i).start && + (*j).ub - (*j).lb == (*i).size) { + if (replaced_one == intervals.end()) { + (*(*j).pos).first = hull; + (*j).lb = int_mod((*j).lb, (*i).modulo); + (*j).ub = int_mod((*j).ub, (*i).modulo); + (*j).modulo = (*i).modulo; + (*j).change = true; + replaced_one = j; + j++; + } + else { + if ((*j).pos == orig) { + std::swap((*replaced_one).pos, (*j).pos); + (*(*replaced_one).pos).first = (*(*j).pos).first; + } + Rs.erase((*j).pos); + j = intervals.erase(j); + } + } + else { + if (int_mod((*j).lb, (*i).modulo) == (*i).start) + (*j).lb = (*j).lb + (*i).size + 1; + else + (*j).ub = (*j).ub - (*i).size - 1; + (*j).change = true; + j++; + } + } + else + j++; + } + } +} +} // namespace + + +// +// Simplify a union of sets/relations to a minimal (may not be +// optimal) number of convex regions. It intends to replace +// CheckForConvexRepresentation and CheckForConvexPairs functions. +// +Relation ConvexRepresentation(NOT_CONST Relation &R) { + Relation l_R = copy(R); + if (!l_R.is_upper_bound_satisfiable() || l_R.number_of_conjuncts() < 2) + return R; + + // separate each conjunct into smooth convex region and holes + std::list<std::pair<Relation, Relation> > Rs; // pair(smooth region, hole condition) + for (DNF_Iterator c(l_R.query_DNF()); c.live(); c++) { + Relation r1 = Relation(l_R, c.curr()); + Relation r2 = Approximate(copy(r1)); + r1 = Gist(r1, copy(r2)); + Rs.push_back(std::make_pair(r2, r1)); + } + + try { + bool change = true; + while (change) { + change = false; + + std::list<std::pair<Relation, Relation> >::iterator i = Rs.begin(); + while (i != Rs.end()) { + // find regions with identical hole conditions to merge + { + std::list<std::pair<Relation, Relation> >::iterator j = i; + j++; + while (j != Rs.end()) { + if (!Difference(copy((*i).second), copy((*j).second)).is_upper_bound_satisfiable() && + !Difference(copy((*j).second), copy((*i).second)).is_upper_bound_satisfiable()) { + if (Must_Be_Subset(copy((*j).first), copy((*i).first))) { + j = Rs.erase(j); + } + else if (Must_Be_Subset(copy((*i).first), copy((*j).first))) { + (*i).first = (*j).first; + j = Rs.erase(j); + change = true; + } + else { + Relation r; + bool already_use_recthull = false; + try { + r = ConvexHull(Union(copy((*i).first), copy((*j).first))); + } + catch (const std::overflow_error &e) { + r = SimpleHull(Union(copy((*i).first), copy((*j).first))); + already_use_recthull = true; + } + retry_recthull: + Relation r2 = Difference(Difference(copy(r), copy((*i).first)), copy((*j).first)); + if (!r2.is_upper_bound_satisfiable()) { // convex hull is tight + (*i).first = r; + j = Rs.erase(j); + change = true; + } + else { + if (!already_use_recthull) { + r = SimpleHull(Union(copy((*i).first), copy((*j).first))); + already_use_recthull = true; + goto retry_recthull; + } + else + j++; + } + } + } + else + j++; + } + } + + // find identical smooth regions as candidates for hole merge + std::list<std::list<std::pair<Relation, Relation> >::iterator> s; + for (std::list<std::pair<Relation, Relation> >::iterator j = Rs.begin(); j != Rs.end(); j++) + if (j != i) { + if (!Intersection(Difference(copy((*i).first), copy((*j).first)), copy((*j).second)).is_upper_bound_satisfiable() && + !Intersection(Difference(copy((*j).first), copy((*i).first)), copy((*i).second)).is_upper_bound_satisfiable()) + s.push_back(j); + } + + if (s.size() != 0) { + // convert hole condition c1*x1+c2*x2+... = c*alpha+d to a pair of inequalities + (*i).second = EQs_to_GEQs((*i).second, false); + + // find potential wildcards that can be used for hole conditions + std::set<Variable_ID> nonsingle_wild; + for (EQ_Iterator ei((*i).second.single_conjunct()); ei; ei++) + if ((*ei).has_wildcards()) + for (Constr_Vars_Iter cvi(*ei, true); cvi; cvi++) + nonsingle_wild.insert(cvi.curr_var()); + for (GEQ_Iterator gei((*i).second.single_conjunct()); gei; gei++) + if ((*gei).has_wildcards()) { + Constr_Vars_Iter cvi(*gei, true); + Constr_Vars_Iter cvi2 = cvi; + cvi2++; + if (cvi2) { + nonsingle_wild.insert(cvi.curr_var()); + for (; cvi2; cvi2++) + nonsingle_wild.insert(cvi2.curr_var()); + } + } + + // find hole condition in c*alpha+d1<=c1*x1+c2*x2+...<=c*alpha+d2 format + for (GEQ_Iterator gei((*i).second.single_conjunct()); gei; gei++) + if ((*gei).has_wildcards()) { + coef_t c; + Variable_ID v; + { + Constr_Vars_Iter cvi(*gei, true); + v = cvi.curr_var(); + c = cvi.curr_coef(); + if (c < 0 || nonsingle_wild.find(v) != nonsingle_wild.end()) + continue; + } + + coef_t lb = posInfinity; + for (GEQ_Iterator gei2((*i).second.single_conjunct()); gei2; gei2++) { + if (!(*gei2 == *gei) && (*gei2).get_coef(v) != 0) { + if (lb != posInfinity) { + nonsingle_wild.insert(v); + break; + } + + bool match = true; + for (Constr_Vars_Iter cvi2(*gei); cvi2; cvi2++) + if (cvi2.curr_coef() != -((*gei2).get_coef(cvi2.curr_var()))) { + match = false; + break; + } + if (match) + for (Constr_Vars_Iter cvi2(*gei2); cvi2; cvi2++) + if (cvi2.curr_coef() != -((*gei).get_coef(cvi2.curr_var()))) { + match = false; + break; + } + if (!match) { + nonsingle_wild.insert(v); + break; + } + + lb = -(*gei2).get_const(); + } + } + + if (nonsingle_wild.find(v) != nonsingle_wild.end()) + continue; + + Relation stride_cond = Relation::True((*i).second); + F_Exists *f_exists = stride_cond.and_with_and()->add_exists(); + Variable_ID e = f_exists->declare(); + F_And *f_root = f_exists->add_and(); + GEQ_Handle h1 = f_root->add_GEQ(); + GEQ_Handle h2 = f_root->add_GEQ(); + for (Constr_Vars_Iter cvi2(*gei); cvi2; cvi2++) { + Variable_ID v = cvi2.curr_var(); + switch (v->kind()) { + case Wildcard_Var: + h1.update_coef(e, cvi2.curr_coef()); + h2.update_coef(e, -cvi2.curr_coef()); + break; + case Global_Var: { + Global_Var_ID g = v->get_global_var(); + Variable_ID v2; + if (g->arity() == 0) + v2 = stride_cond.get_local(g); + else + v2 = stride_cond.get_local(g, v->function_of()); + h1.update_coef(v2, cvi2.curr_coef()); + h2.update_coef(v2, -cvi2.curr_coef()); + break; + } + default: + h1.update_coef(v, cvi2.curr_coef()); + h2.update_coef(v, -cvi2.curr_coef()); + } + } + h1.update_const((*gei).get_const()); + h2.update_const(-lb); + + stride_cond.simplify(); + Relation other_cond = Gist(copy((*i).second), copy(stride_cond)); + + // find regions with potential mergeable stride condition with this one + std::list<Interval> intervals; + intervals.push_back(Interval(i, lb, (*gei).get_const())); + + for (std::list<std::list<std::pair<Relation, Relation> >::iterator>::iterator j = s.begin(); j != s.end(); j++) + if (Must_Be_Subset(copy((**j).second), copy(other_cond))) { + Relation stride_cond2 = Gist(copy((**j).second), copy(other_cond)); + + // interval can be removed + if (stride_cond2.is_obvious_tautology()) { + intervals.push_back(Interval(*j, 0, c-1)); + continue; + } + + stride_cond2 = EQs_to_GEQs(stride_cond2, false); + coef_t lb, ub; + GEQ_Iterator gei2(stride_cond2.single_conjunct()); + coef_t sign = 0; + for (Constr_Vars_Iter cvi(*gei2, true); cvi; cvi++) + if (sign != 0) { + sign = 0; + break; + } + else if (cvi.curr_coef() == c) + sign = 1; + else if (cvi.curr_coef() == -c) + sign = -1; + else { + sign = 0; + break; + } + if (sign == 0) + continue; + + bool match = true; + for (Constr_Vars_Iter cvi(*gei2); cvi; cvi++) { + Variable_ID v = cvi.curr_var(); + if (v->kind() == Wildcard_Var) + continue; + else if (v->kind() == Global_Var) { + Global_Var_ID g = v->get_global_var(); + if (g->arity() == 0) + v = (*i).second.get_local(g); + else + v = (*i).second.get_local(g, v->function_of()); + } + + if (cvi.curr_coef() != sign * (*gei).get_coef(v)) { + match = false; + break; + } + } + if (!match) + continue; + + for (Constr_Vars_Iter cvi(*gei); cvi; cvi++) { + Variable_ID v = cvi.curr_var(); + if (v->kind() == Wildcard_Var) + continue; + else if (v->kind() == Global_Var) { + Global_Var_ID g = v->get_global_var(); + if (g->arity() == 0) + v = stride_cond2.get_local(g); + else + v = stride_cond2.get_local(g, v->function_of()); + } + + if (cvi.curr_coef() != sign * (*gei2).get_coef(v)) { + match = false; + break; + } + } + if (!match) + continue; + if (sign > 0) + ub = (*gei2).get_const(); + else + lb = -(*gei2).get_const(); + + gei2++; + if (!gei2) + continue; + + coef_t sign2 = 0; + for (Constr_Vars_Iter cvi(*gei2, true); cvi; cvi++) + if (sign2 != 0) { + sign2 = 0; + break; + } + else if (cvi.curr_coef() == c) + sign2 = 1; + else if (cvi.curr_coef() == -c) + sign2 = -1; + else { + sign2 = 0; + break; + } + if (sign2 != -sign) + continue; + + for (Constr_Vars_Iter cvi(*gei2); cvi; cvi++) { + Variable_ID v = cvi.curr_var(); + if (v->kind() == Wildcard_Var) + continue; + else if (v->kind() == Global_Var) { + Global_Var_ID g = v->get_global_var(); + if (g->arity() == 0) + v = (*i).second.get_local(g); + else + v = (*i).second.get_local(g, v->function_of()); + } + + if (cvi.curr_coef() != sign2 * (*gei).get_coef(v)) { + match = false; + break; + } + } + if (!match) + continue; + + for (Constr_Vars_Iter cvi(*gei); cvi; cvi++) { + Variable_ID v = cvi.curr_var(); + if (v->kind() == Wildcard_Var) + continue; + else if (v->kind() == Global_Var) { + Global_Var_ID g = v->get_global_var(); + if (g->arity() == 0) + v = stride_cond2.get_local(g); + else + v = stride_cond2.get_local(g, v->function_of()); + } + + if (cvi.curr_coef() != sign2 * (*gei2).get_coef(v)) { + match = false; + break; + } + } + if (!match) + continue; + if (sign2 > 0) + ub = (*gei2).get_const(); + else + lb = -(*gei2).get_const(); + + gei2++; + if (gei2) + continue; + + intervals.push_back(Interval(*j, lb, ub)); + } + + merge_intervals(intervals, c, Rs, i); + + // make current region the last one being updated + bool invalid = false; + for (std::list<Interval>::iterator ii = intervals.begin(); ii != intervals.end(); ii++) + if ((*ii).change && (*ii).pos == i) { + invalid = true; + intervals.push_back(*ii); + intervals.erase(ii); + break; + } + + // update hole condition for each region + for (std::list<Interval>::iterator ii = intervals.begin(); ii != intervals.end(); ii++) + if ((*ii).change) { + change = true; + + if ((*ii).ub - (*ii).lb + 1 >= (*ii).modulo) + (*(*ii).pos).second = copy(other_cond); + else { + Relation stride_cond = Relation::True((*i).second); + F_Exists *f_exists = stride_cond.and_with_and()->add_exists(); + Variable_ID e = f_exists->declare(); + F_And *f_root = f_exists->add_and(); + GEQ_Handle h1 = f_root->add_GEQ(); + GEQ_Handle h2 = f_root->add_GEQ(); + for (Constr_Vars_Iter cvi2(*gei); cvi2; cvi2++) { + Variable_ID v = cvi2.curr_var(); + switch (v->kind()) { + case Wildcard_Var: + h1.update_coef(e, (*ii).modulo); + h2.update_coef(e, -(*ii).modulo); + break; + case Global_Var: { + Global_Var_ID g = v->get_global_var(); + Variable_ID v2; + if (g->arity() == 0) + v2 = stride_cond.get_local(g); + else + v2 = stride_cond.get_local(g, v->function_of()); + h1.update_coef(v2, cvi2.curr_coef()); + h2.update_coef(v2, -cvi2.curr_coef()); + break; + } + default: + h1.update_coef(v, cvi2.curr_coef()); + h2.update_coef(v, -cvi2.curr_coef()); + } + } + h1.update_const((*ii).ub); + h2.update_const(-(*ii).lb); + + (*(*ii).pos).second = Intersection(copy(other_cond), stride_cond); + (*(*ii).pos).second.simplify(); + } + } + + if (invalid) + break; + } + } + i++; + } + } + } + catch (const presburger_error &e) { + throw e; + } + + Relation R2 = Relation::False(l_R); + for (std::list<std::pair<Relation, Relation> >::iterator i = Rs.begin(); i != Rs.end(); i++) + R2 = Union(R2, Intersection((*i).first, (*i).second)); + R2.simplify(0, 1); + + return R2; +} + +// +// Use gist and value range to calculate a quick rectangular hull. It +// intends to replace all hull calculations (QuickHull, BetterHull, +// FastTightHull) beyond the method of ConvexHull (dual +// representations). In the future, it will support max(...)-like +// upper bound. So RectHull complements ConvexHull in two ways: first +// for relations that ConvexHull gets too complicated, second for +// relations where different conjuncts have different symbolic upper +// bounds. +// +Relation RectHull(NOT_CONST Relation &Rel) { + Relation R = Approximate(consume_and_regurgitate(Rel)); + if (!R.is_upper_bound_satisfiable()) + return R; + if (R.has_single_conjunct()) + return R; + + std::vector<std::string> input_names(R.n_inp()); + for (int i = 1; i <= R.n_inp(); i++) + input_names[i-1] = R.input_var(i)->name(); + std::vector<std::string> output_names(R.n_out()); + for (int i = 1; i <= R.n_out(); i++) + output_names[i-1] = R.output_var(i)->name(); + + DNF_Iterator c(R.query_DNF()); + Relation r = Relation(R, c.curr()); + c++; + std::vector<std::pair<coef_t, coef_t> > bounds1(R.n_inp()); + std::vector<std::pair<coef_t, coef_t> > bounds2(R.n_out()); + { + Relation t = Project_Sym(copy(r)); + t.simplify(); + for (int i = 1; i <= R.n_inp(); i++) { + Tuple<Variable_ID> v; + for (int j = 1; j <= R.n_inp(); j++) + if (j != i) + v.append(r.input_var(j)); + for (int j = 1; j <= R.n_out(); j++) + v.append(r.output_var(j)); + Relation t2 = Project(copy(t), v); + t2.query_variable_bounds(t2.input_var(i), bounds1[i-1].first, bounds1[i-1].second); + } + for (int i = 1; i <= R.n_out(); i++) { + Tuple<Variable_ID> v; + for (int j = 1; j <= R.n_out(); j++) + if (j != i) + v.append(r.output_var(j)); + for (int j = 1; j <= R.n_inp(); j++) + v.append(r.input_var(j)); + Relation t2 = Project(copy(t), v); + t2.query_variable_bounds(t2.output_var(i), bounds2[i-1].first, bounds2[i-1].second); + } + } + + while (c.live()) { + Relation r2 = Relation(R, c.curr()); + c++; + Relation x = Gist(copy(r), Gist(copy(r), copy(r2), 1), 1); + if (Difference(copy(r2), copy(x)).is_upper_bound_satisfiable()) + x = Relation::True(R); + Relation y = Gist(copy(r2), Gist(copy(r2), copy(r), 1), 1); + if (Difference(copy(r), copy(y)).is_upper_bound_satisfiable()) + y = Relation::True(R); + r = Intersection(x, y); + + { + Relation t = Project_Sym(copy(r2)); + t.simplify(); + for (int i = 1; i <= R.n_inp(); i++) { + Tuple<Variable_ID> v; + for (int j = 1; j <= R.n_inp(); j++) + if (j != i) + v.append(r2.input_var(j)); + for (int j = 1; j <= R.n_out(); j++) + v.append(r2.output_var(j)); + Relation t2 = Project(copy(t), v); + coef_t lbound, ubound; + t2.query_variable_bounds(t2.input_var(i), lbound, ubound); + bounds1[i-1].first = min(bounds1[i-1].first, lbound); + bounds1[i-1].second = max(bounds1[i-1].second, ubound); + } + for (int i = 1; i <= R.n_out(); i++) { + Tuple<Variable_ID> v; + for (int j = 1; j <= R.n_out(); j++) + if (j != i) + v.append(r2.output_var(j)); + for (int j = 1; j <= R.n_inp(); j++) + v.append(r2.input_var(j)); + Relation t2 = Project(copy(t), v); + coef_t lbound, ubound; + t2.query_variable_bounds(t2.output_var(i), lbound, ubound); + bounds2[i-1].first = min(bounds2[i-1].first, lbound); + bounds2[i-1].second = max(bounds2[i-1].second, ubound); + } + } + + Relation r3(R.n_inp(), R.n_out()); + F_And *f_root = r3.add_and(); + for (int i = 1; i <= R.n_inp(); i++) { + if (bounds1[i-1].first != -posInfinity) { + GEQ_Handle h = f_root->add_GEQ(); + h.update_coef(r3.input_var(i), 1); + h.update_const(-bounds1[i-1].first); + } + if (bounds1[i-1].second != posInfinity) { + GEQ_Handle h = f_root->add_GEQ(); + h.update_coef(r3.input_var(i), -1); + h.update_const(bounds1[i-1].second); + } + } + for (int i = 1; i <= R.n_out(); i++) { + if (bounds2[i-1].first != -posInfinity) { + GEQ_Handle h = f_root->add_GEQ(); + h.update_coef(r3.output_var(i), 1); + h.update_const(-bounds2[i-1].first); + } + if (bounds2[i-1].second != posInfinity) { + GEQ_Handle h = f_root->add_GEQ(); + h.update_coef(r3.output_var(i), -1); + h.update_const(bounds2[i-1].second); + } + } + r = Intersection(r, r3); + r.simplify(); + } + + for (int i = 1; i <= r.n_inp(); i++) + r.name_input_var(i, input_names[i-1]); + for (int i = 1; i <= r.n_out(); i++) + r.name_output_var(i, output_names[i-1]); + r.setup_names(); + return r; +} + +} + diff --git a/omegalib/omega/src/hull_simple.cc b/omegalib/omega/src/hull_simple.cc new file mode 100755 index 0000000..62dcb26 --- /dev/null +++ b/omegalib/omega/src/hull_simple.cc @@ -0,0 +1,1013 @@ +/***************************************************************************** + Copyright (C) 2011 Chun Chen + All Rights Reserved. + + Purpose: + Hull approximation including lattice and irregular constraints that + involves wildcards. + + Notes: + + History: + 03/12/11 Created by Chun Chen + *****************************************************************************/ + +#include <assert.h> +#include <omega.h> +#include <basic/BoolSet.h> +#include <vector> +#include <list> +#include <set> +#include <string> +#include <algorithm> + +namespace omega { + +Relation SimpleHull(const Relation &R, bool allow_stride_constraint, + bool allow_irregular_constraint) { + std::vector<Relation> Rs; + Rs.push_back(R); + return SimpleHull(Rs, allow_stride_constraint, allow_irregular_constraint); +} + +Relation SimpleHull(const std::vector<Relation> &Rs, + bool allow_stride_constraint, bool allow_irregular_constraint) { + // check for sanity of parameters + if (Rs.size() == 0) + return Relation::False(0); + int num_dim = -1; + int first_non_null; + for (int i = 0; i < Rs.size(); i++) { + if (Rs[i].is_null()) + continue; + + if (num_dim == -1) { + num_dim = Rs[i].n_inp(); + first_non_null = i; + } + + if (Rs[i].n_inp() != num_dim) + throw std::invalid_argument( + "relations for hull must have same dimension"); + if (Rs[i].n_out() != 0) + throw std::invalid_argument( + "hull calculation must be set relation"); + } + + // convert to a list of relations each with a single conjunct + std::vector<Relation> l_Rs; + for (int i = 0; i < Rs.size(); i++) { + if (Rs[i].is_null()) + continue; + + Relation r = copy(Rs[i]); + + //r.simplify(2, 4); + r.simplify(); + DNF_Iterator c(r.query_DNF()); + int top = l_Rs.size(); + while (c.live()) { + Relation r2 = Relation(r, c.curr()); + + // quick elimination of redundant conjuncts + bool already_included = false; + for (int j = 0; j < top; j++) + if (Must_Be_Subset(copy(r2), copy(l_Rs[j]))) { + already_included = true; + break; + } else if (Must_Be_Subset(copy(l_Rs[j]), copy(r2))) { + l_Rs.erase(l_Rs.begin() + j); + top--; + break; + } + + if (!already_included) + l_Rs.push_back(r2); + c++; + } + } + + // shortcut for simple case + if (l_Rs.size() == 0) { + if (num_dim == -1) + return Relation::False(0); + else { + Relation r = Relation::False(num_dim); + r.copy_names(Rs[first_non_null]); + r.setup_names(); + return r; + } + } else if (l_Rs.size() == 1) { + if (allow_stride_constraint && allow_irregular_constraint) { + l_Rs[0].copy_names(Rs[first_non_null]); + l_Rs[0].setup_names(); + return l_Rs[0]; + } else if (!allow_stride_constraint && !allow_irregular_constraint) { + l_Rs[0] = Approximate(l_Rs[0]); + l_Rs[0].copy_names(Rs[first_non_null]); + l_Rs[0].setup_names(); + return l_Rs[0]; + } + } + + Relation hull = Relation::True(num_dim); + + // lattice union approximation + if (allow_stride_constraint) { + std::vector<std::vector<std::pair<EQ_Handle, BoolSet<> > > > strides( + l_Rs.size()); + for (int i = 0; i < l_Rs.size(); i++) + for (EQ_Iterator e = l_Rs[i].single_conjunct()->EQs(); e; e++) + if ((*e).has_wildcards()) { + int num_wildcard = 0; + BoolSet<> affected(num_dim); + for (Constr_Vars_Iter cvi(*e); cvi; cvi++) { + if (cvi.curr_var()->kind() == Wildcard_Var) + num_wildcard++; + else if (cvi.curr_var()->kind() == Input_Var) + affected.set(cvi.curr_var()->get_position() - 1); + } + if (num_wildcard == 1) + strides[i].push_back(std::make_pair(*e, affected)); + } + + for (int i = 0; i < strides[0].size(); i++) { + coef_t c = + abs( + strides[0][i].first.get_coef( + Constr_Vars_Iter(strides[0][i].first, true).curr_var())); + coef_t old_c = c; + bool is_stride = true; + for (int j = 1; j < l_Rs.size(); j++) { + std::list<coef_t> candidates; + for (int k = 0; k < strides[j].size(); k++) + if (!(strides[0][i].second & strides[j][k].second).empty()) { + coef_t t = gcd(c, + abs( + strides[j][k].first.get_coef( + Constr_Vars_Iter( + strides[j][k].first, + true).curr_var()))); + if (t != 1) { + std::list<coef_t>::iterator p = candidates.begin(); + while (p != candidates.end() && *p > t) + ++p; + if (p == candidates.end() || *p != t) + candidates.insert(p, t); + + t = gcd(t, abs(strides[0][i].first.get_const())); + t = gcd(t, abs(strides[j][k].first.get_const())); + if (t != 1) { + std::list<coef_t>::iterator p = + candidates.begin(); + while (p != candidates.end() && *p > t) + ++p; + if (p == candidates.end() || *p != t) + candidates.insert(p, t); + } + } + } + + bool found_matched_stride = false; + for (std::list<coef_t>::iterator k = candidates.begin(); + k != candidates.end(); k++) { + Relation r = Relation::True(num_dim); + EQ_Handle h = r.and_with_EQ(strides[0][i].first); + h.update_coef(Constr_Vars_Iter(h, true).curr_var(), + -old_c + *k); + r.simplify(); + if (Must_Be_Subset(copy(l_Rs[j]), copy(r))) { + c = *k; + found_matched_stride = true; + break; + } + } + + if (!found_matched_stride) { + is_stride = false; + break; + } + } + + if (is_stride) { + Relation r = Relation::True(num_dim); + EQ_Handle h = r.and_with_EQ(strides[0][i].first); + h.update_coef(Constr_Vars_Iter(h, true).curr_var(), -old_c + c); + r.simplify(); + hull = Intersection(hull, r); + } + } + } + + // consider some special wildcard constraints + if (allow_irregular_constraint) { + std::vector< + std::vector< + std::pair<Variable_ID, std::map<Variable_ID, coef_t> > > > ranges( + l_Rs.size()); + for (int i = 0; i < l_Rs.size(); i++) { + std::vector<std::pair<GEQ_Handle, std::map<Variable_ID, coef_t> > > geqs_ub; + std::vector<std::pair<GEQ_Handle, std::map<Variable_ID, coef_t> > > geqs_lb; + for (GEQ_Iterator e = l_Rs[i].single_conjunct()->GEQs(); e; e++) + if ((*e).has_wildcards()) { + int num_wildcard = 0; + std::map<Variable_ID, coef_t> formula; + int direction; + for (Constr_Vars_Iter cvi(*e); cvi; cvi++) { + Variable_ID v = cvi.curr_var(); + switch (v->kind()) { + case Wildcard_Var: + num_wildcard++; + if (cvi.curr_coef() > 0) + direction = true; + else + direction = false; + break; + case Input_Var: + case Global_Var: + formula[cvi.curr_var()] = cvi.curr_coef(); + break; + default: + assert(false); + } + } + if (num_wildcard == 1) { + if (direction) { + for (std::map<Variable_ID, coef_t>::iterator j = + formula.begin(); j != formula.end(); j++) + j->second = -j->second; + geqs_ub.push_back(std::make_pair(*e, formula)); + } else + geqs_lb.push_back(std::make_pair(*e, formula)); + } + } + for (int j = 0; j < geqs_lb.size(); j++) { + Variable_ID v = + Constr_Vars_Iter(geqs_lb[j].first, true).curr_var(); + for (int k = 0; k < geqs_ub.size(); k++) + if (v == Constr_Vars_Iter(geqs_ub[k].first, true).curr_var() + && geqs_lb[j].second == geqs_ub[k].second) + ranges[i].push_back( + std::make_pair(v, geqs_lb[j].second)); + } + } + + // find compatible wildcard match + // TODO: evaluate to find the best match, also avoid mapping two wildcards + // in a single conjunct to one variable (incorrect) + std::vector<std::vector<int> > all_match; + for (int i = 0; i < ranges[0].size(); i++) { + std::vector<int> match(l_Rs.size(), -1); + match[0] = i; + for (int j = 1; j < l_Rs.size(); j++) { + for (int k = 0; k < ranges[j].size(); k++) + if (ranges[0][i].second == ranges[j][k].second) { + match[j] = k; + break; + } + if (match[j] == -1) + break; + } + if (match[l_Rs.size() - 1] != -1) + all_match.push_back(match); + } + + // map compatible wildcards to input variables + std::vector<Relation> ll_Rs(l_Rs.size()); + for (int i = 0; i < l_Rs.size(); i++) { + Relation r(num_dim + all_match.size()); + F_Exists *f_exists = r.add_and()->add_exists(); + F_And *f_root = f_exists->add_and(); + std::map<Variable_ID, Variable_ID> wc_map; + for (int j = 0; j < all_match.size(); j++) + wc_map[ranges[i][all_match[j][i]].first] = r.set_var(j + 1); + + for (EQ_Iterator e(l_Rs[i].single_conjunct()->EQs()); e; e++) + if (!(*e).has_wildcards()) { + EQ_Handle h = f_root->add_EQ(); + for (Constr_Vars_Iter cvi(*e); cvi; cvi++) + switch (cvi.curr_var()->kind()) { + case Input_Var: { + h.update_coef( + r.set_var( + cvi.curr_var()->get_position() + + all_match.size()), + cvi.curr_coef()); + break; + } + case Global_Var: { + Global_Var_ID g = cvi.curr_var()->get_global_var(); + Variable_ID v; + if (g->arity() == 0) + v = r.get_local(g); + else + v = r.get_local(g, + cvi.curr_var()->function_of()); + h.update_coef(v, cvi.curr_coef()); + break; + } + default: + assert(false); + } + h.update_const((*e).get_const()); + } + + for (GEQ_Iterator e(l_Rs[i].single_conjunct()->GEQs()); e; e++) { + GEQ_Handle h = f_root->add_GEQ(); + for (Constr_Vars_Iter cvi(*e); cvi; cvi++) + switch (cvi.curr_var()->kind()) { + case Input_Var: { + h.update_coef( + r.set_var( + cvi.curr_var()->get_position() + + all_match.size()), + cvi.curr_coef()); + break; + } + case Global_Var: { + Global_Var_ID g = cvi.curr_var()->get_global_var(); + Variable_ID v; + if (g->arity() == 0) + v = r.get_local(g); + else + v = r.get_local(g, cvi.curr_var()->function_of()); + h.update_coef(v, cvi.curr_coef()); + break; + } + case Wildcard_Var: { + std::map<Variable_ID, Variable_ID>::iterator p = + wc_map.find(cvi.curr_var()); + Variable_ID v; + if (p == wc_map.end()) { + v = f_exists->declare(); + wc_map[cvi.curr_var()] = v; + } else + v = p->second; + h.update_coef(v, cvi.curr_coef()); + break; + } + default: + assert(false); + } + h.update_const((*e).get_const()); + } + + r.simplify(); + ll_Rs[i] = r; + } + + // now use SimpleHull on regular bounds only + Relation result = SimpleHull(ll_Rs, false, false); + + // convert imaginary input variables back to wildcards + Relation mapping(num_dim + all_match.size(), num_dim); + F_And *f_root = mapping.add_and(); + for (int i = 0; i < num_dim; i++) { + EQ_Handle h = f_root->add_EQ(); + h.update_coef(mapping.input_var(all_match.size() + i + 1), 1); + h.update_coef(mapping.output_var(i + 1), -1); + } + result = Range(Restrict_Domain(mapping, result)); + + hull = Intersection(hull, result); + hull.simplify(); + hull.copy_names(Rs[first_non_null]); + hull.setup_names(); + return hull; + } + + // check regular bounds + if (l_Rs.size() == 1) { + hull = Intersection(hull, Approximate(copy(l_Rs[0]))); + } else { + for (int i = 0; i < l_Rs.size(); i++) { + l_Rs[i] = Approximate(l_Rs[i]); + l_Rs[i].simplify(2, 4); + } + + // check global variables + // TODO: global variable function_of() is not considered for now + std::map<Global_Var_ID, int> globals; + for (int i = 0; i < l_Rs.size(); i++) + for (Constraint_Iterator e( + l_Rs[i].single_conjunct()->constraints()); e; e++) + for (Constr_Vars_Iter cvi(*e); cvi; cvi++) + if (cvi.curr_var()->kind() == Global_Var) + globals[cvi.curr_var()->get_global_var()] = -1; + + if (globals.size() > 0) { + int count = 1; + for (std::map<Global_Var_ID, int>::iterator i = globals.begin(); + i != globals.end(); i++) + i->second = count++; + + std::vector<Relation> ll_Rs(l_Rs.size()); + for (int i = 0; i < l_Rs.size(); i++) { + Relation r(num_dim + globals.size()); + F_And *f_root = r.add_and(); + for (EQ_Iterator e(l_Rs[i].single_conjunct()->EQs()); e; e++) { + EQ_Handle h = f_root->add_EQ(); + for (Constr_Vars_Iter cvi(*e); cvi; cvi++) + switch (cvi.curr_var()->kind()) { + case Input_Var: { + h.update_coef( + r.set_var( + cvi.curr_var()->get_position() + + globals.size()), + cvi.curr_coef()); + break; + } + case Global_Var: { + h.update_coef( + r.set_var( + globals[cvi.curr_var()->get_global_var()]), + cvi.curr_coef()); + break; + } + default: + assert(false); + } + h.update_const((*e).get_const()); + } + for (GEQ_Iterator e(l_Rs[i].single_conjunct()->GEQs()); e; + e++) { + GEQ_Handle h = f_root->add_GEQ(); + for (Constr_Vars_Iter cvi(*e); cvi; cvi++) + switch (cvi.curr_var()->kind()) { + case Input_Var: { + h.update_coef( + r.set_var( + cvi.curr_var()->get_position() + + globals.size()), + cvi.curr_coef()); + break; + } + case Global_Var: { + h.update_coef( + r.set_var( + globals[cvi.curr_var()->get_global_var()]), + cvi.curr_coef()); + break; + } + default: + assert(false); + } + h.update_const((*e).get_const()); + } + + ll_Rs[i] = r; + } + + Relation result = SimpleHull(ll_Rs, false, false); + + std::map<int, Global_Var_ID> globals_reverse; + for (std::map<Global_Var_ID, int>::iterator i = globals.begin(); + i != globals.end(); i++) + globals_reverse[i->second] = i->first; + + Relation r(num_dim); + F_And *f_root = r.add_and(); + for (EQ_Iterator e(result.single_conjunct()->EQs()); e; e++) { + EQ_Handle h = f_root->add_EQ(); + for (Constr_Vars_Iter cvi(*e); cvi; cvi++) + switch (cvi.curr_var()->kind()) { + case Input_Var: { + int pos = cvi.curr_var()->get_position(); + if (pos > globals_reverse.size()) + h.update_coef( + r.set_var(pos - globals_reverse.size()), + cvi.curr_coef()); + else { + Global_Var_ID g = globals_reverse[pos]; + h.update_coef(r.get_local(g), cvi.curr_coef()); + } + break; + } + default: + assert(false); + } + h.update_const((*e).get_const()); + } + for (GEQ_Iterator e(result.single_conjunct()->GEQs()); e; e++) { + GEQ_Handle h = f_root->add_GEQ(); + for (Constr_Vars_Iter cvi(*e); cvi; cvi++) + switch (cvi.curr_var()->kind()) { + case Input_Var: { + int pos = cvi.curr_var()->get_position(); + if (pos > globals_reverse.size()) + h.update_coef( + r.set_var(pos - globals_reverse.size()), + cvi.curr_coef()); + else { + Global_Var_ID g = globals_reverse[pos]; + h.update_coef(r.get_local(g), cvi.curr_coef()); + } + break; + } + default: + assert(false); + } + h.update_const((*e).get_const()); + } + + hull = Intersection(hull, r); + hull.simplify(); + hull.copy_names(Rs[first_non_null]); + hull.setup_names(); + return hull; + } else { + std::vector<std::vector<Relation> > projected(num_dim + 1, + std::vector<Relation>(l_Rs.size())); + for (int i = 0; i < l_Rs.size(); i++) { + projected[num_dim][i] = copy(l_Rs[i]); + for (int j = num_dim - 1; j >= 0; j--) { + projected[j][i] = Project(copy(projected[j + 1][i]), + projected[j + 1][i].input_var(j + 1)); + projected[j][i].simplify(2, 4); + } + } + + std::vector<bool> has_lb(num_dim, false); + std::vector<bool> has_ub(num_dim, false); + for (int i = 0; i < num_dim; i++) { + bool skip_lb = false; + bool skip_ub = false; + std::vector<Relation> bound(l_Rs.size()); + for (int j = 0; j < l_Rs.size(); j++) { + bound[j] = Gist(copy(projected[i + 1][j]), + copy(projected[i][j]), 1); + bound[j] = Approximate(bound[j]); + bound[j] = EQs_to_GEQs(bound[j]); + + bool has_lb_not_in_hull = false; + bool has_ub_not_in_hull = false; + for (GEQ_Iterator e = bound[j].single_conjunct()->GEQs(); e; + e++) { + coef_t coef = (*e).get_coef(bound[j].input_var(i + 1)); + if (!skip_lb && coef > 0) { + Relation r = Relation::True(bound[j].n_inp()); + r.and_with_GEQ(*e); + r.simplify(); + + if (j != 0 && l_Rs.size() > 2 + && Must_Be_Subset(copy(hull), copy(r))) + continue; + + bool belong_to_hull = true; + for (int k = 0; k < l_Rs.size(); k++) + if (k != j + && !Must_Be_Subset(copy(l_Rs[k]), + copy(r))) { + belong_to_hull = false; + break; + } + if (belong_to_hull) { + hull.and_with_GEQ(*e); + has_lb[i] = true; + } else + has_lb_not_in_hull = true; + } else if (!skip_ub && coef < 0) { + Relation r = Relation::True(bound[j].n_inp()); + r.and_with_GEQ(*e); + r.simplify(); + + if (j != 0 && l_Rs.size() > 2 + && Must_Be_Subset(copy(hull), copy(r))) + continue; + + bool belong_to_hull = true; + for (int k = 0; k < l_Rs.size(); k++) + if (k != j + && !Must_Be_Subset(copy(l_Rs[k]), + copy(r))) { + belong_to_hull = false; + break; + } + if (belong_to_hull) { + hull.and_with_GEQ(*e); + has_ub[i] = true; + } else + has_ub_not_in_hull = true; + } + } + + if (!has_lb_not_in_hull) + skip_lb = true; + if (!has_ub_not_in_hull) + skip_ub = true; + if (skip_lb && skip_ub) + break; + } + + // no ready lower bound, approximate it + bool got_rect_lb = false; + if (!skip_lb) { + for (int j = 0; j < l_Rs.size(); j++) { + std::set<BoolSet<> > S; + for (GEQ_Iterator e = + bound[j].single_conjunct()->GEQs(); e; e++) { + coef_t coef = (*e).get_coef( + bound[j].input_var(i + 1)); + if (coef > 0) { + BoolSet<> s(i); + for (Constr_Vars_Iter cvi(*e); cvi; cvi++) + if ((*cvi).var->kind() == Input_Var + && (*cvi).var->get_position() - 1 + != i) { + if (((*cvi).coef > 0 + && has_ub[(*cvi).var->get_position() + - 1]) + || ((*cvi).coef < 0 + && has_lb[(*cvi).var->get_position() + - 1])) + s.set( + (*cvi).var->get_position() + - 1); + else { + for (GEQ_Iterator e2 = + bound[j].single_conjunct()->GEQs(); + e2; e2++) + if (e2 != e + && (((*cvi).coef > 0 + && (*e2).get_coef( + (*cvi).var) + < 0) + || ((*cvi).coef + < 0 + && (*e2).get_coef( + (*cvi).var) + > 0))) { + s.set( + (*cvi).var->get_position() + - 1); + break; + } + } + } + + if (s.num_elem() > 0) + S.insert(s); + } + } + + if (S.size() > 0) { + BoolSet<> s(i); + for (std::set<BoolSet<> >::iterator k = S.begin(); + k != S.end(); k++) + s |= *k; + for (int k = 0; k < i; k++) + if (s.get(k)) { + BoolSet<> t(i); + t.set(k); + S.insert(t); + } + + for (std::set<BoolSet<> >::iterator k = S.begin(); + k != S.end(); k++) { + + bool do_again = false; + std::set<int> vars; + int round_trip = 0; + do { + Relation r = copy(projected[i + 1][j]); + + if (!do_again) { + for (int kk = 0; kk < i; kk++) + if ((*k).get(kk)) { + r = Project(r, + r.input_var(kk + 1)); + vars.insert(kk + 1); + } + } else { + for (std::set<int>::iterator vars_it = + vars.begin(); + vars_it != vars.end(); + vars_it++) + if (*vars_it < i + 1) + r = Project(r, + r.input_var(*vars_it)); + } + + r.simplify(2, 4); + Relation r2 = Project(copy(r), + r.input_var(i + 1)); + Relation b = Gist(copy(r), copy(r2), 1); + // Relation c = Project(copy(r),r.input_var(4) ); + + // c.simplify(2,4); + // Relation d = Project(copy(c), r.input_var(i+1)); + // Relation e = Gist(copy(c), copy(d), 1); + + b = Approximate(b); + b = EQs_to_GEQs(b); + + for (GEQ_Iterator e = + b.single_conjunct()->GEQs(); e; + e++) { + coef_t coef = (*e).get_coef( + b.input_var(i + 1)); + if (coef > 0) { + Relation r = Relation::True( + b.n_inp()); + r.and_with_GEQ(*e); + r.simplify(); + + if (Must_Be_Subset(copy(hull), + copy(r))) + continue; + + bool belong_to_hull = true; + for (int k = 0; k < l_Rs.size(); + k++) + if (k != j + && !Must_Be_Subset( + copy(l_Rs[k]), + copy(r))) { + belong_to_hull = false; + break; + } + if (belong_to_hull) { + hull.and_with_GEQ(*e); + got_rect_lb = true; + } + } + } + do_again = false; + if (!got_rect_lb) { + bool found = false; + for (GEQ_Iterator e = + b.single_conjunct()->GEQs(); e; + e++) { + coef_t coef = (*e).get_coef( + b.input_var(i + 1)); + + if (coef > 0) { + for (Constr_Vars_Iter cvi(*e); + cvi; cvi++) + if ((*cvi).var->kind() + == Input_Var + && (*cvi).var->get_position() + - 1 != i) { + + if (((*cvi).coef > 0 + && has_ub[(*cvi).var->get_position() + - 1]) + || ((*cvi).coef + < 0 + && has_lb[(*cvi).var->get_position() + - 1])) { + vars.insert( + (*cvi).var->get_position()); + found = true; + } else { + for (GEQ_Iterator e2 = + b.single_conjunct()->GEQs(); + e2; e2++) + if (e2 != e + && (((*cvi).coef + > 0 + && (*e2).get_coef( + (*cvi).var) + < 0) + || ((*cvi).coef + < 0 + && (*e2).get_coef( + (*cvi).var) + > 0))) { + vars.insert( + (*cvi).var->get_position()); + found = + true; + break; + } + } + + } + + } + if (found) + break; + + } + if (found && (round_trip < i)) + do_again = true; + + } + round_trip++; + } while (do_again); + } + + if (got_rect_lb) + break; + } + } + } + + // no ready upper bound, approximate it + bool got_rect_ub = false; + if (!skip_ub) { + for (int j = 0; j < l_Rs.size(); j++) { + std::set<BoolSet<> > S; + for (GEQ_Iterator e = + bound[j].single_conjunct()->GEQs(); e; e++) { + coef_t coef = (*e).get_coef( + bound[j].input_var(i + 1)); + if (coef < 0) { + BoolSet<> s(i); + for (Constr_Vars_Iter cvi(*e); cvi; cvi++) + if ((*cvi).var->kind() == Input_Var + && (*cvi).var->get_position() - 1 + != i) { + if (((*cvi).coef > 0 + && has_ub[(*cvi).var->get_position() + - 1]) + || ((*cvi).coef < 0 + && has_lb[(*cvi).var->get_position() + - 1])) + s.set( + (*cvi).var->get_position() + - 1); + else { + for (GEQ_Iterator e2 = + bound[j].single_conjunct()->GEQs(); + e2; e2++) + if (e2 != e + && (((*cvi).coef > 0 + && (*e2).get_coef( + (*cvi).var) + < 0) + || ((*cvi).coef + < 0 + && (*e2).get_coef( + (*cvi).var) + > 0))) { + s.set( + (*cvi).var->get_position() + - 1); + break; + } + } + } + + if (s.num_elem() > 0) + S.insert(s); + } + } + + if (S.size() > 0) { + BoolSet<> s(i); + for (std::set<BoolSet<> >::iterator k = S.begin(); + k != S.end(); k++) + s |= *k; + for (int k = 0; k < i; k++) + if (s.get(k)) { + BoolSet<> t(i); + t.set(k); + S.insert(t); + } + + for (std::set<BoolSet<> >::iterator k = S.begin(); + k != S.end(); k++) { + + bool do_again = false; + std::set<int> vars; + int round_trip = 0; + do { + + Relation r = copy(projected[i + 1][j]); + + if (!do_again) { + for (int kk = 0; kk < i; kk++) + if ((*k).get(kk)) { + r = Project(r, + r.input_var(kk + 1)); + vars.insert(kk + 1); + } + } else { + for (std::set<int>::iterator vars_it = + vars.begin(); + vars_it != vars.end(); + vars_it++) + if (*vars_it < i + 1) + r = Project(r, + r.input_var(*vars_it)); + } + + r.simplify(2, 4); + Relation r2 = Project(copy(r), + r.input_var(i + 1)); + // r2.simplify(2,4); + Relation b = Gist(r, r2, 1); + b = Approximate(b); + b = EQs_to_GEQs(b); + + for (GEQ_Iterator e = + b.single_conjunct()->GEQs(); e; + e++) { + coef_t coef = (*e).get_coef( + b.input_var(i + 1)); + if (coef < 0) { + Relation r = Relation::True( + b.n_inp()); + r.and_with_GEQ(*e); + r.simplify(); + + if (Must_Be_Subset(copy(hull), + copy(r))) + continue; + + bool belong_to_hull = true; + for (int k = 0; k < l_Rs.size(); + k++) + if (k != j + && !Must_Be_Subset( + copy(l_Rs[k]), + copy(r))) { + belong_to_hull = false; + break; + } + if (belong_to_hull) { + hull.and_with_GEQ(*e); + got_rect_ub = true; + } + } + } + do_again = false; + if (!got_rect_ub) { + bool found = false; + for (GEQ_Iterator e = + b.single_conjunct()->GEQs(); e; + e++) { + coef_t coef = (*e).get_coef( + b.input_var(i + 1)); + if (coef < 0) { + for (Constr_Vars_Iter cvi(*e); + cvi; cvi++) + if ((*cvi).var->kind() + == Input_Var + && (*cvi).var->get_position() + - 1 != i) { + + if (((*cvi).coef > 0 + && has_ub[(*cvi).var->get_position() + - 1]) + || ((*cvi).coef + < 0 + && has_lb[(*cvi).var->get_position() + - 1])) { + vars.insert( + (*cvi).var->get_position()); + found = true; + } else { + for (GEQ_Iterator e2 = + b.single_conjunct()->GEQs(); + e2; e2++) + if (e2 != e + && (((*cvi).coef + > 0 + && (*e2).get_coef( + (*cvi).var) + < 0) + || ((*cvi).coef + < 0 + && (*e2).get_coef( + (*cvi).var) + > 0))) { + vars.insert( + (*cvi).var->get_position()); + found = + true; + break; + } + } + + } + } + if (found) + break; + } + if (found && (round_trip < i)) + do_again = true; + + } + round_trip++; + } while (do_again); + } + + if (got_rect_ub) + break; + } + } + } + } + } + } + + hull.simplify(); + hull.copy_names(Rs[first_non_null]); + hull.setup_names(); + return hull; +} + +} + diff --git a/omegalib/omega/src/omega_core/oc.cc b/omegalib/omega/src/omega_core/oc.cc new file mode 100644 index 0000000..0dc9b49 --- /dev/null +++ b/omegalib/omega/src/omega_core/oc.cc @@ -0,0 +1,754 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + All Rights Reserved. + + Purpose: + Simplify a problem. + + Notes: + + History: + 12/10/06 Improved gist function, by Chun Chen. +*****************************************************************************/ + +#include <omega/omega_core/oc_i.h> + +namespace omega { + +eqn SUBs[maxVars+1]; +Memory redMemory[maxVars+1]; + +int Problem::reduceProblem() { + int result; + checkVars(nVars+1); + assert(omegaInitialized); + if (nVars > nEQs + 3 * safeVars) + freeEliminations(safeVars); + + check(); + if (!mayBeRed && nSUBs == 0 && safeVars == 0) { + result = solve(OC_SOLVE_UNKNOWN); + nGEQs = 0; + nEQs = 0; + nSUBs = 0; + nMemories = 0; + if (!result) { + int e = newEQ(); + assert(e == 0); + eqnnzero(&EQs[0], nVars); + EQs[0].color = EQ_BLACK; + EQs[0].coef[0] = 1; + } + check(); + return result; + } + return solve(OC_SOLVE_SIMPLIFY); +} + + +int Problem::simplifyProblem(int verify, int subs, int redundantElimination) { + checkVars(nVars+1); + assert(omegaInitialized); + setInternals(); + check(); + if (!reduceProblem()) goto returnFalse; + if (verify) { + addingOuterEqualities++; + int r = verifyProblem(); + addingOuterEqualities--; + if (!r) goto returnFalse; + if (nEQs) { // found some equality constraints during verification + int numRed = 0; + if (mayBeRed) + for (int e = nGEQs - 1; e >= 0; e--) if (GEQs[e].color == EQ_RED) numRed++; + if (mayBeRed && nVars == safeVars && numRed == 1) + nEQs = 0; // discard them + else if (!reduceProblem()) { + assert(0 && "Added equality constraint to verified problem generates false"); + } + } + } + if (redundantElimination) { + if (redundantElimination > 1) { + if (!expensiveEqualityCheck()) goto returnFalse; + } + if (!quickKill(0)) goto returnFalse; + if (redundantElimination > 1) { + if (!expensiveKill()) goto returnFalse; + } + } + resurrectSubs(); + if (redundantElimination) + simplifyStrideConstraints(); + if (redundantElimination > 2 && safeVars < nVars) { + if (!quickKill(0)) goto returnFalse; + return simplifyProblem(verify, subs, redundantElimination-2); + } + setExternals(); + assert(nMemories == 0); + return (1); + +returnFalse: + nGEQs = 0; + nEQs = 0; + resurrectSubs(); + nGEQs = 0; + nEQs = 0; + int neweq = newEQ(); + assert(neweq == 0); + eqnnzero(&EQs[neweq], nVars); + EQs[neweq].color = EQ_BLACK; + EQs[neweq].coef[0] = 1; + nMemories = 0; + return 0; +} + +int Problem::simplifyApproximate(bool strides_allowed) { + int result; + checkVars(nVars+1); + assert(inApproximateMode == 0); + + inApproximateMode = 1; + inStridesAllowedMode = strides_allowed; + if (TRACE) + fprintf(outputFile, "Entering Approximate Mode [\n"); + + assert(omegaInitialized); + result = simplifyProblem(0,0,0); + + while (result && !strides_allowed && nVars > safeVars) { + int e; + for (e = nGEQs - 1; e >= 0; e--) + if (GEQs[e].coef[nVars]) deleteGEQ(e); + for (e = nEQs - 1; e >= 0; e--) + if (EQs[e].coef[nVars]) deleteEQ(e); + nVars--; + result = simplifyProblem(0,0,0); + } + + if (TRACE) + fprintf(outputFile, "] Leaving Approximate Mode\n"); + + assert(inApproximateMode == 1); + inApproximateMode=0; + inStridesAllowedMode = 0; + + assert(nMemories == 0); + return (result); +} + + + + +/* + * Return 1 if red equations constrain the set of possible + * solutions. We assume that there are solutions to the black + * equations by themselves, so if there is no solution to the combined + * problem, we return 1. + */ + +#ifdef GIST_CHECK +Problem full_answer, context; +Problem redProblem; +#endif + +redCheck Problem::redSimplifyProblem(int effort, int computeGist) { + int result; + int e; + + checkVars(nVars+1); + assert(mayBeRed >= 0); + mayBeRed++; + + assert(omegaInitialized); + if (TRACE) { + fprintf(outputFile, "Checking for red equations:\n"); + printProblem(); + } + setInternals(); + +#ifdef GIST_CHECK + int r1,r2; + if (TRACE) + fprintf(outputFile,"Set-up for gist invariant checking[\n"); + redProblem = *this; + redProblem.check(); + full_answer = *this; + full_answer.check(); + full_answer.turnRedBlack(); + full_answer.check(); + r1 = full_answer.simplifyProblem(1,0,1); + full_answer.check(); + if (DBUG) fprintf(outputFile,"Simplifying context [\n"); + context = *this; + context.check(); + context.deleteRed(); + context.check(); + r2 = context.simplifyProblem(1,0,1); + context.check(); + if (DBUG) fprintf(outputFile,"] Simplifying context\n"); + + if (!r2 && TRACE) fprintf(outputFile, "WARNING: Gist context is false!\n"); + if (TRACE) + fprintf(outputFile,"] Set-up for gist invariant checking done\n"); +#endif + + // Save known integer modular equations, -- by chun 12/10/2006 + eqn ModularEQs[nEQs]; + int nModularEQs = 0; + int old_nVars = nVars; + for (int e = 0; e < nEQs; e++) + if (EQs[e].color != EQ_RED) + for (int i = safeVars+1; i <= nVars; i++) + if (EQs[e].coef[i] != 0) { + eqnncpy(&(ModularEQs[nModularEQs++]), &(EQs[e]), nVars); + break; + } + + + if (solveEQ() == false) { + if (TRACE) + fprintf(outputFile, "Gist is FALSE\n"); + if (computeGist) { + nMemories = 0; + nGEQs = 0; + nEQs = 0; + resurrectSubs(); + nGEQs = 0; + nEQs = 0; + int neweq = newEQ(); + assert(neweq == 0); + eqnnzero(&EQs[neweq], nVars); + EQs[neweq].color = EQ_RED; + EQs[neweq].coef[0] = 1; + } + mayBeRed--; + return redFalse; + } + + if (!computeGist && nMemories) + return redConstraints; + if (normalize() == normalize_false) { + if (TRACE) + fprintf(outputFile, "Gist is FALSE\n"); + if (computeGist) { + nGEQs = 0; + nEQs = 0; + resurrectSubs(); + nMemories = 0; + nGEQs = 0; + nEQs = 0; + int neweq = newEQ(); + assert(neweq == 0); + eqnnzero(&EQs[neweq], nVars); + EQs[neweq].color = EQ_RED; + EQs[neweq].coef[0] = 1; + } + mayBeRed--; + return redFalse; + } + + result = 0; + for (e = nGEQs - 1; e >= 0; e--) if (GEQs[e].color == EQ_RED) result = 1; + for (e = nEQs - 1; e >= 0; e--) if (EQs[e].color == EQ_RED) result = 1; + if (nMemories) result = 1; + if (!result) { + if (computeGist) { + nGEQs = 0; + nEQs = 0; + resurrectSubs(); + nGEQs = 0; + nMemories = 0; + nEQs = 0; + } + mayBeRed--; + return noRed; + } + + result = simplifyProblem(effort?1:0,1,0); +#ifdef GIST_CHECK + if (!r1 && TRACE && result) + fprintf(outputFile, "Gist is False but not detected\n"); +#endif + if (!result) { + if (TRACE) + fprintf(outputFile, "Gist is FALSE\n"); + if (computeGist) { + nGEQs = 0; + nEQs = 0; + resurrectSubs(); + nGEQs = 0; + nEQs = 0; + int neweq = newEQ(); + assert(neweq == 0); + nMemories = 0; + eqnnzero(&EQs[neweq], nVars); + EQs[neweq].color = EQ_RED; + EQs[neweq].coef[0] = 1; + } + mayBeRed--; + return redFalse; + } + + freeRedEliminations(); + + result = 0; + for (e = nGEQs - 1; e >= 0; e--) if (GEQs[e].color == EQ_RED) result = 1; + for (e = nEQs - 1; e >= 0; e--) if (EQs[e].color == EQ_RED) result = 1; + if (nMemories) result = 1; + if (!result) { + if (computeGist) { + nGEQs = 0; + nEQs = 0; + resurrectSubs(); + nGEQs = 0; + nMemories = 0; + nEQs = 0; + } + mayBeRed--; + return noRed; + } + + if (effort && (computeGist || !nMemories)) { + if (TRACE) + fprintf(outputFile, "*** Doing potentially expensive elimination tests for red equations [\n"); + quickRedKill(computeGist); + checkGistInvariant(); + result = nMemories; + for (e = nGEQs - 1; e >= 0; e--) if (GEQs[e].color == EQ_RED) result++; + for (e = nEQs - 1; e >= 0; e--) if (EQs[e].color == EQ_RED) result++; + if (result && effort > 1 && (computeGist || !nMemories)) { + expensiveRedKill(); + result = nMemories; + for (e = nGEQs-1; e >= 0; e--) if (GEQs[e].color == EQ_RED) result++; + for (e = nEQs-1; e >= 0; e--) if (EQs[e].color == EQ_RED) result++; + } + + if (!result) { + if (TRACE) + fprintf(outputFile, "]******************** Redudant Red Equations eliminated!!\n"); + if (computeGist) { + nGEQs = 0; + nEQs = 0; + resurrectSubs(); + nGEQs = 0; + nMemories = 0; + nEQs = 0; + } + mayBeRed--; + return noRed; + } + + if (TRACE) fprintf(outputFile, "]******************** Red Equations remain\n"); + if (DEBUG) printProblem(); + } + + if (computeGist) { + resurrectSubs(); + cleanoutWildcards(); + + + // Restore saved modular equations into EQs without affecting the problem + if (nEQs+nModularEQs > allocEQs) { + allocEQs = padEQs(allocEQs, nEQs+nModularEQs); + eqn *new_eqs = new eqn[allocEQs]; + for (int e = 0; e < nEQs; e++) + eqnncpy(&(new_eqs[e]), &(EQs[e]), nVars); + delete[] EQs; + EQs= new_eqs; + } + + for (int e = 0; e < nModularEQs; e++) { + eqnncpy(&(EQs[nEQs+e]), &(ModularEQs[e]), old_nVars); + EQs[nEQs+e].color = EQ_RED; + Tuple<coef_t> t(safeVars); + for (int i = 1; i <= safeVars; i++) + t[i] = ModularEQs[e].coef[var[i]]; + for (int i = 1; i <= safeVars; i++) + EQs[nEQs+e].coef[i] = t[i]; + } + + + // Now simplify modular equations using Chinese remainder theorem -- by chun 12/10/2006 + for (int e = 0; e < nEQs; e++) + if (EQs[e].color == EQ_RED) { + int wild_pos = -1; + for (int i = safeVars+1; i <= nVars; i++) + if (EQs[e].coef[i] != 0) { + wild_pos = i; + break; + } + + if (wild_pos == -1) + continue; + + for (int e2 = e+1; e2 < nEQs+nModularEQs; e2++) + if (EQs[e2].color == EQ_RED) { + int wild_pos2 = -1; + for (int i = safeVars+1; i <= ((e2<nEQs)?nVars:old_nVars); i++) + if (EQs[e2].coef[i] != 0) { + wild_pos2 = i; + break; + } + + if (wild_pos2 == -1) + continue; + + coef_t g = gcd(abs(EQs[e].coef[wild_pos]), abs(EQs[e2].coef[wild_pos2])); + coef_t g2 = 1; + coef_t g3; + EQs[e].color = EQs[e2].color = EQ_BLACK; + while ((g3 = factor(g)) != 1) { + coef_t gg = g2 * g3; + g = g/g3; + + bool match = true; + coef_t c = EQs[e].coef[0]; + coef_t c2 = EQs[e2].coef[0]; + bool change_sign = false; + for (int i = 1; i <= safeVars; i++) { + coef_t coef = int_mod_hat(EQs[e].coef[i], gg); + coef_t coef2 = int_mod_hat(EQs[e2].coef[i], gg); + + if (coef == 0 && coef2 == 0) + continue; + + if (change_sign && coef == -coef2) + continue; + + if (!change_sign) { + if (coef == coef2) + continue; + else if (coef == -coef2) { + change_sign = true; + continue; + } + } + + if (coef != 0) { + coef_t t = query_variable_mod(i, gg/gcd(abs(coef), gg), EQ_RED, nModularEQs, old_nVars); + if (t == posInfinity) { + match = false; + break; + } + + c += coef * t; + } + if (coef2 != 0) { + coef_t t = query_variable_mod(i, gg/gcd(abs(coef2), gg), EQ_RED, nModularEQs, old_nVars); + if (t == posInfinity) { + match = false; + break; + } + + c2 += coef2 * t; + } + } + if ((change_sign && int_mod_hat(c, gg) != -int_mod_hat(c2, gg)) || + (!change_sign && int_mod_hat(c, gg) != int_mod_hat(c2, gg))) + match = false; + + if (match) + g2 = gg; + } + EQs[e].color = EQs[e2].color = EQ_RED; + + if (g2 == 1) + continue; + + if (g2 == abs(EQs[e].coef[wild_pos])) { + EQs[e].color = EQ_BLACK; + break; + } + else if (e2 < nEQs && g2 == abs(EQs[e2].coef[wild_pos2])) + EQs[e2].color = EQ_BLACK; + else { + coef_t g4 = abs(EQs[e].coef[wild_pos])/g2; + while (lcm(g2, g4) != abs(EQs[e].coef[wild_pos])) { + assert(lcm(g2, g4) < abs(EQs[e].coef[wild_pos])); + g4 *= abs(EQs[e].coef[wild_pos])/lcm(g2, g4); + } + + for (int i = 0; i <= safeVars; i++) + EQs[e].coef[i] = int_mod_hat(EQs[e].coef[i], g4); + EQs[e].coef[wild_pos] = (EQs[e].coef[wild_pos]>0?1:-1)*g4; + } + } + } + + deleteBlack(); + } + + setExternals(); + mayBeRed--; + assert(nMemories == 0); + return redConstraints; +} + + +void Problem::convertEQstoGEQs(bool excludeStrides) { + int i; + int e; + if (DBUG) + fprintf(outputFile, "Converting all EQs to GEQs\n"); + simplifyProblem(0,0,0); + for(e=0;e<nEQs;e++) { + bool isStride = 0; + int e2 = newGEQ(); + if (excludeStrides) + for(i = safeVars+1; i <= nVars; i++) + isStride = isStride || (EQs[e].coef[i] != 0); + if (isStride) continue; + eqnncpy(&GEQs[e2], &EQs[e], nVars); + GEQs[e2].touched = 1; + e2 = newGEQ(); + eqnncpy(&GEQs[e2], &EQs[e], nVars); + GEQs[e2].touched = 1; + for (i = 0; i <= nVars; i++) + GEQs[e2].coef[i] = -GEQs[e2].coef[i]; + } + // If we have eliminated all EQs, can set nEQs to 0 + // If some strides are left, we don't know the position of them in the EQs + // array, so decreasing nEQs might remove wrong EQs -- we just leave them + // all in. (could sort the EQs to move strides to the front, but too hard.) + if (!excludeStrides) nEQs=0; + if (DBUG) + printProblem(); +} + + +void Problem::convertEQtoGEQs(int eq) { + int i; + if (DBUG) + fprintf(outputFile, "Converting EQ %d to GEQs\n",eq); + int e2 = newGEQ(); + eqnncpy(&GEQs[e2], &EQs[eq], nVars); + GEQs[e2].touched = 1; + e2 = newGEQ(); + eqnncpy(&GEQs[e2], &EQs[eq], nVars); + GEQs[e2].touched = 1; + for (i = 0; i <= nVars; i++) + GEQs[e2].coef[i] = -GEQs[e2].coef[i]; + if (DBUG) + printProblem(); +} + + +/* + * Calculate value of variable modulo integer from problem's equation + * set plus additional saved modular equations embedded in the same + * EQs array (hinted by nModularEQs) if available. If there is no + * solution, return posInfinity. + */ +coef_t Problem::query_variable_mod(int v, coef_t factor, int color, int nModularEQs, int nModularVars) const { + if (safeVars < v) + return posInfinity; + + Tuple<bool> working_on(safeVars); + for (int i = 1; i <= safeVars; i++) + working_on[i] = false; + + return query_variable_mod(v, factor, color, nModularEQs, nModularVars, working_on); +} + +coef_t Problem::query_variable_mod(int v, coef_t factor, int color, int nModularEQs, int nModularVars, Tuple<bool> &working_on) const { + working_on[v] = true; + + for (int e = 0; e < nEQs+nModularEQs; e++) + if (EQs[e].color == color) { + coef_t coef = int_mod_hat(EQs[e].coef[v], factor); + if (abs(coef) != 1) + continue; + + bool wild_factored = true; + for (int i = safeVars+1; i <= ((e<nEQs)?nVars:nModularVars); i++) + if (int_mod_hat(EQs[e].coef[i], factor) != 0) { + wild_factored = false; + break; + } + if (!wild_factored) + continue; + + coef_t result = 0; + for (int i = 1; i <= safeVars; i++) + if (i != v) { + coef_t p = int_mod_hat(EQs[e].coef[i], factor); + + if (p == 0) + continue; + + if (working_on[i] == true) { + result = posInfinity; + break; + } + + coef_t q = query_variable_mod(i, factor, color, nModularEQs, nModularVars, working_on); + if (q == posInfinity) { + result = posInfinity; + break; + } + result += p*q; + } + + if (result != posInfinity) { + result += EQs[e].coef[0]; + if (coef == 1) + result = -result; + working_on[v] = false; + + return int_mod_hat(result, factor); + } + } + + working_on[v] = false; + return posInfinity; +} + + + +#ifdef GIST_CHECK +enum compareAnswer {apparentlyEqual, mightNotBeEqual, NotEqual}; + +static compareAnswer checkEquiv(Problem *p1, Problem *p2) { + int r1,r2; + + p1->check(); + p2->check(); + p1->resurrectSubs(); + p2->resurrectSubs(); + p1->check(); + p2->check(); + p1->putVariablesInStandardOrder(); + p2->putVariablesInStandardOrder(); + p1->check(); + p2->check(); + p1->ordered_elimination(0); + p2->ordered_elimination(0); + p1->check(); + p2->check(); + r1 = p1->simplifyProblem(1,1,0); + r2 = p2->simplifyProblem(1,1,0); + p1->check(); + p2->check(); + + if (!r1 || !r2) { + if (r1 == r2) return apparentlyEqual; + return NotEqual; + } + if (p1->nVars != p2->nVars + || p1->nGEQs != p2->nGEQs + || p1->nSUBs != p2->nSUBs + || p1->checkSum() != p2->checkSum()) { + r1 = p1->simplifyProblem(0,1,1); + r2 = p2->simplifyProblem(0,1,1); + assert(r1 && r2); + p1->check(); + p2->check(); + if (p1->nVars != p2->nVars + || p1->nGEQs != p2->nGEQs + || p1->nSUBs != p2->nSUBs + || p1->checkSum() != p2->checkSum()) { + r1 = p1->simplifyProblem(0,1,2); + r2 = p2->simplifyProblem(0,1,2); + p1->check(); + p2->check(); + assert(r1 && r2); + if (p1->nVars != p2->nVars + || p1->nGEQs != p2->nGEQs + || p1->nSUBs != p2->nSUBs + || p1->checkSum() != p2->checkSum()) { + p1->check(); + p2->check(); + p1->resurrectSubs(); + p2->resurrectSubs(); + p1->check(); + p2->check(); + p1->putVariablesInStandardOrder(); + p2->putVariablesInStandardOrder(); + p1->check(); + p2->check(); + p1->ordered_elimination(0); + p2->ordered_elimination(0); + p1->check(); + p2->check(); + r1 = p1->simplifyProblem(1,1,0); + r2 = p2->simplifyProblem(1,1,0); + p1->check(); + p2->check(); + } + } + } + + if (p1->nVars != p2->nVars + || p1->nSUBs != p2->nSUBs + || p1->nGEQs != p2->nGEQs + || p1->nSUBs != p2->nSUBs) return NotEqual; + if (p1->checkSum() != p2->checkSum()) return mightNotBeEqual; + return apparentlyEqual; +} +#endif + +void Problem::checkGistInvariant() const { +#ifdef GIST_CHECK + Problem new_answer; + int r; + + check(); + fullAnswer.check(); + context.check(); + + if (safeVars < nVars) { + if (DBUG) { + fprintf(outputFile,"Can't check gist invariant due to wildcards\n"); + printProblem(); + } + return; + } + if (DBUG) { + fprintf(outputFile,"Checking gist invariant on: [\n"); + printProblem(); + } + + new_answer = *this; + new_answer->resurrectSubs(); + new_answer->cleanoutWildcards(); + if (DEBUG) { + fprintf(outputFile,"which is: \n"); + printProblem(); + } + deleteBlack(&new_answer); + turnRedBlack(&new_answer); + if (DEBUG) { + fprintf(outputFile,"Black version of answer: \n"); + printProblem(&new_answer); + } + problem_merge(&new_answer,&context); + + r = checkEquiv(&full_answer,&new_answer); + if (r != apparentlyEqual) { + fprintf(outputFile,"GIST INVARIANT REQUIRES MANUAL CHECK:[\n"); + fprintf(outputFile,"Original problem:\n"); + printProblem(&redProblem); + + fprintf(outputFile,"Context:\n"); + printProblem(&context); + + fprintf(outputFile,"Computed gist:\n"); + printProblem(); + + fprintf(outputFile,"Combined answer:\n"); + printProblem(&full_answer); + + fprintf(outputFile,"Context && red constraints:\n"); + printProblem(&new_answer); + fprintf(outputFile,"]\n"); + } + + if (DBUG) { + fprintf(outputFile,"] Done checking gist invariant on\n"); + } +#endif +} + +} // namespace diff --git a/omegalib/omega/src/omega_core/oc_eq.cc b/omegalib/omega/src/omega_core/oc_eq.cc new file mode 100644 index 0000000..dc595ea --- /dev/null +++ b/omegalib/omega/src/omega_core/oc_eq.cc @@ -0,0 +1,653 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + All Rights Reserved. + + Purpose: + Solve equalities. + + Notes: + + History: + *****************************************************************************/ + +#include <omega/omega_core/oc_i.h> + +namespace omega { + +void Problem::simplifyStrideConstraints() { + int e, e2, i; + if (DBUG) + fprintf(outputFile, "Checking for stride constraints\n"); + for (i = safeVars + 1; i <= nVars; i++) { + if (DBUG) + fprintf(outputFile, "checking %s\n", variable(i)); + for (e = 0; e < nGEQs; e++) + if (GEQs[e].coef[i]) + break; + if (e >= nGEQs) { + if (DBUG) + fprintf(outputFile, "%s passed GEQ test\n", variable(i)); + e2 = -1; + for (e = 0; e < nEQs; e++) + if (EQs[e].coef[i]) { + if (e2 == -1) + e2 = e; + else { + e2 = -1; + break; + } + } + if (e2 >= 0) { + if (DBUG) { + fprintf(outputFile, "Found stride constraint: "); + printEQ(&EQs[e2]); + fprintf(outputFile, "\n"); + } + /* Is a stride constraint */ + coef_t g = abs(EQs[e2].coef[i]); + assert(g>0); + int j; + for (j = 0; j <= nVars; j++) + if (i != j) + EQs[e2].coef[j] = int_mod_hat(EQs[e2].coef[j], g); + } + } + } +} + +void Problem::doMod(coef_t factor, int e, int j) { + /* Solve e = factor alpha for x_j and substitute */ + int k; + eqn eq; + coef_t nFactor; + + int alpha; + + // if (j > safeVars) alpha = j; + // else + if (EQs[e].color) { + rememberRedConstraint(&EQs[e], redEQ, 0); + EQs[e].color = EQ_BLACK; + } + alpha = addNewUnprotectedWildcard(); + eqnncpy(&eq, &EQs[e], nVars); + newVar = alpha; + + if (DEBUG) { + fprintf(outputFile, "doing moding: "); + fprintf(outputFile, "Solve "); + printTerm(&eq, 1); + fprintf(outputFile, " = " coef_fmt " %s for %s and substitute\n", + factor, variable(alpha), variable(j)); + } + for (k = nVars; k >= 0; k--) + eq.coef[k] = int_mod_hat(eq.coef[k], factor); + nFactor = eq.coef[j]; + assert(nFactor == 1 || nFactor == -1); + eq.coef[alpha] = factor / nFactor; + if (DEBUG) { + fprintf(outputFile, "adjusted: "); + fprintf(outputFile, "Solve "); + printTerm(&eq, 1); + fprintf(outputFile, " = 0 for %s and substitute\n", variable(j)); + } + + eq.coef[j] = 0; + substitute(&eq, j, nFactor); + newVar = -1; + deleteVariable(j); + for (k = nVars; k >= 0; k--) { + assert(EQs[e].coef[k] % factor == 0); + EQs[e].coef[k] = EQs[e].coef[k] / factor; + } + if (DEBUG) { + fprintf(outputFile, "Mod-ing and normalizing produces:\n"); + printProblem(); + } +} + +void Problem::substitute(eqn *sub, int i, coef_t c) { + int e, j; + coef_t k; + int recordSubstitution = (i <= safeVars && var[i] >= 0); + + redType clr; + if (sub->color) + clr = redEQ; + else + clr = notRed; + + assert(c == 1 || c == -1); + + if (DBUG || doTrace) { + if (sub->color) + fprintf(outputFile, "RED SUBSTITUTION\n"); + fprintf(outputFile, "substituting using %s := ", variable(i)); + printTerm(sub, -c); + fprintf(outputFile, "\n"); + printVars(); + } +#ifndef NDEBUG + if (i > safeVars && clr) { + bool unsafeSub = false; + for (e = nEQs - 1; e >= 0; e--) + if (!(EQs[e].color || !EQs[e].coef[i])) + unsafeSub = true; + for (e = nGEQs - 1; e >= 0; e--) + if (!(GEQs[e].color || !GEQs[e].coef[i])) + unsafeSub = true; + for (e = nSUBs - 1; e >= 0; e--) + if (SUBs[e].coef[i]) + unsafeSub = true; + if (unsafeSub) { + fprintf(outputFile, "UNSAFE RED SUBSTITUTION\n"); + fprintf(outputFile, "substituting using %s := ", variable(i)); + printTerm(sub, -c); + fprintf(outputFile, "\n"); + printProblem(); + assert(0 && "UNSAFE RED SUBSTITUTION"); + } + } +#endif + + for (e = nEQs - 1; e >= 0; e--) { + eqn *eq; + eq = &(EQs[e]); + k = eq->coef[i]; + if (k != 0) { + k = check_mul(k, c); // Should be k = k/c, but same effect since abs(c) == 1 + eq->coef[i] = 0; + for (j = nVars; j >= 0; j--) { + eq->coef[j] -= check_mul(sub->coef[j], k); + } + } + if (DEBUG) { + printEQ(eq); + fprintf(outputFile, "\n"); + } + } + for (e = nGEQs - 1; e >= 0; e--) { + int zero; + eqn *eq; + eq = &(GEQs[e]); + k = eq->coef[i]; + if (k != 0) { + k = check_mul(k, c); // Should be k = k/c, but same effect since abs(c) == 1 + eq->touched = true; + eq->coef[i] = 0; + zero = 1; + for (j = nVars; j >= 0; j--) { + eq->coef[j] -= check_mul(sub->coef[j], k); + if (j > 0 && eq->coef[j]) + zero = 0; + } + if (zero && clr != notRed && !eq->color) { + coef_t z = int_div(eq->coef[0], abs(k)); + if (DBUG || doTrace) { + fprintf(outputFile, + "Black inequality matches red substitution\n"); + if (z < 0) + fprintf(outputFile, "System is infeasible\n"); + else if (z > 0) + fprintf(outputFile, "Black inequality is redundant\n"); + else { + fprintf(outputFile, + "Black constraint partially implies red equality\n"); + if (k < 0) { + fprintf(outputFile, "Black constraints tell us "); + assert(sub->coef[i] == 0); + sub->coef[i] = c; + printTerm(sub, 1); + sub->coef[i] = 0; + fprintf(outputFile, "<= 0\n"); + } else { + fprintf(outputFile, "Black constraints tell us "); + assert(sub->coef[i] == 0); + sub->coef[i] = c; + printTerm(sub, 1); + sub->coef[i] = 0; + fprintf(outputFile, " >= 0\n"); + } + } + } + if (z == 0) { + if (k < 0) { + if (clr == redEQ) + clr = redGEQ; + else if (clr == redLEQ) + clr = notRed; + } else { + if (clr == redEQ) + clr = redLEQ; + else if (clr == redGEQ) + clr = notRed; + } + } + + } + } + if (DEBUG) { + printGEQ(eq); + fprintf(outputFile, "\n"); + } + } + if (i <= safeVars && clr) { + assert(sub->coef[i] == 0); + sub->coef[i] = c; + rememberRedConstraint(sub, clr, 0); + sub->coef[i] = 0; + } + + if (recordSubstitution) { + int s = nSUBs++; + int kk; + eqn *eq = &(SUBs[s]); + for (kk = nVars; kk >= 0; kk--) + eq->coef[kk] = check_mul(-c, (sub->coef[kk])); + eq->key = var[i]; + if (DEBUG) { + fprintf(outputFile, "Recording substition as: "); + printSubstitution(s); + fprintf(outputFile, "\n"); + } + } + if (DEBUG) { + fprintf(outputFile, "Ready to update subs\n"); + if (sub->color) + fprintf(outputFile, "RED SUBSTITUTION\n"); + fprintf(outputFile, "substituting using %s := ", variable(i)); + printTerm(sub, -c); + fprintf(outputFile, "\n"); + printVars(); + } + + for (e = nSUBs - 1; e >= 0; e--) { + eqn *eq = &(SUBs[e]); + k = eq->coef[i]; + if (k != 0) { + k = check_mul(k, c); // Should be k = k/c, but same effect since abs(c) == 1 + eq->coef[i] = 0; + for (j = nVars; j >= 0; j--) { + eq->coef[j] -= check_mul(sub->coef[j], k); + } + } + if (DEBUG) { + fprintf(outputFile, "updated sub (" coef_fmt "): ", c); + printSubstitution(e); + fprintf(outputFile, "\n"); + } + } + + if (DEBUG) { + fprintf(outputFile, "---\n\n"); + printProblem(); + fprintf(outputFile, "===\n\n"); + } +} + + +void Problem::doElimination(int e, int i) { + if (DBUG || doTrace) + fprintf(outputFile, "eliminating variable %s\n", variable(i)); + + eqn sub; + eqnncpy(&sub, &EQs[e], nVars); + coef_t c = sub.coef[i]; + sub.coef[i] = 0; + + if (c == 1 || c == -1) { + substitute(&sub, i, c); + } else { + coef_t a = abs(c); + if (TRACE) + fprintf(outputFile, + "performing non-exact elimination, c = " coef_fmt "\n", c); + if (DBUG) + printProblem(); + assert(inApproximateMode); + + for (int e2 = nEQs - 1; e2 >= 0; e2--) { + eqn *eq = &(EQs[e2]); + coef_t k = eq->coef[i]; + if (k != 0) { + coef_t l = lcm(abs(k), a); + coef_t scale1 = l / abs(k); + for (int j = nVars; j >= 0; j--) + eq->coef[j] = check_mul(eq->coef[j], scale1); + eq->coef[i] = 0; + coef_t scale2 = l / c; + if (k < 0) + scale2 = -scale2; + for (int j = nVars; j >= 0; j--) + eq->coef[j] -= check_mul(sub.coef[j], scale2); + eq->color |= sub.color; + } + } + for (int e2 = nGEQs - 1; e2 >= 0; e2--) { + eqn *eq = &(GEQs[e2]); + coef_t k = eq->coef[i]; + if (k != 0) { + coef_t l = lcm(abs(k), a); + coef_t scale1 = l / abs(k); + for (int j = nVars; j >= 0; j--) + eq->coef[j] = check_mul(eq->coef[j], scale1); + eq->coef[i] = 0; + coef_t scale2 = l / c; + if (k < 0) + scale2 = -scale2; + for (int j = nVars; j >= 0; j--) + eq->coef[j] -= check_mul(sub.coef[j], scale2); + eq->color |= sub.color; + eq->touched = 1; + } + } + for (int e2 = nSUBs - 1; e2 >= 0; e2--) + if (SUBs[e2].coef[i]) { + eqn *eq = &(EQs[e2]); + assert(0); + // We can't handle this since we can't multiply + // the coefficient of the left-hand side + assert(!sub.color); + for (int j = nVars; j >= 0; j--) + eq->coef[j] = check_mul(eq->coef[j], a); + coef_t k = eq->coef[i]; + eq->coef[i] = 0; + for (int j = nVars; j >= 0; j--) + eq->coef[j] -= check_mul(sub.coef[j], k / c); + } + } + deleteVariable(i); +} + +int Problem::solveEQ() { + check(); + + // Reorder equations according to complexity. + { + int delay[nEQs]; + + for (int e = 0; e < nEQs; e++) { + delay[e] = 0; + if (EQs[e].color) + delay[e] += 8; + int nonunitWildCards = 0; + int unitWildCards = 0; + for (int i = nVars; i > safeVars; i--) + if (EQs[e].coef[i]) { + if (EQs[e].coef[i] == 1 || EQs[e].coef[i] == -1) + unitWildCards++; + else + nonunitWildCards++; + } + int unit = 0; + int nonUnit = 0; + for (int i = safeVars; i > 0; i--) + if (EQs[e].coef[i]) { + if (EQs[e].coef[i] == 1 || EQs[e].coef[i] == -1) + unit++; + else + nonUnit++; + } + if (unitWildCards == 1 && nonunitWildCards == 0) + delay[e] += 0; + else if (unitWildCards >= 1 && nonunitWildCards == 0) + delay[e] += 1; + else if (inApproximateMode && nonunitWildCards > 0) + delay[e] += 2; + else if (unit == 1 && nonUnit == 0 && nonunitWildCards == 0) + delay[e] += 3; + else if (unit > 1 && nonUnit == 0 && nonunitWildCards == 0) + delay[e] += 4; + else if (unit >= 1 && nonunitWildCards <= 1) + delay[e] += 5; + else + delay[e] += 6; + } + + for (int e = 0; e < nEQs; e++) { + int e2, slowest; + slowest = e; + for (e2 = e + 1; e2 < nEQs; e2++) + if (delay[e2] > delay[slowest]) + slowest = e2; + if (slowest != e) { + int tmp = delay[slowest]; + delay[slowest] = delay[e]; + delay[e] = tmp; + eqn eq; + eqnncpy(&eq, &EQs[slowest], nVars); + eqnncpy(&EQs[slowest], &EQs[e], nVars); + eqnncpy(&EQs[e], &eq, nVars); + } + } + } + + // Eliminate all equations. + while (nEQs != 0) { + int e = nEQs - 1; + eqn *eq = &(EQs[e]); + coef_t g, g2; + + assert(mayBeRed || !eq->color); + + check(); + + // get gcd of coefficients of all unprotected variables + g2 = 0; + for (int i = nVars; i > safeVars; i--) + if (eq->coef[i] != 0) { + g2 = gcd(abs(eq->coef[i]), g2); + if (g2 == 1) + break; + } + + // get gcd of coefficients of all variables + g = g2; + if (g != 1) + for (int i = safeVars; i >= 1; i--) + if (eq->coef[i] != 0) { + g = gcd(abs(eq->coef[i]), g); + if (g == 1) + break; + } + + // approximate mode bypass integer modular test; in Farkas(), + // existential variable lambda's are rational numbers. + if (inApproximateMode && g2 != 0) + g = gcd(abs(eq->coef[0]), g); + + // simple test to see if the equation is satisfiable + if (g == 0) { + if (eq->coef[0] != 0) { + return (false); + } else { + nEQs--; + continue; + } + } else if (abs(eq->coef[0]) % g != 0) { + return (false); + } + + // set gcd of all coefficients to 1 + if (g != 1) { + for (int i = nVars; i >= 0; i--) + eq->coef[i] /= g; + g2 = g2 / g; + } + + // exact elimination of unit coefficient variable + if (g2 != 0) { // for constraint with unprotected variable + int i; + for (i = nVars; i > safeVars; i--) + if (abs(eq->coef[i]) == 1) + break; + if (i > safeVars) { + nEQs--; + doElimination(e, i); + continue; + } + } else { // for constraint without unprotected variable + + // pick the unit coefficient variable with complex inequalites + // to eliminate, this will make inequalities tighter. e.g. + // {[t4,t6,t10]:exists (alpha: 0<=t6<=3 && t10=4alpha+t6 && + // 64t4<=t10<=64t4+15)} + int unit_var; + int cost = -1; + + for (int i = safeVars; i > 0; i--) + + if (abs(eq->coef[i]) == 1) { + int cur_cost = 0; + for (int j = 0; j < nGEQs; j++) + if (GEQs[j].coef[i] != 0) { + for (int k = safeVars; k > 0; k--) + if (GEQs[j].coef[k] != 0) { + if (abs(GEQs[j].coef[k]) != 1){ + + cur_cost += 3; + + } + else + cur_cost += 1; + } + } + + if (cur_cost > cost) { + cost = cur_cost; + unit_var = i; + } + + } + + if (cost != -1) { + nEQs--; + doElimination(e, unit_var); + continue; + } + + + } + + // check if there is an unprotected variable as wildcard + if (g2 > 0) { + int pos = 0; + coef_t g3; + for (int k = nVars; k > safeVars; k--) + if (eq->coef[k] != 0) { + int e2; + for (e2 = e - 1; e2 >= 0; e2--) + if (EQs[e2].coef[k]) + break; + if (e2 >= 0) + continue; + for (e2 = nGEQs - 1; e2 >= 0; e2--) + if (GEQs[e2].coef[k]) + break; + if (e2 >= 0) + continue; + for (e2 = nSUBs - 1; e2 >= 0; e2--) + if (SUBs[e2].coef[k]) + break; + if (e2 >= 0) + continue; + + if (pos == 0) { + g3 = abs(eq->coef[k]); + pos = k; + } else { + if (abs(eq->coef[k]) < g3) { + g3 = abs(eq->coef[k]); + pos = k; + } + } + } + + if (pos != 0) { + bool change = false; + for (int k2 = nVars; k2 >= 0; k2--) + if (k2 != pos && eq->coef[k2] != 0) { + coef_t t = int_mod_hat(eq->coef[k2], g3); + if (t != eq->coef[k2]) { + eq->coef[k2] = t; + change = true; + } + } + + // strength reduced, try this equation again + if (change) { + // nameWildcard(pos); + continue; + } + } + } + + // insert new stride constraint + if (g2 > 1 && !(inApproximateMode && !inStridesAllowedMode)) { + int newvar = addNewProtectedWildcard(); + int neweq = newEQ(); + assert(neweq == e+1); + // we were working on highest-numbered EQ + eqnnzero(&EQs[neweq], nVars); + eqnncpy(&EQs[neweq], eq, safeVars); + + for (int k = nVars; k >= 0; k--) { + EQs[neweq].coef[k] = int_mod_hat(EQs[neweq].coef[k], g2); + } + if (EQs[e].color) + rememberRedConstraint(&EQs[neweq], redStride, g2); + EQs[neweq].coef[newvar] = g2; + EQs[neweq].color = EQ_BLACK; + continue; + } + + // inexact elimination of unprotected variable + if (g2 > 0 && inApproximateMode) { + int pos = 0; + for (int k = nVars; k > safeVars; k--) + if (eq->coef[k] != 0) { + pos = k; + break; + } + assert(pos > safeVars); + + // special handling for wildcard used in breaking down + // diophantine equation + if (abs(eq->coef[pos]) > 1) { + int e2; + for (e2 = nSUBs - 1; e2 >= 0; e2--) + if (SUBs[e2].coef[pos]) + break; + if (e2 >= 0) { + protectWildcard(pos); + continue; + } + } + + nEQs--; + doElimination(e, pos); + continue; + } + + // now solve linear diophantine equation using least remainder + // algorithm + { + coef_t factor = (posInfinity); // was MAXINT + int pos = 0; + for (int k = nVars; k > (g2 > 0 ? safeVars : 0); k--) + if (eq->coef[k] != 0 && factor > abs(eq->coef[k]) + 1) { + factor = abs(eq->coef[k]) + 1; + pos = k; + } + assert(pos > (g2>0?safeVars:0)); + doMod(factor, e, pos); + continue; + } + } + + assert(nEQs == 0); + return (OC_SOLVE_UNKNOWN); +} + +} // namespace diff --git a/omegalib/omega/src/omega_core/oc_exp_kill.cc b/omegalib/omega/src/omega_core/oc_exp_kill.cc new file mode 100644 index 0000000..fdb2718 --- /dev/null +++ b/omegalib/omega/src/omega_core/oc_exp_kill.cc @@ -0,0 +1,297 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + All Rights Reserved. + + Purpose: + Expensive inequality elimination. + + Notes: + + History: + 03/31/09 Use BoolSet, Chun Chen +*****************************************************************************/ + +#include <omega/omega_core/oc_i.h> +#include <basic/BoolSet.h> +#include <vector> + +namespace omega { + +int Problem::expensiveKill() { + int e; + if (TRACE) fprintf(outputFile,"Performing expensive kill tests: [\n"); + if (DBUG) printProblem(); + Problem tmpProblem; + int oldTrace = trace; + int constraintsRemoved = 0; + + trace = 0; + conservative++; + + for (e = nGEQs - 1; e >= 0; e--) + if (!GEQs[e].essential) { + if (DBUG) { + fprintf(outputFile, "checking equation %d to see if it is redundant: ", e); + printGEQ(&(GEQs[e])); + fprintf(outputFile, "\n"); + } + tmpProblem = *this; + tmpProblem.negateGEQ(e); + tmpProblem.varsOfInterest = 0; + tmpProblem.nSUBs = 0; + tmpProblem.nMemories = 0; + tmpProblem.safeVars = 0; + tmpProblem.variablesFreed = 0; + tmpProblem.isTemporary = true; + + if (!tmpProblem.solve(false)) { + if (DBUG) + fprintf(outputFile, "redundant!\n"); + constraintsRemoved++; + deleteGEQ(e); + } + } + + if (constraintsRemoved) { + if (TRACE) fprintf(outputFile,"%d Constraints removed!!\n",constraintsRemoved); + } + + trace = oldTrace; + conservative--; + if (TRACE) fprintf(outputFile,"] expensive kill tests done\n"); + return 1; +} + +int Problem::expensiveRedKill() { + int e; + if (TRACE) fprintf(outputFile,"Performing expensive red kill tests: [\n"); + Problem tmpProblem; + int oldTrace = trace; + int constraintsRemoved = 0; + + trace = 0; + conservative++; + + for (e = nGEQs - 1; e >= 0; e--) + if (!GEQs[e].essential && GEQs[e].color) { + if (DEBUG) { + fprintf(outputFile, "checking equation %d to see if it is redundant: ", e); + printGEQ(&(GEQs[e])); + fprintf(outputFile, "\n"); + } + tmpProblem = *this; + tmpProblem.negateGEQ(e); + tmpProblem.varsOfInterest = 0; + tmpProblem.nSUBs = 0; + tmpProblem.nMemories = 0; + tmpProblem.safeVars = 0; + tmpProblem.variablesFreed = 0; + tmpProblem.isTemporary = true; + tmpProblem.turnRedBlack(); + if (!tmpProblem.solve(false)) { + constraintsRemoved++; + deleteGEQ(e); + } + } + + if (constraintsRemoved) { + if (TRACE) fprintf(outputFile,"%d Constraints removed!!\n",constraintsRemoved); + } + + trace = oldTrace; + conservative--; + if (TRACE) fprintf(outputFile,"] expensive red kill tests done\n"); + return 1; +} + + +int Problem::expensiveEqualityCheck() { + int e; + return 1; + if (TRACE) fprintf(outputFile,"Performing expensive equality tests: [\n"); + Problem tmpProblem; + int oldTrace = trace; + int equalitiesFound = 0; + + trace = 0; + conservative++; + + for (e = nGEQs - 1; e >= 0; e--) { + if (DEBUG) { + fprintf(outputFile, "checking equation %d to see if it is an equality: ", e); + printGEQ(&(GEQs[e])); + fprintf(outputFile, "\n"); + } + tmpProblem = *this; + tmpProblem.GEQs[e].coef[0]--; + tmpProblem.varsOfInterest = 0; + tmpProblem.nSUBs = 0; + tmpProblem.nMemories = 0; + tmpProblem.safeVars = 0; + tmpProblem.variablesFreed = 0; + tmpProblem.isTemporary = true; + if (!tmpProblem.solve(false)) { + int neweq = newEQ(); + eqnncpy(&EQs[neweq], &GEQs[e], nVars); + equalitiesFound++; + addingEqualityConstraint(neweq); + } + } + if (equalitiesFound) { + if (TRACE) fprintf(outputFile,"%d Equalities found!!\n",equalitiesFound); + } + + trace = oldTrace; + conservative--; + if (equalitiesFound) { + if (!solveEQ()) return 0; + if (!normalize()) return 0; + } + if (TRACE) fprintf(outputFile,"] expensive equality tests done\n"); + return 1; +} + + +void Problem::quickRedKill(int computeGist) { + if (DBUG) { + fprintf(outputFile, "in quickRedKill: [\n"); + printProblem(); + } + + noteEssential(0); + int moreToDo = chainKill(1,0); + +#ifdef NDEBUG + if (!moreToDo) { + if (DBUG) fprintf(outputFile, "] quickRedKill\n"); + return; + } +#endif + + int isDead[nGEQs]; + int deadCount = 0; + std::vector<BoolSet<> > P(nGEQs, BoolSet<>(nVars)), Z(nGEQs, BoolSet<>(nVars)), N(nGEQs, BoolSet<>(nVars)); + BoolSet<> PP, PZ, PN; /* possible Positives, possible zeros & possible negatives */ + BoolSet<> MZ; /* must zeros */ + + int equationsToKill = 0; + for (int e = nGEQs - 1; e >= 0; e--) { + isDead[e] = 0; + if (GEQs[e].color && !GEQs[e].essential) equationsToKill++; + if (GEQs[e].color && GEQs[e].essential && !computeGist) + if (!moreToDo) { + if (DBUG) fprintf(outputFile, "] quickRedKill\n"); + return; + } + for (int i = nVars; i >= 1; i--) { + if (GEQs[e].coef[i] > 0) + P[e].set(i-1); + else if (GEQs[e].coef[i] < 0) + N[e].set(i-1); + else + Z[e].set(i-1); + } + } + + if (!equationsToKill) + if (!moreToDo) { + if (DBUG) fprintf(outputFile, "] quickRedKill\n"); + return; + } + + for (int e = nGEQs - 1; e > 0; e--) + if (!isDead[e]) + for (int e2 = e - 1; e2 >= 0; e2--) + if (!isDead[e2]) { + coef_t a = 0; + int i, j; + for (i = nVars; i > 1; i--) { + for (j = i - 1; j > 0; j--) { + a = (GEQs[e].coef[i] * GEQs[e2].coef[j] - GEQs[e2].coef[i] * GEQs[e].coef[j]); + if (a != 0) + goto foundPair; + } + } + continue; + + foundPair: + if (DEBUG) { + fprintf(outputFile, "found two equations to combine, i = %s, ", variable(i)); + fprintf(outputFile, "j = %s, alpha = " coef_fmt "\n", variable(j), a); + printGEQ(&(GEQs[e])); + fprintf(outputFile, "\n"); + printGEQ(&(GEQs[e2])); + fprintf(outputFile, "\n"); + } + + MZ = (Z[e] & Z[e2]); + PZ = MZ | (P[e] & N[e2]) | (N[e] & P[e2]); + PP = P[e] | P[e2]; + PN = N[e] | N[e2]; + + for (int e3 = nGEQs - 1; e3 >= 0; e3--) + if (e3 != e && e3 != e2 && GEQs[e3].color && !GEQs[e3].essential) { + coef_t alpha1, alpha2, alpha3; + + if (!PZ.imply(Z[e3]) || MZ.imply(~Z[e3])) continue; + if (!PP.imply(P[e3]) || !PN.imply(N[e3])) continue; + + if (a > 0) { + alpha1 = GEQs[e2].coef[j] * GEQs[e3].coef[i] - GEQs[e2].coef[i] * GEQs[e3].coef[j]; + alpha2 = -(GEQs[e].coef[j] * GEQs[e3].coef[i] - GEQs[e].coef[i] * GEQs[e3].coef[j]); + alpha3 = a; + } + else { + alpha1 = -(GEQs[e2].coef[j] * GEQs[e3].coef[i] - GEQs[e2].coef[i] * GEQs[e3].coef[j]); + alpha2 = -(-(GEQs[e].coef[j] * GEQs[e3].coef[i] - GEQs[e].coef[i] * GEQs[e3].coef[j])); + alpha3 = -a; + } + + if (alpha1 > 0 && alpha2 > 0) { + if (DEBUG) { + fprintf(outputFile, "alpha1 = " coef_fmt ", alpha2 = " coef_fmt "; comparing against: ", alpha1, alpha2); + printGEQ(&(GEQs[e3])); + fprintf(outputFile, "\n"); + } + coef_t c; + int k; + for (k = nVars; k >= 0; k--) { + c = alpha1 * GEQs[e].coef[k] + alpha2 * GEQs[e2].coef[k]; + if (DEBUG) { + if (k>0) + fprintf(outputFile, " %s: " coef_fmt ", " coef_fmt "\n", variable(k), c, alpha3 * GEQs[e3].coef[k]); + else fprintf(outputFile, " constant: " coef_fmt ", " coef_fmt "\n", c, alpha3 * GEQs[e3].coef[k]); + } + if (c != alpha3 * GEQs[e3].coef[k]) + break; + } + if (k < 0 || (k == 0 && c < alpha3 * (GEQs[e3].coef[k]+1))) { + if (DEBUG) { + deadCount++; + fprintf(outputFile, "red equation#%d is dead (%d dead so far, %d remain)\n", e3, deadCount, nGEQs - deadCount); + printGEQ(&(GEQs[e])); + fprintf(outputFile, "\n"); + printGEQ(&(GEQs[e2])); + fprintf(outputFile, "\n"); + printGEQ(&(GEQs[e3])); + fprintf(outputFile, "\n"); + assert(moreToDo); + } + isDead[e3] = 1; + } + } + } + } + + for (int e = nGEQs - 1; e >= 0; e--) + if (isDead[e]) + deleteGEQ(e); + + if (DBUG) { + fprintf(outputFile,"] quickRedKill\n"); + printProblem(); + } +} + +} // namespace diff --git a/omegalib/omega/src/omega_core/oc_global.cc b/omegalib/omega/src/omega_core/oc_global.cc new file mode 100644 index 0000000..17d8a0c --- /dev/null +++ b/omegalib/omega/src/omega_core/oc_global.cc @@ -0,0 +1,45 @@ +#include <omega/omega_core/oc_i.h> + +namespace omega { + +const int Problem::min_alloc = 10; +const int Problem::first_alloc_pad = 5; + +int omega_core_debug = 0; // 3: full debugging info + +int maxEQs = 100; // original 35, increased by chun +int maxGEQs = 200; // original 70, increased by chun + +int newVar = -1; +int findingImplicitEqualities = 0; +int firstCheckForRedundantEquations = 0; +int doItAgain; +int conservative = 0; +FILE *outputFile = stderr; /* printProblem writes its output to this file */ +char wildName[200][20]; +int nextWildcard = 0; +int trace = 1; +int depth = 0; +int headerLevel; +int inApproximateMode = 0; +int inStridesAllowedMode = 0; +int addingOuterEqualities = 0; +int outerColor = 0; +int reduceWithSubs = 1; +int pleaseNoEqualitiesInSimplifiedProblems = 0; +Problem *originalProblem = noProblem; +int omegaInitialized = 0; +int mayBeRed = 0; + + +// Hash table is used to hash all inequalties for all problems. It +// persists across problems for quick problem merging in case. When +// the table is filled to 1/3 full, it is flushed and the filling +// process starts all over again. +int packing[maxVars]; +int hashVersion = 0; +eqn hashMaster[hashTableSize]; +int fastLookup[maxKeys*2]; +int nextKey; + +} // namespace diff --git a/omegalib/omega/src/omega_core/oc_print.cc b/omegalib/omega/src/omega_core/oc_print.cc new file mode 100644 index 0000000..7934713 --- /dev/null +++ b/omegalib/omega/src/omega_core/oc_print.cc @@ -0,0 +1,686 @@ +#include <omega/omega_core/oc_i.h> + +namespace omega { + +int print_in_code_gen_style = 0; + +void Problem::initializeVariables() const { + Problem *p = (Problem *)this; + int i; + assert(!p->variablesInitialized); + for (i = p->nVars; i >= 0; i--) + p->forwardingAddress[i] = p->var[i] = i; + p->variablesInitialized = 1; +} + + +std::string Problem::print_term_to_string(const eqn *e, int c) const { + std::string s=""; + int i; + int first; + int n = nVars; + int wentFirst = -1; + first = 1; + for (i = 1; i <= n; i++) + if (c * e->coef[i] > 0) { + first = 0; + wentFirst = i; + + if (c * e->coef[i] == 1) + s+= variable(i); + else { + s += to_string(c * e->coef[i]); + if (print_in_code_gen_style) s += "*"; + s += variable(i); + } + break; + } + for (i = 1; i <= n; i++) + if (i != wentFirst && c * e->coef[i] != 0) { + if (!first && c * e->coef[i] > 0) + s += "+"; + + first = 0; + + if (c * e->coef[i] == 1) + s += variable(i); + else if (c * e->coef[i] == -1) { + s += "-"; s += variable(i); + } + else { + s += to_string(c * e->coef[i]); + if (print_in_code_gen_style) s += "*"; + s += variable(i); + } + } + if (!first && c * e->coef[0] > 0) + s += "+"; + if (first || c * e->coef[0] != 0) + s += to_string(c * e->coef[0]); + return s; +} + + +void Problem::printTerm(const eqn * e, int c) const { + std::string s = print_term_to_string(e, c); + fprintf(outputFile, "%s", s.c_str()); +} + + +void Problem::printSub(int v) const { + std::string s = print_sub_to_string(v); + fprintf(outputFile, "%s", s.c_str()); +} + + +std::string Problem::print_sub_to_string(int v) const { + std::string s; + + if (v > 0) + s = variable(v); + else + s = print_term_to_string(&SUBs[-v-1], 1); + return s; +} + + +void Problem::clearSubs() { + nSUBs = 0; + nMemories = 0; +} + + +void Problem::printEqn(const eqn *e, int test, int extra) const { + char buf[maxVars * 12 + 180]; // original buf[maxVars * 12 + 80] + + sprintEqn(buf, e, test, extra); + fprintf(outputFile, "%s", buf); +} + + +std::string Problem::printEqnToString(const eqn *e, int test, int extra) const { + char buf[maxVars * 12 + 180]; // original buf[maxVars * 12 + 80] + sprintEqn(buf, e, test, extra); + return std::string(buf); +} + + +void Problem::sprintEqn(char *str, const eqn *e, int test, int extra) const { + int i; + int first; + int n = nVars + extra; + int isLT; + + isLT = test && e->coef[0] == -1; + if (isLT) + isLT = 1; +#if 0 + if (test) { + if (DEBUG && e->touched) { + sprintf(str, "!"); + while (*str) + str++; + } + else if (DBUG && !e->touched && e->key != 0) { + sprintf(str, "%d: ", e->key); + while (*str) + str++; + } + } +#endif + if (e->color) { + sprintf(str, "["); + while (*str) + str++; + } + first = 1; + for (i = isLT; i <= n; i++) + if (e->coef[i] < 0) { + if (!first) { + sprintf(str, "+"); + while (*str) + str++; + } + else + first = 0; + if (i == 0) { + sprintf(str, coef_fmt, -e->coef[i]); + while (*str) + str++; + } + else if (e->coef[i] == -1) { + sprintf(str, "%s", variable(i)); + while (*str) + str++; + } + else { + if (print_in_code_gen_style) + sprintf(str, coef_fmt "*%s", -e->coef[i], variable(i)); + else sprintf(str, coef_fmt "%s", -e->coef[i], variable(i)); + while (*str) + str++; + } + } + if (first) { + if (isLT) { + sprintf(str, "1"); + isLT = 0; + } + else + sprintf(str, "0"); + while (*str) + str++; + } + if (test == 0) { + if (print_in_code_gen_style) sprintf(str, " == "); + else sprintf(str, " = "); + while (*str) + str++; + } + else { + if (isLT) + sprintf(str, " < "); + else + sprintf(str, " <= "); + while (*str) + str++; + } + + first = 1; + for (i = 0; i <= n; i++) + if (e->coef[i] > 0) { + if (!first) { + sprintf(str, "+"); + while (*str) + str++; + } + else + first = 0; + if (i == 0) { + sprintf(str, coef_fmt , e->coef[i]); + while (*str) + str++; + } + else if (e->coef[i] == 1) { + sprintf(str, "%s", variable(i)); + while (*str) + str++; + } + else { + if (print_in_code_gen_style) + sprintf(str, coef_fmt "*%s", e->coef[i], variable(i)); + else + sprintf(str, coef_fmt "%s", e->coef[i], variable(i)); + while (*str) + str++; + } + } + if (first) { + sprintf(str, "0"); + while (*str) + str++; + } + if (e->color) { + sprintf(str, "]"); + while (*str) + str++; + } +} + + +void Problem::printSubstitution(int s) const { + const eqn * eq = &(SUBs[s]); + assert(eq->key > 0); + fprintf(outputFile, "%s := ", orgVariable(eq->key)); + printTerm(eq, 1); +} + + +void Problem::printVars(int /*debug*/) const { + int i; + fprintf(outputFile, "variables = "); + if (safeVars > 0) + fprintf(outputFile, "("); + for (i = 1; i <= nVars; i++) { + fprintf(outputFile, "%s", variable(i)); + if (i == safeVars) + fprintf(outputFile, ")"); + if (i < nVars) + fprintf(outputFile, ", "); + } + fprintf(outputFile, "\n"); + /* + fprintf(outputFile, "forward addresses = "); + if (safeVars > 0) + fprintf(outputFile, "("); + for (i = 1; i <= nVars; i++) + { + int v = forwardingAddress[i]; + if (v > 0) fprintf(outputFile, "%s", variable(i)); + else fprintf(outputFile, "*"); + if (i == safeVars) + fprintf(outputFile, ")"); + if (i < nVars) + fprintf(outputFile, ", "); + }; + fprintf(outputFile, "\n"); + */ +} + + +void printHeader() { + int i; + for(i=0; i<headerLevel; i++) { + fprintf(outputFile, ". "); + } +} + + +void Problem::printProblem(int debug) const { + int e; + + if (!variablesInitialized) + initializeVariables(); + if (debug) { + printHeader(); + fprintf(outputFile, "#vars = %d, #EQ's = %d, #GEQ's = %d, # SUB's = %d, ofInterest = %d\n", + nVars,nEQs,nGEQs,nSUBs,varsOfInterest); + printHeader(); + printVars(debug); + } + for (e = 0; e < nEQs; e++) { + printHeader(); + printEQ(&EQs[e]); + fprintf(outputFile, "\n"); + } + for (e = 0; e < nGEQs; e++) { + printHeader(); + printGEQ(&GEQs[e]); + fprintf(outputFile, "\n"); + } + for (e = 0; e < nSUBs; e++) { + printHeader(); + printSubstitution(e); + fprintf(outputFile, "\n"); + } + + for (e = 0; e < nMemories; e++) { + int i; + printHeader(); + switch(redMemory[e].kind) { + case notRed: + fprintf(outputFile,"notRed: "); + break; + case redGEQ: + fprintf(outputFile,"Red: 0 <= "); + break; + case redLEQ: + fprintf(outputFile,"Red ??: 0 >= "); + break; + case redEQ: + fprintf(outputFile,"Red: 0 == "); + break; + case redStride: + fprintf(outputFile,"Red stride " coef_fmt ": ", redMemory[e].stride); + break; + } + fprintf(outputFile," " coef_fmt, redMemory[e].constantTerm); + for(i=0;i< redMemory[e].length; i++) + if(redMemory[e].coef[i] >= 0) + fprintf(outputFile,"+" coef_fmt "%s", redMemory[e].coef[i], orgVariable(redMemory[e].var[i])); + else + fprintf(outputFile,"-" coef_fmt "%s", -redMemory[e].coef[i], orgVariable(redMemory[e].var[i])); + fprintf(outputFile, "\n"); + } + fflush(outputFile); + + CHECK_FOR_DUPLICATE_VARIABLE_NAMES; +} + + +void Problem::printRedEquations() const { + int e; + + if (!variablesInitialized) + initializeVariables(); + printVars(1); + for (e = 0; e < nEQs; e++) { + if (EQs[e].color == EQ_RED) { + printEQ(&EQs[e]); + fprintf(outputFile, "\n"); + } + } + for (e = 0; e < nGEQs; e++) { + if (GEQs[e].color == EQ_RED) { + printGEQ(&GEQs[e]); + fprintf(outputFile, "\n"); + } + } + for (e = 0; e < nSUBs; e++) { + if (SUBs[e].color) { + printSubstitution(e); + fprintf(outputFile, "\n"); + } + } + fflush(outputFile); +} + + +int Problem::prettyPrintProblem() const { + std::string s = prettyPrintProblemToString(); + fprintf(outputFile, "%s", s.c_str()); + fflush(outputFile); + return 0; +} + + +std::string Problem::prettyPrintProblemToString() const { + std::string s=""; + int e; + int v; + int live[maxmaxGEQs]; + int v1, v2, v3; + int t, change; + int stuffPrinted = 0; + const char *connector = " && "; + + typedef enum { + none, le, lt + } partialOrderType; + + partialOrderType po[maxVars][maxVars]; + int poE[maxVars][maxVars]; + int lastLinks[maxVars]; + int firstLinks[maxVars]; + int chainLength[maxVars]; + int chain[maxVars]; + int varCount[maxVars]; + int i, m, multiprint; + + + if (!variablesInitialized) + initializeVariables(); + + if (nVars > 0) { + for (e = 0; e < nEQs; e++) { + if (stuffPrinted) + s += connector; + stuffPrinted = 1; + s += print_EQ_to_string(&EQs[e]); + } + + for (e = 0; e < nGEQs; e++) { + live[e] = true; + varCount[e] = 0; + for (v = 1; v <= nVars; v++) + if (GEQs[e].coef[v]) varCount[e]++; + } + + if (!print_in_code_gen_style) + while (1) { + for (v = 1; v <= nVars; v++) { + lastLinks[v] = firstLinks[v] = 0; + chainLength[v] = 0; + for (v2 = 1; v2 <= nVars; v2++) + po[v][v2] = none; + } + + for (e = 0; e < nGEQs; e++) + if (live[e] && varCount[e] <= 2) { + for (v = 1; v <= nVars; v++) { + if (GEQs[e].coef[v] == 1) + firstLinks[v]++; + else if (GEQs[e].coef[v] == -1) + lastLinks[v]++; + } + + v1 = nVars; + while (v1 > 0 && GEQs[e].coef[v1] == 0) + v1--; + v2 = v1 - 1; + while (v2 > 0 && GEQs[e].coef[v2] == 0) + v2--; + v3 = v2 - 1; + while (v3 > 0 && GEQs[e].coef[v3] == 0) + v3--; + + if (GEQs[e].coef[0] > 0 || GEQs[e].coef[0] < -1 + || v2 <= 0 || v3 > 0 + || GEQs[e].coef[v1] * GEQs[e].coef[v2] != -1) { + /* Not a partial order relation */ + + } + else { + if (GEQs[e].coef[v1] == 1) { + v3 = v2; + v2 = v1; + v1 = v3; + } + /* relation is v1 <= v2 or v1 < v2 */ + po[v1][v2] = ((GEQs[e].coef[0] == 0) ? le : lt); + poE[v1][v2] = e; + } + } + for (v = 1; v <= nVars; v++) + chainLength[v] = lastLinks[v]; + + /* + * printf("\n\nPartial order:\n"); printf(" "); for (v1 = 1; v1 <= nVars; v1++) + * printf("%7s",variable(v1)); printf("\n"); for (v1 = 1; v1 <= nVars; v1++) { printf("%6s: + * ",variable(v1)); for (v2 = 1; v2 <= nVars; v2++) switch (po[v1][v2]) { case none: printf(" "); + * break; case le: printf(" <= "); break; case lt: printf(" < "); break; } printf("\n"); } + */ + + + /* Just in case nVars <= 0 */ + change = false; + for (t = 0; t < nVars; t++) { + change = false; + for (v1 = 1; v1 <= nVars; v1++) + for (v2 = 1; v2 <= nVars; v2++) + if (po[v1][v2] != none && + chainLength[v1] <= chainLength[v2]) { + chainLength[v1] = chainLength[v2] + 1; + change = true; + } + } + + if (change) { + /* caught in cycle */ + +#if 0 + printf("\n\nPartial order:\n"); printf(" "); + for (v1 = 1; v1 <= nVars; v1++) printf("%7s",variable(v1)); printf("\n"); + for (v1 = 1; v1 <= nVars; v1++) { + printf("%6s: ",variable(v1)); + for (v2 = 1; v2 <= nVars; v2++) switch (po[v1][v2]) { + case none: printf(" "); break; + case le: printf(" <= "); break; + case lt: printf(" < "); break; + } + printf("\n"); + } + + printProblem(1); +#endif + break; + } + + for (v1 = 1; v1 <= nVars; v1++) + if (chainLength[v1] == 0) + firstLinks[v1] = 0; + + v = 1; + for (v1 = 2; v1 <= nVars; v1++) + if (chainLength[v1] + firstLinks[v1] > chainLength[v] + firstLinks[v]) + v = v1; + + if (chainLength[v] + firstLinks[v] == 0) + break; + + if (stuffPrinted) + s += connector; + stuffPrinted = 1; + /* chain starts at v */ + /* print firstLinks */ + { + coef_t tmp; + int first; + first = 1; + for (e = 0; e < nGEQs; e++) + if (live[e] && GEQs[e].coef[v] == 1 && varCount[e] <= 2) { + if (!first) + s += ", "; + tmp = GEQs[e].coef[v]; + ((Problem *)this)-> + GEQs[e].coef[v] = 0; + s += print_term_to_string(&GEQs[e], -1); + ((Problem *)this)-> + GEQs[e].coef[v] = tmp; + live[e] = false; + first = 0; + } + if (!first) + s += " <= "; + } + + + /* find chain */ + chain[0] = v; + m = 1; + while (1) { + /* print chain */ + for (v2 = 1; v2 <= nVars; v2++) + if (po[v][v2] && chainLength[v] == 1 + chainLength[v2]) + break; + if (v2 > nVars) + break; + chain[m++] = v2; + v = v2; + } + + s += variable(chain[0]); + + multiprint = 0; + for (i = 1; i < m; i++) { + v = chain[i - 1]; + v2 = chain[i]; + if (po[v][v2] == le) + s += " <= "; + else + s += " < "; + s += variable(v2); + live[poE[v][v2]] = false; + if (!multiprint && i < m - 1) + for (v3 = 1; v3 <= nVars; v3++) { + if (v == v3 || v2 == v3) + continue; + if (po[v][v2] != po[v][v3]) + continue; + if (po[v2][chain[i + 1]] != po[v3][chain[i + 1]]) + continue; + s += ","; s += variable(v3); + live[poE[v][v3]] = false; + live[poE[v3][chain[i + 1]]] = false; + multiprint = 1; + } + else + multiprint = 0; + } + + v = chain[m - 1]; + /* print lastLinks */ + { + coef_t tmp; + int first; + first = 1; + for (e = 0; e < nGEQs; e++) + if (live[e] && GEQs[e].coef[v] == -1 && varCount[e] <= 2) { + if (!first) + s += ", "; + else + s += " <= "; + tmp = GEQs[e].coef[v]; + ((Problem *)this)-> + GEQs[e].coef[v] = 0; + s += print_term_to_string(&GEQs[e], 1); + ((Problem *)this)-> + GEQs[e].coef[v] = tmp; + live[e] = false; + first = 0; + } + } + } + + + for (e = 0; e < nGEQs; e++) + if (live[e]) { + if (stuffPrinted) + s += connector; + stuffPrinted = 1; + s += print_GEQ_to_string(&GEQs[e]); + } + + for (e = 0; e < nSUBs; e++) { + const eqn * eq = &SUBs[e]; + if (stuffPrinted) + s += connector; + stuffPrinted = 1; + if (eq->key > 0) { + s += orgVariable(eq->key); s += " := "; + } + else { + s += "#"; s += to_string(eq->key); s += " := "; + } + s += print_term_to_string(eq, 1); + } + } + return s; +} + + +int Problem::prettyPrintRedEquations() const { + int e, stuffPrinted = 0; + const char *connector = " && "; + + if (!variablesInitialized) + initializeVariables(); + + for (e = 0; e < nEQs; e++) { + if (EQs[e].color == EQ_RED) { + if (stuffPrinted) + fprintf(outputFile, "%s", connector); + stuffPrinted = 1; + ((Problem *)this)-> + EQs[e].color = EQ_BLACK; + printEQ(&EQs[e]); + ((Problem *)this)-> + EQs[e].color = EQ_RED; + } + } + for (e = 0; e < nGEQs; e++) { + if (GEQs[e].color == EQ_RED) { + if (stuffPrinted) + fprintf(outputFile, "%s", connector); + stuffPrinted = 1; + ((Problem *)this)-> + GEQs[e].color = EQ_BLACK; + printGEQ(&GEQs[e]); + ((Problem *)this)-> + GEQs[e].color = EQ_RED; + } + } + for (e = 0; e < nSUBs; e++) { + if (SUBs[e].color) { + if (stuffPrinted) + fprintf(outputFile, "%s", connector); + stuffPrinted = 1; + printSubstitution(e); + } + } + fflush(outputFile); + + return 0; +} + +} diff --git a/omegalib/omega/src/omega_core/oc_problems.cc b/omegalib/omega/src/omega_core/oc_problems.cc new file mode 100644 index 0000000..8b6e04c --- /dev/null +++ b/omegalib/omega/src/omega_core/oc_problems.cc @@ -0,0 +1,198 @@ +#include <omega/omega_core/oc_i.h> +#include <basic/omega_error.h> + +namespace omega { + +Problem::~Problem() { + delete[] EQs; + delete[] GEQs; +} + + +void check_number_EQs(int n) { + if (n < 0) { + fprintf(stderr,"ERROR: nEQs < 0??\n"); + exit(1); + } + + if (n > maxmaxEQs) { + // clear global variables + inApproximateMode = 0; + outerColor = 0; + + throw presburger_error("\nERROR:\n" + "An attempt was made to set the number of available equality constraints to " + to_string(n) + ".\n" + "The maximum number of equality constraints in a conjunction is " + to_string(maxmaxEQs) + ".\n" + "This limit can be changed by redefining maxmaxEQs in oc.h and recompiling.\n\n"); + + // fprintf(stderr, "\nERROR:\n"); + // fprintf(stderr, "An attempt was made to set the number of available equality constraints to %d.\n", n); + // fprintf(stderr, "The maximum number of equality constraints in a conjunction is %d.\n", maxmaxEQs); + // fprintf(stderr, "This limit can be changed by redefining maxmaxEQs in oc.h and recompiling.\n\n"); + // exit(2); + } +} + +void check_number_GEQs(int n) { + if (n < 0) { + fprintf(stderr,"ERROR: nGEQs < 0??\n"); + exit(1); + } + + if (n > maxmaxGEQs) { + // clear global variables + inApproximateMode = 0; + outerColor = 0; + + throw presburger_error("\nERROR:\n" + "An attempt was made to set the number of available inequality constraints to " + to_string(n) +".\n" + "The maximum number of inequality constraints in a conjunction is " + to_string(maxmaxGEQs) +".\n" + "This limit can be changed by redefining maxmaxGEQs in oc.h and recompiling.\n\n"); + + // fprintf(stderr, "\nERROR:\n"); + // fprintf(stderr, "An attempt was made to set the number of available inequality constraints to %d.\n", n); + // fprintf(stderr, "The maximum number of inequality constraints in a conjunction is %d.\n", maxmaxGEQs); + // fprintf(stderr, "This limit can be changed by redefining maxmaxGEQs in oc.h and recompiling.\n\n"); + // exit(2); + } +} + + +void check_number_EQs_GEQs(int e, int g) { + check_number_EQs(e); + check_number_GEQs(g); +} + + +Problem::Problem(int in_eqs, int in_geqs) { + check_number_EQs_GEQs(in_eqs, in_geqs); + allocEQs = padEQs(in_eqs); + allocGEQs = padGEQs(in_geqs); + assert(allocEQs > 0 && allocGEQs > 0); + EQs = new eqn[allocEQs]; + GEQs = new eqn[allocGEQs]; + nVars = 0; + hashVersion = omega::hashVersion; + variablesInitialized = 0; + variablesFreed = 0; + varsOfInterest = 0; + safeVars = 0; + nEQs = 0; + nGEQs = 0; + nSUBs = 0; + nMemories = 0; + isTemporary = false; +} + +Problem::Problem(const Problem & p2) { + allocEQs = padEQs(p2.nEQs); // Don't over-allocate; p2 might have too many! + allocGEQs = padGEQs(p2.nGEQs); + assert(allocEQs > 0 && allocGEQs > 0); + EQs = new eqn[allocEQs]; + GEQs = new eqn[allocGEQs]; + int e, i; + nVars = p2.nVars; + hashVersion = p2.hashVersion; + variablesInitialized = p2.variablesInitialized; + variablesFreed = p2.variablesFreed; + varsOfInterest = p2.varsOfInterest; + safeVars = p2.safeVars; + nEQs = p2.nEQs; + isTemporary = p2.isTemporary; + //nSUBs = 0; + for (e = p2.nEQs - 1; e >= 0; e--) + eqnncpy(&(EQs[e]), &(p2.EQs[e]), p2.nVars); + nGEQs = p2.nGEQs; + for (e = p2.nGEQs - 1; e >= 0; e--) + eqnncpy(&(GEQs[e]), &(p2.GEQs[e]), p2.nVars); + for (i = 0; i <= p2.nVars; i++) + var[i] = p2.var[i]; + for (i = 0; i <= maxVars; i++) + forwardingAddress[i] = p2.forwardingAddress[i]; + //nMemories = 0; + get_var_name = p2.get_var_name; + getVarNameArgs = p2.getVarNameArgs; +} + +Problem & Problem::operator=(const Problem & p2) { + if (this != &p2) { + if(allocEQs < p2.nEQs) { + delete[] EQs; + allocEQs = padEQs(p2.nEQs); + EQs = new eqn[allocEQs]; + } + if(allocGEQs < p2.nGEQs) { + delete[] GEQs; + allocGEQs = padGEQs(p2.nGEQs); + GEQs = new eqn[allocGEQs]; + } + int e, i; + nVars = p2.nVars; + hashVersion = p2.hashVersion; + variablesInitialized = p2.variablesInitialized; + variablesFreed = p2.variablesFreed; + varsOfInterest = p2.varsOfInterest; + safeVars = p2.safeVars; + nEQs = p2.nEQs; + isTemporary = p2.isTemporary; + //nSUBs = 0; + for (e = p2.nEQs - 1; e >= 0; e--) + eqnncpy(&(EQs[e]), &(p2.EQs[e]), p2.nVars); + nGEQs = p2.nGEQs; + for (e = p2.nGEQs - 1; e >= 0; e--) + eqnncpy(&(GEQs[e]), &(p2.GEQs[e]), p2.nVars); + for (i = 0; i <= p2.nVars; i++) + var[i] = p2.var[i]; + for (i = 0; i <= maxVars; i++) + forwardingAddress[i] = p2.forwardingAddress[i]; + //nMemories = 0; + get_var_name = p2.get_var_name; + getVarNameArgs = p2.getVarNameArgs; + } + return *this; +} + + +void Problem::zeroVariable(int i) { + int e; + for (e = nGEQs - 1; e >= 0; e--) GEQs[e].coef[i] = 0; + for (e = nEQs - 1; e >= 0; e--) EQs[e].coef[i] = 0; + for (e = nSUBs - 1; e >= 0; e--) SUBs[e].coef[i] = 0; +} + +/* Functions for allocating EQ's and GEQ's */ + +int Problem::newGEQ() { + if (++nGEQs > allocGEQs) { + check_number_GEQs(nGEQs); + allocGEQs = padGEQs(allocGEQs, nGEQs); + assert(allocGEQs >= nGEQs); + eqn *new_geqs = new eqn[allocGEQs]; + for (int e = nGEQs - 2; e >= 0; e--) + eqnncpy(&(new_geqs[e]), &(GEQs[e]), nVars); + delete[] GEQs; + GEQs = new_geqs; + } +// problem->GEQs[nGEQs-1].color = black; +// eqnnzero(&problem->GEQs[nGEQs-1],problem->nVars); + return nGEQs-1; +} + +int Problem::newEQ() { + if (++nEQs > allocEQs) { + check_number_EQs(nEQs); + allocEQs = padEQs(allocEQs, nEQs); + assert(allocEQs >= nEQs); + eqn *new_eqs = new eqn[allocEQs]; + for (int e = nEQs - 2; e >= 0; e--) + eqnncpy(&(new_eqs[e]), &(EQs[e]), nVars); + delete[] EQs; + EQs = new_eqs; + } +// Could do this here, but some calls to newEQ do a copy instead of a zero; +// problem->EQs[nEQs-1].color = black; +// eqnnzero(&problem->EQs[nEQs-1],problem->nVars); + return nEQs-1; +} + +} // namespace diff --git a/omegalib/omega/src/omega_core/oc_query.cc b/omegalib/omega/src/omega_core/oc_query.cc new file mode 100644 index 0000000..528b297 --- /dev/null +++ b/omegalib/omega/src/omega_core/oc_query.cc @@ -0,0 +1,478 @@ +#include <omega/omega_core/oc_i.h> + +namespace omega { + +void Problem::unprotectVariable( int v) { + int e, j, i; + coef_t t; + i = forwardingAddress[v]; + if (i < 0) { + i = -1 - i; + nSUBs--; + if (i < nSUBs) { + eqnncpy(&SUBs[i], &SUBs[nSUBs], nVars); + forwardingAddress[SUBs[i].key] = -i - 1; + } + } + else { + int bringToLife[maxVars]; + int comingBack = 0; + int e2; + for (e = nSUBs - 1; e >= 0; e--) + if ((bringToLife[e] = (SUBs[e].coef[i] != 0))) + comingBack++; + + for (e2 = nSUBs - 1; e2 >= 0; e2--) + if (bringToLife[e2]) { + + nVars++; + safeVars++; + if (safeVars < nVars) { + for (e = nGEQs - 1; e >= 0; e--) { + GEQs[e].coef[nVars] = GEQs[e].coef[safeVars]; + GEQs[e].coef[safeVars] = 0; + } + for (e = nEQs - 1; e >= 0; e--) { + EQs[e].coef[nVars] = EQs[e].coef[safeVars]; + EQs[e].coef[safeVars] = 0; + } + for (e = nSUBs - 1; e >= 0; e--) { + SUBs[e].coef[nVars] = SUBs[e].coef[safeVars]; + SUBs[e].coef[safeVars] = 0; + } + var[nVars] = var[safeVars]; + forwardingAddress[var[nVars]] = nVars; + } + else { + for (e = nGEQs - 1; e >= 0; e--) { + GEQs[e].coef[safeVars] = 0; + } + for (e = nEQs - 1; e >= 0; e--) { + EQs[e].coef[safeVars] = 0; + } + for (e = nSUBs - 1; e >= 0; e--) { + SUBs[e].coef[safeVars] = 0; + } + } + + var[safeVars] = SUBs[e2].key; + forwardingAddress[SUBs[e2].key] = safeVars; + + int neweq = newEQ(); + eqnncpy(&(EQs[neweq]), &(SUBs[e2]), nVars); + EQs[neweq].coef[safeVars] = -1; + if (e2 < nSUBs - 1) + eqnncpy(&(SUBs[e2]), &(SUBs[nSUBs - 1]), nVars); + nSUBs--; + } + + if (i < safeVars) { + j = safeVars; + for (e = nSUBs - 1; e >= 0; e--) { + t = SUBs[e].coef[j]; + SUBs[e].coef[j] = SUBs[e].coef[i]; + SUBs[e].coef[i] = t; + } + for (e = nGEQs - 1; e >= 0; e--) + if (GEQs[e].coef[j] != GEQs[e].coef[i]) { + GEQs[e].touched = true; + t = GEQs[e].coef[j]; + GEQs[e].coef[j] = GEQs[e].coef[i]; + GEQs[e].coef[i] = t; + } + for (e = nEQs - 1; e >= 0; e--) { + t = EQs[e].coef[j]; + EQs[e].coef[j] = EQs[e].coef[i]; + EQs[e].coef[i] = t; + } + { + short t; + t = var[j]; + var[j] = var[i]; + var[i] = t; + } + forwardingAddress[var[i]] = i; + forwardingAddress[var[j]] = j; + } + safeVars--; + } + chainUnprotect(); +} + +void Problem::constrainVariableSign( int color, int i, int sign) { + int nV = nVars; + int e, k, j; + + k = forwardingAddress[i]; + if (k < 0) { + k = -1 - k; + + if (sign != 0) { + e = newGEQ(); + eqnncpy(&GEQs[e], &SUBs[k], nVars); + for (j = 0; j <= nV; j++) + GEQs[e].coef[j] *= sign; + GEQs[e].coef[0]--; + GEQs[e].touched = 1; + GEQs[e].color = color; + } + else { + e = newEQ(); + eqnncpy(&EQs[e], &SUBs[k], nVars); + EQs[e].color = color; + } + } + else if (sign != 0) { + e = newGEQ(); + eqnnzero(&GEQs[e], nVars); + GEQs[e].coef[k] = sign; + GEQs[e].coef[0] = -1; + GEQs[e].touched = 1; + GEQs[e].color = color; + } + else { + e = newEQ(); + eqnnzero(&EQs[e], nVars); + EQs[e].coef[k] = 1; + EQs[e].color = color; + } + /* + unprotectVariable(i); + return (simplifyProblem(0,1,0)); + */ +} + +void Problem::constrainVariableValue( int color, int i, int value) { + int e, k; + + k = forwardingAddress[i]; + if (k < 0) { + k = -1 - k; + + e = newEQ(); + eqnncpy(&EQs[e], &SUBs[k], nVars); + EQs[e].coef[0] -= value; + + } + else { + e = newEQ(); + eqnnzero(&EQs[e], nVars); + EQs[e].coef[k] = 1; + EQs[e].coef[0] = -value; + } + EQs[e].color = color; +} + +// Analyze v1-v2 +void Problem:: query_difference(int v1, int v2, coef_t &lowerBound, coef_t &upperBound, bool &guaranteed) { + int nV = nVars; + int e,i,e2; + + coef_t lb1,ub1; + coef_t lb2,ub2; + assert(nSUBs == 0); + lowerBound = negInfinity; + lb1 = lb2 = negInfinity; + upperBound = posInfinity; + ub1 = ub2 = posInfinity; + guaranteed = true; + for (e = nEQs - 1; e >= 0; e--) { + if (EQs[e].coef[v1] == 0 && EQs[e].coef[v2] == 0) + continue; + for(i=nV;i>0;i--) + if (EQs[e].coef[i] && i!=v1 && i != v2) { + break; + } + if (i != 0) { + if (i > safeVars) { + // check to see if this variable appears anywhere else + for(e2 = nEQs-1; e2>=0;e2--) if (e != e2 && EQs[e2].coef[i]) break; + if (e2 < 0) + for(e2 = nGEQs-1; e2>=0;e2--) if (e != e2 && GEQs[e2].coef[i]) break; + if (e2 < 0) + for(e2 = nSUBs-1; e2>=0;e2--) if (e != e2 && SUBs[e2].coef[i]) break; + if (e2 >= 0) guaranteed = false; + } + else guaranteed = false; + continue; + } + if (EQs[e].coef[v1]*EQs[e].coef[v2] == -1) { + // found exact difference + coef_t d = - EQs[e].coef[v1] * EQs[e].coef[0]; + set_max(lowerBound, d); + set_min(upperBound, d); + guaranteed =true; + return; + } + else if (EQs[e].coef[v1] == 0) + lb2 = ub2 = -EQs[e].coef[0]/ EQs[e].coef[v2]; + else if (EQs[e].coef[v2] == 0) + lb1 = ub1 = -EQs[e].coef[0]/ EQs[e].coef[v1]; + else guaranteed = false; + } + + bool isDead[maxmaxGEQs]; + + for (e = nGEQs - 1; e >= 0; e--) isDead[e] = false; + int tryAgain = 1; + while (tryAgain) { + tryAgain = 0; + for (i = nVars; i > 0;i--) + if (i!= v1 && i != v2) { + for (e = nGEQs - 1; e >= 0; e--) + if (!isDead[e] && GEQs[e].coef[i]) + break; + if (e < 0) + e2 = e; + else if (GEQs[e].coef[i] > 0) { + for (e2 = e - 1; e2 >= 0; e2--) + if (!isDead[e2] && GEQs[e2].coef[i] < 0) + break; + } + else { + for (e2 = e - 1; e2 >= 0; e2--) + if (!isDead[e2] && GEQs[e2].coef[i] > 0) + break; + } + if (e2 < 0) { + int e3; + for (e3 = nSUBs - 1; e3 >= 0; e3--) + if (SUBs[e3].coef[i]) + break; + if (e3 >= 0) + continue; + for (e3 = nEQs - 1; e3 >= 0; e3--) + if (EQs[e3].coef[i]) + break; + if (e3 >= 0) + continue; + if (e >= 0) { + isDead[e] = true; + for (e--; e >= 0; e--) + if (GEQs[e].coef[i]) isDead[e] = true; + } + } + } + } + + for (e = nGEQs - 1; e >= 0; e--) + if (!isDead[e]) { + if (GEQs[e].coef[v1] == 0 && GEQs[e].coef[v2] == 0) + continue; + for(i=nV;i>0;i--) + if (GEQs[e].coef[i] && i!=v1 && i != v2) + break; + if (i != 0) { + guaranteed = false; + continue; + } + if (GEQs[e].coef[v1]*GEQs[e].coef[v2] == -1) { + // found relative difference + if (GEQs[e].coef[v1] == 1) { + // v1 - v2 + c >= 0 + set_max(lowerBound, - GEQs[e].coef[0]); + } + else { + // v2 - v1 + c >= 0 + // c >= v1-v2 + set_min(upperBound, GEQs[e].coef[0]); + } + } + else if (GEQs[e].coef[v1] == 0 && GEQs[e].coef[v2] > 0) + lb2 = -GEQs[e].coef[0]/ GEQs[e].coef[v2]; + else if (GEQs[e].coef[v1] == 0 && GEQs[e].coef[v2] < 0) + ub2 = -GEQs[e].coef[0]/ GEQs[e].coef[v2]; + else if (GEQs[e].coef[v2] == 0 && GEQs[e].coef[v1] > 0) + lb1 = -GEQs[e].coef[0]/ GEQs[e].coef[v1]; + else if (GEQs[e].coef[v2] == 0 && GEQs[e].coef[v1] < 0) + ub1 = -GEQs[e].coef[0]/ GEQs[e].coef[v1]; + else guaranteed = false; + } + + // ub1-lb2 >= v1-v2 >= lb1-ub2 + + if (negInfinity < lb2 && ub1 < posInfinity) set_min(upperBound, ub1-lb2); + if (negInfinity < lb1 && ub2 < posInfinity) set_max(lowerBound, lb1-ub2); + if (lowerBound >= upperBound) guaranteed = 1; +} + + +int Problem::queryVariable(int i, coef_t *lowerBound, coef_t *upperBound) { + int nV = nVars; + int e, j; + int isSimple; + int coupled = false; + for(j=1;j<=safeVars;j++) + if (var[j] > 0) + assert(forwardingAddress[var[j]] == j); + + assert(i > 0); + i = forwardingAddress[i]; + assert(i != 0); + + (*lowerBound) = negInfinity; + (*upperBound) = posInfinity; + + if (i < 0) { + int easy = true; + i = -i - 1; + for (j = 1; j <= nV; j++) + if (SUBs[i].coef[j] != 0) + easy = false; + if (easy) { + *upperBound = *lowerBound = SUBs[i].coef[0]; + return (false); + } + return (true); + } + + for (e = nSUBs - 1; e >= 0; e--) + if (SUBs[e].coef[i] != 0) + coupled = true; + + for (e = nEQs - 1; e >= 0; e--) + if (EQs[e].coef[i] != 0) { + isSimple = true; + for (j = 1; j <= nV; j++) + if (i != j && EQs[e].coef[j] != 0) { + isSimple = false; + coupled = true; + break; + } + if (!isSimple) + continue; + else { + *lowerBound = *upperBound = -EQs[e].coef[i] * EQs[e].coef[0]; + return (false); + } + } + for (e = nGEQs - 1; e >= 0; e--) + if (GEQs[e].coef[i] != 0) { + if (GEQs[e].key == i) { + set_max(*lowerBound, -GEQs[e].coef[0]); + } + else if (GEQs[e].key == -i) { + set_min(*upperBound, GEQs[e].coef[0]); + } + else + coupled = true; + } + return (coupled); +} + +int Problem::query_variable_bounds(int i, coef_t *l, coef_t *u) { + int coupled; + *l = negInfinity; + *u = posInfinity; + coupled = queryVariable(i, l, u); + if (!coupled || (nVars == 1 && forwardingAddress[i] == 1)) + return 0; + if (abs(forwardingAddress[i]) == 1 && nVars + nSUBs == 2 && nEQs + nSUBs == 1) { + int couldBeZero; + queryCoupledVariable(i, l, u, &couldBeZero, negInfinity, posInfinity); + return 0; + } + return 1; +} + +void Problem::queryCoupledVariable(int i, coef_t *l, coef_t *u, int *couldBeZero, coef_t lowerBound, coef_t upperBound) { + int e; + coef_t b1, b2; + const eqn *eqn; + coef_t sign; + int v; + + if (abs(forwardingAddress[i]) != 1 || nVars + nSUBs != 2 || nEQs + nSUBs != 1) { + fprintf(outputFile, "queryCoupledVariablecalled with bad parameters\n"); + printProblem(); + exit(2); + } + + if (forwardingAddress[i] == -1) { + eqn = &SUBs[0]; + sign = 1; + v = 1; + } + else { + eqn = &EQs[0]; + sign = -eqn->coef[1]; + v = 2; + } + + /* Variable i is defined in terms of variable v */ + + for (e = nGEQs - 1; e >= 0; e--) + if (GEQs[e].coef[v] != 0) { + if (GEQs[e].coef[v] == 1) { + set_max(lowerBound, -GEQs[e].coef[0]); + } + else { + set_min(upperBound, GEQs[e].coef[0]); + } + } + /* lowerBound and upperBound are bounds on the value of v */ + + if (lowerBound > upperBound) { + *l = posInfinity; + *u = negInfinity; + *couldBeZero = 0; + return; + } + if (lowerBound == negInfinity) { + if (eqn->coef[v] > 0) + b1 = sign * negInfinity; + else + b1 = -sign * negInfinity; + } + else + b1 = sign * (eqn->coef[0] + eqn->coef[v] * lowerBound); + if (upperBound == posInfinity) { + if (eqn->coef[v] > 0) + b2 = sign * posInfinity; + else + b2 = -sign * posInfinity; + } + else + b2 = sign * (eqn->coef[0] + eqn->coef[v] * upperBound); + + /* b1 and b2 are bounds on the value of i (don't know which is upper bound) */ + if (b1 <= b2) { + set_max(*l, b1); + set_min(*u, b2); + } + else { + set_max(*l, b2); + set_min(*u, b1); + } + *couldBeZero = *l <= 0 && 0 <= *u && int_mod(eqn->coef[0], abs(eqn->coef[v])) == 0; +} + + +int Problem::queryVariableSigns(int i, int dd_lt, int dd_eq, int dd_gt, coef_t lowerBound, coef_t upperBound, bool *distKnown, coef_t *dist) { + int result; + coef_t l, u; + int couldBeZero; + + l = negInfinity; + u = posInfinity; + + queryVariable(i, &l, &u); + queryCoupledVariable(i, &l, &u, &couldBeZero, lowerBound, upperBound); + result = 0; + if (l < 0) + result |= dd_gt; + if (u > 0) + result |= dd_lt; + if (couldBeZero) + result |= dd_eq; + if (l == u) { + *distKnown = 1; + *dist = l; + } + else { + *distKnown = 0; + } + return (result); +} + +} // namespace diff --git a/omegalib/omega/src/omega_core/oc_quick_kill.cc b/omegalib/omega/src/omega_core/oc_quick_kill.cc new file mode 100644 index 0000000..1b988d4 --- /dev/null +++ b/omegalib/omega/src/omega_core/oc_quick_kill.cc @@ -0,0 +1,775 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + All Rights Reserved. + + Purpose: + Quick inequality elimination. + + Notes: + + History: + 03/31/09 Use BoolSet, Chun Chen +*****************************************************************************/ + +#include <omega/omega_core/oc_i.h> +#include <vector> +#include <algorithm> +#include <basic/BoolSet.h> + +namespace omega { + +int Problem::combineToTighten() { + int effort = min(12+5*(nVars-safeVars),23); + + if (DBUG) { + fprintf(outputFile, "\nin combineToTighten (%d,%d):\n",effort,nGEQs); + printProblem(); + fprintf(outputFile, "\n"); + } + if (nGEQs > effort) { + if (TRACE) { + fprintf(outputFile, "too complicated to tighten\n"); + } + return 1; + } + + for(int e = 1; e < nGEQs; e++) { + for(int e2 = 0; e2 < e; e2++) { + coef_t g = 0; + + bool has_wildcard = false; + bool has_wildcard2 = false; + for (int i = nVars; i > safeVars; i--) { + coef_t a = GEQs[e].coef[i]; + coef_t b = GEQs[e2].coef[i]; + g = gcd(g, abs(a+b)); + if (a != 0) + has_wildcard = true; + if (b != 0) + has_wildcard2 = true; + } + + coef_t c, c2; + if ((has_wildcard && !has_wildcard2) || (!has_wildcard && has_wildcard2)) + c = 0; + else + c = -1; + for (int i = safeVars; i >= 1; i--) { + coef_t a = GEQs[e].coef[i]; + coef_t b = GEQs[e2].coef[i]; + if (a != 0 || b != 0) { + g = gcd(g, abs(a+b)); + + if (c < 0) { + if (g == 1) + break; + } + else if ((a>0 && b<0) || (a<0 && b>0)) { + if (c == 0) { + try { + coef_t prod = lcm(abs(a), abs(b)); + c = prod/abs(a); + c2 = prod/abs(b); + } + catch (std::overflow_error) { + c = -1; + } + } + else { + if (c*a+c2*b != 0) + c = -1; + } + } + else { + c = -1; + } + } + } + + bool done_unit_combine = false; + if (g > 1 && (GEQs[e].coef[0] + GEQs[e2].coef[0]) % g != 0) { + int e3 = newGEQ(); + for(int i = nVars; i >= 1; i--) { + GEQs[e3].coef[i] = (GEQs[e].coef[i] + GEQs[e2].coef[i])/g; + } + GEQs[e3].coef[0] = int_div(GEQs[e].coef[0] + GEQs[e2].coef[0], g); + GEQs[e3].color = GEQs[e].color || GEQs[e2].color; + GEQs[e3].touched = 1; + if (DBUG) { + fprintf(outputFile, "Combined "); + printGEQ(&GEQs[e]); + fprintf(outputFile,"\n and "); + printGEQ(&GEQs[e2]); + fprintf(outputFile,"\n to get #%d: ",e3); + printGEQ(&GEQs[e3]); + fprintf(outputFile,"\n\n"); + } + + done_unit_combine = true; + if (nGEQs > effort+5 || nGEQs > maxmaxGEQs-10) goto doneCombining; + } + + if (c > 0 && !(c == 1 && c2 == 1 && done_unit_combine)) { + bool still_has_wildcard = false; + coef_t p[nVars-safeVars]; + for (int i = nVars; i > safeVars; i--) { + p[i-safeVars-1] = c * GEQs[e].coef[i] + c2 * GEQs[e2].coef[i]; + if (p[i-safeVars-1] != 0) + still_has_wildcard = true; + } + if (still_has_wildcard) { + int e3 = newGEQ(); + for(int i = nVars; i > safeVars; i--) + GEQs[e3].coef[i] = p[i-safeVars-1]; + for (int i = safeVars; i > 0; i--) + GEQs[e3].coef[i] = 0; + GEQs[e3].coef[0] = c * GEQs[e].coef[0] + c2 * GEQs[e2].coef[0]; + GEQs[e3].color = GEQs[e].color || GEQs[e2].color; + GEQs[e3].touched = 1; + if (DBUG) { + fprintf(outputFile, "Additionally combined "); + printGEQ(&GEQs[e]); + fprintf(outputFile,"\n and "); + printGEQ(&GEQs[e2]); + fprintf(outputFile,"\n to get #%d: ",e3); + printGEQ(&GEQs[e3]); + fprintf(outputFile,"\n\n"); + } + + if (nGEQs > effort+5 || nGEQs > maxmaxGEQs-10) goto doneCombining; + } + } + } + } + +doneCombining: + if (normalize() == normalize_false) return 0; + while (nEQs) { + if (!solveEQ()) return 0; + if (normalize() == normalize_false) return 0; + } + return 1; +} + + +void Problem::noteEssential(int onlyWildcards) { + for (int e = nGEQs - 1; e >= 0; e--) { + GEQs[e].essential = 0; + GEQs[e].varCount = 0; + } + if (onlyWildcards) { + for (int e = nGEQs - 1; e >= 0; e--) { + GEQs[e].essential = 1; + for (int i = nVars; i > safeVars; i--) + if (GEQs[e].coef[i] < -1 || GEQs[e].coef[i] > 1) { + GEQs[e].essential = 0; + break; + } + } + } + for (int i = nVars; i >= 1; i--) { + int onlyLB = -1; + int onlyUB = -1; + for (int e = nGEQs - 1; e >= 0; e--) + if (GEQs[e].coef[i] > 0) { + GEQs[e].varCount ++; + if (onlyLB == -1) onlyLB = e; + else onlyLB = -2; + } + else if (GEQs[e].coef[i] < 0) { + GEQs[e].varCount ++; + if (onlyUB == -1) onlyUB = e; + else onlyUB = -2; + } + if (onlyUB >= 0) { + if (DBUG) { + fprintf(outputFile,"only UB: "); + printGEQ(&GEQs[onlyUB]); + fprintf(outputFile,"\n"); + } + GEQs[onlyUB].essential = 1; + } + if (onlyLB >= 0) { + if (DBUG) { + fprintf(outputFile,"only LB: "); + printGEQ(&GEQs[onlyLB]); + fprintf(outputFile,"\n"); + } + GEQs[onlyLB].essential = 1; + } + } + for (int e = nGEQs - 1; e >= 0; e--) + if (!GEQs[e].essential && GEQs[e].varCount > 1) { + int i1,i2,i3; + for (i1 = nVars; i1 >= 1; i1--) if (GEQs[e].coef[i1]) break; + for (i2 = i1-1; i2 >= 1; i2--) if (GEQs[e].coef[i2]) break; + for (i3 = i2-1; i3 >= 1; i3--) if (GEQs[e].coef[i3]) break; + assert(i2 >= 1); + int e2; + for (e2 = nGEQs - 1; e2 >= 0; e2--) + if (e!=e2) { + coef_t crossProduct; + crossProduct = GEQs[e].coef[i1]*GEQs[e2].coef[i1]; + crossProduct += GEQs[e].coef[i2]*GEQs[e2].coef[i2]; + for (int i = i3; i >= 1; i--) + if (GEQs[e2].coef[i]) + crossProduct += GEQs[e].coef[i]*GEQs[e2].coef[i]; + if (crossProduct > 0) { + if (DBUG) fprintf(outputFile,"Cross product of %d and %d is " coef_fmt "\n", e, e2, crossProduct); + break; + } + } + if (e2 < 0) GEQs[e].essential = 1; + } + if (DBUG) { + fprintf(outputFile,"Computed essential equations\n"); + fprintf(outputFile,"essential equations:\n"); + for (int e = 0; e < nGEQs; e++) + if (GEQs[e].essential) { + printGEQ(&GEQs[e]); + fprintf(outputFile,"\n"); + } + fprintf(outputFile,"potentially redundant equations:\n"); + for (int e = 0; e < nGEQs; e++) + if (!GEQs[e].essential) { + printGEQ(&GEQs[e]); + fprintf(outputFile,"\n"); + } + } +} + + +int Problem::findDifference(int e, int &v1, int &v2) { + // if 1 returned, eqn E is of form v1 -coef >= v2 + for(v1=1;v1<=nVars;v1++) + if (GEQs[e].coef[v1]) break; + for(v2=v1+1;v2<=nVars;v2++) + if (GEQs[e].coef[v2]) break; + if (v2 > nVars) { + if (GEQs[e].coef[v1] == -1) { + v2 = v1; + v1 = 0; + return 1; + } + if (GEQs[e].coef[v1] == 1) { + v2 = 0; + return 1; + } + return 0; + } + if (GEQs[e].coef[v1] * GEQs[e].coef[v2] != -1) return 0; + if (GEQs[e].coef[v1] < 0) std::swap(v1,v2); + return 1; +} + + +namespace { + struct succListStruct { + int num; + int notEssential; + int var[maxVars]; + coef_t diff[maxVars]; + int eqn[maxVars]; + }; +} + + +int Problem::chainKill(int color, int onlyWildcards) { + int v1,v2,e; + int essentialPred[maxVars]; + int redundant[maxmaxGEQs]; + int inChain[maxVars]; + int goodStartingPoint[maxVars]; + int tryToEliminate[maxmaxGEQs]; + int triedDoubleKill = 0; + + succListStruct succ[maxVars]; + +restart: + + int anyToKill = 0; + int anyKilled = 0; + int canHandle = 0; + + for(v1=0;v1<=nVars;v1++) { + succ[v1].num = 0; + succ[v1].notEssential = 0; + goodStartingPoint[v1] = 0; + inChain[v1] = -1; + essentialPred[v1] = 0; + } + + int essentialEquations = 0; + for (e = 0; e < nGEQs; e++) { + redundant[e] = 0; + tryToEliminate[e] = !GEQs[e].essential; + if (GEQs[e].essential) essentialEquations++; + if (color && !GEQs[e].color) tryToEliminate[e] = 0; + } + if (essentialEquations == nGEQs) return 0; + if (2*essentialEquations < nVars) return 1; + + for (e = 0; e < nGEQs; e++) + if (tryToEliminate[e] && GEQs[e].varCount <= 2 && findDifference(e,v1,v2)) { + assert(v1 == 0 || GEQs[e].coef[v1] == 1); + assert(v2 == 0 || GEQs[e].coef[v2] == -1); + succ[v2].notEssential++; + int s = succ[v2].num++; + succ[v2].eqn[s] = e; + succ[v2].var[s] = v1; + succ[v2].diff[s] = -GEQs[e].coef[0]; + goodStartingPoint[v2] = 1; + anyToKill++; + canHandle++; + } + if (!anyToKill) { + return canHandle < nGEQs; + } + for (e = 0; e < nGEQs; e++) + if (!tryToEliminate[e] && GEQs[e].varCount <= 2 && findDifference(e,v1,v2)) { + assert(v1 == 0 || GEQs[e].coef[v1] == 1); + assert(v2 == 0 || GEQs[e].coef[v2] == -1); + int s = succ[v2].num++; + essentialPred[v1]++; + succ[v2].eqn[s] = e; + succ[v2].var[s] = v1; + succ[v2].diff[s] = -GEQs[e].coef[0]; + canHandle++; + } + + + if (DBUG) { + int s; + fprintf(outputFile,"In chainkill: [\n"); + for(v1 = 0;v1<=nVars;v1++) { + fprintf(outputFile,"#%d <= %s: ",essentialPred[v1],variable(v1)); + for(s=0;s<succ[v1].notEssential;s++) + fprintf(outputFile," %s(" coef_fmt ") ",variable(succ[v1].var[s]), succ[v1].diff[s]); + for(;s<succ[v1].num;s++) + fprintf(outputFile," %s[" coef_fmt "] ",variable(succ[v1].var[s]), succ[v1].diff[s]); + fprintf(outputFile,"\n"); + } + } + + for(;v1<=nVars;v1++) + if (succ[v1].num == 1 && succ[v1].notEssential == 1) { + succ[v1].notEssential--; + essentialPred[succ[v1].var[succ[v1].notEssential]]++; + } + + if (DBUG) fprintf(outputFile,"Trying quick double kill:\n"); + int s1a,s1b,s2; + int v3; + for(v1 = 0;v1<=nVars;v1++) + for(s1a=0;s1a<succ[v1].notEssential;s1a++) { + v3 = succ[v1].var[s1a]; + for(s1b=0;s1b<succ[v1].num;s1b++) + if (s1a != s1b) { + v2 = succ[v1].var[s1b]; + for(s2=0;s2<succ[v2].num;s2++) + if (succ[v2].var[s2] == v3 && succ[v1].diff[s1b] + succ[v2].diff[s2] >= succ[v1].diff[s1a]) { + if (DBUG) { + fprintf(outputFile,"quick double kill: "); + printGEQ(&GEQs[succ[v1].eqn[s1a]]); + fprintf(outputFile,"\n"); + } + redundant[succ[v1].eqn[s1a]] = 1; + anyKilled++; + anyToKill--; + goto nextVictim; + } + } + nextVictim: v1 = v1; + } + if (anyKilled) { + for (e = nGEQs-1; e >= 0;e--) + if (redundant[e]) { + if (DBUG) { + fprintf(outputFile,"Deleting "); + printGEQ(&GEQs[e]); + fprintf(outputFile,"\n"); + } + deleteGEQ(e); + } + + if (!anyToKill) return canHandle < nGEQs; + noteEssential(onlyWildcards); + triedDoubleKill = 1; + goto restart; + } + + for(v1 = 0;v1<=nVars;v1++) + if (succ[v1].num == succ[v1].notEssential && succ[v1].notEssential > 0) { + succ[v1].notEssential--; + essentialPred[succ[v1].var[succ[v1].notEssential]]++; + } + + while (1) { + int chainLength; + int chain[maxVars]; + coef_t distance[maxVars]; + // pick a place to start + for(v1 = 0;v1<=nVars;v1++) + if (essentialPred[v1] == 0 && succ[v1].num > succ[v1].notEssential) + break; + if (v1 > nVars) + for(v1 = 0;v1<=nVars;v1++) + if (goodStartingPoint[v1] && succ[v1].num > succ[v1].notEssential) + break; + if (v1 > nVars) break; + + chainLength = 1; + chain[0] = v1; + distance[0] = 0; + inChain[v1] = 0; + int s; + + while (succ[v1].num > succ[v1].notEssential) { + s = succ[v1].num-1; + if (inChain[succ[v1].var[s]] >= 0) { + // Found cycle, don't do anything with them yet + break; + } + succ[v1].num = s; + + distance[chainLength]= distance[chainLength-1] + succ[v1].diff[s]; + v1 = chain[chainLength] = succ[v1].var[s]; + essentialPred[v1]--; + assert(essentialPred[v1] >= 0); + inChain[v1] = chainLength; + chainLength++; + } + + + int c; + if (DBUG) { + fprintf(outputFile,"Found chain: \n"); + for (c = 0; c < chainLength; c++) + fprintf(outputFile,"%s:" coef_fmt " ",variable(chain[c]), distance[c]); + fprintf(outputFile,"\n"); + } + + + for (c = 0; c < chainLength; c++) { + v1 = chain[c]; + for(s=0;s<succ[v1].notEssential;s++) { + if (DBUG) + fprintf(outputFile,"checking for %s + " coef_fmt " <= %s \n", variable(v1), succ[v1].diff[s], variable(succ[v1].var[s])); + if (inChain[succ[v1].var[s]] > c+1) { + if (DBUG) + fprintf(outputFile,"%s + " coef_fmt " <= %s is in chain\n", variable(v1), distance[inChain[succ[v1].var[s]]]- distance[c], variable(succ[v1].var[s])); + if ( distance[inChain[succ[v1].var[s]]]- distance[c] >= succ[v1].diff[s]) { + if (DBUG) + fprintf(outputFile,"%s + " coef_fmt " <= %s is redundant\n", variable(v1),succ[v1].diff[s], variable(succ[v1].var[s])); + redundant[succ[v1].eqn[s]] = 1; + } + } + } + } + for (c = 0; c < chainLength; c++) + inChain[chain[c]] = -1; + } + + for (e = nGEQs-1; e >= 0;e--) + if (redundant[e]) { + if (DBUG) { + fprintf(outputFile,"Deleting "); + printGEQ(&GEQs[e]); + fprintf(outputFile,"\n"); + } + deleteGEQ(e); + anyKilled = 1; + } + + if (anyKilled) noteEssential(onlyWildcards); + + if (anyKilled && DBUG) { + fprintf(outputFile,"\nResult:\n"); + printProblem(); + } + if (DBUG) { + fprintf(outputFile,"] end chainkill\n"); + printProblem(); + } + return canHandle < nGEQs; +} + + +namespace { + struct varCountStruct { + int e; + int safeVarCount; + int wildVarCount; + varCountStruct(int e_, int count1_, int count2_) { + e = e_; + safeVarCount = count1_; + wildVarCount = count2_; } + }; + bool operator<(const varCountStruct &a, const varCountStruct &b) { + if (a.wildVarCount < b.wildVarCount) + return true; + else if (a.wildVarCount > b.wildVarCount) + return false; + else + return a.safeVarCount < b.safeVarCount; + } +} + + +// +// Deduct redundant inequalities by combination of any two inequalities. +// Return value: 0 (no solution), +// 1 (nothing killed), +// 2 (some inequality killed). +// +int Problem::quickKill(int onlyWildcards, bool desperate) { + if (!onlyWildcards && !combineToTighten()) + return 0; + noteEssential(onlyWildcards); + int moreToDo = chainKill(0, onlyWildcards); + +#ifdef NDEBUG + if (!moreToDo) return 1; +#endif + + + if (!desperate && nGEQs > 256) { // original 60, increased by chun + if (TRACE) { + fprintf(outputFile, "%d inequalities are too complicated to quick kill\n", nGEQs); + } + return 1; + } + + if (DBUG) { + fprintf(outputFile, "in eliminate Redudant:\n"); + printProblem(); + } + + int isDead[nGEQs]; + std::vector<varCountStruct> killOrder; + std::vector<BoolSet<> > P(nGEQs, BoolSet<>(nVars)), Z(nGEQs, BoolSet<>(nVars)), N(nGEQs, BoolSet<>(nVars)); + BoolSet<> PP, PZ, PN; // possible Positives, possible zeros & possible negatives + + for (int e = nGEQs - 1; e >= 0; e--) { + isDead[e] = 0; + int safeVarCount = 0; + int wildVarCount = 0; + for (int i = nVars; i >= 1; i--) { + if (GEQs[e].coef[i] == 0) + Z[e].set(i-1); + else { + if (i > safeVars) + wildVarCount++; + else + safeVarCount++; + if (GEQs[e].coef[i] < 0) + N[e].set(i-1); + else + P[e].set(i-1); + } + } + + if (!GEQs[e].essential || wildVarCount > 0) + killOrder.push_back(varCountStruct(e, safeVarCount, wildVarCount)); + } + + sort(killOrder.begin(), killOrder.end()); + + if (DEBUG) { + fprintf(outputFile,"Prefered kill order:\n"); + for (int e3I = killOrder.size()-1; e3I >= 0; e3I--) { + fprintf(outputFile,"%2d: ",nGEQs-1-e3I); + printGEQ(&GEQs[killOrder[e3I].e]); + fprintf(outputFile,"\n"); + } + } + + int e3U = killOrder.size()-1; + while (e3U >= 0) { + // each round of elimination is for inequalities of same complexity and rounds are at descending complexity order + int e3L = e3U-1; + for(; e3L >= 0; e3L--) + if (killOrder[e3L].safeVarCount+killOrder[e3L].wildVarCount != killOrder[e3U].safeVarCount + killOrder[e3U].wildVarCount) + break; + + // check if e3 can be eliminated from combination of e1 and e2 + for (int e1 = 0; e1 < nGEQs; e1++) + if (!isDead[e1]) + for (int e2 = e1+1; e2 < nGEQs; e2++) + if (!isDead[e2]) { + coef_t alpha = 0; + int p, q; + for (p = nVars; p > 1; p--) + for (q = p - 1; q > 0; q--) { + try { + alpha = check_mul(GEQs[e1].coef[p], GEQs[e2].coef[q]) - check_mul(GEQs[e2].coef[p], GEQs[e1].coef[q]); + } + catch (std::overflow_error) { + continue; + } + if (alpha != 0) + goto foundPQ; + } + continue; + + foundPQ: + PZ = (Z[e1] & Z[e2]) | (P[e1] & N[e2]) | (N[e1] & P[e2]); + PP = P[e1] | P[e2]; + PN = N[e1] | N[e2]; + if (DEBUG) { + fprintf(outputFile,"Considering combination of "); + printGEQ(&(GEQs[e1])); + fprintf(outputFile," and "); + printGEQ(&(GEQs[e2])); + fprintf(outputFile,"\n"); + } + + for (int e3I = e3U; e3I > e3L; e3I--) { + int e3 = killOrder[e3I].e; + if (!isDead[e3] && e3 != e1 && e3 != e2) + try { + coef_t alpha1, alpha2, alpha3; + + if (!PZ.imply(Z[e3])) + goto nextE3; + + alpha1 = check_mul(GEQs[e2].coef[q], GEQs[e3].coef[p]) - check_mul(GEQs[e2].coef[p], GEQs[e3].coef[q]); + alpha2 = -(check_mul(GEQs[e1].coef[q], GEQs[e3].coef[p]) - check_mul(GEQs[e1].coef[p], GEQs[e3].coef[q])); + alpha3 = alpha; + + if (alpha1 < 0) { + alpha1 = -alpha1; + alpha2 = -alpha2; + alpha3 = -alpha3; + } + if (alpha1 == 0 || alpha2 <= 0) + goto nextE3; + + { + coef_t g = gcd(gcd(alpha1, alpha2), abs(alpha3)); + alpha1 /= g; + alpha2 /= g; + alpha3 /= g; + } + + if (DEBUG) { + fprintf(outputFile, coef_fmt "e1 + " coef_fmt "e2 = " coef_fmt "e3: ",alpha1,alpha2,alpha3); + printGEQ(&(GEQs[e3])); + fprintf(outputFile,"\n"); + } + + if (alpha3 > 0) { // trying to prove e3 is redundant + if (!GEQs[e3].color && (GEQs[e1].color || GEQs[e2].color)) { + goto nextE3; + } + if (!PP.imply(P[e3]) | !PN.imply(N[e3])) + goto nextE3; + + // verify alpha1*v1+alpha2*v2 = alpha3*v3 + for (int k = nVars; k >= 1; k--) + if (check_mul(alpha3, GEQs[e3].coef[k]) != check_mul(alpha1, GEQs[e1].coef[k]) + check_mul(alpha2, GEQs[e2].coef[k])) + goto nextE3; + + coef_t c = check_mul(alpha1, GEQs[e1].coef[0]) + check_mul(alpha2, GEQs[e2].coef[0]); + if (c < check_mul(alpha3, (GEQs[e3].coef[0] + 1))) { + if (DBUG) { + fprintf(outputFile, "found redundant inequality\n"); + fprintf(outputFile, "alpha1, alpha2, alpha3 = " coef_fmt "," coef_fmt "," coef_fmt "\n", alpha1, alpha2, alpha3); + printGEQ(&(GEQs[e1])); + fprintf(outputFile, "\n"); + printGEQ(&(GEQs[e2])); + fprintf(outputFile, "\n=> "); + printGEQ(&(GEQs[e3])); + fprintf(outputFile, "\n\n"); + assert(moreToDo); + } + + isDead[e3] = 1; + } + } + else { // trying to prove e3 <= 0 or e3 = 0 + if (!PN.imply(P[e3]) | !PP.imply(N[e3])) + goto nextE3; + + // verify alpha1*v1+alpha2*v2 = alpha3*v3 + for (int k = nVars; k >= 1; k--) + if (check_mul(alpha3, GEQs[e3].coef[k]) != check_mul(alpha1, GEQs[e1].coef[k]) + check_mul(alpha2, GEQs[e2].coef[k])) + goto nextE3; + + if (DEBUG) { + fprintf(outputFile,"All but constant term checked\n"); + } + coef_t c = check_mul(alpha1, GEQs[e1].coef[0]) + check_mul(alpha2, GEQs[e2].coef[0]); + if (DEBUG) { + fprintf(outputFile,"All but constant term checked\n"); + fprintf(outputFile,"Constant term is " coef_fmt " vs " coef_fmt "\n", + alpha3*GEQs[e3].coef[0], + alpha3*(GEQs[e3].coef[0]-1)); + } + if (c < check_mul(alpha3, (GEQs[e3].coef[0]))) { + // we just proved e3 < 0, so no solutions exist + if (DBUG) { + fprintf(outputFile, "found implied over tight inequality\n"); + fprintf(outputFile, "alpha1, alpha2, alpha3 = " coef_fmt "," coef_fmt "," coef_fmt "\n", alpha1, alpha2, -alpha3); + printGEQ(&(GEQs[e1])); + fprintf(outputFile, "\n"); + printGEQ(&(GEQs[e2])); + fprintf(outputFile, "\n=> not "); + printGEQ(&(GEQs[e3])); + fprintf(outputFile, "\n\n"); + } + return 0; + } + else if (!GEQs[e3].color && (GEQs[e1].color || GEQs[e2].color)) { + goto nextE3; + } + else if (c < check_mul(alpha3, (GEQs[e3].coef[0] - 1))) { + // we just proved e3 <= 0, so e3 = 0 + if (DBUG) { + fprintf(outputFile, "found implied tight inequality\n"); + fprintf(outputFile, "alpha1, alpha2, alpha3 = " coef_fmt "," coef_fmt "," coef_fmt "\n", alpha1, alpha2, -alpha3); + printGEQ(&(GEQs[e1])); + fprintf(outputFile, "\n"); + printGEQ(&(GEQs[e2])); + fprintf(outputFile, "\n=> inverse "); + printGEQ(&(GEQs[e3])); + fprintf(outputFile, "\n\n"); + } + int neweq = newEQ(); + eqnncpy(&EQs[neweq], &GEQs[e3], nVars); + addingEqualityConstraint(neweq); + isDead[e3] = 1; + } + } + nextE3:; + } + catch (std::overflow_error) { + continue; + } + } + } + + e3U = e3L; + } + + bool anything_killed = false; + for (int e = nGEQs - 1; e >= 0; e--) { + if (isDead[e]) { + anything_killed = true; + deleteGEQ(e); + } + } + + if (DBUG) { + fprintf(outputFile,"\nResult:\n"); + printProblem(); + } + + if (anything_killed) + return 2; + else + return 1; +} + +} // namespace diff --git a/omegalib/omega/src/omega_core/oc_simple.cc b/omegalib/omega/src/omega_core/oc_simple.cc new file mode 100644 index 0000000..0e492db --- /dev/null +++ b/omegalib/omega/src/omega_core/oc_simple.cc @@ -0,0 +1,1373 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + All Rights Reserved. + + Purpose: + Support functions for solving a problem. + + Notes: + + History: + 10/13/08 Complete back substitution process, Chun Chen. + 05/28/09 Extend normalize process to handle redundancy involving + wilddcards, Chun Chen +*****************************************************************************/ + +#include <omega/omega_core/oc_i.h> +#include <basic/BoolSet.h> +#include <algorithm> +#include <vector> + +namespace omega { + +int checkIfSingleVar(eqn* e, int i) { + for (; i > 0; i--) + if (e->coef[i]) { + i--; + break; + } + for (; i > 0; i--) + if (e->coef[i]) + break; + return (i == 0); +} + + +int singleVarGEQ(eqn* e) { + return !e->touched && e->key != 0 && -maxVars <= e->key && e->key <= maxVars; +} + + +void checkVars(int nVars) { + if (nVars > maxVars) { + fprintf(stderr, "\nERROR:\n"); + fprintf(stderr, "An attempt was made to create a conjunction with %d variables.\n", nVars); + fprintf(stderr, "The current limit on variables in a single conjunction is %d.\n", maxVars); + fprintf(stderr, "This limit can be changed by changing the #define of maxVars in oc.h.\n\n"); + exit(2); + } +} + + +void Problem::difficulty(int &numberNZs, coef_t &maxMinAbsCoef, coef_t &sumMinAbsCoef) const { + numberNZs=0; + maxMinAbsCoef=0; + sumMinAbsCoef=0; + for (int e = 0; e < nGEQs; e++) { + coef_t maxCoef = 0; + for(int i = 1;i <= nVars;i++) + if (GEQs[e].coef[i]!=0) { + coef_t a = abs(GEQs[e].coef[i]); + maxCoef = max(maxCoef,a); + numberNZs++; + } + coef_t nextCoef = 0; + for(int i = 1;i <= nVars;i++) + if (GEQs[e].coef[i]!=0) { + coef_t a = abs(GEQs[e].coef[i]); + if (a < maxCoef) nextCoef = max(nextCoef,a); + else if (a == maxCoef) maxCoef = 0x7fffffff; + } + maxMinAbsCoef = max(maxMinAbsCoef,nextCoef); + sumMinAbsCoef += nextCoef; + } + + for (int e = 0; e < nEQs; e++) { + coef_t maxCoef = 0; + for(int i = 1;i <= nVars;i++) + if (EQs[e].coef[i]!=0) { + coef_t a = abs(EQs[e].coef[i]); + maxCoef = max(maxCoef,a); + numberNZs++; + } + coef_t nextCoef = 0; + for(int i = 1;i <= nVars;i++) + if (EQs[e].coef[i]!=0) { + coef_t a = abs(EQs[e].coef[i]); + if (a < maxCoef) nextCoef = max(nextCoef,a); + else if (a == maxCoef) maxCoef = 0x7fffffff; + } + maxMinAbsCoef = max(maxMinAbsCoef,nextCoef); + sumMinAbsCoef += nextCoef; + } +} + +int Problem::countRedGEQs() const { + int result = 0; + for (int e = 0; e < nGEQs; e++) + if (GEQs[e].color == EQ_RED) result++; + return result; +} + +int Problem::countRedEQs() const { + int result = 0; + for (int e = 0; e < nEQs; e++) + if (EQs[e].color == EQ_RED) result++; + return result; +} + +int Problem::countRedEquations() const { + int result = 0; + for (int e = 0; e < nEQs; e++) + if (EQs[e].color == EQ_RED) { + int i; + for (i = nVars; i > 0; i--) if (EQs[e].coef[i]) break; + if (i == 0 && EQs[e].coef[0] != 0) return 0; + else result+=2; + } + for (int e = 0; e < nGEQs; e++) + if (GEQs[e].color == EQ_RED) result+=1; + for (int e = 0; e < nMemories; e++) + switch(redMemory[e].kind ) { + case redEQ: + case redStride: + e++; + case redLEQ: + case redGEQ: + e++; + case notRed: + ; /* avoid warning about notRed not handled */ + } + return result; +} + +void Problem::deleteBlack() { + int RedVar[maxVars]; + for(int i = safeVars+1;i <= nVars;i++) RedVar[i] = 0; + + assert(nSUBs == 0); + + for (int e = nEQs-1; e >= 0; e--) + if (EQs[e].color != EQ_RED) { + eqnncpy(&EQs[e],&EQs[nEQs-1], nVars); + nEQs--; + } + else + for(int i = safeVars+1;i <= nVars;i++) + if (EQs[e].coef[i]) RedVar[i] = 1; + + for (int e = nGEQs-1; e >= 0; e--) + if (GEQs[e].color != EQ_RED) { + eqnncpy(&GEQs[e],&GEQs[nGEQs-1], nVars); + nGEQs--; + } + else + for(int i = safeVars+1;i <= nVars;i++) + if (GEQs[e].coef[i]) RedVar[i] = 1; + + assert(nSUBs == 0); + + for(int i = nVars; i > safeVars;i--) { + if (!RedVar[i]) deleteVariable(i); + } +} + + +void Problem::deleteRed() { + int BlackVar[maxVars]; + for(int i = safeVars+1;i <= nVars;i++) BlackVar[i] = 0; + + assert(nSUBs == 0); + for (int e = nEQs-1; e >=0; e--) + if (EQs[e].color) { + eqnncpy(&EQs[e],&EQs[nEQs-1], nVars); + nEQs--; + } + else + for(int i = safeVars+1;i <= nVars;i++) + if (EQs[e].coef[i]) BlackVar[i] = 1; + + for (int e = nGEQs-1; e >=0; e--) + if (GEQs[e].color) { + eqnncpy(&GEQs[e],&GEQs[nGEQs-1], nVars); + nGEQs--; + } + else + for(int i = safeVars+1;i <= nVars;i++) + if (GEQs[e].coef[i]) BlackVar[i] = 1; + + assert(nSUBs == 0); + + for(int i = nVars; i> safeVars;i--) { + if (!BlackVar[i]) deleteVariable(i); + } +} + + +void Problem::turnRedBlack() { + for (int e = nEQs-1; e >= 0; e--) EQs[e].color = 0; + for (int e = nGEQs-1; e >= 0; e--) GEQs[e].color = 0; +} + + +void Problem::useWildNames() { + for(int i = safeVars+1; i <= nVars; i++) nameWildcard(i); +} + + +void negateCoefficients(eqn* eqn, int nVars) { + for (int i = nVars; i >= 0; i--) + eqn-> coef[i] = -eqn->coef[i]; + eqn->touched = true; +} + + +void Problem::negateGEQ(int e) { + negateCoefficients(&GEQs[e],nVars); + GEQs[e].coef[0]--; +} + + +void Problem:: deleteVariable(int i) { + if (i < safeVars) { + int j = safeVars; + for (int e = nGEQs - 1; e >= 0; e--) { + GEQs[e].touched = true; + GEQs[e].coef[i] = GEQs[e].coef[j]; + GEQs[e].coef[j] = GEQs[e].coef[nVars]; + } + for (int e = nEQs - 1; e >= 0; e--) { + EQs[e].coef[i] = EQs[e].coef[j]; + EQs[e].coef[j] = EQs[e].coef[nVars]; + } + for (int e = nSUBs - 1; e >= 0; e--) { + SUBs[e].coef[i] = SUBs[e].coef[j]; + SUBs[e].coef[j] = SUBs[e].coef[nVars]; + } + var[i] = var[j]; + var[j] = var[nVars]; + } + else if (i < nVars) { + for (int e = nGEQs - 1; e >= 0; e--) + if (GEQs[e].coef[nVars]) { + GEQs[e].coef[i] = GEQs[e].coef[nVars]; + GEQs[e].touched = true; + } + for (int e = nEQs - 1; e >= 0; e--) + EQs[e].coef[i] = EQs[e].coef[nVars]; + for (int e = nSUBs - 1; e >= 0; e--) + SUBs[e].coef[i] = SUBs[e].coef[nVars]; + var[i] = var[nVars]; + } + if (i <= safeVars) + safeVars--; + nVars--; +} + + +void Problem::setInternals() { + if (!variablesInitialized) { + initializeVariables(); + } + + var[0] = 0; + nextWildcard = 0; + for(int i = 1;i <= nVars;i++) + if (var[i] < 0) + var[i] = --nextWildcard; + + assert(nextWildcard >= -maxWildcards); + + CHECK_FOR_DUPLICATE_VARIABLE_NAMES; + + int v = nSUBs; + for(int i = 1;i <= safeVars;i++) if (var[i] > 0) v++; + varsOfInterest = v; + + if (nextKey * 3 > maxKeys) { + omega::hashVersion++; + nextKey = maxVars + 1; + for (int e = nGEQs - 1; e >= 0; e--) + GEQs[e].touched = true; + for (int i = 0; i < hashTableSize; i++) + hashMaster[i].touched = -1; + hashVersion = omega::hashVersion; + } + else if (hashVersion != omega::hashVersion) { + for (int e = nGEQs - 1; e >= 0; e--) + GEQs[e].touched = true; + hashVersion = omega::hashVersion; + } +} + + +void Problem::setExternals() { + for (int i = 1; i <= safeVars; i++) + forwardingAddress[var[i]] = i; + for (int i = 0; i < nSUBs; i++) + forwardingAddress[SUBs[i].key] = -i - 1; +} + + +void setOutputFile(FILE * file) { + /* sets the file to which printProblem should send its output to "file" */ + + outputFile = file; +} + + +void setPrintLevel(int level) { + /* Sets the nber of points printed before constraints in printProblem */ + headerLevel = level; +} + + +void Problem::putVariablesInStandardOrder() { + for(int i = 1;i <= safeVars;i++) { + int b = i; + for(int j=i+1;j<=safeVars;j++) { + if (var[b] < var[j]) b = j; + } + if (b != i) swapVars(i,b); + } +} + + +void Problem::nameWildcard(int i) { + int j; + do { + --nextWildcard; + if (nextWildcard < -maxWildcards) + nextWildcard = -1; + var[i] = nextWildcard; + for(j = nVars; j > 0;j--) if (i!=j && var[j] == nextWildcard) break; + } while (j != 0); +} + + +int Problem::protectWildcard(int i) { + assert (i > safeVars); + if (i != safeVars+1) swapVars(i,safeVars+1); + safeVars++; + nameWildcard(safeVars); + return safeVars; +} + + +int Problem::addNewProtectedWildcard() { + int i = ++safeVars; + nVars++; + if (nVars != i) { + for (int e = nGEQs - 1; e >= 0; e--) { + if (GEQs[e].coef[i] != 0) + GEQs[e].touched = true; + GEQs[e].coef[nVars] = GEQs[e].coef[i]; + } + for (int e = nEQs - 1; e >= 0; e--) { + EQs[e].coef[nVars] = EQs[e].coef[i]; + } + for (int e = nSUBs - 1; e >= 0; e--) { + SUBs[e].coef[nVars] = SUBs[e].coef[i]; + } + var[nVars] = var[i]; + } + for (int e = nGEQs - 1; e >= 0; e--) + GEQs[e].coef[i] = 0; + for (int e = nEQs - 1; e >= 0; e--) + EQs[e].coef[i] = 0; + for (int e = nSUBs - 1; e >= 0; e--) + SUBs[e].coef[i] = 0; + nameWildcard(i); + return (i); +} + + +int Problem::addNewUnprotectedWildcard() { + int i = ++nVars; + for (int e = nGEQs - 1; e >= 0; e--) GEQs[e].coef[i] = 0; + for (int e = nEQs - 1; e >= 0; e--) EQs[e].coef[i] = 0; + for (int e = nSUBs - 1; e >= 0; e--) SUBs[e].coef[i] = 0; + nameWildcard(i); + return i; +} + + +void Problem::cleanoutWildcards() { + bool renormalize = false; + + // substituting wildcard equality + for (int e = nEQs-1; e >= 0; e--) { + for (int i = nVars; i >= safeVars+1; i--) + if (EQs[e].coef[i] != 0) { + coef_t c = EQs[e].coef[i]; + coef_t a = abs(c); + + bool preserveThisConstraint = true; + for (int e2 = nEQs-1; e2 >= 0; e2--) + if (e2 != e && EQs[e2].coef[i] != 0 && EQs[e2].color >= EQs[e].color) { + preserveThisConstraint = preserveThisConstraint && (gcd(a,abs(EQs[e2].coef[i])) != 1); + coef_t k = lcm(a, abs(EQs[e2].coef[i])); + coef_t coef1 = (EQs[e2].coef[i]>0?1:-1) * k / c; + coef_t coef2 = k / abs(EQs[e2].coef[i]); + for (int j = nVars; j >= 0; j--) + EQs[e2].coef[j] = EQs[e2].coef[j] * coef2 - EQs[e].coef[j] * coef1; + + coef_t g = 0; + for (int j = nVars; j >= 0; j--) { + g = gcd(abs(EQs[e2].coef[j]), g); + if (g == 1) + break; + } + if (g != 0 && g != 1) + for (int j = nVars; j >= 0; j--) + EQs[e2].coef[j] /= g; + } + + for (int e2 = nGEQs-1; e2 >= 0; e2--) + if (GEQs[e2].coef[i] != 0 && GEQs[e2].color >= EQs[e].color) { + coef_t k = lcm(a, abs(GEQs[e2].coef[i])); + coef_t coef1 = (GEQs[e2].coef[i]>0?1:-1) * k / c; + coef_t coef2 = k / abs(GEQs[e2].coef[i]); + for (int j = nVars; j >= 0; j--) + GEQs[e2].coef[j] = GEQs[e2].coef[j] * coef2 - EQs[e].coef[j] * coef1; + + GEQs[e2].touched = 1; + renormalize = true; + } + + for (int e2 = nSUBs-1; e2 >= 0; e2--) + if (SUBs[e2].coef[i] != 0 && SUBs[e2].color >= EQs[e].color) { + coef_t k = lcm(a, abs(SUBs[e2].coef[i])); + coef_t coef1 = (SUBs[e2].coef[i]>0?1:-1) * k / c; + coef_t coef2 = k / abs(SUBs[e2].coef[i]); + for (int j = nVars; j >= 0; j--) + SUBs[e2].coef[j] = SUBs[e2].coef[j] * coef2 - EQs[e].coef[j] * coef1; + + coef_t g = 0; + for (int j = nVars; j >= 0; j--) { + g = gcd(abs(SUBs[e2].coef[j]), g); + if (g == 1) + break; + } + if (g != 0 && g != 1) + for (int j = nVars; j >= 0; j--) + SUBs[e2].coef[j] /= g; + } + + // remove redundent wildcard equality + if (!preserveThisConstraint) { + if (e < nEQs-1) + eqnncpy (&EQs[e], &EQs[nEQs-1], nVars); + nEQs--; + deleteVariable(i); + } + + break; + } + } + + // remove multi-wildcard equality in approximation mode + if (inApproximateMode) + for (int e = nEQs-1; e >= 0; e--) + for (int i = nVars; i >= safeVars+1; i--) + if (EQs[e].coef[i] != 0) { + int j = i-1; + for (; j >= safeVars+1; j--) + if (EQs[e].coef[j] != 0) + break; + + if (j != safeVars) { + if (e < nEQs-1) + eqnncpy (&EQs[e], &EQs[nEQs-1], nVars); + nEQs--; + } + + break; + } + + if (renormalize) + normalize(); +} + + +void Problem:: check() const { +#ifndef NDEBUG + int v = nSUBs; + checkVars(nVars+1); + for(int i = 1; i <= safeVars; i++) if (var[i] > 0) v++; + assert(v == varsOfInterest); + for(int e = 0; e < nGEQs; e++) assert(GEQs[e].touched || GEQs[e].key != 0); + if(!mayBeRed) { + for(int e = 0; e < nEQs; e++) assert(!EQs[e].color); + for(int e = 0; e < nGEQs; e++) assert(!GEQs[e].color); + } + else + for(int i = safeVars+1; i <= nVars; i++) { + int isBlack = 0; + int isRed = 0; + for(int e = 0; e < nEQs; e++) + if (EQs[e].coef[i]) { + if (EQs[e].color) isRed = 1; + else isBlack = 1; + } + for(int e = 0; e < nGEQs; e++) + if (GEQs[e].coef[i]) { + if (GEQs[e].color) isRed = 1; + else isBlack = 1; + } + if (isBlack && isRed && 0) { + fprintf(outputFile,"Mixed Red and Black variable:\n"); + printProblem(); + } + } +#endif +} + + +void Problem::rememberRedConstraint(eqn *e, redType type, coef_t stride) { + // Check if this is really a stride constraint + if (type == redEQ && newVar == nVars && e->coef[newVar]) { + type = redStride; + stride = e->coef[newVar]; + } + // else for(int i = safeVars+1; i <= nVars; i++) assert(!e->coef[i]); // outdated -- by chun 10/30/2008 + + assert(type != notRed); + assert(type == redStride || stride == 0); + + if (TRACE) { + fprintf(outputFile,"being asked to remember red constraint:\n"); + switch(type) { + case notRed: fprintf(outputFile,"notRed: "); + break; + case redGEQ: fprintf(outputFile,"Red: 0 <= "); + break; + case redLEQ: fprintf(outputFile,"Red: 0 >= "); + break; + case redEQ: fprintf(outputFile,"Red: 0 == "); + break; + case redStride: fprintf(outputFile,"Red stride " coef_fmt ": ",stride); + break; + } + printTerm(e,1); + fprintf(outputFile,"\n"); + printProblem(); + fprintf(outputFile,"----\n"); + } + + // Convert redLEQ to redGEQ + eqn mem; + eqnncpy(&mem,e, nVars); + e = &mem; + if (type == redLEQ) { + for(int i = 0; i <= safeVars; i++) + e->coef[i] = -e->coef[i]; + type = redGEQ; + } + + // Prepare coefficient array for red constraint + bool has_wildcard = false; + coef_t coef[varsOfInterest-nextWildcard+1]; + for (int i = 0; i <= varsOfInterest-nextWildcard; i++) + coef[i] = 0; + for (int i = 0; i <= safeVars; i++) { + if (var[i] < 0) { + if (e->coef[i] != 0) { + coef[varsOfInterest-var[i]] = e->coef[i]; + has_wildcard = true; + } + } + else + coef[var[i]] = e->coef[i]; + } + + // Sophisticated back substituion for wildcards, use Gaussian elimination + // as a fallback if no simple equations available. -- by chun 10/13/2008 + if (has_wildcard) { + // Find substitutions involving wildcard + coef_t *repl_subs[nSUBs]; + int num_wild_in_repl_subs[nSUBs]; + int num_repl_subs = 0; + for (int i = 0; i < nSUBs; i++) { + int t = 0; + for (int j = 1; j <= safeVars; j++) { + if (var[j] < 0 && SUBs[i].coef[j] != 0) + t++; + } + if (t > 0) { + repl_subs[num_repl_subs] = new coef_t[varsOfInterest-nextWildcard+1]; + for (int j = 0; j <= varsOfInterest-nextWildcard; j++) + repl_subs[num_repl_subs][j] = 0; + + for (int k = 0; k <= safeVars; k++) + repl_subs[num_repl_subs][(var[k]<0)?varsOfInterest-var[k]:var[k]] = SUBs[i].coef[k]; + repl_subs[num_repl_subs][SUBs[i].key] = -1; + num_wild_in_repl_subs[num_repl_subs] = t; + num_repl_subs++; + } + } + + int wild_solved[-nextWildcard+1]; + bool has_unsolved = false; + for (int i = 1; i <= -nextWildcard; i++) { + int minimum_wild = 0; + int pos; + for (int j = 0; j < num_repl_subs; j++) + if (repl_subs[j][varsOfInterest+i] != 0 && (minimum_wild == 0 || num_wild_in_repl_subs[j] < minimum_wild)) { + minimum_wild = num_wild_in_repl_subs[j]; + pos = j; + } + + if (minimum_wild == 0) { + wild_solved[i] = -1; + if (coef[varsOfInterest+i] != 0) { + fprintf(outputFile,"No feasible back substitutions available\n"); + printProblem(); + exit(1); + } + } + else if (minimum_wild == 1) + wild_solved[i] = pos; + else { + wild_solved[i] = -1; + if (coef[varsOfInterest+i] != 0) + has_unsolved = true; + } + } + + // Gaussian elimination + while (has_unsolved) { + for (int i = 0; i < num_repl_subs; i++) + if (num_wild_in_repl_subs[i] > 1) { + for (int j = 1; j <= -nextWildcard; j++) { + if (repl_subs[i][varsOfInterest+j] != 0 && wild_solved[j] >= 0) { + int s = wild_solved[j]; + coef_t l = lcm(abs(repl_subs[i][varsOfInterest+j]), abs(repl_subs[s][varsOfInterest+j])); + coef_t scale_1 = l/abs(repl_subs[i][varsOfInterest+j]); + coef_t scale_2 = l/abs(repl_subs[s][varsOfInterest+j]); + int sign = ((repl_subs[i][varsOfInterest+j]>0)?1:-1) * ((repl_subs[s][varsOfInterest+j]>0)?1:-1); + for (int k = 0; k <= varsOfInterest-nextWildcard; k++) + repl_subs[i][k] = scale_1*repl_subs[i][k] - sign*scale_2*repl_subs[s][k]; + num_wild_in_repl_subs[i]--; + } + } + + if (num_wild_in_repl_subs[i] == 1) { + for (int j = 1; j <= -nextWildcard; j++) + if (repl_subs[i][varsOfInterest+j] != 0) { + assert(wild_solved[j]==-1); + wild_solved[j] = i; + break; + } + } + else if (num_wild_in_repl_subs[i] > 1) { + int pos = 0; + for (int j = 1; j <= -nextWildcard; j++) + if (repl_subs[i][varsOfInterest+j] != 0) { + pos = j; + break; + } + assert(pos > 0); + + for (int j = i+1; j < num_repl_subs; j++) + if (repl_subs[j][varsOfInterest+pos] != 0) { + coef_t l = lcm(abs(repl_subs[i][varsOfInterest+pos]), abs(repl_subs[j][varsOfInterest+pos])); + coef_t scale_1 = l/abs(repl_subs[i][varsOfInterest+pos]); + coef_t scale_2 = l/abs(repl_subs[j][varsOfInterest+pos]); + int sign = ((repl_subs[i][varsOfInterest+pos]>0)?1:-1) * ((repl_subs[j][varsOfInterest+pos]>0)?1:-1); + for (int k = 0; k <= varsOfInterest-nextWildcard; k++) + repl_subs[j][k] = scale_2*repl_subs[j][k] - sign*scale_1*repl_subs[i][k]; + + num_wild_in_repl_subs[j] = 0; + int first_wild = 0; + for (int k = 1; k <= -nextWildcard; k++) + if (repl_subs[j][varsOfInterest+k] != 0) { + num_wild_in_repl_subs[j]++; + first_wild = k; + } + + if (num_wild_in_repl_subs[j] == 1) { + if (wild_solved[first_wild] < 0) + wild_solved[first_wild] = j; + } + } + } + } + + has_unsolved = false; + for (int i = 1; i <= -nextWildcard; i++) + if (coef[varsOfInterest+i] != 0 && wild_solved[i] < 0) { + has_unsolved = true; + break; + } + } + + // Substitute all widecards in the red constraint + for (int i = 1; i <= -nextWildcard; i++) { + if (coef[varsOfInterest+i] != 0) { + int s = wild_solved[i]; + assert(s >= 0); + + coef_t l = lcm(abs(coef[varsOfInterest+i]), abs(repl_subs[s][varsOfInterest+i])); + coef_t scale_1 = l/abs(coef[varsOfInterest+i]); + coef_t scale_2 = l/abs(repl_subs[s][varsOfInterest+i]); + int sign = ((coef[varsOfInterest+i]>0)?1:-1) * ((repl_subs[s][varsOfInterest+i]>0)?1:-1); + for (int j = 0; j <= varsOfInterest-nextWildcard; j++) + coef[j] = scale_1*coef[j] - sign*scale_2*repl_subs[s][j]; + + if (scale_1 != 1) + stride *= scale_1; + } + } + + for (int i = 0; i < num_repl_subs; i++) + delete []repl_subs[i]; + } + + // Ready to insert into redMemory + int m = nMemories++; + redMemory[m].length = 0; + redMemory[m].kind = type; + redMemory[m].constantTerm = coef[0]; + for(int i = 1; i <= varsOfInterest; i++) + if (coef[i]) { + int j = redMemory[m].length++; + redMemory[m].coef[j] = coef[i]; + redMemory[m].var[j] = i; + } + if (type == redStride) redMemory[m].stride = stride; + if (DBUG) { + fprintf(outputFile,"Red constraint remembered\n"); + printProblem(); + } +} + +void Problem::recallRedMemories() { + if (nMemories) { + if (TRACE) { + fprintf(outputFile,"Recalling red memories\n"); + printProblem(); + } + + eqn* e = 0; + for(int m = 0; m < nMemories; m++) { + switch(redMemory[m].kind) { + case redGEQ: + { + int temporary_eqn = newGEQ(); + e = &GEQs[temporary_eqn]; + eqnnzero(e, nVars); + e->touched = 1; + break; + } + case redEQ: + { + int temporary_eqn = newEQ(); + e = &EQs[temporary_eqn]; + eqnnzero(e, nVars); + break; + } + case redStride: + { + int temporary_eqn = newEQ(); + e = &EQs[temporary_eqn]; + eqnnzero(e, nVars); + int i = addNewUnprotectedWildcard(); + e->coef[i] = -redMemory[m].stride; + break; + } + default: + assert(0); + } + e->color = EQ_RED; + e->coef[0] = redMemory[m].constantTerm; + for(int i = 0; i < redMemory[m].length; i++) { + int v = redMemory[m].var[i]; + assert(var[forwardingAddress[v]] == v); + e->coef[forwardingAddress[v]] = redMemory[m].coef[i]; + } + } + + nMemories = 0; + if (TRACE) { + fprintf(outputFile,"Red memories recalled\n"); + printProblem(); + } + } +} + +void Problem::swapVars(int i, int j) { + if (DEBUG) { + use_ugly_names++; + fprintf(outputFile, "Swapping %d and %d\n", i, j); + printProblem(); + use_ugly_names--; + } + std::swap(var[i], var[j]); + for (int e = nGEQs - 1; e >= 0; e--) + if (GEQs[e].coef[i] != GEQs[e].coef[j]) { + GEQs[e].touched = true; + coef_t t = GEQs[e].coef[i]; + GEQs[e].coef[i] = GEQs[e].coef[j]; + GEQs[e].coef[j] = t; + } + for (int e = nEQs - 1; e >= 0; e--) + if (EQs[e].coef[i] != EQs[e].coef[j]) { + coef_t t = EQs[e].coef[i]; + EQs[e].coef[i] = EQs[e].coef[j]; + EQs[e].coef[j] = t; + } + for (int e = nSUBs - 1; e >= 0; e--) + if (SUBs[e].coef[i] != SUBs[e].coef[j]) { + coef_t t = SUBs[e].coef[i]; + SUBs[e].coef[i] = SUBs[e].coef[j]; + SUBs[e].coef[j] = t; + } + if (DEBUG) { + use_ugly_names++; + fprintf(outputFile, "Swapping complete \n"); + printProblem(); + fprintf(outputFile, "\n"); + use_ugly_names--; + } +} + +void Problem::addingEqualityConstraint(int e) { + if (addingOuterEqualities && originalProblem != noProblem && + originalProblem != this && !conservative) { + int e2 = originalProblem->newEQ(); + if (TRACE) + fprintf(outputFile, "adding equality constraint %d to outer problem\n", e2); + eqnnzero(&originalProblem->EQs[e2], originalProblem->nVars); + for (int i = nVars; i >= 1; i--) { + int j; + for (j = originalProblem->nVars; j >= 1; j--) + if (originalProblem->var[j] == var[i]) + break; + if (j <= 0 || (outerColor && j > originalProblem->safeVars)) { + if (DBUG) + fprintf(outputFile, "retracting\n"); + originalProblem->nEQs--; + return; + } + originalProblem->EQs[e2].coef[j] = EQs[e].coef[i]; + } + originalProblem->EQs[e2].coef[0] = EQs[e].coef[0]; + + originalProblem->EQs[e2].color = outerColor; + if (DBUG) + originalProblem->printProblem(); + } +} + + +// Initialize hash codes for inequalities, remove obvious redundancy. +// Case 1: +// a1*x1+a2*x2+...>=c (1) +// a1*x2+a2*x2+...>=c' (2) +// if c>=c' then (2) is redundant, and vice versa. +// +// case 2: +// a1*x1+a2*x2+...>=c (1) +// a1*x1+a2*x2+...<=c' (2) +// if c=c' then add equality of (1) or (2), +// if c>c' then no solution. +// +// Finally it calls extended normalize process which handles +// wildcards in redundacy removal. +normalizeReturnType Problem::normalize() { + int i, j; + bool coupledSubscripts = false; + + check(); + + for (int e = 0; e < nGEQs; e++) { + if (!GEQs[e].touched) { + if (!singleVarGEQ(&GEQs[e])) + coupledSubscripts = true; + } + else { // normalize e + coef_t g; + int topVar; + int i0; + coef_t hashCode; + + { + int *p = &packing[0]; + for (int k = 1; k <= nVars; k++) + if (GEQs[e].coef[k]) { + *(p++) = k; + } + topVar = (p - &packing[0]) - 1; + } + + if (topVar == -1) { + if (GEQs[e].coef[0] < 0) { + // e has no solution + return (normalize_false); + } + deleteGEQ(e); + e--; + continue; + } + else if (topVar == 0) { + int singleVar = packing[0]; + g = GEQs[e].coef[singleVar]; + if (g > 0) { + GEQs[e].coef[singleVar] = 1; + GEQs[e].key = singleVar; + } + else { + g = -g; + GEQs[e].coef[singleVar] = -1; + GEQs[e].key = -singleVar; + } + if (g > 1) + GEQs[e].coef[0] = int_div(GEQs[e].coef[0], g); + } + else { + coupledSubscripts = true; + i0 = topVar; + i = packing[i0--]; + g = GEQs[e].coef[i]; + hashCode = g * (i + 3); + if (g < 0) + g = -g; + for (; i0 >= 0; i0--) { + coef_t x; + i = packing[i0]; + x = GEQs[e].coef[i]; + hashCode = hashCode * keyMult * (i + 3) + x; + if (x < 0) + x = -x; + if (x == 1) { + g = 1; + i0--; + break; + } + else + g = gcd(x, g); + } + for (; i0 >= 0; i0--) { + coef_t x; + i = packing[i0]; + x = GEQs[e].coef[i]; + hashCode = hashCode * keyMult * (i + 3) + x; + } + if (g > 1) { + GEQs[e].coef[0] = int_div(GEQs[e].coef[0], g); + i0 = topVar; + i = packing[i0--]; + GEQs[e].coef[i] = GEQs[e].coef[i] / g; + hashCode = GEQs[e].coef[i] * (i + 3); + for (; i0 >= 0; i0--) { + i = packing[i0]; + GEQs[e].coef[i] = GEQs[e].coef[i] / g; + hashCode = hashCode * keyMult * (i + 3) + GEQs[e].coef[i]; + } + } + + { + coef_t g2 = abs(hashCode); // get e's hash code + j = static_cast<int>(g2 % static_cast<coef_t>(hashTableSize)); + assert (g2 % (coef_t) hashTableSize == j); + while (1) { + eqn *proto = &(hashMaster[j]); + if (proto->touched == g2) { + if (proto->coef[0] == topVar) { + if (hashCode >= 0) + for (i0 = topVar; i0 >= 0; i0--) { + i = packing[i0]; + if (GEQs[e].coef[i] != proto->coef[i]) + break; + } + else + for (i0 = topVar; i0 >= 0; i0--) { + i = packing[i0]; + if (GEQs[e].coef[i] != -proto->coef[i]) + break; + } + + if (i0 < 0) { + if (hashCode >= 0) + GEQs[e].key = proto->key; + else + GEQs[e].key = -proto->key; + break; + } + } + } + else if (proto->touched < 0) { //insert e into the empty entry in hash table + eqnnzero(proto, nVars); + if (hashCode >= 0) + for (i0 = topVar; i0 >= 0; i0--) { + i = packing[i0]; + proto->coef[i] = GEQs[e].coef[i]; + } + else + for (i0 = topVar; i0 >= 0; i0--) { + i = packing[i0]; + proto->coef[i] = -GEQs[e].coef[i]; + } + proto->coef[0] = topVar; + proto->touched = g2; + proto->key = nextKey++; + + if (proto->key > maxKeys) { + fprintf(outputFile, "too many hash keys generated \n"); + fflush(outputFile); + exit(2); + } + if (hashCode >= 0) + GEQs[e].key = proto->key; + else + GEQs[e].key = -proto->key; + break; + } + j = (j + 1) % hashTableSize; + } + } + } + } + + GEQs[e].touched = false; + + { + int eKey = GEQs[e].key; + int e2; + if (e > 0) { + e2 = fastLookup[maxKeys - eKey]; + if (e2 >= 0 && e2 < e && GEQs[e2].key == -eKey) { + // confirm it is indeed a match -- by chun 10/29/2008 + int k; + for (k = nVars; k >= 1; k--) + if (GEQs[e2].coef[k] != -GEQs[e].coef[k]) + break; + + if (k == 0) { + if (GEQs[e2].coef[0] < -GEQs[e].coef[0]) { + // there is no solution from e and e2 + return (normalize_false); + } + else if (GEQs[e2].coef[0] == -GEQs[e].coef[0]) { + // reduce e and e2 to an equation + int neweq = newEQ(); + eqnncpy(&EQs[neweq], &GEQs[e], nVars); + EQs[neweq].color = GEQs[e].color || GEQs[e2].color; + addingEqualityConstraint(neweq); + } + } + } + + e2 = fastLookup[maxKeys + eKey]; + if (e2 >= 0 && e2 < e && GEQs[e2].key == eKey) { + // confirm it is indeed a match -- by chun 10/29/2008 + int k; + for (k = nVars; k >= 1; k--) + if (GEQs[e2].coef[k] != GEQs[e].coef[k]) + break; + + if (k == 0) { + if (GEQs[e2].coef[0] > GEQs[e].coef[0] || + (GEQs[e2].coef[0] == GEQs[e].coef[0] && GEQs[e2].color)) { + // e2 is redundant + GEQs[e2].coef[0] = GEQs[e].coef[0]; + GEQs[e2].color = GEQs[e].color; + deleteGEQ(e); + e--; + continue; + } + else { + // e is redundant + deleteGEQ(e); + e--; + continue; + } + } + } + } + fastLookup[maxKeys + eKey] = e; + } + } + + // bypass entended normalization for temporary problem + if (!isTemporary && !inApproximateMode) + normalize_ext(); + + return coupledSubscripts ? normalize_coupled : normalize_uncoupled; +} + +// +// Extended normalize process, remove redundancy involving wildcards. +// e.g. +// exists alpha, beta: +// v1+8*alpha<=v2<=15+8*alpha (1) +// v1+8*beta<=v2<=15+8*beta (2) +// if there are no other inequalities involving alpha or beta, +// then either (1) or (2) is redundant. Such case can't be simplified +// by fourier-motzkin algorithm due to special meanings of existentials. +// +void Problem::normalize_ext() { + std::vector<BoolSet<> > disjoint_wildcards(nVars-safeVars, BoolSet<>(nVars-safeVars)); + std::vector<BoolSet<> > wildcards_in_inequality(nVars-safeVars, BoolSet<>(nGEQs)); + for (int i = 0; i < nVars-safeVars; i++) { + disjoint_wildcards[i].set(i); + } + + // create disjoint wildcard sets according to inequalities + for (int e = 0; e < nGEQs; e++) { + std::vector<BoolSet<> >::iterator first_set = disjoint_wildcards.end(); + for (int i = 0; i < nVars-safeVars; i++) + if (GEQs[e].coef[i+safeVars+1] != 0) { + wildcards_in_inequality[i].set(e); + + std::vector<BoolSet<> >::iterator cur_set = disjoint_wildcards.end(); + for (std::vector<BoolSet<> >::iterator j = disjoint_wildcards.begin(); j != disjoint_wildcards.end(); j++) + if ((*j).get(i)) { + cur_set = j; + break; + } + assert(cur_set!=disjoint_wildcards.end()); + if (first_set == disjoint_wildcards.end()) + first_set = cur_set; + else if (first_set != cur_set) { + *first_set |= *cur_set; + disjoint_wildcards.erase(cur_set); + } + } + } + + // do not consider wildcards appearing in equalities + for (int e = 0; e < nEQs; e++) + for (int i = 0; i < nVars-safeVars; i++) + if (EQs[e].coef[i+safeVars+1] != 0) { + for (std::vector<BoolSet<> >::iterator j = disjoint_wildcards.begin(); j != disjoint_wildcards.end(); j++) + if ((*j).get(i)) { + disjoint_wildcards.erase(j); + break; + } + } + + // create disjoint inequality sets + std::vector<BoolSet<> > disjoint_inequalities(disjoint_wildcards.size()); + for (size_t i = 0; i < disjoint_wildcards.size(); i++) + for (int j = 0; j < nVars-safeVars; j++) + if (disjoint_wildcards[i].get(j)) + disjoint_inequalities[i] |= wildcards_in_inequality[j]; + + // hash the inequality again, this time separate wildcard variables from + // regular variables + coef_t hash_safe[nGEQs]; + coef_t hash_wild[nGEQs]; + for (int e = 0; e < nGEQs; e++) { + coef_t hashCode = 0; + for (int i = 1; i <= safeVars; i++) + if (GEQs[e].coef[i] != 0) + hashCode = hashCode * keyMult * (i+3) + GEQs[e].coef[i]; + hash_safe[e] = hashCode; + + hashCode = 0; + for (int i = safeVars+1; i <= nVars; i++) + if (GEQs[e].coef[i] != 0) + hashCode = hashCode * keyMult + GEQs[e].coef[i]; + hash_wild[e] = hashCode; + } + + // sort hash keys for each disjoint set + std::vector<std::vector<std::pair<int, std::pair<coef_t, coef_t> > > > disjoint_hash(disjoint_inequalities.size()); + for (size_t i = 0; i < disjoint_inequalities.size(); i++) + for (int e = 0; e < nGEQs; e++) + if (disjoint_inequalities[i].get(e)) { + std::vector<std::pair<int, std::pair<coef_t, coef_t> > >::iterator j = disjoint_hash[i].begin(); + for (; j != disjoint_hash[i].end(); j++) + if ((hash_safe[e] > (*j).second.first) || + (hash_safe[e] == (*j).second.first && hash_wild[e] > (*j).second.second)) + break; + disjoint_hash[i].insert(j, std::make_pair(e, std::make_pair(hash_safe[e], hash_wild[e]))); + } + + // test wildcard equivalance + std::vector<bool> is_dead(nGEQs, false); + for (size_t i = 0; i < disjoint_wildcards.size(); i++) { + if (disjoint_inequalities[i].num_elem() == 0) + continue; + + for (size_t j = i+1; j < disjoint_wildcards.size(); j++) { + if (disjoint_wildcards[i].num_elem() != disjoint_wildcards[j].num_elem() || + disjoint_hash[i].size() != disjoint_hash[j].size()) + continue; + + bool match = true; + for (size_t k = 0; k < disjoint_hash[i].size(); k++) { + if (disjoint_hash[i][k].second != disjoint_hash[j][k].second) { + match = false; + break; + } + } + if (!match) + continue; + + // confirm same coefficients for regular variables + for (size_t k = 0; k < disjoint_hash[i].size(); k++) { + for (int p = 1; p <= safeVars; p++) + if (GEQs[disjoint_hash[i][k].first].coef[p] != GEQs[disjoint_hash[j][k].first].coef[p]) { + match = false; + break; + } + if (!match) + break; + } + if (!match) + continue; + + // now try combinatory wildcard matching + std::vector<int> wild_map(nVars-safeVars, -1); + for (size_t k = 0; k < disjoint_hash[i].size(); k++) { + int e1 = disjoint_hash[i][k].first; + int e2 = disjoint_hash[j][k].first; + + for (int p = 0; p < nVars-safeVars; p++) + if (GEQs[e1].coef[p+safeVars+1] != 0) { + if (wild_map[p] == -1) { + for (int q = 0; q < nVars-safeVars; q++) + if (wild_map[q] == -1 && + GEQs[e2].coef[q+safeVars+1] == GEQs[e1].coef[p+safeVars+1]) { + wild_map[p] = q; + wild_map[q] = p; + break; + } + if (wild_map[p] == -1) { + match = false; + break; + } + } + else if (GEQs[e2].coef[wild_map[p]+safeVars+1] != GEQs[e1].coef[p+safeVars+1]) { + match = false; + break; + } + } + if (!match) + break; + + for (int p = 0; p < nVars-safeVars; p++) + if (GEQs[e2].coef[p+safeVars+1] != 0 && + (wild_map[p] == -1 || GEQs[e2].coef[p+safeVars+1] != GEQs[e1].coef[wild_map[p]+safeVars+1])) { + match = false; + break; + } + if (!match) + break; + } + if (!match) + continue; + + // check constants + int dir = 0; + for (size_t k = 0; k < disjoint_hash[i].size(); k++) { + if (GEQs[disjoint_hash[i][k].first].coef[0] > GEQs[disjoint_hash[j][k].first].coef[0]) { + if (dir == 0) + dir = 1; + else if (dir == -1) { + match = false; + break; + } + } + else if (GEQs[disjoint_hash[i][k].first].coef[0] < GEQs[disjoint_hash[j][k].first].coef[0]) { + if (dir == 0) + dir = -1; + else if (dir == 1) { + match = false; + break; + } + } + } + if (!match) + continue; + + // check redness + int red_dir = 0; + for (size_t k = 0; k < disjoint_hash[i].size(); k++) { + if (GEQs[disjoint_hash[i][k].first].color > GEQs[disjoint_hash[j][k].first].color) { + if (red_dir == 0) + red_dir = 1; + else if (red_dir == -1) { + match = false; + break; + } + } + else if (GEQs[disjoint_hash[i][k].first].color < GEQs[disjoint_hash[j][k].first].color) { + if (red_dir == 0) + red_dir = -1; + else if (red_dir == 1) { + match = false; + break; + } + } + } + if (!match) + continue; + + // remove redundant inequalities + if (dir == 1 || (dir == 0 && red_dir == 1)) { + for (size_t k = 0; k < disjoint_hash[i].size(); k++) { + GEQs[disjoint_hash[i][k].first].coef[0] = GEQs[disjoint_hash[j][k].first].coef[0]; + GEQs[disjoint_hash[i][k].first].color = GEQs[disjoint_hash[j][k].first].color; + is_dead[disjoint_hash[j][k].first] = true; + } + } + else { + for (size_t k = 0; k < disjoint_hash[i].size(); k++) { + is_dead[disjoint_hash[j][k].first] = true; + } + } + } + } + + // eliminate dead inequalities + for (int e = nGEQs-1; e >= 0; e--) + if (is_dead[e]) { + deleteGEQ(e); + } +} + + +void initializeOmega(void) { + if (omegaInitialized) + return; + +// assert(sizeof(eqn)==sizeof(int)*(headerWords)+sizeof(coef_t)*(1+maxVars)); + nextWildcard = 0; + nextKey = maxVars + 1; + for (int i = 0; i < hashTableSize; i++) + hashMaster[i].touched = -1; + + sprintf(wildName[1], "__alpha"); + sprintf(wildName[2], "__beta"); + sprintf(wildName[3], "__gamma"); + sprintf(wildName[4], "__delta"); + sprintf(wildName[5], "__tau"); + sprintf(wildName[6], "__sigma"); + sprintf(wildName[7], "__chi"); + sprintf(wildName[8], "__omega"); + sprintf(wildName[9], "__pi"); + sprintf(wildName[10], "__ni"); + sprintf(wildName[11], "__Alpha"); + sprintf(wildName[12], "__Beta"); + sprintf(wildName[13], "__Gamma"); + sprintf(wildName[14], "__Delta"); + sprintf(wildName[15], "__Tau"); + sprintf(wildName[16], "__Sigma"); + sprintf(wildName[17], "__Chi"); + sprintf(wildName[18], "__Omega"); + sprintf(wildName[19], "__Pi"); + + omegaInitialized = 1; +} + +// +// This is experimental (I would say, clinical) fact: +// If the code below is removed then simplifyProblem cycles. +// +class brainDammage { +public: + brainDammage(); +}; + +brainDammage::brainDammage() { + initializeOmega(); +} + +static brainDammage Podgorny; + +} // namespace diff --git a/omegalib/omega/src/omega_core/oc_solve.cc b/omegalib/omega/src/omega_core/oc_solve.cc new file mode 100644 index 0000000..c25b6d0 --- /dev/null +++ b/omegalib/omega/src/omega_core/oc_solve.cc @@ -0,0 +1,1378 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + All Rights Reserved. + + Purpose: + Solve ineqalities. + + Notes: + + History: +*****************************************************************************/ + +#include <omega/omega_core/oc_i.h> + +namespace omega { + +static int solveDepth = 0; +#define maxDead maxmaxGEQs + +int Problem::solve(int desiredResult) { + assert(omegaInitialized); + int result; + + checkVars(nVars+1); + assert(nVars >= safeVars); + if (desiredResult != OC_SOLVE_SIMPLIFY) + safeVars = 0; + + solveDepth++; + if (solveDepth > 50) { + fprintf(outputFile, "Solve depth = %d, inApprox = %d, aborting\n", solveDepth, inApproximateMode); + printProblem(); + fflush(outputFile); + + if (solveDepth > 60) + exit(2); + } + + check(); + do { + doItAgain = 0; + check(); + if (solveEQ() == false) { + solveDepth--; + return (false); + } + check(); + if (!nGEQs) { + result = true; + nVars = safeVars; + break; + } + else + result = solveGEQ(desiredResult); + check(); + } + while (doItAgain && desiredResult == OC_SOLVE_SIMPLIFY); + solveDepth--; + + return (result); +} + + +// Supporting functions of solveGEQ +int Problem::smoothWeirdEquations() { + int e1, e2, e3, p, q, k; + coef_t alpha, alpha1, alpha2, alpha3; + coef_t c; + int v; + int result = 0; + + for (e1 = nGEQs - 1; e1 >= 0; e1--) + if (!GEQs[e1].color) { + coef_t g = 999999; + for (v = nVars; v >= 1; v--) + if (GEQs[e1].coef[v] != 0 && abs(GEQs[e1].coef[v]) < g) + g = abs(GEQs[e1].coef[v]); + if (g > 20) { + e3 = newGEQ(); /* Create a scratch GEQ,not part of the prob.*/ + nGEQs--; + for (v = nVars; v >= 1; v--) + GEQs[e3].coef[v] = int_div(6 * GEQs[e1].coef[v] + g / 2, g); + GEQs[e3].color = EQ_BLACK; + GEQs[e3].touched = 1; + GEQs[e3].coef[0] = 9997; + if (DBUG) { + fprintf(outputFile, "Checking to see if we can derive: "); + printGEQ(&GEQs[e3]); + fprintf(outputFile, "\n from: "); + printGEQ(&GEQs[e1]); + fprintf(outputFile, "\n"); + } + + + for (e2 = nGEQs - 1; e2 >= 0; e2--) + if (e1 != e2 && !GEQs[e2].color) { + for (p = nVars; p > 1; p--) { + for (q = p - 1; q > 0; q--) { + alpha = check_mul(GEQs[e1].coef[p], GEQs[e2].coef[q]) - check_mul(GEQs[e2].coef[p], GEQs[e1].coef[q]); + if (alpha != 0) + goto foundPQ; + } + } + continue; + + foundPQ: + + alpha1 = check_mul(GEQs[e2].coef[q], GEQs[e3].coef[p]) - check_mul(GEQs[e2].coef[p], GEQs[e3].coef[q]); + alpha2 = -(check_mul(GEQs[e1].coef[q], GEQs[e3].coef[p]) - check_mul(GEQs[e1].coef[p], GEQs[e3].coef[q])); + alpha3 = alpha; + + if (alpha1 * alpha2 <= 0) + continue; + if (alpha1 < 0) { + alpha1 = -alpha1; + alpha2 = -alpha2; + alpha3 = -alpha3; + } + if (alpha3 > 0) { + /* Trying to prove e3 is redundant */ + + /* verify alpha1*v1+alpha2*v2 = alpha3*v3 */ + for (k = nVars; k >= 1; k--) + if (check_mul(alpha3, GEQs[e3].coef[k]) + != check_mul(alpha1, GEQs[e1].coef[k]) + check_mul(alpha2, GEQs[e2].coef[k])) + goto nextE2; + + c = check_mul(alpha1, GEQs[e1].coef[0]) + check_mul(alpha2, GEQs[e2].coef[0]); + if (c < check_mul(alpha3, (GEQs[e3].coef[0] + 1))) + GEQs[e3].coef[0] = int_div(c, alpha3); + + } + nextE2:; + } + if (GEQs[e3].coef[0] < 9997) { + result++; +#if !defined NDEBUG + int e4 = +#endif + newGEQ(); +#if !defined NDEBUG + assert(e3 == e4); +#endif + if (DBUG) { + fprintf(outputFile, "Smoothing wierd equations; adding:\n"); + printGEQ(&GEQs[e3]); + fprintf(outputFile, "\nto:\n"); + printProblem(); + fprintf(outputFile, "\n\n"); + } + } + } + } + return (result); +} + + +void Problem::analyzeElimination( + int &v, + int &darkConstraints, + int &darkShadowFeasible, + int &unit, + coef_t ¶llelSplinters, + coef_t &disjointSplinters, + coef_t &lbSplinters, + coef_t &ubSplinters, + int ¶llelLB) { + + parallelSplinters = (posInfinity); // was MAXINT + disjointSplinters = 0; + lbSplinters = 0; + ubSplinters = 0; + + darkConstraints = 0; + darkShadowFeasible = 1; + coef_t maxUBc = 0; + coef_t maxLBc = 0; + int e,e2; + unit = 0; + int exact = 1; + + for (e = nGEQs - 1; e >= 0; e--) { + coef_t c = GEQs[e].coef[v]; + + if (c < 0) { + coef_t Lc, Uc, g, diff, grey; + + set_max(maxUBc, -c); + Uc = -c; + for (e2 = nGEQs - 1; e2 >= 0; e2--) + if (GEQs[e2].coef[v] > 0) { + Lc = GEQs[e2].coef[v]; + g = 0; + grey = (Lc - 1) * (Uc - 1); + + for (int j = nVars; j >= 1; j--) { + coef_t diff = check_mul(Lc, GEQs[e].coef[j]) + check_mul(Uc, GEQs[e2].coef[j]); + if (diff < 0) diff = -diff; + g = gcd(g, diff); + if (g == 1) + break; + } + diff = check_mul(Lc, GEQs[e].coef[0]) + check_mul(Uc, GEQs[e2].coef[0]); + if (g == 0) { + if (diff < 0) { + /* Real shadow must be true */ + /* otherwise we would have found it during */ + /* check for opposing constraints */ + fprintf(outputFile, "Found conflicting constraints "); + printGEQ(&GEQs[e]); + fprintf(outputFile," and "); + printGEQ(&GEQs[e2]); + fprintf(outputFile,"\nin\n"); + printProblem(); + assert(diff >= 0); + } + if (diff < grey) { + darkShadowFeasible = 0; + if (parallelSplinters > diff+1) { + parallelSplinters = diff + 1; + parallelLB = e2; + } + } + else {/* dark shadow is true, don't need to worry about this constraint pair */ + } + } + else { + coef_t splinters= int_div(diff, g) - int_div(diff - grey, g); + if (splinters) exact = 0; + disjointSplinters += splinters; + if (g > 1) unit++; + darkConstraints++; + } + } + } + else if (c > 0) { + set_max(maxLBc, c); + } /* else + darkConstraints++; */ + } + + if (darkShadowFeasible) { + disjointSplinters++; + ubSplinters++; + lbSplinters++; + } + else disjointSplinters = (posInfinity); // was MAXINT + + + if (!darkShadowFeasible || !exact) + for (e = nGEQs - 1; e >= 0; e--) { + coef_t c = GEQs[e].coef[v]; + if (c < -1) { + c = -c; + ubSplinters += 1+(check_mul(c, maxLBc) - c - maxLBc) / maxLBc; + } + else if (c > 1) { + lbSplinters += 1+ (check_mul(c, maxUBc) - c - maxUBc) / maxUBc; + } + } + + if (DEBUG) { + fprintf(outputFile,"analyzing elimination of %s(%d)\n",variable(v),v); + if (darkShadowFeasible) + fprintf(outputFile," # dark constraints = %d\n", darkConstraints); + else + fprintf(outputFile," dark shadow obviously unfeasible\n"); + + fprintf(outputFile," " coef_fmt " LB splinters\n", lbSplinters); + fprintf(outputFile," " coef_fmt " UB splinters\n", ubSplinters); + if (disjointSplinters != (posInfinity)) + fprintf(outputFile," " coef_fmt " disjoint splinters\n", disjointSplinters); + if (parallelSplinters != (posInfinity)) + fprintf(outputFile," " coef_fmt " parallel splinters\n", parallelSplinters); + fprintf(outputFile, "\n"); + fprintf(outputFile," %3d unit score \n", unit); + } +} + + +void Problem::partialElimination() { + if (DBUG) { + fprintf(outputFile, "Performing Partial elimination\n"); + printProblem(); + } + int fv; + if (0) + fv = 0; + else + fv = safeVars; + bool somethingHappened = false; + for (int i = nVars; i > fv; i--) { + bool isDead[maxmaxGEQs]; + int e; + for (e = nGEQs-1; e >= 0; e--) isDead[e] = false; + int deadEqns[maxDead]; + int numDead = 0; + for (int e1 = nGEQs-1; e1 >= 0; e1--) + if (abs(GEQs[e1].coef[i]) == 1) { + bool isGood = true; + for (int e2 = nGEQs-1; e2 >= 0; e2--) + if (check_mul(GEQs[e2].coef[i], GEQs[e1].coef[i]) < 0) + if (GEQs[e1].key != -GEQs[e2].key) { + coef_t Uc = abs(GEQs[e2].coef[i]); + for (int k = nVars; k > fv; k--) + if (GEQs[e2].coef[k] + check_mul(GEQs[e1].coef[k], Uc) != 0) + isGood = false; + } + if (isGood) { + somethingHappened = true; + for (int e2 = nGEQs-1; e2 >= 0; e2--) + if (check_mul(GEQs[e2].coef[i], GEQs[e1].coef[i]) < 0) { + if (GEQs[e1].key != -GEQs[e2].key) { + coef_t Uc = abs(GEQs[e2].coef[i]); + int new_eqn; + if (numDead == 0) { + new_eqn = newGEQ(); + } + else { + new_eqn = deadEqns[--numDead]; + } + isDead[new_eqn] = false; + if (DBUG) { + fprintf(outputFile,"Eliminating constraint on %s\n", variable(i)); + fprintf(outputFile, "e1 = %d, e2 = %d, gen = %d\n", e1, e2, new_eqn); + printGEQ(&(GEQs[e1])); + fprintf(outputFile, "\n"); + printGEQ(&(GEQs[e2])); + fprintf(outputFile, "\n"); + } + + for (int k = nVars; k >= 0; k--) + GEQs[new_eqn].coef[k] = GEQs[e2].coef[k] + check_mul(GEQs[e1].coef[k], Uc); + GEQs[new_eqn].touched = true; + GEQs[new_eqn].color = GEQs[e2].color | GEQs[e1].color; + if (DBUG) { + fprintf(outputFile, "give "); + printGEQ(&(GEQs[new_eqn])); + fprintf(outputFile, "\n"); + } + assert(GEQs[new_eqn].coef[i] == 0); + } + } + deadEqns[numDead++] = e1; + isDead[e1] = true; + if (DEBUG) + fprintf(outputFile, "Killed %d\n", e1); + } + } + for (e = nGEQs - 1; e >= 0; e--) + if (isDead[e]) { + deleteGEQ(e); + } + } + if (somethingHappened && DBUG) { + fprintf(outputFile, "Result of Partial elimination\n"); + printProblem(); + } +} + + +int Problem:: solveGEQ(int desiredResult) { + int i, j, k, e; + int fv; + int result; + int coupledSubscripts; + int eliminateAgain; + int smoothed = 0; + int triedEliminatingRedundant = 0; + j = 0; + + if (desiredResult != OC_SOLVE_SIMPLIFY) { + nSUBs = 0; + nMemories = 0; + safeVars = 0; + varsOfInterest = 0; + } + +solveGEQstart: + while (1) { + assert(desiredResult == OC_SOLVE_SIMPLIFY || nSUBs == 0); + check_number_GEQs(nGEQs); + + if (DEBUG) { + fprintf(outputFile, "\nSolveGEQ(%d,%d):\n", desiredResult, pleaseNoEqualitiesInSimplifiedProblems); + printProblem(); + fprintf(outputFile, "\n"); + } + +#ifndef NDEBUG + for(e=0;e<nSUBs;e++) + for(i=safeVars+1;i<=nVars;i++) + assert(!SUBs[e].coef[i]); +#endif + + check(); + + if (nVars == 1) { + int uColor = EQ_BLACK; + int lColor = EQ_BLACK; + coef_t upperBound = posInfinity; + coef_t lowerBound = negInfinity; + for (e = nGEQs - 1; e >= 0; e--) { + coef_t a = GEQs[e].coef[1]; + coef_t c = GEQs[e].coef[0]; + /* our equation is ax + c >= 0, or ax >= -c, or c >= -ax */ + if (a == 0) { + if (c < 0) { + if (TRACE) + fprintf(outputFile, "equations have no solution (G)\n"); + return (false); + } + } + else if (a > 0) { + if (a != 1) + c = int_div(c, a); + if (lowerBound < -c || (lowerBound == -c && !isRed(&GEQs[e]))) { + lowerBound = -c; + lColor = GEQs[e].color; + } + } + else { + if (a != -1) + c = int_div(c, -a); + if (upperBound > c || (upperBound == c && !isRed(&GEQs[e]))) { + upperBound = c; + uColor = GEQs[e].color; + } + } + } + if (DEBUG) + fprintf(outputFile, "upper bound = " coef_fmt "\n", upperBound); + if (DEBUG) + fprintf(outputFile, "lower bound = " coef_fmt "\n", lowerBound); + if (lowerBound > upperBound) { + if (TRACE) + fprintf(outputFile, "equations have no solution (H)\n"); + return (false); + } + if (desiredResult == OC_SOLVE_SIMPLIFY) { + nGEQs = 0; + if (safeVars == 1) { + if (lowerBound == upperBound && !uColor && !lColor) { + int e = newEQ(); + assert(e == 0); + EQs[e].coef[0] = -lowerBound; + EQs[e].coef[1] = 1; + EQs[e].color = lColor | uColor; + return (solve(desiredResult)); + } + else { + if (lowerBound > negInfinity) { + int e = newGEQ(); + assert(e == 0); + GEQs[e].coef[0] = -lowerBound; + GEQs[e].coef[1] = 1; + GEQs[e].key = 1; + GEQs[e].color = lColor; + GEQs[e].touched = 0; + } + if (upperBound < posInfinity) { + int e = newGEQ(); + GEQs[e].coef[0] = upperBound; + GEQs[e].coef[1] = -1; + GEQs[e].key = -1; + GEQs[e].color = uColor; + GEQs[e].touched = 0; + } + } + } + else + nVars = 0; + return (true); + } + if (originalProblem != noProblem && !lColor && !uColor && !conservative && lowerBound == upperBound) { + int e = newEQ(); + assert(e == 0); + EQs[e].coef[0] = -lowerBound; + EQs[e].coef[1] = 1; + EQs[e].color = EQ_BLACK; + addingEqualityConstraint(0); + } + return (true); + } + + if (!variablesFreed) { + variablesFreed = 1; + if (desiredResult != OC_SOLVE_SIMPLIFY) + freeEliminations(0); + else + freeEliminations(safeVars); + if (nVars == 1) + continue; + } + + + switch (normalize()) { + case normalize_false: + return (false); + break; + case normalize_coupled: + coupledSubscripts = true; + break; + case normalize_uncoupled: + coupledSubscripts = false; + break; + default: + coupledSubscripts = false; + assert(0 && "impossible case in SolveGEQ"); + } + + + if ((doTrace && desiredResult == OC_SOLVE_SIMPLIFY) || DBUG) { + fprintf(outputFile, "\nafter normalization:\n"); + printProblem(); + fprintf(outputFile, "\n"); + for(e=0;e<nGEQs;e++) assert(!GEQs[e].touched); + fprintf(outputFile, "eliminating variable using fourier-motzkin elimination\n"); + } + + // eliminating variable using fourier-motzkin elimination + do { + eliminateAgain = 0; + + if (nEQs > 0) + return (solve(desiredResult)); + + if (!coupledSubscripts) { + if (safeVars == 0) + nGEQs = 0; + else + for (e = nGEQs - 1; e >= 0; e--) + if (GEQs[e].key > safeVars || -safeVars > GEQs[e].key) + deleteGEQ(e); + nVars = safeVars; + return (true); + } + + if (desiredResult != OC_SOLVE_SIMPLIFY) + fv = 0; + else + fv = safeVars; + + if (nVars == 0 || nGEQs == 0) { + nGEQs = 0; + if (desiredResult == OC_SOLVE_SIMPLIFY) + nVars = safeVars; + return (true); + } + if (desiredResult == OC_SOLVE_SIMPLIFY && nVars == safeVars) { + return (true); + } + + + if (nGEQs+6 > maxGEQs || nGEQs > 2 * nVars * nVars + 4 * nVars + 10) { + if (TRACE) + fprintf(outputFile, "TOO MANY EQUATIONS; %d equations, %d variables, ELIMINATING REDUNDANT ONES\n", nGEQs, nVars); + if (!quickKill(0,true)) + return 0; + if (nEQs > 0) + return (solve(desiredResult)); + if (TRACE) + fprintf(outputFile, "END ELIMINATION OF REDUNDANT EQUATIONS\n"); + if (DBUG) printProblem(); + } + + + { + int darkConstraints, darkShadowFeasible, unit, parallelLB; + coef_t parallelSplinters, disjointSplinters, lbSplinters, ubSplinters, splinters; + coef_t bestScore, score; + int bestVar; + int exact; + int Ue,Le; + + if (desiredResult != OC_SOLVE_SIMPLIFY) fv = 0; + else fv = safeVars; + + if (DEBUG) { + fprintf(outputFile,"Considering elimination possibilities[ \n"); + printProblem(); + } + + analyzeGEQstart: + try { + bestScore = posInfinity; + bestVar = -1; + for (i = nVars; i != fv; i--) { + analyzeElimination(i, darkConstraints, darkShadowFeasible, unit, parallelSplinters, disjointSplinters, lbSplinters, ubSplinters, parallelLB); + + score = min(min(parallelSplinters,disjointSplinters), + min(lbSplinters,ubSplinters)); + exact = score == 1; + score = 10000*(score-1) + darkConstraints; + if (score >= posInfinity) // too big the score + score = posInfinity - 1; + score -= 3*unit; + + if (score < bestScore) { + bestScore = score; + bestVar = i; + if (i > 4 && score < nGEQs) break; + } + } + assert(bestVar>=0); + exact = bestScore < 10000; + i = bestVar; + assert(i<=nVars); + analyzeElimination(i, darkConstraints, darkShadowFeasible, unit, parallelSplinters, disjointSplinters, lbSplinters, ubSplinters, parallelLB); + if (DEBUG) { + fprintf(outputFile,"] Choose to eliminate %s \n",variable(i)); + } + splinters = lbSplinters; + if (splinters <= parallelSplinters) + parallelSplinters = posInfinity; + else splinters = parallelSplinters; + if (disjointSplinters == 1) splinters = 1; + exact = splinters == 1; + if (inApproximateMode) exact = 1; + } + catch (std::overflow_error) { + int result = quickKill(0, true); + if (result == 0) + return 0; + else if (result == 1) + return true; + else { + if (nEQs > 0) + return (solve(desiredResult)); + triedEliminatingRedundant = 1; + goto analyzeGEQstart; + } + } + + if (!triedEliminatingRedundant && darkConstraints > maxGEQs) { + if (TRACE) + fprintf(outputFile, "Elimination will create TOO MANY EQUATIONS; %d equations, %d variables, %d new constraints, ELIMINATING REDUNDANT ONES\n", nGEQs, nVars,darkConstraints); + if (!quickKill(0)) + return 0; + if (nEQs > 0) + return (solve(desiredResult)); + if (TRACE) + fprintf(outputFile, "END ELIMINATION OF REDUNDANT EQUATIONS\n"); + if (DBUG) printProblem(); + + triedEliminatingRedundant = 1; + eliminateAgain = 1; + continue; + } + + if (!exact && !triedEliminatingRedundant && + safeVars > 0 && desiredResult == OC_SOLVE_SIMPLIFY) { + if (TRACE) + fprintf(outputFile, "Trying to produce exact elimination by finding redundant constraints [\n"); + if (!quickKill(1)) return 0; + if (TRACE) + fprintf(outputFile, "]\n"); + triedEliminatingRedundant = 1; + eliminateAgain = 1; + continue; + } + triedEliminatingRedundant = 0; + + if (desiredResult == OC_SOLVE_SIMPLIFY && !exact) { + partialElimination(); + switch (normalize()) { + case normalize_false: + return (false); + break; + case normalize_coupled: + case normalize_uncoupled: + break; + } + if (nEQs) return solveEQ(); + if (DBUG) fprintf(outputFile,"Stopping short due to non-exact elimination\n"); + return (true); + } + + if ( desiredResult == OC_SOLVE_SIMPLIFY && darkConstraints > maxGEQs) { + if (DBUG) fprintf(outputFile,"Stopping short due to overflow of GEQs: %d\n", darkConstraints); + return (true); + } + + if ((doTrace && desiredResult == OC_SOLVE_SIMPLIFY) || DBUG) { + fprintf(outputFile, "going to eliminate %s, (%d)\n", variable(i), i); + if (DEBUG) + printProblem(); + fprintf(outputFile, "score = " coef_fmt "/" coef_fmt "\n", bestScore,splinters); + } + + if (!exact && desiredResult == OC_SOLVE_SIMPLIFY && parallelSplinters == splinters) { + return parallelSplinter(parallelLB, parallelSplinters, desiredResult); + } + + // smoothed = 0; // what a bug!!! -- by chun 6/10/2008 + + if (i != nVars) { + j = nVars; + swapVars(i,j); + + i = j; + } + else if (DEBUG) { + printVars(); + fprintf(outputFile, "No swap needed before eliminating %s(%d/%d)\n",variable(i),i,nVars); + for(j=1;j<=i;j++) fprintf(outputFile,"var #%d = %s(%x)\n",j,variable(j),var[j]); + printProblem(); + } + nVars--; + + if (exact) { + if (nVars == 1) { + coef_t upperBound = posInfinity; + coef_t lowerBound = negInfinity; + int ub_color = 0; + int lb_color = 0; + coef_t constantTerm, coefficient; + int topEqn = nGEQs - 1; + coef_t Lc; + for (Le = topEqn; Le >= 0; Le--) + if ((Lc = GEQs[Le].coef[i]) == 0) { + if (GEQs[Le].coef[1] == 1) { + constantTerm = -GEQs[Le].coef[0]; + if (constantTerm > lowerBound || (constantTerm == lowerBound && !isRed(&GEQs[Le]))) { + lowerBound = constantTerm; + lb_color = GEQs[Le].color; + } + if (DEBUG) { + if (GEQs[Le].color == EQ_BLACK) + fprintf(outputFile, " :::=> %s >= " coef_fmt "\n", variable(1), constantTerm); + else + fprintf(outputFile, " :::=> [%s >= " coef_fmt "]\n", variable(1), constantTerm); + } + } + else { + constantTerm = GEQs[Le].coef[0]; + if (constantTerm < upperBound || (constantTerm == upperBound && !isRed(&GEQs[Le]))) { + upperBound = constantTerm; + ub_color = GEQs[Le].color; + } + if (DEBUG) { + if (GEQs[Le].color == EQ_BLACK) + fprintf(outputFile, " :::=> %s <= " coef_fmt "\n", variable(1), constantTerm); + else + fprintf(outputFile, " :::=> [%s <= " coef_fmt "]\n", variable(1), constantTerm); + } + } + } + else if (Lc > 0) { + for (Ue = topEqn; Ue >= 0; Ue--) + if (GEQs[Ue].coef[i] < 0) { + if (GEQs[Le].key != -GEQs[Ue].key) { + coef_t Uc = -GEQs[Ue].coef[i]; + coefficient = check_mul(GEQs[Ue].coef[1], Lc) + check_mul(GEQs[Le].coef[1], Uc); + constantTerm = check_mul(GEQs[Ue].coef[0], Lc) + check_mul(GEQs[Le].coef[0], Uc); + if (DEBUG) { + printGEQextra(&(GEQs[Ue])); + fprintf(outputFile, "\n"); + printGEQextra(&(GEQs[Le])); + fprintf(outputFile, "\n"); + } + if (coefficient > 0) { + constantTerm = -(int_div(constantTerm, coefficient)); + /* assert(black == 0) */ + if (constantTerm > lowerBound || + (constantTerm == lowerBound && + (desiredResult != OC_SOLVE_SIMPLIFY || (GEQs[Ue].color == EQ_BLACK && GEQs[Le].color == EQ_BLACK)))) { + lowerBound = constantTerm; + lb_color = GEQs[Ue].color || GEQs[Le].color; + } + if (DEBUG) { + if (GEQs[Ue].color || GEQs[Le].color) + fprintf(outputFile, " ::=> [%s >= " coef_fmt "]\n", variable(1), constantTerm); + else + fprintf(outputFile, " ::=> %s >= " coef_fmt "\n", variable(1), constantTerm); + } + } + else if (coefficient < 0) { + constantTerm = (int_div(constantTerm, -coefficient)); + if (constantTerm < upperBound || + (constantTerm == upperBound && GEQs[Ue].color == EQ_BLACK && GEQs[Le].color == EQ_BLACK)) { + upperBound = constantTerm; + ub_color = GEQs[Ue].color || GEQs[Le].color; + } + if (DEBUG) { + if (GEQs[Ue].color || GEQs[Le].color) + fprintf(outputFile, " ::=> [%s <= " coef_fmt "]\n", variable(1), constantTerm); + else + fprintf(outputFile, " ::=> %s <= " coef_fmt "\n", variable(1), constantTerm); + } + } + } + } + } + nGEQs = 0; + if (DEBUG) + fprintf(outputFile, " therefore, %c" coef_fmt " <= %c%s%c <= " coef_fmt "%c\n", lb_color ? '[' : ' ', lowerBound, (lb_color && !ub_color) ? ']' : ' ', variable(1), (!lb_color && ub_color) ? '[' : ' ', upperBound, ub_color ? ']' : ' '); + if (lowerBound > upperBound) + return (false); + + if (upperBound == lowerBound) { + int e = newEQ(); + assert(e == 0); + EQs[e].coef[1] = -1; + EQs[e].coef[0] = upperBound; + EQs[e].color = ub_color | lb_color; + addingEqualityConstraint(0); + } + else if (safeVars == 1) { + if (upperBound != posInfinity) { + int e = newGEQ(); + assert(e == 0); + GEQs[e].coef[1] = -1; + GEQs[e].coef[0] = upperBound; + GEQs[e].color = ub_color; + GEQs[e].key = -1; + GEQs[e].touched = 0; + } + if (lowerBound != negInfinity) { + int e = newGEQ(); + GEQs[e].coef[1] = 1; + GEQs[e].coef[0] = -lowerBound; + GEQs[e].color = lb_color; + GEQs[e].key = 1; + GEQs[e].touched = 0; + } + } + if (safeVars == 0) + nVars = 0; + return (true); + } + eliminateAgain = 1; + + { + int deadEqns[maxDead]; + int numDead = 0; + int topEqn = nGEQs - 1; + int lowerBoundCount = 0; + for (Le = topEqn; Le >= 0; Le--) + if (GEQs[Le].coef[i] > 0) + lowerBoundCount++; + if (DEBUG) + fprintf(outputFile, "lower bound count = %d\n", lowerBoundCount); + if (lowerBoundCount == 0) { + if (desiredResult != OC_SOLVE_SIMPLIFY) fv = 0; + else fv = safeVars; + nVars++; + freeEliminations(fv); + continue; + } + for (Le = topEqn; Le >= 0; Le--) + if (GEQs[Le].coef[i] > 0) { + coef_t Lc = GEQs[Le].coef[i]; + for (Ue = topEqn; Ue >= 0; Ue--) + if (GEQs[Ue].coef[i] < 0) { + if (GEQs[Le].key != -GEQs[Ue].key) { + coef_t Uc = -GEQs[Ue].coef[i]; + int e2; + if (numDead == 0) { + /*( Big kludge warning ) */ + /* this code is still using location nVars+1 */ + /* but newGEQ, if it reallocates, only copies*/ + /* locations up to nVars. This fixes that. */ + nVars++; + e2 = newGEQ(); + nVars--; + } + else { + e2 = deadEqns[--numDead]; + } + if (DEBUG) + fprintf(outputFile, "Le = %d, Ue = %d, gen = %d\n", Le, Ue, e2); + if (DEBUG) { + printGEQextra(&(GEQs[Le])); + fprintf(outputFile, "\n"); + printGEQextra(&(GEQs[Ue])); + fprintf(outputFile, "\n"); + } + eliminateAgain = 0; + coef_t g = gcd(Lc,Uc); + coef_t Lc_over_g = Lc/g; + coef_t Uc_over_g = Uc/g; + + for (k = nVars; k >= 0; k--) + GEQs[e2].coef[k] = + check_mul(GEQs[Ue].coef[k], Lc_over_g) + check_mul(GEQs[Le].coef[k], Uc_over_g); + + GEQs[e2].coef[nVars + 1] = 0; + GEQs[e2].touched = true; + GEQs[e2].color = GEQs[Ue].color | GEQs[Le].color; + + if (DEBUG) { + printGEQ(&(GEQs[e2])); + fprintf(outputFile, "\n"); + } + } + if (lowerBoundCount == 1) { + deadEqns[numDead++] = Ue; + if (DEBUG) + fprintf(outputFile, "Killed %d\n", Ue); + } + } + lowerBoundCount--; + deadEqns[numDead++] = Le; + if (DEBUG) + fprintf(outputFile, "Killed %d\n", Le); + } + + { + int isDead[maxmaxGEQs]; + for (e = nGEQs - 1; e >= 0; e--) + isDead[e] = false; + while (numDead > 0) { + e = deadEqns[--numDead]; + isDead[e] = true; + } + for (e = nGEQs - 1; e >= 0; e--) + if (isDead[e]) { + nVars++; + deleteGEQ(e); + nVars--; + } + } + continue; + } + } + else { + Problem *rS, *iS; + + rS = new Problem; + iS = new Problem; + + iS->nVars = rS->nVars = nVars; // do this immed.; in case of reallocation, we + // need to know how much to copy + rS->get_var_name = get_var_name; + rS->getVarNameArgs = getVarNameArgs; + iS->get_var_name = get_var_name; + iS->getVarNameArgs = getVarNameArgs; + + for (e = 0; e < nGEQs; e++) + if (GEQs[e].coef[i] == 0) { + int re2 = rS->newGEQ(); + int ie2 = iS->newGEQ(); + eqnncpy(&(rS->GEQs[re2]), &GEQs[e], nVars); + eqnncpy(&(iS->GEQs[ie2]), &GEQs[e], nVars); + if (DEBUG) { + int t; + fprintf(outputFile, "Copying (%d, " coef_fmt "): ", i, GEQs[e].coef[i]); + printGEQextra(&GEQs[e]); + fprintf(outputFile, "\n"); + for (t = 0; t <= nVars + 1; t++) + fprintf(outputFile, coef_fmt " ", GEQs[e].coef[t]); + fprintf(outputFile, "\n"); + } + } + for (Le = nGEQs - 1; Le >= 0; Le--) + if (GEQs[Le].coef[i] > 0) { + coef_t Lc = GEQs[Le].coef[i]; + for (Ue = nGEQs - 1; Ue >= 0; Ue--) + if (GEQs[Ue].coef[i] < 0) + if (GEQs[Le].key != -GEQs[Ue].key) { + coef_t Uc = -GEQs[Ue].coef[i]; + coef_t g = gcd(Lc,Uc); + coef_t Lc_over_g = Lc/g; + coef_t Uc_over_g = Uc/g; + int re2 = rS->newGEQ(); + int ie2 = iS->newGEQ(); + rS->GEQs[re2].touched = iS->GEQs[ie2].touched = true; + if (DEBUG) { + fprintf(outputFile, "---\n"); + fprintf(outputFile, "Le(Lc) = %d(" coef_fmt "), Ue(Uc) = %d(" coef_fmt "), gen = %d\n", Le, Lc, Ue, Uc, ie2); + printGEQextra(&GEQs[Le]); + fprintf(outputFile, "\n"); + printGEQextra(&GEQs[Ue]); + fprintf(outputFile, "\n"); + } + + if (Uc == Lc) { + for (k = nVars; k >= 0; k--) + iS->GEQs[ie2].coef[k] = rS->GEQs[re2].coef[k] = + GEQs[Ue].coef[k] + GEQs[Le].coef[k]; + iS->GEQs[ie2].coef[0] -= (Uc - 1); + } + else { + for (k = nVars; k >= 0; k--) + iS->GEQs[ie2].coef[k] = rS->GEQs[re2].coef[k] = + check_mul(GEQs[Ue].coef[k], Lc_over_g) + check_mul(GEQs[Le].coef[k], Uc_over_g); + iS->GEQs[ie2].coef[0] -= check_mul(Uc_over_g-1, Lc_over_g-1); + } + + iS->GEQs[ie2].color = rS->GEQs[re2].color + = GEQs[Ue].color || GEQs[Le].color; + + if (DEBUG) { + printGEQ(&(rS->GEQs[re2])); + fprintf(outputFile, "\n"); + } + // ie2 = iS->newGEQ(); + // re2 = rS->newGEQ(); + } + + } + iS->variablesInitialized = rS->variablesInitialized = 1; + iS->nEQs = rS->nEQs = 0; + assert(desiredResult != OC_SOLVE_SIMPLIFY); + assert(nSUBs == 0); + iS->nSUBs = rS->nSUBs = nSUBs; + iS->safeVars = rS->safeVars = safeVars; + int t; + for (t = nVars; t >= 0; t--) + rS->var[t] = var[t]; + for (t = nVars; t >= 0; t--) + iS->var[t] = var[t]; + nVars++; + if (desiredResult != true) { + int t = trace; + if (TRACE) + fprintf(outputFile, "\nreal solution(%d):\n", depth); + depth++; + trace = 0; + if (originalProblem == noProblem) { + originalProblem = this; + result = rS->solveGEQ(false); + originalProblem = noProblem; + } + else + result = rS->solveGEQ(false); + trace = t; + depth--; + if (result == false) { + delete rS; + delete iS; + return (result); + } + + if (nEQs > 0) { + /* An equality constraint must have been found */ + delete rS; + delete iS; + return (solve(desiredResult)); + } + } + if (desiredResult != false) { + if (darkShadowFeasible) { + if (TRACE) + fprintf(outputFile, "\ninteger solution(%d):\n", depth); + depth++; + conservative++; + result = iS->solveGEQ(desiredResult); + conservative--; + depth--; + if (result != false) { + delete rS; + delete iS; + return (result); + } + } + if (TRACE) + fprintf(outputFile, "have to do exact analysis\n"); + + { + coef_t worstLowerBoundConstant=1; + int lowerBounds = 0; + int lowerBound[maxmaxGEQs]; + int smallest; + int t; + conservative++; + for (e = 0; e < nGEQs; e++) + if (GEQs[e].coef[i] < -1) { + set_max(worstLowerBoundConstant, + -GEQs[e].coef[i]); + } + else if (GEQs[e].coef[i] > 1) + lowerBound[lowerBounds++] = e; + /* sort array */ + for (j = 0; j < lowerBounds; j++) { + smallest = j; + for (k = j + 1; k < lowerBounds; k++) + if (GEQs[lowerBound[smallest]].coef[i] > GEQs[lowerBound[k]].coef[i]) + smallest = k; + t = lowerBound[smallest]; + lowerBound[smallest] = lowerBound[j]; + lowerBound[j] = t; + } + if (DEBUG) { + fprintf(outputFile, "lower bound coeeficients = "); + for (j = 0; j < lowerBounds; j++) + fprintf(outputFile, " " coef_fmt, GEQs[lowerBound[j]].coef[i]); + fprintf(outputFile, "\n"); + } + + + for (j = 0; j < lowerBounds; j++) { + coef_t maxIncr; + coef_t c; + e = lowerBound[j]; + maxIncr = (check_mul(GEQs[e].coef[i]-1, worstLowerBoundConstant-1) - 1) / worstLowerBoundConstant; + + /* maxIncr += 2; */ + if ((doTrace && desiredResult == OC_SOLVE_SIMPLIFY) || DBUG) { + fprintf(outputFile, "for equation "); + printGEQ(&GEQs[e]); + fprintf(outputFile, "\ntry decrements from 0 to " coef_fmt "\n", maxIncr); + printProblem(); + } + if (maxIncr > 50) { + if (!smoothed && smoothWeirdEquations()) { + conservative--; + delete rS; + delete iS; + smoothed = 1; + goto solveGEQstart; + } + } + int neweq = newEQ(); + assert(neweq == 0); + eqnncpy(&EQs[neweq], &GEQs[e], nVars); + /* + * if (GEQs[e].color) fprintf(outputFile,"warning: adding black equality constraint + * based on red inequality\n"); + */ + EQs[neweq].color = EQ_BLACK; + eqnnzero(&GEQs[e], nVars); + GEQs[e].touched = true; + for (c = maxIncr; c >= 0; c--) { + if (DBUG) + fprintf(outputFile, "trying next decrement of " coef_fmt "\n", maxIncr - c); + if (DBUG) + printProblem(); + *rS = *this; + if (DEBUG) + rS->printProblem(); + result = rS->solve(desiredResult); + if (result == true) { + delete rS; + delete iS; + conservative--; + return (true); + } + EQs[0].coef[0]--; + } + if (j + 1 < lowerBounds) { + nEQs = 0; + eqnncpy(&GEQs[e], &EQs[0], nVars); + GEQs[e].touched = 1; + GEQs[e].color = EQ_BLACK; + *rS = *this; + if (DEBUG) + fprintf(outputFile, "exhausted lower bound, checking if still feasible "); + result = rS->solve(false); + if (result == false) + break; + } + } + if ((doTrace && desiredResult == OC_SOLVE_SIMPLIFY) || DBUG) + fprintf(outputFile, "fall-off the end\n"); + delete rS; + delete iS; + + conservative--; + return (false); + } + } + delete rS; + delete iS; + } + return (OC_SOLVE_UNKNOWN); + } + } + while (eliminateAgain); + } +} + + +int Problem::parallelSplinter(int e, int diff, int desiredResult) { + Problem *tmpProblem; + int i; + if (DBUG) { + fprintf(outputFile, "Using parallel splintering\n"); + printProblem(); + } + tmpProblem = new Problem; + int neweq = newEQ(); + assert(neweq == 0); + eqnncpy(&EQs[0], &GEQs[e], nVars); + for (i = 0; i <= diff; i++) { + *tmpProblem = * this; + tmpProblem->isTemporary = true; + if (DBUG) { + fprintf(outputFile, "Splinter # %i\n", i); + printProblem(); + } + if (tmpProblem->solve(desiredResult)) { + delete tmpProblem; + return true; + } + EQs[0].coef[0]--; + } + delete tmpProblem; + return false; +} + + +int Problem::verifyProblem() { + int result; + int e; + int areRed; + check(); + Problem tmpProblem(*this); + tmpProblem.varsOfInterest = 0; + tmpProblem.safeVars = 0; + tmpProblem.nSUBs = 0; + tmpProblem.nMemories = 0; + tmpProblem.isTemporary = true; + areRed = 0; + if (mayBeRed) { + for(e=0; e<nEQs; e++) if (EQs[e].color) areRed = 1; + for(e=0; e<nGEQs; e++) if (GEQs[e].color) areRed = 1; + if (areRed) tmpProblem.turnRedBlack(); + } + originalProblem = this; + assert(!outerColor); + outerColor = areRed; + if (TRACE) { + fprintf(outputFile, "verifying problem: [\n"); + printProblem(); + } + tmpProblem.check(); + tmpProblem.freeEliminations(0); + result = tmpProblem.solve(OC_SOLVE_UNKNOWN); + originalProblem = noProblem; + outerColor = 0; + if (TRACE) { + if (result) + fprintf(outputFile, "] verified problem\n"); + else + fprintf(outputFile, "] disproved problem\n"); + printProblem(); + } + check(); + return result; +} + + +void Problem:: freeEliminations(int fv) { + int tryAgain = 1; + int i, e, e2; + while (tryAgain) { + tryAgain = 0; + for (i = nVars; i > fv; i--) { + for (e = nGEQs - 1; e >= 0; e--) + if (GEQs[e].coef[i]) + break; + if (e < 0) + e2 = e; + else if (GEQs[e].coef[i] > 0) { + for (e2 = e - 1; e2 >= 0; e2--) + if (GEQs[e2].coef[i] < 0) + break; + } + else { + for (e2 = e - 1; e2 >= 0; e2--) + if (GEQs[e2].coef[i] > 0) + break; + } + if (e2 < 0) { + int e3; + for (e3 = nSUBs - 1; e3 >= 0; e3--) + if (SUBs[e3].coef[i]) + break; + if (e3 >= 0) + continue; + for (e3 = nEQs - 1; e3 >= 0; e3--) + if (EQs[e3].coef[i]) + break; + if (e3 >= 0) + continue; + if (DBUG) + fprintf(outputFile, "a free elimination of %s (%d)\n", variable(i),e); + if (e >= 0) { + deleteGEQ(e); + for (e--; e >= 0; e--) + if (GEQs[e].coef[i]) { + deleteGEQ(e); + } + tryAgain = (i < nVars); + } + deleteVariable(i); + } + } + } + + if (DEBUG) { + fprintf(outputFile, "\nafter free eliminations:\n"); + printProblem(); + fprintf(outputFile, "\n"); + } +} + + +void Problem::freeRedEliminations() { + int tryAgain = 1; + int i, e, e2; + int isRedVar[maxVars]; + int isDeadVar[maxVars]; + int isDeadGEQ[maxmaxGEQs]; + for (i = nVars; i > 0; i--) { + isRedVar[i] = 0; + isDeadVar[i] = 0; + } + for (e = nGEQs - 1; e >= 0; e--) { + isDeadGEQ[e] = 0; + if (GEQs[e].color) + for (i = nVars; i > 0; i--) + if (GEQs[e].coef[i] != 0) + isRedVar[i] = 1; + } + + while (tryAgain) { + tryAgain = 0; + for (i = nVars; i > 0; i--) + if (!isRedVar[i] && !isDeadVar[i]) { + for (e = nGEQs - 1; e >= 0; e--) + if (!isDeadGEQ[e] && GEQs[e].coef[i]) + break; + if (e < 0) + e2 = e; + else if (GEQs[e].coef[i] > 0) { + for (e2 = e - 1; e2 >= 0; e2--) + if (!isDeadGEQ[e2] && GEQs[e2].coef[i] < 0) + break; + } + else { + for (e2 = e - 1; e2 >= 0; e2--) + if (!isDeadGEQ[e2] && GEQs[e2].coef[i] > 0) + break; + } + if (e2 < 0) { + int e3; + for (e3 = nSUBs - 1; e3 >= 0; e3--) + if (SUBs[e3].coef[i]) + break; + if (e3 >= 0) + continue; + for (e3 = nEQs - 1; e3 >= 0; e3--) + if (EQs[e3].coef[i]) + break; + if (e3 >= 0) + continue; + if (DBUG) + fprintf(outputFile, "a free red elimination of %s\n", variable(i)); + for (; e >= 0; e--) + if (GEQs[e].coef[i]) + isDeadGEQ[e] = 1; + tryAgain = 1; + isDeadVar[i] = 1; + } + } + } + + for (e = nGEQs - 1; e >= 0; e--) + if (isDeadGEQ[e]) + deleteGEQ(e); + + for (i = nVars; i > safeVars; i--) + if (isDeadVar[i]) + deleteVariable(i); + + + if (DEBUG) { + fprintf(outputFile, "\nafter free red eliminations:\n"); + printProblem(); + fprintf(outputFile, "\n"); + } +} + +} // namespace diff --git a/omegalib/omega/src/omega_core/oc_util.cc b/omegalib/omega/src/omega_core/oc_util.cc new file mode 100644 index 0000000..a7d21be --- /dev/null +++ b/omegalib/omega/src/omega_core/oc_util.cc @@ -0,0 +1,327 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + All Rights Reserved. + + Purpose: + + Notes: + + History: +*****************************************************************************/ + +#include <omega/omega_core/oc_i.h> +#include <algorithm> + +namespace omega { + +void Problem:: problem_merge(Problem &p2) { + int newLocation[maxVars]; + int i,e2; + + resurrectSubs(); + p2.resurrectSubs(); + setExternals(); + p2.setExternals(); + + assert(safeVars == p2.safeVars); + if(DBUG) { + fprintf(outputFile,"Merging:\n"); + printProblem(); + fprintf(outputFile,"and\n"); + p2.printProblem(); + } + for(i=1; i<= p2.safeVars; i++) { + assert(p2.var[i] > 0) ; + newLocation[i] = forwardingAddress[p2.var[i]]; + } + for(; i<= p2.nVars; i++) { + int j = ++(nVars); + newLocation[i] = j; + zeroVariable(j); + var[j] = -1; + } + newLocation[0] = 0; + + for (e2 = p2.nEQs - 1; e2 >= 0; e2--) { + int e1 = newEQ(); + eqnnzero(&(EQs[e1]), nVars); + for(i=0;i<=p2.nVars;i++) + EQs[e1].coef[newLocation[i]] = p2.EQs[e2].coef[i]; + } + for (e2 = p2.nGEQs - 1; e2 >= 0; e2--) { + int e1 = newGEQ(); + eqnnzero(&(GEQs[e1]), nVars); + GEQs[e1].touched = 1; + for(i=0;i<=p2.nVars;i++) + GEQs[e1].coef[newLocation[i]] = p2.GEQs[e2].coef[i]; + } + int w = -1; + for (i = 1; i <= nVars; i++) + if (var[i] < 0) var[i] = w--; + if(DBUG) { + fprintf(outputFile,"to get:\n"); + printProblem(); + } +} + + + +void Problem::chainUnprotect() { + int i, e; + int unprotect[maxVars]; + int any = 0; + for (i = 1; i <= safeVars; i++) { + unprotect[i] = (var[i] < 0); + for (e = nSUBs - 1; e >= 0; e--) + if (SUBs[e].coef[i]) + unprotect[i] = 0; + } + for (i = 1; i <= safeVars; i++) if (unprotect[i]) any=1; + if (!any) return; + + if (DBUG) { + fprintf(outputFile, "Doing chain reaction unprotection\n"); + printProblem(); + for (i = 1; i <= safeVars; i++) + if (unprotect[i]) + fprintf(outputFile, "unprotecting %s\n", variable(i)); + } + for (i = 1; i <= safeVars; i++) + if (unprotect[i]) { + /* wild card */ + if (i < safeVars) { + int j = safeVars; + std::swap(var[i], var[j]); + for (e = nGEQs - 1; e >= 0; e--) { + GEQs[e].touched = 1; + std::swap(GEQs[e].coef[i], GEQs[e].coef[j]); + } + for (e = nEQs - 1; e >= 0; e--) + std::swap(EQs[e].coef[i], EQs[e].coef[j]); + for (e = nSUBs - 1; e >= 0; e--) + std::swap(SUBs[e].coef[i], SUBs[e].coef[j]); + std::swap(unprotect[i], unprotect[j]); + i--; + } + safeVars--; + } + if (DBUG) { + fprintf(outputFile, "After chain reactions\n"); + printProblem(); + } +} + +void Problem::resurrectSubs() { + if (nSUBs > 0 && !pleaseNoEqualitiesInSimplifiedProblems) { + int i, e, n, m,mbr; + mbr = 0; + for (e = nGEQs - 1; e >= 0; e--) if (GEQs[e].color) mbr=1; + for (e = nEQs - 1; e >= 0; e--) if (EQs[e].color) mbr=1; + if (nMemories) mbr = 1; + + assert(!mbr || mayBeRed); + + if (DBUG) { + fprintf(outputFile, "problem reduced, bringing variables back to life\n"); + if(mbr && !mayBeRed) fprintf(outputFile, "Red equations we don't expect\n"); + printProblem(); + } + if (DBUG && nEQs > 0) + fprintf(outputFile,"This is wierd: problem has equalities\n"); + + for (i = 1; i <= safeVars; i++) + if (var[i] < 0) { + /* wild card */ + if (i < safeVars) { + int j = safeVars; + std::swap(var[i], var[j]); + for (e = nGEQs - 1; e >= 0; e--) { + GEQs[e].touched = 1; + std::swap(GEQs[e].coef[i], GEQs[e].coef[j]); + } + for (e = nEQs - 1; e >= 0; e--) + std::swap(EQs[e].coef[i], EQs[e].coef[j]); + for (e = nSUBs - 1; e >= 0; e--) + std::swap(SUBs[e].coef[i], SUBs[e].coef[j]); + i--; + } + safeVars--; + } + + m = nSUBs; + n = nVars; + if (n < safeVars + m) + n = safeVars + m; + for (e = nGEQs - 1; e >= 0; e--) { + if (singleVarGEQ(&GEQs[e])) { + i = abs(GEQs[e].key); + if (i >= safeVars + 1) + GEQs[e].key += (GEQs[e].key > 0 ? m : -m); + } + else { + GEQs[e].touched = true; + GEQs[e].key = 0; + } + } + for (i = nVars; i >= safeVars + 1; i--) { + var[i + m] = var[i]; + for (e = nGEQs - 1; e >= 0; e--) + GEQs[e].coef[i + m] = GEQs[e].coef[i]; + for (e = nEQs - 1; e >= 0; e--) + EQs[e].coef[i + m] = EQs[e].coef[i]; + for (e = nSUBs - 1; e >= 0; e--) + SUBs[e].coef[i + m] = SUBs[e].coef[i]; + } + for (i = safeVars + m; i >= safeVars + 1; i--) { + for (e = nGEQs - 1; e >= 0; e--) GEQs[e].coef[i] = 0; + for (e = nEQs - 1; e >= 0; e--) EQs[e].coef[i] = 0; + for (e = nSUBs - 1; e >= 0; e--) SUBs[e].coef[i] = 0; + } + nVars += m; + safeVars += m; + for (e = nSUBs - 1; e >= 0; e--) + var[safeVars -m + 1 + e] = SUBs[e].key; + for (i = 1; i <= safeVars; i++) + forwardingAddress[var[i]] = i; + if (DBUG) { + fprintf(outputFile,"Ready to wake substitutions\n"); + printProblem(); + } + for (e = nSUBs - 1; e >= 0; e--) { + int neweq = newEQ(); + eqnncpy(&(EQs[neweq]), &(SUBs[e]), nVars); + EQs[neweq].coef[safeVars -m + 1 + e] = -1; + EQs[neweq].color = EQ_BLACK; + if (DBUG) { + fprintf(outputFile, "brought back: "); + printEQ(&EQs[neweq]); + fprintf(outputFile, "\n"); + } + } + nSUBs = 0; + + if (DBUG) { + fprintf(outputFile, "variables brought back to life\n"); + printProblem(); + } + } + + coalesce(); + recallRedMemories(); + cleanoutWildcards(); +} + + +void Problem::reverseProtectedVariables() { + int v1,v2,e,i; + coef_t t; + for (v1 = 1; v1 <= safeVars; v1++) { + v2 = safeVars+1-v1; + if (v2>=v1) break; + for(e=0;e<nEQs;e++) { + t = EQs[e].coef[v1]; + EQs[e].coef[v1] = EQs[e].coef[v2]; + EQs[e].coef[v2] = t; + } + for(e=0;e<nGEQs;e++) { + t = GEQs[e].coef[v1]; + GEQs[e].coef[v1] = GEQs[e].coef[v2]; + GEQs[e].coef[v2] = t; + GEQs[e].touched = 1; + } + for(e=0;e<nSUBs;e++) { + t = SUBs[e].coef[v1]; + SUBs[e].coef[v1] = SUBs[e].coef[v2]; + SUBs[e].coef[v2] = t; + } + } + + for (i = 1; i <= safeVars; i++) + forwardingAddress[var[i]] = i; + for (i = 0; i < nSUBs; i++) + forwardingAddress[SUBs[i].key] = -i - 1; +} + +void Problem::ordered_elimination(int symbolic) { + int i,j,e; + int isDead[maxmaxGEQs]; + for(e=0;e<nEQs;e++) isDead[e] = 0; + + if (!variablesInitialized) { + initializeVariables(); + } + + for(i=nVars;i>symbolic;i--) + for(e=0;e<nEQs;e++) + if (EQs[e].coef[i] == 1 || EQs[e].coef[i] == -1) { + for(j=nVars;j>i;j--) if (EQs[e].coef[j]) break; + if (i==j) { + doElimination(e, i); + isDead[e] = 1; + break; + } + } + + for(e=nEQs-1;e>=0;e--) + if (isDead[e]) { + nEQs--; + if (e < nEQs) eqnncpy(&EQs[e], &EQs[nEQs], nVars); + } + + for (i = 1; i <= safeVars; i++) + forwardingAddress[var[i]] = i; + for (i = 0; i < nSUBs; i++) + forwardingAddress[SUBs[i].key] = -i - 1; +} + + +coef_t Problem::checkSum() const { + coef_t cs; + int e; + cs = 0; + for(e=0;e<nGEQs;e++) { + coef_t c = GEQs[e].coef[0]; + cs += c*c*c; + } + return cs; +} + + +void Problem::coalesce() { + int e, e2, colors; + int isDead[maxmaxGEQs]; + int foundSomething = 0; + + + colors = 0; + for (e = 0; e < nGEQs; e++) + if (GEQs[e].color) + colors++; + if (colors < 2) + return; + for (e = 0; e < nGEQs; e++) + isDead[e] = 0; + for (e = 0; e < nGEQs; e++) + if (!GEQs[e].touched) + for (e2 = e + 1; e2 < nGEQs; e2++) + if (!GEQs[e2].touched && GEQs[e].key == -GEQs[e2].key + && GEQs[e].coef[0] == -GEQs[e2].coef[0]) { + int neweq = newEQ(); + eqnncpy(&EQs[neweq], &GEQs[e], nVars); + EQs[neweq].color = GEQs[e].color || GEQs[e2].color; + foundSomething++; + isDead[e] = 1; + isDead[e2] = 1; + } + for (e = nGEQs - 1; e >= 0; e--) + if (isDead[e]) { + deleteGEQ(e); + } + if (DEBUG && foundSomething) { + fprintf(outputFile, "Coalesced GEQs into %d EQ's:\n", foundSomething); + printProblem(); + } +} + +} // namespace diff --git a/omegalib/omega/src/pres_beaut.cc b/omegalib/omega/src/pres_beaut.cc new file mode 100644 index 0000000..c23962a --- /dev/null +++ b/omegalib/omega/src/pres_beaut.cc @@ -0,0 +1,235 @@ +#include <omega/pres_tree.h> +#include <omega/pres_conj.h> +#include <omega/Relation.h> +#include <omega/omega_i.h> + +///////////////////////// +// // +// Beautify functions // +// // +///////////////////////// + + +namespace omega { + +// +// f & true = f +// f | false = f +// f1 & f2 & ... & fn = Conjunct(f1,f2,...,fn) +// + +void Rel_Body::beautify() { + assert(children().length()==1); + set_parent(NULL,this); + + skip_finalization_check++; + formula()->beautify(); + Formula *child = formula(); + if(child->node_type()==Op_And && child->children().empty()) { + remove_child(child); + delete child; + add_conjunct(); + } + skip_finalization_check--; + + if(pres_debug) { + fprintf(DebugFile, "\n=== Beautified TREE ===\n"); + prefix_print(DebugFile); + } + assert(children().length()==1); +} + +void Formula::beautify() { + // copy list of children, as they may be removed as we work + List<Formula*> kiddies = myChildren; + + for(List_Iterator<Formula*> c(kiddies); c; c++) + (*c)->beautify(); +} + +void F_Exists::beautify() { + Formula::beautify(); + assert(children().length()==1); + + if(myLocals.empty()) { + // exists( : ***) + parent().remove_child(this); + parent().add_child(children().remove_front()); + delete this; + } + else if (children()[1]->node_type() == Op_And && children()[1]->children().empty()) { + // exists(*** : TRUE) --chun 6/4/2008 + parent().remove_child(this); + parent().add_child(children().remove_front()); + delete this; + } + else { + Formula *child = children().front(); + if(child->node_type() == Op_Conjunct || child->node_type() == Op_Exists) { + child->push_exists(myLocals); + parent().remove_child(this); + parent().add_child(child); + children().remove_front(); + delete this; + } + } +} + +void F_Forall::beautify() { + Formula::beautify(); + assert(children().length()==1); + + if(myLocals.empty()) { + parent().remove_child(this); + parent().add_child(children().remove_front()); + delete this; + } +} + + +// +// The Pix-free versions of beautify for And and Or are a bit +// less efficient than the previous code, as we keep moving +// things from one list to another, but they do not depend on +// knowing that a Pix is valid after the list is updated, and +// they can always be optimized later if necessary. +// + +void F_Or::beautify() { + Formula::beautify(); + + List<Formula*> uglies, beauties; + uglies.join(children()); assert(children().empty()); +#if ! defined NDEBUG + foreach(c,Formula*,uglies,c->set_parent(0)); +#endif + + while(!uglies.empty()) { + Formula *f = uglies.remove_front(); + if(f->node_type()==Op_And && f->children().empty() ) { + // smth | true = true + foreach(c,Formula*,uglies,delete c); + foreach(c,Formula*,beauties,delete c); + parent().remove_child(this); + parent().add_and(); + delete f; + delete this; + return; + } + else if(f->node_type()==Op_Or) { + // OR(f[1-m], OR(c[1-n])) = OR(f[1-m], c[1-n]) +#if ! defined NDEBUG + foreach(c,Formula*,f->children(),c->set_parent(0)); +#endif + uglies.join(f->children()); + delete f; + } + else + beauties.prepend(f); + } + + if(beauties.length()==1) { + beauties.front()->set_parent(&parent()); + parent().remove_child(this); + parent().add_child(beauties.remove_front()); + delete this; + } + else { + foreach(c,Formula*,beauties,(c->set_parent(this), + c->set_relation(relation()))); + assert(children().empty()); + children().join(beauties); + } +} + +void F_And::beautify() { + Formula::beautify(); + + Conjunct *conj = NULL; + + List<Formula*> uglies, beauties; + uglies.join(children()); assert(children().empty()); +#if ! defined NDEBUG + foreach(c,Formula*,uglies,c->set_parent(0)); +#endif + + while(!uglies.empty()) { + Formula *f = uglies.remove_front(); + if (f->node_type() == Op_Conjunct) { + if(conj==NULL) + conj = f->really_conjunct(); + else { + Conjunct *conj1 = merge_conjs(conj, f->really_conjunct(), MERGE_REGULAR); + delete f; + delete conj; + conj = conj1; + } + } + else if(f->node_type()==Op_Or && f->children().empty()) { + // smth & false = false + foreach(c,Formula*,uglies,delete c); + foreach(c,Formula*,beauties,delete c); + parent().remove_child(this); + parent().add_or(); + delete f; + delete conj; + delete this; + return; + } + else if(f->node_type()==Op_And) { + // AND(f[1-m], AND(c[1-n])) = AND(f[1-m], c[1-n]) +#if ! defined NDEBUG + foreach(c,Formula*,f->children(),c->set_parent(0)); +#endif + uglies.join(f->children()); + delete f; + } + else + beauties.prepend(f); + } + + if(conj!=NULL) + beauties.prepend(conj); + + if(beauties.length()==1) { + beauties.front()->set_parent(&parent()); + parent().remove_child(this); + parent().add_child(beauties.remove_front()); + delete this; + } + else { + foreach(c,Formula*,beauties,(c->set_parent(this), + c->set_relation(relation()))); + assert(children().empty()); + children().join(beauties); + } +} + +void F_Not::beautify() { + Formula::beautify(); + assert(children().length()==1); + Formula *child = children().front(); + + if(child->node_type()==Op_And && child->children().empty()) { + // Not TRUE = FALSE + parent().remove_child(this); + parent().add_or(); + delete this; + } + else if (child->node_type()==Op_Or && child->children().empty()) { + // Not FALSE = TRUE + parent().remove_child(this); + parent().add_and(); + delete this; + } +} + +void Conjunct::beautify() { + if(is_true()) { + parent().remove_child(this); + parent().add_and(); + delete this; + } +} + +} // namespace diff --git a/omegalib/omega/src/pres_cnstr.cc b/omegalib/omega/src/pres_cnstr.cc new file mode 100644 index 0000000..a8ebd15 --- /dev/null +++ b/omegalib/omega/src/pres_cnstr.cc @@ -0,0 +1,450 @@ +#include <omega/pres_cnstr.h> +#include <omega/pres_conj.h> +#include <omega/Relation.h> +#include <omega/omega_i.h> + +namespace omega { + +Constraint_Handle::Constraint_Handle(Conjunct *_c, eqn **_eqns, int _e): + c(_c), eqns(_eqns), e(_e) { +} + +GEQ_Handle::GEQ_Handle(Conjunct *_c, int _e): + Constraint_Handle(_c,&(_c->problem->GEQs),_e) { +} + +bool Constraint_Handle::is_const(Variable_ID v) { + bool is_const=true; + for(Constr_Vars_Iter cvi(*this, false); cvi && is_const; cvi++) + is_const = ((*cvi).coef == 0 || ((*cvi).var == v && (*cvi).coef !=0)); + return is_const; +} + +bool Constraint_Handle::is_const_except_for_global(Variable_ID v){ + bool is_const=true; + for(Constr_Vars_Iter cvi(*this, false); cvi && is_const; cvi++) + if((*cvi).var->kind() != Global_Var) + is_const = ((*cvi).coef == 0 || ((*cvi).var == v && (*cvi).coef !=0)); + return is_const; +} + +bool EQ_Handle::operator==(const Constraint_Handle &that) { + Constraint_Handle &e1=*this; + const Constraint_Handle &e2=that; + int sign = 0; + for(Constr_Vars_Iter cvi(e1, false); cvi; cvi++) { + coef_t c1 = (*cvi).coef; + coef_t c2 = e2.get_coef((*cvi).var); + if (sign == 0) sign = (c1*c2>=0?1:-1); + if (sign*c1 != c2) return false; + } + assert(sign != 0); + { + for(Constr_Vars_Iter cvi(e2, false); cvi; cvi++) { + coef_t c1 = e1.get_coef((*cvi).var); + coef_t c2 = (*cvi).coef; + if (sign*c1 != c2) return false; + } + } + return sign * e1.get_const() == e2.get_const(); +} + +bool GEQ_Handle::operator==(const Constraint_Handle &that) { + Constraint_Handle &e1=*this; + const Constraint_Handle &e2=that; + for(Constr_Vars_Iter cvi(e1, false); cvi; cvi++) { + coef_t c1 = (*cvi).coef; + coef_t c2 = e2.get_coef((*cvi).var); + if (c1 != c2) return false; + } + { + for(Constr_Vars_Iter cvi(e2, false); cvi; cvi++) { + coef_t c1 = e1.get_coef((*cvi).var); + coef_t c2 = (*cvi).coef; + if (c1 != c2) return false; + } + } + return e1.get_const() == e2.get_const(); +} + + + + +void GEQ_Handle::negate() { + assert(! this->relation()->is_simplified()); + int i; + for(i=1; i<=c->problem->nVars; i++) { + (*eqns)[e].coef[i] = -(*eqns)[e].coef[i]; + } + (*eqns)[e].coef[0] = -(*eqns)[e].coef[0]-1; +} + +bool Constraint_Handle::has_wildcards() const { + Constr_Vars_Iter C(*this, true); + if (C.live()) { + assert(C.curr_var()->kind() == Wildcard_Var); + assert(C.curr_coef() != 0); + return 1; + } + return 0; +} + +int Constraint_Handle::max_tuple_pos() const { + int m = 0; + for( Constr_Vars_Iter C(*this, false); C.live() ; C.next()) { + switch (C.curr_var()->kind()) { + case Input_Var: + case Output_Var: { + int pos = C.curr_var()->get_position(); + if (m < pos) m = pos; + break; + } + default: + ; + } + } + return m; +} + +int Constraint_Handle::min_tuple_pos() const { + int m = 0; + for( Constr_Vars_Iter C(*this, false); C.live() ; C.next()) { + switch (C.curr_var()->kind()) { + case Input_Var: + case Output_Var: { + int pos = C.curr_var()->get_position(); + if (m == 0 || m > pos) m = pos; + break; + } + default: + ; + } + } + return m; +} + + +EQ_Handle::EQ_Handle(Conjunct *_c, int _e): Constraint_Handle(_c,&(_c->problem->EQs),_e) { +} + +// +// Update functions. +// +void Constraint_Handle::update_coef(Variable_ID D, coef_t I) { + assert(! this->relation()->is_simplified()); + assert(D != 0); + // The next two assertions are somewhat high-cost. +#if !defined(NDEBUG) + // skip_set_checks++; + assert((D->kind() != Input_Var || D->get_position() <= this->relation()->n_inp())); + assert((D->kind() != Output_Var || D->get_position() <= this->relation()->n_out())); + // skip_set_checks--; +#endif + int col = c->get_column(D); + (*eqns)[e].coef[col] += I; +} + +void Constraint_Handle::update_const(coef_t I) { + assert(! this->relation()->is_simplified()); + (*eqns)[e].coef[0] += I; +} + + +// update a coefficient of a variable that already exists in mappedvars + +void Constraint_Handle::update_coef_during_simplify(Variable_ID D, coef_t I) { + assert(D != 0); + int col = c->get_column(D); + (*eqns)[e].coef[col] += I; +} + +void Constraint_Handle::update_const_during_simplify(coef_t I) { + (*eqns)[e].coef[0] += I; +} + +// +// Get functions. +// + +coef_t Constraint_Handle::get_coef(Variable_ID v) const { + assert(this->relation()->is_simplified()); + assert(v != 0); + int col = c->find_column(v); + if(col == 0) { + return 0; + } + else { + return (*eqns)[e].coef[col]; + } +} + +coef_t Constraint_Handle::get_coef_during_simplify(Variable_ID v) const { + assert(v != 0); + int col = c->find_column(v); + if(col == 0) { + return 0; + } + else { + return (*eqns)[e].coef[col]; + } +} + +coef_t Constraint_Handle::get_const() const { + assert(this->relation()->is_simplified()); + return((*eqns)[e].coef[0]); +} + +coef_t Constraint_Handle::get_const_during_simplify() const { + return((*eqns)[e].coef[0]); +} + +Variable_ID Constraint_Handle::get_local(const Global_Var_ID G) { + return relation()->get_local(G); +} + +Variable_ID Constraint_Handle::get_local(const Global_Var_ID G, Argument_Tuple of) { + return relation()->get_local(G, of); +} + +void Constraint_Handle::finalize() { + c->n_open_constraints--; +} + +void Constraint_Handle::multiply(int multiplier) { + int i; + assert(! this->relation()->is_simplified()); + for(i=1; i<=c->problem->nVars; i++) { + (*eqns)[e].coef[i] = (*eqns)[e].coef[i] * multiplier; + } + (*eqns)[e].coef[0] = (*eqns)[e].coef[0] * multiplier; +} + +Rel_Body *Constraint_Handle::relation() const { + return c->relation(); +} + + +// +// Variables of constraint iterator. +// +Constr_Vars_Iter::Constr_Vars_Iter(const Constraint_Handle &ch, bool _wild_only): eqns(ch.eqns), e(ch.e), prob(ch.c->problem), vars(ch.c->mappedVars), wild_only(_wild_only) { + assert(vars.size() == prob->nVars); + for(current=1; current<=prob->nVars; current++) { + if((*eqns)[e].coef[current]!=0 && + (!wild_only || vars[current]->kind()==Wildcard_Var)) + return; + } +} + +int Constr_Vars_Iter::live() const { + return (current<=prob->nVars); +} + + +void Constr_Vars_Iter::operator++() { this->operator++(0); } + +void Constr_Vars_Iter::operator++(int) { + for(current++ ; current <=prob->nVars; current++) + if((*eqns)[e].coef[current]!=0 && + (!wild_only || vars[current]->kind()==Wildcard_Var)) + return; +} + + +Variable_ID Constr_Vars_Iter::curr_var() const { + assert(current <= prob->nVars); + return vars[current]; +} + +coef_t Constr_Vars_Iter::curr_coef() const { + assert(current <= prob->nVars); + return (*eqns)[e].coef[current]; +} + +Variable_Info Constr_Vars_Iter::operator*() const { + assert(current <= prob->nVars); + return Variable_Info(vars[current],(*eqns)[e].coef[current]); +} + +// +// Constraint iterator. +// +Constraint_Iterator Conjunct::constraints() { + return Constraint_Iterator(this); +} + +Constraint_Iterator::Constraint_Iterator(Conjunct *_c): c(_c), current(0), + last(c->problem->nGEQs-1), eqns(&(c->problem->GEQs)) { + if(!this->live()) (*this)++; // switch to EQ's if no GEQs +} + +int Constraint_Iterator::live() const { + return current <=last; +} + +void Constraint_Iterator::operator++() { + this->operator++(0); +} + +void Constraint_Iterator::operator++(int) { + if(++current > last) + if(eqns == &(c->problem->GEQs)) { // Switch to EQs + eqns = &(c->problem->EQs); + current = 0; + last = c->problem->nEQs-1; + } +} + +Constraint_Handle Constraint_Iterator::operator*() { + assert((c && eqns && current <= last) && "Constraint_Iterator::operator*: bad call"); + return Constraint_Handle(c,eqns,current); +} + +Constraint_Handle Constraint_Iterator::operator*() const { + assert((c && eqns && current <= last) && "Constraint_Iterator::operator*: bad call"); + return Constraint_Handle(c,eqns,current); +} + + +// +// EQ iterator. +// +EQ_Iterator Conjunct::EQs() { + return EQ_Iterator(this); +} + +EQ_Iterator::EQ_Iterator(Conjunct *_c) : c(_c) { + last = c->problem->nEQs-1; + current = 0; +} + +int EQ_Iterator::live() const { + return current <= last; +} + +void EQ_Iterator::operator++() { + this->operator++(0); +} + +void EQ_Iterator::operator++(int) { + current++; +} + +EQ_Handle EQ_Iterator::operator*() { + assert((c && current <= last) && "EQ_Iterator::operator*: bad call"); + return EQ_Handle(c,current); +} + +EQ_Handle EQ_Iterator::operator*() const { + assert((c && current <= last) && "EQ_Iterator::operator*: bad call"); + return EQ_Handle(c,current); +} + + +// +// GEQ iterator. +// +GEQ_Iterator Conjunct::GEQs() { + return GEQ_Iterator(this); +} + +GEQ_Iterator::GEQ_Iterator(Conjunct *_c) : c(_c) { + last = c->problem->nGEQs-1; + current = 0; +} + +int GEQ_Iterator::live() const { + return current <= last; +} + +void GEQ_Iterator::operator++() { + this->operator++(0); +} + +void GEQ_Iterator::operator++(int) { + current++; +} + + +GEQ_Handle GEQ_Iterator::operator*() { + assert((c && current <= last) && "GEQ_Iterator::operator*: bad call"); + return GEQ_Handle(c,current); +} + +GEQ_Handle GEQ_Iterator::operator*() const { + assert((c && current <= last) && "GEQ_Iterator::operator*: bad call"); + return GEQ_Handle(c,current); +} + + +void copy_constraint(Constraint_Handle H, const Constraint_Handle initial) { + // skip_set_checks++; +// assert(H.relation()->n_inp() == initial.relation()->n_inp()); +// assert(H.relation()->n_out() == initial.relation()->n_out()); + + H.update_const_during_simplify(initial.get_const_during_simplify()); + if (H.relation() == initial.relation()) { + for( Constr_Vars_Iter C(initial, false); C.live() ; C.next()) { + assert(C.curr_var()->kind()!= Forall_Var && + C.curr_var()->kind()!= Exists_Var); + if (C.curr_var()->kind()!= Wildcard_Var) + H.update_coef_during_simplify(C.curr_var(), C.curr_coef()); + else + // Must add a new wildcard, + // since they can't be used outside local Conjunct + H.update_coef_during_simplify(H.c->declare(), C.curr_coef()); + } + } + else { + Rel_Body *this_rel = H.relation(); + for( Constr_Vars_Iter C(initial, false); C.live() ; C.next()) { + switch (C.curr_var()->kind()) { + case Forall_Var: + case Exists_Var: + assert(0 && "Can't copy quantified constraints across relations"); + break; + case Wildcard_Var: + // for each wildcard var we see, create a new wildcard + // will lead to lots of wildcards, but Wayne likes it + // that way + { + H.update_coef_during_simplify(H.c->declare(), C.curr_coef()); + break; + } + case Input_Var: //use variable_ID of corresponding position + { + int pos = C.curr_var()->get_position(); + assert(this_rel->n_inp() >= pos); + Variable_ID V = this_rel->input_var(pos); + H.update_coef_during_simplify(V, C.curr_coef()); + break; + } + case Output_Var: //use variable_ID of corresponding position + { + int pos = C.curr_var()->get_position(); + assert(this_rel->n_out() >= pos); + Variable_ID V = this_rel->output_var(pos); + H.update_coef_during_simplify(V, C.curr_coef()); + break; + } + + case Global_Var: // get this Global's Var_ID in this relation + { + Variable_ID V; + Global_Var_ID G = C.curr_var()->get_global_var(); + if (G->arity() == 0) + V = this_rel->get_local(G); + else + V = this_rel->get_local(G,C.curr_var()->function_of()); + H.update_coef_during_simplify(V, C.curr_coef()); + break; + } + default: + assert(0 && "copy_constraint: variable of impossible type"); + } + } + } + // skip_set_checks--; +} + +} // namespace diff --git a/omegalib/omega/src/pres_col.cc b/omegalib/omega/src/pres_col.cc new file mode 100644 index 0000000..1569116 --- /dev/null +++ b/omegalib/omega/src/pres_col.cc @@ -0,0 +1,104 @@ +#include <omega/pres_conj.h> +#include <omega/RelBody.h> +#include <omega/omega_i.h> + +namespace omega { + +// +// Copy column fr_col of problem fp +// to column to_col of problem tp. +// Displacement for constraints in tp are start_EQ and start_GEQ. +// +void copy_column(Problem *tp, int to_col, + Problem *fp, int fr_col, + int start_EQ, int start_GEQ) { + checkVars(to_col); + assert(start_EQ + fp->nEQs <= tp->allocEQs); + assert(start_GEQ + fp->nGEQs <= tp->allocGEQs); + + int i; + for(i=0; i<fp->nEQs; i++) { + tp->EQs[i+start_EQ].coef[to_col] = fp->EQs[i].coef[fr_col]; + } + for(i=0; i<fp->nGEQs; i++) { + tp->GEQs[i+start_GEQ].coef[to_col] = fp->GEQs[i].coef[fr_col]; + } +} + +// +// Zero column to_col of problem tp. +// Displacement for constraints in to_conj are start_EQ and start_GEQ. +// Number of constraints to zero are no_EQ and no_GEQ. +// +void zero_column(Problem *tp, int to_col, + int start_EQ, int start_GEQ, + int no_EQs, int no_GEQs) { + assert(start_EQ + no_EQs <= tp->allocEQs); + assert(start_GEQ + no_GEQs <= tp->allocGEQs); + + int i; + for(i=0; i<no_EQs; i++) { + tp->EQs[i+start_EQ].coef[to_col] = 0; + } + for(i=0; i<no_GEQs; i++) { + tp->GEQs[i+start_GEQ].coef[to_col] = 0; + } +} + +// +// return column for D in conjunct +// +int Conjunct::get_column(Variable_ID D) { + int col = find_column(D); + if (col == 0) // if it does not already have a column assigned + col = map_to_column(D); + assert(col > 0); // Not substituted + return col; +} + +// +// Find column in conjunct. +// +int Conjunct::find_column(Variable_ID D) { + assert(D != 0); + int column = mappedVars.index(D); + + // If it has been through the omega core (variablesInitialized), + // and it exists in the problem, check to see if it has been forwarded. + // This will likely only be the case if substitutions have been done; + // that won't arise in user code, only in print_with_subs and the + // Substitutions class. + if (problem->variablesInitialized && column > 0) { + assert(problem->forwardingAddress[column] != 0); + column = problem->forwardingAddress[column]; + } + assert (column <= problem->nVars); + return column; +} + +// +// Create new column in conjunct. +// +int Conjunct::map_to_column(Variable_ID D) { + assert(D != 0); + // This heavy-duty assertion says that if you are trying to use a global + // var's local representative in a relation, that you have first told the + // relation that you are using it here. PUFS requires that we know + // all the function symbols that might be used in a relation. + // If one wanted to be less strict, one could just tell the relation + // that the global variable was being used. + assert(D->kind() != Global_Var || + (relation()->has_local(D->get_global_var(), D->function_of()) + && "Attempt to update global var without a local variable ID")); + + cols_ordered = false; // extremely important + checkVars(problem->nVars+2); + int col = ++problem->nVars; + mappedVars.append(D); + problem->forwardingAddress[col] = col; + problem->var[col] = col; + zero_column(problem, col, 0, 0, problem->nEQs, problem->nGEQs); + return col; +} + +} // namespace diff --git a/omegalib/omega/src/pres_conj.cc b/omegalib/omega/src/pres_conj.cc new file mode 100644 index 0000000..f3f458d --- /dev/null +++ b/omegalib/omega/src/pres_conj.cc @@ -0,0 +1,1460 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + All Rights Reserved. + + Purpose: + class Conjunct. + + Notes: + + History: +*****************************************************************************/ + +#include <omega/omega_core/oc.h> +#include <omega/pres_conj.h> +#include <omega/pres_cmpr.h> +#include <omega/Relation.h> +#include <omega/omega_i.h> +#include <set> + +namespace omega { + +int NR_CONJUNCTS, MAX_CONJUNCTS; + +/* + * Make a new wildcard variable, return WC number. + * Should be static to this file, but must be a friend of conjunct. + */ +int new_WC(Conjunct *nc, Problem *) { + Variable_ID wc = nc->declare(); + int wc_no = nc->map_to_column(wc); + return(wc_no); +} + + +const char* get_var_name(unsigned int col, void * void_conj) { +#if defined PRINT_COLUMN_NUMBERS + static char scum[512]; +#endif + Conjunct *c = (Conjunct *) void_conj; + if (col == 0) + return 0; + Variable_ID v = c->mappedVars[col]; + assert(v!=0); +#if defined PRINT_COLUMN_NUMBERS + strcpy(scum, v->char_name()); + sprintf(scum + strlen(scum), "(%d)", col); + return scum; +#endif + return v->char_name(); +} + +void Conjunct::promise_that_ub_solutions_exist(Relation &R) { +#ifndef NDEBUG + Relation verify=Relation(R, this); + assert(verify.is_upper_bound_satisfiable()); +#endif + verified = true; +} + + +int Conjunct::max_ufs_arity_of_set() { + int ma = 0, a; + for (Variable_ID_Iterator v(mappedVars); v; v++) + if ((*v)->kind() == Global_Var && (*v)->function_of() == Set_Tuple + && query_variable_used(*v)) { + a = (*v)->get_global_var()->arity(); + if (a > ma) + ma = a; + } + return ma; +} + + +int Conjunct::max_ufs_arity_of_in() { + int ma = 0, a; + for (Variable_ID_Iterator v(mappedVars); v; v++) + if ((*v)->kind() == Global_Var && (*v)->function_of() == Input_Tuple + && query_variable_used(*v)) { + a = (*v)->get_global_var()->arity(); + if (a > ma) + ma = a; + } + return ma; +} + + +int Conjunct::max_ufs_arity_of_out() { + int ma = 0, a; + for (Variable_ID_Iterator v(mappedVars); v; v++) + if ((*v)->kind() == Global_Var && (*v)->function_of() == Output_Tuple + && query_variable_used(*v)) { + a = (*v)->get_global_var()->arity(); + if (a > ma) + ma = a; + } + return ma; +} + + +bool Conjunct::is_unknown() const { + assert(problem || comp_problem); + return !exact && ((problem && problem->nEQs==0 && problem->nGEQs==0) || + (comp_problem && comp_problem->no_constraints())); +} + + +/* + * Remove black constraints from the problem. + * Make all the remaining red constraints black. + */ +void Conjunct::rm_color_constrs() { + int geqs = 0, eqs = 0; + + possible_leading_0s = -1; + guaranteed_leading_0s = -1; + leading_dir = 0; + + for(int i=0; i<problem->nGEQs; i++) { + if(problem->GEQs[i].color) { + if(geqs!=i) + eqnncpy(&problem->GEQs[geqs], &problem->GEQs[i], problem->nVars); + problem->GEQs[geqs].color = EQ_BLACK; + geqs++; + } + } + problem->nGEQs = geqs; + + for(int i=0; i<problem->nEQs; i++) { + if(problem->EQs[i].color) { + if(eqs!=i) + eqnncpy(&problem->EQs[eqs], &problem->EQs[i], problem->nVars); + problem->EQs[eqs].color = EQ_BLACK; + eqs++; + } + } + problem->nEQs = eqs; +} + + + +// +// Conjunct constructors. +// +Conjunct::Conjunct() : + F_Declaration(NULL, NULL), + mappedVars(0), + n_open_constraints(0), + cols_ordered(false), + simplified(false), + verified(false), + guaranteed_leading_0s(-1), + possible_leading_0s(-1), + leading_dir(0), + exact(true), + r_constrs(0) { + NR_CONJUNCTS++; + if (NR_CONJUNCTS>MAX_CONJUNCTS) MAX_CONJUNCTS = NR_CONJUNCTS; + problem = new Problem; + comp_problem = NULL; + problem->get_var_name = get_var_name; + problem->getVarNameArgs = (void *) this; +} + + +Conjunct::Conjunct(Formula *f, Rel_Body *r) : + F_Declaration(f,r), + mappedVars(0), + n_open_constraints(0), + cols_ordered(false), + simplified(false), + verified(false), + guaranteed_leading_0s(-1), + possible_leading_0s(-1), + leading_dir(0), + exact(true), + r_constrs(0) { + NR_CONJUNCTS++; + if (NR_CONJUNCTS>MAX_CONJUNCTS) MAX_CONJUNCTS = NR_CONJUNCTS; + problem = new Problem; + comp_problem = NULL; + problem->get_var_name = get_var_name; + problem->getVarNameArgs = (void *) this ; +} + +void internal_copy_conjunct(Conjunct* to, Conjunct* fr); + +// +// Copy Conjunct. +// +Conjunct* Conjunct::copy_conj_diff_relation(Formula *parent, Rel_Body *rel_body) { + Conjunct *new_conj; + if(problem && comp_problem==NULL) { + new_conj = new Conjunct(parent, rel_body); + internal_copy_conjunct(new_conj, this); + } + else if (problem==NULL && comp_problem) { + /* copy compressed conjunct */ + assert(0 && "copy compressed conjunct"); + new_conj = 0; + } + else { + assert(0 && "problem == NULL"); + new_conj = 0; + } + return new_conj; +} + + +void internal_copy_conjunct(Conjunct* to, Conjunct* fr) { + copy_conj_header(to, fr); + + // + // We repeat part of what is done by copy_conj_header(to, fr) by + // calling Problem::operator=(const Problem &). + // copy_conj_header should go away, but there is some code that still needs + // it in negate_conj. + // + to->r_constrs = fr->r_constrs; + to->simplified = fr->simplified; + to->verified = fr->verified; + to->guaranteed_leading_0s = fr->guaranteed_leading_0s; + to->possible_leading_0s = fr->possible_leading_0s; + to->leading_dir = fr->leading_dir; + // the following duplicates some work of the "copy_conj_header" brain damage + *to->problem = *fr->problem; + to->problem->getVarNameArgs = (void *)to; // important +} + + +// +// Copy Conjunct variable declarations +// and problem parameters but not problem itself. +// +void copy_conj_header(Conjunct* to, Conjunct* fr) { + free_var_decls(to->myLocals); to->myLocals.clear(); + + copy_var_decls(to->myLocals, fr->myLocals); + to->mappedVars = fr->mappedVars; + to->remap(); + reset_remap_field(fr->myLocals); + + to->cols_ordered = fr->cols_ordered; + to->r_constrs = fr->r_constrs; + to->simplified = fr->simplified; + to->verified = fr->verified; + to->guaranteed_leading_0s = fr->guaranteed_leading_0s; + to->possible_leading_0s = fr->possible_leading_0s; + to->leading_dir = fr->leading_dir; + to->n_open_constraints = fr->n_open_constraints; + to->exact=fr->exact; + + Problem *fp = fr->problem; + Problem *tp = to->problem; + tp->nVars = fp->nVars; + tp->safeVars = fp->safeVars; + tp->variablesInitialized = fp->variablesInitialized; + tp->variablesFreed = fp->variablesFreed; + for(int i=1; i<=maxVars; i++) { // only need nVars of var + tp->forwardingAddress[i] = fp->forwardingAddress[i]; + tp->var[i] = fp->var[i]; + } + to->problem->get_var_name = get_var_name; + to->problem->getVarNameArgs = (void *)to ; +} + + +void Conjunct::reverse_leading_dir_info() { + leading_dir *= -1; +} + + +void Conjunct::enforce_leading_info(int guaranteed, int possible, int dir) { + skip_finalization_check++; + guaranteed_leading_0s = guaranteed; + + int d = min(relation()->n_inp(),relation()->n_out()); + + assert(0 <= guaranteed); + assert(guaranteed <= possible); + assert(possible <= d); + + for(int i = 1; i <= guaranteed; i++) { + EQ_Handle e = add_EQ(); + e.update_coef_during_simplify(input_var(i), -1); + e.update_coef_during_simplify(output_var(i), 1); + e.finalize(); + } + + + if (guaranteed == possible && guaranteed >= 0 && possible+1 <= d && dir) { + GEQ_Handle g = add_GEQ(); + if (dir > 0) { + g.update_coef_during_simplify(input_var(possible+1), -1); + g.update_coef_during_simplify(output_var(possible+1), 1); + } + else { + g.update_coef_during_simplify(input_var(possible+1), 1); + g.update_coef_during_simplify(output_var(possible+1), -1); + } + g.update_const_during_simplify(-1); + g.finalize(); + possible_leading_0s = possible; + leading_dir = dir; + } + else { + possible_leading_0s = d; + leading_dir = 0; + } + + skip_finalization_check--; +#if ! defined NDEBUG + assert_leading_info(); +#endif +} + + +void Conjunct::invalidate_leading_info(int changed) { + if (changed == -1) { + guaranteed_leading_0s = possible_leading_0s = -1; + leading_dir = 0; + } + else { + int d = min(relation()->n_inp(), relation()->n_out()); + assert(1 <= changed && changed <= d); + if (possible_leading_0s == changed -1) { + possible_leading_0s = d; + } + guaranteed_leading_0s = min(guaranteed_leading_0s,changed-1); + } +#if ! defined NDEBUG + assert_leading_info(); +#endif +} + + +int Conjunct::leading_dir_valid_and_known() { + if (relation()->is_set()) { + return 0; + } + // if we know leading dir, we can rule out extra possible 0's + assert(leading_dir == 0 || + possible_leading_0s == guaranteed_leading_0s); + + return (possible_leading_0s == guaranteed_leading_0s && + possible_leading_0s >= 0 && + possible_leading_0s < min(relation()->n_inp(),relation()->n_out()) + && leading_dir); +} + + +#if ! defined NDEBUG +void Conjunct::assert_leading_info() { + if (relation()->is_set()) { + return; + } + + int d = min(relation()->n_inp(), relation()->n_out()); + + if ( guaranteed_leading_0s == -1 + && guaranteed_leading_0s == possible_leading_0s) + assert(leading_dir == 0); + + if(leading_dir != 0 && + possible_leading_0s != guaranteed_leading_0s) { + use_ugly_names++; + prefix_print(DebugFile); + use_ugly_names--; + } + + assert(leading_dir == 0 || possible_leading_0s == guaranteed_leading_0s); + + assert(possible_leading_0s <= d && guaranteed_leading_0s <= d); + + assert(possible_leading_0s == -1 || guaranteed_leading_0s <= possible_leading_0s); + + // check that there must be "guaranteed_leading_0s" 0s + int carried_level; + for (carried_level = 1; carried_level <= guaranteed_leading_0s; carried_level++) { + Variable_ID in = input_var(carried_level), + out = output_var(carried_level); + coef_t lb, ub; + bool guar; + query_difference(out, in, lb, ub, guar); + if (lb != 0 && ub != 0) { + // probably "query_difference" is just approximate + // add the negation of leading_dir and assert that + // the result is unsatisfiable; + // add in > out (in-out-1>=0) and assert unsatisfiable. + + Conjunct *test = copy_conj_same_relation(); + test->problem->turnRedBlack(); + skip_finalization_check++; + + GEQ_Handle g = test->add_GEQ(); + g.update_coef_during_simplify(in, -1); + g.update_coef_during_simplify(out, 1); + g.update_const_during_simplify(-1); + g.finalize(); + assert(!simplify_conj(test, true, 0, 0)); + // test was deleted by simplify_conj, as it was FALSE + + test = copy_conj_same_relation(); + test->problem->turnRedBlack(); + g = test->add_GEQ(); + g.update_coef_during_simplify(in, 1); + g.update_coef_during_simplify(out, -1); + g.update_const_during_simplify(-1); + g.finalize(); + assert(!simplify_conj(test, true, 0, 0)); + // test was deleted by simplify_conj, as it was FALSE + + skip_finalization_check--; + } + } + + carried_level = possible_leading_0s+1; + + // check that there can't be another + if (guaranteed_leading_0s == possible_leading_0s + && possible_leading_0s >= 0 && + carried_level <= min(relation()->n_inp(), relation()->n_out())) { + Variable_ID in = input_var(carried_level), + out = output_var(carried_level); + coef_t lb, ub; + bool guar; + query_difference(out, in, lb, ub, guar); + if (lb <= 0 && ub >= 0) { + // probably "query_difference" is just approximate + // add a 0 and see if its satisfiable + + Conjunct *test = copy_conj_same_relation(); + test->problem->turnRedBlack(); + skip_finalization_check++; + + EQ_Handle e = test->add_EQ(); + e.update_coef_during_simplify(in, -1); + e.update_coef_during_simplify(out, 1); + e.finalize(); + assert(!simplify_conj(test, true, 0, 0)); + // test was deleted by simplify_conj, as it was FALSE + + skip_finalization_check--; + } + } + + // check leading direction info + if (leading_dir_valid_and_known()) { + Variable_ID in = input_var(guaranteed_leading_0s+1), + out = output_var(guaranteed_leading_0s+1); + coef_t lb, ub; + bool guar; + query_difference(out, in, lb, ub, guar); + if ((leading_dir < 0 && ub >= 0) || + (leading_dir > 0 && lb <= 0)) { + // probably "query_difference" is just approximate + // add the negation of leading_dir and assert that + // the result is unsatisfiable; + // eg for leading_dir = +1 (in must be < out), + // add in >= out (in-out>=0) and assert unsatisfiable. + + Conjunct *test = copy_conj_same_relation(); + test->problem->turnRedBlack(); + skip_finalization_check++; + + GEQ_Handle g = test->add_GEQ(); + g.update_coef_during_simplify(in, leading_dir); + g.update_coef_during_simplify(out, -leading_dir); + g.finalize(); + + assert(!simplify_conj(test, true, 0, 0)); + // test was deleted by simplify_conj, as it was FALSE + + skip_finalization_check--; + } + } +} +#endif + + +Variable_ID Conjunct::declare(Const_String s) { + return do_declare(s, Wildcard_Var); +} + +Variable_ID Conjunct::declare() { + return do_declare(Const_String(), Wildcard_Var); +} + +Variable_ID Conjunct::declare(Variable_ID v) { + return do_declare(v->base_name, Wildcard_Var); +} + +Conjunct* Conjunct::really_conjunct() { + return this; +} + + +Variable_ID_Tuple* Conjunct::variables() { + return &mappedVars; +} + +Stride_Handle Conjunct::add_stride(int step, int preserves_level) { + assert_not_finalized(); + Variable_ID wild = declare(); + int c; + c = problem->newEQ(); + simplified = false; + verified = false; + if (! preserves_level) { + if (leading_dir == 0) + possible_leading_0s = -1; + // otherwise we must still have leading_dir, and thus no more 0's + } + problem->EQs[c].color = EQ_BLACK; + eqnnzero(&problem->EQs[c],problem->nVars); + n_open_constraints++; + EQ_Handle h = EQ_Handle(this, c); + h.update_coef(wild,step); + return h; +} + +// This should only be used to copy constraints from simplified relations, +// i.e. there are no quantified variables in c except wildcards. +EQ_Handle Conjunct::add_EQ(const Constraint_Handle &c, int /*preserves_level, currently unused*/) { + EQ_Handle e = add_EQ(); + copy_constraint(e,c); + return e; +} + + +EQ_Handle Conjunct::add_EQ(int preserves_level) { + assert_not_finalized(); + int c; + c = problem->newEQ(); + simplified = false; + verified = false; + if (!preserves_level) { + if (leading_dir == 0) + possible_leading_0s = -1; + // otherwise we must still have leading_dir, and thus no more 0's + } + problem->EQs[c].color = EQ_BLACK; + eqnnzero(&problem->EQs[c],problem->nVars); + n_open_constraints++; + return EQ_Handle(this, c); +} + + +// This should only be used to copy constraints from simplified relations, +// i.e. there are no quantified variables in c except wildcards. +GEQ_Handle Conjunct::add_GEQ(const Constraint_Handle &c, int /*preserves_level, currently unused */) { + GEQ_Handle g = add_GEQ(); + copy_constraint(g,c); + return g; +} + + +GEQ_Handle Conjunct::add_GEQ(int preserves_level) { + assert_not_finalized(); + int c; + c = problem->newGEQ(); + simplified = false; + verified = false; + if (!preserves_level) { + if (leading_dir == 0) + possible_leading_0s = -1; + // otherwise we must still have leading_dir, and thus no more 0's + } + problem->GEQs[c].color = EQ_BLACK; + eqnnzero(&problem->GEQs[c],problem->nVars); + n_open_constraints++; + return GEQ_Handle(this, c); +} + + +Conjunct *Conjunct::find_available_conjunct() { + return this; +} + + +bool Conjunct::can_add_child() { + return false; +} + + +void Conjunct::combine_columns() { + int nvars = mappedVars.size(),i,j,k; + + for(i=1; i<=nvars; i++) + for(j=i+1; j<=nvars; j++) { + // If they are the same, copy into the higher numbered column. + // That way we won't have problems with already-merged columns later + assert(i != j); + if(mappedVars[i] == mappedVars[j]) { + if (pres_debug) + fprintf(DebugFile, "combine_col:Actually combined %d,%d\n", + j,i); + for(k=0; k<problem->nEQs; k++) + problem->EQs[k].coef[j] += problem->EQs[k].coef[i]; + for(k=0; k<problem->nGEQs; k++) + problem->GEQs[k].coef[j] += problem->GEQs[k].coef[i]; + zero_column(problem, i, 0, 0, problem->nEQs, problem->nGEQs); + // Create a wildcard w/no constraints. temporary measure, + // so we don't have to shuffle columns + Variable_ID zero_var = declare(); + mappedVars[i] = zero_var; + break; + } + } +} + + +void Conjunct::finalize() { +// Debugging version of finalize; copy the conjunct and free the old one, +// so that purify will catch accesses to finalized constraints +// assert(n_open_constraints == 0); +// Conjunct *C = this->copy(); +// parent().replace_child(this, C); +// delete this; +} + +Conjunct::~Conjunct() { + NR_CONJUNCTS--; + delete problem; + delete comp_problem; +} + + +// +// Cost = # of terms in DNF when negated +// or CantBeNegated if too bad (i.e. bad wildcards) +// or AvoidNegating if it would be inexact +// +// Also check pres_legal_negations -- +// If set to any_negation, just return the number +// If set to one_geq_or_stride, return CantBeNegated if c > 1 +// If set to one_geq_or_eq, return CantBeNegated if not a single geq or eq +// + +int Conjunct::cost() { + int c; + int i; + int wc_no; + int wc_j = 0; // initialize to shut up the compiler + + // cost 1 per GEQ, and if 1 GEQ has wildcards, +2 for each of them + + c = problem->nGEQs; + for(i=0; i<problem->nGEQs; i++) { + wc_no = 0; + for(int j=1; j<=problem->nVars; j++) if(problem->GEQs[i].coef[j]!=0) { + Variable_ID v = mappedVars[j]; + if(v->kind()==Wildcard_Var) { + wc_no++; + c+=2; + wc_j = j; + } + } + if (wc_no > 1) return CantBeNegated; + } + + for(i=0; i<problem->nEQs; i++) { + wc_no = 0; + for(int j=1; j<=problem->nVars; j++) if(problem->EQs[i].coef[j]!=0) { + Variable_ID v = mappedVars[j]; + if(v->kind()==Wildcard_Var) { + wc_no++; + wc_j = j; + } + } + + if (wc_no == 0) // no wildcards + c+=2; + else if (wc_no == 1) { // one wildcard - maybe we can negate it + int i2; + for(i2=0; i2<problem->nEQs; i2++) + if(i != i2 && problem->EQs[i2].coef[wc_j]!=0) break; + if (i2 >= problem->nEQs) // Stride constraint + c++; + else // We are not ready to handle this + return CantBeNegated; + } + else // Multiple wildcards + return CantBeNegated; + } + if (!exact) return AvoidNegating; + + if (pres_legal_negations == any_negation) { + return c; + } + else { + // single GEQ ok either way as long as no wildcards + // (we might be able to handle wildcards, but I haven't thought about it) + if (problem->nEQs==0 && problem->nGEQs<=1) { + if (c>1) { // the GEQ had a wildcard -- I'm not ready to go here. + if (pres_debug > 0) { + fprintf(DebugFile, + "Refusing to negate a GEQ with wildcard(s)" + " under restricted_negation; " + "It may be possible to fix this.\n"); + } + return CantBeNegated; + } + return c; + } + else if (problem->nEQs==1 && problem->nGEQs==0) { + assert(c == 1 || c == 2); + + if (pres_legal_negations == one_geq_or_stride) { + if (c == 1) + return c; // stride constraint is ok + else { + if (pres_debug > 0) { + fprintf(DebugFile, "Refusing to negate a non-stride EQ under current pres_legal_negations.\n"); + } + return CantBeNegated; + } + } + else { + assert(pres_legal_negations == one_geq_or_eq); + return c; + } + } + else { + if (pres_debug > 0) { + fprintf(DebugFile, "Refusing to negate multiple constraints under current pres_legal_negations.\n"); + } + return CantBeNegated; + } + } +} + + +// +// Merge CONJ1 & CONJ2 -> CONJ. +// Action: MERGE_REGULAR or MERGE_COMPOSE: regular merge. +// MERGE_GIST make constraints from conj2 red, i.e. +// Gist Conj2 given Conj1 (T.S. comment). +// Reorder columns as we go. +// Merge the columns for identical variables. +// We assume we know nothing about the ordering of conj1, conj2. +// +// Does not consume its arguments +// +// Optional 4th argument gives the relation for the result - if +// null, conj1 and conj2 must have the same relation, which will +// be used for the result +// +// The only members of conj1 and conj2 that are used are: problem, +// mappedVars and declare(), and the leading_0s/leading_dir members +// and exact. +// +// NOTE: variables that are shared between conjuncts are necessarily +// declared above, not here; so we can simply create columns for the +// locals of each conj after doing the protected vars. +// +Conjunct* merge_conjs(Conjunct* conj1, Conjunct* conj2, + Merge_Action action, Rel_Body *body) { + // body must be set unless both conjuncts are from the same relation + assert(body || conj1->relation() == conj2->relation()); + + if (body == conj1->relation() && body == conj2->relation()) + body = 0; // we test this later to see if there is a new body + + Conjunct *conj3 = new Conjunct(NULL, body ? body : conj2->relation()); + Problem *p1 = conj1->problem; + Problem *p2 = conj2->problem; + Problem *p3 = conj3->problem; + int i; + + if (action != MERGE_COMPOSE) { + conj1->assert_leading_info(); + conj2->assert_leading_info(); + } + + if(pres_debug>=2) { + use_ugly_names++; + fprintf(DebugFile, ">>> Merge conjuncts: Merging%s:\n", + (action == MERGE_GIST ? " for gist" : + (action == MERGE_COMPOSE ? " for composition" : ""))); + conj1->prefix_print(DebugFile); + conj2->prefix_print(DebugFile); + fprintf(DebugFile, "\n"); + use_ugly_names--; + } + + + + switch(action) { + case MERGE_REGULAR: + case MERGE_COMPOSE: + conj3->exact=conj1->exact && conj2->exact; + break; + case MERGE_GIST: + conj3->exact=conj2->exact; + break; + } + + if (action == MERGE_COMPOSE) { + conj3->guaranteed_leading_0s=min(conj1->guaranteed_leading_0s, + conj2->guaranteed_leading_0s); + conj3->possible_leading_0s=min((unsigned int) conj1->possible_leading_0s, + (unsigned int) conj2->possible_leading_0s); + + assert( conj3->guaranteed_leading_0s <= conj3->possible_leading_0s); + + // investigate leading_dir - not well tested code + if (conj1->guaranteed_leading_0s<0 || conj2->guaranteed_leading_0s<0) { + conj3->leading_dir = 0; + } + else if (conj1->guaranteed_leading_0s == conj2->guaranteed_leading_0s) + if (conj1->leading_dir == conj2->leading_dir) + conj3->leading_dir = conj1->leading_dir; + else + conj3->leading_dir = 0; + else if (conj1->guaranteed_leading_0s < conj2->guaranteed_leading_0s) { + conj3->leading_dir = conj1->leading_dir; + } + else { // (conj1->guaranteed_leading_0s > conj2->guaranteed_leading_0s) + conj3->leading_dir = conj2->leading_dir; + } + + if (conj3->leading_dir == 0) + conj3->possible_leading_0s = min(conj3->relation()->n_inp(), + conj3->relation()->n_out()); + + assert(conj3->guaranteed_leading_0s <= conj3->possible_leading_0s); + assert(conj3->guaranteed_leading_0s == conj3->possible_leading_0s + || !conj3->leading_dir); + } + else if (!body) { // if body is set, who knows what leading 0's mean? + assert(action == MERGE_REGULAR || action == MERGE_GIST); + + int feasable = 1; + + int redAndBlackGuarLeadingZeros = max(conj1->guaranteed_leading_0s, + conj2->guaranteed_leading_0s); + if (action == MERGE_REGULAR) + conj3->guaranteed_leading_0s= redAndBlackGuarLeadingZeros; + else conj3->guaranteed_leading_0s=conj1->guaranteed_leading_0s; + + conj3->possible_leading_0s=min((unsigned)conj1->possible_leading_0s, + (unsigned)conj2->possible_leading_0s); + if (conj3->possible_leading_0s < redAndBlackGuarLeadingZeros) + feasable = 0; + else if (conj3->guaranteed_leading_0s == -1 + || conj3->possible_leading_0s > redAndBlackGuarLeadingZeros) + conj3->leading_dir = 0; + else { + if (conj1->guaranteed_leading_0s == conj2->guaranteed_leading_0s) + if (!conj1->leading_dir_valid_and_known()) + conj3->leading_dir = conj2->leading_dir; + else if (!conj2->leading_dir_valid_and_known()) + conj3->leading_dir = conj1->leading_dir; + else if (conj1->leading_dir * conj2->leading_dir > 0) + conj3->leading_dir = conj1->leading_dir; // 1,2 same dir + else + feasable = 0; // 1 and 2 go in opposite directions + else if (conj3->possible_leading_0s != conj3->guaranteed_leading_0s) + conj3->leading_dir = 0; + else if (conj1->guaranteed_leading_0s<conj2->guaranteed_leading_0s) { + assert(!conj1->leading_dir_valid_and_known()); + conj3->leading_dir = conj2->leading_dir; + } + else { + assert(!conj2->leading_dir_valid_and_known()); + conj3->leading_dir = conj1->leading_dir; + } + } + + if (!feasable) { + if(pres_debug>=2) + fprintf(DebugFile, ">>> Merge conjuncts: quick check proves FALSE.\n"); + + // return 0 = 1 + + int e = p3->newEQ(); + p3->EQs[e].color = EQ_BLACK; + p3->EQs[e].touched = 1; + p3->EQs[e].key = 0; + p3->EQs[e].coef[0] = 1; + + // Make sure these don't blow later assertions + conj3->possible_leading_0s = conj3->guaranteed_leading_0s = -1; + conj3->leading_dir = 0; + + return conj3; + } + } + else { // provided "body" argument but not composing, leading 0s meaningless + conj3->guaranteed_leading_0s = conj3->possible_leading_0s = -1; + conj3->leading_dir = 0; + } + + // initialize omega stuff + + for(i=0; i<p1->nGEQs+p2->nGEQs; i++) { + int e = p3->newGEQ(); + assert(e == i); + p3->GEQs[e].color = EQ_BLACK; + p3->GEQs[e].touched = 1; + p3->GEQs[e].key = 0; + } + for(i=0; i<p1->nEQs+p2->nEQs; i++) { + int e = p3->newEQ(); + assert(e == i); + p3->EQs[e].color = EQ_BLACK; + p3->EQs[e].touched = 1; + p3->EQs[e].key = 0; + } + + assert(p3->nGEQs == p1->nGEQs + p2->nGEQs); + assert(p3->nEQs == p1->nEQs + p2->nEQs); + + // flag constraints from second constraint as red, if necessary + if (action == MERGE_GIST) { + for(i=0; i<p2->nEQs; i++) { + p3->EQs[i+p1->nEQs].color = EQ_RED; + } + for(i=0; i<p2->nGEQs; i++) { + p3->GEQs[i+p1->nGEQs].color = EQ_RED; + } + } + + // copy constant column + copy_column(p3, 0, p1, 0, 0, 0); + copy_column(p3, 0, p2, 0, p1->nEQs, p1->nGEQs); + + // copy protected variables column from conj1 + int new_col = 1; + Variable_Iterator VI(conj1->mappedVars); + for(i=1; VI; VI++, i++) { + Variable_ID v = *VI; + if(v->kind() != Wildcard_Var) { + conj3->mappedVars.append(v); + int fr_ix = i; + copy_column(p3, new_col, p1, fr_ix, 0, 0); + zero_column(p3, new_col, p1->nEQs, p1->nGEQs, + p2->nEQs, p2->nGEQs); + new_col++; + } + } + + // copy protected variables column from conj2, + // checking if conj3 already has this variable from conj1 + for(i=1; i <= conj2->mappedVars.size(); i++) { + Variable_ID v = conj2->mappedVars[i]; + if(v->kind() != Wildcard_Var) { + int to_ix = conj3->mappedVars.index(v); + int fr_ix = i; + if(to_ix > 0) { + // use old column + copy_column(p3, to_ix, p2, fr_ix, p1->nEQs, p1->nGEQs); + } + else { + // create new column + conj3->mappedVars.append(v); + zero_column(p3, new_col, 0, 0, p1->nEQs, p1->nGEQs); + copy_column(p3, new_col, p2, fr_ix, p1->nEQs, p1->nGEQs); + new_col++; + } + } + } + + p3->safeVars = new_col-1; + + // copy wildcards from conj1 + for(i=1; i <= conj1->mappedVars.size(); i++) { + Variable_ID v = conj1->mappedVars[i]; + if(v->kind() == Wildcard_Var) { + Variable_ID nv = conj3->declare(v); + conj3->mappedVars.append(nv); + int fr_ix = i; + copy_column(p3, new_col, p1, fr_ix, 0, 0); + zero_column(p3, new_col, p1->nEQs, p1->nGEQs, + p2->nEQs, p2->nGEQs); + new_col++; + } + } + + // copy wildcards from conj2 + for(i=1; i <= conj2->mappedVars.size(); i++) { + Variable_ID v = conj2->mappedVars[i]; + if(v->kind() == Wildcard_Var) { + Variable_ID nv = conj3->declare(v); + conj3->mappedVars.append(nv); + int fr_ix = i; + zero_column(p3, new_col, 0, 0, p1->nEQs, p1->nGEQs); + copy_column(p3, new_col, p2, fr_ix, p1->nEQs, p1->nGEQs); + new_col++; + } + } + + p3->nVars = new_col-1; + checkVars(p3->nVars); + p3->variablesInitialized = 1; + for(i=1; i<=p3->nVars; i++) + p3->var[i] = p3->forwardingAddress[i] = i; + + conj3->cols_ordered = true; + conj3->simplified = false; + conj3->verified = false; + + if(pres_debug>=2) { + use_ugly_names++; + fprintf(DebugFile, ">>> Merge conjuncts: result is:\n"); + conj3->prefix_print(DebugFile); + fprintf(DebugFile, "\n"); + use_ugly_names--; + } + + conj3->assert_leading_info(); + + return conj3; +} + + + + +// +// Reorder variables by swapping. +// cols_ordered is just a hint that thorough check needs to be done. +// Sets _safeVars. +// +void Conjunct::reorder() { + if(!cols_ordered) { + int var_no = mappedVars.size(); + int first_wild = 1; + int last_prot = var_no; + while(first_wild < last_prot) { + for(; first_wild<=var_no && mappedVars[first_wild]->kind()!=Wildcard_Var; + first_wild++) ; + for(; last_prot>=1 && mappedVars[last_prot]->kind()==Wildcard_Var; + last_prot--) ; + if(first_wild < last_prot) { + problem->swapVars(first_wild, last_prot); + problem->variablesInitialized = false; + Var_Decl *t = mappedVars[first_wild]; + mappedVars[first_wild] = mappedVars[last_prot]; + mappedVars[last_prot] = t; + if(pres_debug) { + fprintf(DebugFile, "<<<OrderConjCols>>>: swapped var-s %d and %d\n", first_wild, last_prot); + } + } + } + + int safe_vars; + for(safe_vars=0; + safe_vars<var_no && mappedVars[safe_vars+1]->kind()!=Wildcard_Var; + safe_vars++) ; + +#if ! defined NDEBUG + for(int s = safe_vars ; s<var_no ; s++ ) { + assert(mappedVars[s+1]->kind() == Wildcard_Var); + } +#endif + + problem->safeVars = safe_vars; + cols_ordered = true; + } +} + + + +// Wherever possible, move function symbols to input tuple. +// This ensures that if in == out, red F(in) = x is redundant +// with black F(out) = x + +void Conjunct::move_UFS_to_input() { + if (guaranteed_leading_0s > 0) { + std::set<Global_Var_ID> already_done; + int remapped = 0; + skip_finalization_check++; + Rel_Body *body = relation(); + + assert(body); + + for (Variable_ID_Iterator func(*body->global_decls()); func; func++) { + Global_Var_ID f = (*func)->get_global_var(); + if (f->arity() <= guaranteed_leading_0s) + if (already_done.find(f) == already_done.end() && + body->has_local(f, Input_Tuple) && + body->has_local(f, Output_Tuple)) { + already_done.insert(f); + + // equatE f(in) = f(out) + Variable_ID f_in = body->get_local(f, Input_Tuple); + Variable_ID f_out = body->get_local(f, Output_Tuple); + if (f_in != f_out) { + EQ_Handle e = add_EQ(1); + + e.update_coef_during_simplify(f_in, -1); + e.update_coef_during_simplify(f_out, 1); + + f_out->remap = f_in; + remapped = 1; + } + } + } + + if (remapped) { + remap(); + combine_columns(); + reset_remap_field(*body->global_decls()); + remapped = 0; + } + + skip_finalization_check--; + } +} + + + + + +// +// Simplify CONJ. +// Return TRUE if there are solutions, FALSE -- no solutions. +// +int simplify_conj(Conjunct* conj, int ver_sim, int simplificationEffort, int color) { + if (conj->verified + && simplificationEffort <= conj->r_constrs + && (conj->simplified || simplificationEffort < 0) + && !color) { + if(pres_debug) { + fprintf(DebugFile, "$$$ Redundant simplify_conj ignored (%d,%d,%d)\n",ver_sim,simplificationEffort,color); + conj->prefix_print(DebugFile); + } + return 1; + } + + if (simplificationEffort < 0) simplificationEffort = 0; + conj->move_UFS_to_input(); + conj->reorder(); + + Problem *p = conj->problem; + + use_ugly_names++; + + int i; + for(i=0; i<p->nGEQs; i++) { + p->GEQs[i].touched = 1; + } + for(i=0; i<p->nEQs; i++) { + p->EQs[i].touched = 1; + } + + if(pres_debug) { + fprintf(DebugFile, "$$$ simplify_conj (%d,%d,%d)[\n",ver_sim,simplificationEffort,color); + conj->prefix_print(DebugFile); + } + + assert(conj->cols_ordered); + + int ret_code; + assert(p == conj->problem); + if(!color) { + ret_code = conj->simplifyProblem(ver_sim && ! conj->verified,0,simplificationEffort); + } + else { + ret_code = conj->redSimplifyProblem(simplificationEffort,1); + ret_code = (ret_code==redFalse ? 0 : 1); + } + assert(p->nSUBs==0); + + if(ret_code == 0) { + if(pres_debug) + fprintf(DebugFile, "] $$$ simplify_conj : false\n\n"); + delete conj; + use_ugly_names--; + return(false); + } + + + // + // mappedVars is mapping from columns to Variable_IDs. + // Recompute mappedVars for problem returned from ip.c + // + Variable_ID_Tuple new_mapped(0); // This is expanded by "append" + for (i=1; i<=p->safeVars; i++) { + // what is now in column i used to be in column p->var[i] + Variable_ID v = conj->mappedVars[p->var[i]]; + assert(v->kind() != Wildcard_Var); + new_mapped.append(v); + } + + /* Redeclare all wildcards that weren't eliminated. */ + free_var_decls(conj->myLocals); conj->myLocals.clear(); + + conj->mappedVars = new_mapped; + for (i = p->safeVars+1; i<=p->nVars; i++) { + Variable_ID v = conj->declare(); + conj->mappedVars.append(v); + } + + // reset var and forwarding address if desired. + p->variablesInitialized = 1; + for(i=1; i<=conj->problem->nVars; i++) + conj->problem->var[i] = conj->problem->forwardingAddress[i] = i; + + if(pres_debug) { + fprintf(DebugFile, "] $$$ simplify_conj\n"); + conj->prefix_print(DebugFile); + fprintf(DebugFile, "\n"); + } + + + use_ugly_names--; + conj->simplified = true; + conj->setup_anonymous_wildcard_names(); + + return(true); +} + + +int Conjunct::rank() { + Conjunct *C = this->copy_conj_same_relation(); + C->reorder(); + C->ordered_elimination(C->relation()->global_decls()->size()); + int C_rank = 0; + for(Variable_Iterator vi = C->mappedVars; vi; vi++) + if(C->find_column(*vi) > 0) C_rank++; + delete C; + return C_rank; + +} + + +void Conjunct::query_difference(Variable_ID v1, Variable_ID v2, coef_t &lowerBound, coef_t &upperBound, bool &guaranteed) { + int c1 = get_column(v1); + int c2 = get_column(v2); + assert(c1 && c2); + problem->query_difference(c1, c2, lowerBound, upperBound, guaranteed); +} + + +void Conjunct::query_variable_bounds(Variable_ID v, coef_t &lowerBound, coef_t &upperBound) { + int c = get_column(v); + assert (c); + problem->query_variable_bounds(c, &lowerBound, &upperBound); +} + +coef_t Conjunct::query_variable_mod(Variable_ID v, coef_t factor) { + int c = get_column(v); + assert(c); + return problem->query_variable_mod(c, factor); +} + +bool Conjunct::query_variable_used(Variable_ID v) { + for (GEQ_Iterator g = GEQs(); g.live(); g.next()) { + if ((*g).get_coef(v)) return true; + } + for (EQ_Iterator e = EQs(); e.live(); e.next()) { + if ((*e).get_coef(v)) return true; + } + return false; +} + + +int Conjunct::simplifyProblem(int verify, int subs, int redundantElimination) { + if (verified) verify = 0; + int result = problem->simplifyProblem(verify, subs, redundantElimination); + if (result == false && !exact) + exact=true; + assert(!(verified && verify && result == false)); + if (verify && result) verified = true; + else if (!result) verified = false; + return result; +} + + +// not as confident about this one as the previous: +int Conjunct::redSimplifyProblem(int effort, int computeGist) { + redCheck result = problem->redSimplifyProblem(effort, computeGist); + if (result == redFalse && !exact) + exact=true; + return result; +} + + +// +// Add given list of wildcards S to this Conjunct. +// Clears argument. (That's very important, otherwise those var_id's get freed) +// Push_exists takes responsibility for reusing or deleting Var_ID's; +// here we reuse them. Must also empty out the Tuple when finished (joins). +void Conjunct::push_exists(Variable_ID_Tuple &S) { + for(Tuple_Iterator<Variable_ID> VI(S); VI; VI++) { + (*VI)->var_kind = Wildcard_Var; + } + myLocals.join(S); // Sets S to be empty. + cols_ordered = false; + simplified = false; +} + + +Conjunct *Formula::add_conjunct() { + assert_not_finalized(); + assert(can_add_child()); + Conjunct *f = new Conjunct(this, myRelation); + myChildren.append(f); + return f; +} + +// Compress/uncompress functions + +bool Conjunct::is_compressed() { + if(problem!=NULL && comp_problem==NULL) { + return false; + } + else if(problem==NULL && comp_problem!=NULL) { + return true; + } + else { + assert(0 && "Conjunct::is_compressed: bad conjunct"); + return false; + } +} + + +void Conjunct::compress() { + if(!is_compressed()) { // compress + comp_problem = new Comp_Problem(problem); + delete problem; + problem = NULL; + } +} + + +void Conjunct::uncompress() { + if(is_compressed()) { + problem = comp_problem->UncompressProblem(); + delete comp_problem; + comp_problem = NULL; + } +} + + +Comp_Problem::Comp_Problem(Problem *problem) : + _nVars(problem->nVars), + _safeVars(problem->safeVars), + _get_var_name(problem->get_var_name), + _getVarNameArgs(problem->getVarNameArgs), + eqs(&problem->EQs[0],problem->nEQs,problem->nVars), + geqs(&problem->GEQs[0],problem->nGEQs,problem->nVars) { +} + +Comp_Constraints::Comp_Constraints(eqn *constrs, int no_constrs, int no_vars) : + n_constrs(no_constrs), + n_vars(no_vars) { + coefs = new coef_t[(n_vars+1)*n_constrs]; + int e, v; + for(e=0; e<n_constrs; e++) { + for(v=0; v<=n_vars; v++) { + coefs[coef_index(e,v)] = constrs[e].coef[v]; + } + } +} + + +Comp_Constraints::~Comp_Constraints() { + delete coefs; +} + + +Problem *Comp_Problem::UncompressProblem() { + Problem *p = new Problem(eqs.n_constraints(), geqs.n_constraints()); + p->get_var_name = get_var_name; + p->getVarNameArgs = _getVarNameArgs; + p->nVars = _nVars; + p->safeVars = _safeVars; + for(int i=1; i<=p->nVars; i++) { + p->forwardingAddress[i] = i; + p->var[i] = i; + } + eqs.UncompressConstr(&p->EQs[0], p->nEQs); + geqs.UncompressConstr(&p->GEQs[0], p->nGEQs); + return p; +} + +void Comp_Constraints::UncompressConstr(eqn *constrs, short &pn_constrs) { + int e, v; + for(e=0; e<n_constrs; e++) { + eqnnzero(&constrs[e], 0); + for(v=0; v<=n_vars; v++) { + constrs[e].coef[v] = coefs[coef_index(e,v)]; + } + constrs[e].touched = 1; + } + pn_constrs = n_constrs; +} + + +void Conjunct::convertEQstoGEQs(bool excludeStrides) { + simplify_conj(this,true,1,EQ_BLACK); // don't remember why I want to comment this statement out with reason "will cause inconsistency between Conjunct::mappedVars and Problem::nVars", 06/09/2009 by chun + problem->convertEQstoGEQs(excludeStrides); +} + + +void Conjunct::calculate_dimensions(Relation &R, int &ndim_all, int &ndim_domain) { + + Conjunct * c = this; + Relation rc=Relation(R, c); + + if(relation_debug) { + fprintf(DebugFile,"{{{\nIn Conjunct::calculate_dimensions:\n"); + rc.prefix_print(DebugFile); + } + + rc=Approximate(rc); + Relation rd=rc; + + if(relation_debug) { + fprintf(DebugFile,"Conjunct::calculate_dimensions: Approximated as:\n"); + rc.prefix_print(DebugFile); + } + + // skip_set_checks++; + + Conjunct * rc_conj=rc.single_conjunct(); + ndim_all=rc.n_inp()+rc.n_out(); + ndim_all-=rc_conj->n_EQs(); + + rc = Project_On_Sym(rc); + rc.simplify(); + + if(relation_debug) { + fprintf(DebugFile, "Conjunct::calculate_dimensions: after project_on_sym\n"); + rc.prefix_print(DebugFile); + } + + int n_eq_sym = 1000; + for (DNF_Iterator s(rc.query_DNF()); s.live(); s.next()) + n_eq_sym = min(n_eq_sym, s.curr()->n_EQs()); + ndim_all+=n_eq_sym; + // skip_set_checks--; + + if (R.is_set()) + ndim_domain = ndim_all; + else { + /* get dimensions for the domain (broadcasting) */ + + rd=Domain(rd); + rd.simplify(); + + if(relation_debug) { + fprintf(DebugFile,"Domain is:\n"); + rd.prefix_print(DebugFile); + } + + rc_conj=rd.single_conjunct(); + ndim_domain=rd.n_set()-rc_conj->n_EQs()+n_eq_sym; + } + + if(relation_debug) { + fprintf(DebugFile,"n_eq_sym=%d \n",n_eq_sym); + fprintf(DebugFile,"Dimensions: all=%d domain=%d\n}}}\n", ndim_all,ndim_domain); + } +} + +} // namespace diff --git a/omegalib/omega/src/pres_decl.cc b/omegalib/omega/src/pres_decl.cc new file mode 100644 index 0000000..f5ac312 --- /dev/null +++ b/omegalib/omega/src/pres_decl.cc @@ -0,0 +1,71 @@ +#include <omega/pres_decl.h> +#include <omega/omega_i.h> + +namespace omega { + +// +// Declare functions. +// +Variable_ID F_Declaration::do_declare(Const_String s, Var_Kind var_type) { + Variable_ID v; + assert(var_type != Global_Var); + if(!s.null()) { + v = new Var_Decl(s, var_type, 0); + } + else { + v = new Var_Decl(var_type, 0); + } + myLocals.append(v); + return v; +} + +Variable_ID F_Declaration::declare(Const_String) { + assert(0); // must be declared in forall, exists, or conjunct + return(NULL); +} + +Section<Variable_ID> F_Declaration::declare_tuple(int n) { + int first = myLocals.size()+1; + + for (int i=1 ; i<=n; i++) + declare(); + + return Section<Variable_ID>(&myLocals, first, n); +} + + +void F_Declaration::finalize() { + assert(n_children() == 1); + Formula::finalize(); +} + +bool F_Declaration::can_add_child() { + return n_children() < 1; +} + + +F_Declaration::F_Declaration(Formula *p, Rel_Body *r): + Formula(p,r), myLocals(0) { +} + +F_Declaration::F_Declaration(Formula *p, Rel_Body *r, Variable_ID_Tuple &S): + Formula(p,r), myLocals(S) { +} + +// +// Destruct declarative node. +// Delete variableID's themselves if they are not global. +// +F_Declaration::~F_Declaration() { + free_var_decls(myLocals); +} + +//Setup names for printing +void F_Declaration::setup_anonymous_wildcard_names() { + for(Tuple_Iterator<Variable_ID> VI(myLocals); VI; VI++) { + Variable_ID v = *VI; + if (v->base_name.null()) v->instance = wildCardInstanceNumber++; + } +} + +} // namespace diff --git a/omegalib/omega/src/pres_dnf.cc b/omegalib/omega/src/pres_dnf.cc new file mode 100644 index 0000000..c9fd7e6 --- /dev/null +++ b/omegalib/omega/src/pres_dnf.cc @@ -0,0 +1,1416 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + All Rights Reserved. + + Purpose: + Functions for disjunctive normal form. + + Notes: + + History: +*****************************************************************************/ + +#include <basic/Bag.h> +#include <omega/pres_dnf.h> +#include <omega/pres_conj.h> +#include <omega/pres_tree.h> /* all DNFize functions are here */ +#include <omega/Relation.h> +#include <omega/omega_i.h> + +namespace omega { + +void DNF::remap() { + for(DNF_Iterator DI(this); DI.live(); DI.next()) { + Conjunct *C = DI.curr(); + C->remap(); + } +} + + +// +// DNF1 & DNF2 -> DNF. +// Free arguments. +// +DNF* DNF_and_DNF(DNF* dnf1, DNF* dnf2) { + DNF* new_dnf = new DNF; + for(DNF_Iterator p(dnf2); p.live(); p.next()) { + new_dnf->join_DNF(DNF_and_conj(dnf1, p.curr())); + } + delete dnf1; + delete dnf2; + if(new_dnf->length() > 1) { + new_dnf->simplify(); + } + + if(pres_debug) { + fprintf(DebugFile, "+++ DNF_and_DNF OUT +++\n"); + new_dnf->prefix_print(DebugFile); + } + return(new_dnf); +} + + +/* + * Remove redundant conjuncts from given DNF. + * If (C1 => C2), remove C1. + * C1 => C2 is TRUE: when problem where C1 is Black and C2 is Red + * Blk Red : has no red constraints. + * It means that C1 is a subset of C2 and therefore C1 is redundant. + * + * Exception: C1 => UNKNOWN - leave them as they are + */ +void DNF::rm_redundant_conjs(int effort) { + if(is_definitely_false() || has_single_conjunct()) + return; + + use_ugly_names++; + // skip_set_checks++; + + int count = 0; + for(DNF_Iterator p(this); p.live(); p.next()) count++; + + if(pres_debug) { + int i = 0; + fprintf(DebugFile, "@@@ rm_redundant_conjs IN @@@[\n"); + prefix_print(DebugFile); + for(DNF_Iterator p(this); p.live(); p.next()) + fprintf(DebugFile, "#%d = %p\n", ++i, p.curr()); + } + + DNF_Iterator pdnext; + DNF_Iterator pdel(this); + for(; pdel.live(); pdel=pdnext) { + pdnext = pdel; + pdnext.next(); + Conjunct *cdel = pdel.curr(); + int del_min_leading_zeros = cdel->query_guaranteed_leading_0s(); + int del_max_leading_zeros = cdel->query_possible_leading_0s(); + + for(DNF_Iterator p(this); p.live(); p.next()) { + Conjunct *c = p.curr(); + if(c != cdel) { + int c_min_leading_zeros = cdel->query_guaranteed_leading_0s(); + int c_max_leading_zeros = cdel->query_possible_leading_0s(); + if(pres_debug) + fprintf(DebugFile, "@@@ rm_redundant_conjs @%p => @%p[\n", cdel, c); + + if (c->is_inexact() && cdel->is_exact()) { + if (pres_debug) + fprintf(DebugFile, "]@@@ rm_redundant_conjs @@@ Exact Conj => Inexact Conj is not tested\n"); + } + else if (del_min_leading_zeros >=0 && c_min_leading_zeros >= 0 + && c_max_leading_zeros >= 0 && del_max_leading_zeros >=0 + && (del_min_leading_zeros > c_max_leading_zeros + || c_min_leading_zeros > del_max_leading_zeros)) { + if (1 || pres_debug) + fprintf(DebugFile, "]@@@ not redundant due to leading zero info\n"); + } + else { + Conjunct *cgist = merge_conjs(cdel, c, MERGE_GIST); + + if (!cgist->redSimplifyProblem(effort,0)) { + if(pres_debug) { + fprintf(DebugFile, "]@@@ rm_redundant_conjs @@@ IMPLICATION TRUE @%p\n", cdel); + cdel->prefix_print (DebugFile); + fprintf(DebugFile, "=>\n"); + c->prefix_print (DebugFile); + } + rm_conjunct(cdel); + delete cdel; + delete cgist; + break; + } + else { + if(pres_debug) { + fprintf(DebugFile, "]@@@ rm_redundant_conjs @@@ IMPLICATION FALSE @%p\n", cdel); + if(pres_debug > 1) + cgist->prefix_print(DebugFile); + } + delete cgist; + } + } + } + } + } + + if(pres_debug) { + fprintf(DebugFile, "]@@@ rm_redundant_conjs OUT @@@\n"); + prefix_print(DebugFile); + } + // skip_set_checks--; + use_ugly_names--; +} + + +/* Remove inexact conjuncts from given DNF if it contains UNKNOWN + * conjunct + */ + +void DNF::rm_redundant_inexact_conjs() { + if (is_definitely_false() || has_single_conjunct()) + return; + + bool has_unknown=false; + bool has_inexact=false; + + Conjunct * c_unknown = 0; // make compiler shut up + for (DNF_Iterator p(this); p.live(); p.next()) { + assert (p.curr()->problem!=NULL); + if (p.curr()->is_inexact()) { + if (p.curr()->is_unknown()) { + has_unknown=true; + c_unknown = p.curr(); + } + else + has_inexact=true; + } + } + + if (! has_unknown || ! has_inexact) + return; + + use_ugly_names++; + // skip_set_checks++; + + DNF_Iterator pdnext; + DNF_Iterator pdel(this); + + for (; pdel.live(); pdel=pdnext) { + pdnext = pdel; + pdnext.next(); + Conjunct * cdel=pdel.curr(); + if (cdel->is_inexact() && cdel!=c_unknown) { + rm_conjunct(cdel); + delete cdel; + } + } + + use_ugly_names--; + // skip_set_checks--; +} + + + +// +// DNF properties. +// +bool DNF::is_definitely_false() const { + return(conjList.empty()); +} + +bool DNF::is_definitely_true() const { + return(has_single_conjunct() && single_conjunct()->is_true()); +} + +int DNF::length() const { + return conjList.length(); +} + +Conjunct *DNF::single_conjunct() const { + assert(conjList.length()==1); + return(conjList.front()); +} + +bool DNF::has_single_conjunct() const { + return (conjList.length()==1); +} + +Conjunct *DNF::rm_first_conjunct() { + if(conjList.empty()) { + return NULL; + } + else { + return conjList.remove_front(); + } +} + + +// +// Convert DNF to Formula and add it root. +// Free this DNF. +// +void DNF::DNF_to_formula(Formula* root) { + Formula *new_or; + if (conjList.length()!=1) { + skip_finalization_check++; + new_or = root->add_or(); + skip_finalization_check--; + } + else { + new_or = root; + } + while(!conjList.empty()) { + Conjunct *conj = conjList.remove_front(); + new_or->add_child(conj); + } + delete this; +} + + +// +// DNF functions. +// +DNF::DNF() : conjList() { +} + +DNF::~DNF() { + // for(DNF_Iterator p(this); p.live(); p.next()) { + // if(p.curr() != NULL) + // delete p.curr(); + // } + for(List_Iterator<Conjunct *> i(conjList); i.live(); i.next()) + delete *i; +} + +// +// Copy DNF +// +DNF* DNF::copy(Rel_Body *rel_body) { + DNF *new_dnf = new DNF; + for(DNF_Iterator pd(this); pd.live(); pd.next()) { + Conjunct *conj = pd.curr(); + if(conj) + new_dnf->add_conjunct(conj->copy_conj_diff_relation(rel_body,rel_body)); + } + return(new_dnf); +} + +// +// Add Conjunct to DNF +// +void DNF::add_conjunct(Conjunct* conj) { + conjList.append(conj); +} + +// +// Add DNF to DNF. +// The second DNF is reused. +// +void DNF::join_DNF(DNF* dnf) { + conjList.join(dnf->conjList); + delete dnf; +} + +// +// Remove conjunct from DNF. +// Conjunct itself is not deleted. +// +void DNF::rm_conjunct(Conjunct *c) { + if(conjList.front() == c) { + conjList.remove_front(); + } + else { + List_Iterator<Conjunct*> p, pp; + for(p=List_Iterator<Conjunct*> (conjList); p; p++) { + if((*p)==c) { + conjList.del_after(pp); + return; + } + pp = p; + } + assert(0 && "DNF::rm_conjunct: no such conjunct"); + } +} + + +// remove (but don't delete) all conjuncts + +void DNF::clear() { + conjList.clear(); +} + + +// +// DNF & CONJ -> new DNF. +// Don't touch arguments. +// +DNF* DNF_and_conj(DNF* dnf, Conjunct* conj) { + DNF* new_dnf = new DNF; + for(DNF_Iterator p(dnf); p.live(); p.next()) { + Conjunct* new_conj = merge_conjs(p.curr(), conj, MERGE_REGULAR); + new_dnf->add_conjunct(new_conj); + } + if(new_dnf->length() > 1) { + new_dnf->simplify(); + } + return(new_dnf); +} + +// +// Compute C0 and not (C1 or C2 or ... CN). +// Reuse/delete its arguments. +// +DNF* conj_and_not_dnf(Conjunct *positive_conjunct, DNF *neg_conjs, bool weak) { + DNF *ret_dnf = new DNF; + int recursive = 0; + use_ugly_names++; + + if(pres_debug) { + fprintf(DebugFile, "conj_and_not_dnf [\n"); + fprintf(DebugFile, "positive_conjunct:\n"); + positive_conjunct->prefix_print(DebugFile); + fprintf(DebugFile, "neg_conjs:\n"); + neg_conjs->prefix_print(DebugFile); + fprintf(DebugFile, "\n\n"); + } + + if (simplify_conj(positive_conjunct, true, false, EQ_BLACK) == false) { + positive_conjunct = NULL; + goto ReturnDNF; + } + + /* Compute gists of negative conjuncts given positive conjunct */ + + + int c0_updated; + c0_updated = true; + while(c0_updated) { + c0_updated = false; + for(DNF_Iterator p(neg_conjs); p.live(); p.next()) { + Conjunct *neg_conj = p.curr(); + if(neg_conj==NULL) continue; + if (!positive_conjunct->is_exact() + && !neg_conj->is_exact()) { + // C1 and unknown & ~(C2 and unknown) = C1 and unknown + delete neg_conj; + p.curr_set(NULL); + continue; + } + Conjunct *cgist = merge_conjs(positive_conjunct, neg_conj, MERGE_GIST); + if(simplify_conj(cgist, false, true, EQ_RED) == false) { + // C1 & ~FALSE = C1 + delete neg_conj; + p.curr_set(NULL); + } + else { + cgist->rm_color_constrs(); + if(cgist->is_true()) { + // C1 & ~TRUE = FALSE + delete cgist; + goto ReturnDNF; + } + else { + if(cgist->cost()==1) { // single inequality + DNF *neg_dnf = negate_conj(cgist); + delete cgist; + Conjunct *conj = + merge_conjs(positive_conjunct, neg_dnf->single_conjunct(), MERGE_REGULAR); + delete positive_conjunct; + delete neg_dnf; + positive_conjunct = conj; + delete neg_conj; + p.curr_set(NULL); + if(!simplify_conj(positive_conjunct, false, false, EQ_BLACK)) { + positive_conjunct = NULL; + goto ReturnDNF; + } + c0_updated = true; + } + else { + delete neg_conj; + p.curr_set(cgist); + } + } + } + } + } + + if(pres_debug) { + fprintf(DebugFile, "--- conj_and_not_dnf positive_conjunct NEW:\n"); + positive_conjunct->prefix_print(DebugFile); + fprintf(DebugFile, "--- conj_and_not_dnf neg_conjs GISTS:\n"); + neg_conjs->prefix_print(DebugFile); + fprintf(DebugFile, "--- conj_and_not_dnf ---\n\n"); + } + + /* Find minimal negative conjunct */ + { + Conjunct *min_conj = NULL; + int min_cost = INT_MAX; + DNF_Iterator min_p; + int live_count = 0; + for(DNF_Iterator q(neg_conjs); q.live(); q.next()) { + Conjunct *neg_conj = q.curr(); + if(neg_conj!=NULL) { + live_count++; + if(neg_conj->cost() < min_cost) { + min_conj = neg_conj; + min_cost = neg_conj->cost(); + min_p = q; + } + } + } + + /* Negate minimal conjunct, AND result with positive conjunct */ + if(weak || min_conj==NULL) { + ret_dnf->add_conjunct(positive_conjunct); + positive_conjunct = NULL; + } + else if (min_cost == CantBeNegated) { + static int OMEGA_WHINGE = -1; + if (OMEGA_WHINGE < 0) { + OMEGA_WHINGE = getenv("OMEGA_WHINGE") ? atoi(getenv("OMEGA_WHINGE")) : 0; + } + if (OMEGA_WHINGE) { + fprintf(stderr, "Ignoring negative clause that can't be negated and generating inexact result\n"); + if (!pres_debug) fprintf(DebugFile, "Ignoring negative clause that can't be negated and generating inexact result\n"); + } + + positive_conjunct->make_inexact(); + ret_dnf->add_conjunct(positive_conjunct); + positive_conjunct = NULL; + if(pres_debug) + fprintf(DebugFile, "Ignoring negative clause that can't be negated and generating inexact upper bound\n"); + } + else { + DNF *neg_dnf = negate_conj(min_conj); + delete min_conj; + min_p.curr_set(NULL); + DNF *new_pos = DNF_and_conj(neg_dnf, positive_conjunct); + delete neg_dnf; + delete positive_conjunct; + positive_conjunct = NULL; + // new_dnf->rm_redundant_conjs(2); + if(live_count>1) { + recursive = 1; + for(DNF_Iterator pd(new_pos); pd.live(); pd.next()) { + Conjunct *conj = pd.curr(); + ret_dnf->join_DNF(conj_and_not_dnf(conj, neg_conjs->copy(conj->relation()))); + pd.curr_set(NULL); + } + delete new_pos; + } + else { + ret_dnf->join_DNF(new_pos); + } + } + } + +ReturnDNF:; + delete positive_conjunct; + delete neg_conjs; + + //if (recursive) ret_dnf->rm_redundant_conjs(1); + + if(pres_debug) { + fprintf(DebugFile, "] conj_and_not_dnf RETURN:\n"); + ret_dnf->prefix_print(DebugFile); + fprintf(DebugFile, "\n\n"); + } + use_ugly_names--; + return ret_dnf; +} + +/* first some functions for manipulating oc "problems" */ + +static void EqnnZero(eqn *e, int s) { +// memset((char*)e, 0, (headerWords+1+s)*sizeof(int)); + e->key = 0; + e->touched = 0; + e->color = EQ_BLACK; + e->essential = 0; + e->varCount = 0; + for (int i = 0; i <= s; i++) + e->coef[i] = 0; +} + +/* + * Make a new black equation in a given problem + */ +static int NewEquation(Problem *p) { + int e = p->newEQ(); + EqnnZero(&p->EQs[e], p->nVars); + return e; +} + +/* + * Make a new black inequality in a given problem + */ +static int NewInequality(Problem *p) { + int g = p->newGEQ(); + EqnnZero(&p->GEQs[g], p->nVars); + return g; +} + +// +// ~CONJ -> DNF +// +DNF* negate_conj(Conjunct* conj) { + if(pres_debug) { + fprintf(DebugFile, "%%%%%% negate_conj IN %%%%%%\n"); + conj->prefix_print(DebugFile); + fprintf(DebugFile, "\n"); + } + + DNF* new_dnf = new DNF; + Problem *p = conj->problem; + int i, j,k; + + if (!conj->is_exact()) new_dnf->add_conjunct(conj->copy_conj_same_relation()); + + Conjunct* true_part = new Conjunct(NULL, conj->relation()); + Problem *tp = true_part->problem; + copy_conj_header(true_part, conj); + true_part->invalidate_leading_info(); + int *wildCard = new int[p->nGEQs]; + int *handleIt = new int[p->nVars+1]; + for(j=1; j<=p->nVars; j++) handleIt[j] = false; + + for(i=0; i<p->nGEQs; i++) { + wildCard[i] = 0; + for(j=1; j<=p->nVars; j++) { + Variable_ID v = conj->mappedVars[j]; + if(v->kind()==Wildcard_Var && p->GEQs[i].coef[j]!=0) { + assert(wildCard[i] == 0); + handleIt[j] = true; + if (p->GEQs[i].coef[j] > 0) wildCard[i] = j; + else wildCard[i] = -j; + } + } + } + + for(i=0; i<p->nGEQs; i++) if (wildCard[i] == 0) { + /* ~(ax + by + c >= 0) = (-ax -by -c-1 >= 0) */ + Conjunct* new_conj = true_part->copy_conj_same_relation(); + Problem *np = new_conj->problem; + new_conj->exact=true; + int n_e = NewInequality(np); + int t_e = NewInequality(tp); + np->GEQs[n_e].coef[0] = -p->GEQs[i].coef[0]-1; + tp->GEQs[t_e].coef[0] = p->GEQs[i].coef[0]; + for(j=1; j<=p->nVars; j++) { + Variable_ID v = conj->mappedVars[j]; + if(v->kind()==Wildcard_Var && p->GEQs[i].coef[j]!=0) { + assert(0 && "negate_conj: wildcard in inequality"); + } + np->GEQs[n_e].coef[j] = -p->GEQs[i].coef[j]; + tp->GEQs[t_e].coef[j] = p->GEQs[i].coef[j]; + + } + assert(j-1 == p->nVars); + assert(j-1 == conj->mappedVars.size()); + new_dnf->add_conjunct(new_conj); + } + + + for(i=0; i<p->nEQs; i++) { + int wc_no = 0; + int wc_j = 0; // make complier shut up + for(j=1; j<=p->nVars; j++) { + Variable_ID v = conj->mappedVars[j]; + if(v->kind()==Wildcard_Var && p->EQs[i].coef[j]!=0) { + wc_no++; + wc_j = j; + } + } + + if(wc_no!=0) { +#if ! defined NDEBUG + int i2; + assert(!handleIt[wc_j]); + for(i2=0; i2<p->nEQs; i2++) + if(i != i2 && p->EQs[i2].coef[wc_j] != 0) break; + assert(i2 >= p->nEQs); +#endif + assert(wc_no == 1 && "negate_conj: more than 1 wildcard in equality"); + + // === Negating equality with a wildcard for K>0 === + // ~(exists v st expr + K v + C = 0) = + // (exists v st 1 <= - expr - K v - C <= K-1) + + Conjunct *nc = true_part->copy_conj_same_relation(); + Problem *np = nc->problem; + nc->exact=true; + + // -K alpha = expr <==> K alpha = expr + if(p->EQs[i].coef[wc_j]<0) + p->EQs[i].coef[wc_j] = -p->EQs[i].coef[wc_j]; + + if(p->EQs[i].coef[wc_j]==2) { + // ~(exists v st expr +2v +C = 0) = + // (exists v st -expr -2v -C = 1) + // That is (expr +2v +C+1 = 0) + int e = NewEquation(np); + np->EQs[e].coef[0] = p->EQs[i].coef[0] +1; + for(j=1; j<=p->nVars; j++) { + np->EQs[e].coef[j] = p->EQs[i].coef[j]; + } + } + else { + // -expr -Kv -C-1 >= 0 + int e = NewInequality(np); + np->GEQs[e].coef[0] = -p->EQs[i].coef[0] -1; + for(j=1; j<=p->nVars; j++) { + np->GEQs[e].coef[j] = -p->EQs[i].coef[j]; + } + + // +expr +Kv +C+K-1 >= 0 + e = NewInequality(np); + np->GEQs[e].coef[0] = p->EQs[i].coef[0] +p->EQs[i].coef[wc_j] -1; + for(j=1; j<=p->nVars; j++) { + np->GEQs[e].coef[j] = p->EQs[i].coef[j]; + } + } + + new_dnf->add_conjunct(nc); + + } + else { + /* ~(ax + by + c = 0) = (-ax -by -c-1 >= 0) Or (ax + by + c -1 >= 0) */ + Conjunct *nc1 = true_part->copy_conj_same_relation(); + Conjunct *nc2 = true_part->copy_conj_same_relation(); + Problem* np1 = nc1->problem; + Problem* np2 = nc2->problem; + nc1->invalidate_leading_info(); + nc2->invalidate_leading_info(); + nc1->exact=true; + nc2->exact=true; + int n_e1 = NewInequality(np1); + int n_e2 = NewInequality(np2); + np1->GEQs[n_e1].coef[0] = -p->EQs[i].coef[0]-1; + np2->GEQs[n_e2].coef[0] = p->EQs[i].coef[0]-1; + for(j=1; j<=p->nVars; j++) { + coef_t coef = p->EQs[i].coef[j]; + np1->GEQs[n_e1].coef[j] = -coef; + np2->GEQs[n_e2].coef[j] = coef; + } + new_dnf->add_conjunct(nc1); + new_dnf->add_conjunct(nc2); + } + { + int e = NewEquation(tp); + tp->EQs[e].coef[0] = p->EQs[i].coef[0]; + for(j=1; j<=p->nVars; j++) + tp->EQs[e].coef[j] = p->EQs[i].coef[j]; + } + } + + for(j=1; j<=p->nVars; j++) + if (handleIt[j]) { + for(i=0; i<p->nGEQs; i++) + if (wildCard[i] == j) + for(k=0; k<p->nGEQs; k++) if (wildCard[k] == -j){ + // E_i <= c_i alpha + // c_k alpha <= E_k + // c_k E_i <= c_i c_k alpha <= c_i E_k + // c_k E_i <= c_i c_k floor (c_i E_k / c_i c_k) + // negating: + // c_k E_i > c_i c_k floor (c_i E_k / c_i c_k) + // c_k E_i > c_i c_k beta > c_i E_k - c_i c_k + // c_k E_i - 1 >= c_i c_k beta >= c_i E_k - c_i c_k + 1 + Conjunct* new_conj = true_part->copy_conj_same_relation(); + Problem *np = new_conj->problem; + coef_t c_k = - p->GEQs[k].coef[j]; + coef_t c_i = p->GEQs[i].coef[j]; + assert(c_k > 0); + assert(c_i > 0); + new_conj->exact=true; + int n_e = NewInequality(np); + // c_k E_i - 1 >= c_i c_k beta + int v; + for(v=0; v<=p->nVars; v++) { + np->GEQs[n_e].coef[v] = - c_k * p->GEQs[i].coef[v]; + } + np->GEQs[n_e].coef[j] = -c_i * c_k; + np->GEQs[n_e].coef[0]--; + + n_e = NewInequality(np); + // c_i c_k beta >= c_i E_k - c_i c_k + 1 + // c_i c_k beta + c_i c_k -1 >= c_i E_k + for(v=0; v<=p->nVars; v++) { + np->GEQs[n_e].coef[v] = - c_i * p->GEQs[k].coef[v]; + } + np->GEQs[n_e].coef[j] = c_i * c_k; + np->GEQs[n_e].coef[0] += c_i * c_k -1; + + new_dnf->add_conjunct(new_conj); + } + } + + if(pres_debug) { + fprintf(DebugFile, "%%%%%% negate_conj OUT %%%%%%\n"); + new_dnf->prefix_print(DebugFile); + } + delete true_part; + delete[] wildCard; + delete[] handleIt; + return(new_dnf); +} + + + + +/////////////////////////////////////////////////////// +// DNFize formula -- this is the real simplification // +// It also destroys the formula it simplifies // +/////////////////////////////////////////////////////// + + + +// +// Try to separate positive and negative clauses below the AND, +// letting us use the techniques described in Pugh & Wonnacott: +// "An Exact Method for Value-Based Dependence Analysis" +// + + +DNF* F_And::DNFize() { + Conjunct *positive_conjunct = NULL; + DNF *neg_conjs = new DNF; + List<DNF*> pos_dnfs; + List_Iterator<DNF*> pos_dnf_i; + DNF *new_dnf = new DNF; + int JustReturnDNF = 0; + + use_ugly_names++; + + if(pres_debug) { + fprintf(DebugFile, "\nF_And:: DNFize [\n"); + prefix_print(DebugFile); + } + + if(children().empty()) { + Conjunct * c=new Conjunct(NULL, relation()); + new_dnf->add_conjunct(c); + } + else { + while(!children().empty()) { + Formula* carg = children().remove_front(); + if(carg->node_type()==Op_Not) { + // DNF1 & ~DNF2 -> DNF + DNF *dnf = carg->children().remove_front()->DNFize(); + delete carg; + neg_conjs->join_DNF(dnf); // negative conjunct + } + else { + // DNF1 & DNF2 -> DNF + DNF *dnf = carg->DNFize(); + int dl = dnf->length(); + if(dl==0) { + // DNF & false -> false + delete this; + JustReturnDNF = 1; + break; + } + else if(dl==1) { + // positive conjunct + Conjunct *conj = dnf->rm_first_conjunct(); + delete dnf; + if(positive_conjunct==NULL) { + positive_conjunct = conj; + } + else { + Conjunct *new_conj = merge_conjs(positive_conjunct, conj, MERGE_REGULAR); + delete conj; + delete positive_conjunct; + positive_conjunct = new_conj; + } + } + else { + // positive DNF + pos_dnfs.append(dnf); + } + } + } + + if (!JustReturnDNF) { + Rel_Body * my_relation = relation(); + delete this; + + // If we have a positive_conjunct, it can serve as the 1st arg to + // conj_and_not_dnf. Otherwise, if pos_dnfs has one DNF, + // use each conjunct there for this purpose. + // Only pass "true" here if there is nothing else to try, + // as long as TRY_TO_AVOID_TRUE_AND_NOT_DNF is set. + // + // Perhaps we should even try to and multiple DNF's? + + if (!positive_conjunct && pos_dnfs.length() == 1) { + if(pres_debug) { + fprintf(DebugFile, "--- F_AND::DNFize() Single pos_dnf:\n"); + pos_dnfs[1]->prefix_print(DebugFile); + fprintf(DebugFile, "--- F_AND::DNFize() vs neg_conjs:\n"); + neg_conjs->prefix_print(DebugFile); + } + + DNF *real_neg_conjs = new DNF; + for (DNF_Iterator nc(neg_conjs); nc; nc++) { + if (simplify_conj((*nc), true, false, EQ_BLACK) != false) + real_neg_conjs->add_conjunct(*nc); + (*nc) = 0; + } + delete neg_conjs; + neg_conjs = real_neg_conjs; + + for(DNF_Iterator pc(pos_dnfs[1]); pc; pc++) { + new_dnf->join_DNF(conj_and_not_dnf((*pc), neg_conjs->copy((*pc)->relation()))); + (*pc) = 0; + } + } + else if(positive_conjunct==NULL && neg_conjs->is_definitely_false()) { + pos_dnf_i = List_Iterator<DNF*>(pos_dnfs); + delete new_dnf; + new_dnf = *pos_dnf_i; + *pos_dnf_i = NULL; + pos_dnf_i++; + for ( ; pos_dnf_i; pos_dnf_i++) { + DNF *pos_dnf = *pos_dnf_i; + new_dnf = DNF_and_DNF(new_dnf, pos_dnf); + *pos_dnf_i = NULL; + } + } + else { + if(positive_conjunct==NULL) { + static int OMEGA_WHINGE = -1; + if (OMEGA_WHINGE < 0) { + OMEGA_WHINGE = getenv("OMEGA_WHINGE") ? atoi(getenv("OMEGA_WHINGE")) : 0; + } + + if (pres_debug || OMEGA_WHINGE) { + fprintf(DebugFile, "Uh-oh: F_AND::DNFize() resorting to TRUE and not DNF\n"); + fprintf(DebugFile, "--- F_AND::DNFize() neg_conjs\n"); + neg_conjs->prefix_print(DebugFile); + fprintf(DebugFile, "--- F_AND::DNFize() pos_dnfs:\n"); + for (pos_dnf_i=List_Iterator<DNF*>(pos_dnfs); pos_dnf_i; pos_dnf_i++) { + (*pos_dnf_i)->prefix_print(DebugFile); + fprintf(DebugFile,"---- --\n"); + } + } + if (OMEGA_WHINGE) { + fprintf(stderr, "Uh-oh: F_AND::DNFize() resorting to TRUE and not DNF\n"); + fprintf(stderr, "--- F_AND::DNFize() neg_conjs\n"); + neg_conjs->prefix_print(stderr); + fprintf(stderr, "--- F_AND::DNFize() pos_dnfs:\n"); + for (pos_dnf_i=List_Iterator<DNF*>(pos_dnfs); pos_dnf_i; pos_dnf_i++) { + (*pos_dnf_i)->prefix_print(stderr); + fprintf(stderr,"---- --\n"); + } + } + positive_conjunct = new Conjunct(NULL, my_relation); + } + + if(!neg_conjs->is_definitely_false()) { + new_dnf->join_DNF(conj_and_not_dnf(positive_conjunct, neg_conjs)); + neg_conjs = NULL; + } + else { + new_dnf->add_conjunct(positive_conjunct); + } + positive_conjunct = NULL; + + // + // AND it with positive DNFs + // + if(pres_debug) { + fprintf(DebugFile, "--- F_AND::DNFize() pos_dnfs:\n"); + for (pos_dnf_i=List_Iterator<DNF*>(pos_dnfs); pos_dnf_i; pos_dnf_i++) + (*pos_dnf_i)->prefix_print(DebugFile); + } + for (pos_dnf_i = List_Iterator<DNF*>(pos_dnfs); pos_dnf_i; pos_dnf_i++) { + DNF *pos_dnf = *pos_dnf_i; + new_dnf = DNF_and_DNF(new_dnf, pos_dnf); + *pos_dnf_i = NULL; + } + } + } + } + + delete positive_conjunct; + delete neg_conjs; + for (pos_dnf_i = List_Iterator<DNF*>(pos_dnfs); pos_dnf_i; pos_dnf_i++) + delete *pos_dnf_i; + + if(pres_debug) { + fprintf(DebugFile, "] F_AND::DNFize() OUT \n"); + new_dnf->prefix_print(DebugFile); + } + + use_ugly_names--; + + return new_dnf; +} + +// +// ~ dnf = true ^ ~ dnf, so just call conj_and_not_dnf +// + +DNF* F_Not::DNFize() { + Conjunct *positive_conjunct = new Conjunct(NULL, relation()); + DNF *neg_conjs = children().remove_front()->DNFize(); + delete this; + DNF *new_dnf = conj_and_not_dnf(positive_conjunct, neg_conjs); + + if(pres_debug) { + fprintf(DebugFile, "=== F_NOT::DNFize() OUT ===\n"); + new_dnf->prefix_print(DebugFile); + } + return new_dnf; +} + + +// +// or is almost in DNF already: +// + +DNF* F_Or::DNFize() { + DNF* new_dnf = new DNF; + bool empty_or=true; + + while(!children().empty()) { + DNF* c_dnf = children().remove_front()->DNFize(); + new_dnf->join_DNF(c_dnf); + empty_or=false; + } + + + delete this; + + if(pres_debug) { + fprintf(DebugFile, "=== F_OR::DNFize() OUT ===\n"); + new_dnf->prefix_print(DebugFile); + } + return(new_dnf); +} + + +// +// exists x : (c1 v c2 v ...) --> (exists x : c1) v (exists x : c2) v ... +// + +DNF* F_Exists::DNFize() { + DNF *dnf = children().remove_front()->DNFize(); + + for (DNF_Iterator pd(dnf); pd.live(); pd.next()) { + Conjunct *conj = pd.curr(); + + // can simply call localize_vars for DNF with a single conjunct + Variable_ID_Tuple locals_copy(myLocals.size()); + copy_var_decls(locals_copy, myLocals); + conj->push_exists(locals_copy); + conj->remap(); + reset_remap_field(myLocals); + + conj->r_constrs = 0; + conj->simplified = false; // who knows + conj->cols_ordered = false; + } + delete this; + + if(pres_debug) { + fprintf(DebugFile, "=== F_EXISTS::DNFize() OUT ===\n"); + dnf->prefix_print(DebugFile); + } + return(dnf); +} + + +// +// Single conjunct is already in DNF. +// + +DNF* Conjunct::DNFize() { + assert(!is_compressed()); + DNF *results = new DNF; + + if (is_true()) { + simplified = true; + verified = true; + results->add_conjunct(this); + } + else { + results->add_conjunct(this); + } + + return results; +} + + +// +// Foralls should have been removed before we get to DNFize +// + +DNF* F_Forall::DNFize() { + assert(0); + return(NULL); +} + +void DNF::count_leading_0s() { + if (conjList.empty()) + return; + + for (DNF_Iterator conj(this); conj; conj++) { + (*conj)->count_leading_0s(); + } +} + + +// return x s.t. forall conjuncts c, c has >= x leading 0s +// if set, always returns -1; arg tells you if it's a set or relation. + +int DNF::query_guaranteed_leading_0s(int what_to_return_for_empty_dnf) { + count_leading_0s(); + int result = what_to_return_for_empty_dnf; // if set, -1; if rel, 0 + bool first = true; + + for (DNF_Iterator conj(this); conj; conj++) { + int tmp = (*conj)->query_guaranteed_leading_0s(); + assert(tmp >= 0 || ((*conj)->relation()->is_set() && tmp == -1)); + if (first || tmp < result) result = tmp; + first = false; + } + + return result; +} + +// return x s.t. forall conjuncts c, c has <= x leading 0s +// if no conjuncts, return the argument + +int DNF::query_possible_leading_0s(int n_input_and_output) { + count_leading_0s(); + int result = n_input_and_output; + bool first = true; + + for (DNF_Iterator conj(this); conj; conj++) { + int tmp = (*conj)->query_possible_leading_0s(); + assert(tmp >= 0 || (tmp == -1 && (*conj)->relation()->is_set())); + if (first || tmp > result) result = tmp; + first = false; + } + + return result; +} + + +// return 0 if we don't know, or +-1 if we do + +int DNF::query_leading_dir() { + count_leading_0s(); + int result = 0; + bool first = true; + + for (DNF_Iterator conj(this); conj; conj++) { + int glz = (*conj)->query_guaranteed_leading_0s(); + int plz = (*conj)->query_possible_leading_0s(); + int rlz = 0; // shut the compiler up + if (glz != plz) + return 0; + + if (first) { + rlz = glz; + result = (*conj)->query_leading_dir(); + first = false; + } + else + if (glz != rlz || result != (*conj)->query_leading_dir()) + return 0; + } + + return result; +} + +void Conjunct::count_leading_0s() { + Rel_Body *body = relation(); + int max_depth = min(body->n_inp(), body->n_out()); + if(body->is_set()) { + assert(guaranteed_leading_0s == -1 && possible_leading_0s == -1); +// guaranteed_leading_0s = possible_leading_0s = -1; + leading_dir = 0; + return; + } + + +#if ! defined NDEBUG + assert_leading_info(); +#endif + if (guaranteed_leading_0s < 0) { + int L; + for (L=1; L <= max_depth; L++) { + Variable_ID in = body->input_var(L), out = body->output_var(L); + coef_t min, max; + bool guaranteed; + + query_difference(out, in, min, max, guaranteed); + if (min < 0 || max > 0) { + if (min > 0 || max < 0) { // we know guaranteed & possible + guaranteed_leading_0s = possible_leading_0s = L-1; + if (min > 0) // We know its 0,..,0,+ + leading_dir = 1; + else // We know its 0,..,0,- + leading_dir = -1; + return; + } + break; + } + } + guaranteed_leading_0s = L-1; + for ( ; L <= max_depth; L++) { + Variable_ID in = body->input_var(L), + out = body->output_var(L); + coef_t min, max; + bool guaranteed; + + query_difference(out, in, min, max, guaranteed); + + if (min > 0 || max < 0) break; + } + possible_leading_0s = L-1; + } +#if ! defined NDEBUG + assert_leading_info(); +#endif +} + +// +// add level-carried DNF form out to level "level" +// + + +void DNF::make_level_carried_to(int level) { + count_leading_0s(); + Rel_Body *body = 0; // make compiler shut up + if (length() > 0 && !(body = conjList.front()->relation())->is_set()) { + // LCDNF makes no sense otherwise + Relation tmp; +#ifndef NDEBUG + tmp = Relation(*body,42); +#endif + + DNF *newstuff = new DNF; + int shared_depth = min(body->n_inp(), body->n_out()); + int split_to = level >= 0 ? min(shared_depth,level) : shared_depth; + + skip_finalization_check++; + EQ_Handle e; + + for (DNF_Iterator conj(this); conj; conj++) { + assert(body = (*conj)->relation()); + int leading_eqs; + + bool is_guaranteed = (*conj)->verified; + + for (leading_eqs=1; leading_eqs <= split_to; leading_eqs++) { + Variable_ID in = body->input_var(leading_eqs), + out = body->output_var(leading_eqs); + coef_t min, max; + bool guaranteed; + + if (leading_eqs > (*conj)->possible_leading_0s && + (*conj)->leading_dir_valid_and_known()) { + leading_eqs--; + break; + } + + if (leading_eqs > (*conj)->guaranteed_leading_0s) { + (*conj)->query_difference(out, in, min, max, guaranteed); + if (min > 0 || max < 0) guaranteed = true; +// fprintf(DebugFile,"Make level carried, %d <= diff%d <= %d (%d):\n", +// min,leading_eqs,max,guaranteed); +// use_ugly_names++; +// (*conj)->prefix_print(DebugFile); +// use_ugly_names--; + if (!guaranteed) is_guaranteed = false; + bool generateLTClause = min < 0; + bool generateGTClause = max > 0; + bool retainEQClause = (leading_eqs <= (*conj)->possible_leading_0s && + min <= 0 && max >= 0); + if (!(generateLTClause || generateGTClause || retainEQClause)) { + // conjunct is infeasible + if (pres_debug) { + fprintf(DebugFile, "Conjunct discovered to be infeasible during make_level_carried_to(%d):\n", level); + (*conj)->prefix_print(DebugFile); + } +#if ! defined NDEBUG + Conjunct *cpy = (*conj)->copy_conj_same_relation(); + assert(!simplify_conj(cpy, true, 32767, 0)); +#endif + } + + if (generateLTClause) { + Conjunct *lt; + if (!generateGTClause && !retainEQClause) + lt = *conj; + else + lt = (*conj)->copy_conj_same_relation(); + if (max >= 0) { + GEQ_Handle l = lt->add_GEQ(); // out<in ==> in-out-1>=0 + l.update_coef_during_simplify(in, 1); + l.update_coef_during_simplify(out, -1); + l.update_const_during_simplify(-1); + } + lt->guaranteed_leading_0s + = lt->possible_leading_0s = leading_eqs-1; + lt->leading_dir = -1; + if (is_guaranteed) { + /* + fprintf(DebugFile,"Promising solutions to: %d <= diff%d <= %d (%d):\n", + min,leading_eqs,max,guaranteed); + use_ugly_names++; + lt->prefix_print(DebugFile); + use_ugly_names--; + */ + lt->promise_that_ub_solutions_exist(tmp); + } + else if (0) { + fprintf(DebugFile,"Can't guaranteed solutions to:\n"); + use_ugly_names++; + lt->prefix_print(DebugFile); + use_ugly_names--; + } + if (generateGTClause || retainEQClause) + newstuff->add_conjunct(lt); + } + + if (generateGTClause) { + Conjunct *gt; + if (retainEQClause) gt = (*conj)->copy_conj_same_relation(); + else gt = *conj; + if (min <= 0) { + GEQ_Handle g = gt->add_GEQ(); // out>in ==> out-in-1>=0 + g.update_coef_during_simplify(in, -1); + g.update_coef_during_simplify(out, 1); + g.update_const_during_simplify(-1); + } + gt->guaranteed_leading_0s = + gt->possible_leading_0s = leading_eqs-1; + gt->leading_dir = 1; + if (is_guaranteed) { + /* + fprintf(DebugFile,"Promising solutions to: %d <= diff%d <= %d (%d):\n", + min,leading_eqs,max,guaranteed); + use_ugly_names++; + gt->prefix_print(DebugFile); + use_ugly_names--; + */ + gt->promise_that_ub_solutions_exist(tmp); + } + else if (0) { + fprintf(DebugFile,"Can't guaranteed solutions to:\n"); + use_ugly_names++; + gt->prefix_print(DebugFile); + use_ugly_names--; + } + if (retainEQClause) newstuff->add_conjunct(gt); + } + + if (retainEQClause) { + assert(min <= 0 && 0 <= max); + + if (min < 0 || max > 0) { + e = (*conj)->add_EQ(1); + e.update_coef_during_simplify(in, -1); + e.update_coef_during_simplify(out, 1); + } + + assert((*conj)->guaranteed_leading_0s == -1 + || leading_eqs > (*conj)->guaranteed_leading_0s); + assert((*conj)->possible_leading_0s == -1 + || leading_eqs <= (*conj)->possible_leading_0s); + + (*conj)->guaranteed_leading_0s = leading_eqs; + } + else break; + } + + { + Set<Global_Var_ID> already_done; + int remapped = 0; + + assert((*conj)->guaranteed_leading_0s == -1 + || leading_eqs <= (*conj)->guaranteed_leading_0s); + + for (Variable_ID_Iterator func(*body->global_decls()); func; func++) { + Global_Var_ID f = (*func)->get_global_var(); + if (!already_done.contains(f) && + body->has_local(f, Input_Tuple) && + body->has_local(f, Output_Tuple) && + f->arity() == leading_eqs) { + already_done.insert(f); + + // add f(in) = f(out), project one away + e = (*conj)->add_EQ(1); + Variable_ID f_in =body->get_local(f,Input_Tuple); + Variable_ID f_out =body->get_local(f,Output_Tuple); + + e.update_coef_during_simplify(f_in, -1); + e.update_coef_during_simplify(f_out, 1); + + f_out->remap = f_in; + remapped = 1; + is_guaranteed = false; + } + } + + if (remapped) { + (*conj)->remap(); + (*conj)->combine_columns(); + reset_remap_field(*body->global_decls()); + remapped = 0; + } + } + } + if (is_guaranteed) + (*conj)->promise_that_ub_solutions_exist(tmp); + else if (0) { + fprintf(DebugFile,"Can't guaranteed solutions to:\n"); + use_ugly_names++; + (*conj)->prefix_print(DebugFile); + use_ugly_names--; + } + } + + skip_finalization_check--; + join_DNF(newstuff); + } + +#if ! defined NDEBUG + for (DNF_Iterator c(this); c; c++) + (*c)->assert_leading_info(); +#endif + + simplify(); +} + +void DNF::remove_inexact_conj() { + bool found_inexact=false; + + do { + bool first=true; + found_inexact=false; + DNF_Iterator c_prev; + for (DNF_Iterator c(this); c; c++) { + if (!(*c)->is_exact()) { // remove it from the list + found_inexact=true; + delete (*c); + if (first) + conjList.del_front(); + else + conjList.del_after(c_prev); + break; + } + else { + first=false; + c_prev=c; + } + } + } + while (found_inexact); +} + + +int s_rdt_constrs; + +// +// Simplify all conjuncts in a DNF +// +void DNF::simplify() { + for (DNF_Iterator pd(this); pd.live(); ) { + Conjunct *conj = pd.curr(); + pd.next(); + if(s_rdt_constrs >= 0 && !simplify_conj(conj, true, s_rdt_constrs, EQ_BLACK)) { + rm_conjunct(conj); + } + } +} + +} // namespace diff --git a/omegalib/omega/src/pres_form.cc b/omegalib/omega/src/pres_form.cc new file mode 100644 index 0000000..82b710b --- /dev/null +++ b/omegalib/omega/src/pres_form.cc @@ -0,0 +1,147 @@ +#include <omega/pres_form.h> +#include <omega/pres_tree.h> +#include <omega/pres_conj.h> +#include <omega/Relation.h> +#include <omega/omega_i.h> + +namespace omega { + +// +// Children and parents. +// +void Formula::remove_child(Formula *kid) { + assert(&kid->parent() == this); + if (myChildren.front() == kid) + myChildren.del_front(); + else { + List_Iterator<Formula*> j,k; + for(j=List_Iterator<Formula*>(myChildren); *j != kid && j; k=j, j++) + ; + + if (k) + myChildren.del_after(k); + else + assert(0 && "Child to be removed not found in child list"); + } +} + + +void Formula::add_child(Formula *kid) { + assert(can_add_child()); + myChildren.append(kid); + kid->myParent = this; + kid->myRelation = this->relation(); +} + +void Formula::replace_child(Formula *child, Formula* new_child) { + assert(&child->parent() == this); + for(List_Iterator<Formula *> LI(myChildren); LI; LI++) + if(*LI == child) { + *LI = new_child; + new_child->myParent = this; + new_child->myRelation = this->relation(); + break; + } +} + +void Formula::set_parent(Formula *parent, Rel_Body *reln) { + myParent = parent; + myRelation = reln; + for(List_Iterator<Formula*> c(myChildren); c; c++) + (*c)->set_parent(this,reln); +} + +// +// Function that sets myRelation pointers in a tree. +// +void Formula::set_relation(Rel_Body *r) { + myRelation = r; + for(List_Iterator<Formula *> FI(myChildren); FI; FI++) + (*FI)->set_relation(r); +} + + +// +// Function that descends to conjuncts to merge columns +// +void Formula::combine_columns() { + foreach(child,Formula *,myChildren,child->combine_columns()); +} + + +void Formula::finalize() { + for(List_Iterator<Formula*> c(children()); c; c++) + (*c)->finalize(); +} + +bool Formula::can_add_child() { + return true; +} + + + +Conjunct *Formula::really_conjunct() { + assert(0 && "really_conjunct() called on something that wasn't"); + return NULL; +} + +Formula::Formula(Formula *p, Rel_Body *r): myParent(p), myRelation(r) { +} + + +void Formula::verify_tree() { // should be const +#if ! defined NDEBUG + Any_Iterator<Formula*> c = myChildren.any_iterator(); + for (; c; c++) { + assert((*c)->myParent==this); + assert((*c)->myRelation==this->myRelation); + (*c)->verify_tree(); + } +#endif +} + +Formula *Formula::copy(Formula *, Rel_Body *) { + assert(0); + return NULL; +} + +Formula::~Formula() { + for(List_Iterator<Formula*> c(myChildren); c; c++) { + delete *c; + } + myChildren.clear(); +} + +void Formula::assert_not_finalized() { + if (!skip_finalization_check) { + assert(! relation()->is_finalized()); + assert(! relation()->is_shared()); + } +} + +void Formula::reverse_leading_dir_info() { + for(List_Iterator<Formula*> c(myChildren); c; c++) + (*c)->reverse_leading_dir_info(); +} + +void Formula::invalidate_leading_info(int changed) { + for(List_Iterator<Formula*> c(myChildren); c; c++) + (*c)->invalidate_leading_info(changed); +} + +void Formula::enforce_leading_info(int guaranteed, int possible, int dir) { + for(List_Iterator<Formula*> c(myChildren); c; c++) + (*c)->enforce_leading_info(guaranteed, possible, dir); +} + +// +// Push_exists functions. +// Push exists takes responsibility for the Variable_ID's in the Tuple. +// It should: +// * Re-use them, or +// * Delete them. +void Formula::push_exists(Variable_ID_Tuple &) { + assert(0); +} + +} // namespace diff --git a/omegalib/omega/src/pres_gen.cc b/omegalib/omega/src/pres_gen.cc new file mode 100644 index 0000000..0f05d40 --- /dev/null +++ b/omegalib/omega/src/pres_gen.cc @@ -0,0 +1,45 @@ +#include <omega/pres_gen.h> + +namespace omega { + +int skip_finalization_check=0; +// int skip_set_checks=0; + +int pres_debug=0 ; +FILE *DebugFile=stderr; // This is the default; it's best to set it yourself. + +negation_control pres_legal_negations = any_negation; + +// +// I/O utility functions. +// +// void PresErrAssert(const char *t) { +// fprintf(stdout, "\nERROR: %s\n", t); +// if(pres_debug) { +// fprintf(DebugFile, "\nERROR: %s\n", t); +// } +// exit(1); +// } + + + +// +// Needed for gprof +// +#if defined PROFILE_MALLOCS +void* operator new(size_t n) { + void *result = malloc (n < 1 ? 1 : n); + if (result) + return result; + else { + write(2,"Virtual memory exceeded in new\n",32); + return 0; + } +} + +void operator delete (void* f) { + if (f) free(f); +} +#endif + +} // namespace diff --git a/omegalib/omega/src/pres_logic.cc b/omegalib/omega/src/pres_logic.cc new file mode 100644 index 0000000..8ee90f1 --- /dev/null +++ b/omegalib/omega/src/pres_logic.cc @@ -0,0 +1,226 @@ +#include <omega/pres_logic.h> +#include <omega/pres_conj.h> +#include <omega/pres_quant.h> +#include <omega/omega_i.h> + +namespace omega { + +GEQ_Handle F_And::add_GEQ(int preserves_level) { + assert_not_finalized(); + if (pos_conj == NULL || pos_conj->problem->nGEQs >= maxGEQs) { + pos_conj = NULL; + for(List_Iterator<Formula*> c(children()); c; c++) { + if ((*c)->node_type()==Op_Conjunct && + ((*c)->really_conjunct())->problem->nGEQs < maxGEQs) { + pos_conj = (*c)->really_conjunct(); + break; + } + } + if(!pos_conj) pos_conj = add_conjunct();// FERD -- set level if preserved? + } + return pos_conj->add_GEQ(preserves_level); +} + + +EQ_Handle F_And::add_EQ(int preserves_level) { + assert_not_finalized(); + if (pos_conj == NULL || pos_conj->problem->nEQs >= maxEQs) { + pos_conj = NULL; + for(List_Iterator<Formula*> c(children()); c; c++) { + if ((*c)->node_type()==Op_Conjunct && + ((*c)->really_conjunct())->problem->nEQs < maxEQs) { + pos_conj = (*c)->really_conjunct(); + break; + } + } + if(!pos_conj) pos_conj = add_conjunct();//FERD-set level info if preserved? + } + return pos_conj->add_EQ(preserves_level); +} + +Stride_Handle F_And::add_stride(int step, int preserves_level) { + assert_not_finalized(); + if (pos_conj == NULL || pos_conj->problem->nEQs >= maxEQs) { + pos_conj = NULL; + for(List_Iterator<Formula*> c(children()); c; c++) { + if ((*c)->node_type()==Op_Conjunct && + ((*c)->really_conjunct())->problem->nEQs < maxEQs) { + pos_conj = (*c)->really_conjunct(); + break; + } + } + if(!pos_conj) pos_conj = add_conjunct(); // FERD -set level if preserved? + } + return pos_conj->add_stride(step, preserves_level); +} + +GEQ_Handle F_And::add_GEQ(const Constraint_Handle &constraint, int preserves_level) { + assert_not_finalized(); + if (pos_conj == NULL || pos_conj->problem->nGEQs >= maxGEQs) { + pos_conj = NULL; + for(List_Iterator<Formula*> c(children()); c; c++) { + if ((*c)->node_type()==Op_Conjunct && + ((*c)->really_conjunct())->problem->nGEQs < maxGEQs) { + pos_conj = (*c)->really_conjunct(); + break; + } + } + if(!pos_conj) pos_conj = add_conjunct();// FERD -- set level if preserved? + } + return pos_conj->add_GEQ(constraint, preserves_level); +} + + +EQ_Handle F_And::add_EQ(const Constraint_Handle &constraint, int preserves_level) { + assert_not_finalized(); + if (pos_conj == NULL || pos_conj->problem->nEQs >= maxEQs) { + pos_conj = NULL; + for(List_Iterator<Formula*> c(children()); c; c++) { + if ((*c)->node_type()==Op_Conjunct && + ((*c)->really_conjunct())->problem->nEQs < maxEQs) { + pos_conj = (*c)->really_conjunct(); + break; + } + } + if(!pos_conj) pos_conj = add_conjunct();//FERD-set level info if preserved? + } + return pos_conj->add_EQ(constraint,preserves_level); +} + + +void F_And::add_unknown() { + assert_not_finalized(); + if (pos_conj == NULL) { + for (List_Iterator<Formula*> c(children()); c; c++) { + if ((*c)->node_type()==Op_Conjunct) { + pos_conj = (*c)->really_conjunct(); + break; + } + } + if(!pos_conj) pos_conj = add_conjunct(); // FERD - set level if preseved? + } + pos_conj->make_inexact(); +} + +Conjunct *F_Or::find_available_conjunct() { + return 0; +} + +Conjunct *F_Not::find_available_conjunct() { + return 0; +} + +Conjunct *F_And::find_available_conjunct() { + for(List_Iterator<Formula*> child(children()); child; child++) { + Conjunct *c = (*child)->find_available_conjunct(); + if (c) return c; + } + return 0; +} + + +void F_Not::finalize() { + assert(n_children() == 1); + Formula::finalize(); +} + +bool F_Not::can_add_child() { + return n_children() < 1; +} + +F_And *F_And::and_with() { + assert_not_finalized(); + assert(can_add_child()); + return this; +} + +F_And::F_And(Formula *p, Rel_Body *r): Formula(p,r), pos_conj(NULL) { +} + +F_Or::F_Or(Formula *p, Rel_Body *r): Formula(p,r){ +} + +F_Not::F_Not(Formula *p, Rel_Body *r): Formula(p,r){ +} + +Formula *F_And::copy(Formula *parent, Rel_Body *reln) { + F_And *f = new F_And(parent, reln); + for(List_Iterator<Formula*> c(children()); c; c++) + f->children().append((*c)->copy(f,reln)); + return f; +} + +Formula *F_Or::copy(Formula *parent, Rel_Body *reln) { + F_Or *f = new F_Or(parent, reln); + for(List_Iterator<Formula*> c(children()); c; c++) + f->children().append((*c)->copy(f,reln)); + return f; +} + +Formula *F_Not::copy(Formula *parent, Rel_Body *reln) { + F_Not *f = new F_Not(parent, reln); + for(List_Iterator<Formula*> c(children()); c; c++) + f->children().append((*c)->copy(f,reln)); + return f; +} + +// +// Create F_Exists nodes below this F_Or. +// Copy list S to each of the created nodes. +// Push_exists takes responsibility for reusing or deleting Var_ID's; +// here we delete them. Must also empty out the Tuple when finished. +void F_Or::push_exists(Variable_ID_Tuple &S) { + List<Formula*> mc; + mc.join(children()); + + while(!mc.empty()) { + Formula *f = mc.remove_front(); + F_Exists *e = add_exists(); + + copy_var_decls(e->myLocals, S); + f->remap(); + reset_remap_field(S); + + e->add_child(f); + } + // Since these are not reused, they have to be deleted + for(Tuple_Iterator<Variable_ID> VI(S); VI; VI++) { + assert((*VI)->kind() == Exists_Var); + delete *VI; + } + S.clear(); +} + +void F_Exists::push_exists(Variable_ID_Tuple &S) { + myLocals.join(S); +} + +F_Not *Formula::add_not() { + assert_not_finalized(); + assert(can_add_child()); + F_Not *f = new F_Not(this, myRelation); + myChildren.append(f); + return f; +} + +F_And *Formula::add_and() { + assert_not_finalized(); + assert(can_add_child()); + F_And *f = new F_And(this, myRelation); + myChildren.append(f); + return f; +} + +F_And *Formula::and_with() { + return add_and(); +} + +F_Or *Formula::add_or() { + assert_not_finalized(); + assert(can_add_child()); + F_Or *f = new F_Or(this, myRelation); + myChildren.append(f); + return f; +} + +} // namespace diff --git a/omegalib/omega/src/pres_print.cc b/omegalib/omega/src/pres_print.cc new file mode 100644 index 0000000..4f2cd0d --- /dev/null +++ b/omegalib/omega/src/pres_print.cc @@ -0,0 +1,908 @@ +#include <omega/pres_gen.h> +#include <omega/pres_var.h> +#include <omega/pres_tree.h> +#include <omega/pres_conj.h> +#include <omega/Relation.h> +#include <basic/Bag.h> +#include <omega/omega_i.h> +#include <omega/omega_core/oc.h> + +namespace omega { + +//////////////////////////////////////// +// // +// Print functions. // +// // +//////////////////////////////////////// + +void Conjunct::reorder_for_print(bool reverseOrder, int first_pass_input, int first_pass_output, bool sort) { + Conjunct *C2 = copy_conj_same_relation(); + Variable_ID_Tuple newpos(0),wcvars(0),gvars(0); + +// We reorder the original Variable_ID's into the newpos list; later, we +// copy from their original column (using find_column) to the new one. + int n = mappedVars.size(); + int i; + // there may be more inp/outp vars than maxVars; must do dynamically + // skip_set_checks++; + Tuple<bool> input_used(myRelation->n_inp()); + Tuple<bool> output_used(myRelation->n_out()); + for(i=1; i<=myRelation->n_inp();i++) input_used[i] = false; + for(i=1; i<=myRelation->n_out();i++) output_used[i] = false; + for(i=1; i<=n;i++) { + if (mappedVars[i]->kind() == Input_Var) + input_used[mappedVars[i]->get_position()] = true; + else if (mappedVars[i]->kind() == Output_Var) + output_used[mappedVars[i]->get_position()] = true; + else if(mappedVars[i]->kind() == Global_Var) + gvars.append(mappedVars[i]); + } + + + if(sort) + for(i=1; i<=gvars.size();i++) + for(int j=1; j <= gvars.size(); j++) + if(gvars[j]->get_global_var()->base_name() + < gvars[j+1]->get_global_var()->base_name()) { + Variable_ID t = gvars[j]; gvars[j] = gvars[j+1]; gvars[j+1] = t; + } + + newpos.join(gvars); + + if(!reverseOrder) { + for(i=1; i<=min(myRelation->n_inp(),first_pass_input);i++) + if (input_used[i]) newpos.append(input_vars[i]); + for(i=1; i<=min(myRelation->n_out(),first_pass_output);i++) + if (output_used[i]) newpos.append(output_vars[i]); + for(i=max(1,first_pass_input+1); i<=myRelation->n_inp();i++) + if (input_used[i]) newpos.append(input_vars[i]); + for(i=max(1,first_pass_output+1); i<=myRelation->n_out();i++) + if (output_used[i]) newpos.append(output_vars[i]); + } + else { + for(i=1; i<=min(myRelation->n_out(),first_pass_output);i++) + if (output_used[i]) newpos.append(output_vars[i]); + for(i=1; i<=min(myRelation->n_inp(),first_pass_input);i++) + if (input_used[i]) newpos.append(input_vars[i]); + for(i=max(1,first_pass_output+1); i<=myRelation->n_out();i++) + if (output_used[i]) newpos.append(output_vars[i]); + for(i=max(1,first_pass_input+1); i<=myRelation->n_inp();i++) + if (input_used[i]) newpos.append(input_vars[i]); + } + + + for(i=1; i<=n;i++) + if (mappedVars[i]->kind() == Wildcard_Var) + wcvars.append(mappedVars[i]); + + if(sort) + for(i=1; i<=gvars.size();i++) + for(int j=1; j <= gvars.size(); j++) + if(gvars[j]->name() < gvars[j+1]->name()) { + Variable_ID t = gvars[j]; gvars[j] = gvars[j+1]; gvars[j+1] = t; + } + + newpos.join(wcvars); + + assert(problem->nVars == newpos.size()); // i.e. no other variable types + + // Copy coef columns into new order. Constant column is unchanged. + for(int e=0; e<problem->nGEQs; e++) problem->GEQs[e].touched = 1; + + for(i=1; i<=problem->nVars; i++) { + int col = find_column(newpos[i]); // Find column in original conj. + assert(col != 0); + copy_column(problem, i, // Copy it from orig. column in the copy. + C2->problem, col, 0, 0); + problem->var[i] = i; + } + for(i=1; i<=problem->nVars; i++) + problem->forwardingAddress[i] = i; + + mappedVars = newpos; + delete C2; + // skip_set_checks--; +} + +void Rel_Body::print_with_subs(FILE *output_file, bool printSym, bool newline) { + std::string s = this->print_with_subs_to_string(printSym, newline); + fprintf(output_file, "%s", s.c_str()); +} + +void Rel_Body::print_with_subs() { + this->print_with_subs(stdout, 0, 1); +} + +static std::string tryToPrintVarToStringWithDiv(Conjunct *C, Variable_ID v) { + std::string s; + bool seen = false; +// This assumes that there is one EQ involving v, that v cannot be +// substituted and hence has a non-unit coefficient. + for(EQ_Iterator e(C); e; e++) { + if ((*e).get_coef(v) != 0) { + assert(!seen); // This asserts just one EQ with v + coef_t v_coef = (*e).get_coef(v); + int v_sign = v_coef > 0 ? 1 : -1; + v_coef *= v_sign; + int sign_adj = -v_sign; + + s += "intDiv("; + bool first=true; + for(Constr_Vars_Iter i(*e,false); i; i++) { + if ((*i).var != v && (*i).coef != 0) { + coef_t this_coef = sign_adj*(*i).coef; + if(!first && this_coef > 0) + s+= "+"; + if (this_coef == 1) + s += (*i).var->name(); + else if (this_coef == -1) { + s += "-"; s += (*i).var->name(); + } + else { + s += to_string(this_coef) + "*" + (*i).var->name(); + } + first = false; + } + } + coef_t the_const = (*e).get_const()* sign_adj; + if (the_const > 0 && !first) + s+= "+"; + if (the_const != 0) + s += to_string(the_const); + s += "," + to_string(v_coef) + ")"; + seen = true; + } + } + return s; +} + + +// This function prints the output tuple of a relation with each one as a +// function of the input variables. +// This will fail or look goofy if the outputs are not affine functions of +// the inputs. +// BIG WARNING: Call this only from the codegen stuff, since it assumes things +// about coefficients +std::string Rel_Body::print_outputs_with_subs_to_string() { + // Rel_Body S(this),Q(this); + Rel_Body *S = this->clone(); + Rel_Body *Q = this->clone(); + S->setup_names(); + Conjunct *C = S->single_conjunct(); + Conjunct *D = Q->single_conjunct(); // ordered_elimination futzes with conj + std::string s; // = "["; + C->reorder_for_print(); + C->ordered_elimination(S->global_decls()->length()); + // Print output names with substitutions in them + for(int i=1; i<=S->n_out(); i++) { + std::string t; + int col = C->find_column(output_vars[i]); + if (col != 0) + t = C->print_sub_to_string(col); + if (col == 0 || t == output_vars[i]->name()) // no sub found + // Assume you can't get a unit coefficient on v, must use div + t = tryToPrintVarToStringWithDiv(D, output_vars[i]); + s += t; + if (i < S->n_out()) s += ","; + } + // s += "] "; + delete S; + delete Q; + return s; +} + +std::string Rel_Body::print_outputs_with_subs_to_string(int i) { + // Rel_Body S(this),Q(this); + Rel_Body *S = this->clone(); + Rel_Body *Q = this->clone(); + S->setup_names(); + Conjunct *C = S->single_conjunct(); + Conjunct *D = Q->single_conjunct(); // ordered_elimination futzes with conj + std::string s; + C->reorder_for_print(); + C->ordered_elimination(S->global_decls()->length()); + // Print output names with substitutions in them + { + std::string t; + int col = C->find_column(output_vars[i]); + if (col != 0) + t = C->print_sub_to_string(col); + if (col == 0 || t == output_vars[i]->name()) // no sub found? + t = tryToPrintVarToStringWithDiv(D, output_vars[i]); + // should check for failure + s += t; + } + delete S; + delete Q; + return s; +} + +std::string Rel_Body::print_with_subs_to_string(bool printSym, bool newline) { + std::string s=""; + + if (pres_debug) { + fprintf(DebugFile,"print_with_subs_to_string:\n"); + prefix_print(DebugFile); + } + + int anythingPrinted = 0; + + if (this->is_null()) { + s = "NULL Rel_Body\n"; + return s; + } + + // Rel_Body R(this); + Rel_Body *R = this->clone(); + bool firstRelation = true; + + for(DNF_Iterator DI(R->query_DNF()); DI.live(); DI.next()) { + Rel_Body S(R, DI.curr()); + Conjunct *C = S.single_conjunct(); + if(!simplify_conj(C, true, 4, EQ_BLACK)) continue; + + S.setup_names(); + + if(! firstRelation) { + s += " union"; + if (newline) s += "\n "; + } + else + firstRelation = false; + + anythingPrinted = 1; + + C->reorder_for_print(false,C->max_ufs_arity_of_in(), + C->max_ufs_arity_of_out()); + C->ordered_elimination(S.Symbolic.length() + +C->max_ufs_arity_of_in() + +C->max_ufs_arity_of_out()); +// assert(C->problem->variablesInitialized); + + if (pres_debug) S.prefix_print(DebugFile); + + /* Do actual printing of Conjunct C as a relation */ + s += "{"; + + if (!Symbolic.empty()) { + if (printSym) s += "Sym=["; + for (Variable_ID_Iterator Sym_I = S.Symbolic; Sym_I;) + { + if (printSym) + s += (*Sym_I)->name(); + Sym_I++; + if (printSym && Sym_I) s+= ","; + } + if (printSym) s += "] "; + } + + int i, col; + + if (S.number_input != 0) { + s += "["; + // Print input names with substitutions in them + for(i=1; i<=S.number_input; i++) { + col = C->find_column(input_vars[i]); + if (col != 0) + s += C->problem->print_sub_to_string(col); + else + s += input_vars[i]->name(); + if (i<S.number_input) s += ","; + } + s += "]"; + } + + if (! S.is_set()) + s += " -> "; + + if (S.number_output != 0) { + s += "["; + + // Print output names with substitutions in them + for(i=1; i<=S.number_output; i++) { + col = C->find_column(output_vars[i]); + if (col != 0) + s += C->problem->print_sub_to_string(col); + else + s += output_vars[i]->name(); + if (i < S.number_output) s += ","; + } + s += "] "; + } + + if (C->is_unknown()) { + if (S.number_input != 0 || S.number_output != 0) + s += ":"; + s += " UNKNOWN"; + } + else { + // Empty conj means TRUE, so don't print colon + if (C->problem->nEQs != 0 || C->problem->nGEQs != 0) { + C->problem->clearSubs(); + if (S.number_input != 0 || S.number_output != 0) + s += ":"; + s += " "; + s += C->print_to_string(false); + } + else { + if (S.number_input == 0 && S.number_output == 0) + s += " TRUE "; + } + } + + s += "}"; + } + + if (!anythingPrinted) { + R->setup_names(); + s = "{"; + s += R->print_variables_to_string(printSym); + if (R->number_input != 0 || R->number_output != 0) + s += " :"; + s += " FALSE }"; + if (newline) s += "\n"; + + delete R; + return s; + } + + if (newline) s += "\n"; + + delete R; + return s; +} + + +void print_var_addrs(std::string &s, Variable_ID v) { + if(pres_debug>=2) { + char ss[20]; + sprintf(ss, "(%p,%p)", v, v->remap); + s += ss; + } +} + +std::string Rel_Body::print_variables_to_string(bool printSym) { + std::string s=""; + + if (! Symbolic.empty()) { + if (printSym) s += " Sym=["; + for (Variable_ID_Iterator Sym_I(Symbolic); Sym_I; ) { + if (printSym) s += (*Sym_I)->name(); + print_var_addrs(s, *Sym_I); + Sym_I++; + if (printSym && Sym_I) s += ","; + } + if (printSym) s += "] "; + } + int i; + + if (number_input) { + s += "["; + for (i=1;i<=number_input;i++) { + s += input_vars[i]->name(); + print_var_addrs(s, input_vars[i]); + if(i < number_input) s += ","; + } + s += "] "; + } + + if (number_output) { + s += "-> ["; + for (i=1;i<=number_output;i++) { + s += output_vars[i]->name(); + print_var_addrs(s, output_vars[i]); + if(i < number_output) s += ","; + } + s += "] "; + } + + return s; +} + + +int F_Declaration::priority() { + return 3; +} + +int Formula::priority () { + return 0; +} + +int F_Or::priority() { + return 0; +} + +int F_And::priority() { + return 1; +} + +int Conjunct::priority() { + return 1; +} + +int F_Not::priority() { + return 2; +} + + +// +// print() functions +// +void Formula::print(FILE *output_file) { + if(myChildren.empty()) { + if(node_type()==Op_Relation || node_type()==Op_Or) + fprintf(output_file, "FALSE"); + else if(node_type()==Op_And) + fprintf(output_file, "TRUE"); + else { + assert(0); + } + } + + for(List_Iterator<Formula*> c(myChildren); c;) { + if (node_type() == Op_Exists || node_type() == Op_Forall + || (*c)->priority() < priority()) + fprintf(output_file, "( "); + (*c)->print(output_file); + if (node_type() == Op_Exists || node_type() == Op_Forall + || (*c)->priority() < priority()) + fprintf(output_file, " )"); + c++; + if(c.live()) + print_separator(output_file); + } +} + +std::string Rel_Body::print_formula_to_string() { + std::string s; + setup_names(); + for(DNF_Iterator DI(query_DNF()); DI.live();) { + + s += (*DI)->print_to_string(1); + DI.next(); + if (DI.live()) s += " && "; + if (pres_debug) fprintf(DebugFile,"Partial print to string: %s\n", + s.c_str()); + } + return s; +} + +void Rel_Body::print(FILE *output_file, bool printSym) { + if (this->is_null()) { + fprintf(output_file, "NULL Rel_Body\n"); + return; + } + + setup_names(); + + fprintf(output_file, "{"); + + std::string s = print_variables_to_string(printSym); + fprintf(output_file, "%s", s.c_str()); + + fprintf(output_file, ": "); + + if(simplified_DNF==NULL) { + Formula::print(output_file); + } + else { + assert(children().empty()); + simplified_DNF->print(output_file); + } + + fprintf(output_file, " }\n"); +} + +void Rel_Body::print() { + this->print(stdout); +} + +void F_Not::print(FILE *output_file) { + fprintf(output_file, " not "); + Formula::print(output_file); +} + +void F_And::print_separator(FILE *output_file) { + fprintf(output_file, " and "); +} + +void Conjunct::print(FILE *output_file) { + std::string s = print_to_string(true); + fprintf(output_file, "%s", s.c_str()); +} + +std::string Conjunct::print_to_string(int true_printed) { + std::string s=""; + + int num = myLocals.size(); + if(num) { + s += "exists ( "; + int i; + for (i = 1; i <= num; i++) { + Variable_ID v = myLocals[i]; + s += v->char_name(); + if(i < num) s += ","; + } + if (true_printed || !(is_true())) s += " : "; + } + + if(is_true()) { + s += true_printed ? "TRUE" : ""; + } + else { + if (is_unknown()) + s += "UNKNOWN"; + else { + s += problem->prettyPrintProblemToString(); + if (!exact) + s += " && UNKNOWN"; + } + } + + + if (num) s += ")"; + return s; +} + +void F_Or::print_separator(FILE *output_file) { + fprintf(output_file, " or "); +} + +void F_Declaration::print(FILE *output_file) { + std::string s=""; + for(Variable_ID_Iterator VI(myLocals); VI; ) { + s += (*VI)->name(); + VI++; + if(VI) s += ","; + } + fprintf(output_file, "( %s : ", s.c_str()); + Formula::print(output_file); + fprintf(output_file, ")"); +} + +void F_Forall::print(FILE *output_file) { + fprintf(output_file, "forall "); + F_Declaration::print(output_file); +} + +void F_Exists::print(FILE *output_file) { + fprintf(output_file, "exists "); + F_Declaration::print(output_file); +} + +void Formula::print_separator(FILE *) { + assert(0); // should never be called, it's only for derived classes +} + +// +// Setup names in formula. +// + +typedef Set<Global_Var_ID> g_set; +void Rel_Body::setup_names() { + int i; + + + if (use_ugly_names) return; + + if (pres_debug>=2) + fprintf(DebugFile,"Setting up names for 0x%p\n", this); + + for(i=1; i<=number_input; i++) { + input_vars[i]->base_name = In_Names[i]; + } + for(i=1; i<=number_output; i++) { + output_vars[i]->base_name = Out_Names[i]; + } + + g_set gbls; + + wildCardInstanceNumber = 0; + + for(i=1; i<= Symbolic.size(); i++) { + gbls.insert(Symbolic[i]->get_global_var()); + } + + foreach(g,Global_Var_ID,gbls, + g->instance = g->base_name()++); + + for(i=1; i<=number_input; i++) { + if (!input_vars[i]->base_name.null()) + input_vars[i]->instance = input_vars[i]->base_name++; + } + for(i=1; i<=number_output; i++) { + if (!output_vars[i]->base_name.null()) + output_vars[i]->instance = output_vars[i]->base_name++; + } + + if (simplified_DNF != NULL) // It is simplified + simplified_DNF->setup_names(); + else // not simplified + Formula::setup_names(); + + for(i=1; i<=number_output; i++) { + if (!output_vars[i]->base_name.null()) + output_vars[i]->base_name--; + } + for(i=1; i<=number_input; i++) { + if (!input_vars[i]->base_name.null()) + input_vars[i]->base_name--; + } + foreach(g,Global_Var_ID,gbls, g->base_name()--); +} + +void Formula::setup_names() { + if (pres_debug>=2) + fprintf(DebugFile,"Setting up names for formula 0x%p\n", this); + + for(List_Iterator<Formula*> c(myChildren); c; c++) + (*c)->setup_names(); +} + +void DNF::setup_names() { + if (pres_debug>=2) + fprintf(DebugFile,"Setting up names for DNF 0x%p\n", this); + + for(DNF_Iterator DI(this); DI.live(); DI.next()) + DI.curr()->setup_names(); +} + + +void F_Declaration::setup_names() { + if (pres_debug>=2) + fprintf(DebugFile,"Setting up names for Declaration 0x%p\n", this); + + // Allow re-use of wc names in other scopes + int savedWildCardInstanceNumber = wildCardInstanceNumber; + + for(Tuple_Iterator<Variable_ID> VI(myLocals); VI; VI++) { + Variable_ID v = *VI; + if (!v->base_name.null()) v->instance = v->base_name++; + else v->instance = wildCardInstanceNumber++; + } + + for(List_Iterator<Formula*> c(children()); c; c++) + (*c)->setup_names(); + + for(Tuple_Iterator<Variable_ID> VI2(myLocals); VI2; VI2++) { + Variable_ID v = *VI2; + if (!v->base_name.null()) v->base_name--; + } + wildCardInstanceNumber = savedWildCardInstanceNumber; +} + + +// +// Prefix_print functions. +// Print Formula Tree, used in debugging. +// +static int level = 0; + +void Formula::prefix_print(FILE *output_file, int debug) { + for(List_Iterator<Formula*> c(children()); c; c++) + (*c)->prefix_print(output_file, debug); + level--; +} + + +void Rel_Body::prefix_print() { + this->prefix_print(stdout, 1); +} + +void Rel_Body::prefix_print(FILE *output_file, int debug) { + int old_use_ugly_names = use_ugly_names; + use_ugly_names = 0; + + setup_names(); + + level = 0; + if(pres_debug>=2) fprintf(output_file, "(@%p)", this); + fprintf(output_file, is_set() ? "SET: " : "RELATION: "); + std::string s = print_variables_to_string(true); + fprintf(output_file, "%s\n", s.c_str()); + + if(simplified_DNF==NULL) { + Formula::prefix_print(output_file, debug); + } else { + assert(children().empty()); + simplified_DNF->prefix_print(output_file, debug, true); + } + fprintf(output_file, "\n"); + use_ugly_names = old_use_ugly_names; +} + + +void F_Forall::prefix_print(FILE *output_file, int debug) { + Formula::print_head(output_file); + fprintf(output_file, "forall ["); + F_Declaration::prefix_print(output_file, debug); + for (Variable_ID_Iterator VI(myLocals); VI; VI++) + assert((*VI)->kind() == Forall_Var); + +} + +void F_Exists::prefix_print(FILE *output_file, int debug) { + Formula::print_head(output_file); + if(pres_debug>=2) fprintf(output_file, "(@%p)", this); + fprintf(output_file, "exists ["); + F_Declaration::prefix_print(output_file, debug); + for (Variable_ID_Iterator VI(myLocals); VI; VI++) + assert((*VI)->kind() == Exists_Var); +} + +void F_Declaration::prefix_print(FILE *output_file, int debug) { + std::string s = ""; + for (Variable_ID_Iterator VI(myLocals); VI; ) { + s += (*VI)->name(); + print_var_addrs(s, *VI); + VI++; + if(VI) s += ","; + } + s += "]\n"; + fprintf(output_file, "%s", s.c_str()); + Formula::prefix_print(output_file, debug); +} + +void F_Or::prefix_print(FILE *output_file, int debug) { + Formula::print_head(output_file); + fprintf(output_file, "or\n"); + Formula::prefix_print(output_file, debug); +} + +void F_And::prefix_print(FILE *output_file, int debug) { + Formula::print_head(output_file); + fprintf(output_file, "and\n"); + Formula::prefix_print(output_file, debug); +} + +void F_Not::prefix_print(FILE *output_file, int debug) { + Formula::print_head(output_file); + fprintf(output_file, "not\n"); + Formula::prefix_print(output_file, debug); +} + +void Conjunct::prefix_print(FILE *output_file, int debug) { + static char dir_glyphs[] = { '-', '?', '+' }; + + if (debug) { + Formula::print_head(output_file); + if(pres_debug>=2) fprintf(output_file, "(@%p)", this); + fprintf(output_file, "%s CONJUNCT, ", exact ? "EXACT" : "INEXACT"); + if (simplified) fprintf(output_file, "simplified, "); + if (verified) fprintf(output_file, "verified, "); + if (possible_leading_0s != -1 && guaranteed_leading_0s != -1) + assert (guaranteed_leading_0s <= possible_leading_0s); + if (guaranteed_leading_0s != -1 && guaranteed_leading_0s == possible_leading_0s) + fprintf(output_file,"# leading 0's = %d,", possible_leading_0s); + else if (possible_leading_0s != -1 || guaranteed_leading_0s != -1) { + if (guaranteed_leading_0s != -1) + fprintf(output_file,"%d <= ",guaranteed_leading_0s); + fprintf(output_file,"#O's"); + if (possible_leading_0s != -1) + fprintf(output_file," <= %d",possible_leading_0s); + fprintf(output_file,", "); + } + if (dir_glyphs[leading_dir+1] != '?') + fprintf(output_file," first = %c, ", dir_glyphs[leading_dir+1]); + fprintf(output_file,"myLocals=["); + std::string s=""; + for (Variable_ID_Iterator VI(myLocals); VI; ) { + assert( (*VI)->kind() == Wildcard_Var); + s += (*VI)->name(); + print_var_addrs(s, *VI); + VI++; + if(VI) s += ","; + } + s += "] mappedVars=["; + for(Variable_ID_Iterator MVI(mappedVars); MVI; ) { + s += (*MVI)->name(); + print_var_addrs(s, *MVI); + MVI++; + if(MVI) s += ","; + } + fprintf(output_file, "%s]\n", s.c_str()); + } + else + level++; + + setOutputFile(output_file); + setPrintLevel(level+1); + problem->printProblem(debug); + setPrintLevel(0); + Formula::prefix_print(output_file, debug); +} + +void Formula::print_head(FILE *output_file) { + level++; + int i; + for(i=0; i<level; i++) { + fprintf(output_file, ". "); + } +} + +// +// Print DNF. +// +void DNF::print(FILE *out_file) { + DNF_Iterator p(this); + if (!p.live()) + fprintf(out_file, "FALSE"); + else + for(; p.live(); ) { + p.curr()->print(out_file); + p.next(); + if(p.live()) + fprintf(out_file, " or "); + } +} + +void DNF::prefix_print(FILE *out_file, int debug, bool parent_names_setup) { + wildCardInstanceNumber = 0; + Variable_ID_Tuple all_vars; + if(!use_ugly_names && !parent_names_setup) { + // We need to manually set up all of the input,output, and symbolic + // variables, since during simplification, a dnf's conjuncts may not + // be listed as part of a relation, or perhaps as part of different + // relations (?) (grr). + for(DNF_Iterator p0(this); p0.live(); p0.next()) { + for(Variable_Iterator vi((*p0)->mappedVars); vi; vi++) + if((*vi)->kind() != Wildcard_Var && all_vars.index(*vi) == 0) + all_vars.append(*vi); + (*p0)->setup_names(); + } + foreach(v,Variable_ID,all_vars, + if (!v->base_name.null()) v->instance = v->base_name++; + else v->instance = wildCardInstanceNumber++; + ); + } + + int i = 1; + level = 0; + for(DNF_Iterator p(this); p.live(); p.next(), i++) { + fprintf(out_file, "#%d ", i); + if(*p == NULL) + fprintf(out_file, "(NULL)\n"); + else + (*p)->prefix_print(out_file, debug); + } + + foreach(v,Variable_ID,all_vars,if (!v->base_name.null()) v->base_name--); + + fprintf(out_file, "\n"); +} + +std::string Constraint_Handle::print_to_string() const { + assert(0); + return "FOO"; +} + +std::string EQ_Handle::print_to_string() const { + relation()->setup_names(); + std::string s = c->print_EQ_to_string(e); + return s; +} + +std::string GEQ_Handle::print_to_string() const { + relation()->setup_names(); + std::string s = c->print_GEQ_to_string(e); + return s; +} + +std::string Constraint_Handle::print_term_to_string() const { + assert(0); + return "FOO"; +} + +std::string EQ_Handle::print_term_to_string() const { + relation()->setup_names(); + std::string s = c->print_EQ_term_to_string(e); + return s; +} + +std::string GEQ_Handle::print_term_to_string() const { + relation()->setup_names(); + std::string s = c->print_GEQ_term_to_string(e); + return s; +} + +} diff --git a/omegalib/omega/src/pres_quant.cc b/omegalib/omega/src/pres_quant.cc new file mode 100644 index 0000000..5483bad --- /dev/null +++ b/omegalib/omega/src/pres_quant.cc @@ -0,0 +1,95 @@ +#include <omega/pres_quant.h> +#include <omega/omega_i.h> + +namespace omega { + +F_Forall::F_Forall(Formula *p, Rel_Body *r): F_Declaration(p,r) { +} + +F_Exists::F_Exists(Formula *p, Rel_Body *r): F_Declaration(p,r) { +} + +F_Exists::F_Exists(Formula *p, Rel_Body *r, Variable_ID_Tuple &S): F_Declaration(p,r,S) { +} + + +Formula *F_Forall::copy(Formula *parent, Rel_Body *reln) { + F_Forall *f = new F_Forall(parent, reln); + copy_var_decls(f->myLocals, myLocals); + for(List_Iterator<Formula*> c(children()); c; c++) + f->children().append((*c)->copy(f,reln)); + reset_remap_field(myLocals); + return f; +} + +Formula *F_Exists::copy(Formula *parent, Rel_Body *reln) { + F_Exists *f = new F_Exists(parent, reln); + copy_var_decls(f->myLocals, myLocals); + for(List_Iterator<Formula*> c(children()); c; c++) + f->children().append((*c)->copy(f,reln)); + reset_remap_field(myLocals); + return f; +} + +Variable_ID F_Forall::declare(Const_String s) { + return do_declare(s, Forall_Var); +} + +Variable_ID F_Forall::declare() { + return do_declare(Const_String(), Forall_Var); +} + +Variable_ID F_Forall::declare(Variable_ID v) { + return do_declare(v->base_name, Forall_Var); +} + + +Variable_ID F_Exists::declare(Const_String s) { + return do_declare(s, Exists_Var); +} + +Variable_ID F_Exists::declare() { + return do_declare(Const_String(), Exists_Var); +} + +Variable_ID F_Exists::declare(Variable_ID v) { + return do_declare(v->base_name, Exists_Var); +} + +Conjunct *F_Forall::find_available_conjunct() { + return 0; +} + +Conjunct *F_Exists::find_available_conjunct() { + assert(children().length() == 1 || children().length() == 0); + if (children().length() == 0) + return 0; + else + return children().front()->find_available_conjunct(); +} + +F_Exists *Formula::add_exists() { + assert_not_finalized(); + assert(can_add_child()); + F_Exists *f = new F_Exists(this, myRelation); + myChildren.append(f); + return f; +} + +F_Exists *Formula::add_exists(Variable_ID_Tuple &S) { + assert_not_finalized(); + assert(can_add_child()); + F_Exists *f = new F_Exists(this, myRelation, S); + myChildren.append(f); + return f; +} + +F_Forall *Formula::add_forall() { + assert_not_finalized(); + assert(can_add_child()); + F_Forall *f = new F_Forall(this, myRelation); + myChildren.append(f); + return f; +} + +} // namespace diff --git a/omegalib/omega/src/pres_rear.cc b/omegalib/omega/src/pres_rear.cc new file mode 100644 index 0000000..508959d --- /dev/null +++ b/omegalib/omega/src/pres_rear.cc @@ -0,0 +1,131 @@ +#include <omega/pres_tree.h> +#include <omega/pres_conj.h> +#include <omega/Relation.h> +#include <omega/omega_i.h> + +namespace omega { + +///////////////////////// +// // +// Rearrange functions // +// // +///////////////////////// + +// +// Rules: +// ~ (f1 | f2 | ... | fn) = ~f1 & ~f2 & ... & fn +// ~ ~ f = f +// Forall v: f = ~ (Exists v: ~ f) +// Exists v: (f1 | ... | fn) = (Exists v: f1) | ... | (Exists v: fn) +// + +void Rel_Body::rearrange() { + assert(children().length()==1); + + skip_finalization_check++; + formula()->rearrange(); + skip_finalization_check--; + + if(pres_debug) { + fprintf(DebugFile, "\n=== Rearranged TREE ===\n"); + prefix_print(DebugFile); + } +} + +void Formula::rearrange() { + // copy list of children, as they may be removed as we work + List<Formula*> kiddies = myChildren; + + for(List_Iterator<Formula*> c(kiddies); c; c++) + (*c)->rearrange(); +} + +// +// Push nots down the tree until quantifier or conjunct, rearrange kids +// +void F_Not::rearrange() { + Formula *child = children().front(); + Formula *new_child, *f; + + switch(child->node_type()) { + case Op_Or: + parent().remove_child(this); + new_child = parent().add_and(); + while(!child->children().empty()) { + f = child->children().remove_front(); + F_Not *new_not = new_child->add_not(); + new_not->add_child(f); + } + delete this; + break; +//case Op_And: +// parent().remove_child(this); +// new_child = parent().add_or(); +// while(!child->myChildren.empty()) { +// f = child->myChildren.remove_front(); +// F_Not *new_not = new_child->add_not(); +// new_not->add_child(f); +// } +// delete this; +// break; + case Op_Not: + parent().remove_child(this); + f = child->children().remove_front(); + parent().add_child(f); + delete this; + f->rearrange(); + return; + default: + new_child = child; + break; + } + + new_child->rearrange(); +} + +// +// Convert a universal quantifier to "not exists not". +// Forall v: f = ~ (Exists v: ~ f) +// +void F_Forall::rearrange() { + Formula &p = parent(); + p.remove_child(this); + + F_Not *topnot = p.add_not(); + F_Exists *exist = topnot->add_exists(); + for (Variable_ID_Iterator VI(myLocals); VI; VI++) + (*VI)->set_kind(Exists_Var); + exist->myLocals.join(myLocals); + + F_Not *botnot = exist->add_not(); + Formula *f = children().remove_front(); + botnot->add_child(f); + + delete this; + + botnot->rearrange(); +} + +// +// Exists v: (f1 | ... | fn) = (Exists v: f1) | ... | (Exists v: fn) +// +void F_Exists::rearrange() { + Formula* child = children().front(); + switch(child->node_type()) { + case Op_Or: + case Op_Conjunct: + case Op_Exists: + child->push_exists(myLocals); + parent().remove_child(this); + parent().add_child(child); + children().remove_front(); + delete this; + break; + default: + break; + } + + child->rearrange(); +} + +} // namespace diff --git a/omegalib/omega/src/pres_subs.cc b/omegalib/omega/src/pres_subs.cc new file mode 100644 index 0000000..9854b09 --- /dev/null +++ b/omegalib/omega/src/pres_subs.cc @@ -0,0 +1,131 @@ +#include <omega/pres_subs.h> + +namespace omega { + +Substitutions::Substitutions(Relation &input_R, Conjunct *input_c) { + int i; + r = new Relation(input_R,input_c); + c = r->single_conjunct(); + c->reorder_for_print(); + c->ordered_elimination(r->global_decls()->length()); + int num_subs = c->problem->nSUBs; + subs = new eqn[num_subs]; + for(i = 0; i < num_subs; i++) + subs[i] = SUBs[i]; + subbed_vars.reallocate(num_subs); + /* Go through and categorize variables as: + 1) substituted, 2) not substituted, 3) wildcard + Safevars number of variables were not able to be substituted. + nVars number of total variables, including wildcards. + nSUBs is the number of substitutions. + nSUBs + nVars == the number of variables that went in. + Then reset var and forwardingAddress arrays in the problem, + so that they will correctly refer to the reconstructed + mappedVars. */ + Variable_ID_Tuple unsubbed_vars(c->problem->safeVars); + for(i = 1; i <= c->mappedVars.size(); i++) + if(c->mappedVars[i]->kind() != Wildcard_Var) { + int addr = c->problem->forwardingAddress[i]; + assert(addr == c->find_column(c->mappedVars[i]) && addr != 0); + if(addr < 0) { + assert(-addr <= subbed_vars.size()); + subbed_vars[-addr] = c->mappedVars[i]; + } + else { + assert(addr <= unsubbed_vars.size()); + unsubbed_vars[addr] = c->mappedVars[i]; + } + } + else { + // Here we don't redeclare wildcards, just re-use them. + unsubbed_vars.append(c->mappedVars[i]); + } + assert(unsubbed_vars.size() + subbed_vars.size() == c->mappedVars.size()); + c->mappedVars = unsubbed_vars; /* These are the variables that remain */ + + for(int col = 1; col <= c->problem->nVars; col++) { + c->problem->var[col] = col; + c->problem->forwardingAddress[col] = col; + } +} + +Substitutions::~Substitutions() { + delete [] subs; + delete r; +} + +bool Substitutions::substituted(Variable_ID v) { + return (subbed_vars.index(v) > 0); +} + +Sub_Handle Substitutions::get_sub(Variable_ID v) { + assert(substituted(v) && "No substitution for variable"); + return Sub_Handle(this,subbed_vars.index(v)-1,v); +} + +bool Substitutions::sub_involves(Variable_ID v, Var_Kind kind) { + assert(substituted(v)); + for(Constr_Vars_Iter i = get_sub(v); i; i++) + if ((*i).var->kind() == kind) + return true; + return false; +} + + +//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + + +int Sub_Iterator::live() const { + return current <= last; +} + +void Sub_Iterator::operator++() { this->operator++(0); } + +void Sub_Iterator::operator++(int) { + current++; +} + +Sub_Handle Sub_Iterator::operator*() { + assert(s && current <= last && "Sub_Iterator::operator*: bad call"); + return Sub_Handle(s,current,s->subbed_vars[current+1]); +} + +Sub_Handle Sub_Iterator::operator*() const { + assert(s && current <= last && "Sub_Iterator::operator*: bad call"); + return Sub_Handle(s,current,s->subbed_vars[current+1]); +} + + +//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + + + +Sub_Handle::Sub_Handle(Substitutions *_s, int _e, Variable_ID _v) : +Constraint_Handle(_s->c,&(_s->subs),_e), v(_v) {} + +std::string Sub_Handle::print_to_string() const { + relation()->setup_names(); + return v->name() + " = " + this->print_term_to_string(); +} + +std::string Sub_Handle::print_term_to_string() const { + /* The horrible truth is that print_term_to_string is a member + function of Conjunct, (and then Problem below it) but uses + nothing from there but the names, so you can pass it a pointer to + an equation that isn't even part of the conjunct. */ + relation()->setup_names(); + return c->print_term_to_string(&((*eqns)[e])); +} + + +/* + String Sub_Handle::print_to_string() const { + return c->problem->print_EQ_to_string(&((*eqns)[e])); + } + + String Sub_Handle::print_term_to_string() const { + return c->print_term_to_string(&(*eqns[e])); + } +*/ + +} // namespace diff --git a/omegalib/omega/src/pres_var.cc b/omegalib/omega/src/pres_var.cc new file mode 100644 index 0000000..0ec406f --- /dev/null +++ b/omegalib/omega/src/pres_var.cc @@ -0,0 +1,459 @@ +#include <omega/pres_var.h> +#include <omega/pres_tree.h> +#include <omega/pres_conj.h> +#include <omega/omega_i.h> + +namespace omega { + +int wildCardInstanceNumber; + +const int Global_Input_Output_Tuple::initial_allocation = 10; + +// Declare named Variable +Var_Decl::Var_Decl(Const_String name, Var_Kind vkind, int pos): + base_name(name), + instance(999), + remap(this), + var_kind(vkind), + position(pos), + global_var(NULL), + of((Argument_Tuple) 0) { + assert(((vkind==Input_Var || vkind==Output_Var) && pos>0) || pos==0); +} + +// Declare unnamed variable +Var_Decl::Var_Decl(Var_Kind vkind, int pos): + instance(999), + remap(this), + var_kind(vkind), + position(pos), + global_var(NULL), + of((Argument_Tuple) 0) { + assert(((vkind==Input_Var || vkind==Output_Var) && pos>0) || pos==0); +} + +// Copy variable declaration +Var_Decl::Var_Decl(Variable_ID v): + base_name(v->base_name), + instance(v->instance), + remap(this), + var_kind(v->var_kind), + position(v->position), + global_var(v->global_var), + of(v->of) { +} + +Var_Decl::Var_Decl(Const_String name, Global_Var_ID v): + base_name(name), + instance(999), + remap(this), + var_kind(Global_Var), + position(0), + global_var(v), + of((Argument_Tuple) 0) { + assert(v->arity() == 0); +} + +Var_Decl::Var_Decl(Const_String name, Global_Var_ID v, Argument_Tuple function_of): + base_name(name), + instance(999), + remap(this), + var_kind(Global_Var), + position(0), + global_var(v), + of(function_of) { +} + +int Var_Decl::get_position() { + assert((var_kind == Input_Var || var_kind == Output_Var) && "Var_Decl::get_position: bad var_kind"); + return(position); +} + +Global_Var_ID Var_Decl::get_global_var() { + assert(var_kind == Global_Var && "Var_Decl::get_global_var: bad var_kind"); + return(global_var); +} + +Argument_Tuple Var_Decl::function_of() { + assert(var_kind == Global_Var); + return(of); +} + + +Omega_Var *Global_Var_Decl::really_omega_var() { + assert(0); + return(NULL); +} + +Coef_Var_Decl *Global_Var_Decl::really_coef_var() { + assert(0); + return(NULL); +} + +// +// Variable name. +// + +const int N_greek_letters = 19; + +char greek_letters[19][10] = { + "alpha" , "beta" , "gamma" , "delta" , "tau" , "sigma" , "chi" , + "omega" , "pi" , "ni" , "Alpha" , "Beta" , "Gamma" , "Delta" , + "Tau" , "Sigma" , "Chi" , "Omega" , "Pi" +}; + +const int numBuffers = 50; +const int bufferSize = 90; +char nameBuffers[numBuffers][bufferSize]; +int nextBuffer = 0; + +int use_ugly_names = 0; + +const char *Var_Decl::char_name() { + char *s = nameBuffers[nextBuffer++]; + char *start = s; + if (nextBuffer >= numBuffers) nextBuffer = 0; + int primes; + + if (use_ugly_names) + primes = 0; + else + primes = instance; + + switch(var_kind) { + case Input_Var: + if (!use_ugly_names && !base_name.null()) + sprintf(s,"%s",(const char *)base_name); + else { + primes = 0; + sprintf(s,"In_%d",position); + } + break; + case Output_Var: + if (!use_ugly_names && !base_name.null()) + sprintf(s,"%s",(const char *)base_name); + else { + primes = 0; + sprintf(s,"Out_%d",position); + } + break; + case Global_Var: + assert(!base_name.null()); + if (use_ugly_names) + sprintf(s,"%s@%p",(const char *)base_name, this); + else { + sprintf(s,"%s",(const char *)base_name); + primes = get_global_var()->instance; + } + break; + default: + if (use_ugly_names) { + if (!base_name.null()) + sprintf(s,"%s@%p",(const char *)base_name, this); + else + sprintf(s,"?@%p", this); + } + else if (!base_name.null()) sprintf(s,"%s",(const char *)base_name); + else { + assert(instance < 999); + sprintf(s,"%s", + greek_letters[instance % N_greek_letters]); + primes = instance/N_greek_letters; + } + break; + } + + while (*s) s++; + int i; + assert(primes < 999); + for(i=1; i<= primes; i++) *s++ = '\''; + *s = '\0'; + if (var_kind == Global_Var) { + int a = get_global_var()->arity(); + if (a) { + if (use_ugly_names) { + static const char *arg_names[4] = { "???", "In", "Out", "In == Out" }; + sprintf(s, "(%s[1#%d])", arg_names[function_of()], a); + } + else { + int f_of = function_of(); + assert(f_of == Input_Tuple || f_of == Output_Tuple); + *s++ = '('; + for(i = 1;i<=a;i++) { + if (f_of == Input_Tuple) + sprintf(s,"%s",(const char *)input_vars[i]->char_name()); + else + sprintf(s,"%s",(const char *)output_vars[i]->char_name()); + while (*s) s++; + if (i<a) *s++ = ','; + } + *s++ = ')'; + *s++ = 0; + } + } + } + + assert(s < start+bufferSize); + return start; +} + +std::string Var_Decl::name() { + return char_name(); +} + +// +// Copy variable declarations. +// +void copy_var_decls(Variable_ID_Tuple &new_vl, Variable_ID_Tuple &vl) { + if (new_vl.size() < vl.size()) { + new_vl.reallocate(vl.size()); + } + for(int p=1; p<=vl.size(); p++) { + Variable_ID v = vl[p]; + if(v->kind()==Global_Var) { + new_vl[p] = v; + v->remap = v; + } + else { + new_vl[p] = new Var_Decl(vl[p]); + v->remap = new_vl[p]; + } + } +} + +// +// Name a variable. +// +void Var_Decl::name_variable(char *newname) { + base_name = newname; +} + + +static Const_String coef_var_name(int i, int v) { + char s[100]; + sprintf(s, "c_%d_%d", i, v); + return Const_String(s); +} + + +// +// Global variables stuff. +// +Global_Var_Decl::Global_Var_Decl(Const_String baseName): + loc_rep1(baseName, this, Input_Tuple), + loc_rep2(baseName, this, Output_Tuple) { +} + +Global_Kind Global_Var_Decl::kind() const { + assert(0); + return Coef_Var; +} + +Coef_Var_Decl::Coef_Var_Decl(int id, int var): + Global_Var_Decl(coef_var_name(id, var)) { + i = id; + v = var; +} + +Coef_Var_Decl *Coef_Var_Decl::really_coef_var() { + return this; +} + +int Coef_Var_Decl::stmt() const { + return i; +} + +int Coef_Var_Decl::var() const { + return v; +} + +Global_Kind Coef_Var_Decl::kind() const { + return Coef_Var; +} + +Free_Var_Decl::Free_Var_Decl(Const_String name): + Global_Var_Decl(name), _arity(0) { +} + +Free_Var_Decl::Free_Var_Decl(Const_String name, int arity): + Global_Var_Decl(name), _arity(arity) { +} + +int Free_Var_Decl::arity() const { + return _arity; +} + +Global_Kind Free_Var_Decl::kind() const { + return Free_Var; +} + + +// +// Delete variable from variable list. Does not preserve order +// +bool rm_variable(Variable_ID_Tuple &vl, Variable_ID v) { + int n = vl.size(); + int i; + for(i = 1; i<n; i++) if (vl[i] == v) break; + if (i>n) return 0; + vl[i] = vl[n]; + vl.delete_last(); + return 1; +} + + +// +// Destroy variable declarations. +// +void free_var_decls(Variable_ID_Tuple &c) { + for(Variable_ID_Iterator p = c; p; p++) { + Variable_ID v = *p; + if(v->kind()!=Global_Var) { + delete v; + } + } +} + + +Variable_ID input_var(int nth) { + assert(1 <= nth && nth <= input_vars.size()); + return input_vars[nth]; +} + +Variable_ID output_var(int nth) { + assert(1<= nth && nth <= output_vars.size()); + return output_vars[nth]; +} + +Variable_ID set_var(int nth) { + assert(1 <= nth && set_vars.size()); + return input_vars[nth]; +} + + +// +// Remap mappedVars in all conjuncts of formula. +// Uses the remap field of the Var_Decl. +// +void Formula::remap() { + for(List_Iterator<Formula*> c(children()); c; c++) + (*c)->remap(); +} + +void Conjunct::remap() { + for(Variable_Iterator VI(mappedVars); VI; VI++) { + Variable_ID v = *VI; + *VI = v->remap; + } + cols_ordered = false; +} + +// Function to reset the remap field of a Variable_ID +void reset_remap_field(Variable_ID v) { + v->remap = v; +} + +// Function to reset the remap fields of a Variable_ID_Seq +void reset_remap_field(Sequence<Variable_ID> &T) { + for(Any_Iterator<Variable_ID> VI(T.any_iterator()); VI; VI++) { + Variable_ID v = *VI; + v->remap = v; + } +} + +// Function to reset the remap fields of a Variable_ID_Tuple, +// more efficiently +void reset_remap_field(Variable_ID_Tuple &T) { + for(Variable_Iterator VI(T); VI; VI++) { + Variable_ID v = *VI; + v->remap = v; + } +} + +void reset_remap_field(Sequence<Variable_ID> &T, int var_no) { + int i=1; + for(Any_Iterator<Variable_ID> VI(T.any_iterator()); i <= var_no && VI; VI++) { + Variable_ID v = *VI; + v->remap = v; + i++; + } +} + +void reset_remap_field(Variable_ID_Tuple &T, int var_no) { + int i=1; + for(Variable_Iterator VI(T); i <= var_no && VI; VI++) { + Variable_ID v = *VI; + v->remap = v; + i++; + } +} + + +// Global input and output variable tuples. +// These Tuples must be initialized as more in/out vars are needed. +//Variable_ID_Tuple input_vars(0); +//Variable_ID_Tuple output_vars(0); +//Variable_ID_Tuple& set_vars = input_vars; +Global_Input_Output_Tuple input_vars(Input_Var); +Global_Input_Output_Tuple output_vars(Output_Var); +Global_Input_Output_Tuple &set_vars = input_vars; + +//////////////////////////////////////// +// // +// Variable and Declaration functions // +// // +//////////////////////////////////////// + +// +// Constructors and properties. +// + + +// Allocate ten variables initially. Most applications won't require more. +Global_Input_Output_Tuple::Global_Input_Output_Tuple(Var_Kind in_my_kind, int init): + my_kind(in_my_kind) { + for (int i=1; i<=(init == -1? initial_allocation: max(0,init)); i++) + this->append(new Var_Decl(Const_String(), my_kind, i)); +} + +Global_Input_Output_Tuple::~Global_Input_Output_Tuple() { + for (int i=1; i<=size(); i++) + delete data[i-1]; +} + +Variable_ID & Global_Input_Output_Tuple::operator[](int index) { + assert(index > 0); + while(size() < index) + this->append(new Var_Decl(Const_String(), my_kind, size()+1)); + return data[index-1]; +} + +const Variable_ID & Global_Input_Output_Tuple::operator[](int index) const { + assert(index > 0); + Global_Input_Output_Tuple *unconst_this = (Global_Input_Output_Tuple *) this; + while(size() < index) + unconst_this->append(new Var_Decl(Const_String(), my_kind, size()+1)); + return data[index-1]; +} + +Variable_ID Var_Decl::UF_owner() { + Variable_ID v = this; + while (v->remap != v) v = v->remap; + return v; +} + +void Var_Decl::UF_union(Variable_ID b) { + Variable_ID a = this; + while (a->remap != a) { + Variable_ID tmp = a->remap; + a->remap = tmp->remap; + a = tmp; + } + while (b->remap != a) { + Variable_ID tmp = b->remap; + b->remap = a; + b = tmp; + } +} + +} // namespace diff --git a/omegalib/omega/src/reach.cc b/omegalib/omega/src/reach.cc new file mode 100644 index 0000000..bde785c --- /dev/null +++ b/omegalib/omega/src/reach.cc @@ -0,0 +1,211 @@ +#include <omega.h> +#include <omega/Relations.h> +#include <basic/Dynamic_Array.h> +#include <omega/reach.h> + +namespace omega { + +typedef Dynamic_Array1<Relation> Rel_Array1; +typedef Dynamic_Array2<Relation> Rel_Array2; + +// This is from parallelism.c, modified + +static void closure_rel(Rel_Array2 &trans, int i, int k, int j) { + Relation tik; + + if (trans[k][k].is_upper_bound_satisfiable()) { + Relation tkk = TransitiveClosure(copy(trans[k][k])); + tkk.simplify(2,4); + tik = Composition(tkk, copy(trans[i][k])); + tik.simplify(2,4); + tik = Union(copy(trans[i][k]), tik); + tik.simplify(2,4); + } + else { + tik = trans[i][k]; + } + Relation fresh; + Relation tkj = trans[k][j]; + fresh = Composition(tkj, tik); + fresh.simplify(2,4); + trans[i][j] = Union(trans[i][j], fresh); + trans[i][j].simplify(2,4); + +#if 0 + fprintf(DebugFile, "%d -> %d -> %d\n", i, k, j); + trans[i][j].print_with_subs(DebugFile); +#endif +} + + +static void close_rels(Rel_Array2 &trans,int n_nodes) { + for (int k=1; k<=n_nodes; k++) + for (int i=1; i<=n_nodes; i++) + if (trans[i][k].is_upper_bound_satisfiable()) + for (int j=1; j<=n_nodes; j++) + if (trans[k][j].is_upper_bound_satisfiable()) + closure_rel(trans, i, k, j); +} + + +void dump_rels(Rel_Array2 &a, reachable_information *reachable_info) { + int i,j; + int n_nodes = reachable_info->node_names.size(); + for(i = 1; i <= n_nodes; i++) + for(j = 1; j <= n_nodes; j++) { + fprintf(stderr,"t[%s][%s] = ", + (reachable_info->node_names[i]).c_str(), + (reachable_info->node_names[j]).c_str()); + a[i][j].print_with_subs(stderr); + } +} + + +void dump_sets(Rel_Array1 &a, reachable_information *reachable_info) { + int i; + int n_nodes = reachable_info->node_names.size(); + for(i = 1; i <= n_nodes; i++) { + fprintf(stderr,"r[%s] = ", (reachable_info->node_names[i]).c_str()); + a[i].print_with_subs(stderr); + } +} + + + +Rel_Array1 *Reachable_Nodes(reachable_information *reachable_info) { + Tuple<std::string> &node_names = reachable_info->node_names; + Tuple<int> &arity = reachable_info->node_arity; + Rel_Array2 &transitions = reachable_info->transitions; + Rel_Array1 &start_nodes = reachable_info->start_nodes; + + int n_nodes = node_names.size(),i,j; + +#define DUMP_INITIAL 1 +#define DUMP_CLOSED 1 + + if(DUMP_INITIAL && relation_debug){ + fprintf(stderr,"Initially:\n"); + dump_rels(transitions, reachable_info); + } + + close_rels(transitions,n_nodes); + + if(DUMP_CLOSED && relation_debug) { + fprintf(stderr,"Closed:\n"); + dump_rels(transitions, reachable_info); + } + + Rel_Array1 *finalp = + new Rel_Array1("node"); + Rel_Array1 &final = *finalp; + final.resize(n_nodes+1); + for (i=1; i<=n_nodes; i++) + final[i] = Relation::False(arity[i]); + + for(i = 1; i <= n_nodes; i++) + for(j = 1; j <= n_nodes; j++) + if(start_nodes[i].is_upper_bound_satisfiable()) + final[j] = Union(final[j], + Composition(copy(transitions[i][j]), + copy(start_nodes[i]))); + return finalp; +} + +static void compute_initially_reachable(Rel_Array1 &r, + Rel_Array1 &start_nodes, + Rel_Array2 &, + Rel_Array2 &closed, + Rel_Array1 &end_nodes, + int n_nodes, Tuple<int> &arity){ + for(int n = 1; n <= n_nodes; n++) + r[n] = Relation::False(arity[n]); + + for(int i = 1; i <= n_nodes; i++) + for(int j = 1; j <= n_nodes; j++) + r[i] = Union(r[i], + Range(Restrict_Domain( + Restrict_Range(copy(closed[j][i]), + copy(end_nodes[i])), + copy(start_nodes[j])))); +} + + +static bool iterate(Rel_Array1 &r, Rel_Array2 &, Rel_Array2 &closed, + Rel_Array1 &, int n_nodes) { + bool changed; + + changed = false; + for(int j = 1; j <= n_nodes; j++) { + for(int i = 1; i <= n_nodes; i++) { + /* look for additional steps from interesting states */ + Relation new_rj = Range(Restrict_Domain(copy(closed[i][j]), + copy(r[i]))); + if(!Must_Be_Subset(copy(new_rj),copy(r[j]))) { + r[j] = Union(r[j],new_rj); + r[j].simplify(2,2); + changed = true; + } + } + } + return changed; +} + + + + +Rel_Array1 *I_Reachable_Nodes(reachable_information *reachable_info) { + bool changed; + + Tuple<std::string> &node_names = reachable_info->node_names; + int n_nodes = node_names.size(); + Tuple<int> &arity = reachable_info->node_arity; + Rel_Array2 &transitions = reachable_info->transitions; + Rel_Array1 &start_nodes = reachable_info->start_nodes; + Rel_Array2 closed("node number","node number"); + closed.resize(n_nodes+1,n_nodes+1); // abuse of dynamic arrays + Rel_Array1 *rp = new Rel_Array1("node number"); + Rel_Array1 &r = *rp; + r.resize(n_nodes+1); // abuse of dynamic arrays + + int i,j; + + Rel_Array1 end_nodes("Hi!"); + end_nodes.resize(n_nodes+1); // for future use + for(int n = 1; n <= n_nodes; n++) end_nodes[n] = Relation::True(arity[n]); + + for(j = 1; j <= n_nodes; j++) { + closed[j][j] = TransitiveClosure(copy(transitions[j][j])); + for(i = 1; i <= n_nodes; i++) + if (i != j) + closed[i][j] = transitions[i][j]; + } + + compute_initially_reachable(r,start_nodes,transitions,closed,end_nodes, + n_nodes,arity); + +#define DUMP_INITIAL 1 +#define DUMP_CLOSED 1 + + if(DUMP_INITIAL && relation_debug > 1) { + fprintf(stderr,"Closed:\n"); + dump_rels(closed, reachable_info); + } + if(DUMP_INITIAL && relation_debug) { + fprintf(stderr,"start nodes:\n"); + dump_sets(start_nodes, reachable_info); + fprintf(stderr,"Initially reachable:\n"); + dump_sets(r, reachable_info); + } + + changed = true; + int iterations = 0, max_iterations = 1000; + while(changed && iterations < max_iterations) { + changed = iterate(r,transitions,closed,start_nodes,n_nodes); + iterations++; + } + if(relation_debug) + fprintf(stdout,"[Iterations to convergence: %d]\n",iterations); + return rp; +} + +} // namespace |