#INCLUDE "course.module"

-- COPYRIGHT    : P.H.Welch, 1984
-- MODIFICATION : T.Curtis, 1988
-- MODIFICATION : P.H.Welch, 2007

--{{{  COMMENT documentation
--
                    --The Sort Pump
                    ---------------
--
--This demonstrates the internal behaviour of the sort pump.
--
--Each cell in the sort pump has two registers: one holds the
--`heavy' character it is retaining and the other holds the
--latest character to arrive.  This program shows this pair
--of registers as adjacent characters.  The sort pump is thus
--represented by a line of 15 character pairs, one for each
--cell.  Each cell refreshes the display of its internal state
--after inputting the latest character and just before it
--compares this with the retained chacter.  Characters from
--the keyboard are output directly into the sort pump, which
--cause the states of all cells to be updated.  Characters
--leaving the sort pump are layed out lower on the screen.
--The end-markers separating the character groups being sorted
--are represented by dots.  Initially, all the cells in the
--sort pump contain these end-markers.
--
--The screen is controlled by a simple de-multiplexer of its
--vector of input channels.
--
--Since the pump is only 15 cells long, the keyboard process
--does not allow groups of more than 16 characters to be sent.
--
--}}}

--{{{  global constants
VAL BYTE end.char    IS '.':
VAL BYTE end.marker  IS 255:
VAL BYTE end.message IS 252:
VAL  INT total       IS 16:
--}}}

--{{{  sort cycles

-- printable ::= ' '..'~'

--{{{  PROC reporting.cell (VAL INT p,  CHAN BYTE in?, report!, out!)
PROC reporting.cell (VAL INT p,  CHAN BYTE in?, report!, out!)
  --{{{  COMMENT some documentation on the traffic carried by in and out
  --
  --in ? {{printable}* end.marker}*
  --out ! {{printable}* end.marker}*
  --report ! {next-character largest-so-far-character}*
  --
  --ASSUME : end.marker > {printable}
  --
  --}}}
  SEQ
    --{{{  initialise
    SEQ i = 0 FOR p + 1
      SEQ
        report ! end.marker
        report ! end.marker
    --}}}
    --{{{  main cycle
    WHILE TRUE
      BYTE largest:
      SEQ
        in ? largest
        --{{{  output monitor info
        SEQ
          report ! largest
          report ! end.marker
        --}}}
        out ! end.marker
        WHILE largest <> end.marker
          BYTE next:
          SEQ
            in ? next
            --{{{  output monitor info
            SEQ
              report ! next
              report ! largest
            --}}}
            IF
              (largest >= next)
                out ! next
              (TRUE)
                SEQ
                  out ! largest
                  largest := next
    --}}}
:
--}}}

--{{{  PROC sort (CHAN BYTE in?, []CHAN BYTE monitor!, CHAN BYTE out!)
PROC sort (CHAN BYTE in?, []CHAN BYTE monitor!, CHAN BYTE out!)
  --{{{  COMMENT some documentation on the traffic carried by in and out
  --
  --in ? {{printable}* end.marker}*
  --out ! {{printable}* end.marker}*
  --monitor[p] ! {next-chacter retained-character}*
  --
  --ASSUME : end.marker > {printable}
  --
  --}}}
  [total - 2]CHAN BYTE c:
  PAR
    reporting.cell (0, in?, monitor[0]!, c[0]!)
    PAR p = 1 FOR total - 3
      reporting.cell (p, c[p - 1]?, monitor[p]!, c[p]!)
    reporting.cell (total - 2, c[total - 3]?, monitor[total - 2]!, out!)
:
--}}}

--}}}

--{{{  test rig

--{{{  PROC decode.escape (CHAN BYTE in?, BYTE ch)
PROC decode.escape (CHAN BYTE in?, BYTE ch)
  SKIP -- should process <ESC>-sequence generated from keyboard
:
--}}}

--{{{  PROC keyboard.manage (CHAN BYTE in?, echo!, out!)
PROC keyboard.manage (CHAN BYTE in?, echo!, out!)
  --{{{  local constants
  VAL BYTE lo IS ' ':
  VAL BYTE hi IS '~':
  --}}}
  --{{{  state variables
  INT count:
  --}}}
  SEQ
    --{{{  initialise
    count := 0
    --}}}
    --{{{  main cycle
    WHILE TRUE
      BYTE ch:
      SEQ
        in ? ch
        IF
          ch = end.char
            --{{{  
            SEQ
              out ! end.marker
              count := 0
            --}}}
          (lo <= ch) AND (ch <= hi)
            --{{{  
            IF
              (count = total)
                SEQ
                  echo ! BELL
                  echo ! end.message
                  out ! end.marker
                  out ! ch
                  count := 1
              (TRUE)
                SEQ
                  out ! ch
                  count := count + 1
            --}}}
          ch = ESCAPE
            --{{{  
            SEQ
              decode.escape (in?, ch)
              echo ! BELL
              echo ! end.message
            --}}}
          TRUE
            --{{{  
            SEQ
              echo ! BELL
              echo ! end.message
            --}}}
    --}}}
:
--}}}

--{{{  PROC screen.plex (VAL INT n.inputs,  []CHAN BYTE in?,  CHAN BYTE out!)
PROC screen.plex (VAL INT n.inputs,  []CHAN BYTE in?,  CHAN BYTE out!)
  SEQ
    --{{{  initialise (clear screen)
    SEQ
      cursor.x.y (1, 1, out!)
      erase.eos (out!)
      flush (out!)
    --}}}
    --{{{  main cycle
    WHILE TRUE
      ALT i = 0 FOR n.inputs
        BYTE ch:
        in[i] ? ch
          --{{{  lock on to this channel for the whole message
          SEQ
            WHILE ch <> end.message
              SEQ
                --{{{  end.marker --> end.char
                IF
                  ch = end.marker
                    ch := end.char
                  TRUE
                    SKIP
                --}}}
                out ! ch
                in[i] ? ch
            flush (out!)
          --}}}
    --}}}
:
--}}}

--{{{  PROC history CHAN BYTE in?, out!)
PROC history (CHAN BYTE in?, out!)
  VAL BYTE first.line IS 6:
  VAL BYTE last.line IS 23:
  VAL BYTE first.col IS 1:
  BYTE xx, yy:
  SEQ
    --{{{  initialise
    SEQ
      xx := first.col
      yy := first.line
    --}}}
    --{{{  main cycle
    WHILE TRUE
      BYTE ch:
      SEQ
        in ? ch
        IF
          ch <> end.marker
            --{{{  
            SEQ
              cursor.x.y (xx, yy, out!)
              out ! ch
              out ! end.message
              xx := xx + 1
            --}}}
          xx > first.col
            --{{{  
            SEQ
              xx := first.col
              IF
                (yy < last.line)
                  yy := yy + 1
                (TRUE)
                  yy := first.line
              cursor.x.y (xx, yy, out!)
              erase.eol (out!)
              out ! end.message
            --}}}
          TRUE
            SKIP
    --}}}
:
--}}}

--{{{  PROC delay (VAL INT n,  CHAN BYTE in?, out!)
PROC delay (VAL INT n,  CHAN BYTE in?, out!)
  VAL max.delay IS 10:
  [max.delay]BYTE buffer:
  INT nn, ptr:
  SEQ
    --{{{  initialise
    SEQ
      --{{{  nn := min.max (0, max.delay)
      IF
        (n > max.delay)
          nn := max.delay
        (n < 0)
          nn := 0
        (TRUE)
          nn := n
      --}}}
      --{{{  fill buffer
      SEQ
        SEQ i = 0 FOR nn
          in ? buffer[i]
        ptr := 0
      --}}}
    --}}}
    --{{{  main cycle
    WHILE TRUE
      BYTE x:
      SEQ
        in ? x
        out ! buffer[ptr]
        buffer[ptr] := x
        ptr := (ptr + 1)\nn
    --}}}
:
--}}}

--{{{  PROC report.plex ([]CHAN BYTE monitor?, CHAN BYTE out!)
PROC report.plex ([]CHAN BYTE monitor?, CHAN BYTE out!)
  [total - 1]BYTE next, largest:
  WHILE TRUE
    SEQ
      --{{{  collect monitor information (in parallel)
      PAR i = 0 FOR total - 1
        SEQ
          monitor[i] ? next[i]
          monitor[i] ? largest[i]
      --}}}
      --{{{  forward message (sequentially)
      SEQ
        cursor.x.y (3, 3, out!)
        SEQ i = 0 FOR total - 1
          SEQ
            out ! next[i]
            out ! largest[i]
            out ! ' '
            out ! ' '
            out ! ' '
        out ! end.message
      --}}}
:
--}}}

--{{{  PROC test.rig (CHAN BYTE in?, out!, probe.in?, probe.out!, []CHAN BYTE monitor?)
PROC test.rig (CHAN BYTE in?, out!, probe.in?, probe.out!,
               []CHAN BYTE monitor?)
  VAL INT n.screen.inputs IS 3:
  [n.screen.inputs]CHAN BYTE mess:
  CHAN BYTE c:
  PAR
    keyboard.manage (in?, mess[0]!, probe.out!)
    report.plex (monitor?, mess[1]!)
    delay (2, probe.in?, c!)
    history (c?, mess[2]!)
    screen.plex (n.screen.inputs, mess?, out!)
:
--}}}

--}}}

PROC sort.inside (CHAN BYTE keyboard?, screen!, error!)
  CHAN BYTE a, b:
  [total - 1]CHAN BYTE monitor:
  PAR
    sort (a?, monitor!, b!)
    test.rig (keyboard?, screen!, b?, a!, monitor?)
:
