<tb@panthema.net>
<http://www.gnu.org/licenses/>
template <size_t numsplitters>
struct TreeBuilderEqual
{
key_type* m_tree;
unsigned char* m_lcp_iter;
key_type* m_samples;
TreeBuilderEqual(key_type* splitter_tree, unsigned char* splitter_lcp,
key_type* samples, size_t samplesize)
: m_tree( splitter_tree ),
m_lcp_iter( splitter_lcp ),
m_samples( samples )
{
key_type sentinel = 0;
recurse(samples, samples + samplesize, 1, sentinel);
assert(m_lcp_iter == splitter_lcp + numsplitters);
splitter_lcp[0] &= 0x80;
splitter_lcp[numsplitters] = 0;
}
ptrdiff_t snum(key_type* s) const
{
return (ptrdiff_t)(s - m_samples);
}
key_type recurse(key_type* lo, key_type* hi, unsigned int treeidx,
key_type& rec_prevkey)
{
DBG(debug_splitter, "rec_buildtree(" << snum(lo) << "," << snum(hi)
<< ", treeidx=" << treeidx << ")");
key_type* mid = lo + (ptrdiff_t)(hi - lo) / 2;
DBG(debug_splitter, "tree[" << treeidx << "] = samples[" << snum(mid) << "] = "
<< toHex(*mid));
key_type mykey = m_tree[treeidx] = *mid;
#if 1
key_type* midlo = mid;
while (lo < midlo && *(midlo-1) == mykey) midlo--;
key_type* midhi = mid;
while (midhi+1 < hi && *midhi == mykey) midhi++;
if (midhi - midlo > 1)
DBG(0, "key range = [" << snum(midlo) << "," << snum(midhi) << ")");
#else
key_type *midlo = mid, *midhi = mid+1;
#endif
if (2 * treeidx < numsplitters)
{
key_type prevkey = recurse(lo, midlo, 2 * treeidx + 0, rec_prevkey);
key_type xorSplit = prevkey ^ mykey;
DBG(debug_splitter, " lcp: " << toHex(prevkey) << " XOR " << toHex(mykey) << " = "
<< toHex(xorSplit) << " - " << count_high_zero_bits(xorSplit) << " bits = "
<< count_high_zero_bits(xorSplit) / 8 << " chars lcp");
*m_lcp_iter++ = (count_high_zero_bits(xorSplit) / 8)
| ((mykey & 0xFF) ? 0 : 0x80);
return recurse(midhi, hi, 2 * treeidx + 1, mykey);
}
else
{
key_type xorSplit = rec_prevkey ^ mykey;
DBG(debug_splitter, " lcp: " << toHex(rec_prevkey) << " XOR " << toHex(mykey) << " = "
<< toHex(xorSplit) << " - " << count_high_zero_bits(xorSplit) << " bits = "
<< count_high_zero_bits(xorSplit) / 8 << " chars lcp");
*m_lcp_iter++ = (count_high_zero_bits(xorSplit) / 8)
| ((mykey & 0xFF) ? 0 : 0x80);
return mykey;
}
}
};
template <size_t treebits>
struct ClassifyEqual
{
static const size_t numsplitters = (1 << treebits) - 1;
key_type splitter_tree[numsplitters+1];
inline unsigned int
find_bkt_tree(const key_type& key) const
{
unsigned int i;
#if 1
asm("mov $1, %%rax \n"
"1: \n"
"cmpq (%[splitter_tree],%%rax,8), %[key] \n"
"je 2f \n"
"lea (%%rax,%%rax), %%rax \n"
"lea 1(%%rax), %%rcx \n"
"cmova %%rcx, %%rax \n"
"cmp %[numsplitters1], %%rax \n"
"jb 1b \n"
"sub %[numsplitters1], %%rax \n"
"lea (%%rax,%%rax), %%rax \n"
"jmp 3f \n"
"2: \n"
"bsr %%rax, %%rdx \n"
"mov %[treebits], %%rcx \n"
"sub %%rdx, %%rcx \n"
"shl %%cl, %%rax \n"
"and %[numsplitters], %%rax \n"
"lea -1(%%rcx), %%rcx \n"
"mov $1, %%rdx \n"
"shl %%cl, %%rdx \n"
"or %%rdx, %%rax \n"
"lea -1(%%rax,%%rax), %%rax \n"
"3: \n"
: "=&a" (i)
: [key] "r" (key), [splitter_tree] "r" (splitter_tree),
[numsplitters1] "g" (numsplitters+1),
[treebits] "g" (treebits),
[numsplitters] "g" (numsplitters)
: "rcx", "rdx");
#else
asm("mov $1, %%rax \n"
"1: \n"
"cmpq (%[splitter_tree],%%rax,8), %[key] \n"
"seta %%cl \n"
"movzb %%cl, %%rcx \n"
"je 2f \n"
"lea (%%rcx,%%rax,2), %%rax \n"
"cmp %[numsplitters1], %%rax \n"
"jb 1b \n"
"sub %[numsplitters1], %%rax \n"
"lea (%%rax,%%rax), %%rax \n"
"jmp 3f \n"
"2: \n"
"bsr %%rax, %%rdx \n"
"mov %[treebits], %%rcx \n"
"sub %%rdx, %%rcx \n"
"shl %%cl, %%rax \n"
"and %[numsplitters], %%rax \n"
"lea -1(%%rcx), %%rcx \n"
"mov $1, %%rdx \n"
"shl %%cl, %%rdx \n"
"or %%rdx, %%rax \n"
"lea -1(%%rax,%%rax), %%rax \n"
"3: \n"
: "=&a" (i)
: [key] "r" (key), [splitter_tree] "r" (splitter_tree),
[numsplitters1] "g" (numsplitters+1),
[treebits] "g" (treebits),
[numsplitters] "g" (numsplitters)
: "rcx", "rdx");
#endif
return i;
}
inline void
classify(string* strB, string* strE, uint16_t* bktout, size_t depth) const
{
for (string* str = strB; str != strE; )
{
key_type key = get_char<key_type>(*str++, depth);
unsigned int b = find_bkt_tree(key);
*bktout++ = b;
}
}
inline key_type get_splitter(unsigned int i) const
{
return splitter_tree[ TreeCalculations<treebits>::in_to_levelorder(i) ];
}
inline void
build(key_type* samples, size_t samplesize, unsigned char* splitter_lcp)
{
TreeBuilderEqual<numsplitters>(splitter_tree, splitter_lcp,
samples, samplesize);
}
};
template <size_t TreeBits>
struct ClassifyEqualUnrollTree
{
static const size_t treebits = TreeBits;
static const size_t numsplitters = (1 << treebits) - 1;
key_type splitter_tree[numsplitters+1];
inline unsigned int
find_bkt_tree(const key_type& key) const;
inline void
classify(string* strB, string* strE, uint16_t* bktout, size_t depth) const
{
for (string* str = strB; str != strE; )
{
key_type key = get_char<key_type>(*str++, depth);
unsigned int b = find_bkt_tree(key);
*bktout++ = b;
}
}
inline key_type get_splitter(unsigned int i) const
{
return splitter_tree[ TreeCalculations<treebits>::in_to_levelorder(i) ];
}
inline void
build(key_type* samples, size_t samplesize, unsigned char* splitter_lcp)
{
TreeBuilderEqual<numsplitters>(splitter_tree, splitter_lcp,
samples, samplesize);
}
#define SPLITTER_TREE_STEP \
\
"cmpq (%[splitter_tree],%%rax,8), %[key] \n" \
"je 2f \n" \
"lea (%%rax,%%rax), %%rax \n" \
"lea 1(%%rax), %%rcx \n" \
"cmova %%rcx, %%rax \n"
#define SPLITTER_TREE_END \
\
"sub %[numsplitters1], %%rax \n" \
"lea (%%rax,%%rax), %%rax \n" \
"jmp 3f \n" \
"2: \n" \
"bsr %%rax, %%rdx \n" \
"mov %[treebits], %%rcx \n" \
"sub %%rdx, %%rcx \n" \
"shl %%cl, %%rax \n" \
"and %[numsplitters], %%rax \n" \
"lea -1(%%rcx), %%rcx \n" \
"mov $1, %%rdx \n" \
"shl %%cl, %%rdx \n" \
"or %%rdx, %%rax \n" \
"lea -1(%%rax,%%rax), %%rax \n" \
"3: \n"
};
template <> inline unsigned int
ClassifyEqualUnrollTree<3>::find_bkt_tree(const key_type& key) const
{
unsigned int i;
asm("mov $1, %%rax \n"
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_END
: "=&a" (i)
: [key] "r" (key), [splitter_tree] "r" (splitter_tree),
[numsplitters1] "g" (numsplitters+1),
[treebits] "g" (treebits),
[numsplitters] "g" (numsplitters)
: "rcx", "rdx");
return i;
}
template <> inline unsigned int
ClassifyEqualUnrollTree<4>::find_bkt_tree(const key_type& key) const
{
unsigned int i;
asm("mov $1, %%rax \n"
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_END
: "=&a" (i)
: [key] "r" (key), [splitter_tree] "r" (splitter_tree),
[numsplitters1] "g" (numsplitters+1),
[treebits] "g" (treebits),
[numsplitters] "g" (numsplitters)
: "rcx", "rdx");
return i;
}
template <> inline unsigned int
ClassifyEqualUnrollTree<5>::find_bkt_tree(const key_type& key) const
{
unsigned int i;
asm("mov $1, %%rax \n"
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_END
: "=&a" (i)
: [key] "r" (key), [splitter_tree] "r" (splitter_tree),
[numsplitters1] "g" (numsplitters+1),
[treebits] "g" (treebits),
[numsplitters] "g" (numsplitters)
: "rcx", "rdx");
return i;
}
template <> inline unsigned int
ClassifyEqualUnrollTree<6>::find_bkt_tree(const key_type& key) const
{
unsigned int i;
asm("mov $1, %%rax \n"
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_END
: "=&a" (i)
: [key] "r" (key), [splitter_tree] "r" (splitter_tree),
[numsplitters1] "g" (numsplitters+1),
[treebits] "g" (treebits),
[numsplitters] "g" (numsplitters)
: "rcx", "rdx");
return i;
}
template <> inline unsigned int
ClassifyEqualUnrollTree<7>::find_bkt_tree(const key_type& key) const
{
unsigned int i;
asm("mov $1, %%rax \n"
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_END
: "=&a" (i)
: [key] "r" (key), [splitter_tree] "r" (splitter_tree),
[numsplitters1] "g" (numsplitters+1),
[treebits] "g" (treebits),
[numsplitters] "g" (numsplitters)
: "rcx", "rdx");
return i;
}
template <> inline unsigned int
ClassifyEqualUnrollTree<8>::find_bkt_tree(const key_type& key) const
{
unsigned int i;
asm("mov $1, %%rax \n"
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_END
: "=&a" (i)
: [key] "r" (key), [splitter_tree] "r" (splitter_tree),
[numsplitters1] "g" (numsplitters+1),
[treebits] "g" (treebits),
[numsplitters] "g" (numsplitters)
: "rcx", "rdx");
return i;
}
template <> inline unsigned int
ClassifyEqualUnrollTree<9>::find_bkt_tree(const key_type& key) const
{
unsigned int i;
asm("mov $1, %%rax \n"
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_END
: "=&a" (i)
: [key] "r" (key), [splitter_tree] "r" (splitter_tree),
[numsplitters1] "g" (numsplitters+1),
[treebits] "g" (treebits),
[numsplitters] "g" (numsplitters)
: "rcx", "rdx");
return i;
}
template <> inline unsigned int
ClassifyEqualUnrollTree<10>::find_bkt_tree(const key_type& key) const
{
unsigned int i;
asm("mov $1, %%rax \n"
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_END
: "=&a" (i)
: [key] "r" (key), [splitter_tree] "r" (splitter_tree),
[numsplitters1] "g" (numsplitters+1),
[treebits] "g" (treebits),
[numsplitters] "g" (numsplitters)
: "rcx", "rdx");
return i;
}
template <> inline unsigned int
ClassifyEqualUnrollTree<11>::find_bkt_tree(const key_type& key) const
{
unsigned int i;
asm("mov $1, %%rax \n"
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_END
: "=&a" (i)
: [key] "r" (key), [splitter_tree] "r" (splitter_tree),
[numsplitters1] "g" (numsplitters+1),
[treebits] "g" (treebits),
[numsplitters] "g" (numsplitters)
: "rcx", "rdx");
return i;
}
template <> inline unsigned int
ClassifyEqualUnrollTree<12>::find_bkt_tree(const key_type& key) const
{
unsigned int i;
asm("mov $1, %%rax \n"
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_END
: "=&a" (i)
: [key] "r" (key), [splitter_tree] "r" (splitter_tree),
[numsplitters1] "g" (numsplitters+1),
[treebits] "g" (treebits),
[numsplitters] "g" (numsplitters)
: "rcx", "rdx");
return i;
}
template <> inline unsigned int
ClassifyEqualUnrollTree<13>::find_bkt_tree(const key_type& key) const
{
unsigned int i;
asm("mov $1, %%rax \n"
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_END
: "=&a" (i)
: [key] "r" (key), [splitter_tree] "r" (splitter_tree),
[numsplitters1] "g" (numsplitters+1),
[treebits] "g" (treebits),
[numsplitters] "g" (numsplitters)
: "rcx", "rdx");
return i;
}
template <> inline unsigned int
ClassifyEqualUnrollTree<14>::find_bkt_tree(const key_type& key) const
{
unsigned int i;
asm("mov $1, %%rax \n"
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_END
: "=&a" (i)
: [key] "r" (key), [splitter_tree] "r" (splitter_tree),
[numsplitters1] "g" (numsplitters+1),
[treebits] "g" (treebits),
[numsplitters] "g" (numsplitters)
: "rcx", "rdx");
return i;
}
template <> inline unsigned int
ClassifyEqualUnrollTree<15>::find_bkt_tree(const key_type& key) const
{
unsigned int i;
asm("mov $1, %%rax \n"
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_END
: "=&a" (i)
: [key] "r" (key), [splitter_tree] "r" (splitter_tree),
[numsplitters1] "g" (numsplitters+1),
[treebits] "g" (treebits),
[numsplitters] "g" (numsplitters)
: "rcx", "rdx");
return i;
}
template <> inline unsigned int
ClassifyEqualUnrollTree<16>::find_bkt_tree(const key_type& key) const
{
unsigned int i;
asm("mov $1, %%rax \n"
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_END
: "=&a" (i)
: [key] "r" (key), [splitter_tree] "r" (splitter_tree),
[numsplitters1] "g" (numsplitters+1),
[treebits] "g" (treebits),
[numsplitters] "g" (numsplitters)
: "rcx", "rdx");
return i;
}
template <> inline unsigned int
ClassifyEqualUnrollTree<17>::find_bkt_tree(const key_type& key) const
{
unsigned int i;
asm("mov $1, %%rax \n"
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_END
: "=&a" (i)
: [key] "r" (key), [splitter_tree] "r" (splitter_tree),
[numsplitters1] "g" (numsplitters+1),
[treebits] "g" (treebits),
[numsplitters] "g" (numsplitters)
: "rcx", "rdx");
return i;
}
template <> inline unsigned int
ClassifyEqualUnrollTree<18>::find_bkt_tree(const key_type& key) const
{
unsigned int i;
asm("mov $1, %%rax \n"
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_END
: "=&a" (i)
: [key] "r" (key), [splitter_tree] "r" (splitter_tree),
[numsplitters1] "g" (numsplitters+1),
[treebits] "g" (treebits),
[numsplitters] "g" (numsplitters)
: "rcx", "rdx");
return i;
}
template <> inline unsigned int
ClassifyEqualUnrollTree<19>::find_bkt_tree(const key_type& key) const
{
unsigned int i;
asm("mov $1, %%rax \n"
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_END
: "=&a" (i)
: [key] "r" (key), [splitter_tree] "r" (splitter_tree),
[numsplitters1] "g" (numsplitters+1),
[treebits] "g" (treebits),
[numsplitters] "g" (numsplitters)
: "rcx", "rdx");
return i;
}
template <> inline unsigned int
ClassifyEqualUnrollTree<20>::find_bkt_tree(const key_type& key) const
{
unsigned int i;
asm("mov $1, %%rax \n"
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_STEP
SPLITTER_TREE_END
: "=&a" (i)
: [key] "r" (key), [splitter_tree] "r" (splitter_tree),
[numsplitters1] "g" (numsplitters+1),
[treebits] "g" (treebits),
[numsplitters] "g" (numsplitters)
: "rcx", "rdx");
return i;
}
#undef SPLITTER_TREE_STEP
#undef SPLITTER_TREE_END
static inline void
parallel_sample_sortBTCE(string* strings, size_t n)
{
parallel_sample_sort_base<ClassifyEqual>(strings, n, 0);
}
CONTESTANT_REGISTER_PARALLEL_LCP(
parallel_sample_sortBTCE,
"bingmann/parallel_sample_sortBTCE",
"pS5: binary tree, equality, bktcache")
static inline void
parallel_sample_sortBTCEU1(string* strings, size_t n)
{
parallel_sample_sort_base<ClassifyEqualUnrollTree>(strings, n, 0);
}
CONTESTANT_REGISTER_PARALLEL_LCP(
parallel_sample_sortBTCEU1,
"bingmann/parallel_sample_sortBTCEU1",
"pS5: binary tree, equality, bktcache, unroll tree")