/*
* Language: Standard C++23 [-std=c++23]
* Author: Zang Vũ aka zvwgvx
* Github & Discord & Facebook: @zvwgvx
*/
#pragma GCC optimize("fast-math,O3")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("unroll-loops")
// #pragma GCC optimize("Ofast")
// #pragma GCC target("tune=native")
// #pragma GCC target("avx,avx2,fma")
#include <bits/stdc++.h>
using namespace std;
#define ENV defined(LOCAL)
#define el cout << '\n'
#define rt return
#define ll long long
#define ull unsigned ll
#define pii pair <int, int>
#define pll pair <ll, ll>
#define v vector
#define vi v <int>
#define vl v <ll>
#define vc v <char>
#define vvi v <v <int>>
#define vvl v <v <ll>>
#define mts multiset
#define mtm multimap
#define ump unordered_map
#define ust unordered_set
#define umts unordered_multiset
#define umtm unordered_multimap
#define priq priority_queue
template <typename T>
T fgcd(T a, T b) {
if (!a || !b) rt a | b;
unsigned shift = __builtin_ctzll(a | b);
a >>= __builtin_ctzll(a);
while (b) {
b >>= __builtin_ctzll(b);
if (a > b) swap(a, b);
b -= a;
}
rt a << shift;
}
template <typename T>
T fpow(T base, T exp, T mod) {
T res = 1;
for (; exp; exp >>= 1, base = base * base % mod)
if (exp & 1) res = res * base % mod;
rt res;
}
template <typename T> T lcm(T a, T b) { rt a * (b / fgcd(a, b)); }
template <typename T> void maximize(T& a, T b) { if (a < b) a = b; }
template <typename T> void minimize(T& a, T b) { if (a > b) a = b; }
template <typename T> double lg(T a, T b) { rt log(b) / log(a); }
template <typename T> ull P(T n, T k) { ull res = 1; for (int i = 0; i < k; ++i) res *= (n - i); rt res; }
template <typename T> ull C(T n, T k) { ull res = 1; for (int i = 1; i <= k; ++i) res = res * (n - i + 1) / i; rt res; }
const int MOD = 1e9 + 7;
const int INF = 1e9;
const int LIMIT = 1e6 + 5;
#if ENV
void open(const string& file) {
freopen((file + ".inp").c_str(), "r", stdin);
freopen((file + ".out").c_str(), "w", stdout);
}
auto start = clock();
void time() { cout << "\n\n[rt] " << 1.0 * (clock() - start) / CLOCKS_PER_SEC; }
#endif
struct Query {
int type, x, y, k, u, v, cx, cy;
};
inline int idx(int var, int val) {
rt 2 * var + val;
}
void add_e(vvi& graph, const Query& q) {
int literal = idx(q.cx, q.k);
int notl = idx(q.cx, 1 - q.k);
graph[notl].push_back(literal);
}
void add_e2(vvi& graph, const Query& q) {
int a = q.cx, b = q.cy;
if(q.k == 0) {
graph[idx(a, 0)].push_back(idx(b, 0));
graph[idx(b, 0)].push_back(idx(a, 0));
graph[idx(a, 1)].push_back(idx(b, 1));
graph[idx(b, 1)].push_back(idx(a, 1));
} else {
graph[idx(a, 0)].push_back(idx(b, 1));
graph[idx(b, 0)].push_back(idx(a, 1));
graph[idx(a, 1)].push_back(idx(b, 0));
graph[idx(b, 1)].push_back(idx(a, 0));
}
}
void add_e3(vvi& graph, const Query& q) {
int idx_a = idx(q.cx, q.u);
int idx_b = idx(q.cy, q.v);
int notA = idx(q.cx, 1 - q.u);
int notB = idx(q.cy, 1 - q.v);
graph[notA].push_back(idx_b);
graph[notB].push_back(idx_a);
}
vi findtopo(const vvi& graph) {
int nodes = graph.size();
vi order;
vector<bool> visited(nodes, false);
order.reserve(nodes);
for (int i = 0; i < nodes; ++i) {
if (!visited[i]) {
stack<pii> que;
que.push({i, 0});
visited[i] = true;
while(!que.empty()) {
auto &top = que.top();
int v = top.first;
int &it = top.second;
if(it < (int)graph[v].size()) {
int w = graph[v][it++];
if(!visited[w]) {
visited[w] = true;
que.push({w, 0});
}
} else {
order.push_back(v);
que.pop();
}
}
}
}
rt order;
}
vi finds(const vvi& graph, const vi& order) {
int nodes = graph.size();
vvi rgraph(nodes);
for (int v = 0; v < nodes; v++) {
for (int w : graph[v]) {
rgraph[w].push_back(v);
}
}
vi comp(nodes, -1);
int id = 0;
for (int i = nodes - 1; i >= 0; i--) {
int v = order[i];
if(comp[v] == -1) {
stack<int> st;
st.push(v);
comp[v] = id;
while(!st.empty()) {
int cur = st.top();
st.pop();
for (int nxt : rgraph[cur]) {
if(comp[nxt] == -1) {
comp[nxt] = id;
st.push(nxt);
}
}
}
id++;
}
}
rt comp;
}
bool valid(const v<Query>& queries, int mid, int m) {
int nodes = 2 * m;
vvi graph(nodes);
for (int i = 0; i < mid; ++i) {
const Query &q = queries[i];
if(q.type == 1) add_e(graph, q);
else if(q.type == 2) add_e2(graph, q);
else if(q.type == 3) add_e3(graph, q);
}
vi order = findtopo(graph);
vi comp = finds(graph, order);
for (int i = 0; i < m; ++i) {
if(comp[idx(i,0)] == comp[idx(i,1)])
rt 0;
}
rt 1;
}
signed main() {
cin.tie(nullptr), cout.tie(nullptr)
-> ios_base::sync_with_stdio(false);
#if ENV
open("main");
srand(time(nullptr));
#endif
int t;
cin >> t;
while(t--){
int n, q;
cin >> n >> q;
vector<Query> queries(q);
vi ids;
ids.reserve(2*q);
for (int i = 0; i < q; ++i){
int typ;
cin >> typ;
queries[i].type = typ;
if(typ == 1){
cin >> queries[i].x >> queries[i].k;
ids.push_back(queries[i].x);
} else if(typ == 2){
cin >> queries[i].x >> queries[i].y >> queries[i].k;
ids.push_back(queries[i].x);
ids.push_back(queries[i].y);
} else if(typ == 3){
cin >> queries[i].x >> queries[i].u >> queries[i].y >> queries[i].v;
ids.push_back(queries[i].x);
ids.push_back(queries[i].y);
}
}
sort(ids.begin(), ids.end());
ids.erase(unique(ids.begin(), ids.end()), ids.end());
int m = ids.size();
for (int i = 0; i < q; ++i){
if(queries[i].type == 1){
int pos = int(lower_bound(ids.begin(), ids.end(), queries[i].x) - ids.begin());
queries[i].cx = pos;
} else if(queries[i].type == 2){
int pos1 = int(lower_bound(ids.begin(), ids.end(), queries[i].x) - ids.begin());
int pos2 = int(lower_bound(ids.begin(), ids.end(), queries[i].y) - ids.begin());
queries[i].cx = pos1;
queries[i].cy = pos2;
} else if(queries[i].type == 3){
int pos1 = int(lower_bound(ids.begin(), ids.end(), queries[i].x) - ids.begin());
int pos2 = int(lower_bound(ids.begin(), ids.end(), queries[i].y) - ids.begin());
queries[i].cx = pos1;
queries[i].cy = pos2;
}
}
int left = 1, right = q + 1;
while (left < right){
int mid = left + (right - left) / 2;
if (valid(queries, mid, m)) left = mid + 1;
else right = mid;
}
if (left > q) cout << -1;
else cout << left;
el;
}
#if ENV
time();
#endif
rt 0;
}