#include <iostream>
#include <sstream>
#include <iomanip>
#include <algorithm>
#include <cmath>
#include <string>
#include <vector>
#include <list>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <bitset>
#include <numeric>
#include <climits>
#include <cfloat>
using namespace std;

class UnionFindTree
{
    vector<int> parent; // em[h
    vector<int> rank;   // ØÌ³ÌãÀ
    int find(int x){
        if(parent[x] == x)
            return x;
        return parent[x] = find(parent[x]);
    }
public:
    UnionFindTree(int n){ // RXgN^
        parent.resize(n);
        for(int i=0; i<n; ++i)
            parent[i] = i;
        rank.assign(n, 0);
    }
    void unite(int x, int y){ // xÆyÌO[vð¹
        if((x = find(x)) != (y = find(y))){
            if(rank[x] < rank[y])
                parent[x] = y;
            else{
                parent[y] = x;
                if(rank[x] == rank[y])
                    ++ rank[x];
            }
        }
    }
    bool same(int x, int y){ // xÆyÌO[vª¯¶©ð²×é
        return find(x) == find(y);
    }
    int num(){ // O[vÌðÔ·
        vector<bool> root(parent.size(), false);
        for(unsigned i=0; i<parent.size(); ++i)
            root[find(i)] = true;
        return count(root.begin(), root.end(), true);
    }
};

class Point
{
public:
    int y, x;
    Point(){
        y = x = 0;
    }
    Point(int y0, int x0){
        y = y0;
        x = x0;
    }
    int dot(const Point& p) const{
        return y * p.y + x * p.x; // |a|*|b|*cosÆ
    }
    int cross(const Point& p) const{
        return x * p.y - y * p.x; // |a|*|b|*sinÆ
    }
    Point operator+(const Point& p) const{
        return Point(y + p.y, x + p.x);
    }
    Point operator-(const Point& p) const{
        return Point(y - p.y, x - p.x);
    }
};

/***************************************************************************************************/
// _ªÊ½p`Ìà(ÓãàÜÞ)É é©²×é
// cp : Ê½p`
// p  : _
/***************************************************************************************************/
bool convexContain(const vector<Point>& cp, const Point& p)
{
    int n = cp.size();
    bool ret1 = true;
    bool ret2 = true;
    for(int i=0; i<n; ++i){
        int a = (cp[(i+1)%n] - cp[i]).cross(p - cp[i]);
        if(a < 0)
            ret1 = false;
        if(a > 0)
            ret2 = false;
    }
    return ret1 || ret2;
}

/***************************************************************************************************/
// 2ÂÌüªÌÕË»è
// a1, a2 : 1ÂÚÌüªÌ[_
// b1, b2 : 2ÂÚÌüªÌ[_
/***************************************************************************************************/
bool linesCollide(const Point& a1, const Point& a2, const Point& b1, const Point& b2)
{
    if(min(a1.x, a2.x) > max(b1.x, b2.x) || min(b1.x, b2.x) > max(a1.x, a2.x) || min(a1.y, a2.y) > max(b1.y, b2.y) || min(b1.y, b2.y) > max(a1.y, a2.y))
        return false;

    return ((a2-a1).cross(b1-a1) * (a2-a1).cross(b2-a1) <= 0) && ((b2-b1).cross(a1-b1) * (b2-b1).cross(a2-b1) <= 0);
}

/***************************************************************************************************/
// 2ÂÌÊ½p`ÌÕË»è
// cp1 : 1ÂÚÌÊ½p`
// cp2 : 2ÂÚÌÊ½p`
/***************************************************************************************************/
bool polygonsCollide(const vector<Point>& cp1, const vector<Point>& cp2)
{
    int n = cp1.size();
    int m = cp2.size();
    for(int i=0; i<n; ++i){
        for(int j=0; j<m; ++j){
            if(linesCollide(cp1[i], cp1[(i+1)%n], cp2[j], cp2[(j+1)%m]))
                return true;
        }
    }

    return convexContain(cp1, cp2[0]) || convexContain(cp2, cp1[0]);
}

int main()
{
    for(;;){
        int n;
        cin >> n;
        if(n == 0)
            return 0;

        while(--n >= 0){
            int m;
            cin >> m;

            vector<vector<Point> > p(m, vector<Point>(4));
            UnionFindTree uft(m);
            for(int i=0; i<m; ++i){
                for(int j=0; j<4; ++j)
                    cin >> p[i][j].x >> p[i][j].y;

                for(int j=0; j<i; ++j){
                    if(polygonsCollide(p[i], p[j]))
                        uft.unite(i, j);
                }
            }

            cout << uft.num() << endl;
        }
    }
}

Measured memory_bytes: 0