home *** CD-ROM | disk | FTP | other *** search
- unit asorts; {Last modified: 09APR91}
- { General-purpose array manipulation routines }
- { Copyright 1991, J. W. Rider }
-
- { Notice: This unit makes extensive use of array types that exceed the
- maximum "safe" size of 65519 bytes. While the compiler "allows" the
- declaration without error, application program should not ordinarily try
- to allocate memory to such structures. Segment wraparound problems can
- otherwise occur. For instance, most of these routines will not work on an
- array that "straddles" a segment boundary. If you notice carefully in
- this unit, the large arrays are used only for typecasting purposes, and
- no memory is allocated to them. }
-
- interface
-
- { $define MONITOR} { <--- remove space before "$" to enable
- monitoring various sorting routines }
- {$ifdef MONITOR}
- var monitor : procedure; { for monitoring results of sort }
- procedure nullmonitor; { to turn monitoring off }
- {$endif}
-
-
- { *** Type definitions *** }
-
- { "comparefunc" -- comparison function argument for "qsort", "bsearch"
- "lfind" and "lsearch"
-
- "icomparefunc"-- comparison function argument for "virtual" routines
-
- "swapproc" -- exchange procedure for "virtual" routines
-
- "testfunc" -- test function argument for "scan" }
-
- type comparefunc = function (var a,b):longint;
- icomparefunc= function (a,b:longint):longint;
- swapproc = procedure(a,b:longint);
- testfunc = function (var a):boolean;
-
-
- { *** C compatibility routines *** }
-
- { "qsort", "bsearch", "lfind", "lsearch" and "swab" are analogous to
- standard C functions of the same names }
-
- { quicksort the elements of an array }
- procedure qsort(var base; length_base, sizeof_element:word;
- f:comparefunc);
-
- { binary search a sorted array for an element}
- function bsearch(var key,base; length_base, sizeof_element:word;
- f:comparefunc):word;
-
- { linear search an array for an element }
- function lfind(var key,base; length_base, sizeof_element:word;
- f:comparefunc):word;
-
- { linear search an array for an element; append if not found }
- function lsearch(var key,base; length_base, sizeof_element:word;
- f:comparefunc):word;
-
- { move one array of words to another, swapping bytes }
- procedure swab(var source, destination; numwords:word);
-
-
- { *** "riderized" (i.e, generally nonstandard) routines *** }
-
- { the remaining routines generally have no standard implementation in other
- languages }
-
- { binary search a sorted array for an element. Return the index of
- its location, or the negative of the index where it should be inserted }
- function bfind(var key,base; length_base, sizeof_element:word;
- f:comparefunc):longint;
-
- { inserts an element into a sorted array. }
- function binsert(var key,base; length_base, sizeof_element:word;
- f:comparefunc):word;
-
- { fibonacci search a sorted array; marginally faster than "bsearch" }
- function fibsearch(var key,base; length_base, sizeof_element:word;
- f:comparefunc):word;
-
- { fill an array with an element }
- procedure fill(var key,destination; count, sizeof_element:word);
-
- { order an array by the "heapsort" algorithm }
- procedure heapsort(var base; length_base, sizeof_element:word;
- f:comparefunc);
-
- { return the address of variable as a longint value }
- function longaddr(var x):longint;
-
- { a not-so-quick sorting routine, compare with qsort }
- procedure naivesort(var base; length_base, sizeof_element:word;
- f:comparefunc);
-
- { scan a subarray for the first element that meets a specific criteria }
- function scan(var source; count, sizeof_element:word; f:testfunc):word;
-
- { order an array by the "selection sort" algorithm }
- procedure selsort(var base; length_base, sizeof_element:word;
- f:comparefunc);
-
- { order an array by the "shell sort" algorithm }
- procedure shellsort(var base; length_base, sizeof_element:word;
- f:comparefunc);
-
- { randomly permute the elements of an array }
- procedure shuffle(var base; length_base, sizeof_element:word);
-
- { fill a subarray with an element }
- procedure subfill(var key,destination;
- count, sizeof_key,sizeof_element:word);
-
- { move subarray to array or array to subarray }
- procedure submove(var source,destination;
- count, sizeof_source, sizeof_destination:word);
-
- { swap two elements or variables of the same size }
- procedure swap(var var1,var2; sizeof_element:word);
-
- { sort a "virtual" array by the quicksort algorithm }
- procedure vqsort(length_base:longint; f:icomparefunc; s:swapproc);
-
- { sort a "virtual" array by using a selection sort algorithm }
- procedure vselsort(length_base:longint; f:icomparefunc; s:swapproc);
-
- { randomly permute a "virtual" array }
- procedure vshuffle(length_base:longint; s:swapproc);
-
- { move subarray to subarray }
- procedure xsubmove(var source,destination;
- count,sizeof_source,sizeof_destination,sizeof_move:word);
-
- implementation
-
- function bfind(var key,base; length_base, sizeof_element:word;
- f:comparefunc):longint;
- var b:array [0..$fffe] of byte absolute base; l,h,x,c:longint;
- begin if length_base>0 then begin
- l:=0; h:=pred(length_base);
- repeat
- x:=(l+h) shr 1; c:=f(key,b[x*sizeof_element]);
- if c<0 then h:=pred(x)
- else if c>0 then l:=succ(x)
- else{if c=0 then}begin bfind:=succ(x); exit; end;
- until l>h;
- bfind:=-l; end
- else bfind:=0; end;
-
-
- function binsert(var key,base;length_base,sizeof_element:word;
- f:comparefunc):word;
- var b:array [0..$fffe] of byte absolute base; x:longint;
- begin
- x:=bfind(key,base,length_base,sizeof_element,f);
- if x<=0 then x:=-x else dec(x);
- move(b[x*sizeof_element],b[succ(x)*sizeof_element],
- (length_base-x)*sizeof_element);
- move(key,b[x*sizeof_element],sizeof_element);
- binsert:=succ(x); end;
-
-
- function bsearch(var key,base; length_base, sizeof_element:word;
- f:comparefunc):word;
- var c:longint;
- begin
- c:=bfind(key,base,length_base,sizeof_element,f);
- if c>0 then bsearch:=c
- else bsearch:=0; end;
-
-
- function fibsearch(var key,base; length_base, sizeof_element:word;
- f:comparefunc):word;
- var b:array [0..$fffe] of byte absolute base; i,p,q,imax:word; t:longint;
- begin
- imax:=length_base*sizeof_element;
- q:=0; p:=sizeof_element; i:=p+q; { set up for fibonacci sequencing }
- while imax>(i+p) do begin q:=p; p:=i; inc(i,q); end;
- dec(i,sizeof_element); {zero-base adjustment}
- while true do begin
- if i<imax then t:=f(key,b[i])
- else t:=-1; { simulate "too big" for "out of range" }
- if t=0 then begin fibsearch:=succ(i div sizeof_element); exit end
- else if t<0 then
- if q=0 then begin fibsearch:=0; exit end
- else begin dec(i,q); q:=p-q; dec(p,q) end
- else { if t>0 then }
- if p=sizeof_element then begin fibsearch:=0; exit end
- else begin inc(i,q); dec(p,q); dec(q,p) end end end;
-
-
- procedure fill(var key,destination; count, sizeof_element:word);
- var b:array [0..$fffe] of byte absolute destination;
- x,moved:word;
- begin if count>0 then begin
- move(key,destination,sizeof_element);
- moved:=1; dec(count); x:=sizeof_element;
- while count>moved do begin
- move(destination,b[x],x);
- dec(count,moved); moved:=moved shl 1; x:=x shl 1; end;
- move(destination,b[x],count*sizeof_element); end; end;
-
-
- procedure heapsort(var base; length_base, sizeof_element:word;
- f:comparefunc);
- var b: array[0..$fffe] of byte absolute base;
- p:pointer; nx:longint; k,kx:word;
-
- procedure aux1(kx:word);
-
- procedure aux2; var jx:word;
- begin
- while kx<=(nx shr 1) do begin
- jx:=kx shl 1;
- if (jx<nx) and (f(b[jx],b[jx+sizeof_element])<0) then
- inc(jx,sizeof_element);
- if f(p^,b[jx])>=0 then exit;
- move(b[jx],b[kx],sizeof_element);
- {$ifdef MONITOR}
- if @monitor<>nil then monitor;
- {$endif}
- kx:=jx end end;
-
- begin {aux1}
- move(b[kx],p^,sizeof_element);
- {$ifdef MONITOR}
- if @monitor<>nil then monitor;
- {$endif}
- aux2;
- move(p^,b[kx],sizeof_element);
- {$ifdef MONITOR}
- if @monitor<>nil then monitor;
- {$endif}
- end;
-
- begin {heapsort}
- getmem(p,sizeof_element);
- nx:=pred(length_base)*sizeof_element;
- for k:=(length_base shr 1) downto 1 do aux1(pred(k)*sizeof_element);
- repeat
- swap(b[0],b[nx],sizeof_element);
- {$ifdef MONITOR}
- if @monitor<>nil then begin monitor; monitor; monitor end;
- {$endif}
- dec(nx,sizeof_element);
- aux1(0);
- until nx<=0;
- freemem(p,sizeof_element) end;
-
- function lfind(var key,base; length_base, sizeof_element:word;
- f:comparefunc):word;
- var b:array [0..$fffe] of byte absolute base; i,j:word;
- begin
- j:=0;
- for i:=1 to length_base do begin
- if f(key,b[j])=0 then begin lfind:=i; exit end;
- inc(j,sizeof_element); end;
- lfind:=0; end;
-
-
- function longaddr(var x):longint;
- begin longaddr:=(longint(seg(x)) shl 4) + ofs(x); end;
-
-
- function lsearch(var key,base; length_base, sizeof_element:word;
- f:comparefunc):word;
- var b:array [0..$fffe] of byte absolute base; i:word;
- begin
- i:=lfind(key,base,length_base,sizeof_element,f);
- if i=0 then begin
- move(key,b[length_base*sizeof_element],sizeof_element);
- lsearch:=succ(length_base); end
- else lsearch:=i; end;
-
- procedure naivesort(var base; length_base, sizeof_element:word;
- f:comparefunc);
- var b: array[0..$fffe] of byte absolute base;
- i,j,l,r:word;
- begin
- i:=0;
- for l:=1 to pred(length_base) do begin
- j:=i+sizeof_element;
- for r:=succ(l) to length_base do begin
- if f(b[i],b[j])>0 then begin
- swap(b[i],b[j],sizeof_element);
- {$ifdef MONITOR}
- if @monitor<>nil then monitor;
- {$endif}
- end;
- inc(j,sizeof_element); end;
- inc(i,sizeof_element); end; end;
-
- {$ifdef MONITOR}
- { dummy "monitor" }
- procedure nullmonitor; begin pointer((@@monitor)^):=NIL end;
- {$endif}
-
- procedure qsort(var base; length_base, sizeof_element:word;
- f:comparefunc);
- var b: array[0..$fffe] of byte absolute base;
- j:longint; x:word; { not preserved during recursion }
-
- procedure sort(l,r: word);
- var i:longint;
- begin
- i:=l*sizeof_element;
- while l<r do begin
- j:=r*sizeof_element;
- x:=((longint(l)+r) SHR 1)*sizeof_element;
- while i<j do begin
- while f(b[i],b[x])<0 do inc(i,sizeof_element);
- while f(b[x],b[j])<0 do dec(j,sizeof_element);
- if i<j then begin
- swap(b[i],b[j],sizeof_element);
- if i=x then x:=j else if j=x then x:=i;
- {$ifdef MONITOR}
- if @monitor<>nil then monitor;
- {$endif}
- end;
- if i<=j then begin
- inc(i,sizeof_element); dec(j,sizeof_element) end; end;
- if (l*sizeof_element)<j then sort(l,j div sizeof_element);
- l:=i div sizeof_element; end; end;
-
- begin sort(0,pred(length_base)); end; {procedure qsort}
-
-
- function scan(var source; count, sizeof_element:word; f:testfunc):word;
- var b:array[0..$fffe] of byte absolute source;
- i,j:word;
- begin
- j:=0;
- for i:=1 to count do begin
- if f(b[j]) then begin scan:=i; exit; end;
- inc(j,sizeof_element); end;
- scan:=0; end;
-
-
- procedure selsort(var base; length_base, sizeof_element:word;
- f:comparefunc);
- var b:array[0..$fffe] of byte absolute base;
- i,ix,j,jx,k,kx:word;
- begin
- ix:=0;
- for i:=1 to pred(length_base) do begin
- kx:=ix; jx:=ix;
- for j:=succ(i) to length_base do begin
- inc(jx,sizeof_element);
- if f(b[jx],b[kx])<0 then kx:=jx end;
- if kx<>ix then begin
- swap(b[kx],b[ix],sizeof_element);
- {$ifdef MONITOR}
- if @monitor<>nil then monitor;
- {$endif}
- end; inc(ix,sizeof_element) end; end;
-
-
- procedure shellsort(var base; length_base, sizeof_element:word;
- f:comparefunc);
- var b:array[0..$fffe] of byte absolute base;
- p:pointer; h,jx:longint; i,hx,ix:word;
-
- procedure aux; begin
- while f(b[jx-hx],p^)>0 do begin
- move(b[jx-hx],b[jx],length_base); dec(jx,hx);
- {$ifdef MONITOR}
- if @monitor<>nil then monitor;
- {$endif}
- if jx<hx then exit end end;
-
- begin if length_base>0 then begin
- getmem(p,length_base);
- if p<>nil then begin
- h:=1; repeat h:=3*h+1 until h>length_base;
- repeat
- h:=h div 3; hx:=h*sizeof_element; ix:=hx;
- for i:=succ(h) to length_base do begin
- move(b[ix],p^,sizeof_element);
- {$ifdef MONITOR}
- if @monitor<>nil then monitor;
- {$endif}
- jx:=ix; aux;
- if jx<>ix then move(p^,b[jx],sizeof_element);
- {$ifdef MONITOR}
- if @monitor<>nil then monitor;
- {$endif}
- inc(ix,sizeof_element) end;
- until h=1;
- freemem(p,length_base) end end end;
-
-
- procedure shuffle(var base; length_base, sizeof_element:word);
- var b: array[0..$fffe] of byte absolute base;
- i,ix,j,jx:word;
- begin if length_base>0 then
- for i:=pred(length_base) downto 1 do begin
- ix:=i*sizeof_element;
- j:=random(succ(i));
- if i<>j then begin
- jx:=j*sizeof_element;
- swap(b[ix],b[jx],sizeof_element); end; end; end;
-
-
- procedure subfill(var key,destination;
- count, sizeof_key,sizeof_element:word);
- var b:array [0..$fffe] of byte absolute destination; i,j:word;
- begin
- j:=0;
- for i:=1 to count do begin
- move(key,b[j],sizeof_key);
- inc(j,sizeof_element); end; end;
-
-
- procedure submove(var source, destination;
- count, sizeof_source,sizeof_destination:word);
- var sm:word;
- begin if sizeof_source=sizeof_destination then
- move(source,destination,count*sizeof_source)
- else begin
- if sizeof_source>sizeof_destination then sm:=sizeof_destination
- else sm:=sizeof_source;
- xsubmove(source,destination,
- count,sizeof_source,sizeof_destination,sm); end; end;
-
-
- procedure swab(var source, destination; numwords:word);
- var a: array [1..$7fff] of word absolute source;
- b: array [1..$7fff] of word absolute destination;
- i:word;
-
- begin if longaddr(source)>=longaddr(destination) then
- for i:=1 to numwords do b[i]:=system.swap(a[i])
- else
- for i:=numwords downto 1 do b[i]:=system.swap(a[i]) end;
-
-
- procedure swap(var var1,var2; sizeof_element:word);
- type chunk = array [0..$f] of byte;
- var a:array [0..$fffe] of byte absolute var1;
- b:array [0..$fffe] of byte absolute var2;
- ac: array [1..$fff] of chunk absolute var1;
- bc: array [1..$fff] of chunk absolute var2;
- c:chunk; { swap buffer }
- k:byte; x:word;
-
- procedure swapchunk(var e,f:chunk);
- begin c:=e; e:=f; f:=c; end;
-
- procedure swapbytes(var e,f; len:byte);
- begin move(e,c,len); move(f,e,len); move(c,f,len); end;
-
- begin
- for k:=1 to (sizeof_element shr 4) do swapchunk(ac[k],bc[k]);
- k:=(sizeof_element and $f);
- if k>0 then begin
- x:=(sizeof_element and $fff0); swapbytes(a[x],b[x],k); end; end;
-
-
- procedure vqsort(length_base:longint; f:icomparefunc; s:swapproc);
- var j,x:longint; { not preserved during recursion }
-
- procedure sort(l,r:longint);
- var i:longint;
- begin
- i:=l; j:=r;
- x:=(i+j) SHR 1;
- while i<j do begin
- while f(i,x)<0 do inc(i);
- while f(x,j)<0 do dec(j);
- if i<j then begin
- s(i,j);
- if i=x then x:=j else if j=x then x:=i; end;
- if i<=j then begin inc(i); dec(j) end; end;
- if l<j then sort(l,j);
- if i<r then sort(i,r); end;
-
- begin sort(1,length_base); end; {procedure vqsort}
-
-
- procedure vselsort(length_base:longint; f:icomparefunc; s:swapproc);
- var i,j,k:longint;
- begin for i:=1 to pred(length_base) do begin
- k:=i;
- for j:=succ(i) to length_base do if f(j,k)<0 then k:=j;
- if k<>i then s(k,i) end end;
-
-
- procedure vshuffle(length_base:longint; s:swapproc);
- var i,j:longint;
- begin for i:=length_base downto 2 do begin
- j:=succ(random(i));
- if i<>j then begin s(i,j); end; end; end;
-
-
- procedure xsubmove(var source,destination;
- count,sizeof_source,sizeof_destination,sizeof_move:word);
- var a:array [0..$fffe] of byte absolute destination;
- b:array [0..$fffe] of byte absolute source;
- i,j,k:word; r:boolean;
- begin
- r:=longaddr(source)>=longaddr(destination);
- if r then begin j:=0; k:=0; end
- else begin
- j:=pred(count)*sizeof_destination; k:=pred(count)*sizeof_source; end;
- for i:=1 to count do begin
- move(b[k],a[j],sizeof_move);
- if r then begin
- inc(j,sizeof_destination); inc(k,sizeof_source) end
- else begin
- dec(j,sizeof_destination); dec(k,sizeof_source) end; end; end;
-
-
- {$ifdef MONITOR}
- begin {initialization}
- nullmonitor;
- {$endif}
-
- end.