#include <bits/stdc++.h>
using namespace std;

vector<int> graph[100005];
map<pair<int, int>, int> edge_idx;
int parent[300005];
int gidx[300005];
set<int> elem[300005];

int idx(int a, int b)
{
	return edge_idx.insert({ {a, b}, (int)edge_idx.size() + 1 }).first->second;
}

int getparent(int t)
{
	if (parent[t] == 0) parent[t] = t;
	return t == parent[t] ? t : parent[t] = getparent(parent[t]);
}

void merge(int a, int b)
{
	parent[getparent(a)] = getparent(b);
}

map<set<int>, int> dual_idx;
vector<int> dual_graph[100005];
pair<int, int> diag[100005];
int ans[100005];
bool block[100005];
int f1, f2;

void dfs(int t, int p, int len, int target = -1)
{
	if (len == target)
	{
		f1 = t;
		return;
	}

	if (f2 < len)
	{
		f2 = len;
		f1 = t;
	}

	for (int nxt : dual_graph[t])
	{
		if (p == nxt || block[nxt]) continue;
		dfs(nxt, t, len + 1, target);
	}
}

void recur(int t, int col)
{
	f2 = -1;

	dfs(t, -1, 0);
	f2 = 0;
	dfs(f1, -1, 0);
	dfs(f1, -1, 0, f2 / 2);

	ans[f1] = col;
	block[f1] = true;
	for (int nxt : dual_graph[f1])
	{
		if (block[nxt]) continue;
		recur(nxt, col + 1);
	}
}

int main()
{
	int n, m;
	scanf("%d%d", &n, &m);
	for (int i = 1; i < n; i++)
	{
		graph[i].push_back(i + 1);
	}
	graph[n].push_back(1);
	for (int i = 0; i < m; i++)
	{
		int a, b;
		scanf("%d%d", &a, &b);
		graph[a].push_back(b);
		graph[b].push_back(a);

		diag[i] = { a, b };
	}

	for (int i = 1; i <= n; i++) sort(graph[i].begin(), graph[i].end());

	for (int i = 1; i <= n; i++)
	{
		for (int j : graph[i])
		{
			int k = (lower_bound(graph[j].begin(), graph[j].end(), i) - graph[j].begin() + graph[j].size() - 1) % graph[j].size();
			merge(idx(i, j), idx(j, graph[j][k]));
		}
	}

	for (int i = 1; i <= n; i++)
	{
		for (int j : graph[i])
		{
			int k = idx(i, j);
			elem[getparent(k)].insert(-i);
			elem[getparent(k)].insert(-j);
		}
	}

	for (int i = 1; i <= edge_idx.size(); i++)
	{
		if (elem[i].empty()) continue;
		dual_idx.emplace(elem[i], 0);
	}

	{
		int f = 0;
		for (auto &&e : dual_idx)
		{
			e.second = f++;
		}
	}

	for (int i = 1; i <= edge_idx.size(); i++)
	{
		if (elem[i].empty()) continue;
		gidx[i] = dual_idx[elem[i]];
	}

	for (int i = 0; i < m; i++)
	{
		int a = idx(diag[i].first, diag[i].second);
		int b = idx(diag[i].second, diag[i].first);

		dual_graph[gidx[getparent(a)]].push_back(gidx[getparent(b)]);
		dual_graph[gidx[getparent(b)]].push_back(gidx[getparent(a)]);
	}

	recur(0, 1);
	for (int i = 0; i < dual_idx.size(); i++)
	{
		printf("%d ", ans[i]);
	}
	printf("\n");
}


#include <bits/stdc++.h>
using namespace std;

vector<int> graph[100005];
map<pair<int, int>, int> edge_idx;
int parent[300005];
int gidx[300005];
set<int, greater<>> elem[300005];

int idx(int a, int b)
{
	return edge_idx.insert({ {a, b}, (int)edge_idx.size() + 1 }).first->second;
}

int getparent(int t)
{
	if (parent[t] == 0) parent[t] = t;
	return t == parent[t] ? t : parent[t] = getparent(parent[t]);
}

void merge(int a, int b)
{
	parent[getparent(a)] = getparent(b);
}

map<set<int, greater<>>, int> dual_idx;
vector<int> dual_graph[100005];
pair<int, int> diag[100005];
int ans[100005];
bool block[100005];
int f1, f2;
int childcnt[100005];

void dfs(int t, int p)
{
	childcnt[t] = 1;

	for (int nxt : graph[t])
	{
		if (nxt == p || block[nxt]) continue;
		dfs(nxt, t);
		childcnt[t] += childcnt[nxt];
	}
}

void dfs2(int t, int p, int total)
{
	for (int nxt : graph[t])
	{
		if (block[nxt] || nxt == p) continue;

		if (childcnt[nxt] > total / 2)
		{
			dfs2(nxt, t, total);
			return;
		}
	}

	f1 = t;
}

void recur(int t, int col)
{
	dfs(t, -1);
	dfs2(t, -1, childcnt[t]);

	ans[f1] = col;
	block[f1] = true;
	for (int nxt : dual_graph[f1])
	{
		if (block[nxt]) continue;
		recur(nxt, col + 1);
	}
}

int main()
{
	int n, m;
	scanf("%d%d", &n, &m);
	for (int i = 1; i < n; i++)
	{
		graph[i].push_back(i + 1);
	}
	graph[n].push_back(1);
	for (int i = 0; i < m; i++)
	{
		int a, b;
		scanf("%d%d", &a, &b);
		//a = 1; b = i + 3;
		graph[a].push_back(b);
		graph[b].push_back(a);

		diag[i] = { a, b };
	}

	for (int i = 1; i <= n; i++) sort(graph[i].begin(), graph[i].end());

	for (int i = 1; i <= n; i++)
	{
		for (int j : graph[i])
		{
			int k = (lower_bound(graph[j].begin(), graph[j].end(), i) - graph[j].begin() + graph[j].size() - 1) % graph[j].size();
			merge(idx(i, j), idx(j, graph[j][k]));
		}
	}

	for (int i = 1; i <= n; i++)
	{
		for (int j : graph[i])
		{
			int k = idx(i, j);
			elem[getparent(k)].insert(i);
			elem[getparent(k)].insert(j);
		}
	}

	for (int i = 1; i <= edge_idx.size(); i++)
	{
		if (elem[i].empty()) continue;
		dual_idx.emplace(elem[i], 0);
	}

	{
		int f = 0;
		for (auto &&e : dual_idx)
		{
			e.second = f++;
		}
	}

	for (int i = 1; i <= edge_idx.size(); i++)
	{
		if (elem[i].empty()) continue;
		gidx[i] = dual_idx[elem[i]];
	}

	for (int i = 0; i < m; i++)
	{
		int a = idx(diag[i].first, diag[i].second);
		int b = idx(diag[i].second, diag[i].first);

		dual_graph[gidx[getparent(a)]].push_back(gidx[getparent(b)]);
		dual_graph[gidx[getparent(b)]].push_back(gidx[getparent(a)]);
	}

	recur(0, 1);
	for (int i = 0; i < dual_idx.size(); i++)
	{
		printf("%d ", ans[i]);
	}
	printf("\n");
}


#include <bits/stdc++.h>
#define MAX_N 1000100
#define MOD 100000000000000007LL
#define STEP 13331
#define STEP2 9876543

using namespace std;
long long pows[MAX_N],pows2[MAX_N];
void generatePows()
{
    pows[0]=1;
    for(int i=1;i<MAX_N;i++)
        pows[i]=(pows[i-1]*STEP)%MOD;
    pows2[0]=1;
    for(int i=1;i<MAX_N;i++)
        pows2[i]=(pows2[i-1]*STEP2)%MOD;
}
vector<long long> graf[MAX_N];
map<pair<long long,long long>,long long> val,val2;
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        u--;v--;
        graf[u].push_back(v);
        graf[v].push_back(u);

    }
    long long sol=0;
    generatePows();
    for(int i=0;i<n;i++)
    {
        sort(graf[i].begin(),graf[i].end());
        long long hsh=0,hsh2=0;
        for(int j=0;j<graf[i].size();j++)
        {
            hsh+=((graf[i][j]+1)*pows[j])%MOD;
            hsh%=MOD;
            hsh2+=((graf[i][j]+1)*pows2[j])%MOD;
            hsh2%=MOD;
        }
        sol+=val[make_pair(hsh,hsh2)];
        val[make_pair(hsh,hsh2)]++;
        graf[i].push_back(i);
        sort(graf[i].begin(),graf[i].end());
        hsh=0,hsh2=0;
        for(int j=0;j<graf[i].size();j++)
        {
            hsh+=((graf[i][j]+1)*pows[j])%MOD;
            hsh%=MOD;
            hsh2+=((graf[i][j]+1)*pows2[j])%MOD;
            hsh2%=MOD;
        }
        sol+=val2[make_pair(hsh,hsh2)];
        val2[make_pair(hsh,hsh2)]++;
    }
    printf("%lld\n",sol);
    return 0;
}


#include <bits/stdc++.h>
#define MAX_N 1000100
#define MOD 1000000007
#define STEP 257
using namespace std;
long long pows[MAX_N];
void generatePows()
{
    pows[0]=1;
    for(int i=1;i<MAX_N;i++)
        pows[i]=(pows[i-1]*STEP)%MOD;
}
vector<long long> graf[MAX_N];
map<int,int> val,val2;
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        u--;v--;
        graf[u].push_back(v);
        graf[v].push_back(u);

    }
    long long sol=0;
    generatePows();
    for(int i=0;i<n;i++)
    {
        sort(graf[i].begin(),graf[i].end());
        long long hsh=0;
        for(int j=0;j<graf[i].size();j++)
        {
            hsh+=((graf[i][j]+1)*pows[j])%MOD;
            hsh%=MOD;
        }
        sol+=val[hsh];
        sol%=MOD;
        val[hsh]++;
        graf[i].push_back(i);
        sort(graf[i].begin(),graf[i].end());
        hsh=0;
        for(int j=0;j<graf[i].size();j++)
        {
            hsh+=((graf[i][j]+1)*pows[j])%MOD;
            hsh%=MOD;
        }
        sol+=val2[hsh];
        sol%=MOD;
        val2[hsh]++;
    }
    printf("%lld\n",sol);
    return 0;
}


#include <bits/stdc++.h>
#define MAX_N 1000100
#define MOD 100000000000000007LL
#define STEP 13331
using namespace std;
long long pows[MAX_N];
void generatePows()
{
    pows[0]=1;
    for(int i=1;i<MAX_N;i++)
        pows[i]=(pows[i-1]*STEP)%MOD;
}
vector<long long> graf[MAX_N];
unordered_map<long long,long long> val,val2;
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        u--;v--;
        graf[u].push_back(v);
        graf[v].push_back(u);

    }
    long long sol=0;
    generatePows();
    for(int i=0;i<n;i++)
    {
        sort(graf[i].begin(),graf[i].end());
        long long hsh=0;
        for(int j=0;j<graf[i].size();j++)
        {
            hsh+=((graf[i][j]+1)*pows[j])%MOD;
            hsh%=MOD;
        }
        sol+=val[hsh];
        val[hsh]++;
        graf[i].push_back(i);
        sort(graf[i].begin(),graf[i].end());
        hsh=0;
        for(int j=0;j<graf[i].size();j++)
        {
            hsh+=((graf[i][j]+1)*pows[j])%MOD;
            hsh%=MOD;
        }
        sol+=val2[hsh];
        val2[hsh]++;
    }
    printf("%lld\n",sol);
    return 0;
}


#include <bits/stdc++.h>
#define MAX_N 1000100
#define MOD 100000000000000007LL
#define STEP 9876543
using namespace std;
long long pows[MAX_N];
void generatePows()
{
    pows[0]=1;
    for(int i=1;i<MAX_N;i++)
        pows[i]=(pows[i-1]*STEP)%MOD;
}
vector<long long> graf[MAX_N];
map<int,long long> val,val2;
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        u--;v--;
        graf[u].push_back(v);
        graf[v].push_back(u);

    }
    long long sol=0;
    generatePows();
    for(int i=0;i<n;i++)
    {
        sort(graf[i].begin(),graf[i].end());
        long long hsh=0;
        for(int j=0;j<graf[i].size();j++)
        {
            hsh+=((graf[i][j]+1)*pows[j])%MOD;
            hsh%=MOD;
        }
        sol+=val[hsh];
        val[hsh]++;
        graf[i].push_back(i);
        sort(graf[i].begin(),graf[i].end());
        hsh=0;
        for(int j=0;j<graf[i].size();j++)
        {
            hsh+=((graf[i][j]+1)*pows[j])%MOD;
            hsh%=MOD;
        }
        sol+=val2[hsh];
        val2[hsh]++;
    }
    printf("%lld\n",sol);
    return 0;
}


#include <bits/stdc++.h>
#define MAX_N 1000100
#define MOD 100000000000007LL
#define STEP 257
using namespace std;
long long pows[MAX_N];
void generatePows()
{
    pows[0]=1;
    for(int i=1;i<MAX_N;i++)
        pows[i]=(pows[i-1]*STEP)%MOD;
}
vector<long long> graf[MAX_N];
map<int,long long> val,val2;
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        u--;v--;
        graf[u].push_back(v);
        graf[v].push_back(u);

    }
    long long sol=0;
    generatePows();
    for(int i=0;i<n;i++)
    {
        sort(graf[i].begin(),graf[i].end());
        long long hsh=0;
        for(int j=0;j<graf[i].size();j++)
        {
            hsh+=((graf[i][j]+1)*pows[j])%MOD;
            hsh%=MOD;
        }
        sol+=val[hsh];
        val[hsh]++;
        graf[i].push_back(i);
        sort(graf[i].begin(),graf[i].end());
        hsh=0;
        for(int j=0;j<graf[i].size();j++)
        {
            hsh+=((graf[i][j]+1)*pows[j])%MOD;
            hsh%=MOD;
        }
        sol+=val2[hsh];
        val2[hsh]++;
    }
    printf("%lld\n",sol);
    return 0;
}


#include <bits/stdc++.h>
#define MAX_N 1000100
#define MOD 100000000000000007LL
#define STEP 97654321
using namespace std;
long long pows[MAX_N];
void generatePows()
{
    pows[0]=STEP;
    for(int i=1;i<MAX_N;i++)
        pows[i]=pows[i-1]*STEP;
}
vector<long long> graf[MAX_N];
vector<long long> a,b;
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        u--;v--;
        graf[u].push_back(v);
        graf[v].push_back(u);
    }
    long long sol=0;
    generatePows();
    for(int i=0;i<n;i++)
    {
        long long hsh=0;
        for(int j=0;j<graf[i].size();j++)
        {
            hsh+=pows[graf[i][j]];
        }
        a.push_back(hsh);
        hsh+=(pows[i]);
        b.push_back(hsh);
    }
    sort(a.begin(),a.end());
    sort(b.begin(),b.end());
    long long cnt=0;
    for(int i=1;i<a.size();i++)
    {
        if(a[i]!=a[i-1])cnt=0;
        else cnt++;
        sol+=cnt;
    }
    cnt=0;
    for(int i=1;i<b.size();i++)
    {
        if(b[i]!=b[i-1])cnt=0;
        else cnt++;
        sol+=cnt;
    }
    printf("%lld\n",sol);
    return 0;
}


#include <bits/stdc++.h>
#define MAX_N 1000100
#define MOD 100000000000000007LL
#define STEP 97654321
using namespace std;
long long pows[MAX_N];
void generatePows()
{
    pows[0]=STEP;
    for(int i=1;i<MAX_N;i++)
        pows[i]=pows[i-1]*STEP;
}
vector<long long> graf[MAX_N];
unordered_map<long long,long long> val,val2;
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        u--;v--;
        graf[u].push_back(v);
        graf[v].push_back(u);
    }
    long long sol=0;
    generatePows();
    for(int i=0;i<n;i++)
    {
        long long hsh=0;
        for(int j=0;j<graf[i].size();j++)
        {
            hsh+=pows[graf[i][j]];
        }
        sol+=val[hsh];
        val[hsh]++;
        hsh+=(pows[i]);
        hsh%=MOD;
        sol+=val2[hsh];
        val2[hsh]++;
    }
    printf("%lld\n",sol);
    return 0;
}


#include<bits/stdc++.h>
#define ALL(X)        X.begin(),X.end()
#define FOR(I,A,B)    for(int (I) = (A); (I) <= (B); (I)++)
#define FORW(I,A,B)   for(int (I) = (A); (I) < (B);  (I)++)
#define FORD(I,A,B)   for(int (I) = (A); (I) >= (B); (I)--)
#define CLEAR(X)      memset(X,0,sizeof(X))
#define SIZE(X)       int(X.size())
#define CONTAINS(A,X) (A.find(X) != A.end())
#define PB            push_back
#define MP            make_pair
#define X             first
#define Y             second
using namespace std;
template<typename T, typename U> ostream& operator << (ostream& os, const pair<T, U> &_p) { return os << "(" << _p.X << "," << _p.Y << ")"; }
template<typename T> ostream& operator << (ostream &os, const vector<T>& _V) { bool f = true; os << "["; for(auto v: _V) { os << (f ? "" : ",") << v; f = false; } return os << "]"; }
template<typename T> ostream& operator << (ostream &os, const set<T>& _S) { bool f = true; os << "("; for(auto s: _S) { os << (f ? "" : ",") << s; f = false; } return os << ")"; }
template<typename T, typename U> ostream& operator << (ostream &os, const map<T, U>& _M) { return os << set<pair<T, U>>(ALL(_M)); }
typedef signed long long slong;
typedef long double ldouble;
const slong INF = 1000000100;
const ldouble EPS = 1e-9;

const int MAXN = 1000100;
vector<int> G[MAXN];
int N;

void read_data() {
    int M;
    scanf("%d %d", &N, &M);
    FOR(i,1,M) {
        int a, b;
        scanf("%d %d", &a, &b);
        G[a].PB(b);
        G[b].PB(a);
    }
}

#define hash pieprzo
typedef pair<slong, slong> hash;
const hash ZERO = MP(0, 0);
hash operator + (const hash &x, const hash &y) { return MP(x.X + y.X, x.Y + y.Y); }
hash operator - (const hash &x, const hash &y) { return MP(x.X - y.X, x.Y - y.Y); }
hash H[MAXN];
hash gen() {
    slong x1 = (slong(rand()) << 48) + (slong(rand()) << 32) + (slong(rand()) << 16) +  rand();
    slong x2 = (slong(rand()) << 48) + (slong(rand()) << 32) + (slong(rand()) << 16) +  rand();
    return MP(x1, x2);
}
hash v_hash[MAXN];

void solve() {
    FOR(i,1,N) H[i] = gen();
    map<hash, int> M;
    FOR(i,1,N) {
        hash h = ZERO;
        for(int j: G[i]) h = h + H[j];
        v_hash[i] = h;
        ++M[h];
    }
    slong result = 0;
    for(auto m: M) {
        result += slong(m.Y) * (m.Y - 1) / 2;
    }
    FOR(i,1,N) for(int j: G[i]) if(i < j) {
        hash x = v_hash[i] - H[j];
        hash y = v_hash[j] - H[i];
        if(x == y) {
            ++result;
        }
    }
    printf("%lld\n", result);
}

int main() {
    srand(time(NULL));
    read_data();
    solve();
}


#include<bits/stdc++.h>
#define ALL(X)        X.begin(),X.end()
#define FOR(I,A,B)    for(int (I) = (A); (I) <= (B); (I)++)
#define FORW(I,A,B)   for(int (I) = (A); (I) < (B);  (I)++)
#define FORD(I,A,B)   for(int (I) = (A); (I) >= (B); (I)--)
#define CLEAR(X)      memset(X,0,sizeof(X))
#define SIZE(X)       int(X.size())
#define CONTAINS(A,X) (A.find(X) != A.end())
#define PB            push_back
#define MP            make_pair
#define X             first
#define Y             second
using namespace std;
template<typename T, typename U> ostream& operator << (ostream& os, const pair<T, U> &_p) { return os << "(" << _p.X << "," << _p.Y << ")"; }
template<typename T> ostream& operator << (ostream &os, const vector<T>& _V) { bool f = true; os << "["; for(auto v: _V) { os << (f ? "" : ",") << v; f = false; } return os << "]"; }
template<typename T> ostream& operator << (ostream &os, const set<T>& _S) { bool f = true; os << "("; for(auto s: _S) { os << (f ? "" : ",") << s; f = false; } return os << ")"; }
template<typename T, typename U> ostream& operator << (ostream &os, const map<T, U>& _M) { return os << set<pair<T, U>>(ALL(_M)); }
typedef signed long long slong;
typedef long double ldouble;
const slong INF = 1000000100;
const ldouble EPS = 1e-9;

const int MAXN = 1000100;
vector<int> G[MAXN];
int N;

void read_data() {
    int M;
    scanf("%d %d", &N, &M);
    FOR(i,1,M) {
        int a, b;
        scanf("%d %d", &a, &b);
        G[a].PB(b);
        G[b].PB(a);
    }
}

#define hash pieprzo
typedef slong hash;
const hash ZERO = 0;
hash H[MAXN];
hash gen() { return (slong(rand()) << 48) + (slong(rand()) << 32) + (slong(rand()) << 16) +  rand(); }
hash v_hash[MAXN];

void solve() {
    FOR(i,1,N) H[i] = gen();
    map<int, int> M;
    FOR(i,1,N) {
        hash h = ZERO;
        for(int j: G[i]) h += H[j];
        v_hash[i] = h;
        ++M[h];
    }
    slong result = 0;
    for(auto m: M) {
        result += slong(m.Y) * (m.Y - 1) / 2;
    }
    FOR(i,1,N) for(int j: G[i]) if(i < j) {
        hash x = v_hash[i] - H[j];
        hash y = v_hash[j] - H[i];
        if(x == y) {
            ++result;
        }
    }
    printf("%lld\n", result);
}

int main() {
    srand(time(NULL));
    read_data();
    solve();
}


#include<bits/stdc++.h>
#define ALL(X)        X.begin(),X.end()
#define FOR(I,A,B)    for(int (I) = (A); (I) <= (B); (I)++)
#define FORW(I,A,B)   for(int (I) = (A); (I) < (B);  (I)++)
#define FORD(I,A,B)   for(int (I) = (A); (I) >= (B); (I)--)
#define CLEAR(X)      memset(X,0,sizeof(X))
#define SIZE(X)       int(X.size())
#define CONTAINS(A,X) (A.find(X) != A.end())
#define PB            push_back
#define MP            make_pair
#define X             first
#define Y             second
using namespace std;
template<typename T, typename U> ostream& operator << (ostream& os, const pair<T, U> &_p) { return os << "(" << _p.X << "," << _p.Y << ")"; }
template<typename T> ostream& operator << (ostream &os, const vector<T>& _V) { bool f = true; os << "["; for(auto v: _V) { os << (f ? "" : ",") << v; f = false; } return os << "]"; }
template<typename T> ostream& operator << (ostream &os, const set<T>& _S) { bool f = true; os << "("; for(auto s: _S) { os << (f ? "" : ",") << s; f = false; } return os << ")"; }
template<typename T, typename U> ostream& operator << (ostream &os, const map<T, U>& _M) { return os << set<pair<T, U>>(ALL(_M)); }
typedef signed long long slong;
typedef long double ldouble;
const slong INF = 1000000100;
const ldouble EPS = 1e-9;

const int MAXN = 100010;
vector<int> G[MAXN];
int N;

void read_data() {
    int M;
    scanf("%d %d", &N, &M);
    FOR(i,1,M) {
        int a, b;
        scanf("%d %d", &a, &b);
        G[a].PB(b);
        G[b].PB(a);
    }
}

#define hash pieprzo
typedef slong hash;
const hash ZERO = 0;
hash H[MAXN];
hash gen() { return (slong(rand()) << 48) + (slong(rand()) << 32) + (slong(rand()) << 16) +  rand(); }
hash v_hash[MAXN];

void solve() {
    FOR(i,1,N) H[i] = gen();
    map<int, int> M;
    FOR(i,1,N) {
        hash h = ZERO;
        for(int j: G[i]) h += H[j];
        v_hash[i] = h;
        ++M[h];
    }
    slong result = 0;
    for(auto m: M) {
        result += slong(m.Y) * (m.Y - 1) / 2;
    }
    FOR(i,1,N) for(int j: G[i]) if(i < j) {
        hash x = v_hash[i] - H[j];
        hash y = v_hash[j] - H[i];
        if(x == y) {
            ++result;
        }
    }
    printf("%lld\n", result);
}

int main() {
    read_data();
    solve();
}


#include<bits/stdc++.h>
#define ALL(X)        X.begin(),X.end()
#define FOR(I,A,B)    for(int (I) = (A); (I) <= (B); (I)++)
#define FORW(I,A,B)   for(int (I) = (A); (I) < (B);  (I)++)
#define FORD(I,A,B)   for(int (I) = (A); (I) >= (B); (I)--)
#define CLEAR(X)      memset(X,0,sizeof(X))
#define SIZE(X)       int(X.size())
#define CONTAINS(A,X) (A.find(X) != A.end())
#define PB            push_back
#define MP            make_pair
#define X             first
#define Y             second
using namespace std;
template<typename T, typename U> ostream& operator << (ostream& os, const pair<T, U> &_p) { return os << "(" << _p.X << "," << _p.Y << ")"; }
template<typename T> ostream& operator << (ostream &os, const vector<T>& _V) { bool f = true; os << "["; for(auto v: _V) { os << (f ? "" : ",") << v; f = false; } return os << "]"; }
template<typename T> ostream& operator << (ostream &os, const set<T>& _S) { bool f = true; os << "("; for(auto s: _S) { os << (f ? "" : ",") << s; f = false; } return os << ")"; }
template<typename T, typename U> ostream& operator << (ostream &os, const map<T, U>& _M) { return os << set<pair<T, U>>(ALL(_M)); }
typedef signed long long slong;
typedef long double ldouble;
const slong INF = 1000000100;
const ldouble EPS = 1e-9;

const int MAXN = 100010;
vector<int> G[MAXN];
int N;

void read_data() {
    int M;
    scanf("%d %d", &N, &M);
    FOR(i,1,M) {
        int a, b;
        scanf("%d %d", &a, &b);
        G[a].PB(b);
        G[b].PB(a);
    }
}

#define hash pieprzo
typedef slong hash;
const hash ZERO = 0;
hash H[MAXN];
hash gen() { return (slong(rand()) << 32) + rand(); }
hash v_hash[MAXN];

void solve() {
    FOR(i,1,N) H[i] = gen();
    map<int, int> M;
    FOR(i,1,N) {
        hash h = ZERO;
        for(int j: G[i]) h += H[j];
        v_hash[i] = h;
        ++M[h];
    }
    slong result = 0;
    for(auto m: M) {
        result += slong(m.Y) * (m.Y - 1) / 2;
    }
    FOR(i,1,N) for(int j: G[i]) if(i < j) {
        hash x = v_hash[i] - H[j];
        hash y = v_hash[j] - H[i];
        if(x == y) {
            ++result;
        }
    }
    printf("%lld\n", result);
}

int main() {
    read_data();
    solve();
}


#include<bits/stdc++.h>
#define ALL(X)        X.begin(),X.end()
#define FOR(I,A,B)    for(int (I) = (A); (I) <= (B); (I)++)
#define FORW(I,A,B)   for(int (I) = (A); (I) < (B);  (I)++)
#define FORD(I,A,B)   for(int (I) = (A); (I) >= (B); (I)--)
#define CLEAR(X)      memset(X,0,sizeof(X))
#define SIZE(X)       int(X.size())
#define CONTAINS(A,X) (A.find(X) != A.end())
#define PB            push_back
#define MP            make_pair
#define X             first
#define Y             second
using namespace std;
template<typename T, typename U> ostream& operator << (ostream& os, const pair<T, U> &_p) { return os << "(" << _p.X << "," << _p.Y << ")"; }
template<typename T> ostream& operator << (ostream &os, const vector<T>& _V) { bool f = true; os << "["; for(auto v: _V) { os << (f ? "" : ",") << v; f = false; } return os << "]"; }
template<typename T> ostream& operator << (ostream &os, const set<T>& _S) { bool f = true; os << "("; for(auto s: _S) { os << (f ? "" : ",") << s; f = false; } return os << ")"; }
template<typename T, typename U> ostream& operator << (ostream &os, const map<T, U>& _M) { return os << set<pair<T, U>>(ALL(_M)); }
typedef signed long long slong;
typedef long double ldouble;
const slong INF = 1000000100;
const ldouble EPS = 1e-9;

const int MAXN = 1000100;
vector<int> G[MAXN];
int N;

void read_data() {
    int M;
    scanf("%d %d", &N, &M);
    FOR(i,1,M) {
        int a, b;
        scanf("%d %d", &a, &b);
        G[a].PB(b);
        G[b].PB(a);
    }
}

#define hash pieprzo
typedef pair<slong, slong> hash;
const hash ZERO = MP(0, 0);
inline hash operator + (const hash &x, const hash &y) { return MP(x.X + y.X, x.Y + y.Y); }
inline hash operator - (const hash &x, const hash &y) { return MP(x.X - y.X, x.Y - y.Y); }
hash H[MAXN];
inline hash gen() {
    slong x1 = (slong(rand()) << 48) + (slong(rand()) << 32) + (slong(rand()) << 16) +  rand();
    slong x2 = (slong(rand()) << 48) + (slong(rand()) << 32) + (slong(rand()) << 16) +  rand();
    return MP(x1, x2);
}
hash v_hash[MAXN];

void solve() {
    FOR(i,1,N) H[i] = gen();
    vector<hash> M;
    FOR(i,1,N) {
        hash h = ZERO;
        for(int j: G[i]) h = h + H[j];
        v_hash[i] = h;
        M.PB(h);
    }
    sort(ALL(M));
    slong result = 0;
    int cnt = 0;
    hash last = {-INF, -INF};
    M.push_back({INF, INF});
    for(hash m: M) {
        if(m != last) {
            result += slong(cnt) * (cnt - 1) / 2;
            cnt = 1;
        } else {
            ++cnt;
        }
        last = m;
    }
    FOR(i,1,N) for(int j: G[i]) if(i < j) {
        hash x = v_hash[i] - H[j];
        hash y = v_hash[j] - H[i];
        if(x == y) {
            ++result;
        }
    }
    printf("%lld\n", result);
}

int main() {
    srand(time(69));
    read_data();
    solve();
}


#include<bits/stdc++.h>
#define ALL(X)        X.begin(),X.end()
#define FOR(I,A,B)    for(int (I) = (A); (I) <= (B); (I)++)
#define FORW(I,A,B)   for(int (I) = (A); (I) < (B);  (I)++)
#define FORD(I,A,B)   for(int (I) = (A); (I) >= (B); (I)--)
#define CLEAR(X)      memset(X,0,sizeof(X))
#define SIZE(X)       int(X.size())
#define CONTAINS(A,X) (A.find(X) != A.end())
#define PB            push_back
#define MP            make_pair
#define X             first
#define Y             second
using namespace std;
template<typename T, typename U> ostream& operator << (ostream& os, const pair<T, U> &_p) { return os << "(" << _p.X << "," << _p.Y << ")"; }
template<typename T> ostream& operator << (ostream &os, const vector<T>& _V) { bool f = true; os << "["; for(auto v: _V) { os << (f ? "" : ",") << v; f = false; } return os << "]"; }
template<typename T> ostream& operator << (ostream &os, const set<T>& _S) { bool f = true; os << "("; for(auto s: _S) { os << (f ? "" : ",") << s; f = false; } return os << ")"; }
template<typename T, typename U> ostream& operator << (ostream &os, const map<T, U>& _M) { return os << set<pair<T, U>>(ALL(_M)); }
typedef signed long long slong;
typedef long double ldouble;
const slong INF = 1000000100;
const ldouble EPS = 1e-9;

const int MAXN = 1000100;
vector<int> G[MAXN];
int N;

void read_data() {
    int M;
    scanf("%d %d", &N, &M);
    FOR(i,1,M) {
        int a, b;
        scanf("%d %d", &a, &b);
        G[a].PB(b);
        G[b].PB(a);
    }
}

#define hash pieprzo
typedef pair<slong, slong> hash;
const hash ZERO = MP(0, 0);
inline hash operator + (const hash &x, const hash &y) { return MP(x.X + y.X, x.Y + y.Y); }
inline hash operator - (const hash &x, const hash &y) { return MP(x.X - y.X, x.Y - y.Y); }
hash H[MAXN];
inline hash gen() {
    slong x1 = (slong(rand()) << 48) + (slong(rand()) << 32) + (slong(rand()) << 16) +  rand();
    slong x2 = (slong(rand()) << 48) + (slong(rand()) << 32) + (slong(rand()) << 16) +  rand();
    return MP(x1, x2);
}
hash v_hash[MAXN];

void solve() {
    FOR(i,1,N) H[i] = gen();
    vector<hash> M;
    FOR(i,1,N) {
        hash h = ZERO;
        for(int j: G[i]) h = h + H[j];
        v_hash[i] = h;
        M.PB(h);
    }
    sort(ALL(M));
    slong result = 0;
    int cnt = 0;
    hash last = {-INF, -INF};
    M.push_back({INF, INF});
    for(hash m: M) {
        if(m != last) {
            result += slong(cnt) * (cnt - 1) / 2;
            cnt = 1;
        } else {
            ++cnt;
        }
        last = m;
    }
    FOR(i,1,N) for(int j: G[i]) if(i < j) {
        hash x = v_hash[i] - H[j];
        hash y = v_hash[j] - H[i];
        if(x == y) {
            ++result;
        }
    }
    printf("%lld\n", result);
}

int main() {
    srand(69);
    read_data();
    solve();
}


#include<bits/stdc++.h>
#define ALL(X)        X.begin(),X.end()
#define FOR(I,A,B)    for(int (I) = (A); (I) <= (B); (I)++)
#define FORW(I,A,B)   for(int (I) = (A); (I) < (B);  (I)++)
#define FORD(I,A,B)   for(int (I) = (A); (I) >= (B); (I)--)
#define CLEAR(X)      memset(X,0,sizeof(X))
#define SIZE(X)       int(X.size())
#define CONTAINS(A,X) (A.find(X) != A.end())
#define PB            push_back
#define MP            make_pair
#define X             first
#define Y             second
using namespace std;
template<typename T, typename U> ostream& operator << (ostream& os, const pair<T, U> &_p) { return os << "(" << _p.X << "," << _p.Y << ")"; }
template<typename T> ostream& operator << (ostream &os, const vector<T>& _V) { bool f = true; os << "["; for(auto v: _V) { os << (f ? "" : ",") << v; f = false; } return os << "]"; }
template<typename T> ostream& operator << (ostream &os, const set<T>& _S) { bool f = true; os << "("; for(auto s: _S) { os << (f ? "" : ",") << s; f = false; } return os << ")"; }
template<typename T, typename U> ostream& operator << (ostream &os, const map<T, U>& _M) { return os << set<pair<T, U>>(ALL(_M)); }
typedef signed long long slong;
typedef long double ldouble;
const slong INF = 1000000100;
const ldouble EPS = 1e-9;

const int MAXN = 1000100;
vector<int> G[MAXN];
int N;

void read_data() {
    int M;
    scanf("%d %d", &N, &M);
    FOR(i,1,M) {
        int a, b;
        scanf("%d %d", &a, &b);
        G[a].PB(b);
        G[b].PB(a);
    }
}

#define hash pieprzo
typedef pair<slong, slong> hash;
const hash ZERO = MP(0, 0);
inline hash operator + (const hash &x, const hash &y) { return MP(x.X + y.X, x.Y + y.Y); }
inline hash operator - (const hash &x, const hash &y) { return MP(x.X - y.X, x.Y - y.Y); }
hash H[MAXN];
inline hash gen() {
    slong x1 = (slong(rand()) << 48) + (slong(rand()) << 32) + (slong(rand()) << 16) +  rand();
    slong x2 = (slong(rand()) << 48) + (slong(rand()) << 32) + (slong(rand()) << 16) +  rand();
    return MP(x1, x2);
}
hash v_hash[MAXN];

void solve() {
    FOR(i,1,N) H[i] = gen();
    vector<hash> M;
    FOR(i,1,N) {
        hash h = ZERO;
        for(int j: G[i]) h = h + H[j];
        v_hash[i] = h;
        M.PB(h);
    }
    sort(ALL(M));
    slong result = 0;
    int cnt = 0;
    hash last = {-INF, -INF};
    M.push_back({INF, INF});
    for(hash m: M) {
        if(m != last) {
            result += slong(cnt) * (cnt - 1) / 2;
            cnt = 1;
        } else {
            ++cnt;
        }
        last = m;
    }
    FOR(i,1,N) for(int j: G[i]) if(i < j) {
        hash x = v_hash[i] - H[j];
        hash y = v_hash[j] - H[i];
        if(x == y) {
            ++result;
        }
    }
    printf("%lld\n", result);
}

int main() {
    srand(time(NULL));
    read_data();
    solve();
}


#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define F first
#define S second
using namespace std;
typedef unsigned long long ull;
const int maxn = 1e6+5, Q = 13331;
pair<int,int> f[maxn];
ull h1[maxn], h2[maxn];
unordered_map<ull,ull> cnt;
void pre(int n){
	h1[0] = 1;
	for(int i=1;i<=n;i++) h1[i] = h1[i-1]*Q;
}
int main(){
	ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	int n, m; cin >> n >> m;
	pre(n);
	for(int i=0;i<m;i++){
		int u, v; cin >> u >> v;
		f[i] = mp(u,v);
		h2[u] += h1[v]; h2[v] += h1[u];
	}
	for(int i=1;i<=n;i++){
		h1[i] += h2[i]; cnt[h2[i]]++;
	}
	ull ans = 0;
	for(auto p: cnt){
		ans += (p.S*(p.S-1))/2;
	}
	for(int i=0;i<m;i++){
		int u = f[i].F, v = f[i].S;
		if(h1[u] == h1[v]){
			ans++;
		}
	}
	cout << ans << endl;
	return 0;
}


#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define F first
#define S second
using namespace std;
typedef unsigned long long ull;
const int maxn = 1e6+5, Q = 13331;
set<int> e[maxn]; pair<int,int> f[maxn];
ull h1[maxn], h2[maxn], pw[maxn];
unordered_map<ull,ull> cnt;
void pre(int n){
	pw[0] = 1;
	for(int i=1;i<=n;i++) pw[i] = pw[i-1]*Q;
}
ull Hash(set<int> &s){
	ull seed = 0;
	for(auto& i : s) {
    	seed += pw[i];
    }
	return seed;
}
int main(){
	ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	int n, m; cin >> n >> m;
	pre(n);
	for(int i=0;i<m;i++){
		int u, v; cin >> u >> v;
		f[i] = mp(u,v);
		e[u].insert(v); e[v].insert(u);
	}
	for(int i=1;i<=n;i++){
		h1[i] = Hash(e[i]);
		cnt[h1[i]]++;
		h2[i] = h1[i] + pw[i];
	}
	ull ans = 0;
	for(auto p: cnt){
		ans += (p.S*(p.S-1))/2;
	}
	for(int i=0;i<m;i++){
		int u = f[i].F, v = f[i].S;
		if(h2[u] == h2[v]){
			ans++;
		}
	}
	cout << ans << endl;
	return 0;
}


#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define F first
#define S second
using namespace std;
typedef long long ll;
const int maxn = 1e6+5, Q = 13331;
set<int> e[maxn]; pair<int,int> f[maxn];
int h1[maxn], h2[maxn], pw[maxn];
unordered_map<int,int> cnt;
void pre(int n){
	pw[0] = 1;
	for(int i=1;i<=n;i++) pw[i] = pw[i-1]*Q;
}
int Hash(set<int> &s){
	int seed = 0;
	for(auto& i : s) {
    	seed += pw[i];
    }
	return seed;
}
int main(){
	ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	int n, m; cin >> n >> m;
	pre(n);
	for(int i=0;i<m;i++){
		int u, v; cin >> u >> v;
		f[i] = mp(u,v);
		e[u].insert(v); e[v].insert(u);
	}
	for(int i=1;i<=n;i++){
		h1[i] = Hash(e[i]);
		cnt[h1[i]]++;
		h2[i] = h1[i] + pw[i];
	}
	ll ans = 0;
	for(auto p: cnt){
		ans += (p.S*1ll*(p.S-1))/2;
	}
	for(int i=0;i<m;i++){
		int u = f[i].F, v = f[i].S;
		if(h2[u] == h2[v]) ans++;
	}
	cout << ans << endl;
	return 0;
}


#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define F first
#define S second
using namespace std;
typedef long long ll;
const int maxn = 1e6+5, Q = 1e9+5, P = 1e9+7;
set<int> e[maxn]; pair<int,int> f[maxn];
int h1[maxn], h2[maxn], pw[maxn];
unordered_map<int,int> cnt;
void pre(int n){
	pw[0] = 1;
	for(int i=1;i<=n;i++) pw[i] = (pw[i-1]*1ll*Q)%P;
}
int Hash(set<int> &s){
	int seed = 0;
	for(auto& i : s) {
    	seed = (seed + pw[i])%P;
    }
	return seed;
}
int main(){
	ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	int n, m; cin >> n >> m; pre(n);
	for(int i=0;i<m;i++){
		int u, v; cin >> u >> v;
		f[i] = mp(u,v);
		e[u].insert(v); e[v].insert(u);
	}
	for(int i=1;i<=n;i++){
		h1[i] = Hash(e[i]);
		cnt[h1[i]]++;
		e[i].insert(i);
		h2[i] = Hash(e[i]);
		e[i].erase(i);
	}
	ll ans = 0;
	for(auto p: cnt){
		ans += (p.S*1ll*(p.S-1))/2;
	}
	for(int i=0;i<m;i++){
		int u = f[i].F, v = f[i].S;
		if(h2[u] == h2[v]) ans++;
	}
	cout << ans << endl;
	return 0;
}


#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define F first
#define S second
using namespace std;
typedef long long ll;
const int maxn = 1e6+5;
set<int> e[maxn]; pair<int,int> f[maxn];
int h1[maxn], h2[maxn];
unordered_map<int,int> cnt;
int Hash(set<int> &s){
	int seed = 0;
	for(auto& i : s) {
    	seed ^= i + 0x9e3779b9 + (seed << 6) + (seed >> 2);
	}
	return seed;
}
int main(){
	ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	int n, m; cin >> n >> m;
	for(int i=0;i<m;i++){
		int u, v; cin >> u >> v;
		f[i] = mp(u,v);
		e[u].insert(v); e[v].insert(u);
	}
	for(int i=1;i<=n;i++){
		h1[i] = Hash(e[i]);
		cnt[h1[i]]++;
		e[i].insert(i);
		h2[i] = Hash(e[i]);
		e[i].erase(i);
	}
	ll ans = 0;
	for(auto p: cnt){
		ans += (p.S*1ll*(p.S-1))/2;
	}
	for(int i=0;i<m;i++){
		int u = f[i].F, v = f[i].S;
		if(h2[u] == h2[v]) ans++;
	}
	cout << ans << endl;
	return 0;
}


#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define F first
#define S second
using namespace std;
typedef long long ll;
const int maxn = 1e6+5, Q = 13331;
set<int> e[maxn];
int pw[maxn];
unordered_map<int,int> cnt, cnt2;
void pre(int n){
	pw[0] = 1;
	for(int i=1;i<=n;i++) pw[i] = pw[i-1]*Q;
}
int Hash(set<int> &s){
	int seed = 0;
	for(auto& i : s) {
    	seed += pw[i];
    }
	return seed;
}
int main(){
	ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	int n, m; cin >> n >> m;
	pre(n);
	for(int i=0;i<m;i++){
		int u, v; cin >> u >> v;
		e[u].insert(v); e[v].insert(u);
	}
	for(int i=1;i<=n;i++){
		int h1 = Hash(e[i]);
		cnt[h1]++;
		int h2 = h1 + pw[i];
		cnt2[h2]++;
	}
	ll ans = 0;
	for(auto p: cnt){
		ans += (p.S*1ll*(p.S-1))/2;
	}
	for(auto p:cnt2){
		ans += (p.S*1ll*(p.S-1))/2;
	}
	cout << ans << endl;
	return 0;
}


#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define F first
#define S second
using namespace std;
typedef long long ll;
const int maxn = 1e6+5, Q = 13331;
set<int> e[maxn]; pair<int,int> f[maxn];
int h1[maxn], h2[maxn], pw[maxn];
unordered_map<int,int> cnt;
void pre(int n){
	pw[0] = 1;
	for(int i=1;i<=n;i++) pw[i] = pw[i-1]*Q;
}
int Hash(set<int> &s){
	int seed = 0;
	for(auto& i : s) {
    	seed += pw[i];
    }
	return seed;
}
int main(){
	ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	int n, m; cin >> n >> m;
	pre(n);
	for(int i=0;i<m;i++){
		int u, v; cin >> u >> v;
		f[i] = mp(u,v);
		e[u].insert(v); e[v].insert(u);
	}
	for(int i=1;i<=n;i++){
		h1[i] = Hash(e[i]);
		cnt[h1[i]]++;
		h2[i] = h1[i] + pw[i];
		e[i].insert(i);
	}
	ll ans = 0;
	for(auto p: cnt){
		ans += (p.S*1ll*(p.S-1))/2;
	}
	for(int i=0;i<m;i++){
		int u = f[i].F, v = f[i].S;
		if(h2[u] == h2[v] && e[u] == e[v]){
			ans++;
		}
	}
	cout << ans << endl;
	return 0;
}


#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

using namespace std;

const int MAXN = 1000 * 1000 + 5;
vector<int> adj[MAXN];

const long long B = 462924769;
const long long M = 1000*1000*1000+7;
long long BP[MAXN];
long long Hash[MAXN];

pair<int, int> edges[MAXN];

int main(){
    int n, m;
    cin >> n >> m;

    BP[0] = 1;
    for(int i=1; i<=n; i++)
        BP[i] = (BP[i-1]*B) % M;

    for(int i=0; i<m; i++){
        int v, u;
        cin >> v >> u;

        edges[i] = {v, u};

        Hash[v] = Hash[v] + BP[u];
        Hash[u] = Hash[u] + BP[v];
    }

    for(int i=1; i<=n; i++) Hash[i] %= M;

    long long ans = 0;
    for(int i=0; i<m; i++){
            int v = edges[i].first;
            int u = edges[i].second;
            if( ((Hash[u] + M - BP[v]) % M) == ((Hash[v] + M - BP[u]) % M) )
                ans++;
    }

    sort(Hash+1, Hash+n+1);

    long long cnt = 0;
    for(int i=2; i<=n; i++){
        if(Hash[i] == Hash[i-1])
            cnt++;
        else{
            cnt++;
            ans += cnt * (cnt-1) / 2;
            cnt = 0;
        }
    }
    cnt++;
    ans += cnt * (cnt-1) / 2;

    cout << ans << endl;
    return 0;
}


#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

using namespace std;

const int MAXN = 1000 * 1000 + 5;

const long long B = 34359333101ll;
const long long M = 20560971501831619ll;
long long BP[MAXN];
long long Hash[MAXN];

pair<int, int> edges[MAXN];

int main(){
	ios::sync_with_stdio(false);

    int n, m;
    cin >> n >> m;

    BP[0] = 1;
    for(int i=1; i<=n; i++)
        BP[i] = (BP[i-1]*B) % M;

    for(int i=0; i<m; i++){
        int v, u;
        cin >> v >> u;

        edges[i] = {v, u};

        Hash[v] = Hash[v] + BP[u];
        Hash[u] = Hash[u] + BP[v];
    }

    for(int i=1; i<=n; i++) Hash[i] %= M;

    long long ans = 0;
    for(int i=0; i<m; i++){
            int v = edges[i].first;
            int u = edges[i].second;
            if( ((Hash[u] + M - BP[v]) % M) == ((Hash[v] + M - BP[u]) % M) )
                ans++;
    }

    sort(Hash+1, Hash+n+1);
    long long cnt = 0;
    for(int i=2; i<=n; i++){
        if(Hash[i] == Hash[i-1])
            cnt++;
        else{
            cnt++;
            ans += cnt * (cnt-1) / 2;
            cnt = 0;
        }
    }
    cnt++;
    ans += cnt * (cnt-1) / 2;

    cout << ans << endl;
    return 0;
}


#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

using namespace std;

const int MAXN = 1000 * 1000 + 5;
vector<int> adj[MAXN];

const long long B = 462924769;
const long long M = 1000*1000*1000+7;
long long BP[MAXN];
long long Hash[MAXN];

int main(){
	ios::sync_with_stdio(false);
    int n, m;
    cin >> n >> m;

    BP[0] = 1;
    for(int i=1; i<=n; i++)
        BP[i] = (BP[i-1]*B) % M;

    for(int i=0; i<m; i++){
        int v, u;
        cin >> v >> u;
        adj[v].push_back(u);
        adj[u].push_back(v);
    }

    for(int i=1; i<=n; i++)
        for(int j=0; j<adj[i].size(); j++){
            int v = adj[i][j];
            Hash[i] = (Hash[i] + BP[v]) % M;
        }
    long long ans = 0;
    for(int i=1; i<=n; i++)
        for(int j=0; j<adj[i].size(); j++){
            int v = adj[i][j];
            if( ((Hash[i] + M - BP[v]) % M) == ((Hash[v] + M - BP[i]) % M) )
                ans++;
        }
    ans /= 2;
    sort(Hash+1, Hash+n+1);
    long long cnt = 0;
    for(int i=2; i<=n; i++){
        if(Hash[i] == Hash[i-1])
            cnt++;
        else{
            cnt++;
            ans += cnt * (cnt-1) / 2;
            cnt = 0;
        }
    }
    cnt++;
    ans += cnt * (cnt-1) / 2;
    cout << ans << endl;
    return 0;
}


#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

using namespace std;

const int MAXN = 1000 * 1000 + 5;
vector<int> adj[MAXN];

const long long B = 462924769;
const long long M = 1000*1000*1000+7;
long long BP[MAXN];
long long Hash[MAXN];

pair<int, int> edges[MAXN];

int main(){
    int n, m;
    cin >> n >> m;

    BP[0] = 1;
    for(int i=1; i<=n; i++)
        BP[i] = (BP[i-1]*B) % M;

    for(int i=0; i<m; i++){
        int v, u;
        cin >> v >> u;

        edges[i] = {v, u};

        Hash[v] = Hash[v] + BP[u];
        Hash[u] = Hash[u] + BP[v];
    }

    long long ans = 0;
    for(int i=0; i<m; i++){
            int v = edges[i].first;
            int u = edges[i].second;
            if( ((Hash[u] + M - BP[v]) % M) == ((Hash[v] + M - BP[u]) % M) )
                ans++;
    }

    sort(Hash+1, Hash+n+1);

    long long cnt = 0;
    for(int i=2; i<=n; i++){
        if(Hash[i] == Hash[i-1])
            cnt++;
        else{
            cnt++;
            ans += cnt * (cnt-1) / 2;
            cnt = 0;
        }
    }
    cnt++;
    ans += cnt * (cnt-1) / 2;

    cout << ans << endl;
    return 0;
}


#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

using namespace std;

const int MAXN = 1000 * 1000 + 5;

const long long B = 6294643877;
const long long M = 1000*1000*1000+9;
long long BP[MAXN];
long long Hash[MAXN];

pair<int, int> edges[MAXN];

int main(){
	ios::sync_with_stdio(false);

    int n, m;
    cin >> n >> m;

    BP[0] = 1;
    for(int i=1; i<=n; i++)
        BP[i] = (BP[i-1]*B) % M;

    for(int i=0; i<m; i++){
        int v, u;
        cin >> v >> u;

        edges[i] = {v, u};

        Hash[v] = (Hash[v] + BP[u]) % M;
        Hash[u] = (Hash[u] + BP[v]) % M;
    }

    for(int i=1; i<=n; i++) Hash[i] %= M;

    long long ans = 0;
    for(int i=0; i<m; i++){
            int v = edges[i].first;
            int u = edges[i].second;
            if( ((Hash[u] + M - BP[v]) % M) == ((Hash[v] + M - BP[u]) % M) )
                ans++;
    }

    sort(Hash+1, Hash+n+1);
    long long cnt = 0;
    for(int i=2; i<=n; i++){
        if(Hash[i] == Hash[i-1])
            cnt++;
        else{
            cnt++;
            ans += cnt * (cnt-1) / 2;
            cnt = 0;
        }
    }
    cnt++;
    ans += cnt * (cnt-1) / 2;

    cout << ans << endl;
    return 0;
}


#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

using namespace std;

const int MAXN = 1000 * 1000 + 5;
vector<int> adj[MAXN];

const long long B = 462924769;
const long long M = 1000*1000*1000+7;
long long BP[MAXN];
long long Hash[MAXN];

int main(){
    int n, m;
    cin >> n >> m;

    BP[0] = 1;
    for(int i=1; i<=n; i++)
        BP[i] = (BP[i-1]*B) % M;

    for(int i=0; i<m; i++){
        int v, u;
        cin >> v >> u;
        adj[v].push_back(u);
        adj[u].push_back(v);
    }

    for(int i=1; i<=n; i++)
        for(int j=0; j<adj[i].size(); j++){
            int v = adj[i][j];
            Hash[i] = (Hash[i] + BP[v]) % M;
        }
    long long ans = 0;
    for(int i=1; i<=n; i++)
        for(int j=0; j<adj[i].size(); j++){
            int v = adj[i][j];
            if( ((Hash[i] + M - BP[v]) % M) == ((Hash[v] + M - BP[i]) % M) )
                ans++;
        }
    ans /= 2;
    sort(Hash+1, Hash+n+1);
    long long cnt = 0;
    for(int i=2; i<=n; i++){
        if(Hash[i] == Hash[i-1])
            cnt++;
        else{
            cnt++;
            ans += cnt * (cnt-1) / 2;
            cnt = 0;
        }
    }
    cnt++;
    ans += cnt * (cnt-1) / 2;
    cout << ans << endl;
    return 0;
}


#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <string>

using namespace std;

const int MAXN = 1000 * 1000 + 5;

const long long B1 = 6294643877;
const int B2 = 14489;
const long long M = 1000*1000*1000+9;
long long BP1[MAXN];
long long BP2[MAXN];
pair<long long, int> Hash[MAXN];

pair<int, int> edges[MAXN];

int main(){

    int n, m;
    scanf("%d %d", &n, &m);

    BP1[0] = BP2[0] = 1;
    for(int i=1; i<=n; i++){
        BP1[i] = (BP1[i-1]*B1) % M;
        BP2[i] = (BP2[i-1]*B2) % M;
    }

    for(int i=0; i<m; i++){
        int v, u;
        scanf("%d %d", &v, &u);

        edges[i] = {v, u};

        Hash[v].first = (Hash[v].first + BP1[u]) % M;
        Hash[u].first = (Hash[u].first + BP1[v]) % M;

        Hash[v].second = (Hash[v].second + BP2[u]) % M;
        Hash[u].second = (Hash[u].second + BP2[v]) % M;
    }

    for(int i=1; i<=n; i++) Hash[i].first %= M, Hash[i].second %= M;

    long long ans = 0;
    for(int i=0; i<m; i++){
            int v = edges[i].first;
            int u = edges[i].second;
            if( ((Hash[u].first + M - BP1[v]) % M) == ((Hash[v].first + M - BP1[u]) % M) )
                ans++;
    }

    sort(Hash+1, Hash+n+1);
    long long cnt = 0;
    for(int i=2; i<=n; i++){
        if(Hash[i].first == Hash[i-1].first and Hash[i].second == Hash[i-1].second)
            cnt++;
        else{
            cnt++;
            ans += cnt * (cnt-1) / 2;
            cnt = 0;
        }
    }
    cnt++;
    ans += cnt * (cnt-1) / 2;

    printf("%I64d\n", ans);
    return 0;
}


#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

using namespace std;

const int MAXN = 1000 * 1000 + 5;

const long long B = 34359333101;
const long long M = 1000*1000*1000+7;
long long BP[MAXN];
long long Hash[MAXN];

pair<int, int> edges[MAXN];

int main(){
	ios::sync_with_stdio(false);

    int n, m;
    cin >> n >> m;

    BP[0] = 1;
    for(int i=1; i<=n; i++)
        BP[i] = (BP[i-1]*B) % M;

    for(int i=0; i<m; i++){
        int v, u;
        cin >> v >> u;

        edges[i] = {v, u};

        Hash[v] = Hash[v] + BP[u];
        Hash[u] = Hash[u] + BP[v];
    }

    for(int i=1; i<=n; i++) Hash[i] %= M;

    long long ans = 0;
    for(int i=0; i<m; i++){
            int v = edges[i].first;
            int u = edges[i].second;
            if( ((Hash[u] + M - BP[v]) % M) == ((Hash[v] + M - BP[u]) % M) )
                ans++;
    }

    sort(Hash+1, Hash+n+1);
    long long cnt = 0;
    for(int i=2; i<=n; i++){
        if(Hash[i] == Hash[i-1])
            cnt++;
        else{
            cnt++;
            ans += cnt * (cnt-1) / 2;
            cnt = 0;
        }
    }
    cnt++;
    ans += cnt * (cnt-1) / 2;

    cout << ans << endl;
    return 0;
}


#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

using namespace std;

const int MAXN = 1000 * 1000 + 5;
const int B = 175141;
const int M = 1000*1000*1000+7;
int BP[MAXN];
int Hash[MAXN];
vector<int> adj[MAXN];

int main(){
    int n, m;
    cin >> n >> m;

    BP[0] = 1;
    for(int i=1; i<=n; i++)
        BP[i] = (BP[i-1]*B) % M;

    for(int i=0; i<m; i++){
        int v, u;
        cin >> v >> u;
        adj[v].push_back(u);
        adj[u].push_back(v);
    }

    for(int i=1; i<=n; i++)
        for(int j=0; j<adj[i].size(); j++){
            int v = adj[i][j];
            Hash[i] = (Hash[i] + BP[v]) % M;
        }
    long long ans = 0;
    for(int i=1; i<=n; i++)
        for(int j=0; j<adj[i].size(); j++){
            int v = adj[i][j];
            if( ((Hash[i] + M - BP[v]) % M) == ((Hash[v] + M - BP[i]) % M) )
                ans++;
        }
    ans /= 2;
    sort(Hash+1, Hash+n+1);
    long long cnt = 0;
    for(int i=2; i<=n; i++){
        if(Hash[i] == Hash[i-1])
            cnt++;
        else{
            cnt++;
            ans += cnt * (cnt-1) / 2;
            cnt = 0;
        }
    }
    cnt++;
    ans += cnt * (cnt-1) / 2;
    cout << ans << endl;
    return 0;
}


#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

using namespace std;

const int MAXN = 1000 * 1000 + 5;
const int B = 175141;
const int M = 1000*1000*1000+7;
int BP[MAXN];
int hash[MAXN];
vector<int> adj[MAXN];

int main(){
    int n, m;
    cin >> n >> m;

    BP[0] = 1;
    for(int i=1; i<=n; i++)
        BP[i] = (BP[i-1]*B) % M;

    for(int i=0; i<m; i++){
        int v, u;
        cin >> v >> u;
        adj[v].push_back(u);
        adj[u].push_back(v);
    }

    for(int i=1; i<=n; i++)
        for(int j=0; j<adj[i].size(); j++){
            int v = adj[i][j];
            hash[i] = (hash[i] + BP[v]) % M;
        }
    long long ans = 0;
    for(int i=1; i<=n; i++)
        for(int j=0; j<adj[i].size(); j++){
            int v = adj[i][j];
            if( ((hash[i] + M - BP[v]) % M) == ((hash[v] + M - BP[i]) % M) )
                ans++;
        }
    ans /= 2;
    sort(hash+1, hash+n+1);
    long long cnt = 0;
    for(int i=2; i<=n; i++){
        if(hash[i] == hash[i-1])
            cnt++;
        else{
            cnt++;
            ans += cnt * (cnt-1) / 2;
            cnt = 0;
        }
    }
    cnt++;
    ans += cnt * (cnt-1) / 2;
    cout << ans << endl;
    return 0;
}


#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

using namespace std;

const int MAXN = 1000 * 1000 + 5;

const long long B1 = 6294643877;
const int B2 = 14489;
const long long M = 1000*1000*1000+9;
long long BP1[MAXN];
long long BP2[MAXN];
pair<long long, int> Hash[MAXN];

pair<int, int> edges[MAXN];

int main(){
	ios::sync_with_stdio(false);

    int n, m;
    cin >> n >> m;

    BP1[0] = BP2[0] = 1;
    for(int i=1; i<=n; i++){
        BP1[i] = (BP1[i-1]*B1) % M;
        BP2[i] = (BP2[i-1]*B2) % M;
    }

    for(int i=0; i<m; i++){
        int v, u;
        cin >> v >> u;

        edges[i] = {v, u};

        Hash[v].first = (Hash[v].first + BP1[u]) % M;
        Hash[u].first = (Hash[u].first + BP1[v]) % M;

        Hash[v].second = (Hash[v].second + BP2[u]) % M;
        Hash[u].second = (Hash[u].second + BP2[v]) % M;
    }

    for(int i=1; i<=n; i++) Hash[i].first %= M, Hash[i].second %= M;

    long long ans = 0;
    for(int i=0; i<m; i++){
            int v = edges[i].first;
            int u = edges[i].second;
            if( ((Hash[u].first + M - BP1[v]) % M) == ((Hash[v].first + M - BP1[u]) % M) )
                ans++;
    }

    sort(Hash+1, Hash+n+1);
    long long cnt = 0;
    for(int i=2; i<=n; i++){
        if(Hash[i].first == Hash[i-1].first and Hash[i].second == Hash[i-1].second)
            cnt++;
        else{
            cnt++;
            ans += cnt * (cnt-1) / 2;
            cnt = 0;
        }
    }
    cnt++;
    ans += cnt * (cnt-1) / 2;

    cout << ans << endl;
    return 0;
}


#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

using namespace std;

const int MAXN = 1000 * 1000 + 5;

const long long B = 462924769;
const long long M = 1000*1000*1000+7;
long long BP[MAXN];
long long Hash[MAXN];

pair<int, int> edges[MAXN];

int main(){
	ios::sync_with_stdio(false);

    int n, m;
    cin >> n >> m;

    BP[0] = 1;
    for(int i=1; i<=n; i++)
        BP[i] = (BP[i-1]*B) % M;

    for(int i=0; i<m; i++){
        int v, u;
        cin >> v >> u;

        edges[i] = {v, u};

        Hash[v] = Hash[v] + BP[u];
        Hash[u] = Hash[u] + BP[v];
    }

    for(int i=1; i<=n; i++) Hash[i] %= M;

    long long ans = 0;
    for(int i=0; i<m; i++){
            int v = edges[i].first;
            int u = edges[i].second;
            if( ((Hash[u] + M - BP[v]) % M) == ((Hash[v] + M - BP[u]) % M) )
                ans++;
    }

    sort(Hash+1, Hash+n+1);
    long long cnt = 0;
    for(int i=2; i<=n; i++){
        if(Hash[i] == Hash[i-1])
            cnt++;
        else{
            cnt++;
            ans += cnt * (cnt-1) / 2;
            cnt = 0;
        }
    }
    cnt++;
    ans += cnt * (cnt-1) / 2;

    cout << ans << endl;
    return 0;
}


#include<bits/stdc++.h>
using namespace std;
const int N=1e6+6;

long long int hsh[N];
long long int md=1e6;
long long int a[N],b[N];

bool pr(long long int a)
{
	for(long long int i=2;i*i<=a;i++)
		if(a%i==0)
			return 0;
	return 1;
}

int main()
{
	md*=md;
	while(!pr(md))
		md--;
	int n,m;
	cin >> n >> m;
	hsh[0]=1;
	for(int i=1;i<=n;i++)
		hsh[i]=(hsh[i-1]*31)%md;
	for(int i=1;i<=m;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		a[u]+=hsh[v];
		a[v]+=hsh[u];
	}
	long long int ans=0;
	for(int i=1;i<=n;i++)
		b[i]=a[i]+hsh[i];
	sort(a+1,a+n+1);
	sort(b+1,b+n+1);
	int t=0;
	for(int i=1;i<=n;i++)
	{
		if(a[i]==a[i-1])
		{
			ans+=t;
			t++;
		}
		else
			t=1;
	}
	t=0;
	for(int i=1;i<=n;i++)
	{
		if(b[i]==b[i-1])
		{
			ans+=t;
			t++;
		}
		else
			t=1;
	}
	cout<<ans;
	return 0;
}


#include<bits/stdc++.h>
using namespace std;
const int N=1e6+6;

long long int hsh[N];
long long int md=1e6;

bool pr(long long int a)
{
	for(long long int i=2;i*i<=a;i++)
		if(a%i==0)
			return 0;
	return 1;
}

long long int z[N];

unordered_map<long long int,int> t;
unordered_map<long long int,int>tt;

int main()
{
	md*=md;
	while(!pr(md))
		md--;
	int n,m;
	cin >> n >> m;
	hsh[0]=1;
	for(int i=1;i<=n;i++)
		hsh[i]=(hsh[i-1]*7)%md;
	for(int i=1;i<=m;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		z[u]+=hsh[v];
		z[v]+=hsh[u];
	}
	long long int ans=0;
	for(int i=1;i<=n;i++)
	{
		ans+=t[z[i]]++;
		ans+=tt[z[i]+hsh[i]]++;
	}
	cout<<ans;
	return 0;
}


#include<bits/stdc++.h>
using namespace std;
const int N=1e6+6;

vector<int> adj[N];
long long int hsh[N];
long long int md=1e6;
bool pr(long long int a)
{
	for(long long int i=2;i*i<=a;i++)
		if(a%i==0)
			return 0;
	return 1;
}

map<long long int ,int> t;
map<long long int ,int>tt;

int main()
{
	md*=md;
	while(!pr(md))
		md--;
	int n,m;
	cin >> n >> m;
	for(int i=1;i<=m;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		adj[u].push_back(v);
		adj[v].push_back(u);
	}
	hsh[0]=1;
	for(int i=1;i<=n;i++)
		hsh[i]=(hsh[i-1]*7)%md;
	for(int i=1;i<=n;i++)
	{
		long long int z=0;
		for(int p:adj[i])
			z+=hsh[p];
		t[z]++;
		tt[z+hsh[i]]++;
	}
	long long int ans=0;
	for(auto p:t)
		ans+=1LL*p.second*(p.second-1)/2;
	for(auto p:tt)
		ans+=1LL*p.second*(p.second-1)/2;
	cout<<ans;
	return 0;
}


#include<bits/stdc++.h>
using namespace std;
const int N=1e6+6;

vector<int> adj[N];
long long int hsh[N];
long long int md=1e6;

bool pr(long long int a)
{
	for(long long int i=2;i*i<=a;i++)
		if(a%i==0)
			return 0;
	return 1;
}

long long int z[N];

map<long long int ,int> t;
map<long long int ,int>tt;

int main()
{
	md*=md;
	while(!pr(md))
		md--;
	int n,m;
	cin >> n >> m;
	hsh[0]=1;
	for(int i=1;i<=n;i++)
		hsh[i]=(hsh[i-1]*7)%md;
	for(int i=1;i<=m;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		z[u]+=hsh[v];
		z[v]+=hsh[u];
	}
	for(int i=1;i<=n;i++)
	{
		t[z[i]]++;
		tt[z[i]+hsh[i]]++;
	}
	long long int ans=0;
	for(auto p:t)
		ans+=1LL*p.second*(p.second-1)/2;
	for(auto p:tt)
		ans+=1LL*p.second*(p.second-1)/2;
	cout<<ans;
	return 0;
}


#include<bits/stdc++.h>
using namespace std;
const int N=1e6+6;

long long int hsh[N];
long long int md=1e6;

bool pr(long long int a)
{
	for(long long int i=2;i*i<=a;i++)
		if(a%i==0)
			return 0;
	return 1;
}

long long int z[N];

unordered_map<long long int ,int> t;
unordered_map<long long int ,int>tt;

int main()
{
	md*=md;
	while(!pr(md))
		md--;
	int n,m;
	cin >> n >> m;
	hsh[0]=1;
	for(int i=1;i<=n;i++)
		hsh[i]=(hsh[i-1]*7)%md;
	for(int i=1;i<=m;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		z[u]+=hsh[v];
		z[v]+=hsh[u];
	}
	for(int i=1;i<=n;i++)
	{
		t[z[i]]++;
		tt[z[i]+hsh[i]]++;
	}
	long long int ans=0;
	for(auto p:t)
		ans+=1LL*p.second*(p.second-1)/2;
	for(auto p:tt)
		ans+=1LL*p.second*(p.second-1)/2;
	cout<<ans;
	return 0;
}


#include<bits/stdc++.h>
using namespace std;
const int N=1e6+6;

vector<int> adj[N];
long long int hsh[N];
long long int md=1e6;
bool pr(long long int a)
{
	for(long long int i=2;i*i<=a;i++)
		if(a%i==0)
			return 0;
	return 1;
}

map<long long int ,int> t;
map<long long int ,int>tt;

int main()
{
	md*=md;
	while(!pr(md))
		md--;
	int n,m;
	cin >> n >> m;
	for(int i=1;i<=m;i++)
	{
		int u,v;
		cin >> u >> v;
		adj[u].push_back(v);
		adj[v].push_back(u);
	}
	hsh[0]=1;
	for(int i=1;i<=n;i++)
		hsh[i]=(hsh[i-1]*7)%md;
	for(int i=1;i<=n;i++)
	{
		long long int z=0;
		for(int p:adj[i])
			z+=hsh[p];
		t[z]++;
		tt[z+hsh[i]]++;
	}
	long long int ans=0;
	for(auto p:t)
		ans+=1LL*p.second*(p.second-1)/2;
	for(auto p:tt)
		ans+=1LL*p.second*(p.second-1)/2;
	cout<<ans;
	return 0;
}


#include<bits/stdc++.h>
using namespace std;
const int N=1e6+6;

long long int hsh[N];
long long int md=1e6;

bool pr(long long int a)
{
	for(long long int i=2;i*i<=a;i++)
		if(a%i==0)
			return 0;
	return 1;
}

long long int z[N];

unordered_map<long long int,int> t;
unordered_map<long long int,int>tt;

int main()
{
	md*=md;
	while(!pr(md))
		md--;
	md=1e9+7;
	int n,m;
	cin >> n >> m;
	hsh[0]=1;
	for(int i=1;i<=n;i++)
		hsh[i]=(hsh[i-1]*7)%md;
	for(int i=1;i<=m;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		z[u]+=hsh[v];
		z[v]+=hsh[u];
	}
	long long int ans=0;
	for(int i=1;i<=n;i++)
	{
		ans+=t[z[i]]++;
		ans+=tt[z[i]+hsh[i]]++;
	}
	cout<<ans;
	return 0;
}


#include<bits/stdc++.h>
using namespace std;
const int N=1e6+6;

long long int hsh[N];
long long int md=1e6;
long long int a[N],b[N];

bool pr(long long int a)
{
	for(long long int i=2;i*i<=a;i++)
		if(a%i==0)
			return 0;
	return 1;
}

int main()
{
	md*=md;
	while(!pr(md))
		md--;
	int n,m;
	cin >> n >> m;
	hsh[0]=1;
	for(int i=1;i<=n;i++)
		hsh[i]=(hsh[i-1]*7)%md;
	for(int i=1;i<=m;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		a[u]+=hsh[v];
		a[v]+=hsh[u];
	}
	long long int ans=0;
	for(int i=1;i<=n;i++)
		b[i]=a[i]+hsh[i];
	sort(a+1,a+n+1);
	sort(b+1,b+n+1);
	int t=0;
	for(int i=1;i<=n;i++)
	{
		if(a[i]==a[i-1])
		{
			ans+=t;
			t++;
		}
		else
			t=1;
	}
	t=0;
	for(int i=1;i<=n;i++)
	{
		if(b[i]==b[i-1])
		{
			ans+=t;
			t++;
		}
		else
			t=1;
	}
	cout<<ans;
	return 0;
}


/*
Nahid Hossain
Jahangirnagar University
Roll:54
*/
#include<bits/stdc++.h>
#define ll          long long int
#define db          double
#define pf          printf
#define sf          scanf
#define ff          first
#define ss          second
#define clr         clear()
#define sz          size()
#define pb          push_back
#define mk          make_pair
#define pi          acos(-1)
#define inf         2000000000000000000
#define mod         1000000021
#define ull         unsigned long long int
#define f(i,k,n)    for(i=k;i<n;i++)
#define fr(i,n,k)   for(i=n;i>=k;i--)
#define ent(a)      scanf("%lld",&a)
#define ent2(a,b)   scanf("%lld%lld",&a,&b)
#define ent3(a,b,c) scanf("%lld%lld%lld",&a,&b,&c)
#define mem(a)      memset(a,0,sizeof(a))
#define vec(v,s)    vector<ll>v[s]
#define arr(a,s)    ll a[s];
#define bitone(x)   __builtin_popcount(x)
//#define check(n,pos) (n&(1<<pos))
//#define set(n,pos)  (n|(1<<pos))
//knight and king//
int dr[]= {2,2,-2,-2,1,-1,1,-1};
int dc[]= {1,-1,1,-1,2,2,-2,-2};
int dr1[]= {0,0,1,1,1,-1,-1,-1};
int dc1[]= {1,-1,1,0,-1,0,1,-1};
int dr2[]= {0,0,1,-1};
int dc2[]= {1,-1,0,0};
////////////////////////////
using namespace std;
#define ma 1000005
ll has1=29,has2=31;
ll mod1=1000000007,mod2=1000000021;
ll po1[ma],a[ma],b[ma],po2[ma];
vector<int>adj[ma];
int main()
{
    ll n,m,i,j,u,v;
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    while(cin>>n>>m)
    {
        ll s=0;
        po2[0]=po1[0]=1;
        for(i=1;i<=n;i++)
        {
            po1[i]=(po1[i-1]*has1)%mod1;
            po2[i]=(po2[i-1]*has2)%mod2;
        }
        map<pair<ll,ll> ,ll >mp;
        for(i=1;i<=m;i++)
        {
            cin>>u>>v;
            a[u]+=po1[v];
            a[v]+=po1[u];
            a[u]%=mod1;a[v]%=mod1;
            b[u]+=po2[v];
            b[v]+=po2[u];
            b[u]%=mod2;b[v]%=mod2;
            adj[u].pb(v);
            adj[v].pb(u);
        }

        ll ans=0;

        for(i=1;i<=n;i++)
        {
            mp[{a[i],b[i]}]++;
        }

        for(i=1;i<=n;i++)
        {
            ll x=a[i];
            ll y=b[i];
            ans+=mp[{x,y}]-1;
            for(j=0;j<adj[i].sz;j++)
            {
                ll p=adj[i][j];
                ll y=(a[i]+po1[i])%mod1;
                ll y1=(a[p]+po1[p])%mod1;
                ll y2=(b[i]+po2[i])%mod2;
                ll y3=(b[p]+po2[p])%mod;
                if(y==y1&&y2==y3)ans++;
            }
        }

        cout<<ans/2<<endl;

    }
    return 0;
}


/*
Nahid Hossain
Jahangirnagar University
Roll:54
*/
#include<bits/stdc++.h>
#define ll          long long int
#define db          double
#define pf          printf
#define sf          scanf
#define ff          first
#define ss          second
#define clr         clear()
#define sz          size()
#define pb          push_back
#define mk          make_pair
#define pi          acos(-1)
#define inf         2000000000000000000
#define mod         1000000021
#define ull         unsigned long long int
#define f(i,k,n)    for(i=k;i<n;i++)
#define fr(i,n,k)   for(i=n;i>=k;i--)
#define ent(a)      scanf("%lld",&a)
#define ent2(a,b)   scanf("%lld%lld",&a,&b)
#define ent3(a,b,c) scanf("%lld%lld%lld",&a,&b,&c)
#define mem(a)      memset(a,0,sizeof(a))
#define vec(v,s)    vector<ll>v[s]
#define arr(a,s)    ll a[s];
#define bitone(x)   __builtin_popcount(x)
//#define check(n,pos) (n&(1<<pos))
//#define set(n,pos)  (n|(1<<pos))
//knight and king//
int dr[]= {2,2,-2,-2,1,-1,1,-1};
int dc[]= {1,-1,1,-1,2,2,-2,-2};
int dr1[]= {0,0,1,1,1,-1,-1,-1};
int dc1[]= {1,-1,1,0,-1,0,1,-1};
int dr2[]= {0,0,1,-1};
int dc2[]= {1,-1,0,0};
////////////////////////////
using namespace std;
#define ma 1000005
ll has=151;
ll po[ma],a[ma];
vector<int>adj[ma];
int main()
{
    ll n,m,i,j,u,v;
    while(cin>>n>>m)
    {
        ll s=0;
        po[0]=1;
        for(i=1;i<=n;i++)
        {
            po[i]=(po[i-1]*31)%mod;
        }
        map<ll,ll>mp;
        for(i=1;i<=m;i++)
        {
            cin>>u>>v;
            a[u]+=po[v];
            a[v]+=po[u];
            a[u]%=mod;a[v]%=mod;
            adj[u].pb(v);
            adj[v].pb(u);
        }

        ll ans=0;

        for(i=1;i<=n;i++)mp[a[i]]++;

        for(i=1;i<=n;i++)
        {
            ll x=a[i];
            ans+=mp[x]-1;
            for(j=0;j<adj[i].sz;j++)
            {
                ll p=adj[i][j];
                ll y=(x+po[i])%mod;
                ll y1=(a[p]+po[p])%mod;
              //  cout<<y<<" "<<y1<<endl;
                if(y==y1)ans++;
            }
        }

        cout<<ans/2<<endl;

    }
    return 0;
}


/*
Nahid Hossain
Jahangirnagar University
Roll:54
*/
#include<bits/stdc++.h>
#define ll          long long int
#define db          double
#define pf          printf
#define sf          scanf
#define ff          first
#define ss          second
#define clr         clear()
#define sz          size()
#define pb          push_back
#define mk          make_pair
#define pi          acos(-1)
#define inf         2000000000000000000
#define mod         1000000021
#define ull         unsigned long long int
#define f(i,k,n)    for(i=k;i<n;i++)
#define fr(i,n,k)   for(i=n;i>=k;i--)
#define ent(a)      scanf("%lld",&a)
#define ent2(a,b)   scanf("%lld%lld",&a,&b)
#define ent3(a,b,c) scanf("%lld%lld%lld",&a,&b,&c)
#define mem(a)      memset(a,0,sizeof(a))
#define vec(v,s)    vector<ll>v[s]
#define arr(a,s)    ll a[s];
#define bitone(x)   __builtin_popcount(x)
//#define check(n,pos) (n&(1<<pos))
//#define set(n,pos)  (n|(1<<pos))
//knight and king//
int dr[]= {2,2,-2,-2,1,-1,1,-1};
int dc[]= {1,-1,1,-1,2,2,-2,-2};
int dr1[]= {0,0,1,1,1,-1,-1,-1};
int dc1[]= {1,-1,1,0,-1,0,1,-1};
int dr2[]= {0,0,1,-1};
int dc2[]= {1,-1,0,0};
////////////////////////////
using namespace std;
#define ma 1000005
ll has=31;
ll po[ma],a[ma];
vector<int>adj[ma];
int main()
{
    ll n,m,i,j,u,v;
    while(cin>>n>>m)
    {
        ll s=0;
        po[0]=1;
        for(i=1;i<=n;i++)
        {
            po[i]=(po[i-1]*31)%mod;
        }
        map<ll,ll>mp;
        for(i=1;i<=m;i++)
        {
            cin>>u>>v;
            a[u]+=po[v];
            a[v]+=po[u];
            a[u]%=mod;a[v]%=mod;
            adj[u].pb(v);
            adj[v].pb(u);
        }

        ll ans=0;

        for(i=1;i<=n;i++)mp[a[i]]++;

        for(i=1;i<=n;i++)
        {
            ll x=a[i];
            ans+=mp[x]-1;
            for(j=0;j<adj[i].sz;j++)
            {
                ll p=adj[i][j];
                ll y=(x+po[i])%mod;
                ll y1=(a[p]+po[p])%mod;
              //  cout<<y<<" "<<y1<<endl;
                if(y==y1)ans++;
            }
        }

        cout<<ans/2<<endl;

    }
    return 0;
}


/*
Nahid Hossain
Jahangirnagar University
Roll:54
*/
#include<bits/stdc++.h>
#define ll          long long int
#define db          double
#define pf          printf
#define sf          scanf
#define ff          first
#define ss          second
#define clr         clear()
#define sz          size()
#define pb          push_back
#define mk          make_pair
#define pi          acos(-1)
#define inf         2000000000000000000
#define mod         1000000021
#define ull         unsigned long long int
#define f(i,k,n)    for(i=k;i<n;i++)
#define fr(i,n,k)   for(i=n;i>=k;i--)
#define ent(a)      scanf("%lld",&a)
#define ent2(a,b)   scanf("%lld%lld",&a,&b)
#define ent3(a,b,c) scanf("%lld%lld%lld",&a,&b,&c)
#define mem(a)      memset(a,0,sizeof(a))
#define vec(v,s)    vector<ll>v[s]
#define arr(a,s)    ll a[s];
#define bitone(x)   __builtin_popcount(x)
//#define check(n,pos) (n&(1<<pos))
//#define set(n,pos)  (n|(1<<pos))
//knight and king//
int dr[]= {2,2,-2,-2,1,-1,1,-1};
int dc[]= {1,-1,1,-1,2,2,-2,-2};
int dr1[]= {0,0,1,1,1,-1,-1,-1};
int dc1[]= {1,-1,1,0,-1,0,1,-1};
int dr2[]= {0,0,1,-1};
int dc2[]= {1,-1,0,0};
////////////////////////////
using namespace std;
#define ma 1000005
ll has1=29,has2=31;
ll mod1=1000000007,mod2=1000000021;
ll po1[ma],a[ma],b[ma],po2[ma];
vector<int>adj[ma];
int main()
{
    ll n,m,i,j,u,v;
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    while(cin>>n>>m)
    {
        ll s=0;
        po2[0]=po1[0]=1;
        for(i=1;i<=n;i++)
        {
            po1[i]=(po1[i-1]*has1)%mod1;
            po2[i]=(po2[i-1]*has2)%mod2;
        }
        map<pair<ll,ll> ,ll >mp;
        for(i=1;i<=m;i++)
        {
            cin>>u>>v;
            a[u]+=po1[v];
            a[v]+=po1[u];
            a[u]%=mod1;a[v]%=mod1;
            b[u]+=po2[v];
            b[v]+=po2[u];
            b[u]%=mod2;a[v]%=mod2;
            adj[u].pb(v);
            adj[v].pb(u);
        }

        ll ans=0;

        for(i=1;i<=n;i++)
        {
            mp[{a[i],b[i]}]++;
        }

        for(i=1;i<=n;i++)
        {
            ll x=a[i];
            ll y=b[i];
            ans+=mp[{x,y}]-1;
            for(j=0;j<adj[i].sz;j++)
            {
                ll p=adj[i][j];
                ll y=(a[i]+po1[i])%mod1;
                ll y1=(a[p]+po1[p])%mod1;
                ll y2=(b[i]+po2[i])%mod2;
                ll y3=(b[p]+po2[p])%mod;
                if(y==y1&&y2==y3)ans++;
            }
        }

        cout<<ans/2<<endl;

    }
    return 0;
}


#include <string.h>
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <unordered_map>
// #include <unordered_set>
#include <utility>
#include <vector>

#define pb push_back
#define mp make_pair

#ifdef ONLINE_JUDGE
#define link renamelink
#define next renamenext
#define prev renameprev
#endif

using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> pii;
typedef pair<LL, LL> pll;
typedef long double ld;

inline void EnableFileIO(const string &fileName, bool local = 0) {
  if (fileName.empty()) return;
  if (local) {
    freopen((fileName + ".in").c_str(), "r", stdin);
    freopen((fileName + ".out").c_str(), "w", stdout);
    return;
  }
#ifdef ONLINE_JUDGE
  freopen((fileName + ".in").c_str(), "r", stdin);
  freopen((fileName + ".out").c_str(), "w", stdout);
#endif
}

const int INF = (1 << 30) - 1;
const LL LINF = (1LL << 61) - 1;
const double EPS = 1e-10, PI = acos(-1);
const int N = 1e6 + 100, P = 1e9 + 7;

int n, m;
set<int> G[N];
unordered_map<ULL, int> a, b;
ULL pw[N], val[N];

ULL calc(set<int> &g) {
  ULL res = 0;
  for (auto i : g) res = res * P + i;
  return res;
}
void pre(int u) {
  a[calc(G[u])]++;
  G[u].insert(u);
  b[calc(G[u])]++;
}
LL count(unordered_map<ULL, int> &a) {
  LL res = 0;
  for (auto i : a) {
    res += i.second * 1LL * (i.second - 1) / 2;
  }
  return res;
}
int main() {
  // srand((int)time(0));
  // printf("Hello, world!\n");
  // EnableFileIO("");

  pw[0] = 1;
  for (int i = 1; i < N; i++) pw[i] = pw[i - 1] + 1;

  cin >> n >> m;
  for (int i = 0; i < m; i++) {
    int u, v;
    cin >> u >> v;
    G[u].insert(v);
    G[v].insert(u);
  }
  for (int i = 1; i <= n; i++) pre(i);

  cout << count(a) + count(b) << endl;
  return 0;
}


#include <string.h>
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <unordered_map>
// #include <unordered_set>
#include <utility>
#include <vector>

#define pb push_back
#define mp make_pair

#ifdef ONLINE_JUDGE
#define link renamelink
#define next renamenext
#define prev renameprev
#endif

using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> pii;
typedef pair<LL, LL> pll;
typedef long double ld;

inline void EnableFileIO(const string &fileName, bool local = 0) {
  if (fileName.empty()) return;
  if (local) {
    freopen((fileName + ".in").c_str(), "r", stdin);
    freopen((fileName + ".out").c_str(), "w", stdout);
    return;
  }
#ifdef ONLINE_JUDGE
  freopen((fileName + ".in").c_str(), "r", stdin);
  freopen((fileName + ".out").c_str(), "w", stdout);
#endif
}

const int INF = (1 << 30) - 1;
const LL LINF = (1LL << 61) - 1;
const double EPS = 1e-10, PI = acos(-1);
const int N = 1e6 + 100, P = 1e9 + 7;

int n, m;
vector<int> G[N];
vector<ULL> a, b;
ULL val[N];
void pre(int u) {
  ULL k = 0;
  for (int i : G[u]) k ^= val[i];
  a.pb(k), b.pb(k ^ val[u]);
}
LL count(vector<ULL> &a) {
  sort(a.begin(), a.end());
  LL res = 0;
  for (int i = 0, j = 0; i < n; i = j) {
    while (j < n && a[j] == a[i]) ++j;
    res += (j - i) * 1LL * (j - i - 1) / 2;
  }
  return res;
}
inline LL bigrand() { return (rand() << 16) | rand(); }
inline LL hugerand() { return (bigrand() << 32) | bigrand(); }
int main() {
  srand((int)time(0));
  // printf("Hello, world!\n");
  // EnableFileIO("");

  scanf("%d%d", &n, &m);
  for (int i = 0, u, v; i < m; i++) {
    scanf("%d%d", &u, &v);
    G[u].pb(v), G[v].pb(u);
  }
  for (int i = 1; i <= n; i++) {
    val[i] = hugerand();
  }
  for (int i = 1; i <= n; i++) pre(i);
  cout << count(a) + count(b) << endl;
  return 0;
}


#include <string.h>
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <unordered_map>
// #include <unordered_set>
#include <utility>
#include <vector>

#define pb push_back
#define mp make_pair

#ifdef ONLINE_JUDGE
#define link renamelink
#define next renamenext
#define prev renameprev
#endif

using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> pii;
typedef pair<LL, LL> pll;
typedef long double ld;

inline void EnableFileIO(const string &fileName, bool local = 0) {
  if (fileName.empty()) return;
  if (local) {
    freopen((fileName + ".in").c_str(), "r", stdin);
    freopen((fileName + ".out").c_str(), "w", stdout);
    return;
  }
#ifdef ONLINE_JUDGE
  freopen((fileName + ".in").c_str(), "r", stdin);
  freopen((fileName + ".out").c_str(), "w", stdout);
#endif
}

const int INF = (1 << 30) - 1;
const LL LINF = (1LL << 61) - 1;
const double EPS = 1e-10, PI = acos(-1);
const int N = 1e6 + 100, P = 1e9 + 7;

int n, m;
vector<int> G[N];
typedef pair<ULL, ULL> puu;
unordered_map<ULL, int> a, b;
ULL val[N];
ULL val2[N];
void pre(int u) {
  ULL k = 0, k2 = 0;
  for (int i : G[u]) {
    k ^= val[i];
    k2 ^= val2[i];
  }
  a[k + k2]++, b[(k ^ val[u]) + (k2 ^ val2[u])]++;
}
LL count(unordered_map<ULL, int> &a) {
  LL res = 0;
  for (auto i : a) {
    res += i.second * 1LL * (i.second - 1) / 2;
  }
  return res;
}
#define bigrand (rand() * 1LL * rand())
int main() {
  srand((int)time(0));
  // printf("Hello, world!\n");
  // EnableFileIO("");

  scanf("%d%d", &n, &m);
  for (int i = 0; i < m; i++) {
    int u, v;
    scanf("%d%d", &u, &v);
    G[u].pb(v);
    G[v].pb(u);
  }
  for (int i = 1; i <= n; i++) {
    val[i] = bigrand ^ bigrand;
    val2[i] = bigrand ^ bigrand;
  }
  for (int i = 1; i <= n; i++) pre(i);
  cout << count(a) + count(b) << endl;
  return 0;
}


#include <string.h>
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
// #include <unordered_map>
// #include <unordered_set>
#include <utility>
#include <vector>

#define pb push_back
#define mp make_pair

#ifdef ONLINE_JUDGE
#define link renamelink
#define next renamenext
#define prev renameprev
#endif

using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> pii;
typedef pair<LL, LL> pll;
typedef long double ld;

inline void EnableFileIO(const string &fileName, bool local = 0) {
  if (fileName.empty()) return;
  if (local) {
    freopen((fileName + ".in").c_str(), "r", stdin);
    freopen((fileName + ".out").c_str(), "w", stdout);
    return;
  }
#ifdef ONLINE_JUDGE
  freopen((fileName + ".in").c_str(), "r", stdin);
  freopen((fileName + ".out").c_str(), "w", stdout);
#endif
}

const int INF = (1 << 30) - 1;
const LL LINF = (1LL << 61) - 1;
const double EPS = 1e-10, PI = acos(-1);
const int N = 1e6 + 100, P = 1e9 + 7;

int n, m;
set<int> G[N];
map<ULL, int> a, b;
ULL pw[N], val[N];

ULL calc(set<int> &g) {
  ULL res = 0;
  for (auto i : g) res = res * P + i;
  return res;
}
void pre(int u) {
  a[calc(G[u])]++;
  G[u].insert(u);
  b[calc(G[u])]++;
}
LL count(map<ULL, int> &a) {
  LL res = 0;
  for (auto i : a) {
    res += i.second * 1LL * (i.second - 1) / 2;
  }
  return res;
}
int main() {
  // srand((int)time(0));
  // printf("Hello, world!\n");
  // EnableFileIO("");

  pw[0] = 1;
  for (int i = 1; i < N; i++) pw[i] = pw[i - 1] + 1;

  cin >> n >> m;
  for (int i = 0; i < m; i++) {
    int u, v;
    cin >> u >> v;
    G[u].insert(v);
    G[v].insert(u);
  }
  for (int i = 1; i <= n; i++) pre(i);

  cout << count(a) + count(b) << endl;
  return 0;
}


#include <string.h>
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <unordered_map>
// #include <unordered_set>
#include <utility>
#include <vector>

#define pb push_back
#define mp make_pair

#ifdef ONLINE_JUDGE
#define link renamelink
#define next renamenext
#define prev renameprev
#endif

using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> pii;
typedef pair<LL, LL> pll;
typedef long double ld;

inline void EnableFileIO(const string &fileName, bool local = 0) {
  if (fileName.empty()) return;
  if (local) {
    freopen((fileName + ".in").c_str(), "r", stdin);
    freopen((fileName + ".out").c_str(), "w", stdout);
    return;
  }
#ifdef ONLINE_JUDGE
  freopen((fileName + ".in").c_str(), "r", stdin);
  freopen((fileName + ".out").c_str(), "w", stdout);
#endif
}

const int INF = (1 << 30) - 1;
const LL LINF = (1LL << 61) - 1;
const double EPS = 1e-10, PI = acos(-1);
const int N = 1e6 + 100, P = 1e9 + 7;

int n, m;
vector<int> G[N];
unordered_map<ULL, int> a, b;
ULL pw[N], val[N];

void pre(int u) {
  sort(G[u].begin(), G[u].end());
  ULL v1 = 0, v2 = 0;
  bool f = 0;
  for (int i = 0; i < G[u].size(); i++) {
    int k = G[u][i];
    v1 = v1 * P + k;
    if (k > u && !f) {
      v2 = v2 * P + u;
      f = 1;
    }
    v2 = v2 * P + k;
  }
  if (!f) {
    v2 = v2 * P + u;
  }
  a[v1]++;
  b[v2]++;
}
LL count(unordered_map<ULL, int> &a) {
  LL res = 0;
  for (auto i : a) {
    res += i.second * 1LL * (i.second - 1) / 2;
  }
  return res;
}
int main() {
  // srand((int)time(0));
  // printf("Hello, world!\n");
  // EnableFileIO("");

  scanf("%d%d",&n,&m);
  for (int i = 0; i < m; i++) {
    int u, v;
    scanf("%d%d",&u,&v);
    G[u].pb(v);
    G[v].pb(u);
  }
  for (int i = 1; i <= n; i++) pre(i);

  cout << count(a) + count(b) << endl;
  return 0;
}


#include <string.h>
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <unordered_map>
// #include <unordered_set>
#include <utility>
#include <vector>

#define pb push_back
#define mp make_pair

#ifdef ONLINE_JUDGE
#define link renamelink
#define next renamenext
#define prev renameprev
#endif

using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> pii;
typedef pair<LL, LL> pll;
typedef long double ld;

inline void EnableFileIO(const string &fileName, bool local = 0) {
  if (fileName.empty()) return;
  if (local) {
    freopen((fileName + ".in").c_str(), "r", stdin);
    freopen((fileName + ".out").c_str(), "w", stdout);
    return;
  }
#ifdef ONLINE_JUDGE
  freopen((fileName + ".in").c_str(), "r", stdin);
  freopen((fileName + ".out").c_str(), "w", stdout);
#endif
}

const int INF = (1 << 30) - 1;
const LL LINF = (1LL << 61) - 1;
const double EPS = 1e-10, PI = acos(-1);
const int N = 1e6 + 100, P = 1e9 + 7;

int n, m;
vector<int> G[N];
typedef pair<ULL, ULL> puu;
map<puu, int> a, b;

ULL val[N];
ULL val2[N];
void pre(int u) {
  ULL k = 0, k2 = 0;
  for (int i : G[u]) {
    k ^= val[i];
    k2 ^= val2[i];
  }
  a[mp(k, k2)]++, b[mp((k ^ val[u]), (k2 ^ val2[u]))]++;
}
LL count(map<puu, int> &a) {
  LL res = 0;
  for (auto i : a) {
    res += i.second * 1LL * (i.second - 1) / 2;
  }
  return res;
}
#define bigrand (rand() * 1LL * rand())
int main() {
  srand((int)time(0));
  // printf("Hello, world!\n");
  // EnableFileIO("");

  scanf("%d%d", &n, &m);
  for (int i = 0; i < m; i++) {
    int u, v;
    scanf("%d%d", &u, &v);
    G[u].pb(v);
    G[v].pb(u);
  }
  for (int i = 1; i <= n; i++) {
    val[i] = bigrand ^ bigrand;
    val2[i] = bigrand ^ bigrand;
  }
  for (int i = 1; i <= n; i++) pre(i);
  cout << count(a) + count(b) << endl;
  return 0;
}


#include <string.h>
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <unordered_map>
// #include <unordered_set>
#include <utility>
#include <vector>

#define pb push_back
#define mp make_pair

#ifdef ONLINE_JUDGE
#define link renamelink
#define next renamenext
#define prev renameprev
#endif

using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> pii;
typedef pair<LL, LL> pll;
typedef long double ld;

inline void EnableFileIO(const string &fileName, bool local = 0) {
  if (fileName.empty()) return;
  if (local) {
    freopen((fileName + ".in").c_str(), "r", stdin);
    freopen((fileName + ".out").c_str(), "w", stdout);
    return;
  }
#ifdef ONLINE_JUDGE
  freopen((fileName + ".in").c_str(), "r", stdin);
  freopen((fileName + ".out").c_str(), "w", stdout);
#endif
}

const int INF = (1 << 30) - 1;
const LL LINF = (1LL << 61) - 1;
const double EPS = 1e-10, PI = acos(-1);
const int N = 1e6 + 100, P = 1e9 + 7;

int n, m;
vector<int> G[N];
unordered_map<ULL, int> a, b;
ULL pw[N], val[N];

ULL calc(vector<int> &g) {
  ULL res = 0;
  for (auto i : g) res ^= pw[i];
  return res;
}
void pre(int u) {
  ULL k = calc(G[u]);
  a[k]++, b[k ^ pw[u]]++;
}
LL count(unordered_map<ULL, int> &a) {
  LL res = 0;
  for (auto i : a) {
    res += i.second * 1LL * (i.second - 1) / 2;
  }
  return res;
}
int main() {
  srand((int)time(0));
  // printf("Hello, world!\n");
  // EnableFileIO("");

  for (int i = 1; i < N; i++) pw[i] = rand();

  cin >> n >> m;
  for (int i = 0; i < m; i++) {
    int u, v;
    cin >> u >> v;
    G[u].pb(v);
    G[v].pb(u);
  }
  for (int i = 1; i <= n; i++) pre(i);

  cout << count(a) + count(b) << endl;
  return 0;
}


#include <string.h>
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <unordered_map>
// #include <unordered_set>
#include <utility>
#include <vector>

#define pb push_back
#define mp make_pair

#ifdef ONLINE_JUDGE
#define link renamelink
#define next renamenext
#define prev renameprev
#endif

using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> pii;
typedef pair<LL, LL> pll;
typedef long double ld;

inline void EnableFileIO(const string &fileName, bool local = 0) {
  if (fileName.empty()) return;
  if (local) {
    freopen((fileName + ".in").c_str(), "r", stdin);
    freopen((fileName + ".out").c_str(), "w", stdout);
    return;
  }
#ifdef ONLINE_JUDGE
  freopen((fileName + ".in").c_str(), "r", stdin);
  freopen((fileName + ".out").c_str(), "w", stdout);
#endif
}

const int INF = (1 << 30) - 1;
const LL LINF = (1LL << 61) - 1;
const double EPS = 1e-10, PI = acos(-1);
const int N = 1e6 + 100, P = 1e9 + 7;

int n, m;
vector<int> G[N];
unordered_map<ULL, int> a, b;
ULL val[N];
void pre(int u) {
  ULL k = 0;
  for (int i : G[u]) k ^= val[i];
  a[k]++, b[k ^ val[u]]++;
}
LL count(unordered_map<ULL, int> &a) {
  LL res = 0;
  for (auto i : a) {
    res += i.second * 1LL * (i.second - 1) / 2;
  }
  return res;
}
inline LL bigrand() { return (rand() << 16) | rand(); }
inline LL hugerand() { return (bigrand() << 32) | bigrand(); }
int main() {
  srand((int)time(0));
  // printf("Hello, world!\n");
  // EnableFileIO("");

  scanf("%d%d", &n, &m);
  for (int i = 0; i < m; i++) {
    int u, v;
    scanf("%d%d", &u, &v);
    G[u].pb(v);
    G[v].pb(u);
  }
  for (int i = 1; i <= n; i++) {
    val[i] = hugerand();
  }
  for (int i = 1; i <= n; i++) pre(i);
  cout << count(a) + count(b) << endl;
  return 0;
}


#include <string.h>
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <unordered_map>
// #include <unordered_set>
#include <utility>
#include <vector>

#define pb push_back
#define mp make_pair

#ifdef ONLINE_JUDGE
#define link renamelink
#define next renamenext
#define prev renameprev
#endif

using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> pii;
typedef pair<LL, LL> pll;
typedef long double ld;

inline void EnableFileIO(const string &fileName, bool local = 0) {
  if (fileName.empty()) return;
  if (local) {
    freopen((fileName + ".in").c_str(), "r", stdin);
    freopen((fileName + ".out").c_str(), "w", stdout);
    return;
  }
#ifdef ONLINE_JUDGE
  freopen((fileName + ".in").c_str(), "r", stdin);
  freopen((fileName + ".out").c_str(), "w", stdout);
#endif
}

const int INF = (1 << 30) - 1;
const LL LINF = (1LL << 61) - 1;
const double EPS = 1e-10, PI = acos(-1);
const int N = 1e6 + 100, P = 1e9 + 7;

int n, m;
vector<int> G[N];
unordered_map<ULL, int> a, b;
ULL val[N];
void pre(int u) {
  ULL k = 0;
  for (int i : G[u]) k ^= val[i];
  a[k]++, b[k ^ val[u]]++;
}
LL count(unordered_map<ULL, int> &a) {
  LL res = 0;
  for (auto i : a) {
    res += i.second * 1LL * (i.second - 1) / 2;
  }
  return res;
}
#define bigrand (rand() * 1LL * rand())
int main() {
  srand((int)time(0));
  // printf("Hello, world!\n");
  // EnableFileIO("");

  scanf("%d%d", &n, &m);
  for (int i = 0; i < m; i++) {
    int u, v;
    scanf("%d%d", &u, &v);
    G[u].pb(v);
    G[v].pb(u);
  }
  for (int i = 1; i <= n; i++) {
    val[i] = bigrand ^ bigrand;
  }
  for (int i = 1; i <= n; i++) pre(i);
  cout << count(a) + count(b) << endl;
  return 0;
}


  #include <string.h>
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <unordered_map>
// #include <unordered_set>
#include <utility>
#include <vector>

#define pb push_back
#define mp make_pair

#ifdef ONLINE_JUDGE
#define link renamelink
#define next renamenext
#define prev renameprev
#endif

using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> pii;
typedef pair<LL, LL> pll;
typedef long double ld;

inline void EnableFileIO(const string &fileName, bool local = 0) {
  if (fileName.empty()) return;
  if (local) {
    freopen((fileName + ".in").c_str(), "r", stdin);
    freopen((fileName + ".out").c_str(), "w", stdout);
    return;
  }
#ifdef ONLINE_JUDGE
  freopen((fileName + ".in").c_str(), "r", stdin);
  freopen((fileName + ".out").c_str(), "w", stdout);
#endif
}

const int INF = (1 << 30) - 1;
const LL LINF = (1LL << 61) - 1;
const double EPS = 1e-10, PI = acos(-1);
const int N = 1e6 + 100, P = 1e9 + 7;

int n, m;
vector<int> G[N];
unordered_map<ULL, int> a, b;
ULL pw[N], val[N];

ULL calc(vector<int> &g) {
  ULL res = 0;
  for (auto i : g) res = res * P + i;
  return res;
}
void pre(int u) {
  ULL k = calc(G[u]);
  a[k]++, b[k + pw[u]]++;
}
LL count(unordered_map<ULL, int> &a) {
  LL res = 0;
  for (auto i : a) {
    res += i.second * 1LL * (i.second - 1) / 2;
  }
  return res;
}
int main() {
  // srand((int)time(0));
  // printf("Hello, world!\n");
  // EnableFileIO("");

  pw[0] = 1;
  for (int i = 1; i < N; i++) pw[i] = pw[i - 1] + 1;

  cin >> n >> m;
  for (int i = 0; i < m; i++) {
    int u, v;
    cin >> u >> v;
    G[u].pb(v);
    G[v].pb(u);
  }
  for (int i = 1; i <= n; i++) pre(i);

  cout << count(a) + count(b) << endl;
  return 0;
}


#include <bits/stdc++.h>
using namespace std;
unsigned long long wjc452 = 10007;
unsigned long long  n,m,_4wjc88[100010];
struct edge{
	unsigned long long  u,v;
	edge(){}
	edge(unsigned long long  _u,unsigned long long  _v){
		u = _u;
		v = _v;
	}
}p[100010];
unsigned long long  sum[100010];
void get(){
    _4wjc88[0]=1;
    for(unsigned long long  i = 1;i <= n;i++){
    	_4wjc88[i] = _4wjc88[i-1]*wjc452;
	}
}
int main(){
	memset(sum,0,sizeof(sum));
	scanf("%llu%llu",&n,&m);
	get();
	for(unsigned long long  i = 1;i <= m;i++){
		scanf("%llu%llu",&p[i].u,&p[i].v);
		sum[p[i].u] += _4wjc88[p[i].v];
		sum[p[i].v] += _4wjc88[p[i].u];
	}
	unsigned long long  fans = 0;
	for(unsigned long long  i = 1;i <= m;i++){
		if(sum[p[i].u]+_4wjc88[p[i].u] == sum[p[i].v]+_4wjc88[p[i].v]){
			fans++;
		}
	}
	unsigned long long  cnt = 1;
	sort(sum+1,sum+1+n);
	for(unsigned long long  i = 2;i <= n;i++){
		if(sum[i] != sum[i-1]){
			fans += cnt*(cnt-1)/2;
			cnt = 1;
		}else{
			cnt++;
		}
	}
	fans += cnt*(cnt-1)/2;
	printf("%llu\n",fans);
	return 0;
}


#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=100000007;

int ha[maxn];
int p=1313;
int e[maxn][2];
void init()              //hash每个点
{
    ha[0]=1;
    for(int i=1;i<maxn;i++)
        ha[i]=ha[i-1]*p;
}
long long a[maxn];
int main()
{
    std::ios::sync_with_stdio(false);
    int n,m;
    cin>>n>>m;
    init();
    for(int i=1;i<=m;i++)
    {
        int u,v;
        cin>>u>>v;
        a[u]+=ha[v];
        a[v]+=ha[u];
        e[i][0]=u;
        e[i][1]=v;
    }
    long long ans=0;
    for(int i=1;i<=m;i++)
    {
        int u=e[i][0],v=e[i][1];
        long long hu=a[u]+ha[u],hv=a[v]+ha[v];
        if(hu==hv) ans++;
    }
    sort(a+1,a+n+1);
    int i=2;
    int cnt=1;
    while(i<=n)
    {
        if(a[i]==a[i-1])
        {
            cnt++;
        }
        else
        {
            ans+=(cnt*(cnt-1))/2;
            cnt=1;
        }
        i++;
    }
    ans+=(cnt*(cnt-1))/2;
    cout<<ans<<endl;
    return 0;
}


#include <bits/stdc++.h>
using namespace std;
long long wjc452 = 100007;
int n,m;
long long _4wjc88[10000010];
struct edge{
	int u,v;
	edge(){}
	edge(int _u,int _v){
		u = _u;
		v = _v;
	}
}p[10000010];
long long  sum[10000010];
void get(){
    _4wjc88[1]=1ll;
    for(int i = 2;i <= n;i++){
    	_4wjc88[i] = _4wjc88[i-1]*10000007;
	}
}
int main(){
	memset(sum,0ll,sizeof(sum));
	scanf("%d%d",&n,&m);
	get();
	for(int i = 1;i <= m;i++){
		scanf("%d%d",&p[i].u,&p[i].v);
		int v = p[i].v;
		int u = p[i].u;
		sum[u] = sum[u]+_4wjc88[v];
		sum[v] = sum[v]+_4wjc88[u];
	}
	long long  fans = 0;
	for(int i = 1;i <= m;i++){
		if(sum[p[i].u]+_4wjc88[p[i].u] == sum[p[i].v]+_4wjc88[p[i].v]){
			fans++;
		}
	}
	long long  cnt = 1;
	sort(sum+1,sum+1+n);
	for(int i = 2;i <= n;i++){
		if(sum[i] != sum[i-1]){
			fans += cnt*(cnt-1)/2;
			cnt = 1;
		}else{
			cnt++;
		}
	}
	fans += cnt*(cnt-1)/2;
	printf("%lld\n",fans);
	return 0;
}


#include <bits/stdc++.h>
using namespace std;
const long long mod = 1e9+7;
const long long wjc452 = 19260817;
long long n,m,hhash[100010];
struct edge{
	long long u,v;
	edge(){}
	edge(long long _u,long long _v){
		u = _u;
		v = _v;
	}
}p[100010];
long long sum[100010];
int main(){
	memset(sum,0,sizeof(sum));
	scanf("%lld%lld",&n,&m);
	hhash[0] = 1;
	for(long long i = 1;i <= n;i++) hhash[i] = hhash[i-1]%mod*wjc452%mod;
	for(long long i = 1;i <= m;i++){
		scanf("%lld%lld",&p[i].u,&p[i].v);
		sum[p[i].u] += hhash[p[i].v];
		sum[p[i].v] += hhash[p[i].u];
	}
	long long fans = 0;
	for(long long i = 1;i <= m;i++){
		if(sum[p[i].u]+hhash[p[i].u] == sum[p[i].v]+hhash[p[i].v]){
			fans++;
		}
	}
	long long cnt = 1;
	sort(sum+1,sum+1+n);
	for(long long i = 2;i <= n;i++){
		if(sum[i] != sum[i-1]){
			fans += cnt*(cnt-1)/2;
			cnt = 1;
		}else{
			cnt++;
		}
	}
	fans += cnt*(cnt-1)/2;
	printf("%lld\n",fans);
	return 0;
}


#include <bits/stdc++.h>
using namespace std;
const long long mod = 1e9+7;
long long n,m,hhash[100010];
struct edge{
	long long u,v;
	edge(){}
	edge(long long _u,long long _v){
		u = _u;
		v = _v;
	}
}p[100010];
long long sum[100010];
int main(){
	memset(sum,0,sizeof(sum));
	scanf("%lld%lld",&n,&m);
	hhash[0] = 1;
	for(long long i = 1;i <= n;i++) hhash[i] = hhash[i-1]*19260817%mod;
	for(long long i = 1;i <= m;i++){
		scanf("%lld%lld",&p[i].u,&p[i].v);
		sum[p[i].u] += hhash[p[i].v];
		sum[p[i].v] += hhash[p[i].u];
	}
	long long fans = 0;
	for(long long i = 1;i <= m;i++){
		if(sum[p[i].u]+hhash[p[i].u] == sum[p[i].v]+hhash[p[i].v]){
			fans++;
		}
	}
	long long cnt = 1;
	sort(sum+1,sum+1+n);
	for(long long i = 2;i <= n;i++){
		if(sum[i] != sum[i-1]){
			fans += cnt*(cnt-1)/2;
			cnt = 1;
		}else{
			cnt++;
		}
	}
	fans += cnt*(cnt-1)/2;
	printf("%lld\n",fans);
	return 0;
}


#include <bits/stdc++.h>
using namespace std;
const long long mod = 1e9+7;
const long long wjc452 = 19260817;
long long n,m,hhash[100010];
struct edge{
	long long u,v;
	edge(){}
	edge(long long _u,long long _v){
		u = _u;
		v = _v;
	}
}p[100010];
long long sum[100010];
int main(){
	memset(sum,0,sizeof(sum));
	scanf("%lld%lld",&n,&m);
	hhash[0] = 1;
	for(long long i = 1;i <= n;i++) hhash[i] = hhash[i-1]*wjc452%mod;
	for(long long i = 1;i <= m;i++){
		scanf("%lld%lld",&p[i].u,&p[i].v);
		sum[p[i].u] += hhash[p[i].v];
		sum[p[i].v] += hhash[p[i].u];
	}
	long long fans = 0;
	for(long long i = 1;i <= m;i++){
		if(sum[p[i].u]+hhash[p[i].u] == sum[p[i].v]+hhash[p[i].v]){
			fans++;
		}
	}
	long long cnt = 1;
	sort(sum+1,sum+1+n);
	for(long long i = 2;i <= n;i++){
		if(sum[i] != sum[i-1]){
			fans += cnt*(cnt-1)/2;
			cnt = 1;
		}else{
			cnt++;
		}
	}
	fans += cnt*(cnt-1)/2;
	printf("%lld\n",fans);
	return 0;
}


#include <bits/stdc++.h>
using namespace std;
const long long mod = 1e9+7;
const long long wjc452 = 8887;
long long n,m,hhash[100010];
struct edge{
	long long u,v;
	edge(){}
	edge(long long _u,long long _v){
		u = _u;
		v = _v;
	}
}p[100010];
long long sum[100010];
int main(){
	memset(sum,0,sizeof(sum));
	scanf("%lld%lld",&n,&m);
	hhash[0] = 1;
	for(long long i = 1;i <= n;i++) hhash[i] = hhash[i-1]%mod*wjc452%mod;
	for(long long i = 1;i <= m;i++){
		scanf("%lld%lld",&p[i].u,&p[i].v);
		sum[p[i].u] += hhash[p[i].v];
		sum[p[i].v] += hhash[p[i].u];
	}
	long long fans = 0;
	for(long long i = 1;i <= m;i++){
		if(sum[p[i].u]+hhash[p[i].u] == sum[p[i].v]+hhash[p[i].v]){
			fans++;
		}
	}
	long long cnt = 1;
	sort(sum+1,sum+1+n);
	for(long long i = 2;i <= n;i++){
		if(sum[i] != sum[i-1]){
			fans += cnt*(cnt-1)/2;
			cnt = 1;
		}else{
			cnt++;
		}
	}
	fans += cnt*(cnt-1)/2;
	printf("%lld\n",fans);
	return 0;
}


#include <bits/stdc++.h>
using namespace std;
const long long mod = 1e9+7;
const long long wjc452 = 1313;
long long n,m,hhash[100010];
struct edge{
	long long u,v;
	edge(){}
	edge(long long _u,long long _v){
		u = _u;
		v = _v;
	}
}p[100010];
long long sum[100010];
int main(){
	memset(sum,0,sizeof(sum));
	scanf("%lld%lld",&n,&m);
	hhash[0] = 1;
	for(long long i = 1;i <= n;i++) hhash[i] = hhash[i-1]%mod*wjc452%mod;
	for(long long i = 1;i <= m;i++){
		scanf("%lld%lld",&p[i].u,&p[i].v);
		sum[p[i].u] += hhash[p[i].v];
		sum[p[i].v] += hhash[p[i].u];
	}
	long long fans = 0;
	for(long long i = 1;i <= m;i++){
		if(sum[p[i].u]+hhash[p[i].u] == sum[p[i].v]+hhash[p[i].v]){
			fans++;
		}
	}
	long long cnt = 1;
	sort(sum+1,sum+1+n);
	for(long long i = 2;i <= n;i++){
		if(sum[i] != sum[i-1]){
			fans += cnt*(cnt-1)/2;
			cnt = 1;
		}else{
			cnt++;
		}
	}
	fans += cnt*(cnt-1)/2;
	printf("%lld\n",fans);
	return 0;
}


#include <bits/stdc++.h>
using namespace std;
const int wjc452 = 1313;
int n,m,hhash[100010];
struct edge{
	int u,v;
	edge(){}
	edge(int _u,int _v){
		u = _u;
		v = _v;
	}
}p[100010];
int sum[100010];
void get(){
    hhash[0]=1;
    for(int i = 1;i <= n;i++){
    	hhash[i] = hhash[i-1]*wjc452;
	}
}
int main(){
	memset(sum,0,sizeof(sum));
	scanf("%d%d",&n,&m);
	get();
	for(long long i = 1;i <= m;i++){
		scanf("%d%d",&p[i].u,&p[i].v);
		sum[p[i].u] += hhash[p[i].v];
		sum[p[i].v] += hhash[p[i].u];
	}
	long long fans = 0;
	for(int i = 1;i <= m;i++){
		if(sum[p[i].u]+hhash[p[i].u] == sum[p[i].v]+hhash[p[i].v]){
			fans++;
		}
	}
	long long cnt = 1;
	sort(sum+1,sum+1+n);
	for(long long i = 2;i <= n;i++){
		if(sum[i] != sum[i-1]){
			fans += cnt*(cnt-1)/2;
			cnt = 1;
		}else{
			cnt++;
		}
	}
	fans += cnt*(cnt-1)/2;
	printf("%lld\n",fans);
	return 0;
}


#include <bits/stdc++.h>
using namespace std;
const int wjc452 = 1313;
int n,m,hhash[100010];
struct edge{
	int u,v;
	edge(){}
	edge(int _u,int _v){
		u = _u;
		v = _v;
	}
}p[100010];
int sum[100010];
void get(){
    hhash[0]=1;
    for(int i = 1;i <= n;i++){
    	hhash[i] = hhash[i-1]%1000000007*wjc452%1000000007;
	}
}
int main(){
	memset(sum,0,sizeof(sum));
	scanf("%d%d",&n,&m);
	get();
	for(long long i = 1;i <= m;i++){
		scanf("%d%d",&p[i].u,&p[i].v);
		sum[p[i].u] += hhash[p[i].v];
		sum[p[i].v] += hhash[p[i].u];
	}
	long long fans = 0;
	for(int i = 1;i <= m;i++){
		if(sum[p[i].u]+hhash[p[i].u] == sum[p[i].v]+hhash[p[i].v]){
			fans++;
		}
	}
	long long cnt = 1;
	sort(sum+1,sum+1+n);
	for(long long i = 2;i <= n;i++){
		if(sum[i] != sum[i-1]){
			fans += cnt*(cnt-1)/2;
			cnt = 1;
		}else{
			cnt++;
		}
	}
	fans += cnt*(cnt-1)/2;
	printf("%lld\n",fans);
	return 0;
}


#include <bits/stdc++.h>
using namespace std;
const long long mod = 1e9+7;
long long n,m,hhash[100010];
struct edge{
	long long u,v;
	edge(){}
	edge(long long _u,long long _v){
		u = _u;
		v = _v;
	}
}p[100010];
long long sum[100010];
int main(){
	memset(sum,0,sizeof(sum));
	scanf("%lld%lld",&n,&m);
	hhash[0] = 1;
	for(long long i = 1;i <= n;i++) hhash[i] = hhash[i-1]*8887%mod;
	for(long long i = 1;i <= m;i++){
		scanf("%lld%lld",&p[i].u,&p[i].v);
		sum[p[i].u] += hhash[p[i].v];
		sum[p[i].v] += hhash[p[i].u];
	}
	long long fans = 0;
	for(long long i = 1;i <= m;i++){
		if(sum[p[i].u]+hhash[p[i].u] == sum[p[i].v]+hhash[p[i].v]){
			fans++;
		}
	}
	long long cnt = 0;
	sort(sum+1,sum+1+n);
	for(long long i = 1;i <= n;i++){
		if(sum[i] != sum[i-1]){
			cnt = 1;
		}else{
			if(sum[i] != sum[i+1]){
				cnt++;
				fans += cnt*(cnt-1)/2;
			}else{
				cnt++;
			}
		}
	}
	fans += cnt*(cnt-1)/2;
	printf("%lld\n",fans);
	return 0;
}


#include <bits/stdc++.h>
using namespace std;
const int wjc452 = 10007;
int n,m,hhash[100010];
struct edge{
	int u,v;
	edge(){}
	edge(int _u,int _v){
		u = _u;
		v = _v;
	}
}p[100010];
int sum[100010];
void get(){
    hhash[0]=1;
    for(int i = 1;i <= n;i++){
    	hhash[i] = hhash[i-1]%1000000007*wjc452%1000000007;
	}
}
int main(){
	memset(sum,0,sizeof(sum));
	scanf("%d%d",&n,&m);
	get();
	for(long long i = 1;i <= m;i++){
		scanf("%d%d",&p[i].u,&p[i].v);
		sum[p[i].u] += hhash[p[i].v];
		sum[p[i].v] += hhash[p[i].u];
	}
	long long fans = 0;
	for(int i = 1;i <= m;i++){
		if(sum[p[i].u]+hhash[p[i].u] == sum[p[i].v]+hhash[p[i].v]){
			fans++;
		}
	}
	long long cnt = 1;
	sort(sum+1,sum+1+n);
	for(long long i = 2;i <= n;i++){
		if(sum[i] != sum[i-1]){
			fans += cnt*(cnt-1)/2;
			cnt = 1;
		}else{
			cnt++;
		}
	}
	fans += cnt*(cnt-1)/2;
	printf("%lld\n",fans);
	return 0;
}