#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const int BASE = 1e9 + 7;
struct Node {
  int v, w, c, sz;
  Node() {}
  Node(int v, int w, int c, int sz = 0) : v(v), w(w), c(c) {}
};
struct LLI {
  long long first;
  int second;
  LLI() {}
  LLI(long long first, int second) : first(first), second(second) {}
};
struct Item {
  int x, y;
  long long P;
  Item() {}
  Item(int x, int y, long long P) : x(x), y(y), P(P) {}
};
bool deleted[N];
long long revAns = 1, totAns = 1;
long long totP = 1, totC = 0;
int n, RSize;
int x[N], y[N], R[4 * N], h[N];
long long P[N];
vector<Node> adj[N];
vector<Item> V[N];
vector<int> Vertex;
LLI operator+(const LLI &A, const LLI &B) {
  return LLI(A.first * B.first % BASE, A.second + B.second);
}
struct FenwickTree {
  static const int N = 4 * (1e5 + 10);
  LLI fw[N + 10];
  void Init() {
    for (int i = 1; i <= N; ++i) fw[i] = LLI(1, 0);
  }
  void Update(int i, LLI v) {
    for (i; i > 0; i -= i & -i) fw[i] = fw[i] + v;
  }
  LLI Query(int i) {
    LLI ans = LLI(1, 0);
    for (i; i <= RSize; i += i & -i) ans = ans + fw[i];
    return ans;
  }
} fw[2];
long long Pow(long long a, long long p) {
  long long ans = 1;
  while (p) {
    if (p & 1) ans = ans * a % BASE;
    a = a * a % BASE;
    p >>= 1;
  }
  return ans;
}
void Compress() {
  sort(R + 1, R + RSize + 1);
  RSize = unique(R + 1, R + RSize + 1) - R - 1;
}
int Pos(int v) { return lower_bound(R + 1, R + RSize + 1, v) - R; }
int DFS(int u, int par, int sz) {
  Vertex.push_back(u);
  int res = 1;
  for (int k = 0; k < adj[u].size(); ++k) {
    int v = adj[u][k].v;
    if (v == par || deleted[v]) continue;
    adj[u][k].sz = DFS(v, u, sz);
    res += adj[u][k].sz;
  }
  for (int k = 0; k < adj[u].size(); ++k) {
    int v = adj[u][k].v;
    if (v != par || deleted[v]) continue;
    adj[u][k].sz = sz - res;
  }
  return res;
}
void DFS1(int u, int par, int i) {
  V[i].push_back(Item(x[u], y[u], P[u]));
  R[++RSize] = 2 * x[u] - y[u];
  R[++RSize] = y[u] - 2 * x[u];
  R[++RSize] = 2 * y[u] - x[u];
  R[++RSize] = x[u] - 2 * y[u];
  for (int k = 0; k < adj[u].size(); ++k) {
    int v = adj[u][k].v;
    if (v == par || deleted[v]) continue;
    x[v] = x[u] + (adj[u][k].c == 0);
    y[v] = y[u] + (adj[u][k].c == 1);
    P[v] = P[u] * adj[u][k].w % BASE;
    DFS1(v, u, i);
  }
}
int FindC(int u, int par, int sz) {
  Vertex.clear();
  int c = u;
  DFS(u, par, sz);
  for (int i = 0; i < Vertex.size(); ++i) {
    int u = Vertex[i];
    bool flag = false;
    for (int k = 0; k < adj[u].size(); ++k) {
      int v = adj[u][k].v;
      if (v == par || deleted[v]) continue;
      if (adj[u][k].sz * 2 > sz) {
        flag = true;
        break;
      }
    }
    if (flag == false) c = u;
  }
  return c;
}
void Update(int i, int d) {
  for (int k = 0; k < V[i].size(); ++k) {
    int x = V[i][k].x, y = V[i][k].y;
    long long P = V[i][k].P;
    if (d == -1) P = Pow(P, BASE - 2);
    totP = totP * P % BASE;
    totC = totC + d;
    fw[0].Update(Pos(y - 2 * x), LLI(P, d));
    fw[1].Update(Pos(x - 2 * y), LLI(P, d));
  }
}
LLI Query(int x, int y) {
  LLI ans = LLI(1, 0);
  ans = ans + fw[0].Query(Pos(2 * x - y) + 1);
  ans = ans + fw[1].Query(Pos(2 * y - x) + 1);
  return ans;
}
void UpdateAns(long long &totAns, LLI ans, long long P) {
  totAns = totAns * ans.first % BASE;
  totAns = totAns * Pow(P, ans.second) % BASE;
}
void Solve(int u, int par, int sz, int prevC = 0) {
  int c = FindC(u, par, sz);
  x[c] = y[c] = 0;
  P[c] = 1;
  RSize = 0;
  R[++RSize] = 2 * x[c] - y[c];
  R[++RSize] = y[c] - 2 * x[c];
  R[++RSize] = 2 * y[c] - x[c];
  R[++RSize] = x[c] - 2 * y[c];
  for (int k = 0; k < adj[c].size(); ++k) {
    int v = adj[c][k].v;
    if (v == par || deleted[v]) continue;
    x[v] = x[c] + (adj[c][k].c == 0);
    y[v] = y[c] + (adj[c][k].c == 1);
    P[v] = P[c] * adj[c][k].w % BASE;
    V[k].clear();
    DFS1(v, c, k);
  }
  Compress();
  deleted[c] = true;
  for (int k = 0; k < adj[c].size(); ++k) {
    int v = adj[c][k].v;
    if (v == par || deleted[v]) continue;
    Update(k, +1);
  }
  LLI ans = Query(0, 0);
  UpdateAns(revAns, ans, 1);
  UpdateAns(totAns, LLI(totP, totC), 1);
  for (int k = 0; k < adj[c].size(); ++k) {
    int v = adj[c][k].v;
    if (v == par || deleted[v]) continue;
    Update(k, -1);
    for (int p = 0; p < V[k].size(); ++p) {
      int x = V[k][p].x, y = V[k][p].y;
      long long P = V[k][p].P;
      LLI ans = Query(x, y);
      UpdateAns(revAns, ans, P);
      UpdateAns(totAns, LLI(totP, totC), P);
    }
  }
  deleted[c] = true;
  for (int k = 0; k < adj[c].size(); ++k) {
    int v = adj[c][k].v;
    if (v == par || deleted[v]) continue;
    Solve(v, c, adj[c][k].sz, c);
  }
}
int main() {
  scanf("%d", &n);
  for (int i = 1; i <= n - 1; ++i) {
    int u, v, w, c;
    scanf("%d%d%d%d", &u, &v, &w, &c);
    adj[u].push_back(Node(v, w, c));
    adj[v].push_back(Node(u, w, c));
  }
  for (int i = 0; i < 2; ++i) fw[i].Init();
  Solve(1, -1, n);
  printf("%I64d", totAns * Pow(revAns, BASE - 2) % BASE);
  return 0;
}
