// Arup Guha
// 11/17/2014
// Solution to 2014 South East Regional D1 and D2 Problem: Gold Leaf

import java.util.*;

public class goldleaf {

	public static int[] ans;

	public static void main(String[] args) {

		// Read in input.
		Scanner stdin = new Scanner(System.in);
		int r = stdin.nextInt();
		int c = stdin.nextInt();
		boolean[][] grid = new boolean[r][c];
		for (int i=0; i<r; i++) {
			String s = stdin.next();
			for (int j=0; j<c; j++)
				grid[i][j] = (s.charAt(j) == '#');
		}

		// Any answer is better than this one - will get overwritten.
		ans = new int[4];
		Arrays.fill(ans, 100);

		// Try horizontal first.
		horizCut(grid, true);

		// Then vertical.
		boolean[][] transGrid = getTransGrid(grid);
		horizCut(transGrid, false);

		// Then diagonal.
		diagCut(grid, true);

		// Then back diag.
		boolean[][] flipGrid = getFlipGrid(grid);
		diagCut(flipGrid, false);

		// Print result.
		System.out.println(ans[0]+" "+ans[1]+" "+ans[2]+" "+ans[3]);
	}

	// Fixes horizontal coordinates to vertical ones.
	public static int[] fix(int[] array) {
		int[] tmp = new int[4];
		tmp[0] = array[1];
		tmp[1] = array[0];
		tmp[2] = array[3];
		tmp[3] = array[2];
		return tmp;
	}

	// Fixes forward diagonal coordinates to backward diagonal ones.
	public static int[] fixDiag(int[] array, int myR, int myC) {
		int[] tmp = new int[4];
		tmp[0] = array[2];
		tmp[1] = myC + 1 - array[3];
		tmp[2] = array[0];
		tmp[3] = myC + 1 - array[1];
		return tmp;
	}

	// Updates ans considering the new solution array, as necessary.
	public static void update(int[] array) {
		if (beat(array, ans))
			for (int i=0; i<ans.length; i++)
				ans[i] = array[i];
	}

	// Returns true iff a comes before b, lexicographically.
	public static boolean beat(int[] a, int[] b) {
		for (int i=0; i<a.length; i++) {
			if (a[i] < b[i]) return true;
			if (a[i] > b[i]) return false;
		}
		return false;
	}

	// Returns the transpose of grid.
	public static boolean[][] getTransGrid(boolean[][] grid) {
		boolean[][] ans = new boolean[grid[0].length][grid.length];
		for (int i=0; i<grid.length; i++)
			for (int j=0; j<grid[0].length; j++)
				ans[j][i] = grid[i][j];
		return ans;
	}

	// Returns grid flipped over the vertical line of symmetry.
	public static boolean[][] getFlipGrid(boolean[][] grid) {
		boolean[][] ans = new boolean[grid.length][grid[0].length];
		for (int i=0; i<grid.length; i++)
			for (int j=0; j<grid[0].length; j++)
				ans[i][grid[0].length-1-j] = grid[i][j];
		return ans;
	}

	// Processes a horizintal cuts if isRegCase = true, vertical cuts otherwise.
	public static void horizCut(boolean[][] grid, boolean isRegCase) {

		int r = grid.length;
		int c = grid[0].length;

		// i is the cut we are trying.
		for (int i=1; i<r; i++) {

			int lowdouble = i;
			int highdouble = Math.min(r-1, 2*i-1);

			// First check every "doubled" square - exactly one must be on.
			boolean flag = true;
			for (int x=lowdouble,xprime=lowdouble-1; x<=highdouble; x++,xprime--) {
				for (int y=0; y<c; y++) {
					if (!xor(grid[x][y], grid[xprime][y])) {
						flag = false;
						break;
					}
				}
				if (!flag) break;
			}

			if (!flag) continue;

			// Fold is no more than half way, need to check items on highest rows, potentially.
			if (lowdouble < r/2) {
				if (allTrue(grid, highdouble+1,0,r-1,c-1)) {
					int[] ans = new int[4];
					ans[0] = i; ans[1] = 1; ans[2] = i; ans[3] = c;
					if (isRegCase) 	update(ans);
					else 			update(fix(ans));
				}
			}

			// Fold is more than half way, need to check items on lowest rows, potentially.
			else {
				if (allTrue(grid, 0,0,2*i-r-1,c-1)) {
					int[] ans = new int[4];
					ans[0] = i; ans[1] = 1; ans[2] = i; ans[3] = c;
					if (isRegCase)	update(ans);
					else			update(fix(ans));
				}
			}
		}
	}

	// Processes forward diagonal cuts if isRegCase = true, backward diagonal cuts otherwise.
	public static void diagCut(boolean[][] grid, boolean isRegCase) {

		int r = grid.length;
		int c = grid[0].length;

		// i is the sum of the r and c on the diagonal being cut.
		for (int i=0; i<r+c-2; i++) {

			// Whole diagonal must be on.
			if (!diagOn(grid, i)) continue;


			boolean flag = true;
			boolean[][] checked = new boolean[grid.length][grid[0].length];

			// Loop through all points to left of fold - checks squares landing off the paper also, marking
			// every checked square.
			for (int x=0; x<i; x++) {
				for (int y=0; x+y<i; y++) {
					if (!inbounds(x,y,grid)) continue;
					checked[x][y] = true;
					int diff = i-x-y;
					boolean oppSq = false;
					if (inbounds(x+diff,y+diff,grid)) {
						oppSq = grid[x+diff][y+diff];
						checked[x+diff][y+diff] = true;
					}
					if (!xor(grid[x][y], oppSq)) {
						flag = false;
						break;
					}
				}
				if (!flag) break;
			}

			if (!flag) continue;

			// Now, we must check any square that wasn't previously checked.
			for (int x=0; x<r; x++) {
				for (int y=0; y<c; y++) {
					if (!checked[x][y] && !grid[x][y]) {
						flag = false;
						break;
					}
				}
				if (!flag) break;
			}

			// Save answer - make sure to deal with "overflow" lines...
			if (flag) {
				int[] ans = new int[4];
				ans[0] = i+1; ans[1] = 1; ans[2] = 1; ans[3] = i+1;
				int overR = Math.max(0, ans[0] - r);
				int overC = Math.max(0, ans[3] - c);
				ans[0] -= overR;
				ans[1] += overR;
				ans[2] += overC;
				ans[3] -= overC;
				if (isRegCase)	update(ans);
				else			update(fixDiag(ans, r, c));
			}
		}
	}

	// Returns true iff the diagonal that adds up to sum is all on in this grid.
	public static boolean diagOn(boolean[][] grid, int sum) {
		for (int i=0; i<=sum; i++) {
			if (i >= grid.length || sum-i >= grid[0].length) continue;
			if (!grid[i][sum-i]) return false;
		}
		return true;
	}

	// Returns true iff (r,c) is inside grid.
	public static boolean inbounds(int r, int c, boolean[][] grid) {
		return r >= 0 && r < grid.length && c >= 0 && c < grid[0].length;
	}

	// Returns a xor b.
	public static boolean xor(boolean a, boolean b) {
		return (a && !b) || (!a && b);
	}

	// Returns true iff everything in grid in box (r1,c1) to (r2,c2) is true.
	public static boolean allTrue(boolean[][] grid, int r1, int c1, int r2, int c2) {
		for (int i=r1; i<=r2; i++)
			for (int j=c1; j<=c2; j++)
				if (!grid[i][j])
					return false;
		return true;
	}
}