#include <iostream>
#include <vector>
#include <string>
using namespace std;
//게임판을 채울 수 있는 도형의 네 가지 모습
int coverType[4][3][2] = {
{{0,0},{1,0},{0,1}},
{{0,0},{0,1},{1,1}},
{{0,0},{1,0},{1,1}},
{{0,0},{1,0},{1,-1}}
};
//게임판의 x,y 위치에서
//delta가 1이면 게임판을 type번 도형 모습으로 덮는다
//delta가 -1이면 게임판의 type번 도형을 없앤다
bool set(vector<vector<int>>& board, int y, int x, int type, int delta) {
bool ok = true;
for (int i = 0; i < 3; i++) {
//x,y를 기준으로 type번째 도형의 모습
int ny = y + coverType[type][i][0];
int nx = x + coverType[type][i][1];
//도형이 게임판 밖으로 나가거나 다른 도형과 겹치는지 확인
if (ny < 0 || ny >= board.size() || nx < 0 || nx >= board[0].size())
ok = false;
//delta가 -1인 경우
else if ((board[ny][nx] += delta) > 1)
ok = false;
}
return ok;
}
int cover(vector<vector<int>>& board) {
int y = -1;
int x = -1;
//도형의 맨 윗줄 왼쪽을 기준으로 가장 먼저 보이는 흰칸을 찾는다
for (int i = 0; i < board.size(); i++) {
for (int j = 0; j < board[i].size(); j++) {
if (board[i][j] == 0) {
y = i;
x = j;
break;
}
}
if (y != -1) break;
}
//모든 칸을 채운 경우
if (y == -1) return 1;
int ret = 0;
for (int type = 0; type < 4; ++type) {
//board(y,x)를 type번 도형으로 덮을 수 있으면 재귀 호출
if (set(board, y, x, type, 1))
ret += cover(board);
//덮었던 블록을 치운다
set(board, y, x, type, -1);
}
return ret;
}
int main()
{
int C;
cin >> C;
while (C--)
{
int y, x;
cin >> y >> x;
vector<vector<int>> board(y, vector<int>(x, 0));
string input;
for (int i = 0; i < y; i++)
{
cin >> input;
for (int j = 0; j < input.length(); j++)
{
if (input[j] == '#')
board[i][j] = 1;
}
}
cout << cover(board) << endl;
}
}
I2luY2x1ZGUgPGlvc3RyZWFtPgojaW5jbHVkZSA8dmVjdG9yPgojaW5jbHVkZSA8c3RyaW5nPgoKdXNpbmcgbmFtZXNwYWNlIHN0ZDsKCi8v6rKM7J6E7YyQ7J2EIOyxhOyauCDsiJgg7J6I64qUIOuPhO2YleydmCDrhKQg6rCA7KeAIOuqqOyKtQppbnQgY292ZXJUeXBlWzRdWzNdWzJdID0gewogICAge3swLDB9LHsxLDB9LHswLDF9fSwKICAgIHt7MCwwfSx7MCwxfSx7MSwxfX0sCiAgICB7ezAsMH0sezEsMH0sezEsMX19LAogICAge3swLDB9LHsxLDB9LHsxLC0xfX0KfTsKCi8v6rKM7J6E7YyQ7J2YIHgseSDsnITsuZjsl5DshJwKLy9kZWx0YeqwgCAx7J2066m0IOqyjOyehO2MkOydhCB0eXBl67KIIOuPhO2YlSDrqqjsirXsnLzroZwg642u64qU64ukCi8vZGVsdGHqsIAgLTHsnbTrqbQg6rKM7J6E7YyQ7J2YIHR5cGXrsogg64+E7ZiV7J2EIOyXhuyVpOuLpApib29sIHNldCh2ZWN0b3I8dmVjdG9yPGludD4+JiBib2FyZCwgaW50IHksIGludCB4LCBpbnQgdHlwZSwgaW50IGRlbHRhKSB7CiAgICBib29sIG9rID0gdHJ1ZTsKICAgIGZvciAoaW50IGkgPSAwOyBpIDwgMzsgaSsrKSB7CiAgICAgICAgLy94LHnrpbwg6riw7KSA7Jy866GcIHR5cGXrsojsp7gg64+E7ZiV7J2YIOuqqOyKtQogICAgICAgIGludCBueSA9IHkgKyBjb3ZlclR5cGVbdHlwZV1baV1bMF07CiAgICAgICAgaW50IG54ID0geCArIGNvdmVyVHlwZVt0eXBlXVtpXVsxXTsKICAgICAgICAvL+uPhO2YleydtCDqsozsnoTtjJAg67CW7Jy866GcIOuCmOqwgOqxsOuCmCDri6Trpbgg64+E7ZiV6rO8IOqyuey5mOuKlOyngCDtmZXsnbgKICAgICAgICBpZiAobnkgPCAwIHx8IG55ID49IGJvYXJkLnNpemUoKSB8fCBueCA8IDAgfHwgbnggPj0gYm9hcmRbMF0uc2l6ZSgpKQogICAgICAgICAgICBvayA9IGZhbHNlOwogICAgICAgIC8vZGVsdGHqsIAgLTHsnbgg6rK97JqwCiAgICAgICAgZWxzZSBpZiAoKGJvYXJkW255XVtueF0gKz0gZGVsdGEpID4gMSkKICAgICAgICAgICAgb2sgPSBmYWxzZTsKICAgIH0KICAgIHJldHVybiBvazsKfQoKaW50IGNvdmVyKHZlY3Rvcjx2ZWN0b3I8aW50Pj4mIGJvYXJkKSB7CiAgICBpbnQgeSA9IC0xOwogICAgaW50IHggPSAtMTsKICAgIC8v64+E7ZiV7J2YIOunqCDsnJfspIQg7Jm87Kq97J2EIOq4sOykgOycvOuhnCDqsIDsnqUg66i87KCAIOuztOydtOuKlCDtnbDsubjsnYQg7LC+64qU64ukCiAgICBmb3IgKGludCBpID0gMDsgaSA8IGJvYXJkLnNpemUoKTsgaSsrKSB7CiAgICAgICAgZm9yIChpbnQgaiA9IDA7IGogPCBib2FyZFtpXS5zaXplKCk7IGorKykgewogICAgICAgICAgICBpZiAoYm9hcmRbaV1bal0gPT0gMCkgewogICAgICAgICAgICAgICAgeSA9IGk7CiAgICAgICAgICAgICAgICB4ID0gajsKICAgICAgICAgICAgICAgIGJyZWFrOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIGlmICh5ICE9IC0xKSBicmVhazsKICAgIH0KCiAgICAvL+uqqOuToCDsubjsnYQg7LGE7Jq0IOqyveyasAogICAgaWYgKHkgPT0gLTEpIHJldHVybiAxOwogICAgaW50IHJldCA9IDA7CiAgICBmb3IgKGludCB0eXBlID0gMDsgdHlwZSA8IDQ7ICsrdHlwZSkgewogICAgICAgIC8vYm9hcmQoeSx4KeulvCB0eXBl67KIIOuPhO2YleycvOuhnCDrja7snYQg7IiYIOyeiOycvOuptCDsnqzqt4Ag7Zi47LacCiAgICAgICAgaWYgKHNldChib2FyZCwgeSwgeCwgdHlwZSwgMSkpCiAgICAgICAgICAgIHJldCArPSBjb3Zlcihib2FyZCk7CiAgICAgICAgLy/rja7sl4jrjZgg67iU66Gd7J2EIOy5mOyatOuLpAogICAgICAgIHNldChib2FyZCwgeSwgeCwgdHlwZSwgLTEpOwogICAgfQogICAgcmV0dXJuIHJldDsKfQoKaW50IG1haW4oKSAKewogICAgaW50IEM7CiAgICBjaW4gPj4gQzsKCiAgICB3aGlsZSAoQy0tKSAKICAgIHsKICAgICAgICBpbnQgeSwgeDsKICAgICAgICBjaW4gPj4geSA+PiB4OwogICAgICAgIHZlY3Rvcjx2ZWN0b3I8aW50Pj4gYm9hcmQoeSwgdmVjdG9yPGludD4oeCwgMCkpOwogICAgICAgIHN0cmluZyBpbnB1dDsKCiAgICAgICAgZm9yIChpbnQgaSA9IDA7IGkgPCB5OyBpKyspIAogICAgICAgIHsKICAgICAgICAgICAgY2luID4+IGlucHV0OwogICAgICAgICAgICBmb3IgKGludCBqID0gMDsgaiA8IGlucHV0Lmxlbmd0aCgpOyBqKyspIAogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBpZiAoaW5wdXRbal0gPT0gJyMnKQogICAgICAgICAgICAgICAgICAgIGJvYXJkW2ldW2pdID0gMTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICBjb3V0IDw8IGNvdmVyKGJvYXJkKSA8PCBlbmRsOwogICAgfQp9