#define _USE_MATH_DEFINES
#include <iostream>
#include <complex>
#include <algorithm>
#include <vector>
#include <stack>
#include <string>
#include <queue>
#include <cmath>
#include <math.h>
#include <numeric>
#include <list>
#include <sstream>
#include <fstream>
#include <iomanip>
#include <climits>
#include <set>
#include <memory.h>
#include <memory>
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cassert>
#include <map>
#include <cassert>
#include <time.h>
#include <ctime>

using namespace std;

typedef complex<double> xy_t;
typedef pair<xy_t, xy_t> line;
typedef vector<xy_t> polygon;
typedef long long ll;
typedef pair<int, int> P;
typedef pair<int , P> PP;
typedef pair<int, string> Ps;
typedef vector<int> vec;
typedef vector<vec> mat;
const int INF = 1 << 29;
const double EPS = 1e-10;
const double PI = 3.1415926535897932384626433832795;
const int CLK = CLOCKS_PER_SEC;

#define rep(i, n) for(int i = 0; i < n; i++)
#define rep2(i, m, n) for(int i = m; i < n; i++)
#define repD(i, n) for(int i = n; i >= 0; i--)

double cross(const xy_t &a, const xy_t &b){
	return imag(conj(a) * b);
}

double dot(const xy_t &a, const xy_t &b){
	return real(conj(a) * b);
}

int ccw(xy_t a, xy_t b, xy_t c){
	b-=a, c-=a;
	if(cross(b, c) > 0) return 1;
	if(cross(b, c) < 0) return -1;
	if(dot(b, c) < 0) return 2;
	if(norm(b) < norm(c)) return -2;
	return 0;
}

bool intersectSS(const line &l, const line &m){
	return ccw(l.first, l.second, m.first) * ccw(l.first, l.second, m.second) <= 0 &&
		ccw(m.first, m.second, l.first) * ccw(m.first, m.second, l.second) <= 0;
}

bool intersectPOP(const polygon &pol, const xy_t &p){
	line l(p, xy_t(1e10, p.imag()));
	int n = pol.size();
	int cnt = 0;
	rep(i, n){
		line lp(pol[i], pol[(i+1)%n]);
		if(abs(cross(l.second - l.first, lp.second - lp.first)) < EPS) continue;
		if(intersectSS(l, lp)) cnt++;
	}
	return cnt % 2 == 1;
}

int intersectPOPO(const polygon &p1, const polygon &p2){
	int n = p1.size();
	int m = p2.size();
	int c1 = 0, c2 = 0;
	rep(i, n) if(intersectPOP(p2, p1[i])) c1++;
	rep(i, m) if(intersectPOP(p1, p2[i])) c2++;
	if(c1 == 0 && c2 == 0) return 0;
	if(c1 == n) return 2;
	if(c2 == m) return 3;
	return 1;
}

polygon pols[100];
bool g[100][100];
bool used[100];

void bfs(int v){
	queue<int> que;
	que.push(v);
	used[v] = true;
	while(!que.empty()){
		int p = que.front();
		que.pop();
		rep(i, 100){
			if(g[p][i] && !used[i]){
				used[i] = true;
				que.push(i);
			}
		}
	}
}

int main(){
	int n, m;
	int x, y;
	while(cin >> n && n){
		rep(k, n){
			cin >> m;
			memset(g, false, sizeof(g));
			memset(used, false, sizeof(used));
			rep(i, m){
				rep(j, 4){
					cin >> x >> y;
					pols[i].push_back(xy_t(x, y));
				}
			}
				
			rep(i, m){
				rep(j, i){
					if(intersectPOPO(pols[i], pols[j]) > 0){
						g[i][j] = true;
						g[j][i] = true;
					}
				}
			}
			int res = 0;
			rep(i, m) if(!used[i]){
				res++;
				bfs(i);
			}
			cout << res << endl;
			rep(i, m) pols[i].clear();
		}
	}
	return 0;
}