#include <bits/stdc++.h>
using namespace std;
const int INF = 1 << 29;
long long modpow(long long a, long long b, long long md) {
  long long res = 1;
  a %= md;
  for (; b; b >>= 1) {
    if (b & 1) res = res * a % md;
    a = a * a % md;
  }
  return res;
}
struct SingHash {
 private:
 public:
  long long p, m;
  vector<long long> pows, invpows;
  SingHash(long long prime, long long mod, int MAX_SIZE) {
    p = prime;
    m = mod;
    pows.resize(MAX_SIZE);
    invpows.resize(MAX_SIZE);
    for (int i = 0; i < MAX_SIZE; i++) {
      if (!i)
        pows[i] = 1;
      else
        pows[i] = (pows[i - 1] * p) % m;
    }
    invpows[MAX_SIZE - 1] = modpow(pows[MAX_SIZE - 1], m - 2, m);
    for (int i = MAX_SIZE - 2; i >= 0; i--)
      invpows[i] = (invpows[i + 1] * p) % m;
  }
  long long compHash(vector<int> v) {
    long long res = 0;
    for (int i = 0; i < v.size(); i++) {
      res += (pows[i] * (v[i] + 1));
      res %= m;
    }
    return res;
  }
  vector<long long> compArr(vector<int> v) {
    if (!v.size()) return vector<long long>{};
    vector<long long> res(v.size(), 0);
    long long tmp;
    for (int i = 0; i < v.size(); i++) {
      tmp = (pows[i] * (v[i] + 1)) % m;
      if (!i)
        res[i] = tmp;
      else
        res[i] = (res[i - 1] + tmp) % m;
    }
    return res;
  }
  long long subarrHash(vector<long long> table, int i, int j) {
    if (i > j) return -1;
    if (!i) return table[j];
    return ((table[j] - table[i - 1]) * invpows[i]) % m;
  }
};
struct DoubHash {
 private:
 public:
  SingHash* myHash[2];
  DoubHash(long long alphabets[2], long long mods[2], int MAX_SIZE) {
    myHash[0] = new SingHash(alphabets[0], mods[0], MAX_SIZE);
    myHash[1] = new SingHash(alphabets[1], mods[1], MAX_SIZE);
  }
  pair<long long, long long> computeHash(vector<int> v) {
    return make_pair(myHash[0]->compHash(v), myHash[1]->compHash(v));
  }
  pair<vector<long long>, vector<long long> > computeHashArray(vector<int> v) {
    return make_pair(myHash[0]->compArr(v), myHash[1]->compArr(v));
  }
  pair<long long, long long> subHash(
      pair<vector<long long>, vector<long long> > table, int i, int j) {
    return make_pair(myHash[0]->subarrHash(table.first, i, j),
                     myHash[1]->subarrHash(table.second, i, j));
  }
};
struct DisjointSet {
  int fcnt;
  vector<int> f, sz, acsz;
  DisjointSet(int size) {
    f.resize(size);
    sz.resize(size);
    acsz.resize(size);
    fcnt = size;
    for (int i = 0; i < size; i++) {
      f[i] = i;
      sz[i] = 1;
      acsz[i] = 1;
    }
  }
  int father(int src) {
    if (src == f[src]) return src;
    return f[src] = father(f[src]);
  }
  bool unite(int a, int b) {
    int fa = father(a), fb = father(b);
    if (sz[fa] < sz[fb]) swap(fa, fb);
    if (fa == fb) return false;
    f[fb] = fa;
    fcnt--;
    sz[fa] = max(sz[fa], sz[fb] + 1);
    acsz[fa] += acsz[fb];
    return true;
  }
};
void subtract(long long& a, long long b, long long md) {
  if (a >= b)
    a = (a - b) % md;
  else
    a = (a - b) % md + md;
}
const int sz = 1e6 + 5;
bitset<sz> isftr;
vector<int> v[sz];
pair<long long, long long> val[sz];
int n, m;
pair<vector<long long>, vector<long long> > tables[sz];
int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  DoubHash d(new long long[2]{1000003, 1000033},
             new long long[2]{1000000007, 1000000009}, 1000000);
  cin >> n >> m;
  DisjointSet dsu(n);
  int a, b;
  for (int i = 0; i < m; i++) {
    cin >> a >> b;
    a--;
    b--;
    v[a].push_back(b);
    v[b].push_back(a);
  }
  for (int i = 0; i < n; i++) sort(v[i].begin(), v[i].end());
  for (int i = 0; i < n; i++) {
    val[i] = (v[i].size() ? d.computeHash(v[i]) : make_pair(-1LL, -1LL));
    tables[i] = d.computeHashArray(v[i]);
  }
  long long p1inv = d.myHash[0]->invpows[1], p2inv = d.myHash[1]->invpows[1],
            m1 = d.myHash[0]->m, m2 = d.myHash[1]->m;
  for (int i = 0; i < n; i++) {
    for (int k : v[i]) {
      int size = v[k].size();
      if (v[k].size() != v[i].size()) continue;
      if (size <= 1) {
        dsu.unite(i, k);
        continue;
      }
      int l = 1, r = size, mid, indexInFirst, indexInSecond;
      while (l <= r) {
        mid = (l + r + 1) / 2;
        if (v[i][mid - 1] > k)
          r = mid - 1;
        else if (v[i][mid - 1] < k)
          l = mid + 1;
        else {
          indexInFirst = mid - 1;
          break;
        }
      }
      l = 1, r = size;
      while (l <= r) {
        mid = (l + r + 1) / 2;
        if (v[k][mid - 1] > i)
          r = mid - 1;
        else if (v[k][mid - 1] < i)
          l = mid + 1;
        else {
          indexInSecond = mid - 1;
          break;
        }
      }
      long long hif, his, hkf, hks;
      hif = (indexInFirst ? tables[i].first[indexInFirst - 1] : 0);
      hkf = (indexInSecond ? tables[k].first[indexInSecond - 1] : 0);
      his = (indexInFirst ? tables[i].second[indexInFirst - 1] : 0);
      hks = (indexInSecond ? tables[k].second[indexInSecond - 1] : 0);
      hif += ((tables[i].first[size - 1] - tables[i].first[indexInFirst] + m1) *
              p1inv) %
             m1;
      his +=
          ((tables[i].second[size - 1] - tables[i].second[indexInFirst] + m2) *
           p2inv) %
          m2;
      hkf +=
          ((tables[k].first[size - 1] - tables[k].first[indexInSecond] + m1) *
           p1inv) %
          m1;
      hks +=
          ((tables[k].second[size - 1] - tables[k].second[indexInSecond] + m2) *
           p2inv) %
          m2;
      if (hif % m1 == hkf % m1 && his % m2 == hks % m2) dsu.unite(i, k);
    }
  }
  unordered_map<long long, int> mem;
  for (int i = 0; i < n; i++) {
    long long pp = val[i].first * m2 + val[i].second;
    if (mem.find(pp) == mem.end())
      mem[pp] = i;
    else
      dsu.unite(i, mem[pp]);
  }
  for (int i = 0; i < n; i++) {
    if (dsu.f[i] == i) isftr[i] = 1;
  }
  long long tot = 0;
  for (int i = 0; i < n; i++) {
    if (!isftr[i]) continue;
    long long famt = (dsu.acsz[i]);
    tot += ((famt * (famt - 1)) / 2);
  }
  cout << tot << endl;
  return 0;
}
