import std.conv, std.stdio, std.string; import std.algorithm, std.array, std.bigint, std.container, std.math, std.numeric, std.range, std.regex, std.typecons; import core.bitop; class EOFException : Throwable { this() { super("EOF"); } } string[] tokens; string readToken() { for (; tokens.empty; ) { if (stdin.eof) { throw new EOFException; } tokens = readln.split; } auto token = tokens.front; tokens.popFront; return token; } int readInt() { return readToken.to!int; } long readLong() { return readToken.to!long; } real readReal() { return readToken.to!real; } bool chmin(T)(ref T t, in T f) { if (t > f) { t = f; return true; } else { return false; } } bool chmax(T)(ref T t, in T f) { if (t < f) { t = f; return true; } else { return false; } } int binarySearch(T)(in T[] as, in bool delegate(T) test) { int low = -1, upp = cast(int)(as.length); for (; low + 1 < upp; ) { int mid = (low + upp) >> 1; (test(as[mid]) ? low : upp) = mid; } return upp; } int lowerBound(T)(in T[] as, in T val) { return as.binarySearch((T a) => (a < val)); } int upperBound(T)(in T[] as, in T val) { return as.binarySearch((T a) => (a <= val)); } enum MO = 10L^^9 + 7; alias Mat = long[4]; Mat IDENTITY = [1, 0, 0, 1]; /* [ a[0] a[1] ] [ b[0] b[1] ] [ a[2] a[3] ] [ b[2] b[3] ] */ Mat mul(in Mat a, in Mat b) { return [ (a[0] * b[0] + a[1] * b[2]) % MO, (a[0] * b[1] + a[1] * b[3]) % MO, (a[2] * b[0] + a[3] * b[2]) % MO, (a[2] * b[1] + a[3] * b[3]) % MO, ]; } class SegmentTree { int n; Mat[] prod; this(int n_) { for (n = 1; n < n_; n <<= 1) {} prod = new Mat[n << 1]; prod[] = IDENTITY; } void update(int a, in Mat val) { prod[a += n] = val; for (; (a >>= 1); ) { prod[a] = mul(prod[a << 1], prod[a << 1 | 1]); } } Mat rangeProd(int a, int b) { Mat retL = IDENTITY, retR = IDENTITY; for (a += n, b += n; a <= b; a >>= 1, b >>= 1) { if ( a & 1) retL = mul(retL, prod[a++]); if (~b & 1) retR = mul(prod[b--], retR); } return mul(retL, retR); } } int N; int[] A, B; int Q; string[] T; int[] I, J; Mat[] X; int[][] G; int[] par, sz; int E; int[] F; int[] es, fs; int[][] us; void dfs(int u, int p) { par[u] = p; sz[u] = 1; foreach (v; G[u]) { if (v != p) { dfs(v, u); sz[u] += sz[v]; } } bool found; foreach (v; G[u]) { if (v != p) { if (sz[u] <= sz[v] * 2) { found = true; es[u] = es[v]; us[es[u]] ~= u; } } } if (!found) { es[u] = E++; us ~= [u]; } } void main() { try { for (; ; ) { N = readInt(); A = new int[N - 1]; B = new int[N - 1]; foreach (i; 0 .. N - 1) { A[i] = readInt(); B[i] = readInt(); } Q = readInt(); T = new string[Q]; I = new int[Q]; J = new int[Q]; X = new Mat[Q]; foreach (q; 0 .. Q) { T[q] = readToken(); switch (T[q]) { case "x": { I[q] = readInt(); X[q][0] = readLong(); X[q][1] = readLong(); X[q][2] = readLong(); X[q][3] = readLong(); } break; case "g": { I[q] = readInt(); J[q] = readInt(); } break; default: assert(false); } } G = new int[][N]; foreach (i; 0 .. N - 1) { G[A[i]] ~= B[i]; G[B[i]] ~= A[i]; } par = new int[N]; sz = new int[N]; E = 0; es = new int[N]; fs = new int[N]; us = []; const rt = 0; dfs(rt, -1); F = new int[E]; foreach (e; 0 .. E) { us[e].reverse; F[e] = cast(int)(us[e].length); foreach (f; 0 .. F[e]) { fs[us[e][f]] = f; } } debug { writeln("par = ", par); writeln("us = ", us); } auto segs = new SegmentTree[E]; foreach (e; 0 .. E) { segs[e] = new SegmentTree(F[e]); } foreach (q; 0 .. Q) { switch (T[q]) { case "x": { const i = I[q]; const u = (par[A[i]] == B[i]) ? A[i] : B[i]; debug { writefln("update %s %s", u, X[q]); } segs[es[u]].update(fs[u], X[q]); } break; case "g": { Mat ans = IDENTITY; int u = I[q], v = J[q]; for (; ; ) { if (es[u] == es[v]) { ans = mul(segs[es[u]].rangeProd(fs[u] + 1, fs[v]), ans); break; } else { ans = mul(segs[es[v]].rangeProd(0, fs[v]), ans); v = par[us[es[v]][0]]; } } writefln("%s %s %s %s", ans[0], ans[1], ans[2], ans[3]); } break; default: assert(false); } } } } catch (EOFException e) { } }