#include <bits/stdc++.h>
using namespace std;
char getc() {
  char c = getchar();
  while ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z') && (c < '0' || c > '9'))
    c = getchar();
  return c;
}
int gcd(int n, int m) { return m == 0 ? n : gcd(m, n % m); }
int read() {
  int x = 0, f = 1;
  char c = getchar();
  while (c < '0' || c > '9') {
    if (c == '-') f = -1;
    c = getchar();
  }
  while (c >= '0' && c <= '9')
    x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
  return x * f;
}
int n, p[100010], len[100010], size[100010], t, ans = 1, mx, mn;
bool flag[100010];
struct data {
  int to, nxt, len, color;
} edge[100010 << 1];
struct data2 {
  int x, y, x1, y1;
} deep[100010];
namespace bitreap {
int root[100010 << 2], cnt = 0;
struct data {
  int ch[2], p, x, s;
} tree[100010 << 5];
void up(int k) {
  tree[k].s = tree[tree[k].ch[0]].s + tree[tree[k].ch[1]].s + 1;
}
void move(int &k, int p) {
  int t = tree[k].ch[p];
  tree[k].ch[p] = tree[t].ch[!p], tree[t].ch[!p] = k, up(k), up(t), k = t;
}
void ins(int &k, int x) {
  if (k == 0) {
    k = ++cnt;
    tree[k].p = rand();
    tree[k].ch[0] = tree[k].ch[1] = 0;
    tree[k].x = x;
    tree[k].s = 1;
    return;
  }
  tree[k].s++;
  if (tree[k].x < x) {
    ins(tree[k].ch[1], x);
    if (tree[tree[k].ch[1]].p > tree[k].p) move(k, 1);
  } else {
    ins(tree[k].ch[0], x);
    if (tree[tree[k].ch[0]].p > tree[k].p) move(k, 0);
  }
}
void del(int &k, int x) {
  if (tree[k].x == x) {
    if (tree[k].ch[0] == 0 || tree[k].ch[1] == 0) {
      k = tree[k].ch[0] | tree[k].ch[1];
      return;
    }
    if (tree[tree[k].ch[0]].p > tree[tree[k].ch[1]].p)
      move(k, 0), del(tree[k].ch[1], x);
    else
      move(k, 1), del(tree[k].ch[0], x);
  } else if (tree[k].x < x)
    del(tree[k].ch[1], x);
  else
    del(tree[k].ch[0], x);
  up(k);
}
int query(int k, int x) {
  if (k == 0) return 0;
  if (tree[k].x < x)
    return query(tree[k].ch[1], x);
  else
    return tree[tree[k].ch[1]].s + 1 + query(tree[k].ch[0], x);
}
void Insert(int k, int x) {
  k -= mn;
  k++;
  while (k <= mx - mn + 1) ins(root[k], x), k += k & -k;
}
void Delete(int k, int x) {
  k -= mn;
  k++;
  while (k <= mx - mn + 1) del(root[k], x), k += k & -k;
}
int Query(int k, int x) {
  k -= mn;
  int s = 0;
  while (k) s -= query(root[k], x), k ^= k & -k;
  k = mx - mn + 1;
  while (k) s += query(root[k], x), k ^= k & -k;
  return s;
}
}  // namespace bitreap
void addedge(int x, int y, int z, int c) {
  t++;
  edge[t].to = y, edge[t].nxt = p[x], edge[t].len = z, edge[t].color = c,
  p[x] = t;
}
int ksm(int a, int k) {
  int s = 1;
  for (; k; k >>= 1, a = 1ll * a * a % 1000000007)
    if (k & 1) s = 1ll * s * a % 1000000007;
  return s;
}
void makes(int k, int from) {
  size[k] = 1;
  for (int i = p[k]; i; i = edge[i].nxt)
    if (edge[i].to != from && !flag[edge[i].to]) {
      makes(edge[i].to, k);
      size[k] += size[edge[i].to];
    }
}
int findroot(int k, int s, int from) {
  int mx = 0;
  for (int i = p[k]; i; i = edge[i].nxt)
    if (edge[i].to != from && !flag[edge[i].to] && size[edge[i].to] > size[mx])
      mx = edge[i].to;
  if ((size[mx] << 1) > s)
    return findroot(mx, s, k);
  else
    return k;
}
void make(int k, int from) {
  mx = max(mx, deep[k].x);
  mx = max(mx, deep[k].y);
  mn = min(mn, deep[k].x);
  mn = min(mn, deep[k].y);
  mx = max(mx, deep[k].x1);
  mx = max(mx, deep[k].y1);
  mn = min(mn, deep[k].x1);
  mn = min(mn, deep[k].y1);
  for (int i = p[k]; i; i = edge[i].nxt)
    if (edge[i].to != from && !flag[edge[i].to]) {
      deep[edge[i].to].x = deep[k].x;
      deep[edge[i].to].y = deep[k].y;
      if (edge[i].color > 0)
        deep[edge[i].to].x++, deep[edge[i].to].y -= 2;
      else
        deep[edge[i].to].y++, deep[edge[i].to].x -= 2;
      deep[edge[i].to].x1 = deep[k].x1;
      deep[edge[i].to].y1 = deep[k].y1;
      if (edge[i].color > 0)
        deep[edge[i].to].x1--, deep[edge[i].to].y1 += 2;
      else
        deep[edge[i].to].y1--, deep[edge[i].to].x1 += 2;
      len[edge[i].to] = 1ll * len[k] * edge[i].len % 1000000007;
      make(edge[i].to, k);
    }
}
void work(int k, int from, int x) {
  if (x == 1)
    bitreap::Insert(deep[k].x1, deep[k].y1);
  else
    bitreap::Delete(deep[k].x1, deep[k].y1);
  for (int i = p[k]; i; i = edge[i].nxt)
    if (edge[i].to != from && !flag[edge[i].to]) work(edge[i].to, k, x);
}
void calc(int k, int from) {
  ans = 1ll * ans * ksm(len[k], bitreap::Query(deep[k].x, deep[k].y)) %
        1000000007;
  for (int i = p[k]; i; i = edge[i].nxt)
    if (edge[i].to != from && !flag[edge[i].to]) calc(edge[i].to, k);
}
void solve(int k) {
  makes(k, k);
  k = findroot(k, size[k], k);
  flag[k] = 1;
  deep[k].x = deep[k].y = deep[k].x1 = deep[k].y1 = 0;
  len[k] = 1;
  mx = mn = 0;
  make(k, k);
  bitreap::cnt = 0;
  for (int i = 0; i <= mx - mn + 1; i++) bitreap::root[i] = 0;
  work(k, k, 1);
  for (int i = p[k]; i; i = edge[i].nxt)
    if (!flag[edge[i].to]) {
      work(edge[i].to, edge[i].to, -1);
      calc(edge[i].to, edge[i].to);
      work(edge[i].to, edge[i].to, 1);
    }
  for (int i = p[k]; i; i = edge[i].nxt)
    if (!flag[edge[i].to]) solve(edge[i].to);
}
signed main() {
  srand(time(0));
  n = read();
  for (int i = 1; i < n; i++) {
    int x = read(), y = read(), z = read(), c = read();
    if (c == 0) c = -1;
    addedge(x, y, z, c), addedge(y, x, z, c);
  }
  solve(1);
  cout << ans;
  return 0;
}
