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

Difficulty: MEDIUM
Tags: Bit manipulation

Graph traversal
Skill types: Bit manipulation

Starter code:

Solutions:
directions = [(-1, 0), (0, 1), (1, 0), (0, -1)]
i_cant_do_bitwise_operations = {0: (False, False, False, False), 1: (False, False, False, True), 2: (False, False, True, False), 3: (False, False, True, True), 4: (False, True, False, False), 5: (False, True, False, True), 6: (False, True, True, False), 7: (False, True, True, True), 8: (True, False, False, False), 9: (True, False, False, True), 10: (True, False, True, False), 11: (True, False, True, True), 12: (True, True, False, False), 13: (True, True, False, True), 14: (True, True, True, False), 15: (True, True, True, True)}
vis = []

def dfs(tiles, i, j):
	vis[i][j] = True
	tot = 1
	for k in range(4):
		if not tiles[i][j][k] and (not vis[i + directions[k][0]][j + directions[k][1]]):
			tot += dfs(tiles, i + directions[k][0], j + directions[k][1])
	return tot
(n, m) = list(map(int, input().split()))
tiles = []
for i in range(n):
	line = input().split()
	tile_line = []
	vis_line = []
	for x in line:
		tile_line.append(i_cant_do_bitwise_operations[int(x)])
		vis_line.append(False)
	tiles.append(tile_line)
	vis.append(vis_line)
rooms = []
for i in range(n):
	for j in range(m):
		if not vis[i][j]:
			rooms.append(dfs(tiles, i, j))
rooms = sorted(rooms, reverse=True)
for x in rooms:
	print(x, end=' ')
print()


from collections import deque
import io, os
input = io.BytesIO(os.read(0, os.fstat(0).st_size)).readline
grid = []
(m, n) = map(int, input().split())
visited = [[False for j in range(n)] for i in range(m)]
for i in range(m):
	for j in range(n):
		grid.append(list(map(int, input().split())))
direc = [[0, -1], [1, 0], [0, 1], [-1, 0]]

def get_dimension(i, j):
	queue = deque()
	output = 0
	queue.append([i, j])
	visited[i][j] = True
	while queue:
		[curri, currj] = queue.popleft()
		output += 1
		for d in range(4):
			if grid[curri][currj] & 1 << d > 0:
				continue
			newi = curri + direc[d][0]
			newj = currj + direc[d][1]
			if newi < 0 or newi >= m or newj < 0 or (newj >= n):
				continue
			if visited[newi][newj]:
				continue
			if grid[newi][newj] & 1 << (d + 2) % 4:
				continue
			visited[newi][newj] = True
			queue.append([newi, newj])
	return output
ans = []
for i in range(m):
	for j in range(n):
		if visited[i][j]:
			continue
		ans.append(get_dimension(i, j))
ans = sorted(ans, reverse=True)
print(' '.join(map(str, ans)) + ' ')


def main():
	(n, m) = map(int, input().split())
	grid = [list(map(int, input().split())) for _ in range(n)]
	visited = [[False] * m for _ in range(n)]
	rooms = []
	for i in range(n):
		for j in range(m):
			if visited[i][j]:
				continue
			room_size = 0
			stack = [(i, j)]
			while stack:
				(x, y) = stack.pop()
				if visited[x][y]:
					continue
				visited[x][y] = True
				room_size += 1
				if x > 0 and (not (grid[x][y] & 8 or grid[x - 1][y] & 2)):
					stack.append((x - 1, y))
				if y < m and (not (grid[x][y] & 4 or grid[x][y + 1] & 1)):
					stack.append((x, y + 1))
				if x < n and (not (grid[x][y] & 2 or grid[x + 1][y] & 8)):
					stack.append((x + 1, y))
				if y > 0 and (not (grid[x][y] & 1 or grid[x][y - 1] & 4)):
					stack.append((x, y - 1))
			rooms.append(room_size)
	rooms.sort(reverse=True)
	print(' '.join(map(str, rooms)) + ' ')
main()


def dfs(i, j):
	vis[i][j] = True
	global cells
	cells += 1
	adj = [(i - 1, j), (i, j + 1), (i + 1, j), (i, j - 1)]
	adj.reverse()
	c = box[i][j]
	for (x, y) in adj:
		if 0 <= x < n and 0 <= y < m and (not vis[x][y]) and (not c % 2):
			dfs(x, y)
		c //= 2
(n, m) = (int(x) for x in input().split())
box = [[int(x) for x in input().split()] for _ in range(n)]
vis = [[False] * m for _ in range(n)]
sizes = []
for i in range(n):
	for j in range(m):
		if not vis[i][j]:
			cells = 0
			dfs(i, j)
			sizes.append(cells)
sizes.sort(reverse=True)
for x in sizes:
	print(x, end=' ')


def naiveSolve():
	return

def solve():
	return

class UnionFind:

	def __init__(self, n):
		self.parent = list(range(n))

	def find(self, a):
		acopy = a
		while a != self.parent[a]:
			a = self.parent[a]
		while acopy != a:
			(self.parent[acopy], acopy) = (a, self.parent[acopy])
		return a

	def union(self, a, b):
		self.parent[self.find(b)] = self.find(a)

def northWall(x):
	return x & 8 > 0

def eastWall(x):
	return x & 4 > 0

def southWall(x):
	return x & 2 > 0

def westWall(x):
	return x & 1 > 0

def main():
	(n, m) = readIntArr()
	grid = []
	for _ in range(n):
		grid.append(readIntArr())
	uf = UnionFind(n * m)
	for i in range(n):
		for j in range(m):
			if j + 1 < m and eastWall(grid[i][j]) == westWall(grid[i][j + 1]) == False:
				uf.union(i * m + j, i * m + j + 1)
			if i + 1 < n and southWall(grid[i][j]) == northWall(grid[i + 1][j]) == False:
				uf.union(i * m + j, (i + 1) * m + j)
	sz = [False] * (n * m)
	for i in range(n):
		for j in range(m):
			sz[uf.find(i * m + j)] += 1
	rooms = []
	for x in range(n * m):
		if sz[x] > 0:
			rooms.append(sz[x])
	rooms.sort(reverse=True)
	oneLineArrayPrint(rooms)
	return
import sys
input = sys.stdin.buffer.readline

def oneLineArrayPrint(arr):
	print(' '.join([str(x) for x in arr]) + ' ')

def multiLineArrayPrint(arr):
	print('\n'.join([str(x) for x in arr]))

def multiLineArrayOfArraysPrint(arr):
	print('\n'.join([' '.join([str(x) for x in y]) for y in arr]))

def readIntArr():
	return [int(x) for x in input().split()]

def makeArr(defaultValFactory, dimensionArr):
	dv = defaultValFactory
	da = dimensionArr
	if len(da) == 1:
		return [dv() for _ in range(da[0])]
	else:
		return [makeArr(dv, da[1:]) for _ in range(da[0])]

def queryInteractive(x):
	print('{}'.format(x))
	sys.stdout.flush()
	return int(input())

def answerInteractive(ans):
	print('! {}'.format(ans))
	sys.stdout.flush()
inf = float('inf')
from math import gcd, floor, ceil
for _abc in range(1):
	main()


import sys
sys.setrecursionlimit(100000)

def _r():
	return sys.stdin.buffer.readline()

def rs():
	return _r().decode('ascii').strip()

def rn():
	return int(_r())

def rnt():
	return map(int, _r().split())

def rnl():
	return list(rnt())

def solve(n, m, mat):
	(dp, color) = ([[0] * m for _ in range(n)], 1)

	def _color(r, c, color):
		(stack, colored) = ([(r, c)], 1)
		dp[r][c] = color
		while stack:
			(r, c) = stack.pop()
			for (dr, dc, mask) in ((-1, 0, 8), (0, 1, 4), (1, 0, 2), (0, -1, 1)):
				(nr, nc) = (r + dr, c + dc)
				if mat[r][c] & mask == 0 and dp[nr][nc] == 0:
					dp[nr][nc] = color
					colored += 1
					stack.append((nr, nc))
		return colored
	sol = []
	for r in range(n):
		for c in range(m):
			if dp[r][c] == 0:
				sol.append(_color(r, c, color))
				color += 1
	return sorted(sol, reverse=True)
(n, m) = rnt()
mat = []
for _ in range(n):
	mat.append(rnl())
print(' '.join(map(str, solve(n, m, mat))), '')


import os
import sys
from io import BytesIO, IOBase
import math as mt
import itertools as it
import operator as op
import bisect as bs
import heapq as hp
from functools import reduce
from io import BytesIO, IOBase
import pprint
import logging
from logging import getLogger
import array
from collections import deque, defaultdict, OrderedDict, Counter, ChainMap, _chain
(maxx, localsys, mod) = (1 << 60, 0, int(1000000000.0 + 7))

def nCr(n, r):
	return reduce(op.mul, range(n - r + 1, n + 1), 1) // mt.factorial(r)

def ceil(a, b):
	return (a + b - 1) // b

def lcm(a, b):
	return a * b // mt.gcd(a, b)
gcdm = lambda *args: reduce(mt.gcd, args, 0)

def lcm(a, b):
	return a * b // mt.gcd(a, b)
lcmm = lambda *args: reduce(lcm, args, 1)
logging.basicConfig(format='%(message)s', level=logging.WARNING)
logger = getLogger(__name__)
logger.setLevel(logging.INFO)

def debug(msg, *args):
	logger.info(f'{msg}={pprint.pformat(args)}')
_str = str
str = lambda x=b'': x if type(x) is bytes else _str(x).encode()
BUFSIZE = 8192

class FastIO(IOBase):
	newlines = 0

	def __init__(self, file):
		self._fd = file.fileno()
		self.buffer = BytesIO()
		self.writable = 'x' in file.mode or 'r' not in file.mode
		self.write = self.buffer.write if self.writable else None

	def read(self):
		while True:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			if not b:
				break
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines = 0
		return self.buffer.read()

	def readline(self):
		while self.newlines == 0:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			self.newlines = b.count(b'\n') + (not b)
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines -= 1
		return self.buffer.readline()

	def flush(self):
		if self.writable:
			os.write(self._fd, self.buffer.getvalue())
			(self.buffer.truncate(0), self.buffer.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')
(sys.stdin, sys.stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))

def input():
	return sys.stdin.readline().rstrip('\r\n')

def maps():
	return map(int, input().split())

class DisjointSetUnion:

	def __init__(self, n):
		self.n = n
		self.parent = list(range(n))
		self.size = [1] * n
		self.numsets = n

	def find(self, x):
		xcopy = x
		while self.parent[x] != x:
			x = self.parent[x]
		while xcopy != x:
			(xcopy, self.parent[xcopy]) = (self.parent[xcopy], x)
		return x

	def union(self, x, y):
		(a, b) = (self.find(x), self.find(y))
		if a != b:
			if self.size[a] < self.size[b]:
				(a, b) = (b, a)
			self.size[a] += self.size[b]
			self.parent[b] = a
			self.numsets -= 1

	def get_size(self, x):
		return self.size[self.find(x)]

	def __len__(self):
		return self.numsets
from collections import defaultdict, Counter
(n, m) = maps()
arr = [['0' * (4 - len(bin(i)[2:])) + bin(i)[2:] for i in maps()] for _ in range(n)]
dsu = DisjointSetUnion(n * m)
cnt = 0
S = 'NESW'
for i in range(n):
	for j in range(m):
		for k in range(4):
			if arr[i][j][k] == '0':
				if S[k] == 'N' and cnt - m >= 0:
					dsu.union(cnt, cnt - m)
				if S[k] == 'E':
					dsu.union(cnt, cnt + 1)
				if S[k] == 'S':
					dsu.union(cnt, cnt + m)
				if S[k] == 'W':
					dsu.union(cnt, cnt - 1)
		cnt += 1
d = defaultdict(int)
ans = []
for i in range(n * m):
	if dsu.find(i) == i:
		ans.append(dsu.get_size(i))
print(*sorted(ans)[::-1], '')


import sys
from collections import deque

def solve():
	visited = [[False] * M for i in range(N)]
	sizes = []
	for r in range(N):
		for c in range(M):
			if not visited[r][c]:
				size = bfs((r, c), A, visited)
				sizes.append(size)
	return sorted(sizes, reverse=True)

def bfs(node, grid, visited):
	q = deque()
	q.append(node)
	size = 0
	while q:
		(row, col) = q.popleft()
		if visited[row][col]:
			continue
		visited[row][col] = True
		size += 1
		walls = [grid[row][col] & 1 << 3 - i for i in range(4)]
		for (wall, offset) in zip(walls, ((-1, 0), (0, 1), (1, 0), (0, -1))):
			r = row + offset[0]
			c = col + offset[1]
			if not wall and (not visited[r][c]):
				assert 0 <= r < N and 0 <= c < M
				q.append((r, c))
	return size
input = sys.stdin.buffer.readline
(N, M) = map(int, input().split())
A = []
for _ in range(N):
	A.append(list(map(int, input().split())))
print(*solve(), end=' ')


import sys
fast = sys.stdin.readline
(n, m) = map(int, fast().split())
grid = []
for _ in range(n):
	grid.append(list(map(int, fast().split())))

def dfs(i, j):
	if i >= 0 and j >= 0 and (i < n) and (j < m) and (grid[i][j] != -1):
		t = grid[i][j]
		grid[i][j] = -1
		ans = 0
		for v in [[1, [0, -1]], [2, [1, 0]], [4, [0, 1]], [8, [-1, -0]]]:
			if v[0] & t == 0:
				ans += dfs(i + v[1][0], j + v[1][1])
		return ans + 1
	else:
		return 0
rs = []
for i in range(n):
	for j in range(m):
		if grid[i][j] != -1:
			rs.append(dfs(i, j))
rs.sort(reverse=True)
for r in rs:
	print(r, end=' ')


from sys import *
input = stdin.readline
import threading
from bisect import *
import math
from collections import *
import operator
from heapq import *
from itertools import *
inf = 10 ** 19
mod = 10 ** 9 + 7
MOD = 998244353
S1 = 'abcdefghijklmnopqrstuvwxyz'
S2 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
dx = [-1, 0]
dy = [0, -1]
setrecursionlimit(200005)

def asc(c):
	return ord(c) - 97 + 1
(n, m) = map(int, input().split())
grid = [list(map(int, input().split())) for _ in range(n)]
visited = [[False] * m for _ in range(n)]
rooms = []
for i in range(n):
	for j in range(m):
		if visited[i][j]:
			continue
		room_size = 0
		stack = [(i, j)]
		while stack:
			(x, y) = stack.pop()
			if visited[x][y]:
				continue
			visited[x][y] = True
			room_size += 1
			if x > 0 and (not (grid[x][y] & 8 or grid[x - 1][y] & 2)):
				stack.append((x - 1, y))
			if y < m and (not (grid[x][y] & 4 or grid[x][y + 1] & 1)):
				stack.append((x, y + 1))
			if x < n and (not (grid[x][y] & 2 or grid[x + 1][y] & 8)):
				stack.append((x + 1, y))
			if y > 0 and (not (grid[x][y] & 1 or grid[x][y - 1] & 4)):
				stack.append((x, y - 1))
		rooms.append(room_size)
rooms.sort(reverse=True)
print(' '.join(map(str, rooms)) + ' ')