#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;   }