packages feed

language-Modula2-0.1: examples/Modula-2_Libraries/PMOS/sources/general/quicksor.mod

IMPLEMENTATION MODULE QuickSortModule;

	(********************************************************)
	(*							*)
	(*	In-memory sort using the QuickSort method	*)
	(*							*)
	(*  Programmer:		P. Moylan			*)
	(*  Last edited:	27 February 1995		*)
	(*  Status:		Working				*)
	(*							*)
	(********************************************************)

FROM SYSTEM IMPORT
    (* proc *)	ADR;

FROM Storage1 IMPORT
    (* proc *)	ALLOCATE, DEALLOCATE;

FROM LowLevel IMPORT
    (* proc *)	Copy,
    (* proc *)	AddOffset, SubtractOffset, Physical;

(************************************************************************)

TYPE
    EltPointer = ADDRESS;
    Array = RECORD
		location: ADDRESS;
		eltsize: CARDINAL;
		greaterorequal: CompareProc;
	    END (*RECORD*);

(************************************************************************)

PROCEDURE CmpPtr (pa, pb: EltPointer): INTEGER;

    (* Returns -1 if pa<pb, 0 if pa=pb, +1 if pa>pb.	*)

    VAR na, nb: LONGCARD;

    BEGIN
	na := Physical (pa);
	nb := Physical (pb);
	IF na < nb THEN RETURN -1;
	ELSIF na > nb THEN RETURN +1;
	ELSE RETURN 0;
	END (*IF*);
    END CmpPtr;

(************************************************************************)

PROCEDURE PtrDiff (high, low: ADDRESS): LONGCARD;

    (* Returns Physical(high) - Physical(low)	*)

    BEGIN
	RETURN Physical (high) - Physical (low);
    END PtrDiff;

(************************************************************************)

PROCEDURE Partition ( VAR (*INOUT*) A: Array;  low: EltPointer;
			VAR (*OUT*) mid: EltPointer;  high: EltPointer);

    (* By shuffling elements of A as necessary, ensures the property	*)
    (*		A[j] <= v	for low <= j < mid			*)
    (*		A[mid] = v						*)
    (*		A[j] >= v	for mid < j <= high			*)
    (* where v is some unspecified value chosen by the procedure.	*)
    (* Input assumption: high > low, i.e. more than one element.	*)
    (* Remark: for an array of <=3 elements, this procedure completely	*)
    (* sorts the array.							*)

    VAR up, down, temp: EltPointer;  N: CARDINAL;

    BEGIN
	down := low;  up := high;
	N := VAL(CARDINAL, PtrDiff (high, low) DIV VAL(LONGCARD,A.eltsize)) + 1;
	mid := AddOffset (low, A.eltsize * (N DIV 2));

	ALLOCATE (temp, A.eltsize);

	(* Pre-sort: first we put the first, middle and last elements	*)
	(* in their correct relative order.				*)
	(* To begin with, ensure that high^ >= low^.			*)

	IF NOT A.greaterorequal(high, low) THEN
	    Copy (low, temp, A.eltsize);
	    Copy (high, low, A.eltsize);
	    Copy (temp, high, A.eltsize);
	END (*IF*);
	IF N = 2 THEN
	    DEALLOCATE (temp, A.eltsize);  RETURN;
	END (*IF*);

	(* Load the middle element into temp^.  By swapping elements as	*)
	(* necessary, ensure that high^ >= temp^ >= low^.		*)

	Copy (mid, temp, A.eltsize);
	IF NOT A.greaterorequal (mid, low) THEN
	    Copy (low, temp, A.eltsize);
	    Copy (mid, low, A.eltsize);
	ELSIF NOT A.greaterorequal (high, mid) THEN
	    Copy (high, temp, A.eltsize);
	    Copy (mid, high, A.eltsize);
	END (*IF*);

	(* For an array of <=3 elements, the above pre-sort is actually	*)
	(* a complete sort.						*)

	IF N <= 3 THEN
	    Copy (temp, mid, A.eltsize);
	    DEALLOCATE (temp, A.eltsize);  RETURN;
	END (*IF*);

	(* v = temp^ *)

	LOOP
	    WHILE (CmpPtr (down, mid) < 0) AND A.greaterorequal (temp, down) DO
		down := AddOffset (down, A.eltsize);
	    END (*WHILE*);

	    (* All elements below down^ <= a			*)
	    (* ((down^ > v) AND (down < mid)) OR down >= mid	*)

	    IF CmpPtr (down, mid) < 0 THEN

		(* All elements below down^ <= v	*)
		(* (down^ > v) AND (down < mid)		*)

		Copy (down, mid, A.eltsize);
		mid := down;
		down := AddOffset (down, A.eltsize);

		(* hole at mid < down	*)

	    END (*IF*);

	    (* Note that down >= mid at this point.	*)

	    WHILE (CmpPtr (up, mid) > 0) AND A.greaterorequal (up, temp) DO
		up := SubtractOffset (up, A.eltsize);
	    END (*WHILE*);

	    (* All elements above up^ >= v			*)
	    (* ((up^ < v) AND (up > mid)) OR up <= mid		*)

	    IF CmpPtr (up, mid) <= 0 THEN EXIT(*LOOP*) END(*IF*);

	    Copy (up, mid, A.eltsize);
	    mid := up;
	    up := SubtractOffset (up, A.eltsize);

	    (* hole at mid > up *)

	END (*LOOP*);
	Copy (temp, mid, A.eltsize);
	DEALLOCATE (temp, A.eltsize);
    END Partition;

(************************************************************************)

PROCEDURE Sort ( VAR (*INOUT*) A: Array;  low, high: EltPointer);

    (* Sorts the subarray A[low..high] inclusive.	*)

    VAR mid: EltPointer;

    BEGIN
	WHILE CmpPtr (high, low) > 0 DO
	    Partition (A, low, mid, high);
	    IF CmpPtr (mid, low) > 0 THEN
		Sort (A, low, SubtractOffset (mid, A.eltsize));
	    END (*IF*);
	    low := AddOffset (mid, A.eltsize);
	END (*WHILE*);
    END Sort;

(************************************************************************)
(*			   THE END-USER VERSION				*)
(************************************************************************)

PROCEDURE QuickSort (VAR (*INOUT*) data: ARRAY OF BYTE;
				N, EltSize: CARDINAL;  GE: CompareProc);

    (* In-place sort of array data[0..N].  EltSize is the element size,	*)
    (* and GE is a user-supplied function to compare elements at two	*)
    (* specified addresses.						*)

    VAR A: Array;

    BEGIN
	WITH A DO
	    location := ADR (data);
	    eltsize := EltSize;
	    greaterorequal := GE;
	    Sort (A, location, AddOffset (location, N*EltSize));
	END (*WITH*);
    END QuickSort;

(************************************************************************)

END QuickSortModule.