#include <bits/stdc++.h>
// #include <atcoder/all>
using namespace std;
// using namespace atcoder;
template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
  os << "(" << p.first << "," << p.second << ")";
  return os;
}
#ifdef __LOCAL
#define debug(x) cerr << __LINE__ << ": " << #x << " = " << (x) << '\n'
#define debugArray(x, n)                                      \
  cerr << __LINE__ << ": " << #x << " = {";                   \
  for (long long hoge = 0; (hoge) < (long long)(n); ++(hoge)) \
    cerr << ((hoge) ? "," : "") << x[hoge];                   \
  cerr << "}" << '\n'
#define debugMatrix(x, h, w)                                         \
  cerr << __LINE__ << ": " << #x << " =\n";                          \
  for (long long hoge = 0; (hoge) < (long long)(h); ++(hoge)) {      \
    cerr << ((hoge ? " {" : "{{"));                                  \
    for (long long fuga = 0; (fuga) < (long long)(w); ++(fuga))      \
      cerr << ((fuga ? ", " : "")) << x[hoge][fuga];                 \
    cerr << "}" << (hoge + 1 == (long long)(h) ? "}" : ",") << '\n'; \
  }
#else
#define debug(x) (void(0))
#define debugArray(x, n) (void(0))
#define debugMatrix(x, h, w) (void(0))
#endif

struct UnionFind {
  vector<int> par;
  UnionFind(int size) : par(size, -1) {}
  bool unite(int u, int v) {
    if ((u = root(u)) == (v = root(v))) return false;
    if (par[u] > par[v]) swap(u, v);
    par[u] += par[v], par[v] = u;
    return true;
  }
  bool same(int u, int v) { return root(u) == root(v); }
  int root(int u) { return par[u] < 0 ? u : par[u] = root(par[u]); }
  int size(int u) { return -par[root(u)]; }
};

template <typename cost_t>
struct MinimumSpanningTree_Kruskal {
  struct Edge {
    int u, v, id;
    cost_t cost;
    bool operator<(const Edge &rhs) const { return this->cost < rhs.cost; }
  };

 private:
  int n;

 public:
  vector<Edge> edges;

 public:
  MinimumSpanningTree_Kruskal(int n) : n(n) {}
  void add_edge(int u, int v, cost_t cost) {
    edges.emplace_back(Edge{u, v, (int)edges.size(), cost});
  }
  pair<cost_t, vector<Edge>> get_MST() {
    UnionFind uf(n);
    sort(begin(edges), end(edges));
    cost_t total = cost_t();
    vector<Edge> es;
    for (auto &e : edges)
      if (uf.unite(e.u, e.v)) {
        es.emplace_back(e);
        total += e.cost;
      }
    return {total, es};
  }
};

template <typename M>
struct LinkCutTree_Monoid {
  using T = typename M::T;
  struct Node {
    Node *ch[2], *par;
    bool rev;
    T val, dat, rdat;
    Node(T init = M::ti()) : rev(false), val(init), dat(init), rdat(init) {
      ch[0] = ch[1] = par = nullptr;
    }
  };

 private:
  bool is_root(Node *x) {
    return !x->par || (x->par->ch[0] != x && x->par->ch[1] != x);
  }
  bool dir(Node *x) { return x->par && x->par->ch[1] == x; }
  void rot(Node *t) {
    Node *p = t->par;
    bool d = dir(t);
    if ((p->ch[d] = t->ch[!d])) p->ch[d]->par = p;
    t->ch[!d] = p;
    pushup(p);
    pushup(t);
    t->par = p->par;
    if (!is_root(p)) {
      p->par->ch[dir(p)] = t;
      pushup(t->par);
    }
    p->par = t;
  }
  void splay(Node *x) {
    eval(x);
    while (!is_root(x)) {
      if (!is_root(x->par)) eval(x->par->par);
      eval(x->par);
      eval(x);
      if (!is_root(x->par)) {
        if (dir(x) == dir(x->par))
          rot(x->par);
        else
          rot(x);
      }
      rot(x);
    }
  }
  void pushup(Node *t) {
    t->rdat = t->dat = t->val;
    if (t->ch[0])
      t->dat = M::f(t->ch[0]->dat, t->dat),
      t->rdat = M::f(t->rdat, t->ch[0]->rdat);
    if (t->ch[1])
      t->dat = M::f(t->dat, t->ch[1]->dat),
      t->rdat = M::f(t->ch[1]->rdat, t->rdat);
  }
  Node *expose(Node *x) {
    Node *r = nullptr;
    for (Node *p = x; p; p = p->par) {
      splay(p);
      p->ch[1] = r;
      pushup(p);
      r = p;
    }
    splay(x);
    return r;
  }
  void toggle(Node *t) {
    swap(t->ch[0], t->ch[1]);
    swap(t->dat, t->rdat);
    t->rev ^= 1;
  }
  Node *eval(Node *t) {
    if (t->rev) {
      if (t->ch[0]) toggle(t->ch[0]);
      if (t->ch[1]) toggle(t->ch[1]);
      t->rev = false;
    }
    return t;
  }

 public:
  vector<Node> ns;

 private:
  size_t linkcnt;

 public:
  LinkCutTree_Monoid(int n, T init = M::ti()) : ns(n, init), linkcnt(0) {}
  // make k the root
  void evert(int k) {
    expose(&ns[k]);
    toggle(&ns[k]);
    eval(&ns[k]);
  }
  // add link from c to p
  void link(int c, int p) {
    assert(linkcnt++ < ns.size() - 1);
    evert(c);
    expose(&ns[p]);
    ns[p].ch[1] = &ns[c];
    ns[c].par = &ns[p];
    pushup(&ns[p]);
  }
  // cut link from c to p
  void cut(int c, int p) {
    linkcnt--;
    evert(p);
    expose(&ns[c]);
    Node *y = ns[c].ch[0];
    ns[c].ch[0] = y->par = nullptr;
    pushup(&ns[c]);
  }
  int lca(int x, int y) {
    expose(&ns[x]);
    Node *u = expose(&ns[y]);
    return ns[x].par ? u - &ns[0] : -1;
  }
  T operator[](int k) { return ns[k].val; }
  // [a,b] closed section
  T query(int a, int b) {
    evert(a);
    expose(&ns[b]);
    return ns[b].dat;
  }
  void set_val(int k, T v) {
    expose(&ns[k]);
    ns[k].val = v;
    eval(&ns[k]);
  }
};

struct Mono {
  using T = long long;
  static T ti() { return -1; }
  static T f(T vl, T vr) { return max(vl, vr); }
};

template <int mod>
struct ModInt {
  int64_t x;
  ModInt() : x(0) {}
  ModInt(int64_t y) : x(y >= 0 ? y % mod : (mod - (-y) % mod)) {}
  ModInt &operator+=(const ModInt &p) {
    if ((x += p.x) >= mod) x -= mod;
    return *this;
  }
  ModInt &operator-=(const ModInt &p) {
    if ((x += mod - p.x) >= mod) x -= mod;
    return *this;
  }
  ModInt &operator*=(const ModInt &p) {
    x = (int)(1LL * x * p.x % mod);
    return *this;
  }
  ModInt &operator/=(const ModInt &p) { return *this *= p.inverse(); }
  ModInt operator-() const { return ModInt() - *this; }
  ModInt operator+(const ModInt &p) const { return ModInt(*this) += p; }
  ModInt operator-(const ModInt &p) const { return ModInt(*this) -= p; }
  ModInt operator*(const ModInt &p) const { return ModInt(*this) *= p; }
  ModInt operator/(const ModInt &p) const { return ModInt(*this) /= p; }
  bool operator==(const ModInt &p) const { return x == p.x; }
  bool operator!=(const ModInt &p) const { return x != p.x; }
  ModInt inverse() const {
    int a = x, b = mod, u = 1, v = 0, t;
    while (b) t = a / b, swap(a -= t * b, b), swap(u -= t * v, v);
    return ModInt(u);
  }
  ModInt pow(int64_t e) const {
    ModInt ret(1);
    for (ModInt b = *this; e; e >>= 1, b *= b)
      if (e & 1) ret *= b;
    return ret;
  }
  friend ostream &operator<<(ostream &os, const ModInt &p) { return os << p.x; }
  friend istream &operator>>(istream &is, ModInt &a) {
    int64_t t;
    is >> t;
    a = ModInt<mod>(t);
    return (is);
  }
  static int modulo() { return mod; }
  int get() const { return x; }
};

signed main() {
  cin.tie(0);
  ios::sync_with_stdio(0);
  using Mint = ModInt<int(1e9 + 7)>;
  int N, M;
  cin >> N >> M;
  long long X;
  cin >> X;
  MinimumSpanningTree_Kruskal<long long> graph(N);
  for (int i = 0; i < M; i++) {
    int u, v;
    long long w;
    cin >> u >> v >> w;
    graph.add_edge(--u, --v, w);
  }
  auto res = graph.get_MST();
  LinkCutTree_Monoid<Mono> lct(2 * N);
  int mid = N;
  bool used[M];
  fill(used, used + M, false);
  for (auto e : res.second) {
    used[e.id] = true;
    lct.link(e.u, mid);
    lct.link(mid, e.v);
    lct.set_val(mid++, e.cost);
  }
  int lw = 0, eq = 0, hi = 0;
  long long D = X - res.first;
  for (auto e : graph.edges) {
    if (used[e.id]) continue;
    long long diff = e.cost - lct.query(e.u, e.v);
    if (diff < D) lw++;
    if (diff == D) eq++;
    if (diff > D) hi++;
  }
  Mint ans = 0;
  if (D >= 0) ans += (Mint(2).pow(eq) - 1) * Mint(2).pow(hi) * 2;
  if (D == 0) ans += (Mint(2).pow(N - 1) - 2) * Mint(2).pow(M - N + 1);
  cout << ans << '\n';
  return 0;
}


Measured memory_bytes: 3760