#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<string>
#include<limits>
#include<queue>
using namespace std;
#define MS 1005
#define identity 0
#define ll long long
#define MOD 1000000000
#define dd float
#include<iomanip>
#include<list>
int a[MS][MS];//matrix that takes input
bool visited[MS];//marks visited elements
struct node //stores information about the node
{ int index;
int value;
int pos;
node()
{
value=2;
}
}b[MS];
class Graph
{
int V;
list<int> *adj;
public:
Graph(int V);
void addEdge(int v, int w);
void bfs(int);
};
Graph::Graph(int V)
{
this->V = V;
adj = new list<int>[V];//stores adjacent element
}
void Graph::addEdge(int v, int w)
{
adj[v].push_back(w); //for adding edge in graph
}
struct loop
{
int sum0;//stores count of 1(lower number)
int sum1;//stores count of 0(higher numvber)
int diff;//stores absolute diff
int rev;//stores value for reversal of values in that index
loop()
{
rev=0;
}
}x[MS];
int y[MS];
int flag2=1,cnt[2];//flag to store if traversal if the initial condition has a solution or not
//cnt to store count of 0 and 1 for that index
//push in queue to add node to the queue
void push_in_queue(queue<int> &s,int top,int index,int val)
{
b[top].value=1-val;
b[top].index=index;
cnt[1-val]++;
s.push(top);
visited[top]=true;
}
//bfs to traverse through graph
void Graph::bfs(int start)
{
queue<int> s;
s.push(start);
visited[start]=true;
while(s.empty()==false)
{
int top=s.front();
s.pop();
list<int>::iterator i;
for(i = adj[top].begin(); i != adj[top].end(); ++i)
{
if(visited[*i]==true)
{
if(b[*i].value==b[top].value)
{ flag2=0;
return;
}
}
else
{
push_in_queue(s,*i,b[top].index,b[top].value);
}
}
}
return ;
}
int main()
{
int t;
cin>>t;
while(t--)
{
int n;cin>>n;
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)cin>>a[i][j];
}
int flag=1;
for(int i=0;i<n;i++)
{
if(a[i][i]==1)flag=0;
}
// flag to check if any diagonal element has an entry 1 or not
if(flag==1)
{
flag2=1;
int idx=0;
Graph g(n);
for(int i=0;i<n;i++)
{
for(int j=i+1;j<n;j++)
{
if(a[i][j]==1)
{
g.addEdge(i,j);
g.addEdge(j,i);
}
}
}
idx=0;
//initializing different values with 0 values
for(int i=0;i<n;i++)
{
b[i].value=2;x[i].rev=0; visited[i]=false; b[i].pos=i; x[i].sum0=0; x[i].sum1=0; x[i].rev=0; x[i].diff=0;
}
for(int i=0;i<n;i++)
{
if(!visited[i])
{
// if not visited , update index value and perform bfs and mark the nodes with their values
{ idx++;
b[i].value=1;
b[i].index=idx;
cnt[1]=1;cnt[0]=0;
g.bfs(i);
if(cnt[1]>cnt[0])
{
x[idx].sum0=cnt[0];
x[idx].sum1=cnt[1];
x[idx].diff=cnt[1]-cnt[0];
x[idx].rev=1;
}
else
{
x[idx].sum0=cnt[1];
x[idx].sum1=cnt[0];
x[idx].diff=cnt[0]-cnt[1];
x[idx].rev=0;
}
}
}
}
//y to store the the index of difference value that added upto make the value
for(int i=0;i<=n;i++)
{
y[i]=0;
}
y[0]=1;
//forming the array y[]
for(int i=1;i<=idx;i++)
{ if(x[i].diff>0)
{
for(int j=n;j>=x[i].diff;j--)
{
if(y[j-x[i].diff]!=0)
{ if(y[j]==0)
y[j]=i;
}
}
}
}
int flag3=1,track=0,sum=0;
//sum stores the minimum sum
for(int i=1;i<=idx;i++)
{
sum=sum+x[i].sum0;
}
int temp=0;
//temp stores the maximum product of (number of 0s and number of 1s)
int rem= (sum*(n-sum));
if(rem>temp)
{
temp=rem;track=0;
}
for(int i=1;i<=(n-2*sum);i++)
{
if(y[i]!=0)
{
int rem= (sum+i)*(n-sum-i);
if(rem>temp)
{track=i; temp=rem; }
}
}
//now , updating the values of zeros , if needed
while(track>0)
{
x[y[track]].rev=1-x[y[track]].rev;
track=track-x[y[track]].diff;
}
//reversing tha values of b[] for which rev is 1
for(int i=0;i<n;i++)
{
if(x[b[i].index].rev==1)
{
b[i].value=1-b[i].value;
}
}
//if the input conditoin has a solution ....print ans
if(flag2==1)
{
for(int i=0;i<n;i++)
{cout<<b[i].value<<"\t"; }
cout<<endl;
}
else
{
cout<<"-1"<<endl;
}
}
else
{
cout<<"-1"<<endl;
}
}
return 0;
}