#include <bits/stdc++.h>
#pragma GCC optimize("O3")
#pragma GCC target("sse4")
using namespace std;
template <class T>
bool ckmin(T& a, const T& b) {
  return b < a ? a = b, 1 : 0;
}
template <class T>
bool ckmax(T& a, const T& b) {
  return a < b ? a = b, 1 : 0;
}
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
const int MOD = 1000000007;
const char nl = '\n';
const int MX = 100001;
int base;
struct hsh {
  long long base, p1, p2;
  long long val1, val2;
  long long inv1, inv2;
  long long modExp(long long power, long long prime) {
    if (power == 0) {
      return 1;
    } else {
      long long cur = modExp(power / 2, prime);
      cur = cur * cur;
      cur = cur % prime;
      if (power % 2 == 1) cur = cur * base;
      cur = cur % prime;
      return cur;
    }
  }
  hsh(int b) {
    base = b;
    p1 = 1000000007;
    p2 = 1000000009;
    val1 = 0;
    val2 = 0;
    inv2 = modExp(p2 - 2, p2);
    inv1 = modExp(p1 - 2, p1);
  }
  hsh(long long b, string S) {
    p1 = 1000000007;
    p2 = 1000000009;
    base = b;
    val1 = 0;
    val2 = 0;
    inv2 = modExp(p2 - 2, p2);
    inv1 = modExp(p1 - 2, p1);
    for (int i = 0; i < ((int)(S).size()); i++) {
      push_back(S[i] - 'a');
    }
  }
  void push_back(long long v) {
    val1 *= base;
    val1 %= p1;
    val1 += v;
    val1 %= p1;
    val2 *= base;
    val2 %= p2;
    val2 += v;
    val2 %= p2;
  }
  pair<long long, long long> get() { return {val1, val2}; }
};
void prepHash() { base = uniform_int_distribution<int>(1000, MOD - 2)(rng); }
template <int SZ>
struct DSU {
  int parent[SZ], size[SZ];
  DSU() {
    for (int i = 0; i < (SZ); i++) parent[i] = i, size[i] = 0;
  }
  int get(int x) {
    if (parent[x] != x) parent[x] = get(parent[x]);
    return parent[x];
  }
  void unify(int x, int y) {
    x = get(x);
    y = get(y);
    if (x == y) return;
    if (size[x] < size[y]) swap(x, y);
    if (size[x] == size[y]) size[x]++;
    parent[y] = x;
  }
};
int main() {
  ios_base::sync_with_stdio(0);
  cin.tie(0);
  prepHash();
  int N, K;
  cin >> N >> K;
  int D[K][N];
  int V[K];
  bool used[N];
  for (int i = 0; i < (N); i++) used[i] = false;
  for (int i = 0; i < (K); i++) {
    V[i] = -1;
    for (int j = 0; j < (N); j++) {
      cin >> D[i][j];
      if (D[i][j] == 0) {
        if (V[i] != -1) {
          cout << -1 << nl;
          return 0;
        }
        V[i] = j;
      }
    }
    if (V[i] == -1) {
      cout << -1 << nl;
      return 0;
    }
    if (used[V[i]]) {
      cout << -1 << nl;
      return 0;
    }
    used[V[i]] = true;
  }
  vector<vector<int> > graph(N);
  bool proc[N];
  for (int i = 0; i < (N); i++) proc[i] = false;
  proc[V[0]] = true;
  pair<long long, long long> H[N];
  set<pair<pair<long long, long long>, int> > hmap;
  hmap.insert({{2000000000, 2000000000}, -1});
  for (int i = 0; i < (N); i++) {
    hsh cur(base);
    for (int j = 0; j < (K); j++) {
      cur.push_back(D[j][i]);
    }
    H[i] = cur.get();
    hmap.insert({H[i], i});
  }
  hmap.erase({H[V[0]], V[0]});
  queue<int> q;
  q.push(V[0]);
  for (int i = 1; i < (K); i++) {
    vector<int> line(D[0][V[i]] + 1);
    for (int j = 0; j < (D[0][V[i]] + 1); j++) line[j] = -1;
    line[D[0][V[i]]] = V[i];
    line[0] = V[0];
    for (int j = 0; j < (N); j++) {
      if (D[0][j] + D[i][j] == D[0][V[i]]) {
        line[D[0][j]] = j;
      }
    }
    for (auto& a : line) {
      if (a == -1) {
        cout << -1 << nl;
        return 0;
      }
    }
    for (int i = 1; i < ((int)(line).size()); i++) {
      if (!proc[line[i]]) {
        proc[line[i]] = true;
        graph[line[i]].push_back(line[i - 1]);
        graph[line[i - 1]].push_back(line[i]);
        hmap.erase({H[line[i]], line[i]});
        q.push(line[i]);
      }
    }
  }
  hsh oneCur(base);
  for (int i = 0; i < (K); i++) oneCur.push_back(1);
  pair<long long, long long> oneHash = oneCur.get();
  while (!q.empty()) {
    int cur = q.front();
    q.pop();
    pair<long long, long long> curHash = {H[cur].first + oneHash.first,
                                          H[cur].second + oneHash.second};
    curHash.first %= MOD;
    curHash.second %= MOD + 2;
    while (hmap.lower_bound({curHash, -1})->first == curHash) {
      int v = hmap.lower_bound({curHash, -1})->second;
      hmap.erase(hmap.lower_bound({curHash, -1}));
      q.push(v);
      graph[cur].push_back(v);
      graph[v].push_back(cur);
    }
  }
  hmap.erase(hmap.begin());
  if (!hmap.empty()) {
    cout << -1 << nl;
    return 0;
  }
  int dist[K][N];
  for (int r = 0; r < (K); r++) {
    for (int i = 0; i < (N); i++) {
      dist[r][i] = N + 1;
    }
    dist[r][V[r]] = 0;
    q.push(V[r]);
    while (!q.empty()) {
      int cur = q.front();
      q.pop();
      for (auto& a : graph[cur]) {
        if (dist[r][a] > dist[r][cur] + 1) {
          dist[r][a] = dist[r][cur] + 1;
          q.push(a);
        }
      }
    }
  }
  for (int i = 0; i < (K); i++) {
    for (int j = 0; j < (N); j++) {
      if (dist[i][j] != D[i][j]) {
        cout << -1 << nl;
        return 0;
      }
    }
  }
  for (int i = 0; i < (N); i++) {
    for (auto& a : graph[i]) {
      if (a > i) continue;
      cout << i + 1 << " " << a + 1 << nl;
    }
  }
  return 0;
}
