#include <bits/stdc++.h>
using namespace std;
template <class T, class U>
ostream& operator<<(ostream& os, const pair<T, U>& p) {
  os << "(" << p.first << ", " << p.second << ")";
  return os;
}
template <class T>
ostream& operator<<(ostream& os, const vector<T>& v) {
  os << "{";
  for (long long i = 0; i < (long long)v.size(); i++) {
    if (i) os << ", ";
    os << v[i];
  }
  os << "}";
  return os;
}
template <typename Arg1>
void __f(const char* name, Arg1&& arg1) {
  cerr << name << " : " << arg1 << std::endl;
}
template <typename Arg1, typename... Args>
void __f(const char* names, Arg1&& arg1, Args&&... args) {
  const char* comma = strchr(names + 1, ',');
  cerr.write(names, comma - names) << " : " << arg1 << " | ";
  __f(comma + 1, args...);
}
const long long N = 2005;
vector<long long> X[N];
long long x[N], y[N], c[N];
long long cx[N], cy[N];
long long rev_x[N], rev_y[N];
const long long mod = 1e9 + 7;
inline long long add(long long x, long long y) {
  x += y;
  if (x >= mod) x -= mod;
  return x;
}
inline long long sub(long long x, long long y) {
  x -= y;
  if (x < 0) x += mod;
  return x;
}
inline long long mul(long long x, long long y) { return (x * 1ll * y) % mod; }
long long f[N], pf[N];
long long L;
struct segtree {
  vector<long long> tree, rgt, lazy;
  long long n;
  segtree(vector<long long> v) : n(v.size()) {
    tree.assign(4 * n + 10, 0);
    rgt.assign(4 * n + 10, 0);
    lazy.assign(4 * n + 10, -10);
    function<void(long long, long long, long long)> make =
        [&](long long s, long long e, long long ind) {
          if (s == e) {
            tree[ind] = mul(f[s], sub(L, v[s]));
            rgt[ind] = v[s];
            return;
          }
          long long mid = (s + e) >> 1;
          make(s, mid, ind << 1);
          make(mid + 1, e, ind << 1 | 1);
          tree[ind] = add(tree[ind << 1], tree[ind << 1 | 1]);
          rgt[ind] = rgt[ind << 1 | 1];
        };
    make(0, n - 1, 1);
  }
  void push(long long s, long long e, long long ind) {
    if (lazy[ind] == -10) return;
    tree[ind] = mul(sub(pf[e], s == 0 ? 0 : pf[s - 1]), sub(L, lazy[ind]));
    rgt[ind] = lazy[ind];
    if (s != e) {
      lazy[ind << 1] = lazy[ind << 1 | 1] = lazy[ind];
    }
    lazy[ind] = -10;
  }
  void update(long long l, long long r, long long x, long long s, long long e,
              long long ind) {
    push(s, e, ind);
    if (l > e || s > r) return;
    if (s >= l && e <= r) {
      lazy[ind] = x;
      push(s, e, ind);
      return;
    }
    long long mid = (s + e) >> 1;
    update(l, r, x, s, mid, ind << 1);
    update(l, r, x, mid + 1, e, ind << 1 | 1);
    tree[ind] = add(tree[ind << 1], tree[ind << 1 | 1]);
    rgt[ind] = rgt[ind << 1 | 1];
  }
  long long getFirst(long long v, long long s, long long e, long long ind) {
    push(s, e, ind);
    if (s == e) return s;
    long long mid = (s + e) >> 1;
    push(s, mid, ind << 1);
    if (rgt[ind << 1] >= v)
      return getFirst(v, s, mid, ind << 1);
    else
      return getFirst(v, mid + 1, e, ind << 1 | 1);
  }
  long long getFirst(long long v) { return getFirst(v, 0, n - 1, 1); }
  long long getSum() { return tree[1]; }
  void update(long long l, long long r, long long v) {
    update(l, r, v, 0, n - 1, 1);
  }
};
signed main() {
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);
  long long n = 2000, k = 2;
  L = 1000000000;
  cin >> n >> k >> L;
  map<long long, long long> compress_x, compress_y;
  set<long long> st_x = {-1, L}, st_y = {-1, L};
  long long cnt_x = 0, cnt_y = 0;
  for (long long i = 0; i < n; i++) {
    x[i] = rand() % L;
    y[i] = rand() % L;
    c[i] = rand() % k + 1;
    cin >> x[i] >> y[i] >> c[i];
    c[i]--;
    st_x.insert(x[i]);
    st_y.insert(y[i]);
  }
  for (auto it : st_x) {
    rev_x[cnt_x] = it;
    compress_x[it] = cnt_x++;
  }
  for (auto it : st_y) {
    rev_y[cnt_y] = it;
    compress_y[it] = cnt_y++;
  }
  for (long long i = 0; i < n; i++) {
    cx[i] = compress_x[x[i]];
    cy[i] = compress_y[y[i]];
    X[cx[i]].push_back(i);
  }
  for (long long i = 1; i < cnt_y; i++) {
    f[i] = rev_y[i] - rev_y[i - 1];
    pf[i] = add(pf[i - 1], f[i]);
  }
  long long ans = 0;
  for (long long i = 1; i < cnt_x - 1; i++) {
    vector<multiset<long long>> positions(k);
    vector<vector<long long>> at_y(cnt_y);
    for (long long j = i; j < cnt_x; j++)
      for (long long ind : X[j]) {
        positions[c[ind]].insert(cy[ind]);
        at_y[cy[ind]].push_back(ind);
      }
    vector<long long> mn(cnt_y);
    multiset<long long> mx;
    vector<long long> mn_pos(k, cnt_y - 1);
    for (long long j = 0; j < k; j++) mx.insert(cnt_y - 1);
    for (long long j = cnt_y - 1; j >= 0; j--) {
      for (long long ind : at_y[j]) {
        mx.erase(mx.find(mn_pos[c[ind]]));
        mn_pos[c[ind]] = j;
        mx.insert(j);
      }
      mn[j] = rev_y[*mx.rbegin()];
    }
    for (long long i = 1; i < cnt_y; i++) assert(mn[i] >= mn[i - 1]);
    segtree st(mn);
    long long R = sub(rev_x[i], rev_x[i - 1]);
    long long sm = 0;
    for (long long j = cnt_x - 2; j >= i; j--) {
      for (long long ind : X[j + 1]) {
        long long col = c[ind];
        auto it = positions[col].lower_bound(cy[ind]);
        assert(*it == cy[ind]);
        auto it2 = it;
        auto it3 = it;
        long long prv = 0, nxt = cnt_y - 1;
        if (it3 != positions[col].begin()) {
          it3--;
          prv = *it3;
        }
        it2++;
        if (it2 != positions[col].end()) {
          nxt = *it2;
        }
        positions[col].erase(it);
        long long fst = st.getFirst(rev_y[nxt]);
        assert(fst <= nxt);
        if (prv + 1 < fst) {
          long long new_val = rev_y[nxt];
          st.update(prv + 1, fst - 1, new_val);
        }
      }
      long long val = mul(sub(rev_x[j + 1], rev_x[j]), st.getSum());
      sm = add(sm, val);
    }
    ans = add(ans, mul(R, sm));
  }
  cout << ans << endl;
}
