#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <math.h>
#include <assert.h>
#include <vector>

using namespace std;
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
static const double EPS = 1e-9;
static const double PI = acos(-1.0);

#define REP(i, n) for (int i = 0; i < (int)(n); i++)
#define FOR(i, s, n) for (int i = (s); i < (int)(n); i++)
#define FOREQ(i, s, n) for (int i = (s); i <= (int)(n); i++)
#define FORIT(it, c) for (__typeof((c).begin())it = (c).begin(); it != (c).end(); it++)
#define MEMSET(v, h) memset((v), h, sizeof(v))

#include <complex>

typedef complex<double> Point;
typedef vector<Point> Polygon;

static const double INF = 1e+10;

#define CURR(P, i) (P[i])
#define NEXT(P, i) (P[(i + 1) % P.size()])

struct Line : public vector<Point> {
  Line() {;}
  Line(Point a, Point b) { push_back(a); push_back(b); }
};

struct Circle {
  Point p;
  double r;
  Circle() {;}
  Circle(Point p, double r) : p(p), r(r) {;}
};

namespace std {
  bool operator<(const Point &lhs, const Point &rhs) {
    return lhs.real() == rhs.real() ? lhs.imag() < rhs.imag() : lhs.real() < rhs.real();
  }
}

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

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

inline int ccw(Point a, Point b, Point 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 intersectLL(const Line &l, const Line &m) {
  return abs(cross(l[1] - l[0], m[1] - m[0])) > EPS ||
         abs(cross(l[1] - l[0], m[0] - l[0])) < EPS;
}

bool intersectLS(const Line &l, const Line &s) {
  return cross(l[1] - l[0], s[0] - l[0]) *
         cross(l[1] - l[0], s[1] - l[0]) < EPS;
}

bool intersectLP(const Line &l, const Point &p) {
  return abs(cross(l[1] - p, l[0] - p)) < EPS;
}

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;
}

bool intersectSP(const Line &s, const Point &p) {
  return abs(s[0] - p) + abs(s[1] - p) - abs(s[1] - s[0]) < EPS;
}

bool intersectSPolygon(const Line &l, const Polygon &P) {
  for (int i = 0; i < (int)P.size(); i++) {
    Point A = CURR(P, i), B = NEXT(P, i);
    if (intersectSS(l, Line(A, B))) { return true; }
  }
  return false;
}


enum { OUT, ON, IN };
int Contains(const Polygon& P, const Point& p) {
  bool in = false;
  for (int i = 0; i < (int)P.size(); ++i) {
    Point a = CURR(P,i) - p, b = NEXT(P,i) - p;
    if (imag(a) > imag(b)) swap(a, b);
    if (imag(a) <= 0 && 0 < imag(b))
      if (cross(a, b) < 0) in = !in;
    if (cross(a, b) == 0 && dot(a, b) <= 0) return ON;
  }
  return in ? IN : OUT;
}

bool intersectPP(const Polygon &P1, const Polygon &P2) {
  for (int i = 0; i < (int)P1.size(); i++) {
    Point A = CURR(P1, i), B = NEXT(P1, i);
    if (intersectSPolygon(Line(A, B), P2)) { return true; }
  }
  return Contains(P1, P2[0]) || Contains(P2, P1[0]);
}

typedef vector<vector<int> > Graph;
Graph g;

bool visit[110];
void dfs(int from) {
  if (visit[from]) { return; }
  visit[from] = true;
  FORIT(it, g[from]) {
    dfs(*it);
  }
}

int main() {
  int n, test;
  while (scanf("%d", &test), test) {
    REP(iter, test) {
      scanf("%d", &n);
      g = Graph(n);
      vector<Polygon> polys;
      REP(i, n) {
        Polygon p;
        REP(j, 4) {
          double x, y;
          scanf("%lf %lf", &x, &y);
          p.push_back(Point(x, y));
        }
        reverse(p.begin(), p.end());
        polys.push_back(p);
      }
      REP(i, n) {
        REP(j, n) {
          if (i == j) { continue; }
          if (intersectPP(polys[i], polys[j])) {
            g[i].push_back(j);
          }
        }
      }
      MEMSET(visit, false);
      int ans = 0;
      REP(i, n) {
        if (visit[i]) { continue; }
        ans++;
        dfs(i);
      }
      printf("%d\n", ans);
    }
  }
}