resource jacobi
  import main
  op toprow([*] real)
  op bottomrow([*] real)
  op compute(cap jacobi up, cap jacobi down, cap main coord)
body jacobi(int id,int N,int PR,int S,real left,real top,real right,real bottom)
  real grid[0:S+1,0:N+1]
  real newgrid[1:S,1:N]   # work area
  real diff               # max difference
  # initialize boundaries then interior of grid
  for [ i = 0 to S+1 ] {  # left and right
    grid[i,0] = left; grid[i,N+1] = right
  }
  for [ j = 0 to N+1 ] { grid[0,j] = top }
  for [ j = 0 to N+1 ] { grid[S+1,j] = bottom }
  for [ i = 1 to S, j = 1 to N ] { grid[i,j] = 0.0 }

  proc compute(up, down, coord)  {
    while (true) {
      # compute next iteration
      diff = 0.0
      for [ i = 1 to S, j = 1 to N ] {
	newgrid[i,j] = (grid[i-1,j]+grid[i,j-1]+grid[i+1,j]+grid[i,j+1])/4
	diff = max(diff, abs(newgrid[i,j] - grid[i,j]))
      }
      # replace grid by newgrid
      for [ i = 1 to S, j = 1 to N ] {
	grid[i,j] = newgrid[i,j]
      }
      # check for termination
      if (coord.terminate(diff)) { exit }
      # exchange top and bottom rows with neighbors
      send up.bottomrow(grid[1,*])
      send down.toprow(grid[S,*])
      if (id != 1) { receive toprow(grid[0,*]) }
      if (id != PR) { receive bottomrow(grid[S+1,*]) }
    }
  }

  final {
    for [ i = 1 to S ] {
      for [ j = 1 to N ] { printf("%5.3f ",grid[i,j]) }
      write()
    }
  }
end

resource main
  import jacobi
  op terminate(real diff) returns bool  ans
body main()
  int N = 8    # grid dimension
  int PR = 2   # number of processes
  int S        # strip size
  # read command-line arguments, if present
  getarg(1, N); getarg(2, PR); S = N/PR
  if ((N mod PR) != 0) {
    write("N must be a multiple of PR"); stop(1)
  }
  # read boundary values and epsilon
  real left=1.0
  real top=2.1
  real right=3.3
  real bottom=4.9
  real epsilon = 0.3

  # create virtual machines
  file fd
  string[100] fname
  fd = open("hosts", READ)
  cap vm  vmcap[1:PR]
  for [ i = 1 to PR ] {
    read(fd, fname)
    vmcap[i] = create vm() on fname
  }

  # create instances of jacobi()
  cap jacobi  jcap[1:PR]
  jcap[1] = create jacobi(1,N,PR,S,left,top,right,0.0) on vmcap[1]
  for [ i = 2 to PR-1 ] {
    jcap[i] = create jacobi(i,N,PR,S,left,0.0,right,0.0) on vmcap[i]
  }
  if (PR > 1) {
    jcap[PR] = create jacobi(PR,N,PR,S,left,0.0,right,bottom) on vmcap[PR]
  }

  # start the computation
  send jcap[1].compute(noop,jcap[2],myresource())
  for [ i = 2 to PR-1 ] {
    send jcap[i].compute(jcap[i-1],jcap[i+1],myresource())
  }
  if (PR > 1) {
    send jcap[PR].compute(jcap[PR-1],noop,myresource())
  }

  int iters = 0
  # do termination checks until convergence
  while (true) {
    iters++
    # wait for all processes to call terminate, then
    # service invocation with largest value for diff
    in terminate(diff) returns ans st ?terminate == PR by -diff ->
      ans = diff <= epsilon
      for [ i = 1 to PR-1 ] {
	in terminate(diff2) returns ans2 -> ans2 = ans ni
      }
      if (ans) { exit }
    ni
  }
  stop

  final {  # print results
    write("convergence after", iters, "iterations\n")
    for [ i = 1 to PR ] {
      destroy jcap[i]
      nap(5000)                 # allow output to return from remote pipe
    }
  }
end
