#include <bits/stdc++.h>
using namespace std;
#define debug(x) cerr << #x << " = " << x << '\n';
#define BP() cerr << "OK!\n";
#define PR(A, n) {cerr << #A << " = "; for (int _=1; _<=n; ++_) cerr << A[_] << ' '; cerr << '\n';}
#define PR0(A, n) {cerr << #A << " = "; for (int _=0; _<n; ++_) cerr << A[_] << ' '; cerr << '\n';}
bool maximize(int &a, int b) {if (a>=b) return false; a = b; return true;}
const int maxn = 100002;
int n, m, ID[maxn];
vector<int> g[maxn], node;
vector<pair<int, int> > edge;
bool visited[maxn];
class solver { //Solve for connected graph
public:
vector<int> g[maxn], BCC[maxn], tmp;
int n, nTime, nBCC, num[maxn], low[maxn], nChild[maxn];
int par[maxn], lastComponent[maxn];
stack<int> st;
map<int, int> f[maxn];
void init(int _n, vector<pair<int, int> > e) {
n = _n;
for (int i=1; i<=n; ++i) {
g[i].clear();
BCC[i].clear();
f[i].clear();
}
for (int i=0; i<e.size(); ++i) {
g[e[i].first].push_back(e[i].second);
g[e[i].second].push_back(e[i].first);
}
nBCC = 0;
nTime = 0;
while (st.size())
st.pop();
memset(lastComponent, 0, sizeof(lastComponent));
memset(num, 0, sizeof(num));
}
int dfs(int u) {
num[u] = low[u] = ++nTime;
nChild[u] = 1;
int tmp = 0;
for (int i=0; i<g[u].size(); ++i) {
int v = g[u][i];
if (num[v])
low[u] = min(low[u], num[v]);
else {
st.push(u);
nChild[u] += dfs(v);
low[u] = min(low[u], low[v]);
if (low[v]>=num[u]) {
tmp += nChild[v];
++nBCC;
while (true) {
int t = st.top(); st.pop();
if (maximize(lastComponent[t], nBCC))
BCC[nBCC].push_back(t);
if (t==u)
break;
}
f[u][nBCC] = nChild[v] + 1;
}
}
}
par[u] = n - tmp - 1;
st.push(u);
return nChild[u];
}
int64_t solve() {
if (n==1)
return 0;
int64_t res = 0;
for (int i=1; i<=nBCC; ++i) {
int sz = BCC[i].size();
tmp.clear();
for (int j=0; j<sz; ++j) {
int u = BCC[i][j];
if (f[u].count(i))
tmp.push_back(n-f[u][i]);
else
tmp.push_back(n-par[u]-1);
}
res += 1LL*sz*(sz-1)*(sz-2);
int64_t T = 0, sqrT = 0;
for (int i=0; i<tmp.size(); ++i) {
res += (1LL*(n-sz)*(sz-1) - (n-sz-tmp[i]));
res += (1LL*(n-sz-tmp[i])*(sz-1) - (n-sz-tmp[i]));
T += tmp[i];
sqrT += 1LL*tmp[i]*(tmp[i]-1);
}
for (int i=0; i<tmp.size(); ++i) {
res += (1LL*(T-tmp[i])*(T-tmp[i]-1) - sqrT + 1LL*tmp[i]*(tmp[i]-1));
res += 1LL*tmp[i]*(T-tmp[i]);
}
}
return res;
}
} S;
void visit(int u) {
node.push_back(u);
visited[u] = true;
for (int i=0; i<g[u].size(); ++i) {
int v = g[u][i];
edge.push_back(make_pair(max(u, v), min(u, v)));
if (!visited[v]) {
visit(v);
}
}
}
void make_graph() {
int cnt = 0;
sort(node.begin(), node.end());
sort(edge.begin(), edge.end());
edge.resize(unique(edge.begin(), edge.end())-edge.begin());
for (int i=0; i<node.size(); ++i) {
ID[node[i]] = i+1;
}
for (int i=0; i<edge.size(); ++i) {
edge[i].first = ID[edge[i].first];
edge[i].second = ID[edge[i].second];
}
//PR0(node, node.size());
//for (int i=0; i<edge.size(); ++i)
// cerr << edge[i].first << ' ' << edge[i].second << '\n';
}
int main() {
//freopen("count-triplets.inp", "r", stdin);
//freopen("count-triplets.out", "w", stdout);
ios::sync_with_stdio(0); cin.tie(0);
cin >> n >> m;
for (int i=1; i<=m; ++i) {
int u, v; cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
int64_t res = 0;
for (int i=1; i<=n; ++i) {
if (!visited[i]) {
edge.clear();
node.clear();
visit(i);
make_graph();
S.init(node.size(), edge);
S.dfs(1);
res += S.solve();
}
}
cout << res;
}