libwordring
trie_base.hpp
1 #pragma once
2 
3 #include <wordring/trie/list_trie_iterator.hpp>
4 #include <wordring/trie/trie_base_iterator.hpp>
5 #include <wordring/trie/trie_construct_iterator.hpp>
6 #include <wordring/trie/trie_heap.hpp>
7 
8 #include <algorithm>
9 #include <cassert>
10 #include <climits>
11 #include <cstdint>
12 #include <istream>
13 #include <iterator>
14 #include <memory>
15 #include <ostream>
16 #include <stdexcept>
17 #include <type_traits>
18 #include <utility>
19 
20 namespace wordring::detail
21 {
22  // ------------------------------------------------------------------------
23  // trie_base
24  // ------------------------------------------------------------------------
25 
63  template <typename Allocator = std::allocator<trie_node>>
64  class trie_base : public trie_heap<Allocator>
65  {
66  template <typename Allocator1>
67  friend std::ostream& operator<<(std::ostream&, trie_base<Allocator1> const&);
68 
69  template <typename Allocator1>
70  friend std::istream& operator>>(std::istream&, trie_base<Allocator1>&);
71 
72  protected:
74 
75  using typename base_type::container;
76  using typename base_type::label_vector;
77  using typename base_type::index_type;
78  using typename base_type::node_type;
79 
80  using base_type::null_value;
81 
82  public:
83  using label_type = typename base_type::label_type;
84  using value_type = std::uint32_t;
85  using size_type = typename container::size_type;
86  using allocator_type = Allocator;
88  using const_reference = trie_value_proxy const;
90 
91  public:
92  using typename base_type::serialize_iterator;
93 
95  using base_type::ibegin;
96  using base_type::iend;
97  using base_type::clear;
98 
99  protected:
100  using base_type::assign;
101  using base_type::limit;
102  using base_type::free;
103  using base_type::is_tail;
104  using base_type::has_child;
105  using base_type::has_null;
107  using base_type::at;
108  using base_type::add;
109 
110  using base_type::m_c;
111 
112  public:
116  : base_type()
117  {
118  }
119 
124  explicit trie_base(allocator_type const& alloc)
125  : base_type(alloc)
126  {
127  }
128 
140  template <typename InputIterator, typename std::enable_if_t<std::is_integral_v<typename std::iterator_traits<InputIterator>::value_type>, std::nullptr_t> = nullptr>
141  trie_base(InputIterator first, InputIterator last, allocator_type const& alloc = allocator_type())
142  : base_type(alloc)
143  {
144  assign(first, last);
145  }
146 
156  template <typename ForwardIterator, typename std::enable_if_t<std::negation_v<std::is_integral<typename std::iterator_traits<ForwardIterator>::value_type>>, std::nullptr_t> = nullptr>
157  trie_base(ForwardIterator first, ForwardIterator last, allocator_type const& alloc = allocator_type())
158  : base_type(alloc)
159  {
160  assign(first, last);
161  }
162 
170  trie_base(std::initializer_list<trie_node> il, allocator_type const& alloc = allocator_type())
171  : base_type(il, alloc)
172  {
173  }
174 
187  template <typename InputIterator, typename std::enable_if_t<std::is_integral_v<typename std::iterator_traits<InputIterator>::value_type>, std::nullptr_t> = nullptr>
188  void assign(InputIterator first, InputIterator last)
189  {
190  base_type::assign(first, last);
191  }
192 
202  template <typename ForwardIterator, typename std::enable_if_t<std::negation_v<std::is_integral<typename std::iterator_traits<ForwardIterator>::value_type>>, std::nullptr_t> = nullptr>
203  void assign(ForwardIterator first, ForwardIterator last)
204  {
205  using iterator_category = typename std::iterator_traits<ForwardIterator>::iterator_category;
206 
207  static_assert(
208  std::is_same_v<iterator_category, std::forward_iterator_tag>
209  || std::is_same_v<iterator_category, std::bidirectional_iterator_tag>
210  || std::is_same_v<iterator_category, std::random_access_iterator_tag>);
211 
212  clear();
213 
214  if (!std::is_sorted(first, last) || std::adjacent_find(first, last) != last)
215  {
216  while (first != last)
217  {
218  insert(*first);
219  ++first;
220  }
221  return;
222  }
223 
224  using list_iterator = const_list_trie_iterator<ForwardIterator>;
225  using construct_iterator = trie_construct_iterator<list_iterator>;
226 
227  auto li = list_iterator(first, last);
228  auto it = construct_iterator(li);
229 
230  while (!it.empty() && !it.children().empty())
231  {
232  auto view = it.parent();
233  auto it1 = lookup(view.first, view.second);
234  if (it1.first.m_index == 0) it1.first.m_index = 1; // rootの場合。
235  add(it1.first.m_index, it.children());
236  ++it;
237  }
238 
239  m_c.front().m_base = std::distance(first, last);
240  }
241 
242  // 要素アクセス --------------------------------------------------------
243 
258  {
259  node_type* d = m_c.data();
260  // 子遷移が有り、なおかつ文字列終端の場合に対応する。
261  index_type base = (d + pos.m_index)->m_base;
262  index_type idx = (base <= 0)
263  ? pos.m_index
264  : base + null_value;
265 
266  return reference(d + idx);
267  }
268 
277  const_reference at(const_iterator pos) const
278  {
279  return const_cast<trie_base*>(this)->at(pos);
280  }
281 
295  template <typename InputIterator>
296  reference at(InputIterator first, InputIterator last)
297  {
298  auto it = find(first, last);
299  if (it == cend()) throw std::out_of_range("");
300 
301  node_type* d = m_c.data();
302  // 子遷移が有り、なおかつ文字列終端の場合に対応する。
303  index_type base = (d + it.m_index)->m_base;
304  index_type idx = (base <= 0)
305  ? it.m_index
306  : base + null_value;
307 
308  return reference(d + idx);
309  }
310 
320  template <typename InputIterator>
321  const_reference at(InputIterator first, InputIterator last) const
322  {
323  return const_cast<trie_base*>(this)->at(first, last);
324  }
325 
338  template <typename Key>
339  reference at(Key const& key)
340  {
341  return at(std::begin(key), std::end(key));
342  }
343 
354  template <typename Key>
355  const_reference const at(Key const& key) const
356  {
357  return at(std::begin(key), std::end(key));
358  }
359 
370  template <typename Key>
371  reference operator[](Key const& key)
372  {
373  auto it = find(key);
374  if (it == cend()) it = insert(key);
375 
376  node_type* d = m_c.data();
377  // 子遷移が有り、なおかつ文字列終端の場合に対応する。
378  index_type base = (d + it.m_index)->m_base;
379  index_type idx = (base <= 0)
380  ? it.m_index
381  : base + null_value;
382 
383  return reference(d + idx);
384  }
385 
386  // イテレータ ----------------------------------------------------------
387 
394  const_iterator begin() const noexcept { return const_iterator(m_c, 1); }
395 
402  const_iterator cbegin() const noexcept { return const_iterator(m_c, 1); }
403 
411  const_iterator end() const noexcept { return const_iterator(m_c, 0); }
412 
420  const_iterator cend() const noexcept { return const_iterator(m_c, 0); }
421 
422  // 容量 ---------------------------------------------------------------
423 
428  bool empty() const noexcept { return size() == 0; }
429 
434  size_type size() const noexcept { return m_c.front().m_base; }
435 
440  static constexpr size_type max_size() noexcept
441  {
442  return std::numeric_limits<std::int32_t>::max() / sizeof(node_type);
443  }
444 
445  // 変更 ---------------------------------------------------------------
446 
457  template <typename InputIterator>
458  const_iterator insert(InputIterator first, InputIterator last, value_type value = 0)
459  {
460  assert(value <= static_cast<value_type>(std::numeric_limits<int32_t>::max()));
461 
462  if (first == last) return cend();
463 
464  index_type parent = 1;
465 
466  // 登録済み遷移をスキップする
467  for (index_type idx = at(parent, static_cast<std::uint8_t>(*first)); idx != 0; idx = at(parent, static_cast<std::uint8_t>(*first)))
468  {
469  parent = idx;
470  ++first;
471  if (first == last) break;
472  }
473  if (first != last) ++m_c.front().m_base;
474 
475  // 空遷移の解決
476  if (has_child(parent)) // 既存遷移有り、新規文字列無し
477  {
478  if (first == last && !has_null(parent))
479  {
480  index_type base = add(parent, null_value);
481  assert((m_c.data() + base + null_value)->m_base == 0);
482  (m_c.data() + base + null_value)->m_base = -static_cast<index_type>(value);
483  ++m_c.front().m_base;
484  }
485  }
486  else if (parent != 1 && !has_null(parent) && first != last) // 後続遷移無し、新規文字列有り
487  {
488  index_type val = (m_c.data() + parent)->m_base;
489  assert(val <= 0);
490  index_type base = add(parent, null_value);
491  assert((m_c.data() + base + null_value)->m_base == 0);
492  (m_c.data() + base + null_value)->m_base = val;
493  }
494 
495  // 新規文字列による遷移を追加
496  index_type base = 0;
497  while (first != last)
498  {
499  std::uint16_t label = static_cast<std::uint8_t>(*first);
500  ++first;
501  base = add(parent, label);
502  parent = base + label;
503  }
504  if (base != 0)
505  {
506  assert((m_c.data() + parent)->m_base == 0);
507  (m_c.data() + parent)->m_base = -static_cast<index_type>(value);
508  }
509 
510  assert(parent != 1);
511 
512  return const_iterator(m_c, parent);
513  }
514 
524  template <typename Key>
525  const_iterator insert(Key const& key, value_type value = 0)
526  {
527  return insert(std::begin(key), std::end(key), value);
528  }
529 
537  {
538  index_type idx = pos.m_index;
539  assert(0 <= idx && idx < limit());
540 
541  if (idx <= 1 || !is_tail(idx)) return;
542 
543  while (true)
544  {
545  if (has_null(idx))
546  {
547  index_type i = (m_c.data() + idx)->m_base + null_value;
548  assert((m_c.data() + i)->m_check == idx);
549 
550  free(i);
551  if (!has_child(idx)) (m_c.data() + idx)->m_base = 0;
552 
553  break;
554  }
555 
556  index_type parent = 0;
557  if (!has_sibling(idx)) parent = (m_c.data() + idx)->m_check; // 兄弟が無い場合
558  assert(0 <= parent && parent < limit());
559 
560  if (idx != 1) free(idx);
561  if (parent == 0) break;
562 
563  idx = parent;
564  }
565 
566  --m_c.front().m_base;
567  if (empty()) clear();
568  }
569 
577  template <typename InputIterator>
578  void erase(InputIterator first, InputIterator last)
579  {
580  erase(find(first, last));
581  }
582 
589  template <typename Key>
590  void erase(Key const& key)
591  {
592  erase(std::begin(key), std::end(key));
593  }
594 
595  void swap(trie_base& other)
596  {
597  base_type::swap(other);
598  }
599 
600  // 検索 ---------------------------------------------------------------
601 
602  protected:
617  template <typename InputIterator>
618  auto lookup(InputIterator first, InputIterator last, std::uint32_t& i) const
619  {
620  assert(i == 0);
621 
622  index_type parent = 1;
623 
624  while (first != last)
625  {
626  std::uint16_t ch = static_cast<std::uint8_t>(*first);
627  index_type idx = at(parent, ch);
628  if (idx == 0) break;
629  ++first;
630  ++i;
631  parent = idx;
632  assert(1 <= parent && parent < limit());
633  }
634 
635  return std::make_pair(const_iterator(m_c, parent), first);
636  }
637 
638  public:
648  template <typename InputIterator>
649  auto lookup(InputIterator first, InputIterator last) const
650  {
651  std::uint32_t i = 0;
652  return lookup(first, last, i);
653  }
654 
663  template <typename InputIterator>
664  const_iterator search(InputIterator first, InputIterator last) const
665  {
666  auto pair = lookup(first, last);
667 
668  return (pair.second == last)
669  ? pair.first
670  : cend();
671  }
672 
681  template <typename Key>
682  const_iterator search(Key const& key) const
683  {
684  return search(std::begin(key), std::end(key));
685  }
686 
696  template <typename InputIterator>
697  const_iterator find(InputIterator first, InputIterator last) const
698  {
699  auto pair = lookup(first, last);
700 
701  auto it = pair.second;
702  index_type idx = pair.first.m_index;
703 
704  return (it == last && is_tail(idx))
705  ? pair.first
706  : cend();
707  }
708 
717  template <typename Key>
718  const_iterator find(Key const& key) const
719  {
720  return find(std::begin(key), std::end(key));
721  }
722 
730  template <typename InputIterator>
731  bool contains(InputIterator first, InputIterator last) const
732  {
733  auto pair = lookup(first, last);
734 
735  auto it = pair.second;
736  index_type idx = pair.first.m_index;
737 
738  return !(it != last || !is_tail(idx));
739  }
740 
747  template <typename Key>
748  bool contains(Key const& key) const
749  {
750  return contains(std::begin(key), std::end(key));
751  }
752  };
753 
758  template <typename Allocator1>
759  inline std::ostream& operator<<(std::ostream& os, trie_base<Allocator1> const& trie)
760  {
761  typename trie_base<Allocator1>::base_type const& heap = trie;
762  return os << heap;
763  }
764 
769  template <typename Allocator1>
770  inline std::istream& operator>>(std::istream& is, trie_base<Allocator1>& trie)
771  {
772  typename trie_base<Allocator1>::base_type& heap = trie;
773  return is >> heap;
774  }
775 }
wordring::detail::trie_base::at
const_reference at(const_iterator pos) const
葉の値への参照を返す
Definition: trie_base.hpp:277
wordring::detail::trie_base::trie_base
trie_base(std::initializer_list< trie_node > il, allocator_type const &alloc=allocator_type())
初期化子リストからの構築
Definition: trie_base.hpp:170
wordring::detail::trie_base::search
const_iterator search(InputIterator first, InputIterator last) const
前方一致検索
Definition: trie_base.hpp:664
wordring::detail::trie_base::size
size_type size() const noexcept
格納しているキー文字列数を調べる
Definition: trie_base.hpp:434
wordring::detail::trie_heap::has_null
bool has_null(index_type parent) const
Definition: trie_heap.hpp:690
wordring::detail::trie_base::trie_base
trie_base(InputIterator first, InputIterator last, allocator_type const &alloc=allocator_type())
直列化データからの構築
Definition: trie_base.hpp:141
wordring::detail::trie_heap::free
index_type free(index_type idx, index_type before=0)
Definition: trie_heap.hpp:526
wordring::detail::trie_base::erase
void erase(InputIterator first, InputIterator last)
キー文字列を削除する
Definition: trie_base.hpp:578
wordring::detail::trie_heap::is_tail
bool is_tail(index_type idx) const
Definition: trie_heap.hpp:651
wordring::detail::trie_base::trie_base
trie_base()
空のコンテナを構築する
Definition: trie_base.hpp:115
wordring::detail::trie_base::cend
const_iterator cend() const noexcept
根の終端を指すイテレータを返す
Definition: trie_base.hpp:420
wordring::detail::trie_base::insert
const_iterator insert(Key const &key, value_type value=0)
キー文字列を挿入する
Definition: trie_base.hpp:525
wordring::detail::trie_base::at
reference at(Key const &key)
葉の値への参照を返す
Definition: trie_base.hpp:339
wordring::detail::trie_base::operator>>
friend std::istream & operator>>(std::istream &, trie_base< Allocator1 > &)
ストリームから入力する
Definition: trie_base.hpp:770
wordring::detail::trie_heap::has_child
bool has_child(index_type parent) const
Definition: trie_heap.hpp:664
wordring::detail::const_trie_base_iterator
Definition: trie_base_iterator.hpp:8
wordring::detail::trie_base::trie_base
trie_base(ForwardIterator first, ForwardIterator last, allocator_type const &alloc=allocator_type())
文字列リストからの構築
Definition: trie_base.hpp:157
wordring::trie
basic_trie< Label, detail::trie_base< Allocator > > trie
メモリー使用量削減を目標とする汎用Trie
Definition: trie.hpp:1026
wordring::detail::trie_base::erase
void erase(const_iterator pos)
キー文字列を削除する
Definition: trie_base.hpp:536
wordring::detail::trie_construct_iterator
Definition: trie_construct_iterator.hpp:16
wordring::static_vector< std::uint16_t, 257 >
wordring::detail::operator<<
std::ostream & operator<<(std::ostream &os, stable_trie_base< Allocator1 > const &trie)
ストリームへ出力する
Definition: stable_trie_base.hpp:752
wordring::detail::trie_base::at
const const_reference at(Key const &key) const
葉の値への参照を返す
Definition: trie_base.hpp:355
wordring::detail::trie_base::operator[]
reference operator[](Key const &key)
葉の値への参照を返す
Definition: trie_base.hpp:371
wordring::detail::trie_base::at
const_reference at(InputIterator first, InputIterator last) const
葉の値への参照を返す
Definition: trie_base.hpp:321
wordring::detail::trie_base::at
reference at(const_iterator pos)
葉の値への参照を返す
Definition: trie_base.hpp:257
wordring::detail::trie_heap::assign
void assign(InputIterator first, InputIterator last)
直列化データから割り当てる
Definition: trie_heap.hpp:260
wordring::detail::trie_base::insert
const_iterator insert(InputIterator first, InputIterator last, value_type value=0)
キー文字列を挿入する
Definition: trie_base.hpp:458
wordring::detail::trie_base::trie_base
trie_base(allocator_type const &alloc)
アロケータを指定して空のコンテナを構築する
Definition: trie_base.hpp:124
wordring::detail::trie_base::end
const_iterator end() const noexcept
根の終端を指すイテレータを返す
Definition: trie_base.hpp:411
wordring::detail::trie_heap::clear
void clear() noexcept
すべての要素を削除する
Definition: trie_heap.hpp:364
wordring::detail::trie_base::begin
const_iterator begin() const noexcept
根を指すイテレータを返す
Definition: trie_base.hpp:394
wordring::detail::const_list_trie_iterator
文字列の集合をTrie構築に適した形で走査するイテレータ
Definition: list_trie_iterator.hpp:20
wordring::detail::trie_base::search
const_iterator search(Key const &key) const
前方一致検索
Definition: trie_base.hpp:682
wordring::basic_trie
任意の整数型をラベルとして用いることが出来る汎用Trie
Definition: trie.hpp:130
wordring::detail::trie_base::find
const_iterator find(InputIterator first, InputIterator last) const
完全一致検索
Definition: trie_base.hpp:697
wordring::detail::trie_heap::get_allocator
allocator_type get_allocator() const
コンテナに関連付けられているアロケータを返す
Definition: trie_heap.hpp:226
wordring::detail::trie_base::cbegin
const_iterator cbegin() const noexcept
根を指すイテレータを返す
Definition: trie_base.hpp:402
wordring::detail::trie_node
Definition: trie_heap.hpp:23
wordring::detail::trie_base::lookup
auto lookup(InputIterator first, InputIterator last) const
部分一致検索
Definition: trie_base.hpp:649
wordring::detail::trie_base::max_size
static constexpr size_type max_size() noexcept
キー文字列の格納可能な最大数を調べる
Definition: trie_base.hpp:440
wordring::detail::trie_base::empty
bool empty() const noexcept
キー文字列を格納していないことを調べる
Definition: trie_base.hpp:428
wordring::detail::trie_heap::ibegin
serialize_iterator ibegin() const
直列化用のイテレータを返す
Definition: trie_heap.hpp:342
wordring::detail::trie_base::assign
void assign(InputIterator first, InputIterator last)
直列化データからの割り当て
Definition: trie_base.hpp:188
wordring::detail
wordring::detail::trie_base::at
reference at(InputIterator first, InputIterator last)
葉の値への参照を返す
Definition: trie_base.hpp:296
wordring::detail::trie_heap
ダブル・アレイによるTrie実装のメモリー管理を行う
Definition: trie_heap.hpp:200
wordring::detail::trie_heap::at
index_type at(index_type parent, std::uint16_t label) const
Definition: trie_heap.hpp:766
wordring::detail::trie_base
終端の空遷移を併合したTrie木の実装
Definition: trie_base.hpp:64
wordring::detail::trie_heap::has_sibling
bool has_sibling(index_type idx) const
Definition: trie_heap.hpp:710
wordring::detail::trie_base::find
const_iterator find(Key const &key) const
完全一致検索
Definition: trie_base.hpp:718
wordring::detail::trie_base::contains
bool contains(InputIterator first, InputIterator last) const
キー文字列が格納されているか調べる
Definition: trie_base.hpp:731
wordring::detail::trie_value_proxy
Definition: trie_heap.hpp:40
wordring::detail::trie_base::assign
void assign(ForwardIterator first, ForwardIterator last)
文字列リストからの割り当て
Definition: trie_base.hpp:203
wordring::detail::trie_base::erase
void erase(Key const &key)
キー文字列を削除する
Definition: trie_base.hpp:590
wordring::detail::trie_base::contains
bool contains(Key const &key) const
キー文字列が格納されているか調べる
Definition: trie_base.hpp:748
wordring::detail::trie_heap::iend
serialize_iterator iend() const
直列化用のイテレータを返す
Definition: trie_heap.hpp:353
wordring::detail::trie_base::lookup
auto lookup(InputIterator first, InputIterator last, std::uint32_t &i) const
部分一致検索
Definition: trie_base.hpp:618
wordring::detail::operator>>
std::istream & operator>>(std::istream &is, stable_trie_base< Allocator1 > &trie)
ストリームから入力する
Definition: stable_trie_base.hpp:763
wordring::detail::trie_base::operator<<
friend std::ostream & operator<<(std::ostream &, trie_base< Allocator1 > const &)
ストリームへ出力する
Definition: trie_base.hpp:759
wordring::detail::trie_heap_serialize_iterator
Definition: trie_heap.hpp:78