#include <bits/stdc++.h>
#pragma warning(disable : 4996)
#pragma comment(linker, "/stack:200000000")
#pragma GCC optimize("Ofast")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
#pragma GCC optimize("-ffloat-store")
using namespace std;
clock_t time_p = clock();
void aryanc403() {
  time_p = clock() - time_p;
  cerr << "Time Taken : " << (float)(time_p) / CLOCKS_PER_SEC << "\n";
}
const long long int INF = 0xFFFFFFFFFFFFFFFL;
long long int seed;
mt19937 rng(seed = chrono::steady_clock::now().time_since_epoch().count());
inline long long int rnd(long long int l = 0, long long int r = INF) {
  return uniform_int_distribution<long long int>(l, r)(rng);
}
class CMP {
 public:
  bool operator()(pair<long long int, long long int> a,
                  pair<long long int, long long int> b) {
    return !(a.first < b.first || a.first == b.first && a.second <= b.second);
  }
};
void add(map<long long int, long long int> &m, long long int x,
         long long int cnt = 1) {
  auto jt = m.find(x);
  if (jt == m.end())
    m.insert({x, cnt});
  else
    jt->second += cnt;
}
void del(map<long long int, long long int> &m, long long int x,
         long long int cnt = 1) {
  auto jt = m.find(x);
  if (jt->second <= cnt)
    m.erase(jt);
  else
    jt->second -= cnt;
}
bool cmp(const pair<long long int, long long int> &a,
         const pair<long long int, long long int> &b) {
  return a.first < b.first || (a.first == b.first && a.second < b.second);
}
const long long int mod = 1000000007L;
struct edge {
  long long int from, to, cap, flow, index;
  edge(long long int from, long long int to, long long int cap,
       long long int flow, long long int index)
      : from(from), to(to), cap(cap), flow(flow), index(index) {}
};
ostream &operator<<(ostream &out, const edge &a) {
  return out << "{" << a.from << "," << a.to << "," << a.cap << "}";
}
struct PushRelabel {
  static const long long INF = 1e18;
  long long int n;
  vector<vector<edge>> g;
  vector<long long> excess;
  vector<long long int> height;
  PushRelabel(long long int n) : n(n), g(n), excess(n), height(n) {}
  void addEdge(long long int from, long long int to, long long int cap) {
    g[from].push_back(edge(from, to, cap, 0, g[to].size()));
    if (from == to) g[from].back().index++;
    g[to].push_back(edge(to, from, 0, 0, g[from].size() - 1));
  }
  void push(edge &e) {
    long long int amt =
        (long long int)min(excess[e.from], (long long)e.cap - e.flow);
    if (height[e.from] <= height[e.to] || amt == 0) return;
    e.flow += amt;
    g[e.to][e.index].flow -= amt;
    excess[e.to] += amt;
    excess[e.from] -= amt;
  }
  void relabel(long long int u) {
    long long int d = 2e5;
    for (auto &it : g[u]) {
      if (it.cap - it.flow > 0) d = min(d, height[it.to]);
    }
    if (d < INF) height[u] = d + 1;
  }
  vector<long long int> find_max_height_vertices(long long int source,
                                                 long long int dest) {
    vector<long long int> max_height;
    for (long long int i = 0; i < n; i++) {
      if (i != source && i != dest && excess[i] > 0) {
        if (!max_height.empty() && height[i] > height[max_height[0]])
          max_height.clear();
        if (max_height.empty() || height[i] == height[max_height[0]])
          max_height.push_back(i);
      }
    }
    return max_height;
  }
  long long max_flow(long long int source, long long int dest) {
    excess.assign(n, 0);
    height.assign(n, 0);
    height[source] = n;
    excess[source] = INF;
    for (auto &it : g[source]) push(it);
    vector<long long int> current;
    while (!(current = find_max_height_vertices(source, dest)).empty()) {
      for (auto i : current) {
        bool pushed = false;
        for (auto &e : g[i]) {
          if (excess[i] == 0) break;
          if (e.cap - e.flow > 0 && height[e.from] == height[e.to] + 1) {
            push(e);
            pushed = true;
          }
        }
        if (!pushed) {
          relabel(i);
          break;
        }
      }
    }
    long long max_flow = 0;
    for (auto &e : g[source]) max_flow += e.flow;
    return max_flow;
  }
};
long long int T, n, i, j, k, in, cnt, l, r, u, v, x, y;
long long int m;
string s;
vector<long long int> xx, yy;
vector<pair<long long int, long long int>> ed;
vector<pair<pair<long long int, long long int>,
            pair<long long int, long long int>>>
    st;
template <typename TT>
void unq(vector<TT> &a) {
  sort(a.begin(), a.end());
  a.resize(unique(a.begin(), a.end()) - a.begin());
}
int main(void) {
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);
  cout.tie(NULL);
  {
    cin >> n >> m;
    if (m == 0) {
      cout << 0 << "\n";
      return 0;
    }
    xx.push_back(-INF);
    xx.push_back(INF);
    yy.push_back(-INF);
    yy.push_back(INF);
    for (i = 0; i < (m); ++i) {
      long long int x1, x2, y1, y2;
      cin >> x1 >> y1 >> x2 >> y2;
      st.push_back({{x1, y1}, {x2, y2}});
      xx.push_back(x1);
      xx.push_back(x2 + 1);
      yy.push_back(y1);
      yy.push_back(y2 + 1);
    }
    unq(xx);
    unq(yy);
    n = (long long int)xx.size();
    n -= 3;
    m = (long long int)yy.size();
    m -= 3;
    for (auto x : st) {
      for (i = (1); i <= (n); ++i)
        for (j = (1); j <= (m); ++j) {
          if (x.first.first <= xx[i] && xx[i] <= x.second.first &&
              x.first.second <= yy[j] && yy[j] <= x.second.second)
            ed.push_back({i, n + j});
        }
    };
    unq(ed);
    ;
    PushRelabel ans(n + m + 2);
    for (auto x : ed) ans.addEdge(x.first, x.second, INF);
    for (i = (1); i <= (n); ++i) ans.addEdge(0, i, xx[i + 1] - xx[i]);
    for (j = (1); j <= (m); ++j)
      ans.addEdge(n + j, n + m + 1, yy[j + 1] - yy[j]);
    cout << ans.max_flow(0, n + m + 1) << "\n";
  }
  aryanc403();
  return 0;
}
