#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <climits>
#include <cfloat>
#include <cstring>
#include <map>
#include <utility>
#include <set>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <list>
#include <algorithm>
#include <functional>
#include <sstream>
#include <complex>
#include <stack>
#include <queue>
#include <unordered_set>
#include <unordered_map>
#include <array>
#include <cassert>
#include <bitset>
using namespace std;
using LL = long long;


#include <vector>

class union_find {
private:
	vector<int>parent;
	vector<int>rank;
	vector<int>clump;
public:
	union_find(int siz) {
		parent.resize(siz);
		rank.resize(siz);
		clump.resize(siz);
		for (int i = 0; i < siz; ++i) {
			parent[i] = i;
			rank[i] = 0;
			clump[i] = 1;
		}
	}
	void unite(int x, int y) {
		int xr = find(x);
		int yr = find(y);
		if (xr == yr)return;
		if (rank[xr] > rank[yr]) {
			parent[yr] = xr;
			clump[xr] += clump[yr];
		}
		else if (rank[yr] > rank[xr]) {
			parent[xr] = yr;
			clump[yr] += clump[xr];
		}
		else if (xr != yr) {
			parent[yr] = xr;
			clump[xr] += clump[yr];
			++rank[xr];
		}
	}
	int find(int x) {
		if (parent[x] == x) {
			return x;
		}
		else {
			return parent[x] = find(parent[x]);
		}
	}
	bool check(int x, int y) {
		return find(x) == find(y);
	}
	int size(int x) {
		return clump[find(x)];
	}
};

struct edge
{
	int to;
	long long cost;
	edge(int to_, long long cost_) :
		to(to_), cost(cost_) {}
};

namespace krustkal_sort
{
	bool asc(const pair<int, edge>&e1, const pair<int, edge>&e2)
	{
		return e1.second.cost < e2.second.cost;
	}
}

LL costs[1234][1234];
vector<edge>tree[1234];

//戻り値＝最小全域木の合計コスト
//グラフは有向辺でも無向として判定
//最小全域木自体はソースを少し改良して
long long krustkal(vector<edge>graph_[], int n_)
{
	vector<pair<int, edge> >vec;
	for (int i = 0; i < n_; ++i)
	{
		for (int j = 0; j < graph_[i].size(); ++j)
		{
			vec.push_back(make_pair(i, graph_[i][j]));
		}
	}
	sort(vec.begin(), vec.end(), krustkal_sort::asc);
	union_find uf(n_);
	long long res = 0;
	for (int i = 0; i < vec.size(); ++i)
	{
		int a = vec[i].first;
		int b = vec[i].second.to;
		int cost = vec[i].second.cost;
		if (!uf.check(a, b))
		{
			uf.unite(a, b);
			tree[a].push_back(edge(b, cost));
			tree[b].push_back(edge(a, cost));
			costs[a][b] = costs[b][a] = cost;
			res += cost;
		}
	}
	return res;
}

vector<edge>graph[1234];
int N, M;
LL X;
const LL MOD = 1000000007LL;
vector<LL>two;

//vを探したいとき、探すまでの経路上の最大値
pair<bool, LL> dfs(int now, int las, int v) {
	if (now == v) {
		return { true,0 };
	}
	pair<bool, LL>res = {};
	for (auto&e : tree[now]) {
		int to = e.to;
		LL cost = e.cost;
		if (las == to)continue;
		auto can = dfs(to, now, v);
		if (can.first) {
			res.first = true;
			res.second = max(res.second, max(e.cost, can.second));
		}
	}
	return res;
}

int main(void)
{
	cin >> N >> M;
	if (M == 1) {
		cout << 0 << endl;
		return 0;
	}
	cin >> X;
	for (int i = 0; i < M; ++i) {
		int u, v; LL w;
		cin >> u >> v >> w;
		--u; --v;
		graph[u].push_back(edge(v, w));
		//graph[v].push_back(edge(u, w));
	}
	LL sum = krustkal(graph, N);
	two.push_back(1);
	for (int x = 0; x < 123456; ++x) {
		two.push_back(two.back() * 2LL % MOD);
	}
	LL ans = 0;
	if (sum == X) {
		ans += two[M];
		ans += MOD - two[M - (N - 1) + 1];
		ans %= MOD;
		//ans += MOD - 2;
		//ans %= MOD;
	}
	vector<pair<pair<int, int>, LL>>vec;//使われなかった辺
	for (int i = 0; i < N; ++i) {
		for (auto&e : graph[i]) {
			int to = e.to;
			if (!costs[i][to]) {
				vec.push_back({ {i,to},e.cost });
			}
		}
	}
	vector<LL>tame;
	for (auto& elm : vec) {
		int u = elm.first.first;
		int v = elm.first.second;
		LL C = elm.second;
		auto x = dfs(u, -1, v);
		assert(x.first);
		tame.push_back(C - x.second);
	}
	sort(tame.begin(), tame.end());
	for (int i = 0; i < tame.size(); ++i) {
		int index = (int)tame.size() - i;
		if (sum + tame[i] == X) {
			ans += two[index];
			ans %= MOD;
		}
	}
	cout << ans << endl;
	return 0;
}


Measured memory_bytes: 11128