#include<bits/stdc++.h>
using namespace std;
const int MM=400005;
int dfn[MM],low[MM],dfc,cnt,in[MM],tot,v[MM],vis[MM],siz[MM],n,m,V,U;
long long ans;
int stk[MM],tp;
vector<int> e[MM],t[MM];
stack<int> s;
void tarjan(int now)///建立圆方树
{
dfn[now]=low[now]=++dfc;
stk[++tp]=now;
++tot;
for(int i=0;i<e[now].size();i++)
{
int to=e[now][i];
//cout<<"qwq"<<' '<<now<<' '<<to<<endl;
if(!dfn[to])
{
//cout<<"in"<<' '<<to<<endl;
tarjan(to);
//cout<<"out"<<' '<<now<<' '<<to<<' '<<low[to]<<endl;
low[now]=min(low[now],low[to]);
if(low[to]==dfn[now])///now就是桥
{
cnt++;///方点+1
for(int x=0;x!=to;--tp)///由于桥可能同时在多个双连通分量内,所以now不出栈
{
x=stk[tp];
v[cnt]++;
t[cnt].push_back(x);
t[x].push_back(cnt);///连接方点和圆点
}
t[now].push_back(cnt);
t[cnt].push_back(now);
v[cnt]++;
}
}
else ///由于是双向边,此时to[i]必然是i的父节点,根据low的定义无需考虑to[i]。
low[now]=min(low[now],dfn[to]);
}
}
void dfs(int now,int fa)
{
siz[now]=(now<=n);
for(int i=0;i<t[now].size();i++)
{
if(t[now][i]==fa)
continue;
dfs(t[now][i],now);
ans+=2ll*v[now]*siz[now]*siz[t[now][i]];
siz[now]+=siz[t[now][i]];
}
ans+=2ll*v[now]*siz[now]*(tot-siz[now]);
}
int main()
{
cin>>n>>m;
cnt=n;
for(int i=1;i<=n;i++)
v[i]=-1;
for(int i=1;i<=m;i++)
{
cin>>U>>V;
e[U].push_back(V);
e[V].push_back(U);
}
for(int i=1;i<=n;i++)
if(!dfn[i])
{
tot=0;
tarjan(i);
tp--;
dfs(i,0);
}
cout<<ans;
return 0;
}