!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! sorting.h v1.2, 17 Sep 2003 ! Sort functions for Inform. Written by Fredrik Ramsberg. ! Thanks to Neil Cerutti and Roger Firth for submitting bug reports. ! Freely usable by anyone for any purpose. I request but don't demand ! that you give due credit if this code is used in something that is ! released to the public. I'd also appreciate if you'd mail me bug ! reports or your opinions if you use this little library. ! -- Fredrik Ramsberg, fredrik {AT} ramsberg {DOT} net ! ! Publically usable routines: ! ! QUICKSORT group ! --------------- ! inssort_bytes_idx(arr, index, from, to); ! inssort_words_idx(arr, index, from, to [, comparison]); ! qsort_bytes_idx(arr, index, from, to); ! qsort_words_idx(arr, index, from, to [, comparison]); ! inssort_bytes(arr, from, to); ! inssort_words(arr, from, to [, comparison]); ! qsort_bytes(arr, from, to); ! qsort_words(arr, from, to [, comparison]); ! ! SHELLSORT group ! --------------- ! shsort_bytes_idx(arr, index, from, to); ! shsort_words_idx(arr, index, from, to [, comparison]); ! shsort_bytes(arr, from, to); ! shsort_words(arr, from, to [, comparison]); ! ! Which one of these three sort algorithms is the fastest, depends ! on the data to be sorted. Here's a short guide: ! ! Use InsertionSort (inssort) when no value is expected to be far from ! its correct position (up to roughly ten positions away is fine). Many ! small disorders are quite ok. Also use InsertionSort when the sorting ! has to be stable. This means that if there are two equal values in ! the data, the one that is to the left of the other when the sorting ! starts must still be to the left of the other when the sorting ends. ! This is not often a demand, but it happens. ! ! Use QuickSort (qsort) when the data is expected to be entirely unsorted. ! ! Use ShellSort (shsort) when the data may or may not be nearly in order, ! when there may or may not be elements that are far from their correct ! position, and when you can't decide which algorithm is the best. It's ! a very good allround algorithm. ! ! The InsertionSort algorithm is very closely related to ShellSort. ! However, the InsertionSort functions are in the QuickSort group in this ! library. This is only because this particular QuickSort implementation ! utilises InsertionSort in the final stages of sorting, for better speed. ! This means that the InsertionSort routines can't be removed if one wants ! to use the QuickSort routines. ! ! All functions that take an index argument are called index-based sort ! functions. These will not alter the order of the original array, but ! will instead build an index on the side that says in which order the ! array elements should be. The index entries are always word-sized, ! regardless of the size of the data elements. This is quite powerful ! because it means you can keep track of related data on the side, i.e. ! if you sort an array containing people's salaries, you will still know ! what person had what salary even after sorting. ! ! All sort-functions that are not index-based are referred to as direct sort ! functions. ! ! The 'from' argument should contain the index of the first element to be ! sorted, usually 0 if the data is in an ordinary array, or 1 if it's in ! a table. The 'to' argument should hold the index of the last element to ! be sorted. ! ! For functions that take an optional comparison argument, this argument is ! expected to be a function that takes two arguments, a and b, and which ! returns a non-zero value iff a should end up after b in the sorted array. ! Important: Such a function must never return a non-zero value if a and b ! are equivalent, from a sorting point of view. ! Sample function to sort an array of words in descending order: ! [ lessthan a b; return a0, lessthan); ! ! All functions that sort word-data use the standard Inform signed comparison ! by default. If unsigned comparison is required, supply your own comparison ! function instead. ! ! To stop some of the routines from compiling (in order to save space), one ! can set one or more of the following constants, before including this file: ! NOQUICKSORT, NOSHELLSORT, ! NOINDEXSORT, NODIRECTSORT, ! NOBYTESORT, NOWORDSORT ! If no routines are removed in this manner, this package will take up roughly ! 3.5 KB in the story file, with debugging information turned off. ! ! There are two more constants which may be defined before including this file: ! QUICKSORTLIMIT ! SHELLSORTCONST ! If you're _really_ interested in fine-tuning your sorting, these constants ! may be of interest to you. Otherwise, feel free to forget about them right ! away. If any of them are defined, it must be set to a value greater than ! zero, or the sorting routines will crash. Any value higher than 20 is ! highly unlikely to be useful. Which values are the best (i.e. giving the ! fastest sorting) depends on which interpreter will be used to run the ! program, as well as the nature of the data to be sorted. ! The programmer is free to define both constants, define one of them, or ! leave both undefined. Leaving them undefined will set them to values ! optimised for sorting random data under WinFrotz (QUICKSORTLIMIT=10, ! SHELLSORTCONST=3), and this is generally good enough for any data under any ! interpreter. Of course, QUICKSORTLIMIT only affects QuickSort, and ! SHELLSORTCONST only affects ShellSort. ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! System_file; #Ifndef NOQUICKSORT; Default QUICKSORTLIMIT 10; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Index-based InsertionSort and QuickSort routines. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! #Ifndef NOINDEXSORT; #Ifndef NOBYTESORT; [ _isbi_sub arr index from to i j v vi f1; f1=from + 1; for(i=f1, j=i, vi=index-->i, v=arr->vi : i<=to : index-->(j+1)=vi, i++, j=i, vi=index-->i, v=arr->vi ) while(j-- >= f1 && arr->(index-->j) > v) index-->(j+1)=index-->j; ]; [ inssort_bytes_idx arr index from to i; for(i=from:i<=to:i++) index-->i=i; _isbi_sub(arr, index, from, to); ]; [ _qsbi_sub arr index from to i j m temp vm vt p; while(from>=0) { i=from-1; j=to; m=from+(to-from)/2; p=arr->(index-->from); vm=arr->(index-->m); vt=arr->(index-->to); if((p<=vm && vm<=vt) || (vt<=vm && vm<=p)) { p=vm; temp=index-->m; index-->m=index-->from; index-->from=temp; } else if((p<=vt && vt<=vm) || (vm<=vt && vt<=p)) { p=vt; temp=index-->to; index-->to=index-->from; index-->from=temp; } while(++i<=j) { if(arr->(index-->i)>p) { while(arr->(index-->j)>p && --j>i); if(j>i) { temp=index-->j; index-->j=index-->i; index-->i=temp; } else i--; } } i--; temp=index-->i; index-->i=index-->from; index-->from=temp; if(i-from>to-i) { if(to-i>QUICKSORTLIMIT) _qsbi_sub(arr, index, i+1, to); if(i-from>QUICKSORTLIMIT) to=i-1; else from=-1; } else { if(i-from>QUICKSORTLIMIT) _qsbi_sub(arr, index, from, i-1); if(to-i>QUICKSORTLIMIT) from=i+1; else from=-1; } } ]; [ qsort_bytes_idx arr index from to i; for(i=from:i<=to:i++) index-->i=i; _qsbi_sub(arr, index, from, to); #Iftrue QUICKSORTLIMIT > 1; _isbi_sub(arr, index, from, to); #Endif; ]; #Endif; ! Not NOBYTESORT; #Ifndef NOWORDSORT; [ _iswi_sub arr index from to i j v vi f1; f1=from + 1; for(i=f1, j=i, vi=index-->i, v=arr-->vi : i<=to : index-->(j+1)=vi, i++, j=i, vi=index-->i, v=arr-->vi ) while(j-- >= f1 && arr-->(index-->j) > v) index-->(j+1)=index-->j; ]; [ _isgi_sub arr index from to comp i j v vi f1; f1=from + 1; for(i=f1, j=i, vi=index-->i, v=arr-->vi : i<=to : index-->(j+1)=vi, i++, j=i, vi=index-->i, v=arr-->vi ) while(j-- >= f1 && comp(arr-->(index-->j), v)) index-->(j+1)=index-->j; ]; [ inssort_words_idx arr index from to comp i; for(i=from:i<=to:i++) index-->i=i; if(comp) return _isgi_sub(arr, index, from, to, comp); _iswi_sub(arr, index, from, to); ]; [ _qswi_sub arr index from to i j m temp vm vt p; while(from>=0) { i=from-1; j=to; m=from+(to-from)/2; p=arr-->(index-->from); vm=arr-->(index-->m); vt=arr-->(index-->to); if((p<=vm && vm<=vt) || (vt<=vm && vm<=p)) { p=vm; temp=index-->m; index-->m=index-->from; index-->from=temp; } else if((p<=vt && vt<=vm) || (vm<=vt && vt<=p)) { p=vt; temp=index-->to; index-->to=index-->from; index-->from=temp; } while(++i<=j) { if(arr-->(index-->i)>p) { while(arr-->(index-->j)>p && --j>i); if(j>i) { temp=index-->j; index-->j=index-->i; index-->i=temp; } else i--; } } i--; temp=index-->i; index-->i=index-->from; index-->from=temp; if(i-from>to-i) { if(to-i>QUICKSORTLIMIT) _qswi_sub(arr, index, i+1, to); if(i-from>QUICKSORTLIMIT) to=i-1; else from=-1; } else { if(i-from>QUICKSORTLIMIT) _qswi_sub(arr, index, from, i-1); if(to-i>QUICKSORTLIMIT) from=i+1; else from=-1; } } ]; [ _qsgi_sub arr index from to comp i j m temp vm vt p; while(from>=0) { i=from-1; j=to; m=from+(to-from)/2; p=arr-->(index-->from); vm=arr-->(index-->m); vt=arr-->(index-->to); if(~~(((comp(p,vm)) || comp(vm,vt)) && ((comp(vt,vm)) || comp(vm,p)))) { p=vm; temp=index-->m; index-->m=index-->from; index-->from=temp; } else if(~~(((comp(p,vt)) || comp(vt,vm)) && ((comp(vm,vt)) || comp(vt,p)))) { p=vt; temp=index-->to; index-->to=index-->from; index-->from=temp; } while(++i<=j) { if(comp(arr-->(index-->i),p)) { while(comp(arr-->(index-->j),p) && --j>i); if(j>i) { temp=index-->j; index-->j=index-->i; index-->i=temp; } else i--; } } i--; temp=index-->i; index-->i=index-->from; index-->from=temp; if(i-from>to-i) { if(to-i>QUICKSORTLIMIT) _qsgi_sub(arr, index, i+1, to, comp); if(i-from>QUICKSORTLIMIT) to=i-1; else from=-1; } else { if(i-from>QUICKSORTLIMIT) _qsgi_sub(arr, index, from, i-1, comp); if(to-i>QUICKSORTLIMIT) from=i+1; else from=-1; } } ]; [ qsort_words_idx arr index from to comp i; for(i=from:i<=to:i++) index-->i=i; if(comp) { _qsgi_sub(arr, index, from, to, comp); #Iftrue QUICKSORTLIMIT > 1; _isgi_sub(arr, index, from, to, comp); #Endif; } else { _qswi_sub(arr, index, from, to); #Iftrue QUICKSORTLIMIT > 1; _iswi_sub(arr, index, from, to); #Endif; } ]; #Endif; ! Not NOWORDSORT; #Endif; ! Not NOINDEXSORT !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Direct InsertionSort and QuickSort routines. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! #Ifndef NODIRECTSORT; #Ifndef NOBYTESORT; [ inssort_bytes arr from to i j v f1; f1=from + 1; for(i=f1, j=i, v=arr->i : i<=to : arr->(j+1)=v, i++, j=i, v=arr->i) while(j-- >= f1 && arr->j > v) arr->(j+1)=arr->j; ]; [ _qsb_sub arr from to i j m temp vm vt p; while(from>=0) { i=from-1; j=to; m=from+(to-from)/2; p=arr->from; vm=arr->m; vt=arr->to; if((p<=vm && vm<=vt) || (vt<=vm && vm<=p)) { p=vm; temp=arr->m; arr->m=arr->from; arr->from=temp; } else if((p<=vt && vt<=vm) || (vm<=vt && vt<=p)) { p=vt; temp=arr->to; arr->to=arr->from; arr->from=temp; } while(++i<=j) { if(arr->i > p) { while(arr->j > p && --j>i); if(j>i) { temp=arr->j; arr->j=arr->i; arr->i=temp; } else i--; } } i--; temp=arr->i; arr->i=arr->from; arr->from=temp; if(i-from>to-i) { if(to-i>QUICKSORTLIMIT) _qsb_sub(arr, i+1, to); if(i-from>QUICKSORTLIMIT) to=i-1; else from=-1; } else { if(i-from>QUICKSORTLIMIT) _qsb_sub(arr, from, i-1); if(to-i>QUICKSORTLIMIT) from=i+1; else from=-1; } } ]; [ qsort_bytes arr from to; _qsb_sub(arr, from, to); #Iftrue QUICKSORTLIMIT > 1; inssort_bytes(arr, from, to); #Endif; ]; #Endif; ! Not NOBYTESORT; #Ifndef NOWORDSORT; [ _isg_sub arr from to comp i j v f1; f1=from + 1; for(i=f1, j=i, v=arr-->i : i<=to : arr-->(j+1)=v, i++, j=i, v=arr-->i) while(j-- >= f1 && comp(arr-->j, v)) arr-->(j+1)=arr-->j; ]; [ inssort_words arr from to comp i j v f1; if(comp) return _isg_sub(arr, from, to, comp); f1=from + 1; for(i=f1, j=i, v=arr-->i : i<=to : arr-->(j+1)=v, i++, j=i, v=arr-->i) while(j-- >= f1 && arr-->j > v) arr-->(j+1)=arr-->j; ]; [ _qsw_sub arr from to i j m temp vm vt p; while(from>=0) { i=from-1; j=to; m=from+(to-from)/2; p=arr-->from; vm=arr-->m; vt=arr-->to; if((p<=vm && vm<=vt) || (vt<=vm && vm<=p)) { p=vm; temp=arr-->m; arr-->m=arr-->from; arr-->from=temp; } else if((p<=vt && vt<=vm) || (vm<=vt && vt<=p)) { p=vt; temp=arr-->to; arr-->to=arr-->from; arr-->from=temp; } while(++i<=j) { if(arr-->i > p) { while(arr-->j > p && --j>i); if(j>i) { temp=arr-->j; arr-->j=arr-->i; arr-->i=temp; } else i--; } } i--; temp=arr-->i; arr-->i=arr-->from; arr-->from=temp; if(i-from>to-i) { if(to-i>QUICKSORTLIMIT) _qsw_sub(arr, i+1, to); if(i-from>QUICKSORTLIMIT) to=i-1; else from=-1; } else { if(i-from>QUICKSORTLIMIT) _qsw_sub(arr, from, i-1); if(to-i>QUICKSORTLIMIT) from=i+1; else from=-1; } } ]; [ _qsg_sub arr from to comp i j m temp vm vt p; while(from>=0) { i=from-1; j=to; m=from+(to-from)/2; p=arr-->from; vm=arr-->m; vt=arr-->to; if(~~(((comp(p,vm)) || comp(vm,vt)) && ((comp(vt,vm)) || comp(vm,p)))) { p=vm; temp=arr-->m; arr-->m=arr-->from; arr-->from=temp; } else if(~~(((comp(p,vt)) || comp(vt,vm)) && ((comp(vm,vt)) || comp(vt,p)))) { p=vt; temp=arr-->to; arr-->to=arr-->from; arr-->from=temp; } while(++i<=j) { if(comp(arr-->i,p)) { while(comp(arr-->j,p) && --j>i); if(j>i) { temp=arr-->j; arr-->j=arr-->i; arr-->i=temp; } else i--; } } i--; temp=arr-->i; arr-->i=arr-->from; arr-->from=temp; if(i-from>to-i) { if(to-i>QUICKSORTLIMIT) _qsg_sub(arr, i+1, to, comp); if(i-from>QUICKSORTLIMIT) to=i-1; else from=-1; } else { if(i-from>QUICKSORTLIMIT) _qsg_sub(arr, from, i-1, comp); if(to-i>QUICKSORTLIMIT) from=i+1; else from=-1; } } ]; [ qsort_words arr from to comp; if(comp) { _qsg_sub(arr, from, to, comp); #Iftrue QUICKSORTLIMIT > 1; _isg_sub(arr, from, to, comp); #Endif; } else { _qsw_sub(arr, from, to); #Iftrue QUICKSORTLIMIT > 1; inssort_words(arr, from, to); #Endif; } ]; #Endif; ! Not NOWORDSORT; #Endif; ! Not NODIRECTSORT #Endif; ! Not NOQUICKSORT #Ifndef NOSHELLSORT; Default SHELLSORTCONST 3; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Index-based ShellSort routines. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! #Ifndef NOINDEXSORT; #Ifndef NOBYTESORT; [ shsort_bytes_idx arr index from to i vi j v h k len; h=1; len=to-from+1; while(h < len) h=SHELLSORTCONST*h+1; for(i=from:i<=to:i++) index-->i=i; while((h=(h-1)/SHELLSORTCONST)>=1) for(k=0: ki; v=arr->vi; while((j=j-h) >= from && arr->(index-->j) > v) index-->(j+h)=index-->j; index-->(j+h)=vi; } ]; #Endif; ! Not NOBYTESORT; #Ifndef NOWORDSORT; [ _ssgi_sub arr index from to comp h i vi j v k; while((h=(h-1)/SHELLSORTCONST)>=1) for(k=0: ki; v=arr-->vi; while((j=j-h) >= from && comp(arr-->(index-->j), v)) index-->(j+h)=index-->j; index-->(j+h)=vi; } ]; [ shsort_words_idx arr index from to comp h i vi j v k len; for(i=from:i<=to:i++) index-->i=i; h=1; len=to-from+1; while(h < len) h=SHELLSORTCONST*h+1; if(comp) return _ssgi_sub(arr, index, from, to, comp, h); while((h=(h-1)/SHELLSORTCONST)>=1) for(k=0: ki; v=arr-->vi; while((j=j-h) >= from && arr-->(index-->j) > v) index-->(j+h)=index-->j; index-->(j+h)=vi; } ]; #Endif; ! Not NOWORDSORT; #Endif; ! Not NOINDEXSORT; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Direct ShellSort routines. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! #Ifndef NODIRECTSORT; #Ifndef NOBYTESORT; [ shsort_bytes arr from to i j v h k len; h=1; len=to-from+1; while(h < len) h=SHELLSORTCONST*h+1; while((h=(h-1)/SHELLSORTCONST)>=1) for(k=0: ki; while((j=j-h) >= from && arr->j > v) arr->(j+h)=arr->j; arr->(j+h)=v; } ]; #Endif; ! Not NOBYTESORT; #Ifndef NOWORDSORT; [ _ssg_sub arr from to comp h i j v k; while((h=(h-1)/SHELLSORTCONST)>=1) for(k=0: ki; while(((j=j-h) >= from) && comp(arr-->j, v)) arr-->(j+h)=arr-->j; arr-->(j+h)=v; } ]; [ shsort_words arr from to comp i j v h k len; h=1; len=to-from+1; while(h < len) h=SHELLSORTCONST*h+1; if(comp) return _ssg_sub(arr, from, to, comp, h); while((h=(h-1)/SHELLSORTCONST)>=1) for(k=0: ki; while((j=j-h) >= from && arr-->j > v) arr-->(j+h)=arr-->j; arr-->(j+h)=v; } ]; #Endif; ! Not NOWORDSORT; #Endif; ! Not NODIRECTSORT; #Endif; ! Not NOSHELLSORT