#include<iostream>
#include<sstream>
#include<vector>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cassert>
using namespace std;

#define rep(i,n) for(int i=0;i<(int)n;i++)
#define each(i,c) for(__typeof(c.begin()) i=c.begin();i!=c.end();i++)
#define pb push_back
#define mp make_pair
#define all(c) c.begin(),c.end()
#define dbg(x) cerr<<__LINE__<<": "<<#x<<" = "<<(x)<<endl

typedef vector<int> vi;
typedef pair<int,int> pi;
typedef long long ll;
const int inf=(int)1e9;
const double EPS = 1e-8, INF = 1e12, PI = acos(-1.0);
typedef complex<double> P;
namespace std{
	bool operator<(const P& a, const P& b){
		return real(a) != real(b) ? real(a) < real(b) : imag(a) < imag(b);
	}
}
inline double cross(const P& a, const P& b){ return imag(conj(a)*b); }
inline 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;

inline 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 intersect_1pt(const P& a, const P& b,
                   const P& c, const P& d, P &r) {
  double D =  cross(b - a, d - c);
  if (abs(D) < EPS) return false;
  double t =  cross(c - a, d - c) / D;
  double s = -cross(a - c, b - a) / D;
  r = a + t * (b - a);
  return t > -EPS && t < 1 + EPS && s > -EPS && s < 1 + EPS;
}
enum { OUT, ON, IN };
int convex_contains(const G &po, const P &p) {
  const int n = po.size();
  P g = (po[0] + po[n/3] + po[2*n/3]) / 3.0; // inner-P
  int a = 0, b = n;
  while (a+1 < b) { // invariant: c is in fan g-po[a]-po[b]
    int c = (a + b) / 2;
    if (cross(po[a]-g, po[c]-g) > 0) { // angle < 180 deg
      if (cross(po[a]-g, p-g) > 0 && cross(po[c]-g, p-g) < 0) b = c;
      else                                                  a = c;
    } else {
      if (cross(po[a]-g, p-g) < 0 && cross(po[c]-g, p-g) > 0) a = c;
      else                                                  b = c;
    }
  }
  b %= n;
  if (cross(po[a] - p, po[b] - p) < 0) return 0;
  if (cross(po[a] - p, po[b] - p) > 0) return 2;
  return 1;
}
G convex_intersect(const G &p, const G &Q) {
  const int n = p.size(), m = Q.size();
  int a = 0, b = 0, aa = 0, ba = 0;
  enum { Pin, Qin, Unknown } in = Unknown;
  G R;
  do {
    int a1 = (a+n-1) % n, b1 = (b+m-1) % m;
    double C = cross(p[a] - p[a1], Q[b] - Q[b1]);
    double A = cross(p[a1] - Q[b], p[a] - Q[b]);
    double B = cross(Q[b1] - p[a], Q[b] - p[a]);
    P r;
    if (intersect_1pt(p[a1], p[a], Q[b1], Q[b], r)) {
      if (in == Unknown) aa = ba = 0;
      R.push_back( r );
      in = B > 0 ? Pin : A > 0 ? Qin : in;
    }
    if (C == 0 && B == 0 && A == 0) {
      if (in == Pin) { b = (b + 1) % m; ++ba; }
      else           { a = (a + 1) % m; ++aa; }
    } else if (C >= 0) {
      if (A > 0) { if (in == Pin) R.push_back(p[a]); a = (a+1)%n; ++aa; }
      else       { if (in == Qin) R.push_back(Q[b]); b = (b+1)%m; ++ba; }
    } else {
      if (B > 0) { if (in == Qin) R.push_back(Q[b]); b = (b+1)%m; ++ba; }
      else       { if (in == Pin) R.push_back(p[a]); a = (a+1)%n; ++aa; }
    }
  } while ( (aa < n || ba < m) && aa < 2*n && ba < 2*m );
  if (in == Unknown) {
    if (convex_contains(Q, p[0])) return p;
    if (convex_contains(p, Q[0])) return Q;
  }
  return R;
}
G convex_hull(G ps) {
  int n = ps.size(), k = 0;
  sort(ps.begin(), ps.end());
  G ch(2*n);
  for (int i = 0; i < n; ch[k++] = ps[i++]) // lower-hull
    while (k >= 2 && ccw(ch[k-2], ch[k-1], ps[i]) <= 0) --k;
  for (int i = n-2, t = k+1; i >= 0; ch[k++] = ps[i--]) // upper-hull
    while (k >= t && ccw(ch[k-2], ch[k-1], ps[i]) <= 0) --k;
  ch.resize(k-1);
  return ch;
}

int p[100];
int root(int x){
	if(x == p[x]) return x;
	return p[x];
}

int main(){
	int n;
	while(cin >> n, n){
		while(n--){
			int m;
			cin >> m;
			vector<G> v;
			rep(i, m) p[i] = i;
			int ans = m;
			
			rep(i, m){
				G g(4);
				rep(j, 4) cin >> g[j].real() >> g[j].imag();
				v.pb(convex_hull(g));
				rep(j, i){
					bool flag = 0;
					rep(k, 4) if(convex_contains(v[j], g[k]) != OUT) flag = 1;
					G h = convex_intersect(g, v[j]);
					if(h.size() || flag){
						int a = root(i), b = root(j);
						if(a != b) p[a] = b, ans--;
					}
				}
			}
			cout << ans << endl;
		}
	}
	return 0;
}