Browse Source

improvements to ordered_vector

David Rose 24 years ago
parent
commit
f81078dd29

+ 267 - 8
panda/src/putil/ordered_vector.I

@@ -17,6 +17,31 @@
 ////////////////////////////////////////////////////////////////////
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ordered_vector::EquivalentTest::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+template<class Key, class Compare>
+INLINE ordered_vector<Key, Compare>::EquivalentTest::
+EquivalentTest(const Compare &compare) :
+  _compare(compare)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ordered_vector::EquivalentTest::operator ()
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+template<class Key, class Compare>
+INLINE bool ordered_vector<Key, Compare>::EquivalentTest::
+operator () (const ordered_vector<Key, Compare>::key_type &a,
+             const ordered_vector<Key, Compare>::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<class Key, class Compare>
+INLINE ordered_vector<Key, Compare>::reference ordered_vector<Key, Compare>::
+operator [] (ordered_vector<Key, Compare>::size_type n) {
+  return _vector[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ordered_vector::operator []
+//       Access: Public
+//  Description: Returns the nth element.
+////////////////////////////////////////////////////////////////////
+template<class Key, class Compare>
+INLINE ordered_vector<Key, Compare>::const_reference ordered_vector<Key, Compare>::
+operator [] (ordered_vector<Key, Compare>::size_type n) const {
+  return _vector[n];
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: ordered_vector::size
 //       Access: Public
@@ -274,17 +321,54 @@ operator >= (const ordered_vector<Key, Compare> &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<class Key, class Compare>
+INLINE pair<ordered_vector<Key, Compare>::iterator, bool> ordered_vector<Key, Compare>::
+insert_unique(const ordered_vector<Key, Compare>::value_type &key) {
+  iterator position = find_insert_position(begin(), end(), key);
+#ifdef NDEBUG
+  pair<iterator, bool> 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<iterator, bool> result(position - 1, false);
+    nassertr(!_compare(key, *(position - 1)), result);
+    return result;
+  }
+
+  iterator result = _vector.insert(position, key);
+  verify_list();
+  return pair<iterator, bool>(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<class Key, class Compare>
 INLINE ordered_vector<Key, Compare>::iterator ordered_vector<Key, Compare>::
-insert(const ordered_vector<Key, Compare>::value_type &key) {
+insert_nonunique(const ordered_vector<Key, Compare>::value_type &key) {
   iterator position = find_insert_position(begin(), end(), key);
   nassertr(position >= begin() && position <= end(), end());
 
@@ -528,17 +612,52 @@ reserve(ordered_vector<Key, Compare>::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<class Key, class Compare>
 INLINE void ordered_vector<Key, Compare>::
-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<class Key, class Compare>
+INLINE void ordered_vector<Key, Compare>::
+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<class Key, class Compare>
+INLINE void ordered_vector<Key, Compare>::
+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<class Key, class Compare>
+INLINE ov_set<Key, Compare>::
+ov_set(const Compare &compare) :
+  ordered_vector(compare)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ov_set::Copy Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+template<class Key, class Compare>
+INLINE ov_set<Key, Compare>::
+ov_set(const ov_set<Key, Compare> &copy) :
+  ordered_vector(copy)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ov_set::Copy Assignment Operator
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+template<class Key, class Compare>
+INLINE ov_set<Key, Compare> &ov_set<Key, Compare>::
+operator = (const ov_set<Key, Compare> &copy) {
+  ordered_vector::operator = (copy);
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ov_set::insert
+//       Access: Public
+//  Description: Maps to insert_unique().
+////////////////////////////////////////////////////////////////////
+template<class Key, class Compare>
+ov_set<Key, Compare>::iterator ov_set<Key, Compare>::
+insert(ordered_vector<Key, Compare>::iterator position, 
+       const ordered_vector<Key, Compare>::value_type &key) {
+  return ordered_vector::insert_unique(position, key);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ov_set::insert
+//       Access: Public
+//  Description: Maps to insert_unique().
+////////////////////////////////////////////////////////////////////
+template<class Key, class Compare>
+INLINE pair<ov_set<Key, Compare>::iterator, bool> ov_set<Key, Compare>::
+insert(const ordered_vector<Key, Compare>::value_type &key) {
+  return ordered_vector::insert_unique(key);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ov_set::sort
+//       Access: Public
+//  Description: Maps to sort_unique().
+////////////////////////////////////////////////////////////////////
+template<class Key, class Compare>
+INLINE void ov_set<Key, Compare>::
+sort() {
+  return ordered_vector::sort_unique();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ov_multiset::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+template<class Key, class Compare>
+INLINE ov_multiset<Key, Compare>::
+ov_multiset(const Compare &compare) :
+  ordered_vector(compare)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ov_multiset::Copy Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+template<class Key, class Compare>
+INLINE ov_multiset<Key, Compare>::
+ov_multiset(const ov_multiset<Key, Compare> &copy) :
+  ordered_vector(copy)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ov_multiset::Copy Assignment Operator
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+template<class Key, class Compare>
+INLINE ov_multiset<Key, Compare> &ov_multiset<Key, Compare>::
+operator = (const ov_multiset<Key, Compare> &copy) {
+  ordered_vector::operator = (copy);
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ov_multiset::insert
+//       Access: Public
+//  Description: Maps to insert_nonunique().
+////////////////////////////////////////////////////////////////////
+template<class Key, class Compare>
+ov_multiset<Key, Compare>::iterator ov_multiset<Key, Compare>::
+insert(ordered_vector<Key, Compare>::iterator position, 
+       const ordered_vector<Key, Compare>::value_type &key) {
+  return ordered_vector::insert_nonunique(position, key);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ov_multiset::insert
+//       Access: Public
+//  Description: Maps to insert_nonunique().
+////////////////////////////////////////////////////////////////////
+template<class Key, class Compare>
+INLINE pair<ov_multiset<Key, Compare>::iterator, bool> ov_multiset<Key, Compare>::
+insert(const ordered_vector<Key, Compare>::value_type &key) {
+  return ordered_vector::insert_nonunique(key);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ov_multiset::sort
+//       Access: Public
+//  Description: Maps to sort_nonunique().
+////////////////////////////////////////////////////////////////////
+template<class Key, class Compare>
+INLINE void ov_multiset<Key, Compare>::
+sort() {
+  return ordered_vector::sort_nonunique();
+}

+ 58 - 6
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<class Key, class Compare>
 ordered_vector<Key, Compare>::iterator ordered_vector<Key, Compare>::
-insert(ordered_vector<Key, Compare>::iterator position, 
-       const ordered_vector<Key, Compare>::value_type &key) {
+insert_unique(ordered_vector<Key, Compare>::iterator position, 
+              const ordered_vector<Key, Compare>::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<class Key, class Compare>
+ordered_vector<Key, Compare>::iterator ordered_vector<Key, Compare>::
+insert_nonunique(ordered_vector<Key, Compare>::iterator position, 
+                 const ordered_vector<Key, Compare>::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);
     }
   }
 

+ 90 - 9
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 Key, class Compare = less<Key> >
 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<Key, Compare> &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<iterator, bool> 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<Key, Compare> &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 Key, class Compare = less<Key> >
+class ov_set : public ordered_vector<Key, Compare> {
+public:
+  INLINE ov_set(const Compare &compare = Compare());
+  INLINE ov_set(const ov_set<Key, Compare> &copy);
+  INLINE ov_set<Key, Compare> &operator = (const ov_set<Key, Compare> &copy);
+
+  INLINE iterator insert(iterator position, const value_type &key);
+  INLINE pair<iterator, bool> 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 Key, class Compare = less<Key> >
+class ov_multiset : public ordered_vector<Key, Compare> {
+public:
+  INLINE ov_multiset(const Compare &compare = Compare());
+  INLINE ov_multiset(const ov_multiset<Key, Compare> &copy);
+  INLINE ov_multiset<Key, Compare> &operator = (const ov_multiset<Key, Compare> &copy);
+
+  INLINE iterator insert(iterator position, const value_type &key);
+  INLINE pair<iterator, bool> insert(const value_type &key);
+
+  INLINE void sort();
+};
+
 #include "ordered_vector.I"
 #include "ordered_vector.T"
 

+ 1 - 1
panda/src/putil/test_ordered_vector.cxx

@@ -18,7 +18,7 @@
 
 #include "ordered_vector.h"
 
-typedef ordered_vector<int> myvec;
+typedef ov_multiset<int> myvec;
 
 void
 search(myvec &v, int element) {