public int[][] strassen(int[][] X, int[][] Y) {
        try {
            int half = X.length / 2;
            int full = X.length;
            int[][] result = new int[full][full];

            if (full == 2) {
                result[0][0] = X[0][0] * Y[0][0] + X[0][1] * Y[1][0];
                result[0][1] = X[0][0] * Y[0][1] + X[0][1] * Y[1][1];
                result[1][0] = X[1][0] * Y[0][0] + X[1][1] * Y[1][0];
                result[1][1] = X[1][0] * Y[0][1] + X[1][1] * Y[1][1];
            } else {
                // create sub matrices
                int[][] X00 = initSubMatrix(X, 0, half, 0, half);
                int[][] X01 = initSubMatrix(X, 0, half, half, full);
                int[][] X10 = initSubMatrix(X, half, full, 0, half);
                int[][] X11 = initSubMatrix(X, half, full, half, full);
                int[][] Y00 = initSubMatrix(Y, 0, half, 0, half);
                int[][] Y01 = initSubMatrix(Y, 0, half, half, full);
                int[][] Y10 = initSubMatrix(Y, half, full, 0, half);
                int[][] Y11 = initSubMatrix(Y, half, full, half, full);

                // intermediate sub matrices
                int[][] P1 = divideNConquer(X00, subMatrix(Y01, Y11));
                int[][] P2 = divideNConquer(addMatrix(X00, X01), Y11);
                int[][] P3 = divideNConquer(addMatrix(X10, X11), Y00);
                int[][] P4 = divideNConquer(X11, subMatrix(Y10, Y00));
                int[][] P5 = divideNConquer(addMatrix(X00, X11), addMatrix(Y00, Y11));
                int[][] P6 = divideNConquer(subMatrix(X01, X11), addMatrix(Y10, Y11));
                int[][] P7 = divideNConquer(subMatrix(X00, X10), addMatrix(Y00, Y01));

                // sub matrices
                int[][] C00 = subMatrix(addMatrix(P5, P4), subMatrix(P2, P6));
                int[][] C01 = addMatrix(P1, P2);
                int[][] C10 = addMatrix(P3, P4);
                int[][] C11 = subMatrix(addMatrix(P1, P5), addMatrix(P3, P7));

                // merge back to original matrix
                copyBack(result, 0, half, 0, half, C00);
                copyBack(result, 0, half, half, full, C01);
                copyBack(result, half, full, 0, half, C10);
                copyBack(result, half, full, half, full, C11);
            }

            return result;

        } catch (NullPointerException e) {
            e.printStackTrace();

            return null;
        }
    }