public class GrassPasture {
    private int[][] pastures;

    public GrassPasture(int[][] pastures) {
        this.pastures = pastures;
    }

    public int getTotalGrass() {
        int sum = 0;
        for (int[] row : pastures) {
            for (int i : row) {
                sum += i;
            }
        }
        return sum;
    }

    public int maxSquare() {
        int[][] sumArray = new int[pastures.length][pastures[0].length]; // sum array from [0][0] to [i][j]

        for (int i = 0; i < pastures.length; i++) { // create the sum array
            for (int j = 0; j < pastures[0].length; j++) {
                sumArray[i][j] = pastures[i][j]; // add the index
                if (i != 0) {
                    sumArray[i][j] += sumArray[i - 1][j]; // add the top neighbor if it exists
                }
                if (j != 0) {
                    sumArray[i][j] += sumArray[i][j - 1]; // add the right neighbor if it exists
                }
                if (i != 0 && j != 0) {
                    sumArray[i][j] += sumArray[i - 1][j - 1]; // add the top right neighbor if it exists
                }
            }
        }

        int max = Integer.MIN_VALUE; // initial max is lowest integer value
        int sum; // to save sum values
        for (int n = 1; n <= Integer.min(pastures.length, pastures[0].length); n++) { // for every size square of n x n
            for (int i = 0; i < pastures.length - n + 1; i++) {
                for (int j = 0; j < pastures[0].length - n + 1; j++) {
                    sum = sumArray[n + i - 1][n + j - 1]; // add the sum from [0][0] to [i][j]
                    if (i != 0) {
                        sum -= sumArray[i - 1][n + j - 1]; // subtract the sums outside the square
                    }
                    if (j != 0) {
                        sum -= sumArray[n + i - 1][j - 1];
                    }
                    if (i != 0 && j != 0) {
                        sum -= sumArray[i - 1][j - 1];
                    }
                    max = Integer.max(max, sum); // keep sum if higher than max
                }
            }
        }

        return max; // return max
    }

    public int maxSubarraySum() {
        int[] flat = new int[pastures.length * pastures[0].length]; // create new flat array of size of number of total elements in pastures

        for (int i = 0; i < pastures.length; i++) { // copy each array into flat
            System.arraycopy(pastures[i], 0, flat, i * pastures.length, pastures[0].length);
        }

        // kadane's algorithm
        int max = Integer.MIN_VALUE;
        int sum = 0;

        for (int i : flat) {
            sum = Integer.max(i, sum + i);
            max = Integer.max(max, sum);
        }
        return max;
    }

    public static void main(String[] args) {
        int[][] pastures = {
                {-3, 6, -1},
                {2, -1, 5},
                {-9, 4, -1}
        };

        GrassPasture gp = new GrassPasture(pastures);
        System.out.println("Total Tastiness: " + gp.getTotalGrass());
        System.out.println("Max Square Sum: " + gp.maxSquare());
        System.out.println("Max Subarray Sum: " + gp.maxSubarraySum());

        /* Extra credit
         * 1. my time complexity for maxSquare() is O(pastures.length * pastures[0].length * Integer.min(pastures.length, pastures[0].length)).
         *    it uses a triple nested for loop looking from 1 to n (minimum of x and y length), 1 to i (x length), and 1 to j (y length).
         * 3. my time complexity for maxSubarraySum() is O(pastures.length * pastures[0].length).
         *    it uses a kadane's algorithm which is O(n*m) and flattening the array is also O(n*m)
         */

    }
}