41 namespace suffix_sorting {
47 template <
typename Index>
53 return os <<
'(' << ri.index <<
'|' << ri.rank <<
')';
58 template <
typename Index>
59 struct IndexQuadRank {
64 return std::tie(rank[0], rank[1], rank[2], rank[3])
65 == std::tie(b.rank[0], b.rank[1], b.rank[2], b.rank[3]);
68 bool operator < (
const IndexQuadRank& b)
const {
69 return rank[0] < b.rank[0] ||
70 (rank[0] == b.rank[0] && rank[1] < b.rank[1]) ||
71 (rank[0] == b.rank[0] && rank[1] == b.rank[1]
72 && rank[2] < b.rank[2]) ||
73 (rank[0] == b.rank[0] && rank[1] == b.rank[1]
74 && rank[2] == b.rank[2] && rank[3] < b.rank[3]) ||
75 (rank[0] == b.rank[0] && rank[1] == b.rank[1]
76 && rank[2] == b.rank[2] && rank[3] == b.rank[3]
80 friend std::ostream&
operator << (std::ostream& os,
const IndexQuadRank& ri) {
81 return os <<
'(' << ri.index <<
'|' << ri.rank[0] <<
'|' << ri.rank[1]
82 <<
'|' << ri.rank[2] <<
'|' << ri.rank[3] <<
')';
86 template <
typename AlphabetType,
typename Index>
87 struct QuadCharIndex {
92 return std::tie(ch[0], ch[1], ch[2], ch[3])
93 == std::tie(b.ch[0], b.ch[1], b.ch[2], b.ch[3]);
96 bool operator < (
const QuadCharIndex& b)
const {
97 return std::tie(ch[0], ch[1], ch[2], ch[3])
98 < std::tie(b.ch[0], b.ch[1], b.ch[2], b.ch[3]);
101 friend std::ostream&
operator << (std::ostream& os,
const QuadCharIndex& chars) {
102 return os <<
'[' << chars.index <<
": " << chars.ch[0] <<
',' << chars.ch[1]
103 <<
',' << chars.ch[2] <<
',' << chars.ch[3] <<
']';
114 template <
typename Index>
115 struct IndexRankStatus {
121 bool operator == (
const IndexRankStatus& b)
const {
122 return rank == b.rank;
129 bool operator < (
const IndexRankStatus& b)
const {
130 return rank < b.rank || (rank == b.rank && index > b.index);
133 friend std::ostream&
operator << (std::ostream& os,
const IndexRankStatus& irs) {
134 return os <<
"(i: " << irs.index <<
"| r: " << irs.rank <<
"| s: " 135 <<
static_cast<uint8_t
>(irs.status) <<
")";
140 template <
typename Index>
141 struct IndexQuadRankStatus {
146 friend std::ostream&
operator << (std::ostream& os,
const IndexQuadRankStatus& iqrs) {
147 return os <<
"(i: " << iqrs.index <<
"| r1: " << iqrs.rank[0] <<
"| r2: " 148 << iqrs.rank[1] <<
"| r3: " << iqrs.rank[2] <<
"| r4: " 149 << iqrs.rank[3] <<
"| s: " <<
static_cast<uint8_t
>(iqrs.status) <<
")";
154 template <
typename Index>
161 friend std::ostream&
operator << (std::ostream& os,
const Index3Rank& irrr) {
162 return os <<
"( i: " << irrr.index <<
"| r1: " << irrr.rank1
163 <<
"| r2: " << irrr.rank2 <<
"| r3: " << irrr.rank3 <<
")";
167 template <
typename Index,
typename InputDIA>
169 LOG1 <<
"Running PrefixQuadruplingDiscarding";
171 using Char =
typename InputDIA::ValueType;
173 using IndexQuadRank = suffix_sorting::IndexQuadRank<Index>;
174 using QuadCharIndex = suffix_sorting::QuadCharIndex<Char, Index>;
175 using IndexQuadRankStatus = suffix_sorting::IndexQuadRankStatus<Index>;
176 using IndexRankStatus = suffix_sorting::IndexRankStatus<Index>;
177 using Index3Rank = suffix_sorting::Index3Rank<Index>;
179 size_t input_bit_size =
sizeof(Char) << 3;
180 size_t k_fitting =
sizeof(Index) /
sizeof(Char);
182 if (packed && k_fitting <= 4) {
183 LOG1 <<
"Ignoring packing, as it would have no effect." 184 <<
" More than 4 characters must be packed to make a difference." 185 <<
" Currently " << k_fitting <<
" characters are packed.";
188 if (packed && k_fitting < 16) {
189 LOG1 << k_fitting <<
" bytes can be packed into the space used by one suffix array index.";
190 LOG1 <<
"This is not enough to skip quadrupling steps. We can decide more suffixes initially, though.";
193 size_t iteration = 1;
195 if (packed && k_fitting >= 16) {
197 size_t tmp = k_fitting;
198 while (tmp >>= 1) ++iteration;
207 .template FlatWindow<IndexRank>(
210 size_t result = rb[0];
211 for (
size_t i = 1; i < k_fitting; ++i)
212 result = (result << input_bit_size) | rb[i];
213 emit(
IndexRank { Index(index), Index(result) });
214 if (index + k_fitting == input_size) {
215 for (
size_t i = 1; i < k_fitting; ++i) {
217 for (
size_t j = i + 1; j < k_fitting; ++j)
218 result = (result << input_bit_size) | rb[j];
219 result <<= i * input_bit_size;
220 emit(
IndexRank { Index(index + i), Index(result) });
225 return a.rank < b.rank;
229 chars_sorted.
Keep().
Print(
"chars_sorted packed");
233 .template FlatWindow<IndexRank>(
239 rb[1].
index, Index(rb[0].rank == rb[1].rank ? 0 : index + 2)
245 (a.rank > b.rank ? a.rank : b.rank)
252 .template FlatWindow<QuadCharIndex>(
256 { rb[0], rb[1], rb[2], rb[3] }, Index(index)
260 if (index == input_size - 3) {
262 { rb[0], rb[1], rb[2],
263 std::numeric_limits<Char>::lowest() },
268 std::numeric_limits<Char>::lowest(),
269 std::numeric_limits<Char>::lowest() },
274 std::numeric_limits<Char>::lowest(),
275 std::numeric_limits<Char>::lowest(),
276 std::numeric_limits<Char>::lowest() },
284 chars_sorted.Keep().Print(
"chars_sorted");
288 .template FlatWindow<IndexRank>(
301 (a.rank > b.rank ? a.rank : b.rank)
307 names.
Keep().Print(
"names");
311 .template FlatWindow<IndexRankStatus>(
316 emit(IndexRankStatus { rb[0].index, rb[0].rank, status });
318 if (rb[0].rank != rb[1].rank && rb[1].rank != rb[2].rank)
319 emit(IndexRankStatus { rb[1].index, rb[1].rank,
Status::UNIQUE });
322 if (index == input_size - 3) {
324 emit(IndexRankStatus { rb[2].index, rb[2].rank, status });
328 auto names_unique_sorted =
330 .Sort([iteration](
const IndexRankStatus& a,
const IndexRankStatus& b) {
331 Index mod_mask = (Index(1) << (iteration << 1)) - 1;
332 Index div_mask = ~mod_mask;
334 if ((a.index & mod_mask) == (b.index & mod_mask))
335 return (a.index & div_mask) < (b.index & div_mask);
337 return (a.index & mod_mask) < (b.index & mod_mask);
341 names_unique_sorted.Keep().Print(
"Names unique sorted");
343 std::vector<DIA<IndexQuadRank> > fully_discarded;
346 size_t next_index = size_t(1) << (iteration << 1);
347 size_t names_size = names_unique_sorted.Keep().Size();
351 auto discarded_names =
352 names_unique_sorted.Keep()
353 .template FlatWindow<IndexQuadRankStatus>(
359 emit(IndexQuadRankStatus {
361 { rb[0].rank, Index(0), Index(0), Index(0) },
365 emit(IndexQuadRankStatus {
367 { rb[1].rank, Index(0), Index(0), Index(0) },
371 emit(IndexQuadRankStatus {
373 { rb[2].rank, Index(0), Index(0), Index(0) },
377 emit(IndexQuadRankStatus {
379 { rb[3].rank, Index(0), Index(0), Index(0) },
386 emit(IndexQuadRankStatus {
388 { rb[4].rank, Index(0), Index(0), Index(0) },
392 emit(IndexQuadRankStatus {
394 { rb[4].rank, Index(0), Index(0), Index(0) },
399 Index rank1 = (rb[0].index + Index(next_index) == rb[1].index) ? rb[1].rank : Index(0);
400 Index rank2 = (rb[0].index + Index(2 * next_index) == rb[2].index) ? rb[2].rank : Index(0);
401 Index rank3 = (rb[0].index + Index(3 * next_index) == rb[3].index) ? rb[3].rank : Index(0);
402 emit(IndexQuadRankStatus { rb[0].index, { rb[0].rank, rank1, rank2, rank3 },
Status::UNDECIDED });
408 emit(IndexQuadRankStatus {
410 { rb[0].rank, Index(0), Index(0), Index(0) },
414 emit(IndexQuadRankStatus {
416 { rb[0].rank, Index(0), Index(0), Index(0) },
421 emit(IndexQuadRankStatus {
423 { rb[1].rank, Index(0), Index(0), Index(0) },
426 else if (rb.size() > 1)
427 emit(IndexQuadRankStatus {
429 { rb[1].rank, Index(0), Index(0), Index(0) },
434 emit(IndexQuadRankStatus {
436 { rb[2].rank, Index(0), Index(0), Index(0) },
439 else if (rb.size() > 2)
440 emit(IndexQuadRankStatus {
442 { rb[2].rank, Index(0), Index(0), Index(0) },
447 emit(IndexQuadRankStatus {
449 { rb[3].rank, Index(0), Index(0), Index(0) },
452 else if (rb.size() > 3)
453 emit(IndexQuadRankStatus {
455 { rb[3].rank, Index(0), Index(0), Index(0) },
459 if (index == names_size - 4) {
465 rank1 = (rb[0].index + Index(next_index) == rb[1].index) ? rb[1].rank : Index(0);
466 rank2 = (rb[0].index + Index(2 * next_index) == rb[2].index) ? rb[2].rank : Index(0);
467 rank3 = (rb[0].index + Index(3 * next_index) == rb[3].index) ? rb[3].rank : Index(0);
468 emit(IndexQuadRankStatus { rb[0].index, { rb[0].rank, rank1, rank2, rank3 },
Status::UNDECIDED });
471 rank1 = (rb[1].index + Index(next_index) == rb[2].index) ? rb[2].rank : Index(0);
472 rank2 = (rb[1].index + Index(2 * next_index) == rb[3].index) ? rb[3].rank : Index(0);
473 emit(IndexQuadRankStatus { rb[1].index, { rb[1].rank, rank1, rank2, Index(0) },
Status::UNDECIDED });
476 rank1 = (rb[2].index + Index(next_index) == rb[3].index) ? rb[3].rank : Index(0);
477 emit(IndexQuadRankStatus { rb[2].index, { rb[2].rank, rank1, Index(0), Index(0) },
Status::UNDECIDED });
480 emit(IndexQuadRankStatus { rb[3].index, { rb[3].rank, Index(0), Index(0), Index(0) },
Status::UNDECIDED });
485 discarded_names.Keep()
486 .Filter([](
const IndexQuadRankStatus& iqrs) {
489 .Map([](
const IndexQuadRankStatus& iqrs) {
490 return IndexQuadRank {
492 { iqrs.rank[0], iqrs.rank[1], iqrs.rank[2], iqrs.rank[3] }
497 new_decided.Keep().Print(
"new_decided");
499 auto partial_discarded =
500 discarded_names.Keep()
501 .Filter([](
const IndexQuadRankStatus& iqrs) {
504 .Map([](
const IndexQuadRankStatus& iqrs) {
505 return IndexRankStatus {
513 partial_discarded.Keep().Print(
"partial_discarded");
517 .Filter([](
const IndexQuadRankStatus& iqrs) {
520 .Map([](
const IndexQuadRankStatus& iqrs) {
521 return IndexQuadRank {
523 { iqrs.rank[0], iqrs.rank[1], iqrs.rank[2], iqrs.rank[3] }
526 .Sort([](
const IndexQuadRank& a,
const IndexQuadRank& b) {
531 undiscarded.Keep().Print(
"undiscarded");
533 fully_discarded.emplace_back(new_decided.Cache());
535 size_t duplicates = undiscarded.Keep().Size();
537 if (input_dia.context().my_rank() == 0) {
538 sLOG1 <<
"iteration" << iteration - 1
539 <<
"duplicates" << duplicates;
542 if (duplicates == 0) {
545 Union(fully_discarded)
547 .Map([](
const IndexQuadRank& iqr) {
550 return sa.Collapse();
555 .template FlatWindow<Index3Rank>(
559 emit(Index3Rank { rb[0].index, Index(0), Index(0), rb[0].rank[0] });
561 Index i = rb[0].rank[0] == rb[1].rank[0] ? Index(0) : Index(index + 1);
563 if (rb[0].rank[0] != rb[1].rank[0])
564 r = Index(index + 1);
566 r = (rb[0].rank[1] == rb[1].rank[1] &&
567 rb[0].rank[2] == rb[1].rank[2] &&
568 rb[0].rank[3] == rb[1].rank[3]) ? Index(0) : Index(index + 1);
569 emit(Index3Rank { rb[1].index, i, r, rb[1].rank[0] });
573 emit(Index3Rank { rb[0].index, Index(0), Index(0), rb[0].rank[0] });
575 .PrefixSum([](
const Index3Rank& a,
const Index3Rank& b) {
578 std::max<Index>(a.rank1, b.rank1),
579 std::max<Index>(a.rank2, b.rank2),
583 .Map([](
const Index3Rank& ir) {
584 return IndexRank { ir.index, ir.rank3 + (ir.rank2 - ir.rank1) };
591 .template FlatWindow<IndexRankStatus>(
596 emit(IndexRankStatus { rb[0].index, rb[0].rank, status });
598 if (rb[0].rank != rb[1].rank && rb[1].rank != rb[2].rank)
599 emit(IndexRankStatus { rb[1].index, rb[1].rank,
Status::UNIQUE });
602 if (index == duplicates - 3) {
604 emit(IndexRankStatus { rb[2].index, rb[2].rank, status });
609 emit(IndexRankStatus { rb[0].index, rb[0].rank,
Status::UNIQUE });
610 emit(IndexRankStatus { rb[1].index, rb[1].rank,
Status::UNIQUE });
615 names_unique.Keep().Print(
"names_unique");
617 names_unique_sorted =
619 .Union(partial_discarded)
620 .Sort([iteration](
const IndexRankStatus& a,
const IndexRankStatus& b) {
621 Index mod_mask = (Index(1) << (iteration << 1)) - 1;
622 Index div_mask = ~mod_mask;
624 if ((a.index & mod_mask) == (b.index & mod_mask))
625 return (a.index & div_mask) < (b.index & div_mask);
627 return (a.index & mod_mask) < (b.index & mod_mask);
631 names_unique_sorted.Keep().Print(
"names_unique_sorted");
635 template <
typename Index,
typename InputDIA>
637 LOG1 <<
"Running PrefixQuadrupling";
639 using Char =
typename InputDIA::ValueType;
641 using IndexQuadRank = suffix_sorting::IndexQuadRank<Index>;
642 using QuadCharIndex = suffix_sorting::QuadCharIndex<Char, Index>;
644 size_t input_bit_size =
sizeof(Char) << 3;
645 size_t k_fitting =
sizeof(Index) /
sizeof(Char);
647 if (packed && k_fitting <= 4) {
648 LOG1 <<
"Ignoring packing, as it would have no effect." 649 <<
" More than 4 characters must be packed to make a difference." 650 <<
" Currently " << k_fitting <<
" characters are packed.";
653 if (packed && k_fitting < 16) {
654 LOG1 << k_fitting <<
" bytes can be packed into the space used by one suffix array index.";
655 LOG1 <<
"This is not enough to skip quadrupling steps. We can decide more suffixes initially, though.";
658 size_t iteration = 1;
660 if (packed && k_fitting >= 16) {
662 size_t tmp = k_fitting;
663 while (tmp >>= 1) ++iteration;
672 .template FlatWindow<IndexRank>(
675 size_t result = rb[0];
676 for (
size_t i = 1; i < k_fitting; ++i)
677 result = (result << input_bit_size) | rb[i];
678 emit(
IndexRank { Index(index), Index(result) });
679 if (index + k_fitting == input_size) {
680 for (
size_t i = 1; i < k_fitting; ++i) {
682 for (
size_t j = i + 1; j < k_fitting; ++j)
683 result = (result << input_bit_size) | rb[j];
684 result <<= i * input_bit_size;
685 emit(
IndexRank { Index(index + i), Index(result) });
690 return a.rank < b.rank;
694 chars_sorted.
Keep().
Print(
"chars_sorted packed");
698 .template FlatWindow<IndexRank>(
704 rb[1].
index, Index(rb[0].rank == rb[1].rank ? 0 : index + 2)
711 .template FlatWindow<QuadCharIndex>(
715 { rb[0], rb[1], rb[2], rb[3] }, Index(index)
719 if (index == input_size - 3) {
721 { rb[0], rb[1], rb[2],
722 std::numeric_limits<Char>::lowest() },
727 std::numeric_limits<Char>::lowest(),
728 std::numeric_limits<Char>::lowest() },
733 std::numeric_limits<Char>::lowest(),
734 std::numeric_limits<Char>::lowest(),
735 std::numeric_limits<Char>::lowest() },
744 .template FlatWindow<IndexRank>(
756 auto number_duplicates =
759 return ir.rank == Index(0);
763 if (number_duplicates == 0) {
764 if (input_dia.context().my_rank() == 0)
765 sLOG1 <<
"Finished before doubling in loop";
773 return sa.Collapse();
784 names.
Keep().Print(
"names before loop");
790 Index mod_mask = (Index(1) << (iteration << 1)) - 1;
791 Index div_mask = ~mod_mask;
793 if ((a.index & mod_mask) == (b.index & mod_mask))
794 return (a.index & div_mask) < (b.index & div_mask);
796 return (a.index & mod_mask) < (b.index & mod_mask);
799 size_t next_index = size_t(1) << (iteration << 1);
804 .template FlatWindow<IndexQuadRank>(
807 Index rank1 = (rb[0].index + Index(next_index) == rb[1].index) ? rb[1].rank : Index(0);
808 Index rank2 = (rb[0].index + Index(2 * next_index) == rb[2].index) ? rb[2].rank : Index(0);
809 Index rank3 = (rb[0].index + Index(3 * next_index) == rb[3].index) ? rb[3].rank : Index(0);
810 emit(IndexQuadRank { rb[0].index, { rb[0].rank, rank1, rank2, rank3 }
814 if (index == input_size - 3) {
815 Index rank1 = (rb[0].index + Index(next_index) == rb[1].index) ? rb[1].rank : Index(0);
816 Index rank2 = (rb[0].index + Index(2 * next_index) == rb[2].index) ? rb[2].rank : Index(0);
817 emit(IndexQuadRank { rb[0].index, { rb[0].rank, rank1, rank2, Index(0) }
820 rank1 = (rb[1].index + Index(next_index) == rb[2].index) ? rb[2].rank : Index(0);
821 emit(IndexQuadRank { rb[1].index, { rb[1].rank, rank1, Index(0), Index(0) }
823 emit(IndexQuadRank { rb[2].index, { rb[2].rank, Index(0), Index(0), Index(0) }
831 .template FlatWindow<IndexRank>(
836 if (rb[0] == rb[1] && (rb[0].rank[1] != Index(0) || rb[0].rank[2] != Index(0) || rb[0].rank[3] != Index(0)))
845 return ir.rank == Index(0);
849 if (input_dia.context().my_rank() == 0) {
850 sLOG1 <<
"iteration" << iteration - 1
851 <<
"duplicates" << number_duplicates;
854 if (number_duplicates == 0) {
861 return sa.Collapse();
871 names.
Keep().Print(
"names");
876 const DIA<uint8_t>& input_dia,
size_t input_size,
bool packed);
879 const DIA<uint8_t>& input_dia,
size_t input_size,
bool packed);
881 #if !THRILL_ON_TRAVIS 884 const DIA<uint8_t>& input_dia,
size_t input_size,
bool packed);
887 const DIA<uint8_t>& input_dia,
size_t input_size,
bool packed);
890 const DIA<uint8_t>& input_dia,
size_t input_size,
bool packed);
893 const DIA<uint8_t>& input_dia,
size_t input_size,
bool packed);
tlx::RingBuffer< Type, Allocator > RingBuffer
template DIA< uint64_t > PrefixQuadruplingDiscarding< uint64_t >(const DIA< uint8_t > &input_dia, size_t input_size, bool packed)
DIA is the interface between the user and the Thrill framework.
static uint_pair max()
return an uint_pair instance containing the largest value possible
auto Union(const FirstDIA &first_dia, const DIAs &... dias)
Union is a LOp, which creates the union of all items from any number of DIAs as a single DIA...
A ring (circular) buffer of static (non-growing) size.
auto PrefixSum(const SumFunction &sum_function=SumFunction(), const ValueType &initial_element=ValueType()) const
PrefixSum is a DOp, which computes the (inclusive) prefix sum of all elements.
template DIA< uint32_t > PrefixQuadrupling< uint32_t >(const DIA< uint8_t > &input_dia, size_t input_size, bool packed)
void Print(const std::string &name=std::string()) const
Print is an Action, which collects all data of the DIA at the worker 0 and prints using ostream seria...
auto Sort(const CompareFunction &compare_function=CompareFunction()) const
Sort is a DOp, which sorts a given DIA according to the given compare_function.
auto Map(const MapFunction &map_function) const
Map applies map_function : to each item of a DIA and delivers a new DIA contains the returned values...
bool operator==(const uint_pair &b) const
equality checking operator
std::ostream & operator<<(std::ostream &os, const Status &s)
DIA< Index > PrefixQuadrupling(const InputDIA &input_dia, size_t input_size, bool packed)
struct examples::suffix_sorting::IndexRank TLX_ATTRIBUTE_PACKED
DIA< Index > PrefixQuadruplingDiscarding(const InputDIA &input_dia, size_t input_size, bool packed)
template DIA< uint32_t > PrefixQuadruplingDiscarding< uint32_t >(const DIA< uint8_t > &input_dia, size_t input_size, bool packed)
const DIA & Keep(size_t increase=1) const
Mark the referenced DIANode for keeping, which makes children not consume the data when executing...
bool operator<(const uint_pair &b) const
less-than comparison operator
template DIA< uint64_t > PrefixQuadrupling< uint64_t >(const DIA< uint8_t > &input_dia, size_t input_size, bool packed)