#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> // 包含 tree、gp_hash_table、cc_hash_table
#include <ext/pb_ds/priority_queue.hpp> // 引入后使用 STL 的堆需要 std::
#include <ext/rope>
using namespace __gnu_pbds;
using namespace __gnu_cxx;
using namespace std;
template<class key, class val = null_type, class cmp = less<>>
using rb_tree = tree<key, val, cmp, rb_tree_tag, tree_order_statistics_node_update>;
template<class key, class cmp = less<>>
using pairing_heap = __gnu_pbds::priority_queue<key, cmp>;
typedef long long ll;
#define int long long
#define debug(...) (cerr << #__VA_ARGS__ << " = ", ((cerr << __VA_ARGS__ << ", "), ...), cerr << "\n")
mt19937_64 rd(time(0));
template <class type>
struct point : array<type, 3> {
using p = array<type, 3>;
type operator / (const point & t) const { return p::at(0) * t[0] + p::at(1) * t[1] + p::at(2) * t[2]; }
point operator * (const point & t) const { return {p::at(1) * t[2] - p::at(2) * t[1], p::at(2) * t[0] - p::at(0) * t[2], p::at(0) * t[1] - p::at(1) * t[0]}; }
point operator + (const point & t) const { return point(*this) += t; }
point operator - (const point & t) const { return point(*this) -= t; }
point operator * (const type & t) const { return point(*this) *= t; }
point operator / (const type & t) const { return point(*this) /= t; }
point & operator += (const point & t) { return *this = {p::at(0) + t[0], p::at(1) + t[1], p::at(2) + t[2]}; }
point & operator -= (const point & t) { return *this = {p::at(0) - t[0], p::at(1) - t[1], p::at(2) - t[2]}; }
point & operator *= (const type & t) { return *this = {p::at(0) * t, p::at(1) * t, p::at(2) * t}; }
point & operator /= (const type & t) { return *this = {p::at(0) / t, p::at(1) / t, p::at(2) / t}; }
type sqrlen() const { return *this / *this; }
type len() const { return sqrt(sqrlen()); }
void shake(type eps = 1e-12) { // 微小扰动,去掉四点共面
uniform_real_distribution<type> dist(-0.5, 0.5);
for (auto & t : *this) {
t += dist(rd) * eps;
}
}
struct line : array<point, 2> {
using p = array<point, 2>; // 0 -> 1
point v() { return p::at(1) - p::at(0); }
point pos(const point & t) { return v() * (t - p::at(0)); } // sqrlen() == 0 在直线上
point pos(const line & t) { return v() * t.v(); } // sqrlen() == 0 平行
type dir(const line & t) { return v() / t.v(); } // == 0 垂直,> 0 同向
type dis_to_line(const point & t) { return pos(t).len() / v().len(); }
bool equal(const line & t) { return dir(t) > 0 && pos(t[0]).sqrlen() == 0; }
point intersect(const line & t) { // 需要保证不平行,如果直线异面,返回的是俩直线的垂线在 *this 直线上的垂足
return p::at(0) + v() * (t.pos(p::at(0)) / pos(t)) / pos(t).sqrlen();
}
};
struct face : array<point, 3> {
using p = array<point, 3>; // 从上方看逆时针 0 -> 1 - > 2
point v() { return (p::at(1) - p::at(0)) * (p::at(2) - p::at(0)); }
type pos(const point & t) { return v() / (t - p::at(0)); } // > 0 严格在上方,== 0 在面上
type pos(const line & t) { return v() / t.v(); } // == 0 平行
point pos(const face & t) { return v() * t.v(); } // sqrlen() == 0 平行
point dir(const line & t) { return v() * t.v(); } // sqrlen() == 0 垂直
type dir(const face & t) { return v() / t.v(); } // == 0 垂直, > 0 同向
type dis_to_face(const point & t) { return pos(t) / v().len(); } // 在法向量上的投影
bool equal(const face & t) { return dir(t) > 0 && pos(t).len() == 0 && pos(t[0]) == 0; } // 面是否相等
type volume(const point & t) { return (p::at(0) - t) * (at(1) - t) / (at(2) - t); }
};
struct hull : vector<face> {
using f = vector<face>;
hull(vector<point> t = {}) {
if (t.size() < 4) return; // 不允许四点共面
shuffle(t.begin(), t.end(), rd), f::push_back({t[0], t[1], t[2]}), f::push_back({t[2], t[1], t[0]});
for (int i = 3; i < t.size(); i++) {
hull tmp;
set<pair<point, point>> edge;
for (auto & x : *this) {
if (x.pos(t[i]) < 0) tmp.emplace_back(x);
else edge.emplace(x[0], x[1]), edge.emplace(x[1], x[2]), edge.emplace(x[2], x[0]);
}
for (auto & [x, y] : edge) {
if (!edge.count({y, x})) tmp.push_back({x, y, t[i]});
}
swap(*this, tmp);
}
}
template <class ret = type> // 2 * 表面积
ret area(ret t = 0) { return accumulate(f::begin(), f::end(), t, [](ret t, auto & x) { return t + x.v().len(); }); }
template <class ret = type> // 6 * 体积
ret volume(ret t = 0) { return accumulate(f::begin(), f::end(), t, [](ret t, auto & x) { return t + x.volume({0, 0, 0}); }); }
point centroid(point G = {}, type sum = 0) { // 重心
for (auto & x : *this) {
auto ng = x[0] + x[1] + x[2];
type nv = x[0] * x[1] / x[2];
sum -= nv;
G -= ng * nv;
}
return G / 4 / sum;
}
};
};
void output(__int128 x) {
if(x == 0) {
return;
}
output(x / 10);
cout << (int)(x % 10);
}
void QAQ() {
int n;
cin >> n;
vector<point<int>> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i][0] >> a[i][1] >> a[i][2];
}
__int128 ans = 0;
while (a.size() > 3) {
point<int>::hull res(a);
set<point<int>> s;
ans = res.volume(ans);
for (auto & x : res) {
for (auto & idx : x) {
s.emplace(idx);
}
}
vector<point<int>> ta;
for (int i = 0; i < a.size(); i++) {
if (!s.count(a[i])) {
ta.push_back(a[i]);
}
}
swap(a, ta);
}
output(ans);
cout << "\n";
}
signed main() {
cin.tie(0) -> sync_with_stdio(0);
cout << fixed << setprecision(12);
int t = 1;
cin >> t;
while (t--) {
QAQ();
}
}