/* ACM ICPC -- CERC 2011
 * Solution for the problem: Regulate
 * Idea: keep a balanced tree for each path in each company's network
 *       - update the tree by a split (selling a cable) or merge (buying)
 *       - balance by rebuilding (amortized sqrt(N) per operation)
 * Author: Josef Cibulka
 */ 
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer; 
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Collections;
import java.util.List;



public class regulate
{
	final static int MAXN=10000;
	final static int MAXM=100000;
	final static int MAXC=100;
	final static int MAXT=100000;


	public static class Trees
	{
		int parent[];
		int l[];
		int r[];
		int new_tree_list[];
		boolean reverse[];
		final static int MAXDEP=100; // depth of a query after which a tree is rebuilt

		public Trees(int n)
		{
			parent = new int[n];
			l = new int[n];
			r = new int[n];
			reverse = new boolean[n];
			new_tree_list = new int[n];
			for(int i=0; i<n; i++)
			{
				parent[i]=l[i]=r[i] = -1;
				reverse[i] = false;
			}
		}


		// build an optimal tree from values on positions beg...end in ntl[] and report the new root
		private int build_opt_tree(int _parent, int beg, int end)
		{
			if(beg>end) return -1;
			int mid = (beg+end)/2;
			int nmid = new_tree_list[mid];
			parent[nmid] = _parent;
			l[nmid] = build_opt_tree(nmid, beg, mid-1);
			r[nmid] = build_opt_tree(nmid, mid+1, end);
			reverse[nmid] = false;
			return nmid;
		}

		// if root is reverted, "sends" reverse to the two children
		void push_reverse_down(int root)
		{
			if(!reverse[root]) return;
			int lr = l[root], rr = r[root];
			if(lr!=-1) reverse[lr] = !reverse[lr];
			if(rr!=-1) reverse[rr] = !reverse[rr];
			l[root] = rr; r[root] = lr;
			reverse[root] = false;
		}

		// if v is reverted, "sends" reverse to the parent and the sibling
		void push_reverse_up(int v)
		{
			if(!reverse[v]) return;
			int par = parent[v];
			if(par!=-1)
			{
				reverse[par] = !reverse[par];
				int lp = l[par], rp = r[par];
				if(lp!=-1) reverse[lp] = !reverse[lp];
				if(rp!=-1) reverse[rp] = !reverse[rp];
				l[par] = rp; r[par] = lp;
			}
			else reverse[v] = false; // we can change the direction of a path
		}

		
		int ntl_size;

		static int fn_stack[] = new int[MAXN];
		static int fn_stack_size;
		static boolean fn_stack_processed[] = new boolean[MAXN];
		
		// dfs with infix writing of nodes into new_tree_list
		void find_nodes(int root)
		{
			// dfs without recursion as java can recurse to depth about 1000 
			if(root==-1) return;
			fn_stack_size = 1;
			fn_stack[0] = root;	fn_stack_processed[0] = false;
			while(fn_stack_size > 0)
			{
				fn_stack_size--;
				int cur = fn_stack[fn_stack_size];
				if(fn_stack_processed[fn_stack_size]) 
					new_tree_list[ntl_size++] = cur;
				else
				{
					push_reverse_down(cur);
					if(r[cur]!=-1)
					{
						fn_stack_processed[fn_stack_size] = false;
						fn_stack[fn_stack_size++] = r[cur];
					}
					fn_stack_processed[fn_stack_size] = true;
					fn_stack[fn_stack_size++] = cur;
					if(l[cur]!=-1)
					{
						fn_stack_processed[fn_stack_size] = false;
						fn_stack[fn_stack_size++] = l[cur];
					}
				}
			}
		}

		// keep the root (because its id is reported as the path number) and 
		// rebuild its subtrees
		private void rebuild(int root)
		{
			push_reverse_down(root);
			ntl_size = 0;
			find_nodes(l[root]);
			l[root] = build_opt_tree(root, 0, ntl_size-1);
			ntl_size = 0;
			find_nodes(r[root]);
			r[root] = build_opt_tree(root, 0, ntl_size-1);
		}

		public int query(int qi, boolean can_rebuild)
		{
			int pos = qi;
			int dst;
			push_reverse_up(pos);
			for(dst=0; parent[pos]!=-1; dst++) 
			{ 
				pos = parent[pos]; 
				push_reverse_up(pos);
			}
			if(can_rebuild && dst>=MAXDEP) rebuild(pos);
			return pos;
		}

		// merge the prepared trees rooted in root1 (left tree) and root2 (right tree)
		private void merge_prepared(int root1, int root2)
		{
			int lr = root1; // the rightmost in the left tree
			push_reverse_down(lr);
			while(r[lr]!=-1) 
			{
				lr = r[lr]; 
				push_reverse_down(lr); 
			}
			// set left son of lr to the right son of parent[lr]
			if(lr!=root1)
			{
				int lrpar = parent[lr];
				int lrson = l[lr];
				if(lrson!=-1) parent[lrson] = lrpar;
				if(lrpar!=-1) r[lrpar] = lrson;
			}
			// make lr the new root
			parent[lr]=-1;
			if(lr!=root1) { l[lr] = root1; parent[root1] = lr; }
			r[lr] = root2;
			parent[root2] = lr;
		}

		// merge trees containing m1 and m2 (expects that the nodes are extremes of
		// the path represented by their trees and that the trees are different)
		public void merge(int m1, int m2)
		{
			int root1 = query(m1, true); // also clear all rever-
			int root2 = query(m2, true); // ses on the path mi<->ri
			int p1 = parent[m1];
			int p2 = parent[m2];
			boolean is1right = (p1==-1)?(r[m1]==-1):(r[p1]==m1);
			boolean is2left = (p2==-1)?(l[m2]==-1):(l[p2]==m2);
			if(is1right != is2left) 
			{
			 	reverse[root2] = !reverse[root2];
				is2left = !is2left; 
			}
			if(is1right) merge_prepared(root1,root2);
			else merge_prepared(root2,root1);
		}
		
		// split between s1 and s2
		public void split(int s1, int s2)
		{
			// s1 and s2 are neighbors on the path, so one is an ancestor of the other
			// find who is the ancestor and split on the edge from it towards the descendant
			int cur = s1, prev = -1;
			while(cur!=s2 && cur!=-1) { prev = cur; cur = parent[cur]; }
			if(cur==-1)
			{
				cur = s2;
				while(cur!=s1 && cur!=-1) { prev = cur; cur = parent[cur]; }
			}
			query(prev, false); // to clear all reverses
			// split on the tree-edge prev-cur
			int lroot=-1, rroot=-1;
			boolean just_started = true;
			while(cur!=-1)
			{
				if(prev == l[cur] && (rroot!=-1 || just_started))
				{
					lroot = prev;
					parent[prev] = -1;
					l[cur] = rroot;
					if(rroot!=-1) parent[rroot] = cur;
					rroot = -1;
				}
				else if(prev == r[cur] && (lroot!=-1 || just_started))
				{
					rroot = prev;
					parent[prev] = -1;
					r[cur] = lroot;
					if(lroot!=-1) parent[lroot] = cur;
					lroot = -1;
				}
				just_started = false;
				prev = cur;
				cur = parent[cur];
			}

		}

	}



	StringTokenizer st = new StringTokenizer("");
	BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
	String nextToken() throws Exception {
		while (!st.hasMoreTokens()) st = new StringTokenizer(input.readLine());
		return st.nextToken();
	}
	int nextInt() throws Exception {
		int ret = Integer.parseInt(nextToken());
		return ret;
	} 

		
	public static void main(String[] args) throws Exception 
	{
		regulate inst = new regulate();
		for(int ii=1; ; ii++)
		{
			try {	inst.run(ii);	} catch(Exception e) { return; }
		}
	}

	public class Edge
	{
		int owner, to;
		Edge(int _to, int _owner)
		{
			to = _to; owner = _owner;
		}
	}
	
	public class EdgeComparator implements Comparator<Edge> 
	{
    public int compare(Edge e1, Edge e2) 
		{
			return e1.to-e2.to;
    }
	}

	boolean run(int ii) throws Exception 
	{
		int n = nextInt();
		if (n<=0) throw new Exception();
		int m = nextInt();
		int c = nextInt();
		int t = nextInt();
		Trees tree[] = new Trees[c];
		for (int i = 0; i < c; i++) tree[i] = new Trees(n);
		int deg[][] = new int[c][n]; // java initializes to 0's
		List<Edge> ed[] = new List[n];
		for (int i = 0; i < n; ++i) ed[i] = new ArrayList<Edge>(2*c);
		Comparator<Edge> edgeCompar = new EdgeComparator();
		
		
		for(int i=0; i<m; i++)
		{
			int s1 = nextInt();
			int s2 = nextInt();
			int o = nextInt();
			o--; s1--; s2--;

			if(s1>s2) { int tmp = s1; s1 = s2; s2 = tmp; }
			ed[s1].add(new Edge(s2,o));
			deg[o][s1]++; deg[o][s2]++;

			tree[o].merge(s1,s2);
		}
		for(int s1=0; s1<n; s1++)
		{
			Collections.sort(ed[s1], edgeCompar);
		//	for(int i=0;i<totdeg[s1];i++) System.out.println(ed[s1][i].to + " " + ed[s1][i].owner);
		}
		
		for(int i=0; i<t; i++)
		{
			int s1 = nextInt();
			int s2 = nextInt();
			int o = nextInt();
			o--; s1--; s2--;
			if(s1>s2) { int tmp = s1; s1 = s2; s2 = tmp; }
			int epos = Collections.binarySearch(ed[s1], new Edge(s2, 0), edgeCompar);
			if(epos<0) 
			{ 
				System.out.println("No such cable."); continue; 
			}
			int prevo = ed[s1].get(epos).owner;
			if(o == prevo)
			{ 
				System.out.println("Already owned."); continue; 
			}
			if(deg[o][s1]==2 || deg[o][s2]==2) 
			{ 
				System.out.println("Forbidden: monopoly."); continue; 
			}
			int r1 = tree[o].query(s1, true);
			int r2 = tree[o].query(s2, true); 
			if(r1==r2) 
			{ 
				System.out.println("Forbidden: redundant."); continue; 
			}
			tree[o].merge(s1,s2);
			deg[prevo][s1]--; deg[prevo][s2]--;
			tree[prevo].split(s1,s2);
			deg[o][s1]++; deg[o][s2]++;
			ed[s1].get(epos).owner = o;
			System.out.println("Sold.");
		}
		System.out.println();
		
		return true;
	}

}
