#include <bits/stdc++.h>
template <typename T>
inline void read(T &x) {
  x = 0;
  char c = getchar();
  bool flag = false;
  while (!isdigit(c)) {
    if (c == '-') flag = true;
    c = getchar();
  }
  while (isdigit(c)) x = x * 10 + (c ^ 48), c = getchar();
  if (flag) x = -x;
}
using namespace std;
const int inf = 987654321;
int n, m, s, t;
struct edge {
  int nxt, to, val;
  edge() {}
  edge(int jzp, int zzz, int lsr) { nxt = jzp, to = zzz, val = lsr; }
} e[5001000];
int head[101000], ecnt = 1, hcur[101000];
inline void addedge(int from, int to, int val) {
  e[++ecnt] = edge(head[from], to, val);
  head[from] = ecnt;
}
inline void Addedge(int u, int v, int w) { addedge(u, v, w), addedge(v, u, 0); }
int ls[101000], rs[101000], root_in, root_out, ttot;
void build_out(int L, int R, int &cur) {
  cur = ++ttot;
  if (L == R) return Addedge(s, cur, 1), void();
  int mid = (L + R) >> 1;
  build_out(L, mid, ls[cur]), build_out(mid + 1, R, rs[cur]);
  Addedge(ls[cur], cur, inf);
  Addedge(rs[cur], cur, inf);
}
void build_in(int L, int R, int &cur) {
  cur = ++ttot;
  if (L == R) return Addedge(cur, t, 1), void();
  int mid = (L + R) >> 1;
  build_in(L, mid, ls[cur]), build_in(mid + 1, R, rs[cur]);
  Addedge(cur, ls[cur], inf), Addedge(cur, rs[cur], inf);
}
void Addedge_out(int L, int R, int l, int r, int p, int cur) {
  if (l <= L && R <= r) return Addedge(cur, p, inf), void();
  int mid = (L + R) >> 1;
  if (l <= mid) Addedge_out(L, mid, l, r, p, ls[cur]);
  if (r > mid) Addedge_out(mid + 1, R, l, r, p, rs[cur]);
}
void Addedge_in(int L, int R, int l, int r, int p, int cur) {
  if (l <= L && R <= r) return Addedge(p, cur, inf), void();
  int mid = (L + R) >> 1;
  if (l <= mid) Addedge_in(L, mid, l, r, p, ls[cur]);
  if (r > mid) Addedge_in(mid + 1, R, l, r, p, rs[cur]);
}
inline void Seg_Addedge(int l, int r, int d, int u) {
  if (l > r || d > u) return;
  ++ttot;
  Addedge_out(1, n, l, r, ttot, root_out);
  Addedge_in(1, n, d, u, ttot, root_in);
}
struct node {
  int x, l, r, t;
  node() {}
  node(int jzp, int zzz, int lsr, int chr) {
    x = jzp, l = zzz, r = lsr, t = chr;
  }
} nd[101000];
int ntot;
inline bool cmp(const node &a, const node &b) {
  return a.x < b.x || (a.x == b.x && a.t > b.t);
}
struct ODT {
  int l, r, v;
  ODT() {}
  ODT(int jzp, int zzz, int lsr) { l = jzp, r = zzz, v = lsr; }
  inline bool operator<(const ODT &a) const { return l < a.l; }
};
set<ODT> st;
inline set<ODT>::iterator Split(int p) {
  if (p > n) return st.end();
  set<ODT>::iterator it = --st.upper_bound(ODT(p, 0, 0));
  if (it->l == p) return it;
  if (it->r < p) return ++it;
  int l = it->l, r = it->r, v = it->v;
  st.erase(it);
  st.insert(ODT(l, p - 1, v));
  return st.insert(ODT(p, r, v)).first;
}
inline void Assign(int l, int r, int x) {
  set<ODT>::iterator itr = Split(r + 1), itl = Split(l);
  for (set<ODT>::iterator it = itl; it != itr; ++it) {
    Seg_Addedge(it->v, x - 1, it->l, it->r);
  }
  st.erase(itl, itr);
}
inline void Insert(int l, int r, int v) { st.insert(ODT(l, r, v)); }
int dep[101000], que[101000], front, rear;
inline bool bfs() {
  memset(dep, 0x3f, sizeof(dep));
  front = rear = 0;
  for (int i = 1; i <= ttot; ++i) hcur[i] = head[i];
  dep[s] = 0;
  que[++rear] = s;
  while (front < rear) {
    int cur = que[++front];
    for (int i = head[cur]; i; i = e[i].nxt) {
      int to = e[i].to;
      if (!e[i].val || dep[to] < 5001000) continue;
      que[++rear] = to;
      dep[to] = dep[cur] + 1;
    }
  }
  return dep[t] <= 5001000;
}
int dfs(int cur, int limi) {
  if (cur == t || !limi) return limi;
  int flow = 0;
  for (int i = hcur[cur]; i; i = e[i].nxt) {
    int to = e[i].to;
    hcur[cur] = i;
    if (dep[to] != dep[cur] + 1) continue;
    int tmp = dfs(to, min(e[i].val, limi));
    if (!tmp) continue;
    e[i].val -= tmp;
    e[i ^ 1].val += tmp;
    flow += tmp;
    limi -= tmp;
    if (!limi) break;
  }
  return flow;
}
int main() {
  read(n);
  read(m);
  for (int i = 1; i <= m; ++i) {
    int x_1, y_1, x_2, y_2;
    read(x_1), read(y_1), read(x_2), read(y_2);
    nd[++ntot] = node(x_1, y_1, y_2, 0);
    nd[++ntot] = node(x_2 + 1, y_1, y_2, 1);
  }
  sort(nd + 1, nd + 1 + ntot, cmp);
  s = 1, t = 2;
  ttot = 2;
  build_out(1, n, root_out);
  build_in(1, n, root_in);
  Insert(1, n, 1);
  for (int i = 1; i <= ntot; ++i) {
    int x = nd[i].x, l = nd[i].l, r = nd[i].r, t = nd[i].t;
    if (t == 0) {
      Assign(l, r, x);
    } else {
      Insert(l, r, x);
    }
  }
  for (set<ODT>::iterator it = st.begin(); it != st.end(); ++it) {
    Seg_Addedge(it->v, n, it->l, it->r);
  }
  int mxflow = 0;
  while (bfs()) mxflow += dfs(s, inf);
  printf("%d\n", mxflow);
  return 0;
}
