#include <bits/stdc++.h>
using namespace std;
const int inf = (1 << 30) - 1;
const long long linf = (1ll << 62) - 1;
const int N = 1e6 + 100;
const int M = 1e9 + 7;
const int C = 40000;
int n;
int a[N], b[N], x[N], c[N];
vector<int> g[N];
int ans = 1;
namespace root {
int cnt[N], comp[N], compLen = 0;
int parent[N];
void dfsSize(int v, int p = -1) {
  comp[compLen++] = v;
  parent[v] = p;
  cnt[v] = 1;
  for (int e : g[v]) {
    int to = a[e] ^ b[e] ^ v;
    if (p != to) {
      dfsSize(to, v);
      cnt[v] += cnt[to];
    }
  }
}
int findRoot(int v) {
  compLen = 0;
  dfsSize(v);
  int total = cnt[v];
  for (int i = 0; i < compLen; i++) {
    int v = comp[i];
    if ((total - cnt[v]) * 2 > total) {
      continue;
    }
    bool flag = true;
    for (int e : g[v]) {
      int to = a[e] ^ b[e] ^ v;
      if (to != parent[v] && cnt[to] * 2 > total) {
        flag = false;
        break;
      }
    }
    if (flag) {
      return v;
    }
  }
  assert(false);
}
}  // namespace root
int at[N], bt[N], mult[N];
void dfsValues(int v, int x, int y, int mult = 1, int p = -1) {
  at[v] = x;
  bt[v] = y;
  ::mult[v] = mult;
  for (int e : g[v]) {
    int to = a[e] ^ b[e] ^ v, c = ::c[e];
    if (p != to) {
      if (c == 0) {
        dfsValues(to, x + 1, y, (1ll * mult * ::x[e]) % M, v);
      } else {
        dfsValues(to, x, y + 1, (1ll * mult * ::x[e]) % M, v);
      }
    }
  }
}
struct event {
  int t, x, y, value;
  friend bool operator<(const event &a, const event &b) {
    if (a.x != b.x) {
      return a.x < b.x;
    }
    if (a.t != b.t) {
      return a.t < b.t;
    }
    return false;
  }
};
int rmq[N * 4 + 100], sum[N * 4 + 100];
int wh[N];
void make_rmq(int i, int l, int r) {
  rmq[i] = 1, sum[i] = 0;
  if (l == r) {
    return void(wh[l] = i);
  }
  make_rmq(i * 2, l, (l + r) / 2);
  make_rmq(i * 2 + 1, (l + r) / 2 + 1, r);
}
inline int power(int a, int b) {
  int result = 1;
  for (; b; b >>= 1) {
    if (b & 1) {
      result = (1ll * result * a) % M;
    }
    a = (1ll * a * a) % M;
  }
  return result;
}
inline int inv(int x) { return power(x, M - 2); }
void update(int i, int v) {
  i = wh[i];
  for (; i; i >>= 1) {
    rmq[i] = (1ll * rmq[i] * v) % M;
    sum[i]++;
  }
}
inline void getValue(int i, int ll, int rr, int l, int r, int &a, int &b) {
  if (ll > r || rr < l) {
    return;
  }
  if (l <= ll && rr <= r) {
    a = (1ll * a * rmq[i]) % M;
    b += sum[i];
    return;
  }
  getValue(i * 2, ll, (ll + rr) / 2, l, r, a, b);
  getValue(i * 2 + 1, (ll + rr) / 2 + 1, rr, l, r, a, b);
}
long long run(const std::vector<tuple<int, int, int>> &pt) {
  vector<event> events;
  for (const auto &p : pt) {
    int a, b, c;
    tie(a, b, c) = p;
    events.push_back({-1, b - 2 * a, a - 2 * b, c});
    events.push_back({1, 2 * a - b, 2 * b - a, c});
  }
  static int z[N];
  int cc = 0;
  for (const auto &event : events) {
    z[cc++] = event.y;
  }
  z[cc++] = -inf;
  z[cc++] = inf;
  sort(z, z + cc);
  cc = unique(z, z + cc) - z;
  for (auto &event : events) {
    event.y = lower_bound(z, z + cc, event.y) - z;
  }
  sort((events).begin(), (events).end());
  make_rmq(1, 0, cc - 1);
  int ans = 1;
  for (const auto &event : events) {
    if (event.t == -1) {
      update(event.y, event.value);
    } else {
      int rmq = 1, sum = 0;
      getValue(1, 0, cc - 1, 0, event.y, rmq, sum);
      ans = (1ll * ans * power(event.value, sum)) % M;
    }
  }
  return ans;
}
vector<tuple<int, int, int>> pt;
void dfsGo(int v, int p = -1) {
  pt.push_back(make_tuple(at[v], bt[v], mult[v]));
  for (int e : g[v]) {
    int to = a[e] ^ b[e] ^ v;
    if (p != to) {
      dfsGo(to, v);
    }
  }
}
void divideAndConquer(int v) {
  v = root::findRoot(v);
  for (int e : g[v]) {
    int to = a[e] ^ b[e] ^ v;
    g[to].erase(find((g[to]).begin(), (g[to]).end(), e));
  }
  dfsValues(v, 0, 0);
  {
    vector<tuple<int, int, int>> pt;
    for (int i = 0; i < root::compLen; i++) {
      int v = root::comp[i];
      pt.push_back(make_tuple(at[v], bt[v], mult[v]));
    }
    ans = (1ll * ans * run(pt)) % M;
  }
  for (int e : g[v]) {
    int to = a[e] ^ b[e] ^ v;
    pt.clear();
    dfsGo(to);
    ans = (1ll * ans * inv(run(pt))) % M;
  }
  for (int e : g[v]) {
    divideAndConquer(a[e] ^ b[e] ^ v);
  }
}
int generator(int p) {
  vector<int> fact;
  int phi = p - 1, n = phi;
  for (int i = 2; i * i <= n; ++i)
    if (n % i == 0) {
      fact.push_back(i);
      while (n % i == 0) n /= i;
    }
  if (n > 1) fact.push_back(n);
  for (int res = 2; res <= p; ++res) {
    bool ok = true;
    for (size_t i = 0; i < fact.size() && ok; ++i)
      ok &= power(res, phi / fact[i]) != 1;
    if (ok) return res;
  }
  assert(false);
}
inline int solveLog(int x, int a) {
  set<pair<int, int>> values;
  for (int i = 0; i < C; i++) {
    values.insert(make_pair(power(x, i * C), i * C));
  }
  for (int i = 0; i < C; i++) {
    int cur = power(x, i);
    auto it = values.lower_bound(make_pair((1ll * a * cur) % M, -1));
    if (it != values.end() && it->first == (1ll * a * cur) % M) {
      return ((it->second - i) % (M - 1) + (M - 1)) % (M - 1);
    }
  }
  assert(false);
}
int rootg(int x) {
  int root = generator(M);
  int g = (1ll * root * root) % M;
  int result = solveLog(g, x);
  int ans = power(root, result);
  if (ans > (M - ans) % M) {
    ans = (M - ans) % M;
  }
}
int main() {
  cin >> n;
  for (int i = 0; i < n; i++) {
    scanf("%d%d%d%d", &a[i], &b[i], &x[i], &c[i]);
    a[i]--, b[i]--;
    g[a[i]].push_back(i);
    g[b[i]].push_back(i);
  }
  divideAndConquer(0);
  cout << ans << endl;
  return 0;
}
