#include <bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
const int INF = 0x3f3f3f3f;
map<int, int> mx, my;
const int MOD = 1e9 + 7;
int n, N, M, k, l, realx[2055], realy[2055], x[2005], y[2005], c[2005];
long long quick(int A, int B) {
  if (!B) return 1ll;
  long long tmp = quick(A, B >> 1);
  tmp *= tmp;
  tmp %= MOD;
  if (B & 1) {
    tmp *= A;
    tmp %= MOD;
  }
  return tmp;
}
multiset<int> s[2005];
multiset<int>::iterator ite;
vector<pair<int, int> > have[2005];
long long pre1[2005], pre2[2005], cnt[2005];
struct node {
  long long mi, se, sum, rest;
};
const int MAXN = 1 << 11;
node tree[MAXN + MAXN];
int tag[MAXN + MAXN];
void build(int index) {
  if (index >= MAXN) {
    tree[index] =
        node{0, INF, realy[index - MAXN + 1] - realy[index - MAXN], 0};
  } else {
    build(index << 1);
    build(index << 1 | 1);
    tree[index].mi = 0;
    tree[index].se = INF;
    tree[index].sum = tree[index << 1].sum + tree[index << 1 | 1].sum;
    tree[index].sum %= MOD;
    tree[index].rest = 0;
  }
}
void push_up(int index) {
  tree[index].mi = min(tree[index << 1].mi, tree[index << 1 | 1].mi);
  tree[index].se = INF;
  if (tree[index << 1].mi != tree[index].mi) {
    tree[index].se = min(tree[index].se, tree[index << 1].mi);
  } else {
    tree[index].se = min(tree[index].se, tree[index << 1].se);
  }
  if (tree[index << 1 | 1].mi != tree[index].mi) {
    tree[index].se = min(tree[index].se, tree[index << 1 | 1].mi);
  } else {
    tree[index].se = min(tree[index].se, tree[index << 1 | 1].se);
  }
  tree[index].sum = 0;
  if (tree[index << 1].mi == tree[index].mi) {
    tree[index].sum += tree[index << 1].sum;
    tree[index].sum %= MOD;
  }
  if (tree[index << 1 | 1].mi == tree[index].mi) {
    tree[index].sum += tree[index << 1 | 1].sum;
    tree[index].sum %= MOD;
  }
  tree[index].rest = tree[index << 1].rest + tree[index << 1 | 1].rest;
  tree[index].rest %= MOD;
}
void add_tag(int index, int x) {
  if (x <= tree[index].mi) return;
  if (x < tree[index].se) {
    tag[index] = max(tag[index], x);
    tree[index].rest += 1ll * (x - tree[index].mi) * tree[index].sum;
    tree[index].rest %= MOD;
    tree[index].mi = x;
    return;
  }
  add_tag(index << 1, x);
  add_tag(index << 1 | 1, x);
  push_up(index);
}
void push_down(int index) {
  add_tag(index << 1, tag[index]);
  add_tag(index << 1 | 1, tag[index]);
  tag[index] = 0;
}
void Check_Max(int a, int b, int val, int now = 1, int l = 1,
               int r = MAXN + 1) {
  if (r <= a || l >= b) {
    return;
  }
  if (r <= b && l >= a) {
    add_tag(now, val);
    return;
  }
  push_down(now);
  int mid = (l + r) >> 1;
  Check_Max(a, b, val, now << 1, l, mid);
  Check_Max(a, b, val, now << 1 | 1, mid, r);
  push_up(now);
}
signed main() {
  cin >> n >> k;
  cin >> l;
  int cnt_ = 0;
  mx[0] = my[0] = 1;
  mx[l + 1] = my[l + 1] = 1;
  for (int i = 1; i <= n; ++i) {
    cin >> x[i] >> y[i] >> c[i];
    x[i]++;
    y[i]++;
    mx[x[i]] = 1;
    my[y[i]] = 1;
  }
  cnt_ = 0;
  for (map<int, int>::iterator ite = mx.begin(); ite != mx.end(); ite++) {
    realx[cnt_] = ite->first;
    ite->second = cnt_++;
  }
  N = cnt_ - 2;
  cnt_ = 0;
  for (map<int, int>::iterator ite = my.begin(); ite != my.end(); ite++) {
    realy[cnt_] = ite->first;
    ite->second = cnt_++;
  }
  M = cnt_ - 2;
  for (int i = 1; i <= n; ++i) x[i] = mx[x[i]], y[i] = my[y[i]];
  long long rest = 0;
  for (int i = 1; i <= M; ++i) {
    pre1[i] = pre1[i - 1];
    pre2[i] = pre2[i - 1];
    pre1[i] += realy[i] - realy[i - 1];
    pre1[i] %= MOD;
    pre2[i] += 1ll * (realy[i] - realy[i - 1]) * realy[i] % MOD;
    pre2[i] %= MOD;
  }
  long long hooo = pre1[M];
  hooo *= (l + 1);
  hooo %= MOD;
  for (int bot = 1; bot <= N; ++bot) {
    memset(tag, 0, sizeof(tag));
    build(1);
    int pi = 1, have_ = 0;
    for (int i = 1; i <= k; ++i) cnt[i] = 0;
    vector<pair<int, int> > v;
    for (int i = 1; i <= N; ++i) have[i].clear();
    for (int i = 1; i <= k; ++i) s[i].clear();
    for (int i = 1; i <= n; ++i) {
      if (x[i] <= bot) {
        v.push_back(make_pair(y[i], c[i]));
        s[c[i]].insert(y[i]);
        have[x[i]].push_back(make_pair(y[i], c[i]));
      }
    }
    for (int i = 1; i <= k; ++i) s[i].insert(M + 1);
    sort(v.begin(), v.end());
    int is = 0;
    for (int i = 1; i <= M; ++i) {
      while (is < v.size() && v[is].first <= i) {
        if (!cnt[v[is].second]) {
          have_++;
        }
        cnt[v[is].second]++;
        is++;
      }
      if (have_ == k) break;
      pi = i + 1;
    }
    int iss = 0;
    for (int i = 1; i <= M; ++i) {
      while (have_ != k && is < v.size()) {
        pi = v[is].first;
        if (!cnt[v[is].second]) {
          have_++;
        }
        cnt[v[is].second]++;
        is++;
      }
      while (is < v.size() && v[is].first <= pi) {
        cnt[v[is].second]++;
        is++;
      }
      if (have_ != k) {
        pi = M + 1;
      }
      Check_Max(i, i + 1, realy[pi]);
      while (iss < v.size() && v[iss].first <= i) {
        cnt[v[iss].second]--;
        if (!cnt[v[iss].second]) {
          have_--;
        }
        iss++;
      }
    }
    for (int top = 1; top <= bot; ++top) {
      for (auto it : have[top - 1]) {
        s[it.second].erase(s[it.second].find(it.first));
        int l, r;
        ite = s[it.second].lower_bound(it.first);
        r = realy[*ite];
        ite = s[it.second].upper_bound(it.first);
        if (ite != s[it.second].begin()) {
          ite--;
          l = *ite;
        } else {
          l = 0;
        }
        l++;
        if (l <= it.first) Check_Max(l, it.first + 1, r);
      }
      rest += ((hooo - tree[1].rest) + MOD) % MOD *
              (realx[top] - realx[top - 1]) % MOD *
              (realx[bot + 1] - realx[bot]) % MOD;
      rest %= MOD;
    }
  }
  cout << rest << endl;
  return 0;
}
