#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
struct node {
  int x, y, c;
} s[2005], tmp[2005];
int n, k, l, cx, cy;
int lx[2005], ly[2005], bk[2005];
multiset<int> t[2005];
namespace Tree {
int mn[8005], mx[8005], sum[8005], lzy[8005];
inline void pushtag(int p, int l, int r, int val) {
  mx[p] = mn[p] = lzy[p] = val;
  sum[p] = 1ll * (ly[r + 1] - ly[l]) * val % mod;
}
inline void down(int p, int l, int mid, int r) {
  pushtag(p << 1, l, mid, lzy[p]);
  pushtag(p << 1 | 1, mid + 1, r, lzy[p]);
  lzy[p] = -1;
}
void insert(int p, int id, int val, int l, int r) {
  if (l == r)
    return mx[p] = mn[p] = val,
           sum[p] = 1ll * (ly[id + 1] - ly[id]) * val % mod, void();
  const int mid = l + r >> 1;
  if (id <= mid)
    insert(p << 1, id, val, l, mid);
  else
    insert(p << 1 | 1, id, val, mid + 1, r);
  mn[p] = min(mn[p << 1], mn[p << 1 | 1]);
  mx[p] = max(mx[p << 1], mx[p << 1 | 1]);
  sum[p] = (sum[p << 1] + sum[p << 1 | 1]) % mod;
}
void qmin(int p, int val, int l, int r, int L, int R) {
  if (L >= l && R <= r) {
    if (mx[p] <= val)
      return;
    else if (mn[p] >= val)
      pushtag(p, L, R, val);
    else {
      const int mid = L + R >> 1;
      if (~lzy[p]) down(p, L, mid, R);
      qmin(p << 1, val, l, r, L, mid);
      qmin(p << 1 | 1, val, l, r, mid + 1, R);
      mn[p] = min(mn[p << 1], mn[p << 1 | 1]);
      mx[p] = max(mx[p << 1], mx[p << 1 | 1]);
      sum[p] = (sum[p << 1] + sum[p << 1 | 1]) % mod;
    }
    return;
  }
  const int mid = L + R >> 1;
  if (~lzy[p]) down(p, L, mid, R);
  if (l <= mid) qmin(p << 1, val, l, r, L, mid);
  if (r > mid) qmin(p << 1 | 1, val, l, r, mid + 1, R);
  mn[p] = min(mn[p << 1], mn[p << 1 | 1]);
  mx[p] = max(mx[p << 1], mx[p << 1 | 1]);
  sum[p] = (sum[p << 1] + sum[p << 1 | 1]) % mod;
}
}  // namespace Tree
int main() {
  scanf("%d%d%d", &n, &k, &l);
  lx[++cx] = 0;
  ly[++cy] = 0;
  lx[++cx] = l + 1;
  ly[++cy] = l + 1;
  int ans = 0;
  for (int i = 1; i <= n; ++i)
    scanf("%d%d%d", &s[i].x, &s[i].y, &s[i].c), lx[++cx] = ++s[i].x,
                                                ly[++cy] = ++s[i].y;
  sort(lx + 1, lx + cx + 1);
  cx = unique(lx + 1, lx + cx + 1) - lx - 1;
  sort(ly + 1, ly + cy + 1);
  cy = unique(ly + 1, ly + cy + 1) - ly - 1;
  for (int i = 1; i <= n; ++i)
    s[i].x = lower_bound(lx + 1, lx + cx + 1, s[i].x) - lx,
    s[i].y = lower_bound(ly + 1, ly + cy + 1, s[i].y) - ly, tmp[i] = s[i];
  sort(s + 1, s + n + 1,
       [](const node &a, const node &b) { return a.x < b.x; });
  sort(tmp + 1, tmp + n + 1,
       [](const node &a, const node &b) { return a.y < b.y; });
  for (int u = 1; u < cx; ++u) {
    memset(bk, 0, sizeof(bk));
    memset(Tree::lzy, -1, sizeof(Tree::lzy));
    for (int i = 1, j = 1, p = 1, cnt = 0; i < cy; ++i) {
      while (j <= n && tmp[j].y <= i) {
        if (tmp[j].x <= u)
          ++j;
        else {
          if (!bk[tmp[j].c]) ++cnt;
          ++bk[tmp[j++].c];
        }
      }
      while (cnt == k) {
        if (tmp[p].x <= u)
          ++p;
        else {
          --bk[tmp[p].c];
          if (!bk[tmp[p++].c]) --cnt;
        }
      }
      Tree::insert(1, i, ly[tmp[p - 1].y], 1, cy - 1);
    }
    for (int i = 1; i <= k; ++i) t[i].clear();
    for (int i = 1; i <= n; ++i)
      if (s[i].x > u) t[s[i].c].insert(s[i].y);
    for (int d = cx - 1, i = n; d > u; --d) {
      (ans += 1ll * (lx[d + 1] - lx[d]) * (lx[u + 1] - lx[u]) % mod *
              Tree::sum[1] % mod) %= mod;
      while (i && s[i].x >= d) {
        auto it = t[s[i].c].find(s[i].y);
        int pre = it == t[s[i].c].begin() ? 0 : *prev(it);
        int nxt = next(it) == t[s[i].c].end() ? cy : *next(it);
        t[s[i].c].erase(it);
        if (s[i].y < nxt) Tree::qmin(1, ly[pre], s[i].y, nxt - 1, 1, cy - 1);
        --i;
      }
    }
  }
  return printf("%d\n", ans), 0;
}
