#ifndef QSORT_H
#define QSORT_H

#include <assert.h>
#include <stdio.h>

namespace gengraph {

//___________________________________________________________________________
// check if every element is zero
inline bool check_zero(int *mem, int n) {
  for(int *v = mem+n; v!=mem; ) if(*(--v)!=0) return false;
  return true;
}

//___________________________________________________________________________
//  Sort simple integer arrays in ASCENDING order
//___________________________________________________________________________
inline int med3(int a, int b, int c) {
  if(a<b) {
    if(c<b) return (a<c) ? c : a;
    else return b;
  }
  else {
    if(c<a) return (b<c) ? c : b;
    else return a;
  }
}

inline void isort(int *v, int t) {
  if(t<2) return;
  for(int i=1; i<t; i++) {
    register int *w = v+i;
    int tmp = *w;
    while(w!=v && *(w-1)>tmp) { *w = *(w-1); w--; }
    *w = tmp;
  }
}

inline int partitionne(int *v, int t, int p) {
  int i=0;
  int j=t-1;
  while(i<j) {
    while(i<=j && v[i]<p) i++;
    while(i<=j && v[j]>p) j--;
    if(i<j) {
      int tmp=v[i];
      v[i++]=v[j];
      v[j--]=tmp;
    }
  }
  if(i==j && v[i]<p) i++;
  assert(i!=0 && i!=t);
  return i;
}
  
inline void qsort(int *v, int t) {
  if(t<15) isort(v,t);
  else {
    int x = partitionne(v, t, med3(v[t>>1], v[(t>>2)+2], v[t-(t>>1)-2]));
    qsort(v,x);
    qsort(v+x,t-x);
  }
}

inline int qsort_median(int *v, int t, int pos) {
  if(t<10) { isort(v,t); return v[pos]; }
  int x = partitionne(v, t, med3(v[t>>1], v[(t>>2)+2], v[t-(t>>1)-2]));
  if(pos<x) return qsort_median(v, x, pos);
  else return qsort_median(v+x, t-x, pos-x);
} 
  
inline int qsort_median(int *v, int t) { return qsort_median(v,t,t/2); }

//___________________________________________________________________________
//  Sort simple double arrays in ASCENDING order
//___________________________________________________________________________
inline double med3(double a, double b, double c) {
  if(a<b) {
    if(c<b) return (a<c) ? c : a;
    else return b;
  }
  else {
    if(c<a) return (b<c) ? c : b;
    else return a;
  }
}

inline void isort(double *v, int t) {
  if(t<2) return;
  for(int i=1; i<t; i++) {
    register double *w = v+i;
    double tmp = *w;
    while(w!=v && *(w-1)>tmp) { *w = *(w-1); w--; }
    *w = tmp;
  }
}

inline int partitionne(double *v, int t, double p) {
  int i=0;
  int j=t-1;
  while(i<j) {
    while(i<=j && v[i]<p) i++;
    while(i<=j && v[j]>p) j--;
    if(i<j) {
      double tmp=v[i];
      v[i++]=v[j];
      v[j--]=tmp;
    }
  }
  if(i==j && v[i]<p) i++;
  assert(i!=0 && i!=t);
  return i;
}
  
inline void qsort(double *v, int t) {
  if(t<15) isort(v,t);
  else {
    int x = partitionne(v, t, med3(v[t>>1], v[(t>>2)+2], v[t-(t>>1)-2]));
    qsort(v,x);
    qsort(v+x,t-x);
  }
}

inline double qsort_median(double *v, int t, int pos) {
  if(t<10) { isort(v,t); return v[pos]; }
  int x = partitionne(v, t, med3(v[t>>1], v[(t>>2)+2], v[t-(t>>1)-2]));
  if(pos<x) return qsort_median(v, x, pos);
  else return qsort_median(v+x, t-x, pos-x);
} 

inline double qsort_median(double *v, int t) { return qsort_median(v,t,t/2); }

//___________________________________________________________________________
// Sort integer arrays according to value stored in mem[], in ASCENDING order
inline void isort(int *mem, int *v, int t) {
  if(t<2) return;
  for(int i=1; i<t; i++) {
    int vtmp = v[i];
    int tmp = mem[vtmp];
    int j;
    for(j=i; j>0 && tmp<mem[v[j-1]]; j--) v[j]=v[j-1];
    v[j] = vtmp;
  }
}

inline void qsort(int *mem, int *v, int t) {
  if(t<15) isort(mem,v,t);
  else {
    int p = med3(mem[v[t>>1]], mem[v[(t>>2)+3]], mem[v[t-(t>>1)-3]]);
    int i=0;
    int j=t-1;
    while(i<j) {
      while(i<=j && mem[v[i]]<p)  i++;
      while(i<=j && mem[v[j]]>p) j--;
      if(i<j) {
        int tmp=v[i];
        v[i++]=v[j];
        v[j--]=tmp;
      }
    }
    if(i==j && mem[v[i]]<p) i++;
    assert(i!=0 && i!=t);
    qsort(mem,v,i);
    qsort(mem,v+i,t-i);
  }
}

//Box-Sort 1..n according to value stored in mem[], in DESCENDING order.
inline int *pre_boxsort(int *mem, int n, int &offset) {
  int *yo;
  // maximum and minimum
  int mx = mem[0];
  int mn = mem[0];
  for(yo=mem+n-1; yo!=mem; yo--) {
    register int x = *yo;
    if(x>mx) mx=x;
    if(x<mn) mn=x;
  }
  // box
  int c = mx-mn+1;
  int *box = new int[c];
  for(yo=box+c; yo!=box; *(--yo)=0);
  for(yo=mem+n; yo!=mem; box[*(--yo)-mn]++);
  // cumul sum
  int sum=0;
  for(yo=box+c; yo!=box; ) {
    sum += *(--yo);
    *yo = sum;
  }
  offset = mn;
  return box;
}

inline int *boxsort(int *mem, int n, int *buff=NULL) {
  int i;
  if(n<=0) return buff;
  int offset=0;
  int *box = pre_boxsort(mem,n,offset);
  // sort
  if(buff==NULL) buff=new int[n];
  for(i=0; i<n; i++) buff[--box[mem[i]-offset]]=i;
  // clean
  delete[] box;
  return buff;
}

// merge two sorted arays in their intersection. Store the result in first array, and return length
inline int intersect(int *a, int a_len, int *b, int b_len) {
  if(a_len==0 || b_len==0) return 0;
  int *asup = a+a_len;
  int *bsup = b+b_len;
  int len=0;
  int *p = a;
  do {
    if (*a == *b) p[len++] = *a;
    do if(++a == asup) return len; while(*a<*b);
    if (*a == *b) p[len++] = *a;
    do if(++b == bsup) return len; while(*b<*a);
  } while(true);
}

// merge two sorted arays in their union, store result in m
inline int unify(int *m, int *a, int a_len, int *b, int b_len) {
  int *asup = a+a_len;
  int *bsup = b+b_len;
  int len=0;
  while(a!=asup && b!=bsup) {
    if(*a < *b) m[len++] = *(a++);
    else {
      if (*a == *b) a++;
      m[len++] = *(b++);
    }
  }
  while(a!=asup) m[len++]=*(a++);
  while(b!=asup) m[len++]=*(b++);
  return len;
}

// lexicographic compare
inline int lex_comp(int *v1, int *v2, int n) {
  int *stop = v1+n;
  while(v1!=stop && *v1==*v2) { v1++; v2++; };
  if(v1==stop) return 0; else if(*v1<*v2) return -1; else return 1;
}
// lexicographic median of three
inline int *lex_med3(int *a, int *b, int *c, int s) {
  int ab = lex_comp(a,b,s);
  if(ab==0) return a;
  else {
    int cb = lex_comp(c,b,s);
    if(cb==0) return b;
    int ca = lex_comp(c,a,s);
    if(ab<0) { if(cb>0) return b; else return (ca>0) ? c : a; }
    else     { if(cb<0) return b; else return (ca<0) ? c : a; }
  }
}

// Lexicographic sort
inline void lex_isort(int **l, int *v, int t, int s) {
  if(t<2) return;
  for(int i=1; i<t; i++) {
    register int *w = v+i;
    int tmp = *w;
    while(w!=v && lex_comp(l[tmp],l[*(w-1)],s)<0) { *w = *(w-1); w--; }
    *w = tmp;
  }
}

#ifdef _STABLE_SORT_ONLY
#define _CRITICAL_SIZE_QSORT 0x7FFFFFFF
#warning "lex_qsort will be replaced by lex_isort"
#else
#define _CRITICAL_SIZE_QSORT 15
#endif

inline void lex_qsort(int **l, int *v, int t, int s) {

  if(t<_CRITICAL_SIZE_QSORT) lex_isort(l,v,t,s);
  else {
    int *p = lex_med3(l[v[t>>1]], l[v[(t>>2)+2]], l[v[t-(t>>1)-2]], s);
    int i=0;
    int j=t-1;
//    printf("pivot = %d\n",p);
    while(i<j) {
//      for(int k=0; k<t; k++) printf("%d ",v[k]);
      while(i<=j && lex_comp(l[v[i]],p,s)<0) i++;
      while(i<=j && lex_comp(l[v[j]],p,s)>0) j--;
      if(i<j) {
//        printf("  swap %d[%d] with %d[%d]\n",i,v[i],j,v[j]);
        int tmp=v[i];
        v[i++]=v[j];
        v[j--]=tmp;
      }
    }
    if(i==j && lex_comp(l[v[i]],p,s)<0) i++;
    assert(i!=0 && i!=t);
    lex_qsort(l,v,i,s);
    lex_qsort(l,v+i,t-i,s);
  }
}

// lexicographic indirect compare
inline int lex_comp_indirect(int *key, int *v1, int *v2, int n) {
  int *stop = v1+n;
  while(v1!=stop && key[*v1]==key[*v2]) { v1++; v2++; };
  if(v1==stop) return 0; else if(key[*v1]<key[*v2]) return -1; else return 1;
}

inline int qsort_min(const int a, const int b) { return a<=b ? a : b; }

// mix indirect compare
inline int mix_comp_indirect(int *key, int a, int b, int **neigh, int *degs)
{
  if(key[a]<key[b]) return -1;
  else if(key[a]>key[b]) return 1;
  else {
    int cmp=lex_comp_indirect(key,neigh[a],neigh[b],qsort_min(degs[a],degs[b]));
    if(cmp==0) {
      if(degs[a]>degs[b]) return -1;
      if(degs[a]<degs[b]) return 1;
    }
    return cmp;
  }
}
// lexicographic indirect median of three
inline int mix_med3_indirect(int *key, int a, int b, int c, int **neigh, int *degs) {
  int ab = mix_comp_indirect(key,a,b,neigh,degs);
  if(ab==0) return a;
  else {
    int cb = mix_comp_indirect(key,c,b,neigh,degs);
    if(cb==0) return b;
    int ca = mix_comp_indirect(key,c,a,neigh,degs);
    if(ab<0) { if(cb>0) return b; else return (ca>0) ? c : a; }
    else     { if(cb<0) return b; else return (ca<0) ? c : a; }
  }
}

// Sort integer arrays in ASCENDING order
inline void mix_isort_indirect(int *key, int *v, int t, int **neigh, int *degs)
{
  if(t<2) return;
  for(int i=1; i<t; i++) {
    register int *w = v+i;
    int tmp = *w;
    while(w!=v && mix_comp_indirect(key,tmp,*(w-1),neigh,degs)<0) {
      *w = *(w-1);
      w--;
    }
    *w = tmp;
  }
}

inline void mix_qsort_indirect(int *key, int *v, int t, int **neigh, int *degs)
{
  if(t<15) mix_isort_indirect(key,v,t,neigh,degs);
  else {
    int p = mix_med3_indirect(key, v[t>>1], v[(t>>2)+2], v[t-(t>>1)-2], neigh, degs);
    int i=0;
    int j=t-1;
//    printf("pivot = %d\n",p);
    while(i<j) {
//      for(int k=0; k<t; k++) printf("%d ",v[k]);
      while(i<=j && mix_comp_indirect(key,v[i],p,neigh,degs)<0) i++;
      while(i<=j && mix_comp_indirect(key,v[j],p,neigh,degs)>0) j--;
      if(i<j) {
//        printf("  swap %d[%d] with %d[%d]\n",i,v[i],j,v[j]);
        int tmp=v[i];
        v[i++]=v[j];
        v[j--]=tmp;
      }
    }
    if(i==j && mix_comp_indirect(key,v[i],p,neigh,degs)<0) i++;
    assert(i!=0 && i!=t);
    mix_qsort_indirect(key,v,i,neigh,degs);
    mix_qsort_indirect(key,v+i,t-i,neigh,degs);
  }
}

} // namespace gengraph

#endif //QSORT_H
