import mpi.MPI;
import java.util.Random;

public class MPIMinimumMatrix {
    private static final int SIZE = 3000;
    public static final int MaxValue = 10000;

    public static void main(String[] args) {
        long startTime = System.nanoTime();

        MPI.Init(args);
        int rank = MPI.COMM_WORLD.Rank();
        int MPIsize = MPI.COMM_WORLD.Size();

        int[][] matrix2D = null;
        int[] flatMatrix = null;
        int localMin = Integer.MAX_VALUE;

        if (rank == 0) {
            matrix2D = generateMatrix(SIZE);
            flatMatrix = new int[SIZE * SIZE];
            for (int i = 0; i < SIZE; i++) {
                for (int j = 0; j < SIZE; j++) {
                    flatMatrix[i * SIZE + j] = matrix2D[i][j];
                }
            }
            System.out.println("[Container 1] Matrix generated. Sending data to container 2...");
        }

        int rowsPerProcess = SIZE / MPIsize;
        int[] localRows = new int[rowsPerProcess * SIZE];

        MPI.COMM_WORLD.Scatter(
                flatMatrix, 0, rowsPerProcess * SIZE, MPI.INT,
                localRows,  0, rowsPerProcess * SIZE, MPI.INT,
                0
        );

        System.out.println("[Container " + rank + "] Received matrix size: " + rowsPerProcess + "x" + SIZE);

        localMin = findMin(localRows);
        System.out.println("[Container " + rank + "] Local minimum: " + localMin);

        int[] globalMin = new int[1];
        MPI.COMM_WORLD.Reduce(new int[]{localMin}, 0, globalMin, 0, 1, MPI.INT, MPI.MIN, 0);

        if (rank == 0) {
            System.out.println("[Container 1] All minimums received. Global minimum: " + globalMin[0]);
        }

        long endTime = System.nanoTime();
        if (rank == 0) {
            System.out.println("[Container 1] Time last: " + (endTime - startTime) / 1000000 + " ms");
        }

        MPI.Finalize();
    }

    private static int[][] generateMatrix(int size) {
        Random rand = new Random();
        int[][] matrix = new int[size][size];
        for (int i = 0; i < size; i++) {
            for (int j = 0; j < size; j++) {
                matrix[i][j] = rand.nextInt(MaxValue);
            }
        }
        return matrix;
    }

    private static int findMin(int[] arr) {
        int my_min = arr[0];
        for(int i = 1; i < arr.length; i++){
            my_min = min(my_min, arr[i]);
        }
        return my_min;
    }
}
