#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
#define PB push_back
#define ST first
#define ND second
#define RD third
#define PAIR(a,b) make_pair((a), (b))
#define REP(i,n) for(int i=0;i<(n);++i)
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define FOREACH(it,V) for(typeof((V).begin()) it = (V).begin(); it!=(V).end(); ++it)
struct Triple {
int first, second, third;
Triple(int first, int second, int third) : first(first), second(second), third(third) { }
};
Triple getLast(vector<Triple> const& collection){
return *collection.rbegin();
}
Triple getPenultimate(vector<Triple> const& collection){
typeof(collection.rbegin()) it = collection.rbegin();
++it;
return *it;
}
int next(int i, int n){
++i;
return i==n?0:i;
}
int prev(int i, int n){
--i;
return i==-1?n-1:i;
}
long long ilWek(Triple const& A, Triple const& B, Triple const& C){
int adx = A.ST-C.ST,
bdx = B.ST-C.ST,
ady = A.ND-C.ND,
bdy = B.ND-C.ND;
return adx*(long long)bdy-bdx*(long long)ady;
}
vector<Triple> getTriangulation(vector<Triple> const& points){
vector<Triple> result, hull;
int n = points.size();
Triple lmyT = points[0];
int lmy = 0;
REP(i,n){
if(points[i].ND>lmyT.ND||points[i].ND==lmyT.ND&&points[i].ST<lmyT.ST){
lmyT = points[i];
lmy = i;
}
}
int i = lmy;
int j = prev(lmy, n);
int color, curr_color, curr_it;
bool once = false;
//printf("lmy: poz:%d (x: %d, y: %d, id: %d)\n", i, points[i].ST, points[i].ND, points[i].RD);
//printf("(i,j): (%d,%d)\n", i, j);
while(i!=next(j,n)||!once){
once = true;
if(points[j].ND>=points[i].ND){
curr_color = 1;
curr_it = j;
j = prev(j, n);
} else {
curr_color = -1;
curr_it = i;
i = next(i, n);
}
if(hull.size()>=2){
Triple top = getLast(hull);
while(hull.size()>=2){
Triple last = getLast(hull);
Triple penultimate = getPenultimate(hull);
long long k = color * ilWek(points[curr_it], last, penultimate);
//printf("k: %lld color: %d ilWek: %lld\n", k, color, ilWek(points[curr_it], last, penultimate));
if(k>0){
if(color==1){
result.PB(Triple(
points[curr_it].RD,
last.RD,
penultimate.RD));
} else {
result.PB(Triple(
points[curr_it].RD,
penultimate.RD,
last.RD));
}
//printf("new triangle: (%d,%d,%d)\n", points[curr_it].RD, last.RD, penultimate.RD);
}
if(color!=curr_color || k>0){
//printf("drop point from hull: id: %d\n", getLast(hull).RD);
hull.pop_back();
} else {
break;
}
}
if(color!=curr_color){
while(!hull.empty()){
//printf("drop point from huLL: id: %d\n", getLast(hull).RD);
hull.pop_back();
}
//printf("push point on huLL: %d\n", top.RD);
hull.PB(top);
}
}
//printf("push point on hull: %d\n", curr_it);
hull.PB(points[curr_it]);
color = curr_color;
//printf("(i,j): (%d,%d)\n", i, j);
}
return result;
}
int main()
{
int z,n,x,y; scanf("%d", &z); while(z--){
scanf("%d", &n);
vector<Triple> points;
REP(i,n){
scanf("%d%d", &x, &y);
points.PB(Triple(x,y,i));
}
vector<Triple> triangulation = getTriangulation(points);
printf("%d\n", (int)triangulation.size());
REP(i, triangulation.size()){
printf("%d %d %d\n", triangulation[i].ST, triangulation[i].ND, triangulation[i].RD);
}
}
return 0;
}