#include <bits/stdc++.h>

using namespace std;

struct Edge {
    int v[2], idx;
    Edge() {}
    Edge(int v1, int v2) : v{v1, v2}, idx(-1) {}
};
using Edges = vector<Edge>;
using Graph = vector<Edges>;

class UnionFind {
public:
    UnionFind(int size) : data(size, -1) {}

    bool UnionSet(int x, int y) {
        x = root(x); y = root(y);
        if (x != y) {
            if (data[y] < data[x]) swap(x, y);
            data[x] += data[y]; data[y] = x;
        }
        return x != y;
    }
    bool FindSet(int x, int y) { return root(x) == root(y); }
    int Size(int x) { return -data[root(x)]; }

private:
    std::vector<int> data;
    int root(int x) { return data[x] < 0 ? x : data[x] = root(data[x]); }
};


Graph Compression(const int n, const int m, vector<set<int>> &g) {
    stack<int> st;
    for (int v = 0; v < n; ++v)
        if (g[v].size() == 1) st.push(v);

    while (!st.empty()) {
        int cur = st.top(), dst = *g[cur].begin();
        st.pop();
        g[cur].clear();
        g[dst].erase(cur);
        if (g[dst].size() == 1) st.push(dst);
    }

    for (int v = 0; v < n; ++v)
        if (g[v].size() == 2) st.push(v);

    while (!st.empty()) {
        int cur = st.top(); st.pop();

        if (g[cur].size() != 2) continue;
        int d1 = *g[cur].begin(), d2 = *g[cur].rbegin();

        if (d1 == d2 || g[d1].count(d2) != 0) continue;

        g[cur].clear();
        g[d1].erase(cur);
        g[d2].erase(cur);

        g[d1].insert(d2);
        g[d2].insert(d1);

        if (g[d1].size() == 2) st.push(d1);
        if (g[d2].size() == 2) st.push(d2);
    }

    map<int, int> idx_v;
    int size = 0;
    for (int v = 0; v < n; ++v)
        if (2 <= g[v].size()) idx_v[v] = size++;

    Graph h(size);
    for (int v = 0; v < n; ++v) {
        if (idx_v.count(v) == 0) continue;

        int cur = idx_v[v];
        for (int dst : g[v]) {
            if (idx_v.count(dst) == 0) continue;
            h[cur].emplace_back(Edge(cur, idx_v[dst]));
        }
    }

    return h;
}

bool FindCycle(const int p, const int cur, const int dst, const Graph &g, const auto &tree, auto &cycle) {
    if (cur == dst) return true;

    for (const auto &e : g[cur]) {
        if (e.v[1] == p || !tree[e.idx]) continue;
        if (FindCycle(cur, e.v[1], dst, g, tree, cycle)) {
            cycle.set(e.idx);
            return true;
        }
    }

    return false;
}

bool IsSimpleCycle(const Graph &g, const Edges &edges, const auto &cycle) {
    const int n = g.size(), m = edges.size();
    vector<int> deg(n);

    int start = -1;
    for (int i = 0; i < m; ++i)
        if (cycle[i]) {
            ++deg[edges[i].v[0]];
            ++deg[edges[i].v[1]];

            if (2 < deg[edges[i].v[0]] || 2 < deg[edges[i].v[1]]) return false;
            if (start == -1) start = edges[i].v[0];
        }

    stack<int> st;
    st.push(start);
    deg[start] = 0;
    while (!st.empty()) {
        const int cur = st.top(); st.pop();

        for (const auto &e : g[cur]) {
            if (!cycle[e.idx] || deg[e.v[1]] == 0) continue;
            deg[e.v[1]] = 0;
            st.push(e.v[1]);
        }
    }

    return all_of(deg.begin(), deg.end(), [](int x) { return x == 0; });
}


int CountingCycles(Graph g) {
    const int n = g.size();

    if (n <= 1) return 0;

    int m = 0;
    for_each(g.begin(), g.end(), [&m](const auto &it){ m += it.size(); });
    m /= 2;

    if (m <= n - 1) return 0;

    map<pair<int, int>, int> edge_id;
    Edges edges(m);
    for (int v = 0, idx = 0; v < n; ++v)
        for_each(g[v].begin(), g[v].end(),
                 [&edge_id, &edges, &idx](Edge &e) {
                     if (e.v[0] < e.v[1]) { edge_id[make_pair(e.v[0], e.v[1])] = e.idx = idx; edges[idx++] = e; }
                     else e.idx = edge_id[make_pair(e.v[1], e.v[0])];
                 });

    bitset<60> tree;
    UnionFind uf(n);
    for (int i = 0; i < m; ++i)
        if (!uf.FindSet(edges[i].v[0], edges[i].v[1])) {
            uf.UnionSet(edges[i].v[0], edges[i].v[1]);
            tree.set(i);
        }

    const int size_basis = m - n + 1;
    vector<bitset<60>> basis(size_basis);
    for (int i = 0, idx = 0; i < m; ++i)
        if (!tree[i]) {
            FindCycle(-1, edges[i].v[0], edges[i].v[1], g, tree, basis[idx]);
            basis[idx++].set(i);
        }

    int cnt = 0;
    for (int s = 1; s < (1 << size_basis); ++s) {
        bitset<60> cycle;
        for (int i = 0; i < size_basis; ++i)
            if (s >> i & 1) cycle ^= basis[i];

        if (IsSimpleCycle(g, edges, cycle)) ++cnt;
    }

    return cnt;
}


int main()
{
    cin.tie(0);
    ios::sync_with_stdio(false);

    int n, m, u, v;

    cin >> n >> m;
    vector<set<int>> g(n);
    for (int i = 0; i < m; ++i) {
        cin >> u >> v;
        g[u - 1].insert(v - 1);
        g[v - 1].insert(u - 1);
    }

    cout << CountingCycles(Compression(n, m, g)) << endl;

    return 0;
}



Measured memory_bytes: 17516