#include <bits/stdc++.h>
using namespace std;
int N, K;
vector<int> G[30005];
vector<int> Path[30005];
vector<int> Sub[30005];
int V[205], counter;
int D[205][30005], Ord[30005];
bool In[30005];
bool Use[30005];
int LCA[205][30005];
int n;
bool ok = 1;
void Read() {
  scanf("%d%d", &N, &K);
  for (int i = 1; i <= K; i++) {
    for (int j = 1; j <= N; j++) {
      scanf("%d", &D[i][j]);
      if (D[i][j] == 0) {
        if (V[i] != 0) {
          ok = 0;
          return;
        }
        V[i] = j;
      }
    }
    if (V[i] == 0) {
      ok = 0;
      return;
    }
    In[V[i]] = 1;
  }
}
inline bool cmp(int a, int b) { return D[1][a] < D[1][b]; }
inline bool cmp2(int a, int b) { return D[1][a] < D[1][b]; }
void buildPath() {
  Path[V[1]].push_back(V[1]);
  Use[V[1]] = 1;
  for (int i = 2; i <= K; i++) {
    int node = V[i];
    n = i;
    for (int j = 1; j <= N; j++) {
      if (D[1][j] + D[i][j] == D[1][V[i]]) {
        Path[node].push_back(j);
      }
    }
    sort(Path[node].begin(), Path[node].end(), cmp);
    for (int k = 0; k < Path[node].size() - 1; k++) {
      int x = Path[node][k];
      int y = Path[node][k + 1];
      Use[x] = Use[y] = 1;
      G[x].push_back(y);
      G[y].push_back(x);
    }
  }
}
void AddRest() {
  for (int i = 1; i <= N; i++) Ord[i] = i;
  sort(Ord + 1, Ord + N + 1, cmp2);
  for (int i = 1; i <= N; i++)
    if (Use[i] == 1) Sub[i].push_back(i);
  for (int i = 1; i <= N; i++) {
    int node = Ord[i];
    if (Use[node] == 1) continue;
    int Max = -1, PMax = 0;
    for (int j = 1; j <= K; j++) {
      int distance = D[1][V[j]] + D[1][node] - D[j][node];
      if (distance % 2 == 1) {
        ok = 0;
        return;
      }
      distance /= 2;
      if (Path[V[j]].size() <= distance) {
        ok = 0;
        return;
      }
      int n = Path[V[j]][distance];
      if (Max < D[1][n]) {
        Max = D[1][n];
        PMax = n;
      }
    }
    if (PMax == 0) {
      ok = 0;
      return;
    }
    if (D[1][node] <= D[1][PMax]) {
      ok = 0;
      return;
    }
    if (Sub[PMax].size() <= D[1][node] - D[1][PMax] - 1) {
      ok = 0;
      return;
    }
    if (Sub[PMax].size() - 1 == D[1][node] - D[1][PMax] - 1)
      Sub[PMax].push_back(node);
    G[node].push_back(Sub[PMax][D[1][node] - D[1][PMax] - 1]);
    G[Sub[PMax][D[1][node] - D[1][PMax] - 1]].push_back(node);
  }
}
void DFS(int start, int node, int father, int lv) {
  if (D[start][node] != lv) {
    ok = 0;
    return;
  }
  ++counter;
  if (ok == 0) return;
  for (int i = 0; i < G[node].size(); i++) {
    int neighb = G[node][i];
    if (neighb == father) continue;
    DFS(start, neighb, node, lv + 1);
  }
}
int main() {
  Read();
  if (ok == 0) {
    printf("-1\n");
    return 0;
  }
  buildPath();
  AddRest();
  if (ok == 0) {
    printf("-1\n");
    return 0;
  }
  for (int i = 1; i <= N; i++) {
    sort(G[i].begin(), G[i].end());
    G[i].erase(unique(G[i].begin(), G[i].end()), G[i].end());
  }
  for (int i = 1; i <= K; i++) {
    counter = 0;
    DFS(i, V[i], 0, 0);
    if (ok == 0 || counter < N) {
      printf("-1\n");
      return 0;
    }
  }
  for (int i = 1; i <= N; i++) {
    for (int j = 0; j < G[i].size(); j++) {
      int neighb = G[i][j];
      if (j > 0 && G[i][j] == G[i][j - 1]) continue;
      if (neighb > i) printf("%d %d\n", i, neighb);
    }
  }
  return 0;
}
