#include "mult.hh"
#include "random.hh"

// start a multiplier on every host but the one, I'm running on.

void
StartMultTasks (Pvm::TaskSet &Tasks)
{
  Pvm::HostSet Hosts;
  Pvm::Pvm ().Hosts (Hosts);		// get list of all hosts
  Hosts.erase (Pvm::Pvm ().I ().Host ());	// remove my host from this list
  if (Hosts.empty ())
    {
      std::cerr << "Need at least 2 hosts in the PVM for this program." 
		<< std::endl;
      exit (1);
    }
  Pvm::HostSet::iterator Current;
  for (Current = Hosts.begin (); Current != Hosts.end (); ++Current)
    // iterate through all hosts and start a multiplier on them
    Tasks.insert ((*Current).Spawn (PROGNAME));
}

// generate 2 matrices to be multiplied. Size denotes the maximal size
// in one dimension of the matrices.

void
InitMatrices (TransmitMatrices &Matrices, DoubleMatrix &Result,
	      unsigned int Size)
{
  int MinSize = (Size * 7) / 10;
  Matrices.RowsA = Random (MinSize, Size);
  Matrices.ColumnsA_RowsB = Random (MinSize, Size);
  Matrices.ColumnsB = Random (MinSize, Size);
  Matrices.MatrixA.insert (Matrices.MatrixA.begin (), Matrices.RowsA,
			   DoubleVector (Matrices.ColumnsA_RowsB));

  for (int Row = 0; Row < Matrices.RowsA; ++Row)
    for (int Column = 0; Column < Matrices.ColumnsA_RowsB; ++Column)
      Matrices.MatrixA[Row][Column] = Random (MaxValueOfElements);

  Matrices.MatrixB.insert (Matrices.MatrixB.begin (), Matrices.ColumnsA_RowsB,
			   DoubleVector (Matrices.ColumnsB));

  for (int Row = 0; Row < Matrices.ColumnsA_RowsB; ++Row)
    for (int Column = 0; Column < Matrices.ColumnsB; ++Column)
      Matrices.MatrixB[Row][Column] = Random (MaxValueOfElements);
  
  Result.insert (Result.begin (), Matrices.RowsA, DoubleVector ());
}

// receives a result and returns, which task sent it.

Pvm::Task ReceiveResult (DoubleMatrix &Result)
{
  Pvm::Task From;
  TransmitResult Received;
  Received.Receive (From);
  Result[Received.Row] = Received.Result;
  return (From);
}

// the parameter to this program is the maximal size of the matrices

int
main (int argc, char **argv)
{
  unsigned int MatrixSize = MaxNumOfRowsColumns;
  if (argc > 1)
    MatrixSize = atoi (argv[1]);
   
  Pvm::TaskSet MultTasks;
  StartMultTasks (MultTasks);

  unsigned int NumOfTasks = MultTasks.size ();

  TransmitMatrices Matrices;
  DoubleMatrix Result;
  InitMatrices (Matrices, Result, MatrixSize);

  std::cout << "Multiplying two matrices ( " << Matrices.RowsA << ", "
    << Matrices.ColumnsA_RowsB << " ) and  ( " << Matrices.ColumnsA_RowsB
    << ", " << Matrices.ColumnsB << " ) " <<  std::endl
    << "            resulting in a matrix ( "
    << Matrices.RowsA << ", " << Matrices.ColumnsB << " )." << std::endl;

  Matrices.Send (MultTasks);

  // here the number of processed rows per task (equivalent rows per
  // host here) is stored
  std::map< Pvm::Task, int > ProcessedRows;
  for (Pvm::TaskSet::iterator i = MultTasks.begin (); i != MultTasks.end ();
       ++i)
    ProcessedRows[(*i)] = 0;
   
  std::cout << "Processing Row ";
  TransmitCoords Coords;
  for (Coords.Row = 0; Coords.Row < Matrices.RowsA; ++Coords.Row)
    {				// iterate through all rows and schedule them
      std::cout << (Coords.Row != 0 ? ", " : "") 
		<< Coords.Row + 1 << std::flush;

      if (MultTasks.empty ())
	// wait for results and mark the task as ready
	MultTasks.insert (ReceiveResult (Result));
	
      // send current job to a multiplier task
      Coords.Send (*MultTasks.begin ());
      // add it to its account
      ProcessedRows[*MultTasks.begin ()]++;
      // and remove it from the set of ready tasks
      MultTasks.erase (MultTasks.begin ());
    }
  while (MultTasks.size () != NumOfTasks)
    // wait until all tasks are ready
    MultTasks.insert (ReceiveResult (Result));
  
  JobCompleted.Send (MultTasks);
  std::cout << std::endl << "Ready" << std::endl;
  // output a short statistic on who has done the work
  for (Pvm::TaskSet::iterator i = MultTasks.begin (); i != MultTasks.end ();
       ++i)
    std::cout << "Host " << (*i).Host ().Name () << " processed "
	      << ProcessedRows[(*i)] << " Rows." << std::endl;
}


syntax highlighted by Code2HTML, v. 0.9.1