#include using namespace std; const int MAXN = (int) 52; const int INF = (int) 1e6; const int debug = false; typedef long long T; struct POINT { T x, y; POINT() {} POINT(T x, T y) : x(x), y(y) {} bool operator<(const POINT &rhs) const { return make_pair(y,x) < make_pair(rhs.y,rhs.x); } bool operator==(const POINT &rhs) const { return make_pair(y,x) == make_pair(rhs.y,rhs.x); } }; T cross(POINT p, POINT q) { return p.x*q.y-p.y*q.x; } T area2(POINT a, POINT b, POINT c) { return cross(a,b) + cross(b,c) + cross(c,a); } void ConvexHull(vector &pts) { if (pts.size() <= 3) { return; } sort(pts.begin(), pts.end()); pts.erase(unique(pts.begin(), pts.end()), pts.end()); vector up, dn; for (int i = 0; i < pts.size(); i++) { while (up.size() > 1 && area2(up[up.size()-2], up.back(), pts[i]) >= 0) up.pop_back(); while (dn.size() > 1 && area2(dn[dn.size()-2], dn.back(), pts[i]) <= 0) dn.pop_back(); up.push_back(pts[i]); dn.push_back(pts[i]); } pts = dn; for (int i = (int) up.size() - 2; i >= 1; i--) pts.push_back(up[i]); } const int mod = (int) 1e9 + 7; int n, m, totalWeightInside[MAXN][MAXN][MAXN], color[MAXN], totalLineWeight[MAXN][MAXN]; struct point { int x, y; point(int x, int y) : x(x), y(y) {} }; vector a, b; long long area(point a, point b, point c) { return a.x * (long long) (b.y - c.y) + b.x * (long long) (c.y - a.y) + c.x * (long long) (a.y - b.y); } int liesInside(point d, point a, point b, point c) { long long A = area(a, b, c); long long A1 = area(a, b, d); long long A2 = area(a, c, d); long long A3 = area(b, c, d); return (abs(A) == abs(A1) + abs(A2) + abs(A3)); } int liesLeftSide(point c, point a, point b) { return area(a, b, c) > 0; } int liesOnSegment(point c, point a, point b) { if (c.x >= min(a.x, b.x) && c.x <= max(a.x, b.x) && c.y >= min(a.y, b.y) && c.y <= max(a.y, b.y)) { return area(a, b, c) == 0; } return false; } void sortIt(vector &order, int idx) { for (int i = 0; i < order.size(); i++) { for (int j = i + 1; j < order.size(); j++) { int id1 = order[i]; int id2 = order[j]; if (liesLeftSide(a[id1], a[idx], a[id2])) { swap(order[i], order[j]); } } } } vector order; long long memo[MAXN][2 * MAXN][4]; const int DELTA = MAXN; int p1; // cur is last taken. long long dp(int cur, int diff, int taken) { //cerr << "cur " << cur << " diff " << " " << diff << " taken " << taken << endl; long long &res = memo[cur][diff + DELTA][taken]; if (res == -1) { res = 0; // don't take case. if (diff == 0 && taken >= 3) { res += 1; if (res >= mod) { res -= mod; } } // take it. for (int i = cur + 1; i < order.size(); i++) { int newDiff = diff; if (cur == 0) { int lineDiff = totalLineWeight[order[cur]][order[i]]; //cerr << "i " << i << " lineDiff " << lineDiff << endl; newDiff += lineDiff; } else { int triangleDiff = totalWeightInside[p1][order[cur]][order[i]]; //triangleDiff -= totalLineWeight[p1][p1]; int lineDiff = totalLineWeight[p1][order[cur]]; triangleDiff -= lineDiff; //triangleDiff += totalLineWeight[p1][p1]; newDiff += triangleDiff; //cerr << "i " << i << " triangleDiff " << triangleDiff << endl; assert(triangleDiff == color[order[i]] == 0 ? 1: -1); } int newTaken = taken + 1; if (newTaken >= 3) { newTaken = 3; } res += dp(i, newDiff, newTaken); } } return res; } void checkArePointsOnConvexHull() { vector c; for (int i = 0; i < n; i++) { c.push_back(POINT(a[i].x, a[i].y)); } ConvexHull(c); //cerr << "c.size() " << c.size() << endl; assert(c.size() == n); } long long C(int n, int r) { long long ans = 1; for (int i = 0; i < r; i++) { int num = n - i; int den = i + 1; int g = __gcd(num, den); num /= g; den /= g; ans *= num; ans /= den; } return ans; } int main() { /* for (int n = 1; n <= 10; n++) { for (int r = 1; r <= n; r++) { cout << C(n, r) << " "; } cout << endl; } cout << endl << endl; */ ios_base::sync_with_stdio(false); cin.tie(NULL); int T; cin >> T; assert(T >= 1 && T <= 20); while (T--) { cin >> n >> m; assert(n >= 1 && n <= 50); assert(m >= 1 && m <= 50); a.clear(); b.clear(); set > st; for (int i = 0; i < n; i++) { int x, y; cin >> x >> y; assert(x >= -INF && x <= INF); assert(y >= -INF && y <= INF); a.push_back(point(x, y)); st.insert(make_pair(x, y)); } // check all are distinct. assert(st.size() == n); checkArePointsOnConvexHull(); // select points lying above point p1 (i.e. having larger or equal y coordinates) p1 = 0; int minX = INF, minY = INF; for (int i = 0; i < n; i++) { if (a[i].x < minX) { p1 = i; minX = a[i].x; minY = a[i].y; } else if (a[i].x == minX) { if (a[i].y < minY) { p1 = i; minX = a[i].x; minY = a[i].y; } } } // Check anti clockwise condition int cur = p1; for (int i = 0; i < n; i++) { assert(liesLeftSide(a[(cur + 2) % n], a[cur], a[(cur + 1) % n])); } // Check non-collinearty condition. for (int i = 0; i < n; i++) for (int j = i + 1; j < n; j++) for (int k = j + 1; k < n; k++) assert(area(a[i], a[j], a[k]) != 0); int blue = 0; int red = 0; for (int i = 0; i < m; i++) { int x, y, col; cin >> x >> y >> col; assert(x >= -INF && x <= INF); assert(y >= -INF && y <= INF); assert(col >= 0 && col <= 1); b.push_back(point(x, y)); color[i] = col; if (col == 0) red++; else blue++; } long long bans = 0; for (int r = 2; r <= min(red, blue); r++) { //fprintf(stderr, "red=%d, blue=%d, r=%d\n", red, blue, r); bans = (bans + C(red, r) * (long long) C(blue, r)); } memset(totalWeightInside, 0, sizeof(totalWeightInside)); memset(totalLineWeight, 0, sizeof(totalLineWeight)); // calculate total weight in the triangles for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { for (int k = 0; k < n; k++) { if (i == j || i == k || j == k) { continue; } for (int l = 0; l < m; l++) { if (liesInside(b[l], a[i], a[j], a[k])) { totalWeightInside[i][j][k] += color[l] == 0 ? 1 : -1; } } } } } // calculate line weight for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { for (int l = 0; l < m; l++) { if (liesOnSegment(b[l], a[i], a[j])) { //printf("i=%d, j=%d, l=%d\n", i, j, l); totalLineWeight[i][j] += color[l] == 0 ? 1 : -1; } } //cout << totalLineWeight[i][j] << " "; } //cout << endl; } // check whether the points lie on convex hull condition long long ans = 0; for (p1 = 0; p1 < n; p1++) { //if (p1 > 0) break; //cerr << "p1 " << p1 << endl; // select points lying above point p1 (i.e. having larger or equal y coordinates) order.clear(); for (int j = 0; j < n; j++) { if (j != p1 && make_pair(a[j].y, a[j].x) > make_pair(a[p1].y, a[p1].x)) { order.push_back(j); } } sortIt(order, p1); //order.insert(order.begin(), p1); reverse(order.begin(), order.end()); order.push_back(p1); reverse(order.begin(), order.end()); /* cerr << "order.size() " << order.size() << endl; for (int x : order) { cerr << a[x].x << " " << a[x].y << endl; } cerr << endl; */ /* cerr << "order.size()" << endl; for (int x: order) cerr << x << " "; cerr << endl; */ memset(memo, -1, sizeof(memo)); long long add = dp(0, 0, 1); ans += add; //cerr << "added " << add << endl; //cerr << endl << endl; } printf("%lld\n", ans); if (ans != bans) { //cerr << "ans " << ans << endl; //cerr << "bans " << bans << endl; } //assert(ans == bans); } return 0; }