#include <bits/stdc++.h>
using namespace std;
void err(istream_iterator<string> it) {}
template <typename T, typename... Args>
void err(istream_iterator<string> it, T a, Args... args) {
  cerr << *it << " = " << a << '\n';
  err(++it, args...);
}
template <class T1, class T2>
ostream &operator<<(ostream &os, const pair<T1, T2> &a) {
  return os << '(' << a.first << ", " << a.second << ')';
}
template <class T>
ostream &operator<<(ostream &os, const vector<T> &a) {
  os << '[';
  for (unsigned int i = 0; i < a.size(); i++)
    os << a[i] << (i < a.size() - 1 ? ", " : "");
  os << ']';
  return os;
}
template <class T>
ostream &operator<<(ostream &os, const set<T> &a) {
  os << '{';
  for (typename set<T>::iterator it = a.begin(); it != a.end(); it++) {
    typename set<T>::iterator jt = it;
    os << *it << (++jt != a.end() ? ", " : "");
  }
  os << '}';
  return os;
}
template <class T1, class T2>
ostream &operator<<(ostream &os, map<T1, T2> &a) {
  os << "{\n";
  for (typename map<T1, T2>::iterator it = a.begin(); it != a.end(); it++) {
    typename map<T1, T2>::iterator jt = it;
    os << "  " << it->first << ": " << it->second
       << (++jt != a.end() ? ",\n" : "\n");
  }
  os << '}';
  return os;
}
namespace eulerPath {
vector<int> deg;
struct dsu {
  vector<int> par;
  void init(int n) {
    par.assign(n + 5, 0);
    for (int i = 0; i < n + 5; i++) par[i] = i;
  }
  int find(int x) {
    if (par[x] == x) return x;
    return par[x] = find(par[x]);
  }
  void uni(int x, int y) {
    x = find(x);
    y = find(y);
    if (x != y) par[x] = y;
  }
} d;
bool solve(int peak, vector<pair<int, int> > edge) {
  d.init(peak);
  deg.assign(peak + 5, 0);
  for (auto it : edge) {
    deg[it.first]++;
    deg[it.second]++;
    d.uni(it.first, it.second);
  }
  for (int i = 1; i <= peak; i++)
    if (d.find(i) != d.find(1)) return false;
  int cnt = 0;
  for (int i = 1; i <= peak; i++)
    if (deg[i] % 2 == 1) cnt++;
  return (cnt == 2 || cnt == 0);
}
};  // namespace eulerPath
int n, m;
int a[1005][1005];
int visited[1005][1005];
bool pass[1005][1005];
pair<int, int> trueRoot;
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, 1, 0, -1};
int mx = 0;
bool checkPath(pair<int, int> u, pair<int, int> v) {
  for (int i = min(u.first, v.first); i <= max(u.first, v.first); i++)
    for (int j = min(u.second, v.second); j <= max(u.second, v.second); j++)
      if (a[i][j] == 0) return false;
  for (int i = min(u.first, v.first); i <= max(u.first, v.first); i++)
    for (int j = min(u.second, v.second); j <= max(u.second, v.second); j++)
      pass[i][j] = true;
  return true;
}
bool between(int u, int x, int v) {
  if (u <= x && x <= v) return true;
  return false;
}
bool check(int len) {
  memset(pass, 0, sizeof(pass));
  memset(visited, 0, sizeof(visited));
  int peak = 0;
  queue<pair<int, int> > q;
  q.push(trueRoot);
  visited[trueRoot.first][trueRoot.second] = ++peak;
  vector<pair<int, int> > edge;
  while (!q.empty()) {
    auto u = q.front();
    q.pop();
    for (int i = 0; i < 4; i++) {
      auto v = make_pair(u.first + dx[i] * len, u.second + dy[i] * len);
      if (!between(1, v.first, n) || !between(1, v.second, m) ||
          !checkPath(u, v))
        continue;
      if (!visited[v.first][v.second])
        visited[v.first][v.second] = ++peak, q.push(v);
      edge.push_back(make_pair(
          min(visited[u.first][u.second], visited[v.first][v.second]),
          max(visited[u.first][u.second], visited[v.first][v.second])));
    }
  }
  for (int i = 1; i <= n; i++)
    for (int j = 1; j <= m; j++)
      if (!pass[i][j] && a[i][j] == 1) return false;
  sort(edge.begin(), edge.end());
  edge.resize(distance(edge.begin(), unique(edge.begin(), edge.end())));
  if (!eulerPath::solve(peak, edge)) return false;
  return true;
}
void prep() {
  for (int i = 1; i <= n; i++) {
    int it = -1;
    ;
    for (int j = 1; j <= m; j++) {
      if (a[i][j] == 1) {
        trueRoot = make_pair(i, j);
        if (it == -1) it = j;
        mx = max(mx, j - it);
      } else
        it = -1;
    }
  }
  for (int i = 1; i <= m; i++) {
    int it = -1;
    for (int j = 1; j <= n; j++) {
      if (a[i][j] == 1) {
        trueRoot = make_pair(i, j);
        if (it == -1) it = j;
        mx = max(mx, j - it);
      } else
        it = -1;
    }
  }
}
signed main() {
  ios_base::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  cin >> n >> m;
  for (int i = 1; i <= n; i++)
    for (int j = 1; j <= m; j++) cin >> a[i][j];
  prep();
  if (mx == 0) {
    cout << -1 << '\n';
    exit(0);
  }
  vector<int> order;
  for (int i = 2; i <= mx; i++) {
    if (mx % i == 0) {
      if (check(i)) order.push_back(i);
    }
  }
  if (order.size() == 0)
    cout << -1 << '\n';
  else {
    for (auto it : order) cout << it << ' ';
    cout << '\n';
  }
}
