#ifndef BBOXSET1_HH #define BBOXSET1_HH #include #include #include #include #include #include "bbox.hh" #include "defs.hh" #include "vect.hh" using namespace std; // Choose the implementation of bboxset by #defining exactly one of // these #undef BBOXSET_SET // outdated #undef BBOXSET_LIST // well tested #define BBOXSET_VECTOR // brand new namespace bboxset1 { // Forward declaration template class bboxset; // template // bboxset operator+ (const bbox& b1, const bbox& b2); // template // bboxset operator+ (const bbox& b, const bboxset& s); // template // bboxset operator- (const bbox& b1, const bbox& b2); // template // bboxset operator- (const bbox& b, const bboxset& s); // Input template istream& operator>> (istream& is, bboxset& s); // Output template ostream& operator<< (ostream& os, const bboxset& s); // Bounding box set class template class bboxset { // Cost annotations depend on the number of bset elements n, and // assume that normalization is skipped. struct skip_normalize_t { bboxset& s; bool const saved_skip_normalize; skip_normalize_t(bboxset& s_) : s(s_), saved_skip_normalize(s.skip_normalize) { s.skip_normalize = true; } ~skip_normalize_t() { s.skip_normalize = saved_skip_normalize; s.normalize(); } }; // Types typedef bbox box; #ifdef BBOXSET_SET //S typedef set bset; #endif #ifdef BBOXSET_LIST typedef list bset; #endif #ifdef BBOXSET_VECTOR typedef vector bset; #endif // Fields bset bs; // Invariant: // All bboxes have the same stride. // No bbox is empty. // The bboxes don't overlap. bool skip_normalize; public: // Constructors bboxset (); // cost: O(1) bboxset (const box& b); // cost: O(1) bboxset (const bboxset& s); // cost: O(n) bboxset (const list& lb); bboxset (const set& sb); bboxset (const vector& vb); bboxset (const vector >& vlb); template bboxset (const vector& vb, const bbox U::* const v); template bboxset (const vector& vb, const bboxset U::* const v); static bboxset poison (); // Invariant bool invariant () const CCTK_MEMBER_ATTRIBUTE_PURE; private: // Normalisation void normalize (); public: // Accessors bool empty () const { return bs.empty(); } // cost: O(1) // T size () const; typedef typename box::size_type size_type; size_type size () const CCTK_MEMBER_ATTRIBUTE_PURE; // cost: O(n) int setsize () const { return bs.size(); } // cost: O(1) // Find out whether the bbox contains the point x bool contains (const vect& x) const CCTK_MEMBER_ATTRIBUTE_PURE; // cost: O(n) // Find out whether this bboxset intersects the bbox b bool intersects (const box& b) const CCTK_MEMBER_ATTRIBUTE_PURE; // cost: O(n) // Add (bboxes that don't overlap) bboxset& operator+= (const box& b); // cost: O(1) bboxset& operator+= (const bboxset& s); // cost: O(n) bboxset& add_transfer (bboxset& s); // cost: O(1) bboxset operator+ (const box& b) const CCTK_MEMBER_ATTRIBUTE_PURE; // cost: O(n) bboxset operator+ (const bboxset& s) const CCTK_MEMBER_ATTRIBUTE_PURE; // cost: O(n) // Union bboxset& operator|= (const box& b); // cost: O(n) bboxset& operator|= (const bboxset& s); // cost: O(n^2) bboxset operator| (const box& b) const CCTK_MEMBER_ATTRIBUTE_PURE; // cost: O(n) bboxset operator| (const bboxset& s) const CCTK_MEMBER_ATTRIBUTE_PURE; // cost: O(n^2) // Intersection bboxset operator& (const box& b) const CCTK_MEMBER_ATTRIBUTE_PURE; // cost: O(n) bboxset operator& (const bboxset& s) const CCTK_MEMBER_ATTRIBUTE_PURE; // cost: O(n) bboxset& operator&= (const box& b); // cost: O(n) bboxset& operator&= (const bboxset& s); // cost: O(n) // Difference // friend bboxset operator- (const box& b1, const box& b2); static bboxset minus (const box& b1, const box& b2) CCTK_ATTRIBUTE_PURE; // cost: O(1) bboxset operator- (const box& b) const CCTK_MEMBER_ATTRIBUTE_PURE; // cost: O(n) // cost: O(n) bboxset& operator-= (const box& b) { *this = *this - b; assert (invariant()); return *this; } bboxset& operator-= (const bboxset& s); // cost: O(n^2) bboxset operator- (const bboxset& s) const CCTK_MEMBER_ATTRIBUTE_PURE; // cost: O(n^2) // friend bboxset operator- (const box& b, const bboxset& s); static bboxset minus (const box& b, const bboxset& s) CCTK_ATTRIBUTE_PURE; // cost: O(n^2) /** Find a bbox containing the whole set. */ box container () const CCTK_MEMBER_ATTRIBUTE_PURE; // cost: O(n) /** Find the pseudo-inverse. */ bboxset pseudo_inverse (const int n) const CCTK_MEMBER_ATTRIBUTE_PURE; /** Expand (enlarge) the bboxset by multiples of the stride. */ bboxset expand (const vect& lo, const vect& hi) const CCTK_MEMBER_ATTRIBUTE_PURE; bboxset expand (const vect,2>& lohi) const { return expand (lohi[0], lohi[1]); } /** Shift the bboxset by multiples of the stride. */ bboxset shift (const vect& v) const { return expand (-v, v); } /** Expand (enlarge) the bboxset by multiples of a fraction of the stride. */ // cost: O(n^2) in general, but only O(n) for shifting bboxset expand (const vect& lo, const vect& hi, const vect& denom) const CCTK_MEMBER_ATTRIBUTE_PURE; // cost: O(n^2) in general, but only O(n) for shifting bboxset expand (const vect,2>& lohi, const vect& denom) const { return expand (lohi[0], lohi[1], denom); } /** Shift the bboxset by multiples of a fraction of the stride. */ // cost: O(n) bboxset shift (const vect& v, const vect& denom) const { return expand (-v, v, denom); } /** Find the smallest b-compatible box around this bbox. ("compatible" means having the same stride.) */ bboxset expanded_for (const box& b) const CCTK_MEMBER_ATTRIBUTE_PURE; // TODO: this is incorrect #if 1 /** Find the largest b-compatible box inside this bbox. */ bboxset contracted_for (const box& b) const CCTK_MEMBER_ATTRIBUTE_PURE; #endif // Equality bool operator== (const bboxset& s) const CCTK_MEMBER_ATTRIBUTE_PURE; bool operator!= (const bboxset& s) const CCTK_MEMBER_ATTRIBUTE_PURE; bool operator< (const bboxset& s) const CCTK_MEMBER_ATTRIBUTE_PURE; bool operator<= (const bboxset& s) const CCTK_MEMBER_ATTRIBUTE_PURE; bool operator> (const bboxset& s) const CCTK_MEMBER_ATTRIBUTE_PURE; bool operator>= (const bboxset& s) const CCTK_MEMBER_ATTRIBUTE_PURE; // Iterators typedef typename bset::const_iterator const_iterator; typedef typename bset::iterator iterator; const_iterator begin () const { return bs.begin(); } const_iterator end () const { return bs.end(); } // iterator begin () const { return bs.begin(); } // iterator end () const { return bs.end(); } template void serialise (C& out) const; // Memory usage size_t memory () const { return memoryof (bs); } // Input istream& input (istream& is); // Output ostream& output (ostream& os) const; }; template inline bboxset operator+ (const bbox& b1, const bbox& b2) { return bboxset(b1) + bboxset(b2); } template inline bboxset operator+ (const bbox& b, const bboxset& s) { return bboxset(b) + s; } // cost: O(1) template inline bboxset operator- (const bbox& b1, const bbox& b2) { return bboxset::minus(b1,b2); } // cost: O(n^2) template inline bboxset operator- (const bbox& b, const bboxset& s) { return bboxset::minus(b,s); } template inline bboxset operator| (const bbox& b, const bboxset& s) { return s | b; } template inline bboxset operator& (const bbox& b, const bboxset& s) { return s & b; } template inline bool operator== (const bbox& b, const bboxset& s) { return bboxset(b) == s; } template inline bool operator!= (const bbox& b, const bboxset& s) { return bboxset(b) != s; } template inline bool operator< (const bbox& b, const bboxset& s) { return bboxset(b) < s; } template inline bool operator<= (const bbox& b, const bboxset& s) { return bboxset(b) <= s; } template inline bool operator> (const bbox& b, const bboxset& s) { return bboxset(b) > s; } template inline bool operator>= (const bbox& b, const bboxset& s) { return bboxset(b) >= s; } template inline bool operator== (const bboxset& s, const bbox& b) { return s == bboxset(b); } template inline bool operator!= (const bboxset& s, const bbox& b) { return s != bboxset(b); } template inline bool operator< (const bboxset& s, const bbox& b) { return s < bboxset(b); } template inline bool operator<= (const bboxset& s, const bbox& b) { return s <= bboxset(b); } template inline bool operator> (const bboxset& s, const bbox& b) { return s > bboxset(b); } template inline bool operator>= (const bboxset& s, const bbox& b) { return s >= bboxset(b); } // Memory usage template inline size_t memoryof (bboxset const & s) { return s.memory(); } // Input template inline istream& operator>> (istream& is, bboxset& s) { return s.input(is); } // Output template inline ostream& operator<< (ostream& os, const bboxset& s) { return s.output(os); } //////////////////////////////////////////////////////////////////////////////// // Implementation //////////////////////////////////////////////////////////////////////////////// #define SKIP_NORMALIZE(s) skip_normalize_t skip_normalize_v(s) // #define SKIP_NORMALIZE(s) do { } while(0) // Constructors template bboxset::bboxset () : skip_normalize(false) { assert (invariant()); } template bboxset::bboxset (const box& b) : skip_normalize(false) { //S if (not b.empty()) bs.insert(b); if (not b.empty()) bs.push_back(b); assert (invariant()); } template bboxset::bboxset (const bboxset& s) : bs(s.bs), skip_normalize(false) { assert (invariant()); } template bboxset::bboxset (const list& lb) : skip_normalize(false) { SKIP_NORMALIZE(*this); for (typename list::const_iterator li = lb.begin(), le = lb.end(); li != le; ++ li) { *this |= *li; } } template bboxset::bboxset (const set& sb) : skip_normalize(false) { SKIP_NORMALIZE(*this); for (typename set::const_iterator vi = sb.begin(), ve = sb.end(); vi != ve; ++ vi) { *this |= *vi; } } template bboxset::bboxset (const vector& vb) : skip_normalize(false) { SKIP_NORMALIZE(*this); for (typename vector::const_iterator vi = vb.begin(), ve = vb.end(); vi != ve; ++ vi) { *this |= *vi; } } template bboxset::bboxset (const vector >& vlb) : skip_normalize(false) { SKIP_NORMALIZE(*this); for (typename vector >::const_iterator vli = vlb.begin(), vle = vlb.end(); vli != vle; ++ vli) { *this |= bboxset (*vli); } } template template bboxset::bboxset (const vector& vb, const bbox U::* const v) : skip_normalize(false) { SKIP_NORMALIZE(*this); for (typename vector::const_iterator vi = vb.begin(), ve = vb.end(); vi != ve; ++ vi) { *this |= (*vi).*v; } } template template bboxset::bboxset (const vector& vb, const bboxset U::* const v) : skip_normalize(false) { SKIP_NORMALIZE(*this); for (typename vector::const_iterator vi = vb.begin(), ve = vb.end(); vi != ve; ++ vi) { *this |= (*vi).*v; } } template bboxset bboxset::poison () { return bboxset (bbox::poison()); } // Invariant template bool bboxset::invariant () const { // This is very slow when there are many bboxes #if 0 && defined(CARPET_DEBUG) for (const_iterator bi=begin(), be=end(); bi!=be; ++bi) { if ((*bi).empty()) return false; if (not (*bi).is_aligned_with(*bs.begin())) return false; // check for overlap (quadratic -- expensive) for (const_iterator bi2=begin(); bi2!=bi; ++bi2) { if ((*bi2).intersects(*bi)) return false; } } #endif return true; } // Normalisation template void bboxset::normalize () { if (skip_normalize) return; assert (invariant()); bboxset const oldbs = * this; size_type const oldsize = this->size(); // Split all bboxes into small pieces which have all their // boundaries aligned. for (int d=0; d buf; typedef vector buf; buf sbnds; sbnds.reserve (2 * bs.size()); for (typename bset::const_iterator si = bs.begin(), se = bs.end(); si != se; ++ si) { box const & b = * si; int const bstr = b.stride()[d]; int const blo = b.lower()[d]; int const bhi = b.upper()[d] + bstr; //S sbnds.insert (blo); //S sbnds.insert (bhi); sbnds.push_back (blo); sbnds.push_back (bhi); } sort (sbnds.begin(), sbnds.end()); typename buf::iterator const last = unique (sbnds.begin(), sbnds.end()); sbnds.resize (last - sbnds.begin()); // Split bboxes bset nbs; for (typename bset::const_iterator si = bs.begin(), se = bs.end(); si != se; ++ si) { box const & b = * si; int const bstr = b.stride()[d]; int const blo = b.lower()[d]; int const bhi = b.upper()[d] + bstr; typename buf::const_iterator const ilo = lower_bound (sbnds.begin(), sbnds.end(), blo); typename buf::const_iterator const ihi = lower_bound (sbnds.begin(), sbnds.end(), bhi); assert (ilo != sbnds.end()); assert (ihi != sbnds.end()); assert (* ilo == blo); assert (* ihi == bhi); // Split one bbox for (typename buf::const_iterator curr = ilo; curr != ihi; ++ curr) { typename buf::const_iterator next = curr; advance (next, 1); int const nblo = * curr; int const nbhi = * next; assert (nbhi > nblo); // ensure that the set remains sorted box const nb (b.lower().replace(d, nblo), b.upper().replace(d, nbhi - bstr), b.stride()); //S nbs.insert (nb); nbs.push_back (nb); } } // Replace old set bs.clear (); bs.swap (nbs); assert (invariant()); } // Combine bboxes if possible for (int d=0; dsize(); // Can't use operators on *this since these would call normalize again // assert (*this == oldbs); assert (newsize == oldsize); } // Accessors // cost: O(n) template typename bboxset::size_type bboxset::size () const { size_type s = 0; for (const_iterator bi=begin(), be=end(); bi!=be; ++bi) { const size_type bsz = (*bi).size(); assert (numeric_limits::max() - bsz >= s); s += bsz; } return s; } // Queries // Containment // cost: O(n) template bool bboxset::contains (const vect& x) const { for (const_iterator bi=begin(), be=end(); bi!=be; ++bi) { if ((*bi).contains(x)) return true; } return false; } // Intersection // cost: O(n) template bool bboxset::intersects (const box& b) const { for (const_iterator bi=begin(), be=end(); bi!=be; ++bi) { if ((*bi).intersects(b)) return true; } return false; } // Add (bboxes that don't overlap) // cost: O(1) template bboxset& bboxset::operator+= (const box& b) { if (b.empty()) return *this; // This is very slow when there are many bboxes #if 0 && defined(CARPET_DEBUG) // check for overlap for (const_iterator bi=begin(), be=end(); bi!=be; ++bi) { assert (not (*bi).intersects(b)); } #endif //S bs.insert(b); bs.push_back(b); assert (invariant()); normalize(); return *this; } // cost: O(n) template bboxset& bboxset::operator+= (const bboxset& s) { SKIP_NORMALIZE(*this); for (const_iterator bi=s.begin(), be=s.end(); bi!=be; ++bi) { *this += *bi; } assert (invariant()); return *this; } //L cost: O(1) // cost O(n) template bboxset& bboxset::add_transfer (bboxset& s) { #ifdef BBOXSET_LIST bs.splice (bs.end(), s.bs); #else bs.insert (bs.end(), s.bs.begin(), s.bs.end()); s.bs.clear (); #endif assert (invariant()); normalize(); return *this; } // cost: O(n) template bboxset bboxset::operator+ (const box& b) const { bboxset r(*this); r += b; assert (r.invariant()); return r; } // cost: O(n) template bboxset bboxset::operator+ (const bboxset& s) const { bboxset r(*this); r += s; assert (r.invariant()); return r; } // Union // cost: O(n) template bboxset& bboxset::operator|= (const box& b) { if (b.empty()) return *this; #if 0 // this has a cost of O(n^2) bboxset tmp = b - *this; add_transfer (tmp); #else // this has a cost of O(n) bset oldbs; oldbs.swap(bs); bs.push_back(b); SKIP_NORMALIZE(*this); for (const_iterator bi=oldbs.begin(), be=oldbs.end(); bi!=be; ++bi) { bboxset tmp = *bi - b; add_transfer(tmp); } #endif assert (invariant()); return *this; } // cost: O(n^2) template bboxset& bboxset::operator|= (const bboxset& s) { bboxset tmp = s - *this; add_transfer (tmp); assert (invariant()); return *this; } // cost: O(n) template bboxset bboxset::operator| (const box& b) const { bboxset r(*this); r |= b; assert (r.invariant()); return r; } // cost: O(n^2) template bboxset bboxset::operator| (const bboxset& s) const { bboxset r(*this); r |= s; assert (r.invariant()); return r; } // Intersection // cost: O(n) template bboxset bboxset::operator& (const box& b) const { // start with an empty set bboxset r; { SKIP_NORMALIZE(r); // walk all my elements for (const_iterator bi=begin(), be=end(); bi!=be; ++bi) { // insert the intersection with the bbox r += *bi & b; } } assert (r.invariant()); return r; } // cost: O(n) template bboxset bboxset::operator& (const bboxset& s) const { // start with an empty set bboxset r; { SKIP_NORMALIZE(r); // walk all the bboxes for (const_iterator bi=s.begin(), be=s.end(); bi!=be; ++bi) { // insert the intersection with this bbox bboxset tmp = *this & *bi; r.add_transfer (tmp); } } assert (r.invariant()); return r; } // cost: O(n) template bboxset& bboxset::operator&= (const box& b) { *this = *this & b; assert (invariant()); return *this; } // cost: O(n) template bboxset& bboxset::operator&= (const bboxset& s) { *this = *this & s; assert (invariant()); return *this; } // Difference // cost: O(1) template bboxset bboxset::minus (const bbox& b1, const bbox& b2) { assert (b1.is_aligned_with(b2)); if (b1.empty()) return bboxset(); if (not b1.intersects(b2)) return bboxset(b1); vect const b2lo = max (b1.lower(), b2.lower()); vect const b2up = min (b1.upper(), b2.upper()); vect const & b1lo = b1.lower(); vect const & b1up = b1.upper(); vect const & str = b1.stride(); bboxset r; SKIP_NORMALIZE(r); { for (int d=0; d lb, ub; bbox b; for (int dd=0; ddd) { lb[dd] = b1lo[dd]; ub[dd] = b1up[dd]; } } lb[d] = b1lo[d]; ub[d] = b2lo[d] - str[d]; b = bbox(lb,ub,str); r += b; lb[d] = b2up[d] + str[d]; ub[d] = b1up[d]; b = bbox(lb,ub,str); r += b; } } assert (r.invariant()); return r; } // cost: O(n) template bboxset bboxset::operator- (const box& b) const { // start with an empty set bboxset r; { SKIP_NORMALIZE(r); // walk all my elements for (const_iterator bi=begin(), be=end(); bi!=be; ++bi) { // insert the difference with the bbox bboxset tmp = *bi - b; r.add_transfer (tmp); } } assert (r.invariant()); return r; } // cost: O(n^2) template bboxset& bboxset::operator-= (const bboxset& s) { SKIP_NORMALIZE(*this); for (const_iterator bi=s.begin(), be=s.end(); bi!=be; ++bi) { *this -= *bi; } assert (invariant()); return *this; } // cost: O(n^2) template bboxset bboxset::operator- (const bboxset& s) const { bboxset r(*this); r -= s; assert (r.invariant()); return r; } // cost: O(n^2) template bboxset bboxset::minus (const bbox& b, const bboxset& s) { bboxset r = bboxset(b) - s; assert (r.invariant()); return r; } // cost: O(n) template typename bboxset::box bboxset::container () const { box b; for (const_iterator bi=begin(), be=end(); bi!=be; ++bi) { b = b.expanded_containing(*bi); } return b; } template bboxset bboxset::pseudo_inverse (const int n) const { assert (not empty()); box const c = container().expand(n,n); return c - *this; } // cost: O(n^2) in general, but only O(n) for shifting template bboxset bboxset::expand (const vect& lo, const vect& hi) const { bboxset res; { SKIP_NORMALIZE(res); if (all (lo == -hi)) { // Special case for shifting, since this is faster for (const_iterator bi=begin(), be=end(); bi!=be; ++bi) { res += (*bi).expand(lo,hi); } } else { // We don't know (yet?) how to shrink a set assert (all (lo>=0 and hi>=0)); for (const_iterator bi=begin(), be=end(); bi!=be; ++bi) { res |= (*bi).expand(lo,hi); } } } return res; } // cost: O(n^2) in general, but only O(n) for shifting template bboxset bboxset::expand (const vect& lo, const vect& hi, const vect& denom) const { assert (all(denom > vect(0))); bboxset res; { SKIP_NORMALIZE(res); if (all (lo == -hi)) { // Special case for shifting, since this is faster for (const_iterator bi=begin(), be=end(); bi!=be; ++bi) { res += (*bi).expand(lo,hi,denom); } } else { // We don't know (yet?) how to shrink a set assert (all ((lo>=0 and hi>=0) or (lo == hi))); for (const_iterator bi=begin(), be=end(); bi!=be; ++bi) { res |= (*bi).expand(lo,hi,denom); } } } return res; } template bboxset bboxset::expanded_for (const box& b) const { bboxset res; { SKIP_NORMALIZE(res); for (const_iterator bi=begin(), be=end(); bi!=be; ++bi) { res |= (*bi).expanded_for(b); } } return res; } // TODO: this is incorrect #if 1 template bboxset bboxset::contracted_for (const box& b) const { bboxset res; { SKIP_NORMALIZE(res); for (const_iterator bi=begin(), be=end(); bi!=be; ++bi) { res |= (*bi).contracted_for(b); } } return res; } #endif // Equality template bool bboxset::operator<= (const bboxset& s) const { return (*this - s).empty(); } template bool bboxset::operator< (const bboxset& s) const { return (*this - s).empty() && not (s - *this).empty(); } template bool bboxset::operator>= (const bboxset& s) const { return s <= *this; } template bool bboxset::operator> (const bboxset& s) const { return s < *this; } template bool bboxset::operator== (const bboxset& s) const { return (*this <= s) && (*this >= s); } template bool bboxset::operator!= (const bboxset& s) const { return not (*this == s); } // Serialise template template void bboxset::serialise (C& out) const { for (bboxset::const_iterator it = begin(); it != end(); ++it) { out.insert(out.end(), *it); } } // Input template istream& bboxset::input (istream& is) { T Tdummy; try { skipws (is); consume (is, "bboxset<"); consume (is, typestring(Tdummy)); consume (is, ","); int D_; is >> D_; if (D_ != D) { cout << "Input error: Wrong bboxset dimension " << D_ << ", expected " << D << endl; throw input_error(); } consume (is, ">:{"); consume (is, "size="); size_type size_; is >> size_; consume (is, ","); consume (is, "setsize="); int setsize_; is >> setsize_; consume (is, ","); consume (is, "set="); is >> bs; consume (is, "}"); } catch (input_error & err) { cout << "Input error while reading a bboxset<" << typestring(Tdummy) << "," << D << ">" << endl; throw err; } normalize(); return is; } // Output template ostream& bboxset::output (ostream& os) const { T Tdummy; os << "bboxset<" << typestring(Tdummy) << "," << D << ">:{" << "size=" << size() << "," << "setsize=" << setsize() << "," << "set=" << bs << "}"; return os; } #undef SKIP_NORMALIZE } // namespace bboxset1 #endif // BBOXSET1_HH