-- RandomizeWords.mesa 
--   Edited by Sweet, 22-Jan-81 15:48:41

DIRECTORY
  Ascii,
  IODefs,
  OutputDefs,
  Random,
  Segments,
  Storage,
  StreamDefs,
  Streams,
  String;

RandomizeWords: PROGRAM
  IMPORTS IODefs, OutputDefs, Random, Segments, Storage, StreamDefs, Streams, String =
  BEGIN

  Sort: PUBLIC PROCEDURE [
      a: DESCRIPTOR FOR ARRAY OF UNSPECIFIED, 
      Greater: PROC[UNSPECIFIED, UNSPECIFIED] RETURNS [BOOLEAN]] =
    BEGIN
    n: CARDINAL = LENGTH[a];
    i: CARDINAL;
    temp: CARDINAL;
    SiftUp: PROC [l, u: CARDINAL] =
      BEGIN
      s: CARDINAL;
      key: CARDINAL ← a[l-1];
      DO
        s ← l*2;
        IF s > u THEN EXIT;
        IF s < u AND Greater[a[s+1-1], a[s-1]] THEN s ← s+1;
        IF Greater[key, a[s-1]] THEN EXIT;
        a[l-1] ← a[s-1];
        l ← s;
        ENDLOOP;
      a[l-1] ← key;
      END;
    FOR i DECREASING IN [2..n/2] DO SiftUp[i, n]; ENDLOOP;
    FOR i DECREASING IN [2..n] DO
      SiftUp[1, i];
      temp ← a[1-1];
      a[1-1] ← a[i-1];
      a[i-1] ← temp;
      ENDLOOP;
    END;

  Randomize: PROC [rand: DESCRIPTOR FOR ARRAY OF UNSPECIFIED] =
    BEGIN
    r: DESCRIPTOR FOR ARRAY OF CARDINAL;
    rGreater: PROC [x, y: CARDINAL] RETURNS [BOOLEAN] =
      {RETURN [r[x] > r[y]]};
    i: CARDINAL;
    r ← DESCRIPTOR[Storage.Node[LENGTH[rand]], LENGTH[rand]];
    FOR i IN [0..LENGTH[rand]) DO
      r[i] ← Random.InRange[0, 1000];
      ENDLOOP;
    Sort[rand, rGreater];
    Storage.Free[BASE[r]];
    END;
      
  ReadDataFile: PROC [file: STRING, Add: PROC [STRING]] =
    BEGIN
    in: Streams.Handle ← NIL;
    streamEnd: BOOLEAN ← FALSE;
    name: STRING = [40];
    number: STRING = [20];
    ch: CHARACTER;

    GetToken: PROC [token: STRING] =
      BEGIN ENABLE Streams.End[] => {streamEnd ← TRUE; GO TO done};
      token.length ← 0;
      IF streamEnd THEN RETURN;
      WHILE ch = Ascii.SP OR ch = Ascii.CR DO
        ch ← Streams.GetChar[in];
	ENDLOOP;
      WHILE (ch IN ['A..'Z]) OR (ch IN ['a..'z]) OR (ch IN ['0..'9]) DO
	String.AppendChar[token, ch];
        ch ← Streams.GetChar[in];
	ENDLOOP;
      IF token.length = 0 AND ~streamEnd THEN ERROR;
      EXITS
        done => RETURN;
      END;
    BEGIN
    ENABLE {
      String.StringBoundsFault => GO TO tooLong;
      String.InvalidNumber => GO TO badNumber};

    in ← Streams.NewStream [file, Streams.Read !
      Segments.FileNameProblem[] => GO TO notFound];
    ch ← Streams.GetChar[in ! Streams.End[] => GO TO badFormat];
    WHILE ~streamEnd DO 
      GetToken[name]; 
      Add[name];
      ENDLOOP;
    EXITS
      notFound => {
	IODefs.WriteString[file];
	IODefs.WriteLine[" not found."L]};
      tooLong => {
	IODefs.WriteString["token too long at "L];
	WriteLongNumber[Streams.GetIndex[in]]};
      badNumber => {
	IODefs.WriteString["invalid number at "L];
	WriteLongNumber[Streams.GetIndex[in]]};
      badFormat => {
	IODefs.WriteString["invalid format at "L];
	WriteLongNumber[Streams.GetIndex[in]]};
    END;
    IF in # NIL THEN Streams.Destroy[in];
    END;

  input: DESCRIPTOR FOR ARRAY OF STRING;

  WriteLongNumber: PROC [ln: LONG CARDINAL] =
    BEGIN
    ls: STRING = [20];
    String.AppendLongNumber[ls, ln, 10];
    IODefs.WriteString[ls];
    END;

  
  Driver: PROC =
    BEGIN OPEN IODefs, StreamDefs, OutputDefs;
    nS: CARDINAL ← 0;
    Count: PROC [s: STRING] = {nS ← nS + 1};
    Insert: PROC [s: STRING] = 
      {input[nS] ← Storage.CopyString[s]; nS ← nS + 1};
    inputFile: STRING ← [40];
    WriteString["Input data: "L];
    ReadID[inputFile]; WriteChar[CR];
    ReadDataFile[inputFile, Count];
    input ← DESCRIPTOR[Storage.Node[nS], nS]; nS ← 0;
    ReadDataFile[inputFile, Insert];
    Randomize[input];
    OpenOutput[inputFile, ".rand"];
    FOR i: CARDINAL IN [0..nS) DO
      PutString[input[i]];
      PutCR[];
      ENDLOOP;
    CloseOutput[];
    Storage.FreeWords[BASE[input]];
    END;
    
  Driver[];
  END.