Problem:
Keima
You are given a set of points aligned in a grid of
N
rows and
M
columns.
Each of the points is named
P
i
,
j
with its row number
i
(1 ≤
i
≤
N
)
and its column number
j
(1 ≤
j
≤
M
).
Each point is colored either white or black.
Your task is to compute the number of subsets
T
of white points
satisfying the following conditions:
∀
i
∈ {3, 4,...,
N
},
		∀
j
∈ {2, 3,...,
M
},
P
i
,
j
∈
T
⇒
P
i
-2,
j
-1
∉
T
,
∀
i
∈ {3, 4,...,
N
},
		∀
j
∈ {1, 2,...,
M
−1},
P
i
,
j
∈
T
⇒
P
i
-2,
j
+1
∉
T
.
For example, in the figure below, if you include the point marked with a star to the set
T
, you can include neither of the two crossed points.
If you are familiar with the game of
shogi
(Japanese chess),
you may find similarities between the possible moves of a
keima
with the relative positions of the star point and crossed points.
Because the answer can be huge, output the remainder divided by 10
9
+7.
Input
The input consists of multiple datasets, each in the following format.
N
M
a
1,1
...
a
1,
M
...
a
N,
1
...
a
N,M
The first line of each dataset contains two integers
N
(2 ≤
N
≤ 60) and
M
(2 ≤
M
≤ 60) representing the numbers of rows and columns of the grid, respectively.
Each of the following
N
lines contains
M
characters.
The
j
-th character of the
i
-th line,
a
i,j
,
represents the color of the point
P
i, j
, where '.' denotes white and '#' denotes black.
The end of the input is indicated by a line containing two zeros.
The number of datasets does not exceed 100.
Output
For each dataset, output one line containing the number of subsets satisfying the conditions modulo 10
9
+7.
Sample Input
3 2
.#
##
#.
3 3
.#.
#.#
..#
6 5
.#..#
...#.
#....
..#.#
#....
..#..
0 0
Output for the Sample Input
3
20
103320

Submitted solutions:

code_snippet: #include <bits/stdc++.h>
using namespace std;

template<int MOD>
struct modint {
    modint() : x(0) {}
    modint(long long v) {
        long long y = v % m();
        if (y < 0) y += m();
        x = (unsigned int)(y);
    }
    static modint raw(int v) {
        modint a;
        a.x = v;
        return a;
    }
    static constexpr int mod() { return m(); }
    unsigned int val() const { return x; }
    modint& operator++() {
        x++;
        if (x == m()) x = 0;
        return *this;
    }
    modint& operator--() {
        if (x == 0) x = m();
        x--;
        return *this;
    }
    modint operator++(int) {
        modint res = *this;
        ++*this;
        return res;
    }
    modint operator--(int) {
        modint res = *this;
        --*this;
        return res;
    }
    modint& operator+=(const modint &r) {
        x += r.x;
        if (x >= m()) x -= m();
        return *this;
    }
    modint& operator-=(const modint &r) {
        x -= r.x;
        if (x >= m()) x += m();
        return *this;
    }
    modint& operator*=(const modint &r) {
        unsigned long long y = x;
        y *= r.x;
        x = (unsigned int)(y % m());
        return *this;
    }
    modint &operator/=(const modint &r) {
        return *this = *this * r.inv();
    }
    friend modint operator+(const modint &a, const modint &b) {
        return modint(a) += b;
    }
    friend modint operator-(const modint &a, const modint &b) {
        return modint(a) -= b;
    }
    friend modint operator*(const modint &a, const modint &b) {
        return modint(a) *= b;
    }
    friend modint operator/(const modint &a, const modint &b) {
        return modint(a) /= b;
    }
    friend bool operator==(const modint &a, const modint &b) {
        return a.x == b.x;
    }
    friend bool operator!=(const modint &a, const modint &b) {
        return a.x != b.x;
    }
    modint operator+() const { return *this; }
    modint operator-() const { return modint() - *this; }
    modint pow(long long k) const {
        assert(k >= 0);
        modint a = *this;
        modint res = 1;
        while (k > 0) {
            if (k & 1) res *= a;
            a *= a;
            k >>= 1;
        }
        return res;
    }
    modint inv() const {
        long long a = x, b = m(), u = 1, v = 0;
        while (b > 0) {
            long long t = a / b;
            a -= t * b;
            swap(a, b);
            u -= t * v;
            swap(u, v);
        }
        return modint(u);
    }
private:
    unsigned int x;
    static constexpr unsigned int m() { return MOD; }
};
using mint = modint<int(1e9 + 7)>;

mint f(vector<string> S) {
    // cout << "case :\n";
    // for (auto s : S) cout << s << endl;
    int H = S.size(), W = S[0].size();
    int N = (H + 1) / 2, M = H / 2;
    vector<vector<int>> nxt1(1 << N), nxt2(1 << M);
    for (int i = 0; i < 1 << N; i++) {
        auto rec = [&] (auto rec, int j, int k) -> void {
            if (j == M) {
                nxt1[i].push_back(k);
                return;
            }
            rec(rec, j + 1, k);
            if (!(i >> j & 1) && !(i >> (j + 1) & 1)) {
                rec(rec, j + 1, k ^ (1 << j));
            }
        };
        rec(rec, 0, 0);
    }
    for (int j = 0; j < 1 << M; j++) {
        auto rec = [&] (auto rec, int i, int k) -> void {
            if (i == N) {
                nxt2[j].push_back(k);
                return;
            }
            rec(rec, i + 1, k);
            if (!(j >> i & 1) && !(j >> (i ? i - 1 : i) & 1)) {
                rec(rec, i + 1, k ^ (1 << i));
            }
        };
        rec(rec, 0, 0);
    }
    // cout << "nxt1 :\n";
    // for (int i = 0; i < 1 << N; i++) {
    //     cout << i << " :";
    //     for (int j : nxt1[i]) cout << ' ' << j;
    //     cout << endl;
    // }
    // cout << "nxt2 :\n";
    // for (int i = 0; i < 1 << M; i++) {
    //     cout << i << " :";
    //     for (int j : nxt2[i]) cout << ' ' << j;
    //     cout << endl;
    // }
    vector<mint> dp1(1 << N), dp2(1 << M);
    dp1[0] = dp2[0] = 1;
    for (int j = 0; j < W; j++) {
        vector<mint> ep1(1 << N), ep2(1 << M);
        int m1 = 0, m2 = 0;
        for (int i = 0; i < H; i++) {
            if (S[i][j] == '.') {
                (i % 2 == 0 ? m1 : m2) ^= 1 << (i / 2);
            }
        }
        for (int j = 0; j < 1 << N; j++) {
            for (int k : nxt1[j]) {
                if ((k & m2) == k) ep2[k] += dp1[j];
            }
        }
        for (int j = 0; j < 1 << M; j++) {
            for (int k : nxt2[j]) {
                if ((k & m1) == k) ep1[k] += dp2[j];
            }
        }
        dp1 = ep1, dp2 = ep2;
        // cout << "dp1 :";
        // for (auto x : dp1) cout << ' ' << x.val();
        // cout << endl;
        // cout << "dp2 :";
        // for (auto x : dp2) cout << ' ' << x.val();
        // cout << endl;
    }
    auto sum = [] (vector<mint> dp) -> mint {
        mint res = 0;
        for (auto x : dp) res += x;
        return res;
    };
    return sum(dp1) * sum(dp2);
}

bool solve() {
    int H, W;
    cin >> H >> W;
    if (H == 0 && W == 0) return false;
    vector<string> S, T;
    while (H--) {
        string s;
        cin >> s;
        S.push_back(s);
        swap(S, T);
    }
    cout << (f(S) * f(T)).val() << '\n';
    return true;
}

int main() {
    while (solve()) {}
}
accuracy: 1
time_ms: 4080
memory_kb: 23600
final_rank: 18

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ld = long double;
using ull = unsigned long long;

#define rep(i,n) for(ll i=0;i<n;++i)
#define all(a) (a).begin(),(a).end()
ll intpow(ll a, ll b){ ll ans = 1; while(b){ if(b & 1) ans *= a; a *= a; b /= 2; } return ans; }
ll modpow(ll a, ll b, ll p){ ll ans = 1; while(b){ if(b & 1) (ans *= a) %= p; (a *= a) %= p; b /= 2; } return ans; }
template<class T> T div_floor(T a, T b) { return a / b - ((a ^ b) < 0 && a % b); }
template<class T> T div_ceil(T a, T b) { return a / b + ((a ^ b) > 0 && a % b); }
template <typename T, typename U> inline bool chmin(T &x, U y) { return (y < x) ? (x = y, true) : false; }
template <typename T, typename U> inline bool chmax(T &x, U y) { return (x < y) ? (x = y, true) : false; }

template<typename T>
ostream &operator<<(ostream &os, const vector<T> &a){
	if (a.empty()) return os;
	os << a.front();
	for (auto e : a | views::drop(1)){
		os << ' ' << e;
	}
	return os;
}
void dump(auto... vs) {
	((cout << vs << ' '), ...) << endl;
}


void solve(ll N,ll M) {
	const ll Mod=1000000007;
	vector<string> S(N);
	rep(i,N)cin>>S[N-i-1];
	auto zeta=[&](vector<ll> &A,ll n){
		rep(i,n){
			rep(bi,1<<n){
				if (bi>>i&1)continue;
				ll nbi=bi^(1LL<<i);
				A[bi]+=A[nbi];
			}
		}
		rep(i,1<<n){
			A[i]%=Mod;
		}
		return;
	};
	ll ans=1;
	rep(st,4){
		vector<ll> dp(1,1);
		rep(j,M){
			ll b=0;
			ll len=0;
			ll low=st+2*j;
			low%=4;
			rep(i,N){
				if (low+4*i>=N)break;
				if (S[low+4*i][j]=='.')b|=1<<i;
				len++;
			}
			vector<ll> ndp(1<<len,0);
			// dump(ndp);
			// dump(b);
			rep(bi,(ll)dp.size()){
				// dump(bi);
				// dump(ndp);
				if (low<2){
					ndp[(~bi)&(~(bi<<1))&b]+=dp[bi];
				}
				else{
					ndp[(~bi)&(~(bi>>1))&b]+=dp[bi];
				}
			}
			rep(i,1<<len)ndp[i]%=Mod;
			zeta(ndp,len);
			rep(i,1<<len){
				if ((i&b)!=i){
					ndp[i]=0;
				}
			}
			// if (st==2){
			// 	dump(ndp);
			// }
			swap(dp,ndp);
		}
		ll sum=0;
		for (ll v:dp){
			sum+=v;
			sum%=Mod;
		}
		ans*=sum;
		ans%=Mod;
	}
	ans%=Mod;
	cout<<ans<<endl;
    return;
}


int main() {
    while (true){
		ll N,M;
		cin>>N>>M;
		if (N==0)break;
        solve(N,M);
    }
    return 0;
}
accuracy: 1
time_ms: 510
memory_kb: 3932
final_rank: 1

code_snippet: #pragma GCC target("avx2")
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")

// #ifndef ONLINE_JUDGE
// #define _GLIBCXX_DEBUG // 配列外参照のエラー
// #endif

// スタンダードライブラリ
#include <bits/stdc++.h>
using namespace std;

// 多倍長整数
//  #if __has_include(<boost/multiprecision/cpp_int.hpp>)
//  #include <boost/multiprecision/cpp_int.hpp>
//  using lll =boost::multiprecision::int128_t;
//  #endif

// atcoder関連
#if __has_include(<atcoder/all>)
#include <atcoder/all>
using namespace atcoder;
using mint = modint998244353;
using MINT = modint1000000007;
#endif

// 型関連
using ll = long long;
using lint = long long;
using pll = pair<ll, ll>;
using plll = tuple<ll, ll, ll>;
using pii = pair<int, int>;
using piii = tuple<ll, ll, ll>;
template <class T> using prique = priority_queue<T>;

// 型定数
constexpr ll mod = 998244353;
constexpr ll MOD = 1000000007;
int inf = 2e9;
ll INF = 9e18;
#define endl '\n';

// 新規マクロ
#define all(a) (a).begin(), (a).end()
#define rep(i, N, limit) for (int i = (int)N; i < (int)limit; i++)

///////////////////// vector関連
template <class T, size_t n, size_t idx = 0>
auto make_vec(const size_t (&d)[n], const T &init) noexcept {
    if constexpr (idx < n)
        return std::vector(d[idx], make_vec<T, n, idx + 1>(d, init));
    else
        return init;
}

template <class T, size_t n> auto make_vec(const size_t (&d)[n]) noexcept {
    return make_vec(d, T{});
}

int dx[4] = {1, 0, -1, 0};
int dy[4] = {0, 1, 0, -1};

/////////////////////////// print関連
template <typename T> void print(vector<T> A);
void print();
template <class Head, class... Tail> void print(Head &&head, Tail &&...tail);
void print(string S);
template <typename T> void print(vector<vector<T>> &F);

////////////////////////////
inline string input();
inline ll I();
inline pll II();

/////////////////////出力関連
inline void Yes(bool f);

ll solve3(vector<ll> &B, int B_size, int o) {
    int W = B.size();
    vector<vector<ll>> dp(W + 1, vector<ll>(1 << B_size, 0));

    auto add = [&](int i, int j, ll val) {
        dp[i][j] += val;
        dp[i][j] %= MOD;
    };

    dp[0][0] = 1;
    int j_max = (1 << B_size) - 1;
    for (int i = 0; i < W; i++) {
        for (int j = 0; j <= j_max; j++) {
            ll val = dp[i][j];
            int k = (j << 1);
            if ((o + i) % 2==0) {
                k= ( j>>1) ;
            }

            int ok = j_max & ( j_max ^ (j | k | B[i]));
            int l = ok;
            while (l > 0) {
                add(i + 1, l, val);
                l = (l - 1) & ok;
            }
            add(i + 1, 0, val);
        }
    }
    ll ans = 0;
    for (int i = 0; i <= j_max; i++) {
        ans += dp[W][i];
        ans%=MOD;
    }
    return ans;
}

ll solve2(vector<string> &F) {
    int h = F.size(); // 定義されているHとは異なる（Hの半分）
    int W = F[0].size();

    vector<ll> B1, B2;
    int B1_size = (h + 1) / 2, B2_size = (h + 1) / 2;
    for (int j = 0; j < W; j++) {
        ll b1 = 0LL, b2 = 0LL;
        for (int i = 0; i < h; i++) {
            ll k = i / 2;
            ll f = ll(F[i][j] == '#');
            if ((i + j) % 2 == 0) {
                b1 += (1LL << k) * f;
            } else {
                b2 += (1LL << k) * f;
            }

            if (i == h - 1) {
                if (j % 2 && h % 2) {
                    b1+=(1LL<<k);
                }
                if (j % 2 == 0 && h % 2) {
                    b2+=(1LL<<k);
                }
            }
        }
        int p1 = 1, p2 = 1;
        // if (h == 1) {
        //     if (j % 2) {
        //         p1=0;
        //     } else {
        //         p2=0;
        //     }
        // }
        if (p1)B1.push_back(b1);
        if (p2)B2.push_back(b2);
    }

    return solve3(B1, B1_size, 1) * solve3(B2, B2_size, 0) % MOD;
}

bool solve() {
    int H, W;
    cin >> H >> W;
    if (H == 0) {
        return false;
    }
    vector<string> F1, F2;
    for (int i = 0; i < H; i++) {
        string S;
        cin >> S;
        if (i % 2 == 0) {
            F1.push_back(S);
        } else {
            F2.push_back(S);
        }
    }

    print(solve2(F1) * solve2(F2) % MOD);

    return true;
}

///////////////////ここから//////////////////////
int main() {
    ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    while (solve())
        ;
}

////////////////////print/////////////////////////
// 1次元ベクトルを出力する
template <typename T> void print(vector<T> A) {
    if (A.size() == 0) {
        return;
    }
    for (size_t i = 0; i < A.size() - 1; i++) {
        cout << A[i] << ' ';
    }
    cout << A[A.size() - 1] << endl;
    return;
}

// 2次元ベクトルを出力する
template <typename T> void print(vector<vector<T>> &F) {
    for (size_t i = 0; i < F.size(); i++) {
        for (size_t j = 0; j < F[i].size(); j++) {
            cout << F[i][j];
            if (j < F[i].size() - 1) {
                cout << ' ';
            }
        }
        cout << endl;
    }
}

// 空白行を出力するprint
void print() { cout << endl; }

// 数値・文字列を空白区切りで出力する. print(a,b)など
template <class Head, class... Tail> void print(Head &&head, Tail &&...tail) {
    cout << head;
    if (sizeof...(tail) != 0)
        cout << " ";
    print(forward<Tail>(tail)...);
}

void print(string S) { cout << S << endl; }

/////////////////////初期化///////////////////////

inline string input() {
    string S;
    cin >> S;
    return S;
}
inline ll I() {
    ll ret;
    cin >> ret;
    return ret;
}
inline pll II() {
    pll ret;
    cin >> ret.first >> ret.second;
    return ret;
}

//////////////出力関連

inline void Yes(bool f) {
    if (f) {
        cout << "Yes" << endl;
    } else {
        cout << "No" << endl;
    }
}
accuracy: 1
time_ms: 2330
memory_kb: 19456
final_rank: 12

code_snippet: // #pragma GCC target("avx2")
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#include <bits/stdc++.h>
using namespace std;
#include <atcoder/all> 
using namespace atcoder;

// 数値型
using mint = modint1000000007;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
using P = pair<int,int>;
using Pll = pair<ll, ll>;
using Pli = pair<ll, int>;
using Pil = pair<int, ll>;

// vector関連
using vi = vector<int>;
using vvi = vector<vi>;
using vvvi = vector<vvi>;
using vll = vector<ll>;
using vvll = vector<vll>;
using vvvll = vector<vvll>;
template<typename T>
using vc = vector<T>;
template<typename T>
using vvc = vector<vc<T>>;
template<typename T>
using vvvc = vector<vvc<T>>;
template<typename T>
using vvvvc = vector<vvvc<T>>;

// priority_queue
template<typename T>
using pq = priority_queue<T>;
template<typename T>
using pqg = priority_queue<T, vc<T>, greater<T>>;

#define rep(i, n) for(int i = 0; i < (int)(n); i++)
#define FOR(i, a, b) for(int i = a; i < (int)(b); i++)
#define all(a) (a).begin(),(a).end()
#define rall(a) (a).rbegin(),(a).rend()
#define MIN(vec) *min_element(all(vec))
#define MAX(vec) *max_element(all(vec))
#define next_perm(vec) (vec).begin(), (vec).end()
#define UNIQUE(vec) vec.erase(unique(vec.begin(), vec.end()), vec.end())
#define el "\n"
#define Yes cout << "Yes" << el
#define No cout << "No" << el
#define YES cout << "YES" << el
#define NO cout << "NO" << el
#define EPS 1e-8
#define Equal(a, b) (fabs((a)-(b)) < EPS) 
#ifdef ONLINE_JUDGE
#define dbg(x) (void)0
#else
#define dbg(x) cerr << #x << "=" << x << el 
#endif
// 定数
const string abc = "abcdefghijklmnopqrstuvwxyz";
const string ABC = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
constexpr int INF = 1001001001;
constexpr ll LINF = 1001001001001001001ll;
constexpr int DX[] = {1, 0, -1, 0};
constexpr int DY[] = {0, 1, 0, -1};
constexpr int DX8[] = {1, 0, -1, 0, 1, 1, -1, -1};
constexpr int DY8[] = {0, 1, 0, -1, 1, -1, 1, -1};

template<typename T1, typename T2>
ostream &operator<< (ostream &os, pair<T1, T2> p) {
    os << "{" << p.first << "," << p.second << "}";
    return os;
}
template<typename T>
ostream &operator<< (ostream &os, vc<T> &vec) {
    int sz = vec.size();
    rep(i, sz){
        os << vec[i] << (i==sz-1?"":" ");
    }
    return os;
}

ostream &operator<< (ostream &os, mint &a) {
    os << a.val();
    return os;
}

istream &operator>> (istream &is, mint &a) {
    ll x;
    is >> x;
    a = x;
    return is;
}
template<typename T1, typename T2>
istream &operator>> (istream &is, pair<T1, T2> &p) {
    is >> p.first >> p.second;
    return is;
}
template<typename T>
istream &operator>> (istream &is, vc<T> &vec) {
    int sz = vec.size();
    rep(i, sz) { is >> vec[i]; }
    return is;
}
/// @brief aとｂの最大値をaに格納。更新があったかbool値を返す
/// @tparam T1 
/// @tparam T2 
/// @param a 
/// @param b 
/// @return bool
template<typename T1, typename T2>
inline bool chmax(T1 &a, T2 b){
    bool ret = a<b;
    if(ret) a = b;
    return ret;
}

/// @brief aとｂの最小値をaに格納。更新があったかbool値を返す
/// @tparam T1 
/// @tparam T2 
/// @param a 
/// @param b 
/// @return bool
template<typename T1, typename T2>
inline bool chmin(T1 &a, T2 b){
    bool ret = a>b;
    if(ret) {a = b;}
    return ret;
}

inline void YesNo(bool flag){
    if(flag) {Yes;}
    else {No;}
    return;
}

inline void YESNO(bool flag){
    if(flag) {YES;}
    else {NO;}
    return;
}

inline bool outof(ll x, ll xlim){
    return (x<0 || x>=xlim);
}

template<typename T>
inline T sqnorm(T x, T y){
    return x*x+y*y;
}

/// @brief char->int
/// @param c 
/// @return int
inline int ctoi(char c){
    return c-'0';
}

/// @brief xを素因数分解
/// @param x 
/// @return vector<Pli>, 素因数の昇順に {p, cnt}
vector<Pli> prime_fact(ll x){
    vector<Pli> ret;
    for(ll i=2; i*i<=x; i++){
        if(x%i == 0){
            ret.emplace_back(i, 0);
            while(x%i == 0){
                ret.back().second++;
                x /= i;
            }
        }
    }
    if(x != 1) ret.emplace_back(x, 1);
    return ret;
}

/// @brief xの約数列挙
/// @param x 
/// @return vll, 約数の昇順
vll divisor_enum(ll x){
    vector<ll> ret;
    for(ll i=1; i*i<=x; i++){
        if(x%i == 0){
            ret.push_back(x/i);
            ret.push_back(i);
        }
    }
    sort(all(ret));
    UNIQUE(ret);
    return ret;
}

/// @brief 繰り返し二乗法。
/// @tparam T 
/// @param x 
/// @param k 
/// @param op 
/// @param e 
/// @return 
template<typename T>
T pow_t(T x, ll k, T (*op)(T, T), T (*e)()){
    T ret = e();
    while(k){
        if(k&1) ret *= x;
        x *= x;
        k >>= 1;
    }
    return ret;
}

ll powll(ll x, ll k){
    return pow_t<ll>(x, k, [](ll a, ll b) -> ll{return a*b;}, []() -> ll{return 1;});
}

inline int pop_cnt(ll x) { return __builtin_popcountll(x); }
inline int top_bit(ll x) { return (x==0?-1:63-__builtin_clzll(x));}

void main2();

int main(){
    ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    main2();
}

mint f(vc<string> s, int n, int m){
    vc<mint> dp1((1<<(n/2+1)), 0), dp2((1<<(n/2+1)), 0);
    dp1[0] = 1;
    dp2[0] = 1;
    rep(j, m){
        rep(i, n){
            vc<mint> ndp((1<<(n/2+1)), 0);
            if(s[i][j] == '#'){
                rep(S, 1<<(n/2+1)){
                    if((i+j)%2 == 0){
                        ndp[(S>>1)] += dp1[S];
                    }
                    else{
                        ndp[(S>>1)] += dp2[S];
                    }
                }
            }
            else rep(S, 1<<(n/2+1)){
                if((i+j)%2 == 0){
                    if(j%2 == 0){
                        ndp[S>>1] += dp1[S];
                        // if(i != 0 && j+1 != m && s[i-1][j+1] == '#') continue;
                        if(i != 0 && (S&1) > 0) continue;
                        if((i != n-1 && (S&2) > 0)) continue;
                        ndp[(S>>1)|(1<<(n/2))] += dp1[S];
                    }
                    else{
                        ndp[S>>1] += dp1[S];
                        // if(i != 0 && j+1 != m && s[i-1][j+1] == '#') continue;
                        if(i != 0 && (S>>(1-n%2)&1) > 0) continue;
                        if(i != n-1 && (S>>(2-n%2)&1) > 0) continue;
                        ndp[(S>>1)|(1<<(n/2))] += dp1[S];
                    }
                }
                else{
                    if(j%2 == 0){
                        ndp[S>>1] += dp2[S];
                        // if(i != 0 && j+1 != m && s[i-1][j+1] == '#') continue;
                        if(i != 0 && (S>>(1-n%2)&1) > 0) continue;
                        if(i != n-1 && (S>>(2-n%2)&1) > 0) continue;
                        ndp[(S>>1)|(1<<(n/2))] += dp2[S];
                    }
                    else{
                        ndp[S>>1] += dp2[S];
                        // if(i != 0 && j+1 != m && s[i-1][j+1] == '#') continue;
                        if(i != 0 && (S&1) > 0) continue;
                        if((i != n-1 && (S&2) > 0)) continue;
                        ndp[(S>>1)|(1<<(n/2))] += dp2[S];
                    }
                }
            }
            if((i+j)%2 == 0) swap(ndp, dp1);
            else swap(ndp, dp2);
        }
    }
    mint ans1 = 0, ans2 = 0;
    rep(i, 1<<(n/2+1)) ans1 += dp1[i], ans2 += dp2[i];
    return ans1*ans2;
}
bool solve(){
    int n,m;
    cin >> n >> m;
    if(n == 0) return false;
    vc<string> s(n);
    cin >> s;

    // split
    vc<string> s_odd(n/2), s_even((n+1)/2);
    rep(i, n){
        if(i%2 == 0) s_even[i/2] = s[i];
        else s_odd[i/2] = s[i];
    }
    mint ans = f(s_odd, n/2, m)*f(s_even, (n+1)/2, m);
    // cout << f(s_odd, n/2, m).val() << el;
    cout << ans << el;
    return true;
}
void main2(){
    while(solve()){
        ;
    }
}
accuracy: 1
time_ms: 2700
memory_kb: 4476
final_rank: 8

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;

bool solve(){
    int n, m; cin >> n >> m;
    if(n == 0 && m == 0) return false;
    vector<string> a(n);
    for(int i = 0; i < n; i++) cin >> a[i];
    constexpr ll MOD = 1e9+7;
    ll ans = 1;
    for(int i0 = 0; i0 < 4; i0++) {
        vector<int> is;
        for(int i = i0; i < n; i += 4) {
            is.push_back(i);
        }
        vector<ll> dp(1 << ssize(is));
        int valid = 0;
        for(int ii = 0; ii < ssize(is); ii++) {
            if(a[is[ii]][0] == '.') valid |= 1 << ii;
        }
        for(int sii = valid;; sii = (sii-1) & valid) {
            dp[sii] = 1;
            if(sii == 0) break;
        }
        for(int j = 1; j < m; j++) {
            vector<int> is;
            for(int i = (i0+j*2) % 4; i < n; i += 4) {
                is.push_back(i);
            }
            vector<ll> ndp(1 << ssize(is));
            for(int sii = 0; sii < ssize(dp); sii++) {
                int valid = 0;
                for(int nii = 0; nii < ssize(is); nii++) {
                    if(a[is[nii]][j] == '.') valid |= 1 << nii;
                }
                valid &= ~sii;
                if((i0/2 + j) % 2 == 1) {
                    valid &= ~(sii >> 1);
                } else {
                    valid &= ~(sii << 1);
                }
                for(int nsii = valid;; nsii = (nsii-1) & valid) {
                    if((ndp[nsii] += dp[sii]) >= MOD) ndp[nsii] -= MOD;
                    if(nsii == 0) break;
                }
            }
            dp.swap(ndp);
        }
        ll tmp = 0;
        for(int sii = 0; sii < ssize(dp); sii++) {
            if((tmp += dp[sii]) >= MOD) tmp -= MOD;
        }
        ans *= tmp;
        ans %= MOD;
    }
    cout << ans << '\n';
    return true;
}

int main() {
    while(solve());
}
accuracy: 1
time_ms: 2160
memory_kb: 4188
final_rank: 4

code_snippet: #include <bits/stdc++.h>
using namespace std;

#define rep2(i, m, n) for (int i = (m); i < (n); ++i)
#define rep(i, n) rep2(i, 0, n)
#define drep2(i, m, n) for (int i = (m)-1; i >= (n); --i)
#define drep(i, n) drep2(i, n, 0)
#define all(...) std::begin(__VA_ARGS__), std::end(__VA_ARGS__)
#define rall(...) std::rbegin(__VA_ARGS__), std::rend(__VA_ARGS__)
#define FOR(i, a, b) for (int i = (a), i##_len = (b); i <= i##_len; ++i)
#define REV(i, a, b) for (int i = (a); i >= (b); --i)
#define CLR(a, b) memset((a), (b), sizeof(a))
#define DUMP(x) cout << #x << " = " << (x) << endl;
#define INF 1001001001001001001ll
#define inf (int)1001001000
#define MOD 998244353
#define MOD1 1000000007
#define PI 3.14159265358979
#define Dval 1e-12
#define fcout cout << fixed << setprecision(12)
#define MP make_pair
#define PB push_back
#define fi first
#define se second
#define SORT(x) sort(x.begin(),x.end())
#define ERASE(x) x.erase(unique(x.begin(),x.end()),x.end())
#define POSL(x,v) (lower_bound(x.begin(),x.end(),v)-x.begin())
#define POSU(x,v) (upper_bound(x.begin(),x.end(),v)-x.begin())

using ll = long long;
using ld = long double;
using vi = vector<int>;
using vl = vector<long long>;
using vs = vector<string>;
using vd = vector<double>;
using vld = vector<long double>;
using vc = vector<char>;
using vb = vector<bool>;
using vpii = vector<pair<int, int>>;
using vpil = vector<pair<int, long long>>;
using vpll = vector<pair<long long, long long>>;
using vvi = vector<vector<int>>;
using vvl = vector<vector<long long>>;
using vvd = vector<vector<double>>;
using vvld = vector<vector<long double>>;
using vvc = vector<vector<char>>;
using vvb = vector<vector<bool>>;
using vvpii = vector<vector<pair<int,int>>>;
using vvpll = vector<vector<pair<long long,long long>>>;
using vvvi = vector<vector<vector<int>>>;
using vvvl = vector<vector<vector<long long>>>;
using pii = pair<int, int>;
using pll = pair<long long, long long>;

ll gcd(ll x, ll y) {	if (x == 0) return y;	return gcd(y%x, x);} 
template<typename T>
T POW(T x, ll n){T ret=1;	while(n>0){		if(n&1) ret=ret*x;		x=x*x;		n>>=1;	}	return ret;}
template<typename T>
T modpow(T a, ll n, T p) {	if(n==0) return (T)1;  if (n == 1) return a % p;  if (n % 2 == 1) return (a * modpow(a, n - 1, p)) % p;  T t = modpow(a, n / 2, p);  return (t * t) % p;}
template<typename T>
T modinv(T a, T m) {	if(m==0)return (T)1;	T b = m, u = 1, v = 0;	while (b) {		T t = a / b;		a -= t * b; swap(a, b);		u -= t * v; swap(u, v);	}	u %= m;	if (u < 0) u += m;	return u;}
template<typename T>
T REM(T a, T b){ return (a % b + b) % b;}
template<typename T>
T QUO(T a, T b){ return (a - REM(a, b)) / b;}
/* 
const int MAXCOMB=510000;
ll MODCOMB = 998244353;
ll fac[MAXCOMB], finv[MAXCOMB], inv[MAXCOMB]; 
void COMinit() {	fac[0] = fac[1] = 1;	finv[0] = finv[1] = 1;	inv[1] = 1;	for (int i = 2; i < MAXCOMB; i++) {		fac[i] = fac[i - 1] * i % MODCOMB;		inv[i] = MODCOMB - inv[MODCOMB%i] * (MODCOMB / i) % MODCOMB;		finv[i] = finv[i - 1] * inv[i] % MODCOMB;	}}
ll COM(ll n, ll k) {	if (n < k) return 0;	if (n < 0 || k < 0) return 0;	return fac[n] * (finv[k] * finv[n - k] % MODCOMB) % MODCOMB;}
ll com(ll n,ll m){   if(n<m || n<=0 ||m<0){		return 0;	}	if( m==0 || n==m){		return 1;	}	ll k=1;	for(ll i=1;i<=m;i++){       k*=(n-i+1); 	   k%=MODCOMB;	   k*=modinv(i,MODCOMB);	   k%=MODCOMB;	}	return k;}
*/
/*
const int MAXCOMB=510000;
std::vector<mint> fac(MAXCOMB), finv(MAXCOMB), inv(MAXCOMB);
void COMinit() {fac[0] = fac[1] = 1;finv[0] = finv[1] = 1;inv[1] = 1;for (int i = 2; i < MAXCOMB; i++) {fac[i] = fac[i - 1] * i;inv[i] = mint(0) - inv[mint::mod() % i] * (mint::mod() / i);finv[i] = finv[i - 1] * inv[i];}}
mint COM(int n, int k) {if (n < k) return 0;if (n < 0 || k < 0) return 0;return fac[n] * finv[k] * finv[n - k];}
*/
template <typename T> inline bool chmax(T &a, T b) { return ((a < b) ? (a = b, true) : (false));}
template <typename T> inline bool chmin(T &a, T b) { return ((a > b) ? (a = b, true) : (false));}
template <class T> T BS(vector<T> &vec, T key) { auto itr = lower_bound(vec.begin(), vec.end(), key); return distance(vec.begin(), itr); }
template<class T> pair<T,T> RangeBS(vector<T> &vec, T lowv, T highv){auto itr_l = lower_bound(vec.begin(), vec.end(), lowv); auto itr_r = upper_bound(vec.begin(), vec.end(), highv); return make_pair(distance(vec.begin(), itr_l), distance(vec.begin(), itr_r)-1);}
void fail() { cout << "-1\n"; exit(0); } void no() { cout << "No\n"; exit(0); } void yes() { cout << "Yes\n"; exit(0); }
template<class T> void er(T a) { cout << a << '\n'; exit(0); }
int dx[] = { 1,0,-1,0,1,1,-1,-1 }; int dy[] = { 0,1,0,-1,1,-1,1,-1};
bool range_in(int i, int j, int h, int w){ if(i<0 || j<0 || i>=h || j>=w) return false; return true;} 
int bitcount(int n){n=(n&0x55555555)+(n>>1&0x55555555); n=(n&0x33333333)+(n>>2&0x33333333); n=(n&0x0f0f0f0f)+(n>>4&0x0f0f0f0f); n=(n&0x00ff00ff)+(n>>8&0x00ff00ff); n=(n&0x0000ffff)+(n>>16&0x0000ffff); return n;}

template<typename T>
struct Edge{
    int from, to, index;
    T cost;
    Edge(int _to) : from(-1), to(_to), index(-1), cost(0) {}
    Edge(int _to, T _cost) : from(-1), to(_to), index(-1), cost(_cost) {}
    Edge(int _from, int _to, int _index) : from(_from), to(_to), index(_index), cost(0) {}
    Edge(int _from, int _to, int _index, T _cost) 
        : from(_from), to(_to), index(_index), cost(_cost) {}
    bool operator<(const Edge<T>& other) const {
        return cost < other.cost; 
    }
    Edge &operator=(const int &x) {
        to = x;
        return *this;
    }
    operator int() const { return to; }
};
using Graph = vector<vector<int>>; 
template <typename T>
using WGraph = vector<vector<Edge<T>>>; 


template <uint32_t mod>
struct LazyMontgomeryModInt {
  using mint = LazyMontgomeryModInt;
  using i32 = int32_t;
  using u32 = uint32_t;
  using u64 = uint64_t;

  static constexpr u32 get_r() {
    u32 ret = mod;
    for (i32 i = 0; i < 4; ++i) ret *= 2 - mod * ret;
    return ret;
  }

  static constexpr u32 r = get_r();
  static constexpr u32 n2 = -u64(mod) % mod;
  static_assert(mod < (1 << 30), "invalid, mod >= 2 ^ 30");
  static_assert((mod & 1) == 1, "invalid, mod % 2 == 0");
  static_assert(r * mod == 1, "this code has bugs.");

  u32 a;

  constexpr LazyMontgomeryModInt() : a(0) {}
  constexpr LazyMontgomeryModInt(const int64_t &b)
      : a(reduce(u64(b % mod + mod) * n2)){};

  static constexpr u32 reduce(const u64 &b) {
    return (b + u64(u32(b) * u32(-r)) * mod) >> 32;
  }

  constexpr mint &operator+=(const mint &b) {
    if (i32(a += b.a - 2 * mod) < 0) a += 2 * mod;
    return *this;
  }

  constexpr mint &operator-=(const mint &b) {
    if (i32(a -= b.a) < 0) a += 2 * mod;
    return *this;
  }

  constexpr mint &operator*=(const mint &b) {
    a = reduce(u64(a) * b.a);
    return *this;
  }

  constexpr mint &operator/=(const mint &b) {
    *this *= b.inverse();
    return *this;
  }

  constexpr mint operator+(const mint &b) const { return mint(*this) += b; }
  constexpr mint operator-(const mint &b) const { return mint(*this) -= b; }
  constexpr mint operator*(const mint &b) const { return mint(*this) *= b; }
  constexpr mint operator/(const mint &b) const { return mint(*this) /= b; }
  constexpr bool operator==(const mint &b) const {
    return (a >= mod ? a - mod : a) == (b.a >= mod ? b.a - mod : b.a);
  }
  constexpr bool operator!=(const mint &b) const {
    return (a >= mod ? a - mod : a) != (b.a >= mod ? b.a - mod : b.a);
  }
  constexpr mint operator-() const { return mint() - mint(*this); }
  constexpr mint operator+() const { return mint(*this); }

  constexpr mint pow(u64 n) const {
    mint ret(1), mul(*this);
    while (n > 0) {
      if (n & 1) ret *= mul;
      mul *= mul;
      n >>= 1;
    }
    return ret;
  }

  constexpr mint inverse() const {
    int x = get(), y = mod, u = 1, v = 0, t = 0, tmp = 0;
    while (y > 0) {
      t = x / y;
      x -= t * y, u -= t * v;
      tmp = x, x = y, y = tmp;
      tmp = u, u = v, v = tmp;
    }
    return mint{u};
  }

  friend ostream &operator<<(ostream &os, const mint &b) {
    return os << b.get();
  }

  friend istream &operator>>(istream &is, mint &b) {
    int64_t t;
    is >> t;
    b = LazyMontgomeryModInt<mod>(t);
    return (is);
  }

  constexpr u32 get() const {
    u32 ret = reduce(a);
    return ret >= mod ? ret - mod : ret;
  }

  static constexpr u32 get_mod() { return mod; }
};

template <typename mint>
struct NTT {
  static constexpr uint32_t get_pr() {
    uint32_t _mod = mint::get_mod();
    using u64 = uint64_t;
    u64 ds[32] = {};
    int idx = 0;
    u64 m = _mod - 1;
    for (u64 i = 2; i * i <= m; ++i) {
      if (m % i == 0) {
        ds[idx++] = i;
        while (m % i == 0) m /= i;
      }
    }
    if (m != 1) ds[idx++] = m;

    uint32_t _pr = 2;
    while (1) {
      int flg = 1;
      for (int i = 0; i < idx; ++i) {
        u64 a = _pr, b = (_mod - 1) / ds[i], r = 1;
        while (b) {
          if (b & 1) r = r * a % _mod;
          a = a * a % _mod;
          b >>= 1;
        }
        if (r == 1) {
          flg = 0;
          break;
        }
      }
      if (flg == 1) break;
      ++_pr;
    }
    return _pr;
  };

  static constexpr uint32_t mod = mint::get_mod();
  static constexpr uint32_t pr = get_pr();
  static constexpr int level = __builtin_ctzll(mod - 1);
  mint dw[level], dy[level];

  void setwy(int k) {
    mint w[level], y[level];
    w[k - 1] = mint(pr).pow((mod - 1) / (1 << k));
    y[k - 1] = w[k - 1].inverse();
    for (int i = k - 2; i > 0; --i)
      w[i] = w[i + 1] * w[i + 1], y[i] = y[i + 1] * y[i + 1];
    dw[1] = w[1], dy[1] = y[1], dw[2] = w[2], dy[2] = y[2];
    for (int i = 3; i < k; ++i) {
      dw[i] = dw[i - 1] * y[i - 2] * w[i];
      dy[i] = dy[i - 1] * w[i - 2] * y[i];
    }
  }

  NTT() { setwy(level); }

  void fft4(vector<mint> &a, int k) {
    if ((int)a.size() <= 1) return;
    if (k == 1) {
      mint a1 = a[1];
      a[1] = a[0] - a[1];
      a[0] = a[0] + a1;
      return;
    }
    if (k & 1) {
      int v = 1 << (k - 1);
      for (int j = 0; j < v; ++j) {
        mint ajv = a[j + v];
        a[j + v] = a[j] - ajv;
        a[j] += ajv;
      }
    }
    int u = 1 << (2 + (k & 1));
    int v = 1 << (k - 2 - (k & 1));
    mint one = mint(1);
    mint imag = dw[1];
    while (v) {
      // jh = 0
      {
        int j0 = 0;
        int j1 = v;
        int j2 = j1 + v;
        int j3 = j2 + v;
        for (; j0 < v; ++j0, ++j1, ++j2, ++j3) {
          mint t0 = a[j0], t1 = a[j1], t2 = a[j2], t3 = a[j3];
          mint t0p2 = t0 + t2, t1p3 = t1 + t3;
          mint t0m2 = t0 - t2, t1m3 = (t1 - t3) * imag;
          a[j0] = t0p2 + t1p3, a[j1] = t0p2 - t1p3;
          a[j2] = t0m2 + t1m3, a[j3] = t0m2 - t1m3;
        }
      }
      // jh >= 1
      mint ww = one, xx = one * dw[2], wx = one;
      for (int jh = 4; jh < u;) {
        ww = xx * xx, wx = ww * xx;
        int j0 = jh * v;
        int je = j0 + v;
        int j2 = je + v;
        for (; j0 < je; ++j0, ++j2) {
          mint t0 = a[j0], t1 = a[j0 + v] * xx, t2 = a[j2] * ww,
               t3 = a[j2 + v] * wx;
          mint t0p2 = t0 + t2, t1p3 = t1 + t3;
          mint t0m2 = t0 - t2, t1m3 = (t1 - t3) * imag;
          a[j0] = t0p2 + t1p3, a[j0 + v] = t0p2 - t1p3;
          a[j2] = t0m2 + t1m3, a[j2 + v] = t0m2 - t1m3;
        }
        xx *= dw[__builtin_ctzll((jh += 4))];
      }
      u <<= 2;
      v >>= 2;
    }
  }

  void ifft4(vector<mint> &a, int k) {
    if ((int)a.size() <= 1) return;
    if (k == 1) {
      mint a1 = a[1];
      a[1] = a[0] - a[1];
      a[0] = a[0] + a1;
      return;
    }
    int u = 1 << (k - 2);
    int v = 1;
    mint one = mint(1);
    mint imag = dy[1];
    while (u) {
      // jh = 0
      {
        int j0 = 0;
        int j1 = v;
        int j2 = v + v;
        int j3 = j2 + v;
        for (; j0 < v; ++j0, ++j1, ++j2, ++j3) {
          mint t0 = a[j0], t1 = a[j1], t2 = a[j2], t3 = a[j3];
          mint t0p1 = t0 + t1, t2p3 = t2 + t3;
          mint t0m1 = t0 - t1, t2m3 = (t2 - t3) * imag;
          a[j0] = t0p1 + t2p3, a[j2] = t0p1 - t2p3;
          a[j1] = t0m1 + t2m3, a[j3] = t0m1 - t2m3;
        }
      }
      // jh >= 1
      mint ww = one, xx = one * dy[2], yy = one;
      u <<= 2;
      for (int jh = 4; jh < u;) {
        ww = xx * xx, yy = xx * imag;
        int j0 = jh * v;
        int je = j0 + v;
        int j2 = je + v;
        for (; j0 < je; ++j0, ++j2) {
          mint t0 = a[j0], t1 = a[j0 + v], t2 = a[j2], t3 = a[j2 + v];
          mint t0p1 = t0 + t1, t2p3 = t2 + t3;
          mint t0m1 = (t0 - t1) * xx, t2m3 = (t2 - t3) * yy;
          a[j0] = t0p1 + t2p3, a[j2] = (t0p1 - t2p3) * ww;
          a[j0 + v] = t0m1 + t2m3, a[j2 + v] = (t0m1 - t2m3) * ww;
        }
        xx *= dy[__builtin_ctzll(jh += 4)];
      }
      u >>= 4;
      v <<= 2;
    }
    if (k & 1) {
      u = 1 << (k - 1);
      for (int j = 0; j < u; ++j) {
        mint ajv = a[j] - a[j + u];
        a[j] += a[j + u];
        a[j + u] = ajv;
      }
    }
  }

  void ntt(vector<mint> &a) {
    if ((int)a.size() <= 1) return;
    fft4(a, __builtin_ctz(a.size()));
  }

  void intt(vector<mint> &a) {
    if ((int)a.size() <= 1) return;
    ifft4(a, __builtin_ctz(a.size()));
    mint iv = mint(a.size()).inverse();
    for (auto &x : a) x *= iv;
  }

  vector<mint> multiply(const vector<mint> &a, const vector<mint> &b) {
    int l = a.size() + b.size() - 1;
    if (min<int>(a.size(), b.size()) <= 40) {
      vector<mint> s(l);
      for (int i = 0; i < (int)a.size(); ++i)
        for (int j = 0; j < (int)b.size(); ++j) s[i + j] += a[i] * b[j];
      return s;
    }
    int k = 2, M = 4;
    while (M < l) M <<= 1, ++k;
    setwy(k);
    vector<mint> s(M);
    for (int i = 0; i < (int)a.size(); ++i) s[i] = a[i];
    fft4(s, k);
    if (a.size() == b.size() && a == b) {
      for (int i = 0; i < M; ++i) s[i] *= s[i];
    } else {
      vector<mint> t(M);
      for (int i = 0; i < (int)b.size(); ++i) t[i] = b[i];
      fft4(t, k);
      for (int i = 0; i < M; ++i) s[i] *= t[i];
    }
    ifft4(s, k);
    s.resize(l);
    mint invm = mint(M).inverse();
    for (int i = 0; i < l; ++i) s[i] *= invm;
    return s;
  }

  void ntt_doubling(vector<mint> &a) {
    int M = (int)a.size();
    auto b = a;
    intt(b);
    mint r = 1, zeta = mint(pr).pow((mint::get_mod() - 1) / (M << 1));
    for (int i = 0; i < M; i++) b[i] *= r, r *= zeta;
    ntt(b);
    copy(begin(b), end(b), back_inserter(a));
  }
};

template <typename Int, typename UInt, typename Long, typename ULong, int id>
struct ArbitraryLazyMontgomeryModIntBase {
  using mint = ArbitraryLazyMontgomeryModIntBase;

  inline static UInt mod;
  inline static UInt r;
  inline static UInt n2;
  static constexpr int bit_length = sizeof(UInt) * 8;

  static UInt get_r() {
    UInt ret = mod;
    while (mod * ret != 1) ret *= UInt(2) - mod * ret;
    return ret;
  }
  static void set_mod(UInt m) {
    assert(m < (UInt(1u) << (bit_length - 2)));
    assert((m & 1) == 1);
    mod = m, n2 = -ULong(m) % m, r = get_r();
  }
  UInt a;

  ArbitraryLazyMontgomeryModIntBase() : a(0) {}
  ArbitraryLazyMontgomeryModIntBase(const Long &b)
      : a(reduce(ULong(b % mod + mod) * n2)){};

  static UInt reduce(const ULong &b) {
    return (b + ULong(UInt(b) * UInt(-r)) * mod) >> bit_length;
  }

  mint &operator+=(const mint &b) {
    if (Int(a += b.a - 2 * mod) < 0) a += 2 * mod;
    return *this;
  }
  mint &operator-=(const mint &b) {
    if (Int(a -= b.a) < 0) a += 2 * mod;
    return *this;
  }
  mint &operator*=(const mint &b) {
    a = reduce(ULong(a) * b.a);
    return *this;
  }
  mint &operator/=(const mint &b) {
    *this *= b.inverse();
    return *this;
  }

  mint operator+(const mint &b) const { return mint(*this) += b; }
  mint operator-(const mint &b) const { return mint(*this) -= b; }
  mint operator*(const mint &b) const { return mint(*this) *= b; }
  mint operator/(const mint &b) const { return mint(*this) /= b; }

  bool operator==(const mint &b) const {
    return (a >= mod ? a - mod : a) == (b.a >= mod ? b.a - mod : b.a);
  }
  bool operator!=(const mint &b) const {
    return (a >= mod ? a - mod : a) != (b.a >= mod ? b.a - mod : b.a);
  }
  mint operator-() const { return mint(0) - mint(*this); }
  mint operator+() const { return mint(*this); }

  mint pow(ULong n) const {
    mint ret(1), mul(*this);
    while (n > 0) {
      if (n & 1) ret *= mul;
      mul *= mul, n >>= 1;
    }
    return ret;
  }

  friend ostream &operator<<(ostream &os, const mint &b) {
    return os << b.get();
  }

  friend istream &operator>>(istream &is, mint &b) {
    Long t;
    is >> t;
    b = ArbitraryLazyMontgomeryModIntBase(t);
    return (is);
  }

  mint inverse() const {
    Int x = get(), y = get_mod(), u = 1, v = 0;
    while (y > 0) {
      Int t = x / y;
      swap(x -= t * y, y);
      swap(u -= t * v, v);
    }
    return mint{u};
  }

  UInt get() const {
    UInt ret = reduce(a);
    return ret >= mod ? ret - mod : ret;
  }

  static UInt get_mod() { return mod; }
};

// id に適当な乱数を割り当てて使う
template <int id>
using ArbitraryLazyMontgomeryModInt =
    ArbitraryLazyMontgomeryModIntBase<int, unsigned int, long long,
                                      unsigned long long, id>;
template <int id>
using ArbitraryLazyMontgomeryModInt64bit =
    ArbitraryLazyMontgomeryModIntBase<long long, unsigned long long, __int128_t,
                                      __uint128_t, id>;

namespace ArbitraryNTT {
using i64 = int64_t;
using u128 = __uint128_t;
constexpr int32_t m0 = 167772161;
constexpr int32_t m1 = 469762049;
constexpr int32_t m2 = 754974721;
using mint0 = LazyMontgomeryModInt<m0>;
using mint1 = LazyMontgomeryModInt<m1>;
using mint2 = LazyMontgomeryModInt<m2>;
constexpr int r01 = mint1(m0).inverse().get();
constexpr int r02 = mint2(m0).inverse().get();
constexpr int r12 = mint2(m1).inverse().get();
constexpr int r02r12 = i64(r02) * r12 % m2;
constexpr i64 w1 = m0;
constexpr i64 w2 = i64(m0) * m1;

template <typename T, typename submint>
vector<submint> mul(const vector<T> &a, const vector<T> &b) {
  static NTT<submint> ntt;
  vector<submint> s(a.size()), t(b.size());
  for (int i = 0; i < (int)a.size(); ++i) s[i] = i64(a[i] % submint::get_mod());
  for (int i = 0; i < (int)b.size(); ++i) t[i] = i64(b[i] % submint::get_mod());
  return ntt.multiply(s, t);
}

template <typename T>
vector<int> multiply(const vector<T> &s, const vector<T> &t, int mod) {
  auto d0 = mul<T, mint0>(s, t);
  auto d1 = mul<T, mint1>(s, t);
  auto d2 = mul<T, mint2>(s, t);
  int n = d0.size();
  vector<int> ret(n);
  const int W1 = w1 % mod;
  const int W2 = w2 % mod;
  for (int i = 0; i < n; i++) {
    int n1 = d1[i].get(), n2 = d2[i].get(), a = d0[i].get();
    int b = i64(n1 + m1 - a) * r01 % m1;
    int c = (i64(n2 + m2 - a) * r02r12 + i64(m2 - b) * r12) % m2;
    ret[i] = (i64(a) + i64(b) * W1 + i64(c) * W2) % mod;
  }
  return ret;
}

template <typename mint>
vector<mint> multiply(const vector<mint> &a, const vector<mint> &b) {
  if (a.size() == 0 && b.size() == 0) return {};
  if (min<int>(a.size(), b.size()) < 128) {
    vector<mint> ret(a.size() + b.size() - 1);
    for (int i = 0; i < (int)a.size(); ++i)
      for (int j = 0; j < (int)b.size(); ++j) ret[i + j] += a[i] * b[j];
    return ret;
  }
  vector<int> s(a.size()), t(b.size());
  for (int i = 0; i < (int)a.size(); ++i) s[i] = a[i].get();
  for (int i = 0; i < (int)b.size(); ++i) t[i] = b[i].get();
  vector<int> u = multiply<int>(s, t, mint::get_mod());
  vector<mint> ret(u.size());
  for (int i = 0; i < (int)u.size(); ++i) ret[i] = mint(u[i]);
  return ret;
}

template <typename T>
vector<u128> multiply_u128(const vector<T> &s, const vector<T> &t) {
  if (s.size() == 0 && t.size() == 0) return {};
  if (min<int>(s.size(), t.size()) < 128) {
    vector<u128> ret(s.size() + t.size() - 1);
    for (int i = 0; i < (int)s.size(); ++i)
      for (int j = 0; j < (int)t.size(); ++j) ret[i + j] += i64(s[i]) * t[j];
    return ret;
  }
  auto d0 = mul<T, mint0>(s, t);
  auto d1 = mul<T, mint1>(s, t);
  auto d2 = mul<T, mint2>(s, t);
  int n = d0.size();
  vector<u128> ret(n);
  for (int i = 0; i < n; i++) {
    i64 n1 = d1[i].get(), n2 = d2[i].get();
    i64 a = d0[i].get();
    i64 b = (n1 + m1 - a) * r01 % m1;
    i64 c = ((n2 + m2 - a) * r02r12 + (m2 - b) * r12) % m2;
    ret[i] = a + b * w1 + u128(c) * w2;
  }
  return ret;
}
}  // namespace ArbitraryNTT


template <typename mint>
struct FormalPowerSeries : vector<mint> {
  using vector<mint>::vector;
  using FPS = FormalPowerSeries;

  FPS &operator+=(const FPS &r) {
    if (r.size() > this->size()) this->resize(r.size());
    for (int i = 0; i < (int)r.size(); i++) (*this)[i] += r[i];
    return *this;
  }

  FPS &operator+=(const mint &r) {
    if (this->empty()) this->resize(1);
    (*this)[0] += r;
    return *this;
  }

  FPS &operator-=(const FPS &r) {
    if (r.size() > this->size()) this->resize(r.size());
    for (int i = 0; i < (int)r.size(); i++) (*this)[i] -= r[i];
    return *this;
  }

  FPS &operator-=(const mint &r) {
    if (this->empty()) this->resize(1);
    (*this)[0] -= r;
    return *this;
  }

  FPS &operator*=(const mint &v) {
    for (int k = 0; k < (int)this->size(); k++) (*this)[k] *= v;
    return *this;
  }

  FPS &operator/=(const FPS &r) {
    if (this->size() < r.size()) {
      this->clear();
      return *this;
    }
    int n = this->size() - r.size() + 1;
    if ((int)r.size() <= 64) {
      FPS f(*this), g(r);
      g.shrink();
      mint coeff = g.back().inverse();
      for (auto &x : g) x *= coeff;
      int deg = (int)f.size() - (int)g.size() + 1;
      int gs = g.size();
      FPS quo(deg);
      for (int i = deg - 1; i >= 0; i--) {
        quo[i] = f[i + gs - 1];
        for (int j = 0; j < gs; j++) f[i + j] -= quo[i] * g[j];
      }
      *this = quo * coeff;
      this->resize(n, mint(0));
      return *this;
    }
    return *this = ((*this).rev().pre(n) * r.rev().inv(n)).pre(n).rev();
  }

  FPS &operator%=(const FPS &r) {
    *this -= *this / r * r;
    shrink();
    return *this;
  }

  FPS operator+(const FPS &r) const { return FPS(*this) += r; }
  FPS operator+(const mint &v) const { return FPS(*this) += v; }
  FPS operator-(const FPS &r) const { return FPS(*this) -= r; }
  FPS operator-(const mint &v) const { return FPS(*this) -= v; }
  FPS operator*(const FPS &r) const { return FPS(*this) *= r; }
  FPS operator*(const mint &v) const { return FPS(*this) *= v; }
  FPS operator/(const FPS &r) const { return FPS(*this) /= r; }
  FPS operator%(const FPS &r) const { return FPS(*this) %= r; }
  FPS operator-() const {
    FPS ret(this->size());
    for (int i = 0; i < (int)this->size(); i++) ret[i] = -(*this)[i];
    return ret;
  }

  void shrink() {
    while (this->size() && this->back() == mint(0)) this->pop_back();
  }

  FPS rev() const {
    FPS ret(*this);
    reverse(begin(ret), end(ret));
    return ret;
  }

  FPS dot(FPS r) const {
    FPS ret(min(this->size(), r.size()));
    for (int i = 0; i < (int)ret.size(); i++) ret[i] = (*this)[i] * r[i];
    return ret;
  }

  // 前 sz 項を取ってくる。sz に足りない項は 0 埋めする
  FPS pre(int sz) const {
    FPS ret(begin(*this), begin(*this) + min((int)this->size(), sz));
    if ((int)ret.size() < sz) ret.resize(sz);
    return ret;
  }

  FPS operator>>(int sz) const {
    if ((int)this->size() <= sz) return {};
    FPS ret(*this);
    ret.erase(ret.begin(), ret.begin() + sz);
    return ret;
  }

  FPS operator<<(int sz) const {
    FPS ret(*this);
    ret.insert(ret.begin(), sz, mint(0));
    return ret;
  }

  FPS diff() const {
    const int n = (int)this->size();
    FPS ret(max(0, n - 1));
    mint one(1), coeff(1);
    for (int i = 1; i < n; i++) {
      ret[i - 1] = (*this)[i] * coeff;
      coeff += one;
    }
    return ret;
  }

  FPS integral() const {
    const int n = (int)this->size();
    FPS ret(n + 1);
    ret[0] = mint(0);
    if (n > 0) ret[1] = mint(1);
    auto mod = mint::get_mod();
    for (int i = 2; i <= n; i++) ret[i] = (-ret[mod % i]) * (mod / i);
    for (int i = 0; i < n; i++) ret[i + 1] *= (*this)[i];
    return ret;
  }

  mint eval(mint x) const {
    mint r = 0, w = 1;
    for (auto &v : *this) r += w * v, w *= x;
    return r;
  }

  FPS log(int deg = -1) const {
    assert(!(*this).empty() && (*this)[0] == mint(1));
    if (deg == -1) deg = (int)this->size();
    return (this->diff() * this->inv(deg)).pre(deg - 1).integral();
  }

  FPS pow(int64_t k, int deg = -1) const {
    const int n = (int)this->size();
    if (deg == -1) deg = n;
    if (k == 0) {
      FPS ret(deg);
      if (deg) ret[0] = 1;
      return ret;
    }
    for (int i = 0; i < n; i++) {
      if ((*this)[i] != mint(0)) {
        mint rev = mint(1) / (*this)[i];
        FPS ret = (((*this * rev) >> i).log(deg) * k).exp(deg);
        ret *= (*this)[i].pow(k);
        ret = (ret << (i * k)).pre(deg);
        if ((int)ret.size() < deg) ret.resize(deg, mint(0));
        return ret;
      }
      if (__int128_t(i + 1) * k >= deg) return FPS(deg, mint(0));
    }
    return FPS(deg, mint(0));
  }

  static void *ntt_ptr;
  static void set_fft();
  FPS &operator*=(const FPS &r);
  void ntt();
  void intt();
  void ntt_doubling();
  static int ntt_pr();
  FPS inv(int deg = -1) const;
  FPS exp(int deg = -1) const;
};
template <typename mint>
void *FormalPowerSeries<mint>::ntt_ptr = nullptr;

template <typename mint>
void FormalPowerSeries<mint>::set_fft() {
  ntt_ptr = nullptr;
}

template <typename mint>
void FormalPowerSeries<mint>::ntt() {
  exit(1);
}

template <typename mint>
void FormalPowerSeries<mint>::intt() {
  exit(1);
}

template <typename mint>
void FormalPowerSeries<mint>::ntt_doubling() {
  exit(1);
}

template <typename mint>
int FormalPowerSeries<mint>::ntt_pr() {
  exit(1);
}

template <typename mint>
FormalPowerSeries<mint>& FormalPowerSeries<mint>::operator*=(
    const FormalPowerSeries<mint>& r) {
  if (this->empty() || r.empty()) {
    this->clear();
    return *this;
  }
  auto ret = ArbitraryNTT::multiply(*this, r);
  return *this = FormalPowerSeries<mint>(ret.begin(), ret.end());
}

template <typename mint>
FormalPowerSeries<mint> FormalPowerSeries<mint>::inv(int deg) const {
  assert((*this)[0] != mint(0));
  if (deg == -1) deg = (*this).size();
  FormalPowerSeries<mint> ret({mint(1) / (*this)[0]});
  for (int i = 1; i < deg; i <<= 1)
    ret = (ret + ret - ret * ret * (*this).pre(i << 1)).pre(i << 1);
  return ret.pre(deg);
}

template <typename mint>
FormalPowerSeries<mint> FormalPowerSeries<mint>::exp(int deg) const {
  assert((*this).size() == 0 || (*this)[0] == mint(0));
  if (deg == -1) deg = (int)this->size();
  FormalPowerSeries<mint> ret({mint(1)});
  for (int i = 1; i < deg; i <<= 1) {
    ret = (ret * (pre(i << 1) + mint(1) - ret.log(i << 1))).pre(i << 1);
  }
  return ret.pre(deg);
}

int SOLVEFIN = 0;

using mint = LazyMontgomeryModInt<1000000007>;
using fps = FormalPowerSeries<mint>;

int n,m;
pii prev1(pii x){
    return {x.fi-2,x.se-1};
}
pii prev2(pii x){
    return {x.fi+2,x.se-1};
}
bool range_in(pii x){
    if(x.fi>=0 && x.fi<n && x.se>=0 && x.se<m) return true;
    else return false;
}

template <typename T>
void zt_des(vector<T> &A, int n) {
    int An = A.size();
    for(int i=0;i<n;i++){ //各次元に対しての更新
        for(int j=0;j<An;j++){ //各要素を見る
            if(j & (1LL<<i)) A[j] += A[j^(1LL<<i)];
        }
    }    
    return;
}

void solve(){
    //縦に見ると15マス!!!
    cin>>n>>m;
    if(n==0){
        SOLVEFIN = 1;
        return;
    }
    vs gr(n);
    rep(i,n)cin>>gr[i];
    mint ans = 1;
   
    for(int k=0;k<=3;k++){
        int prevcount = 0;
        vector<vector<mint>> dp(m,vector<mint>(POW((int)2,15)));
        for(int j=0;j<m;j++){
            int count = 0;
            int s = k;
            if(j%2==1) s = (k+2)%4; 
            int now = s;
            while(now<n){
                count++;
                now+=4;
            }
            
            vector<mint> prevdp;
            if(j) {prevdp = dp[j-1];
            zt_des(prevdp ,prevcount);}

            //ありうる状態を列挙
            
            for(int st=0;st<(1<<count);st++){
                int imp = 0;
                for(int t=0;t<count;t++){
                    if(gr[s+4*t][j]=='#' && ((st>>t)&1) ){imp=1;break;}
                }
                if(imp)continue;
                if(j==0){dp[0][st]=1;continue;}
                int offset = 0;
                unsigned int maxstate = POW(2,prevcount)-1;
                if(range_in(prev1({s,j})))offset= 1;
                //cout<<k<<" "<<j<<" "<<st<<" "<<offset<<" "<<s<<endl;
                for(int t=0;t<count;t++){
                    if(((st>>t)&1) && (t+offset)<prevcount){
                       if((maxstate>>(t+offset))&1) maxstate = ~(~maxstate | (1<<(t+offset)));
                    }
                    if(((st>>t)&1) && (t+offset-1)<prevcount && (t+offset-1)>=0){
                        if((maxstate>>(t+offset-1))&1)maxstate = ~(~maxstate | (1<<(t+offset-1)));
                    }
                }
                //if(st==2 && k==0 && j==2)cout<<maxstate<<" "<<prevcount<<" "<<count<<"A"<<endl;
                
                dp[j][st] = prevdp[maxstate];

            }

            prevcount = count;
        }
        mint res = 0;
        // rep(o,m){
        //     rep(p,(1<<prevcount)){
        //         cout<<k<<" "<<" "<<o<<" "<<p<<" "<<dp[o][p]<<endl;
        //     }
        // }
        for(int i=0;i<(1<<15);i++){
            res += dp[m-1][i];
        }
        ans*=res;
        //cout<<dp[0][0]<<" "<<dp[0][1]<<endl;
    }
    cout<<ans<<endl;
    

   
}

signed main(){
	cin.tie(0);
	ios::sync_with_stdio(0);
	cout<<fixed<<setprecision(20);
	while(SOLVEFIN == 0) solve();
}
accuracy: 1
time_ms: 2470
memory_kb: 10932
final_rank: 9

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define all(v) (v).begin(),(v).end()
#define pb(a) push_back(a)
#define rep(i, n) for(int i=0;i<n;i++)
#define foa(e, v) for(auto&& e : v)
using ll = long long;
const ll MOD7 = 1000000007, MOD998 = 998244353, INF = (1LL << 60);
#define dout(a) cout<<fixed<<setprecision(10)<<a<<endl;

template<int MOD> struct Modint {
    long long val;
    constexpr Modint(long long v = 0) noexcept : val(v % MOD) { if (val < 0) val += MOD; }
    constexpr int mod() const { return MOD; }
    constexpr long long value() const { return val; }
    constexpr Modint operator - () const noexcept { return val ? MOD - val : 0; }
    constexpr Modint operator + (const Modint& r) const noexcept { return Modint(*this) += r; }
    constexpr Modint operator - (const Modint& r) const noexcept { return Modint(*this) -= r; }
    constexpr Modint operator * (const Modint& r) const noexcept { return Modint(*this) *= r; }
    constexpr Modint operator / (const Modint& r) const noexcept { return Modint(*this) /= r; }
    constexpr Modint& operator += (const Modint& r) noexcept {
        val += r.val;
        if (val >= MOD) val -= MOD;
        return *this;
    }
    constexpr Modint& operator -= (const Modint& r) noexcept {
        val -= r.val;
        if (val < 0) val += MOD;
        return *this;
    }
    constexpr Modint& operator *= (const Modint& r) noexcept {
        val = val * r.val % MOD;
        return *this;
    }
    constexpr Modint& operator /= (const Modint& r) noexcept {
        long long a = r.val, b = MOD, u = 1, v = 0;
        while (b) {
            long long t = a / b;
            a -= t * b, swap(a, b);
            u -= t * v, swap(u, v);
        }
        val = val * u % MOD;
        if (val < 0) val += MOD;
        return *this;
    }
    constexpr bool operator == (const Modint& r) const noexcept { return this->val == r.val; }
    constexpr bool operator != (const Modint& r) const noexcept { return this->val != r.val; }
    friend constexpr istream& operator >> (istream& is, Modint<MOD>& x) noexcept {
        is >> x.val;
        x.val %= MOD;
        if (x.val < 0) x.val += MOD;
        return is;
    }
    friend constexpr ostream& operator << (ostream& os, const Modint<MOD>& x) noexcept {
        return os << x.val;
    }
    constexpr Modint<MOD> pow(long long n) noexcept {
        if (n == 0) return 1;
        if (n < 0) return this->pow(-n).inv();
        Modint<MOD> ret = pow(n >> 1);
        ret *= ret;
        if (n & 1) ret *= *this;
        return ret;
    }
    constexpr Modint<MOD> inv() const noexcept {
        long long a = this->val, b = MOD, u = 1, v = 0;
        while (b) {
            long long t = a / b;
            a -= t * b, swap(a, b);
            u -= t * v, swap(u, v);
        }
        return Modint<MOD>(u);
    }
};

const int MOD = MOD7;
using mint = Modint<MOD>;

mint solve(vector<string> s, int m, int f) {
    int n = s.size();
    
    
    vector<pair<int, int>> vec;
    
    for(int j = 0; j < m; j ++) {
        for(int i = (j & 1 ? f^1 : f); i < n; i += 2) {
            vec.push_back({i, j});
        }
    }
    int sz = vec.size();
    int num = (n + 3) / 2;
    num = min(num, sz);
    int mask = 1 << num;
    vector<mint> dp(mask, mint(0));
    rep(bit, mask) {
        bool ng = 0;
        rep(i, num) if(bit >> i & 1) {
            if(s[vec[i].first][vec[i].second] == '#') {
                ng = 1;
                break;
            }
            for(int j = i + 1; j < num; j ++) {
                if(abs(vec[j].first - vec[i].first) == 1 and abs(vec[j].second - vec[i].second) == 1 and (bit >> j & 1)) {
                    ng = 1;
                    i = num;
                    break;
                }
            }
        }
        if(!ng) {
            dp[bit] = 1;
        }
    }
    
    for(int i = num; i < sz; i ++) {
        vector<mint> nx(mask, 0);
        rep(bit, mask) {
            nx[bit >> 1] += dp[bit];
            if(s[vec[i].first][vec[i].second] == '#') continue;
            bool ng = 0;
            for(int j = 0; j < 3; j ++) if(bit >> j & 1) {
                if(j >= num) break;
                int id = i - num + j;
                if(abs(vec[i].first - vec[id].first) == 1 and abs(vec[i].second - vec[id].second) == 1) {
                    ng = 1;
                    break;
                }
            }
            if(!ng) nx[(bit >> 1) | (1 << (num - 1))] += dp[bit];
        }
        swap(nx, dp);
    }
    mint ans = accumulate(all(dp), mint(0));
    return ans;
}
int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    int n, m; cin >> n >> m;
    while(n) {
        vector<string> s, t;
        rep(i, n) {
            string in; cin >> in;
            if(i & 1) s.pb(in);
            else t.pb(in);
        }
        cout << solve(t, m, 0) * solve(s, m, 0) * solve(t, m, 1) * solve(s, m, 1) << endl;  
        cin >> n >> m;
    }
    return 0;
}
accuracy: 1
time_ms: 3270
memory_kb: 4608
final_rank: 10

code_snippet: // (⁠◕⁠ᴗ⁠◕⁠✿⁠)

// #pragma GCC target("avx2")
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#include <bits/stdc++.h>
#define rep(i, n) for (ll i = 0; i < (n); i++)
#define srep(i, s, n) for (ll i = s; i < (n); i++)
#define len(x) ((int)(x).size())
#define all(x) (x).begin(), (x).end()
using namespace std;
template<typename T> using vc = vector<T>;
template<typename T> using vv = vc<vc<T>>;
template<typename T> using vvv = vv<vc<T>>;
using vi = vc<int>;using vvi = vv<int>; using vvvi = vv<vi>;
using ll = long long;using vl = vc<ll>;using vvl = vv<ll>; using vvvl = vv<vl>;
using ld = long double; using vld = vc<ld>; using vvld = vc<vld>; using vvvld = vc<vvld>;
using uint = unsigned int;
using ull = unsigned long long;
const ld pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1000000007;
// const ll mod = 998244353;
inline bool inside(ll y, ll x, ll H, ll W) {return 0 <= (y) and (y) < (H) and 0 <= (x) and (x) < (W); }

#define debug(var)  do{std::cout << #var << " : \n";view(var);}while(0)
template<typename T> void view(T e){cout << e << endl;}
template<typename T> void view(const vc<T>& v){for(const auto& e : v){ cout << e << " "; } cout << endl;}
template<typename T> void view(const vv<T>& vv){ for(const auto& v : vv){ view(v); } }

struct modint {
    int Mod = mod;
    int val;
    using mint = modint;
    modint() {};
    modint(ll v) {
        if (0 <= v && v < Mod) val = v;
        else{
            v %= Mod;
            if (v < 0) v += Mod;
            val = v;
        }
    }
    mint operator++(int){
        val++;
        if (val == Mod) val = 0;
        return *this;
    }
    mint operator--(int){
        if (val == 0) val = Mod;
        val--;
        return *this;
    }
    mint& operator+=(const mint& v){
        val += v.val;
        if (val >= Mod) val -= Mod;
        return *this;
    }
    mint& operator-=(const mint& v){
        val -= v.val;
        if (val < 0) val += Mod;
        return *this;
    }
    mint& operator*=(const mint& v){
        val = (int)(1ll * val * v.val % Mod);
        return *this;
    }
    modint inverse() const {
        int a = val, b = mod, u = 1, v = 0, t;
        while (b > 0) {
        t = a / b;
        swap(a -= t * b, b), swap(u -= t * v, v);
        }
        return modint(u);
    }
    mint pow(ll n) const {
        mint a = *this, ret = 1;
        while (n > 0){
            if (n & 1) ret *= a;
            a *= a;
            n >>= 1;
        }
        return ret;
    }
    mint& operator>>=(ll k){
        *this *= mint(2).pow(k).pow(Mod - 2);
        return *this;
    }
    mint& operator<<=(ll k){
        *this *= mint(2).pow(k);
        return *this;
}
    mint operator/=(const mint& v){
        *this *= v.inverse();
        return *this;
    }
    mint operator-() const { return mint(-val);}
    friend mint operator+(const mint& u, const mint& v){return mint(u) += v;}
    friend mint operator-(const mint& u, const mint& v){return mint(u) -= v;}
    friend mint operator*(const mint& u, const mint& v){return mint(u) *= v;}
    friend mint operator/(const mint& u, const mint& v){return mint(u) /= v;}
    friend bool operator==(const mint& u, const mint& v){return u.val == v.val;}
    friend bool operator!=(const mint& u, const mint& v){return u.val != v.val;}
    friend bool operator>(const mint& u, const mint& v){return u.val > v.val;}
    friend bool operator<(const mint& u, const mint& v){return u.val < v.val;}
    friend mint operator>>(mint& u, const ll k){return u >>= k;}
    friend mint operator<<(mint& u, const ll k){return u <<= k;}
    friend ostream& operator<<(ostream& stream, mint& v){
        stream << v.val;
        return stream;
    }
};

using mint = modint;

mint dp[2][1 << 18];

mint f(vc<string> &S){
    int nw = 0, nxt = 1;
    int H = len(S), W = len(S[0]);
    rep(j, 1 << (W + 1)) dp[nw][j] = 0;
    dp[0][0] = 1;
    rep(i, H * W){
        int x = i / W, y = i % W;
        rep(j, 1 << (W + 1)) dp[nxt][j] = 0;
        rep(j, 1 << (W + 1)) dp[nxt][j >> 1] += dp[nw][j];
        if (S[x][y] == '.'){
            rep(j, 1 << (W + 1)){
                bool flag = true;
                if (j >> 1 & 1) flag = false;
                if (x % 2 && y > 0 && j & 1) flag = false;
                if (x % 2 == 0 && y < W - 1 && j >> 2 & 1) flag = false;
                if (flag) dp[nxt][(j >> 1) | (1 << W)] += dp[nw][j];
            }
        }
        swap(nw, nxt);
    }
    mint ans = 0;
    rep(j, 1 << (W + 1)) ans += dp[nw][j];
    return ans;
}

void solve(int N, int M){
    vc<string> A(N); rep(i, N) cin >> A[i];
    mint ans = 1;
    rep(k, 4){
        vc<string> B(M, string(N, '#'));
        rep(j, M){
            for (int i = k - (j % 2) * 2, ni = 0; i < N; i += 4, ni++) if (inside(i, j, N, M)){
                B[j][ni] = A[i][j];
            }
        }
        while (len(B[0]) >= 2){
            bool flag = true;
            rep(i, M) if (B[i].back() == '.'){
                flag = false;
                break;
            }
            if (!flag) break;
            rep(i, M) B[i].pop_back();
        }
        ans *= f(B);
    }
    cout << ans << endl;
}

int main(){
    while (true){
        int N, M; cin >> N >> M;
        if (N == 0) break;
        solve(N, M);
    }
}
accuracy: 1
time_ms: 3080
memory_kb: 7444
final_rank: 11

code_snippet: #include<bits/stdc++.h>
// #include<atcoder/all>
// #include<boost/multiprecision/cpp_int.hpp>

using namespace std;
// using namespace atcoder;
// using bint = boost::multiprecision::cpp_int;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
using P = pair<ll,ll>;
using vi = vector<ll>;
using vvi = vector<vi>;
using vvvi = vector<vvi>;
using ve = vector<vector<int>>;
using vb = vector<bool>;
using vvb = vector<vb>;
#define rep(i,n) for(ll i = 0;i < (ll)n;i++)
#define ALL(x) (x).begin(),(x).end()
#define sz(c) ((ll)(c).size())
#define LB(A,x) (int)(lower_bound(A.begin(),A.end(),x)-A.begin())
#define UB(A,x) (int)(upper_bound(A.begin(),A.end(),x)-A.begin())
#define MOD 1000000007
// #define MOD 998244353
template<typename T>using min_priority_queue=priority_queue<T,vector<T>,greater<T>>;
template<typename T>ostream&operator<<(ostream&os,vector<T>&v){for(int i = 0;i < v.size();i++)os<<v[i]<<(i+1!=v.size()?" ":"");return os;}
template<typename T>istream&operator>>(istream&is,vector<T>&v){for(T&in:v)is>>in;return is;}
template<typename T1,typename T2>ostream&operator<<(ostream&os,pair<T1,T2>&p){os<<p.first<<" "<<p.second;return os;}
template<typename T1,typename T2>istream&operator>>(istream&is,pair<T1,T2>&p){is>>p.first>>p.second;return is;}
template<typename T> inline bool chmax(T &a,T b){if(a < b){a = b;return true;}return false;}
template<typename T> inline bool chmin(T &a,T b){if(a > b){a = b;return true;}return false;}
ld dist(ld x1,ld y1,ld x2, ld y2){return sqrtl((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));}


template< int mod >
struct ModInt {
  int x;

  ModInt() : x(0) {}

  ModInt(int64_t y) : x(y >= 0 ? y % mod : (mod - (-y) % mod) % mod) {}

  ModInt &operator+=(const ModInt &p) {
    if((x += p.x) >= mod) x -= mod;
    return *this;
  }

  ModInt &operator-=(const ModInt &p) {
    if((x += mod - p.x) >= mod) x -= mod;
    return *this;
  }

  ModInt &operator*=(const ModInt &p) {
    x = (int) (1LL * x * p.x % mod);
    return *this;
  }

  ModInt &operator/=(const ModInt &p) {
    *this *= p.inverse();
    return *this;
  }

  ModInt operator-() const { return ModInt(-x); }

  ModInt operator+(const ModInt &p) const { return ModInt(*this) += p; }

  ModInt operator-(const ModInt &p) const { return ModInt(*this) -= p; }

  ModInt operator*(const ModInt &p) const { return ModInt(*this) *= p; }

  ModInt operator/(const ModInt &p) const { return ModInt(*this) /= p; }

  bool operator==(const ModInt &p) const { return x == p.x; }

  bool operator!=(const ModInt &p) const { return x != p.x; }

  ModInt inverse() const {
    int a = x, b = mod, u = 1, v = 0, t;
    while(b > 0) {
      t = a / b;
      swap(a -= t * b, b);
      swap(u -= t * v, v);
    }
    return ModInt(u);
  }

  ModInt pow(int64_t n) const {
    ModInt ret(1), mul(x);
    while(n > 0) {
      if(n & 1) ret *= mul;
      mul *= mul;
      n >>= 1;
    }
    return ret;
  }

  friend ostream &operator<<(ostream &os, const ModInt &p) {
    return os << p.x;
  }

  friend istream &operator>>(istream &is, ModInt &a) {
    int64_t t;
    is >> t;
    a = ModInt< mod >(t);
    return (is);
  }

  static int get_mod() { return mod; }
};

using modint = ModInt< MOD >;
using mvi = vector<modint>;
using mvvi = vector<mvi>;

int main(){
  
  ios_base::sync_with_stdio(0), cin.tie(0);
  while(1){
    int n,m;cin >> n >> m;
    if(!n)break;
    vector<string> a(n);
    cin >> a;
    modint res = 0;
    auto c2 = [&](vi &od,vi &ev)->modint{
      int N = max(sz(od),sz(ev));
      mvi dp(1ll << N);
      dp[0] = 1;
      rep(j,m){
        mvi DP(1ll << N);
        if(!(j & 1)){
          for(int bit = 0;bit < 1ll << sz(ev);bit++){
            bool is = true;
            rep(i,sz(ev))if((bit >> i & 1) && a[ev[i]][j] == '#'){
              is = false;break;
            }
            if(!is)continue;
            int msk = (1ll << sz(od))-1;
            rep(i,sz(ev))if(bit >> i & 1){
              int k = (ev[i]+2)/2;k = k/2;
              if(k < sz(od))msk = msk & (~(1ll << k));
              if(ev[i] < 2)continue;
              k = (ev[i]-2)/2;k = k/2;
              if(k < sz(od))msk = msk & (~(1ll << k));
            }
            for(int S = msk;S > 0;S = (S-1) & msk){
              DP[bit] += dp[S];
            }
            DP[bit] += dp[0];
          }
        }else{
          for(int bit = 0;bit < 1ll << sz(od);bit++){
            bool is = true;
            rep(i,sz(od))if((bit >> i & 1) && a[od[i]][j] == '#'){
              is = false;break;
            }
            if(!is)continue;
            int msk = (1ll << sz(ev))-1;
            rep(i,sz(od))if(bit >> i & 1){
              int k = (od[i]+2)/2;k = k/2;
              if(k < sz(ev))msk = msk & (~(1ll << k));
              if(od[i] < 2)continue;
              k = (od[i]-2)/2;k = k/2;
              if(k < sz(ev))msk = msk & (~(1ll << k));
            }
            for(int S = msk;S > 0;S = (S-1) & msk){
              DP[bit] += dp[S];
            }
            DP[bit] += dp[0];
          }
        }
        swap(dp,DP);
      }
      modint ret = 0;
      rep(i,1ll << N)ret += dp[i];
      return ret;
    };
    auto calc = [&](vi &p)->modint{
      int N = sz(p);
      vi ev,od;
      rep(i,N){
        if(!(i & 1))ev.emplace_back(p[i]);
        else od.emplace_back(p[i]);
      }
      modint ret = c2(od,ev);
      ret = ret*c2(ev,od);
      return ret;
    };
    
    vi p((n+1)/2);
    for(int i = 0;i < n;i+=2)p[i/2] = i;
    res = calc(p);
    // cout << res << " a\n";
    p.resize(n/2);
    for(int i = 1;i < n;i+=2)p[i/2] = i;
    res = res*calc(p);
    // cout << calc(p) << " a\n";
    cout << res << "\n";
  }
  

  return 0;
}
accuracy: 1
time_ms: 5680
memory_kb: 3456
final_rank: 15

code_snippet: #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef vector<ll> vi;
typedef vector<vi> vvi;
typedef vector<vvi> vvvi;
typedef vector<bool> vb;
typedef vector<vb> vvb;
typedef vector<vvb> vvvb;
typedef vector<vvvb> vvvvb;
typedef pair<ll,ll> pi;
typedef pair<ll,pi> ppi;
#define FOR(i,l,r) for(ll i=l;i<r;i++)
#define REP(i,n) FOR(i,0,n)
#define RFOR(i,l,r) for(ll i=r-1;i>=l;i--)
#define RREP(i,n) RFOR(i,0,n)
#define sz(A) (ll)(A.size())
#define ALL(A) A.begin(),A.end()
#define LB(A,x) (ll)(lower_bound(ALL(A),x)-A.begin())
#define UB(A,x) (ll)(upper_bound(ALL(A),x)-A.begin())
#define COU(A,x) (UB(A,x)-LB(A,x))
#define F first
#define S second
template<typename T>using min_priority_queue=priority_queue<T,vector<T>,greater<T>>;
template<typename T1,typename T2>ostream&operator<<(ostream&os,pair<T1,T2>&p){os<<p.F<<" "<<p.S;return os;}
template<typename T1,typename T2>istream&operator>>(istream&is,pair<T1,T2>&p){is>>p.F>>p.S;return is;}
template<typename T>ostream&operator<<(ostream&os,vector<T>&v){REP(i,sz(v))os<<v[i]<<(i+1!=sz(v)?" ":"");return os;}
template<typename T>istream&operator>>(istream&is,vector<T>&v){for(T&in:v)is>>in;return is;}
template<class T>bool chmax(T&a,T b){if(a<b){a=b;return 1;}return 0;}
template<class T>bool chmin(T&a,T b){if(b<a){a=b;return 1;}return 0;}
const ll mod=998244353;
template<const long long int mod=998244353>
struct modint{
  using mint=modint<mod>;
  long long int x;
  modint(long long int _x=0):x(_x%mod){if(x<0)x+=mod;}
  long long int val(){return x;}
  mint&operator=(const mint&a){x=a.x;return *this;}
  mint&operator+=(const mint&a){x+=a.x;if(x>=mod)x-=mod;return *this;}
  mint&operator-=(const mint&a){x-=a.x;if(x<0)x+=mod;return *this;}
  mint&operator*=(const mint&a){x*=a.x;x%=mod;return *this;}
  friend mint operator+(const mint&a,const mint&b){return mint(a)+=b;}
  friend mint operator-(const mint&a,const mint&b){return mint(a)-=b;}
  friend mint operator*(const mint&a,const mint&b){return mint(a)*=b;}
  mint operator-()const{return mint(0)-*this;}
  mint pow(long long int n){
    if(!n)return 1;
    mint a=1;
    mint _x=x;
    while(n){
      if(n&1)a*=_x;
      _x=_x*_x;n>>=1;
    }
    return a;
  }
  mint inv(){return pow(mod-2);}
  mint&operator/=(mint&a){return *this*=a.inv();}
  friend mint operator/(const mint&a,mint b){return mint(a)/=b;}
};
//using mint=modint<998244353>;
using mint=modint<1000000007>;
vvi LS(1<<15),SL(1<<15);
void init(){
    REP(i,1<<15){
        ll s=(1<<15)-1;
        REP(j,15)if((i>>j)%2){
            s-=s&(1<<j);
            if(j)s-=s&(1<<(j-1));
        }
        for(ll j=s;j;j=(j-1)&s)LS[i].emplace_back(j);
        LS[i].emplace_back(0);
        reverse(ALL(LS[i]));
    }
    REP(i,1<<15){
        ll s=(1<<15)-1;
        REP(j,15)if((i>>j)%2){
            s-=s&(1<<j);
            if(j+1<15)s-=s&(1<<(j+1));
        }
        for(ll j=s;j;j=(j-1)&s)SL[i].emplace_back(j);
        SL[i].emplace_back(0);
        reverse(ALL(SL[i]));
    }
}
mint f(vector<string>S,ll H,ll W){
    mint ans1=0,ans2=0;
    {
        //even part
        vi A(W);
        REP(i,W)REP(j,H)if((i+j)%2==0&&S[j][i]=='.')A[i]+=1<<(j/2);
        vector<vector<mint>>DP(W,vector<mint>(1<<((H+1)/2)));
        for(ll i=A[0];i;i=(i-1)&A[0])DP[0][i]=1;DP[0][0]=1;
        REP(i,W-1)REP(j,1<<((H+1-i%2)/2)){
            if(i%2){
                //SL
                for(auto k:SL[j])if((k&A[i+1])==k)DP[i+1][k]+=DP[i][j];
            }
            else{
                //LS
                for(auto k:LS[j])if((k&A[i+1])==k)DP[i+1][k]+=DP[i][j];
            }
        }
        REP(i,sz(DP[W-1]))ans1+=DP[W-1][i];
    }
    {
        //odd part
        vi A(W);
        REP(i,W)REP(j,H)if((i+j)%2==1&&S[j][i]=='.')A[i]+=1<<(j/2);
        vector<vector<mint>>DP(W,vector<mint>(1<<((H+1)/2)));
        for(ll i=A[0];i;i=(i-1)&A[0])DP[0][i]=1;DP[0][0]=1;
        REP(i,W-1)REP(j,1<<((H+i%2)/2)){
            if(i%2==0){
                //SL
                for(auto k:SL[j])if((k&A[i+1])==k)DP[i+1][k]+=DP[i][j];
            }
            else{
                //LS
                for(auto k:LS[j])if((k&A[i+1])==k)DP[i+1][k]+=DP[i][j];
            }
        }
        REP(i,sz(DP[W-1]))ans2+=DP[W-1][i];
    }
    return ans1*ans2;
}
int main(){
    init();
    while(1){
        ll H,W;cin>>H>>W;
        if(!H)return 0;
        vector<string>S(H);cin>>S;
        vector<string>S1,S2;
        REP(i,H)(i%2?S2:S1).emplace_back(S[i]);
        mint ans=f(S1,sz(S1),W)*f(S2,sz(S2),W);
        cout<<ans.val()<<endl;
    }
    return 0;
}
accuracy: 1
time_ms: 4330
memory_kb: 55440
final_rank: 20

code_snippet: #include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b) for (int i = (int)(a); i < (int)(b); i++)
#define rrep(i, a, b) for (int i = (int)(b)-1; i >= (int)(a); i--)
#define ALL(v) (v).begin(), (v).end()
#define UNIQUE(v) sort(ALL(v)), (v).erase(unique(ALL(v)), (v).end())
#define SZ(v) (int)v.size()
#define MIN(v) *min_element(ALL(v))
#define MAX(v) *max_element(ALL(v))
#define LB(v, x) int(lower_bound(ALL(v), (x)) - (v).begin())
#define UB(v, x) int(upper_bound(ALL(v), (x)) - (v).begin())

using uint = unsigned int;
using ll = long long int;
using ull = unsigned long long;
using i128 = __int128_t;
using u128 = __uint128_t;
const int inf = 0x3fffffff;
const ll INF = 0x1fffffffffffffff;

template <typename T> inline bool chmax(T &a, T b) {
    if (a < b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T> inline bool chmin(T &a, T b) {
    if (a > b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T, typename U> T ceil(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? (x + y - 1) / y : x / y);
}
template <typename T, typename U> T floor(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? x / y : (x - y + 1) / y);
}
template <typename T> int popcnt(T x) {
    return __builtin_popcountll(x);
}
template <typename T> int topbit(T x) {
    return (x == 0 ? -1 : 63 - __builtin_clzll(x));
}
template <typename T> int lowbit(T x) {
    return (x == 0 ? -1 : __builtin_ctzll(x));
}

template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << "P(" << p.first << ", " << p.second << ")";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const vector<T> &vec) {
    os << "{";
    for (int i = 0; i < vec.size(); i++) {
        os << vec[i] << (i + 1 == vec.size() ? "" : ", ");
    }
    os << "}";
    return os;
}
template <typename T, typename U>
ostream &operator<<(ostream &os, const map<T, U> &map_var) {
    os << "{";
    for (auto itr = map_var.begin(); itr != map_var.end(); itr++) {
        os << "(" << itr->first << ", " << itr->second << ")";
        itr++;
        if (itr != map_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const set<T> &set_var) {
    os << "{";
    for (auto itr = set_var.begin(); itr != set_var.end(); itr++) {
        os << *itr;
        ++itr;
        if (itr != set_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
#ifdef LOCAL
#define show(...) _show(0, #__VA_ARGS__, __VA_ARGS__)
#else
#define show(...) true
#endif
template <typename T> void _show(int i, T name) {
    cerr << '\n';
}
template <typename T1, typename T2, typename... T3>
void _show(int i, const T1 &a, const T2 &b, const T3 &...c) {
    for (; a[i] != ',' && a[i] != '\0'; i++)
        cerr << a[i];
    cerr << ":" << b << " ";
    _show(i + 1, a, c...);
}

#include <unistd.h>

namespace fastio {
static constexpr uint32_t SZ = 1 << 17;
char ibuf[SZ];
char obuf[SZ];
char out[100];
// pointer of ibuf, obuf


uint32_t pil = 0, pir = 0, por = 0;

struct Pre {
    char num[10000][4];
    constexpr Pre() : num() {
        for (int i = 0; i < 10000; i++) {
            int n = i;
            for (int j = 3; j >= 0; j--) {
                num[i][j] = n % 10 | '0';
                n /= 10;
            }
        }
    }
} constexpr pre;

inline void load() {
    memmove(ibuf, ibuf + pil, pir - pil);
    pir = pir - pil + fread(ibuf + pir - pil, 1, SZ - pir + pil, stdin);
    pil = 0;
    if (pir < SZ)
        ibuf[pir++] = '\n';
}

inline void flush() {
    fwrite(obuf, 1, por, stdout);
    por = 0;
}

void rd(char &c) {
    do {
        if (pil + 1 > pir)
            load();
        c = ibuf[pil++];
    } while (isspace(c));
}

void rd(string &x) {
    x.clear();
    char c;
    do {
        if (pil + 1 > pir)
            load();
        c = ibuf[pil++];
    } while (isspace(c));
    do {
        x += c;
        if (pil == pir)
            load();
        c = ibuf[pil++];
    } while (!isspace(c));
}

template <typename T> void rd_real(T &x) {
    string s;
    rd(s);
    x = stod(s);
}

template <typename T> void rd_integer(T &x) {
    if (pil + 100 > pir)
        load();
    char c;
    do
        c = ibuf[pil++];
    while (c < '-');
    bool minus = 0;
    if constexpr (is_signed<T>::value || is_same_v<T, i128>) {
        if (c == '-') {
            minus = 1, c = ibuf[pil++];
        }
    }
    x = 0;
    while ('0' <= c) {
        x = x * 10 + (c & 15), c = ibuf[pil++];
    }
    if constexpr (is_signed<T>::value || is_same_v<T, i128>) {
        if (minus)
            x = -x;
    }
}

void rd(int &x) {
    rd_integer(x);
}
void rd(ll &x) {
    rd_integer(x);
}
void rd(i128 &x) {
    rd_integer(x);
}
void rd(uint &x) {
    rd_integer(x);
}
void rd(ull &x) {
    rd_integer(x);
}
void rd(u128 &x) {
    rd_integer(x);
}
void rd(double &x) {
    rd_real(x);
}
void rd(long double &x) {
    rd_real(x);
}

template <class T, class U> void rd(pair<T, U> &p) {
    return rd(p.first), rd(p.second);
}
template <size_t N = 0, typename T> void rd_tuple(T &t) {
    if constexpr (N < std::tuple_size<T>::value) {
        auto &x = std::get<N>(t);
        rd(x);
        rd_tuple<N + 1>(t);
    }
}
template <class... T> void rd(tuple<T...> &tpl) {
    rd_tuple(tpl);
}

template <size_t N = 0, typename T> void rd(array<T, N> &x) {
    for (auto &d : x)
        rd(d);
}
template <class T> void rd(vector<T> &x) {
    for (auto &d : x)
        rd(d);
}

void read() {}
template <class H, class... T> void read(H &h, T &...t) {
    rd(h), read(t...);
}

void wt(const char c) {
    if (por == SZ)
        flush();
    obuf[por++] = c;
}
void wt(const string s) {
    for (char c : s)
        wt(c);
}
void wt(const char *s) {
    size_t len = strlen(s);
    for (size_t i = 0; i < len; i++)
        wt(s[i]);
}

template <typename T> void wt_integer(T x) {
    if (por > SZ - 100)
        flush();
    if (x < 0) {
        obuf[por++] = '-', x = -x;
    }
    int outi;
    for (outi = 96; x >= 10000; outi -= 4) {
        memcpy(out + outi, pre.num[x % 10000], 4);
        x /= 10000;
    }
    if (x >= 1000) {
        memcpy(obuf + por, pre.num[x], 4);
        por += 4;
    } else if (x >= 100) {
        memcpy(obuf + por, pre.num[x] + 1, 3);
        por += 3;
    } else if (x >= 10) {
        int q = (x * 103) >> 10;
        obuf[por] = q | '0';
        obuf[por + 1] = (x - q * 10) | '0';
        por += 2;
    } else
        obuf[por++] = x | '0';
    memcpy(obuf + por, out + outi + 4, 96 - outi);
    por += 96 - outi;
}

template <typename T> void wt_real(T x) {
    ostringstream oss;
    oss << fixed << setprecision(15) << double(x);
    string s = oss.str();
    wt(s);
}

void wt(int x) {
    wt_integer(x);
}
void wt(ll x) {
    wt_integer(x);
}
void wt(i128 x) {
    wt_integer(x);
}
void wt(uint x) {
    wt_integer(x);
}
void wt(ull x) {
    wt_integer(x);
}
void wt(u128 x) {
    wt_integer(x);
}
void wt(double x) {
    wt_real(x);
}
void wt(long double x) {
    wt_real(x);
}

template <class T, class U> void wt(const pair<T, U> val) {
    wt(val.first);
    wt(' ');
    wt(val.second);
}
template <size_t N = 0, typename T> void wt_tuple(const T t) {
    if constexpr (N < std::tuple_size<T>::value) {
        if constexpr (N > 0) {
            wt(' ');
        }
        const auto x = std::get<N>(t);
        wt(x);
        wt_tuple<N + 1>(t);
    }
}
template <class... T> void wt(tuple<T...> tpl) {
    wt_tuple(tpl);
}
template <class T, size_t S> void wt(const array<T, S> val) {
    auto n = val.size();
    for (size_t i = 0; i < n; i++) {
        if (i)
            wt(' ');
        wt(val[i]);
    }
}
template <class T> void wt(const vector<T> val) {
    auto n = val.size();
    for (size_t i = 0; i < n; i++) {
        if (i)
            wt(' ');
        wt(val[i]);
    }
}

void print() {
    wt('\n');
}
template <class Head, class... Tail> void print(Head &&head, Tail &&...tail) {
    wt(head);
    if (sizeof...(Tail))
        wt(' ');
    print(forward<Tail>(tail)...);
}
void __attribute__((destructor)) _d() {
    flush();
}
} // namespace fastio


using fastio::flush;
using fastio::print;
using fastio::read;

inline void first(bool i = true) {
    print(i ? "first" : "second");
}
inline void Alice(bool i = true) {
    print(i ? "Alice" : "Bob");
}
inline void Takahashi(bool i = true) {
    print(i ? "Takahashi" : "Aoki");
}
inline void yes(bool i = true) {
    print(i ? "yes" : "no");
}
inline void Yes(bool i = true) {
    print(i ? "Yes" : "No");
}
inline void No() {
    print("No");
}
inline void YES(bool i = true) {
    print(i ? "YES" : "NO");
}
inline void NO() {
    print("NO");
}
inline void Yay(bool i = true) {
    print(i ? "Yay!" : ":(");
}
inline void Possible(bool i = true) {
    print(i ? "Possible" : "Impossible");
}
inline void POSSIBLE(bool i = true) {
    print(i ? "POSSIBLE" : "IMPOSSIBLE");
}

/**
 * @brief Fast IO
 */

template <unsigned mod = 1000000007> struct fp {
    unsigned v;
    static constexpr int get_mod() {
        return mod;
    }
    constexpr unsigned inv() const {
        assert(v != 0);
        int x = v, y = mod, p = 1, q = 0, t = 0, tmp = 0;
        while (y > 0) {
            t = x / y;
            x -= t * y, p -= t * q;
            tmp = x, x = y, y = tmp;
            tmp = p, p = q, q = tmp;
        }
        if (p < 0)
            p += mod;
        return p;
    }
    constexpr fp(ll x = 0) : v(x >= 0 ? x % mod : (mod - (-x) % mod) % mod) {}
    fp operator-() const {
        return fp() - *this;
    }
    fp pow(ull t) {
        fp res = 1, b = *this;
        while (t) {
            if (t & 1)
                res *= b;
            b *= b;
            t >>= 1;
        }
        return res;
    }
    fp &operator+=(const fp &x) {
        if ((v += x.v) >= mod)
            v -= mod;
        return *this;
    }
    fp &operator-=(const fp &x) {
        if ((v += mod - x.v) >= mod)
            v -= mod;
        return *this;
    }
    fp &operator*=(const fp &x) {
        v = ull(v) * x.v % mod;
        return *this;
    }
    fp &operator/=(const fp &x) {
        v = ull(v) * x.inv() % mod;
        return *this;
    }
    fp operator+(const fp &x) const {
        return fp(*this) += x;
    }
    fp operator-(const fp &x) const {
        return fp(*this) -= x;
    }
    fp operator*(const fp &x) const {
        return fp(*this) *= x;
    }
    fp operator/(const fp &x) const {
        return fp(*this) /= x;
    }
    bool operator==(const fp &x) const {
        return v == x.v;
    }
    bool operator!=(const fp &x) const {
        return v != x.v;
    }
    friend istream &operator>>(istream &is, fp &x) {
        return is >> x.v;
    }
    friend ostream &operator<<(ostream &os, const fp &x) {
        return os << x.v;
    }
};

template <unsigned mod> void rd(fp<mod> &x) {
    fastio::rd(x.v);
}
template <unsigned mod> void wt(fp<mod> x) {
    fastio::wt(x.v);
}

template <typename T> T Inv(ll n) {
    static const int md = T::get_mod();
    static vector<T> buf({0, 1});
    assert(n > 0);
    n %= md;
    while (SZ(buf) <= n) {
        int k = SZ(buf), q = (md + k - 1) / k;
        buf.push_back(buf[k * q - md] * q);
    }
    return buf[n];
}

template <typename T> T Fact(ll n, bool inv = 0) {
    static const int md = T::get_mod();
    static vector<T> buf({1, 1}), ibuf({1, 1});
    assert(n >= 0 and n < md);
    while (SZ(buf) <= n) {
        buf.push_back(buf.back() * SZ(buf));
        ibuf.push_back(ibuf.back() * Inv<T>(SZ(ibuf)));
    }
    return inv ? ibuf[n] : buf[n];
}

template <typename T> T nPr(int n, int r, bool inv = 0) {
    if (n < 0 || n < r || r < 0)
        return 0;
    return Fact<T>(n, inv) * Fact<T>(n - r, inv ^ 1);
}
template <typename T> T nCr(int n, int r, bool inv = 0) {
    if (n < 0 || n < r || r < 0)
        return 0;
    return Fact<T>(n, inv) * Fact<T>(r, inv ^ 1) * Fact<T>(n - r, inv ^ 1);
}
template <typename T> T nHr(int n, int r, bool inv = 0) {
    return nCr<T>(n + r - 1, r, inv);
}

/**
 * @brief Modint
 */

using Fp = fp<1000000007>;

int main() {
while(1) {
    int N, M;
    cin >> N >> M;
    if (N == 0 && M == 0) return 0;
    vector X(N, vector<bool>(M));
    rep(i,0,N) {
        rep(j,0,M) {
            char C;
            cin >> C;
            if (C == '.') X[i][j] = true;
            else X[i][j] = false;
        }
    }
    Fp ANS = 1;
    rep(_,0,4) {
        vector<vector<int>> V(M);
        vector<int> W(M), B(M,0);
        rep(j,0,M) {
            rep(i,0,N) {
                if ((i+j*2)%4==_) V[j].push_back(i), B[j]+=(1<<(i/4))*(int)(X[i][j]);
            }
            W[j]=V[j].size();
        }
        vector<Fp> DP(1<<W[0],0);
        rep(i,0,1<<W[0]) {
            if ((i&B[0])==i) DP[i]=1;
        }
        rep(j,1,M) {
            vector<Fp> DP2(1<<W[j],0);
            rep(k,0,1<<W[j-1]) {
                vector<bool> Flag(W[j]);
                rep(l,0,W[j]) Flag[l] = X[V[j][l]][j];
                rep(l,0,W[j-1]) {
                    if ((k & (1<<l)) != 0) {
                        int Now = V[j-1][l];
                        if (Now-2 >= 0) {
                            Flag[(Now-2)/4] = false;
                        }
                        if (Now+2 < N) {
                            Flag[(Now+2)/4] = false;
                        }
                    }
                }
                int XX = 0;
                rep(l,0,W[j]) if (Flag[l]) XX += (1<<l);
                DP2[XX] += DP[k];
            }
            rep(l,0,W[j]) {
                rep(m,0,1<<W[j]) {
                    if ((m & (1<<l)) == 0) DP2[m] += DP2[m | (1<<l)];
                }
            }
            DP.resize(1<<W[j]);
            rep(k,0,1<<W[j]) DP[k] = DP2[k];
        }
        Fp COUNT = 0;
        rep(__,0,1<<W[M-1]) COUNT += DP[__];
        ANS *= COUNT;
    }
    cout << ANS << endl;
}
}
accuracy: 1
time_ms: 6860
memory_kb: 3700
final_rank: 19

code_snippet: #include<bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i,n) for(ll i=0;i<ll(n);i++)
#define rep2(i,m,n) for(ll i=ll(m);i<ll(n);i++)
using vl = vector<ll>;
using vvl = vector<vl>;
using vvvl = vector<vvl>;
using pl = pair<ll,ll>;
using vpl = vector<pl>;
#define pb push_back

const long double EPS = 0.0000000001;
const ll INF = 1000000000000000000;
const double pi = std::acos(-1.0);

__int128 read_int128(){ //__int128を入力する
  string S;
  cin >> S;
  int N = S.size();
  int st = 0;
  bool minus = false;
  if(S[0] == '-'){
    minus = true;
    st = 1;
  }
  __int128 res = 0;
  rep2(i,st,N) res = res*10+int(S[i]-'0');
  if(minus) res *= -1;
  return res;
}

std::ostream &operator<<(std::ostream &dest, __int128_t value) { //これでcoutで__int128を出力できるように
  std::ostream::sentry s(dest);
  if (s) {
    __uint128_t tmp = value < 0 ? -value : value;
    char buffer[128];
    char *d = std::end(buffer);
    do {
      --d;
      *d = "0123456789"[tmp % 10];
      tmp /= 10;
    } while (tmp != 0);
    if (value < 0) {
      --d;
      *d = '-';
    }
    int len = std::end(buffer) - d;
    if (dest.rdbuf()->sputn(d, len) != len) {
      dest.setstate(std::ios_base::badbit);
    }
  }
  return dest;
}

void Yes(){ cout << "Yes" << endl; } //文字列"Yes"を標準出力
void No(){ cout << "No" << endl; } //文字列"No"を標準出力

void Sort(vl &A){ sort(A.begin(),A.end()); } //long longの配列を昇順ソート(破壊的)
void rSort(vl &A){ sort(A.rbegin(),A.rend()); } //long longの配列を降順ソート(破壊的)
vl Sorted(vl A){ //long longの配列を昇順ソートしたものを返す(非破壊的)
  vl res = A;
  Sort(res);
  return res;
}
vl rSorted(vl A){ //long longの配列を降順ソートしたものを返す(非破壊的)
  vl res = A;
  rSort(res);
  return res;
}

struct cumulative_sum1d{ //1次元累積和
  int N;
  vl S;
  cumulative_sum1d(int n,vl A):N(),S(n+1,0){
    N = n;
    rep(i,N) S[i+1] = S[i]+A[i];
  }
  
  ll sum(int l,int r){ //区間[l,r]の和を出力(0<=l<=r<=N-1)
    return S[r+1]-S[l];
  }
};

template<class T> bool chmin(T& a,T b){
  if(a > b){
    a = b;
    return true;
  }
  else return false;
}

template<class T> bool chmax(T& a,T b){
  if(a < b){
    a = b;
    return true;
  }
  else return false;
}

/*
辺の貼り方
xi=0ならコストAi：s->iにAi
xi=1ならコストBi：i->tにBi
正負によってはポテンシャル付ける

xi=1かつxj=0のときコストC：i->jにC
xi=1かつxj=1のとき利得P：s->iにP、i->jにP、-Pのポテンシャル
xi=0かつxj=0のとき利得P：i->jにP、j->tにP、-Pのポテンシャル

コストがxi=0,xj=0でA、xi=0,xj=1でB、xi=1,xj=0でC、xi=1,xj=1でDで、B+C >= A+Dとする。
i->tにB-A、j->tにD-B、i->jにB+C-A-D、Aのポテンシャル

*/

template< typename flow_t >
struct Dinic {
  const flow_t INF;

  struct edge {
    int to;
    flow_t cap;
    int rev;
    bool isrev;
    int idx;
  };

  vector< vector< edge > > graph;
  vector< int > min_cost, iter;

  Dinic(int V) : INF(numeric_limits< flow_t >::max()), graph(V) {}

  void add_edge(int from, int to, flow_t cap, int idx = -1) {
    graph[from].emplace_back((edge) {to, cap, (int) graph[to].size(), false, idx});
    graph[to].emplace_back((edge) {from, 0, (int) graph[from].size() - 1, true, idx});
  }

  bool bfs(int s, int t) {
    min_cost.assign(graph.size(), -1);
    queue< int > que;
    min_cost[s] = 0;
    que.push(s);
    while(!que.empty() && min_cost[t] == -1) {
      int p = que.front();
      que.pop();
      for(auto &e : graph[p]) {
        if(e.cap > 0 && min_cost[e.to] == -1) {
          min_cost[e.to] = min_cost[p] + 1;
          que.push(e.to);
        }
      }
    }
    return min_cost[t] != -1;
  }

  flow_t dfs(int idx, const int t, flow_t flow) {
    if(idx == t) return flow;
    for(int &i = iter[idx]; i < graph[idx].size(); i++) {
      edge &e = graph[idx][i];
      if(e.cap > 0 && min_cost[idx] < min_cost[e.to]) {
        flow_t d = dfs(e.to, t, min(flow, e.cap));
        if(d > 0) {
          e.cap -= d;
          graph[e.to][e.rev].cap += d;
          return d;
        }
      }
    }
    return 0;
  }

  flow_t max_flow(int s, int t) {
    flow_t flow = 0;
    while(bfs(s, t)) {
      iter.assign(graph.size(), 0);
      flow_t f = 0;
      while((f = dfs(s, t, INF)) > 0) flow += f;
    }
    return flow;
  }

  void output() {
    for(int i = 0; i < graph.size(); i++) {
      for(auto &e : graph[i]) {
        if(e.isrev) continue;
        auto &rev_e = graph[e.to][e.rev];
        cout << i << "->" << e.to << " (flow: " << rev_e.cap << "/" << e.cap + rev_e.cap << ")" << endl;
      }
    }
  }
};

const long long int MOD = 1000000007;

long long int modpow(long long int a,long long int n,long long int mod){
  long long int res = 1;
  while(n>0){
    if(n&1) res = res*a%mod;
    a = a*a%mod;
    n >>= 1;
  }
  return res;
}

const long long int MAX = 510000; //最大

long long int fac[MAX], finv[MAX], inv[MAX];

void COMinit() {
  fac[0] = fac[1] = 1;
  finv[0] = finv[1] = 1;
  inv[1] = 1;
  for(int i=2;i<MAX;i++){
    fac[i] = fac[i-1]*i%MOD;
    inv[i] = MOD-inv[MOD%i]*(MOD/i)%MOD;
    finv[i] = finv[i-1]*inv[i]%MOD;
  }
}

long long int COM(long long int n,long long int k){
  if(n<k) return 0;
  if(n<0||k<0) return 0;
  return fac[n]*(finv[k]*finv[n-k]%MOD)%MOD;
}

struct UnionFind{ //グラフの連結性を管理
  vector<int> par,siz;
  UnionFind(int n):par(n,-1),siz(n,1){}
  
  int root(int x){ //頂点xが属する連結成分の代表点を求める
    if(par[x] == -1) return x;
    else return par[x] = root(par[x]);
  }
  bool issame(int x,int y){ //頂点x,yが同じ連結成分に属しているか
    if(root(x) == root(y)) return true;
    else return false;
  }
  bool unite(int x,int y){ //頂点x,yを辺で結ぶ
    x = root(x);
    y = root(y);
    if(x == y) return false;
    if(siz[x] < siz[y]) swap(x,y);
    par[y] = x;
    siz[x] += siz[y];
    return true;
  }
  int size(int x){ //頂点xが属する連結成分の大きさを求める
    return siz[root(x)];
  }
};

/*遅延セグメントツリー
X:セグメントに入れる値の型
M:作用させるものの型
fx:セグメントどうしの結合
fa:XにMを作用させるとき
fm:Mが追加されたとき
ex:Xの単位元
em:Mの単位元

RSQだったらこうする
using X = long long;
using M = long long;
auto fx = [](X x1, X x2) -> X { return x1 + x2; };
auto fa = [](X x, M m) -> X { return x + m; };
auto fm = [](M m1, M m2) -> M { return m1 + m2; };
long long ex = 0;
long long em = 0;
SegTreeLazy<X, M> rsq(n,fx,fa,fm,ex,em);
*/
template <typename X, typename M>
struct SegTreeLazy {
  using FX = function<X(X,X)>;
  using FA = function<X(X,M)>;
  using FM = function<M(M,M)>;
  int n;
  FX fx;
  FA fa;
  FM fm;
  const X ex;
  const M em;
  vector<X> dat;
  vector<M> lazy;
  SegTreeLazy(int n_,FX fx_,FA fa_, FM fm_, X ex_, M em_)
    : n(), fx(fx_), fa(fa_), fm(fm_), ex(ex_), em(em_), dat(n_ * 4, ex), lazy(n_ * 4, em) {
    int x = 1;
    while(n_ > x) x *= 2;
    n = x;
  }
  void set(int i, X x) { dat[i+n-1] = x; }
  void build(){
    for(int k=n-2;k>=0;k--) dat[k] = fx(dat[2*k+1],dat[2*k+2]);
  }
  void eval(int k){
    if(lazy[k] == em) return;
    if(k < n-1){
      lazy[k*2+1] = fm(lazy[k*2+1],lazy[k]);
      lazy[k*2+2] = fm(lazy[k*2+2],lazy[k]);
    }
    dat[k] = fa(dat[k],lazy[k]);
    lazy[k] = em;
  }
  void update(int a,int b,M x,int k,int l,int r){
    eval(k);
    if(a <= l && r <= b){
      lazy[k] = fm(lazy[k],x);
      eval(k);
    } 
    else if(a < r && l < b){
      update(a,b,x,k*2+1,l,(l+r)/2);
      update(a,b,x,k*2+2,(l+r)/2,r);
      dat[k] = fx(dat[k*2+1],dat[k*2+2]);
    }
  }
  void update(int a,int b,M x) { update(a,b,x,0,0,n); }
  X query_sub(int a,int b,int k,int l,int r) {
    eval(k);
    if(r <= a || b <= l){
      return ex;
    } 
    else if(a <= l && r <= b){
      return dat[k];
    } 
    else{
      X vl = query_sub(a,b,k*2+1,l,(l+r)/2);
      X vr = query_sub(a,b,k*2+2,(l+r)/2,r);
      return fx(vl, vr);
    }
  }
  X query(int a,int b){ return query_sub(a,b,0,0,n); }
};

void dft_for_fft(vector<complex<long double>> &func, int inverse){
  int len = func.size();
  if(len == 1) return;
  vector<complex<long double>> A,B;
  rep(i,len/2){
    A.push_back(func[2*i]);
    B.push_back(func[2*i+1]);
  }
  dft_for_fft(A,inverse);
  dft_for_fft(B,inverse);
  complex<long double> now = 1,zeta = polar((long double)1.0,inverse*2.0*pi/(long double)len);
  rep(i,len){
    func[i] = A[i%(len/2)]+now*B[i%(len/2)];
    now *= zeta;
  }
}

template<typename T>
vector<long double> multiply(vector<T> f, vector<T> g){
  int len = 1;
  while(len < f.size()+g.size()) len *= 2;
  vector<complex<long double>> nf(len),ng(len);
  rep(i,len){
    if(i < f.size()) nf[i] = f[i];
    if(i < g.size()) ng[i] = g[i];
  }
  dft_for_fft(nf,1);
  dft_for_fft(ng,1);
  rep(i,len) nf[i] *= ng[i];
  dft_for_fft(nf,-1);
  vector<long double> res;
  rep(i,len) res.push_back(nf[i].real()/len);
  return res;
}

ll depth_cnt(ll N,ll u,int d){
  vector<ll> res(61,0);
  res[0] = 1;
  ll l = 2LL*u,r=2LL*u+1;
  rep2(i,1,61){
    if(l > N) break;
    else if(r <= N) res[i] = r-l+1;
    else{
      res[i] = N-l+1;
      break;
    }
    l *= 2LL;
    r *= 2LL;
    r++;
  }
  if(d < 0 || d > 60) return 0;
  return res[d];
}

template< typename flow_t, typename cost_t >
struct PrimalDual {
  const cost_t INF;

  struct edge {
    int to;
    flow_t cap;
    cost_t cost;
    int rev;
    bool isrev;
  };
  vector< vector< edge > > graph;
  vector< cost_t > potential, min_cost;
  vector< int > prevv, preve;

  PrimalDual(int V) : graph(V), INF(numeric_limits< cost_t >::max()) {}

  void add_edge(int from, int to, flow_t cap, cost_t cost) {
    graph[from].emplace_back((edge) {to, cap, cost, (int) graph[to].size(), false});
    graph[to].emplace_back((edge) {from, 0, -cost, (int) graph[from].size() - 1, true});
  }

  cost_t min_cost_flow(int s, int t, flow_t f) {
    int V = (int) graph.size();
    cost_t ret = 0;
    using Pi = pair< cost_t, int >;
    priority_queue< Pi, vector< Pi >, greater< Pi > > que;
    potential.assign(V, 0);
    preve.assign(V, -1);
    prevv.assign(V, -1);

    while(f > 0) {
      min_cost.assign(V, INF);
      que.emplace(0, s);
      min_cost[s] = 0;
      while(!que.empty()) {
        Pi p = que.top();
        que.pop();
        if(min_cost[p.second] < p.first) continue;
        for(int i = 0; i < graph[p.second].size(); i++) {
          edge &e = graph[p.second][i];
          cost_t nextCost = min_cost[p.second] + e.cost + potential[p.second] - potential[e.to];
          if(e.cap > 0 && min_cost[e.to] > nextCost) {
            min_cost[e.to] = nextCost;
            prevv[e.to] = p.second, preve[e.to] = i;
            que.emplace(min_cost[e.to], e.to);
          }
        }
      }
      if(min_cost[t] == INF) return INF;
      for(int v = 0; v < V; v++) potential[v] += min_cost[v];
      flow_t addflow = f;
      for(int v = t; v != s; v = prevv[v]) {
        addflow = min(addflow, graph[prevv[v]][preve[v]].cap);
      }
      f -= addflow;
      ret += addflow * potential[t];
      for(int v = t; v != s; v = prevv[v]) {
        edge &e = graph[prevv[v]][preve[v]];
        e.cap -= addflow;
        graph[v][e.rev].cap += addflow;
      }
    }
    return ret;
  }

  void output() {
    for(int i = 0; i < graph.size(); i++) {
      for(auto &e : graph[i]) {
        if(e.isrev) continue;
        auto &rev_e = graph[e.to][e.rev];
        cout << i << "->" << e.to << " (flow: " << rev_e.cap << "/" << rev_e.cap + e.cap << ")" << endl;
      }
    }
  }
};

template <class F>
vector<ll> monotone_maxima(F &f, int h, int w) {
    vector<ll> ret(h);
    auto sol = [&](auto &&self, const int l_i, const int r_i, const int l_j, const int r_j) -> void {
        const int m_i = (l_i + r_i) / 2;
        int max_j = l_j;
        ll max_val = -INF;
        for (int j = l_j; j <= r_j; ++j) {
            const ll v = f(m_i, j);
            if (v > max_val) {
                max_j = j;
                max_val = v;
            }
        }
        ret[m_i] = max_val;

        if (l_i <= m_i - 1) {
            self(self, l_i, m_i - 1, l_j, max_j);
        }
        if (m_i + 1 <= r_i) {
            self(self, m_i + 1, r_i, max_j, r_j);
        }
    };
    sol(sol, 0, h - 1, 0, w - 1);
    return ret;
}

vector<ll> max_plus_convolution(const vector<ll> &a, const vector<ll> &b) {
    const int n = (int)a.size(), m = (int)b.size();
    auto f = [&](int i, int j) {
        if (i < j or i - j >= m) {
            return -INF;
        }
        return a[j] + b[i - j];
    };

    return monotone_maxima(f, n + m - 1, n);
}
 
vector<vector<long long int>> mat_mul(vector<vector<long long int>> &A,vector<vector<long long int>> &B){
  vector<vector<long long int>> C(A.size(),vector<long long int>(B[0].size()));
  for(int i=0;i<(int)A.size();i++) for(int k=0;k<(int)B.size();k++) for(int j=0;j<(int)B[0].size();j++) C[i][j] = (C[i][j]+A[i][k]*B[k][j])%MOD;
  return C;
}
 
vector<vector<long long int>> mat_pow(vector<vector<long long int>> &A,long long int n){
  ll N = n;
  vector<vector<long long int>> B(A.size(),vector<long long int>(A.size()));
  for(int i=0;i<(int)A.size();i++) B[i][i] = 1;
  while(N > 0){
    if(N & 1) B = mat_mul(B,A);
    A = mat_mul(A,A);
    N >>= 1;
  }
  return B;
}

void MergeSort(vl &a,ll left, ll right){
  if(right-left == 1) return;
  int mid = (left+right)/2;
  MergeSort(a,left,mid);
  MergeSort(a,mid,right);
  vl buf;
  for(int i=left;i<mid;i++) buf.push_back(a[i]);
  for(int i=right-1;i>=mid;i--) buf.push_back(a[i]);
  int index_left = 0;
  int index_right = (int)buf.size()-1;
  for(int i=left;i<right-1;i++){
    if(buf[index_left] <= buf[index_right]){
      a[i] = buf[index_left];
      index_left++;
    }
    else{
      a[i] = buf[index_right];
      index_right--;
    }
  }
  a[right-1] = buf[index_left];
}

template<class COST> struct TwoVariableSubmodularOpt {
    // edge class
    struct Edge {
        // core members
        int rev, from, to;
        COST cap, icap, flow;
        
        // constructor
        Edge(int r, int f, int t, COST c)
        : rev(r), from(f), to(t), cap(c), icap(c), flow(0) {}
        void reset() { cap = icap, flow = 0; }
        
        // debug
        friend ostream& operator << (ostream& s, const Edge& E) {
            return s << E.from << "->" << E.to << '(' << E.flow << '/' << E.icap << ')';
        }
    };
    
    // constructor
    TwoVariableSubmodularOpt() : N(2), S(0), T(0), OFFSET(0) {}
    TwoVariableSubmodularOpt(int n, COST inf = 0)
    : N(n), S(n), T(n + 1), OFFSET(0), INF(inf), list(n + 2) {}
    void init(int n, COST inf = 0) {
        N = n, S = n, T = n + 1;
        OFFSET = 0, INF = inf;
        list.assign(N + 2, Edge());
        pos.clear();
    }
    friend ostream& operator << (ostream& s, const TwoVariableSubmodularOpt &G) {
        const auto &edges = G.get_edges();
        for (const auto &e : edges) s << e << endl;
        return s;
    }

    // add 1-Variable submodular functioin
    void add_single_cost(int xi, COST false_cost, COST true_cost) {
        assert(0 <= xi && xi < N);
        if (false_cost >= true_cost) {
            OFFSET += true_cost;
            add_edge(S, xi, false_cost - true_cost);
        } else {
            OFFSET += false_cost;
            add_edge(xi, T, true_cost - false_cost);
        }
    }
    
    // add "project selection" constraint (xi = T, xj = F is penalty C)
    void add_psp_penalty(int xi, int xj, COST cost) {
        assert(0 <= xi && xi < N);
        assert(0 <= xj && xj < N);
        assert(cost >= 0);
        add_edge(xi, xj, cost);
    }
    
    // add general 2-variable submodular function
    // (xi, xj) = (F, F): A, (F, T): B
    // (xi, xj) = (T, F): C, (T, T): D
    void add_submodular_function(int xi, int xj, COST A, COST B, COST C, COST D) {
        assert(0 <= xi && xi < N);
        assert(0 <= xj && xj < N);
        assert(B + C >= A + D);  // assure submodular function
        OFFSET += A;
        add_single_cost(xi, 0, D - B);
        add_single_cost(xj, 0, B - A);
        add_psp_penalty(xi, xj, B + C - A - D);
    }
    
    // add all True profit
    void add_all_true_profit(const vector<int> &xs, COST profit) {
        assert(profit >= 0);
        int slack = (int)list.size();
        list.resize(slack + 1);
        OFFSET -= profit;
        add_edge(S, slack, profit);
        for (auto xi : xs) {
            assert(xi >= 0 && xi < N);
            add_edge(slack, xi, INF);
        }
    }
    
    // solve
    COST solve() {
        return dinic() + OFFSET;
    }
    vector<bool> reconstruct() {
        vector<bool> res(N, false), seen(list.size(), false);
        queue<int> que;
        seen[S] = true;
        que.push(S);
        while (!que.empty()) {
            int v = que.front();
            que.pop();
            for (const auto &e : list[v]) {
                if (e.cap && !seen[e.to]) {
                    if (e.to < N) res[e.to] = true;
                    seen[e.to] = true;
                    que.push(e.to);
                }
            }
        }
        return res;
    }
    
private:
    // inner data
    int N, S, T;
    COST OFFSET, INF;
    vector<vector<Edge>> list;
    vector<pair<int,int>> pos;
    
    // add edge
    Edge &get_rev_edge(const Edge &e) {
        if (e.from != e.to) return list[e.to][e.rev];
        else return list[e.to][e.rev + 1];
    }
    Edge &get_edge(int i) {
        return list[pos[i].first][pos[i].second];
    }
    const Edge &get_edge(int i) const {
        return list[pos[i].first][pos[i].second];
    }
    vector<Edge> get_edges() const {
        vector<Edge> edges;
        for (int i = 0; i < (int)pos.size(); ++i) {
            edges.push_back(get_edge(i));
        }
        return edges;
    }
    void add_edge(int from, int to, COST cap) {
        if (!cap) return;
        pos.emplace_back(from, (int)list[from].size());
        list[from].push_back(Edge((int)list[to].size(), from, to, cap));
        list[to].push_back(Edge((int)list[from].size() - 1, to, from, 0));
    }
    
    // Dinic's algorithm
    COST dinic(COST limit_flow) {
        COST current_flow = 0;
        vector<int> level((int)list.size(), -1), iter((int)list.size(), 0);
        
        // Dinic BFS
        auto bfs = [&]() -> void {
            level.assign((int)list.size(), -1);
            level[S] = 0;
            queue<int> que;
            que.push(S);
            while (!que.empty()) {
                int v = que.front();
                que.pop();
                for (const Edge &e : list[v]) {
                    if (level[e.to] < 0 && e.cap > 0) {
                        level[e.to] = level[v] + 1;
                        if (e.to == T) return;
                        que.push(e.to);
                    }
                }
            }
        };
        
        // Dinic DFS
        auto dfs = [&](auto self, int v, COST up_flow) {
            if (v == T) return up_flow;
            COST res_flow = 0;
            for (int &i = iter[v]; i < (int)list[v].size(); ++i) {
                Edge &e = list[v][i], &re = get_rev_edge(e);
                if (level[v] >= level[e.to] || e.cap == 0) continue;
                COST flow = self(self, e.to, min(up_flow - res_flow, e.cap));
                if (flow <= 0) continue;
                res_flow += flow;
                e.cap -= flow, e.flow += flow;
                re.cap += flow, re.flow -= flow;
                if (res_flow == up_flow) break;
            }
            return res_flow;
        };
        
        // flow
        while (current_flow < limit_flow) {
            bfs();
            if (level[T] < 0) break;
            iter.assign((int)iter.size(), 0);
            while (current_flow < limit_flow) {
                COST flow = dfs(dfs, S, limit_flow - current_flow);
                if (!flow) break;
                current_flow += flow;
            }
        }
        return current_flow;
    };
    COST dinic() {
        return dinic(numeric_limits<COST>::max());
    }
};

ll calc(vvvl G,vector<vector<char>> A,int n,int M){
  vvl dp(M,vl(1<<n,0));
  rep(i,1<<n){
    bool ok = true;
    //iあえて塗らない集合
    rep(j,n){
      if((i & (1<<j)) > 0 && A[j][0] == '#'){
        ok = false;
        break;
      }
    }
    if(ok) dp[0][i] = 1;
  }
  rep(i,n){
    int bit = 1<<i;
    rep(j,1<<n){
      if((j & bit) == 0){
        dp[0][j] += dp[0][j | bit];
        dp[0][j] %= MOD;
      }
    }
  }
  rep2(i,1,M){
    rep(j,1<<n){
      bool ok = true;
      rep(k,n){
        if((j & (1<<k)) > 0 && A[k][i] == '#'){
          ok = false;
          break;
        }
      }
      if(!ok) continue;
      ll t = 0;
      rep(k,n){
        if((j & (1<<k)) == 0 && A[k][i] == '.'){
          rep(l,G[k][i].size()){
            t |= 1<<G[k][i][l];
          }
        }
      }
      //tは塗っちゃいけない集合
      dp[i][j] = dp[i-1][t];
    }
    if(i != M-1){
      rep(j,n){
        int bit = 1<<j;
        rep(k,1<<n){
          if((k & bit) == 0){
            dp[i][k] += dp[i][k | bit];
            dp[i][k] %= MOD;
          }
        }
      }
    }
  }
  ll res = 0;
  rep(i,1<<n) res += dp[M-1][i];
  res %= MOD;
  return res;
}

int main(){
  for(;;){
    int N,M;
    cin >> N >> M;
    if(N == 0) break;
    vector<string> a(N);
    rep(i,N) cin >> a[i];
    int n = (N+3)/4;
    ll ans = 1;
    rep(i,4){
      vector<vector<char>> A(n,vector<char>(M,'#'));
      rep(j,n){
        rep(k,M){
          if(k%2 == 0){
            int inow = i+4*j;
            if(inow < N) A[j][k] = a[inow][k];
          }
          else if(i<2){
            int inow = 2+i+4*j;
            if(inow < N) A[j][k] = a[inow][k];
          }
          else{
            int inow = -2+i+4*j;
            if(inow < N) A[j][k] = a[inow][k];
          }
        }
      }
      vvvl G(n,vvl(M));
      rep2(j,1,M){
        rep(k,n){
          if(j%2 == 1){
            if(i < 2){
              int nowi = 2+i+4*k;
              if(nowi < 0 || nowi >= N) continue;
              if(a[nowi][j] == '#') continue;
              if(nowi-2 >= 0 && a[nowi-2][j-1] == '.') G[k][j].push_back(k);
              if(nowi+2 < N && k+1 < n && a[nowi+2][j-1] == '.') G[k][j].push_back(k+1);
            }
            else{
              int nowi = -2+i+4*k;
              if(nowi < 0 || nowi >= N) continue;
              if(a[nowi][j] == '#') continue;
              if(nowi+2 < N && a[nowi+2][j-1] == '.') G[k][j].push_back(k);
              if(nowi-2 >= 0 && k >= 1 && a[nowi-2][j-1] == '.') G[k][j].push_back(k-1);
            }
          }
          else{
            if(i >= 2){
              int nowi = i+4*k;
              if(nowi < 0 || nowi >= N) continue;
              if(a[nowi][j] == '#') continue;
              if(nowi-2 >= 0 && a[nowi-2][j-1] == '.') G[k][j].push_back(k);
              if(nowi+2 < N && k+1 < n && a[nowi+2][j-1] == '.') G[k][j].push_back(k+1);
            }
            else{
              int nowi = i+4*k;
              if(nowi < 0 || nowi >= N) continue;
              if(a[nowi][j] == '#') continue;
              if(nowi+2 < N && a[nowi+2][j-1] == '.') G[k][j].push_back(k);
              if(nowi-2 >= 0 && k >= 1 && a[nowi-2][j-1] == '.') G[k][j].push_back(k-1);
            }
          }
        }
      }
      ll p = calc(G,A,n,M);
      ans *= p;
      ans %= MOD;
    }
    cout << ans << endl;
  }
}
accuracy: 1
time_ms: 3100
memory_kb: 18760
final_rank: 13

code_snippet: #include <utility>

namespace atcoder {

namespace internal {

constexpr long long safe_mod(long long x, long long m) {
    x %= m;
    if (x < 0) x += m;
    return x;
}

struct barrett {
    unsigned int _m;
    unsigned long long im;

    barrett(unsigned int m) : _m(m), im((unsigned long long)(-1) / m + 1) {}

    unsigned int umod() const { return _m; }

    unsigned int mul(unsigned int a, unsigned int b) const {

        unsigned long long z = a;
        z *= b;
#ifdef _MSC_VER
        unsigned long long x;
        _umul128(z, im, &x);
#else
        unsigned long long x =
            (unsigned long long)(((unsigned __int128)(z)*im) >> 64);
#endif
        unsigned int v = (unsigned int)(z - x * _m);
        if (_m <= v) v += _m;
        return v;
    }
};

constexpr long long pow_mod_constexpr(long long x, long long n, int m) {
    if (m == 1) return 0;
    unsigned int _m = (unsigned int)(m);
    unsigned long long r = 1;
    unsigned long long y = safe_mod(x, m);
    while (n) {
        if (n & 1) r = (r * y) % _m;
        y = (y * y) % _m;
        n >>= 1;
    }
    return r;
}

constexpr bool is_prime_constexpr(int n) {
    if (n <= 1) return false;
    if (n == 2 || n == 7 || n == 61) return true;
    if (n % 2 == 0) return false;
    long long d = n - 1;
    while (d % 2 == 0) d /= 2;
    constexpr long long bases[3] = {2, 7, 61};
    for (long long a : bases) {
        long long t = d;
        long long y = pow_mod_constexpr(a, t, n);
        while (t != n - 1 && y != 1 && y != n - 1) {
            y = y * y % n;
            t <<= 1;
        }
        if (y != n - 1 && t % 2 == 0) {
            return false;
        }
    }
    return true;
}
template <int n> constexpr bool is_prime = is_prime_constexpr(n);

constexpr std::pair<long long, long long> inv_gcd(long long a, long long b) {
    a = safe_mod(a, b);
    if (a == 0) return {b, 0};

    long long s = b, t = a;
    long long m0 = 0, m1 = 1;

    while (t) {
        long long u = s / t;
        s -= t * u;
        m0 -= m1 * u;  // |m1 * u| <= |m1| * s <= b


        auto tmp = s;
        s = t;
        t = tmp;
        tmp = m0;
        m0 = m1;
        m1 = tmp;
    }
    if (m0 < 0) m0 += b / s;
    return {s, m0};
}

constexpr int primitive_root_constexpr(int m) {
    if (m == 2) return 1;
    if (m == 167772161) return 3;
    if (m == 469762049) return 3;
    if (m == 754974721) return 11;
    if (m == 998244353) return 3;
    int divs[20] = {};
    divs[0] = 2;
    int cnt = 1;
    int x = (m - 1) / 2;
    while (x % 2 == 0) x /= 2;
    for (int i = 3; (long long)(i)*i <= x; i += 2) {
        if (x % i == 0) {
            divs[cnt++] = i;
            while (x % i == 0) {
                x /= i;
            }
        }
    }
    if (x > 1) {
        divs[cnt++] = x;
    }
    for (int g = 2;; g++) {
        bool ok = true;
        for (int i = 0; i < cnt; i++) {
            if (pow_mod_constexpr(g, (m - 1) / divs[i], m) == 1) {
                ok = false;
                break;
            }
        }
        if (ok) return g;
    }
}
template <int m> constexpr int primitive_root = primitive_root_constexpr(m);

}  // namespace internal

}  // namespace atcoder


#include <cassert>
#include <numeric>
#include <type_traits>

namespace atcoder {

namespace internal {

#ifndef _MSC_VER
template <class T>
using is_signed_int128 =
    typename std::conditional<std::is_same<T, __int128_t>::value ||
                                  std::is_same<T, __int128>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using is_unsigned_int128 =
    typename std::conditional<std::is_same<T, __uint128_t>::value ||
                                  std::is_same<T, unsigned __int128>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using make_unsigned_int128 =
    typename std::conditional<std::is_same<T, __int128_t>::value,
                              __uint128_t,
                              unsigned __int128>;

template <class T>
using is_integral = typename std::conditional<std::is_integral<T>::value ||
                                                  is_signed_int128<T>::value ||
                                                  is_unsigned_int128<T>::value,
                                              std::true_type,
                                              std::false_type>::type;

template <class T>
using is_signed_int = typename std::conditional<(is_integral<T>::value &&
                                                 std::is_signed<T>::value) ||
                                                    is_signed_int128<T>::value,
                                                std::true_type,
                                                std::false_type>::type;

template <class T>
using is_unsigned_int =
    typename std::conditional<(is_integral<T>::value &&
                               std::is_unsigned<T>::value) ||
                                  is_unsigned_int128<T>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using to_unsigned = typename std::conditional<
    is_signed_int128<T>::value,
    make_unsigned_int128<T>,
    typename std::conditional<std::is_signed<T>::value,
                              std::make_unsigned<T>,
                              std::common_type<T>>::type>::type;

#else

template <class T> using is_integral = typename std::is_integral<T>;

template <class T>
using is_signed_int =
    typename std::conditional<is_integral<T>::value && std::is_signed<T>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using is_unsigned_int =
    typename std::conditional<is_integral<T>::value &&
                                  std::is_unsigned<T>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using to_unsigned = typename std::conditional<is_signed_int<T>::value,
                                              std::make_unsigned<T>,
                                              std::common_type<T>>::type;

#endif

template <class T>
using is_signed_int_t = std::enable_if_t<is_signed_int<T>::value>;

template <class T>
using is_unsigned_int_t = std::enable_if_t<is_unsigned_int<T>::value>;

template <class T> using to_unsigned_t = typename to_unsigned<T>::type;

}  // namespace internal

}  // namespace atcoder

#include <cassert>
#include <numeric>
#include <type_traits>

#ifdef _MSC_VER
#include <intrin.h>
#endif

namespace atcoder {

namespace internal {

struct modint_base {};
struct static_modint_base : modint_base {};

template <class T> using is_modint = std::is_base_of<modint_base, T>;
template <class T> using is_modint_t = std::enable_if_t<is_modint<T>::value>;

}  // namespace internal

template <int m, std::enable_if_t<(1 <= m)>* = nullptr>
struct static_modint : internal::static_modint_base {
    using mint = static_modint;

  public:
    static constexpr int mod() { return m; }
    static mint raw(int v) {
        mint x;
        x._v = v;
        return x;
    }

    static_modint() : _v(0) {}
    template <class T, internal::is_signed_int_t<T>* = nullptr>
    static_modint(T v) {
        long long x = (long long)(v % (long long)(umod()));
        if (x < 0) x += umod();
        _v = (unsigned int)(x);
    }
    template <class T, internal::is_unsigned_int_t<T>* = nullptr>
    static_modint(T v) {
        _v = (unsigned int)(v % umod());
    }
    static_modint(bool v) { _v = ((unsigned int)(v) % umod()); }

    unsigned int val() const { return _v; }

    mint& operator++() {
        _v++;
        if (_v == umod()) _v = 0;
        return *this;
    }
    mint& operator--() {
        if (_v == 0) _v = umod();
        _v--;
        return *this;
    }
    mint operator++(int) {
        mint result = *this;
        ++*this;
        return result;
    }
    mint operator--(int) {
        mint result = *this;
        --*this;
        return result;
    }

    mint& operator+=(const mint& rhs) {
        _v += rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator-=(const mint& rhs) {
        _v -= rhs._v;
        if (_v >= umod()) _v += umod();
        return *this;
    }
    mint& operator*=(const mint& rhs) {
        unsigned long long z = _v;
        z *= rhs._v;
        _v = (unsigned int)(z % umod());
        return *this;
    }
    mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }

    mint operator+() const { return *this; }
    mint operator-() const { return mint() - *this; }

    mint pow(long long n) const {
        assert(0 <= n);
        mint x = *this, r = 1;
        while (n) {
            if (n & 1) r *= x;
            x *= x;
            n >>= 1;
        }
        return r;
    }
    mint inv() const {
        if (prime) {
            assert(_v);
            return pow(umod() - 2);
        } else {
            auto eg = internal::inv_gcd(_v, m);
            assert(eg.first == 1);
            return eg.second;
        }
    }

    friend mint operator+(const mint& lhs, const mint& rhs) {
        return mint(lhs) += rhs;
    }
    friend mint operator-(const mint& lhs, const mint& rhs) {
        return mint(lhs) -= rhs;
    }
    friend mint operator*(const mint& lhs, const mint& rhs) {
        return mint(lhs) *= rhs;
    }
    friend mint operator/(const mint& lhs, const mint& rhs) {
        return mint(lhs) /= rhs;
    }
    friend bool operator==(const mint& lhs, const mint& rhs) {
        return lhs._v == rhs._v;
    }
    friend bool operator!=(const mint& lhs, const mint& rhs) {
        return lhs._v != rhs._v;
    }

  private:
    unsigned int _v;
    static constexpr unsigned int umod() { return m; }
    static constexpr bool prime = internal::is_prime<m>;
};

template <int id> struct dynamic_modint : internal::modint_base {
    using mint = dynamic_modint;

  public:
    static int mod() { return (int)(bt.umod()); }
    static void set_mod(int m) {
        assert(1 <= m);
        bt = internal::barrett(m);
    }
    static mint raw(int v) {
        mint x;
        x._v = v;
        return x;
    }

    dynamic_modint() : _v(0) {}
    template <class T, internal::is_signed_int_t<T>* = nullptr>
    dynamic_modint(T v) {
        long long x = (long long)(v % (long long)(mod()));
        if (x < 0) x += mod();
        _v = (unsigned int)(x);
    }
    template <class T, internal::is_unsigned_int_t<T>* = nullptr>
    dynamic_modint(T v) {
        _v = (unsigned int)(v % mod());
    }
    dynamic_modint(bool v) { _v = ((unsigned int)(v) % mod()); }

    unsigned int val() const { return _v; }

    mint& operator++() {
        _v++;
        if (_v == umod()) _v = 0;
        return *this;
    }
    mint& operator--() {
        if (_v == 0) _v = umod();
        _v--;
        return *this;
    }
    mint operator++(int) {
        mint result = *this;
        ++*this;
        return result;
    }
    mint operator--(int) {
        mint result = *this;
        --*this;
        return result;
    }

    mint& operator+=(const mint& rhs) {
        _v += rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator-=(const mint& rhs) {
        _v += mod() - rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator*=(const mint& rhs) {
        _v = bt.mul(_v, rhs._v);
        return *this;
    }
    mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }

    mint operator+() const { return *this; }
    mint operator-() const { return mint() - *this; }

    mint pow(long long n) const {
        assert(0 <= n);
        mint x = *this, r = 1;
        while (n) {
            if (n & 1) r *= x;
            x *= x;
            n >>= 1;
        }
        return r;
    }
    mint inv() const {
        auto eg = internal::inv_gcd(_v, mod());
        assert(eg.first == 1);
        return eg.second;
    }

    friend mint operator+(const mint& lhs, const mint& rhs) {
        return mint(lhs) += rhs;
    }
    friend mint operator-(const mint& lhs, const mint& rhs) {
        return mint(lhs) -= rhs;
    }
    friend mint operator*(const mint& lhs, const mint& rhs) {
        return mint(lhs) *= rhs;
    }
    friend mint operator/(const mint& lhs, const mint& rhs) {
        return mint(lhs) /= rhs;
    }
    friend bool operator==(const mint& lhs, const mint& rhs) {
        return lhs._v == rhs._v;
    }
    friend bool operator!=(const mint& lhs, const mint& rhs) {
        return lhs._v != rhs._v;
    }

  private:
    unsigned int _v;
    static internal::barrett bt;
    static unsigned int umod() { return bt.umod(); }
};
template <int id> internal::barrett dynamic_modint<id>::bt = 998244353;

using modint998244353 = static_modint<998244353>;
using modint1000000007 = static_modint<1000000007>;
using modint = dynamic_modint<-1>;

namespace internal {

template <class T>
using is_static_modint = std::is_base_of<internal::static_modint_base, T>;

template <class T>
using is_static_modint_t = std::enable_if_t<is_static_modint<T>::value>;

template <class> struct is_dynamic_modint : public std::false_type {};
template <int id>
struct is_dynamic_modint<dynamic_modint<id>> : public std::true_type {};

template <class T>
using is_dynamic_modint_t = std::enable_if_t<is_dynamic_modint<T>::value>;

}  // namespace internal

}  // namespace atcoder


#include <algorithm>

#ifdef _MSC_VER
#include <intrin.h>
#endif

namespace atcoder {

namespace internal {

int ceil_pow2(int n) {
    int x = 0;
    while ((1U << x) < (unsigned int)(n)) x++;
    return x;
}

int bsf(unsigned int n) {
#ifdef _MSC_VER
    unsigned long index;
    _BitScanForward(&index, n);
    return index;
#else
    return __builtin_ctz(n);
#endif
}

}  // namespace internal

}  // namespace atcoder

#include <cassert>
#include <vector>

namespace atcoder {

template <class S, S (*op)(S, S), S (*e)()> struct segtree {
  public:
    segtree() : segtree(0) {}
    segtree(int n) : segtree(std::vector<S>(n, e())) {}
    segtree(const std::vector<S>& v) : _n(int(v.size())) {
        log = internal::ceil_pow2(_n);
        size = 1 << log;
        d = std::vector<S>(2 * size, e());
        for (int i = 0; i < _n; i++) d[size + i] = v[i];
        for (int i = size - 1; i >= 1; i--) {
            update(i);
        }
    }

    void set(int p, S x) {
        assert(0 <= p && p < _n);
        p += size;
        d[p] = x;
        for (int i = 1; i <= log; i++) update(p >> i);
    }

    S get(int p) {
        assert(0 <= p && p < _n);
        return d[p + size];
    }

    S prod(int l, int r) {
        assert(0 <= l && l <= r && r <= _n);
        S sml = e(), smr = e();
        l += size;
        r += size;

        while (l < r) {
            if (l & 1) sml = op(sml, d[l++]);
            if (r & 1) smr = op(d[--r], smr);
            l >>= 1;
            r >>= 1;
        }
        return op(sml, smr);
    }

    S all_prod() { return d[1]; }

    template <bool (*f)(S)> int max_right(int l) {
        return max_right(l, [](S x) { return f(x); });
    }
    template <class F> int max_right(int l, F f) {
        assert(0 <= l && l <= _n);
        assert(f(e()));
        if (l == _n) return _n;
        l += size;
        S sm = e();
        do {
            while (l % 2 == 0) l >>= 1;
            if (!f(op(sm, d[l]))) {
                while (l < size) {
                    l = (2 * l);
                    if (f(op(sm, d[l]))) {
                        sm = op(sm, d[l]);
                        l++;
                    }
                }
                return l - size;
            }
            sm = op(sm, d[l]);
            l++;
        } while ((l & -l) != l);
        return _n;
    }

    template <bool (*f)(S)> int min_left(int r) {
        return min_left(r, [](S x) { return f(x); });
    }
    template <class F> int min_left(int r, F f) {
        assert(0 <= r && r <= _n);
        assert(f(e()));
        if (r == 0) return 0;
        r += size;
        S sm = e();
        do {
            r--;
            while (r > 1 && (r % 2)) r >>= 1;
            if (!f(op(d[r], sm))) {
                while (r < size) {
                    r = (2 * r + 1);
                    if (f(op(d[r], sm))) {
                        sm = op(d[r], sm);
                        r--;
                    }
                }
                return r + 1 - size;
            }
            sm = op(d[r], sm);
        } while ((r & -r) != r);
        return 0;
    }

  private:
    int _n, size, log;
    std::vector<S> d;

    void update(int k) { d[k] = op(d[2 * k], d[2 * k + 1]); }
};

}  // namespace atcoder

#include <iostream> // cout, endl, cin
#include <string> // string, to_string, stoi
#include <vector> // vector
#include <algorithm> // min, max, swap, sort, reverse, lower_bound, upper_bound
#include <utility> // pair, make_pair
#include <tuple> // tuple, make_tuple
#include <cstdint> // int64_t, int*_t
#include <cstdio> // printf
#include <map> // map
#include <queue> // queue, priority_queue
#include <set> // set
#include <stack> // stack
#include <deque> // deque #include <unordered_map> // unordered_map #include <unordered_set> // unordered_set #include <bitset> // bitset
#include <cctype> // isupper, islower, isdigit, toupper, tolower
#include <math.h>
#include <iomanip>
#include <functional>
#include<unordered_map>
#include <random>
#include<bitset>
using namespace std;  
using namespace atcoder;
#define rep(i, n) for (int i = 0; i < (int)(n); i++)
#define repi(i, a, b) for (int i = (int)(a); i < (int)(b); i++)
//#define endl '\n'
#define all(x) (x).begin(),(x).end()
#define arr(x) (x).rbegin(),(x).rend()
typedef long long ll;
typedef unsigned long long ull;
const ll inf=4e18;  
using graph = vector<vector<int> > ;
using vi=vector<int>;
using P= pair<ll,ll>;  
using vvi=vector<vi>;
using vvvi=vector<vvi>;
using vll=vector<ll>; 
using vvll=vector<vll>;
using vp=vector<P>;
using vvp=vector<vp>;
using vd=vector<double>;
using vvd =vector<vd>;
//using vs=vector<string>;
//string T="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
//string S="abcdefghijklmnopqrstuvwxyz";
//g++ main.cpp -std=c++17 -I .  
//cout <<setprecision(20);
//cout << fixed << setprecision(10);
//cin.tie(0); ios::sync_with_stdio(false);

const double PI = acos(-1);
int vx[]={0,1,0,-1,-1,1,1,-1},vy[]={1,0,-1,0,1,1,-1,-1};
void putsYes(bool f){cout << (f?"Yes":"No") << endl;}
void putsYES(bool f){cout << (f?"YES":"NO") << endl;}
void putsFirst(bool f){cout << (f?"First":"Second") << endl;}
void debug(int test){cout << "TEST" << " " << test << endl;}
ll pow_pow(ll x,ll n,ll mod){
    if(n==0) return 1; 
    x%=mod;
    ll res=pow_pow(x*x%mod,n/2,mod);
    if(n&1)res=res*x%mod;
    return res;
}
ll gcdll(ll x,ll y){
    if(y==0)return x;
    return gcdll(y,x%y);
}
ll INFF=1000100100100100100;
ll lcmll(ll x,ll y){
    ll X=(x/gcdll(x,y)),Y=y;
    if(X<=INFF/Y)return X*Y;
    else return INFF;
}
template<class T> bool chmin(T& a, T b) {
    if (a > b) {
        a = b;
        return true;
    }
    else return false;
}
template<class T> bool chmax(T& a, T b) {
    if (a < b) {
        a = b;
        return true;
    }
    else return false;
}
struct edge{
  int to; ll cost;int t;
  edge(int to,ll cost,int t) : to(to),cost(cost),t(t){}
};
using ve=vector<edge>;
using vve=vector<ve>;
using mint = modint;
//using mint = modint1000000007;
using vm=vector<mint>;
using vvm=vector<vm>;
using vvvm=vector<vvm>;
int mod = 1e9+9;
//int mod=1e9+7;
constexpr int MAX =10;
ll fact[MAX],finv[MAX],inv[MAX];
void initcomb(){
  fact[0]=fact[1]=1;
  finv[0]=finv[1]=1;
  inv[1]=1;
  for(int i=2;i<MAX;i++){
    fact[i]=fact[i-1]*i%mod;
    inv[i]=mod-inv[mod%i]*(mod/i)%mod;
    finv[i]=finv[i-1]*inv[i]%mod;
  }
}
ll comb(ll n,ll k){
  if(n<k) return 0;
  if(n<0||k<0) return 0;
  return fact[n]*(finv[k]*finv[n-k]%mod)%mod;
}

struct UnionFind { 
    vector<int> par, siz;
    UnionFind(int n) : par(n, -1) , siz(n, 1) { } 
    int root(int x) { 
        if (par[x] == -1) return x;
        else return par[x] = root(par[x]);
    }
    bool issame(int x, int y) {
        return root(x) == root(y);
    }
    bool unite(int x, int y) {
        x = root(x), y = root(y);
        if (x == y) return false; 
        if (siz[x] < siz[y]) swap(x, y);
        par[y] = x;
        siz[x] += siz[y];
        return true;
    }
    int size(int x) {
        return siz[root(x)];
    }
};
struct Compress{
   vll a;
   int cnt;
   Compress() :cnt(0) {}
   void add(ll x){a.push_back(x);}
   void init(){
       sort(all(a));
       a.erase(unique(all(a)),a.end());
       cnt=a.size();
   }
   ll to(ll x){
       int index=lower_bound(all(a),x)-a.begin();
       return index;
   } //変換先
   ll from(int x){return a[x];}//逆引き
   int size(){return cnt;}
};
vll anss;
int EXIT=0;
void solve(int test){
    int n,m; cin >> n >> m;
    int h=n,w=m;
    if(n==0 && m==0){
        EXIT=1;
        return;
    }
    vector<string>  s(n);
    rep(i,n)cin >> s[i];
    mint ans=1;
    UnionFind uf(n*m);
    rep(i,n)rep(j,m){
        if(i-2>=0 && j-1>=0){
            uf.unite(i*w+j,(i-2)*w+j-1);
        }
        if(i-2>=0 && j+1<m){
            uf.unite(i*w+j,(i-2)*w+j+1);
        }
    }
    vvi id(n,vi(m));
    vvi seen(n,vi(m));
    rep(sx,n)rep(sy,m){
        if(seen[sx][sy])continue;
        vm dp;
        int presi=0;
        rep(j,m){
            int si=0;
            vi vs;
            rep(i,n){
                if(uf.issame(sx*w+sy,i*w+j)){
                    id[i][j]=si++;
                    seen[i][j]=1;
                    vs.push_back(i);
                }
            }
            if(j==0){
                vm next(1<<si);
                rep(bit,1<<si){
                    int ng=0;
                    rep(k,si)if(bit>>k&1){
                        int i=vs[k];
                        if(s[i][j]=='#'){ng=1;break;}
                    }
                    if(!ng)next[bit]=1;
                }
                swap(next,dp);
            }
            else {
                rep(i,presi)for(int bit=dp.size()-1; bit>=0; bit--)if(bit>>i&1){
                   dp[bit]+=dp[bit-(1<<i)];
                }
                vm next(1<<si);
                rep(bit,1<<si){
                    int nbit=dp.size()-1;
                    int ng=0;
                    rep(k,si)if(bit>>k&1){
                        int i=vs[k];
                        if(s[i][j]=='#'){ng=1;break;}
                        if(i-2>=0 && j-1>=0){
                            nbit&=((1<<presi)-1-(1<<id[i-2][j-1]));
                        }
                        if(i+2<n && j-1>=0){
                            nbit&=((1<<presi)-1-(1<<id[i+2][j-1]));
                        }
                    }
                    if(!ng)next[bit]=dp[nbit];
                    else next[bit]=0;
                }
                swap(next,dp);
            }
            presi=si;
        }
        mint tot=0;
        rep(bit,dp.size())tot+=dp[bit];
        ans*=tot;
    }
    cout << ans.val() << endl;
}
//g++ cf.cpp -std=c++17 -I .
int main(){cin.tie(0);ios::sync_with_stdio(false);
    {
        mod=1e9+7;
        initcomb();
        modint::set_mod(mod);
    }
    for(int test=0;; test++){
        solve(test);
        if(EXIT)break;
    }
    for(auto ans:anss){
        cout<< ans << endl;
    }
}
accuracy: 1
time_ms: 2080
memory_kb: 3584
final_rank: 3

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, n) for (int i = 0; i < n; i++)
#define all(v) v.begin(), v.end()
template <class T, class U>
inline bool chmax(T &a, U b) {
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}
template <class T, class U>
inline bool chmin(T &a, U b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}
constexpr int INF = 1000000000;
constexpr int64_t llINF = 3000000000000000000;
constexpr double eps = 1e-10;
const double pi = acos(-1);
struct linear_sieve {
    vector<int> least_factor, prime_list;
    linear_sieve(int n) : least_factor(n + 1, 0) {
        for (int i = 2; i <= n; i++) {
            if (least_factor[i] == 0) {
                least_factor[i] = i;
                prime_list.push_back(i);
            }
            for (int p : prime_list) {
                if (ll(i) * p > n || p > least_factor[i]) break;
                least_factor[i * p] = p;
            }
        }
    }
};
ll extgcd(ll a, ll b, ll &x, ll &y) {
    if (b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    ll d = extgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}
ll modpow(ll a, ll b, ll m) {
    ll res = 1;
    while (b) {
        if (b & 1) {
            res *= a;
            res %= m;
        }
        a *= a;
        a %= m;
        b >>= 1;
    }
    return res;
}
template <int modulo>
struct modint {
    int x;
    modint() : x(0) {}
    modint(int64_t y) : x(y >= 0 ? y % modulo : (modulo - (-y) % modulo) % modulo) {}
    modint &operator+=(const modint &p) {
        if ((x += p.x) >= modulo) x -= modulo;
        return *this;
    }
    modint &operator-=(const modint &p) {
        if ((x += modulo - p.x) >= modulo) x -= modulo;
        return *this;
    }
    modint &operator*=(const modint &p) {
        x = (int)(1LL * x * p.x % modulo);
        return *this;
    }
    modint &operator/=(const modint &p) {
        *this *= p.inv();
        return *this;
    }
    modint operator-() const { return modint(-x); }
    modint operator+(const modint &p) const { return modint(*this) += p; }
    modint operator-(const modint &p) const { return modint(*this) -= p; }
    modint operator*(const modint &p) const { return modint(*this) *= p; }
    modint operator/(const modint &p) const { return modint(*this) /= p; }
    bool operator==(const modint &p) const { return x == p.x; }
    bool operator!=(const modint &p) const { return x != p.x; }
    modint inv() const {
        int a = x, b = modulo, u = 1, v = 0, t;
        while (b > 0) {
            t = a / b;
            swap(a -= t * b, b);
            swap(u -= t * v, v);
        }
        return modint(u);
    }
    modint pow(int64_t n) const {
        modint ret(1), mul(x);
        while (n > 0) {
            if (n & 1) ret *= mul;
            mul *= mul;
            n >>= 1;
        }
        return ret;
    }
    friend ostream &operator<<(ostream &os, const modint &p) { return os << p.x; }
    friend istream &operator>>(istream &is, modint &a) {
        int64_t t;
        is >> t;
        a = modint<modulo>(t);
        return (is);
    }
    int val() const { return x; }
    static constexpr int mod() { return modulo; }
    static constexpr int half() { return (modulo + 1) >> 1; }
};
template <typename T>
struct Binomial {
    vector<T> inv, fact, factinv;
    Binomial(int n) {
        inv.resize(n + 1);
        fact.resize(n + 1);
        factinv.resize(n + 1);
        inv[0] = fact[0] = factinv[0] = 1;
        for (int i = 1; i <= n; i++) fact[i] = fact[i - 1] * i;
        factinv[n] = fact[n].inv();
        inv[n] = fact[n - 1] * factinv[n];
        for (int i = n - 1; i >= 1; i--) {
            factinv[i] = factinv[i + 1] * (i + 1);
            inv[i] = fact[i - 1] * factinv[i];
        }
    }
    T C(int n, int r) {
        if (n < 0 || n < r || r < 0) return 0;
        return fact[n] * factinv[n - r] * factinv[r];
    }
    T P(int n, int r) {
        if (n < 0 || n < r || r < 0) return 0;
        return fact[n] * factinv[n - r];
    }
    T H(int n, int r) {
        if (n == 0 && r == 0) return 1;
        if (n < 0 || r < 0) return 0;
        return r == 0 ? 1 : C(n + r - 1, r);
    }
};
template <class T>
struct Matrix {
    int n;
    vector<vector<T>> m;
    Matrix() : Matrix(0) {}
    Matrix(int x) : Matrix(vector<vector<T>>(x, vector<T>(x, 0))) {}
    Matrix(const vector<vector<T>> &a) {
        n = a.size();
        m = a;
    }
    vector<T> &operator[](int i) { return m[i]; }
    const vector<T> &operator[](int i) const { return m[i]; }
    static Matrix identity(int x) {
        Matrix res(x);
        for (int i = 0; i < x; i++) res[i][i] = 1;
        return res;
    }
    Matrix operator+(const Matrix &a) const {
        Matrix x = (*this);
        return x += a;
    }
    Matrix operator*(const Matrix &a) const {
        Matrix x = (*this);
        return x *= a;
    }
    Matrix &operator+=(const Matrix &a) {
        Matrix res(n);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                res[i][j] = m[i][j] + a[i][j];
            }
        }
        m = res.m;
        return *this;
    }
    Matrix &operator*=(const Matrix &a) {
        Matrix res(n);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                for (int k = 0; k < n; k++) {
                    res[i][j] += m[i][k] * a[k][j];
                }
            }
        }
        m = res.m;
        return *this;
    }
    Matrix pow(ll b) const {
        Matrix x = *this, res = identity(n);
        while (b) {
            if (b & 1) {
                res *= x;
            }
            x *= x;
            b >>= 1;
        }
        return res;
    }
};
template <class S, S (*op)(S, S), S (*e)()>
struct segtree {
    int siz = 1;
    vector<S> dat;
    segtree(int n) : segtree(vector<S>(n, e())) {}
    segtree(const vector<S> &a) {
        while (siz < a.size()) siz <<= 1;
        dat = vector<S>(siz << 1, e());
        for (int i = 0; i < a.size(); i++) dat[siz + i] = a[i];
        for (int i = siz - 1; i >= 1; i--) dat[i] = op(dat[2 * i], dat[2 * i + 1]);
    }
    void set(int p, S x) {
        p += siz;
        dat[p] = x;
        while (p > 0) {
            p >>= 1;
            dat[p] = op(dat[2 * p], dat[2 * p + 1]);
        }
    }
    void add(int p, S x) { set(p, get(p) + x); }
    S get(int p) { return dat[p + siz]; }
    S prod(int l, int r) {
        S vl = e(), vr = e();
        l += siz, r += siz;
        while (l < r) {
            if (l & 1) vl = op(vl, dat[l++]);
            if (r & 1) vr = op(dat[--r], vr);
            l >>= 1, r >>= 1;
        }
        return op(vl, vr);
    }
    S all_prod() { return dat[1]; }
};

template <class T>
struct BIT {
    // 1-indexed
    int n, beki = 1;
    vector<T> bit;
    BIT(int x) {
        bit.resize(x + 1, 0);
        n = x;
        while (beki * 2 <= n) beki *= 2;
    }
    T sum(int i) {
        T res = 0;
        while (i > 0) {
            res += bit[i];
            i -= i & -i;
        }
        return res;
    }
    T sum(int l, int r) {
        //[l,r]
        return sum(r) - (l == 0 ? 0 : sum(l - 1));
    }
    void add(int i, T x) {
        while (i <= n) {
            bit[i] += x;
            i += i & -i;
        }
    }
    int lowerbound(T w) {
        if (w <= 0) return 0;
        int x = 0;
        for (int k = beki; k > 0; k >>= 1) {
            if (x + k <= n && bit[x + k] < w) {
                w -= bit[x + k];
                x += k;
            }
        }
        return x + 1;
    }
};
template <class T, T (*op)(T, T), T (*e)()>
struct sparsetable {
    vector<vector<T>> table;
    vector<int> logtable;
    sparsetable(vector<T> v) {
        int len = 0;
        while ((1 << len) <= v.size()) len++;
        table.assign(len, vector<T>(1 << len));
        for (int i = 0; i < (int)v.size(); i++) table[0][i] = v[i];
        for (int i = 1; i < len; i++) {
            for (int j = 0; j + (1 << i) <= (1 << len); j++) {
                table[i][j] = op(table[i - 1][j], table[i - 1][j + (1 << (i - 1))]);
            }
        }
        logtable.resize(v.size() + 1);
        for (int i = 2; i < logtable.size(); i++) {
            logtable[i] = logtable[(i >> 1)] + 1;
        }
    }
    T query(int l, int r) {
        assert(l <= r);
        if (l == r) return e();
        int len = logtable[r - l];
        return op(table[len][l], table[len][r - (1 << len)]);
    }
};
template <class T, T (*op)(T, T), T (*e)()>
struct disjointsparsetable {
    vector<vector<T>> table;
    vector<int> logtable;
    disjointsparsetable(vector<T> v) {
        int len = 0;
        while ((1 << len) <= v.size()) len++;
        table.assign(len, vector<T>(1 << len, e()));
        for (int i = 0; i < (int)v.size(); i++) table[0][i] = v[i];
        for (int i = 1; i < len; i++) {
            int shift = 1 << i;
            for (int j = 0; j < (int)v.size(); j += shift << 1) {
                int t = min(j + shift, (int)v.size());
                table[i][t - 1] = v[t - 1];
                for (int k = t - 2; k >= j; k--) table[i][k] = op(v[k], table[i][k + 1]);
                if (v.size() <= t) break;
                table[i][t] = v[t];
                int r = min(t + shift, (int)v.size());
                for (int k = t + 1; k < r; k++) table[i][k] = op(table[i][k - 1], v[k]);
            }
        }
        logtable.resize(1 << len);
        for (int i = 2; i < logtable.size(); i++) {
            logtable[i] = logtable[(i >> 1)] + 1;
        }
    }
    T query(int l, int r) {
        if (l == r) return e();
        if (l >= --r) return table[0][l];
        int len = logtable[l ^ r];
        return op(table[len][l], table[len][r]);
    };
};
struct lca_tree {
    int n, size;
    vector<vector<int>> par;
    vector<int> depth;
    lca_tree(vector<vector<int>> g, int root = 0) : n((int)g.size()), size(log2(n) + 2), par(size, vector<int>(n, -1)), depth(vector<int>(n, n)) {
        queue<int> que;
        depth[root] = 0;
        que.push(root);
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            for (int i : g[p]) {
                if (depth[i] > depth[p] + 1) {
                    depth[i] = depth[p] + 1;
                    par[0][i] = p;
                    que.push(i);
                }
            }
        }
        for (int k = 0; k < size - 1; k++) {
            for (int i = 0; i < n; i++) {
                if (par[k][i] == -1)
                    par[k + 1][i] = -1;
                else
                    par[k + 1][i] = par[k][par[k][i]];
            }
        }
    }
    int query(int u, int v) {
        if (depth[u] > depth[v]) swap(u, v);
        for (int k = size - 1; k >= 0; k--) {
            if (((depth[v] - depth[u]) >> k) & 1) v = par[k][v];
            if (u == v) return u;
        }
        for (int k = size - 1; k >= 0; k--) {
            if (par[k][u] != par[k][v]) {
                u = par[k][u];
                v = par[k][v];
            }
        }
        return par[0][u];
    }
    int dist(int u, int v) {
        int l = query(u, v);
        return depth[u] + depth[v] - 2 * depth[l];
    }
};
struct UnionFind {
    vector<int> par, siz, es;
    UnionFind(int x) {
        par.resize(x);
        siz.resize(x);
        es.resize(x);
        for (int i = 0; i < x; i++) {
            par[i] = i;
            siz[i] = 1;
            es[i] = 0;
        }
    }
    int find(int x) {
        if (par[x] == x) return x;
        return par[x] = find(par[x]);
    }
    bool unite(int x, int y) {
        x = find(x), y = find(y);
        if (x == y) {
            es[x]++;
            return false;
        }
        if (siz[x] < siz[y]) swap(x, y);
        par[y] = x;
        siz[x] += siz[y];
        es[x] += es[y] + 1;
        return true;
    }
    bool same(int x, int y) { return find(x) == find(y); }
    int size(int x) { return siz[find(x)]; }
    int edges(int x) { return es[find(x)]; }
};
struct Mo {
    int n;
    vector<pair<int, int>> lr;

    explicit Mo(int n) : n(n) {}

    void add(int l, int r) { /* [l, r) */ lr.emplace_back(l, r); }

    template <typename AL, typename AR, typename EL, typename ER, typename O>
    void build(const AL &add_left, const AR &add_right, const EL &erase_left, const ER &erase_right, const O &out) {
        int q = (int)lr.size();
        int bs = n / min<int>(n, sqrt(q));
        vector<int> ord(q);
        iota(begin(ord), end(ord), 0);
        sort(begin(ord), end(ord), [&](int a, int b) {
            int ablock = lr[a].first / bs, bblock = lr[b].first / bs;
            if (ablock != bblock) return ablock < bblock;
            return (ablock & 1) ? lr[a].second > lr[b].second : lr[a].second < lr[b].second;
        });
        int l = 0, r = 0;
        for (auto idx : ord) {
            while (l > lr[idx].first) add_left(--l);
            while (r < lr[idx].second) add_right(r++);
            while (l < lr[idx].first) erase_left(l++);
            while (r > lr[idx].second) erase_right(--r);
            out(idx);
        }
    }

    template <typename A, typename E, typename O>
    void build(const A &add, const E &erase, const O &out) {
        build(add, add, erase, erase, out);
    }
};
template <class S, S (*op)(S, S), S (*e)(), class F, S (*mapping)(F, S), F (*composition)(F, F), F (*id)()>
struct lazy_segtree {
    int _n, sz = 1;
    vector<S> dat;
    vector<F> lazy;
    lazy_segtree(vector<S> a) : _n(int(a.size())) {
        while (sz < _n) sz <<= 1;
        dat.resize(sz * 2, e());
        lazy.resize(sz * 2, id());
        rep(i, _n) dat[sz + i] = a[i];
        for (int i = sz - 1; i >= 1; i--) dat[i] = op(dat[2 * i], dat[2 * i + 1]);
    }
    void eval(int k) {
        dat[k] = mapping(lazy[k], dat[k]);
        if (k < sz) {
            lazy[k * 2] = composition(lazy[k], lazy[k * 2]);
            lazy[k * 2 + 1] = composition(lazy[k], lazy[k * 2 + 1]);
        }
        lazy[k] = id();
    }
    void apply(int a, int b, F f, int k = 1, int l = 0, int r = -1) {
        eval(k);
        if (r == -1) r = sz;
        if (r <= a || b <= l) return;
        if (a <= l && r <= b) {
            lazy[k] = composition(f, lazy[k]);
            eval(k);
            return;
        }
        int m = (l + r) >> 1;
        apply(a, b, f, 2 * k, l, m);
        apply(a, b, f, 2 * k + 1, m, r);
        dat[k] = op(dat[2 * k], dat[2 * k + 1]);
    }
    S prod(int a, int b, int k = 1, int l = 0, int r = -1) {
        eval(k);
        if (r == -1) r = sz;
        if (r <= a || b <= l) return e();
        if (a <= l && r <= b) return dat[k];
        int m = (l + r) >> 1;
        S vl = prod(a, b, 2 * k, l, m);
        S vr = prod(a, b, 2 * k + 1, m, r);
        return op(vl, vr);
    }
};

template <class S, S (*op)(S, S), S (*e)()>
struct dual_segtree {
    int sz = 1, log = 0;
    vector<S> lz;
    dual_segtree(vector<S> a) {
        int n = a.size();
        while (sz < n) {
            sz <<= 1;
            log++;
        }
        lz.assign(sz << 1, e());
        for (int i = 0; i < n; i++) lz[i + sz] = a[i];
    }
    void push(int k) {
        int b = __builtin_ctz(k);
        for (int d = log; d > b; d--) {
            lz[k >> d << 1] = op(lz[k >> d << 1], lz[k >> d]);
            lz[k >> d << 1 | 1] = op(lz[k >> d << 1 | 1], lz[k >> d]);
            lz[k >> d] = e();
        }
    }
    void apply(int l, int r, S x) {
        l += sz, r += sz;
        push(l);
        push(r);
        while (l < r) {
            if (l & 1) {
                lz[l] = op(lz[l], x);
                l++;
            }
            if (r & 1) {
                r--;
                lz[r] = op(lz[r], x);
            }
            l >>= 1, r >>= 1;
        }
    }
    S get(int k) {
        k += sz;
        S res = e();
        while (k) {
            res = op(res, lz[k]);
            k >>= 1;
        }
        return res;
    }
};
ll modinv(int x, int modulo) {
    int a = x, b = modulo, u = 1, v = 0, t;
    while (b > 0) {
        t = a / b;
        swap(a -= t * b, b);
        swap(u -= t * v, v);
    }
    return (u >= 0 ? u % modulo : (modulo - (-u) % modulo) % modulo);
}
struct auxiliary_tree : lca_tree {
    vector<int> in;
    vector<vector<int>> G;
    auxiliary_tree(vector<vector<int>> &g) : lca_tree(g), in((int)g.size()), G((int)g.size()) {
        int t = 0;
        dfs(0, -1, t, g);
    }
    void dfs(int v, int p, int &t, vector<vector<int>> &g) {
        in[v] = t++;
        for (auto u : g[v]) {
            if (u != p) dfs(u, v, t, g);
        }
    }
    using lca_tree::depth;
    pair<int, vector<int>> query(vector<int> vs, bool decending = false) {
        // decending: only parent to child
        assert(!vs.empty());
        sort(vs.begin(), vs.end(), [&](int a, int b) { return in[a] < in[b]; });
        int m = vs.size();
        stack<int> st;
        st.push(vs[0]);
        for (int i = 0; i < m - 1; i++) {
            int w = lca_tree::query(vs[i], vs[i + 1]);
            if (w != vs[i]) {
                int l = st.top();
                st.pop();
                while (!st.empty() && depth[w] < depth[st.top()]) {
                    if (!decending) G[l].push_back(st.top());
                    G[st.top()].push_back(l);
                    l = st.top();
                    st.pop();
                }
                if (st.empty() || st.top() != w) {
                    st.push(w);
                    vs.push_back(w);
                }
                if (!decending) G[l].push_back(w);
                G[w].push_back(l);
            }
            st.push(vs[i + 1]);
        }
        while (st.size() > 1) {
            int x = st.top();
            st.pop();
            if (!decending) G[x].push_back(st.top());
            G[st.top()].push_back(x);
        }
        // {root,vertex_list}
        return make_pair(st.top(), vs);
    }
    void clear(vector<int> vs) {
        for (int v : vs) G[v].clear();
    }
};
/*
#include <atcoder/convolution>
template <int T>
inline std::ostream &operator<<(std::ostream &os, const atcoder::static_modint<T> &p) {
    return os << p.val();
}
template <int T>
inline std::istream &operator>>(std::istream &is, atcoder::static_modint<T> &a) {
    int64_t t;
    is >> t;
    a = t;
    return is;
}

template <typename mint>
struct FormalPowerSeries : vector<mint> {
    using vector<mint>::vector;
    using FPS = FormalPowerSeries;
    FPS &operator+=(const FPS &r) {
        if (r.size() > this->size()) this->resize(r.size());
        for (int i = 0; i < (int)r.size(); i++) (*this)[i] += r[i];
        return *this;
    }
    FPS &operator+=(const mint &r) {
        if (this->empty()) this->resize(1);
        (*this)[0] += r;
        return *this;
    }

    FPS &operator-=(const FPS &r) {
        if (r.size() > this->size()) this->resize(r.size());
        for (int i = 0; i < (int)r.size(); i++) (*this)[i] -= r[i];
        return *this;
    }

    FPS &operator-=(const mint &r) {
        if (this->empty()) this->resize(1);
        (*this)[0] -= r;
        return *this;
    }
    FPS &operator*=(const FPS &r) {
        if (this->empty() || r.empty()) {
            this->clear();
            return *this;
        }
        assert(mint::mod() == 998244353);
        vector<mint> prod = atcoder::convolution(*this, r);
        this->resize((int)prod.size());
        for (int i = 0; i < (int)this->size(); i++) (*this)[i] = prod[i];
        return *this;
    }
    FPS &operator*=(const mint &v) {
        for (int i = 0; i < (int)this->size(); i++) (*this)[i] *= v;
        return *this;
    }
    FPS &operator/=(const FPS &r) {
        if (this->size() < r.size()) {
            this->clear();
            return *this;
        }
        int n = this->size() - r.size() + 1;
        return *this = (rev().pre(n) * r.rev().inv(n)).pre(n).rev();
    }
    FPS &operator%=(const FPS &r) {
        *this -= *this / r * r;
        shrink();
        return *this;
    }
    FPS operator+(const FPS &r) const { return FPS(*this) += r; }
    FPS operator+(const mint &v) const { return FPS(*this) += v; }
    FPS operator-(const FPS &r) const { return FPS(*this) -= r; }
    FPS operator-(const mint &v) const { return FPS(*this) -= v; }
    FPS operator*(const FPS &r) const { return FPS(*this) *= r; }
    FPS operator*(const mint &v) const { return FPS(*this) *= v; }
    FPS operator/(const FPS &r) const { return FPS(*this) /= r; }
    FPS operator%(const FPS &r) const { return FPS(*this) %= r; }
    FPS operator-() const {
        FPS ret(this->size());
        for (int i = 0; i < (int)this->size(); i++) ret[i] = -(*this)[i];
        return ret;
    }
    void shrink() {
        while (this->size() && this->back() == mint(0)) this->pop_back();
    }
    FPS operator>>(int sz) const {
        if ((int)this->size() <= sz) return {};
        FPS ret(*this);
        ret.erase(ret.begin(), ret.begin() + sz);
        return ret;
    }
    FPS operator<<(int sz) const {
        FPS ret(*this);
        ret.insert(ret.begin(), sz, mint(0));
        return ret;
    }
    FPS pre(int sz) const { return FPS(begin(*this), begin(*this) + min((int)this->size(), sz)); }
    FPS rev() const {
        FPS ret(*this);
        reverse(begin(ret), end(ret));
        return ret;
    }
    FPS diff() const {
        const int n = this->size();
        FPS ret(max(0, n - 1));
        for (int i = 1; i < n; i++) ret[i - 1] = (*this)[i] * mint(i);
        return ret;
    }
    FPS integral() const {
        const int n = this->size();
        FPS ret(n + 1);
        ret[0] = mint(0);
        if (n > 0) ret[1] = mint(1);
        auto mod = mint::mod();
        for (int i = 2; i <= n; i++) ret[i] = (-ret[mod % i] * (mod / i));
        for (int i = 0; i < n; i++) ret[i + 1] *= (*this)[i];
        return ret;
    }
    FPS inv(int deg = -1) const {
        assert(((*this)[0]) != mint(0));
        const int n = this->size();
        if (deg == -1) deg = n;
        FPS ret({mint(1) / (*this)[0]});
        for (int i = 1; i < deg; i <<= 1) {
            ret = (ret + ret - ret * ret * pre(i << 1)).pre(i << 1);
        }
        return ret.pre(deg);
    }
    FPS log(int deg = -1) {
        assert((*this)[0] == mint(1));
        if (deg == -1) deg = this->size();
        return (this->diff() * this->inv(deg)).pre(deg - 1).integral();
    }
    FPS exp(int deg = -1) const {
        assert((*this)[0] == mint(0));
        const int n = this->size();
        if (deg == -1) deg = n;
        FPS ret({mint(1)});
        for (int i = 1; i < deg; i <<= 1) {
            ret = (ret * (pre(i << 1) + mint(1) - ret.log(i << 1))).pre(i << 1);
        }
        return ret.pre(deg);
    }
    FPS pow(int64_t k, int deg = -1) const {
        const int n = this->size();
        if (deg == -1) deg = n;
        if (k == 0) {
            FPS ret(deg);
            if (deg) ret[0] = 1;
            return ret;
        }
        for (int i = 0; i < n; i++) {
            if ((*this)[i] != mint(0)) {
                mint rev = mint(1) / (*this)[i];
                FPS ret = (((*this * rev) >> i).log(deg) * k).exp(deg);
                ret *= (*this)[i].pow(k);
                ret = (ret << (i * k)).pre(deg);
                if ((int)ret.size() < deg) ret.resize(deg, mint(0));
                return ret;
            }
            if (__int128_t(i + 1) * k >= deg) return FPS(deg, mint(0));
        }
        return FPS(deg, mint(0));
    }
    FPS sqrt(int deg = -1) const {
        const int n = this->size();
        if (deg == -1) deg = n;
        if (n == 0) return FPS(deg, 0);
        if ((*this)[0] == mint(0)) {
            for (int i = 1; i < n; i++) {
                if ((*this)[i] != mint(0)) {
                    if (i & 1) return {};
                    if (deg - i / 2 <= 0) break;
                    auto ret = (*this >> i).sqrt(deg - i / 2);
                    if (ret.empty()) return {};
                    ret = ret << (i / 2);
                    if ((int)ret.size() < deg) ret.resize(deg, mint(0));
                    return ret;
                }
            }
            return FPS(deg, 0);
        }
        int64_t sqr = mod_sqrt((*this)[0].val(), mint::mod());
        if (sqr == -1) return {};
        assert(sqr * sqr % mint::mod() == (*this)[0].val());
        FPS ret({mint(sqr)});
        mint inv2 = mint(2).inv();
        for (int i = 1; i < deg; i <<= 1) {
            ret = (ret + pre(i << 1) * ret.inv(i << 1)) * inv2;
        }
        return ret.pre(deg);
    }
    mint eval(mint x) const {
        mint r = 0, w = 1;
        for (auto &v : *this) r += w * v, w *= x;
        return r;
    }
};
// calculate f(x + a)
template <typename mint>
FormalPowerSeries<mint> TaylorShift(FormalPowerSeries<mint> f, mint a, Binomial<mint> &bin) {
    int n = f.size();
    for (int i = 0; i < n; i++) f[i] *= bin.fact[i];
    f = f.rev();
    FormalPowerSeries<mint> g(n, mint(1));
    for (int i = 1; i < n; i++) g[i] = g[i - 1] * a * bin.inv[i];
    f = (f * g).pre(n);
    f = f.rev();
    for (int i = 0; i < n; i++) f[i] *= bin.factinv[i];
    return f;
}
template <typename mint>
FormalPowerSeries<mint> FPS_Product(vector<FormalPowerSeries<mint>> f) {
    int n = (int)f.size();
    if (n == 0) return {1};
    function<FormalPowerSeries<mint>(int, int)> calc = [&](int l, int r) {
        if (r - l == 1) return f[l];
        int m = (l + r) / 2;
        return calc(l, m) * calc(m, r);
    };
    return calc(0, n);
}
#include <atcoder/string>
int string_period(string s) {
    int n = s.size();
    auto z = atcoder::z_algorithm(s);
    for (int i = 1; i < n; i++) {
        if (n % i == 0 && z[i] == n - i) return i;
    }
    return n;
}
*/
struct LowLink {
    vector<vector<int>> g;
    vector<int> ord, low, out;
    vector<bool> used;
    vector<pair<int, int>> bridge;
    vector<pair<int, int>> articulation;
    int unions;
    LowLink(vector<vector<int>> g) : g(g) {
        int n = (int)g.size();
        ord.resize(n);
        low.resize(n);
        out.resize(n);
        used.resize(n);
        unions = 0;
        int t = 0;
        for (int i = 0; i < n; i++) {
            if (!used[i]) {
                dfs(i, t, -1);
                unions++;
            }
        }
    }
    void dfs(int v, int &t, int par) {
        used[v] = true;
        ord[v] = t++, low[v] = ord[v];
        int cnt = 0;
        bool par_back = false;
        for (int to : g[v]) {
            if (!used[to]) {
                dfs(to, t, v);
                low[v] = min(low[v], low[to]);
                if (ord[v] < low[to]) bridge.push_back(minmax(v, to));
                if (ord[v] <= low[to]) cnt++;
            } else if (to != par || par_back) {
                low[v] = min(low[v], ord[to]);
            } else
                par_back = true;
        }
        if (par != -1) cnt++;
        if (cnt >= 2) articulation.push_back({v, cnt});
        out[v] = t;
    }
};
void solve(int n, int m) {
    using mint = modint<1000000007>;
    vector<string> s(n);
    rep(i, n) cin >> s[i];
    // ほうじょ？
    mint ans = 1;
    rep(d, 4) {
        vector<int> u, v;
        rep(i, n) {
            if (i % 4 == d) u.push_back(i);
            if ((i + 2) % 4 == d) v.push_back(i);
        }
        int k = v.size();
        vector<mint> dp(1 << k, 0);
        dp[0] = 1;
        rep(i, m) {
            auto ref = (i % 2 == 0 ? v : u);
            auto nxt = (i % 2 == 0 ? u : v);
            vector<mint> tmp(1 << (nxt.size()), 0);
            rep(S, dp.size()) {
                if (dp[S] == 0) continue;
                vector<bool> ng(n);
                rep(p, ref.size()) {
                    if ((S >> p) & 1) {
                        if (ref[p] + 2 < n) ng[ref[p] + 2] = true;
                        if (ref[p] >= 2) ng[ref[p] - 2] = true;
                    }
                }
                int T = 0;
                int t = 0;
                for (int j : nxt) {
                    if (s[j][i] == '.' && !ng[j]) T |= 1 << t;
                    t++;
                }
                tmp[T] += dp[S];
            }

            swap(dp, tmp);
            rep(j, nxt.size()) {
                rep(S, dp.size()) {
                    if ((S >> j) & 1) {
                        dp[S & ~(1 << j)] += dp[S];
                    }
                }
            }
        }
        mint sum = 0;
        for (auto x : dp) sum += x;
        ans *= sum;
    }
    cout << ans << endl;
}
int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    int n, m, k, x, y;
    ll p;
    string s, t;
    while (cin >> n >> m) {
        if (n == 0) break;
        solve(n, m);
    }
}
accuracy: 1
time_ms: 2490
memory_kb: 3508
final_rank: 5

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, n) for (int i = 0; i < n; i++)
#define all(v) v.begin(), v.end()
template <class T, class U>
inline bool chmax(T &a, U b) {
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}
template <class T, class U>
inline bool chmin(T &a, U b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}
constexpr int INF = 1000000000;
constexpr int64_t llINF = 3000000000000000000;
constexpr double eps = 1e-10;
const double pi = acos(-1);
struct linear_sieve {
    vector<int> least_factor, prime_list;
    linear_sieve(int n) : least_factor(n + 1, 0) {
        for (int i = 2; i <= n; i++) {
            if (least_factor[i] == 0) {
                least_factor[i] = i;
                prime_list.push_back(i);
            }
            for (int p : prime_list) {
                if (ll(i) * p > n || p > least_factor[i]) break;
                least_factor[i * p] = p;
            }
        }
    }
};
ll extgcd(ll a, ll b, ll &x, ll &y) {
    if (b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    ll d = extgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}
ll modpow(ll a, ll b, ll m) {
    ll res = 1;
    while (b) {
        if (b & 1) {
            res *= a;
            res %= m;
        }
        a *= a;
        a %= m;
        b >>= 1;
    }
    return res;
}
template <int modulo>
struct modint {
    int x;
    modint() : x(0) {}
    modint(int64_t y) : x(y >= 0 ? y % modulo : (modulo - (-y) % modulo) % modulo) {}
    modint &operator+=(const modint &p) {
        if ((x += p.x) >= modulo) x -= modulo;
        return *this;
    }
    modint &operator-=(const modint &p) {
        if ((x += modulo - p.x) >= modulo) x -= modulo;
        return *this;
    }
    modint &operator*=(const modint &p) {
        x = (int)(1LL * x * p.x % modulo);
        return *this;
    }
    modint &operator/=(const modint &p) {
        *this *= p.inv();
        return *this;
    }
    modint operator-() const { return modint(-x); }
    modint operator+(const modint &p) const { return modint(*this) += p; }
    modint operator-(const modint &p) const { return modint(*this) -= p; }
    modint operator*(const modint &p) const { return modint(*this) *= p; }
    modint operator/(const modint &p) const { return modint(*this) /= p; }
    bool operator==(const modint &p) const { return x == p.x; }
    bool operator!=(const modint &p) const { return x != p.x; }
    modint inv() const {
        int a = x, b = modulo, u = 1, v = 0, t;
        while (b > 0) {
            t = a / b;
            swap(a -= t * b, b);
            swap(u -= t * v, v);
        }
        return modint(u);
    }
    modint pow(int64_t n) const {
        modint ret(1), mul(x);
        while (n > 0) {
            if (n & 1) ret *= mul;
            mul *= mul;
            n >>= 1;
        }
        return ret;
    }
    friend ostream &operator<<(ostream &os, const modint &p) { return os << p.x; }
    friend istream &operator>>(istream &is, modint &a) {
        int64_t t;
        is >> t;
        a = modint<modulo>(t);
        return (is);
    }
    int val() const { return x; }
    static constexpr int mod() { return modulo; }
    static constexpr int half() { return (modulo + 1) >> 1; }
};
template <typename T>
struct Binomial {
    vector<T> inv, fact, factinv;
    Binomial(int n) {
        inv.resize(n + 1);
        fact.resize(n + 1);
        factinv.resize(n + 1);
        inv[0] = fact[0] = factinv[0] = 1;
        for (int i = 1; i <= n; i++) fact[i] = fact[i - 1] * i;
        factinv[n] = fact[n].inv();
        inv[n] = fact[n - 1] * factinv[n];
        for (int i = n - 1; i >= 1; i--) {
            factinv[i] = factinv[i + 1] * (i + 1);
            inv[i] = fact[i - 1] * factinv[i];
        }
    }
    T C(int n, int r) {
        if (n < 0 || n < r || r < 0) return 0;
        return fact[n] * factinv[n - r] * factinv[r];
    }
    T P(int n, int r) {
        if (n < 0 || n < r || r < 0) return 0;
        return fact[n] * factinv[n - r];
    }
    T H(int n, int r) {
        if (n == 0 && r == 0) return 1;
        if (n < 0 || r < 0) return 0;
        return r == 0 ? 1 : C(n + r - 1, r);
    }
};
template <class T>
struct Matrix {
    int n;
    vector<vector<T>> m;
    Matrix() : Matrix(0) {}
    Matrix(int x) : Matrix(vector<vector<T>>(x, vector<T>(x, 0))) {}
    Matrix(const vector<vector<T>> &a) {
        n = a.size();
        m = a;
    }
    vector<T> &operator[](int i) { return m[i]; }
    const vector<T> &operator[](int i) const { return m[i]; }
    static Matrix identity(int x) {
        Matrix res(x);
        for (int i = 0; i < x; i++) res[i][i] = 1;
        return res;
    }
    Matrix operator+(const Matrix &a) const {
        Matrix x = (*this);
        return x += a;
    }
    Matrix operator*(const Matrix &a) const {
        Matrix x = (*this);
        return x *= a;
    }
    Matrix &operator+=(const Matrix &a) {
        Matrix res(n);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                res[i][j] = m[i][j] + a[i][j];
            }
        }
        m = res.m;
        return *this;
    }
    Matrix &operator*=(const Matrix &a) {
        Matrix res(n);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                for (int k = 0; k < n; k++) {
                    res[i][j] += m[i][k] * a[k][j];
                }
            }
        }
        m = res.m;
        return *this;
    }
    Matrix pow(ll b) const {
        Matrix x = *this, res = identity(n);
        while (b) {
            if (b & 1) {
                res *= x;
            }
            x *= x;
            b >>= 1;
        }
        return res;
    }
};
template <class S, S (*op)(S, S), S (*e)()>
struct segtree {
    int siz = 1;
    vector<S> dat;
    segtree(int n) : segtree(vector<S>(n, e())) {}
    segtree(const vector<S> &a) {
        while (siz < a.size()) siz <<= 1;
        dat = vector<S>(siz << 1, e());
        for (int i = 0; i < a.size(); i++) dat[siz + i] = a[i];
        for (int i = siz - 1; i >= 1; i--) dat[i] = op(dat[2 * i], dat[2 * i + 1]);
    }
    void set(int p, S x) {
        p += siz;
        dat[p] = x;
        while (p > 0) {
            p >>= 1;
            dat[p] = op(dat[2 * p], dat[2 * p + 1]);
        }
    }
    void add(int p, S x) { set(p, get(p) + x); }
    S get(int p) { return dat[p + siz]; }
    S prod(int l, int r) {
        S vl = e(), vr = e();
        l += siz, r += siz;
        while (l < r) {
            if (l & 1) vl = op(vl, dat[l++]);
            if (r & 1) vr = op(dat[--r], vr);
            l >>= 1, r >>= 1;
        }
        return op(vl, vr);
    }
    S all_prod() { return dat[1]; }
};

template <class T>
struct BIT {
    // 1-indexed
    int n, beki = 1;
    vector<T> bit;
    BIT(int x) {
        bit.resize(x + 1, 0);
        n = x;
        while (beki * 2 <= n) beki *= 2;
    }
    T sum(int i) {
        T res = 0;
        while (i > 0) {
            res += bit[i];
            i -= i & -i;
        }
        return res;
    }
    T sum(int l, int r) {
        //[l,r]
        return sum(r) - (l == 0 ? 0 : sum(l - 1));
    }
    void add(int i, T x) {
        while (i <= n) {
            bit[i] += x;
            i += i & -i;
        }
    }
    int lowerbound(T w) {
        if (w <= 0) return 0;
        int x = 0;
        for (int k = beki; k > 0; k >>= 1) {
            if (x + k <= n && bit[x + k] < w) {
                w -= bit[x + k];
                x += k;
            }
        }
        return x + 1;
    }
};
template <class T, T (*op)(T, T), T (*e)()>
struct sparsetable {
    vector<vector<T>> table;
    vector<int> logtable;
    sparsetable(vector<T> v) {
        int len = 0;
        while ((1 << len) <= v.size()) len++;
        table.assign(len, vector<T>(1 << len));
        for (int i = 0; i < (int)v.size(); i++) table[0][i] = v[i];
        for (int i = 1; i < len; i++) {
            for (int j = 0; j + (1 << i) <= (1 << len); j++) {
                table[i][j] = op(table[i - 1][j], table[i - 1][j + (1 << (i - 1))]);
            }
        }
        logtable.resize(v.size() + 1);
        for (int i = 2; i < logtable.size(); i++) {
            logtable[i] = logtable[(i >> 1)] + 1;
        }
    }
    T query(int l, int r) {
        assert(l <= r);
        if (l == r) return e();
        int len = logtable[r - l];
        return op(table[len][l], table[len][r - (1 << len)]);
    }
};
template <class T, T (*op)(T, T), T (*e)()>
struct disjointsparsetable {
    vector<vector<T>> table;
    vector<int> logtable;
    disjointsparsetable(vector<T> v) {
        int len = 0;
        while ((1 << len) <= v.size()) len++;
        table.assign(len, vector<T>(1 << len, e()));
        for (int i = 0; i < (int)v.size(); i++) table[0][i] = v[i];
        for (int i = 1; i < len; i++) {
            int shift = 1 << i;
            for (int j = 0; j < (int)v.size(); j += shift << 1) {
                int t = min(j + shift, (int)v.size());
                table[i][t - 1] = v[t - 1];
                for (int k = t - 2; k >= j; k--) table[i][k] = op(v[k], table[i][k + 1]);
                if (v.size() <= t) break;
                table[i][t] = v[t];
                int r = min(t + shift, (int)v.size());
                for (int k = t + 1; k < r; k++) table[i][k] = op(table[i][k - 1], v[k]);
            }
        }
        logtable.resize(1 << len);
        for (int i = 2; i < logtable.size(); i++) {
            logtable[i] = logtable[(i >> 1)] + 1;
        }
    }
    T query(int l, int r) {
        if (l == r) return e();
        if (l >= --r) return table[0][l];
        int len = logtable[l ^ r];
        return op(table[len][l], table[len][r]);
    };
};
struct lca_tree {
    int n, size;
    vector<vector<int>> par;
    vector<int> depth;
    lca_tree(vector<vector<int>> g, int root = 0) : n((int)g.size()), size(log2(n) + 2), par(size, vector<int>(n, -1)), depth(vector<int>(n, n)) {
        queue<int> que;
        depth[root] = 0;
        que.push(root);
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            for (int i : g[p]) {
                if (depth[i] > depth[p] + 1) {
                    depth[i] = depth[p] + 1;
                    par[0][i] = p;
                    que.push(i);
                }
            }
        }
        for (int k = 0; k < size - 1; k++) {
            for (int i = 0; i < n; i++) {
                if (par[k][i] == -1)
                    par[k + 1][i] = -1;
                else
                    par[k + 1][i] = par[k][par[k][i]];
            }
        }
    }
    int query(int u, int v) {
        if (depth[u] > depth[v]) swap(u, v);
        for (int k = size - 1; k >= 0; k--) {
            if (((depth[v] - depth[u]) >> k) & 1) v = par[k][v];
            if (u == v) return u;
        }
        for (int k = size - 1; k >= 0; k--) {
            if (par[k][u] != par[k][v]) {
                u = par[k][u];
                v = par[k][v];
            }
        }
        return par[0][u];
    }
    int dist(int u, int v) {
        int l = query(u, v);
        return depth[u] + depth[v] - 2 * depth[l];
    }
};
struct UnionFind {
    vector<int> par, siz, es;
    UnionFind(int x) {
        par.resize(x);
        siz.resize(x);
        es.resize(x);
        for (int i = 0; i < x; i++) {
            par[i] = i;
            siz[i] = 1;
            es[i] = 0;
        }
    }
    int find(int x) {
        if (par[x] == x) return x;
        return par[x] = find(par[x]);
    }
    bool unite(int x, int y) {
        x = find(x), y = find(y);
        if (x == y) {
            es[x]++;
            return false;
        }
        if (siz[x] < siz[y]) swap(x, y);
        par[y] = x;
        siz[x] += siz[y];
        es[x] += es[y] + 1;
        return true;
    }
    bool same(int x, int y) { return find(x) == find(y); }
    int size(int x) { return siz[find(x)]; }
    int edges(int x) { return es[find(x)]; }
};
struct Mo {
    int n;
    vector<pair<int, int>> lr;

    explicit Mo(int n) : n(n) {}

    void add(int l, int r) { /* [l, r) */ lr.emplace_back(l, r); }

    template <typename AL, typename AR, typename EL, typename ER, typename O>
    void build(const AL &add_left, const AR &add_right, const EL &erase_left, const ER &erase_right, const O &out) {
        int q = (int)lr.size();
        int bs = n / min<int>(n, sqrt(q));
        vector<int> ord(q);
        iota(begin(ord), end(ord), 0);
        sort(begin(ord), end(ord), [&](int a, int b) {
            int ablock = lr[a].first / bs, bblock = lr[b].first / bs;
            if (ablock != bblock) return ablock < bblock;
            return (ablock & 1) ? lr[a].second > lr[b].second : lr[a].second < lr[b].second;
        });
        int l = 0, r = 0;
        for (auto idx : ord) {
            while (l > lr[idx].first) add_left(--l);
            while (r < lr[idx].second) add_right(r++);
            while (l < lr[idx].first) erase_left(l++);
            while (r > lr[idx].second) erase_right(--r);
            out(idx);
        }
    }

    template <typename A, typename E, typename O>
    void build(const A &add, const E &erase, const O &out) {
        build(add, add, erase, erase, out);
    }
};
template <class S, S (*op)(S, S), S (*e)(), class F, S (*mapping)(F, S), F (*composition)(F, F), F (*id)()>
struct lazy_segtree {
    int _n, sz = 1;
    vector<S> dat;
    vector<F> lazy;
    lazy_segtree(vector<S> a) : _n(int(a.size())) {
        while (sz < _n) sz <<= 1;
        dat.resize(sz * 2, e());
        lazy.resize(sz * 2, id());
        rep(i, _n) dat[sz + i] = a[i];
        for (int i = sz - 1; i >= 1; i--) dat[i] = op(dat[2 * i], dat[2 * i + 1]);
    }
    void eval(int k) {
        dat[k] = mapping(lazy[k], dat[k]);
        if (k < sz) {
            lazy[k * 2] = composition(lazy[k], lazy[k * 2]);
            lazy[k * 2 + 1] = composition(lazy[k], lazy[k * 2 + 1]);
        }
        lazy[k] = id();
    }
    void apply(int a, int b, F f, int k = 1, int l = 0, int r = -1) {
        eval(k);
        if (r == -1) r = sz;
        if (r <= a || b <= l) return;
        if (a <= l && r <= b) {
            lazy[k] = composition(f, lazy[k]);
            eval(k);
            return;
        }
        int m = (l + r) >> 1;
        apply(a, b, f, 2 * k, l, m);
        apply(a, b, f, 2 * k + 1, m, r);
        dat[k] = op(dat[2 * k], dat[2 * k + 1]);
    }
    S prod(int a, int b, int k = 1, int l = 0, int r = -1) {
        eval(k);
        if (r == -1) r = sz;
        if (r <= a || b <= l) return e();
        if (a <= l && r <= b) return dat[k];
        int m = (l + r) >> 1;
        S vl = prod(a, b, 2 * k, l, m);
        S vr = prod(a, b, 2 * k + 1, m, r);
        return op(vl, vr);
    }
};

template <class S, S (*op)(S, S), S (*e)()>
struct dual_segtree {
    int sz = 1, log = 0;
    vector<S> lz;
    dual_segtree(vector<S> a) {
        int n = a.size();
        while (sz < n) {
            sz <<= 1;
            log++;
        }
        lz.assign(sz << 1, e());
        for (int i = 0; i < n; i++) lz[i + sz] = a[i];
    }
    void push(int k) {
        int b = __builtin_ctz(k);
        for (int d = log; d > b; d--) {
            lz[k >> d << 1] = op(lz[k >> d << 1], lz[k >> d]);
            lz[k >> d << 1 | 1] = op(lz[k >> d << 1 | 1], lz[k >> d]);
            lz[k >> d] = e();
        }
    }
    void apply(int l, int r, S x) {
        l += sz, r += sz;
        push(l);
        push(r);
        while (l < r) {
            if (l & 1) {
                lz[l] = op(lz[l], x);
                l++;
            }
            if (r & 1) {
                r--;
                lz[r] = op(lz[r], x);
            }
            l >>= 1, r >>= 1;
        }
    }
    S get(int k) {
        k += sz;
        S res = e();
        while (k) {
            res = op(res, lz[k]);
            k >>= 1;
        }
        return res;
    }
};
ll modinv(int x, int modulo) {
    int a = x, b = modulo, u = 1, v = 0, t;
    while (b > 0) {
        t = a / b;
        swap(a -= t * b, b);
        swap(u -= t * v, v);
    }
    return (u >= 0 ? u % modulo : (modulo - (-u) % modulo) % modulo);
}
struct auxiliary_tree : lca_tree {
    vector<int> in;
    vector<vector<int>> G;
    auxiliary_tree(vector<vector<int>> &g) : lca_tree(g), in((int)g.size()), G((int)g.size()) {
        int t = 0;
        dfs(0, -1, t, g);
    }
    void dfs(int v, int p, int &t, vector<vector<int>> &g) {
        in[v] = t++;
        for (auto u : g[v]) {
            if (u != p) dfs(u, v, t, g);
        }
    }
    using lca_tree::depth;
    pair<int, vector<int>> query(vector<int> vs, bool decending = false) {
        // decending: only parent to child
        assert(!vs.empty());
        sort(vs.begin(), vs.end(), [&](int a, int b) { return in[a] < in[b]; });
        int m = vs.size();
        stack<int> st;
        st.push(vs[0]);
        for (int i = 0; i < m - 1; i++) {
            int w = lca_tree::query(vs[i], vs[i + 1]);
            if (w != vs[i]) {
                int l = st.top();
                st.pop();
                while (!st.empty() && depth[w] < depth[st.top()]) {
                    if (!decending) G[l].push_back(st.top());
                    G[st.top()].push_back(l);
                    l = st.top();
                    st.pop();
                }
                if (st.empty() || st.top() != w) {
                    st.push(w);
                    vs.push_back(w);
                }
                if (!decending) G[l].push_back(w);
                G[w].push_back(l);
            }
            st.push(vs[i + 1]);
        }
        while (st.size() > 1) {
            int x = st.top();
            st.pop();
            if (!decending) G[x].push_back(st.top());
            G[st.top()].push_back(x);
        }
        // {root,vertex_list}
        return make_pair(st.top(), vs);
    }
    void clear(vector<int> vs) {
        for (int v : vs) G[v].clear();
    }
};
/*
#include <atcoder/convolution>
template <int T>
inline std::ostream &operator<<(std::ostream &os, const atcoder::static_modint<T> &p) {
    return os << p.val();
}
template <int T>
inline std::istream &operator>>(std::istream &is, atcoder::static_modint<T> &a) {
    int64_t t;
    is >> t;
    a = t;
    return is;
}

template <typename mint>
struct FormalPowerSeries : vector<mint> {
    using vector<mint>::vector;
    using FPS = FormalPowerSeries;
    FPS &operator+=(const FPS &r) {
        if (r.size() > this->size()) this->resize(r.size());
        for (int i = 0; i < (int)r.size(); i++) (*this)[i] += r[i];
        return *this;
    }
    FPS &operator+=(const mint &r) {
        if (this->empty()) this->resize(1);
        (*this)[0] += r;
        return *this;
    }

    FPS &operator-=(const FPS &r) {
        if (r.size() > this->size()) this->resize(r.size());
        for (int i = 0; i < (int)r.size(); i++) (*this)[i] -= r[i];
        return *this;
    }

    FPS &operator-=(const mint &r) {
        if (this->empty()) this->resize(1);
        (*this)[0] -= r;
        return *this;
    }
    FPS &operator*=(const FPS &r) {
        if (this->empty() || r.empty()) {
            this->clear();
            return *this;
        }
        assert(mint::mod() == 998244353);
        vector<mint> prod = atcoder::convolution(*this, r);
        this->resize((int)prod.size());
        for (int i = 0; i < (int)this->size(); i++) (*this)[i] = prod[i];
        return *this;
    }
    FPS &operator*=(const mint &v) {
        for (int i = 0; i < (int)this->size(); i++) (*this)[i] *= v;
        return *this;
    }
    FPS &operator/=(const FPS &r) {
        if (this->size() < r.size()) {
            this->clear();
            return *this;
        }
        int n = this->size() - r.size() + 1;
        return *this = (rev().pre(n) * r.rev().inv(n)).pre(n).rev();
    }
    FPS &operator%=(const FPS &r) {
        *this -= *this / r * r;
        shrink();
        return *this;
    }
    FPS operator+(const FPS &r) const { return FPS(*this) += r; }
    FPS operator+(const mint &v) const { return FPS(*this) += v; }
    FPS operator-(const FPS &r) const { return FPS(*this) -= r; }
    FPS operator-(const mint &v) const { return FPS(*this) -= v; }
    FPS operator*(const FPS &r) const { return FPS(*this) *= r; }
    FPS operator*(const mint &v) const { return FPS(*this) *= v; }
    FPS operator/(const FPS &r) const { return FPS(*this) /= r; }
    FPS operator%(const FPS &r) const { return FPS(*this) %= r; }
    FPS operator-() const {
        FPS ret(this->size());
        for (int i = 0; i < (int)this->size(); i++) ret[i] = -(*this)[i];
        return ret;
    }
    void shrink() {
        while (this->size() && this->back() == mint(0)) this->pop_back();
    }
    FPS operator>>(int sz) const {
        if ((int)this->size() <= sz) return {};
        FPS ret(*this);
        ret.erase(ret.begin(), ret.begin() + sz);
        return ret;
    }
    FPS operator<<(int sz) const {
        FPS ret(*this);
        ret.insert(ret.begin(), sz, mint(0));
        return ret;
    }
    FPS pre(int sz) const { return FPS(begin(*this), begin(*this) + min((int)this->size(), sz)); }
    FPS rev() const {
        FPS ret(*this);
        reverse(begin(ret), end(ret));
        return ret;
    }
    FPS diff() const {
        const int n = this->size();
        FPS ret(max(0, n - 1));
        for (int i = 1; i < n; i++) ret[i - 1] = (*this)[i] * mint(i);
        return ret;
    }
    FPS integral() const {
        const int n = this->size();
        FPS ret(n + 1);
        ret[0] = mint(0);
        if (n > 0) ret[1] = mint(1);
        auto mod = mint::mod();
        for (int i = 2; i <= n; i++) ret[i] = (-ret[mod % i] * (mod / i));
        for (int i = 0; i < n; i++) ret[i + 1] *= (*this)[i];
        return ret;
    }
    FPS inv(int deg = -1) const {
        assert(((*this)[0]) != mint(0));
        const int n = this->size();
        if (deg == -1) deg = n;
        FPS ret({mint(1) / (*this)[0]});
        for (int i = 1; i < deg; i <<= 1) {
            ret = (ret + ret - ret * ret * pre(i << 1)).pre(i << 1);
        }
        return ret.pre(deg);
    }
    FPS log(int deg = -1) {
        assert((*this)[0] == mint(1));
        if (deg == -1) deg = this->size();
        return (this->diff() * this->inv(deg)).pre(deg - 1).integral();
    }
    FPS exp(int deg = -1) const {
        assert((*this)[0] == mint(0));
        const int n = this->size();
        if (deg == -1) deg = n;
        FPS ret({mint(1)});
        for (int i = 1; i < deg; i <<= 1) {
            ret = (ret * (pre(i << 1) + mint(1) - ret.log(i << 1))).pre(i << 1);
        }
        return ret.pre(deg);
    }
    FPS pow(int64_t k, int deg = -1) const {
        const int n = this->size();
        if (deg == -1) deg = n;
        if (k == 0) {
            FPS ret(deg);
            if (deg) ret[0] = 1;
            return ret;
        }
        for (int i = 0; i < n; i++) {
            if ((*this)[i] != mint(0)) {
                mint rev = mint(1) / (*this)[i];
                FPS ret = (((*this * rev) >> i).log(deg) * k).exp(deg);
                ret *= (*this)[i].pow(k);
                ret = (ret << (i * k)).pre(deg);
                if ((int)ret.size() < deg) ret.resize(deg, mint(0));
                return ret;
            }
            if (__int128_t(i + 1) * k >= deg) return FPS(deg, mint(0));
        }
        return FPS(deg, mint(0));
    }
    FPS sqrt(int deg = -1) const {
        const int n = this->size();
        if (deg == -1) deg = n;
        if (n == 0) return FPS(deg, 0);
        if ((*this)[0] == mint(0)) {
            for (int i = 1; i < n; i++) {
                if ((*this)[i] != mint(0)) {
                    if (i & 1) return {};
                    if (deg - i / 2 <= 0) break;
                    auto ret = (*this >> i).sqrt(deg - i / 2);
                    if (ret.empty()) return {};
                    ret = ret << (i / 2);
                    if ((int)ret.size() < deg) ret.resize(deg, mint(0));
                    return ret;
                }
            }
            return FPS(deg, 0);
        }
        int64_t sqr = mod_sqrt((*this)[0].val(), mint::mod());
        if (sqr == -1) return {};
        assert(sqr * sqr % mint::mod() == (*this)[0].val());
        FPS ret({mint(sqr)});
        mint inv2 = mint(2).inv();
        for (int i = 1; i < deg; i <<= 1) {
            ret = (ret + pre(i << 1) * ret.inv(i << 1)) * inv2;
        }
        return ret.pre(deg);
    }
    mint eval(mint x) const {
        mint r = 0, w = 1;
        for (auto &v : *this) r += w * v, w *= x;
        return r;
    }
};
// calculate f(x + a)
template <typename mint>
FormalPowerSeries<mint> TaylorShift(FormalPowerSeries<mint> f, mint a, Binomial<mint> &bin) {
    int n = f.size();
    for (int i = 0; i < n; i++) f[i] *= bin.fact[i];
    f = f.rev();
    FormalPowerSeries<mint> g(n, mint(1));
    for (int i = 1; i < n; i++) g[i] = g[i - 1] * a * bin.inv[i];
    f = (f * g).pre(n);
    f = f.rev();
    for (int i = 0; i < n; i++) f[i] *= bin.factinv[i];
    return f;
}
template <typename mint>
FormalPowerSeries<mint> FPS_Product(vector<FormalPowerSeries<mint>> f) {
    int n = (int)f.size();
    if (n == 0) return {1};
    function<FormalPowerSeries<mint>(int, int)> calc = [&](int l, int r) {
        if (r - l == 1) return f[l];
        int m = (l + r) / 2;
        return calc(l, m) * calc(m, r);
    };
    return calc(0, n);
}
#include <atcoder/string>
int string_period(string s) {
    int n = s.size();
    auto z = atcoder::z_algorithm(s);
    for (int i = 1; i < n; i++) {
        if (n % i == 0 && z[i] == n - i) return i;
    }
    return n;
}
*/
struct LowLink {
    vector<vector<int>> g;
    vector<int> ord, low, out;
    vector<bool> used;
    vector<pair<int, int>> bridge;
    vector<pair<int, int>> articulation;
    int unions;
    LowLink(vector<vector<int>> g) : g(g) {
        int n = (int)g.size();
        ord.resize(n);
        low.resize(n);
        out.resize(n);
        used.resize(n);
        unions = 0;
        int t = 0;
        for (int i = 0; i < n; i++) {
            if (!used[i]) {
                dfs(i, t, -1);
                unions++;
            }
        }
    }
    void dfs(int v, int &t, int par) {
        used[v] = true;
        ord[v] = t++, low[v] = ord[v];
        int cnt = 0;
        bool par_back = false;
        for (int to : g[v]) {
            if (!used[to]) {
                dfs(to, t, v);
                low[v] = min(low[v], low[to]);
                if (ord[v] < low[to]) bridge.push_back(minmax(v, to));
                if (ord[v] <= low[to]) cnt++;
            } else if (to != par || par_back) {
                low[v] = min(low[v], ord[to]);
            } else
                par_back = true;
        }
        if (par != -1) cnt++;
        if (cnt >= 2) articulation.push_back({v, cnt});
        out[v] = t;
    }
};
void solve(int n, int m) {
    using mint = modint<1000000007>;
    vector<string> s(n);
    rep(i, n) cin >> s[i];
    // ほうじょ？
    mint ans = 1;
    rep(d, 4) {
        vector<int> u, v;
        rep(i, n) {
            if (i % 4 == d) u.push_back(i);
            if ((i + 2) % 4 == d) v.push_back(i);
        }
        int k = v.size();
        vector<mint> dp(1 << k, 0);
        dp[0] = 1;
        rep(i, m) {
            auto ref = (i % 2 == 0 ? v : u);
            auto nxt = (i % 2 == 0 ? u : v);
            vector<mint> tmp(1 << (nxt.size()), 0);
            rep(S, dp.size()) {
                if (dp[S] == 0) continue;
                vector<bool> ng(n);
                rep(p, ref.size()) {
                    if ((S >> p) & 1) {
                        if (ref[p] + 2 < n) ng[ref[p] + 2] = true;
                        if (ref[p] >= 2) ng[ref[p] - 2] = true;
                    }
                }
                int T = 0;
                int t = 0;
                for (int j : nxt) {
                    if (s[j][i] == '.' && !ng[j]) T |= 1 << t;
                    t++;
                }
                tmp[T] += dp[S];
            }

            swap(dp, tmp);
            rep(j, nxt.size()) {
                rep(S, dp.size()) {
                    if ((S >> j) & 1) {
                        dp[S & ~(1 << j)] += dp[S];
                    }
                }
            }
        }
        mint sum = 0;
        for (auto x : dp) sum += x;
        ans *= sum;
    }
    cout << ans << endl;
}
int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    int n, m, k, x, y;
    ll p;
    string s, t;
    while (cin >> n >> m) {
        if (n == 0) break;
        solve(n, m);
    }
}
accuracy: 1
time_ms: 2500
memory_kb: 3492
final_rank: 6

code_snippet: #include<bits/stdc++.h>
using namespace std;
#define rep(i,n) for (long long i=0;i<n;i++)
#define loop(i,m,n) for(long long i=m;i<=n;i++)
#define range(value,range) for(const auto &value : range)
#define ll long long
#define vl vector<long long>
#define vvl vector<vector<long long>>
#define vdbg(a) rep(ii,a.size()){cout<<a[ii]<<" ";}cout<<endl;
#define vvdbg(a) rep(ii,a.size()){rep(jj,a[ii].size()){cout<<a[ii][jj]<<" ";}cout<<endl;}
#define inf 4000000000000000000LL
#define mod 1000000007LL
bool solve(){
	ll h,w;
	cin>>h>>w;
	if(h==0)return false;

	vector<string> a(h);
	rep(i,h)cin>>a[i];

	vector<string> s(((h+3)/4)*4,string(w,'#'));
	rep(i,h)rep(j,w)s[i][j]=a[i][j];
	h=((h+3)/4)*4;

	ll ans=1;
	//4分割
	rep(z,4){
		//模様の取り出し、向き合わせ(下２つのグループは上下反転)&(偶数列は上下反転)
		vvl moyou(w,vl(h/4));
		rep(i,w){
			rep(j,h/4){
				if(s[j*4+(z+(i%2)*2)%4][i]=='.')moyou[i][j]=1;
				else moyou[i][j]=0;
			}
			//この時反転
			if((i%2==1&&z<=1)||(i%2==0&&z>=2))reverse(moyou[i].begin(),moyou[i].end());
		}

		//上下交互に＆を取ったビット列を生成後、3^N DP
		vvl dp(w+1,vl(1<<(h/4),0));
		
		dp[0][0]=1;

		//渡すDP
		rep(i,w){
			rep(j,1<<(h/4)){
				//dp[i][j]が0ならcontinue
				if(dp[i][j]==0)continue;

				//tmpはjのビットを配列として反転したもの
				ll tmp=0;
				rep(k,h/4)if((1<<k)&j)tmp+=1<<((h/4-1)-k);

				//bitは今、遷移先として、bitを1にできるものを全て1にした物。
				ll bit=0;
				if(tmp%2==0&&moyou[i][0]==1)bit++;
				loop(k,1,(h/4-1))if(((1<<k)&tmp)==0&&((1<<(k-1))&tmp)==0&&moyou[i][k]==1)bit+=1<<k;

				//bitの部分列全てに、dp[i][j]を足す。3^N DP
				ll b=bit;
				while(1){
					dp[i+1][b]+=dp[i][j];
					dp[i+1][b]%=mod;
					if(b==0)break;
					b=((b-1)&bit);
				}
			}
		}
		ll mul=0;
		rep(j,1<<(h/4))mul+=dp[w][j];
		mul%=mod;
		ans*=mul,ans%=mod;

		//vvdbg(dp);
		//cout<<endl;
	}
	cout<<ans<<endl;
	return true;
}

int main(){
	while(solve());
	return 0;
}
accuracy: 1
time_ms: 3180
memory_kb: 18908
final_rank: 14

code_snippet: #include <bits/stdc++.h>
using namespace std;

using ll = long long;
using vll = vector<ll>;
using vvll = vector<vll>;
using vvvll = vector<vvll>;
using vb = vector<bool>;
using vvb = vector<vb>;
using vvvb = vector<vvb>;
#define all(A) A.begin(),A.end()
#define rep(i, n) for (int i = 0; i < (int) (n); i++)
template<class T>
void chmax(T& p, T q) { if (p < q)p = q; };



const ll mod = 1000000007;

void Zetasuptrans(vll& A) {
    ll N = A.size();
    for (int i = 1; i < N; i *= 2) {
        for (int j = 0; j < N; j++) {
            if (!(j & i))A[j] += A[i | j];
            A[j] %= mod;
            if (A[j] < 0)A[j] += mod;
        }
    }
}

ll N, M;
void solve(ll N, ll M) {
    vector<string> S(N);
    rep(i, N)cin >> S[i];
    while (N % 4 != 0) {
        S.push_back("");
        rep(i, M)S.back().push_back('#');
        N++;
    }

    ll an = 1;
    ll QN = N / 4;
    rep(p, 4) {
        vll DP(1 << QN, 0);
        rep(bit, 1 << QN) {
            bool OK = 1;
            rep(q, QN) {
                if (bit & (1 << q)) {
                    if (S[q * 4 + p][0] == '#')OK = 0;
                }
            }
            if (OK)DP[bit] = 1;
        }
        rep(i, M - 1) {
            vll NDP(1 << QN, 0);
            bool m = (i % 2 == 0) ^ (p < 2);
            //1:右下, 0:右上
            rep(bit, 1 << QN) {
                ll is_OK = (1 << QN) - 1;
                rep(q, QN) {
                    if (bit & (1 << q)) {
                        is_OK &= ((1 << QN) - 1) - (1 << q);
                        if ((!m) && (q != 0)) {
                            is_OK &= (((1 << QN) - 1) - (1 << (q - 1)));
                        }
                        else if (m && (q != QN - 1)) {
                            is_OK &= (((1 << QN) - 1) - (1 << (q + 1)));
                        }
                    }
                }
                NDP[is_OK]=(NDP[is_OK]+DP[bit])%mod;
            }
            Zetasuptrans(NDP);

            rep(bit, 1 << QN) {
                bool OK = 1;
                rep(q, QN) {
                    if (bit & (1 << q)) {
                        if (i % 2 == 1) {
                            if (S[q * 4 + p][i + 1] == '#')OK = 0;
                        }
                        else if (p < 2) {
                            if (S[q * 4 + p + 2][i + 1] == '#')OK = 0;
                        }
                        else {
                            if (S[q * 4 + p - 2][i + 1] == '#')OK = 0;
                        }
                    }
                }
                if (OK)DP[bit] = NDP[bit] % mod;
                else DP[bit] = 0;
            }
        }

        ll res = 0;
        rep(bit, 1 << QN){
            res += DP[bit];
            res%=mod;
        }
        an *= ((res) % mod);
        an %= mod;
    }
    if (an < 0)an += mod;
    cout << an << endl;



}

int main() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);


    while (cin >> N >> M) {
        if (N == 0)return 0;
        solve(N, M);
    }

}
accuracy: 1
time_ms: 5690
memory_kb: 3648
final_rank: 16

code_snippet: #include <bits/stdc++.h>

constexpr long long MOD{1000000007};

long long mult(long long a, long long b) {
    return (a * b) % MOD;
}

long long add(long long a, long long b) {
    a += b;
    if (a >= MOD) a -= MOD;
    return a;
}

long long calc2(std::vector<std::vector<int>> A) {
    std::vector<long long> dp((1 << A[0].size()), 1);
    for (int i{1} ; i < (int)A.size() ; i++) {
        // dpをゼータ変換
        for (int j{} ; j < (int)A[i - 1].size() ; j++) {
            for (int bit{} ; bit < (int)(1 << A[i - 1].size()) ; bit++) {
                if (bit & (1 << j)) {
                    dp[bit] = add(dp[bit], dp[bit ^ (1 << j)]);
                }
            }
        }
        std::vector<long long> next((1 << A[i].size()));
        std::map<int, int> map;
        for (int j{} ; j < (int)A[i - 1].size() ; j++) {
            map[A[i - 1][j]] = j;
        }
        for (int bit{} ; bit < (int)(1 << A[i].size()) ; bit++) {
            std::vector<bool> ok(A[i - 1].size(), true);
            for (int j{} ; j < (int)A[i].size() ; j++) {
                if (!(bit & (1 << j))) continue;
                {
                    auto it{map.find(A[i][j] - 1)};
                    if (it != map.end()){
                        ok[it->second] = false;
                    }
                }
                {
                    auto it{map.find(A[i][j] + 1)};
                    if (it != map.end()){
                        ok[it->second] = false;
                    }
                }
            }
            int maximal{};
            for (int j{} ; j < (int)A[i - 1].size() ; j++) {
                if (ok[j]) maximal |= (1 << j);
            }
            next[bit] = dp[maximal];
        }
        dp = std::move(next);
    }
    long long res{};
    for (auto x : dp) res = add(res, x);
    return res;
}


long long calc(std::vector<std::string> S) {
    int M{(int)S[0].size()};
    std::vector<std::vector<int>> A(M), B(M);
    for (int i{} ; i < (int)S.size() ; i++) {
        for (int j{} ; j < (int)S[i].size() ; j++) {
            if (S[i][j] == '.') {
                if ((i + j) % 2) {
                    A[j].push_back(i);
                }
                else {
                    B[j].push_back(i);
                }
            }
        }
    }
    long long res{mult(calc2(A), calc2(B))};
    return res;
}

bool solve() {
    int N, M;
    std::cin >> N >> M;
    if (N == 0 and M == 0) {
        return false;
    }
    std::vector<std::string> S(N);
    for (auto& s : S) {
        std::cin >> s;
    }
    std::vector<std::string> odd, even;
    for (int i{} ; i < N ; i++) {
        if (i % 2) odd.push_back(S[i]);
        else even.push_back(S[i]);
    }
    long long ans{mult(calc(odd), calc(even))};
    std::cout << ans << '\n';
    return true;
}


int main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
    while (solve()) ;
}
accuracy: 1
time_ms: 2550
memory_kb: 4028
final_rank: 7

code_snippet: #line 2 "cp-library/src/cp-template.hpp"
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ld = long double;
using uint = unsigned int;
using ull  = unsigned long long;
using i32 = int;
using u32 = unsigned int;
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128_t;
template < class T > bool chmin(T& a, T b) { if(a > b) { a = b; return true; } return false; }
template < class T > bool chmax(T& a, T b) { if(a < b) { a = b; return true; } return false; }
template < class T, class U > T ceil (T x, U y) { return (x > 0 ? (x + y - 1) / y :           x / y); }
template < class T, class U > T floor(T x, U y) { return (x > 0 ?           x / y : (x - y + 1) / y); }
int popcnt(i32 x) { return __builtin_popcount(x); }
int popcnt(u32 x) { return __builtin_popcount(x); }
int popcnt(i64 x) { return __builtin_popcountll(x); }
int popcnt(u64 x) { return __builtin_popcountll(x); }

#line 2 "cp-library/src/utility/rep_itr.hpp"
template < class T > struct itr_rep {
    T i, d;
    constexpr itr_rep(const T i) noexcept : i(i), d(1) {}
    constexpr itr_rep(const T i, const T d) noexcept : i(i), d(d) {}
    void operator++() noexcept { i += d; }
    constexpr int operator*() const noexcept { return i; }
    constexpr bool operator!=(const itr_rep x) const noexcept { return d > 0 ? i < x.i : i > x.i; }
};

template < class T > struct rep {
    const itr_rep< T > s, t;
    constexpr rep(const T t) noexcept : s(0), t(t) {}
    constexpr rep(const T s, const T t) noexcept : s(s), t(t) {}
    constexpr rep(const T s, const T t, const T d) noexcept : s(s, d), t(t, d) {}
    constexpr auto begin() const noexcept { return s; }
    constexpr auto end  () const noexcept { return t; }
};

template < class T > struct revrep {
    const itr_rep < T > s, t;
    constexpr revrep(const T t) noexcept : s(t - 1, -1), t(-1, -1) {}
    constexpr revrep(const T s, const T t) noexcept : s(t - 1, -1), t(s - 1, -1) {}
    constexpr revrep(const T s, const T t, const T d) noexcept : s(t - 1, -d), t(s - 1, -d) {}
    constexpr auto begin() const noexcept { return s; }
    constexpr auto end  () const noexcept { return t; }
};
#line 3 "cp-library/src/utility/io.hpp"

/* 128bit integer */
istream& operator>>(istream& is, i128& x) {
    std::string s; is >> s;
    int pm = (s[0] == '-');
    x = 0;
    for(int i : rep(pm, int(s.size()))) x = x * 10 + (s[i] - '0');
    if(pm) x *= -1;
    return is;
}
ostream& operator<<(ostream& os, const i128& x) {
    if(x == 0) return os << '0';
    i128 y = x;
    if(y < 0) { os << '-'; y *= -1; }
    std::vector<int> ny;
    while(y > 0) { ny.push_back(y % 10); y /= 10; }
    for(int i : revrep(ny.size())) os << ny[i];
    return os;
}

template < class S, class T > istream& operator>>(istream& is,       std::pair< S, T >& x) { is >> x.first        >> x.second; return is; }
template < class S, class T > ostream& operator<<(ostream& os, const std::pair< S, T >& x) { os << x.first << " " << x.second; return os; }

namespace scanner {
    struct sca {
        template < class T > operator T() {
            T s; std::cin >> s; return s;
        }
    };
    struct vec {
        int n;
        vec(int n) : n(n) {}
        template < class T > operator std::vector< T >() {
            std::vector< T > v(n);
            for(T& x : v) std::cin >> x;
            return v;
        }
    };
    struct mat {
        int h, w;
        mat(int h, int w) : h(h), w(w) {}
        template < class T > operator std::vector< std::vector< T > >() {
            std::vector m(h, std::vector< T >(w));
            for(std::vector< T >& v : m) for(T& x : v) std::cin >> x;
            return m;
        }
    };
    struct speedup {
        speedup() {
            std::cin.tie(0);
            std::ios::sync_with_stdio(0);
        }
    } speedup_instance;
}
scanner::sca in() { return scanner::sca(); }
scanner::vec in(int n) { return scanner::vec(n); }
scanner::mat in(int h, int w) { return scanner::mat(h, w); }

namespace printer {
    void precision(int d) { std::cout << std::fixed << std::setprecision(d); }
    void flush() { std::cout.flush(); }
}

template < class T >
ostream& operator<<(ostream& os, const std::vector< T > a) {
    int n = a.size();
    for(int i : rep(n)) { os << a[i]; if(i != n - 1) os << ' '; }
    return os;
}

int print() { std::cout << '\n'; return 0; }
template < class head, class... tail > int print(head&& h, tail&&... t) {
    std::cout << h; if(sizeof...(tail)) std::cout << ' ';
    return print(std::forward<tail>(t)...);
}
template < class T > int print_n(const std::vector< T > a) {
    int n = a.size();
    for(int i : rep(n)) std::cout << a[i] << "\n";
    return 0;
}


#line 2 "cp-library/src/utility/key_val.hpp"

template < class K, class V >
struct key_val {
    K key; V val;
    key_val() {}
    key_val(K key, V val) : key(key), val(val) {}
    template < std::size_t Index >
    std::tuple_element_t< Index, key_val >& get() {
        if constexpr (Index == 0) return key;
        if constexpr (Index == 1) return val;
    }
};

namespace std {

template < class K, class V > struct tuple_size < key_val< K, V > > : integral_constant< size_t, 2 > {};
template < class K, class V > struct tuple_element < 0, key_val< K, V > > { using type = K; };
template < class K, class V > struct tuple_element < 1, key_val< K, V > > { using type = V; };

}
#line 2 "cp-library/src/utility/vec_op.hpp"
template < class T > key_val< int, T > max_of(const vector< T >& a) {
    int i = std::max_element(a.begin(), a.end()) - a.begin();
    return {i, a[i]};
}
template < class T > key_val< int, T > min_of(const vector< T >& a) {
    int i = std::min_element(a.begin(), a.end()) - a.begin();
    return {i, a[i]};
}
template < class S, class T > S sum_of(const vector< T >& a) {
    S sum = 0;
    for(const T x : a) sum += x;
    return sum;
}
template < class S, class T > vector< S > freq_of(const vector< T >& a, T L, T R) {
    vector< S > res(R - L, S(0));
    for(const T x : a) res[x - L] += 1;
    return res;
}
template < class S, class T > struct prefix_sum {
    vector< S > s;
    prefix_sum(const vector< T >& a) : s(a) {
        s.insert(s.begin(), S(0));
        for(int i : rep(a.size())) s[i + 1] += s[i];
    }
    // [L, R)
    S sum(int L, int R) { return s[R] - s[L]; }
};
#line 3 "cp-library/src/utility/heap.hpp"

template < class T > using heap_min = std::priority_queue< T, std::vector< T >, std::greater< T > >;
template < class T > using heap_max = std::priority_queue< T, std::vector< T >, std::less< T > >;

#line 27 "cp-library/src/cp-template.hpp"

#line 1 "cp-library/src/algorithm/bin_search.hpp"
template < class T, class F >
T bin_search(T ok, T ng, F f) {
    while(abs(ng - ok) > 1) {
        T mid = (ok + ng) / 2;
        (f(mid) ? ok : ng) = mid;
    }
    return ok;
}

template < class T, class F >
T bin_search_real(T ok, T ng, F f, int step = 80) {
    while(step--) {
        T mid = (ok + ng) / 2;
        (f(mid) ? ok : ng) = mid;
    }
    return ok;
}
#line 2 "cp-library/src/algorithm/argsort.hpp"

template < class T > std::vector< int > argsort(const std::vector< T > &a) {
    std::vector< int > ids((int)a.size());
    std::iota(ids.begin(), ids.end(), 0);
    std::sort(ids.begin(), ids.end(), [&](int i, int j) {
        return a[i] < a[j] || (a[i] == a[j] && i < j);
    });
    return ids;
}
#line 1 "macro.hpp"
namespace macro {

using size_type = int;
template < class container > void  sort(container& a) { std::sort(std:: begin(a), std:: end(a)); }
template < class container > void rsort(container& a) { std::sort(std::rbegin(a), std::rend(a)); }
template < class container > void reverse(container& a) { std::reverse(std::begin(a), std::end(a)); }
template < class container > void unique(container& a) {
    std::sort(std::begin(a), std::end(a));
    a.erase(std::unique(std::begin(a), std::end(a)), std::end(a));
}
template < class container > container  sorted(const container& a) { container b = a;  sort(b); return std::move(b); }
template < class container > container rsorted(const container& a) { container b = a; rsort(b); return std::move(b); }
template < class container, class compare > void sort(container& a, const compare& cmp) { std::sort(std::begin(a), std::end(a), cmp); }
template < class container, class compare > container sorted(const container& a, const compare& cmp) { container b = a; sort(b, cmp); return std::move(b); }
template < class container, class value > size_type lower_bound(const container& a, const value& x) { return std::lower_bound(std::begin(a), std::end(a), x) - std::begin(a); }
template < class container, class value > size_type upper_bound(const container& a, const value& x) { return std::upper_bound(std::begin(a), std::end(a), x) - std::begin(a); }

const std::vector<std::pair<size_type, size_type>> dir4 = { {+1,  0}, {-1,  0}, { 0, +1}, { 0, -1} };
const std::vector<std::pair<size_type, size_type>> dir8 = { {-1, -1}, {-1,  0}, {-1, +1}, { 0, -1}, { 0, +1}, {+1, -1}, {+1,  0}, {+1, +1} };

#ifdef _DEBUG
#define debug(x) std::cout << "[" << __LINE__ << "] " << #x << ": " << x << std::endl
#else
#define debug(x)
#endif

template < class container > void concat(container& a, const container& b) {
    a.insert(std::end(a), std::begin(b), std::end(b));
}
std::vector<size_type> iota(const size_type n) {
    std::vector<size_type> I(n);
    std::iota(std::begin(I), std::end(I), 0);
    return I;
}
template < class container > std::vector<size_type> sort_idx(const container& a) {
    const size_type n = a.size();
    std::vector<size_type> I = iota(n);
    std::sort(std::begin(I), std::end(I), [&](size_type i, size_type j) { return a[i] < a[j] or (a[i] == a[j] and i < j); });
    return I;
}
template < class container, class compare > std::vector<size_type> sort_idx(const container& a, const compare& cmp) {
    const size_type n = a.size();
    std::vector<size_type> I = iota(n);
    std::sort(std::begin(I), std::end(I), [&](size_type i, size_type j) { return cmp(a[i], a[j]) or (a[i] == a[j] and i < j); });
    return std::move(I);
}

struct grid {
    using size_type = int;
    size_type H, W;
    grid(const size_type H, const size_type W) : H(H), W(W) {}
    bool contains(const size_type i, const size_type j) {
        return 0 <= i and i < H and 0 <= j and j < W;
    }
};

using f64 = long double;

} // namespace macro

using namespace macro;
#line 3 "A.cpp"
const i64 MOD = 1e9+7;

i64 solve2(vector<string> S) {
    const int H = S.size();
    if(H == 0) return 1;
    const int W = S[0].size();

    vector ID(H, vector(W, int(-1)));
    vector<vector<int>> T(W);
    for(int j : rep(W)) for(int i : rep(H)) {
        if(S[i][j] == '.') {
            ID[i][j] = T[j].size();
            T[j].push_back(i);
        }
    }

    vector dp(1 << T[0].size(), i64(1));
    for(int j : rep(W - 1)) {
        const int nj = j + 1;
        vector nt(1 << T[nj].size(), i64(0));
        for(int set : rep(1 << T[j].size())) {
            int nt_set = (1 << T[nj].size()) - 1;
            for(int k : rep(T[j].size())) if(set & (1 << k)) {
                const int i = T[j][k];
                for(const int ni : {i - 1, i + 1}) {
                    if(0 <= ni and ni < H and S[ni][nj] == '.') {
                        if(nt_set & (1 << ID[ni][nj])) nt_set ^= (1 << ID[ni][nj]);
                    }
                }
            }
            nt[nt_set] += dp[set];
            nt[nt_set] %= MOD;
        }

        for(int k : rep(T[nj].size())) {
            for(int set : rep(1 << T[nj].size())) {
                if(not(set & (1 << k))) {
                    nt[set] += nt[set | (1 << k)];
                    nt[set] %= MOD;
                }
            }
        }

        dp = move(nt);
    }

    i64 ans = 0;
    for(int set : rep(1 << T[W - 1].size())) {
        ans = (ans + dp[set]) % MOD;
    }
    return ans;
}

i64 solve1(vector<string> S) {
    const int H = S.size();
    if(H == 0) return 1;
    const int W = S[0].size();

    vector<string> T, U;
    for(int i : rep(H)) {
        string t = "", u = "";
        for(int j : rep(W)) {
            if((i + j) % 2 == 0) t += S[i][j], u += '#';
            if((i + j) % 2 == 1) u += S[i][j], t += '#';
        }

        T.push_back(t);
        U.push_back(u);
    }
    return solve2(T) * solve2(U) % MOD;
}

i64 solve(int H, int W) {
    vector<string> S = in(H);
    vector<string> T, U;
    for(int i : rep(H)) {
        if(i % 2 == 0) T.push_back(S[i]);
        if(i % 2 == 1) U.push_back(S[i]);
    }
    return solve1(T) * solve1(U) % MOD;
}

int main() {
    while(true) {
        int H = in(), W = in();
        if(make_pair(H, W) == make_pair(0, 0)) return 0;
        print(solve(H, W));
    }
}
accuracy: 1
time_ms: 1700
memory_kb: 3992
final_rank: 2

code_snippet: #include <cassert>
#include <iostream>
#include <vector>

using namespace std;
using i64 = long long;

#define rep(i, n) for (int i = 0; i < n; i++)

const i64 mod = 1'000'000'007;

void zeta(vector<i64>& a) {
    int n = a.size();
    int c = 0;
    int tmp = n;
    while (tmp) {
        c++;
        tmp >>= 1;
    }
    for (int j = c - 1; j >= 0; j--) {
        rep(i, n) {
            if (i >> j & 1) {
                a[i] = (a[i ^ (1 << j)] + a[i]) % mod;
            }
        }
    }
}
char a[60][60];
char s[60][60];

void solve(int h, int w) {
    rep(i, h) rep(j, w) cin >> a[i][j];
    rep(i, h) rep(j, w) s[j][i] = a[i][j];
    swap(h, w);
    int b = (w + 3) / 4;
    i64 ans = 1;
    rep(k, 4) {
        vector dp(h, vector<i64>(1 << b));
        rep(i, 1 << b) {
            int f = 1;
            rep(j, b) {
                if (i >> j & 1) {
                    if (j * 4 + k >= w || s[0][j * 4 + k] == '#') f = 0;
                }
            }
            dp[0][i] = f;
        }
        zeta(dp[0]);
        int now = k, nx = (k + 2) % 4;
        for (int i = 1; i < h; i++) {
            rep(nxt, 1 << b) {
                int g = 1;
                rep(j, b) {
                    if (nxt >> j & 1) {
                        if (j * 4 + nx >= w || s[i][j * 4 + nx] == '#') g = 0;
                    }
                }
                if (!g) {
                    continue;
                }
                int bit = 0;
                vector<int> fl(b, 1);
                if (now >= 2) {
                    rep(j, b) {
                        auto xj = j * 4 + nx;
                        if (xj >= w || s[i][xj] == '#') continue;
                        if (xj + 2 < w) {
                            if (nxt >> j & 1) fl[j] = 0;
                        }
                        if (xj - 2 >= 0) {
                            assert(j >= 1);
                            if (nxt >> j & 1) fl[j - 1] = 0;
                        }
                    }
                } else {
                    rep(j, b) {
                        auto xj = j * 4 + nx;
                        if (xj >= w || s[i][xj] == '#') continue;
                        if (xj - 2 >= 0) {
                            if (nxt >> j & 1) fl[j] = 0;
                        }
                        if (xj + 2 < w) {
                            assert(j + 1 < b);
                            if (nxt >> j & 1) fl[j + 1] = 0;
                        }
                    }
                }
                rep(j, b) bit += (1 << j) * fl[j];
                // cout << "nxt: " << nxt << endl;
                // cout << dp[i - 1][bit] << "\n";
                dp[i][nxt] = (dp[i - 1][bit] + dp[i][nxt]) % mod;
            }
            zeta(dp[i]);
            swap(now, nx);
        }
        // cout << dp[h - 1][(1 << b) - 1] << endl;
        ans = (ans * dp[h - 1][(1 << b) - 1]) % mod;
    }
    cout << ans << endl;
}

int main() {
    while (true) {
        int n, m;
        cin >> n >> m;
        if (n == 0) return 0;
        solve(n, m);
    }
}
accuracy: 1
time_ms: 4670
memory_kb: 18760
final_rank: 17