#include <bits/stdc++.h>
using namespace std;
const int NMax = 1005;
int N, M;
queue<pair<int, int> > Q;
int dx[] = {0, 0, 1, -1};
int dy[] = {-1, 1, 0, 0};
int Matrix[NMax][NMax], Left[NMax][NMax], Up[NMax][NMax], Sum[NMax][NMax];
int X;
bool Use[NMax][NMax], Use2[NMax][NMax];
int X1, Y1;
void Read() {
  cin >> N >> M;
  for (int i = 1; i <= N; i++)
    for (int j = 1; j <= M; j++) {
      cin >> Matrix[i][j];
      if (Matrix[i][j] == 1 && X1 == 0) X1 = i, Y1 = j;
    }
}
int GCD(int a, int b) {
  if (a == 0) return b;
  if (b == 0) return a;
  int r = a % b;
  while (r != 0) {
    a = b;
    b = r;
    r = a % b;
  }
  return b;
}
void buildLeft() {
  for (int i = 1; i <= N; i++)
    for (int j = 1; j <= M; j++)
      if (Matrix[i][j] == 1) Left[i][j] = Left[i][j - 1] + 1;
}
void buildUp() {
  for (int i = 1; i <= N; i++)
    for (int j = 1; j <= M; j++)
      if (Matrix[i][j] == 1) Up[i][j] = Up[i - 1][j] + 1;
}
void Update(int& X, int val) {
  if (X == 0)
    X = val;
  else
    X = GCD(X, val);
}
void findX() {
  for (int i = 1; i <= N; i++)
    for (int j = 1; j <= M; j++) {
      if (Matrix[i][j] == 1 && Matrix[i + 1][j] == 0) Update(X, Up[i][j] - 1);
      if (Matrix[i][j] == 1 && Matrix[i][j + 1] == 0) Update(X, Left[i][j] - 1);
    }
}
inline bool Valid(int x, int y) {
  return x > 0 && y > 0 && x <= N && y <= M && Matrix[x][y] == 1;
}
bool isPath(int x1, int y1, int x2, int y2) {
  if (x1 > x2) swap(x1, x2);
  if (y1 > y2) swap(y1, y2);
  if (x1 == x2) return Left[x1][y2] - Left[x1][y1] == y2 - y1;
  if (y1 == y2) return Up[x2][y1] - Up[x1][y1] == x2 - x1;
}
bool buildGraph(int val) {
  memset(Use, 0, sizeof(Use));
  memset(Use2, 0, sizeof(Use2));
  int nb = 0, nb2 = 0;
  for (int i = 1; i <= N; i++)
    for (int j = 1; j <= M; j++)
      if (Matrix[i][j] == 1 && (i - X1) % val == 0 && (j - Y1) % val == 0) ++nb;
  for (int i = 0; i <= N; i++)
    for (int j = 0; j <= M; j++) Sum[i][j] = 0;
  Q.push(make_pair(X1, Y1));
  Use[X1][Y1] = 1;
  int cnt = 0;
  while (!Q.empty()) {
    int x = Q.front().first, y = Q.front().second;
    Use2[x][y] = 1;
    ++nb2;
    Q.pop();
    for (int i = 0; i < 4; i++)
      if (Valid(x + dx[i] * val, y + dy[i] * val) == 1 &&
          isPath(x, y, x + dx[i] * val, y + dy[i] * val) == 1 &&
          Use2[x + dx[i] * val][y + dy[i] * val] == 0) {
        Sum[x][y] = 1 - Sum[x][y];
        if (Sum[x][y] == 0)
          --cnt;
        else
          ++cnt;
        Sum[x + dx[i] * val][y + dy[i] * val] =
            1 - Sum[x + dx[i] * val][y + dy[i] * val];
        if (Sum[x + dx[i] * val][y + dy[i] * val] == 0)
          --cnt;
        else
          ++cnt;
        if (Use[x + dx[i] * val][y + dy[i] * val] == 0)
          Q.push(make_pair(x + dx[i] * val, y + dy[i] * val));
        Use[x + dx[i] * val][y + dy[i] * val] = 1;
      }
  }
  return (cnt == 0 || cnt == 2) && nb == nb2;
}
bool Lee() {
  Q.push(make_pair(X1, Y1));
  Use[X1][Y1] = 1;
  while (!Q.empty()) {
    int x = Q.front().first, y = Q.front().second;
    Q.pop();
    for (int i = 0; i < 4; i++)
      if (Valid(x + dx[i], y + dy[i]) == 1 && Use[x + dx[i]][y + dy[i]] == 0) {
        Use[x + dx[i]][y + dy[i]] = 1;
        Q.push(make_pair(x + dx[i], y + dy[i]));
      }
  }
  for (int i = 1; i <= N; i++)
    for (int j = 1; j <= M; j++)
      if (Matrix[i][j] == 1 && Use[i][j] == 0) return 0;
  return 1;
}
bool check(int val) {
  for (int i = 1; i <= N; i++)
    for (int j = 1; j <= M; j++)
      if (Matrix[i][j] == 1 && ((i - X1) % val != 0 && (j - Y1) % val != 0))
        return 0;
  return buildGraph(val);
}
void getAnswer() {
  int cnt = 0;
  for (int i = 2; i <= X; i++)
    if (X % i == 0)
      if (check(i) == 1) {
        ++cnt;
        cout << i << " ";
      }
  if (cnt == 0) cout << "-1\n";
}
int main() {
  Read();
  buildLeft();
  buildUp();
  if (Lee() == 0) {
    cout << "-1\n";
    return 0;
  }
  findX();
  getAnswer();
  return 0;
}
