import mpi.MPI;
import java.net.InetAddress;
import java.util.Arrays;
import java.util.Random;

public class Main {
    public static void main(String[] args) throws Exception {
        MPI.Init(args);
        int rank = MPI.COMM_WORLD.Rank();
        int size = MPI.COMM_WORLD.Size();

        String hostInfo = getHostInfo();
        System.out.println("Process " + rank + " is running on: " + hostInfo);

        int rows = 5000; // Размерность строк для большой матрицы
        int cols = 5000; // Размерность столбцов для большой матрицы
        int[][] matrix = new int[rows][cols];
        int partSize = cols / (size - 1);

        // Вставляем маленькую матрицу
        int[][] smallMatrix = new int[][] {
                {3, 1, 2, 4, 6, 5},
                {50, 1410, 0, 40, 120, 80},
                {2100, 114200, 12300, 400, 12400, 100}
        };

        // Сортируем маленькую матрицу
        smallMatrix = sortColumnsByFirstElement(smallMatrix);
        System.out.println("Master: Sorted small matrix:");
        printMatrix(smallMatrix);  // Печать отсортированной маленькой матрицы

        if (rank == 0) {
            // Создание большой матрицы 5000x5000 с случайными числами
            Random rand = new Random();
            for (int i = 0; i < rows; i++) {
                for (int j = 0; j < cols; j++) {
                    matrix[i][j] = rand.nextInt(1000); // случайное число от 0 до 999
                }
            }

            // Замер времени начала сортировки
            long startTime = System.nanoTime();

            // Отправка частей большой матрицы рабочим процессам
            for (int i = 1; i < size; i++) {
                for (int j = 0; j < rows; j++) {
                    MPI.COMM_WORLD.Send(matrix[j], (i - 1) * partSize, partSize, MPI.INT, i, j);
                }
            }
            System.out.println("Master: Sent large matrix parts to workers\n");

            // Получение результатов сортировки большой матрицы
            for (int i = 1; i < size; i++) {
                for (int j = 0; j < rows; j++) {
                    MPI.COMM_WORLD.Recv(matrix[j], (i - 1) * partSize, partSize, MPI.INT, i, j);
                }
            }

            // Замер времени окончания сортировки
            long endTime = System.nanoTime();
            System.out.println("Master: Final sorted large matrix (showing a small portion):");
            // Вывод небольшой части отсортированной большой матрицы
            printMatrix(matrix);

            // Вывод времени сортировки
            long duration = (endTime - startTime) / 1000000; // в миллисекундах
            System.out.println("Sorting large matrix took " + duration + " milliseconds.");
        } else {
            int[][] subMatrix = new int[rows][partSize];
            for (int i = 0; i < rows; i++) {
                MPI.COMM_WORLD.Recv(subMatrix[i], 0, partSize, MPI.INT, 0, i);
            }
            System.out.println("Worker " + rank + " on " + hostInfo + ": Received submatrix:");
            printMatrix(subMatrix);

            subMatrix = sortColumnsByFirstElement(subMatrix);

            for (int i = 0; i < rows; i++) {
                MPI.COMM_WORLD.Send(subMatrix[i], 0, partSize, MPI.INT, 0, i);
            }
            System.out.println("Worker " + rank + " on " + hostInfo + ": Sorted submatrix sent back to master\n");
        }

        MPI.Finalize();
    }

    private static int[][] sortColumnsByFirstElement(int[][] matrix) {
        int cols = matrix[0].length;
        int rows = matrix.length;
        Integer[] indices = new Integer[cols];
        for (int i = 0; i < cols; i++) indices[i] = i;

        Arrays.sort(indices, (a, b) -> Integer.compare(matrix[0][a], matrix[0][b]));

        int[][] sortedMatrix = new int[rows][cols];
        for (int i = 0; i < cols; i++) {
            for (int j = 0; j < rows; j++) {
                sortedMatrix[j][i] = matrix[j][indices[i]];
            }
        }
        return sortedMatrix;
    }

    private static void printMatrix(int[][] matrix) {
        if (matrix.length < 15) {
            // Печатаем всю матрицу, если она небольшая
            for (int i = 0; i < matrix.length; i++) {
                System.out.println(Arrays.toString(matrix[i]));
            }
            System.out.println();
        } else {
            // Печатаем только верхнюю часть матрицы (например, 15x15)
            for (int i = 0; i < 15; i++) {
                for (int j = 0; j < 15; j++) {
                    System.out.print(matrix[i][j] + " ");
                }
                System.out.println();
            }
        }
    }

    private static String getHostInfo() {
        try {
            InetAddress addr = InetAddress.getLocalHost();
            return addr.getHostName() + " (" + addr.getHostAddress() + ")";
        } catch (Exception e) {
            return "Unknown host";
        }
    }
}
