<[email protected]>
<http://www.gnu.org/licenses/>
#include <string.h>
#include "../tools/stringtools.h"
#include "../tools/contest.h"
#ifndef BINGMANN_LCP_INSSORT_H_
#define BINGMANN_LCP_INSSORT_H_
namespace bingmann_lcp_inssort {
using namespace stringtools;
static inline
void lcp_insertion_sort(string* str, uintptr_t* lcp, size_t n, size_t depth)
{
    if (n <= 1) return;
    for (size_t j = 0; j < n - 1; ++j)
    {
        
        string new_str = str[j];
        size_t new_lcp = depth; 
        size_t i = j;
        while (i > 0)
        {
            size_t prev_lcp = new_lcp;
            string cur_str = str[i - 1];
            size_t cur_lcp = lcp[i];
            if (cur_lcp < new_lcp)
            {
                
                break;
            }
            else if (cur_lcp == new_lcp)
            {
                
                string s1 = new_str + new_lcp;
                string s2 = cur_str + new_lcp;
                while (*s1 != 0 && *s1 == *s2)
                    ++s1, ++s2, ++new_lcp;
                
                if (*s1 >= *s2)
                {
                    
                    lcp[i] = new_lcp;
                    
                    new_lcp = prev_lcp;
                    break;
                }
            }
            
            str[i] = cur_str;
            lcp[i + 1] = cur_lcp;
            --i;
        }
        str[i] = new_str;
        lcp[i + 1] = new_lcp;
    }
    
    {
        size_t j = n - 1;
        
        string new_str = str[j];
        size_t new_lcp = depth; 
        size_t i = j;
        while (i > 0)
        {
            size_t prev_lcp = new_lcp;
            string cur_str = str[i - 1];
            size_t cur_lcp = lcp[i];
            if (cur_lcp < new_lcp)
            {
                
                break;
            }
            else if (cur_lcp == new_lcp)
            {
                
                string s1 = new_str + new_lcp;
                string s2 = cur_str + new_lcp;
                while (*s1 != 0 && *s1 == *s2)
                    ++s1, ++s2, ++new_lcp;
                
                if (*s1 >= *s2)
                {
                    
                    lcp[i] = new_lcp;
                    
                    new_lcp = prev_lcp;
                    break;
                }
            }
            
            str[i] = cur_str;
            if (i + 1 < n) 
                lcp[i + 1] = cur_lcp;
            --i;
        }
        str[i] = new_str;
        if (i + 1 < n) 
            lcp[i + 1] = new_lcp;
    }
}
static inline
void lcp_insertion_sort_pseudocode(string* str, uintptr_t* lcp, size_t n, size_t depth)
{
    unsigned int cmp = 0;
    for (size_t j = 0; j < n; ++j)
    {
        
        string snew = str[j];
        size_t h = depth; 
        size_t i = j;
        while (i > 0)
        {
            if (lcp[i] < h)
            {
                
                break;
            }
            else if (lcp[i] == h)
            {
                
                size_t prev_lcp = h;
                string s2 = str[i - 1];
                while (cmp++, snew[h] != 0 && snew[h] == s2[h])
                    ++h;
                
                if (snew[h] >= s2[h])
                {
                    
                    lcp[i] = h;
                    
                    h = prev_lcp;
                    break;
                }
            }
            
            str[i] = str[i - 1];
            lcp[i + 1] = lcp[i];
            --i;
        }
        str[i] = snew;
        lcp[i + 1] = h;
    }
    std::cout << "lcp_inssort comparisons = " << cmp << "\n";
}
static inline
void lcp_insertion_sort(string* str, size_t n, size_t depth)
{
    uintptr_t tmp_lcp[n];
    return lcp_insertion_sort(str, tmp_lcp, n, depth);
}
static inline
void lcp_insertion_sort(StringShadowPtr& str, size_t depth)
{
    return lcp_insertion_sort(str.active(), str.size(), depth);
}
static inline
void lcp_insertion_sort(StringShadowLcpPtr& str, size_t depth)
{
    return lcp_insertion_sort(str.active(), str.lcparray(), str.size(), depth);
}
static inline
void test_lcp_insertion_sort(string* strings, size_t n)
{
    lcp_insertion_sort(strings, n, 0);
}
CONTESTANT_REGISTER(test_lcp_insertion_sort,
    "bingmann/lcp_insertion_sort",
    "LCP-aware insertion sort")
static inline
void test_lcp_insertion_sort_nolcp(string* strings, size_t n)
{
    string* shadow = new string[n]; 
    StringShadowPtr strptr(strings, shadow, n);
    lcp_insertion_sort(strptr, 0);
    delete [] shadow;
}
CONTESTANT_REGISTER(test_lcp_insertion_sort_nolcp,
    "bingmann/lcp_insertion_sort_nolcp",
    "LCP-aware insertion sort (without LCP output)")
static inline
void test_lcp_insertion_sort_pseudocode(string* strings, size_t n)
{
    string* shadow = new string[n+1]; 
    StringShadowLcpPtr strptr(strings, shadow, n);
    strptr.lcp(0) = 42; 
    lcp_insertion_sort_pseudocode(strptr.active(), strptr.lcparray(), n, 0);
    stringtools::verify_lcp(strptr.active(), strptr.lcparray(), n, 42);
    delete [] shadow;
}
CONTESTANT_REGISTER(test_lcp_insertion_sort_pseudocode,
    "bingmann/lcp_insertion_sort_pseudocode",
    "LCP-aware insertion sort close to pseudo-code, with checking")
static inline
void lcp_insertion_sort_cache(string* str, uintptr_t* lcp, char_type* cache,
                              size_t n, size_t depth)
{
    if (n <= 1) return;
    for (size_t j = 0; j < n - 1; ++j)
    {
        
        string new_str = str[j];
        char_type new_ch = new_str[depth];
        size_t new_lcp = depth; 
        size_t i = j;
        while (i > 0)
        {
            size_t prev_lcp = new_lcp;
            string cur_str = str[i - 1];
            size_t cur_lcp = lcp[i];
            if (cur_lcp < new_lcp)
            {
                
                break;
            }
            else if (cur_lcp == new_lcp)
            {
                
                string s2 = cur_str + new_lcp;
                while (new_ch != 0 && new_ch == *s2)
                {
                    ++s2, ++new_lcp;
                    new_ch = new_str[new_lcp];
                }
                
                if (new_ch >= *s2)
                {
                    
                    lcp[i] = new_lcp;
                    cache[i] = new_ch;
                    
                    new_lcp = prev_lcp;
                    break;
                }
            }
            
            str[i] = cur_str;
            cache[i] = cache[i - 1];
            lcp[i + 1] = cur_lcp;
            --i;
        }
        str[i] = new_str;
        lcp[i + 1] = new_lcp;
        cache[i + 1] = str[i + 1][new_lcp];
    }
    
    {
        size_t j = n - 1;
        
        string new_str = str[j];
        char_type new_ch = new_str[depth];
        size_t new_lcp = depth; 
        size_t i = j;
        while (i > 0)
        {
            size_t prev_lcp = new_lcp;
            string cur_str = str[i - 1];
            size_t cur_lcp = lcp[i];
            if (cur_lcp < new_lcp)
            {
                
                break;
            }
            else if (cur_lcp == new_lcp)
            {
                
                string s2 = cur_str + new_lcp;
                while (new_ch != 0 && new_ch == *s2)
                {
                    ++s2, ++new_lcp;
                    new_ch = new_str[new_lcp];
                }
                
                if (new_ch >= *s2)
                {
                    
                    lcp[i] = new_lcp;
                    cache[i] = new_ch;
                    
                    new_lcp = prev_lcp;
                    break;
                }
            }
            
            str[i] = cur_str;
            cache[i] = cache[i - 1];
            if (i + 1 < n) 
                lcp[i + 1] = cur_lcp;
            --i;
        }
        str[i] = new_str;
        if (i + 1 < n) 
        {
            lcp[i + 1] = new_lcp;
            cache[i + 1] = str[i + 1][new_lcp];
        }
    }
}
static inline
void test_lcp_insertion_sort_cache(string* strings, size_t n)
{
    lcp_t* lcps = new lcp_t[n]; 
    char_type* cache = new char_type[n]; 
    lcp_insertion_sort_cache(strings, lcps, cache, n, 0);
    stringtools::verify_lcp_cache(strings, lcps, cache, n, -1);
    delete [] lcps;
    delete [] cache;
}
CONTESTANT_REGISTER(test_lcp_insertion_sort_cache,
    "bingmann/lcp_insertion_sort_cache",
    "LCP-aware insertion sort (with distinguishing character cache)")
} 
#endif