From f81078dd29fc6915564d661b57f7a3b656c2185e Mon Sep 17 00:00:00 2001 From: David Rose Date: Fri, 22 Feb 2002 18:56:04 +0000 Subject: [PATCH] improvements to ordered_vector --- panda/src/putil/ordered_vector.I | 275 +++++++++++++++++++++++- panda/src/putil/ordered_vector.T | 64 +++++- panda/src/putil/ordered_vector.h | 99 ++++++++- panda/src/putil/test_ordered_vector.cxx | 2 +- 4 files changed, 416 insertions(+), 24 deletions(-) diff --git a/panda/src/putil/ordered_vector.I b/panda/src/putil/ordered_vector.I index e2146b4a9f..85ba3bbbf6 100644 --- a/panda/src/putil/ordered_vector.I +++ b/panda/src/putil/ordered_vector.I @@ -17,6 +17,31 @@ //////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////// +// Function: ordered_vector::EquivalentTest::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ordered_vector::EquivalentTest:: +EquivalentTest(const Compare &compare) : + _compare(compare) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ordered_vector::EquivalentTest::operator () +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool ordered_vector::EquivalentTest:: +operator () (const ordered_vector::key_type &a, + const ordered_vector::key_type &b) { + nassertr(!_compare(b, a), false); + return !_compare(a, b); +} + //////////////////////////////////////////////////////////////////// // Function: ordered_vector::Constructor // Access: Public @@ -161,6 +186,28 @@ rend() const { return _vector.rend(); } +//////////////////////////////////////////////////////////////////// +// Function: ordered_vector::operator [] +// Access: Public +// Description: Returns the nth element. +//////////////////////////////////////////////////////////////////// +template +INLINE ordered_vector::reference ordered_vector:: +operator [] (ordered_vector::size_type n) { + return _vector[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: ordered_vector::operator [] +// Access: Public +// Description: Returns the nth element. +//////////////////////////////////////////////////////////////////// +template +INLINE ordered_vector::const_reference ordered_vector:: +operator [] (ordered_vector::size_type n) const { + return _vector[n]; +} + //////////////////////////////////////////////////////////////////// // Function: ordered_vector::size // Access: Public @@ -274,17 +321,54 @@ operator >= (const ordered_vector &other) const { //////////////////////////////////////////////////////////////////// -// Function: ordered_vector::insert +// Function: ordered_vector::insert_unique +// Access: Public +// Description: Inserts the indicated key into the ordered vector, at +// the appropriate place. If there is already an element +// sorting equivalent to the key in the vector, the new +// key is not inserted. +// +// The return value is a pair, where the first component +// is the iterator referencing the new element (or the +// original element), and the second componet is true if +// the insert operation has taken place. +//////////////////////////////////////////////////////////////////// +template +INLINE pair::iterator, bool> ordered_vector:: +insert_unique(const ordered_vector::value_type &key) { + iterator position = find_insert_position(begin(), end(), key); +#ifdef NDEBUG + pair bogus_result(end(), false); + nassertr(position >= begin() && position <= end(), bogus_result); +#endif + + // If there's already an equivalent key in the vector, it's at + // *(position - 1). + if (position != begin() && !_compare(*(position - 1), key)) { + pair result(position - 1, false); + nassertr(!_compare(key, *(position - 1)), result); + return result; + } + + iterator result = _vector.insert(position, key); + verify_list(); + return pair(result, true); +} + +//////////////////////////////////////////////////////////////////// +// Function: ordered_vector::insert_nonunique // Access: Public // Description: Inserts the indicated key into the ordered vector, at // the appropriate place. If there are already elements // sorting equivalent to the key in the vector, the new -// value is inserted following them. The return value -// is the iterator referencing the new element. +// value is inserted following them. + +// The return value is the iterator referencing the new +// element. //////////////////////////////////////////////////////////////////// template INLINE ordered_vector::iterator ordered_vector:: -insert(const ordered_vector::value_type &key) { +insert_nonunique(const ordered_vector::value_type &key) { iterator position = find_insert_position(begin(), end(), key); nassertr(position >= begin() && position <= end(), end()); @@ -528,17 +612,52 @@ reserve(ordered_vector::size_type n) { } //////////////////////////////////////////////////////////////////// -// Function: ordered_vector::sort +// Function: ordered_vector::sort_unique // Access: Public // Description: Ensures that the vector is properly sorted after a // potentially damaging operation. This should not // normally need to be called, unless the user has -// written to the vector using the non-const iterators. +// written to the vector using the non-const iterators +// or has called push_back(). +// +// This flavor of sort also eliminates repeated +// elements. //////////////////////////////////////////////////////////////////// template INLINE void ordered_vector:: -sort() { - ::sort(begin(), end(), _compare); +sort_unique() { + sort(begin(), end(), _compare); + iterator new_end = unique(begin(), end(), EquivalentTest(_compare)); + erase(new_end, end()); +} + +//////////////////////////////////////////////////////////////////// +// Function: ordered_vector::sort_nonunique +// Access: Public +// Description: Ensures that the vector is properly sorted after a +// potentially damaging operation. This should not +// normally need to be called, unless the user has +// written to the vector using the non-const iterators +// or has called push_back(). +//////////////////////////////////////////////////////////////////// +template +INLINE void ordered_vector:: +sort_nonunique() { + sort(begin(), end(), _compare); +} + +//////////////////////////////////////////////////////////////////// +// Function: ordered_vector::push_back +// Access: Public +// Description: Adds the new element to the end of the vector without +// regard for proper sorting. This is a bad idea to do +// except to populate the vector the first time; be sure +// to call sort() after you have added all the elements. +//////////////////////////////////////////////////////////////////// +template +INLINE void ordered_vector:: +push_back(const value_type &key) { + _vector.push_back(key); } //////////////////////////////////////////////////////////////////// @@ -606,3 +725,143 @@ verify_list() { #endif return true; } + +//////////////////////////////////////////////////////////////////// +// Function: ov_set::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ov_set:: +ov_set(const Compare &compare) : + ordered_vector(compare) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ov_set::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ov_set:: +ov_set(const ov_set ©) : + ordered_vector(copy) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ov_set::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ov_set &ov_set:: +operator = (const ov_set ©) { + ordered_vector::operator = (copy); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: ov_set::insert +// Access: Public +// Description: Maps to insert_unique(). +//////////////////////////////////////////////////////////////////// +template +ov_set::iterator ov_set:: +insert(ordered_vector::iterator position, + const ordered_vector::value_type &key) { + return ordered_vector::insert_unique(position, key); +} + +//////////////////////////////////////////////////////////////////// +// Function: ov_set::insert +// Access: Public +// Description: Maps to insert_unique(). +//////////////////////////////////////////////////////////////////// +template +INLINE pair::iterator, bool> ov_set:: +insert(const ordered_vector::value_type &key) { + return ordered_vector::insert_unique(key); +} + +//////////////////////////////////////////////////////////////////// +// Function: ov_set::sort +// Access: Public +// Description: Maps to sort_unique(). +//////////////////////////////////////////////////////////////////// +template +INLINE void ov_set:: +sort() { + return ordered_vector::sort_unique(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ov_multiset::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ov_multiset:: +ov_multiset(const Compare &compare) : + ordered_vector(compare) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ov_multiset::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ov_multiset:: +ov_multiset(const ov_multiset ©) : + ordered_vector(copy) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ov_multiset::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ov_multiset &ov_multiset:: +operator = (const ov_multiset ©) { + ordered_vector::operator = (copy); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: ov_multiset::insert +// Access: Public +// Description: Maps to insert_nonunique(). +//////////////////////////////////////////////////////////////////// +template +ov_multiset::iterator ov_multiset:: +insert(ordered_vector::iterator position, + const ordered_vector::value_type &key) { + return ordered_vector::insert_nonunique(position, key); +} + +//////////////////////////////////////////////////////////////////// +// Function: ov_multiset::insert +// Access: Public +// Description: Maps to insert_nonunique(). +//////////////////////////////////////////////////////////////////// +template +INLINE pair::iterator, bool> ov_multiset:: +insert(const ordered_vector::value_type &key) { + return ordered_vector::insert_nonunique(key); +} + +//////////////////////////////////////////////////////////////////// +// Function: ov_multiset::sort +// Access: Public +// Description: Maps to sort_nonunique(). +//////////////////////////////////////////////////////////////////// +template +INLINE void ov_multiset:: +sort() { + return ordered_vector::sort_nonunique(); +} diff --git a/panda/src/putil/ordered_vector.T b/panda/src/putil/ordered_vector.T index d96c2c28b4..53811627ec 100644 --- a/panda/src/putil/ordered_vector.T +++ b/panda/src/putil/ordered_vector.T @@ -18,31 +18,83 @@ //////////////////////////////////////////////////////////////////// -// Function: ordered_vector::insert +// Function: ordered_vector::insert_unique // Access: Public // Description: Inserts the indicated key into the ordered vector. // The iterator is a hint to the expected position; if // this is correct, the insert operation is likely to be // faster. The return value is the iterator referencing // the new element. +// +// This flavor of insert does not allow multiple copies +// of the same key to be inserted. If the key is +// already present, it is not inserted, and the iterator +// referencing the original value is returned. //////////////////////////////////////////////////////////////////// template ordered_vector::iterator ordered_vector:: -insert(ordered_vector::iterator position, - const ordered_vector::value_type &key) { +insert_unique(ordered_vector::iterator position, + const ordered_vector::value_type &key) { + if (position != end()) { + // If we're not inserting at the end, the element we're + // inserting before should not lexicographically precede this one. + if (_compare(*position, key)) { + return insert_unique(key).first; + + } else if (!_compare(key, *position)) { + // Oops, !(*position < key) and !(key < *position). That means + // they're equivalent, and we shouldn't insert a new one. + return position; + } + } + if (position != begin()) { // If we're not inserting at the beginning, this element should // not lexicographically precede the one we're inserting after. if (_compare(key, *(position - 1))) { - return insert(key); + return insert_unique(key).first; + + } else if (!_compare(*(position - 1), key)) { + // Once again, they're equivalent. + return position - 1; } } + // Otherwise, we may insert where the caller requested. + iterator result = _vector.insert(position, key); + verify_list(); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: ordered_vector::insert_nonunique +// Access: Public +// Description: Inserts the indicated key into the ordered vector. +// The iterator is a hint to the expected position; if +// this is correct, the insert operation is likely to be +// faster. The return value is the iterator referencing +// the new element. +// +// This flavor of insert allows multiple copies of the +// same key to be inserted. +//////////////////////////////////////////////////////////////////// +template +ordered_vector::iterator ordered_vector:: +insert_nonunique(ordered_vector::iterator position, + const ordered_vector::value_type &key) { if (position != end()) { - // If we're not inserting at the beginning, the element we're + // If we're not inserting at the end, the element we're // inserting before should not lexicographically precede this one. if (_compare(*position, key)) { - return insert(key); + return insert_nonunique(key); + } + } + + if (position != begin()) { + // If we're not inserting at the beginning, this element should + // not lexicographically precede the one we're inserting after. + if (_compare(key, *(position - 1))) { + return insert_nonunique(key); } } diff --git a/panda/src/putil/ordered_vector.h b/panda/src/putil/ordered_vector.h index ace4a7f4bb..c075e6347b 100644 --- a/panda/src/putil/ordered_vector.h +++ b/panda/src/putil/ordered_vector.h @@ -29,13 +29,38 @@ //////////////////////////////////////////////////////////////////// // Class : ordered_vector // Description : This template class presents an interface similar to -// the STL multiset, but it is implemented using a vector -// that is kept always in sorted order. +// the STL set or multiset (and ov_set and ov_multiset +// are implemented specifically, below), but it is +// implemented using a vector that is kept always in +// sorted order. // -// This allows the ordered_vector to maintain stability -// of order between elements that sort equally: they are -// stored in the order in which they were added, from -// back to front. +// In most cases, an ov_set or ov_multiset may be +// dropped in transparently in place of a set or +// multiset, but the implementation difference has a few +// implications: +// +// (1) The ov_multiset will maintain stability of order +// between elements that sort equally: they are stored +// in the order in which they were added, from back to +// front. +// +// (2) Insert and erase operations into the middle of +// the set can be slow, just as inserting into the +// middle of a vector can be slow. In fact, building up +// an ov_set by inserting elements one at a time is an +// n^2 operation. On the other hand, building up an +// ov_set by adding elements to the end, one at time, is +// somewhat faster than building up a traditional set; +// and you can even add unsorted elements with +// push_back() and then call sort() when you're done, +// for a log(n) operation. +// +// (3) Iterators may not be valid for the life of the +// ordered_vector. If the vector reallocates itself, +// all iterators are invalidated. +// +// (4) Random access into the set is easy with the [] +// operator. //////////////////////////////////////////////////////////////////// template > class ordered_vector { @@ -81,6 +106,10 @@ public: INLINE const_reverse_iterator rbegin() const; INLINE const_reverse_iterator rend() const; + // Random access. + INLINE reference operator [] (size_type n); + INLINE const_reference operator [] (size_type n) const; + // Size information. INLINE size_type size() const; INLINE size_type max_size() const; @@ -96,8 +125,10 @@ public: INLINE bool operator >= (const ordered_vector &other) const; // Insert operations. - iterator insert(iterator position, const value_type &key); - INLINE iterator insert(const value_type &key); + iterator insert_unique(iterator position, const value_type &key); + iterator insert_nonunique(iterator position, const value_type &key); + INLINE pair insert_unique(const value_type &key); + INLINE iterator insert_nonunique(const value_type &key); // Erase operations. INLINE iterator erase(iterator position); @@ -122,7 +153,10 @@ public: // Special operations. INLINE void swap(ordered_vector &other); INLINE void reserve(size_type n); - INLINE void sort(); + INLINE void sort_unique(); + INLINE void sort_nonunique(); + + INLINE void push_back(const value_type &key); private: INLINE iterator nci(const_iterator iterator); @@ -151,10 +185,57 @@ private: bool verify_list_impl(iterator first, iterator last); #endif + // This function object is used in sort_unique(). It returns true + // if two consecutive sorted elements are equivalent. + class EquivalentTest { + public: + INLINE EquivalentTest(const Compare &compare); + INLINE bool operator () (const key_type &a, const key_type &b); + Compare _compare; + }; + Compare _compare; Vector _vector; }; +//////////////////////////////////////////////////////////////////// +// Class : ov_set +// Description : A specialization of ordered_vector that emulates a +// standard STL set: one copy of each element is +// allowed. +//////////////////////////////////////////////////////////////////// +template > +class ov_set : public ordered_vector { +public: + INLINE ov_set(const Compare &compare = Compare()); + INLINE ov_set(const ov_set ©); + INLINE ov_set &operator = (const ov_set ©); + + INLINE iterator insert(iterator position, const value_type &key); + INLINE pair insert(const value_type &key); + + INLINE void sort(); +}; + +//////////////////////////////////////////////////////////////////// +// Class : ov_multiset +// Description : A specialization of ordered_vector that emulates a +// standard STL set: many copies of each element are +// allowed. +//////////////////////////////////////////////////////////////////// +template > +class ov_multiset : public ordered_vector { +public: + INLINE ov_multiset(const Compare &compare = Compare()); + INLINE ov_multiset(const ov_multiset ©); + INLINE ov_multiset &operator = (const ov_multiset ©); + + INLINE iterator insert(iterator position, const value_type &key); + INLINE pair insert(const value_type &key); + + INLINE void sort(); +}; + #include "ordered_vector.I" #include "ordered_vector.T" diff --git a/panda/src/putil/test_ordered_vector.cxx b/panda/src/putil/test_ordered_vector.cxx index 761c363b07..b698954091 100644 --- a/panda/src/putil/test_ordered_vector.cxx +++ b/panda/src/putil/test_ordered_vector.cxx @@ -18,7 +18,7 @@ #include "ordered_vector.h" -typedef ordered_vector myvec; +typedef ov_multiset myvec; void search(myvec &v, int element) {