Browse Source

Initial code dump.
- Contains insert, index, iterate (Pre,Post,In)

Samuel Jaffe 8 năm trước cách đây
mục cha
commit
ef5d2e867b
5 tập tin đã thay đổi với 706 bổ sung0 xóa
  1. 111 0
      trie.hpp
  2. 60 0
      trie.t.h
  3. 285 0
      trie.xcodeproj/project.pbxproj
  4. 90 0
      trie_impl.hpp
  5. 160 0
      trie_iterator.hpp

+ 111 - 0
trie.hpp

@@ -0,0 +1,111 @@
+//
+//  trie.hpp
+//  trie
+//
+//  Created by Sam Jaffe on 6/16/17.
+//
+
+#pragma once
+
+#include "pointers/const_propogating_ptr.hpp"
+#include "pointers/not_null.hpp"
+#include "pointers/value_ptr.hpp"
+#include <map>
+
+template <typename K, typename V, typename Compare = std::less<K>>
+class trie;
+
+template <typename Trie, typename V, typename Iter>
+class trie_iterator;
+template <typename Trie, typename V, typename Iter>
+class trie_reverse_iterator;
+
+template <typename K, typename V, typename Compare>
+class trie {
+private:
+  using self_t = trie<K, V, Compare>;
+  using layer_t = value_ptr<self_t>;
+  using backing_t = std::map<K, const_propogating_ptr<not_null<layer_t>>, Compare>;
+  template <typename KS>
+  using is_collection_t = typename std::enable_if<std::is_same<K, std::decay_t<decltype(*std::begin(std::declval<KS>()))>>::value>::type;
+
+public:
+  using key_type = K;
+  using mapped_type = V;
+  using key_compare = Compare;
+  
+  using local_iterator = typename backing_t::iterator;
+  using local_const_iterator = typename backing_t::const_iterator;
+  using local_reverse_iterator = typename backing_t::reverse_iterator;
+  using local_const_reverse_iterator = typename backing_t::const_reverse_iterator;
+  
+  using iterator = trie_iterator<self_t, mapped_type, local_iterator>;
+  using const_iterator = trie_iterator<self_t const, mapped_type const, local_const_iterator>;
+  using post_iterator = trie_iterator<self_t, mapped_type, local_reverse_iterator>;
+  using const_post_iterator = trie_iterator<self_t const, mapped_type const, local_const_reverse_iterator>;
+  using reverse_iterator = trie_reverse_iterator<self_t, mapped_type, local_reverse_iterator>;
+  using const_reverse_iterator = trie_reverse_iterator<self_t const, mapped_type const,  local_const_reverse_iterator>;
+public:
+  trie() = default;
+  trie(mapped_type const & value);
+  trie(trie const & other);
+  trie(trie && other) = default;
+  self_t & operator=(mapped_type const & value);
+  self_t & operator=(trie const & value);
+  self_t & operator=(trie && value) = default;
+
+  operator mapped_type &() { return value_; }
+  operator mapped_type const &() const { return value_; }
+  mapped_type & value() { return value_; }
+  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);
+  
+  template <typename KS>
+  void insert(KS const & keys, mapped_type const & value);
+  void insert(std::initializer_list<key_type> keys, mapped_type const & value);
+  std::pair<iterator, bool> insert(key_type const & key, mapped_type const & value);
+  
+  iterator begin() { return {this}; }
+  iterator end() { return {}; }
+  const_iterator begin() const { return {this}; }
+  const_iterator end() const { return {}; }
+  const_iterator cbegin() const { return begin(); }
+  const_iterator cend() const { return end(); }
+  reverse_iterator rbegin() { return {this}; }
+  reverse_iterator rend() { return {}; }
+  const_reverse_iterator rbegin() const { return {this}; }
+  const_reverse_iterator rend() const { return {}; }
+  const_reverse_iterator crbegin() const { return rbegin(); }
+  const_reverse_iterator crend() const { return rend(); }
+  
+  local_iterator local_begin() { return impl_.begin(); }
+  local_iterator local_end() { return impl_.end(); }
+  local_const_iterator local_begin() const { return impl_.begin(); }
+  local_const_iterator local_end() const { return impl_.end(); }
+
+  local_reverse_iterator local_rbegin() { return impl_.rbegin(); }
+  local_reverse_iterator local_rend() { return impl_.rend(); }
+  local_const_reverse_iterator local_rbegin() const { return impl_.rbegin(); }
+  local_const_reverse_iterator local_rend() const { return impl_.rend(); }
+  
+  void clear() { impl_.clear(); value_ = mapped_type{}; }
+private:
+  friend bool operator==(trie const & lhs, trie const & rhs) {
+    auto it1 = lhs.begin(), it2 = rhs.begin(), end1 = lhs.end(), end2 = lhs.end();
+    for ( ; it1 != end1 && it2 != end2 && it1 == it2; ++it1, ++it2 );
+    return it1 == end1 && it2 == end2;
+  }
+  friend void swap(trie & lhs, trie & rhs) {
+    using std::swap;
+    swap(lhs.value_, rhs.value_);
+    swap(lhs.impl_, rhs.impl_);
+  }
+  mapped_type value_;
+  backing_t impl_;
+};
+
+#include "trie_impl.hpp"

+ 60 - 0
trie.t.h

@@ -0,0 +1,60 @@
+//
+//  trie.t.h
+//  trie
+//
+//  Created by Sam Jaffe on 6/16/17.
+//
+
+#pragma once
+
+#include <cxxtest/TestSuite.h>
+
+#include "trie.hpp"
+
+class trie_TestSuite : public CxxTest::TestSuite {
+private:
+  trie<int, int> data;
+  
+  void setUp() override {
+    data.clear();
+    data = -1;
+    data[0] = 5;
+    data[1] = 2;
+    data[{0, 1}] = 4;
+  }
+public:
+  void testIterationIsPreOrder() {
+    auto it = data.cbegin();
+    TS_ASSERT_EQUALS(*it, -1);
+    TS_ASSERT_EQUALS(*++it, 5);
+    TS_ASSERT_EQUALS(*++it, 4);
+    TS_ASSERT_EQUALS(*++it, 2);
+    TS_ASSERT_EQUALS(++it, data.cend());
+  }
+  
+  void testPostIterationIsPostOrder() {
+    auto it = trie<int, int>::const_post_iterator{&data};
+    decltype(it) end{};
+    TS_ASSERT_EQUALS(*it, -1);
+    TS_ASSERT_EQUALS(*++it, 2);
+    TS_ASSERT_EQUALS(*++it, 5);
+    TS_ASSERT_EQUALS(*++it, 4);
+    TS_ASSERT_EQUALS(++it, end);
+  }
+
+  void testReverseIterationIsPostOrder() {
+    auto it = data.crbegin();
+    TS_ASSERT_EQUALS(*it, 2);
+    TS_ASSERT_EQUALS(*++it, 4);
+    TS_ASSERT_EQUALS(*++it, 5);
+    TS_ASSERT_EQUALS(*++it, -1);
+    TS_ASSERT_EQUALS(++it, data.crend());
+  }
+
+  void testCopyCtorIsDeepCopy() {
+    trie<int, int> copy{data};
+    TS_ASSERT_EQUALS(data, copy);
+    copy[{0, 1}] = 3;
+    TS_ASSERT_DIFFERS(data, copy);
+  }
+};

+ 285 - 0
trie.xcodeproj/project.pbxproj

@@ -0,0 +1,285 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		CD46DF461EF3FDCE0092D121 /* trie_tc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD46DF441EF3FDCE0092D121 /* trie_tc.cpp */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+		CD46DF351EF3FD540092D121 /* CopyFiles */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = /usr/share/man/man1/;
+			dstSubfolderSpec = 0;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 1;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+		CD46DF371EF3FD540092D121 /* trie_tc */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = trie_tc; sourceTree = BUILT_PRODUCTS_DIR; };
+		CD46DF441EF3FDCE0092D121 /* trie_tc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = trie_tc.cpp; sourceTree = "<group>"; };
+		CD46DF451EF3FDCE0092D121 /* trie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = trie.hpp; sourceTree = "<group>"; };
+		CD46DF471EF3FDDD0092D121 /* trie.t.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = trie.t.h; sourceTree = "<group>"; };
+		CD46DF481EF47C520092D121 /* trie_impl.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = trie_impl.hpp; sourceTree = "<group>"; };
+		CD46DF4A1EF497E30092D121 /* trie_iterator.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = trie_iterator.hpp; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		CD46DF341EF3FD540092D121 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		CD46DF2E1EF3FD540092D121 = {
+			isa = PBXGroup;
+			children = (
+				CD46DF421EF3FD6D0092D121 /* src */,
+				CD46DF411EF3FD670092D121 /* test */,
+				CD46DF381EF3FD540092D121 /* Products */,
+			);
+			sourceTree = "<group>";
+		};
+		CD46DF381EF3FD540092D121 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				CD46DF371EF3FD540092D121 /* trie_tc */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		CD46DF411EF3FD670092D121 /* test */ = {
+			isa = PBXGroup;
+			children = (
+				CD46DF471EF3FDDD0092D121 /* trie.t.h */,
+				CD46DF441EF3FDCE0092D121 /* trie_tc.cpp */,
+			);
+			name = test;
+			sourceTree = "<group>";
+		};
+		CD46DF421EF3FD6D0092D121 /* src */ = {
+			isa = PBXGroup;
+			children = (
+				CD46DF451EF3FDCE0092D121 /* trie.hpp */,
+				CD46DF4A1EF497E30092D121 /* trie_iterator.hpp */,
+				CD46DF481EF47C520092D121 /* trie_impl.hpp */,
+			);
+			name = src;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		CD46DF361EF3FD540092D121 /* trie_tc */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = CD46DF3E1EF3FD540092D121 /* Build configuration list for PBXNativeTarget "trie_tc" */;
+			buildPhases = (
+				CD46DF431EF3FD9A0092D121 /* ShellScript */,
+				CD46DF331EF3FD540092D121 /* Sources */,
+				CD46DF341EF3FD540092D121 /* Frameworks */,
+				CD46DF351EF3FD540092D121 /* CopyFiles */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = trie_tc;
+			productName = trie;
+			productReference = CD46DF371EF3FD540092D121 /* trie_tc */;
+			productType = "com.apple.product-type.tool";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		CD46DF2F1EF3FD540092D121 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 0720;
+				ORGANIZATIONNAME = "Sam Jaffe";
+				TargetAttributes = {
+					CD46DF361EF3FD540092D121 = {
+						CreatedOnToolsVersion = 7.2.1;
+					};
+				};
+			};
+			buildConfigurationList = CD46DF321EF3FD540092D121 /* Build configuration list for PBXProject "trie" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = English;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+			);
+			mainGroup = CD46DF2E1EF3FD540092D121;
+			productRefGroup = CD46DF381EF3FD540092D121 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				CD46DF361EF3FD540092D121 /* trie_tc */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXShellScriptBuildPhase section */
+		CD46DF431EF3FD9A0092D121 /* ShellScript */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+				"$(SRCROOT)/trie.t.h",
+			);
+			outputPaths = (
+				"$(SRCROOT)/trie_tc.cpp",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "cxxtestgen --error-printer -o trie_tc.cpp trie.t.h";
+		};
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		CD46DF331EF3FD540092D121 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				CD46DF461EF3FDCE0092D121 /* trie_tc.cpp in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+		CD46DF3C1EF3FD540092D121 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				CODE_SIGN_IDENTITY = "-";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				MACOSX_DEPLOYMENT_TARGET = 10.10;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = macosx;
+			};
+			name = Debug;
+		};
+		CD46DF3D1EF3FD540092D121 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				CODE_SIGN_IDENTITY = "-";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				MACOSX_DEPLOYMENT_TARGET = 10.10;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = macosx;
+			};
+			name = Release;
+		};
+		CD46DF3F1EF3FD540092D121 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				HEADER_SEARCH_PATHS = /usr/local/include;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				USER_HEADER_SEARCH_PATHS = "../../types ../";
+			};
+			name = Debug;
+		};
+		CD46DF401EF3FD540092D121 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				HEADER_SEARCH_PATHS = /usr/local/include;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				USER_HEADER_SEARCH_PATHS = "../../types ../";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		CD46DF321EF3FD540092D121 /* Build configuration list for PBXProject "trie" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				CD46DF3C1EF3FD540092D121 /* Debug */,
+				CD46DF3D1EF3FD540092D121 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		CD46DF3E1EF3FD540092D121 /* Build configuration list for PBXNativeTarget "trie_tc" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				CD46DF3F1EF3FD540092D121 /* Debug */,
+				CD46DF401EF3FD540092D121 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = CD46DF2F1EF3FD540092D121 /* Project object */;
+}

+ 90 - 0
trie_impl.hpp

@@ -0,0 +1,90 @@
+//
+//  trie_impl.hpp
+//  trie
+//
+//  Created by Sam Jaffe on 6/16/17.
+//
+
+#pragma once
+
+#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)
+// Operations: O(n*d)
+template <typename K, typename V, typename C>
+trie<K, V, C>::trie(trie const & other) {
+  for (const_iterator it = other.begin(), end = other.end(); it != end; ++it) {
+    insert(it.keys, *it);
+  }
+}
+
+template <typename K, typename V, typename C>
+auto trie<K, V, C>::operator=(mapped_type const & value) -> self_t & {
+  value_ = value;
+  return *this;
+}
+
+template <typename K, typename V, typename C>
+auto trie<K, V, C>::operator=(trie const & other) -> self_t & {
+  for (const_iterator it = other.begin(), end = other.end(); it != end; ++it) {
+    insert(it.keys, *it);
+  }
+  return *this;
+}
+
+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;
+}
+
+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);
+}
+
+template <typename K, typename V, typename C>
+auto trie<K, V, C>::operator[](std::initializer_list<key_type> keys) -> self_t & {
+  self_t * rec = this;
+  for ( key_type const & key : keys ) {
+    rec = &(*rec)[key];
+  }
+  return *rec;
+}
+
+template <typename K, typename V, typename C>
+template <typename KS>
+auto trie<K, V, C>::insert(KS const & keys, mapped_type const & value) -> void {
+  operator[](keys) = value;
+}
+
+template <typename K, typename V, typename C>
+auto trie<K, V, C>::insert(std::initializer_list<key_type> keys, mapped_type const & value) -> void {
+  operator[](keys) = value;
+}
+
+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> {
+  auto it = impl_.lower_bound(key);
+  if ( it == impl_.end() || key_compare()(key, it->first) ) {
+    return { impl_.emplace_hint(it, key, make_value<self_t>(value)), true };
+  }
+  return { it, false };
+}

+ 160 - 0
trie_iterator.hpp

@@ -0,0 +1,160 @@
+//
+//  trie_iterator.hpp
+//  trie
+//
+//  Created by Sam Jaffe on 6/16/17.
+//
+
+#pragma once
+
+#include "trie.hpp"
+
+#include "iterator/end_aware_iterator.hpp"
+#include <stack>
+
+namespace detail {
+  template <typename Iter>
+  struct trie_iterator_next {
+    template <typename Trie>
+    ::iterator::end_aware_iterator<Iter> operator()(Trie & tr) {
+      return { tr.local_begin(), tr.local_end() };
+    }
+  };
+
+  template <typename Iter>
+  struct trie_iterator_next<std::reverse_iterator<Iter>> {
+    template <typename Trie>
+    ::iterator::end_aware_iterator<std::reverse_iterator<Iter>> operator()(Trie & tr) {
+      return { tr.local_rbegin(), tr.local_rend() };
+    }
+  };
+}
+
+template <typename Trie, typename V, typename Iter>
+class trie_iterator {
+private:
+  using impl_t = ::iterator::end_aware_iterator<Iter>;
+public:
+  using value_type = V;
+  using reference = value_type &;
+  using pointer = value_type *;
+  using difference_type = std::ptrdiff_t;
+  using iterator_category = std::forward_iterator_tag;
+public:
+  trie_iterator() : done{true} {}
+  trie_iterator(Trie * tr) { stk.push(tr); }
+  reference operator*() const { return *stk.top(); }
+  trie_iterator & operator++() {
+    if (done) return *this;
+    if ( iters.empty() || can_recurse() ) { recurse(); }
+    else { advance(); }
+    return *this;
+  }
+private:
+  bool can_recurse() { return !next().done(); }
+  void recurse() {
+    auto it = next();
+    iters.push(it);
+    keys.push_back(it->first);
+    stk.push(it->second.get());
+  }
+  impl_t next() { return detail::trie_iterator_next<Iter>()(*stk.top()); }
+  void advance() {
+    ++iters.top();
+    while (iters.top().done()) {
+      stk.pop();
+      iters.pop();
+      keys.pop_back();
+      if ( iters.empty() ) break;
+      ++iters.top();
+    }
+    if (!iters.empty()) {
+      stk.top() = iters.top()->second.get();
+      keys.back() = iters.top()->first;
+    } else {
+      done = true;
+    }
+  }
+  friend bool operator!=(trie_iterator const & lhs, trie_iterator const & rhs) {
+    return !(lhs == rhs);
+  }
+  friend bool operator==(trie_iterator const & lhs, trie_iterator const & rhs) {
+    if ( lhs.done && rhs.done ) return true;
+    else if (lhs.keys != rhs.keys) return false;
+    else if ( lhs.done != rhs.done ) return false;
+    else return *lhs == *rhs;
+  }
+  friend Trie;
+  std::stack<Trie *> stk;
+  std::list<typename Trie::key_type> keys;
+  std::stack<impl_t> iters;
+  bool done{false};
+};
+
+template <typename Trie, typename V, typename Iter>
+class trie_reverse_iterator {
+private:
+  using impl_t = ::iterator::end_aware_iterator<Iter>;
+public:
+  using value_type = V;
+  using reference = value_type &;
+  using pointer = value_type *;
+  using difference_type = std::ptrdiff_t;
+  using iterator_category = std::forward_iterator_tag;
+public:
+  trie_reverse_iterator() : done{true} {}
+  trie_reverse_iterator(Trie * tr) {
+    stk.push(tr);
+    if (can_recurse()) recurse();
+  }
+  reference operator*() const { return *stk.top(); }
+  trie_reverse_iterator & operator++() {
+    if (done || iters.empty()) { done = true; return *this; }
+    advance();
+    return *this;
+  }
+private:
+  bool can_recurse() { return !next().done(); }
+  void recurse() {
+    do {
+      auto it = next();
+      iters.push(it);
+      keys.push_back(it->first);
+      stk.push(it->second.get());
+    } while ( can_recurse() );
+  }
+  impl_t next() { return detail::trie_iterator_next<Iter>()(*stk.top()); }
+  void advance() {
+    ++iters.top();
+    if (iters.top().done()) {
+      while (iters.top().done()) {
+        stk.pop();
+        iters.pop();
+        keys.pop_back();
+        if ( iters.empty() ) break;
+      }
+      if (!iters.empty()) {
+        stk.top() = iters.top()->second.get();
+        keys.back() = iters.top()->first;
+      }
+    } else {
+      stk.top() = iters.top()->second.get();
+      keys.back() = iters.top()->first;
+      if (can_recurse()) recurse();
+    }
+  }
+  friend bool operator!=(trie_reverse_iterator const & lhs, trie_reverse_iterator const & rhs) {
+    return !(lhs == rhs);
+  }
+  friend bool operator==(trie_reverse_iterator const & lhs, trie_reverse_iterator const & rhs) {
+    if ( lhs.done && rhs.done ) return true;
+    else if (lhs.keys != rhs.keys) return false;
+    else if ( lhs.done != rhs.done ) return false;
+    else return *lhs == *rhs;
+  }
+  friend Trie;
+  std::stack<Trie *> stk;
+  std::list<typename Trie::key_type> keys;
+  std::stack<impl_t> iters;
+  bool done{false};
+};