// #pragma GCC optimize("Ofast,unroll-loops")
// #pragma GCC target("avx,avx2,fma")

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

#define ll long long
#define ull unsigned long long
#define dd double
#define ld long double
#define sl(n) scanf("%lld", &n)
#define si(n) scanf("%d", &n)
#define sd(n) scanf("%lf", &n)
#define pll pair <ll, ll>
#define pii pair <int, int>
#define mp make_pair
#define pb push_back
#define all(v) v.begin(), v.end()
#define inf (1LL << 61)
#define loop(i, start, stop, inc) for(ll i = start; i <= stop; i += inc)
#define for1(i, stop) for(ll i = 1; i <= stop; ++i)
#define for0(i, stop) for(ll i = 0; i < stop; ++i)
#define rep1(i, start) for(ll i = start; i >= 1; --i)
#define rep0(i, start) for(ll i = (start-1); i >= 0; --i)
#define ms(n, i) memset(n, i, sizeof(n))
#define casep(n) printf("Case %lld:", ++n)
#define pn printf("\n")
#define pf printf
#define EL '\n'
#define fastio std::ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);

// === Debug macro starts here ===
#ifndef ONLINE_JUDGE
#define DEBUG
#define SYS_COL system("COLOR")
#endif

int recur_depth = 0;
#ifdef DEBUG
#define dbg(x) {++recur_depth; auto x_=x; --recur_depth; SYS_COL; \
                cerr<<string(recur_depth, '\t')<<"\e[91m"<<__func__<<":" \
                <<__LINE__<<"\t"<<#x<<" = "<<x_<<"\e[39m"<<endl;}

template<typename Ostream, typename Cont>
typename enable_if<is_same<Ostream,ostream>::value,
            Ostream&>::type operator<<(Ostream& os,  const Cont& v) {
	os<<"[";
	for(auto& x:v){os<<x<<", ";}
	return os<<"]";
}
template<typename Ostream, typename ...Ts>
Ostream& operator<<(Ostream& os,  const pair<Ts...>& p){
	return os<<"{"<<p.first<<", "<<p.second<<"}";
}
#else
#define dbg(x)
#endif
// === Debug macro ends here ===

#define ff first
#define ss second

const ll sz = 22;
vector <int> g[sz], comp[sz];
int col[sz], vis[sz];

void comp_dfs(ll u, ll id)
{
    vis[u] = 1;
    comp[id].pb(u);

    for(int &v : g[u]) {
        if(!vis[v])
            comp_dfs(v, id);
    }
}

ll dfs(ll pos, ll id)
{
    if(pos == comp[id].size())
        return 1;
    ll u = comp[id][pos];

    set <int> exist;
    for(int &v : g[u])
        if(col[v])
            exist.insert(col[v]);

    ll ret = 0;
    for1(i, 3) {
        if(exist.find(i) != exist.end())
            continue;

        col[u] = i;
        ret += dfs(pos+1, id);
        col[u] = 0;
    }

    return ret;
}

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

    for1(i, m) {
        int u, v;
        si(u), si(v);

        g[u].pb(v);
        g[v].pb(u);
    }

    ll ans = 1, id = 0;

    for1(i, n) {
        if(vis[i])
            continue;

        comp_dfs(i, ++id);
    }

    for1(i, id)
        ans *= dfs(0, i);

    cout << ans << EL;

    return 0;
}

#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,fma,avx512f,avx512bw,avx512bitalg")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")

//雪花飄飄北風嘯嘯
//天地一片蒼茫

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/rope>
using namespace std;
using namespace __gnu_pbds;
using namespace __gnu_cxx;
#define ll long long
#define ii pair<ll,ll>
#define iii pair<ii,ll>
#define fi first
#define se second
#define endl '\n'
#define debug(x) cout << #x << " is " << x << endl

#define pub push_back
#define pob pop_back
#define puf push_front
#define pof pop_front
#define lb lower_bound
#define ub upper_bound

#define rep(x,start,end) for(int x=start;x!=end;x++)
#define all(x) (x).begin(),(x).end()
#define sz(x) (int)(x).size()

#define indexed_set tree<ll,null_type,less<ll>,rb_tree_tag,tree_order_statistics_node_update>
//change less to less_equal for non distinct pbds, but erase will bug

mt19937 rng(chrono::system_clock::now().time_since_epoch().count());

int B,m;
bool bad[25][25];

int ind[1050000];

int ahat[21][1050000];
int bhat[21][1050000];
ll hat[21][1050000];

void conv(){
	for (int x=0;x<1048576;x++) {
		ahat[__builtin_popcount(x)][x]=ind[x];
		bhat[__builtin_popcount(x)][x]=ind[x];
	}
	
	rep(cnt,0,21){
		rep(bit,0,20){
			for (int a=0;a<1048576;a+=2<<bit){
				int b=a+(1<<bit);
				for (int x=0;x<(1<<bit);x++){
					ahat[cnt][b+x]+=ahat[cnt][a+x];
					bhat[cnt][b+x]+=bhat[cnt][a+x];
				}
			}
		}
	}
	
	rep(cnt,0,21){
		rep(cnt2,0,cnt+1){
			for (int x=0;x<1048576;x++){
				hat[cnt][x]+=(ll)ahat[cnt2][x]*bhat[cnt-cnt2][x];
			}
		}
	}
	
	rep(cnt,0,21){
		rep(bit,0,20){
			for (int a=0;a<1048576;a+=2<<bit){
				int b=a+(1<<bit);
				for (int x=0;x<(1<<bit);x++){
					hat[cnt][b+x]-=hat[cnt][a+x];
				}
			}
		}
	}
	
	ll ans=0;
    for (int x=0;x<(1<<B);x++){
    	ans+=ind[x^((1<<B)-1)]*hat[__builtin_popcount(x)][x];
	}
	cout<<ans<<endl;
}

int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin.exceptions(ios::badbit | ios::failbit);
	
	cin>>B>>m;
	
	int a,b;
	rep(x,0,m){
		cin>>a>>b;
		a--,b--;
		bad[a][b]=bad[b][a]=true;
	}
	
	ind[0]=1;
	rep(x,1,1<<B){
		int temp=__builtin_ctz(x);
		int nx=x^(1<<temp);
		
		ind[x]=ind[nx];
		
		rep(bit,0,B) if (nx&(1<<bit) && bad[temp][bit]){
			ind[x]=0;
		}
	}
	
	conv();
}
