import mpi.*;
import java.net.*;

public class MatrixSortMPI {

    public static void main(String[] args) throws Exception {
        // Инициализация MPI
        MPI.Init(args);

        // Получение ранга и количества процессов
        int rank = MPI.COMM_WORLD.Rank();
        int size = MPI.COMM_WORLD.Size();

        // Получение IP-адреса машины
        String ipAddress = "Unknown";
        try {
            ipAddress = InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }

        System.out.println("Процесс " + rank + " запущен на IP: " + ipAddress);

        // Размер матрицы
        int rows = 10000;
        int cols = 1000;

        int rowsPerProcess = rows / size;  // Количество строк, обрабатываемых каждым процессом
        int[][] localMatrix = new int[rowsPerProcess][cols];  // Локальная матрица для каждого процесса
        int[] localRowSums = new int[rowsPerProcess];  // Массив для хранения сумм строк

        // Начало измерения времени
        long startTime = System.currentTimeMillis();

        // 1. Генерация матрицы (каждый процесс генерирует свою часть)
        System.out.println("Процесс " + rank + " считает сумму строк...");
        for (int i = 0; i < rowsPerProcess; i++) {
            for (int j = 0; j < cols; j++) {
                localMatrix[i][j] = (int) (Math.random() * 100);
            }
        }
        long genTime = System.currentTimeMillis();

        // 2. Подсчет суммы строк
        System.out.println("Процесс " + rank + " считает сумму строк...");
        for (int i = 0; i < rowsPerProcess; i++) {
            int sum = 0;
            for (int j = 0; j < cols; j++) {
                sum += localMatrix[i][j];
            }
            localRowSums[i] = sum;
        }
        long sumTime = System.currentTimeMillis();

        // 3. Сбор сумм строк на процессе 0
        int[] allRowSums = new int[rows];
        MPI.COMM_WORLD.Gather(localRowSums, 0, rowsPerProcess, MPI.INT, allRowSums, 0, rowsPerProcess, MPI.INT, 0);

        // 4. Сортировка индексов на процессе 0
        int[] sortedIndexes = new int[rows];
        if (rank == 0) {
            System.out.println("Процесс 0 сортирует строки...");
            for (int i = 0; i < rows; i++) {
                sortedIndexes[i] = i;  // Инициализируем массив индексов строк
            }

            // Сортируем индексы по суммам строк
            for (int i = 0; i < rows - 1; i++) {
                for (int j = i + 1; j < rows; j++) {
                    if (allRowSums[sortedIndexes[i]] < allRowSums[sortedIndexes[j]]) {
                        int temp = sortedIndexes[i];
                        sortedIndexes[i] = sortedIndexes[j];
                        sortedIndexes[j] = temp;
                    }
                }
            }
        }
        long sortTime = System.currentTimeMillis();

        // 5. Перестановка строк согласно отсортированным индексам
        MPI.COMM_WORLD.Bcast(sortedIndexes, 0, rows, MPI.INT, 0);  // Рассылаем отсортированные индексы всем процессам

        // Локальная перестановка строк
        System.out.println("Процесс " + rank + " выполняет перестановку строк...");
        int[][] sortedMatrix = new int[rowsPerProcess][cols];
        for (int i = 0; i < rowsPerProcess; i++) {
            int globalRowIndex = rank * rowsPerProcess + i; // Глобальный индекс строки
            sortedMatrix[i] = localMatrix[i];  // По умолчанию строка остается без изменений
        }
        long replaceTime = System.currentTimeMillis();

        // Завершение работы MPI
        MPI.Finalize();
        long endTime = System.currentTimeMillis();
        if (rank == 0) {
            System.out.println("\nВремя выполнения генерации: " + (genTime - startTime) + " миллисекунд");
            System.out.println("\nВремя выполнения суммтрования: " + (sumTime - genTime) + " миллисекунд");
            System.out.println("\nВремя выполнения сортровки: " + (sortTime - sumTime) + " миллисекунд");
            System.out.println("\nВремя выполнения перестановки: " + (replaceTime - sortTime) + " миллисекунд");
            System.out.println("\nВремя выполнения: " + (endTime - startTime) + " миллисекунд");
        }
    }
}
