#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <list>
#include <climits>
#include <string>
#include <stack>
#include <map>
#include <set>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <sstream>
#include <iomanip>
#define ALL(v) v.begin(), v.end()
#define REP(i, a, b) for(int i = a; i < b; i++)
#define REPD(i, a, b) for(int i = a; i > b; i--)
#define REPLL(i, a, b) for(ll i = a; i < b; i++)
#define FOR(i, a, b) for(int i = a; i <= b; i++)
#define FORD(i, a, b) for(int i = a; i >= b; i--)
#define FORLL(i, a, b) for(ll i = a; i <= b; i++)
#define INF 100001
#define vit vector<int>::iterator
#define sit set<int>::iterator
#define vi vector<int>
#define vpii vector<pii >
#define ll long long
#define ld long double
#define PB push_back
#define MP make_pair
#define pii pair<int, int>
#define pld pair<ld, ld>
#define st first
#define nd second
#define EPS 1e-9
#define PI acos(-1.0)
#define MAXN 1000
using namespace std;
int z, n, a, b;
struct point {
ld x, y;
point() { x = y = 0.0; }
point(ld _x, ld _y) : x(_x), y(_y) {}
};
struct vec {
ld x, y;
vec(ld _x, ld _y) : x(_x), y(_y) {}
};
ld cross(vec a, vec b) {
return a.x * b.y - a.y * b.x;
}
vec make_vec(point a, point b) { // convert 2 points to vector a->b
return vec(b.x - a.x, b.y - a.y);
}
vec scale(vec v, ld s) { // nonnegative s = [<1 .. 1 .. >1]
return vec(v.x * s, v.y * s);
}
point translate(point p, vec v) { // translate p according to v
return point(p.x + v.x , p.y + v.y);
}
ld dot(vec a, vec b) {
return (a.x * b.x + a.y * b.y);
}
ld norm_sq(vec v) {
return v.x * v.x + v.y * v.y;
}
ld ccw(point p, point q, point r) {
return cross(make_vec(p, q), make_vec(p, r)); // >=0 for collinear
}
ld dist(point p1, point p2) {
return (p1.x - p2.x)*(p1.x - p2.x) + (p1.y - p2.y)*(p1.y - p2.y);
}
struct line {
point a, b;
line() {}
line(point _a, point _b) : a(_a), b(_b) {
if(a.x > b.x) swap(a, b);
}
line get_perpendicular() const {
return line(point((-1.0)*a.y,a.x),point((-1.0)*b.y,b.x));
}
point get_point() const {
vec v = make_vec(a, point(0, 0));
return translate(b, v);
}
bool operator<(const line& l) const {
return ccw(point(0, 0), get_point(), l.get_point()) > EPS;
}
bool operator==(const line& l) const {
return fabs(ccw(point(0, 0), get_point(), l.get_point())) < EPS ;
}
};
ld cross(line a, line b) {
return cross(make_vec(a.a, a.b), make_vec(b.a, b.b));
}
map<pair<line, ld>, ll> m;
vector<line> lines;
ld dist_to_line(point p, point a, point b, point &c) {
// formula: c = a + u * ab
vec ap = make_vec(a, p), ab = make_vec(a, b);
ld u = dot(ap, ab) / norm_sq(ab);
c = translate(a, scale(ab, u)); // translate a to c
return dist(p, c);
}
ld get_dist(int i, int j) {
point c;
return dist_to_line(lines[i].a, lines[j].a, lines[j].b, c);
}
int main()
{
ios_base::sync_with_stdio(0);
cin >> z;
while(z--) {
m.clear();
cin >> n;
lines.clear();
REP(i, 0, n) {
cin >> a >> b;
point p1(a, b);
cin >> a >> b;
point p2(a, b);
line l = line(p1, p2);
lines.PB(l);
}
REP(i, 0, lines.size()) REP(j, i+1, lines.size()) {
if(lines[i] == lines[j]) {
m[make_pair(lines[i], get_dist(i, j))]++;
}
}
ll result = 0;
for(map<pair<line, ld>, ll>::iterator it = m.begin(); it != m.end() ; it++) {
result += it->nd * m[MP(it->st.st.get_perpendicular(), it->st.nd)];
}
cout << result/2 << endl;
}
return 0;
}
I2luY2x1ZGUgPGlvc3RyZWFtPgojaW5jbHVkZSA8YWxnb3JpdGhtPgojaW5jbHVkZSA8dmVjdG9yPgojaW5jbHVkZSA8cXVldWU+CiNpbmNsdWRlIDxsaXN0PgojaW5jbHVkZSA8Y2xpbWl0cz4KI2luY2x1ZGUgPHN0cmluZz4KI2luY2x1ZGUgPHN0YWNrPgojaW5jbHVkZSA8bWFwPgojaW5jbHVkZSA8c2V0PgojaW5jbHVkZSA8Y21hdGg+CiNpbmNsdWRlIDxjc3RkaW8+CiNpbmNsdWRlIDxjc3RyaW5nPgojaW5jbHVkZSA8Y3N0ZGxpYj4KI2luY2x1ZGUgPHNzdHJlYW0+CiNpbmNsdWRlIDxpb21hbmlwPgoKI2RlZmluZSBBTEwodikgdi5iZWdpbigpLCB2LmVuZCgpCiNkZWZpbmUgUkVQKGksIGEsIGIpIGZvcihpbnQgaSA9IGE7IGkgPCBiOyBpKyspCiNkZWZpbmUgUkVQRChpLCBhLCBiKSBmb3IoaW50IGkgPSBhOyBpID4gYjsgaS0tKQojZGVmaW5lIFJFUExMKGksIGEsIGIpIGZvcihsbCBpID0gYTsgaSA8IGI7IGkrKykKI2RlZmluZSBGT1IoaSwgYSwgYikgZm9yKGludCBpID0gYTsgaSA8PSBiOyBpKyspCiNkZWZpbmUgRk9SRChpLCBhLCBiKSBmb3IoaW50IGkgPSBhOyBpID49IGI7IGktLSkKI2RlZmluZSBGT1JMTChpLCBhLCBiKSBmb3IobGwgaSA9IGE7IGkgPD0gYjsgaSsrKQojZGVmaW5lIElORiAxMDAwMDEKCiNkZWZpbmUgdml0IHZlY3RvcjxpbnQ+OjppdGVyYXRvcgojZGVmaW5lIHNpdCBzZXQ8aW50Pjo6aXRlcmF0b3IKI2RlZmluZSB2aSB2ZWN0b3I8aW50PgojZGVmaW5lIHZwaWkgdmVjdG9yPHBpaSA+CgojZGVmaW5lIGxsIGxvbmcgbG9uZwojZGVmaW5lIGxkIGxvbmcgZG91YmxlCgojZGVmaW5lIFBCIHB1c2hfYmFjawojZGVmaW5lIE1QIG1ha2VfcGFpcgojZGVmaW5lIHBpaSBwYWlyPGludCwgaW50PgojZGVmaW5lIHBsZCBwYWlyPGxkLCBsZD4KI2RlZmluZSBzdCBmaXJzdAojZGVmaW5lIG5kIHNlY29uZAoKI2RlZmluZSBFUFMgMWUtOQojZGVmaW5lIFBJIGFjb3MoLTEuMCkKI2RlZmluZSBNQVhOIDEwMDAKCnVzaW5nIG5hbWVzcGFjZSBzdGQ7CgppbnQgeiwgbiwgYSwgYjsKCnN0cnVjdCBwb2ludCB7CiAgICBsZCB4LCB5OwogICAgcG9pbnQoKSB7IHggPSB5ID0gMC4wOyB9CiAgICBwb2ludChsZCBfeCwgbGQgX3kpIDogeChfeCksIHkoX3kpIHt9Cn07CgpzdHJ1Y3QgdmVjIHsKICAgIGxkIHgsIHk7CiAgICB2ZWMobGQgX3gsIGxkIF95KSA6IHgoX3gpLCB5KF95KSB7fQp9OwoKbGQgY3Jvc3ModmVjIGEsIHZlYyBiKSB7CiAgICByZXR1cm4gYS54ICogYi55IC0gYS55ICogYi54Owp9Cgp2ZWMgbWFrZV92ZWMocG9pbnQgYSwgcG9pbnQgYikgeyAgICAgICAvLyBjb252ZXJ0IDIgcG9pbnRzIHRvIHZlY3RvciBhLT5iCiAgICByZXR1cm4gdmVjKGIueCAtIGEueCwgYi55IC0gYS55KTsKfQoKdmVjIHNjYWxlKHZlYyB2LCBsZCBzKSB7ICAgICAgICAvLyBub25uZWdhdGl2ZSBzID0gWzwxIC4uIDEgLi4gPjFdCiAgICByZXR1cm4gdmVjKHYueCAqIHMsIHYueSAqIHMpOwp9Cgpwb2ludCB0cmFuc2xhdGUocG9pbnQgcCwgdmVjIHYpIHsgICAgICAgIC8vIHRyYW5zbGF0ZSBwIGFjY29yZGluZyB0byB2CiAgICByZXR1cm4gcG9pbnQocC54ICsgdi54ICwgcC55ICsgdi55KTsKfQoKbGQgZG90KHZlYyBhLCB2ZWMgYikgewogICAgcmV0dXJuIChhLnggKiBiLnggKyBhLnkgKiBiLnkpOwp9CgpsZCBub3JtX3NxKHZlYyB2KSB7CiAgICByZXR1cm4gdi54ICogdi54ICsgdi55ICogdi55Owp9CgoKbGQgY2N3KHBvaW50IHAsIHBvaW50IHEsIHBvaW50IHIpIHsKICAgIHJldHVybiBjcm9zcyhtYWtlX3ZlYyhwLCBxKSwgbWFrZV92ZWMocCwgcikpOyAvLyA+PTAgZm9yIGNvbGxpbmVhcgp9CgpsZCBkaXN0KHBvaW50IHAxLCBwb2ludCBwMikgewogICAgcmV0dXJuIChwMS54IC0gcDIueCkqKHAxLnggLSBwMi54KSArIChwMS55IC0gcDIueSkqKHAxLnkgLSBwMi55KTsKfQoKc3RydWN0IGxpbmUgewogICAgcG9pbnQgYSwgYjsKICAgIGxpbmUoKSB7fQogICAgbGluZShwb2ludCBfYSwgcG9pbnQgX2IpIDogYShfYSksIGIoX2IpIHsKICAgICAgICBpZihhLnggPiBiLngpIHN3YXAoYSwgYik7CiAgICB9CgogICAgbGluZSBnZXRfcGVycGVuZGljdWxhcigpIGNvbnN0IHsKICAgICAgICByZXR1cm4gbGluZShwb2ludCgoLTEuMCkqYS55LGEueCkscG9pbnQoKC0xLjApKmIueSxiLngpKTsKICAgIH0KCiAgICBwb2ludCBnZXRfcG9pbnQoKSBjb25zdCB7CiAgICAgICAgdmVjIHYgPSBtYWtlX3ZlYyhhLCBwb2ludCgwLCAwKSk7CiAgICAgICAgcmV0dXJuIHRyYW5zbGF0ZShiLCB2KTsKICAgIH0KCiAgICBib29sIG9wZXJhdG9yPChjb25zdCBsaW5lJiBsKSBjb25zdCB7CiAgICAgICAgcmV0dXJuIGNjdyhwb2ludCgwLCAwKSwgZ2V0X3BvaW50KCksIGwuZ2V0X3BvaW50KCkpID4gRVBTOwogICAgfQoKICAgIGJvb2wgb3BlcmF0b3I9PShjb25zdCBsaW5lJiBsKSBjb25zdCB7CiAgICAgICAgcmV0dXJuIGZhYnMoY2N3KHBvaW50KDAsIDApLCBnZXRfcG9pbnQoKSwgbC5nZXRfcG9pbnQoKSkpIDwgRVBTIDsKICAgIH0KfTsKCmxkIGNyb3NzKGxpbmUgYSwgbGluZSBiKSAgewogICAgcmV0dXJuIGNyb3NzKG1ha2VfdmVjKGEuYSwgYS5iKSwgbWFrZV92ZWMoYi5hLCBiLmIpKTsKfQoKCm1hcDxwYWlyPGxpbmUsIGxkPiwgbGw+IG07CnZlY3RvcjxsaW5lPiBsaW5lczsKCmxkIGRpc3RfdG9fbGluZShwb2ludCBwLCBwb2ludCBhLCBwb2ludCBiLCBwb2ludCAmYykgewogIC8vIGZvcm11bGE6IGMgPSBhICsgdSAqIGFiCiAgICB2ZWMgYXAgPSBtYWtlX3ZlYyhhLCBwKSwgYWIgPSBtYWtlX3ZlYyhhLCBiKTsKICAgIGxkIHUgPSBkb3QoYXAsIGFiKSAvIG5vcm1fc3EoYWIpOwogICAgYyA9IHRyYW5zbGF0ZShhLCBzY2FsZShhYiwgdSkpOyAgICAgICAgICAgICAgICAgIC8vIHRyYW5zbGF0ZSBhIHRvIGMKICAgIHJldHVybiBkaXN0KHAsIGMpOwp9CgpsZCBnZXRfZGlzdChpbnQgaSwgaW50IGopIHsKICAgIHBvaW50IGM7CiAgICByZXR1cm4gZGlzdF90b19saW5lKGxpbmVzW2ldLmEsIGxpbmVzW2pdLmEsIGxpbmVzW2pdLmIsIGMpOwp9CgoKaW50IG1haW4oKQp7CiAgICBpb3NfYmFzZTo6c3luY193aXRoX3N0ZGlvKDApOwogICAgY2luID4+IHo7CgogICAgd2hpbGUoei0tKSB7CiAgICAgICAgbS5jbGVhcigpOwogICAgICAgIGNpbiA+PiBuOwogICAgICAgIGxpbmVzLmNsZWFyKCk7CiAgICAgICAgUkVQKGksIDAsIG4pIHsKICAgICAgICAgICAgY2luID4+IGEgPj4gYjsKICAgICAgICAgICAgcG9pbnQgcDEoYSwgYik7CgogICAgICAgICAgICBjaW4gPj4gYSA+PiBiOwogICAgICAgICAgICBwb2ludCBwMihhLCBiKTsKCiAgICAgICAgICAgIGxpbmUgbCA9IGxpbmUocDEsIHAyKTsKICAgICAgICAgICAgbGluZXMuUEIobCk7CiAgICAgICAgfQogICAgICAgIFJFUChpLCAwLCBsaW5lcy5zaXplKCkpIFJFUChqLCBpKzEsIGxpbmVzLnNpemUoKSkgewogICAgICAgICAgICBpZihsaW5lc1tpXSA9PSBsaW5lc1tqXSkgewogICAgICAgICAgICAgICAgbVttYWtlX3BhaXIobGluZXNbaV0sIGdldF9kaXN0KGksIGopKV0rKzsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICBsbCByZXN1bHQgPSAwOwogICAgICAgIGZvcihtYXA8cGFpcjxsaW5lLCBsZD4sIGxsPjo6aXRlcmF0b3IgaXQgPSBtLmJlZ2luKCk7IGl0ICE9IG0uZW5kKCkgOyBpdCsrKSB7CiAgICAgICAgICAgIHJlc3VsdCArPSBpdC0+bmQgKiBtW01QKGl0LT5zdC5zdC5nZXRfcGVycGVuZGljdWxhcigpLCBpdC0+c3QubmQpXTsKICAgICAgICB9CiAgICAgICAgY291dCA8PCByZXN1bHQvMiA8PCBlbmRsOwogICAgfQogICAgcmV0dXJuIDA7Cn0=