/*
 *
 *  findroot.c :  A parallel root finding program
 *  author:       Victor R. Volkman
 *
 *  Based on a problem suggested by: Professor Dave Poplawski
 *                                   Department of Computer Science
 *                                   Michigan Technological University
 *                                   Houghton, Michigan 49931
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "dvapi.h"
#include "dvapi2.h"
#include "monitor.h"

#include "monitor.fd"
#ifndef MAKEFD
#include "findroot.fd"
#endif

double  f();
static double global_x0, global_x1, global_y0, global_y1;
#define STACKSIZE 1000

int master[NUMPRC];
DESQ_HAN glob_win_han;
static MONITOR *root_mon;


/* The main() program serves a passive role in the root finding
   process.  The main() program activates node-0 which assumes
   temporary control over the other subprocesses.  Note that
   the lower and upper bounds (global_x0 and global_x1 must be
   set prior to activating node-0.  */
  
main()
{
   int i;
   int version;
   DESQ_HAN task_han;
   char stacks[NUMPRC][STACKSIZE];
   char node_str[40];
   double dummy;

   global_x0 = 0.0;       /* Lower Limit */
   global_x1 = 1.0;       /* Upper Limit */

   version = api_init();
   if (version < REQ_DESQVIEW) {
      printf("This program requires DESQView %d.%02d or later.\n",
              REQ_DESQVIEW >> 8, REQ_DESQVIEW & 0xFF);
      exit(1);
      }
   /* set minimum API function level */
   api_level(REQ_DESQVIEW);

   if (NUMPRC % 2 != 0) {
      printf("NUMPRC is %d, please use an even number of nodes\n",NUMPRC);
      exit(-1);
      }

   glob_win_han = win_new(GLOB_NAME,strlen(GLOB_NAME),
                          GLOB_HEIGHT,GLOB_WIDTH);
   win_move(glob_win_han,0,0);         /* Set screen row=0, col=0       */
   win_logattr(glob_win_han,TRUE);     /* Use logical screen attributes */
   win_attr(glob_win_han,1);           /* Display text in default color */
   win_frame(glob_win_han,FALSE);      /* Don't display a box border    */
   win_unhide(glob_win_han);           /* Mark this window as visible   */
   win_top(glob_win_han);              /* Bring to top & redraw window  */

   win_printf(glob_win_han,
             "Root finder activated with %d subprocesses and resolution %f...\n",
              NUMPRC, EPSILON);
   global_y0 = f(global_x0);          /* Establish initial values */
   global_y1 = f(global_x1);
   open_monitor("rootfinder",&root_mon);
   sendv(root_mon,0.0,MASTER);  /* Allow MASTER signal to be claimed */

   for (i=0; i<NUMPRC; i++) {
      master[i] = 0;
      task_han = tsk_new(task_findroot, stacks[i], STACKSIZE, "", 0, 0, 0);
      win_printf(glob_win_han,"spawned task %d\n",i);
      win_top(glob_win_han);
      sprintf(node_str,"%d",i);
      mal_write(mal_of(task_han), node_str, sizeof(node_str));
      }

   recvl(root_mon,&dummy,TERMINATE);
   win_printf(glob_win_han,"Lower limit X0 is %f, 
              f(X0) is %f\n",global_x0,global_y0);
   win_printf(glob_win_han,"Upper limit X1 is %f, f(X1) is %f\n",
              global_x1,global_y1);
   win_printf(glob_win_han,"Master status %d, %d, %d, %d\n",
              master[0],master[1],master[2],master[3]);
   win_printf(glob_win_han,"Press any key to continue...\n");
   win_top(glob_win_han);
   close_monitor(&root_mon);
   getch();
}



task_findroot(void)
{
   char *msg_buffer;
   char title[30];
   DESQ_HAN my_mal_han;
   DESQ_HAN win_han;
   double interv, y0, y1, dummy;
   int lastnode, indx;
   int msg_len;
   int node;
   int term;

   my_mal_han = mal_me();    /* Find out and read mail from parent */
   mal_read(my_mal_han,&msg_buffer,&msg_len);
   sscanf(msg_buffer,"%d",&node);

   sprintf(title,"Node %03d",node);
   win_han = win_new(title,strlen(title),3,38+(node<4)*40);
   win_move(win_han,7+(node%4)*5,1+(node>3)*40);
   win_logattr(win_han,1);            /* Use logical screen attributes */
   win_attr(win_han,1);               /* Display text in default color */
   win_unhide(win_han);               /* Mark this window as visible   */
   win_top(win_han);                  /* Bring to top & redraw window  */

   lastnode = (node==(NUMPRC-1));
   do
      {
      interv = (global_x1-global_x0)/(NUMPRC);
      y1 = f(global_x0+interv*(node+1));

      /* First, all even nodes send & odd nodes receive, via recv semafore */
      if (node % 2 == 0)
         sendv(root_mon,y1,node+1);
      else  /* I'm an odd node */
         recvl(root_mon,&y0,node);

      /* Second, all odd nodes send & even nodes receive, via recv semafore */
      /* Node 0 may not receive, and the lastnode may not send */
      if (!lastnode)
         {
         if (node % 2 == 1)
            sendv(root_mon,y1,node+1);
         else if (node) /* I'm an even node > 0 */
            recvl(root_mon,&y0, node);
         else         /* I'm node 0, so my left value is already computed */
            y0 = global_y0;
         }

      win_printf(win_han,"y0 = %f, y1 = %f\n",y0,y1); win_top(win_han);
      /* Determine which process will be the controller */
      if ( (y1*y0) < 0.0)
         {
         /* MASTER status must be inherited from the ex-controller */
         win_fprintf(win_han,"Node %d waiting for MASTER status\n",node);
         win_top(win_han);
         recvl(root_mon,&dummy,MASTER);
         master[node]++;
         win_printf(win_han,"Node %d has received MASTER status\n",node);
         win_top(win_han);

         /* compute new boundary conditions */
         global_x0 += interv * node;
         global_y0 = y0;

         if (!lastnode)
            global_x1 = global_x0 + interv;
         global_y1 = y1;
         term = (interv < EPSILON);

         /* synchronize waiting processes below */
         for (indx=0; indx<NUMPRC; indx++)
            if (indx!=node)
               sendv(root_mon, (double)term, indx+NUMPRC);

         /* Relinquish MASTER status to the next controller or TERMINATE */
         if (term)
             sendv(root_mon,0.0,TERMINATE);
         else
             sendv(root_mon,0.0,MASTER);
         }
      else /* synchronize */
         {
         win_printf(win_han,"Node %d suspended until notified \n",node);
         win_top(win_han);
         recvl(root_mon,&dummy,node+NUMPRC);
         term = (int) dummy;
         win_printf(win_han,"Node %d restarted by the MASTER  \n",node);
         win_top(win_han);
         }
      }
      while (!term);

   win_printf(win_han,"Leaving...\n");
   tsk_free(tsk_me());  /* De-allocate DESQview resources */
}


double f(x)
float x;
{
   return(x*x*x - x*x + x - 0.5);
}
