#include <bits/stdc++.h>
using namespace std;
const int INF = 1e9 + 7;
struct Tree {
  int n;
  vector<long long> t;
  vector<int> mx;
  vector<int> val;
  vector<int> weights;
  void add_val(int v, int l, int r, int now) {
    t[v] = weights[v] * (long long)now;
    mx[v] = now;
    val[v] = now;
  }
  void push(int v, int l, int r) {
    if (val[v] == -1) return;
    int m = (l + r) / 2;
    add_val(2 * v, l, m, val[v]);
    add_val(2 * v + 1, m, r, val[v]);
    val[v] = -1;
  }
  void relax(int v) {
    t[v] = t[2 * v] + t[2 * v + 1];
    mx[v] = max(mx[2 * v], mx[2 * v + 1]);
    weights[v] = weights[2 * v] + weights[2 * v + 1];
  }
  void build(vector<int>& a, vector<int>& w, int v, int l, int r) {
    if (r - l == 1) {
      t[v] = a[l] * (long long)w[l];
      mx[v] = a[l];
      weights[v] = w[l];
      return;
    }
    int m = (l + r) / 2;
    build(a, w, 2 * v, l, m);
    build(a, w, 2 * v + 1, m, r);
    relax(v);
  }
  Tree(vector<int>& a, vector<int>& w)
      : n(a.size()), t(4 * n), val(4 * n, -1), weights(4 * n), mx(4 * n) {
    build(a, w, 1, 0, n);
  }
  long long get_sum(int l, int r, int vt, int lt, int rt) {
    if (l >= rt || lt >= r) {
      return 0;
    }
    if (l <= lt && rt <= r) {
      return t[vt];
    }
    push(vt, lt, rt);
    int mt = (lt + rt) / 2;
    auto first = get_sum(l, r, 2 * vt, lt, mt);
    auto second = get_sum(l, r, 2 * vt + 1, mt, rt);
    relax(vt);
    return first + second;
  }
  long long get_sum(int l, int r) { return get_sum(l, r, 1, 0, n); }
  void change_max(int l, int r, int now, int vt, int lt, int rt) {
    if (l >= rt || lt >= r) {
      return;
    }
    int mt = (lt + rt) / 2;
    if (l <= lt && rt <= r) {
      if (mx[vt] < now) {
        add_val(vt, lt, rt, now);
        return;
      }
      if (rt - lt == 1) return;
      push(vt, lt, rt);
      if (mx[2 * vt] < now) {
        add_val(2 * vt, lt, mt, now);
        change_max(l, r, now, 2 * vt + 1, mt, rt);
      } else {
        change_max(l, r, now, 2 * vt, lt, mt);
      }
      relax(vt);
      return;
    }
    push(vt, lt, rt);
    change_max(l, r, now, 2 * vt, lt, mt);
    change_max(l, r, now, 2 * vt + 1, mt, rt);
    relax(vt);
  }
  void change_max(int l, int r, int now) { change_max(l, r, now, 1, 0, n); }
  int get_pos(int val, int vt, int lt, int rt) {
    if (rt - lt == 1) {
      return (t[vt] >= val ? lt : -1);
    }
    push(vt, lt, rt);
    int mt = (lt + rt) / 2;
    if (mx[2 * vt] >= val) {
      return get_pos(val, 2 * vt, lt, mt);
    }
    return get_pos(val, 2 * vt + 1, mt, rt);
  }
  int get_pos(int val) { return get_pos(val, 1, 0, n); }
  void print(int vt, int lt, int rt) {
    if (rt - lt == 1) {
      cout << mx[vt] << " ";
      return;
    }
    push(vt, lt, rt);
    int mt = (lt + rt) / 2;
    print(2 * vt, lt, mt);
    print(2 * vt + 1, mt, rt);
    relax(vt);
  }
  void print() {
    cout << "FXS\n";
    print(1, 0, n);
    cout << "\n";
  }
};
void add(int& first, int second) {
  first += second;
  if (first >= INF) {
    first -= INF;
  }
  if (first < 0) {
    first += INF;
  }
}
int main() {
  ios_base::sync_with_stdio(0);
  cin.tie(0);
  int n, k, L;
  cin >> n >> k >> L;
  set<int> xs;
  set<int> ys;
  vector<int> color(n);
  vector<pair<int, int>> points(n);
  map<int, vector<int>> ymp;
  for (int i = 0; i < n; ++i) {
    int c, first, second;
    cin >> first >> second >> c;
    --c;
    color[i] = c;
    points[i] = {first, second};
    ymp[second].push_back(i);
    xs.insert(first);
    ys.insert(second);
  }
  ys.insert(L);
  map<int, int> nx;
  vector<int> xval;
  int now = 0;
  for (int first : xs) {
    nx[first] = now++;
    xval.push_back(first);
  }
  int res = 0;
  vector<pair<int, int>> xcur;
  vector<multiset<int>> allx(k);
  int y_prev = 0;
  for (int y_up : ys) {
    vector<int> fx;
    vector<int> lens;
    map<int, int> posq;
    int pr = 0;
    for (auto t : xcur) {
      lens.push_back(xval[t.first] - pr + 1);
      posq[t.first] = lens.size() - 1;
      pr = xval[t.first] + 1;
    }
    lens.push_back(L - pr);
    vector<int> color_count(k, 0);
    int exist_colors = 0;
    int rt = 0;
    while (rt < (int)xcur.size() && exist_colors < k) {
      int c = xcur[rt].second;
      ++color_count[c];
      exist_colors += (color_count[c] == 1);
      ++rt;
    }
    if (exist_colors == k) {
      fx.push_back(xval[xcur[rt - 1].first]);
    }
    for (int lt = 0; lt < (int)xcur.size(); ++lt) {
      int c = xcur[lt].second;
      --color_count[c];
      exist_colors -= (color_count[c] == 0);
      while (rt < (int)xcur.size() && exist_colors < k) {
        int c = xcur[rt].second;
        ++color_count[c];
        exist_colors += (color_count[c] == 1);
        ++rt;
      }
      if (exist_colors == k) {
        fx.push_back(xval[xcur[rt - 1].first]);
      } else
        break;
    }
    while (fx.size() < lens.size()) {
      fx.push_back(L);
    }
    Tree tree(fx, lens);
    pr = 0;
    for (int y_down : ys) {
      if (y_down == y_up) break;
      int dy = y_down - pr + 1;
      int ddy = y_up - y_prev;
      add(res, ddy * (long long)dy % INF *
                   ((L * (long long)L - tree.get_sum(0, fx.size())) % INF) %
                   INF);
      for (auto ind : ymp[y_down]) {
        int first = nx[points[ind].first];
        int c = color[ind];
        allx[c].erase(allx[c].find(first));
      }
      vector<pair<int, pair<int, int>>> queries;
      for (auto ind : ymp[y_down]) {
        int first = nx[points[ind].first];
        int c = color[ind];
        int x_ind =
            upper_bound(xcur.begin(), xcur.end(), make_pair(first, INF)) -
            xcur.begin() - 1;
        auto it = allx[c].lower_bound(first);
        int prv = 0, nxt;
        if (it != allx[c].end()) {
          nxt = xval[*it];
        } else {
          nxt = L;
        }
        it = allx[c].upper_bound(first);
        if (it != allx[c].begin()) {
          prv = posq[*(--it)] + 1;
        }
        prv = max(prv, tree.get_pos(xval[first]));
        queries.push_back({x_ind, {prv, nxt}});
      }
      sort(queries.rbegin(), queries.rend());
      for (auto t : queries) {
        int x_ind = t.first;
        int prv = t.second.first;
        int nxt = t.second.second;
        tree.change_max(prv, x_ind + 1, nxt);
      }
      pr = y_down + 1;
    }
    for (auto ind : ymp[y_up]) {
      xcur.push_back({nx[points[ind].first], color[ind]});
    }
    sort(xcur.begin(), xcur.end());
    for (int i = 0; i < k; ++i) {
      allx[i].clear();
    }
    for (auto t : xcur) {
      allx[t.second].insert(t.first);
    }
    y_prev = y_up;
  }
  cout << res << "\n";
}
