CodeEff/ECCO huggingface dataset:
# Original solution
from collections import deque
import copy
# https://atcoder.jp/contests/abc054/tasks/abc054_c
# n!の全探索が答えというのは知っているが、自分の開放の誤りを見つけるために自分のやり方でといてみる。

N, M = list(map(int, input().split()))
V = [[] for _ in range(N)]
cont = [0 for _ in range(N)]
for _ in range(M):
    a, b = list(map(int, input().split()))
    V[a-1].append([b, copy.deepcopy(cont)])
    V[b-1].append([a, copy.deepcopy(cont)])

# solution
q = deque()
q.append([1, copy.deepcopy(cont)])
count = 0
while q:
    c_v = q.pop()
    c_v[1][c_v[0]-1] = 1
    next_vs = V[c_v[0]-1]
    ifComplete = 1
    for v in next_vs:
        if c_v[1][v[0]-1] == 1:
            continue
        else:
            ifComplete = 0
        # ここで続くvの探索が始まる時に全て訪問されていればcountを増やす。
        # その状態をどうやって管理すればいい？
        v[1] = copy.deepcopy(c_v[1])
        q.append(v)
    # 遷移先のノードが存在しない場合
    if ifComplete:
        flag = 1
        for x in c_v[1]:
            if x == 0:
                flag = 0
                break
        if flag:
            count += 1
print(count)


# Optimized solution
import itertools

N, M = list(map(int, input().split()))
V = [[] for _ in range(N)]
bag = set()
for _ in range(M):
    a, b = list(map(int, input().split()))
    bag.add(a)
    bag.add(b)
    V[a-1].append(b)
    V[b-1].append(a)

count = 0
for c in itertools.permutations(bag):
    if not c[0] == 1:
        continue
    flag = False
    for i in range(N-1):
        if not c[i+1] in V[c[i]-1]:
            flag = True
            break
    if not flag:
        count += 1
print(count)
# Original solution
N, M=list(map(int,input().split()))
a=[0]*0
b=[0]*0
choice=[0]*(N-1)
chosen_a=[0]*(N-1)
chosen_b=[0]*(N-1)
c_patern=0
for i in range(M):
    a_tmp, b_tmp=list(map(int,input().split()))
    a.append(a_tmp)
    b.append(b_tmp)
# for i in range(M):
#     c_point = [0] * N
#     if a[i]==1:
#         c_point[a[i]-1]+=1
#     for j in range(M):
#         if a[j]==b[i]:
# for i in range(N):
#     if c_point[i]!=1:
#         break
#     c_patern+=1
for i in range(pow(M,(N-1))):
    flg=0
    for j in range(N-1):
        choice[j]=i//pow(M,(N-1)-j-1)
        chosen_a[j]=a[choice[j]]
        chosen_b[j]=b[choice[j]]
        i=i%pow(M,(N-1)-j-1)
    if chosen_a[0]!=1:
        flg=1
    for k in range(N-1):
        if k>0:
            if chosen_a[k]!=chosen_b[k-1]:
                tmp=chosen_a[k]
                chosen_a[k]=chosen_b[k]
                chosen_b[k]=tmp
                if chosen_a[k]!=chosen_b[k-1]:
                    flg=1
    chosen_b_sorted=sorted(chosen_b)
    for l in range(N-1):
        if chosen_b_sorted[l]!=l+2:
            flg=1
    if flg==0:
        c_patern+=1
print(c_patern)

# Optimized solution
#ABC054C
import itertools
N, M=list(map(int,input().split()))
a=[0]*0
b=[0]*0
order=list(itertools.permutations(list(range(N-1)) ) )#0～N-2のN-1個の順列ができる。2を足すと、2～Nの配列。
c_patern=0
for i in range(M):
    a_tmp, b_tmp=list(map(int,input().split()))
    a.append(a_tmp)
    b.append(b_tmp)
for i in range(len(order)):
    for j in range(N-1):
        flg = False
        if j==0:
            for k in range(M):
                if (a[k]==1 and b[k]==order[i][0]+2):
                    flg=True
                    break
        else:
            for k in range(M):
                if (a[k]==order[i][j-1]+2 and b[k]==order[i][j]+2) or (a[k]==order[i][j]+2 and b[k]==order[i][j-1]+2):
                    flg=True
                    break
        if flg==False:
            break
    if flg==True:
        c_patern+=1
print(c_patern)
# Original solution
def dfs(parent, connect, passed, N): #深さ優先探索
    cnt = 0
    passed.append(parent) #通った点をメモ
    if len(passed) == N: #全ての点を通過したかの判定
        cnt = 1
    for next_ in connect[parent]:
        if next_ in passed:
            continue
        cnt += dfs(next_, connect, passed, N) #全ての点を通るまで再帰. 戻り値の和で何通りかカウント
    passed.pop() #分岐点に戻る前にメモを1段だけ破棄
    return cnt    

N, M = list(map(int, input().split()))
connect = [[] for _ in range(N)]
for _ in range(M):
    a, b = list(map(int, input().split()))
    connect[a-1].append(b-1)
    connect[b-1].append(a-1)
ans = dfs(0, connect, [], N) #点0からのdfs
print(ans)

# Optimized solution
N, M = list(map(int, input().split()))
Edge = [tuple(map(int, input().split())) for _ in range(M)]

Graph = [[] for _ in range(N)]
for a,b in Edge:
    Graph[a-1].append(b-1)
    Graph[b-1].append(a-1)


def dfs(v, seen):
    global Graph
    seen.add(v)
    if len(seen) == N:
        seen.remove(v)
        return 1
    Res = [dfs(nv, seen) for nv in Graph[v] if not nv in seen]
    seen.remove(v)
    return sum(Res)

print((dfs(0, set())))
# Original solution
N, M = list(map(int, input().split()))
Edge = [tuple(map(int, input().split())) for _ in range(M)]

Graph = [[] for _ in range(N)]
for a,b in Edge:
    Graph[a-1].append(b-1)
    Graph[b-1].append(a-1)

seen = set()
def dfs(v):
    global Graph, seen
    seen.add(v)
    if len(seen) == N:
        seen.remove(v)
        return 1
    Res = [dfs(nv) for nv in Graph[v] if not nv in seen]
    seen.remove(v)
    return sum(Res)

print((dfs(0)))

# Optimized solution
N, M = list(map(int, input().split()))
Edge = [tuple(map(int, input().split())) for _ in range(M)]

Graph = [[] for _ in range(N)]
for a,b in Edge:
    Graph[a-1].append(b-1)
    Graph[b-1].append(a-1)

seen = set()
def dfs(v):
    seen.add(v)
    if len(seen) == N:
        seen.remove(v)
        return 1
    Res = [dfs(nv) for nv in Graph[v] if not nv in seen]
    seen.remove(v)
    return sum(Res)

print((dfs(0)))
# Original solution
# ABC054C - One-stroke Path
# graph theory
# an undirected unweighted graph is given
# -> count up paths which visit all the vertices exactly once
import sys
input = sys.stdin.readline

from itertools import permutations as perm


def main():
    N, M = tuple(map(int, input().split()))
    A = set(tuple(map(int, input().split())) for _ in range(M))
    ans = 0
    for path in perm(list(range(2, N + 1)), N - 1):  # check all possible paths (O(N!))
        path = (1,) + path
        if all((i, j) in A or (j, i) in A for i, j in zip(path, path[1:])):
            ans += 1
    print(ans)


if __name__ == "__main__":
    main()

# Optimized solution
# ABC054C - One-stroke Path
# an undirected unweighted graph is given
# -> count up paths which visit all the vertices exactly once
# exhaustive search
from itertools import permutations as perm


def main():
    N, M = tuple(map(int, input().split()))
    E = set(tuple(map(int, input().split())) for _ in range(M))  # edges
    E |= {(j, i) for i, j in E}  # undirected graph -> add reversed paths
    ans = 0
    for path in perm(list(range(2, N + 1)), N - 1):  # check all possible paths (O(N!))
        path = (1,) + path
        if all((i, j) in E for i, j in zip(path, path[1:])):
            ans += 1
    print(ans)


if __name__ == "__main__":
    main()
# Original solution
import itertools
n, m = list(map(int, input().split()))
ab_list = [tuple(map(int, input().split())) for _ in range(m)]

path_list = sorted(iter for iter in itertools.permutations(list(range(2, n+1)), n-1))

counter = 0

for path_i in path_list:
  path = (1, )+path_i
  
  result_list = [True for s_e in zip(path, path[1:]) if tuple(sorted(s_e)) in ab_list]
  
  if sum(result_list) == len(path)-1: counter += 1
    
print(counter)

# Optimized solution
import itertools

n,m = list(map(int, input().split()))
edge_set = {tuple(map(int, input().split())) for _ in range(m)}
counter = 0

for i in itertools.permutations(list(range(2, n+1)), n-1):
  path = [1] + list(i)
  path_set = {tuple(sorted(edge)) for edge in zip(path, path[1:])}
  
  if path_set <= edge_set:
    counter += 1
 
  
print(counter)
# Original solution
n, m = list(map(int, input().split()))

sign = []
for i in range(n):
    sign.append("o")
    
graph = []
for i in range(n):
    graph.append([])
    
for i in range(m):
    a, b = list(map(int, input().split()))
    a -= 1
    b -= 1
    for j in range(n):
        if a == j:
            graph[j].append(b)
        if b == j:
            graph[j].append(a)

def dfs(x, i=0):
    sign[x] = "x"
    if sign == ["x" for k in range(n)]:
        return i + 1
    for j in graph[x]:
        if sign[j] == "o":
            sign[j] = "x"
            i = dfs(j, i)
            sign[j] = "o"
    return i 

print((dfs(0)))


# Optimized solution
###入力
n, m = list(map(int, input().split()))

###到達しているか否かのサイン
sign = []
for i in range(n):
    sign.append("o")

###グラフ
graph = []
for i in range(n):
    graph.append([])

###隣接行列
for i in range(m):
    a, b = list(map(int, input().split()))
    a -= 1
    b -= 1
    for j in range(n):
        if a == j:
            graph[j].append(b)
        if b == j:
            graph[j].append(a)

###深さ優先探索
###dfsはxからスタートしてoの箇所を全て回収する場合の数
def dfs(x, i=0):
    sign[x] = "x"
    
    ###すべてのノードが到達済みなら終了
    if sign == ["x" for k in range(n)]:
        return i + 1
    
    ###隣接するすべての点に対して、未到達ならdfsを適用
    for j in graph[x]:
        if sign[j] == "o":
            
            ###一旦xにする（後で戻す）
            sign[j] = "x"
            
            ###重要重要重要重要重要重要重要重要重要重要重要重要重要重要重要重要重要重要
            ###右辺のiにはこれまでの場合の数が蓄積されている
            ###そのiの上にjスタートの場合の数が加算される
            ###それを新たなiとする
            ###つまり、足し算の操作は右辺で完結している
            ###iはここでしか動かないことに注意
            ###重要重要重要重要重要重要重要重要重要重要重要重要重要重要重要重要重要重要
            
            i = dfs(j, i)
            
            ###戻した
            sign[j] = "o"
    return i

print((dfs(0)))
# Original solution
import collections
def main():
    N, M = list(map(int, input().split()))
    graph = collections.defaultdict(list)
    for _ in range(M):
        a, b = list(map(int, input().split()))
        graph[a].append(b)
        graph[b].append(a)

    cur = [[1, set([1])]]
    for _ in range(N-1):
        temp = []
        for a, visited in cur:
            for b in graph[a]:
                if b not in visited:
                    temp.append([b, visited | {b}])
        cur = temp
    return len(cur)


if __name__ == '__main__':
    print((main()))

# Optimized solution
import collections
def main():

    N, M = list(map(int, input().split()))
    graph = collections.defaultdict(list)
    for _ in range(M):
        a, b = list(map(int, input().split()))
        graph[a].append(b)
        graph[b].append(a)

    # print(graph)

    cur = [[1, 1 << 1]]
    for _ in range(N-1):
        temp = []
        for a, h in cur:
            for b in graph[a]:
                if (h >> b) % 2 == 0:
                    temp.append([b, h | (1 << b)])
        cur = temp
        # print(cur)
    return len(cur)

if __name__ == '__main__':
    print((main()))
# Original solution
import itertools
n, m = list(map(int, input().split()))
l = [list(map(int, input().split())) for i in range(m)]
r = [[] for i in range(n + 1)]
ans = 0
for i in range(m):
    r[l[i][0]].append(l[i][1])
    r[l[i][1]].append(l[i][0])
p = []
for i in range(n):
    p.append(i + 1)
p = list(itertools.permutations(p))
for i in range(len(p)):
    check = True
    t = list(p[i])
    if t[0] != 1:
        check = False
    for j in range(len(t)-1):
        if not t[j + 1] in r[t[j]]:
            check = False
    if check:
        ans += 1
print(ans)

# Optimized solution
import itertools

n, m = list(map(int, input().split()))
l = [list(map(int, input().split())) for i in range(m)]
r = [[] for i in range(n + 1)]
ans = 0
for i in range(m):
    r[l[i][0]].append(l[i][1])
    r[l[i][1]].append(l[i][0])
p = "2345678"[:n-1]
p = list(itertools.permutations(p))
for i in range(len(p)):
    check = True
    t = "1" + "".join(p[i])
    for j in range(len(t)-1):
        if not int(t[j + 1]) in r[int(t[j])]:
            check = False
    if check:
        ans += 1
print(ans)
# Original solution
n,m=list(map(int,input().split()))
edge=[list(map(int,input().split())) for i in range(m)]
ans=0
rout=[[0 for i in range(n)]]
rout[0][0]=1
for i in range(2,n+1):
  rout2=[]
  for j in range(len(rout)):
    for k in range(n):
      if rout[j][k]==0:
        r=rout[j][:]
        r[k]=i
        rout2.append(r)
  rout=rout2
for r in rout:
  lim=0
  while lim<n-1:
    if [r[lim],r[lim+1]] in edge or [r[lim+1],r[lim]] in edge:
      lim+=1
    else:
      lim=0
      break
  if lim:
    ans+=1
print(ans)

# Optimized solution
import itertools
n,m=list(map(int,input().split()))
edge=[list(map(int,input().split())) for i in range(m)]
ans=0
nums=[i for i in range(1,n+1)]
rout=itertools.permutations(nums,n)
for r in rout:
  if r[0]!=1:
    continue
  lim=0
  while lim<n-1:
    if [r[lim],r[lim+1]] in edge or [r[lim+1],r[lim]] in edge:
      lim+=1
    else:
      lim=0
      break
  if lim:
    ans+=1
print(ans)
# Original solution
f=lambda:list(map(int,input().split()))
n,m=f()
g=[[0]*n for _ in range(n)]
for i in range(m):
  a,b=f(); g[a-1][b-1]=g[b-1][a-1]=1
import itertools as it
c=0
for t in it.permutations(list(range(1,n))):
  p,d=0,1
  for q in t: d*=g[p][q]; p=q
  c+=d
print(c)

# Optimized solution
f=lambda:list(map(int,input().split()))
n,m=f()
g=[[0]*n for _ in range(n)]
for i in range(m):
  a,b=f(); g[a-1][b-1]=g[b-1][a-1]=1
import itertools as it
c=0
for t in it.permutations(list(range(1,n))):
  p=0
  for q in t:
    if g[p][q]<1: break
    p=q
  else: c+=1
print(c)
# Original solution
f=lambda:list(map(int,input().split()))
n,m=f()
g=[[0]*n for _ in range(n)]
for _ in range(m):
  a,b=f()
  g[a-1][b-1]=1
  g[b-1][a-1]=1
from itertools import *
p=[*permutations(list(range(1,n)))]
a=0
for t in p:
  v=0
  for c in t:
    if g[v][c]: v=c
    else: break
  else: a+=1
print(a)

# Optimized solution
f=lambda:list(map(int,input().split()))
n,m=f()
g=[[] for _ in range(n)]
for _ in range(m):
  a,b=f()
  g[a-1]+=[b-1]
  g[b-1]+=[a-1]
from itertools import *
p=[*permutations(list(range(1,n)))]
a=0
for t in p:
  v=0
  for c in t:
    if c in g[v]: v=c
    else: break
  else: a+=1
print(a)
# Original solution
from itertools import permutations
n, m = list(map(int, input().split()))
pair = [tuple(map(int, input().split())) for i in range(m)]
num = [i+1 for i in range(n)]
box = []
for p in permutations(num):
    if p[0] == 1:
        box.append(p)
ans = 0
for p in box:
    already = []
    state = True
    for i in range(n-1):
        if p[i] in already:
            state = False
            break
        if (min(p[i], p[i+1]), max(p[i], p[i+1])) not in pair:
            state = False
            break
        already.append(p[i])
    if state:
        ans += 1
print(ans)


# Optimized solution
from itertools import permutations
n, m = list(map(int, input().split()))
pair = [tuple(map(int, input().split())) for i in range(m)]
num = [i for i in range(1,n+1)]
ans = 0
for p in permutations(num):
    if p[0] != 1:
        continue
    already = []
    state = True
    for i in range(n-1):
        if p[i] in already:
            state = False
            break
        if (min(p[i], p[i+1]), max(p[i], p[i+1])) not in pair:
            state = False
            break
        already.append(p[i])
    if state:
        ans += 1
print(ans)
# Original solution
def dfs(t):
    if 0 not in already:
        return 1
    allReach=0
    for i in range(n):
        if connect[t][i]==1 and already[i]==0:
            already[i]=1
            allReach+=dfs(i)
            already[i]=0
    return allReach

n,m=list(map(int,input().split()))
connect=[[0 for i in range(n)]for j in range(n)]
for i in range(m):
    a,b=list(map(int,input().split()))
    connect[a-1][b-1]=1
    connect[b-1][a-1]=1
already=[0 for i in range(n)]
already[0]=1
print((dfs(0)))


# Optimized solution
def dfs(t):
    if 0 not in already:#全ての辺を訪問していた場合
        return 1
    allReach=0
    for i in range(n):
        if connect[t][i]==1 and already[i]==0:#辺があり、訪問しいない場合
            already[i]=1
            allReach+=dfs(i)
            already[i]=0
    return allReach

n,m=list(map(int,input().split()))
connect=[[0 for i in range(n)]for j in range(n)]
for i in range(m):
    a,b=list(map(int,input().split()))
    #辺の情報を記録する
    connect[a-1][b-1]=1
    connect[b-1][a-1]=1
already=[0 for i in range(n)]#訪問した点を記録する
already[0]=1#必ず1から始まるので訪問履歴をつけておく
print((dfs(0)))#記録した情報をもとに0からdfs
# Original solution
def dfs(v,n,visited):
    all_visted=1
    for i in range(n):
        if visited[i]==0:
            all_visted=0
    if all_visted==1:
        return 1
    ret=0
    for i in range(n):
        if path[v][i]==0:
            continue
        if visited[i]==1:
            continue
        visited[i]=1
        ret+=dfs(i,n,visited)
        visited[i]=0
    return ret
n,m=list(map(int,input().split()))
path=[[0]*n for i in range(n)]
for i in range(m):
    a,b=list(map(int,input().split()))
    path[a-1][b-1]=1
    path[b-1][a-1]=1
visited=[0]*n
visited[0]=1
print((dfs(0,n,visited)))


# Optimized solution
def dfs(v,n,visited):
    all_visted=1
    for i in range(n):
        if visited[i]==0:
            all_visted=0
    if all_visted==1:
        return 1
    count=0#全ての辺を訪問する回数を格納する変数
    for i in range(n):
        if path[v][i]==0:#辺がないとき、つまり隣接していない時
            continue
        if visited[i]==1:#既に訪問している
            continue
        visited[i]=1#訪問する
        count+=dfs(i,n,visited)
        visited[i]=0
    return count
#グラフの位置情報を読み取る
n,m=list(map(int,input().split()))
path=[[0]*n for i in range(n)]#0は未訪問,1は訪問済み
for i in range(m):
    a,b=list(map(int,input().split()))
    path[a-1][b-1]=1#隣接しているところは1になる
    path[b-1][a-1]=1
visited=[0]*n
visited[0]=1#点1は既に訪問済みなので記録
print((dfs(0,n,visited)))
# Original solution
n,m=list(map(int,input().split()))
def dfs(now,depth):
    if used[now]:
        return 0
    if depth==n:
        return 1
    used[now]=1
    ans=0
    for i in range(n):
        if connect[now][i]:
            ans+=dfs(i,depth+1)
    used[now]=0
    return ans
edge=[[int(i)for i in input().split()]for i in range(m)]#入力値受け取り、グラフの初期設定
connect=[[0 for i in range(n)]for i in range(n)]
for i in range(m):
    connect[edge[i][0]-1][edge[i][1]-1]=1
    connect[edge[i][1]-1][edge[i][0]-1]=1
used=[0]*n
print((dfs(0,1)))



# Optimized solution
n,m=list(map(int,input().split()))
def dfs(now,depth):#現在見ている点、今まで列挙した頂点数
    if used[now]:
        return 0
    if depth==n:
        return 1
    used[now]=1#使用済みグラフを立てる
    ans=0#出力値を数える変数
    for i in range(n):#全部の遷移先を調べる
        if connect[now][i]:#nowから繋がっている頂点であれば遷移を試す。
            ans+=dfs(i,depth+1)
    used[now]=0#使用済みグラフを折る
    return ans
edge=[[int(i)for i in input().split()]for i in range(m)]#入力値受け取り、グラフの初期設定
connect=[[0 for i in range(n)]for i in range(n)]#接続状況を管理する配列
for i in range(m):
    connect[edge[i][0]-1][edge[i][1]-1]=1
    connect[edge[i][1]-1][edge[i][0]-1]=1
used=[0]*n#既にその頂点を訪問したか管理する配列
print((dfs(0,1)))
# Original solution
n,m=list(map(int,input().split()))
def dfs(now,depth):#現在見ている点、今まで列挙した頂点数
    if used[now]:
        return 0
    if depth==n:
        return 1
    used[now]=1#使用済みグラフを立てる
    ans=0#出力値を数える変数
    for i in range(n):#全部の遷移先を調べる
        if connect[now][i]:#nowから繋がっている頂点であれば遷移を試す。
            ans+=dfs(i,depth+1)
    used[now]=0#使用済みグラフを折る
    return ans
edge=[[int(i)for i in input().split()]for i in range(m)]#入力値受け取り、グラフの初期設定
connect=[[0 for i in range(n)]for i in range(n)]#接続状況を管理する配列
for i in range(m):
    connect[edge[i][0]-1][edge[i][1]-1]=1
    connect[edge[i][1]-1][edge[i][0]-1]=1
used=[0]*n#既にその頂点を訪問したか管理する配列
print((dfs(0,1)))


# Optimized solution
def dfs(now,depth):
    if visited[now]:
        return 0
    if depth==n:
        return 1
    visited[now]=1
    ans=0
    for i in range(n):
        if connect[now][i]==1:
            ans+=dfs(i,depth+1)
    visited[now]=0
    return ans
n,m=list(map(int,input().split()))
visited=[0]*n
edge=[[int(i)for i in input().split()]for i in range(m)]#入力値を受け取っている
connect=[[0 for i in range(n)]for i in range(n)]#隣接行列
for i in range(m):
    connect[edge[i][0]-1][edge[i][1]-1]=1
    connect[edge[i][1]-1][edge[i][0]-1]=1
print((dfs(0,1)))
# Original solution
import itertools

N, M = list(map(int, input().split()))
ab=[]
for _ in range(M):
    ab.append(tuple(map(int, input().split())))

lst=[[] for _ in range(N+1)]
for el in ab:
    lst[el[0]].append(el[1])
    lst[el[1]].append(el[0])
#print(lst)

count=0
for permu in itertools.permutations(list(range(1,N+1))):
    if permu[0] == 1:
        for i in range(0,N-1):
            if permu[i+1] in lst[permu[i]]:
                pass
            else:
                break
        else:
            count+=1
            #print(permu)
print(count)



# Optimized solution
N, M = list(map(int, input().split()))
ab=[]
for _ in range(M):
    ab.append(tuple(map(int, input().split())))

lst=[[] for _ in range(N+1)]
for el in ab:
    lst[el[0]].append(el[1])
    lst[el[1]].append(el[0])
#print(lst)

seen=[False]*(N+1)
todo=[]


counter=0
def func(n):
    global counter
    #print(n, seen, seen.count(True), counter)
    if seen.count(True)==N:
        counter+=1
        #print(counter)

    seen[n]=True
    for val in lst[n]:
        if seen[val]==False:
            seen[val]=True
            func(val)
            seen[val]=False
func(1)
print(counter)
# Original solution
from itertools import permutations

n,m=list(map(int,input().split()))
es=set()
for _ in range(m):
    u,v=list(map(int,input().split()))
    u,v=u-1,v-1
    es|={(u,v),(v,u)}

res=0
for p in permutations(list(range(n))):
    if p[0]!=0: continue
    if all((p[i],p[i+1]) in es for i in range(n-1)):
        res+=1
print(res)


# Optimized solution
from itertools import permutations

n,m=list(map(int,input().split()))
es=set()
for _ in range(m):
    u,v=list(map(int,input().split()))
    es|={(u-1,v-1),(v-1,u-1)}

print((sum(all((u,v) in es for u,v in zip(p,p[1:]))
          for p in permutations(list(range(n))) if p[0]==0)))
# Original solution
#
# Written by NoKnowledgeGG @YlePhan
# ('ω')
#
#import math
#mod = 10**9+7
#import itertools
#import fractions
#import numpy as np
#mod = 10**4 + 7
"""def kiri(n,m):
  r_ = n / m
  if (r_ - (n // m)) > 0:
    return (n//m) + 1
  else:
    return (n//m)"""

""" n! mod m 階乗
mod = 1e9 + 7
N = 10000000
fac = [0] * N
def ini():
  fac[0] = 1 % mod
  for i in range(1,N):
    fac[i] = fac[i-1] * i % mod"""

"""mod = 1e9+7
N = 10000000
pw = [0] * N
def ini(c):
  pw[0] = 1 % mod
  for i in range(1,N):
    pw[i] = pw[i-1] * c % mod"""

"""
def YEILD():
  yield 'one'
  yield 'two'
  yield 'three'
generator = YEILD()
print(next(generator))
print(next(generator))
print(next(generator))
"""
"""def gcd_(a,b):
  if b == 0:#結局はc,0の最大公約数はcなのに
    return a
  return gcd_(a,a % b) # a = p * b + q"""
"""def extgcd(a,b,x,y):
  d = a
  if b!=0:
    d = extgcd(b,a%b,y,x)
    y -= (a//b) * x
    print(x,y)
  else:
    x = 1
    y = 0
  return d"""


def readInts():
  return list(map(int,input().split()))
mod = 10**9 + 7
#def main():
n,m = readInts()
a = [0] * m
b = [0] * m

graph = [[False for i in range(n)] for j in range(n)]

for i in range(m):
  a[i],b[i] = list(map(int,input().split()))
  a[i] -=1
  b[i] -=1
  graph[a[i]][b[i]] = True
  graph[b[i]][a[i]] = True

cnt = 0
now = [0] * n

def dfs(pos,nya):
  global cnt
  if pos == n:
    if now[0] == 0:
      for i in range(n-1):
        if graph[now[i]][now[i+1]] == False:
          break
        if i == n-2:
          cnt += 1
    return
  for i in range(n):
    if nya & (1 << i):
      now[pos] = i
      dfs(pos+1,nya^(1<<i))
dfs(0,(1<<n)-1)
print(cnt)
  
#if __name__ == '__main__':
#  main()

# Optimized solution
n, m = list(map(int,input().split()))
G = [[] for _ in range(n)]
for i in range(m):
    a,b = [int(x)-1 for x in input().split()]
    G[a].append(b)
    G[b].append(a)
SAW = [False] * n

def dfs(v,SAW):
    global res
    end = True
    for i in range(n):
        if not SAW[i] and i != v:
            end = False
    if end:
        res += 1
        return
    SAW[v] = True
    for nv in G[v]:
        if SAW[nv]:
            continue
        dfs(nv,SAW)
    SAW[v] = False
res = 0
dfs(0,SAW)
print(res)