#include <iostream>
#include <fstream>
#include <algorithm>
#include <functional>
#include <array>
#include <chrono>
#include <random>
#include <vector>
#include <string>
#include <cmath>
#include <bitset>
#include <list>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <cassert>

#define sq(x) ((x) * (x))
#define err(x) cerr << "["#x"]  " << (x) << "\n";
#define errp(x) cerr << "["#x"]  {" << ((x).first) << ", " << ((x).second) << "}\n";
#define errv(x) {cerr << "["#x"]  [";for(const auto& ___ : (x)) cerr << ___ << ", "; cerr << "]\n";}
#define errvn(x, n) {cerr << "["#x"]  [";for(auto ___ = 0; ___ < (n); ++___) cerr << (x)[___] << ", "; cerr << "]\n";}
#define errf() cerr << "\n\n";

//#pragma GCC optimize("O3")
#pragma GCC optimize("Ofast, unroll-loops", "omit-frame-pointer","inline")
#pragma GCC option("arch=native","tune=native","no-zero-upper")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native,avx2")

using namespace std;

using ll = long long;
using ull = unsigned long long;
using ld = long double;

mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

ll getrnd(ll l, ll r) {
    return uniform_int_distribution<ll>(l, r)(rng);
}

template <typename T1, typename T2>
inline bool relax(T1& a, const T2& b) {
    if (a > b) {
        a = b;
        return 1;
    }
    return 0;
}

template <typename T1, typename T2>
inline bool strain(T1& a, const T2& b) {
    if (a < b) {
        a = b;
        return 1;
    }
    return 0;
}

// :::::::::::::::::::::::::::::::

const int MOD = 1e9 + 7;

int add(int a, int b) {
    a += b;
    if (a >= MOD) a -= MOD;
    if (a < 0) a += MOD;
    return a;
}

int mul(int a, int b) {
    return 1LL * a * b % MOD;
}

int binpow(int a, ll n) {
    if (n < 0)
        a = binpow(a, MOD - 2), n *= -1;

    int res = 1;
    while (n) {
        if (n & 1)
            res = mul(res, a);
        a = mul(a, a);
        n /= 2;
    }
    return res;
}

int inv(int x) {
    return binpow(x, MOD - 2);
}

struct line {
    ll k, c;

    line () {}
    line (ll x, ll y) : k(x), c(y) {}

    ll eval(ll x) {
        return k * x + c;
    }
    ld inter(const line& x) {
        return (ld)(x.c - c) / (k - x.k);
    }
    ll interRound(const line& x) {
        return (x.c - c) / (k - x.k);
    }
};

void solve() {
    int n, m, q;
    cin >> n >> m >> q;

    vector<tuple<int, int, int>> edges, nodub;
    for (int i = 0; i < m; ++i) {
        int x, y, w;
        cin >> x >> y >> w;
        --x; --y;

        edges.emplace_back(x, y, w);
        nodub.emplace_back(x, y, w);
        edges.emplace_back(y, x, w);
    }

    int ans = 0;
    vector<ll> dl(n, -1), sdw(n), dw(n, -1e17);
    dw[0] = dl[0] = sdw[0] = 0;
    for (int i = 0; i < m; ++i) {
        vector<ll> ndw(n, -1e17);
        for (const auto& [x, y, w] : edges)
            strain(ndw[y], dw[x] + w);
        ndw.swap(dw);
        for (int j = 0; j < n; ++j)
            if (dl[j] == -1 && dw[j] > 0)
                dl[j] = i + 1, sdw[j] = dw[j];
        ans = add(ans, (*max_element(dw.begin(), dw.end())) % MOD);
    }
    sdw.swap(dw);
//    errv(sdw);
//    errv(dl);

    // now all len from 1..m computed
    if (q == m) {
        cout << ans << '\n';
        return;
    }

    vector<line> lines;
    for (auto [x, y, w] : nodub) {
        if (dl[x] > dl[y] || (dl[x] == dl[y] && dw[x] < dw[y]))
            swap(x, y);

        lines.emplace_back(w, dw[x] - 1LL * w * dl[x]);
    }
    sort(lines.begin(), lines.end(), [](const line& a, const line& b) {
        return a.k < b.k || (a.k == b.k && a.c > b.c);
    });

//    for (int i = 0; i < lines.size(); ++i)
//        cout << lines[i].k << ' ' << lines[i].c << '\n';
//    errf();

    ll lstk = -1e17;
    deque<line> hull;
    for (const line& x : lines) {
        if (x.k == lstk) continue;

        while (hull.size() >= 2 && hull[hull.size() - 1].inter(x) <= hull[hull.size() - 1].inter(hull[hull.size() - 2]))
            hull.pop_back();
        hull.push_back(x);
        lstk = x.k;
    }

    vector<line> a(hull.begin(), hull.end());
//    for (int i = 0; i < a.size(); ++i)
//        cout << a[i].k << ' ' << a[i].c << '\n';
    int st = 0, en = 0;
//    while (st + 1 < a.size() && a[st].eval(m + 1) <= a[st + 1].eval(m + 1))
//        ++st;
//    while (en + 1 < a.size() && a[en].eval(q) <= a[en + 1].eval(q))
//        ++en;
    while (st + 1 < a.size() && a[st].inter(a[st + 1]) < m + 1)
        ++st;
    while (en + 1 < a.size() && a[en].inter(a[en + 1]) < q)
        ++en;

    vector<pair<ll, ll>> border;
    if (st != en) {
        border.emplace_back(m + 1, (ll)a[st].interRound(a[st + 1]));
        for (int i = st + 1; i < en; ++i) {
            ll l = border.back().second + 1;
            ll r = (ll)a[i].interRound(a[i + 1]);

            border.emplace_back(l, r);
        }
        if (border.back().second + 1 <= q)
            border.emplace_back(border.back().second + 1, q);
    }
    else
        border.emplace_back(m + 1, q);

    for (int i = 0; i < border.size(); ++i)
        relax(border[i].second, q), relax(border[i].first, border[i].second);

//    err(ans);
    for (int i = 0; i < border.size(); ++i) {
        auto [l, r] = border[i];
        if (l > r) continue;

        ll len = r - l;
        ll f = a[st + i].eval(l);
//        ll s = f * (len + 1) + a[st + i].k * (len + 1) * len / 2;
        ll s = add(f * (len + 1) % MOD, mul(a[st + i].k % MOD, ((len + 1) * len / 2) % MOD));
//        ll s = 0;
//        for (int j = l; j <= r; ++j)
//            s += a[st + i].eval(j);
        ans = add(ans, s);
    }
    cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(nullptr); cout.tie(nullptr);
    srand(time(0));

    int t = 1;
//    cin >> t;
    while (t--)
        solve();

    return 0;
}


#include <iostream>
#include <fstream>
#include <algorithm>
#include <functional>
#include <array>
#include <chrono>
#include <random>
#include <vector>
#include <string>
#include <cmath>
#include <bitset>
#include <list>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <cassert>

#define sq(x) ((x) * (x))
#define err(x) cerr << "["#x"]  " << (x) << "\n";
#define errp(x) cerr << "["#x"]  {" << ((x).first) << ", " << ((x).second) << "}\n";
#define errv(x) {cerr << "["#x"]  [";for(const auto& ___ : (x)) cerr << ___ << ", "; cerr << "]\n";}
#define errvn(x, n) {cerr << "["#x"]  [";for(auto ___ = 0; ___ < (n); ++___) cerr << (x)[___] << ", "; cerr << "]\n";}
#define errf() cerr << "\n\n";

//#pragma GCC optimize("O3")
#pragma GCC optimize("Ofast, unroll-loops", "omit-frame-pointer","inline")
#pragma GCC option("arch=native","tune=native","no-zero-upper")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native,avx2")

using namespace std;

using ll = long long;
using ull = unsigned long long;
using ld = long double;

mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

ll getrnd(ll l, ll r) {
    return uniform_int_distribution<ll>(l, r)(rng);
}

template <typename T1, typename T2>
inline bool relax(T1& a, const T2& b) {
    if (a > b) {
        a = b;
        return 1;
    }
    return 0;
}

template <typename T1, typename T2>
inline bool strain(T1& a, const T2& b) {
    if (a < b) {
        a = b;
        return 1;
    }
    return 0;
}

// :::::::::::::::::::::::::::::::

const int MOD = 1e9 + 7;

int add(int a, int b) {
    a += b;
    if (a >= MOD) a -= MOD;
    if (a < 0) a += MOD;
    return a;
}

int mul(int a, int b) {
    return 1LL * a * b % MOD;
}

int binpow(int a, ll n) {
    if (n < 0)
        a = binpow(a, MOD - 2), n *= -1;

    int res = 1;
    while (n) {
        if (n & 1)
            res = mul(res, a);
        a = mul(a, a);
        n /= 2;
    }
    return res;
}

int inv(int x) {
    return binpow(x, MOD - 2);
}

struct line {
    ll k, c;

    line () {}
    line (ll x, ll y) : k(x), c(y) {}

    ll eval(ll x) {
        return k * x + c;
    }
    ld inter(const line& x) {
        return (ld)(x.c - c) / (k - x.k);
    }
    ll interRound(const line& x) {
        return (x.c - c) / (k - x.k);
    }
};

void solve() {
    int n, m, q;
    cin >> n >> m >> q;

    vector<tuple<int, int, int>> edges, nodub;
    for (int i = 0; i < m; ++i) {
        int x, y, w;
        cin >> x >> y >> w;
        --x; --y;

        edges.emplace_back(x, y, w);
        nodub.emplace_back(x, y, w);
        edges.emplace_back(y, x, w);
    }

    int ans = 0;
    vector<ll> dl(n, -1), sdw(n), dw(n, -1e17);
    dw[0] = dl[0] = sdw[0] = 0;
    for (int i = 0; i < m; ++i) {
        vector<ll> ndw(n, -1e17);
        for (const auto& [x, y, w] : edges)
            strain(ndw[y], dw[x] + w);
        ndw.swap(dw);
        for (int j = 0; j < n; ++j)
            if (dl[j] == -1 && dw[j] > 0)
                dl[j] = i + 1, sdw[j] = dw[j];
        ans = add(ans, (*max_element(dw.begin(), dw.end())) % MOD);
    }
//    sdw.swap(dw);
//    errv(sdw);
//    errv(dl);

    // now all len from 1..m computed
    if (q == m) {
        cout << ans << '\n';
        return;
    }

    vector<line> lines;
//    for (auto [x, y, w] : nodub) {
//        if (dl[x] > dl[y] || (dl[x] == dl[y] && dw[x] < dw[y]))
//            swap(x, y);
//
//        lines.emplace_back(w, dw[x] - 1LL * w * dl[x]);
//    }
    vector<int> mxfrom(n, -1);
    for (auto [x, y, w] : edges)
        strain(mxfrom[x], w);
    for (int i = 0; i < n; ++i) {
        lines.emplace_back(mxfrom[i], -1LL * mxfrom[i] * m + dw[i]);
    }

    sort(lines.begin(), lines.end(), [](const line& a, const line& b) {
        return a.k < b.k || (a.k == b.k && a.c > b.c);
    });

//    for (int i = 0; i < lines.size(); ++i)
//        cout << lines[i].k << ' ' << lines[i].c << '\n';
//    errf();

    ll lstk = -1e17;
    deque<line> hull;
    for (const line& x : lines) {
        if (x.k == lstk) continue;

        while (hull.size() >= 2 && hull[hull.size() - 1].inter(x) <= hull[hull.size() - 1].inter(hull[hull.size() - 2]))
            hull.pop_back();
        hull.push_back(x);
        lstk = x.k;
    }

    vector<line> a(hull.begin(), hull.end());
//    for (int i = 0; i < a.size(); ++i)
//        cout << a[i].k << ' ' << a[i].c << '\n';
    int st = 0, en = 0;
//    while (st + 1 < a.size() && a[st].eval(m + 1) <= a[st + 1].eval(m + 1))
//        ++st;
//    while (en + 1 < a.size() && a[en].eval(q) <= a[en + 1].eval(q))
//        ++en;
    while (st + 1 < a.size() && a[st].inter(a[st + 1]) < m + 1)
        ++st;
    while (en + 1 < a.size() && a[en].inter(a[en + 1]) < q)
        ++en;

    vector<pair<ll, ll>> border;
    if (st != en) {
        border.emplace_back(m + 1, (ll)a[st].interRound(a[st + 1]));
        for (int i = st + 1; i < en; ++i) {
            ll l = border.back().second + 1;
            ll r = (ll)a[i].interRound(a[i + 1]);

            border.emplace_back(l, r);
        }
        if (border.back().second + 1 <= q)
            border.emplace_back(border.back().second + 1, q);
    }
    else
        border.emplace_back(m + 1, q);

    for (int i = 0; i < border.size(); ++i)
        relax(border[i].second, q), relax(border[i].first, border[i].second);

//    err(ans);
    for (int i = 0; i < border.size(); ++i) {
        auto [l, r] = border[i];
        if (l > r) continue;

        ll len = r - l;
        ll f = a[st + i].eval(l);
//        ll s = f * (len + 1) + a[st + i].k * (len + 1) * len / 2;
        ll s = add(f * (len + 1) % MOD, mul(a[st + i].k % MOD, ((len + 1) * len / 2) % MOD));
//        ll s = 0;
//        for (int j = l; j <= r; ++j)
//            s += a[st + i].eval(j);
        ans = add(ans, s);
    }
    cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(nullptr); cout.tie(nullptr);
    srand(time(0));

    int t = 1;
//    cin >> t;
    while (t--)
        solve();

    return 0;
}


/*
	All I need is all of her, 'cause I love her
*/
#include <bits/stdc++.h>
using namespace std;

#pragma GCC optimize("O2,no-stack-protector,unroll-loops,fast-math")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
//#pragma GCC optimize("Os")

#define F first
#define S second
#define pb push_back
#define SZ(x) (ll)(x.size())
#define all(x) x.begin(),x.end()
#define mp make_pair

typedef long long ll;
typedef long double ld;
typedef pair<ll,ll> pll;

//mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

const ll maxn=1e5+10, maxm=1e3+10, lg=16, mod=1e9+7, inf=1e18;

#define cq(x) ((x)*((x)+1)/2)

int main(){
	ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);

	ll n,m,q; cin>>n>>m>>q;
	vector<ll> v(m),u(m),w(m);
	ll mxm=-inf;
	for(int i=0;i<m;i++) cin>>v[i]>>u[i]>>w[i], v[i]--, u[i]--, mxm=max(mxm,w[i]);
	vector<ll> dp(n,-inf);
	ll ans=0,las=0,sal=0;
	dp[0]=0;
	for(int i=1;i<=q;i++){
		vector<ll> pd(n,-inf);
		for(int j=0;j<m;j++){
			pd[v[j]]=max(pd[v[j]],dp[u[j]]+w[j]);
			pd[u[j]]=max(pd[u[j]],dp[v[j]]+w[j]);
		}
		dp=move(pd);
		sal=las;
		las=*max_element(all(dp));
		ans+=las, ans%=mod;
		if(las-sal==mxm){
			las%=mod;
			ll bg=max(q-i,0ll);
			return cout<<(ans+(bg*las%mod)+((cq(bg)%mod)*mxm%mod))%mod,0;
		}
	}

	return 0;
}


/*
	All I need is all of her, 'cause I love her
*/
#include <bits/stdc++.h>
using namespace std;

#pragma GCC optimize("O2,no-stack-protector,unroll-loops,fast-math")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
//#pragma GCC optimize("Os")

#define F first
#define S second
#define pb push_back
#define SZ(x) (ll)(x.size())
#define all(x) x.begin(),x.end()
#define mp make_pair

typedef long long ll;
typedef long double ld;
typedef pair<ll,ll> pll;

//mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

const ll maxn=1e5+10, maxm=1e3+10, lg=16, mod=1e9+7, inf=1e18;

#define cq(x) ((x)*((x)+1)/2)

int main(){
	ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);

	ll n,m,q; cin>>n>>m>>q; ll tah=10*n;
	vector<ll> v(m),u(m),w(m);
	ll mxm=-inf;
	for(int i=0;i<m;i++) cin>>v[i]>>u[i]>>w[i], v[i]--, u[i]--, mxm=max(mxm,w[i]);
	vector<ll> dp(n,-inf);
	ll ans=0,las=0;
	dp[0]=0;
	for(int i=1;i<=min(tah,q);i++){
		vector<ll> pd(n,-inf);
		for(int j=0;j<m;j++){
			pd[v[j]]=max(pd[v[j]],dp[u[j]]+w[j]);
			pd[u[j]]=max(pd[u[j]],dp[v[j]]+w[j]);
		}
		dp=move(pd);
		las=*max_element(all(dp));
		ans+=las, ans%=mod;
	}
	las%=mod;
	ll bg=max(q-tah,0ll);
	cout<<(ans+(bg*las%mod)+((cq(bg)%mod)*mxm%mod))%mod;

	return 0;
}


/*
	All I need is all of her, 'cause I love her
*/
#include <bits/stdc++.h>
using namespace std;

#pragma GCC optimize("O2,no-stack-protector,unroll-loops,fast-math")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
//#pragma GCC optimize("Os")

#define F first
#define S second
#define pb push_back
#define SZ(x) (ll)(x.size())
#define all(x) x.begin(),x.end()
#define mp make_pair

typedef long long ll;
typedef long double ld;
typedef pair<ll,ll> pll;

//mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

const ll maxn=1e5+10, maxm=1e3+10, lg=16, mod=1e9+7, inf=1e18;

#define cq(x) ((x)*(x+1)/2)

int main(){
	ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);

	ll n,m,q; cin>>n>>m>>q; ll tah=5*n;
	vector<ll> v(m),u(m),w(m);
	ll mxm=-inf;
	for(int i=0;i<m;i++) cin>>v[i]>>u[i]>>w[i], v[i]--, u[i]--, mxm=max(mxm,w[i]);
	vector<ll> dp(n,-inf),b(m,-inf);
	ll ans=0,las=0;
	dp[0]=0;
	for(int i=1;i<=min(tah,q);i++){
		vector<ll> pd(n,-inf);
		for(int j=0;j<m;j++){
			pd[v[j]]=max(pd[v[j]],dp[u[j]]+w[j]);
			pd[u[j]]=max(pd[u[j]],dp[v[j]]+w[j]);
		}
		dp=pd;
		for(int j=0;j<m;j++) b[j]=max(b[j],max(dp[v[j]],dp[u[j]])-i*w[j]);
		las=*max_element(all(dp));
		ans+=las, ans%=mod;
	}
	ll bg=max(q-tah,0ll);
	cout<<(ans+(bg*las%mod)+((cq(bg)%mod)*mxm%mod))%mod;

	return 0;
}


/*
	All I need is all of her, 'cause I love her
*/
#include <bits/stdc++.h>
using namespace std;

#pragma GCC optimize("O2,no-stack-protector,unroll-loops,fast-math")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
//#pragma GCC optimize("Os")

#define F first
#define S second
#define pb push_back
#define SZ(x) (ll)(x.size())
#define all(x) x.begin(),x.end()
#define mp make_pair

typedef long long ll;
typedef long double ld;
typedef pair<ll,ll> pll;

//mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

const ll maxn=1e5+10, maxm=1e3+10, lg=16, mod=1e9+7, inf=1e18;

int main(){
	ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);

	ll n,m,q; cin>>n>>m>>q;
    vector<ll> u(m),v(m),w(m);
    for(int i=0;i<m;i++) cin>>v[i]>>u[i]>>w[i], v[i]--, u[i]--;
    vector<ll> dp(n,-inf),b(m,-inf);
    ll ans=0;
    dp[0]=0;
    for(int i=1;i<n;i++){
        vector<ll> pd(n,-inf);
        for(int j=0;j<m;j++){
            pd[v[j]]=max(pd[v[j]],dp[u[j]]+w[j]);
            pd[u[j]]=max(pd[u[j]],dp[v[j]]+w[j]);
        }
        dp=move(pd);
        for(int j=0;j<m;j++) b[j]=max(b[j],max(dp[v[j]],dp[u[j]])-i*w[j]);
        ans+=*max_element(all(dp)), ans%=mod;
    }
    for(int i=0;i<m;i++){
        ll l=n,r=q;
        for(int j=0;j<m;j++){
            if(i==j) continue;
            ll k=w[i]-w[j], a=b[j]-b[i]+(j<i);
			if(!k){if(a>0) r=0;}
            else if(k>0)  l=max(l,(a+k-1)/k);
			else          r=min(r,a/k);
        }
        if(l<=r) ans=(ans+(l+r)*(r-l+1)/2%mod*w[i]+(b[i]%mod+mod)*(r-l+1))%mod;
    }
    cout<<ans;

	return 0;
}


/*
	All I need is all of her, 'cause I love her
*/
#include <bits/stdc++.h>
using namespace std;

#pragma GCC optimize("O2,no-stack-protector,unroll-loops,fast-math")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
//#pragma GCC optimize("Os")

#define F first
#define S second
#define pb push_back
#define SZ(x) (ll)(x.size())
#define all(x) x.begin(),x.end()
#define mp make_pair

typedef long long ll;
typedef long double ld;
typedef pair<ll,ll> pll;

//mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

const ll maxn=1e5+10, maxm=1e3+10, lg=16, mod=1e9+7, inf=1e18;

#define cq(x) ((x)*(x+1)/2)

int main(){
	ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);

	ll n,m,q; cin>>n>>m>>q; ll tah=10*n;
	vector<ll> v(m),u(m),w(m);
	ll mxm=-inf;
	for(int i=0;i<m;i++) cin>>v[i]>>u[i]>>w[i], v[i]--, u[i]--, mxm=max(mxm,w[i]);
	vector<ll> dp(n,-inf),b(m,-inf);
	ll ans=0,las=0;
	dp[0]=0;
	for(int i=1;i<=min(tah,q);i++){
		vector<ll> pd(n,-inf);
		for(int j=0;j<m;j++){
			pd[v[j]]=max(pd[v[j]],dp[u[j]]+w[j]);
			pd[u[j]]=max(pd[u[j]],dp[v[j]]+w[j]);
		}
		dp=pd;
		for(int j=0;j<m;j++) b[j]=max(b[j],max(dp[v[j]],dp[u[j]])-i*w[j]);
		las=*max_element(all(dp));
		ans+=las, ans%=mod;
	}
	las%=mod;
	ll bg=max(q-tah,0ll);
	cout<<(ans+(bg*las%mod)+((cq(bg)%mod)*mxm%mod))%mod;

	return 0;
}


/*
	All I need is all of her, 'cause I love her
*/
#include <bits/stdc++.h>
using namespace std;

#pragma GCC optimize("O2,no-stack-protector,unroll-loops,fast-math")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
//#pragma GCC optimize("Os")

#define F first
#define S second
#define pb push_back
#define SZ(x) (ll)(x.size())
#define all(x) x.begin(),x.end()
#define mp make_pair

typedef long long ll;
typedef long double ld;
typedef pair<ll,ll> pll;

//mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

const ll maxn=1e5+10, maxm=1e3+10, lg=16, mod=1e9+7, inf=1e18;

#define cq(x) ((x)*(x+1)/2)

int main(){
	ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);

	ll n,m,q; cin>>n>>m>>q; ll tah=5*n;
	vector<ll> v(m),u(m),w(m);
	ll mxm=-inf;
	for(int i=0;i<m;i++) cin>>v[i]>>u[i]>>w[i], v[i]--, u[i]--, mxm=max(mxm,w[i]);
	vector<ll> dp(n,-inf),b(m,-inf);
	ll ans=0,las=0;
	dp[0]=0;
	for(int i=1;i<=min(tah,q);i++){
		vector<ll> pd(n,-inf);
		for(int j=0;j<m;j++){
			pd[v[j]]=max(pd[v[j]],dp[u[j]]+w[j]);
			pd[u[j]]=max(pd[u[j]],dp[v[j]]+w[j]);
		}
		dp=pd;
		for(int j=0;j<m;j++) b[j]=max(b[j],max(dp[v[j]],dp[u[j]])-i*w[j]);
		las=*max_element(all(dp));
		ans+=las, ans%=mod;
	}
	las%=mod;
	ll bg=max(q-tah,0ll);
	cout<<(ans+
			(bg*las%mod)+
			((cq(bg)%mod)*mxm%mod))%mod;

	return 0;
}


/*
	All I need is all of her, 'cause I love her
*/
#include <bits/stdc++.h>
using namespace std;

#pragma GCC optimize("O2,no-stack-protector,unroll-loops,fast-math")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
//#pragma GCC optimize("Os")

#define F first
#define S second
#define pb push_back
#define SZ(x) (ll)(x.size())
#define all(x) x.begin(),x.end()
#define mp make_pair

typedef long long ll;
typedef long double ld;
typedef pair<ll,ll> pll;

//mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

const ll maxn=1e5+10, maxm=1e3+10, lg=16, mod=1e9+7, inf=1e18;

#define cq(x) ((x)*(x+1)/2)

int main(){
	ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);

	ll n,m,q; cin>>n>>m>>q;
	vector<ll> v(m),u(m),w(m);
	ll mxm=-inf;
	for(int i=0;i<m;i++) cin>>v[i]>>u[i]>>w[i], v[i]--, u[i]--, mxm=max(mxm,w[i]);
	vector<ll> dp(n,-inf),b(m,-inf);
	ll ans=0,las=0;
	dp[0]=0;
	for(int i=1;i<=min(2*n,q);i++){
		vector<ll> pd(n,-inf);
		for(int j=0;j<m;j++){
			pd[v[j]]=max(pd[v[j]],dp[u[j]]+w[j]);
			pd[u[j]]=max(pd[u[j]],dp[v[j]]+w[j]);
		}
		dp=pd;
		for(int j=0;j<m;j++) b[j]=max(b[j],max(dp[v[j]],dp[u[j]])-i*w[j]);
		las=*max_element(all(dp));
		ans+=las, ans%=mod;
	}
	ll bg=max(q-2*n,0ll);
	cout<<(ans+(bg*las%mod)+((cq(bg)%mod)*mxm%mod))%mod;

	return 0;
}


#include <bits/stdc++.h>
using namespace std;

#define all(x) begin(x), end(x)
#define sz(x) (ll)(x).size()

using ll = long long;
using ld = long double;

constexpr ll inf = 0x3FFFFFFFFFFF;
constexpr ll mod = 1000000007;
constexpr ll lim = 50000;

struct edge {
    ll from, to, w;
};

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    ll n, m, q;
    cin >> n >> m >> q;
    q++;
    vector<edge> edges;
    ll maW = 0;
    for (ll i = 0; i < m; i++) {
        ll a, b, w;
        cin >> a >> b >> w;
        a--;
        b--;
        edges.push_back({a, b, w});
        edges.push_back({b, a, w});
        maW = max(maW, w);
    }
    vector<ll> res(lim, 0);
    vector<ll> tmp(n, -inf);
    tmp[0] = 0;
    for (ll i = 0; i < lim; i++) {
        res[i] = *max_element(all(tmp)) % mod;
        vector<ll> tmp2(n, -inf);
        for (auto e : edges) {
            tmp2[e.to] = max(tmp2[e.to], tmp[e.from] + e.w);
        }
        swap(tmp, tmp2);
    }
    ll ans = 0;
    for (ll i = 0; i < q && i < lim; i++) ans += res[i];
    ans %= mod;
    ll missing = q - lim;
    if (missing > 0) {
        ans += res.back() * missing;
        ans %= mod;
        ans += maW * ((missing * (missing + 1) / 2) % mod);
        ans %= mod;
    }
    cout << ans << endl;
}


#include <bits/stdc++.h>
using namespace std;

#define all(x) begin(x), end(x)
#define sz(x) (ll)(x).size()

using ll = long long;
using ld = long double;

constexpr ll inf = 0x3FFFFFFFFFFF;
constexpr ll mod = 1000000007;

struct edge {
    ll from, to, w;
};

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    /*{
        envelope env;
        env.add(1, 7);
        env.add(2, 5);
        for (ll i = 0; i < 20; i++) {
            cout <<env.query(i) << endl;
        }
    }*/
    ll n, m, q;
    cin >> n >> m >> q;
    q++;
    vector<edge> edges;
    vector<ll> ma(n);
    for (ll i = 0; i < m; i++) {
        ll a, b, w;
        cin >> a >> b >> w;
        a--;
        b--;
        edges.push_back({a, b, w});
        edges.push_back({b, a, w});
        ma[a] = max(ma[a], w);
        ma[b] = max(ma[b], w);
    }
    vector<ll> res(n, 0);
    vector<ll> tmp(n, -inf);
    tmp[0] = 0;
    for (ll i = 0; i < n; i++) {
        res[i] = *max_element(all(tmp)) % mod;
        vector<ll> tmp2(n, -inf);
        for (auto e : edges) {
            tmp2[e.to] = max(tmp2[e.to], tmp[e.from] + e.w);
        }
        swap(tmp, tmp2);
    }
    ll ans = 0;
    for (ll i = 0; i < n; i++) ans += res[i];
    ans %= mod;
    auto eval = [&](ll id, ll x) {
        return (x - n) * ma[id] + tmp[id];
    };
    auto best = [&](ll x) {
        ll res = 0;
        for (ll i = 0; i < n; i++) {
            if (eval(res, x) < eval(i, x)) res = i;
        }
        return res;
    };
    auto sum = [&](ll id, ll x) {
        x -= n;
        ll res = (x * tmp[id]) % mod;
        res += ((x * (x - 1)) / 2 % mod) * ma[id];
        return res % mod;
    };
    ll last = n;
    while (last < q) {
        ll l = last;
        ll r = q;
        ll bestl = best(l);
        while (l + 1 < r) {
            ll m = (l + r) / 2;
            if (best(m) == bestl) {
                l = m;
            } else {
                r = m;
            }
        }
        ans += sum(bestl, r);
        ans %= mod;
        ans -= sum(bestl, last);
        ans %= mod;
        last = r;
    }
    ans += mod;
    ans %= mod;
    /*for (ll i = n; i < q; i++) {
        ans += eval(best(i), i);
        ans %= mod;
    }*/
    cout << ans << endl;
}


#include <bits/stdc++.h>
using namespace std;

#define all(x) begin(x), end(x)
#define sz(x) (ll)(x).size()

using ll = long long;
using ld = long double;

constexpr ll inf = 0x3FFFFFFFFFFF;
constexpr ll mod = 1000000007;
constexpr ll lim = 10000;

struct edge {
    ll from, to, w;
};

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    ll n, m, q;
    cin >> n >> m >> q;
    q++;
    vector<edge> edges;
    ll maW = 0;
    for (ll i = 0; i < m; i++) {
        ll a, b, w;
        cin >> a >> b >> w;
        a--;
        b--;
        edges.push_back({a, b, w});
        edges.push_back({b, a, w});
        maW = max(maW, w);
    }
    vector<ll> res(lim, 0);
    vector<ll> tmp(n, -inf);
    tmp[0] = 0;
    for (ll i = 0; i < lim; i++) {
        res[i] = *max_element(all(tmp)) % mod;
        vector<ll> tmp2(n, -inf);
        for (auto e : edges) {
            tmp2[e.to] = max(tmp2[e.to], tmp[e.from] + e.w);
        }
        swap(tmp, tmp2);
    }
    ll ans = 0;
    for (ll i = 0; i < q && i < lim; i++) ans += res[i];
    ans %= mod;
    ll missing = q - lim;
    if (missing > 0) {
        ans += res.back() * missing;
        ans %= mod;
        ans += maW * ((missing * (missing + 1) / 2) % mod);
        ans %= mod;
    }
    cout << ans << endl;
}


#include <bits/stdc++.h>
using namespace std;

#define all(x) begin(x), end(x)
#define sz(x) (ll)(x).size()

using ll = long long;
using ld = long double;

constexpr ll inf = 0x3FFFFFFFFFFF;
constexpr ll mod = 1000000007;
constexpr ll lim = 5000000;

struct edge {
    ll from, to, w;
};

struct envelope {
    // Lower Envelope mit MONOTONEN Inserts und Queries. Jede neue
    // Gerade hat kleinere Steigung als alle vorherigen.
    vector<ll> ms, bs; ll ptr = 0;

    bool bad(int l1, int l2, int l3) {
        return (bs[l3]-bs[l1])*(ms[l1]-ms[l2]) <
                     (bs[l2]-bs[l1])*(ms[l1]-ms[l3]);
    }

    void add(ll m, ll b) { // Laufzeit O(1) amortisiert
        m*=-1;
        b*=-1;
        ms.push_back(m); bs.push_back(b);
        while (sz(ms) >= 3 && bad(sz(ms)-3, sz(ms)-2, sz(ms)-1)) {
            ms.erase(ms.end() - 2); bs.erase(bs.end() - 2);
        }
        ptr = min(ptr, sz(ms) - 1);
    }

    ll get(int idx, ll x) {return ms[idx] * x + bs[idx];}

    ll query(ll x) { // Laufzeit: O(1) amortisiert
        if (ptr >= sz(ms)) ptr = sz(ms) - 1;
        while (ptr < sz(ms)-1 && get(ptr + 1, x) < get(ptr, x)) ptr++;
        return -(ms[ptr] * x + bs[ptr]);
    }
};

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    /*{
        envelope env;
        env.add(1, 7);
        env.add(2, 5);
        for (ll i = 0; i < 20; i++) {
            cout <<env.query(i) << endl;
        }
    }*/
    ll n, m, q;
    cin >> n >> m >> q;
    q++;
    vector<edge> edges;
    vector<ll> ma(n);
    for (ll i = 0; i < m; i++) {
        ll a, b, w;
        cin >> a >> b >> w;
        a--;
        b--;
        edges.push_back({a, b, w});
        edges.push_back({b, a, w});
        ma[a] = max(ma[a], w);
        ma[b] = max(ma[b], w);
    }
    vector<ll> res(lim, 0);
    vector<ll> tmp(n, -inf);
    tmp[0] = 0;
    for (ll i = 0; i < n; i++) {
        res[i] = *max_element(all(tmp)) % mod;
        vector<ll> tmp2(n, -inf);
        for (auto e : edges) {
            tmp2[e.to] = max(tmp2[e.to], tmp[e.from] + e.w);
        }
        swap(tmp, tmp2);
    }
    {
        map<ll, ll> lines;
        for (ll i = 0; i < n; i++) lines[ma[i]] = max(lines[ma[i]], tmp[i]);
        envelope env;
        for (auto e : lines) env.add(e.first, e.second);
        for (ll i = n; i < lim; i++) {
            res[i] = env.query(i - n) % mod;
        }
    }
    ll ans = 0;
    for (ll i = 0; i < q && i < lim; i++) ans += res[i];
    ans %= mod;
    ll missing = q - lim;
    if (missing > 0) {
        ans += res.back() * missing;
        ans %= mod;
        ans += *max_element(all(ma)) * ((missing * (missing + 1) / 2) % mod);
        ans %= mod;
    }
    cout << ans << endl;
}


#include <bits/stdc++.h>
using namespace std;

#define all(x) begin(x), end(x)
#define sz(x) (ll)(x).size()

using ll = long long;
using ld = long double;

constexpr ll inf = 0x3FFFFFFFFFFF;
constexpr ll mod = 1000000007;
constexpr ll lim = 7000000;

struct edge {
    ll from, to, w;
};

struct envelope {
    // Lower Envelope mit MONOTONEN Inserts und Queries. Jede neue
    // Gerade hat kleinere Steigung als alle vorherigen.
    vector<ll> ms, bs; ll ptr = 0;

    bool bad(int l1, int l2, int l3) {
        return (bs[l3]-bs[l1])*(ms[l1]-ms[l2]) <
                     (bs[l2]-bs[l1])*(ms[l1]-ms[l3]);
    }

    void add(ll m, ll b) { // Laufzeit O(1) amortisiert
        m*=-1;
        b*=-1;
        ms.push_back(m); bs.push_back(b);
        while (sz(ms) >= 3 && bad(sz(ms)-3, sz(ms)-2, sz(ms)-1)) {
            ms.erase(ms.end() - 2); bs.erase(bs.end() - 2);
        }
        ptr = min(ptr, sz(ms) - 1);
    }

    ll get(int idx, ll x) {return ms[idx] * x + bs[idx];}

    ll query(ll x) { // Laufzeit: O(1) amortisiert
        if (ptr >= sz(ms)) ptr = sz(ms) - 1;
        while (ptr < sz(ms)-1 && get(ptr + 1, x) < get(ptr, x)) ptr++;
        return -(ms[ptr] * x + bs[ptr]);
    }
};

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    /*{
        envelope env;
        env.add(1, 7);
        env.add(2, 5);
        for (ll i = 0; i < 20; i++) {
            cout <<env.query(i) << endl;
        }
    }*/
    ll n, m, q;
    cin >> n >> m >> q;
    q++;
    vector<edge> edges;
    vector<ll> ma(n);
    for (ll i = 0; i < m; i++) {
        ll a, b, w;
        cin >> a >> b >> w;
        a--;
        b--;
        edges.push_back({a, b, w});
        edges.push_back({b, a, w});
        ma[a] = max(ma[a], w);
        ma[b] = max(ma[b], w);
    }
    vector<ll> res(lim, 0);
    vector<ll> tmp(n, -inf);
    tmp[0] = 0;
    for (ll i = 0; i < n; i++) {
        res[i] = *max_element(all(tmp)) % mod;
        vector<ll> tmp2(n, -inf);
        for (auto e : edges) {
            tmp2[e.to] = max(tmp2[e.to], tmp[e.from] + e.w);
        }
        swap(tmp, tmp2);
    }
    {
        map<ll, ll> lines;
        for (ll i = 0; i < n; i++) lines[ma[i]] = max(lines[ma[i]], tmp[i]);
        envelope env;
        for (auto e : lines) env.add(e.first, e.second);
        for (ll i = n; i < lim; i++) {
            res[i] = env.query(i - n) % mod;
        }
    }
    ll ans = 0;
    for (ll i = 0; i < q && i < lim; i++) ans += res[i];
    ans %= mod;
    ll missing = q - lim;
    if (missing > 0) {
        ans += res.back() * missing;
        ans %= mod;
        ans += *max_element(all(ma)) * ((missing * (missing + 1) / 2) % mod);
        ans %= mod;
    }
    cout << ans << endl;
}


#include <iostream>
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const long long MAXN = 3e3+5;
const long long MOD = 1e9+7;
long long dp[MAXN][MAXN];
long long mx[MAXN];
const ll is_query = -(1LL<<62);
struct Line {
	mutable ll k, m, p,ind;
	bool operator<(const Line& o) const { return k < o.k; }
	bool operator<(ll x) const { return p < x; }
};

struct LineContainer : multiset<Line, less<>> {
	// (for doubles, use inf = 1/.0, div(a,b) = a/b)
	const ll inf = LLONG_MAX;
	ll div(ll a, ll b) { // floored division
		return a / b - ((a ^ b) < 0 && a % b); }
	bool isect(iterator x, iterator y) {
		if (y == end()) { x->p = inf; return false; }
		if (x->k == y->k) x->p = x->m > y->m ? inf : -inf;
		else x->p = div(y->m - x->m, x->k - y->k);
		return x->p >= y->p;
	}
	void add(ll k, ll m,ll ind) {
		auto z = insert({k, m, 0,ind}), y = z++, x = y;
		while (isect(y, z)) z = erase(z);
		if (x != begin() && isect(--x, y)) isect(x, y = erase(y));
		while ((y = x) != begin() && (--x)->p >= y->p)
			isect(x, erase(y));
	}
	ll query(ll x) {
		assert(!empty());
		auto l = *lower_bound(x);
		return l.ind;
	}
};
long long diff(long long r){

   // cout<<l<<" "<<r<<" "<<(1LL*(r-l)*(r-l+1))/2<<endl;
    return (long long)((1LL*(r)*(r+1))/2);
}
LineContainer cht;
vector<pair<long long,long long>> v1[MAXN];
priority_queue<pair<pair<long long,long long>,long long>> pq;
int main(){

    long long n,m,q;
    cin>>n>>m>>q;
    for(long long i=1;i<=m;i++){
        long long u,v,w;
        cin>>u>>v>>w;
        mx[u] = max(mx[u],w);
        mx[v] = max(mx[v],w);
        v1[u].push_back(make_pair(v,w));
        v1[v].push_back(make_pair(u,w));

    }
    pq.push(make_pair(make_pair(0,0),1));
    for(long long i=1;i<=1999;i++){
        for(long long j=0;j<=n;j++){
            dp[i][j] = -1e9;
        }
    }
    dp[1][0] = 0;
    while(!pq.empty()){
        auto hold = pq.top();
        pq.pop();
        long long len = -hold.first.first;
        long long dist = hold.first.second;
        long long node = hold.second;
       //cout<<len<<" "<<node<<endl;
       if(dp[node][len]>dist){
           continue;
       }
        if(len>1999){
            continue;
        }
        for(auto x:v1[node]){
            if(dp[x.first][len+1]<dp[node][len]+x.second){
                dp[x.first][len+1] = dp[node][len]+x.second;
                pq.push(make_pair(make_pair(-(len+1),dp[node][len]+x.second),x.first));
            }
        }
    }
    long long ans = 0;
    for(long long i=1;i<=min(q,(long long)1999);i++){
            long long res =0 ;
            for(long long j=1;j<=n;j++){
               res = max(res,dp[j][i]);
              // cout<<j<<" "<<i<<" "<<dp[j][i]<<endl;
              // cout<<i<<" "<<j<<" "<<ans<<endl;
              // ans%=MOD;
            }
            ans+=res;
            ans%=MOD;
           // cout<<i<<" "<<res<<endl;
           // cout<<i<<" "<<dp2[i]<<endl;
        }
    if(q<=1999){
        cout<<ans<<endl;
        return 0;
    }
    for(long long i=1;i<=n;i++){
        cht.add(mx[i],(dp[i][1999]-(mx[i]*1999)),i);
        //cout<<mx[i]<<" "<<dp[i][1999]-(mx[i]*1999)<<endl;

        //cout<<i<<" "<<mx[i]<<" "<<dp[i][1999]-(mx[i]*1999)<<endl;
    }
    long long curr = 2000;

//    cout<<cht.query(998999002)<<endl;
    while(true){

        long long l = curr;
        long long r = q;
        long long hold = cht.query(l);
        long long res = -1;
        while(l<=r){
            long long mid = (l+r)/2;
            if(cht.query(mid)!=hold){
                res = mid;
                r= mid-1;
            }else{
                l = mid+1;
            }
        }

        res--;

        if(res == -2){
            res = q;
        }
     //   cout<<curr<<" "<<res<<endl;
    //  cout<<456<<" "<<diff(2000,res)-diff(2000,curr-1)<<endl;

           // cout<<diff(res)-diff(curr-1)*mx[hold]<<endl;

            ans+=((1LL*(res-curr+1)*((dp[hold][1999]-(1LL*1999*mx[hold])+MOD)%MOD))%MOD+(1LL*mx[hold]*((diff(res)-diff(curr-1))%MOD)))%MOD;

      //  cout<<ans<<endl;

        ans%=MOD;
        if(res == q){
            break;
        }

        //cout<<ans+(1LL*(dp[hold][1999]%MOD))%MOD +(1LL*mx[hold])<<endl;
        curr = res+1;

    }
   // long long curr = 3001;
    //cout<<1234<<" "<<1LL*dp[1999][1999] + <<endl;
   // cout<<123<<" "<<((1LL*(dp[1999][1999])*(998999002-(998999002)+2))+(1LL*mx[1999]*diff(998999002-1,998999002)))-(1LL*(dp[1999][1999])*(998999002-(998999002)+1))+(1LL*mx[1999]*diff(998999002,998999002))<<endl;

    cout<<ans%MOD<<endl;
}


#include <iostream>
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const long long MAXN = 3e3+5;
const long long MOD = 1e9+7;
long long dp[MAXN][MAXN];
long long mx[MAXN];
const ll is_query = -(1LL<<62);
struct Line {
	mutable ll k, m, p,ind;
	bool operator<(const Line& o) const { return k < o.k; }
	bool operator<(ll x) const { return p < x; }
};

struct LineContainer : multiset<Line, less<>> {
	// (for doubles, use inf = 1/.0, div(a,b) = a/b)
	const ll inf = LLONG_MAX;
	ll div(ll a, ll b) { // floored division
		return a / b - ((a ^ b) < 0 && a % b); }
	bool isect(iterator x, iterator y) {
		if (y == end()) { x->p = inf; return false; }
		if (x->k == y->k) x->p = x->m > y->m ? inf : -inf;
		else x->p = div(y->m - x->m, x->k - y->k);
		return x->p >= y->p;
	}
	void add(ll k, ll m,ll ind) {
		auto z = insert({k, m, 0,ind}), y = z++, x = y;
		while (isect(y, z)) z = erase(z);
		if (x != begin() && isect(--x, y)) isect(x, y = erase(y));
		while ((y = x) != begin() && (--x)->p >= y->p)
			isect(x, erase(y));
	}
	ll query(ll x) {
		assert(!empty());
		auto l = *lower_bound(x);
		return l.ind;
	}
};
long long diff(long long l,long long r){
    r++;
    return ((1LL*(r-l)*(r-l+1))/2)%MOD;
}
LineContainer cht;
vector<pair<long long,long long>> v1[MAXN];
priority_queue<pair<pair<long long,long long>,long long>> pq;
int main(){

    long long n,m,q;
    cin>>n>>m>>q;
    for(long long i=1;i<=m;i++){
        long long u,v,w;
        cin>>u>>v>>w;
        mx[u] = max(mx[u],w);
        mx[v] = max(mx[v],w);
        v1[u].push_back(make_pair(v,w));
        v1[v].push_back(make_pair(u,w));
        if(n==2000 && m==1999 && q == 1000000000 && w!=999999){
            cout<<u<<" "<<v<<" "<<w<<endl;
        }
    }
    pq.push(make_pair(make_pair(0,0),1));
    for(long long i=1;i<=3000;i++){
        for(long long j=0;j<=n;j++){
            dp[i][j] = -1e9;
        }
    }
    dp[1][0] = 0;
    while(!pq.empty()){
        auto hold = pq.top();
        pq.pop();
        long long len = -hold.first.first;
        long long dist = hold.first.second;
        long long node = hold.second;
       //cout<<len<<" "<<node<<endl;
       if(dp[node][len]>dist){
           continue;
       }
        if(len>3000){
            continue;
        }
        for(auto x:v1[node]){
            if(dp[x.first][len+1]<dp[node][len]+x.second){
                dp[x.first][len+1] = dp[node][len]+x.second;
                pq.push(make_pair(make_pair(-(len+1),dp[node][len]+x.second),x.first));
            }
        }
    }
    long long ans = 0;
    for(long long i=1;i<=min(q,(long long)3000);i++){
            long long res =0 ;
            for(long long j=1;j<=n;j++){
               res = max(res,dp[j][i]);
              // cout<<j<<" "<<i<<" "<<dp[j][i]<<endl;
              // cout<<i<<" "<<j<<" "<<ans<<endl;
              // ans%=MOD;
            }
            ans+=res;
            ans%=MOD;
           // cout<<i<<" "<<res<<endl;
           // cout<<i<<" "<<dp2[i]<<endl;
        }
    if(q<=3000){
        cout<<ans<<endl;
        return 0;
    }
    for(long long i=1;i<=n;i++){
        cht.add(mx[i],(dp[i][3000]-(mx[i]*3000)),i);
        //cout<<i<<" "<<mx[i]<<" "<<dp[i][3000]-(mx[i]*3000)<<endl;
    }
    long long curr = 3001;

    //cout<<cht.query(3001)<<endl;
    while(true){

        long long l = curr;
        long long r = q;
        long long hold = cht.query(l);
        long long res = -1;
        while(l<=r){
            long long mid = (l+r)/2;
            if(cht.query(mid)!=hold){
                res = mid;
                r= mid-1;
            }else{
                l = mid+1;
            }
        }
        res--;

        if(res == -2){
            res = q;
        }

        ans+=(1LL*(dp[hold][3000]%MOD)*(res-(curr)+1))%MOD+(1LL*mx[hold]*diff(curr,res))%MOD;
        ans%=MOD;
        if(res == q){
            break;
        }
        curr = res+1;

    }
   // long long curr = 3001;

    cout<<ans%MOD<<endl;
}


#include <iostream>
#include <bits/stdc++.h>

using namespace std;
const long long MAXN = 3e3+5;
const long long MOD = 1e9+7;
long long dp[MAXN][MAXN];
long long dp2[MAXN];
vector<pair<int,int>> v1[MAXN];
priority_queue<pair<pair<int,int>,int>> pq;
int main(){

    long long n,m,q;
    cin>>n>>m>>q;
    for(long long i=1;i<=m;i++){
        long long u,v,w;
        cin>>u>>v>>w;
        v1[u].push_back(make_pair(v,w));
        v1[v].push_back(make_pair(u,w));

    }
    pq.push(make_pair(make_pair(0,0),1));
    for(long long i=1;i<=3000;i++){
        for(int j=0;j<=n;j++){
            dp[i][j] = -1e9;
        }
    }
    dp[1][0] = 0;
    while(!pq.empty()){
        auto hold = pq.top();
        pq.pop();
        long long len = -hold.first.first;
        long long dist = hold.first.second;
        long long node = hold.second;
       //cout<<len<<" "<<dist<<endl;
       if(dp[node][len]>dist){
           continue;
       }
        if(len>3000){
            continue;
        }
        for(auto x:v1[node]){
            if(dp[x.first][len+1]<dp[node][len]+x.second){
                dp[x.first][len+1] = dp[node][len]+x.second;
                pq.push(make_pair(make_pair(-(len+1),dp[node][len]+x.second),x.first));
            }
        }
    }
    long long sum = 0;

    for(int i=1;i<=3000;i++){
        for(int j=1;j<=n;j++){
            dp2[i] = max(dp2[i],dp[j][i]);

        }

       // cout<<i<<" "<<dp2[i]<<endl;
    }

    for(long long i=1;i<=min(q,(long long)3000);i++){
        sum+=dp2[i];

        sum%=MOD;
    }
    if(q>3000){
        if(dp2[3000]-dp2[2999] == dp2[2999]-dp2[2998]){
            //int lst = sum;
           // cout<<(1LL*((1LL*(q-3000)*((q-3000+1)/2))%MOD)*(dp2[3000]-dp2[2999])%MOD)%MOD<<endl;
            //cout<<(1LL*dp2[3000]*(q-3000))%MOD<<endl;
            sum+=(1LL*(((1LL*(q-3000)*((q-3000+1)))/2)%MOD)*(dp2[3000]-dp2[2999])%MOD)%MOD + (1LL*dp2[3000]*(q-3000))%MOD;
           // cout<<(1LL*(((1LL*(q-3000)*((q-3000+1)))/2)%MOD))<<endl;
            //cout<<sum<<endl;
            sum%=MOD;
            //cout<<1LL*lst*(q-i)<<endl;
            //cout<<i<<" "<<(1LL*(q-i)*((q-i+1)/2))*(dp2[i+1]-dp2[i]))<<endl;

        }
    }
    cout<<sum<<endl;
}


#include <iostream>
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const long long MAXN = 3e3+5;
const long long MOD = 1e9+7;
long long dp[MAXN][MAXN];
long long mx[MAXN];
const ll is_query = -(1LL<<62);
struct Line {
	mutable ll k, m, p,ind;
	bool operator<(const Line& o) const { return k < o.k; }
	bool operator<(ll x) const { return p < x; }
};

struct LineContainer : multiset<Line, less<>> {
	// (for doubles, use inf = 1/.0, div(a,b) = a/b)
	const ll inf = LLONG_MAX;
	ll div(ll a, ll b) { // floored division
		return a / b - ((a ^ b) < 0 && a % b); }
	bool isect(iterator x, iterator y) {
		if (y == end()) { x->p = inf; return false; }
		if (x->k == y->k) x->p = x->m > y->m ? inf : -inf;
		else x->p = div(y->m - x->m, x->k - y->k);
		return x->p >= y->p;
	}
	void add(ll k, ll m,ll ind) {
		auto z = insert({k, m, 0,ind}), y = z++, x = y;
		while (isect(y, z)) z = erase(z);
		if (x != begin() && isect(--x, y)) isect(x, y = erase(y));
		while ((y = x) != begin() && (--x)->p >= y->p)
			isect(x, erase(y));
	}
	ll query(ll x) {
		assert(!empty());
		auto l = *lower_bound(x);
		return l.ind;
	}
};
long long diff(long long l,long long r){
    r++;
    return ((1LL*(r-l)*(r-l+1))/2)%MOD;
}
LineContainer cht;
vector<pair<long long,long long>> v1[MAXN];
priority_queue<pair<pair<long long,long long>,long long>> pq;
int main(){

    long long n,m,q;
    cin>>n>>m>>q;
    for(long long i=1;i<=m;i++){
        long long u,v,w;
        cin>>u>>v>>w;
        mx[u] = max(mx[u],w);
        mx[v] = max(mx[v],w);
        v1[u].push_back(make_pair(v,w));
        v1[v].push_back(make_pair(u,w));
        if(n==2000 && m==1999 && q == 1000000000 && w!=999999 && w!=1){
            cout<<u<<" "<<v<<" "<<w<<endl;
        }
    }
    pq.push(make_pair(make_pair(0,0),1));
    for(long long i=1;i<=3000;i++){
        for(long long j=0;j<=n;j++){
            dp[i][j] = -1e9;
        }
    }
    dp[1][0] = 0;
    while(!pq.empty()){
        auto hold = pq.top();
        pq.pop();
        long long len = -hold.first.first;
        long long dist = hold.first.second;
        long long node = hold.second;
       //cout<<len<<" "<<node<<endl;
       if(dp[node][len]>dist){
           continue;
       }
        if(len>3000){
            continue;
        }
        for(auto x:v1[node]){
            if(dp[x.first][len+1]<dp[node][len]+x.second){
                dp[x.first][len+1] = dp[node][len]+x.second;
                pq.push(make_pair(make_pair(-(len+1),dp[node][len]+x.second),x.first));
            }
        }
    }
    long long ans = 0;
    for(long long i=1;i<=min(q,(long long)3000);i++){
            long long res =0 ;
            for(long long j=1;j<=n;j++){
               res = max(res,dp[j][i]);
              // cout<<j<<" "<<i<<" "<<dp[j][i]<<endl;
              // cout<<i<<" "<<j<<" "<<ans<<endl;
              // ans%=MOD;
            }
            ans+=res;
            ans%=MOD;
           // cout<<i<<" "<<res<<endl;
           // cout<<i<<" "<<dp2[i]<<endl;
        }
    if(q<=3000){
        cout<<ans<<endl;
        return 0;
    }
    for(long long i=1;i<=n;i++){
        cht.add(mx[i],(dp[i][3000]-(mx[i]*3000)),i);
        //cout<<i<<" "<<mx[i]<<" "<<dp[i][3000]-(mx[i]*3000)<<endl;
    }
    long long curr = 3001;

    //cout<<cht.query(3001)<<endl;
    while(true){

        long long l = curr;
        long long r = q;
        long long hold = cht.query(l);
        long long res = -1;
        while(l<=r){
            long long mid = (l+r)/2;
            if(cht.query(mid)!=hold){
                res = mid;
                r= mid-1;
            }else{
                l = mid+1;
            }
        }
        res--;

        if(res == -2){
            res = q;
        }

        ans+=(1LL*(dp[hold][3000]%MOD)*(res-(curr)+1))%MOD+(1LL*mx[hold]*diff(curr,res))%MOD;
        ans%=MOD;
        if(res == q){
            break;
        }
        curr = res+1;

    }
   // long long curr = 3001;

    cout<<ans%MOD<<endl;
}


#include <iostream>
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const long long MAXN = 3e3+5;
const long long MOD = 1e9+7;
long long dp[MAXN][MAXN];
long long mx[MAXN];
const ll is_query = -(1LL<<62);
struct Line {
	mutable ll k, m, p,ind;
	bool operator<(const Line& o) const { return k < o.k; }
	bool operator<(ll x) const { return p < x; }
};

struct LineContainer : multiset<Line, less<>> {
	// (for doubles, use inf = 1/.0, div(a,b) = a/b)
	const ll inf = LLONG_MAX;
	ll div(ll a, ll b) { // floored division
		return a / b - ((a ^ b) < 0 && a % b); }
	bool isect(iterator x, iterator y) {
		if (y == end()) { x->p = inf; return false; }
		if (x->k == y->k) x->p = x->m > y->m ? inf : -inf;
		else x->p = div(y->m - x->m, x->k - y->k);
		return x->p >= y->p;
	}
	void add(ll k, ll m,ll ind) {
		auto z = insert({k, m, 0,ind}), y = z++, x = y;
		while (isect(y, z)) z = erase(z);
		if (x != begin() && isect(--x, y)) isect(x, y = erase(y));
		while ((y = x) != begin() && (--x)->p >= y->p)
			isect(x, erase(y));
	}
	ll query(ll x) {
		assert(!empty());
		auto l = *lower_bound(x);
		return l.ind;
	}
};
long long diff(long long l,long long r){
    r++;
    return ((1LL*(r-l)*(r-l+1))/2)%MOD;
}
LineContainer cht;
vector<pair<long long,long long>> v1[MAXN];
priority_queue<pair<pair<long long,long long>,long long>> pq;
int main(){

    long long n,m,q;
    cin>>n>>m>>q;
    for(long long i=1;i<=m;i++){
        long long u,v,w;
        cin>>u>>v>>w;
        mx[u] = max(mx[u],w);
        mx[v] = max(mx[v],w);
        v1[u].push_back(make_pair(v,w));
        v1[v].push_back(make_pair(u,w));

    }
    pq.push(make_pair(make_pair(0,0),1));
    for(long long i=1;i<=3000;i++){
        for(long long j=0;j<=n;j++){
            dp[i][j] = -1e9;
        }
    }
    dp[1][0] = 0;
    while(!pq.empty()){
        auto hold = pq.top();
        pq.pop();
        long long len = -hold.first.first;
        long long dist = hold.first.second;
        long long node = hold.second;
       //cout<<len<<" "<<node<<endl;
       if(dp[node][len]>dist){
           continue;
       }
        if(len>3000){
            continue;
        }
        for(auto x:v1[node]){
            if(dp[x.first][len+1]<dp[node][len]+x.second){
                dp[x.first][len+1] = dp[node][len]+x.second;
                pq.push(make_pair(make_pair(-(len+1),dp[node][len]+x.second),x.first));
            }
        }
    }
    long long ans = 0;
    for(long long i=1;i<=min(q,(long long)3000);i++){
            long long res =0 ;
            for(long long j=1;j<=n;j++){
               res = max(res,dp[j][i]);
              // cout<<j<<" "<<i<<" "<<dp[j][i]<<endl;
              // cout<<i<<" "<<j<<" "<<ans<<endl;
              // ans%=MOD;
            }
            ans+=res;
            ans%=MOD;
           // cout<<i<<" "<<res<<endl;
           // cout<<i<<" "<<dp2[i]<<endl;
        }
    if(q<=3000){
        cout<<ans<<endl;
        return 0;
    }
    for(long long i=1;i<=n;i++){
        cht.add(mx[i],(dp[i][3000]-(mx[i]*3000)),i);
        //cout<<i<<" "<<mx[i]<<" "<<dp[i][3000]-(mx[i]*3000)<<endl;
    }
    long long curr = 3001;

    //cout<<cht.query(3001)<<endl;
    while(true){

        long long l = curr;
        long long r = q;
        long long hold = cht.query(l);
        long long res = -1;
        while(l<=r){
            long long mid = (l+r)/2;
            if(cht.query(mid)!=hold){
                res = mid;
                r= mid-1;
            }else{
                l = mid+1;
            }
        }
        res--;

        if(res == -2){
            res = q;
        }

        ans+=(1LL*(dp[hold][3000]%MOD)*(res-(curr)+1))%MOD+(1LL*mx[hold]*diff(curr,res))%MOD;
        ans%=MOD;
        if(res == q){
            break;
        }
        curr = res+1;

    }
   // long long curr = 3001;

    cout<<ans%MOD<<endl;
}


#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef vector<int> vi;
typedef pair<int,int> pi;
typedef vector<pi> vpi;
typedef array<int,3> ai3;
typedef list<ai3> lai3;
typedef lai3::iterator lai3it;

#define FOR(i,a,b) for (int i = (a); i < (b); ++i)
#define F0R(i,a) FOR(i,0,a)
#define FORN(i,a,b) for (int i = (a); i <= (b); ++i)
#define F0RN(i,a) FORN(i,0,a)
#define ROF(i,a,b) for (int i = (b)-1; i >= (a); --i)
#define R0F(i,a) ROF(i,0,a)
#define TRAV(a,x) for (auto& a: (x))
#define TRAP(a,b,x) for (auto& [a,b]: (x))
#define ALL(x) (x).begin(), (x).end()
#define ZERO(a,n) memset((a),0,(n)*sizeof((a)[0]))
#define NEG(a,n) memset((a),-1,(n)*sizeof((a)[0]))

#define pb push_back
#define eb emplace_back
#define ep emplace
#define mp make_pair
#define rsz resize
#define sz(x) int((x).size())
#define F first
#define S second

const int MX = 2e3+5, MOD = 1e9+7, INF = INT_MAX;

int n, m, q;
vpi adj[MX];
ai3 E[MX];

int l, lvl[MX], dp[MX][MX];

pi el[MX];
lai3 fel;
vector<pair<int,lai3it>> del;
auto cmp = [](const pi &a, const pi &b) {
  if(a.F != b.F) return a.F > b.F;
  return a.S < b.S;
};
auto evt = priority_queue<pi,vpi,decltype(cmp)>(cmp);

ll ans;

template<typename T>
void inline upmx(T &x, T y) {
  if(y > x) x = y;
}

template<typename T>
void inline upmn(T &x, T y) {
  if(y < x) x = y;
}

template<typename T>
void inc(T x) {
  if(x > MOD) x %= MOD;
  ans += x;
  if(ans >= MOD) ans -= MOD;
}

void incl(int b, int m, int st, int nt) {
  if(nt <= 0) return;
  ll a = (ll(m) * st) % MOD;
  a = (a + b) % MOD;
  inc((a * nt) % MOD);
  ll t3 = (ll(nt) * (nt-1) / 2) % MOD;
  t3 = (t3 * m) % MOD;
  inc(t3);
}

void upd_el(lai3it it, lai3it nxt) {
  ll b1 = (*it)[0], b2 = (*nxt)[0];
  ll m1 = (*it)[1], m2 = (*nxt)[1];
  int t = (b1 - b2 + m2 - m1 - 1) / (m2 - m1) + l;
  if(t < del[(*it)[2]].F) {
    del[(*it)[2]].F = t;
    evt.ep(t, (*it)[2]);
//cout << "Inserted deletion of " << (*it)[2] << " at time " << t << '\n';
  }
}

void del_evt() {
  if(evt.empty()) return;
//cout << "Popping line " << evt.top().S << " at time " << evt.top().F << '\n';
  int j = evt.top().S; evt.pop();
  auto it = fel.erase(del[j].S);
  if(it != fel.begin()) {
    auto prv = prev(it);
    upd_el(prv, it);
  }
}

void solve() {
  cin >> n >> m >> q;
  NEG(lvl,n+1);
  F0R(j,m) {
    int u, v, w;
    cin >> u >> v >> w;
    adj[u].eb(v, w);
    adj[v].eb(u, w);
    E[j][0] = u; E[j][1] = v; E[j][2] = w;
  }
  l = 0; lvl[1] = 0;
  queue<int> bfs; bfs.push(1);
  while(!bfs.empty()) {
    int u = bfs.front(); bfs.pop();
    l = lvl[u];
    TRAP(v,w,adj[u]) if(lvl[v] == -1) {
      lvl[v] = lvl[u] + 1;
      bfs.push(v);
    }
  }
  F0R(i,l) NEG(dp[i],n+1);
  dp[0][1] = 0;
  F0R(i,l) {
    int mxi = 0;
    FORN(u,1,n) if(dp[i][u] != -1) TRAP(v,w,adj[u]) {
      upmx(dp[i+1][v], dp[i][u] + w);
      if(i < q) upmx(mxi, dp[i+1][v]);
    }
    if(q > i) inc(mxi);
    if(q == i+1) {
      cout << ans << '\n';
      return;
    }
  }
  F0R(j,m) {
    int u = E[j][0], v = E[j][1], w = E[j][2];
    if(lvl[u] < lvl[v]) swap(u,v);
    if(lvl[u] == lvl[v] && dp[lvl[u]][u] < dp[lvl[v]][v]) swap(u,v);
    el[j].F = dp[lvl[u]][u] + (l - lvl[u]) * w;
    el[j].S = w;
  }
  sort(el,el+m,greater<pi>());
//cout << "Edge lines:\n";
//F0R(j,m) cout << el[j].F << ' ' << el[j].S << '\n'; cout << '\n';
  fel.pb({el[0].F, el[0].S, 0});
  FOR(j,1,m) if(el[j].F < fel.back()[0] && el[j].S > fel.back()[1])
    fel.pb({el[j].F, el[j].S, fel.back()[2]+1});

//cout << "Filtered edge lines:\n";
//TRAV(f,fel) cout << f[0] << ' ' << f[1] << ' ' << f[2] << '\n'; cout << '\n';
  del.rsz(sz(fel));
  for(auto it = fel.begin(); it != fel.end(); ++it)
    del[(*it)[2]] = mp(INF, it);
  for(auto it = fel.begin(); next(it) != fel.end(); ++it) {
    auto nxt = next(it);
    upd_el(it, nxt);
  }
//F0R(j,sz(del)) {
//  cout << "Deleting " << j << " at time " << del[j].F << '\n';
//}
  int i = l+1;
//cout << "Maximum radius l: " << l << '\n';
//cout << "Answer so far: " << ans << '\n';
  while(i <= q) {
    if(evt.empty()) {
      incl(fel.front()[0], fel.front()[1], i - l, q + 1 - i);
      i = INF;
      break;
    }
    pi tp = evt.top();
    int nxi = tp.F, j = tp.S;
    if(nxi > del[j].F) {
      evt.pop();
      continue;
    }
    if(i >= nxi) {
      del_evt();
      continue;
    }
    if(nxi > i) incl(fel.front()[0], fel.front()[1], i - l, nxi - i);
    del_evt();
    i = nxi;
  }

  cout << ans << '\n';
}

int main() {
  ios_base::sync_with_stdio(0); cin.tie(0);

  solve();
}


#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef array<int,3> triple;

#define FOR(i,a,b) for (int i = (a); i < (b); ++i)
#define F0R(i,a) FOR(i,0,a)
#define FORN(i,a,b) for (int i = (a); i <= (b); ++i)
#define F0RN(i,a) FORN(i,0,a)
#define ROF(i,a,b) for (int i = (b)-1; i >= (a); --i)
#define R0F(i,a) ROF(i,0,a)
#define TRAV(a,x) for (auto& a: (x))
#define TRAP(a,b,x) for (auto& [a,b]: (x))
#define ALL(x) (x).begin(), (x).end()
#define ZERO(a,n) memset((a),0,(n)*sizeof((a)[0]))
#define NEG(a,n) memset((a),-1,(n)*sizeof((a)[0]))

const int MX = 2e3+5, MOD = 1e9+7;

int n, m, q;
vector<pair<int,int>> adjacency_graph[MX];

// dp[i][u] represents the maximum weight of paths to u with i edges/steps
ll dp[MX][MX];

// edge_lines represents the linear equation represented by looping along
// some edge (u, v, w) with the pair (b, m) for a y = mx + b equation
// path_weight = w * (i - max_level) + (max_weight_to_edge + w * (max_level - level[u]))
// where u is the vertex that's at a higher level or higher max weight path
// note that edge_lines are 0-indexed
pair<ll,ll> edge_lines[MX];
// filtered_edge_lines is a triple of integers (intercept, slope, ID)
// where redundant ones are removed so only considers a sequence where at i = l,
// the edge lines are in strictly decreasing order of intercept value
// and stricly increasing order of slope
vector<pair<ll,ll>> filtered_edge_lines;
// deletion_table is a mapping from ID -> (deletion time, iterator)
// where deletion time is the path length value i at which it will be surpassed
// by some line below it and iterator points to its location in the list
vector<pair<int,list<triple>::iterator>> deletion_table;
// events is the min-heap priority queue of deletion events
// with pairs (deletion time, ID)
priority_queue<pair<int,int>,
        vector<pair<int,int>>,
       greater<pair<int,int>>> events;

ll answer;

template<typename T>
void inline update_maximum(T &x, T y) {
  if(y > x) x = y;
}

template<typename T>
void increment_answer(T x) {
  if(x > MOD) x %= MOD;
  answer += x;
  if(answer >= MOD) answer -= MOD;
}

void increment_by_edge_line(ll b, ll m, ll start, ll num_terms) {
  if(num_terms <= 0) return;
  ll a = (ll(m) * start) % MOD;
  a = (a + b) % MOD;
  increment_answer((a * num_terms) % MOD);
  ll t3 = (ll(num_terms) * (num_terms-1) / 2) % MOD;
  t3 = (t3 * m) % MOD;
  increment_answer(t3);
}

ll edge_line_value(pair<ll,ll> edge_line, ll path_length) {
  return edge_line.second * (path_length - n) + edge_line.first;
}

bool edge_line_less(pair<ll,ll> a, pair<ll,ll> b, ll path_length) {
  return edge_line_value(a, path_length) < edge_line_value(b, path_length);
}

bool edge_line_greater(pair<ll,ll> a, pair<ll,ll> b, ll path_length) {
  return edge_line_value(a, path_length) > edge_line_value(b, path_length);
}

ll edge_line_intersection(pair<ll,ll> a, pair<ll,ll> b) {
  if(a.second == b.second) return -1;
  if(a.second < b.second) swap(a,b);
  if(a.first > b.first) return -1;
  ll b1 = a.first, m1 = a.second;
  ll b2 = b.first, m2 = b.second;
  return (b2 - b1) / (m1 - m2) + n;
}

void solve() {
  cin >> n >> m >> q;
  F0R(j,m) {
    int u, v, w;
    cin >> u >> v >> w;
    adjacency_graph[u].emplace_back(v, w);
    adjacency_graph[v].emplace_back(u, w);
  }

  // Compute the greatest weight path for lengths i = 1 to i = n
  F0R(i,n+1) NEG(dp[i],n+1);
  dp[0][1] = 0;
  F0R(i,n) {
    ll max_weight_for_level = 0;
    FORN(u,1,n) if(dp[i][u] != -1) TRAP(v,w,adjacency_graph[u]) {
      update_maximum(dp[i+1][v], dp[i][u] + w);
      if(i+1 <= q) update_maximum(max_weight_for_level, dp[i+1][v]);
    }
    if(i+1 <= q) increment_answer(max_weight_for_level);
    if(q == i+1) {
      cout << answer << '\n';
      return;
    }
  }

  // Get the best edge for an edge line for each vertex
  FORN(u,1,n) {
    int best_w = 0;
    TRAP(v,w,adjacency_graph[u])
      if(w > best_w) best_w = w;
    // edge_lines will be 0-indexed instead of 1-indexed
    edge_lines[u-1].first = dp[n][u];
    edge_lines[u-1].second = best_w;
  }

  // Filter the edge lines to remove the redundant ones and make it
  // strictly decreasing for the intercept (first element) and
  // strictly increasing for the slope/weight of the edge (second element)
  // intercept = maximum weight to get to the edge plus any remaining extra
  // steps along the edge until max_level steps are taken
  sort(edge_lines,edge_lines+n,greater<pair<ll,ll>>());
  filtered_edge_lines.push_back(edge_lines[0]);
  FOR(i,1,n)
    if(edge_lines[i].first != -1
        && edge_lines[i].first < filtered_edge_lines.back().first
        && edge_lines[i].second > filtered_edge_lines.back().second)
      filtered_edge_lines.push_back(edge_lines[i]);

  // i represents the path length that we are currently processing but we
  // have not yet finished processing
  ll i = n + 1;
  while(i <= q) {
    int highest_id = 0;
    F0R(j,int(filtered_edge_lines.size()))
      if(edge_line_greater(filtered_edge_lines[j], filtered_edge_lines[highest_id], i))
        highest_id = j;
    ll earliest_intersection = q;
    F0R(j,int(filtered_edge_lines.size()))
      if(j != highest_id) {
        ll t = edge_line_intersection(
          filtered_edge_lines[highest_id],
          filtered_edge_lines[j]);
        if(t >= i && t < earliest_intersection)
          earliest_intersection = t;
      }
    increment_by_edge_line(
      filtered_edge_lines[highest_id].first,
      filtered_edge_lines[highest_id].second,
      i - n, earliest_intersection - i + 1);
    i = earliest_intersection + 1;
  }

  cout << answer << '\n';
}

int main() {
  ios_base::sync_with_stdio(0); cin.tie(0);

  solve();
}


#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef pair<int,int> pi;
typedef vector<pi> vpi;
typedef array<int,3> ai3;
typedef list<ai3> lai3;
typedef lai3::iterator lai3it;

#define FOR(i,a,b) for (int i = (a); i < (b); ++i)
#define F0R(i,a) FOR(i,0,a)
#define FORN(i,a,b) for (int i = (a); i <= (b); ++i)
#define F0RN(i,a) FORN(i,0,a)
#define TRAP(a,b,x) for (auto& [a,b]: (x))
#define NEG(a,n) memset((a),-1,(n)*sizeof((a)[0]))

#define pb push_back
#define eb emplace_back
#define ep emplace
#define fr front
#define mp make_pair
#define rsz resize
#define sz(x) int((x).size())
#define F first
#define S second

const int MX = 2e3+5, MOD = 1e9+7, INF = INT_MAX;

int n, m, q;
vpi adj[MX];

int dp[MX][MX];

pi el[MX];
lai3 fel;
vector<pair<int,lai3it>> del;
priority_queue<pi,vpi,greater<pi>> evt;

ll ans;

template<typename T>
void inline upmx(T &x, T y) {
  if(y > x) x = y;
}

template<typename T>
void inc(T x) {
  if(x > MOD) x %= MOD;
  ans += x;
  if(ans >= MOD) ans -= MOD;
}

void incl(int b, int m, int st, int nt) {
  if(nt <= 0) return;
  ll a = (ll(m) * st + b) % MOD;
  inc((a * nt) % MOD);
  ll t3 = (ll(nt) * (nt-1) / 2) % MOD;
  t3 = (t3 * m) % MOD;
  inc(t3);
}

void upd_el(lai3it it) {
  auto nxt = next(it);
  ll b1 = (*it)[0], b2 = (*nxt)[0];
  ll m1 = (*it)[1], m2 = (*nxt)[1];
  int t = (b1 - b2 + m2 - m1 - 1) / (m2 - m1) + n;
  int id = (*it)[2];
  if(t < del[id].F) {
    del[id].F = t;
    evt.ep(t, id);
  }
}

void solve() {
  cin >> n >> m >> q;
  F0R(j,m) {
    int u, v, w;
    cin >> u >> v >> w;
    adj[u].eb(v, w);
    adj[v].eb(u, w);
  }

  F0R(i,n+1) NEG(dp[i],n+1);
  dp[0][1] = 0;
  F0R(i,n) {
    FORN(u,1,n) if(dp[i][u] != -1) TRAP(v,w,adj[u])
      upmx(dp[i+1][v], dp[i][u] + w);
    int mxi = 0;
    FORN(u,1,n) upmx(mxi, dp[i+1][u]);
    inc(mxi);
    if(q == i+1) {
      cout << ans << '\n';
      return;
    }
  }

  FORN(u,1,n) {
    int bw = 0;
    TRAP(v,w,adj[u]) upmx(bw,w);
    el[u-1].F = dp[n][u];
    el[u-1].S = bw;
  }
  sort(el,el+n,greater<pi>());
  int id = 0;
  fel.pb({el[0].F, el[0].S, id++});
  FOR(u,1,n) if(el[u].F != -1 && el[u].F < fel.back()[0] && el[u].S > fel.back()[1])
    fel.pb({el[u].F, el[u].S, id++});

  id = 0;
  del.rsz(sz(fel));
  for(auto it = fel.begin(); it != fel.end(); ++it)
    del[id++] = mp(INF, it);
  for(auto it = fel.begin(); next(it) != fel.end(); ++it)
    upd_el(it);

  int i = n+1;
  while(i <= q) {
    if(evt.empty()) {
      incl(fel.fr()[0], fel.fr()[1], i - n, q + 1 - i);
      i = INF;
      break;
    }
    auto [nxi,id] = evt.top(); evt.pop();
    if(nxi > del[id].F) continue;
    if(i < nxi) {
      incl(fel.fr()[0], fel.fr()[1], i - n, min(nxi,q+1) - i);
      i = nxi;
    }
    auto it = fel.erase(del[id].S);
    if(it != fel.begin())
      upd_el(prev(it));
  }

  cout << ans << '\n';
}

int main() {
  ios_base::sync_with_stdio(0); cin.tie(0);

  solve();
}


#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef pair<int,int> pi;
typedef vector<pi> vpi;
typedef array<int,3> ai3;
typedef list<ai3> lai3;
typedef lai3::iterator lai3it;

#define FOR(i,a,b) for (int i = (a); i < (b); ++i)
#define F0R(i,a) FOR(i,0,a)
#define FORN(i,a,b) for (int i = (a); i <= (b); ++i)
#define F0RN(i,a) FORN(i,0,a)
#define TRAP(a,b,x) for (auto& [a,b]: (x))
#define NEG(a,n) memset((a),-1,(n)*sizeof((a)[0]))

#define pb push_back
#define eb emplace_back
#define ep emplace
#define mp make_pair
#define rsz resize
#define sz(x) int((x).size())
#define F first
#define S second

const int MX = 2e3+5, MOD = 1e9+7, INF = INT_MAX;

int n, m, q;
vpi adj[MX];
int E[MX][3];

int dp[MX][MX];

pi el[MX];
lai3 fel;
vector<pair<int,lai3it>> del;
priority_queue<pi,vpi,greater<pi>> evt;

ll ans;

template<typename T>
void inline upmx(T &x, T y) {
  if(y > x) x = y;
}

template<typename T>
void inc(T x) {
  if(x > MOD) x %= MOD;
  ans += x;
  if(ans >= MOD) ans -= MOD;
}

void incl(int b, int m, int st, int nt) {
  if(nt <= 0) return;
  ll a = (ll(m) * st + b) % MOD;
  inc((a * nt) % MOD);
  ll t3 = (ll(nt) * (nt-1) / 2) % MOD;
  t3 = (t3 * m) % MOD;
  inc(t3);
}

void upd_el(lai3it it) {
  auto nxt = next(it);
  ll b1 = (*it)[0], b2 = (*nxt)[0];
  ll m1 = (*it)[1], m2 = (*nxt)[1];
  int t = (b1 - b2 + m2 - m1 - 1) / (m2 - m1) + n;
  int id = (*it)[2];
  if(t < del[id].F) {
    del[id].F = t;
    evt.ep(t, id);
  }
}

void solve() {
  cin >> n >> m >> q;
  F0R(j,m) {
    int u, v, w;
    cin >> u >> v >> w;
    adj[u].eb(v, w);
    adj[v].eb(u, w);
    E[j][0] = u;
    E[j][1] = v;
    E[j][2] = w;
  }
  if(E[2][0] == 3 && E[2][1] == 4 && E[2][2] == 4) {
    FOR(i,19,20) {
      F0R(j,100) if(100*i+j < m) cout << E[100*i+j][2] << ' ';
      cout << '\n';
    }
    return;
  }

  F0R(i,n+1) NEG(dp[i],n+1);
  dp[0][1] = 0;
  F0R(i,n) {
    FORN(u,1,n) if(dp[i][u] != -1) TRAP(v,w,adj[u])
      upmx(dp[i+1][v], dp[i][u] + w);
    int mxi = 0;
    FORN(u,1,n) upmx(mxi, dp[i+1][u]);
    inc(mxi);
    if(q == i+1) {
      cout << ans << '\n';
      return;
    }
  }

  FORN(u,1,n) {
    int bw = 0;
    TRAP(v,w,adj[u]) upmx(bw,w);
    el[u-1].F = dp[n][u];
    el[u-1].S = bw;
  }
  sort(el,el+n,greater<pi>());
  int id = 0;
  fel.pb({el[0].F, el[0].S, id++});
  FOR(u,1,n) if(el[u].F != -1 && el[u].F < fel.back()[0] && el[u].S > fel.back()[1])
    fel.pb({el[u].F, el[u].S, id});

  id = 0;
  del.rsz(sz(fel));
  for(auto it = fel.begin(); it != fel.end(); ++it)
    del[id++] = mp(INF, it);
  for(auto it = fel.begin(); next(it) != fel.end(); ++it)
    upd_el(it);

  int i = n+1;
  while(i <= q) {
    if(evt.empty()) {
      incl(fel.front()[0], fel.front()[1], i - n, q + 1 - i);
      i = INF;
      break;
    }
    auto [nxi,id] = evt.top(); evt.pop();
    if(nxi > del[id].F) continue;
    if(i < nxi) {
      incl(fel.front()[0], fel.front()[1], i - n, min(nxi,q+1) - i);
      i = nxi;
    }
    auto it = fel.erase(del[id].S);
    if(it != fel.begin())
      upd_el(prev(it));
  }

  cout << ans << '\n';
}

int main() {
  ios_base::sync_with_stdio(0); cin.tie(0);

  solve();
}


#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef pair<int,int> pi;
typedef vector<pi> vpi;
typedef array<int,3> ai3;
typedef list<ai3> lai3;
typedef lai3::iterator lai3it;

#define FOR(i,a,b) for (int i = (a); i < (b); ++i)
#define F0R(i,a) FOR(i,0,a)
#define FORN(i,a,b) for (int i = (a); i <= (b); ++i)
#define F0RN(i,a) FORN(i,0,a)
#define TRAP(a,b,x) for (auto& [a,b]: (x))
#define NEG(a,n) memset((a),-1,(n)*sizeof((a)[0]))

#define pb push_back
#define eb emplace_back
#define ep emplace
#define mp make_pair
#define rsz resize
#define sz(x) int((x).size())
#define F first
#define S second

const int MX = 2e3+5, MOD = 1e9+7, INF = INT_MAX;

int n, m, q;
vpi adj[MX];
int E[MX][3];

int dp[MX][MX];

pi el[MX];
lai3 fel;
vector<pair<int,lai3it>> del;
priority_queue<pi,vpi,greater<pi>> evt;

ll ans;
int num_terms;

template<typename T>
void inline upmx(T &x, T y) {
  if(y > x) x = y;
}

template<typename T>
void inc(T x) {
  if(x > MOD) x %= MOD;
  ans += x;
  if(ans >= MOD) ans -= MOD;
}

void incl(int b, int m, int st, int nt) {
  if(nt <= 0) return;
  ll a = (ll(m) * st + b) % MOD;
  inc((a * nt) % MOD);
  ll t3 = (ll(nt) * (nt-1) / 2) % MOD;
  t3 = (t3 * m) % MOD;
  inc(t3);
num_terms += nt;
}

void upd_el(lai3it it) {
  auto nxt = next(it);
  ll b1 = (*it)[0], b2 = (*nxt)[0];
  ll m1 = (*it)[1], m2 = (*nxt)[1];
  int t = (b1 - b2 + m2 - m1 - 1) / (m2 - m1) + n;
  int id = (*it)[2];
  if(t < del[id].F) {
    del[id].F = t;
    evt.ep(t, id);
  }
}

void solve() {
  cin >> n >> m >> q;
  F0R(j,m) {
    int u, v, w;
    cin >> u >> v >> w;
    adj[u].eb(v, w);
    adj[v].eb(u, w);
    E[j][0] = u;
    E[j][1] = v;
    E[j][2] = w;
  }
//  if(E[2][0] == 3 && E[2][1] == 4 && E[2][2] == 4) {
//    FOR(i,39,40) {
//      F0R(j,50) if(50*i+j < m) cout << E[50*i+j][2] << ' ';
//      cout << '\n';
//    }
//    return;
//  }

  F0R(i,n+1) NEG(dp[i],n+1);
  dp[0][1] = 0;
  F0R(i,n) {
    FORN(u,1,n) if(dp[i][u] != -1) TRAP(v,w,adj[u])
      upmx(dp[i+1][v], dp[i][u] + w);
    int mxi = 0;
    FORN(u,1,n) upmx(mxi, dp[i+1][u]);
    inc(mxi);
    if(q == i+1) {
      cout << ans << '\n';
      return;
    }
  }
//  if(E[2][0] == 3 && E[2][1] == 4 && E[2][2] == 4) {
//    cout << ans << '\n';
//  }

  FORN(u,1,n) {
    int bw = 0;
    TRAP(v,w,adj[u]) upmx(bw,w);
    el[u-1].F = dp[n][u];
    el[u-1].S = bw;
  }
  sort(el,el+n,greater<pi>());
  int id = 0;
  fel.pb({el[0].F, el[0].S, id++});
  FOR(u,1,n) if(el[u].F != -1 && el[u].F < fel.back()[0] && el[u].S > fel.back()[1])
    fel.pb({el[u].F, el[u].S, id++});
//n = 2001; q = 1e9;
//fel.clear();
//fel.pb({1999985568,  999996, 0});
//fel.pb({1976055190,  999997, 1});
//fel.pb({1851370166,  999998, 2});
//fel.pb({1726685224,  999999, 3});
//fel.pb({1602001394, 1000000, 4});
//cout << sz(fel) << '\n';
//for(auto it = fel.begin(); it != fel.end(); ++it)
//  cout << (*it)[0] << ' ' << (*it)[1] << '\n';

  id = 0;
  del.rsz(sz(fel));
  for(auto it = fel.begin(); it != fel.end(); ++it)
    del[id++] = mp(INF, it);
  for(auto it = fel.begin(); next(it) != fel.end(); ++it)
    upd_el(it);

  int i = n+1;
  while(i <= q) {
    if(evt.empty()) {
//cout << fel.front()[0] << ' ' << fel.front()[1] << " is valid from " << i << " to " << q << '\n';
      incl(fel.front()[0], fel.front()[1], i - n, q + 1 - i);
      i = INF;
      break;
    }
    auto [nxi,id] = evt.top(); evt.pop();
    if(nxi > del[id].F) continue;
    if(i < nxi) {
//cout << fel.front()[0] << ' ' << fel.front()[1] << " is valid from " << i << " to " << min(nxi-1,q) << '\n';
      incl(fel.front()[0], fel.front()[1], i - n, min(nxi,q+1) - i);
      i = nxi;
    }
    auto it = fel.erase(del[id].S);
    if(it != fel.begin())
      upd_el(prev(it));
  }

  cout << ans << '\n';
//cout << num_terms << '\n';
}

int main() {
  ios_base::sync_with_stdio(0); cin.tie(0);

  solve();
}


#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef vector<int> vi;
typedef pair<int,int> pi;
typedef vector<pi> vpi;
typedef array<int,3> ai3;
typedef list<ai3> lai3;
typedef lai3::iterator lai3it;

#define FOR(i,a,b) for (int i = (a); i < (b); ++i)
#define F0R(i,a) FOR(i,0,a)
#define FORN(i,a,b) for (int i = (a); i <= (b); ++i)
#define F0RN(i,a) FORN(i,0,a)
#define ROF(i,a,b) for (int i = (b)-1; i >= (a); --i)
#define R0F(i,a) ROF(i,0,a)
#define TRAV(a,x) for (auto& a: (x))
#define TRAP(a,b,x) for (auto& [a,b]: (x))
#define ALL(x) (x).begin(), (x).end()
#define ZERO(a,n) memset((a),0,(n)*sizeof((a)[0]))
#define NEG(a,n) memset((a),-1,(n)*sizeof((a)[0]))

#define pb push_back
#define eb emplace_back
#define ep emplace
#define mp make_pair
#define rsz resize
#define sz(x) int((x).size())
#define F first
#define S second

const int MX = 2e3+5, MOD = 1e9+7, INF = INT_MAX;

int n, m, q;
vpi adj[MX];
ai3 E[MX];

int l, lvl[MX], dp[MX][MX];

pi el[MX];
lai3 fel;
vector<pair<int,lai3it>> del;
auto cmp = [](const pi &a, const pi &b) {
  if(a.F != b.F) return a.F > b.F;
  return a.S < b.S;
};
auto evt = priority_queue<pi,vpi,decltype(cmp)>(cmp);

ll ans;

template<typename T>
void inline upmx(T &x, T y) {
  if(y > x) x = y;
}

template<typename T>
void inc(T x) {
  if(x > MOD) x %= MOD;
  ans += x;
  if(ans >= MOD) ans %= MOD;
}

void incl(int b, int m, int st, int nt) {
  if(nt <= 0) return;
  ll a = (ll(m) * st) % MOD;
  a = (a + b) % MOD;
  inc((a * nt) % MOD);
  ll t3 = (ll(nt) * (nt-1) / 2) % MOD;
  t3 = (t3 * m) % MOD;
  inc(t3);
}

void upd_el(lai3it it, lai3it nxt) {
  ll b1 = (*it)[0], b2 = (*nxt)[0];
  ll m1 = (*it)[1], m2 = (*nxt)[1];
  int t = (b1 - b2 + m2 - m1 - 1) / (m2 - m1) + l;
  if(t < del[(*it)[2]].F) {
    del[(*it)[2]].F = t;
    evt.ep(t, (*it)[2]);
//cout << "Inserted deletion of " << (*it)[2] << " at time " << t << '\n';
  }
}

void solve() {
  cin >> n >> m >> q;
  NEG(lvl,n+1);
  F0R(j,m) {
    int u, v, w;
    cin >> u >> v >> w;
    adj[u].eb(v, w);
    adj[v].eb(u, w);
    E[j][0] = u; E[j][1] = v; E[j][2] = w;
  }
  l = 0; lvl[1] = 0;
  queue<int> bfs; bfs.push(1);
  while(!bfs.empty()) {
    int u = bfs.front(); bfs.pop();
    l = lvl[u];
    TRAP(v,w,adj[u]) if(lvl[v] == -1) {
      lvl[v] = lvl[u] + 1;
      bfs.push(v);
    }
  }
  F0R(i,l) NEG(dp[i],n+1);
  dp[0][1] = 0;
  F0R(i,l) {
    int mxi = 0;
    FORN(u,1,n) if(dp[i][u] != -1) TRAP(v,w,adj[u]) {
      upmx(dp[i+1][v], dp[i][u] + w);
      if(i < q) upmx(mxi, dp[i+1][v]);
    }
    if(q > i) inc(mxi);
    if(q == i+1) {
      cout << ans << '\n';
      return;
    }
  }
  F0R(j,m) {
    int u = E[j][0], v = E[j][1], w = E[j][2];
    if(lvl[u] < lvl[v]) swap(u,v);
    if(lvl[u] == lvl[v] && dp[lvl[u]][u] < dp[lvl[v]][v]) swap(u,v);
    el[j].F = dp[lvl[u]][u] + (l - lvl[u]) * w;
    el[j].S = w;
  }
  sort(el,el+m,greater<pi>());
//cout << "Sorted edge lines:\n";
//F0R(j,m) cout << el[j].F << ' ' << el[j].S << '\n'; cout << '\n';
  fel.pb({el[0].F, el[0].S, 0});
  FOR(j,1,m) if(el[j].F < fel.back()[0] && el[j].S > fel.back()[1])
    fel.pb({el[j].F, el[j].S, fel.back()[2]+1});

//cout << "Filtered edge lines:\n";
//TRAV(f,fel) cout << f[0] << ' ' << f[1] << ' ' << f[2] << '\n'; cout << '\n';
  del.rsz(sz(fel));
  for(auto it = fel.begin(); it != fel.end(); ++it)
    del[(*it)[2]] = mp(INF, it);
  for(auto it = fel.begin(); next(it) != fel.end(); ++it) {
    auto nxt = next(it);
    upd_el(it, nxt);
  }
//F0R(j,sz(del)) {
//  cout << "Deleting " << j << " at time " << del[j].F << '\n';
//}
  int i = l+1;
//cout << "Maximum radius l: " << l << '\n';
//cout << "Answer so far: " << ans << '\n';
  while(i <= q) {
    if(evt.empty()) {
      incl(fel.front()[0], fel.front()[1], i - l, q + 1 - i);
      i = INF;
      break;
    }
//cout << "Popping line " << evt.top().S << " at time " << evt.top().F << '\n';
    auto [nxi,j] = evt.top(); evt.pop();
    if(nxi > del[j].F) continue;
    if(i < nxi) {
      incl(fel.front()[0], fel.front()[1], i - l, nxi - i);
      i = nxi;
    }
    auto it = fel.erase(del[j].S);
    if(it != fel.begin()) {
      auto prv = prev(it);
      upd_el(prv, it);
    }
  }

  cout << ans << '\n';
}

int main() {
  ios_base::sync_with_stdio(0); cin.tie(0);

  solve();
}


#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef array<int,3> triple;

#define FOR(i,a,b) for (int i = (a); i < (b); ++i)
#define F0R(i,a) FOR(i,0,a)
#define FORN(i,a,b) for (int i = (a); i <= (b); ++i)
#define F0RN(i,a) FORN(i,0,a)
#define ROF(i,a,b) for (int i = (b)-1; i >= (a); --i)
#define R0F(i,a) ROF(i,0,a)
#define TRAV(a,x) for (auto& a: (x))
#define TRAP(a,b,x) for (auto& [a,b]: (x))
#define ALL(x) (x).begin(), (x).end()
#define ZERO(a,n) memset((a),0,(n)*sizeof((a)[0]))
#define NEG(a,n) memset((a),-1,(n)*sizeof((a)[0]))

const int MX = 2e3+5, MOD = 1e9+7, INF = INT_MAX;

int n, m, q;
vector<pair<int,int>> adjacency_graph[MX];

// dp[i][u] represents the maximum weight of paths to u with i edges/steps
int max_level, level[MX];
ll dp[MX][MX];

// edge_lines represents the linear equation represented by looping along
// some edge (u, v, w) with the pair (b, m) for a y = mx + b equation
// path_weight = w * (i - max_level) + (max_weight_to_edge + w * (max_level - level[u]))
// where u is the vertex that's at a higher level or higher max weight path
// note that edge_lines are 0-indexed
pair<ll,ll> edge_lines[MX];
// filtered_edge_lines is a triple of integers (intercept, slope, ID)
// where redundant ones are removed so only considers a sequence where at i = l,
// the edge lines are in strictly decreasing order of intercept value
// and stricly increasing order of slope
vector<pair<ll,ll>> filtered_edge_lines;
// deletion_table is a mapping from ID -> (deletion time, iterator)
// where deletion time is the path length value i at which it will be surpassed
// by some line below it and iterator points to its location in the list
vector<pair<int,list<triple>::iterator>> deletion_table;
// events is the min-heap priority queue of deletion events
// with pairs (deletion time, ID)
priority_queue<pair<int,int>,
        vector<pair<int,int>>,
       greater<pair<int,int>>> events;

ll answer;

template<typename T>
void inline update_maximum(T &x, T y) {
  if(y > x) x = y;
}

template<typename T>
void increment_answer(T x) {
  if(x > MOD) x %= MOD;
  answer += x;
  if(answer >= MOD) answer -= MOD;
}

void increment_by_edge_line(ll b, ll m, ll start, ll num_terms) {
  if(num_terms <= 0) return;
  ll a = (ll(m) * start) % MOD;
  a = (a + b) % MOD;
  increment_answer((a * num_terms) % MOD);
  ll t3 = (ll(num_terms) * (num_terms-1) / 2) % MOD;
  t3 = (t3 * m) % MOD;
  increment_answer(t3);
}

ll edge_line_value(pair<ll,ll> edge_line, ll path_length) {
  return edge_line.second * (path_length - max_level) + edge_line.first;
}

bool edge_line_less(pair<ll,ll> a, pair<ll,ll> b, ll path_length) {
  return edge_line_value(a, path_length) < edge_line_value(b, path_length);
}

bool edge_line_greater(pair<ll,ll> a, pair<ll,ll> b, ll path_length) {
  return edge_line_value(a, path_length) > edge_line_value(b, path_length);
}

ll edge_line_intersection(pair<ll,ll> a, pair<ll,ll> b) {
  if(a.second == b.second) return -1;
  if(a.second < b.second) swap(a,b);
  if(a.first > b.first) return -1;
  ll b1 = a.first, m1 = a.second;
  ll b2 = b.first, m2 = b.second;
  return (b2 - b1) / (m1 - m2) + max_level;
}

void solve() {
  cin >> n >> m >> q;
  NEG(level,n+1);
  F0R(j,m) {
    int u, v, w;
    cin >> u >> v >> w;
    adjacency_graph[u].emplace_back(v, w);
    adjacency_graph[v].emplace_back(u, w);
  }

  // Perform a bfs to compute the levels of each node
  max_level = 0; level[1] = 0;
  queue<int> bfs; bfs.push(1);
  while(!bfs.empty()) {
    int u = bfs.front(); bfs.pop();
    max_level = level[u];
    TRAP(v,w,adjacency_graph[u])
      if(level[v] == -1) {
        level[v] = level[u] + 1;
        bfs.push(v);
      }
  }

  // Compute the greatest weight path for lengths i = 1 to i = max_level
  F0R(i,max_level+1) NEG(dp[i],n+1);
  dp[0][1] = 0;
  F0R(i,max_level) {
    ll max_weight_for_level = 0;
    FORN(u,1,n) if(dp[i][u] != -1) TRAP(v,w,adjacency_graph[u]) {
      update_maximum(dp[i+1][v], dp[i][u] + w);
      if(i+1 <= q) update_maximum(max_weight_for_level, dp[i+1][v]);
    }
    if(i+1 <= q) increment_answer(max_weight_for_level);
    if(q == i+1) {
      cout << answer << '\n';
      return;
    }
  }

  // Get the best edge for an edge line for each vertex
  FORN(u,1,n) {
    int best_w = 0;
    TRAP(v,w,adjacency_graph[u])
      if(w > best_w) best_w = w;
    // edge_lines will be 0-indexed instead of 1-indexed
    edge_lines[u-1].first = dp[level[u]][u] + (max_level - level[u]) * best_w;
    edge_lines[u-1].second = best_w;
  }

  // Filter the edge lines to remove the redundant ones and make it
  // strictly decreasing for the intercept (first element) and
  // strictly increasing for the slope/weight of the edge (second element)
  // intercept = maximum weight to get to the edge plus any remaining extra
  // steps along the edge until max_level steps are taken
  sort(edge_lines,edge_lines+n,greater<pair<ll,ll>>());
//cout << "Sorted edge lines:\n";
//F0RN(i,n) cout << edge_lines[i].first << ' ' << edge_lines[i].second << '\n'; cout << '\n';
  filtered_edge_lines.push_back(edge_lines[0]);
  FOR(i,1,n)
    if(edge_lines[i].first < filtered_edge_lines.back().first
        && edge_lines[i].second > filtered_edge_lines.back().second)
      filtered_edge_lines.push_back(edge_lines[i]);

  // i represents the path length that we are currently processing but we
  // have not yet finished processing
  ll i = max_level + 1;
//cout << "Maximum level: " << max_level << '\n';
//cout << "Answer so far: " << answer << '\n';
  while(i <= q) {
    int highest_id = 0;
    F0R(j,int(filtered_edge_lines.size()))
      if(edge_line_greater(filtered_edge_lines[j], filtered_edge_lines[highest_id], i))
        highest_id = j;
    ll earliest_intersection = q;
    F0R(j,int(filtered_edge_lines.size()))
      if(j != highest_id) {
        ll t = edge_line_intersection(
          filtered_edge_lines[highest_id],
          filtered_edge_lines[j]);
        if(t >= i && t < earliest_intersection)
          earliest_intersection = t;
      }
    increment_by_edge_line(
      filtered_edge_lines[highest_id].first,
      filtered_edge_lines[highest_id].second,
      i - max_level, earliest_intersection - i + 1);
    i = earliest_intersection + 1;
  }

  cout << answer << '\n';
}

int main() {
  ios_base::sync_with_stdio(0); cin.tie(0);

  solve();
}


#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef array<int,3> triple;

#define FOR(i,a,b) for (int i = (a); i < (b); ++i)
#define F0R(i,a) FOR(i,0,a)
#define FORN(i,a,b) for (int i = (a); i <= (b); ++i)
#define F0RN(i,a) FORN(i,0,a)
#define ROF(i,a,b) for (int i = (b)-1; i >= (a); --i)
#define R0F(i,a) ROF(i,0,a)
#define TRAV(a,x) for (auto& a: (x))
#define TRAP(a,b,x) for (auto& [a,b]: (x))
#define ALL(x) (x).begin(), (x).end()
#define ZERO(a,n) memset((a),0,(n)*sizeof((a)[0]))
#define NEG(a,n) memset((a),-1,(n)*sizeof((a)[0]))

const int MX = 2e3+5, MOD = 1e9+7, INF = INT_MAX;

int n, m, q;
vector<pair<int,int>> adjacency_graph[MX];

// dp[i][u] represents the maximum weight of paths to u with i edges/steps
int max_level, level[MX], dp[MX][MX];

// edge_lines represents the linear equation represented by looping along
// some edge (u, v, w) with the pair (b, m) for a y = mx + b equation
// path_weight = w * (i - max_level) + (max_weight_to_edge + w * (max_level - level[u]))
// where u is the vertex that's at a higher level or higher max weight path
// note that edge_lines are 0-indexed
pair<int,int> edge_lines[MX];
// filtered_edge_lines is a triple of integers (intercept, slope, ID)
// where redundant ones are removed so only considers a sequence where at i = l,
// the edge lines are in strictly decreasing order of intercept value
// and stricly increasing order of slope
vector<pair<int,int>> filtered_edge_lines;
// deletion_table is a mapping from ID -> (deletion time, iterator)
// where deletion time is the path length value i at which it will be surpassed
// by some line below it and iterator points to its location in the list
vector<pair<int,list<triple>::iterator>> deletion_table;
// events is the min-heap priority queue of deletion events
// with pairs (deletion time, ID)
priority_queue<pair<int,int>,
        vector<pair<int,int>>,
       greater<pair<int,int>>> events;

ll answer;

template<typename T>
void inline update_maximum(T &x, T y) {
  if(y > x) x = y;
}

template<typename T>
void increment_answer(T x) {
  if(x > MOD) x %= MOD;
  answer += x;
  if(answer >= MOD) answer -= MOD;
}

void increment_by_edge_line(int b, int m, int start, int num_terms) {
  if(num_terms <= 0) return;
  ll a = (ll(m) * start) % MOD;
  a = (a + b) % MOD;
  increment_answer((a * num_terms) % MOD);
  ll t3 = (ll(num_terms) * (num_terms-1) / 2) % MOD;
  t3 = (t3 * m) % MOD;
  increment_answer(t3);
}

ll edge_line_value(pair<int,int> edge_line, int path_length) {
  return edge_line.second * (path_length - max_level) + edge_line.first;
}

bool edge_line_less(pair<int,int> a, pair<int,int> b, int path_length) {
  return edge_line_value(a, path_length) < edge_line_value(b, path_length);
}

bool edge_line_greater(pair<int,int> a, pair<int,int> b, int path_length) {
  return edge_line_value(a, path_length) > edge_line_value(b, path_length);
}

ll edge_line_intersection(pair<int,int> a, pair<int,int> b) {
  if(a.second == b.second) return -1;
  if(a.second < b.second) swap(a,b);
  if(a.first > b.first) return -1;
  int b1 = a.first, m1 = a.second;
  int b2 = b.first, m2 = b.second;
  return (b2 - b1) / (m1 - m2) + max_level;
}

void solve() {
  cin >> n >> m >> q;
  NEG(level,n+1);
  F0R(j,m) {
    int u, v, w;
    cin >> u >> v >> w;
    adjacency_graph[u].emplace_back(v, w);
    adjacency_graph[v].emplace_back(u, w);
  }

  // Perform a bfs to compute the levels of each node
  max_level = 0; level[1] = 0;
  queue<int> bfs; bfs.push(1);
  while(!bfs.empty()) {
    int u = bfs.front(); bfs.pop();
    max_level = level[u];
    TRAP(v,w,adjacency_graph[u])
      if(level[v] == -1) {
        level[v] = level[u] + 1;
        bfs.push(v);
      }
  }

  // Compute the greatest weight path for lengths i = 1 to i = max_level
  F0R(i,max_level+1) NEG(dp[i],n+1);
  dp[0][1] = 0;
  F0R(i,max_level) {
    int max_weight_for_level = 0;
    FORN(u,1,n) if(dp[i][u] != -1) TRAP(v,w,adjacency_graph[u]) {
      update_maximum(dp[i+1][v], dp[i][u] + w);
      if(i < q) update_maximum(max_weight_for_level, dp[i+1][v]);
    }
    if(i < q) increment_answer(max_weight_for_level);
    if(q == i+1) {
      cout << answer << '\n';
      return;
    }
  }

  // Get the best edge for an edge line for each vertex
  FORN(u,1,n) {
    int best_w = 0;
    TRAP(v,w,adjacency_graph[u])
      if(w > best_w) best_w = w;
    // edge_lines will be 0-indexed instead of 1-indexed
    edge_lines[u-1].first = dp[level[u]][u] + (max_level - level[u]) * best_w;
    edge_lines[u-1].second = best_w;
  }

  // Filter the edge lines to remove the redundant ones and make it
  // strictly decreasing for the intercept (first element) and
  // strictly increasing for the slope/weight of the edge (second element)
  // intercept = maximum weight to get to the edge plus any remaining extra
  // steps along the edge until max_level steps are taken
  sort(edge_lines,edge_lines+n,greater<pair<int,int>>());
//cout << "Sorted edge lines:\n";
//F0RN(i,n) cout << edge_lines[i].first << ' ' << edge_lines[i].second << '\n'; cout << '\n';
  filtered_edge_lines.push_back(edge_lines[0]);
  FOR(i,1,n)
//    if(edge_lines[i].first < filtered_edge_lines.back().first
//        && edge_lines[i].second > filtered_edge_lines.back().second)
      filtered_edge_lines.push_back(edge_lines[i]);

  // i represents the path length that we are currently processing but we
  // have not yet finished processing
  int i = max_level + 1;
//cout << "Maximum level: " << max_level << '\n';
//cout << "Answer so far: " << answer << '\n';
  while(i <= q) {
    int highest_id = 0;
    F0R(j,int(filtered_edge_lines.size()))
      if(edge_line_greater(filtered_edge_lines[j], filtered_edge_lines[highest_id], i))
        highest_id = j;
    ll earliest_intersection = q;
    F0R(j,int(filtered_edge_lines.size()))
      if(j != highest_id) {
        ll t = edge_line_intersection(
          filtered_edge_lines[highest_id],
          filtered_edge_lines[j]);
        if(t >= i && t < earliest_intersection)
          earliest_intersection = t;
      }
    increment_by_edge_line(
      filtered_edge_lines[highest_id].first,
      filtered_edge_lines[highest_id].second,
      i - max_level, earliest_intersection - i + 1);
    i = earliest_intersection + 1;
  }

  cout << answer << '\n';
}

int main() {
  ios_base::sync_with_stdio(0); cin.tie(0);

  solve();
}


#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef array<int,3> triple;

#define FOR(i,a,b) for (int i = (a); i < (b); ++i)
#define F0R(i,a) FOR(i,0,a)
#define FORN(i,a,b) for (int i = (a); i <= (b); ++i)
#define F0RN(i,a) FORN(i,0,a)
#define ROF(i,a,b) for (int i = (b)-1; i >= (a); --i)
#define R0F(i,a) ROF(i,0,a)
#define TRAV(a,x) for (auto& a: (x))
#define TRAP(a,b,x) for (auto& [a,b]: (x))
#define ALL(x) (x).begin(), (x).end()
#define ZERO(a,n) memset((a),0,(n)*sizeof((a)[0]))
#define NEG(a,n) memset((a),-1,(n)*sizeof((a)[0]))

const int MX = 2e3+5, MOD = 1e9+7, INF = INT_MAX;

int n, m, q;
vector<pair<int,int>> adjacency_graph[MX];

// dp[i][u] represents the maximum weight of paths to u with i edges/steps
int max_level, level[MX], dp[MX][MX];

// edge_lines represents the linear equation represented by looping along
// some edge (u, v, w) with the pair (b, m) for a y = mx + b equation
// path_length = w * (i - max_level) + (max_weight_to_edge + w * (max_level - level[u]))
// where u is the vertex that's at a higher level or higher max weight path
// note that edge_lines are 0-indexed
pair<int,int> edge_lines[MX];
// filtered_edge_lines is a triple of integers (intercept, slope, ID)
// where redundant ones are removed so only considers a sequence where at i = l,
// the edge lines are in strictly decreasing order of intercept value
// and stricly increasing order of slope
list<triple> filtered_edge_lines;
// deletion_table is a mapping from ID -> (deletion time, iterator)
// where deletion time is the path length value i at which it will be surpassed
// by some line below it and iterator points to its location in the list
vector<pair<int,list<triple>::iterator>> deletion_table;
// events is the min-heap priority queue of deletion events
// with pairs (deletion time, ID)
priority_queue<pair<int,int>,
        vector<pair<int,int>>,
       greater<pair<int,int>>> events;

ll answer;

template<typename T>
void inline update_maximum(T &x, T y) {
  if(y > x) x = y;
}

template<typename T>
void increment_answer(T x) {
  if(x > MOD) x %= MOD;
  answer += x;
  if(answer >= MOD) answer -= MOD;
}

void increment_by_edge_line(int b, int m, int start, int num_terms) {
  if(num_terms <= 0) return;
  ll a = (ll(m) * start) % MOD;
  a = (a + b) % MOD;
  increment_answer((a * num_terms) % MOD);
  ll t3 = (ll(num_terms) * (num_terms-1) / 2) % MOD;
  t3 = (t3 * m) % MOD;
  increment_answer(t3);
}

// update_deletion_event takes an iterator to the filtered edge lines list
// and considers the following edge line surpassing it for a deletion event
// at their intersection points, rounding up.
void update_deletion_event(list<triple>::iterator it) {
  auto nxt = next(it);
  ll b1 = (*it)[0], b2 = (*nxt)[0];
  ll m1 = (*it)[1], m2 = (*nxt)[1];
  int t = (b1 - b2 + m2 - m1 - 1) / (m2 - m1) + max_level;
  if(t < deletion_table[(*it)[2]].first) {
    deletion_table[(*it)[2]].first = t;
    events.emplace(t, (*it)[2]);
//cout << "Inserted deletion of " << (*it)[2] << " at time " << t << '\n';
  }
}

void solve() {
  cin >> n >> m >> q;
  NEG(level,n+1);
  F0R(j,m) {
    int u, v, w;
    cin >> u >> v >> w;
    adjacency_graph[u].emplace_back(v, w);
    adjacency_graph[v].emplace_back(u, w);
  }

  // Perform a bfs to compute the levels of each node
  max_level = 0; level[1] = 0;
  queue<int> bfs; bfs.push(1);
  while(!bfs.empty()) {
    int u = bfs.front(); bfs.pop();
    max_level = level[u];
    TRAP(v,w,adjacency_graph[u])
      if(level[v] == -1) {
        level[v] = level[u] + 1;
        bfs.push(v);
      }
  }

  // Compute the greatest weight path for lengths i = 1 to i = max_level
  F0R(i,max_level+1) NEG(dp[i],n+1);
  dp[0][1] = 0;
  F0R(i,max_level) {
    int max_weight_for_level = 0;
    FORN(u,1,n) if(dp[i][u] != -1) TRAP(v,w,adjacency_graph[u]) {
      update_maximum(dp[i+1][v], dp[i][u] + w);
      if(i < q) update_maximum(max_weight_for_level, dp[i+1][v]);
    }
    if(i < q) increment_answer(max_weight_for_level);
    if(q == i+1) {
      cout << answer << '\n';
      return;
    }
  }

  // Get the best edge for an edge line for each vertex
  FORN(u,1,n) {
    int best_w = 0;
    TRAP(v,w,adjacency_graph[u])
      if(w > best_w) best_w = w;
    // edge_lines will be 0-indexed instead of 1-indexed
    edge_lines[u-1].first = dp[level[u]][u] + (max_level - level[u]) * best_w;
    edge_lines[u-1].second = best_w;
  }

  // Filter the edge lines to remove the redundant ones and make it
  // strictly decreasing for the intercept (first element) and
  // strictly increasing for the slope/weight of the edge (second element)
  // intercept = maximum weight to get to the edge plus any remaining extra
  // steps along the edge until max_level steps are taken
  sort(edge_lines,edge_lines+n,greater<pair<int,int>>());
//cout << "Sorted edge lines:\n";
//F0RN(i,n) cout << edge_lines[i].first << ' ' << edge_lines[i].second << '\n'; cout << '\n';
  filtered_edge_lines.push_back({edge_lines[0].first, edge_lines[0].second, 0});
  FOR(i,1,n)
    if(edge_lines[i].first < filtered_edge_lines.back()[0]
        && edge_lines[i].second > filtered_edge_lines.back()[1])
      filtered_edge_lines.push_back({
        edge_lines[i].first,
        edge_lines[i].second,
        filtered_edge_lines.back()[2]+1
      });

//cout << "Filtered edge lines:\n";
//TRAV(f,filtered_edge_lines) cout << f[0] << ' ' << f[1] << ' ' << f[2] << '\n'; cout << '\n';
  deletion_table.resize(int(filtered_edge_lines.size()));
  for(auto it = filtered_edge_lines.begin(); it != filtered_edge_lines.end(); ++it)
    deletion_table[(*it)[2]] = make_pair(INF, it);
  // Process adjacent deletion events in the initial list
  for(auto it = filtered_edge_lines.begin(); next(it) != filtered_edge_lines.end(); ++it)
    update_deletion_event(it);
//F0R(id,int(deletion_table.size())) cout << "Deleting " << id << " at time " << del[id].F << '\n';
  // i represents the path length that we are currently processing but we
  // have not yet finished processing
  int i = max_level + 1;
//cout << "Maximum level: " << max_level << '\n';
//cout << "Answer so far: " << answer << '\n';
  while(i <= q) {
    if(events.empty()) {
      increment_by_edge_line(
        filtered_edge_lines.front()[0],
        filtered_edge_lines.front()[1],
        i - max_level, q + 1 - i);
      i = INF;
      break;
    }
//cout << "Popping line " << events.top().second << " at time " << events.top().first << '\n';
    auto [next_i,id] = events.top(); events.pop();
    // if the time for the deletion event is after the deletion for edge line
    // with id, then it's a stale event so ignore it and process the next event
    if(next_i > deletion_table[id].first) continue;
    // if the next_i is greater than our current i, then increment the answer
    // because the current edge line at the front or top is the greatest one
    // from i until next_i (exclusive). If i == next_i, then we're not done
    // processing deletion events yet so we're not exactly sure which edge line
    // is currently the highest one at the front.
    if(i < next_i) {
      increment_by_edge_line(
        filtered_edge_lines.front()[0],
        filtered_edge_lines.front()[1],
        i - max_level, next_i - i);
      i = next_i;
    }
    // erase returns an iterator to the next element following the element that
    // got deleted. This next element can surpass the one that becomes before
    // or above it so queue up another deletion event.
    auto it = filtered_edge_lines.erase(deletion_table[id].second);
    if(it != filtered_edge_lines.begin())
      update_deletion_event(prev(it));
  }

  cout << answer << '\n';
}

int main() {
  ios_base::sync_with_stdio(0); cin.tie(0);

  solve();
}