#include <bits/stdc++.h>
const long long MOD = 1e9 + 7;
const long long INF = 1e9;
const long long INFLL = 1e18;
using namespace std;
int cx[4] = {0, 0, 1, -1};
int cy[4] = {-1, 1, 0, 0};
string Yes[2] = {"No\n", "Yes\n"};
string YES[2] = {"NO\n", "YES\n"};
string Possible[2] = {"Impossible\n", "Possible\n"};
string POSSIBLE[2] = {"IMPOSSIBLE\n", "POSSIBLE\n"};
struct Tree {
  vector<long long> a, b;
  int sz = 0;
  long long L;
  long long C;
  vector<long long> tree, push, mnA, mxA, sB;
  void Build(int L, int R, int V) {
    if (L + 1 == R) {
      tree[V] = a[L] * b[L] % MOD;
      mnA[V] = a[L];
      mxA[V] = a[L];
      sB[V] = b[L];
      return;
    }
    int M = (L + R) / 2;
    Build(L, M, 2 * V + 1);
    Build(M, R, 2 * V + 2);
    tree[V] = (tree[2 * V + 1] + tree[2 * V + 2]) % MOD;
    sB[V] = (sB[2 * V + 1] + sB[2 * V + 2]) % MOD;
    mnA[V] = min(mnA[2 * V + 1], mnA[2 * V + 2]);
    mxA[V] = max(mxA[2 * V + 1], mxA[2 * V + 2]);
  }
  Tree(vector<long long> _a, vector<long long> _b, long long _L) {
    sz = _a.size();
    tree.resize(4 * sz);
    push.resize(4 * sz, -1);
    mnA.resize(4 * sz);
    mxA.resize(4 * sz);
    sB.resize(4 * sz);
    a = _a;
    b = _b;
    L = _L;
    C = 0;
    for (int i = 0; i < b.size(); i++) C = (C + b[i]) % MOD;
    C = C * L % MOD;
    Build(0, sz, 0);
  }
  long long Get() {
    long long res = -tree[0];
    return ((C + res) % MOD + MOD) % MOD;
  }
  void Push(int V, int L, int M, int R) {
    if (push[V] != -1) {
      push[2 * V + 1] = push[2 * V + 2] = push[V];
      mnA[2 * V + 1] = mnA[2 * V + 2] = push[V];
      mxA[2 * V + 1] = mxA[2 * V + 2] = push[V];
      tree[2 * V + 1] = sB[2 * V + 1] * push[V] % MOD;
      tree[2 * V + 2] = sB[2 * V + 2] * push[V] % MOD;
      push[V] = -1;
    }
  }
  void Set(int l, int r, long long x, int L, int R, int V) {
    int M = (L + R) / 2;
    if (L + 1 != R) Push(V, L, M, R);
    if (r <= L || R <= l) return;
    if (l <= L && R <= r && mxA[V] <= x) {
      mxA[V] = x;
      mnA[V] = x;
      tree[V] = sB[V] * x % MOD;
      push[V] = x;
      return;
    }
    if (L + 1 == R) return;
    Set(l, r, x, L, M, 2 * V + 1);
    if (x > mnA[2 * V + 2]) Set(l, r, x, M, R, 2 * V + 2);
    tree[V] = (tree[2 * V + 1] + tree[2 * V + 2]) % MOD;
    mnA[V] = min(mnA[2 * V + 1], mnA[2 * V + 2]);
    mxA[V] = max(mxA[2 * V + 1], mxA[2 * V + 2]);
  }
  void Set(long long L, long long R, long long x) {
    Set(L, R + 1, x, 0, sz, 0);
  }
};
int main() {
  ios_base::sync_with_stdio(false);
  cin.tie(0);
  int n, k, L;
  cin >> n >> k >> L;
  struct candy {
    int x, y, c, id;
  };
  vector<candy> A(n);
  for (int i = 0; i < n; i++) cin >> A[i].x >> A[i].y >> A[i].c, A[i].c--;
  sort((A).begin(), (A).end(), [&](candy A, candy B) { return A.y < B.y; });
  for (int i = 0; i < n; i++) A[i].id = i;
  long long ans = 0;
  for (int lowest = 0; lowest < n; lowest++) {
    long long Down = A[lowest].y - (lowest ? A[lowest - 1].y : -1);
    vector<candy> omega;
    vector<set<int> > positions(k);
    for (int i = lowest; i < n; i++) omega.push_back(A[i]);
    sort((omega).begin(), (omega).end(),
         [&](candy A, candy B) { return A.x < B.x; });
    map<int, int> S;
    int j = -1;
    vector<int> nxt(omega.size());
    vector<int> pos(n);
    for (int i = 0; i < omega.size(); i++) {
      pos[omega[i].id] = i;
      positions[omega[i].c].insert(i);
      while (j + 1 < omega.size() && S.size() != k) {
        S[omega[j + 1].c]++;
        j++;
      }
      if (S.size() != k) {
        j = omega.size();
      }
      nxt[i] = j;
      S[omega[i].c]--;
      if (S[omega[i].c] == 0) S.erase(omega[i].c);
    }
    vector<long long> a(omega.size()), b(omega.size());
    for (int j = 0; j < omega.size(); j++) {
      if (nxt[j] == omega.size())
        a[j] = L;
      else
        a[j] = omega[nxt[j]].x;
      b[j] = omega[j].x - (j ? omega[j - 1].x : -1);
    }
    Tree T(a, b, L);
    for (int i = n - 1; i >= lowest; i--) {
      long long cur = (i + 1 < n ? A[i + 1].y : L) - A[i].y;
      long long res = 0;
      res = T.Get();
      int pos_omega = pos[i];
      int color = A[i].c;
      auto it = positions[color].find(pos_omega);
      assert(it != positions[color].end());
      int Pr = -1;
      if (it != positions[color].begin()) {
        it--;
        Pr = *it;
        it++;
      }
      int Nxt = omega.size();
      it++;
      if (it != positions[color].end()) {
        Nxt = *it;
      }
      it--;
      T.Set(Pr + 1, pos_omega, (Nxt == omega.size() ? L : omega[Nxt].x));
      positions[color].erase(it);
      ans = (ans + cur * Down % MOD * res) % MOD;
    }
  }
  cout << ans;
}
