#include <bits/stdc++.h>
using namespace std;
const long long MOD = 1e9 + 7;
template <class T>
void chmin(T &a, const T &b) {
  if (a > b) a = b;
}
template <class T>
void chmax(T &a, const T &b) {
  if (a < b) a = b;
}
template <typename T>
class SegmentTree {
 public:
  using F = function<T(T, T)>;
  int n;
  vector<T> tree;
  F operation;
  T def;
  SegmentTree(int size, F _operation, T _def)
      : operation(_operation), def(_def) {
    n = 1;
    while (n < size) n *= 2;
    tree.resize(2 * n - 1, def);
  }
  SegmentTree() {}
  void embody(int size, F _operation, T _def) {
    operation = _operation;
    def = _def;
    n = 1;
    while (n < size) n *= 2;
    tree.resize(2 * n - 1, def);
  }
  void initialize(vector<T> v) {
    int vSize = v.size();
    n = 1;
    while (n < vSize) n *= 2;
    tree.resize(2 * n - 1, def);
    for (int i = 0; i < vSize; i++) tree[i + n - 1] = v[i];
    for (int i = n - 2; i >= 0; i--)
      tree[i] = operation(tree[2 * i + 1], tree[2 * i + 2]);
  }
  void update(int index, T value) {
    index += n - 1;
    tree[index] = value;
    while (index > 0) {
      index = (index - 1) / 2;
      tree[index] = operation(tree[2 * index + 1], tree[2 * index + 2]);
    }
  }
  T query(int a, int b, int k = 0, int l = 0, int r = -1) {
    if (r < 0) r = n;
    if (r <= a || b <= l)
      return def;
    else if (a <= l && r <= b)
      return tree[k];
    else {
      T lval = query(a, b, 2 * k + 1, l, (l + r) / 2);
      T rval = query(a, b, 2 * k + 2, (l + r) / 2, r);
      return operation(lval, rval);
    }
  }
  int find(int x, int k = 0, int l = 0, int r = -1) {
    if (r < 0) r = n;
    if (r - l == 1) return k - (n - 1);
    if (tree[2 * k + 1] >= x)
      return find(x, 2 * k + 1, l, (l + r) / 2);
    else
      return find(x - tree[2 * k + 1], 2 * k + 2, (l + r) / 2, r);
  }
};
const int L = 1e6;
int main() {
  cin.tie(0);
  ios::sync_with_stdio(false);
  int N, M;
  cin >> N >> M;
  vector<int> y(N), lx(N), rx(N);
  for (int i = 0; i < (N); ++i) cin >> y[i] >> lx[i] >> rx[i];
  vector<int> x(M), ly(M), ry(M);
  for (int i = 0; i < (M); ++i) cin >> x[i] >> ly[i] >> ry[i];
  vector<tuple<int, int, int>> v;
  y.push_back(0);
  lx.push_back(0);
  rx.push_back(L);
  y.push_back(L);
  lx.push_back(0);
  rx.push_back(L);
  v.push_back(make_tuple(0, 1, N));
  v.push_back(make_tuple(L, 1, N + 1));
  for (int i = 0; i < (N); ++i) {
    v.push_back(make_tuple(y[i], 1, i));
  }
  for (int i = 0; i < (M); ++i) {
    v.push_back(make_tuple(ly[i], 0, i));
    v.push_back(make_tuple(ry[i], 2, i));
  }
  sort(v.begin(), v.end());
  SegmentTree<int> seg(
      L + 1, [](int a, int b) { return a + b; }, 0);
  long long ans = 0;
  set<int> se;
  for (int i = 0; i < v.size(); i++) {
    int now = get<2>(v[i]);
    int type = get<1>(v[i]);
    int h = get<0>(v[i]);
    if (type == 0) {
      se.insert(x[now]);
    } else if (type == 1) {
      ans += seg.query(lx[now], rx[now] + 1);
      if (lx[now] == 0 && rx[now] == L) ans++;
      auto itr = se.lower_bound(lx[now]);
      while (itr != se.end()) {
        int tar = *itr;
        if (tar > rx[now]) break;
        seg.update(tar, 1);
        itr = se.erase(itr);
      }
    } else {
      if (se.count(x[now])) se.erase(x[now]);
      seg.update(x[now], 0);
    }
  }
  ans--;
  cout << ans << endl;
  return 0;
}
