libm3/src/list/ListSort.mg


 Copyright (C) 1993, Digital Equipment Corporation           
 All rights reserved.                                        
 See the file COPYRIGHT for a full description.              
 Last modified on Thu Sep 22 19:40:50 PDT 1994 by heydon     
      modified on Thu Apr 22 08:34:33 PDT 1993 by mcjones    
      modified on Wed Feb 17 21:55:29 PST 1993 by mjordan    

GENERIC MODULE ListSort(Elem);

PROCEDURE Sort(l: T; c := Elem.Compare): T=
  BEGIN
    RETURN SortD(Copy(l), c);
  END Sort;

PROCEDURE SortD(l: T; c := Elem.Compare): T=
  VAR
    l1, l2, lm, lmHead: T;
    i, iHigh: CARDINAL;
    a: ARRAY [0..27] OF T;
    (* a[i] is a sorted list of length 0 or 2^(i+1).  Hence when a
       fills up, there are 2^(HIGH(a)+2)-1 list cells allocated, at
       least 8 bytes each. *)
  BEGIN
    iHigh := 0;
    lmHead := NEW(T);

    (* dismantle l, filling a *)
    LOOP
      (* merge two length-one lists into l1 *)
      l1 := l;
      IF l1 = NIL THEN EXIT; END;
      l2 := l1.tail;
      IF l2 = NIL THEN EXIT; END;
      l := l2.tail;
      IF c( l1.head, l2.head ) = -1 THEN
          l1.tail := l2;  l2.tail := NIL;
      ELSE
          l2.tail := l1;  l1.tail := NIL;  l1 := l2;
      END;

      (* l1 is a sorted length-two list; merge into a *)
      i := 0;
      LOOP
        l2 := a[i];
        IF l2 = NIL THEN
          a[i] := l1;
          EXIT
        ELSE
          (* merge equal-length sorted lists l1 and l2 *)
          a[i] := NIL;
          lm := lmHead;
          LOOP
            <* ASSERT l1 # NIL AND l2 # NIL *>
            IF c( l1.head, l2.head ) = -1 THEN
              lm.tail := l1;  lm := l1;  l1 := l1.tail;
              IF l1 = NIL THEN  lm.tail := l2; EXIT END;
            ELSE
              lm.tail := l2;  lm := l2;  l2 := l2.tail;
              IF l2 = NIL THEN  lm.tail := l1; EXIT END;
            END;
          END; (*LOOP*)
          l1 := lmHead.tail;
          INC(i);
          IF i > iHigh THEN iHigh := i END;
        END
      END (*LOOP*)
    END; (*LOOP*)

    (* l1 is a list of length 0 or 1; merge l1 and a[0..iHigh] into l1 *)
    i := 0;
    IF l1 = NIL THEN
      WHILE (a[i] = NIL) AND (i # iHigh) DO INC(i) END;
      l1 := a[i];
      INC(i);
    END;

    (* l1 # NIL or i > iHigh *)
    WHILE i <= iHigh DO
      l2 := a[i];
      IF l2 # NIL THEN
        lm := lmHead;
        LOOP
          IF c( l1.head, l2.head ) = -1 THEN
            lm.tail := l1;  lm := l1;  l1 := l1.tail;
            IF l1 = NIL THEN lm.tail := l2; EXIT END;
          ELSE
            lm.tail := l2;  lm := l2;  l2 := l2.tail;
            IF l2 = NIL THEN lm.tail := l1; EXIT END;
          END
        END; (*LOOP*)
        l1 := lmHead.tail
      END;
      INC(i)
    END;

    RETURN l1;
  END SortD;

PROCEDURE Copy(l: T): T RAISES {} =
  VAR last, rest, result: T; BEGIN
    IF l = NIL THEN RETURN NIL; END;
    result := NEW( T, head := l.head, tail := NIL );
    last := result;
    rest := l.tail;
    WHILE rest # NIL DO
      last.tail := NEW(T, head := rest.head, tail := NIL);
      last := last.tail;
      rest := rest.tail;
    END;
    RETURN result;
  END Copy;

BEGIN
END ListSort.