#include <iostream>
#include <complex>
#include <vector>
#include <set>
#include <cmath>
using namespace std;

// Union-Find
struct union_find{
	vector<int> par, rank;
	
	// コンストラクタで初期化
	union_find(int n){
		par = vector<int>(n);
		rank = vector<int>(n, 0);
		for(int i = 0 ; i < n ; i++ ){
			par[i] = i;
		}
	}
	// x の root を返す
	int find(int x){
		return (par[x] == x)? x : (par[x] = find(par[x]) );
	}
	// x と y の属する集合を併合する
	void unite(int x, int y){
		x = find(x);
		y = find(y);
		if( x == y ) return;
		
		if( rank[x] < rank[y] ){
			par[x] = y;
		}else{
			par[y] = x;
			if( rank[x] == rank[y] )
				rank[x]++;
		}
	}
	// x と y が同じ集合に属するかどうか
	bool same(int x, int y){
		return find(x) == find(y);
	}
	// 集合の数を返す
	int size(){
		set<int> s;
		for(int i=0 ; i < par.size() ; i++ ){
			s.insert( this->find(i) );
		}
		return s.size();
	}
};

typedef complex<double> P;
const double EPS = 1e-6;
const double PI = acos(-1.0);

// 2つのスカラーが等しいかどうか
bool equal(double a, double b) {
	return fabs(a - b) < EPS;
}
// 内積 (dot product) : a・b = |a||b|cosΘ
double dot(P a, P b) {
	return a.real() * b.real() + a.imag() * b.imag();
}
// 外積 (cross product) : a×b = |a||b|sinΘ
double cross(P a, P b) {
	return a.real() * b.imag() - a.imag() * b.real();
}
// CCW : 反時計回り (Counter Clock Wise)
// CW : 時計回り (Clock Wise)
enum {CCW=1, CW=-1, ON=0};
int ccw(const P &a, P b, P c) {
	b -= a, c -= a;
	if( cross(b,c) >  EPS ) return CCW; // CCW : 反時計回り
	if( cross(b,c) < -EPS ) return CW;  // CW : 時計回り
	if( dot(b, c)  < -EPS ) return +2;  // c--a--b on line
	if( dot(b, b)  + EPS < dot(c, c) ) return -2; // a--b--c on line 
	return ON;
}
struct Line : public vector<P> {
	Line(){}
	Line(const P& a, const P& b) {
		push_back(a); push_back(b);
	}
};


// 線分 s と 線分 t の交差判定
bool intersectSS(const Line& s, const Line& t) {
	return 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;
}

struct Triangle{
private:
	// 余弦定理から 3 つの角の角度を求める (内部の関数で使用)
	double law_of_cos(double a, double b, double c) {
		return acos( (b*b + c*c - a*a) / (2.0 * b * c) );
	}
	// 点 a , b を端点とする線分に点 c が含まれているかどうか
	bool contain(P a, P b, P c) {
		return abs(a - c) + abs(b - c) - abs(b - a) < EPS;
	}
public:
	// 3 つの頂点
	P a, b, c;
	// 3 つの辺の長さ
	double edgeA, edgeB, edgeC;
	// 3 つの角の角度 (ラジアン)
	double angleA,angleB,angleC;
	// コンストラクタで初期化
	Triangle(P p1, P p2, P p3) {
		a = p1; b = p2; c = p3;
		edgeB = abs(c - a);
		edgeA = abs(b - c);
		edgeC = abs(a - b);
		angleA = law_of_cos(edgeA, edgeB, edgeC);
		angleB = law_of_cos(edgeB, edgeC, edgeA);
		angleC = law_of_cos(edgeC, edgeA, edgeB);
	}
	// 点 p が三角形の辺上にあるかどうか
	bool on_boundary(P p) {
		return contain(a, b, p) || contain(b, c, p) || contain(c, a, p);
	}
	// 点 p が三角形の内部にあるかどうか ( AOJ 0012 で検証済み )
	bool is_inside(P p) {
		// 辺上にあるとき
		if( this->on_boundary(p) ) return true;
		
		Triangle t1(a, b, p);
		Triangle t2(b, c, p);
		Triangle t3(c, a, p);
		// 角APB + 角BPB + 角CPA が 360度なら 点 p は三角形の内部にある
		return equal(t1.angleC + t2.angleC + t3.angleC, 2.0 * PI);
	}
};

int N, M;
double xa[101], ya[101], xb[101], yb[101], xc[101], yc[101], xd[101], yd[101];

bool intersect(int i, int j){
	// 四角形 i が四角形 j を完全に含んでいるかチェック
	Triangle t1(P(xa[i], ya[i]), P(xb[i], yb[i]), P(xc[i], yc[i]));
	Triangle t2(P(xc[i], yc[i]), P(xd[i], yd[i]), P(xa[i], ya[i]));
	bool fa = t1.is_inside(P(xa[j], ya[j])) || t2.is_inside(P(xa[j], ya[j]));
	bool fb = t1.is_inside(P(xb[j], yb[j])) || t2.is_inside(P(xb[j], yb[j]));
	bool fc = t1.is_inside(P(xc[j], yc[j])) || t2.is_inside(P(xc[j], yc[j]));
	bool fd = t1.is_inside(P(xd[j], yd[j])) || t2.is_inside(P(xd[j], yd[j]));
	if( fa && fb && fc && fd ) return true;
	
	// 四角形の辺でひとつでも交差している辺があるかどうか
	vector<Line> v1, v2;
	v1.push_back(Line(P(xa[i], ya[i]), P(xb[i], yb[i])));
	v1.push_back(Line(P(xb[i], yb[i]), P(xc[i], yc[i])));
	v1.push_back(Line(P(xc[i], yc[i]), P(xd[i], yd[i])));
	v1.push_back(Line(P(xd[i], yd[i]), P(xa[i], ya[i])));
	
	v2.push_back(Line(P(xa[j], ya[j]), P(xb[j], yb[j])));
	v2.push_back(Line(P(xb[j], yb[j]), P(xc[j], yc[j])));
	v2.push_back(Line(P(xc[j], yc[j]), P(xd[j], yd[j])));
	v2.push_back(Line(P(xd[j], yd[j]), P(xa[j], ya[j])));
	
	for(int k = 0 ; k < v1.size() ; k++ ){
		for(int l = 0 ; l < v2.size() ; l++ ){
			Line l1 = v1[k], l2 = v2[l];
			if( intersectSS(l1, l2) ) return true;
		}
	}
	return false;
}

int solve(){
	union_find uf(M);
	for(int i = 0 ; i < M ; i++ ){
		for(int j = 0 ; j < M ; j++ ){
			if( i == j ) continue;
			if( intersect(i, j) ){
				uf.unite(i, j);
			}
		}
	}
	return uf.size();
}

int main(){
	while( cin >> N, N ){
		for(; N-- ; ){
			cin >> M;
			for(int i = 0 ; i < M ; i++ ){
				cin >> xa[i] >> ya[i] >> xb[i] >> yb[i] >> xc[i] >> yc[i] >> xd[i] >> yd[i];
			}
			cout << solve() << endl;
		}
	}
}

Measured memory_bytes: 1360