#include <bits/stdc++.h>
  
using namespace std;
  
#define rep(i,n) REP(i,0,n)
#define REP(i,s,e) for(int i=(s); i<(int)(e); i++)
#define pb push_back
#define all(r) (r).begin(),(r).end()
#define rall(r) (r).rbegin(),(r).rend()
#define fi first
#define se second
  
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
 
const double EPS = 1e-8;
const double INF = 1e12;
const double PI = acos(-1);
typedef complex<double> P;
typedef complex<double> point;
namespace std {
	bool operator < (const P& a, const P& b) {
		return real(a) != real(b) ? real(a) < real(b) : imag(a) < imag(b);
	}
}
double cross(const P& a, const P& b) {
	return imag(conj(a)*b);
}
double dot(const P& a, const P& b) {
	return real(conj(a)*b);
}

struct L : public vector<P> {
	L(const P &a, const P &b) {
		push_back(a); push_back(b);
	}
};

typedef vector<P> G;
typedef vector<P> polygon;

struct C {
	P p; double r;
	C(const P &p, double r) : p(p), r(r) { }
};
//??????????????????
int ccw(P a, P b, P c) {
	b -= a; c -= a;
	if (cross(b, c) > 0)   return +1;       // counter clockwise
	if (cross(b, c) < 0)   return -1;       // clockwise
	if (dot(b, c) < 0)     return +2;       // c--a--b on line
	if (norm(b) < norm(c)) return -2;       // a--b--c on line
	return 0;
}
bool intersectSS(const L &s, const L &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;
}
bool intersectSP(const L &s, const P &p) {
	return abs(s[0]-p)+abs(s[1]-p)-abs(s[1]-s[0]) < EPS; // triangle inequality
}
#define curr(P, i) P[i]
#define next(P, i) P[(i+1)%P.size()]


//polygon??????????¨??????????????????????????????????
enum { OUT, ON, IN };
int contains(const G& pol, const P& p){
	if(pol.size() < 2) return OUT;
	if(intersectSP({pol[0], pol[1]}, p)) return ON;
	bool f = cross(pol[1]-pol[0], p - pol[0]) > 0;
	for(int i = 1; i < pol.size(); i++) {
		auto& now = pol[i];
		auto& nxt = pol[(i+1) % pol.size()];
		if(intersectSP({nxt, now}, p)) return ON;
		if(f^(cross(nxt-now, p-now)>0)) return OUT;
	}
	return IN;
}
//????????´???????£???????????????????(??????????????£???????????????)
enum{NO_HIT=0, HIT, A_IN_B, B_IN_A};
int HitPol_Pol(const polygon& a, const polygon& b){
	bool f = true, hit = false;
	rep(i, a.size()){
		if(contains(b, a[i]) == OUT) f = false;
		else hit = true;
	}
	if(f) return A_IN_B;
	f = true;
	rep(i, b.size()){
		if(contains(a, b[i]) == OUT) f = false;
		else hit = true;
	}
	if(f) return B_IN_A;
	if(hit) return HIT;
	rep(i, a.size()) rep(j, b.size()){
		L la(curr(a, i), next(a, i)), lb(curr(b, j), next(b, j));
		if(intersectSS(la, lb)) return HIT;
	}
	return NO_HIT;
}

struct UFT{ //O(loga(n))
    int n;
    vi d, r;
    UFT(int n) : n(n), d(n, -1), r(n, 0){};
    int root(int i){
        if(d[i] < 0) return i;
        return d[i] = root(d[i]);
    }
    bool same(int x, int y){
        return root(x) == root(y);
    }
    bool unite(int x, int y){
        x = root(x);
        y = root(y);
        if(x == y) return false;
 
        if(r[x] < r[y]) swap(x, y);
        else if(r[x] == r[y]) r[x]++;
        d[x] += d[y];
        d[y] = x;
        return true;
    }
    int size(int i){
        return -d[root(i)];
    }
};

int main(){
	int N;
	while(cin >> N && N) {
 		rep(_, N) {
 			int m;
 			cin >> m;
 			UFT uf(m);
 			vector<polygon> ps(m);
 			rep(i, m) {
 				double x, y;
 				rep(j,4) {
 					cin >> x>> y;
 					ps[i].pb({x, y});
 				}
 			}
 			rep(i, m) rep(j, i) {
 				if(HitPol_Pol(ps[i], ps[j])) uf.unite(i, j);
 			}
 			int ans = 0;
 			rep(i, m) if(uf.root(i) == i) ans++;
 			cout<<ans<<endl;
		}
	}
}

Measured memory_bytes: 3156