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.

{ General-purpose array manipulation routines by J. W. Rider }

unit asorts;                               {Last modified: 09APR91}
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.
 

Notes on using the above unit:

                             ASORTS

   General-purpose routines for sorting, searching and moving arrays of arbitrary elements.

                 by J. W. Rider
 

ASORTS provides the Turbo Pascal programmer with type definitions, functions and procedures to handle a variety of array sorting and searching tasks.  Several of these are analogous to functions of the same name in the C programming language:

    qsort   --  sort an array

    bsearch --  do a binary search of a sorted array for an element that satisfies some key

    lfind   --  do a sequential ("linear") search of an unsorted (or sorted) array for an element that satisfies some key

    lsearch --  do a sequential search of an unsorted (or sorted) array for an element that satisfies some key.  If
                the element is not found, then it is appended to the end of the array.

    (Non-C programmers: please note that "bsearch" and "lfind" -- not "lsearch"! -- do the equivalent task for sorted and unsorted arrays, respectively.  It would make more sense to have "bfind" to search through a sorted array, and make "bsearch" insert a missing element into the array at the right location. However, these routines are provided for compatibility in converting C programs.)

    swab     -- move one portion of memory to another, swapping high and low order bytes in the process

There are some routines that are provided for utility above and beyond what is available in standard C libraries:

    bfind    -- do a binary search of a sorted array for an element that satisfies some key. (This differs from "bsearch" in that is "bfind" reports the "insertion index" if the key element is not found.)

    binsert  -- do a binary search of a sorted array for an element that satisfies some key.  If the element is not found, then the key element is inserted in the proper location in the array to maintain the sorted order.

    fibsearch-- searches an ordered array using a "fibonacci" search algorithm.

    fill     -- moves a single item into all of the elements of an array

    heapsort -- order an array by the "heapsort" algorithm.

    longaddr -- returns the address of a variable expressed as a longint value

    naivesort-- a particularly inefficient sorting algorithm that the author has been known to use when "qsort" was not available. (The use of "naivesort" in applications is NOT recommended!)

    scan     -- returns the index of the first element in an array that meets a specific criterion.

    selsort  -- "selection" sorting, another sorting routine.

    shellsort-- yet another sorting routine, different from the rest.

    shuffle  -- randomly permutes ("unsorts") the elements of an array

    subfill  -- moves a single item into all of the elements of a subarray of the base array

    submove  -- moves the elements of the source array into a subarray of the destination array (or, the elements of a subarray of the source to the destination array)

    swap     -- Exchanges two array elements or variables.

                (NOTE!  The "Asorts.Swap" procedure replaces the default "System.Swap" which simply exchanges the high and low-order bytes of a two byte expression.)

    vqsort   -- a quicksort algorithm for "virtual" arrays.  "VQSort" does not presume any kind of structure inherent within the "thing" being sorted. Instead, "VQSort" provides the sorting logic to other routines that will perform the actual comparisons and exchanges.

    vselsort -- a "virtual" selection sort.

    vshuffle -- a "virtual" shuffler.

    xsubmove -- moves the elements of a subarray of the source array into the elements of a subarray of the destination.
 

While these routines are provided to be of assistance to the programmer, the number of different searching and sorting algorithms does raise the issue of how to select any one algorithm for the programmer to employ.
Unfortunately, that is very application dependent.  For general purpose array sorting, you would do well to compare the "qsort" and "shellsort" routines on your actual data.  For sorting of typed files, I'd suggest a
variant of "VSelSort".
 

CONCEPTS

The ARRAYs to be manipulated are passed as "untyped vars" to these routines.  (In the "Interface" section, these arrays are called "base", "source" or "destination".)  These routines will treat the ARRAYs as if they were declared to be of type:

         ArrayType = ARRAY [1..MaxArray] OF ElementType

   WARNING!  It is *very* important to avoid defining an array so that the last byte is in a memory segment different from the first byte.  As long as you never declare an array larger than 65519, or $10000-15, bytes, it should not be a problem.

Each ELEMENT of the ARRAY is presumed to be fixed size, and this size must be passed to the routines.  (In the "Interface" section, if an ELEMENT needs to passed directly to a routine as an argument, it is passed as an untyped var called "key".) Also, the number of elements in the ARRAY must also be passed.  For
instance, to fill an array of real numbers with 0:

    var RealArray : array [1..10] of Real;
        x         : real;

    x:=0;
    fill(RealArray,10,sizeof(real),x);

A SUBARRAY is a "byte-slice" of an array.  For instance, if "ElementType" is an "array [1..8] of byte", then a "subelement" would be any contiguous collection of bytes within the element, like 3,4 and 5.  The SUBARRAY would be the collection of all of the subelements stored in an ARRAY.  If "ElementType" is a record of fields, then a "subelement" would be any contiguous group of fields.

For sorting and searching, a COMPARISON FUNCTION must be passed to the routines.  COMPARISON FUNCTIONs take two untyped vars, return a longint value, and must be declared "far" at compilation. (DIFFERENT! In C, only an integer-sized value is returned.)  For instance, to sort the array of real numbers
declared earlier:

    function RealCompare(var a,b):longint; far;
    begin
       if real(a)>real(b) then realcompare:=1
       else if real(a)<real(b) then realcompare:=-1
       else realcompare:=0;
    end;

    qsort(realarray,10,sizeof(real),realcompare);
 

"Virtual" arrays are data structures whose elements can be accessed indirectly by an index.  For instance, information that is physically stored in multiple arrays might be sorted by a key in just one of the arrays.  ASORTS provides a few routines for handling such "virtual" arrays.  "VQSort" and "VSelSort" will provide the sorting logic for ordering the arrays.  "VShuffle" will similarly "unorder" the array. To sort an array of "DBRec" with respect to an array of integer priorities, declare:

   var Array1 : array [1..MaxDBRec] of DBRec;
       Priority: array [1..MaxDBRec] of integer;

   function ComparePriority(a,b:longint):longint; far;
   begin ComparePriority:=longint(Priority[a])-Priority[b] end;

   procedure SwapPriDBRec(a,b:longint); far;
   begin asorts.swap(Priority[a],Priority[b],sizeof(integer));
         asorts.swap(Array1[a],Array1[b],sizeof(DBRec)); end;

and sort the two arrays with:

   vqsort(MaxDBRec,ComparePriority,SwapPriDBRec);
 

INTERFACE

function bfind(var key{:element}, base{:array};
                   length_base, sizeof_element:word;
                   f:comparefunc):longint;

    Searches a sorted array for a "key" element. Return the index of its location, or the negative of the index of the largest element in the array that is smaller than the key (i.e., the element that you want to insert the new element after).
 

function binsert(var key{:element}, base{:array};
                     length_base, sizeof_element:word;
                     f:comparefunc):longint;

   WARNING: This routine overwrites memory if used incorrectly.

    Inserts the key element into the correct position of an ordered array. Unlike "lsearch", which only adds the key if it's not already present, "binsert" ALWAYS inserts a new element into the array. "Binsert" returns the index where the element is inserted.
 

function bsearch(var key{:element}, base{:array};
                     length_base, sizeof_element:word;
                     f:comparefunc):word;

    "Bsearch" attempts to locate the "key" element within the previously SORTED array "base".  If successful, the index of the found element is returned; otherwise, 0 is returned to indicate that the element is not present.
 

type comparefunc = function (var a,b{:element}):longint; {far;}

    Declares the type of the comparison function to be passed to sorting and searching routines. CompareFunc's are user-defined functions that takes two arguments a and b. A and B must be typeless in the declaration, but otherwise are of the same type as the elements of the "base" array. For "qsort" and "bsearch", the function needs to return a negative integer if "A<B"; a positive integer if "A>B"; and 0 if "A=B". For "lfind" and "lsearch", the function needs to return 0 if "A=B", and some non-zero integer if "A<>B".
 

function fibsearch(var key{:element}, base{:array};
                       length_base, sizeof_element:word;
                       f:comparefunc):word;

    "Fibsearch" attempts to locate the "key" element within the previously SORTED array "base".  If successful, the index of the found element is returned; otherwise, 0 is returned to indicate that the element is not present.  (This procedure is included because a user asked about the algorithm on one of Borland's CompuServe Forums.  It looks like an interesting alternative to "bsearch", but I have not run extensive comparisons.)
 

procedure fill(var key{:element}, destination{:array};
                   count, sizeof_element:word);

   WARNING: This routine overwrites memory if used incorrectly.

    Moves the "key" element to the first "count" indices in the "destination" array.
 

procedure heapsort(var base {:array};
                   length_base, sizeof_element:word;
                   f:comparefunc);

   WARNING: This routine overwrites memory if used incorrectly.

    "HeapSort" reorders the elements of the "base" array using a heapsort algorithm (like, you expected something else?).  The function "f" is used to compare two elements of the array, and must return a negative number if the first argument is "less than" the second, a postive number if the first argument is "greater than" the second, and zero if the two arguments are "equal".
 

type icomparefunc = function (a,b:longint):longint;

    Declares the type of the comparison function to be passed as an argument for sorting "virtual" arrays.  Instead of passing the elements to be compared as is done for "comparefunc", ICompareFunc's use the location of the elements.
 

function lfind(var key{:element}, base{:array};
                   length_base, sizeof_element:word;
                   f:comparefunc):word;

    "Lfind" attempts to locate the "key" element within the array "base".  If successful, the index of the found element is returned; otherwise, 0 is returnd to indicate the element is not present.
 

function longaddr (var x): longint;

    Returns the address of a variable expressed as a longint value so that address can be used for comparisons, etc.
 

function lsearch(var key{:element}, base{:array};
                     length_base, sizeof_element:word;
                     f:comparefunc):word;

   WARNING: This routine overwrites memory if used incorrectly.
 

    Does a linear search of the "base" array, looking for the "key" element.  If the key element is found, "lsearch" returns the index of the array.  *** NOTE! ***  Otherwise, the key element is appended to the end of the array.  It is the programmer's responsibility to ensure that "sizeof_element" bytes are available at the end of the array and that the count of contained elements is adjusted.  To avoid the append, use "lfind" instead.
 

var monitor : procedure;
procedure nullmonitor;

    "Monitor" and "NullMonitor" were debugging devices developed in the process of putting together the ASORTS unit. They can be optionally declared by defining a compilation variable "MONITOR". Every time that the "Qsort" algorithm swaps a pair of array elements, the "monitor" procedure is called. This will allow the user to watch the progress of the sort. This is of marginal practical value, and by default, these two identifiers are not defined. Calling "NullMonitor" will turn off monitoring even if the "monitor" procedure variable is defined.
 

procedure naivesort(var base {:array};
                    length_base, sizeof_element:word;
                    f:comparefunc);

  WARNING: This routine slowly overwrites memory if used incorrectly.

    "NaiveSort" also reorders the elements or an array.  However, it does it more slowly than "QSort".  The use of the procedure is not recommended.  It is provided for comparison only.  (i.e., don't waste your time trying to improve upon it.  It's only here for comic relief.)
 

procedure qsort(var base {:array};
                    length_base, sizeof_element:word;
                    f:comparefunc);

   WARNING: This routine overwrites memory if used incorrectly.

    "Qsort" reorders the elements of the "base" array using a quicksort algorithm.  The function "f" is used to compare two elements of the array, and must return a negative number if the first argument is "less than" the second, a postive number if the first argument is "greater than" the second, and zero if the two arguments are "equal".
 

function scan(var source; count, sizeof_element:word;
                  f:testfunc):word;

    "Scan" does a linear search of the "source" array and returns the index of the first element that satisfies the test function "f".  If no element is found, "scan" returns 0.
 

procedure selsort(var base{:array};
                  length_base, sizeof_element:word;
                  f:comparefunc);

   WARNING: This routine overwrites memory if used incorrectly.

    "SelSort" reorders the elements of the "base" array using a selection sort algorithm.  This algorithm minimizes the number of times that each element is moved, but will maximize the of comparisons.  For most applications, the comparisons take more time than the exchanges, so you are not likely to want to use this algorithm for ordinary array sorting.  See also, "VSelSort".
 

procedure shellsort(var base{:array};
                    length_base, sizeof_element:word;
                    f:comparefunc);

   WARNING: This routine overwrites memory if used incorrectly.

    "ShellSort" reorders the elements of the "base" array using a sort routine first defined by an individual whose last name was Shell.  In concept, the algorithm is an insertion-type sort (as opposed to exchange-type sort (of which "qsort" and "selsort" are examples).  This turns out to be a very efficient sorting routine for in-memory sorting.
 

procedure shuffle(var base{:array}; length_base, sizeof_element:word);

   WARNING: This routine overwrites memory if used incorrectly.

    Randomly permutes ("unsorts") the elements of an array. The SYSTEM "Randomize" procedure should be called at least once in a program that shuffles an array.
 

procedure subfill(var key{:subelement}, destination{:subarray};
                  count, sizeof_key, sizeof_element:word);

   WARNING: This routine overwrites memory if used incorrectly.

    Partially fills an array with the "key". The address of "Destination" should be the address of the first byte in the
    subarray. The portion of the array outside of the subarray is left unchanged.

procedure submove(var source{:[sub]array},
                      destination{:[sub]array};
                      count,
                      sizeof_source_element,
                      sizeof_destination_element:word);

   WARNING: This routine overwrites memory if used incorrectly.

    If the size of the source elements are smaller than that of the destination elements, moves the first "count" elements of the source into a subarray of the same size in destination. If larger, only moves that portion of the source that will fill the first "count" elements of the destination. If equal in size, does a simple "move" of the bytes.
 

procedure swab(var source, destination; numwords:word);

   WARNING: This routine overwrites memory if used incorrectly.

    Moves the contents of source to the destination, swapping high and low order bytes in the process.  (Note:  while this is provided for C compatibility, the third argument is used differently.  In C, the third argument is the number of bytes to move and is expected to be even.  Here, the third argument is the number of byte-pairs to move.)
 

procedure swap(var var1,var2; sizeof_element:word);

   WARNING: This routine overwrites memory if used incorrectly.

    "Swap" exchanges the contents of the variable "var1" with the contents of the variable "var2".  (Note: this is a redefinition of the "swap" function defined in the System unit.)
 

type swapproc = procedure (a,b:longint); {far;}

    Declares the type of the exchange procedure that is passed as an argument to "selsort" and other virtual exchange sort algorithms.
 

type testfunc = function (var a):boolean; {far;}

    Declares the type of the criterion function to be passed to the "scan" function.  The actual TestFunc should expect an array element to be passed through "a" and return true if the element satisfies the criteria.  Otherwise, it should return false.
 

procedure vqsort(length_base:longint; f:icomparefunc; s:swapproc);

    "VQSort" provides the logic to do a quicksort of an indexed entity (a "virtual" array), but depends upon the user-defined routines "f" and "s" to do the actual work of accessing specific elements in the array, comparing and exchanging them.  This makes VQSort useful for sorting elements when they are stored in something other than a single contiguous array.
 

procedure vselsort(length_base:longint; f:icomparefunc; s:swapproc);

    "VSelSort" provides the logic to do a selection sort of an indexed entity (a "virtual" array), but depends upon the user-defined routines "f" and "s" to do the actual work of accessing specific elements in the array, comparing and exchanging them.  As mentioned in the description for "SelSort", the algorithm minimizes the number of exchanges of elements required to put something into sorted order, but makes a large number of comparisons to do so.  This would make "VSelSort" useful for something like sorting an external file of larger records based upon integer keys stored in an array in memory.
 

procedure vshuffle(length_base:longint; s:swapproc);

    Of course, what fun is there in being able to order virtual arrays if we can't mix all the elements up again?

procedure xsubmove(var source{:subarray}, destination{:subarray};
                       count, sizeof_source_element,
                       sizeof_destination_element,
                       sizeof_subelement:word);

   WARNING: This routine overwrites memory if used incorrectly.

    Moves a subarray from the source to the destination. The size of the subelements is presumed to be the same in both subarrays. "XsubMove" does not check to make sure that the sizes are consistent.
 

REFERENCES

Knuth, The Art of Computer Programming, Sorting and Searching.

Press, et al., Numerical Recipes.

Sedgewick, Algorithms.