#include <bits/stdc++.h>
using namespace std;
const int maxN = 4e5 + 7, maxM = 1e6 + 7;
int N, M, head[maxM], cnt, tot, rt1, rt2;
struct Eddge {
  int nex, to;
  long long flow;
  Eddge(int a = -1, int b = 0, long long c = 0) : nex(a), to(b), flow(c) {}
} edge[maxM];
inline void addEddge(int u, int v, long long w) {
  edge[cnt] = Eddge(head[u], v, w);
  head[u] = cnt++;
}
inline void _add(int u, int v, long long w) {
  addEddge(u, v, w);
  addEddge(v, u, 0);
}
struct Dinic {
  int S, T, cur[maxN], node;
  int deep[maxN], que[maxN], top, tail;
  inline bool bfs() {
    for (int i = 0; i <= node; i++) deep[i] = 0;
    top = tail = 0;
    que[tail++] = S;
    deep[S] = 1;
    while (top < tail) {
      int u = que[top++];
      for (int i = head[u], v; ~i; i = edge[i].nex) {
        v = edge[i].to;
        long long f = edge[i].flow;
        if (!deep[v] && f) {
          deep[v] = deep[u] + 1;
          que[tail++] = v;
        }
      }
    }
    return deep[T];
  }
  long long dfs(int u, long long dist) {
    if (u == T) return dist;
    for (int &i = cur[u], v; ~i; i = edge[i].nex) {
      v = edge[i].to;
      long long f = edge[i].flow;
      if (deep[v] == deep[u] + 1 && f) {
        long long flow = dfs(v, min(f, dist));
        if (flow) {
          edge[i].flow -= flow;
          edge[i ^ 1].flow += flow;
          return flow;
        }
      }
    }
    return 0;
  }
  inline long long Max_Flow() {
    long long ans = 0, tmp;
    while (bfs()) {
      for (int i = 0; i <= node; i++) cur[i] = head[i];
      while ((tmp = dfs(S, 0x3f3f3f3f))) ans += tmp;
    }
    return ans;
  }
} mf;
namespace Segement {
int lc[maxN], rc[maxN], a[maxN];
void build(int &rt, int fa, int l, int r, int d) {
  rt = ++tot;
  if (fa) {
    if (!d)
      _add(rt, fa, 0x3f3f3f3f);
    else
      _add(fa, rt, 0x3f3f3f3f);
  }
  if (l == r) return;
  int mid = (l + r) >> 1;
  build(lc[rt], rt, l, mid, d);
  build(rc[rt], rt, mid + 1, r, d);
}
void query(int rt, int l, int r, int ql, int qr, int y, int d) {
  if (!rt || l > qr || r < ql) return;
  if (ql <= l && r <= qr) {
    if (!d)
      _add(rt, y, 0x3f3f3f3f);
    else
      _add(y, rt, 0x3f3f3f3f);
    return;
  }
  int mid = (l + r) >> 1;
  if (qr <= mid)
    query(lc[rt], l, mid, ql, qr, y, d);
  else if (ql > mid)
    query(rc[rt], mid + 1, r, ql, qr, y, d);
  else {
    query(lc[rt], l, mid, ql, qr, y, d);
    query(rc[rt], mid + 1, r, ql, qr, y, d);
  }
}
void Insert(int x, int l, int r) {
  int pos = -1, las = -1, y = 0;
  for (int i = l; i <= r + 1; i++) {
    if (i <= r) {
      y = a[i] + 1;
      a[i] = -1;
    }
    if (y != las || i == r + 1) {
      if (~pos) {
        int z = ++tot;
        query(rt1, 1, N, pos, i - 1, z, 0);
        query(rt2, 1, N, las, x, z, 1);
      }
      pos = i;
      las = y;
    }
  }
}
void update(int x, int l, int r) {
  for (int i = l; i <= r; i++) a[i] = x;
}
void build_S_T(int rt, int l, int r, int d) {
  if (l == r) {
    if (!d)
      _add(mf.S, rt, 1);
    else
      _add(rt, mf.T, 1);
    return;
  }
  int mid = (l + r) >> 1;
  build_S_T(lc[rt], l, mid, d);
  build_S_T(rc[rt], mid + 1, r, d);
}
};  // namespace Segement
using namespace Segement;
struct node {
  int x, l, r, op;
  node(int a = 0, int b = 0, int c = 0, int d = 0) : x(a), l(b), r(c), op(d) {}
  friend bool operator<(node e1, node e2) {
    return e1.x == e2.x ? e1.op < e2.op : e1.x < e2.x;
  }
} c[maxN << 1];
inline void init() {
  cnt = tot = 0;
  mf.S = 0;
  memset(head, -1, sizeof(head));
}
int main() {
  init();
  scanf("%d%d", &N, &M);
  build(rt1, 0, 1, N, 0);
  build(rt2, 0, 1, N, 1);
  int cnum = 0;
  for (int i = 1, xl, yl, xr, yr; i <= M; i++) {
    scanf("%d%d%d%d", &xl, &yl, &xr, &yr);
    c[++cnum] = node(xl - 1, yl, yr, 1);
    c[++cnum] = node(xr, yl, yr, -1);
  }
  sort(c + 1, c + cnum + 1);
  for (int i = 1; i <= cnum; i++) {
    if (~c[i].op) {
      Insert(c[i].x, c[i].l, c[i].r);
    } else {
      update(c[i].x, c[i].l, c[i].r);
    }
  }
  Insert(N, 1, N);
  mf.T = ++tot;
  mf.node = tot + 1;
  build_S_T(rt1, 1, N, 0);
  build_S_T(rt2, 1, N, 1);
  printf("%lld\n", mf.Max_Flow());
  return 0;
}
