#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;

#define MAX 1000

int n, m; //ツノ-ツドツ青? ツδ?ツタツ青?
int a[52][52];
int dist[MAX][MAX]; //ツδ?ツタiツつゥツづァツノ-ツドjツづ慊づ?づ個仰猟猟」
int cnt[MAX]; //ツδ?ツタiツづ個篠淞青?
int near[MAX]; //ツノ-ツドツづ個暗ェツ氾板凝淞つュツづ可つ?づゥツδ?ツタツづ個氾板債?
bool used[MAX];

vector<int> t[MAX];

void init(){
  m = 0;
  memset(dist, -1, sizeof(dist));
  memset(cnt, 0, sizeof(cnt));
  memset(near, -1, sizeof(near));

  for(int i = 0; i < MAX; i++){
    t[i].clear();
  }

  near[0] = 0;
  near[1] = a[0][1] - 2;

  //1ツ妥、ツ姪堋づ?ツ妥、ツ姪堋づ個甘板つセツつッツ陛環渉按猟?
  for(int i = 0; i < a[0][1] - 1; i++){
    cnt[i] = 2;
    dist[i][0] = i + 1;
    dist[i][1] = a[0][1] - dist[i][0];
    m++;

    if(i < a[0][1] - 2){
      t[i].push_back(i + 1);
      t[i + 1].push_back(i);
    }
  }
}

void show(){
  cout<<"---------------------------------------------\n";
  cout<<"--dist table---------------------------------\n";
  for(int i = 0; i < m; i++){
    for(int j = 0; j < n; j++){
      printf("%3d", dist[i][j]);
    }

    cout<<"    ";
    printf("%3d",cnt[i]);
    cout<<endl;
  }

  cout<<"--near router--------------------------------\n";
  for(int i = 0; i < n; i++){
    cout << "node " << i<<" : ";
    cout << near[i] << endl;
  }

  cout<<"--router graph-------------------------------\n";

  for(int i = 0; i < m; i++){
    cout<<i<<" : ";
    for(int j = 0; j < t[i].size(); j++){
      cout << t[i][j] << ", ";
    }
    cout<<endl;
  }
}

//to : ツ新ツつオツつュツ津?嘉?つキツづゥツノ-ツドツ氾板債?
//id : ツδ?ツタツ氾板債?
bool check(int to, int id){
  int diff = a[to][0] - dist[id][0];

  for(int i = 1; i < to; i++){
    if(diff != a[to][i] - dist[id][i]){
      return false;
    }
  }

  return true;
}

//A : ツ新ツつオツつュツ津?嘉?つキツづゥツノ-ツドツ氾板債?
//B : Aツづーツづつづ按つーツづゥツδ?ツタツ氾板債?
//return : ツノ-ツドAツづ個づ?づ?づ?づ?凝淞つュツづ可つュツづ?づつつ「ツづ?づゥツδ?ツタツ氾板債?
int connectAB(int A, int B){
  int d = a[A][0] - dist[B][0];

  if(d == 1){
    cnt[B]++;
    return B;
  }

  for(int i = 0; i < d - 1; i++){
    if(i == 0){
      t[B].push_back(m);
      t[m].push_back(B);
      cnt[m]++;
      cnt[B]++;
    }
    else{
      t[m].push_back(m - 1);
      t[m - 1].push_back(m);
      cnt[m]++;
      cnt[m - 1]++;
    }
    m++;
  }
  cnt[m - 1]++;

  return m - 1;
}

//A : ツ新ツつオツつュツ津?嘉?つキツづゥツノ-ツドツ氾板債?
//dist[id][A]ツづ営ostツ渉堕つォツ債楪づ?
void dfs(int A, int id, int cost){
  dist[id][A] = cost;
  used[id] = true;

  for(int i = 0; i < t[id].size(); i++){
    int to = t[id][i];
    if(!used[to]){
      dfs(A, to, cost + 1);
    }
  }
}

void solve(){
  for(int i = 2; i < n; i++){
    int connect = -1;

    for(int j = 0; j < m; j++){
      if(check(i, j)){
        connect = j;
        break;
      }
    }

    //cout<<"connect : " << connect<<endl;

    near[i] = connectAB(i, connect);
    for(int j = 0; j <= i; j++){
      memset(used, 0, sizeof(used));
      dfs(j, near[j], 1);
    }
  }

  vector<int> ans;
  for(int i = 0; i < m; i++){
    ans.push_back(cnt[i]);
  }
  sort(ans.begin(), ans.end());

  for(int i = 0; i < m; i++){
    if(i != 0) cout << " ";
    cout << ans[i];
  }
  cout<<endl;
}

int validate_dfs(int from, int to, int cost){
  used[from] = true;
  if(from == to){
    return cost;
  }

  for(int i = 0; i < t[from].size(); i++){
    int TO = t[from][i];
    if(!used[TO]){
      int tmp = validate_dfs(TO, to, cost + 1);
      if(tmp != -1) return tmp;
    }
  }

  return -1;
}

bool validate(){
  for(int i = 0; i < n; i++){
    for(int j = 0; j < i; j++){
      if(near[i] == -1 || near[j] == -1) return false;
      memset(used,0,sizeof(used));
      //printf("%d(%d) --> %d(%d)\n", near[i], i,near[j], j);
      if(validate_dfs(near[i], near[j], 2) != a[i][j]){
        return false;
      }
    }
  }

  return true;
}

int main(){
  while(cin >> n, n){
    for(int i = 0; i < n; i++){
      for(int j = 0; j < n; j++){
        cin >> a[i][j];
      }
    }

    init();
    solve();

    //cout << (validate() ? "yes" : "no") << endl;
    //show();
  }
}

Measured memory_bytes: 4864