# Gauss-Seidel				Qiang A. Zhao, October 92
#
# Usage: a.out Size Workers North East South West < Color-file
#
#	 Color-file:
#		<# of colors>
#		<r> <g> <b>
#		...
#		<from violet to red>
#		...

resource gauss()

  import SRWin

  ### Var declarations/initializations

  const	EPSILON := 0.0001
  const	MAXCV := 65535
  const	MINS := 100
  const	MINWK := 2
  const	MINC := 2
  const	SCALE := 5
  const	TIMES := 100

  var	SIZE : int := MINS;		getarg(1, SIZE)
  var	WORKERS : int := MINWK;		getarg(2, WORKERS)
  var	North := 0.0;			getarg(3, North)
  var	East := 0.333;			getarg(4, East)
  var	South := 0.666;			getarg(5, South)
  var	West := 1.0;			getarg(6, West)
  var	passes : int := 0

  if (SIZE < MINS) or (WORKERS < MINWK) ->
    write(stderr, "Invalid arguments")
    stop(1)
  fi

  if SIZE mod WORKERS != 0 ->
    write(stderr, "Number of workers must evenly divide image size.")
    stop(1)
  fi

  const	STRIP : int := SIZE / WORKERS
  var	COLORS : int := MINC;
  var	stay[WORKERS] := ([WORKERS]true)
  var	changed[WORKERS] := ([WORKERS]false)
  sem	done := 0, continue[WORKERS] := ([WORKERS]0)
  var	image[0:SIZE+1, 0:SIZE+1] : real

  var	mainwin: winWindow
  var	wim : winImage
  op	ec : winEventChannel
  var	ev: winEvent

  ### open a window, create an image

  mainwin := WinOpen("", "Gauss-Seidel", ec, UseDefault, SIZE*SCALE, SIZE*SCALE)
  if mainwin = null ->
    write(stderr, "Ouch, can't open window")
    stop(1)
  fi
  WinSetEventMask(mainwin, Ev_KeyUp | Ev_ButtonUp)
  wim := WinCreateImage(mainwin, UseDefault, SIZE*SCALE, SIZE*SCALE)
  if wim = null ->
    write(stderr, "Ouch, can't create image")
    stop(1)
  fi
  WinFlush(mainwin)

  # how many colors?
  read(COLORS)
  if COLORS < MINC ->
    write(stderr, "Invalid number of colors")
    WinClose(mainwin)
    stop(1)
  fi

  # additional vars
  var	cwins[0:COLORS-1] : winWindow
  var	cpvs[0:COLORS-1] : winPixel

  # initialize the "constant" part of the image
  fa i:=0 to SIZE+1 ->
    image[i, 0] := North
    image[i, SIZE+1] := South
    image[0, i] := West
    image[SIZE+1, i] := East
  af

  # initialize colors
  const	CSTRIP : int := SIZE*SCALE/COLORS
  fa i:= 0 to COLORS-1 ->
    cwins[i] := WinNewContext(mainwin)
    if cwins[i] = null ->
      write(stderr, "No enough memory... give up")
      WinClose(mainwin)
      stop(1)
    fi

    ### grey-scale
    #var cv : int := MAXCV * (i+1) / COLORS
    #var str: string[5]
    #sprintf(str, "%04x", cv)
    #WinSetForeground(cwins[i], "#" || str || str || str)

    ### read-in colors
    var	r : int, g : int, b : int
    var	sr, sg, sb : string[5]
    read(r, g, b)
    sprintf(sr, "%04x", r*255)
    sprintf(sg, "%04x", g*255)
    sprintf(sb, "%04x", b*255)
    cpvs[i] := WinSetForeground(cwins[i], "#" || sr || sg || sb)

    WinFillRectangle(cwins[i], winRectangle(CSTRIP*i, 0, CSTRIP, SIZE*SCALE))
  af
  WinFlush(mainwin)

  ### Worker processes to compute averages

  # using checkerboard partitioning
  process worker(w := 1 to WORKERS)
    const R := (w - 1) * STRIP+1		# starting row of strip
    const EP := R + STRIP - 1
    var	tmp : real

    var phase : int := w mod 2
    var ptmp1, ptmp2, wk, i, j : int

    fa i:= R to R+STRIP-1 ->
      fa j:=1 to SIZE ->
	image[i, j] := 0.0
      af
    af

    P(continue[w])

    do stay[w] ->
      fa wk := phase to WORKERS-1 by 2 ->
	ptmp1 := wk * STRIP + 1
	ptmp2 := ptmp1 + STRIP - 1
	fa i:= R to EP ->
	  fa j:= ptmp1 to ptmp2 ->
	    tmp := (image[i+1, j] + image[i-1, j] +
			image[i, j+1] + image[i, j-1])/4.0
	    if abs(tmp - image[i, j]) > EPSILON ->
	      changed[w] := true
	      if passes = 0 or passes = 1 ->
	      var x := (i-1)*SCALE, y := (j-1)*SCALE
	      fa l := 0 to SCALE-1, k := 0 to SCALE-1 ->
	        WinPutPixel(wim, winPoint(x+l, y+k), cpvs[int(tmp*COLORS)])
	      af
	      fi
	    fi
	    image[i, j] := tmp
	  af
        af
      af	# wk

      V(done)
      phase := (phase+1) mod 2
      P(continue[w])
    od

    V(done)		# signal leave
  end			# end of worker

  ### Coordinator

  const	Rolling[0:3] : char := ('-', '\\', '|', '/')
  var	end_time : int
  var	start_time : int

  var lightYellow : winPixel := WinSetForeground(mainwin, "LightYellow")
  fa i := 0 to SIZE * SCALE - 1 ->
    fa j := 0 to SIZE * SCALE - 1 ->
      WinPutPixel(wim, winPoint(i, j), lightYellow)
    af
  af

  writes(stderr, "Press a key in the window to start...")
  receive ec(ev)
  write(stderr)
  start_time := age()
  fa mm := 1 to WORKERS -> V(continue[mm]) af

  # wait to quit
  var	ac : bool := true
  do ac ->
    # check workers
    fa k := 1 to WORKERS -> P(done) af
    #writes(stderr, "\r", Rolling[passes mod 4 + 1])
    if passes = 1 ->
      WinPutImage(mainwin, wim, winRectangle(0, 0, SIZE*SCALE, SIZE*SCALE), winPoint(0, 0))
      WinFlush(mainwin)
    fi
    if ac ->		# check convergence
      ac := false
      fa k := 1 to WORKERS -> ac := ac or changed[k]; changed[k] := false af
    fi
    if not ac -> stay := ([WORKERS] false) fi
    passes := (passes + 1) mod TIMES
    fa k := 1 to WORKERS -> V(continue[k]) af
  od

  end_time := age()
  fa k := 1 to WORKERS -> P(done) af
  write(stderr)
  writes(stderr, "Press a key in the window to quit...")
  receive ec(ev)
  stop(0)

  # final code -- close connection...
  final
    write(stderr)
    write(stderr, "Computation time: ", end_time - start_time)
    WinClose(mainwin)
  end

end gauss

