#include<iostream>
#include<vector>
#include<queue>
#include<cmath>
using namespace std;
struct Point { long double px, py; };
Point Minus(const Point& a, const Point& b) { return Point{ a.px - b.px,a.py - b.py }; }
long double norm(const Point& a) { return a.px * a.px + a.py * a.py; }
long double abs(const Point& a) { return sqrtl(norm(a)); }
long double arg(const Point& a) { return atan2l(a.py, a.px); }
long double dot(const Point& a, const Point& b) { return a.px * b.px + a.py * b.py; }
long double crs(const Point& a, const Point& b) { return a.px * b.py - a.py * b.px; }
int ccw(const Point& p0, const Point& p1, const Point& p2) {
	Point a = Minus(p1, p0), b = Minus(p2, p0);
	if (crs(a, b) > 1e-10) return 1;
	if (crs(a, b) < -1e-10) return -1;
	if (dot(a, b) < -1e-10) return 2;
	if (norm(a) < norm(b)) return -2;
	return 0;
}
bool its(const Point& p1, const Point& p2, const Point& p3, const Point& p4) { return (ccw(p1, p2, p3) * ccw(p1, p2, p4) <= 0 && ccw(p3, p4, p1) * ccw(p3, p4, p2) <= 0); }
int contain(vector<Point> v, Point p) {
	bool in = false;
	for (int i = 0; i < v.size(); ++i) {
		Point a = Minus(v[i], p), b = Minus(v[(i + 1) % v.size()], p);
		if (a.py > b.py) swap(a, b);
		if (a.py <= 0 && 0 < b.py)
			if (crs(a, b) < 0) in = !in;
		if (crs(a, b) == 0 && dot(a, b) <= 0) return 1;
	}
	return in ? 2 : 0;
}
bool connect(vector<Point>p1, vector<Point>p2) {
	for (int i = 0; i < p1.size(); i++) {
		for (int j = 0; j < p2.size(); j++) {
			if (its(p1[i], p1[(i + 1) % p1.size()], p2[j], p2[(j + 1) % p2.size()]) == true)return true;
		}
	}
	for (int i = 0; i < p2.size(); i++) { if (contain(p1, p2[i]) != 0)return true; }
	for (int i = 0; i < p1.size(); i++) { if (contain(p2, p1[i]) != 0)return true; }
	return false;
}
vector<Point>r[200]; int n, t; vector<int>X[200]; bool unused[200];
int main() {
	while (true) {
		cin >> t; if (t == 0)break;
		for (int h = 0; h < t; h++) {
			for (int i = 0; i < 200; i++) { r[i].clear(); r[i].resize(4); X[i].clear(); unused[i] = false; }
			cin >> n; int cnt = 0;
			for (int j = 0; j < n; j++) {
				cin >> r[cnt][0].px >> r[cnt][0].py >> r[cnt][1].px >> r[cnt][1].py;
				cin >> r[cnt][2].px >> r[cnt][2].py >> r[cnt][3].px >> r[cnt][3].py; cnt++;
			}
			for (int i = 0; i < cnt; i++) {
				for (int j = i + 1; j < cnt; j++) {
					if (connect(r[i], r[j]) == true) { X[i].push_back(j); X[j].push_back(i); }
				}
			}
			queue<int>Q; int ans = 0;
			for (int i = 0; i < cnt; i++) {
				if (unused[i] == true)continue;
				Q.push(i); unused[i] = true; ans++;
				while (!Q.empty()) {
					int a1 = Q.front(); Q.pop();
					for (int j = 0; j < X[a1].size(); j++) {
						if (unused[X[a1][j]] == false) { unused[X[a1][j]] = true; Q.push(X[a1][j]); }
					}
				}
			}
			cout << ans << endl;
		}
	}
	return 0;
}