#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2e3 + 5;
const int MAXD = 2e3 + 5;
const int mod = 1e9 + 7;
inline void add_mod(int &a, int b) {
  a += b;
  if (a >= mod) a -= mod;
}
inline long long pw(long long a, long long b) {
  long long res = 1;
  while (b) {
    if (b & 1) res = res * a % mod;
    a = a * a % mod;
    b >>= 1;
  }
  return res;
}
template <typename T>
struct DSC {
  T dsc[MAXN];
  int dcnt = 0;
  DSC(void) : dcnt(0) {}
  inline void clear(void) { dcnt = 0; }
  inline T &operator[](const int x) { return dsc[x]; }
  inline const T &operator[](const int x) const { return dsc[x]; }
  inline int size(void) { return dcnt; }
  inline void insert(const T &x) { dsc[++dcnt] = x; }
  inline void build(void) {
    sort(dsc + 1, dsc + dcnt + 1);
    dcnt = unique(dsc + 1, dsc + dcnt + 1) - dsc - 1;
  }
  inline int get(const T &x) {
    return lower_bound(dsc + 1, dsc + dcnt + 1, x) - dsc;
  }
};
struct Segment_Tree {
  int coef[MAXN << 2], sum[MAXN << 2], mx[MAXN << 2], tag[MAXN << 2];
  inline void push_up(int u) {
    sum[u] = (sum[((u) << 1)] + sum[((u) << 1 | 1)]) % mod;
    mx[u] = mx[((u) << 1 | 1)];
  }
  inline void push_down(int u) {
    if (~tag[u]) {
      sum[((u) << 1)] = (long long)coef[((u) << 1)] * tag[u] % mod;
      sum[((u) << 1 | 1)] = (long long)coef[((u) << 1 | 1)] * tag[u] % mod;
      mx[((u) << 1)] = mx[((u) << 1 | 1)] = tag[((u) << 1)] =
          tag[((u) << 1 | 1)] = tag[u];
      tag[u] = -1;
    }
  }
  void build(int u, int l, int r, int a[], int val[]) {
    tag[u] = -1;
    if (l == r) {
      sum[u] = (long long)a[l] * val[l] % mod;
      mx[u] = a[l];
      coef[u] = val[l];
      return;
    }
    int mid = (l + r) >> 1;
    build(((u) << 1), l, mid, a, val);
    build(((u) << 1 | 1), mid + 1, r, a, val);
    push_up(u);
    coef[u] = (coef[((u) << 1)] + coef[((u) << 1 | 1)]) % mod;
  }
  void update(int u, int l, int r, int ql, int qr, int k) {
    if (ql <= l && r <= qr) {
      sum[u] = (long long)coef[u] * k % mod;
      mx[u] = tag[u] = k;
      return;
    }
    push_down(u);
    int mid = (l + r) >> 1;
    if (ql <= mid) update(((u) << 1), l, mid, ql, qr, k);
    if (mid < qr) update(((u) << 1 | 1), mid + 1, r, ql, qr, k);
    push_up(u);
  }
  int query(int u, int l, int r, int ql, int qr) {
    if (ql <= l && r <= qr) return sum[u];
    push_down(u);
    int mid = (l + r) >> 1, res = 0;
    if (ql <= mid) add_mod(res, query(((u) << 1), l, mid, ql, qr));
    if (mid < qr) add_mod(res, query(((u) << 1 | 1), mid + 1, r, ql, qr));
    push_up(u);
    return res;
  }
  int lower_bound(int u, int l, int r, int q) {
    if (mx[u] < q) return r + 1;
    if (l == r) return l;
    push_down(u);
    int mid = (l + r) >> 1, res = lower_bound(((u) << 1), l, mid, q);
    if (res > mid) res = lower_bound(((u) << 1 | 1), mid + 1, r, q);
    push_up(u);
    return res;
  }
};
struct Node {
  int x, y, clr;
} p[MAXN];
inline bool cmp(const Node &p, const Node &q) { return p.y > q.y; }
int main(void) {
  int n, d, L;
  scanf("%d%d%d", &n, &d, &L);
  for (int i = 1; i <= n; ++i)
    scanf("%d%d%d", &p[i].x, &p[i].y, &p[i].clr), ++p[i].x, ++p[i].y;
  static DSC<int> dscx, dsc;
  for (int i = 1; i <= n; ++i) dscx.insert(p[i].x), dsc.insert(p[i].y);
  dscx.insert(L + 1);
  dsc.insert(L + 1);
  dscx.build();
  dsc.build();
  for (int i = 1; i <= n; ++i)
    p[i].x = dscx.get(p[i].x), p[i].y = dsc.get(p[i].y);
  static vector<Node> nodex[MAXN], nodey[MAXN];
  for (int i = 1; i <= n; ++i)
    nodex[p[i].x].push_back(p[i]), nodey[p[i].y].push_back(p[i]);
  for (int i = 1; i < dscx.size(); ++i)
    sort(nodex[i].begin(), nodex[i].end(), cmp);
  long long ans = 0;
  for (int k = 1; k < dscx.size(); ++k) {
    static int freq[MAXD];
    memset(freq, 0, sizeof(freq));
    int ccnt = 0;
    static int f[MAXN];
    int maxl = 0;
    for (int l = 1, r = 1; l < dsc.size(); ++l) {
      while (r <= dsc.size() && ccnt < d) {
        for (int i = 0; i < (int)nodey[r].size(); ++i)
          if (nodey[r][i].x <= k)
            if (++freq[nodey[r][i].clr] == 1) ++ccnt;
        ++r;
      }
      if (r > dsc.size()) break;
      maxl = l;
      f[l] = dsc[r - 1];
      for (int i = 0; i < (int)nodey[l].size(); ++i)
        if (nodey[l][i].x <= k)
          if (--freq[nodey[l][i].clr] == 0) --ccnt;
    }
    if (!maxl) continue;
    static int val[MAXN];
    long long sum = 0;
    for (int i = 1; i <= maxl; ++i)
      val[i] = dsc[i] - dsc[i - 1],
      sum = (sum + (long long)val[i] * (L + 1)) % mod;
    static Segment_Tree tree;
    tree.build(1, 1, maxl, f, val);
    static multiset<int> t[MAXN];
    for (int i = 1; i <= n; ++i) {
      t[i].clear();
      t[i].insert(0);
      t[i].insert(dsc.size());
    }
    for (int i = 1; i <= n; ++i)
      if (p[i].x <= k) t[p[i].clr].insert(p[i].y);
    for (int i = 1; i <= k; ++i) {
      long long res = (sum + mod - tree.query(1, 1, maxl, 1, maxl)) % mod;
      ans = (ans +
             res * (dscx[k + 1] - dscx[k]) % mod * (dscx[i] - dscx[i - 1])) %
            mod;
      for (int j = 0; j < (int)nodex[i].size(); ++j) {
        multiset<int> &tt = t[nodex[i][j].clr];
        tt.erase(tt.lower_bound(nodex[i][j].y));
      }
      for (int j = 0; j < (int)nodex[i].size(); ++j) {
        Node &cur = nodex[i][j];
        multiset<int> &tt = t[cur.clr];
        multiset<int>::iterator it = tt.lower_bound(cur.y);
        if (*it == cur.y) continue;
        int nxt = dsc[*it];
        if (*--it + 1 > maxl) continue;
        int pos = min(cur.y, tree.lower_bound(1, 1, maxl, nxt) - 1);
        if (*it + 1 <= pos) tree.update(1, 1, maxl, *it + 1, pos, nxt);
      }
    }
  }
  printf("%lld", ans);
  return 0;
}
