#include<cstdio>
#include<cstdint>
#include<array>
#include<utility>
#include<initializer_list>
#include<limits>
#include<string>
#include<vector>
#include<queue>
/**
* Author: Cracker
* Version: 0x02
*/
namespace Default_Source{
#ifndef _linux_
#define likely(x) __builtin_expect(!!(x),1)
#define unlikely(x) __builtin_expect(!!(x),0)
#endif
namespace{
template<typename T>
constexpr T Inf{std::numeric_limits<T>::max()};
template<typename T>
constexpr T abs(const T&x){return x<T(0)?-x:x;}
template<typename T>
constexpr const T&min(const T&lhs,const T&rhs){return lhs<rhs?lhs:rhs;}
template<typename T>
constexpr T min(const std::initializer_list<T>&L){
T ret=*L.begin();
for(const T&it:L) ret=min(ret,it);
return ret;
}
template<typename T>
constexpr const T&max(const T&lhs,const T&rhs){return lhs<rhs?rhs:lhs;}
template<typename T>
constexpr T max(const std::initializer_list<T>&L){
T ret=*L.begin();
for(const T&it:L) ret=max(ret,it);
return ret;
}
template<typename T>
constexpr void swap(T&x,T&y){
T tmp=std::move(x);
x=std::move(y),y=std::move(tmp);
}
}
namespace Bit{
template<typename T>
typename std::enable_if<std::is_signed<T>::value,T>::type bswap(T value){
size_t n{(sizeof(T)<<3)-1};
T ret{};
for(size_t i=0,j=n-1;i<n;++i,--j) if((value>>i)&1) ret|=1<<j;
return ret;
}
template<typename T>
typename std::enable_if<std::is_unsigned<T>::value,T>::type bswap(T value){
size_t n{sizeof(T)<<3};
T ret{};
for(size_t i=0,j=n-1;i<n;++i,--j) if((value>>i)&1) ret|=1<<j;
return ret;
}
}
namespace IO{
namespace Flush{
struct Input_Flush_Base{char getc(){return getchar();}};
template<size_t BUFSIZE>
class Fast_Input_Flush_Base{
protected:
char buf[BUFSIZE],*cur=buf;
public:
Fast_Input_Flush_Base(){std::fread(buf,1,BUFSIZE,stdin);}
char getc(){return*cur++;}
};
template<size_t BUFSIZE>
class Fast_Input_Flush_Safer{
protected:
char buf[BUFSIZE],*cur,*nil;
bool reload(){return (nil=(cur=buf)+std::fread(buf,1,BUFSIZE,stdin))!=buf;}
public:
Fast_Input_Flush_Safer(){reload();}
char getc(){return (cur!=nil||reload())?*cur++:EOF;}
};
struct Output_Flush_Base{void putc(char ch){putchar(ch);}};
template<size_t BUFSIZE>
class Fast_Output_Flush_Base{
char buf[BUFSIZE],*cur=buf;
public:
~Fast_Output_Flush_Base(){std::fwrite(buf,1,cur-buf,stdout);}
void putc(char ch){*cur++=ch;}
};
template<size_t BUFSIZE>
class Fast_Output_Flush_Safer{
char buf[BUFSIZE],*cur=buf;
bool freeze(){return likely(cur!=buf)&&(fwrite(buf,1,cur-buf,stdout),cur=buf);}
public:
~Fast_Output_Flush_Safer(){freeze();}
void putc(char ch){cur-buf==BUFSIZE&&freeze(),*cur++=ch;}
};
}
template<typename _Input_Flush=Flush::Input_Flush_Base>
class InStream{
static _Input_Flush Input;
public:
template<typename T>
typename std::enable_if<std::is_integral<T>::value,InStream>::type&operator>>(T&x){
x=0;bool sign=false;char Ch=Input.getc();
for(;!isdigit(Ch);Ch=Input.getc()) if(Ch=='-') sign=true;
for(;isdigit(Ch);Ch=Input.getc()) x=x*10+(sign?-(Ch&15):(Ch&15));
return *this;
}
InStream&operator>>(char*str){
while(isspace(*str=Input.getc()));
while(!isspace(*++str=Input.getc()));
return *str='\0',*this;
}
template<typename ...Args>
InStream&read(Args&&...args){return std::initializer_list<InStream>{*this>>args...},*this;}
template<typename T>
T read(){
static T x;
return read(x),x;
}
};
template<typename _Input_Flush>
_Input_Flush InStream<_Input_Flush>::Input;
template<typename _Output_Flush=Flush::Output_Flush_Base>
class OutStream{
static _Output_Flush Output;
public:
template<typename T>
typename std::enable_if<std::is_integral<T>::value,OutStream>::type&operator<<(T x){
static char sta[114];
int top=0;
if(x<0){
Output.putc('-');
do sta[top++]=(-(x%10))|48,x/=10;
while(x);
}
else
do sta[top++]=(x%10)|48,x/=10;
while(x);
while(top) Output.putc(sta[--top]);
return *this;
}
OutStream&operator<<(char Ch){return Output.putc(Ch),*this;}
OutStream&operator<<(const char*Str){while(*Str) Output.putc(*Str++);return *this;}
OutStream&operator<<(char*Str){return *this<<(static_cast<const char*>(Str));}
template<typename ...Args>
OutStream&write(Args...args){return std::initializer_list<OutStream>{*this<<args...},*this;}
template<typename ...Args>
OutStream&writeln(Args...args){return write(args...),Output.putc('\n'),*this;}
};
template<typename _Output_Flush>
_Output_Flush OutStream<_Output_Flush>::Output;
}
namespace ModTool{
template<typename _Tp,_Tp mod,_Tp phi=mod-1>
struct Moder{
static_assert(std::is_integral<_Tp>::value,"Only integers can be modulus.");
template<typename T>
constexpr _Tp norm(T x)const{return x<0?x%mod+mod:x%mod;}
template<typename T>
constexpr _Tp unorm(T x)const{return x%mod;}
template<typename T,typename...Args>
constexpr void plus(T&x,Args...args)const{x=norm(x),void(std::initializer_list<T>{(x=unorm(x+norm(args)))...});}
template<typename T,typename...Args>
constexpr void mult(T&x,Args...args)const{x=norm(x),void(std::initializer_list<T>{(x=unorm(x*norm(args)))...});}
template<typename...Args>
constexpr _Tp sum(Args...args)const{
_Tp ret{0};
return plus(ret,args...),ret;
}
template<typename...Args>
constexpr _Tp prod(Args...args)const{
_Tp ret{1};
return mult(ret,args...),ret;
}
template<typename T>
constexpr _Tp qpow(_Tp x,T pw)const{
_Tp ret{1};
x=norm(x);
for(;pw;pw>>=1,x=unorm(x*x)) if(pw&1) ret=unorm(ret*x);
return ret;
}
constexpr _Tp inv(_Tp x)const{return qpow(x,phi-1);}
template<size_t N>
constexpr auto getFac(){
std::array<_Tp,N> Fac{};
Fac[0]=1;
for(size_t i=1;i<N;++i) Fac[i]=unorm(Fac[i-1]*i);
return Fac;
}
template<size_t N>
constexpr auto getInv(_Tp Fac){
std::array<_Tp,N> Inv{};
Inv[N-1]=inv(Fac),Inv[0]=1;
for(size_t i=N-2;i;--i) Inv[i]=unorm(Inv[i+1]*(i+1));
return Inv;
}
};
}
namespace DS{
namespace Sgt{
enum SegmentTree_Type{Single=1,Interval=2};
template<typename Info,SegmentTree_Type _Modify,SegmentTree_Type _Query,typename Vec=std::vector<Info>>
class SegmentTree{};
template<typename Info,typename Vec>
class SegmentTree<Info,Interval,Interval,Vec>{
private:
int st,ed;
Vec tr;
void build(int p,int l,int r){
tr[p]={};
if(l==r) return tr[p].init(l);
int mid=(l+r)>>1;
build(p<<1,l,mid),build(p<<1|1,mid+1,r);
tr[p].pull(tr[p<<1],tr[p<<1|1]);
}
void modify(int p,int l,int r,int L,int R,const Info&V){
if(L<=l&&r<=R) return tr[p].get(V);
int mid=(l+r)>>1;
tr[p].push(tr[p<<1],tr[p<<1|1]);
if(L<=mid) modify(p<<1,l,mid,L,R,V);
if(R>mid) modify(p<<1|1,mid+1,r,L,R,V);
tr[p].pull(tr[p<<1],tr[p<<1|1]);
}
Info query(int p,int l,int r,int L,int R){
if(L<=l&&r<=R) return tr[p];
int mid=(l+r)>>1;
tr[p].push(tr[p<<1],tr[p<<1|1]);
if(L<=mid&&R>mid) return merge(query(p<<1,l,mid,L,R),query(p<<1|1,mid+1,r,L,R));
else return L<=mid?query(p<<1,l,mid,L,R):query(p<<1|1,mid+1,r,L,R);
}
public:
SegmentTree():st{},ed{},tr{}{}
SegmentTree(int _st,int _ed):st{_st},ed{_ed}{tr.resize((ed-st+1)<<2);}
void build(){build(1,st,ed);}
void resize(int _st,int _ed){st=_st,ed=_ed,tr.resize((ed-st+1)<<2);}
void modify(int L,int R,const Info&V){modify(1,st,ed,L,R,V);}
Info query(int L,int R){return query(1,st,ed,L,R);}
void modify(int X,const Info&V){modify(1,st,ed,X,X,V);}
Info query(int X){return query(1,st,ed,X,X);}
};
}
}
namespace NetFlow{
template<typename _Cap,template<typename...> typename _Vec=std::vector>
class Maxi_Flow{
struct edge{int ver,nxt;_Cap cap;};
_Vec<int> Head;
_Vec<edge> Edge;
public:
using edge_type=edge;
using edge_point=int;
edge_point edgeEnd()const{return -1;}
edge_point edgeRev(edge_point e)const{return e^1;}
Maxi_Flow():Head{},Edge{},Dep{}{}
Maxi_Flow(int n):Edge{}{Head.resize(n,-1),Dep.resize(n),Que.resize(n);}
Maxi_Flow(int n,int m){Head.resize(n,-1),Edge.reserve(m<<1),Dep.resize(n),Que.resize(n);}
void clear(){std::fill(Head.begin(),Head.end(),-1),Edge.clear();}
void resize(int n){Head.clear(),Head.resize(n,-1),Dep.resize(n),Que.resize(n);}
void resize(int n,int m){Head.clear(),Head.resize(n,-1),Edge.reserve(m<<1),Dep.resize(n),Que.resize(n);}
edge_point add(int u,int v,_Cap c){return Edge.push_back({v,Head[u],c}),Head[u]=Edge.size()-1;}
edge_point link(int u,int v,_Cap c){return add(v,u,0),add(u,v,c);}
const edge_point&getHead(int u)const{return Head[u];}
const edge_type&getEdge(edge_point e)const{return Edge[e];}
edge_type&cgEdge(edge_point e){return Edge[e];}
private:
_Vec<int> Dep,Cur,Que;
bool bfs(int s,int t){
std::fill(Dep.begin(),Dep.end(),-1);
Que.resize(Head.size());
static int head,tail;
Dep[Que[head=tail=0]=s]=0;
for(int u;head<=tail;) for(int e=Head[u=Que[head++]];~e;e=Edge[e].nxt)
if(Edge[e].cap&&!~Dep[Edge[e].ver]) Dep[Que[++tail]=Edge[e].ver]=Dep[u]+1;
return Dep[t]!=-1;
}
_Cap dfs(int u,int t,_Cap flow){
if(u==t) return flow;
_Cap rest=flow;
for(int&e=Cur[u];~e&&rest;e=Edge[e].nxt)
if(Edge[e].cap&&Dep[Edge[e].ver]==Dep[u]+1){
_Cap cap=dfs(Edge[e].ver,t,min(rest,Edge[e].cap));
if(cap!=0) Edge[e].cap-=cap,Edge[edgeRev(e)].cap+=cap,rest-=cap;
else Dep[Edge[e].ver]=-1;
}
return flow-rest;
}
public:
void output(){for(auto i=0u;i<Head.size();++i) for(int e=Head[i];~e;e=Edge[e].nxt) if(Edge[e].cap) printf("%d %d %d\n",i,Edge[e].ver,Edge[e].cap);}
_Cap flow(int s,int t,_Cap limit=Inf<_Cap>){
_Cap flow{};
while(limit&&bfs(s,t)) for(Cur=Head;_Cap cap=dfs(s,t,limit);) flow+=cap,limit-=cap;
return flow;
}
};
template<typename _Cap,typename _Cost,template<typename...> typename _Vec=std::vector,template<typename...> typename _Que=std::queue>
class Mini_Cost_Maxi_Flow{
struct edge{int ver,nxt;_Cap cap;_Cost cost;};
_Vec<int> Head;
_Vec<edge> Edge;
public:
using edge_type=edge;
using edge_point=int;
edge_point edgeEnd()const{return -1;}
edge_point edgeRev(edge_point e)const{return e^1;}
Mini_Cost_Maxi_Flow():Head{},Edge{}{}
Mini_Cost_Maxi_Flow(int n):Edge{}{Head.resize(n,-1),Dis.resize(n),Inq.resize(n);}
Mini_Cost_Maxi_Flow(int n,int m){Head.resize(n,-1),Edge.reserve(m<<1),Dis.resize(n),Inq.resize(n);}
void clear(){std::fill(Head.begin(),Head.end(),-1),Edge.clear();}
void resize(int n){Head.clear(),Head.resize(n,-1),Dis.resize(n),Inq.resize(n);}
void resize(int n,int m){Head.clear(),Head.resize(n,-1),Edge.reserve(m<<1),Dis.resize(n),Inq.resize(n);}
edge_point add(int u,int v,_Cap c,_Cost w){return Edge.push_back({v,Head[u],c,w}),Head[u]=Edge.size()-1;}
edge_point link(int u,int v,_Cap c,_Cost w){return add(v,u,0,-w),add(u,v,c,w);}
const edge_point&getHead(int u)const{return Head[u];}
const edge_type&getEdge(edge_point e)const{return Edge[e];}
private:
_Vec<int> Cur,Inq;
_Vec<_Cost> Dis;
bool spfa(int s,int t){
std::fill(Dis.begin(),Dis.end(),Inf<_Cost>);
std::fill(Inq.begin(),Inq.end(),0);
static _Que<int> q;
q.emplace(s),Dis[s]=0,Inq[s]=1;
for(int u;!q.empty();){
Inq[u=q.front()]=0,q.pop();
for(int e=Head[u];~e;e=Edge[e].nxt)
if(Edge[e].cap&&Dis[Edge[e].ver]>Dis[u]+Edge[e].cost)
if(Dis[Edge[e].ver]=Dis[u]+Edge[e].cost,!Inq[Edge[e].ver]) q.emplace(Edge[e].ver),Inq[Edge[e].ver]=1;
}
return Dis[t]!=Inf<_Cost>;
}
_Cap dfs(int u,int t,_Cap flow){
if(u==t) return flow;
_Cap rest=flow;
Inq[u]=1;
for(int&e=Cur[u];~e&&rest;e=Edge[e].nxt)
if(Edge[e].cap&&!Inq[Edge[e].ver]&&Dis[Edge[e].ver]==Dis[u]+Edge[e].cost){
_Cap cap=dfs(Edge[e].ver,t,min(rest,Edge[e].cap));
if(cap!=0) Edge[e].cap-=cap,Edge[edgeRev(e)].cap+=cap,rest-=cap;
else Dis[Edge[e].ver]=Inf<_Cost>;
}
Inq[0]=1;
return flow-rest;
}
public:
void output(){for(auto i=0u;i<Head.size();++i) for(int e=Head[i];~e;e=Edge[e].nxt) if(Edge[e].cap) printf("%d %d %d\n",i,Edge[e].ver,Edge[e].cap);}
std::pair<_Cap,_Cost> flow(int s,int t,_Cap limit=Inf<_Cap>){
_Cap flow{};
_Cost cost{};
while(limit&&spfa(s,t)) for(Cur=Head;_Cap cap=dfs(s,t,limit);) flow+=cap,limit-=cap,cost+=cap*Dis[t];
return {flow,cost};
}
};
}
namespace Mat{
template<typename T>
class Matrix{
protected:
int n,m;
int**v;
void Alloca(){
if(n>0&&m>0){
v=new int*[n]{};
for(int i=0;i<n;++i) v[i]=new int[m]{};
}
else v=nullptr,n=m=0;
}
void Freeze(){
if(n>0&&m>0){
for(int i=0;i<n;++i) delete[] v[i];
delete[] v;
}
v=nullptr,n=m=0;
}
void clone(int**p){for(int i=0;i<n;++i) for(int j=0;j<m;++j) v[i][j]=p[i][j];}
public:
int row()const{return n;}
int col()const{return m;}
Matrix():n{},m{},v{}{}
Matrix(int _n,int _m):n{_n},m{_m},v{}{Alloca();}
Matrix(int _n):n{_n},m{_n},v{}{Alloca(),init();}
Matrix(const Matrix&it):n{it.n},m{it.m}{Alloca(),clone(it.v);}
Matrix(Matrix&&it):Matrix{}{swap(n,it.n),swap(m,it.m),swap(v,it.v);}
Matrix&operator=(const Matrix&it){return resize(it.n,it.m),clone(it.v),*this;}
Matrix&operator=(Matrix&&it){return swap(n,it.n),swap(m,it.m),swap(v,it.v),*this;}
void resize(int _n,int _m,int _v={}){
Freeze(),n=_n,m=_m,Alloca();
for(int i=0;i<n;++i) for(int j=0;j<m;++j) v[i][j]=_v;
}
T*operator[](int x){return v[x];}
const T*operator[](int x)const{return v[x];}
void init(){for(int i=0;i<n;++i) (*this)[i][i]=1;}
~Matrix(){Freeze();}
template<typename U>
operator Matrix<U>()const{
Matrix<U> ret(n,m);
return ret.Alloca(),ret.clone(v),ret;
}
};
template<typename Flush,typename T>
IO::OutStream<Flush>&operator<<(IO::OutStream<Flush>&os,const Matrix<T>&it){
for(int i=0;i<it.row();++i,os<<'\n') for(int j=0;j<it.col();++j) os<<it[i][j]<<' ';
return os;
}
}
}
using namespace Default_Source;
/**
*/
constexpr int N=200;
IO::InStream<> Input;
IO::OutStream<> Output;
int n,a[N+1][N+1];
std::pair<int,int> id[N+1][N+1];
template<typename Flush>
IO::OutStream<Flush>&operator<<(IO::OutStream<Flush>&os,std::pair<int,int> p){return os<<p.first<<' '<<p.second;}
void print(){
Output.writeln(n*(n-1)>>1);
for(int i=1;i<=n;++i) for(int j=1;j<i;++j)
Output.writeln(id[i][j],' ',id[j][i]);
}
NetFlow::Maxi_Flow<int> G;
signed main(){
for(int Test=Input.read<int>();Test;--Test){
Input.read(n);
for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) Input.read(a[i][j]),id[i][j]={i,j};
int s=0,t=(n<<1)+1;
G.resize(t+1);
for(int T=1;T<=n;++T){
G.clear();
for(int i=1;i<=n;++i) G.link(s,i,1),G.link(i+n,t,1);
static int c[N+1][N+1];
__builtin_memset(c,0,sizeof(c));
for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) if(!c[i][a[i][j]]) c[i][a[i][j]]=j,G.link(i,a[i][j]+n,1);
// G.output();
// Output.writeln();
if(G.flow(s,t)!=n) return 0;
for(int i=1;i<=n;++i) for(int e=G.getHead(i);e!=G.edgeEnd();e=G.getEdge(e).nxt)
if(!G.getEdge(e).cap){
int k=c[i][G.getEdge(e).ver-n];
swap(id[i][k],id[i][T]),swap(a[i][k],a[i][T]);
}
}
print();
}
return 0;
}