#include<cmath>
#include<cstdio>
#include<vector>
#include<complex>
#include<algorithm>

#define	pb			push_back
#define	rep(i,n)	for(int i=0;i<n;i++)

using namespace std;

typedef	complex<double>	Point;

enum {CCW=1,CW=-1,ON=0};
const double EPS=1e-9;

class Line:public vector<Point>{
public:
	Line(){}
	Line(const Point &a,const Point &b){
		pb(a),pb(b);
	}
};

class Segment:public Line{
public:
	Segment(){}
	Segment(const Point &a,const Point &b):Line(a,b){}
};

typedef	vector<Point>	Polygon;

namespace std{
	bool operator <(const Point &a,const Point &b){
		return (real(a)==real(b))?(imag(a)<imag(b)):(real(a)<real(b));
	}
	bool operator >(const Point &a,const Point &b){
		return b<a;
	}
}

double dot(const Point &a,const Point &b){
	return real(a)*real(b)+imag(a)*imag(b);
}

double cross(const Point &a,const Point &b){
	return real(a)*imag(b)-imag(a)*real(b);
}

int ccw(const Point &a,Point b,Point c){
	b-=a,c-=a;
	double rotdir=cross(b,c);
	if(rotdir>EPS)		return CCW;
	if(rotdir<-EPS)		return CW;
	return ON;
}

bool intersect(const Segment &s,const Segment &t){
	if(max(real(s[0]),real(s[1]))<min(real(t[0]),real(t[1]))
	|| max(real(t[0]),real(t[1]))<min(real(s[0]),real(s[1]))
	|| max(imag(s[0]),imag(s[1]))<min(imag(t[0]),imag(t[1]))
	|| max(imag(t[0]),imag(t[1]))<min(imag(s[0]),imag(s[1])))	return false;

	if(ccw(s[0],s[1],t[0])*ccw(s[0],s[1],t[1])<=0
	&& ccw(t[0],t[1],s[0])*ccw(t[0],t[1],s[1])<=0)	return true;
	return false;
}

bool contain(const Polygon &g,const Point &p){
	int n=g.size();
	int dir=ccw(g[n-1],g[0],p);
	if(dir==ON)	return dot(g[0]-p,g[n-1]-p)<EPS;
	rep(i,n-1){
		int a=ccw(g[i],g[i+1],p);
		if(a==ON)	return dot(g[i+1]-p,g[i]-p)<EPS;
		if(a!=dir)	return false;
	}
	return true;
}

bool contain(const Polygon &g1,const Polygon &g2){
	rep(i,g2.size()) if(contain(g1,g2[i])) return true;
	return false;
}

bool intersect(const Polygon &g1,const Polygon &g2){
	int n1=g1.size(),n2=g2.size();
	rep(i,n1)rep(j,n2){
		if(intersect(Segment(g1[i],g1[(i+1)%n1]),Segment(g2[j],g2[(j+1)%n2])))	return true;
	}
	return contain(g1,g2) || contain(g2,g1);
}

typedef	vector<int>	vi;

class UnionFind{
	vi a;
	int root(int x){ return ~a[x]?a[x]=root(a[x]):x; }
public:
	UnionFind(int n):a(n,-1){}
	bool find(int x,int y){ return root(x)==root(y); }
	bool unite(int x,int y){ return find(x,y)?0:(a[root(y)]=root(x),1); }
};

int main(){
	for(int n;scanf("%d",&n),n;){
		rep(k,n){
			int m;	scanf("%d",&m);
			Polygon g[100];
			rep(i,m){
				rep(j,4){
					int x,y;	scanf("%d%d",&x,&y);
					g[i].pb(Point(x,y));
				}
				reverse(g[i].begin(),g[i].end());
			}

			int cnt=m;
			UnionFind uf(m);
			rep(i,m)rep(j,i) if(intersect(g[i],g[j])) cnt-=uf.unite(i,j);
			printf("%d\n",cnt);
		}
	}

	return 0;
}