#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FUNC_log2floor 1
#include "util.h"
#include "sort.h"

#define USE_QUADSORT 0

/*
 * Sorting arrays of integers.
 *
 * We really have two quite different use cases.  The first is for internal
 * use, where it's very common to have a small number of inputs.
 * E.g. sorting roots, factors, divisors, totients, prime powers, etc.  Most
 * of these will have small arrays and the overall time is dominated by the
 * real work that created the data.
 * There are some degenerate cases that generate many inputs, but these are
 * exceptional.  Most sorts from our test suite are 32 or fewer items, with
 * the largest being 576 items.
 *
 * The second use is from vecsort, where the user or our PP code has given
 * us a possibly large array to sort.  Here we have the additional challenge
 * of making sure the overhead of Perl->C->Perl is as small as possible.
 *
 *  We have a number of possible choices.
 *
 * 1) Perl's sort.  A cache-aware merge sort, which makes a lot of sense for
 *    its use with arbitrary and complicated data structures, possibly
 *    expensive comparisons, and where a stable sort is highly desirable.
 *    Most of this is irrelevant for sorting simple integers.
 *    Problem 1: We can sort SV's but there isn't a simple UV interface.
 *    Problem 2: It's slow for shuffled inputs, like most stable merge sorts.
 *
 * 2) qsort.  Easy and works, but system dependent.
 *    Can be quite fast -- MacOS/clang is 3x faster than merge sort for
 *    shuffled inputs, and has fast behavior with sorted/reversed data.
 *
 * 3) fluxsort/quadsort/timsort/powersort/glidesort/etc.
 *    fluxsort is extremely fast and has excellent behavior with ordered data.
 *    The main reason it isn't being used here is the code size.
 *
 * 4) insertion / Shell.  Fastest on tiny arrays and very compact.  We use
 *    insertion sort for small chunks.
 *
 * 5) heapsort.  lobby99's implementation here is surprisingly fast and very
 *    consistent across a variety of inputs.  It is used as a fallback if
 *    quicksort is choosing bad partitions.
 *
 * 6) quicksort.  Yes, yet another quicksort implementation.  Fast for small
 *    inputs, competitive for larger.  This uses quasi-median of 9
 *    partitioning, insertion sort for small partitions, and will switch to
 *    heapsort after enough bad partitions, so there is no O(n^2) disaster.
 *
 * 5) radix sort.  With enough integers, radix sort beats everything else on
 *    shuffled data.  Performance on ordered data is decent though not like
 *    fluxsort.  Uses auxiliary data equal to the input size.
 *
 * We use our quicksort for small arrays, radixsort for larger.
 *
 */


/******************************************************************************/

static void insertionsort_uv(UV *array, size_t len) {
  size_t i,j;
  for (i = 1; i < len; i++) {
    UV t = array[i];
    for (j = i; j > 0 && array[j-1] > t; j--)
      array[j] = array[j-1];
    array[j] = t;
  }
}
static void insertionsort_iv(IV *array, size_t len) {
  size_t i,j;
  for (i = 1; i < len; i++) {
    IV t = array[i];
    for (j = i; j > 0 && array[j-1] > t; j--)
      array[j] = array[j-1];
    array[j] = t;
  }
}

#if 0
static void shellsort_uv(UV *array, size_t len) {
  static unsigned short sgaps[] = {209,109,41,19,5,1}; /* Sedgewick 1986 */
  size_t i, j, gap, gi = 0;
  do {
    gap = sgaps[gi++];
    for (i = gap; i < len; i++) {
      UV t = array[i];
      for (j = i; j >= gap && array[j-gap] > t; j -= gap)
        array[j] = array[j-gap];
      array[j] = t;
    }
  } while (gap > 1);
}
static void shellsort_iv(IV *array, size_t len) {
  static unsigned short sgaps[] = {209,109,41,19,5,1}; /* Sedgewick 1986 */
  size_t i, j, gap, gi = 0;
  do {
    gap = sgaps[gi++];
    for (i = gap; i < len; i++) {
      IV t = array[i];
      for (j = i; j >= gap && array[j-gap] > t; j -= gap)
        array[j] = array[j-gap];
      array[j] = t;
    }
  } while (gap > 1);
}
#endif

/******************************************************************************/
/*                             RADIX SORT                                     */
/******************************************************************************/
#define RADIX_BIT 8
#define RADIX     (1u<<RADIX_BIT)
static bool _radixsort(UV *array, size_t n, bool is_iv)
{
  size_t i, j, count[RADIX];
  unsigned r, pass, npass = 0, signshift, shifts[sizeof(UV)];
  UV *a, *b, *ptr[RADIX];
  UV first, prev, varymask = 0, widthmask = 0;
  bool ascending = 1, descending = 1;

  if (n < 2) return 1;

  first = array[0];
  prev = first;
  memset(count, 0, sizeof count);
  for (i = 0; i < n; i++) {
    UV d = array[i];
    varymask |= d ^ first;
    count[d & (RADIX-1)]++;
    if (is_iv) {
      /* Complement negative values so repeated sign bits do not add width. */
      widthmask |= d ^ ((UV)0 - (d >> (BITS_PER_WORD-1)));
      ascending  &= (IV)prev <= (IV)d;
      descending &= (IV)prev >= (IV)d;
    } else {
      ascending  &= prev <= d;
      descending &= prev >= d;
    }
    prev = d;
  }
  if (ascending) return 1;
  if (descending) {
    for (i = 0, j = n-1; i < j; i++, j--) {
      UV t = array[i]; array[i] = array[j]; array[j] = t;
    }
    return 1;
  }

  /* Signed values only need enough bytes to include their effective sign
   * bit.  This avoids sorting redundant sign-extension bytes. */
  signshift = BITS_PER_WORD-RADIX_BIT;
  if (is_iv) {
    signshift = 0;
    while (signshift < BITS_PER_WORD-RADIX_BIT &&
           (widthmask >> (signshift + RADIX_BIT-1)) != 0)
      signshift += RADIX_BIT;
  }

  /* Constant radix digits do not affect ordering. */
  for (r = 0; r <= signshift; r += RADIX_BIT)
    if ((varymask >> r) & (RADIX-1))
      shifts[npass++] = r;
  if (npass == 0) return 1;  /* All equal; normally caught by ascending. */

  /* The initial scan counted byte zero.  Recount if it is constant and the
   * first varying byte is elsewhere. */
  if (shifts[0] != 0) {
    unsigned sh = shifts[0];
    memset(count, 0, sizeof count);
    for (i = 0; i < n; i++)
      count[(array[i] >> sh) & (RADIX-1)]++;
  }

  /* With one varying byte, reconstruct directly from its histogram.  Keep
   * narrowed signed values on the normal path so their original sign
   * extension is preserved. */
  if (npass == 1 && (!is_iv || signshift == BITS_PER_WORD-RADIX_BIT)) {
    unsigned sh = shifts[0];
    unsigned signbit = is_iv && sh == signshift
                     ? RADIX >> 1 : 0;
    UV base = first & ~((UV)(RADIX-1) << sh);
    size_t lim = 0;
    j = 0;
    for (r = 0; r < RADIX; r++) {
      unsigned d = r ^ signbit;
      UV v = base | ((UV)d << sh);
      for (lim += count[d]; j < lim; j++)
        array[j] = v;
    }
    return 1;
  }

  /* Allocate second ping-pong buffer */
  a = array;
  b = (UV*)malloc(n * sizeof(UV));
  if (b == 0) return 0;

  /* Each pass radix-sorts and counts the next varying byte. */
  for (pass = 0; pass < npass; pass++) {
    unsigned sh = shifts[pass];
    unsigned signbit = is_iv && sh == signshift
                     ? RADIX >> 1 : 0;
    UV *p = b;
    for (r = 0; r < RADIX; r++) {
      unsigned d = r ^ signbit;
      ptr[d] = p;
      p += count[d];
    }
    /* assert(p == b + n); */
    if (pass+1 < npass) {
      unsigned nextsh = shifts[pass+1];
      memset(count, 0, sizeof count);
      for (i = 0; i < n; i++) {
        UV d = a[i];
        *(ptr[(d >> sh) & (RADIX-1)]++) = d;
        count[(d >> nextsh) & (RADIX-1)]++;
      }
    } else {
      for (i = 0; i < n; i++) {
        UV d = a[i];
        *(ptr[(d >> sh) & (RADIX-1)]++) = d;
      }
    }
    p = b; b = a; a = p;
  }

  /* Move back to input array if necessary */
  if (a != array) {
    memcpy(array, a, n * sizeof *array);
    b = a;
  }
  free(b);
  return 1;
}
#undef RADIX_BIT
#undef RADIX

/******************************************************************************/
/*                              HEAP SORT                                     */
/******************************************************************************/
static void _heapsort(UV *array, size_t len, bool is_iv)
{
  size_t a = len/2;

  if (!a)               /* Trivial cases: len < 2 */
    return;

  for (len--;;) {
    UV r;               /* Value from array[a] being sifted down */
    size_t b, c;        /* Current descendent and its child */

    /*
     * Elements [0,a) are unsorted.
     * Elements [a,n] are in the heap.
     * Elements (n,...) are sorted.
     */
    if (a > 0)          /* Building heap: sift down array[--a] */
      r = array[--a];
    else if (len > 0) { /* Extracting: Swap root<->array[n--] */
      r = array[len]; array[len--] = array[0];
    } else              /* Extraction complete */
      return;

    /* Sift element r (at "a") down into heap. */
    if (!is_iv) {
      for (b = a; (c = 2*b + 1) < len; b = c) {
        UV s = array[c];
        if (array[c+1] >= s)
          s = array[++c];
        if (r >= s)
          goto sift_done;
        array[b] = s;
      }
    } else {
      for (b = a; (c = 2*b + 1) < len; b = c) {
        IV s = array[c];
        if ((IV)array[c+1] >= s)
          s = array[++c];
        if ((IV)r >= s)
          goto sift_done;
        array[b] = s;
      }
    }
    if (c == len) { /* Corner case: last leaf with no sibling */
      if ( (!is_iv && r < array[c]) || (is_iv && (IV)r < (IV)array[c]) ) {
        array[b] = array[c];
        b = c;
      }
    }
sift_done:
    array[b] = r;
  }
}

#define radixsort_uv(L,len)  _radixsort(L, len, 0)
#define radixsort_iv(L,len)  _radixsort((UV*)L, len, 1)
#define heapsort_uv(L,len)   _heapsort(L, len, 0)
#define heapsort_iv(L,len)   _heapsort((UV*)L, len, 1)

/******************************************************************************/
/*                              QUICK SORT                                    */
/******************************************************************************/

static UV _mid3_uv_val(UV* L, size_t a, size_t b, size_t c) {
  const UV s[3] = {L[a],L[b],L[c]};  /* Scandum's branchless method */
  int x = s[0] > s[1];
  int y = s[0] > s[2];
  int z = s[1] > s[2];
  return s[(x == y) + (y ^ z)];
}
static IV _mid3_iv_val(IV* L, size_t a, size_t b, size_t c) {
  const IV s[3] = {L[a],L[b],L[c]};  /* Scandum's branchless method */
  int x = s[0] > s[1];
  int y = s[0] > s[2];
  int z = s[1] > s[2];
  return s[(x == y) + (y ^ z)];
}

static void _mid2_of_4_uv(UV* L) {
  UV swap;   /* 1) put first two and last two in order             */
  size_t x;  /* 2) L[2] = max(L[0],L[2]);  L[1] = min(L[1],L[3]);  */
            x = L[0] > L[1];  swap = L[!x];  L[0]=L[x];  L[1]=swap;
  L += 2;   x = L[0] > L[1];  swap = L[!x];  L[0]=L[x];  L[1]=swap;
  L -= 2;   x = (L[0] <= L[2]) * 2;  L[2] = L[x];
  L += 1;   x = (L[0]  > L[2]) * 2;  L[0] = L[x];
}
static void _mid2_of_4_iv(IV* L) {
  IV swap;   /* 1) put first two and last two in order             */
  size_t x;  /* 2) L[2] = max(L[0],L[2]);  L[1] = min(L[1],L[3]);  */
            x = L[0] > L[1];  swap = L[!x];  L[0]=L[x];  L[1]=swap;
  L += 2;   x = L[0] > L[1];  swap = L[!x];  L[0]=L[x];  L[1]=swap;
  L -= 2;   x = (L[0] <= L[2]) * 2;  L[2] = L[x];
  L += 1;   x = (L[0]  > L[2]) * 2;  L[0] = L[x];
}

/* Using scandum's median of 9 gives better partitions than the median of
 * three medians, and gives better actual run times for large inputs.
 */
static size_t _partition_uv(UV* L, size_t lo, size_t hi) {
  size_t i = lo-1, j = hi+1, len = hi-lo+1;
  UV pivot;
  if (len <= 7) {
    pivot = L[lo + len/2];
  } else if (len <= 40) {
    pivot = _mid3_uv_val(L, lo, lo+(hi-lo)/2, hi);
  } else { /* Fluxsort's median_of_nine */
    UV swap[9], *Llo = L+lo;
    size_t x, step = (len-1)/8;
    for (x = 0; x < 9; x++) { swap[x] = Llo[x*step]; }
    _mid2_of_4_uv(swap);     /* [X v v X v v v v v] */
    _mid2_of_4_uv(swap+4);   /* [X v v X X v v X v] */
    swap[0] = swap[5];  swap[3] = swap[8];
    _mid2_of_4_uv(swap);     /* [X v v X X X v X X] */
    pivot = _mid3_uv_val(swap, 6, 1, 2);
  }
  while (1) {
    do { i++; } while (L[i] < pivot);
    do { j--; } while (L[j] > pivot);
    if (i >= j) return j;
    { UV t = L[i];  L[i] = L[j];  L[j] = t; }
  }
}
static size_t _partition_iv(IV* L, size_t lo, size_t hi) {
  size_t i = lo-1, j = hi+1, len = hi-lo+1;
  IV pivot;
  if (len <= 7) {
    pivot = L[lo + len/2];
  } else if (len <= 40) {
    pivot = _mid3_iv_val(L, lo, lo+(hi-lo)/2, hi);
  } else { /* Fluxsort's median_of_nine */
    IV swap[9], *Llo = L+lo;
    size_t x, step = (len-1)/8;
    for (x = 0; x < 9; x++) { swap[x] = Llo[x*step]; }
    _mid2_of_4_iv(swap);     /* [X v v X v v v v v] */
    _mid2_of_4_iv(swap+4);   /* [X v v X X v v X v] */
    swap[0] = swap[5];  swap[3] = swap[8];
    _mid2_of_4_iv(swap);     /* [X v v X X X v X X] */
    pivot = _mid3_iv_val(swap, 6, 1, 2);
  }
  while (1) {
    do { i++; } while (L[i] < pivot);
    do { j--; } while (L[j] > pivot);
    if (i >= j) return j;
    { IV t = L[i];  L[i] = L[j];  L[j] = t; }
  }
}

static void _qs_uv(UV* L, size_t lo, size_t hi, int badpartsleft) {
  size_t p, size = hi-lo+1;

  if (size <= 16)
    { insertionsort_uv(L+lo, size);  return; }

  p = _partition_uv(L, lo, hi);

  { /* check for unbalanced partitions, same as pdqsort */
    size_t l_size = p - lo + 1;
    size_t r_size = hi - (p+1) + 1;
    bool highly_unbalanced = l_size < size / 8 || r_size < size / 8;
    if (highly_unbalanced && --badpartsleft <= 0)
      { heapsort_uv(L+lo, size);  return; }
  }

  _qs_uv(L, lo, p, badpartsleft);
  _qs_uv(L, p+1, hi, badpartsleft);
}
static void _qs_iv(IV* L, size_t lo, size_t hi, int badpartsleft) {
  size_t p, size = hi-lo+1;

  if (size <= 16)
    { insertionsort_iv(L+lo, size);  return; }

  p = _partition_iv(L, lo, hi);

  { /* check for unbalanced partitions, same as pdqsort */
    size_t l_size = p - lo + 1;
    size_t r_size = hi - (p+1) + 1;
    bool highly_unbalanced = l_size < size / 8 || r_size < size / 8;
    if (highly_unbalanced && --badpartsleft <= 0)
      { heapsort_iv(L+lo, size);  return; }
  }

  _qs_iv(L, lo, p, badpartsleft);
  _qs_iv(L, p+1, hi, badpartsleft);
}

static void quicksort_uv(UV *L, size_t len) {
  if (len > 1) _qs_uv(L, 0, len-1, log2floor(len));
}
static void quicksort_iv(IV *L, size_t len) {
  if (len > 1) _qs_iv(L, 0, len-1, log2floor(len));
}


#if USE_QUADSORT

#include "quadsortuv.h"
void sort_uv_array(UV* L, size_t len) { quadsort_uv(L, len, 0); }
void sort_iv_array(IV* L, size_t len) { quadsort_iv(L, len, 0); }

#else

void sort_uv_array(UV* L, size_t len)
{
  if (len < 400) {
    quicksort_uv(L, len);
  } else {
    /* We could use an in-place radix sort like Ska Sort.  Our radix sort
     * is traditional and uses O(n) extra memory.  If we cannot get the
     * extra memory, we fall back to an in-place sort. */
    if (!radixsort_uv(L, len))
      quicksort_uv(L, len);
  }
}

void sort_iv_array(IV* L, size_t len)
{
  if (len < 400) {
    quicksort_iv(L, len);
  } else {
    if (!radixsort_iv(L, len))   /* radixsort could fail aux allocation */
      quicksort_iv(L, len);
  }
}

#endif

/******************************************************************************/

void sort_dedup_uv_array(UV* L, bool data_is_signed, size_t *len)
{
  if (*len > 1) {
    size_t i, j;
    if (data_is_signed)  sort_iv_array((IV *)L, *len);
    else                 sort_uv_array(L, *len);
    for (i=0, j=1; j < *len; j++) {
      i += (L[i] != L[j]);
      L[i] = L[j];
    }
    *len = i+1;
  }
}
