#include <iostream>
#include <vector>
#include <queue>
#include <set>
#include <deque>
#include <assert.h>
#include <random>
#include <utility>

struct djset {
  std::vector<int> upper;
  std::vector<int> id;
  int n;
  djset(int n_) {
    n=n_;
    upper.resize(n);
    id.resize(n);
    for (int i=0;i<n;++i) upper[i]=-1;
  }
  int root(int x) {
    return upper[x]<0?x:(upper[x]=root(upper[x]));
  }
  bool equiv(int x,int y) {
    return root(x)==root(y);
  }
  void unite(int x,int y) {
    x=root(x);y=root(y);
    if (x==y) return;
    if (upper[x]<upper[y]) std::swap(x,y);
    upper[y]+=upper[x];
    upper[x]=y;
  }
  int build_id() {
    for (int i=0,p=0;i<n;++i) if (i==root(i)) id[i]=p++;
  }
  int components() {
    int e=0;
    for (int i=0;i<n;++i) if (i==root(i)) ++e;
    return e;
  }
  int get_id(int x) {
    return id[root(x)];
  }
};

bool dfs(int cur,int par,std::vector<bool> &bridge_vertex,std::vector<std::vector<int>> &g,djset &ds) {
  bool bridge=bridge_vertex[cur];
  int del=0;
  for (int dst:g[cur]) {
    if (dst!=par) {
      if (!dfs(dst,cur,bridge_vertex,g,ds)) ++del;
      else bridge=true;
    }
  }
  if (par!=-1 && !bridge) ds.unite(cur,par);
  if (par!=-1 && !bridge_vertex[cur] && !bridge_vertex[par] && g[cur].size()-del<=2) ds.unite(cur,par);
  return bridge;
}

void dfs2(int cur,int par,std::vector<std::vector<int>> &g,djset &ds,std::vector<int> &parent) {
  if (par!=-1 && ds.get_id(cur)!=ds.get_id(par)) parent[ds.get_id(cur)]=ds.get_id(par);
  for (int dst:g[cur]) {
    if (dst==par) continue;
    dfs2(dst,cur,g,ds,parent);
  }
}

int main() {
  int n,m;
  std::cin>>n>>m;
  std::vector<int> u(m);
  std::vector<int> v(m);
  std::vector<bool> bridge_vertex(n,false);
  std::vector<std::pair<int,int>> bridge;
  std::vector<std::vector<int>> g(n);
  std::vector<bool> tree_edge(m,false);
  for (int i=0;i<m;++i) {
    std::cin>>u[i]>>v[i];
    --u[i];--v[i];
  }
  djset tree(n);
  djset tree2(n);
  for (int i=0;i<m;++i) {
    if (!tree.equiv(u[i],v[i])) {
      tree.unite(u[i],v[i]);
      tree_edge[i]=true;
    } else {
      bridge_vertex[u[i]]=true;
      bridge_vertex[v[i]]=true;
      bridge.push_back({u[i],v[i]});
    }
  }
  for (int i=0;i<m;++i) {
    if (tree_edge[i]) {
      g[u[i]].push_back(v[i]);
      g[v[i]].push_back(u[i]);
    }
  }
  dfs(0,-1,bridge_vertex,g,tree2);
  tree2.build_id();
  int N=tree2.components();
  int M=m-(n-1);
  int ans=0;
  std::vector<int> parent(N,-1);
  dfs2(0,-1,g,tree2,parent);
  int mat[N][N];
  for (int s=1;s<1<<M;++s) {
    for (int i=0;i<N;++i) for (int j=0;j<N;++j) mat[i][j]=0;
    for (int i=0;i<M;++i) {
      if ((s>>i)%2==0) continue;
      int a=tree2.get_id(bridge[i].first);
      int b=tree2.get_id(bridge[i].second);
      assert(a!=b && mat[a][b]==0);
      mat[a][b]=1;
      mat[b][a]=1;
      std::vector<int> e={a,b};
      for (int cur:e) {
        while (cur!=tree2.get_id(0)) {
          mat[cur][parent[cur]]^=1;
          mat[parent[cur]][cur]^=1;
          cur=parent[cur];
        }
      }
    }
    std::vector<int> deg(N,0);
    for (int i=0;i<N;++i) {
      for (int j=i+1;j<N;++j) {
        if (mat[i][j]==1) {
          ++deg[i];
          ++deg[j];
        }
      }
    }
    bool ok=true;
    for (int i=0;i<N;++i) {
      ok&=deg[i]==2||deg[i]==0;
    }
    if (!ok) continue;
    int cnt=0;
    std::vector<bool> vis(N,false);
    for (int i=0;i<N;++i) {
      if (deg[i]==0) continue;
      if (vis[i]) continue;
      int cur=i,pre=-1;
      do {
        for (int next=0;next<N;++next) {
          if (next==pre) continue;
          if (mat[cur][next]==1) {
            vis[cur]=true;
            pre=cur;
            cur=next;
            break;
          }
        }
      } while (cur!=i);
      ++cnt;
    }
    if (cnt==1) ++ans;
  }
  std::cout<<ans<<std::endl;
}



Measured memory_bytes: 29508