Browse Source

Reducing duplication between 1-key and N-key functions.
Adding emplace, find, and erase (drop tree).
Slaving operator[] to emplace function.

Samuel Jaffe 8 years ago
parent
commit
84224185bb
3 changed files with 122 additions and 59 deletions
  1. 46 10
      trie.hpp
  2. 28 0
      trie.t.h
  3. 48 49
      trie_impl.hpp

+ 46 - 10
trie.hpp

@@ -46,14 +46,14 @@ public:
   using reverse_iterator = trie_reverse_iterator<self_t, local_reverse_iterator>;
   using const_reverse_iterator = trie_reverse_iterator<self_t const,  local_const_reverse_iterator>;
 public:
-  trie() = default;
-  trie(mapped_type const & value);
+  trie() : value_() {}
+  trie(mapped_type const & value) : value_(value) {}
   trie(trie const & other);
-  trie(trie && other) = default;
-  ~trie();
+  trie(trie && other) { swap(other); }
+  ~trie() { clear(); }
   self_t & operator=(mapped_type const & value);
   self_t & operator=(trie const & value);
-  self_t & operator=(trie && value) = default;
+  self_t & operator=(trie && value);
 
   operator mapped_type &() { return value_; }
   operator mapped_type const &() const { return value_; }
@@ -61,14 +61,34 @@ public:
   mapped_type const & value() const { return value_; }
   
   template <typename KS, typename = is_collection_t<KS>>
-  self_t & operator[](KS const & keys);
-  self_t & operator[](key_type const & key);
-  self_t & operator[](std::initializer_list<key_type> key);
+  self_t & operator[](KS const & keys) {
+    return *emplace(keys).first.stk.top();
+  }
+  self_t & operator[](key_type const & key) {
+    return *emplace(key).first.stk.top();
+  }
+  self_t & operator[](std::initializer_list<key_type> keys) {
+    return operator[]<std::initializer_list<key_type>>(keys);
+  }
   
   template <typename KS>
   std::pair<iterator, bool> insert(KS const & keys, mapped_type const & value);
-  std::pair<iterator, bool> insert(key_type const & key, mapped_type const & value);
-  std::pair<iterator, bool> insert(std::initializer_list<key_type> keys, mapped_type const & value);
+  std::pair<iterator, bool> insert(key_type const & key, mapped_type const & value) {
+    return insert({key}, value);
+  }
+  std::pair<iterator, bool> insert(std::initializer_list<key_type> keys, mapped_type const & value) {
+    return insert<std::initializer_list<key_type>>(keys, value);
+  }
+  template <typename KS, typename... Args>
+  std::pair<iterator, bool> emplace(KS const & keys, Args &&... args);
+  template <typename... Args>
+  std::pair<iterator, bool> emplace(key_type const & key, Args &&... args) {
+    return emplace({key}, std::forward<Args>(args)...);
+  }
+  template <typename... Args>
+  std::pair<iterator, bool> emplace(std::initializer_list<key_type> keys, Args &&... args) {
+    return emplace<std::initializer_list<key_type>>(keys, std::forward<Args>(args)...);
+  }
   
   iterator begin() { return {this}; }
   iterator end() { return {}; }
@@ -93,8 +113,24 @@ public:
   local_const_reverse_iterator local_rbegin() const { return impl_.rbegin(); }
   local_const_reverse_iterator local_rend() const { return impl_.rend(); }
   
+  
+  template <typename KS>
+  iterator find(KS const & keys);
+  iterator find(key_type const & key);
+  iterator find(std::initializer_list<key_type> keys) {
+    return find<std::initializer_list<key_type>>(keys);
+  }
+  
+  template <typename KS>
+  void erase(KS const & keys) { drop(find(keys)); }
+  void erase(key_type const & key) { drop(find(key)); }
+  void erase(std::initializer_list<key_type> keys) {
+    erase<std::initializer_list<key_type>>(keys);
+  }
+  
   void clear();
 private:
+  void drop(iterator it);
   template <typename... Args>
   void insert_impl(std::pair<iterator, bool> & out, key_type const & key, Args &&... args);
   

+ 28 - 0
trie.t.h

@@ -79,6 +79,21 @@ public:
     TS_ASSERT(pair.second);
   }
 
+  void testEmplaceNewElementOutputsTrue() {
+    trie<int, int> test;
+    auto pair = test.emplace(1, 2);
+    TS_ASSERT_EQUALS(*pair.first, 2);
+    TS_ASSERT(pair.second);
+  }
+
+  void testInsertArrayOfElementCreatesAllEntries() {
+    trie<int, int> test;
+    auto pair = test.insert({1, 1, 1}, 2);
+    TS_ASSERT_EQUALS(*pair.first, 2);
+    TS_ASSERT(pair.second);
+    TS_ASSERT_EQUALS(test[1][1][1].value(), 2);
+  }
+
   void testInsertNotDestructive() {
     trie<int, int> test;
     auto pair = test.insert(1, 2);
@@ -86,4 +101,17 @@ public:
     TS_ASSERT_EQUALS(*pair.first, 2);
     TS_ASSERT(!pair.second);
   }
+  
+  void testCanLocateElement() {
+    TS_ASSERT_EQUALS(*data.find({0, 1, 2}), 6);
+  }
+
+  void testFindAbsentElementReturnsEnd() {
+    TS_ASSERT_EQUALS(data.find({0, 3, 2}), data.end());
+  }
+  
+  void testEraseDropsEntireBranch() {
+    data.erase(0);
+    TS_ASSERT_EQUALS(data.find(0), data.end());
+  }
 };

+ 48 - 49
trie_impl.hpp

@@ -10,12 +10,6 @@
 #include "trie.hpp"
 #include "trie_iterator.hpp"
 
-template <typename K, typename V, typename C>
-trie<K, V, C>::trie(mapped_type const & value)
-: value_(value) {
-  
-}
-
 // n := total elements
 // d := average depth
 // Stack: O(1)
@@ -30,11 +24,6 @@ trie<K, V, C>::trie(trie const & other) : value_(other.value_) {
   }
 }
 
-template <typename K, typename V, typename C>
-trie<K, V, C>::~trie() {
-  clear();
-}
-
 template <typename K, typename V, typename C>
 auto trie<K, V, C>::operator=(mapped_type const & value) -> self_t & {
   value_ = value;
@@ -48,39 +37,15 @@ auto trie<K, V, C>::operator=(trie const & other) -> self_t & {
   return *this;
 }
 
-// n := total elements
-// d := average depth
-// Operations: O(d*log(n/d))
 template <typename K, typename V, typename C>
-template <typename KS, typename>
-auto trie<K, V, C>::operator[](KS const & keys) -> self_t & {
-  self_t * rec = this;
-  for ( key_type const & key : keys ) {
-    rec = &(*rec)[key];
-  }
-  return *rec;
-}
-
-// n := total elements
-// d := average depth
-// Operations: O(log(n))
-template <typename K, typename V, typename C>
-auto trie<K, V, C>::operator[](key_type const & key) -> self_t & {
-  auto it = impl_.lower_bound(key);
-  if ( it == impl_.end() || key_compare()(key, it->first) ) {
-    it = impl_.emplace_hint(it, key, make_value<self_t>());
-  }
-  return *(it->second);
+auto trie<K, V, C>::operator=(trie && other) -> self_t & {
+  swap(*this, other);
+  return *this;
 }
 
 // n := total elements
 // d := average depth
-// Operations: O(d*log(n/d))
-template <typename K, typename V, typename C>
-auto trie<K, V, C>::operator[](std::initializer_list<key_type> keys) -> self_t & {
-  return operator[]<std::initializer_list<key_type>>(keys);
-}
-
+// Operations: O(log(n)/d)
 template <typename K, typename V, typename C>
 template <typename... Args>
 void trie<K, V, C>::insert_impl(std::pair<iterator, bool> & out, key_type const & key, Args &&... args) {
@@ -92,30 +57,53 @@ void trie<K, V, C>::insert_impl(std::pair<iterator, bool> & out, key_type const
   out.first.push(make_end_aware_iterator(it, impl_.end()));
 }
 
+// n := total elements
+// d := average depth
+// Operations: O(log(n))
 template <typename K, typename V, typename C>
 template <typename KS>
 auto trie<K, V, C>::insert(KS const & keys, mapped_type const & value) -> std::pair<iterator, bool> {
   std::pair<iterator, bool> rval{this, false};
-  auto size = std::distance(std::begin(keys), std::end(keys));
   for ( key_type const & key : keys ) {
-    if (size-- == 1) { insert_impl(rval, key, value); }
-    else { insert_impl(rval, key); }
+    rval.first.stk.top()->insert_impl(rval, key);
   }
+  if (rval.second) { *rval.first.stk.top() = value; }
   return rval;
 }
 
-template <typename K, typename V, typename C>
-auto trie<K, V, C>::insert(std::initializer_list<key_type> keys, mapped_type const & value) -> std::pair<iterator, bool> {
-  return insert<std::initializer_list<key_type>>(keys, value);
-}
-
 // n := total elements
 // d := average depth
 // Operations: O(log(n))
 template <typename K, typename V, typename C>
-auto trie<K, V, C>::insert(key_type const & key, mapped_type const & value) -> std::pair<iterator, bool> {
+template <typename KS, typename... Args>
+auto trie<K, V, C>::emplace(KS const & keys, Args &&... args) -> std::pair<iterator, bool> {
   std::pair<iterator, bool> rval{this, false};
-  insert_impl(rval, key, value);
+  for ( key_type const & key : keys ) {
+    rval.first.stk.top()->insert_impl(rval, key);
+  }
+  if (rval.second) { rval.first.stk.top()->value() = {std::forward<Args>(args)...}; }
+  return rval;
+}
+
+template <typename K, typename V, typename C>
+template <typename KS>
+auto trie<K, V, C>::find(KS const & keys) -> iterator {
+  iterator rval{this};
+  for (auto & key : keys) {
+    auto & top = rval.stk.top()->impl_;
+    auto it = top.find(key), end = top.end();
+    if ( it == end ) { return {}; }
+    rval.push({it, end});
+  }
+  return rval;
+}
+
+template <typename K, typename V, typename C>
+auto trie<K, V, C>::find(key_type const & key) -> iterator {
+  auto it = impl_.find(key), end = impl_.end();
+  if ( it == end ) { return {}; }
+  iterator rval{this};
+  rval.push({it, end});
   return rval;
 }
 
@@ -132,3 +120,14 @@ void trie<K, V, C>::clear() {
   value_ = mapped_type{};
   impl_.clear();
 }
+
+template <typename K, typename V, typename C>
+void trie<K, V, C>::drop(iterator it) {
+  if (it == end()) return;
+  it.stk.top()->clear();
+  if (!it.iters.empty()) {
+    auto to_erase = it.iters.top().current();
+    it.pop();
+    it.stk.top()->impl_.erase(to_erase);
+  }
+}