#include <bits/stdc++.h>
using namespace std;
const int N = 5005;
const int M = 100010;
struct Edge {
int to, next;
} edge[M];
int head[N];
int cntE;
void addedge(int u, int v) {
edge[cntE].to = v; edge[cntE].next = head[u]; head[u] = cntE++;
edge[cntE].to = u; edge[cntE].next = head[v]; head[v] = cntE++;
}
int dfn[N], low[N], idx;
int stk[N], top;
bool cut[N];
int block[N];
// 一个割点可能在很多个连通分量
// 如果一个双连通分量内的某些顶点在一个奇圈中(即双连通分量含有奇圈
// 那么这个双连通分量的其他顶点也在某个奇圈中
// 如果一个双连通分量含有奇圈,则他必定不是一个二分图。反过来也成立,这是一个充要条件。
int no;
// block[i] 是去掉i这个节点能够多几个联通块
void tarjan(int u, int fa) {
dfn[u] = low[u] = ++idx;
stk[top++] = u;
block[u] = 0;
int son = 0;
for (int i = head[u]; ~i; i = edge[i].next) {
int v = edge[i].to;
if (v == fa || v == no) continue;
if (!dfn[v]) {
son++;
tarjan(v, u);
low[u] = min(low[u], low[v]);
if (u != fa && low[v] >= dfn[u]) {
cut[u] = true;
block[u]++;
}
/* 求每一个连通分量
if (low[v] >= dfn[u]) {
int x;
do {
x = stk[--top];
push(x);
} while (x != v);
push(u);
}
*/
} else {
low[u] = min(low[u], dfn[v]);
}
}
if (u == fa) {
if (son > 1) cut[u] = 1;
block[u] = son-1;
}
}
void init() {
memset(dfn, 0, sizeof dfn);
top = idx = cntE = 0;
}
int main()
{
int n, m;
while (~scanf("%d%d", &n, &m)) {
int u, v;
memset(head, -1, sizeof head);
for (int i = 0; i < m; ++i) {
scanf("%d%d", &u, &v);
addedge(u, v);
}
int ans = 0;
for (u = 0; u < n; ++u) {
init(); int cnt = 0; no = u;
for (v = 0; v < n; ++v) {
if (v != u && !dfn[v]) {
tarjan(v, v);
cnt++;
}
}
for (v = 0; v < n; ++v) {
if (v != u) ans = max(ans, cnt + block[v]);
}
}
printf("%d\n", ans);
}
return 0;
}