//Konrad Paluszek,University of Warsaw(former XIV LO Staszic)
//it's nice to stick a fork into an open socket
//clock says it's time to sleep
//our getppid() in heaven, hallowed by ttyname
#ifndef LOCAL
#pragma GCC optimize("O3")
#endif
#define TIME (chrono::steady_clock::now().time_since_epoch().count())
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
#define xfm(a,b)a##b
#define xwb(a,b)xfm(a,b)
#define _ xwb(nvj,__LINE__)
#define FOR(i,b,e)for(urs((b)+(e))i=(b);i<=(e);++i)
#define REP(i,n)for(urs(n)i=0;i<(n);++i)
#define FORD(i,b,e)for(urs((b)+(e))i=(b);i>=(int)(e);--i)
#define UNTIL(t)while(clock()<(t)*CLOCKS_PER_SEC)
#define PER(r...)for(bool _=1;_||next_permutation(r);_=false)
#define ALL(r)(r).begin(),(r).end()
#define FS(r)r.first,r.second
#define sim template<class c
#define ros return o
#define rans return ans
#define forbits(i,m)if(m)for(urs(m)i=ctz(m),i##nvj=m;i##nvj;i##nvj^=((urs(m))1<<i),i=ctz(i##nvj))
#define fordbits(i,m)if(m)for(urs(m)i=8*sizeof(m)-clz(m)-1,i##nxd=m;i##nxd;i##nxd^=((urs(m))1<<i),i=8*sizeof(m)-clz(i##nxd)-1)
#define ksets(t, m, k, n) for (t m = (((t)1 << (k)) - 1); m < ((t)1 << (n)); m = nux(m))
#define urs(r...)typename decay<decltype(r)>::type
#define hur(f,g,r)sim>int f(c a){if(sizeof(c)==16)return r;if(sizeof(c)==8)return g##ll(a);return g(a);}
#define pwq(t,i) int clz(t x){return clz<int>(x)-i;}
using namespace __gnu_pbds;using namespace std;using ll=long long;using ld=long double;using ull=unsigned long long;using pii=pair<int,int>;using pll=pair<ll,ll>;using vpii=vector<pii>;using unt=unsigned int;sim>using min_queue=priority_queue<c,vector<c>,greater<c>>;sim>using ordered_set=tree<c,null_type,less<c>,rb_tree_tag,tree_order_statistics_node_update>;hur(popc,__builtin_popcount,popc<ull>(a)+popc<ull>(a>>64))hur(ctz,__builtin_ctz,(ull)a?ctz<ull>(a):64+ctz<ull>(a>>64))hur(clz,__builtin_clz,a>>64?clz<ull>(a>>64):64+clz<ull>(a))pwq(short,16)pwq(uint16_t,16)pwq(char,24)pwq(int8_t,24)pwq(uint8_t,24)sim,class N>bool mini(c&o,const N&h){if(o>h)ros=h,1;return 0;}sim,class N>bool maxi(c&o,const N&h){if(o<h)ros=h,1;return 0;}sim,class n>using gyv=c;
#ifdef LOCAL
#include </home/kjp/headers/debuglib.hpp>
#else
#define loc(...)
#define onl(r...)r
#define debug(...)
#define print_stack(...)
#define mark_stack(...)
#define set_pre(...)
#define reg_it(...)
#endif
#define next nexT
#define prev preV
#define tree trEE
#define left lefT
#define right righT
#define div diV
#define y1 y_1
ull mix(ull o){o+=0x9e3779b97f4a7c15;o=(o^(o>>30))*0xbf58476d1ce4e5b9;o=(o^(o>>27))*0x94d049bb133111eb;ros^(o>>31);}ull SALT=0x7a14a4b0881ebf9,tqu=0x7a14a4b0881ebf9;ull my_rand(){return tqu=mix(tqu);}void my_srand(ull x){SALT=tqu=x;}const int inf = 1023400000;const ll llinf=1234567890000000000ll;
#define rand my_rand
#define srand my_srand
#define random_shuffle(r...)random_shuffle(r,[](int _){return my_rand()%_;})
sim>inline c nux(c m){if(!m)return numeric_limits<c>::max();c A=m&-m;c B=~((A-1)^m);c C=B&-B;c D=(C>>(1+ctz(A)))-1;return C|(m&~(C-1))|D;}__attribute__((no_sanitize_undefined))ll mul(ll a,ll b,ll m){ll q=a*(ld)b/m;ll o=a*b-q*m;o%=m;if(o<0)o+=m;ros;}sim>void unq(c&x){x.resize(unique(ALL(x))-x.begin());}
#if ((ULONG_MAX) != (UINT_MAX))
namespace std {template <> struct is_signed<__int128> : public true_type {};}
#endif
#define q31 (8 * sizeof(c) - 1)
sim> typename enable_if<is_signed<c>::value, c>::type FastMax(c x, c y) { return ((x - y) >> q31 & (x ^ y)) ^ x; }
sim> typename enable_if<is_signed<c>::value, c>::type FastMin(c x, c y) { return ((x - y) >> q31 & (x ^ y)) ^ y; }
sim> typename enable_if<is_signed<c>::value, c>::type FastAbs(c x)        { return (x ^ x >> q31) - (x >> q31); }
sim> typename enable_if<is_signed<c>::value, c>::type gcd(c a, c b) {
	loc(assert(a > 0 && b > 0));
	int aa = ctz(a), bb = ctz(b);
	int r = FastMin(aa, bb);
	a >>= aa;
	while (a && b) {
		b >>= ctz(b);
		c x = FastAbs(a - b);
		a = FastMin(a, b);
		b = x;
	}
	return (a + b) << r;
}
sim> c lcm(c a, c b) {return a / gcd(a, b) * b;}
sim>typename enable_if<is_integral<c>::value,ull>::type my_hash(c x){
	if(sizeof(c)>8) return mix((ull)x^SALT)^mix((ull)(x>>64)^SALT);
	return mix(x^SALT);
}
template<size_t N>ull my_hash(const bitset<N>&x){
	return mix(hash<bitset<N>>()(x)^SALT);
}
sim,class n>ull my_hash(const pair<c,n>&);
sim>gyv<ull,typename c::value_type>my_hash(const c&x){
	ull o=SALT;
	for(auto&d:x)o=mix(o^my_hash(d));
	ros;
}
sim,class n>ull my_hash(const pair<c,n>&x){return mix(my_hash(x.first))^my_hash(x.second);}
sim>struct my_hasher{size_t operator()(const c&o)const{return my_hash(o);}};
sim, class m>
#ifdef LOCAL
using _un0rDer3d_M4P=unordered_map<c,m,my_hasher<c>>;
sim> using _un0rDer3d_5et = unordered_set<c, my_hasher<c>>;
#else
struct _un0rDer3d_M4P : public cc_hash_table<c,m,my_hasher<c>>{bool count(const c&x)const{return this->find(x) != this->end();}};
sim> using _un0rDer3d_5et = _un0rDer3d_M4P<c, null_type>;
#endif
#define unordered_map _un0rDer3d_M4P
#define unordered_set _un0rDer3d_5et
#if defined(LOCAL) || defined(LOCAL2)
void __tmi() {cerr << "total time: " << clock() / (ld)CLOCKS_PER_SEC << "s" << endl;}int _ = (atexit(__tmi), 0);
#endif
const int mod = 119 << 23 | 1, nax = 21;
int ans[2][nax][nax];
int dp[nax][nax][16];
int cho[nax][nax];
void calc_dp() {
	REP(type, 2) {
		memset(dp, 0, sizeof(dp));
		dp[0][0][0] = 1;
		if (type == 0) {
			FOR(i, 1, nax - 1)
				dp[i][0][12] = 1;
			FOR(i, 1, nax - 1)
				dp[0][i][3] = 1;
		}
		else {
			FOR(i, 1, nax - 1) {
				dp[i][0][12] = (1 << i) - 2;
				dp[i][0][4] = 1;
				dp[i][0][8] = 1;
				dp[0][i][3] = (1 << i) - 2;
				dp[0][i][2] = 1;
				dp[0][i][1] = 1;
			}
		}
		FOR(h, 1, nax - 1) FOR(w, 1, nax - 1) {
			FOR(m, 1, 15) {
				bool wc = (m >> 0 & 1), bc = (m >> 1 & 1), wr = (m >> 2 & 1), br = (m >> 3 & 1);
				if (wc && br) continue;
				if (bc && wr) continue;
				// if (h == 1 && w == 1) debug(imie(m), imask(m));
				int poss = 0;
				if (wc) poss |= 1 << 3;
				if (bc) poss |= 1 << 2;
				if (wr) poss |= 1 << 1;
				if (br) poss |= 1 << 0;
				// debug(imask(m), imask(poss));
				FOR(wcs, wc ? 1 : 0, wc ? w : 0) FOR(bcs, bc ? 1 : 0, bc ? w : 0) if (bcs + wcs <= w)
					FOR(wrs, wr ? 1 : 0, wr ? h : 0) FOR(brs, br ? 1 : 0, br ? h : 0) if (wrs + brs <= h) {
						int ways = cho[w][wcs] * 1ll *
							cho[w - wcs][bcs] % mod *
							cho[h][wrs] % mod *
							cho[h - wrs][brs] % mod;
						assert(ways);
						int mul = 0;
						for (int sub = poss; ; sub = (sub - 1) & poss) {
							// debug(arr(dp, h - wrs - brs, w - wcs - bcs, imask(sub)));
							mul += dp[h - wrs - brs][w - wcs - bcs][sub];
							if (mul >= mod) mul -= mod;
							if (!sub) break;
						}
						// if (ways || mul) debug(imask(m), imie(mul), imie(ways), imie(wcs), imie(bcs), imie(wrs), imie(brs));
						dp[h][w][m] = (dp[h][w][m] + ways * 1ll * mul) % mod;
					}
				// if (dp[h][w][m])
					// debug(arr(dp, h, w, imask(m)));
				ans[type][h][w] += dp[h][w][m];
				if (ans[type][h][w] >= mod) ans[type][h][w] -= mod;
			}
			// debug(arr(ans, type, h, w));
		}
	}
	ans[1][0][0] = 1;
	FOR(i, 1, nax - 1) ans[1][0][i] = ans[1][i][0] = (1 << i);
}
void test() {
#define check(i, j, v) if (i < nax && j < nax && ans[0][i][j] != v) debug(arr(ans[0], i, j), "expected", v);
	check(20, 1,1048576);
	check(19, 1,524288);
	check(18, 1,262144);
	check(17, 1,131072);
	check(16, 1,65536);
	check(15, 1,32768);
	check(14, 1,16384);
	check(13, 1,8192);
	check(12, 1,4096);
	check(11, 1,2048);
	check(10, 2,117074);
	check(10, 1,1024);
	check(9, 2,38854);
	check(9, 1,512);
	check(8, 2,12866);
	check(8, 1,256);
	check(7, 2,4246);
	check(7, 1,128);
	check(6, 3,20266);
	check(6, 2,1394);
	check(6, 1,64);
	check(5, 4,41506);
	check(5, 3,4718);
	check(5, 2,454);
	check(5, 1,32);
	check(4, 5,41506);
	check(4, 4,6902);
	check(4, 3,1066);
	check(4, 2,146);
	check(4, 1,16);
	check(3, 6,20266);
	check(3, 5,4718);
	check(3, 4,1066);
	check(3, 3,230);
	check(3, 2,46);
	check(3, 1,8);
	check(2, 10,117074);
	check(2, 9,38854);
	check(2, 8,12866);
	check(2, 7,4246);
	check(2, 6,1394);
	check(2, 5,454);
	check(2, 4,146);
	check(2, 3,46);
	check(2, 2,14);
	check(2, 1,4);
	check(1, 20,1048576);
	check(1, 19,524288);
	check(1, 18,262144);
	check(1, 17,131072);
	check(1, 16,65536);
	check(1, 15,32768);
	check(1, 14,16384);
	check(1, 13,8192);
	check(1, 12,4096);
	check(1, 11,2048);
	check(1, 10,1024);
	check(1, 9,512);
	check(1, 8,256);
	check(1, 7,128);
	check(1, 6,64);
	check(1, 5,32);
	check(1, 4,16);
	check(1, 3,8);
	check(1, 2,4);
	check(1, 1,2);
	check(7, 3,85310);
	check(8, 3,354106);
	check(9, 3,1455278);
	check(5, 5,329462);
	check(6, 5,2441314);
	check(6, 4,237686);
	check(4, 6,237686);
}
char board[nax][nax];
int main() {
	REP(i, nax) {
		cho[i][i] = cho[i][0] = 1;
		FOR(j, 1, i - 1) {
			cho[i][j] = cho[i - 1][j] + cho[i - 1][j - 1];
			if (cho[i][j] >= mod) cho[i][j] -= mod;
		}
	}
	calc_dp();
	test();
	int h, w;
	scanf("%d%d", &h, &w);
	for (int i = 0; i < h; ++i) scanf("%s", board[i]);
	ll out = ans[0][h][w];
	debug(arr(ans, 0, h, w));
	REP(rows, (1 << h))
		REP(cols, (1 << w)) {
			if (rows == 0 || cols == 0) continue;
			bool good = true;
			forbits(i, rows) {
				bool H = false, D = false;
				forbits(j, cols) {
					if (board[i][j] == '#') H = true;
					else D = true;
				}
				if (!H || !D) good = false;
			}
			forbits(j, cols) {
				bool H = false, D = false;
				forbits(i, rows) {
					if (board[i][j] == '#') H = true;
					else D = true;
				}
				if (!H || !D) good = false;
			}
			if (good) {
				debug(imask(rows), imask(cols), imie(good), arr(ans, 1, h - popc(rows), w - popc(cols)));
				out += ans[1][h - popc(rows)][w - popc(cols)];
			}
		}
	out %= mod;
	if (out < 0) out += mod;
	printf("%lld\n", out);
}


Measured memory_bytes: 256