#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <climits>
#include <cassert>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <algorithm>
#include <numeric>
#include <complex>
#include <stack>
#include <queue>
#include <list>
#include <set>
#include <map>
#include <bitset>
#include <functional>
#include <iterator>

using namespace std;

#define dump(n) cout<<"# "<<#n<<"="<<(n)<<endl
#define repi(i,a,b) for(int i=int(a);i<int(b);i++)
#define peri(i,a,b) for(int i=int(b);i-->int(a);)
#define rep(i,n) repi(i,0,n)
#define per(i,n) peri(i,0,n)
#define iter(c) __typeof__((c).begin())
#define foreach(i,c) for(iter(c) i=(c).begin();i!=(c).end();++i)
#define all(c) (c).begin(),(c).end()
#define mp make_pair

typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef vector<int> vi;
typedef vector<vi> vvi;
typedef vector<double> vd;
typedef vector<vd> vvd;
typedef vector<string> vs;
typedef pair<int,int> pii;

const int INFTY=1<<29;
const double EPS=1e-9;

template<typename T1,typename T2>
ostream& operator<<(ostream& os,const pair<T1,T2>& p){
	return os<<'('<<p.first<<','<<p.second<<')';
}
template<typename T>
ostream& operator<<(ostream& os,const vector<T>& a){
	os<<'[';
	rep(i,a.size()) os<<(i?" ":"")<<a[i];
	return os<<']';
}

typedef complex<double> Point;

struct Line{
	Point pos,dir;
	Line(){}
	Line(Point p,Point d):pos(p),dir(d){}
};
typedef Line Segment;

int Signum(double x)
{
	return abs(x)<EPS?0:x<0?-1:1;
}

double Dot(Point a,Point b)
{
	return real(conj(a)*b);
}

double Cross(Point a,Point b)
{
	return imag(conj(a)*b);
}

int CCW(Point a,Point b,Point c)
{
	Point d1=b-a,d2=c-a;
	if(int sign=Signum(Cross(d1,d2)))
		return sign;	// 1:ccw,-1:cw
	if(Dot(d1,d2)<-EPS)
		return -2;		// c-a-b
	if(norm(d1)<norm(d2)-EPS)
		return 2;		// a-b-c
	return 0;			// a-c-b
}

bool InterLS(Line l,Segment s)
{
	Point a=l.pos,b=l.pos+l.dir;
	Point c=s.pos,d=s.pos+s.dir;
	return CCW(a,b,c)*CCW(a,b,d)<0;
}
bool InterSS(Segment a,Segment b)
{
	return InterLS(a,b) && InterLS(b,a);
}

bool Inner(const vector<Point>& ps,Point p)
{
	bool res=true;
	rep(i,ps.size())
		res&=CCW(ps[i],ps[(i+1)%ps.size()],p)>=0;
	return res;
}

struct UnionFind{
	vi data;
	UnionFind(int size):data(size,-1){}
	int Find(int n){
		return data[n]<0?n:(data[n]=Find(data[n]));
	}
	void Unite(int a,int b){
		int ra=Find(a),rb=Find(b);
		if(ra!=rb){
			if(-data[ra]<-data[rb])
				swap(ra,rb);
			data[ra]+=data[rb];
			data[rb]=ra;
		}
	}
	int Size(int n){
		return -data[Find(n)];
	}
};

int main()
{
	for(int n;cin>>n,n;){
		while(n--){
			int m; cin>>m;
			vector<vector<Point> > qs(m,vector<Point>(4));
			rep(i,m){
				rep(j,4)
					cin>>real(qs[i][j])>>imag(qs[i][j]);
				reverse(all(qs[i]));
			}
			UnionFind uf(m);
			
			rep(i,m) repi(j,i+1,m){
				bool flg=false;
				rep(k,4){
					flg|=Inner(qs[i],qs[j][k]);
					flg|=Inner(qs[j],qs[i][k]);
				}
				rep(k,4) rep(l,4){
					Segment s1(qs[i][k],qs[i][(k+1)%4]-qs[i][k]);
					Segment s2(qs[j][l],qs[j][(l+1)%4]-qs[j][l]);
					flg|=InterSS(s1,s2);
				}
				if(flg)
					uf.Unite(i,j);
			}
			int res=0;
			rep(i,uf.data.size())
				if(uf.data[i]<0)
					res++;
			cout<<res<<endl;
		}
	}
	
	return 0;
}

Measured memory_bytes: 1264