#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, a, b) for (int i = (a); i < (b); i++)
#ifdef tabr
#include "library/debug.cpp"
#else
#define debug(...) 0
#endif

template <ll mod>
struct modular {
    ll value;
    modular(ll x = 0) {
        value = x % mod;
        if (value < 0) value += mod;
    }

    modular& operator+=(modular other) {
        if ((value += other.value) >= mod) value -= mod;
        return *this;
    }
    modular& operator-=(modular other) {
        if ((value -= other.value) < 0) value += mod;
        return *this;
    }
    modular& operator*=(modular other) {
        value = value * other.value % mod;
        return *this;
    }
    modular& operator/=(modular other) {
        ll a = 0, b = 1, c = other.value, m = mod;
        while (c != 0) {
            ll t = m / c;
            m -= t * c;
            swap(c, m);
            a -= t * b;
            swap(a, b);
        }
        a %= mod;
        if (a < 0) a += mod;
        value = value * a % mod;
        return *this;
    }
    modular& operator^=(modular other) {
        ll r = 1, x = value, n = other.value;
        while (n > 0) {
            if (n & 1) r = r * x % mod;
            x = x * x % mod;
            n >>= 1;
        }
        value = r;
        return *this;
    }
    modular operator+(modular rhs) {
        return modular(*this) += rhs;
    }
    modular operator-(modular rhs) {
        return modular(*this) -= rhs;
    }
    modular operator*(modular rhs) {
        return modular(*this) *= rhs;
    }
    modular operator/(modular rhs) {
        return modular(*this) /= rhs;
    }
    modular operator^(modular rhs) {
        return modular(*this) ^= rhs;
    }
    bool operator==(modular rhs) {
        return value == rhs.value;
    };
    bool operator!=(modular rhs) {
        return value != rhs.value;
    };
    bool operator<(modular rhs) {
        return value < rhs.value;
    }
    bool operator>(modular rhs) {
        return value > rhs.value;
    }
};
template <ll mod>
string to_string(modular<mod> x) {
    return to_string(x.value);
}
template <ll mod>
ostream& operator<<(ostream& stream, modular<mod> x) {
    x.value %= mod;
    if (x.value < 0) x.value += mod;
    return stream << x.value;
}
template <ll mod>
istream& operator>>(istream& stream, modular<mod>& x) {
    stream >> x.value;
    x.value %= mod;
    if (x.value < 0) x.value += mod;
    return stream;
}
const ll mod = 998244353;
using mint = modular<mod>;

mint abs(mint a) {
    return a.value;
}

template <class T>
struct matrix {
    vector<vector<T>> a;
    matrix(int height, int width)
        : a(height, vector<T>(width)) {}
    matrix(int n)
        : a(n, vector<T>(n)) {}
    int size() {
        return a.size();
    }
    inline vector<T>& operator[](int i) {
        return a[i];
    }
};
template <class T>
matrix<T> operator*(matrix<T> a, matrix<T> b) {
    matrix<T> r(a.size(), b[0].size());
    for (int i = 0; i < a.size(); i++)
        for (int j = 0; j < b[0].size(); j++)
            for (int k = 0; k < b.size(); k++) r[i][j] += a[i][k] * b[k][j];
    return r;
}
template <class T>
matrix<T> operator+(matrix<T> a, matrix<T> b) {
    matrix<T> r(a.size(), a[0].size());
    for (int i = 0; i < a.size(); i++)
        for (int j = 0; j < a[0].size(); j++) r[i][j] = a[i][j] + b[i][j];
    return r;
}
template <class T>
matrix<T> operator-(matrix<T> a, matrix<T> b) {
    matrix<T> r(a.size(), a[0].size());
    for (int i = 0; i < a.size(); i++)
        for (int j = 0; j < a[0].size(); j++) r[i][j] = a[i][j] - a[i][j];
    return r;
}
template <class T>
matrix<T> operator^(matrix<T> a, ll n) {
    matrix<T> r(a.size());
    for (int i = 0; i < a.size(); i++) r[i][i] = 1;
    while (n > 0) {
        if (n & 1) r = r * a;
        a = a * a;
        n >>= 1;
    }
    return r;
}
template <class T>
bool operator==(matrix<T> a, matrix<T> b) {
    for (int i = 0; i < a.size(); i++)
        for (int j = 0; j < a[0].size(); j++)
            if (a[i][j] != b[i][j]) return false;
    return true;
}
template <class T>
bool operator!=(matrix<T> a, matrix<T> b) {
    return !(a == b);
}
template <class T>
void print(matrix<T> a) {
    for (int i = 0; i < a.size(); i++)
        for (int j = 0; j < a[0].size(); j++)
            cout << a[i][j] << ((j != a[0].size() - 1) ? ' ' : '\n');
}
template <class T>
string to_string(matrix<T> a) {
    return to_string(a.a);
}
template <class T>
int gaussian_elimination(matrix<T>& a, bool is_extended = false) {
    int m = a.size(), n = a[0].size();
    int rank = 0;
    T det = 1;
    for (int col = 0; col < n; ++col) {
        if (is_extended && col == n - 1) break;
        int pivot = -1;
        T mx = 1e-5;
        for (int row = rank; row < m; ++row) {
            if (abs(a[row][col]) > mx) {
                mx = abs(a[row][col]);
                pivot = row;
            }
        }
        if (pivot == -1) continue;
        swap(a[pivot], a[rank]);
        auto fac = a[rank][col];
        det *= fac;
        for (int col2 = 0; col2 < n; col2++) a[rank][col2] /= fac;
        for (int row = 0; row < m; row++) {
            if (row != rank && abs(a[row][col]) > 1e-9) {
                auto fac = a[row][col];
                for (int col2 = 0; col2 < n; col2++) {
                    a[row][col2] -= a[rank][col2] * fac;
                    a[row][col2] %= 2;
                    if (a[row][col2] < 0) a[row][col2] += 2;
                }
            }
        }
        rank++;
    }
    return rank;
    // return (m == rank ? det : 0);
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n, m;
    cin >> n >> m;
    matrix<int> a(n, m);
    rep(i, 0, n) rep(j, 0, m) cin >> a[i][j];
    int r = gaussian_elimination(a);
    debug(r);
    mint ans = (mint)2 ^ (mint)(n + m - 1);
    ans -= (mint)2 ^ (mint)(n + m - r - 1);
    cout << ans << endl;
    return 0;
}

Measured memory_bytes: 640