# Quadrature
#
# Vincent W. Freeh, Qiang A. Zhao, November 92
#
# Usage: winquad StartX EndX Delay

resource winquad()

  import MPDWin

  const int JS = 4                 # number of job servers
  const int W = 800, H = 400
  const real EPS = 0.00001
  const int DHGT = 10
  real A = 0.0;          getarg(1, A)
  real B = 2.0;          getarg(2, B)
  int  D = 50;           getarg(3, D)
  int  SW = 400

  op getJob(real a, real b, real f_a, real f_b, real a3)
  op ftn(real) returns real
  op banner(winWindow win, string[*] str)
  op DrawArea(winWindow w, real a, real b, real f_a, real f_b, bool fill)
  op EraseArea(winWindow w, real a, real b, real f_a, real f_b)
  op DrawDoneBar(winWindow w, real a, real b)
  op winEventChannel ec
  winWindow  mainwin
  string[6] colors[1:JS]
	colors[1] = "red"
	colors[2] = "yellow"
	colors[3] = "green"
	colors[4] = "cyan"
  winFont  ff 
  real  sum[JS]
  real  a3, f_a, f_b
  real  wid, hgt

  mainwin = WinOpen("", "Quadrature", ec, UseDefault, W, H+DHGT)
  if (mainwin == null) {
    write("Oops.  cannot open mainwin")
    stop
  }
  WinSetEventMask(mainwin, Ev_KeyUp | Ev_ButtonUp)
  WinSetForeground(mainwin, "YellowGreen")
  ff = WinLoadFont(mainwin, "lucidasanstypewriter-bold-24")
  if (ff != null) {
    SW = WinTextWidth(ff, "Press SPACE to go")
    WinSetFont(mainwin, ff)
  }

  ## the function
  proc ftn(x) returns e  {
    real i = 1.0
    real rx = x

    e = 1.0 + x
    while (abs(rx) > 0.00001) {
      i += 1.0
      rx *= x/i
      e += rx
    }
    return
  }

  proc banner(win, str)  {
    WinClearArea(mainwin, winRectangle(0, 0, W, H+DHGT))
    WinDrawString(win, winPoint((W-SW)/2, (H+DHGT)/2), str)
  }

  proc DrawDoneBar(w, a, b)  {
    winRectangle  rect

    rect.x = int(real(W) * (real(a)-A)/wid)
    rect.y = 0
    rect.w = int(real(W) * (real(b)-A)/wid) - rect.x
    rect.h = DHGT

    WinFillRectangle(w, rect)
    WinSync(mainwin, false)
  }

  proc DrawArea(w, a, b, f_a, f_b, fill)  {
    winPoint  pts[4]

    pts[1].x = pts[4].x = int(real(W) * (real(a)-A)/wid)
    pts[2].x = pts[3].x = int(real(W) * (real(b)-A)/wid)
    pts[1].y = pts[2].y = H + DHGT
    pts[3].y = H - int(real(H)*f_b/hgt) + DHGT
    pts[4].y = H - int(real(H)*f_a/hgt) + DHGT

    if (fill) {
      WinFillPolygon(w, pts)
    } else {
      WinDrawPolygon(w, pts)
    }
    WinSync(mainwin, false)
  }

  proc EraseArea(w, a, b, f_a, f_b)  {
    winRectangle  rect

    rect.x = int(real(W) * (real(a)-A)/wid)
    rect.y = DHGT
    rect.w = int(real(W) * (real(b)-A)/wid) - rect.x
    rect.h = H + DHGT

    WinEraseArea(w, rect)
    WinSync(mainwin, false)
  }

  process worker [ id = 1 to JS ] {
    winWindow  mywin
    real  a, b, c, f_a, f_b, f_c, a1, a2, a3
    real  a1_2

    mywin = WinNewContext(mainwin)
    WinSetForeground(mywin, colors[id])

    receive getJob(a, b, f_a, f_b, a3)

    while (true) {
      DrawArea(mywin, a, b, f_a, f_b, true)
      nap(D)
      c = (a+b)/2.0
      f_c = ftn(c)

      a1 = 0.5 * (c - a) * (f_a + f_c)         # area in interval [a,c]
      a2 = 0.5 * (b - c) * (f_c + f_b)         # area in interval [c,b]
      a1_2 = a1 + a2

      if ((abs(a1_2 - a3)) < EPS) {
	sum[id] += a1_2
	DrawDoneBar(mywin, a, b)
	receive getJob(a, b, f_a, f_b, a3)
      } else {
	EraseArea(mywin, a, b, f_a, f_b)
	DrawArea(mywin, c, b, f_c, f_b, false)
	send getJob(c, b, f_c, f_b, a2) # start job for right interval
	b = c                          # continue working on left interval
	f_b = f_c
	a3 = a1
      }
    }
  }

  #################
  int  got, tmp 
  winEvent  ev 
  bool started = false
  banner(mainwin, "Press 'SPACE' to go")
  while (true) {
    in ec(ev) ->
      if (ev.event_type == Ev_KeyUp) {
	char ch = char(ev.data)
	if (ch == 'q' | ch == 'Q' | ch == '\177' | ch == '\003') {
	  stop
	} else if (ch == ' ' and not started ) {
	  ## insert a job
	  banner(mainwin, "")
	  started = true
	  for [ i= 1 to JS ] {
	    sum[i] = 0.0
	  }
	  wid = B-A
	  hgt = ftn(B)
	  f_a = ftn(A); f_b = hgt
	  a3 = 0.5 * (wid) * (f_a + f_b)
	  send getJob(A, B, f_a, f_b, a3)
	} else if (ch == '-' ) {
	  D += 50
	} else if (ch == '+' ) {
	  D = max(0, D-50)
	} else if (ch >= '0' and ch <= '9' ) {
	  D = (int(ch)-int('0')) * 100
	} else if (ch == 'r' ) {
	  started = false
	  banner(mainwin, "Press 'SPACE' to go")
	}

      } else if (ev.event_type == Ev_DeleteWindow ) {
	stop

      }
    ni
  }

  final {
    WinClose(mainwin)
  }
end
