You have received data from a Bubble bot. You know your task is to make factory facilities, but before you even start, you need to know how big the factory is and how many rooms it has. When you look at the data you see that you have the dimensions of the construction, which is in rectangle shape: N x M. 

Then in the next N lines you have M numbers. These numbers represent factory tiles and they can go from 0 to 15. Each of these numbers should be looked in its binary form. Because from each number you know on which side the tile has walls. For example number 10 in it's binary form is 1010, which means that it has a wall from the North side, it doesn't have a wall from the East, it has a wall on the South side and it doesn't have a wall on the West side. So it goes North, East, South, West. 

It is guaranteed that the construction always has walls on it's edges. The input will be correct. 

Your task is to print the size of the rooms from biggest to smallest. 

Input

The first line has two numbers which are N and M, the size of the construction. Both are integers: 

n (1 ≤ n ≤ 10^3)

m (1 ≤ m ≤ 10^3)

Next N x M numbers represent each tile of construction.

Output

Once you finish processing the data your output consists of one line sorted from biggest to smallest room sizes. 

Example

Input


4 5
9 14 11 12 13
5 15 11 6 7
5 9 14 9 14
3 2 14 3 14


Output


9 4 4 2 1 
6 1
13
5
7
15
13
7

3 2 1 

import java.util.*;
import java.math.*;
import java.io.*;
import java.lang.Math.*;

public class KickStart2020 {
	 static class FastReader {
		BufferedReader br;
		StringTokenizer st;

		public FastReader() {
			br = new BufferedReader(new InputStreamReader(System.in));
		}

		String next() {
			while (st == null || !st.hasMoreElements()) {
				try {
					st = new StringTokenizer(br.readLine());
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			return st.nextToken();
		}

		int nextInt() {
			return Integer.parseInt(next());
		}

		long nextLong() {
			return Long.parseLong(next());
		}

		double nextDouble() {
			return Double.parseDouble(next());
		}

		float nextFloat() {
			return Float.parseFloat(next());
		}

		String nextLine() {
			String str = "";
			try {
				str = br.readLine();
			} catch (IOException e) {
				e.printStackTrace();
			}
			return str;
		}
	}

	static long gcd(long a, long b) {
		if (b == 0)
			return a;
		return gcd(b, a % b);

	}

	static long lcm(long a, long b) {
		return a / gcd(a, b) * b;
	}

	public static class Pair implements Comparable<Pair> {
		public int index;
		public int value;

		public Pair(int index, int value) {
			this.index = index;
			this.value = value;
		}

		@Override
		public int compareTo(Pair other) {
			// multiplied to -1 as the author need descending sort order
			if(other.index < this.index) return 1;
			if(other.index > this.index) return -1;
			if(other.value < this.value) return 1;
			if(other.value > this.value) return -1;
			else return 0;
		}
		@Override
		public String toString() {
			return this.index + " " + this.value;
		}
	}
	static boolean isPrime(long d) {
		if (d == 1)
			return false;
		for (int i = 2; i <= (long) Math.sqrt(d); i++) {
			if (d % i == 0)
				return false;
		}
		return true;
	}

	static void decimalTob(int n, int k, Stack<Integer> ss) {
          int x = n % k;
          int y = n / k;
          ss.push(x);
          if(y > 0) {
        	   decimalTob(y, k, ss);
          }
	}
	static int[] binaryArray(int n) {
		int x = n;
		Stack<Integer> ss = new Stack<>(); 
		for(int i = 0; i < 3; i++) {
			ss.add(x % 2);
			x /= 2;
		}
		ss.add(x);
		int arr[] = new int[4];
		for(int i = 0; i < 4; i++) arr[i] = ss.pop();
		return arr;
	}

	static long powermod(long x, long y, long mod) {
		long ans = 1;
		x = x % mod;
		if (x == 0)
			return 0;
		int i = 1;
		while (y > 0) {
			if ((y & 1) != 0)
				ans = (ans * x) % mod;
			y = y >> 1;
			x = (x * x) % mod;
		}
		return ans;
	}
	static int dfs(int node, int i, int j, int arr[][], HashMap<Integer, int[]> ss, boolean hachu[]) {
		if(hachu[node]) return 0;
		hachu[node] = true;
		int count = 1;
		int temp[] = ss.get(node);
		if(temp[0] == 0) count += dfs(arr[i - 1][j], i - 1, j, arr, ss, hachu);
		if(temp[1] == 0) count += dfs(arr[i][j + 1], i, j + 1, arr, ss, hachu);
		if(temp[2] == 0) count += dfs(arr[i + 1][j], i + 1, j, arr, ss, hachu);
		if(temp[3] == 0) count += dfs(arr[i][j - 1], i, j - 1, arr, ss, hachu);
		return count;
	}
	public static void main(String[] args) throws Exception {
		FastReader sc = new FastReader();
		PrintWriter out = new PrintWriter(System.out);
		int crr[][] = new int[16][];
		for(int i = 0; i < 16; i++) crr[i] = binaryArray(i);
        int t = 1; 
        while(t-- > 0) {
        	int n = sc.nextInt();
        	int m = sc.nextInt();
        	int arr[][] = new int[n][m]; 
        	int brr[][] = new int[n][m];
        	int k = 1;
        	for(int i = 0; i < n; i++) for(int j = 0; j < m; j++) {
        		arr[i][j] = sc.nextInt();
        		brr[i][j] = k++;
        	}
        	HashMap<Integer, int[]> ss = new HashMap<>();
        	for(int i = 0; i < n; i++) {
        		for(int j = 0; j < m; j++) {
        			ss.put(brr[i][j], crr[arr[i][j]]);
        		}
        	}
        	boolean hachu[] = new boolean[k];
        	ArrayList<Integer> ssd = new ArrayList<>();
        	for(int i = 0; i < n; i++) 
        		for(int j = 0; j < m; j++)
        		if(!hachu[brr[i][j]]) {
        			ssd.add(dfs(brr[i][j], i, j, brr, ss, hachu));
        		}
        	Collections.sort(ssd, Collections.reverseOrder());
        	for(int e: ssd) out.print(e + " ");
        }
		out.close();
	}
}