(* Polyphase sort program.  There are n-1 source files for
  merging and a single output file.  The destination of the
  merged data chabges, when a certain number of runs has been
  distributed.  This number is computed according to a
  Fibonacci distribution. *)

MODULE polysort;

FROM InOut       IMPORT WriteCard;
FROM Terminal    IMPORT WriteString, WriteLn, Read;
FROM FileSystem  IMPORT File, Lookup, Create, Reset, SetPos, GetPos, Response, Close;
FROM ByteBlockIO IMPORT ReadByteBlock, WriteByteBlock;

CONST n = 6;            (* # of files *)
     numrecs = 10;

TYPE item = RECORD
             key: CARDINAL;
           END;

   tapeno = [1..n];

VAR leng,high,low,rand: CARDINAL;
   eot: BOOLEAN;
   buf,next: item;
   f0: File;
   f: ARRAY [1..n] OF File;
   ch: CHAR;

PROCEDURE list(VAR f: File; n: tapeno);
VAR z: CARDINAL;

BEGIN
 z := 0;
 WriteLn; WriteString(' tape ');
 WriteCard(n,2); WriteLn;
 LOOP
   ReadByteBlock(f,buf);
   IF f.eof THEN EXIT END;
   WriteCard(buf.key,5);
   INC(z);
   IF z = 15 THEN WriteLn; z := 0 END
 END;
 WriteLn;
 Reset(f)
END list;


PROCEDURE polyphasesort;
VAR i,j,mx,tn,dn,x,min,z: CARDINAL(* tapeno *);
   k,level:CARDINAL;
   a,d,last,t,ta: ARRAY tapeno OF CARDINAL;
     (* a[j] = ideal # of runs on file j *)
     (* d[j] = # of dummy runs on file *)
     (* last[j] = key of tail item on tape *)
     (* t,ta = mappings of tape #'s *)

 PROCEDURE selectfile;
 VAR i: tapeno;
     z: CARDINAL;

 BEGIN
   IF d[j] < d[j+1] THEN
     INC(j)
   ELSE
     IF d[j] = 0 THEN
       INC(level); z := a[1];
       FOR i := 1 TO n-1 DO
         d[i] := z + a[i+1] - a[i];
         a[i] := z + a[i+1]
       END
     END;
     j := 1
   END;
   DEC(d[j]);
 END selectfile;

 PROCEDURE copyrun;
 VAR buf,next: item;
     high,low : CARDINAL;

 BEGIN (*copy one run from x to y*)
   ReadByteBlock(f0,next);
   REPEAT
     buf := next;
     IF NOT f0.eof THEN
       WriteByteBlock(f[j],buf);
       GetPos(f0,high,low);
       ReadByteBlock(f0,next);
     END;
   UNTIL f0.eof OR (buf.key > next.key);
   IF NOT f0.eof THEN SetPos(f0,high,low) END;
   last[j] := buf.key
 END copyrun;

BEGIN  (* polyphasesort *)
 FOR i := 1 TO n(* -1 *) DO
   a[i] := 1; d[i] := 1;
   Create(f[i],'DK.')
 END;
 level := 1; j := 1;
 a[n] := 0; d[n] := 0;
 REPEAT
   selectfile;
   copyrun;
 UNTIL f0.eof OR (j = n-1);
 LOOP
   IF f0.eof THEN EXIT END;
   selectfile;
   GetPos(f0,high,low);
   ReadByteBlock(f0,next);
   SetPos(f0,high,low);
   IF last[j] <= next.key THEN
     copyrun;
     IF f0.eof THEN d[j] := d[j]+1 ELSE copyrun END
   ELSE copyrun
   END
 END;
 FOR i := 1 TO n-1 DO Reset(f[i]) END;

 FOR i := 1 TO n DO t[i] := i END;
 REPEAT
   z := a[n-1]; d[n] := 0;
   Close(f[t[n]]); Create(f[t[n]],'DK.');
   WriteString(' level'); WriteCard(level,4); WriteLn;
   WriteString(' tape'); WriteCard(t[n],4); WriteLn;
   FOR i := 1 TO n DO
     WriteCard(t[i],6);
     WriteCard(a[i],6);
     WriteCard(d[i],6);
     WriteLn
   END;
   REPEAT
     k := 0;
     FOR i := 1 TO n-1 DO
       IF d[i] > 0 THEN
         DEC(d[i])
       ELSE
         INC(k);
         ta[k] := t[i]
       END
     END;
     IF k = 0 THEN
       INC(d[n])
     ELSE
       REPEAT
         i := 1; mx := 1;
         GetPos(f[ta[1]],high,low);
         ReadByteBlock(f[ta[1]],next);
         SetPos(f[ta[1]],high,low);
         min := next.key;
         WHILE i < k DO
           INC(i);
           GetPos(f[ta[i]],high,low);
           ReadByteBlock(f[ta[i]],next);
           SetPos(f[ta[i]],high,low);
           x := next.key;
           IF x < min THEN
             min := x;
             mx := i
           END
         END;
         (* ta[mx] has minimal element, move it to t[j] *)
         ReadByteBlock(f[ta[mx]],buf);
         WriteByteBlock(f[t[n]],buf);
         GetPos(f[ta[mx]],high,low);
         ReadByteBlock(f[ta[mx]],next);
         eot := f[ta[mx]].eof;
         SetPos(f[ta[mx]],high,low);
         IF (buf.key > next.key) OR eot THEN
           ta[mx] := ta[k];
           DEC(k)
         END
       UNTIL k = 0;
     END;
     DEC(z);
   UNTIL z = 0;
   Reset(f[t[n]]);
   list(f[t[n]],t[n]);
   tn := t[n];
   dn := d[n];
   z := a[n-1];
   FOR i := n TO 2 BY -1 DO
     t[i] := t[i-1];
     d[i] := d[i-1];
     a[i] := a[i-1] - z
   END;
   t[1] := tn;
   d[1] := dn;
   a[1] := z;
   DEC(level)
 UNTIL level = 0;
END polyphasesort;

BEGIN
 leng := numrecs;
 Lookup(f0,'tmp.TEXT',TRUE);
 IF f0.res # done THEN WriteString(' File not opened. ') END;
 REPEAT
   buf.key := leng;
   WriteCard(buf.key,4);
   WriteByteBlock(f0,buf);
   DEC(leng);
   IF (leng MOD 20) = 0 THEN WriteLn END;
 UNTIL leng = 0;
 WriteLn;
 Reset(f0); list(f0,1);
 polyphasesort;
 FOR low := 1 TO n-1 DO Close(f[low]) END;
END  polysort.