1 /*******************************************************************************
2  * tlx/sort/strings/string_set.hpp
3  *
4  * Implementations of a StringSet concept. This is an internal implementation
5  * header, see tlx/sort/strings.hpp for public front-end functions.
6  *
7  * A StringSet abstracts from arrays of strings, we provide four abstractions:
8  *
9  * - UCharStringSet: (const) unsigned char**
10  * - StdStringSet: std::string*
11  * - UPtrStdStringSet: std::unique_ptr<std::string>*
12  * - StringSuffixSet: suffix sorting indexes of a std::string text
13  *
14  * Part of tlx - http://panthema.net/tlx
15  *
16  * Copyright (C) 2015-2019 Timo Bingmann <tb@panthema.net>
17  *
18  * All rights reserved. Published under the Boost Software License, Version 1.0
19  ******************************************************************************/
20 
21 #ifndef TLX_SORT_STRINGS_STRING_SET_HEADER
22 #define TLX_SORT_STRINGS_STRING_SET_HEADER
23 
24 #include <cassert>
25 #include <cstdint>
26 #include <memory>
27 #include <string>
28 #include <utility>
29 #include <vector>
30 
31 #include <tlx/logger/core.hpp>
32 #include <tlx/math/bswap.hpp>
33 #include <tlx/meta/enable_if.hpp>
34 
35 namespace tlx {
36 
37 //! \addtogroup tlx_sort
38 //! \{
39 
40 namespace sort_strings_detail {
41 
42 /******************************************************************************/
43 
44 /*!
45  * Base class for common string set functions, included via CRTP.
46  */
47 template <typename StringSet, typename Traits>
48 class StringSetBase
49 {
50 public:
51     //! index-based array access (readable and writable) to String objects.
at(size_t i) const52     typename Traits::String& at(size_t i) const {
53         const StringSet& ss = *static_cast<const StringSet*>(this);
54         return *(ss.begin() + i);
55     }
56 
57     //! \name CharIterator Comparisons
58     //! \{
59 
60     //! check equality of two strings a and b at char iterators ai and bi.
is_equal(const typename Traits::String & a,const typename Traits::CharIterator & ai,const typename Traits::String & b,const typename Traits::CharIterator & bi) const61     bool is_equal(const typename Traits::String& a,
62                   const typename Traits::CharIterator& ai,
63                   const typename Traits::String& b,
64                   const typename Traits::CharIterator& bi) const {
65         const StringSet& ss = *static_cast<const StringSet*>(this);
66         return !ss.is_end(a, ai) && !ss.is_end(b, bi) && (*ai == *bi);
67     }
68 
69     //! check if string a is less or equal to string b at iterators ai and bi.
is_less(const typename Traits::String & a,const typename Traits::CharIterator & ai,const typename Traits::String & b,const typename Traits::CharIterator & bi) const70     bool is_less(const typename Traits::String& a,
71                  const typename Traits::CharIterator& ai,
72                  const typename Traits::String& b,
73                  const typename Traits::CharIterator& bi) const {
74         const StringSet& ss = *static_cast<const StringSet*>(this);
75         return ss.is_end(a, ai) ||
76                (!ss.is_end(a, ai) && !ss.is_end(b, bi) && *ai < *bi);
77     }
78 
79     //! check if string a is less or equal to string b at iterators ai and bi.
is_leq(const typename Traits::String & a,const typename Traits::CharIterator & ai,const typename Traits::String & b,const typename Traits::CharIterator & bi) const80     bool is_leq(const typename Traits::String& a,
81                 const typename Traits::CharIterator& ai,
82                 const typename Traits::String& b,
83                 const typename Traits::CharIterator& bi) const {
84         const StringSet& ss = *static_cast<const StringSet*>(this);
85         return ss.is_end(a, ai) ||
86                (!ss.is_end(a, ai) && !ss.is_end(b, bi) && *ai <= *bi);
87     }
88 
89     //! \}
90 
91     //! \name Character Extractors
92     //! \{
93 
94     typename Traits::Char
get_char(const typename Traits::String & s,size_t depth) const95     get_char(const typename Traits::String& s, size_t depth) const {
96         const StringSet& ss = *static_cast<const StringSet*>(this);
97         return *ss.get_chars(s, depth);
98     }
99 
100     //! Return up to 1 characters of string s at iterator i packed into a
101     //! uint8_t (only works correctly for 8-bit characters)
get_uint8(const typename Traits::String & s,typename Traits::CharIterator i) const102     uint8_t get_uint8(
103         const typename Traits::String& s, typename Traits::CharIterator i) const {
104         const StringSet& ss = *static_cast<const StringSet*>(this);
105 
106         if (ss.is_end(s, i)) return 0;
107         return uint8_t(*i);
108     }
109 
110     //! Return up to 2 characters of string s at iterator i packed into a
111     //! uint16_t (only works correctly for 8-bit characters)
get_uint16(const typename Traits::String & s,typename Traits::CharIterator i) const112     uint16_t get_uint16(
113         const typename Traits::String& s, typename Traits::CharIterator i) const {
114         const StringSet& ss = *static_cast<const StringSet*>(this);
115 
116         uint16_t v = 0;
117         if (ss.is_end(s, i)) return v;
118         v = (uint16_t(*i) << 8);
119         ++i;
120         if (ss.is_end(s, i)) return v;
121         v |= (uint16_t(*i) << 0);
122         return v;
123     }
124 
125     //! Return up to 4 characters of string s at iterator i packed into a
126     //! uint32_t (only works correctly for 8-bit characters)
get_uint32(const typename Traits::String & s,typename Traits::CharIterator i) const127     uint32_t get_uint32(
128         const typename Traits::String& s, typename Traits::CharIterator i) const {
129         const StringSet& ss = *static_cast<const StringSet*>(this);
130 
131         uint32_t v = 0;
132         if (ss.is_end(s, i)) return v;
133         v = (uint32_t(*i) << 24);
134         ++i;
135         if (ss.is_end(s, i)) return v;
136         v |= (uint32_t(*i) << 16);
137         ++i;
138         if (ss.is_end(s, i)) return v;
139         v |= (uint32_t(*i) << 8);
140         ++i;
141         if (ss.is_end(s, i)) return v;
142         v |= (uint32_t(*i) << 0);
143         return v;
144     }
145 
146     //! Return up to 8 characters of string s at iterator i packed into a
147     //! uint64_t (only works correctly for 8-bit characters)
get_uint64(const typename Traits::String & s,typename Traits::CharIterator i) const148     uint64_t get_uint64(
149         const typename Traits::String& s, typename Traits::CharIterator i) const {
150         const StringSet& ss = *static_cast<const StringSet*>(this);
151 
152         uint64_t v = 0;
153         if (ss.is_end(s, i)) return v;
154         v = (uint64_t(*i) << 56);
155         ++i;
156         if (ss.is_end(s, i)) return v;
157         v |= (uint64_t(*i) << 48);
158         ++i;
159         if (ss.is_end(s, i)) return v;
160         v |= (uint64_t(*i) << 40);
161         ++i;
162         if (ss.is_end(s, i)) return v;
163         v |= (uint64_t(*i) << 32);
164         ++i;
165         if (ss.is_end(s, i)) return v;
166         v |= (uint64_t(*i) << 24);
167         ++i;
168         if (ss.is_end(s, i)) return v;
169         v |= (uint64_t(*i) << 16);
170         ++i;
171         if (ss.is_end(s, i)) return v;
172         v |= (uint64_t(*i) << 8);
173         ++i;
174         if (ss.is_end(s, i)) return v;
175         v |= (uint64_t(*i) << 0);
176         return v;
177     }
178 
get_uint8(const typename Traits::String & s,size_t depth) const179     uint8_t get_uint8(const typename Traits::String& s, size_t depth) const {
180         const StringSet& ss = *static_cast<const StringSet*>(this);
181         return get_uint8(s, ss.get_chars(s, depth));
182     }
183 
get_uint16(const typename Traits::String & s,size_t depth) const184     uint16_t get_uint16(const typename Traits::String& s, size_t depth) const {
185         const StringSet& ss = *static_cast<const StringSet*>(this);
186         return get_uint16(s, ss.get_chars(s, depth));
187     }
188 
get_uint32(const typename Traits::String & s,size_t depth) const189     uint32_t get_uint32(const typename Traits::String& s, size_t depth) const {
190         const StringSet& ss = *static_cast<const StringSet*>(this);
191         return get_uint32(s, ss.get_chars(s, depth));
192     }
193 
get_uint64(const typename Traits::String & s,size_t depth) const194     uint64_t get_uint64(const typename Traits::String& s, size_t depth) const {
195         const StringSet& ss = *static_cast<const StringSet*>(this);
196         return get_uint64(s, ss.get_chars(s, depth));
197     }
198 
199     //! \}
200 
201     //! Subset this string set using index range.
subi(size_t begin,size_t end) const202     StringSet subi(size_t begin, size_t end) const {
203         const StringSet& ss = *static_cast<const StringSet*>(this);
204         return ss.sub(ss.begin() + begin, ss.begin() + end);
205     }
206 
check_order(const typename Traits::String & s1,const typename Traits::String & s2) const207     bool check_order(const typename Traits::String& s1,
208                      const typename Traits::String& s2) const {
209         const StringSet& ss = *static_cast<const StringSet*>(this);
210 
211         typename StringSet::CharIterator c1 = ss.get_chars(s1, 0);
212         typename StringSet::CharIterator c2 = ss.get_chars(s2, 0);
213 
214         while (ss.is_equal(s1, c1, s2, c2))
215             ++c1, ++c2;
216 
217         return ss.is_leq(s1, c1, s2, c2);
218     }
219 
check_order() const220     bool check_order() const {
221         const StringSet& ss = *static_cast<const StringSet*>(this);
222 
223         for (typename Traits::Iterator pi = ss.begin();
224              pi + 1 != ss.end(); ++pi)
225         {
226             if (!check_order(*pi, *(pi + 1))) {
227                 TLX_LOG1 << "check_order() failed at position " << pi - ss.begin();
228                 return false;
229             }
230         }
231         return true;
232     }
233 
print() const234     void print() const {
235         const StringSet& ss = *static_cast<const StringSet*>(this);
236         size_t i = 0;
237         for (typename Traits::Iterator pi = ss.begin(); pi != ss.end(); ++pi)
238         {
239             TLX_LOG1 << "[" << i++ << "] = " << *pi
240                      << " = " << ss.get_string(*pi, 0);
241         }
242     }
243 };
244 
245 template <typename Type, typename StringSet>
246 inline
247 typename enable_if<sizeof(Type) == 4, uint32_t>::type
get_key(const StringSet & strset,const typename StringSet::String & s,size_t depth)248 get_key(const StringSet& strset,
249         const typename StringSet::String& s, size_t depth) {
250     return strset.get_uint32(s, depth);
251 }
252 
253 template <typename Type, typename StringSet>
254 inline
255 typename enable_if<sizeof(Type) == 8, uint64_t>::type
get_key(const StringSet & strset,const typename StringSet::String & s,size_t depth)256 get_key(const StringSet& strset,
257         const typename StringSet::String& s, size_t depth) {
258     return strset.get_uint64(s, depth);
259 }
260 
261 template <typename Type, typename StringSet>
262 inline
get_key_at(const StringSet & strset,size_t idx,size_t depth)263 Type get_key_at(const StringSet& strset, size_t idx, size_t depth) {
264     return get_key<Type>(strset, strset.at(idx), depth);
265 }
266 
267 /******************************************************************************/
268 
269 /*!
270  * Traits class implementing StringSet concept for char* and unsigned char*
271  * strings.
272  */
273 template <typename CharType>
274 class GenericCharStringSetTraits
275 {
276 public:
277     //! exported alias for character type
278     typedef CharType Char;
279 
280     //! String reference: pointer to first character
281     typedef Char* String;
282 
283     //! Iterator over string references: pointer over pointers
284     typedef String* Iterator;
285 
286     //! iterator of characters in a string
287     typedef const Char* CharIterator;
288 
289     //! exported alias for assumed string container
290     typedef std::pair<Iterator, size_t> Container;
291 };
292 
293 /*!
294  * Class implementing StringSet concept for char* and unsigned char* strings.
295  */
296 template <typename CharType>
297 class GenericCharStringSet
298     : public GenericCharStringSetTraits<CharType>,
299       public StringSetBase<GenericCharStringSet<CharType>,
300                            GenericCharStringSetTraits<CharType> >
301 {
302 public:
303     typedef GenericCharStringSetTraits<CharType> Traits;
304 
305     typedef typename Traits::Char Char;
306     typedef typename Traits::String String;
307     typedef typename Traits::Iterator Iterator;
308     typedef typename Traits::CharIterator CharIterator;
309     typedef typename Traits::Container Container;
310 
311     //! Construct from begin and end string pointers
GenericCharStringSet(Iterator begin,Iterator end)312     GenericCharStringSet(Iterator begin, Iterator end)
313         : begin_(begin), end_(end)
314     { }
315 
316     //! Construct from a string container
GenericCharStringSet(const Container & c)317     explicit GenericCharStringSet(const Container& c)
318         : begin_(c.first), end_(c.first + c.second)
319     { }
320 
321     //! Return size of string array
size() const322     size_t size() const { return end_ - begin_; }
323     //! Iterator representing first String position
begin() const324     Iterator begin() const { return begin_; }
325     //! Iterator representing beyond last String position
end() const326     Iterator end() const { return end_; }
327 
328     //! Iterator-based array access (readable and writable) to String objects.
operator [](Iterator i) const329     String& operator [] (Iterator i) const
330     { return *i; }
331 
332     //! Return CharIterator for referenced string, which belong to this set.
get_chars(const String & s,size_t depth) const333     CharIterator get_chars(const String& s, size_t depth) const
334     { return s + depth; }
335 
336     //! Returns true if CharIterator is at end of the given String
is_end(const String &,const CharIterator & i) const337     bool is_end(const String&, const CharIterator& i) const
338     { return (*i == 0); }
339 
340     //! Return complete string (for debugging purposes)
get_string(const String & s,size_t depth=0) const341     std::string get_string(const String& s, size_t depth = 0) const
342     { return std::string(reinterpret_cast<const char*>(s) + depth); }
343 
344     //! Subset this string set using iterator range.
sub(Iterator begin,Iterator end) const345     GenericCharStringSet sub(Iterator begin, Iterator end) const
346     { return GenericCharStringSet(begin, end); }
347 
348     //! Allocate a new temporary string container with n empty Strings
allocate(size_t n)349     static Container allocate(size_t n)
350     { return std::make_pair(new String[n], n); }
351 
352     //! Deallocate a temporary string container
deallocate(Container & c)353     static void deallocate(Container& c)
354     { delete[] c.first; c.first = nullptr; }
355 
356 protected:
357     //! array of string pointers
358     Iterator begin_, end_;
359 };
360 
361 typedef GenericCharStringSet<char> CharStringSet;
362 typedef GenericCharStringSet<unsigned char> UCharStringSet;
363 
364 typedef GenericCharStringSet<const char> CCharStringSet;
365 typedef GenericCharStringSet<const unsigned char> CUCharStringSet;
366 
367 /******************************************************************************/
368 
369 /*!
370  * Class implementing StringSet concept for a std::string objects.
371  */
372 class StdStringSetTraits
373 {
374 public:
375     //! exported alias for character type
376     typedef uint8_t Char;
377 
378     //! String reference: std::string, which should be reference counted.
379     typedef std::string String;
380 
381     //! Iterator over string references: pointer to std::string.
382     typedef String* Iterator;
383 
384     //! iterator of characters in a string
385     typedef const Char* CharIterator;
386 
387     //! exported alias for assumed string container
388     typedef std::pair<Iterator, size_t> Container;
389 };
390 
391 /*!
392  * Class implementing StringSet concept for arrays of std::string objects.
393  */
394 class StdStringSet
395     : public StdStringSetTraits,
396       public StringSetBase<StdStringSet, StdStringSetTraits>
397 {
398 public:
399     //! Construct from begin and end string pointers
StdStringSet(const Iterator & begin,const Iterator & end)400     StdStringSet(const Iterator& begin, const Iterator& end)
401         : begin_(begin), end_(end)
402     { }
403 
404     //! Construct from a string container
StdStringSet(Container & c)405     explicit StdStringSet(Container& c)
406         : begin_(c.first), end_(c.first + c.second)
407     { }
408 
409     //! Return size of string array
size() const410     size_t size() const { return end_ - begin_; }
411     //! Iterator representing first String position
begin() const412     Iterator begin() const { return begin_; }
413     //! Iterator representing beyond last String position
end() const414     Iterator end() const { return end_; }
415 
416     //! Array access (readable and writable) to String objects.
operator [](const Iterator & i) const417     String& operator [] (const Iterator& i) const
418     { return *i; }
419 
420     //! Return CharIterator for referenced string, which belongs to this set.
get_chars(const String & s,size_t depth) const421     CharIterator get_chars(const String& s, size_t depth) const
422     { return reinterpret_cast<CharIterator>(s.data()) + depth; }
423 
424     //! Returns true if CharIterator is at end of the given String
is_end(const String & s,const CharIterator & i) const425     bool is_end(const String& s, const CharIterator& i) const
426     { return (i >= reinterpret_cast<CharIterator>(s.data()) + s.size()); }
427 
428     //! Return complete string (for debugging purposes)
get_string(const String & s,size_t depth=0) const429     std::string get_string(const String& s, size_t depth = 0) const
430     { return s.substr(depth); }
431 
432     //! Subset this string set using iterator range.
sub(Iterator begin,Iterator end) const433     StdStringSet sub(Iterator begin, Iterator end) const
434     { return StdStringSet(begin, end); }
435 
436     //! Allocate a new temporary string container with n empty Strings
allocate(size_t n)437     static Container allocate(size_t n)
438     { return std::make_pair(new String[n], n); }
439 
440     //! Deallocate a temporary string container
deallocate(Container & c)441     static void deallocate(Container& c)
442     { delete[] c.first; c.first = nullptr; }
443 
444 protected:
445     //! pointers to std::string objects
446     Iterator begin_, end_;
447 };
448 
449 /******************************************************************************/
450 
451 /*!
452  * Class implementing StringSet concept for a std::unique_ptr<std::string
453  * objects, which are non-copyable.
454  */
455 class UPtrStdStringSetTraits
456 {
457 public:
458     //! exported alias for character type
459     typedef uint8_t Char;
460 
461     //! String reference: std::string, which should be reference counted.
462     typedef std::unique_ptr<std::string> String;
463 
464     //! Iterator over string references: using std::vector's iterator
465     typedef String* Iterator;
466 
467     //! iterator of characters in a string
468     typedef const Char* CharIterator;
469 
470     //! exported alias for assumed string container
471     typedef std::pair<Iterator, size_t> Container;
472 };
473 
474 /*!
475  * Class implementing StringSet concept for a std::vector containing std::string
476  * objects.
477  */
478 class UPtrStdStringSet
479     : public UPtrStdStringSetTraits,
480       public StringSetBase<UPtrStdStringSet, UPtrStdStringSetTraits>
481 {
482 public:
483     //! Construct from begin and end string pointers
UPtrStdStringSet(const Iterator & begin,const Iterator & end)484     UPtrStdStringSet(const Iterator& begin, const Iterator& end)
485         : begin_(begin), end_(end)
486     { }
487 
488     //! Construct from a string container
UPtrStdStringSet(Container & c)489     explicit UPtrStdStringSet(Container& c)
490         : begin_(c.first), end_(c.first + c.second)
491     { }
492 
493     //! Return size of string array
size() const494     size_t size() const { return end_ - begin_; }
495     //! Iterator representing first String position
begin() const496     Iterator begin() const { return begin_; }
497     //! Iterator representing beyond last String position
end() const498     Iterator end() const { return end_; }
499 
500     //! Array access (readable and writable) to String objects.
operator [](const Iterator & i) const501     String& operator [] (const Iterator& i) const
502     { return *i; }
503 
504     //! Return CharIterator for referenced string, which belongs to this set.
get_chars(const String & s,size_t depth) const505     CharIterator get_chars(const String& s, size_t depth) const
506     { return reinterpret_cast<CharIterator>(s->data()) + depth; }
507 
508     //! Returns true if CharIterator is at end of the given String
is_end(const String & s,const CharIterator & i) const509     bool is_end(const String& s, const CharIterator& i) const
510     { return (i >= reinterpret_cast<CharIterator>(s->data()) + s->size()); }
511 
512     //! Return complete string (for debugging purposes)
get_string(const String & s,size_t depth=0) const513     std::string get_string(const String& s, size_t depth = 0) const
514     { return s->substr(depth); }
515 
516     //! Subset this string set using iterator range.
sub(Iterator begin,Iterator end) const517     UPtrStdStringSet sub(Iterator begin, Iterator end) const
518     { return UPtrStdStringSet(begin, end); }
519 
520     //! Allocate a new temporary string container with n empty Strings
allocate(size_t n)521     static Container allocate(size_t n)
522     { return std::make_pair(new String[n], n); }
523 
524     //! Deallocate a temporary string container
deallocate(Container & c)525     static void deallocate(Container& c)
526     { delete[] c.first; c.first = nullptr; }
527 
print() const528     void print() const {
529         size_t i = 0;
530         for (Iterator pi = begin(); pi != end(); ++pi)
531         {
532             TLX_LOG1 << "[" << i++ << "] = " << pi->get()
533                      << " = " << get_string(*pi, 0);
534         }
535     }
536 
537 protected:
538     //! vector of std::string objects
539     Iterator begin_, end_;
540 };
541 
542 /******************************************************************************/
543 
544 /*!
545  * Class implementing StringSet concept for suffix sorting indexes of a
546  * std::string text object.
547  */
548 class StringSuffixSetTraits
549 {
550 public:
551     //! exported alias for assumed string container
552     typedef std::string Text;
553 
554     //! exported alias for character type
555     typedef uint8_t Char;
556 
557     //! String reference: suffix index of the text.
558     typedef typename Text::size_type String;
559 
560     //! Iterator over string references: using std::vector's iterator over
561     //! suffix array vector
562     typedef typename std::vector<String>::iterator Iterator;
563 
564     //! iterator of characters in a string
565     typedef const Char* CharIterator;
566 
567     //! exported alias for assumed string container
568     typedef std::pair<Text, std::vector<String> > Container;
569 };
570 
571 /*!
572  * Class implementing StringSet concept for suffix sorting indexes of a
573  * std::string text object.
574  */
575 class StringSuffixSet
576     : public StringSuffixSetTraits,
577       public StringSetBase<StringSuffixSet, StringSuffixSetTraits>
578 {
579 public:
580     //! Construct from begin and end string pointers
StringSuffixSet(const Text & text,const Iterator & begin,const Iterator & end)581     StringSuffixSet(const Text& text,
582                     const Iterator& begin, const Iterator& end)
583         : text_(&text),
584           begin_(begin), end_(end)
585     { }
586 
587     //! Initializing constructor which fills output vector sa with indices.
588     static StringSuffixSet
Initialize(const Text & text,std::vector<String> & sa)589     Initialize(const Text& text, std::vector<String>& sa) {
590         sa.resize(text.size());
591         for (size_t i = 0; i < text.size(); ++i)
592             sa[i] = i;
593         return StringSuffixSet(text, sa.begin(), sa.end());
594     }
595 
596     //! Return size of string array
size() const597     size_t size() const { return end_ - begin_; }
598     //! Iterator representing first String position
begin() const599     Iterator begin() const { return begin_; }
600     //! Iterator representing beyond last String position
end() const601     Iterator end() const { return end_; }
602 
603     //! Array access (readable and writable) to String objects.
operator [](const Iterator & i) const604     String& operator [] (const Iterator& i) const
605     { return *i; }
606 
607     //! Return CharIterator for referenced string, which belongs to this set.
get_chars(const String & s,size_t depth) const608     CharIterator get_chars(const String& s, size_t depth) const
609     { return reinterpret_cast<CharIterator>(text_->data()) + s + depth; }
610 
611     //! Returns true if CharIterator is at end of the given String
is_end(const String &,const CharIterator & i) const612     bool is_end(const String&, const CharIterator& i) const
613     { return (i >= reinterpret_cast<CharIterator>(text_->data()) + text_->size()); }
614 
615     //! Return complete string (for debugging purposes)
get_string(const String & s,size_t depth=0) const616     std::string get_string(const String& s, size_t depth = 0) const
617     { return text_->substr(s + depth); }
618 
619     //! Subset this string set using iterator range.
sub(Iterator begin,Iterator end) const620     StringSuffixSet sub(Iterator begin, Iterator end) const
621     { return StringSuffixSet(*text_, begin, end); }
622 
623     //! Allocate a new temporary string container with n empty Strings
allocate(size_t n) const624     Container allocate(size_t n) const
625     { return std::make_pair(*text_, std::vector<String>(n)); }
626 
627     //! Deallocate a temporary string container
deallocate(Container & c)628     static void deallocate(Container& c)
629     { std::vector<String> v; v.swap(c.second); }
630 
631     //! Construct from a string container
StringSuffixSet(Container & c)632     explicit StringSuffixSet(Container& c)
633         : text_(&c.first),
634           begin_(c.second.begin()), end_(c.second.end())
635     { }
636 
637 protected:
638     //! reference to base text
639     const Text* text_;
640 
641     //! iterators inside the output suffix array.
642     Iterator begin_, end_;
643 };
644 
645 /******************************************************************************/
646 
647 } // namespace sort_strings_detail
648 
649 //! \}
650 
651 } // namespace tlx
652 
653 #endif // !TLX_SORT_STRINGS_STRING_SET_HEADER
654 
655 /******************************************************************************/
656