/********* Grail sorting *********************************/ /* */ /* (c) 2013 by Andrey Astrelin */ /* */ /* */ /* Stable sorting that works in O(N*log(N)) worst time */ /* and uses O(1) extra memory */ /* */ /* Define SORT_TYPE and SORT_CMP */ /* and then call GrailSort() function */ /* */ /*********************************************************/ #include #include #include "sorters.h" #define SORT_TYPE int static cmpfun SORT_CMP; #define GRAIL_EXT_BUFFER_LENGTH 512 static inline void grail_swap1(SORT_TYPE *a,SORT_TYPE *b){ SORT_TYPE c=*a; *a=*b; *b=c; } static inline void grail_swapN(SORT_TYPE *a,SORT_TYPE *b,int n){ while(n--) grail_swap1(a++,b++); } static void grail_rotate(SORT_TYPE *a,int l1,int l2){ while(l1 && l2){ if(l1<=l2){ grail_swapN(a,a+l1,l1); a+=l1; l2-=l1; } else{ grail_swapN(a+(l1-l2),a+l1,l2); l1-=l2; } } } static int grail_BinSearchLeft(SORT_TYPE *arr,int len,SORT_TYPE *key){ int a=-1,b=len,c; while(a>1); if(SORT_CMP(arr+c,key)>=0) b=c; else a=c; } return b; } static int grail_BinSearchRight(SORT_TYPE *arr,int len,SORT_TYPE *key){ int a=-1,b=len,c; while(a>1); if(SORT_CMP(arr+c,key)>0) b=c; else a=c; } return b; } // cost: 2*len+nk^2/2 static int grail_FindKeys(SORT_TYPE *arr,int len,int nkeys){ int h=1,h0=0; // first key is always here int u=1,r; while(u arr[M,M+L1+L2-1] static void grail_MergeLeft(SORT_TYPE *arr,int L1,int L2,int M){ int p0=0,p1=L1; L2+=L1; while(p10){ grail_swap1(arr+(M++),arr+(p1++)); } else{ grail_swap1(arr+(M++),arr+(p0++)); } } if(M!=p0) grail_swapN(arr+M,arr+p0,L1-p0); } static void grail_MergeRight(SORT_TYPE *arr,int L1,int L2,int M){ int p0=L1+L2+M-1,p2=L1+L2-1,p1=L1-1; while(p1>=0){ if(p20){ grail_swap1(arr+(p0--),arr+(p1--)); } else{ grail_swap1(arr+(p0--),arr+(p2--)); } } if(p2!=p0) while(p2>=L1) grail_swap1(arr+(p0--),arr+(p2--)); } static void grail_SmartMergeWithBuffer(SORT_TYPE *arr,int *alen1,int *atype,int len2,int lkeys){ int p0=-lkeys,p1=0,p2=*alen1,q1=p2,q2=p2+len2; int ftype=1-*atype; // 1 if inverted while(p1=0){ while(len1){ h=ftype ? grail_BinSearchLeft(arr+len1,len2,arr) : grail_BinSearchRight(arr+len1,len2,arr); if(h!=0){ grail_rotate(arr,len1,h); arr+=h; len2-=h; } if(len2==0){ *alen1=len1; return; } do{ arr++; len1--; } while(len1 && SORT_CMP(arr,arr+len1)-ftype<0); } } *alen1=len2; *atype=ftype; } /***** Sort With Extra Buffer *****/ // arr[M..-1] - free, arr[0,L1-1]++arr[L1,L1+L2-1] -> arr[M,M+L1+L2-1] static void grail_MergeLeftWithXBuf(SORT_TYPE *arr,int L1,int L2,int M){ int p0=0,p1=L1; L2+=L1; while(p10) arr[M++]=arr[p1++]; else arr[M++]=arr[p0++]; } if(M!=p0) while(p00, nblock2=0 is possible. static void grail_MergeBuffersLeftWithXBuf(SORT_TYPE *keys,SORT_TYPE *midkey,SORT_TYPE *arr,int nblock,int lblock,int nblock2,int llast){ int l,prest,lrest,frest,pidx,cidx,fnext,plast; if(nblock==0){ l=nblock2*lblock; grail_MergeLeftWithXBuf(arr,l,llast,-lblock); return; } lrest=lblock; frest=SORT_CMP(keys,midkey)<0 ? 0 : 1; pidx=lblock; for(cidx=1;cidx0) u=1; arr[m-3]=arr[m-1+u]; arr[m-2]=arr[m-u]; } if(L%2) arr[L-3]=arr[L-1]; arr-=2; for(h=2;hh){ grail_MergeLeftWithXBuf(arr+p0,h,rest-h,-h); } else { for(;p00) u=1; grail_swap1(arr+(m-3),arr+(m-1+u)); grail_swap1(arr+(m-2),arr+(m-u)); } if(L%2) grail_swap1(arr+(L-1),arr+(L-3)); arr-=2; h=2; } for(;hh){ grail_MergeLeft(arr+p0,h,rest-h,-h); } else grail_rotate(arr+p0-h,h,rest); arr-=h; } restk=L%(2*K); p=L-restk; if(restk<=K) grail_rotate(arr+p,restk,K); else grail_MergeRight(arr+p,K,restk-K,K); while(p>0){ p-=2*K; grail_MergeRight(arr+p,K,K,K); } } // arr - starting array. arr[-lblock..-1] - buffer (if havebuf). // lblock - length of regular blocks. First nblocks are stable sorted by 1st elements and key-coded // keys - arrays of keys, in same order as blocks. key0, nblock2=0 is possible. static void grail_MergeBuffersLeft(SORT_TYPE *keys,SORT_TYPE *midkey,SORT_TYPE *arr,int nblock,int lblock,bool havebuf,int nblock2,int llast){ int l,prest,lrest,frest,pidx,cidx,fnext,plast; if(nblock==0){ l=nblock2*lblock; if(havebuf) grail_MergeLeft(arr,l,llast,-lblock); else grail_MergeWithoutBuffer(arr,l,llast); return; } lrest=lblock; frest=SORT_CMP(keys,midkey)<0 ? 0 : 1; pidx=lblock; for(cidx=1;cidx=0 && SORT_CMP(arr+(j+1),arr+j)<0;j--) grail_swap1(arr+j,arr+(j+1)); } } static void grail_LazyStableSort(SORT_TYPE *arr,int L){ int m,u,h,p0,p1,rest; for(m=1;m0) grail_swap1(arr+(m-1),arr+m); } for(h=2;hh) grail_MergeWithoutBuffer(arr+p0,h,rest-h); } } // keys are on the left of arr. Blocks of length LL combined. We'll combine them in pairs // LL and nkeys are powers of 2. (2*LL/lblock) keys are guarantied static void grail_CombineBlocks(SORT_TYPE *keys,SORT_TYPE *arr,int len,int LL,int lblock,bool havebuf,SORT_TYPE *xbuf){ int M,nkeys,b,NBlk,midkey,lrest,u,p,v,kc,nbl2,llast; SORT_TYPE *arr1; M=len/(2*LL); lrest=len%(2*LL); nkeys=(2*LL)/lblock; if(lrest<=LL){ len-=lrest; lrest=0; } if(xbuf) memcpy(xbuf,arr-lblock,lblock*sizeof(SORT_TYPE)); for(b=0;b<=M;b++){ if(b==M && lrest==0) break; arr1=arr+b*2*LL; NBlk=(b==M ? lrest : 2*LL)/lblock; grail_SortIns(keys,NBlk+(b==M ? 1 : 0)); midkey=LL/lblock; for(u=1;u0 || (kc==0 && SORT_CMP(keys+p,keys+v)>0)) p=v; } if(p!=u-1){ grail_swapN(arr1+(u-1)*lblock,arr1+p*lblock,lblock); grail_swap1(keys+(u-1),keys+p); if(midkey==u-1 || midkey==p) midkey^=(u-1)^p; } } nbl2=llast=0; if(b==M) llast=lrest%lblock; if(llast!=0){ while(nbl2=0;) arr[p]=arr[p-lblock]; memcpy(arr-lblock,xbuf,lblock*sizeof(SORT_TYPE)); }else if(havebuf) while(--len>=0) grail_swap1(arr+len,arr+len-lblock); } static void grail_commonSort(SORT_TYPE *arr,int Len,SORT_TYPE *extbuf,int LExtBuf){ int lblock,nkeys,findkeys,ptr,cbuf,lb,nk; bool havebuf,chavebuf; long long s; if(Len<16){ grail_SortIns(arr,Len); return; } lblock=1; while(lblock*lblockfindkeys) nkeys/=2; havebuf=false; lblock=0; } ptr=lblock+nkeys; cbuf=havebuf ? lblock : nkeys; if(havebuf) grail_BuildBlocks(arr+ptr,Len-ptr,cbuf,extbuf,LExtBuf); else grail_BuildBlocks(arr+ptr,Len-ptr,cbuf,NULL,0); // 2*cbuf are built while(Len-ptr>(cbuf*=2)){ lb=lblock; chavebuf=havebuf; if(!havebuf){ if(nkeys>4 && nkeys/8*nkeys>=cbuf){ lb=nkeys/2; chavebuf=true; } else{ nk=1; s=(long long)cbuf*findkeys/2; while(nkLExtBuf && lb*lb>2*cbuf) lb/=2; // set size of block close to sqrt(new_block_length) } #endif } grail_CombineBlocks(arr,arr+ptr,Len-ptr,cbuf,lb,chavebuf,chavebuf && lb<=LExtBuf ? extbuf : NULL); } grail_SortIns(arr,ptr); grail_MergeWithoutBuffer(arr,ptr,Len-ptr); } static void GrailSort(SORT_TYPE *arr,int Len){ grail_commonSort(arr,Len,NULL,0); } void grailsort_ref(void *base, size_t nmel, size_t width, cmpfun cmp) { SORT_CMP = cmp; GrailSort(base, nmel); }