#pragma GCC optimize("Ofast")
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#include <iostream>
#include <cmath>
#include <algorithm>
#include <iomanip>
#include <numeric>
#include <vector>
#include <string>
#include <set>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <stack>
#include <bitset>
#include <random>
#include <queue>
#include "triangles.h"
#include "guess.h"
#include <cstdint>
using namespace std;
#pragma GCC target("avx,avx2,tune=native,sse,sse2")
#define int long long

const int mod = 998244353;

/*struct Triangle {
	int i, j, k;
	Triangle() = default;
	Triangle(int i_, int j_, int k_) : i(i_), j(j_), k(k_) {}
};

namespace {
	constexpr int kMaxQ = 2'000'000;
	int verbose = 0;

	void WA(const string& msg) {
		cout << "WA " << msg << "\n";
		exit(0);
	}

	int n, group;
	vector<pair<int64_t, int64_t>> points;
	uint64_t cur_queries;

	void ValidateTriangle(const Triangle& t) {
		if (t.i < 0 || t.i >= n) {
			WA("triangle i is out of range [0,n-1]");
		}
		if (t.j < 0 || t.j >= n) {
			WA("triangle j is out of range [0,n-1]");
		}
		if (t.k < 0 || t.k >= n) {
			WA("triangle k is out of range [0,n-1]");
		}
		if (t.i == t.j) {
			WA("triangle i and j are equal");
		}
		if (t.i == t.k) {
			WA("triangle i and k are equal");
		}
		if (t.j == t.k) {
			WA("triangle j and k are equal");
		}
	}

	uint64_t GetDoubleArea(const Triangle& t) {
		pair<int64_t, int64_t> v1 = { points[t.j].first - points[t.i].first, points[t.j].second - points[t.i].second };
		pair<int64_t, int64_t> v2 = { points[t.k].first - points[t.i].first, points[t.k].second - points[t.i].second };
		return abs(v1.first * v2.second - v1.second * v2.first);
	}

	void LogCompare(const vector<Triangle>& left, const vector<Triangle>& right) {
		cout << "compare #" << cur_queries << ":\n";
		cout << "left " << left.size() << " triangle" << (left.size() > 1 ? "s" : "") << ": ";
		for (size_t i = 0; i < left.size(); ++i) {
			cout << (i == 0 ? "(" : ", (") << left[i].i << "," << left[i].j << "," << left[i].k << ")";
		}
		cout << "\n";
		cout << "right " << right.size() << " triangle" << (right.size() > 1 ? "s" : "") << ": ";
		for (size_t i = 0; i < left.size(); ++i) {
			cout << (i == 0 ? "(" : ", (") << right[i].i << "," << right[i].j << "," << right[i].k << ")";
		}
		cout << "\n";
	}
}

int compare(const vector<Triangle>& left, const vector<Triangle>& right) {
	cur_queries++;
	if (cur_queries > kMaxQ) {
		WA("too many queries");
	}
	if (verbose > 0) {
		LogCompare(left, right);
	}
	if (left.empty()) {
		WA("left should be non-empty");
	}
	if (right.empty()) {
		WA("right should be non-empty");
	}
	if (left.size() + right.size() > 4) {
		WA("the number of triangles should be at most 4");
	}
	for (const auto& t : left) {
		ValidateTriangle(t);
	}
	for (const auto& t : right) {
		ValidateTriangle(t);
	}

	uint64_t left_area = 0;
	for (const auto& t : left) {
		left_area += GetDoubleArea(t);
	}
	uint64_t right_area = 0;
	for (const auto& t : right) {
		right_area += GetDoubleArea(t);
	}

	if (verbose > 0) {
		cout << "result: ";
		if (left_area < right_area) {
			cout << "left is smaller (-1)\n";
		}
		else if (left_area == right_area) {
			cout << "equal (0)\n";
		}
		else {
			cout << "right is smaller (1)\n";
		}
	}

	return left_area < right_area ? -1 : (left_area > right_area ? 1 : 0);
}*/

inline bool vsuprun() {
	static int it = 0;
	static bool done = false;
	if (it++ == 5) {
		it = 0;
		if (1.0 * clock() / CLOCKS_PER_SEC > 0.9) {
			done = true;
		}
	}
	return done;
}

int binpow(int a, int b) {
	if (b == 0) {
		return 1;
	}
	int t = binpow(a, b / 2);
	if (b % 2 == 0) {
		return (t * t) % mod;
	}
	return (((t * t) % mod) * a) % mod;
}

struct SegmentTree {
	struct Node {
		int sum = 0;
	};

	Node combine(Node a, Node b) {
		return { a.sum + b.sum };
	}

	vector<Node> tree;
	int size = 0;

	void init(int n) {
		size = 1;
		while (size <= n) {
			size *= 2;
		}
		tree.assign(size * 2 - 1, Node());
	}

	void build(vector<int>& a) {
		int n = static_cast<int>(a.size());
		init(n);
		for (int i = 0; i < n; ++i) {
			tree[i + size - 1] = { a[i] };
		}
		for (int i = size - 2; i >= 0; --i) {
			tree[i] = combine(tree[i * 2 + 1], tree[i * 2 + 2]);
		}
	}

	void set(int i, int v, int x, int lx, int rx) {
		if (rx - lx == 1) {
			tree[x] = { v };
			return;
		}
		int mid = (lx + rx) / 2;
		if (i < mid) {
			set(i, v, x * 2 + 1, lx, mid);
		}
		else {
			set(i, v, x * 2 + 2, mid, rx);
		}
		tree[x] = combine(tree[x * 2 + 1], tree[x * 2 + 2]);
	}

	void set(int i, int v) {
		set(i, v, 0, 0, size);
	}

	Node get(int l, int r, int x, int lx, int rx) {
		if (lx >= l && rx <= r) {
			return tree[x];
		}
		if (lx >= r || rx <= l) {
			return Node();
		}
		int mid = (lx + rx) / 2;
		return combine(get(l, r, x * 2 + 1, lx, mid), get(l, r, x * 2 + 2, mid, rx));
	}

	int get(int l, int r) {
		return get(l, r, 0, 0, size).sum;
	}
};

vector<vector<int>> solve(int n) {
	if (n == 4) {
		bool fl0 = 0, fl1 = 0, fl2 = 0, fl3 = 0;
		vector<Triangle> left;
		vector<Triangle> right;
		left.push_back(Triangle(0, 1, 2));
		left.push_back(Triangle(0, 1, 3));
		left.push_back(Triangle(0, 2, 3));
		right.push_back(Triangle(1, 2, 3));
		int c = compare(left, right);
		if (c == 0) {
			fl0 = 1;
		}
		left.clear();
		right.clear();

		left.push_back(Triangle(1, 2, 0));
		left.push_back(Triangle(1, 3, 0));
		left.push_back(Triangle(1, 2, 3));
		right.push_back(Triangle(0, 2, 3));
		c = compare(left, right);
		if (c == 0) {
			fl1 = 1;
		}
		left.clear();
		right.clear();

		left.push_back(Triangle(2, 1, 0));
		left.push_back(Triangle(2, 3, 0));
		left.push_back(Triangle(2, 3, 1));
		right.push_back(Triangle(0, 1, 3));
		c = compare(left, right);
		if (c == 0) {
			fl2 = 1;
		}
		left.clear();
		right.clear();

		left.push_back(Triangle(3, 0, 1));
		left.push_back(Triangle(3, 0, 2));
		left.push_back(Triangle(3, 1, 2));
		right.push_back(Triangle(0, 1, 2));
		c = compare(left, right);
		if (c == 0) {
			fl3 = 1;
		}
		left.clear();
		right.clear();

		if (fl0 == 1) {
			vector<vector<int>> ans;
			vector<int> h = { 1, 2, 3 };
			ans.push_back(h);
			return ans;
		}
		if (fl1 == 1) {
			vector<vector<int>> ans;
			vector<int> h = { 0, 2, 3 };
			ans.push_back(h);
			return ans;
		}
		if (fl2 == 1) {
			vector<vector<int>> ans;
			vector<int> h = { 0, 1, 3 };
			ans.push_back(h);
			return ans;
		}
		if (fl3 == 1) {
			vector<vector<int>> ans;
			vector<int> h = { 0, 1, 2 };
			ans.push_back(h);
			return ans;
		}
		vector<vector<int>> ans;
		ans.push_back({ 0, 1, 2 });
		ans.push_back({ 0, 2, 3 });
		return ans;
	}
}

/*signed main() {
	cin >> n;
	points.resize(n);
	for (int i = 0; i < n; ++i) {
		cin >> points[i].first >> points[i].second;
	}
	cur_queries = 0;

	auto result = solve(n);

	cout << result.size() << "\n";
	for (const auto& polygon : result) {
		cout << polygon.size() << "\n";
		for (size_t i = 0; i < polygon.size(); ++i) {
			cout << polygon[i] << " \n"[i + 1 == polygon.size()];
		}
	}
	if (verbose > 0) {
		cout << cur_queries << (cur_queries == 1 ? " query\n" : " queries\n");
	}
	return 0;
}*/