#include <bits/stdc++.h>
using namespace std;
template<class T> inline bool chmax(T& a, T b) { if (a < b) { a = b; return 1; } return 0; }
template<class T> inline bool chmin(T& a, T b) { if (a > b) { a = b; return 1; } return 0; }


#define COUT(x) cout << #x << " = " << (x) << " (L" << __LINE__ << ")" << endl
template<class T1, class T2> ostream& operator << (ostream &s, pair<T1,T2> P)
{ return s << '<' << P.first << ", " << P.second << '>'; }
template<class T> ostream& operator << (ostream &s, vector<T> P)
{ for (int i = 0; i < P.size(); ++i) { if (i > 0) { s << " "; } s << P[i]; } return s; }
template<class T> ostream& operator << (ostream &s, vector<vector<T> > P)
{ for (int i = 0; i < P.size(); ++i) { s << endl << P[i]; } return s << endl; }
template<class T> ostream& operator << (ostream &s, set<T> P)
{ for(auto it : P) { s << "<" << it << "> "; } return s << endl; }
template<class T1, class T2> ostream& operator << (ostream &s, map<T1,T2> P)
{ for(auto it : P) { s << "<" << it.first << "->" << it.second << "> "; } return s << endl; }



template<int MOD> struct Fp {
    long long val;
    constexpr Fp(long long v = 0) noexcept : val(v % MOD) {
        if (val < 0) val += MOD;
    }
    constexpr int getmod() { return MOD; }
    constexpr Fp operator - () const noexcept {
        return val ? MOD - val : 0;
    }
    constexpr Fp operator + (const Fp& r) const noexcept { return Fp(*this) += r; }
    constexpr Fp operator - (const Fp& r) const noexcept { return Fp(*this) -= r; }
    constexpr Fp operator * (const Fp& r) const noexcept { return Fp(*this) *= r; }
    constexpr Fp operator / (const Fp& r) const noexcept { return Fp(*this) /= r; }
    constexpr Fp& operator += (const Fp& r) noexcept {
        val += r.val;
        if (val >= MOD) val -= MOD;
        return *this;
    }
    constexpr Fp& operator -= (const Fp& r) noexcept {
        val -= r.val;
        if (val < 0) val += MOD;
        return *this;
    }
    constexpr Fp& operator *= (const Fp& r) noexcept {
        val = val * r.val % MOD;
        return *this;
    }
    constexpr Fp& operator /= (const Fp& 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 Fp& r) const noexcept {
        return this->val == r.val;
    }
    constexpr bool operator != (const Fp& r) const noexcept {
        return this->val != r.val;
    }
    friend constexpr ostream& operator << (ostream &os, const Fp<MOD>& x) noexcept {
        return os << x.val;
    }
    friend constexpr Fp<MOD> modpow(const Fp<MOD> &a, long long n) noexcept {
        if (n == 0) return 1;
        auto t = modpow(a, n / 2);
        t = t * t;
        if (n & 1) t = t * a;
        return t;
    }
};

// 二項係数ライブラリ
template<class T> struct BiCoef {
    vector<T> fact_, inv_, finv_;
    constexpr BiCoef() {}
    constexpr BiCoef(int n) noexcept : fact_(n, 1), inv_(n, 1), finv_(n, 1) {
        init(n);
    }
    constexpr void init(int n) noexcept {
        fact_.assign(n, 1), inv_.assign(n, 1), finv_.assign(n, 1);
        int MOD = fact_[0].getmod();
        for(int i = 2; i < n; i++){
            fact_[i] = fact_[i-1] * i;
            inv_[i] = -inv_[MOD%i] * (MOD/i);
            finv_[i] = finv_[i-1] * inv_[i];
        }
    }
    constexpr T com(int n, int k) const noexcept {
        if (n < k || n < 0 || k < 0) return 0;
        return fact_[n] * finv_[k] * finv_[n-k];
    }
    constexpr T fact(int n) const noexcept {
        if (n < 0) return 0;
        return fact_[n];
    }
    constexpr T inv(int n) const noexcept {
        if (n < 0) return 0;
        return inv_[n];
    }
    constexpr T finv(int n) const noexcept {
        if (n < 0) return 0;
        return finv_[n];
    }
};


set<vector<string>> allfi, allnotin, allbein;
void rec(vector<string> fi, set<int> &yoko, set<int> &tate, bool bein, int H, int W) {
    if (yoko.size() == H && tate.size() == W) {
        allfi.insert(fi);
        if (bein) allbein.insert(fi);
        else allnotin.insert(fi);
    }
    auto ifi = fi;
    for (int h = 0; h < H; ++h) {
        if (yoko.count(h)) continue;
        yoko.insert(h);
        for (int w = 0; w < W+1; ++w) fi[h][w] = '.';
        rec(fi, yoko, tate, bein, H, W);
        for (int w = 0; w < W+1; ++w) fi[h][w] = '#';
        rec(fi, yoko, tate, (bein || (tate.empty() && true)), H, W);
        fi = ifi;
        yoko.erase(h);
    }
    for (int w = 0; w < W; ++w) {
        if (tate.count(w)) continue;
        tate.insert(w);
        for (int h = 0; h < H+1; ++h) fi[h][w] = '.';
        rec(fi, yoko, tate, bein, H, W);
        for (int h = 0; h < H+1; ++h) fi[h][w] = '#';
        rec(fi, yoko, tate, (bein || (yoko.empty() && true)), H, W);
        fi = ifi;
        tate.erase(w);
    }
}

set<vector<string>> allsub;
void rec2(vector<string> fi, set<int> &yoko, set<int> &tate, int H, int W) {
    if (yoko.size() == H && tate.size() == W) {
        allsub.insert(fi);
    }
    auto ifi = fi;
    for (int h = 0; h < H; ++h) {
        if (yoko.count(h)) continue;
        yoko.insert(h);
        for (int w = 0; w < W+1; ++w) fi[h][w] = '.';
        rec2(fi, yoko, tate, H, W);
        fi = ifi;
        yoko.erase(h);
    }
    for (int w = 0; w < W; ++w) {
        if (tate.count(w)) continue;
        tate.insert(w);
        for (int h = 0; h < H+1; ++h) fi[h][w] = '#';
        rec2(fi, yoko, tate, H, W);
        fi = ifi;
        tate.erase(w);
    }
}

long long calc(int H, int W) {
    allfi.clear();
    allnotin.clear(), allbein.clear();
    set<int> yoko, tate;
    vector<string> fi(H+1, string(W+1, '.'));
    auto fi2 = fi;
    rec(fi, yoko, tate, false, H, W);
    return allfi.size();
}

long long calc2(int H, int W) {
    set<int> yoko, tate;
    vector<string> fi(H+1, string(W+1, '.'));
    allsub.clear();
    rec2(fi, yoko, tate, H, W);
    return allsub.size();
}

const int MOD = 998244353;
using mint = Fp<MOD>;
BiCoef<mint> bc;

int H, W;
vector<string> A;

int main() {
    bc.init(110);

    // 長方形
    vector<vector<mint>> all(15, vector<mint>(15, 0));
    auto bein = all;
    auto notin = all;
    for (int i = 0; i < 15; ++i) all[0][i] = all[i][0] = modpow(mint(2), i);
    for (int a = 1; a < 15; ++a) {
        for (int b = 1; b < 15; ++b) {
            notin[a][b] = 1;
            for (int i = 1; i < a; ++i) {
                for (int j = 1; j < b; ++j) {
                    notin[a][b] += notin[a-i][b-j] * bc.com(a, i) * bc.com(b, j);
                }
            }
        }
    }
    for (int a = 1; a < 15; ++a) {
        for (int b = 1; b < 15; ++b) {
            bein[a][b] = 1;
            for (int i = 0; i < a; ++i) {
                for (int j = 0; j < b; ++j) {
                    if (i + j == 0) continue;
                    bein[a][b] += notin[a-i][b-j] * bc.com(a, i) * bc.com(b, j);
                }
            }
            all[a][b] = notin[a][b] + bein[a][b];
        }
    }

    // はみ出しあり
    vector<vector<mint>> subsub(15, vector<mint>(15, 0));
    auto sub = subsub, part = subsub;
    for (int i = 0; i < 15; ++i) {
        subsub[1][i] = 1;
        subsub[i][1] = modpow(mint(2), i) - 1;
    }
    for (int s = 2; s < 30; ++s) {
        for (int a = 1; a < 15; ++a) {
            int b = s - a;
            if (b < 1 || b >= 15) continue;
            subsub[a][b] = 1;
            for (int i = 1; i < a; ++i) {
                subsub[a][b] += bc.com(a, i) * subsub[b][a-i];
            }
        }
    }
    for (int a = 0; a < 15; ++a) {
        for (int b = 0; b < 15; ++b) {
            if (a == 0) sub[a][b] = 1;
            else if (b == 0) sub[a][b] = 1;
            else sub[a][b] = subsub[a][b] + subsub[b][a];
        }
    }

    for (int i = 0; i < 15; ++i) part[0][i] = part[i][0] = modpow(mint(2), i);
    for (int a = 1; a < 14; ++a) {
        for (int b = 1; b < 14; ++b) {
            for (int i = 0; i <= a; ++i) {
                for (int j = 0; j <= b; ++j) {
                    part[a][b] += bc.com(a, i) * bc.com(b, j) *
                        sub[i][j] * sub[a-i][b-j];
                }
            }
        }
    }

    /*
    for (int a = 0; a <= 5; ++a) {
        for (int b = 0; b <= 5; ++b) {
            calc(a, b);
            cout << a << "," << b << ": " << part[a][b] << "( " << sub[a][b] << " ), " << allfi.size() << ", " << calc2(a, b) << endl;
        }
    }
    
    
    calc(1, 2);
    COUT(allfi.size());
    for (auto fi : allfi) {
        for (auto s : fi) cout << s << endl;
        cout << endl;
    }
    */
    

    cin >> H >> W;
    A.resize(H);
    for (int h = 0; h < H; ++h) cin >> A[h];

    mint res = all[H][W];
    for (int bit = 0; bit < (1<<H)-1; ++bit) {
        vector<int> tate;
        for (int i = 0; i < H; ++i) if (!(bit & (1<<i))) tate.push_back(i);
        for (int bit2 = 0; bit2 < (1<<W)-1; ++bit2) {
            vector<int> yoko;
            for (int i = 0; i < W; ++i) if (!(bit2 & (1<<i))) yoko.push_back(i);

            bool ok = true;
            for (auto i : tate) {
                set<char> se;
                for (auto j : yoko) se.insert(A[i][j]);
                if (se.size() != 2) ok = false;
            }
            for (auto j : yoko) {
                set<char> se;
                for (auto i : tate) se.insert(A[i][j]);
                if (se.size() != 2) ok = false;
            }

            if (ok) {
                int c = __builtin_popcount(bit);
                int r = __builtin_popcount(bit2);
                mint add = part[c][r];
                res += add;

                //cout << bit << ", " << bit2 << ": " << tate << ", " << yoko << ";; " << c << ", " << r << ": " << add << endl;
            }
        }
    }
    cout << res << endl;
}


Measured memory_bytes: 256