#include <bits/stdc++.h>
#pragma GCC optimize("O3")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")
using namespace std;
const long long maxn = 1e4;
const long long mod = 1e9 + 7;
long long n, m, k, t, len;
long long A[maxn];
long long MUL[maxn];
template <typename T>
struct range_segtree {
  long long n;
  T zero, zero1;
  inline T mrg(T a, T b) { return (a + b) % mod; }
  inline void add(T &a, T b) { a = b; }
  vector<T> V;
  vector<long long> M;
  vector<long long> S;
  vector<long long> MX;
  range_segtree(long long n_, T zero_ = 0) {
    n = n_;
    V.resize(4 * n_ + 7);
    M.resize(4 * n_ + 7);
    S.resize(4 * n_ + 7);
    MX.resize(4 * n_ + 7);
    for (int i = 0; i < M.size(); i++) {
      M[i] = -1;
    }
    zero = zero_;
  }
  inline void build(long long v, long long tl, long long tr) {
    if (tl == tr) {
      V[v] = A[tl] * MUL[tl] % mod;
      S[v] = MUL[tl];
      MX[v] = A[tl];
      return;
    }
    long long tm = (tl + tr) / 2;
    build(2 * v, tl, tm);
    build(2 * v + 1, tm + 1, tr);
    V[v] = mrg(V[2 * v], V[2 * v + 1]);
    S[v] = S[2 * v] + S[2 * v + 1];
    S[v] %= mod;
    MX[v] = max(MX[2 * v], MX[2 * v + 1]);
  }
  inline void build() { build(1, 0, n - 1); }
  inline void push(long long v, long long tl, long long tr) {
    if (M[v] == -1) {
      return;
    }
    V[v] = M[v] * S[v] % mod;
    MX[v] = max(M[v], MX[v]);
    if (tl != tr) {
      M[2 * v] = max(M[v], M[2 * v]);
      M[2 * v + 1] = max(M[v], M[2 * v + 1]);
    }
    M[v] = -1;
  }
  inline void update(long long v, long long tl, long long tr, long long l,
                     long long r, long long color) {
    push(v, tl, tr);
    if (l > tr || tl > r || l > r) {
      return;
    }
    if (l == tl && r == tr) {
      add(M[v], color);
      push(v, tl, tr);
      return;
    }
    long long tm = (tl + tr) / 2;
    update(2 * v, tl, tm, l, min(r, tm), color);
    update(2 * v + 1, tm + 1, tr, max(tm + 1, l), r, color);
    V[v] = mrg(V[2 * v], V[2 * v + 1]);
    MX[v] = max(MX[2 * v], MX[2 * v + 1]);
  }
  inline T getans(long long v, long long tl, long long tr, long long l,
                  long long r) {
    push(v, tl, tr);
    if (l > tr || tl > r || l > r) {
      return zero;
    }
    if (tl == l && tr == r) {
      return V[v];
    }
    long long tm = (tl + tr) / 2;
    T ans1 = getans(2 * v, tl, tm, l, min(r, tm));
    T ans2 = getans(2 * v + 1, tm + 1, tr, max(l, tm + 1), r);
    return mrg(ans1, ans2);
  }
  inline void update(long long l, long long r, long long color) {
    update(1, 0, n - 1, l, r, color);
  }
  inline T getans(long long l, long long r) {
    T ans = getans(1, 0, n - 1, l, r);
    return ans;
  }
  long long go(long long v, long long nw, long long tl, long long tr) {
    push(v, tl, tr);
    if (tl == tr) {
      return tl;
    }
    long long tm = (tl + tr) / 2;
    push(2 * v, tl, tm);
    if (MX[2 * v] >= nw) {
      return go(2 * v, nw, tl, tm);
    } else {
      return go(2 * v + 1, nw, tm + 1, tr);
    }
  }
};
long long X[maxn];
long long Y[maxn];
long long C[maxn];
vector<long long> XI, YI;
long long getxi(long long first) {
  return (lower_bound(XI.begin(), XI.end(), first)) - XI.begin();
}
long long getyi(long long first) {
  return (lower_bound(YI.begin(), YI.end(), first)) - YI.begin();
}
vector<long long> V[maxn];
vector<multiset<long long> > S;
int main() {
  ios_base ::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  long long q, w, e, a, b, c;
  cin >> n >> k >> len;
  S.resize(k);
  for (q = 0; q < n; q++) {
    cin >> X[q] >> Y[q] >> C[q];
    C[q]--;
    XI.push_back(X[q]);
    YI.push_back(Y[q]);
  }
  YI.push_back(-1);
  YI.push_back(len);
  XI.push_back(-1);
  XI.push_back(len);
  sort(XI.begin(), XI.end());
  XI.resize(unique(XI.begin(), XI.end()) - XI.begin());
  sort(YI.begin(), YI.end());
  YI.resize(unique(YI.begin(), YI.end()) - YI.begin());
  for (q = 0; q < n; q++) {
    X[q] = getxi(X[q]);
    Y[q] = getyi(Y[q]);
    V[Y[q]].push_back(q);
  }
  for (q = 0; q < k; q++) {
    S[q].insert(XI.size() - 1);
  }
  long long ans = 0;
  for (q = YI.size() - 2; q >= 1; q--) {
    long long mul1 = YI[q] - YI[q - 1];
    long long dans = 0;
    for (auto i : V[q]) {
      long long color = C[i], first = X[i];
      S[color].insert(first);
    }
    vector<long long> VX[maxn];
    for (long long i = 0; i < S.size(); i++) {
      for (auto first : S[i]) {
        VX[first].push_back(i);
      }
    }
    long long i = 0;
    for (w = 0; w < k; w++) {
      i = max(i, *S[w].begin());
    }
    for (w = 0; w < XI.size(); w++) {
      A[w] = XI[i];
      for (auto i1 : VX[w]) {
        long long di = (*S[i1].lower_bound(w + 1));
        i = max(i, di);
      }
    }
    for (w = 1; w < XI.size(); w++) {
      MUL[w] = XI[w] - XI[w - 1];
    }
    vector<multiset<long long> > S1 = S;
    range_segtree<long long> st(XI.size());
    st.build();
    for (w = YI.size() - 2; w >= q; w--) {
      long long sum = st.getans(0, XI.size() - 1);
      long long mul2 = YI[w + 1] - YI[w];
      dans += (len * (len + 1) % mod - sum % mod + mod) % mod * mul2 % mod;
      dans %= mod;
      for (auto i : V[w]) {
        long long color = C[i];
        long long first = X[i];
        auto it = S1[color].find(first);
        long long l;
        if (it == S1[color].begin()) {
          l = -1;
        } else {
          it--;
          l = *(it);
        }
        l++;
        S1[color].erase(S1[color].lower_bound(first));
        long long nw = XI[*S1[color].lower_bound(first)];
        long long r = max(st.go(1, nw, 0, XI.size() - 1) - 1, 0LL);
        if (r >= l) {
          st.update(l, r, nw);
        }
      }
    }
    dans *= mul1;
    dans %= mod;
    ans += dans;
    ans %= mod;
  }
  cout << ans;
  return 0;
}
