#include<bits/stdc++.h>
#define rep(i, a, b) for (int i = (a); i <= (b); i++)
#define lop(i, a, b) for (int i = (a); i < (b); i++)
#define dwn(i, a, b) for (int i = (a); i >= (b); i--)
#define pr pair<int, int>
#define mp(a, b) make_pair(a, b)
#define pb push_back
#define iosfst ios::sync_with_stdio(false); cin.tie(0), cout.tie(0)
#define File(x) freopen(x".in", "r", stdin), freopen(x".out", "w", stdout)
using namespace std;
mt19937 Rnd(time(0));
namespace An{
const int N = 1e4 + 10;
int n, m, c, k;
map<pr, int> mp;
struct LCT{
bool fx[N];
int n, m, top, d[N];
struct FHQ_Treap{
int lsn, rsn, sum, pri, tag, fa, key, uni;
} tr[N];
void updateroot(int p) {
tr[p].sum = max(tr[tr[p].lsn].sum, max(tr[tr[p].rsn].sum, tr[p].key));
if (tr[p].lsn) tr[tr[p].lsn].fa = p;
if (tr[p].rsn) tr[tr[p].rsn].fa = p;
return;
}
bool isroot(int p) { return (tr[tr[p].fa].lsn != p && tr[tr[p].fa].rsn != p) || !tr[p].fa; }
void pushdown(int p) {
if (tr[p].tag) {
swap(tr[p].lsn, tr[p].rsn);
tr[tr[p].lsn].tag ^= 1, tr[tr[p].rsn].tag ^= 1;
tr[p].tag = 0;
}
return;
}
int merge(int L, int R) {
if (L == 0 || R == 0) return L + R;
if (tr[L].pri < tr[R].pri) {
pushdown(L);
tr[L].rsn = merge(tr[L].rsn, R);
updateroot(L);
return L;
}
else {
pushdown(R);
tr[R].lsn = merge(L, tr[R].lsn);
updateroot(R);
return R;
}
}
void split(int p, int &L, int &R){
if(!top) return pushdown(p), L = p, R = tr[p].rsn, tr[p].rsn = 0, updateroot(p), void();
bool d = fx[top--];
d ^= tr[p].tag;
if(d) R = p, split(tr[p].lsn, L, tr[p].lsn);
else L = p,split(tr[p].rsn, tr[p].rsn, R);
updateroot(p);
return;
}
int findroot(int p) {
top = 0;
while (!isroot(p)) fx[++top] = (tr[tr[p].fa].lsn == p), p = tr[p].fa;
return p;
}
int findminn(int p) {
p = findroot(p), pushdown(p);
while(tr[p].lsn) p = tr[p].lsn, pushdown(p);
return p;
}
int access(int p) {
int lst = 0;
while(p) {
int L, R;
split(findroot(p), L, R);
tr[findminn(lst)].uni = 0;
lst = merge(L, lst);
tr[findminn(R)].uni = p;
p = tr[findminn(lst)].uni;
}
return lst;
}
int root(int p) { return findminn(access(p)); }
void changeroot(int p) { tr[access(p)].tag ^= 1; }
void link(int x, int y) { d[x]++, d[y]++, changeroot(x), tr[x].uni = y; }
void cut(int x,int y) { d[x]--, d[y]--, changeroot(x), access(y), access(x), tr[y].uni = 0; }
int query(int x,int y) { return tr[findroot(y)].sum; }
void change(int x, int val) {
changeroot(x);
access(x);
tr[x].key = val;
updateroot(x);
return;
}
} T[11];
void lover() {
cin >> n >> m >> c >> k;
rep (i, 1, n) {
int x; cin >> x;
rep (j, 1, c) T[j].tr[i].key = T[j].tr[i].sum = x, T[j].tr[i].pri = Rnd();
}
rep (i, 1, m) {
int u, v, w; cin >> u >> v >> w;
w++;
T[w].link(u, v);
mp[mp(u, v)] = w, mp[mp(v, u)] = w;
}
rep (i, 1, k) {
int op; cin >> op;
if (op == 0) {
int x, val; cin >> x >> val;
rep (j, 1, c) T[j].change(x, val);
}
if (op == 1) {
int u, v, w; cin >> u >> v >> w;
w++;
if (mp[mp(u, v)] == w) cout << "Success.\n";
else if (!mp.count(mp(u, v))) cout << "No such edge.\n";
else if (T[w].d[u] >= 2 || T[w].d[v] >= 2) { cout << "Error 1.\n"; }
else {
T[w].changeroot(u), T[w].access(v);
if (T[w].findroot(u) == T[w].findroot(v)) { cout << "Error 2.\n"; }
else {
T[mp[mp(u, v)]].cut(u, v);
mp[mp(u, v)] = w, mp[mp(v, u)] = w;
T[w].link(u, v);
cout << "Success.\n";
}
}
}
if (op == 2) {
int cc, u, v; cin >> cc >> u >> v;
cc++;
T[cc].changeroot(u), T[cc].access(v);
if (T[cc].findroot(u) != T[cc].findroot(v)) cout << "-1\n";
else cout << T[cc].query(u, v) << '\n';
}
}
return;
}
}
signed main() {
// iosfst;
An::lover();
return 0;
}