SeqAn3
bi_fm_index_iterator.hpp
Go to the documentation of this file.
1 // ============================================================================
2 // SeqAn - The Library for Sequence Analysis
3 // ============================================================================
4 //
5 // Copyright (c) 2006-2018, Knut Reinert & Freie Universitaet Berlin
6 // Copyright (c) 2016-2018, Knut Reinert & MPI Molekulare Genetik
7 // All rights reserved.
8 //
9 // Redistribution and use in source and binary forms, with or without
10 // modification, are permitted provided that the following conditions are met:
11 //
12 // * Redistributions of source code must retain the above copyright
13 // notice, this list of conditions and the following disclaimer.
14 // * Redistributions in binary form must reproduce the above copyright
15 // notice, this list of conditions and the following disclaimer in the
16 // documentation and/or other materials provided with the distribution.
17 // * Neither the name of Knut Reinert or the FU Berlin nor the names of
18 // its contributors may be used to endorse or promote products derived
19 // from this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 // ARE DISCLAIMED. IN NO EVENT SHALL KNUT REINERT OR THE FU BERLIN BE LIABLE
25 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31 // DAMAGE.
32 //
33 // ============================================================================
34 
40 #pragma once
41 
42 #include <array>
43 
44 #include <sdsl/suffix_trees.hpp>
45 
46 #include <range/v3/view/iota.hpp>
47 #include <range/v3/view/slice.hpp>
48 
49 #include <seqan3/alphabet/all.hpp>
53 
54 namespace seqan3
55 {
56 
82 template <typename index_t>
84 {
85 
86 public:
87 
89  using index_type = index_t;
90 
94  using size_type = typename index_type::size_type;
97 
106 
107 protected:
109 
111  using sdsl_char_type = typename index_type::sdsl_char_type;
112 
114  index_type const * index;
115 
119  size_type fwd_lb;
122  size_type fwd_rb;
124  size_type rev_lb;
126  size_type rev_rb;
127  //\}
128 
135  // parent_* and _last_char only have to be stored for the (unidirectional) iterator that has been used last for
136  // extend_right() or cycle_back() resp. extend_left() or cycle_front(), (i.e. either fwd or rev). Thus there is no
137  // need to store it twice. Once the iterator is switched, the information becomes invalid anyway.
138 
140  size_type parent_lb;
142  size_type parent_rb;
144  sdsl_char_type _last_char;
145  //\}
146 
148  size_type depth; // equal for both iterators. only stored once
149 
150  // supports assertions to check whether cycle_back() resp. cycle_front() is called on the same direction as the last
151  // extend_right([...]) resp. extend_left([...])
152 #ifndef NDEBUG
153  // cycle_back() and cycle_front()
155  bool fwd_iter_last_used = false;
156 #endif
157 
159  size_type offset() const noexcept
160  {
161  assert(index->size() > query_length());
162  return index->size() - query_length() - 1; // since the string is reversed during construction
163  }
164 
166  template <detail::sdsl_index_concept csa_t>
167  bool bidirectional_search(csa_t const & csa, sdsl_char_type const c,
168  size_type & l_fwd, size_type & r_fwd,
169  size_type & l_bwd, size_type & r_bwd) const noexcept
170  {
171  assert((l_fwd <= r_fwd) && (r_fwd < csa.size()));
172  assert(r_fwd + 1 >= l_fwd);
173  assert(r_bwd + 1 - l_bwd == r_fwd + 1 - l_fwd);
174 
175  size_type _l_fwd, _r_fwd, _l_bwd, _r_bwd;
176 
177  size_type cc = c;
179  {
180  cc = csa.char2comp[c];
181  if (cc == 0 && c > 0) // [[unlikely]]
182  return false;
183  }
184 
185  size_type const c_begin = csa.C[cc];
186  if (r_fwd + 1 - l_fwd == csa.size()) // [[unlikely]]
187  {
188  _l_fwd = c_begin;
189  _l_bwd = c_begin;
190  _r_fwd = csa.C[cc + 1] - 1;
191  _r_bwd = _r_fwd;
192  // if we use not the plain_byte_alphabet, we could return always return true here
193  }
194  else
195  {
196  auto const r_s_b = csa.wavelet_tree.lex_count(l_fwd, r_fwd + 1, c);
197  size_type const rank_l = std::get<0>(r_s_b);
198  size_type const s = std::get<1>(r_s_b), b = std::get<2>(r_s_b);
199  size_type const rank_r = r_fwd - l_fwd - s - b + rank_l;
200  _l_fwd = c_begin + rank_l;
201  _r_fwd = c_begin + rank_r;
202  _l_bwd = l_bwd + s;
203  _r_bwd = r_bwd - b;
204  }
205 
206  if (_r_fwd >= _l_fwd)
207  {
208  l_fwd = _l_fwd;
209  r_fwd = _r_fwd;
210  l_bwd = _l_bwd;
211  r_bwd = _r_bwd;
212  assert(r_fwd + 1 >= l_fwd);
213  assert(r_bwd + 1 - l_bwd == r_fwd + 1 - l_fwd);
214  return true;
215  }
216  return false;
217  }
218 
220  template <detail::sdsl_index_concept csa_t>
221  bool bidirectional_search_cycle(csa_t const & csa, sdsl_char_type const c,
222  size_type const l_parent, size_type const r_parent,
223  size_type & l_fwd, size_type & r_fwd,
224  size_type & l_bwd, size_type & r_bwd) const noexcept
225  {
226  assert((l_parent <= r_parent) && (r_parent < csa.size()));
227 
228  size_type c_begin;
230  c_begin = csa.C[c]; // TODO: check whether this can be removed
231  else
232  c_begin = csa.C[csa.char2comp[c]];
233 
234  auto const r_s_b = csa.wavelet_tree.lex_count(l_parent, r_parent + 1, c);
235  size_type const s = std::get<1>(r_s_b),
236  b = std::get<2>(r_s_b),
237  rank_l = std::get<0>(r_s_b),
238  rank_r = r_parent - l_parent - s - b + rank_l;
239 
240  size_type const _l_fwd = c_begin + rank_l;
241  size_type const _r_fwd = c_begin + rank_r;
242  size_type const _l_bwd = r_bwd + 1;
243  size_type const _r_bwd = r_bwd + 1 + rank_r - rank_l;
244 
245  if (_r_fwd >= _l_fwd)
246  {
247  l_fwd = _l_fwd;
248  r_fwd = _r_fwd;
249  l_bwd = _l_bwd;
250  r_bwd = _r_bwd;
251  assert(r_fwd + 1 >= l_fwd);
252  assert(r_bwd + 1 - l_bwd == r_fwd + 1 - l_fwd);
253  return true;
254  }
255  return false;
256  }
257 
258 public:
259 
263  // Default construction is necessary to make this class semi-regular and e.g., to allow construction of
265  // std::array of iterators.
266  bi_fm_index_iterator() noexcept = default;
267  bi_fm_index_iterator(bi_fm_index_iterator const &) noexcept = default;
268  bi_fm_index_iterator & operator=(bi_fm_index_iterator const &) noexcept = default;
269  bi_fm_index_iterator(bi_fm_index_iterator &&) noexcept = default;
270  bi_fm_index_iterator & operator=(bi_fm_index_iterator &&) noexcept = default;
271 
272  bi_fm_index_iterator(index_t const & _index) noexcept : index(&_index),
273  fwd_lb(0), fwd_rb(_index.size() - 1),
274  rev_lb(0), rev_rb(_index.size() - 1),
275  depth(0)
276  {}
277  //\}
278 
291  bool operator==(bi_fm_index_iterator const & rhs) const noexcept
292  {
293  assert(index != nullptr);
294  // equal SA interval implies equal parent node information (or both are root nodes)
295  assert(!(fwd_lb == rhs.fwd_lb && fwd_rb == rhs.fwd_rb && depth == rhs.depth) ||
296  depth == 0 ||
297  parent_lb == rhs.parent_lb && parent_rb == rhs.parent_rb && _last_char == rhs._last_char);
298 
299  return std::tie(fwd_lb, fwd_rb, depth) == std::tie(rhs.fwd_lb, rhs.fwd_rb, rhs.depth);
300  }
301 
314  bool operator!=(bi_fm_index_iterator const & rhs) const noexcept
315  {
316  assert(index != nullptr);
317 
318  return !(*this == rhs);
319  }
320 
338  bool extend_right() noexcept
339  {
340  #ifndef NDEBUG
341  fwd_iter_last_used = true;
342  #endif
343 
344  assert(index != nullptr);
345 
346  size_type new_parent_lb = fwd_lb, new_parent_rb = fwd_rb;
347 
348  sdsl_char_type c = 1; // NOTE: start with 0 or 1 depending on implicit_sentintel
349  while (c < index->fwd_fm.index.sigma &&
350  !bidirectional_search(index->fwd_fm.index, index->fwd_fm.index.comp2char[c],
351  fwd_lb, fwd_rb, rev_lb, rev_rb))
352  {
353  ++c;
354  }
355 
356  if (c != index->fwd_fm.index.sigma)
357  {
358  parent_lb = new_parent_lb;
359  parent_rb = new_parent_rb;
360 
361  _last_char = c;
362  ++depth;
363 
364  return true;
365  }
366  return false;
367  }
368 
386  bool extend_left() noexcept
387  {
388  #ifndef NDEBUG
389  fwd_iter_last_used = false;
390  #endif
391 
392  assert(index != nullptr);
393 
394  size_type new_parent_lb = rev_lb, new_parent_rb = rev_rb;
395 
396  sdsl_char_type c = 1; // NOTE: start with 0 or 1 depending on implicit_sentintel
397  while (c < index->rev_fm.index.sigma &&
398  !bidirectional_search(index->rev_fm.index, index->rev_fm.index.comp2char[c],
399  rev_lb, rev_rb, fwd_lb, fwd_rb))
400  {
401  ++c;
402  }
403 
404  if (c != index->rev_fm.index.sigma)
405  {
406  parent_lb = new_parent_lb;
407  parent_rb = new_parent_rb;
408 
409  _last_char = c;
410  ++depth;
411 
412  return true;
413  }
414  return false;
415  }
416 
431  template <alphabet_concept char_t>
435  bool extend_right(char_t const c) noexcept
436  {
437  #ifndef NDEBUG
438  fwd_iter_last_used = true;
439  #endif
440 
441  assert(index != nullptr);
442 
443  size_type new_parent_lb = fwd_lb, new_parent_rb = fwd_rb;
444 
445  auto c_char = to_rank(c) + 1;
446  if (bidirectional_search(index->fwd_fm.index, c_char, fwd_lb, fwd_rb, rev_lb, rev_rb))
447  {
448  parent_lb = new_parent_lb;
449  parent_rb = new_parent_rb;
450 
451  _last_char = c_char;
452  ++depth;
453 
454  return true;
455  }
456  return false;
457  }
458 
473  template <alphabet_concept char_t>
477  bool extend_left(char_t const c) noexcept
478  {
479  #ifndef NDEBUG
480  fwd_iter_last_used = false;
481  #endif
482 
483  assert(index != nullptr);
484 
485  size_type new_parent_lb = rev_lb, new_parent_rb = rev_rb;
486 
487  auto c_char = to_rank(c) + 1;
488  if (bidirectional_search(index->rev_fm.index, c_char, rev_lb, rev_rb, fwd_lb, fwd_rb))
489  {
490  parent_lb = new_parent_lb;
491  parent_rb = new_parent_rb;
492 
493  _last_char = c_char;
494  ++depth;
495 
496  return true;
497  }
498  return false;
499  }
500 
517  template <std::ranges::RandomAccessRange seq_t>
519  requires implicitly_convertible_to_concept<innermost_value_type_t<seq_t>, typename index_t::char_type>
521  bool extend_right(seq_t && seq) noexcept
522  {
523  assert(index != nullptr);
524 
525  auto first = seq.begin();
526  auto last = seq.end();
527 
528  #ifndef NDEBUG
529  fwd_iter_last_used = (first != last); // only if seq was not empty
530  #endif
531 
532  size_type _fwd_lb = fwd_lb, _fwd_rb = fwd_rb, _rev_lb = rev_lb, _rev_rb = rev_rb;
533  size_type new_parent_lb = parent_lb, new_parent_rb = parent_rb;
534  sdsl_char_type c = _last_char;
535 
536  for (auto it = first; it != last; ++it)
537  {
538  c = to_rank(*it) + 1;
539 
540  new_parent_lb = _fwd_lb;
541  new_parent_rb = _fwd_rb;
542  if (!bidirectional_search(index->fwd_fm.index, c, _fwd_lb, _fwd_rb, _rev_lb, _rev_rb))
543  return false;
544  }
545 
546  fwd_lb = _fwd_lb;
547  fwd_rb = _fwd_rb;
548  rev_lb = _rev_lb;
549  rev_rb = _rev_rb;
550 
551  parent_lb = new_parent_lb;
552  parent_rb = new_parent_rb;
553 
554  _last_char = c;
555  depth += last - first;
556 
557  return true;
558  }
559 
580  template <std::ranges::RandomAccessRange seq_t>
582  requires implicitly_convertible_to_concept<innermost_value_type_t<seq_t>, typename index_t::char_type>
584  bool extend_left(seq_t && seq) noexcept
585  {
586  assert(index != nullptr);
587 
588  auto rev_seq = view::reverse(seq);
589  auto first = rev_seq.begin();
590  auto last = rev_seq.end();
591 
592  #ifndef NDEBUG
593  if (first != last) // only if seq was not empty
594  fwd_iter_last_used = false;
595  #endif
596 
597  size_type _fwd_lb = fwd_lb, _fwd_rb = fwd_rb,
598  _rev_lb = rev_lb, _rev_rb = rev_rb;
599  size_type new_parent_lb = parent_lb, new_parent_rb = parent_rb;
600  sdsl_char_type c = _last_char;
601 
602  for (auto it = first; it != last; ++it)
603  {
604  c = to_rank(*it) + 1;
605 
606  new_parent_lb = _rev_lb;
607  new_parent_rb = _rev_rb;
608  if (!bidirectional_search(index->rev_fm.index, c, _rev_lb, _rev_rb, _fwd_lb, _fwd_rb))
609  return false;
610  }
611 
612  fwd_lb = _fwd_lb;
613  fwd_rb = _fwd_rb;
614  rev_lb = _rev_lb;
615  rev_rb = _rev_rb;
616 
617  parent_lb = new_parent_lb;
618  parent_rb = new_parent_rb;
619  _last_char = c;
620  depth += last - first;
621 
622  return true;
623  }
624 
651  bool cycle_back() noexcept
652  {
653  #ifndef NDEBUG
654  // cycle_back() can only be used if the last extension was to the right.
655  assert(fwd_iter_last_used);
656  #endif
657 
658  assert(index != nullptr && query_length() > 0);
659 
660  sdsl_char_type c = _last_char + 1;
661 
662  while (c < index->fwd_fm.index.sigma &&
663  !bidirectional_search_cycle(index->fwd_fm.index, index->fwd_fm.index.comp2char[c],
664  parent_lb, parent_rb, fwd_lb, fwd_rb, rev_lb, rev_rb))
665  {
666  ++c;
667  }
668 
669  if (c != index->fwd_fm.index.sigma)
670  {
671  _last_char = c;
672 
673  return true;
674  }
675  return false;
676  }
677 
704  bool cycle_front() noexcept
705  {
706  #ifndef NDEBUG
707  // cycle_front() can only be used if the last extension was to the left.
708  assert(!fwd_iter_last_used);
709  #endif
710 
711  assert(index != nullptr && query_length() > 0);
712 
713  sdsl_char_type c = _last_char + 1;
714  while (c < index->rev_fm.index.sigma &&
715  !bidirectional_search_cycle(index->rev_fm.index, index->rev_fm.index.comp2char[c],
716  parent_lb, parent_rb, rev_lb, rev_rb, fwd_lb, fwd_rb))
717  {
718  ++c;
719  }
720 
721  if (c != index->rev_fm.index.sigma)
722  {
723  _last_char = c;
724 
725  return true;
726  }
727  return false;
728  }
729 
730 
747  typename index_t::char_type last_char() noexcept
748  {
749  assert(index != nullptr && query_length() > 0);
750 
751  typename index_t::char_type c;
752  assign_rank(c, index->fwd_fm.index.comp2char[_last_char] - 1); // text is not allowed to contain ranks of 0
753  return c;
754  }
755 
768  size_type query_length() const noexcept
769  {
770  assert(index != nullptr);
771  // depth == 0 -> root node
772  assert(depth != 0 ||
773  (fwd_lb == rev_lb &&
774  fwd_rb == rev_rb &&
775  fwd_lb == 0 &&
776  fwd_rb == index->size() - 1));
777 
778  return depth;
779  }
780 
800  fwd_iterator to_fwd_iterator() const noexcept
801  {
802  assert(index != nullptr);
803 
804  fwd_iterator it{index->fwd_fm};
805  it.parent_lb = parent_lb;
806  it.parent_rb = parent_rb;
807  it.node = {fwd_lb, fwd_rb, depth, _last_char};
808 
809  #ifndef NDEBUG
810  if (!fwd_iter_last_used)
811  {
812  // invalidate parent interval
813  it.parent_lb = 1;
814  it.parent_rb = 0;
815  }
816  #endif
817 
818  return it;
819  }
820 
842  rev_iterator to_rev_iterator() const noexcept
843  {
844  assert(index != nullptr);
845 
846  rev_iterator it{index->rev_fm};
847  it.parent_lb = parent_lb;
848  it.parent_rb = parent_rb;
849  it.node = {rev_lb, rev_rb, depth, _last_char};
850 
851  #ifndef NDEBUG
852  if (fwd_iter_last_used)
853  {
854  // invalidate parent interval
855  it.parent_lb = 1;
856  it.parent_rb = 0;
857  }
858  #endif
859 
860  return it;
861  }
862 
876  auto query() const noexcept
877  {
878  assert(index != nullptr && index->text != nullptr);
879 
880  size_type const query_begin = offset() - index->fwd_fm.index[fwd_lb];
881  return *index->text | ranges::view::slice(query_begin, query_begin + query_length());
882  }
883 
885  auto operator*() const noexcept
886  {
887  assert(index != nullptr && index->text != nullptr);
888 
889  return query();
890  }
891 
903  size_type count() const noexcept
904  {
905  assert(index != nullptr && (1 + fwd_rb - fwd_lb == 1 + rev_rb - rev_lb));
906 
907  return 1 + fwd_rb - fwd_lb;
908  }
909 
921  std::vector<size_type> locate() const
922  {
923  assert(index != nullptr);
924 
925  std::vector<size_type> occ(count());
926  for (size_type i = 0; i < occ.size(); ++i)
927  {
928  occ[i] = offset() - index->fwd_fm.index[fwd_lb + i];
929  }
930  return occ;
931  }
932 
945  auto lazy_locate() const
946  {
947  assert(index != nullptr);
948 
949  return ranges::view::iota(fwd_lb, fwd_lb + count())
950  | view::transform([*this, _offset = offset()] (auto sa_pos)
951  {
952  return _offset - index->fwd_fm.index[sa_pos];
953  });
954  }
955 
956 };
957 
959 
960 } // namespace seqan3
constexpr simd_t iota(typename simd_traits< simd_t >::scalar_type const offset)
Fills a seqan3::simd::simd_type vector with the scalar values offset, offset+1, offset+2, ...
Definition: simd_algorithm.hpp:100
constexpr auto transform
A range adaptor that takes a invocable and returns a view of the elements with the invocable applied...
Definition: transform.hpp:95
bool extend_left(char_t const c) noexcept
Tries to extend the query by the character c to the left.
Definition: bi_fm_index_iterator.hpp:477
bool extend_left(seq_t &&seq) noexcept
Tries to extend the query by seq to the left.
Definition: bi_fm_index_iterator.hpp:584
bi_fm_index_iterator() noexcept=default
Default constructor. Accessing member functions on a default constructed object is undefined behavior...
bool cycle_back() noexcept
Tries to replace the rightmost character of the query by the next lexicographically larger character ...
Definition: bi_fm_index_iterator.hpp:651
bool extend_right(seq_t &&seq) noexcept
Tries to extend the query by seq to the right.
Definition: bi_fm_index_iterator.hpp:521
bool operator!=(bi_fm_index_iterator const &rhs) const noexcept
Compares two iterators.
Definition: bi_fm_index_iterator.hpp:314
The main SeqAn3 namespace.
Definition: aligned_sequence_concept.hpp:58
rev_iterator to_rev_iterator() const noexcept
Returns a unidirectional seqan3::fm_index_iterator on the reversed text. query() on the returned unid...
Definition: bi_fm_index_iterator.hpp:842
std::vector< size_type > locate() const
Locates the occurrences of the searched query in the text.
Definition: bi_fm_index_iterator.hpp:921
The SeqAn FM Index Iterator.
Definition: fm_index_iterator.hpp:91
bool extend_right(char_t const c) noexcept
Tries to extend the query by the character c to the right.
Definition: bi_fm_index_iterator.hpp:435
auto query() const noexcept
Returns the searched query.
Definition: bi_fm_index_iterator.hpp:876
size_type query_length() const noexcept
Returns the depth of the iterator node in the implicit suffix tree, i.e. the length of the sequence s...
Definition: bi_fm_index_iterator.hpp:768
auto operator*() const noexcept
Returns the searched query.
Definition: bi_fm_index_iterator.hpp:885
fwd_iterator to_fwd_iterator() const noexcept
Returns a unidirectional seqan3::fm_index_iterator on the original text. query() on the returned unid...
Definition: bi_fm_index_iterator.hpp:800
typename index_type::size_type size_type
Type for representing positions in the indexed text.
Definition: bi_fm_index_iterator.hpp:95
constexpr alphabet_type & assign_rank(alphabet_type &alph, underlying_rank_t< alphabet_type > const rank) requires requires(alphabet_type alph)
Implementation of seqan3::semi_alphabet_concept::assign_rank() that delegates to a member function...
Definition: member_exposure.hpp:110
Meta-header for the alphabet module.
size_type count() const noexcept
Counts the number of occurrences of the searched query in the text.
Definition: bi_fm_index_iterator.hpp:903
index_t::char_type last_char() noexcept
Outputs the rightmost respectively leftmost character depending on whether extend_right() or extend_l...
Definition: bi_fm_index_iterator.hpp:747
auto lazy_locate() const
Locates the occurrences of the searched query in the text on demand, i.e. a ranges::view is returned ...
Definition: bi_fm_index_iterator.hpp:945
The concept std::Same<T, U> is satisfied if and only if T and U denote the same type.
Provides the bidirectional seqan3::bi_fm_index.
Provides seqan3::view::transform.
The SeqAn Bidirectional FM Index Iterator.
Definition: bi_fm_index_iterator.hpp:83
index_t index_type
Type of the index.
Definition: bi_fm_index_iterator.hpp:89
Resolves to std::ranges::ImplicitlyConvertibleTo<type1, type2>().
Provides various metafunctions used by the range module.
bool operator==(bi_fm_index_iterator const &rhs) const noexcept
Compares two iterators.
Definition: bi_fm_index_iterator.hpp:291
constexpr auto reverse
A range adaptor that presents the underlying range in reverse order.
Definition: reverse.hpp:93
bool extend_left() noexcept
Tries to extend the query by the smallest possible character to the left such that the query is found...
Definition: bi_fm_index_iterator.hpp:386
constexpr underlying_rank_t< alphabet_type > to_rank(alphabet_type const alph) requires requires(alphabet_type alph)
Implementation of seqan3::semi_alphabet_concept::to_rank() that delegates to a member function...
Definition: member_exposure.hpp:97
bool extend_right() noexcept
Tries to extend the query by the smallest possible character to the right such that the query is foun...
Definition: bi_fm_index_iterator.hpp:338
bool cycle_front() noexcept
Tries to replace the leftmost character of the query by the next lexicographically larger character s...
Definition: bi_fm_index_iterator.hpp:704