
// MatrixMultRow.java
// Andrew Davison, ad@coe.psu.ac.th, May 2023

/* n*n matrix multiplication by dividing the work into
   row and column multiplications, and farming these
   out to n^2 MultWorkerRow workers.

   Each worker does one row-column multiplication
   and then terminates.

   The 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[])
  
   A worker reads in copies of the relevant row and column arrays
   based on the row and column ids passed to it in its constructor.
  
   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.
  
 
   Inspired by Session #5 of the slides:
        Tutorial on Parallel Programming with Linda
        Scientific Computing Associates, 2006
  at
    https://www.cse.chalmers.se/edu/year/2019/course/TDA384_LP1/files/lectures/semantic-linda-biglecture.pdf

  and  
       Designing and Building Parallel Programs,
       Ian Foster, Addison Wesley, 1995
       Section 4.6, Case Study: Matrix Multiplication
    at
     https://www.mcs.anl.gov/~itf/dbpp/text/node45.html#SECTION02560000000000000000


  Usage:
    > compile.bat MatrixMultRow.java
    > run.bat MatrixMultRow

    Part of the application made up of:
       * MatrixMultRow.java
       * MultWorkerRow.java

  Other version: 
    * MatrixMultCell.java
*/

import java.util.*;

import org.jspace.*;


public class MatrixMultRow
{
  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 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("Creating " + (ARR_SIZE*ARR_SIZE) + " workers");
    for (int row=0; row < ARR_SIZE; row++)
      for (int col=0; col < ARR_SIZE; col++)
        new Thread( new MultWorkerRow(row, col, space), 
                 "W" + row + col).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);
  } /* main */

} // end of MatrixMultRow class
