
// MatrixMultCell.java
// Andrew Davison, ad@coe.psu.ac.th, May 2023

/* n*n matrix multiplication by dividing the work into
   row and column multiplications multiplications, farming 
   the task out to n (ARR_SIZE) MultWorkerCell workers.

   In this version of the problem, each worker carries
   out multiple tasks until the matrix multiplication is
   completed. There's nothing special about having n
   workers, any number could be used.

   As in MatrixMultRow.java, n rows of the first matrix 
   are added to the space as tuples of the form:
     ("ROW", row-id, int[])
  
   The n columns of the second matrix are added to the 
   space as tuples of the form:
     ("COLUMN", col-id, int[])

   The new data are n*n TASK tuples of the form:
      ("TASK", row-id, col-id)
 
   These are the tasks for all the workers. A worker *removes* a
   TASK tuple, and then *reads* in copies of the corresponding 
   row and column arrays.
  
   The result (a single integer value) is put into the space in
   a tuple of the form:
       ("RESULT", row-id, col-id, result)

   The n^2 results are read in by main(), and printed as
   the result matrix.

   When there are no TASK tuples left, main() will issue a 
   ("DIE") tuple, which is used by the workers to terminate.

  Usage:
    > compile.bat MatrixMultCell.java
    > run.bat MatrixMultCell

    Part of the application made up of:
       * MatrixMultCell.java
       * MultWorkerCell.java

  Other version: 
    * MatrixMultRow.java
*/

import java.util.*;

import org.jspace.*;


public class MatrixMultCell
{
  private static final int ARR_SIZE = 3;  // n


  public static void main(String[] args) 
                            throws InterruptedException
  {
    // Space space = new SequentialSpace();
    Space space = new VisSpace(100);

    // generate two n*n arrays full of random numbers
    Random r = new Random();
    int[][] arr1 = SpaceUtils.randArr(ARR_SIZE, 1, 4, r);
            // each cell is a random integer in the range 1-4 
    System.out.println("Array 1:");
    SpaceUtils.printArr(arr1);

    int[][] arr2 = SpaceUtils.randArr(ARR_SIZE, 1, 4, r);
    System.out.println("Array 2:");
    SpaceUtils.printArr(arr2);

    System.out.println("Placing " + ARR_SIZE + 
                    " rows of array 1 in space");
    for (int row=0; row < ARR_SIZE; row++)
       space.put("ROW", row, arr1[row]);

    System.out.println("Placing " + ARR_SIZE + 
                    " columns of array 2 in space");
    for (int col=0; col < ARR_SIZE; col++)
      space.put("COLUMN", col, SpaceUtils.getColumn(arr2, col));

    System.out.println("Placing " + (ARR_SIZE*ARR_SIZE) + 
                                 " tasks in space");
    for (int row=0; row < ARR_SIZE; row++)
      for (int col=0; col < ARR_SIZE; col++)
        space.put("TASK", row, col);

    PrintAreas pas = new PrintAreas(ARR_SIZE);
    System.out.println("Creating " + ARR_SIZE + " workers");
    for (int i=0; i < ARR_SIZE; i++)
        new Thread( new MultWorkerCell(i, pas, space), 
                 "W-" + i).start();

    System.out.println("Build multiplication result");
    int[][] result = new int[ARR_SIZE][ARR_SIZE];
    for (int row=0; row < ARR_SIZE; row++) {
      for (int col=0; col < ARR_SIZE; col++) {
        Object[] item = space.get(
                    new ActualField("RESULT"), 
                    new ActualField(row), 
                    new ActualField(col),
                    new FormalField(Integer.class));
        result[row][col] =  (int)item[3];
      }
    }

    System.out.println("\nResult:");
    SpaceUtils.printArr(result);

    System.out.println("Tell workers to terminate");
    space.put("DIE");
  } /* main */

} // end of MatrixMultCell class
