import std.stdio;
import std.c.stdio;
import std.range;
import std.array;
import std.functional;
import std.algorithm;
import std.conv;
import std.container;
import std.math;
import std.numeric;
import std.string;
import std.c.string;
import std.regex;
import std.typecons;

class UnionFind {
    int[] parent;
    this(int size) {
        parent = new int[size];
        parent[] = -1;
    }
    int root(int x) {
        if (parent[x] == -1) return x;
        return parent[x] = root(parent[x]);
    }
    void merge(int x, int y) {
        x = root(x); y = root(y);
        if (x == y) return;
        parent[x] = y;
    }
    ulong[] roots() {
        ulong[] ret;
        foreach (i, p; parent) {
            if (p < 0) ret ~= i;
        }
        return ret;
    }
}

const EPS = 1e-8;

struct Point {
    double y, x;
    Point opBinary(string op)(in Point a) const {
        static if (op == "+" || op == "-") {
            return Point(mixin("y" ~ op ~ "a.y"),
                         mixin("x" ~ op ~ "a.x"));
        }
    }
    Point opBinary(string op)(double a) const {
        static if (op == "*" || op == "/") {
            return Point(mixin("y" ~ op ~ "a"),
                         mixin("x" ~ op ~ "a"));
        }
    }
}
double norm(in Point a) {
    return sqrt(a.x * a.x + a.y * a.y);
}
double dot(in Point a, in Point b) {
    return a.x * b.x + a.y * b.y;
}
double cross(in Point a, in Point b) {
    return a.x * b.y - a.y * b.x;
}
int CCW(Point a, Point b, Point c) {
    b = b - a; c = c - a;
    if (cross(b, c) > EPS) return +1;
    if (cross(b, c) < -EPS) return -1;
    if (dot(b, c) < 0) return +2;
    if (b.norm < c.norm) return -2;
    return 0;
}
 
struct Rect {
    Point[4] vs;
}
int next(int x) { return (x + 1) % 4; }
bool contains(in Rect a, in Point p) {
    int ccw = CCW(a.vs[0], a.vs[1], p);
    foreach (i; 0 .. 4) {
        if (ccw != CCW(a.vs[i], a.vs[i.next], p)) return false;
    }
    return true;
}
bool contains(in Rect a, in Rect b) {
    foreach (i; 0 .. 4) {
        if (!a.contains(b.vs[i])) {
            return false;
        }
    }
    return true;
}
bool intersect(in Point s1, in Point t1, in Point s2, in Point t2) {
    return CCW(s1, t1, s2) * CCW(s1, t1, t2) <= 0 &&
        CCW(s2, t2, s1) * CCW(s2, t2, t1) <= 0;
}
bool intersect(in Rect a, in Rect b) {
    foreach (i; 0 .. 4)
        foreach (j; 0 .. 4)
            if (intersect(a.vs[i], a.vs[i.next], b.vs[j], b.vs[j.next]))
                return true;
    return false;
}

int N;
void main() {
    Rect[] rs;

    bool solve() {
        scanf("%d\n", &N);
        if (N == 0) return false;
        foreach (i; 0 ..  N) {
            int M; scanf("%d\n", &M);
            rs = new Rect[M];
            foreach (j; 0 .. M) {
                foreach (k; 0 .. 4) {
                    int y, x; scanf("%d %d", &y, &x);
                    rs[j].vs[k] = Point(y, x);
                }
            }

            void F() {
                auto uf = new UnionFind(M);
                foreach (a; 0 .. M) {
                    foreach (b; 0 .. M) {
                        if (a == b) continue;
                        if (rs[a].contains(rs[b])) {
                            uf.merge(a, b);
                        }
                        if (rs[a].intersect(rs[b])) {
                            uf.merge(a, b);
                        }
                    }
                }
                writeln(uf.roots.length);
            }

            F;
        }
        return true;
    }

    while (solve) {}
}

Measured memory_bytes: 1104