import java.util.Arrays;
import java.util.concurrent.*;

public class ExecutorThreadPool {
    private final int[][] array;
    private final ThreadPoolExecutor threadPoolExecutor;
    private static final int THRESHOLD = 20;

    public ExecutorThreadPool(int countThread, int[][] array) {
        this.threadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(countThread);
        this.array = array;
    }

    public int[][] operationMatrixThreadPoolExecutor() throws ExecutionException, InterruptedException {
        int[][] result = operationMatrix(array);
        threadPoolExecutor.shutdown();
        return result;
    }

    private int[][] operationMatrix(int[][] array) throws ExecutionException, InterruptedException {
        if (array.length <= 1) {
            return array;
        }
        if (array.length <= THRESHOLD) {
            return singleOperationMatrix(array);
        }

        int mid = array.length / 2;
        int[][] leftPart = Arrays.copyOfRange(array, 0, mid);
        int[][] rightPart = Arrays.copyOfRange(array, mid, array.length);

        Future<int[][]> futureLeft = threadPoolExecutor.submit(() -> singleOperationMatrix(leftPart));
        Future<int[][]> futureRight = threadPoolExecutor.submit(() -> singleOperationMatrix(rightPart));

        int[][] left = futureLeft.get();
        int[][] right = futureRight.get();

        return merge(left, right);
    }

    private int[][] singleOperationMatrix(int[][] array) {
        if (array.length <= 1) {
            return array;
        }

        int mid = array.length / 2;
        int[][] left = Arrays.copyOfRange(array, 0, mid);
        int[][] right = Arrays.copyOfRange(array, mid, array.length);

        left = singleOperationMatrix(left);
        right = singleOperationMatrix(right);

        return merge(left, right);
    }

    private int[][] merge(int[][] left, int[][] right) {
        int[][] result = new int[left.length + right.length][];
        int i = 0, j = 0, k = 0;

        while (i < left.length && j < right.length) {
            if (findMaxElementRow(left[i]) >= findMaxElementRow(right[j])) {
                result[k++] = left[i++];
            } else {
                result[k++] = right[j++];
            }
        }

        while (i < left.length) {
            result[k++] = left[i++];
        }
        while (j < right.length) {
            result[k++] = right[j++];
        }

        return result;
    }

    private static int findMaxElementRow(int[] row){
        int maxCurrentValue = Integer.MIN_VALUE;
        for(int i = 0; i < row.length; i++){
            maxCurrentValue = (row[i] >= maxCurrentValue ? row[i] : maxCurrentValue);
        }
        return maxCurrentValue;
    }
}