#include <bits/stdc++.h>
#define int long long int
#define mod(x) ((x % MOD) + MOD) % MOD
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define FORE(i,a,b) for(int i=(a);i<=(b);++i)
#define RFOR(i,a,b) for(int i=(b)-1;i>=(a);--i)
#define RFORE(i,a,b) for(int i=(b);i>=(a);--i)
#define REP(i,n) FOR(i,0,n)
#define ALL(c) (c).begin(),(c).end()
#define RALL(c) (c).rbegin(),(c).rend()
#define SORT(c) sort(ALL(c))
#define RSORT(c) sort(RALL(c))
#define SZ(c) (int)((c).size())
#define EACH(i,c) for(typeof((c).begin()) i=(c).begin(); i!=(c).end(); ++i)
#define LB(c,x) (int)(lower_bound(ALL(c),x)-(c).begin())
#define UB(c,x) (int)(upper_bound(ALL(c),x)-(c).begin())
#define COUNT(c,x) (UB(c,x)-LB(c,x))
#define UNIQUE(c) SORT(c); (c).erase(unique(ALL(c)),(c).end());
#define COPY(c1,c2) copy(ALL(c1),(c2).begin())
#define EXIST(s,e) (bool)((s).find(e)!=(s).end())
#define PB push_back
#define MP make_pair
#define DEL(v) decltype(v)().swap(v)
#define DUMP(x)  cerr << #x << " = " << (x) << endl;
using namespace std;
template<typename T1, typename T2> using P = pair<T1,T2>;
template<typename T> using V = vector<T>;
struct edge { int from, to; int cost; };
bool operator< (const edge &edge1, const edge &edge2) { return edge1.cost < edge2.cost; };
const int INF = 1e18;
const int MOD = 998244353;

template<typename T> ostream& operator << (ostream& s, const V<T>& v) {
   s << "[";
   for (int i = 0; i < v.size(); i++) { s << v[i]; if (i < v.size() - 1) s << " "; }
   s << "]";
   return s;
}
template<typename T1, typename T2> ostream& operator << (ostream& s, const P<T1,T2>& p) {
   s << "(" << p.first << "," << p.second << ")";
   return s;
}

struct GF2 {
   int n;
   GF2(int m=0) { n = m; }
   const bool operator==(int m) const { return n == (m%2); }
   const bool operator!=(int m) const { return n != (m%2); }
   const bool operator>(const GF2 a) const { return n > a.n; }
   const GF2 operator+(const GF2& a) const { return GF2{n ^ a.n}; }
   const GF2 operator-(const GF2& a) const { return GF2{n ^ a.n}; }
   const GF2 operator*(const GF2& a) const { return GF2{n * a.n}; }
   const GF2 operator/(const GF2& a) const { if (a.n == 0) throw "Can't devide by 0"; return GF2{n / a.n}; }
   GF2& operator+=(const GF2& a) { n ^= a.n; return *this; }
   GF2& operator-=(const GF2& a) { n ^= a.n; return *this; }
   GF2& operator*=(const GF2& a) { n *= a.n; return *this; }
   GF2& operator/=(const GF2& a) { if (a.n == 0) throw "Can't devide by 0"; n /= a.n; return *this; }
   GF2& operator=(int m) { n = m; return *this; }
};
const GF2 abs(GF2 x) { return x; }
ostream& operator << (ostream& s, const GF2& a) { s << a.n; return s; }

template<typename T> class Matrix
{
   private:
      vector<vector<T>> val;
      int m, n;
   public:
      Matrix(int a=0, int b=0) : m(a), n(b) {
         val.resize(m, vector<T>(n));
      }
      Matrix(const vector<vector<T>>& vec) : m(vec.size()), n(vec[0].size()) {
         val.resize(m, vector<T>(n));
         for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) val[i][j] = vec[i][j];
      }
      ~Matrix() {
         vector<vector<T>>().swap(val);
      }
      Matrix& operator=(const Matrix& mat) {
         try { if (m != mat.m || n != mat.n) throw "Matrix can't be substituted"; }
         catch (const char* e) { cerr << e << endl; }
         for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) val[i][j] = mat.val[i][j];
         return *this;
      }
      T operator()(int i, int j) {
         try { if (i < 0 || m <= i || j < 0 || n <= j) throw "Bad access"; }
         catch (const char* e) { cerr << e << endl; }
         return val[i][j];
      }
      const Matrix operator+(const Matrix& mat) const {
         try { if (m != mat.m || n != mat.n) throw "Matrix can't be added"; }
         catch (const char* e) { cerr << e << endl; }
         Matrix tmp(m, n);
         for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) tmp.val[i][j] = val[i][j] + mat.val[i][j];
         return tmp;
      }
      const Matrix operator-(const Matrix& mat) const {
         try { if (m != mat.m || n != mat.n) throw "Matrix can't be added"; }
         catch (const char* e) { cerr << e << endl; }
         Matrix tmp(m, n);
         for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) tmp.val[i][j] = val[i][j] - mat.val[i][j];
         return tmp;
      }
      const Matrix operator*(const Matrix& mat) const {
         try { if (n != mat.m) throw "Matrix can't be producted"; }
         catch (const char* e) { cerr << e << endl; }
         Matrix tmp(m, mat.n);
         for (int i = 0; i < m; i++) for (int j = 0; j < mat.n; j++) for (int k = 0; k < n; k++) tmp.val[i][j] += val[i][k]*mat.val[k][j];
         return tmp;
      }
      Matrix& operator+=(const Matrix& mat) {
         try { if (m != mat.m || n != mat.n) throw "Matrix can't be added"; }
         catch (const char* e) { cerr << e << endl; }
         for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) val[i][j] += mat.val[i][j];
         return *this;
      }
      Matrix& operator-=(const Matrix& mat) {
         try { if (m != mat.m || n != mat.n) throw "Matrix can't be added"; }
         catch (const char* e) { cerr << e << endl; }
         for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) val[i][j] -= mat.val[i][j];
         return *this;
      }
      Matrix operator*=(const Matrix& mat) {
         try { if (n != mat.m) throw "Matrix can't be producted"; }
         catch (const char* e) { cerr << e << endl; }
         Matrix tmp(m, mat.n);
         for (int i = 0; i < m; i++) for (int j = 0; j < mat.n; j++) for (int k = 0; k < n; k++) tmp.val[i][j] += val[i][k]*mat.val[k][j];
         n = mat.n;
         val.resize(m, vector<T>(n));
         *this = tmp;
         return *this;
      }
      Matrix transpose() const {
         Matrix tmp(n, m);
         for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) tmp.val[j][i] = val[i][j];
         return tmp;
      }
      Matrix gaussElimination() const {
         Matrix<T> A(val);
         int col = 0;
         for (int i = 0; i < m; i++) {
            int pivot = i;
            while (true) {
               for (int j = i+1; j < m; j++) if (abs(A.val[j][col]) > abs(A.val[pivot][col])) pivot = j;
               if (A.val[pivot][col] == 0) {
                  col++;
                  if (col == n) return A;
               } else break;
            }
            swap(A.val[i], A.val[pivot]);
            for (int j = i+1; j < m; j++) {
               T ratio = A.val[j][col] / A.val[i][col];
               for (int k = col; k < n; k++) A.val[j][k] -= A.val[i][k] * ratio;
            }
            col++;
         }
         return A;
      }
      int rank() const {
         Matrix A = gaussElimination();
         int j = 0;
         for (int i = 0; i < m; i++) {
            while (A.val[i][j] == 0) {
               j++; if (j == n) return i;
            }
         }
         return m;
      }
      ostream& info(ostream& s) const {
         s << endl << "[";
         for (int i = 0; i < m; i++) {
            s << "[";
            for (int j = 0; j < n; j++) {
               s << val[i][j];
               if (j < n-1) s << " ";
            }
            s << "]"; if (i < m-1) s << endl;
         }
         s << "]";
         return s;
      }
};
template<typename T> ostream& operator<<(ostream& s, const Matrix<T>& mat) {
   mat.info(s);
   return s;
}

template<typename T> class SquareMatrix : public Matrix<T>
{
   private:
      int num;
   public:
      SquareMatrix(int a=0) : Matrix<T>(a, a) { num = a; }
      SquareMatrix(const vector<vector<T>>& vec) : num(vec.size()), Matrix<T>(vec) { 
         try { if (vec.size() != vec[0].size()) throw "Matrix isn't square matrix"; }
         catch (const char* e) { cerr << e << endl; }
      }
      T determinant() {
         Matrix<T> A = this->gaussElimination();
         T det = 1;
         for (int i = 0; i < num; i++) det *= A(i,i);
         return det;
      }
};


int powM(int x, int n) {
   int res = 1;
   while (n > 0) {
      if ((n & 1) == 1) {
         res = mod(res * x);
      }
      x = mod(x * x);
      n >>= 1;
   }
   return res;
}

signed main()
{
   int n, m; cin >> n >> m;
   V<V<GF2>> a(n, V<GF2>(m));
   REP(i, n) {
      REP(j, m) {
         int b; cin >> b;
         a[i][j] = b;
      }
   }

   Matrix<GF2> A(a);

   int rank = A.rank();

   int ans = mod(powM(2,n+m-1) - powM(2,n+m-rank-1));
   cout << ans << endl;

   return 0;
}


Measured memory_bytes: 2432