CodeEff/ECCO huggingface dataset:
# Original solution
import sys
import copy

read = sys.stdin.buffer.read
readline = sys.stdin.buffer.readline

in_n = lambda: int(readline())
in_nn = lambda: list(map(int, readline().split()))
in_nl = lambda: list(map(int, readline().split()))
in_na = lambda: list(map(int, read().split()))
in_s = lambda: readline().rstrip().decode('utf-8')

sys.setrecursionlimit(1000000)


ans = 0


def bfs(N, edge, v, search):

    search |= {v}

    global ans
    if len(search) == N:
        ans += 1
        return

    for e in edge[v]:
        if e not in search:
            #print('test:{},{},{}'.format(e, v, search))
            bfs(N, edge, e, copy.copy(search))


def main():

    N, M = in_nn()

    edge = [[] for _ in range(N)]

    for i in range(M):
        a, b = in_nn()
        a, b = a - 1, b - 1
        edge[a].append(b)
        edge[b].append(a)

    bfs(N, edge, 0, set())

    print(ans)


if __name__ == '__main__':
    main()


# Optimized solution
import sys
import copy

read = sys.stdin.buffer.read
readline = sys.stdin.buffer.readline

in_n = lambda: int(readline())
in_nn = lambda: list(map(int, readline().split()))
in_nl = lambda: list(map(int, readline().split()))
in_na = lambda: list(map(int, read().split()))
in_s = lambda: readline().rstrip().decode('utf-8')

sys.setrecursionlimit(1000000)


ans = 0


def bfs(N, edge, v, search):

    search |= {v}

    global ans
    if len(search) == N:
        ans += 1
        return

    for e in edge[v]:
        if e not in search:
            bfs(N, edge, e, copy.copy(search))


def main():

    N, M = in_nn()

    edge = [[] for _ in range(N)]

    for i in range(M):
        a, b = in_nn()
        a, b = a - 1, b - 1
        edge[a].append(b)
        edge[b].append(a)

    bfs(N, edge, 0, set())

    print(ans)


if __name__ == '__main__':
    main()
# Original solution
import sys
sys.setrecursionlimit(10000000)
import copy

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

ans = 0

def dfs(visited, now):
    tv = copy.copy(visited)
    tv[now-1] = True
    
    if all(tv):
        return 1

    temp = 0
    for i in graph[now]:
        if tv[i-1] != 1:
            temp += dfs(tv, i)
    return temp

ans = dfs([False] * n, 1)
print(ans)

# Optimized solution
import copy
n, m = list(map(int, input().split()))

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

def dfs(visited, now):
    tv = copy.copy(visited)
    tv[now-1] = 1
    if all(tv) == 1:
        return 1

    count = 0
    for i in graph[now]:
        if tv[i-1] == 0:
            count += dfs(tv, i)
    return count

print((dfs([0] * n, 1)))
# Original solution
# 頂点および辺の数を入力
N, M = list(map(int, input().split()))
# 辺のリストを作成
list_edge = [[] for _ in range(N + 1)]
for _ in range(M):
    a, b = list(map(int, input().split()))
    list_edge[a].append(b)
    list_edge[b].append(a)
# パス（リスト）のリストを初期化
list_path = [[1]]
# パスが存在する、かつパスの長さが頂点の数未満である場合に繰り返し
while list_path != [] and len(list_path[0]) < N:
    # パスのリストから先頭のパスを取り出す
    path = list_path.pop(0)
    # パスの最後尾の頂点から訪問可能な頂点について、、
    for node in list_edge[path[-1]]:
        # その頂点にまだ訪問していない場合、、
        if node not in path:
            # パスのリストに新たなパスを加える
            list_path.append(path + [node])
# パスの数を出力する
print((len(list_path)))

# Optimized solution
# 頂点および辺の数を入力
N, M = list(map(int, input().split()))
# 辺のリストを作成
list_edge = [[] for _ in range(N + 1)]
for _ in range(M):
    a, b = list(map(int, input().split()))
    list_edge[a].append(b)
    list_edge[b].append(a)

# パスの数を返す関数を定義
def count_path(node, path, list_edge, N):
    # パスの数を初期化
    count = 0
    # 全ての頂点を訪問した場合、、
    if len(path) == N:
        # パスの数に1を加える
        count += 1
    # 全ての頂点を訪問していない場合、、
    else:
        # 現在の頂点から訪問できる各頂点について、、
        for next_node in list_edge[node]:
            # その頂点にまだ訪問していない場合、、
            if next_node not in path:
                # 再帰
                count += count_path(next_node, path + [next_node], list_edge, N)
    # パスの本数を返す
    return count
# パスの本数を出力する
print((count_path(1, [1], list_edge, N)))
# Original solution
import os,re,sys,operator
from collections import Counter,deque
from operator import itemgetter
from itertools import accumulate,combinations,groupby
from sys import stdin,setrecursionlimit
from copy import deepcopy
import heapq

setrecursionlimit(10**6)

def dfs(now,depth):
    global used
    global gragh
    global n
    ans=0

    if used[now]:
        return 0
    if depth==n:
        return 1
    used[now]=1
    for i in range(n):
        if gragh[now][i]:
            ans+=dfs(i,depth+1)
    used[now]=0
    return ans

n,m=list(map(int,stdin.readline().rstrip().split()))
gragh=[[0]*n for _ in range(n)]

for i in range(m):
    a,b=list(map(int,stdin.readline().rstrip().split()))
    gragh[a-1][b-1]=gragh[b-1][a-1]=1

used=[0]*n
print((dfs(0,1)))

# Optimized solution
import sys
sys.setrecursionlimit(10**6)

input=sys.stdin.readline

n,m=list(map(int,input().split()))
G=[[False]*n for _ in range(n)]
used=[False]*n

def dfs(now,depth):
    if used[now]:
        return 0
    if n==depth:
        return 1
    used[now]=True
    ans=0
    for i in range(n):
        if G[now][i]:
            ans+=dfs(i,depth+1)
    used[now]=False
    return ans

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

print((dfs(0,1)))
# Original solution
num_ver, num_edge = list(map(int,input().split()))
# 隣接行列
graph_mat = [[0]*num_ver for _ in range(num_ver)]

for i in range(num_edge):
    start, goal = list(map(int,input().split()))
    start -= 1
    goal -= 1
    graph_mat[start][goal] = 1
    graph_mat[goal][start] = 1

visited = [0] * num_ver


#再帰関数
def dfs(now, depth):
    if visited[now]:
        return 0
    if depth == num_ver - 1:
        return 1

    visited[now] = 1
    total_paths = 0
    # 「同じ深さの処理」＝場合わけ
    for i in range(num_ver):
        if graph_mat[now][i]:
            total_paths += dfs(i, depth + 1)

    visited[now] = 0

    return total_paths

print((dfs(0, 0)))




# Optimized solution
num_ver, num_edge = list(map(int,input().split()))
# 隣接行列
graph_mat = [[0]*num_ver for _ in range(num_ver)]

for i in range(num_edge):
    start, goal = list(map(int,input().split()))
    start -= 1
    goal -= 1
    graph_mat[start][goal] = 1
    graph_mat[goal][start] = 1

visited = [0] * num_ver

def dfs(now, depth):
    if visited[now]:
        return 0
    if depth == num_ver - 1:
        return 1
    
    visited[now] = 1
    num_path = 0

    for ver in range(num_ver):
        if graph_mat[now][ver]:
            num_path += dfs(ver, depth + 1)
    
    # 別の一筆書きの道として探索されることを考慮、もちろん頂点1についてはこれは意味ない
    visited[now] = 0

    return num_path
print((dfs(0,0)))
# Original solution
from itertools import permutations
n_ver,n_edge = list(map(int,input().split()))
path_mat = [[0]*n_ver for _ in range(n_ver)]
for i in range(n_edge):
    a,b = list(map(int,input().split()))
    a -= 1
    b -= 1
    path_mat[a][b] = 1
    path_mat[b][a] = 1
ans = 0
vers = [i for i in range(n_ver)]
for path in permutations(vers):
    if path[0] != 0:
        continue
    can = True
    for i in range(n_ver-1):
        from_ = path[i]
        to = path[i+1]
        if not path_mat[from_][to]:
            can = False
            break
    if can:
        ans += 1
print(ans)



# Optimized solution
import sys
sys.setrecursionlimit(10**7)
n_ver, n_edge = list(map(int,input().split()))
ad_ls = [[] for _ in range(n_ver)]
for i in range(n_edge):
    a,b = list(map(int,input().split()))
    a -= 1
    b -= 1
    ad_ls[a].append(b)
    ad_ls[b].append(a)
done_ls = [0] * n_ver
def dfs(ver):
    done_ls[ver] = 1
    num = 0
    if done_ls == [1] * n_ver:
        done_ls[ver] = 0
        return 1
    for nex in ad_ls[ver]:
        if done_ls[nex]:
            continue
        num += dfs(nex)
    done_ls[ver] = 0
    return num
print((dfs(0)))
# Original solution
import itertools

n,m = list(map(int,input().split()))#頂点の数、辺の数

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

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

ans = 0
for i in itertools.permutations(list(range(n))):
    if i[0] != 0:
        continue
    visited = [0]*n
    visited[0] += 1
    for j in range(n):
        if j == n-1:
            if 0 not in visited:
                ans += 1
            break
        if graph[i[j]][i[j+1]] == True:
            visited[i[j+1]] += 1
        
            
print(ans)


# Optimized solution
import itertools

n,m = list(map(int,input().split()))
path = [[] for i in range(n+1)]
for i in range(m):
    a,b = list(map(int,input().split()))
    path[a].append(b)
    path[b].append(a)

ans = 0
l = [i for i in range(2,n+1)]
for v in itertools.permutations(l,n-1):
    flag = True
    if v[0] not in path[1]:
        flag = False
        continue
    for k in range(1,n-1):
        if v[k] not in path[v[k-1]]:
            flag = False
            continue
    if flag:
        ans += 1

print(ans)
# Original solution
import itertools

N,M=list(map(int,input().split()))
route=[list(map(int,input().split())) for _ in range(M)]
candidate=list(itertools.permutations(list(range(1,N+1))))

ans=0
for c in candidate:
  if c[0]==1:
    for i in range(N-1):
      l=sorted([c[i], c[i+1]])
      if l not in route:
         break
    else:
      ans+=1
print(ans)



# Optimized solution
import itertools
N, M = list(map(int, input().split()))

G = {k: set() for k in range(N+1)}
for _ in range(M):
    a, b = list(map(int, input().split()))
    # 無向グラフ
    G[a].add(b)
    G[b].add(a)

ans = 0
for p in itertools.permutations(list(range(2, N+1))):
    c = 1
    for n in p:
        if n not in G[c]:
            break
        c = n
    else:
        ans += 1

print(ans)
# Original solution
from collections import deque
N, M = list(map(int, input().split()))
V = []
for i in range(M):
    a, b = list(map(int, input().split()))
    V.append((a-1, b-1))


def bfs(G):
    visited = [False] * N
    visited[0] = True
    que = deque([(0, -1, 0)])
    r = 0
    while que:
        ci, p, d = que.popleft()
        for ni in G[ci]:
            if visited[ni] is False:
                visited[ni] = True
                que.append((ni, ci, d+1))
                r = max(r, d+1)

    return r


ans = 0
for i in range(2 ** M):
    if bin(i).count('1') != N-1:
        continue
    else:
        G = {k: [] for k in range(N)}
        for j in range(M):  # このループが一番のポイント
            if ((i >> j) & 1):  # 順に右にシフトさせ最下位bitのチェックを行う
                (a, b) = V[j]
                G[a].append(b)
                G[b].append(a)
        if bfs(G) == N-1:
            ans += 1
print(ans)


# Optimized solution
import sys
sys.setrecursionlimit(1000000)
N, M = list(map(int, input().split()))
G = {k: [] for k in range(N)}
for _ in range(M):
    a, b = list(map(int, input().split()))
    # 無向グラフ
    G[a-1].append(b-1)
    G[b-1].append(a-1)

visited = [False]*N
visited[0] = True


def dfs(v):
    if all(visited):
        return 1
    r = 0
    for nv in G[v]:
        if visited[nv] is False:
            visited[nv] = True
            r += dfs(nv)
            visited[nv] = False
    return r


print((dfs(0)))
# Original solution
import sys
sys.setrecursionlimit(1000000)

N,M = list(map(int, input().split()))
P = [list(map(int, input().split())) for _ in range(M)]

vis_N = [-1]*(N+1)
count = 0
vis_N[1] = 1

def search(a):
    global count,vis_N,vis_Nij
    #print(count,vis_N)
    if sum(vis_N)+1 == N:
        count = count + 1
        return# count#print(count,"A",i)
    
    for i in range(M):
        if (P[i][0] == a and vis_N[P[i][1]] == -1) or (P[i][1] == a and vis_N[P[i][0]] == -1):
            if P[i][0] == a:
                nxt = P[i][1]
            else:
                nxt = P[i][0]
            #vis_N[a] = 1
            vis_N[nxt] = 1
            search(nxt)
            vis_N[nxt] = -1
    
    return# count

search(1)
print(count)

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

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

def dfs(v, used):
    if not False in used:
        return 1
    
    ans = 0
    for i in range(N):
        if not adj_matrix[v][i]:
            continue
        if used[i]:
            continue
        
        used[i] = True
        ans += dfs(i, used)
        used[i] = False
    
    return ans

used = [False] * N
used[0] = True

print((dfs(0, used)))
# Original solution
# 基本課題 3．経路探索の解を求める
#
# 5 つの地点 0 ～ 4 を結ぶ道路網が図のようにある ( 図は授業支援システム参照 )
# 始点を 0 とし終点を 4 とした時、0 から 4 に至る経路をすべて求めなさい。
# ただし、同じ地点を２度通ってはいけない
#
# -*- coding: utf-8 -*-

ANS2=[] #0から4までの経路列挙(2次元配列)

#引数
#G:道路網を表す2次元配列(隣接リスト)
#LT:今のところの経路(だんだん広げていく)(最初は[[0]])
#返り値
#広げられなくなるまで広げた経路
#副作用
#ANS2を更新
#ANS2:0から4までの経路
def loot(G,LT):
    if LT==[]:
        return []
    else:    
        ANS=[] #今の地点から一回広げた時点での経路
        for i in range(len(LT)):
            for j in range(len(G[LT[i][-1]])):
                if not(G[LT[i][-1]][j] in LT[i]):
                    TMP=LT[i]
                    ANS.append(TMP+[G[LT[i][-1]][j]])
                else:
                    if not LT[i] in ANS2:
                        ANS2.append(LT[i])         
        return loot(G,ANS)

#2次元リストを標準出力
def print_list(lst):
    for i in range(len(lst)):
        for j in range(len(lst[i])):
            print(ANS2[i][j],end=' ')
        print('')    

N,M=map(int,input().split())
G=[[] for i in range(N)]
for i in range(M):
    a,b=map(int,input().split())
    a-=1
    b-=1
    G[a].append(b)
    G[b].append(a)
               

loot(G,[[0]])

cnt=0

for i in ANS2:
    if len(i)==N:
        cnt+=1

print(cnt)



# Optimized solution
# 基本課題 3．経路探索の解を求める
#
# 5 つの地点 0 ～ 4 を結ぶ道路網が図のようにある ( 図は授業支援システム参照 )
# 始点を 0 とし終点を 4 とした時、0 から 4 に至る経路をすべて求めなさい。
# ただし、同じ地点を２度通ってはいけない
#
# -*- coding: utf-8 -*-

ANS2=[] #0から4までの経路列挙(2次元配列)

#引数
#G:道路網を表す2次元配列(隣接リスト)
#LT:今のところの経路(だんだん広げていく)(最初は[[0]])
#返り値
#広げられなくなるまで広げた経路
#副作用
#ANS2を更新
#ANS2:0から4までの経路
def loot(G,LT,N):
    if LT==[]:
        return []
    else:    
        ANS=[] #今の地点から一回広げた時点での経路
        for i in range(len(LT)):
            for j in range(len(G[LT[i][-1]])):
                if not(G[LT[i][-1]][j] in LT[i]):
                    TMP=LT[i]
                    ANS.append(TMP+[G[LT[i][-1]][j]])
                else:
                    if not LT[i] in ANS2:
                        if len(LT[i])==N:
                            ANS2.append(LT[i])         
        return loot(G,ANS,N)

#2次元リストを標準出力
def print_list(lst):
    for i in range(len(lst)):
        for j in range(len(lst[i])):
            print(ANS2[i][j],end=' ')
        print('')    

N,M=map(int,input().split())
G=[[] for i in range(N)]
for i in range(M):
    a,b=map(int,input().split())
    a-=1
    b-=1
    G[a].append(b)
    G[b].append(a)
               

loot(G,[[0]],N)

cnt=0

print(len(ANS2))
# Original solution
import copy

N, M = [ int(x) for x in input().split()]

class Node:
    def __init__(self, id):
        self.id = id
        self.path = []
        self.passed = False
    def __str__(self):
        return "id: " + str(self.id) + " path: " + str(self.path) + " passed: " + str(self.passed)

nodes = []
for i in range(N):
    nodes.append(Node(i))

for i in range(M):
    from_id, to_id = [ int(x) - 1 for x in input().split()]
    nodes[from_id].path.append(to_id)
    nodes[to_id].path.append(from_id)

def if_all_passed(nodes_current):
    for node in nodes_current:
        if not node.passed:
            return False
    return True

def go_child(id_new, nodes_current):
    nodes_current[id_new].passed = True
    if if_all_passed(nodes_current):
        return 1

    count = 0
    for i in nodes[id_new].path:
        if not nodes_current[i].passed:
            count += go_child(i, copy.deepcopy(nodes_current))
    return count

count = go_child(0, nodes)
print(count)


# Optimized solution
import copy

N, M = [ int(x) for x in input().split() ]

class Node:
    def __init__(self, id):
        self.id = id
        self.path = []
    def __str__(self):
        return "id: " + str(self.id) + " path: " + str(self.path)

nodes = []
for i in range(N):
    nodes.append(Node(i))

for i in range(M):
    from_id, to_id = [ int(x) - 1 for x in input().split() ]
    nodes[from_id].path.append(to_id)
    nodes[to_id].path.append(from_id)

def if_all_passed(passed_nodes):
    if len(passed_nodes) == N:
        return True
    else:
        return False

def go_child(id_new, passed_nodes):
    passed_nodes.append(id_new)
    if if_all_passed(passed_nodes):
        return 1

    count = 0
    for i in nodes[id_new].path:
        if not i in passed_nodes:
            count += go_child(i, copy.deepcopy(passed_nodes))
    return count

count = go_child(0, [])
print(count)
# Original solution
import itertools
n, m=list(map(int, input().split()))
ab=[set(map(int, input().split())) for _ in range(m)]
root=list(itertools.permutations(ab, n-1))
cnt=0
for r in root:
    trace=[i+1 for i in range(n)]
    other=1
    for i in range(0, n-1):
        if other in r[i]:
            try:
                trace.remove(other)
            except ValueError:
                break
            other=list(r[i]-{other})[0]
            continue
        else: break
    try:
        trace.remove(other)
    except ValueError:
        pass
    if trace==[]:
        cnt+=1
print(cnt)

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

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

def dfs(v, used):
    if not False in used:
        return 1
    
    ans = 0
    for i in range(N):
        if not adj_matrix[v][i]:
            continue
        if used[i]:
            continue
        
        used[i] = True
        ans += dfs(i, used)
        used[i] = False
    
    return ans

used = [False] * N
used[0] = True

print((dfs(0, used)))
# Original solution
import itertools

n, m = list(map(int, input().split()))
path = [[False] * n for i in range(n)]

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

    ans = 0

for i in itertools.permutations(list(range(n)), n):
    # 頂点1が始点
    if i[0] == 0:
        for j in range(n):
            if j == n - 1:
                ans += 1
                break
            if not path[i[j]][i[j + 1]]:
                break

print(ans)


# Optimized solution
def main():
    N, M = list(map(int, input().split()))
    matrix = [[0] * N for _ in range(N)]

    for _ in range(M):
        a, b = list(map(int, input().split()))
        matrix[a-1][b-1] = 1
        matrix[b-1][a-1] = 1

    def dfs(v, used):
        if False not in used:
            return 1

        ans = 0
        for i in range(N):
            if not matrix[v][i]:
                continue
            if used[i]:
                continue

            used[i] = True
            ans += dfs(i, used)
            used[i] = False

        return ans

    used = [False] * N
    used[0] = True

    print((dfs(0, used)))


if __name__ == '__main__':
    main()
# Original solution
import itertools

n, m = list(map(int, input().split()))
v = []
for i in range(m):
  tpl = tuple(map(int, input().split()))
  v.append(tpl)
  v.append((tpl[1], tpl[0]))

perms = list(itertools.permutations(v, n-1))
c = 0
for i in perms:
  if i[0][0] != 1:
    continue
  visited = []
  for j in range(len(i)-1):
    if i[j][1] != i[j+1][0]:
      break
    visited.append(i[j][0])
    if i[j+1][1] in visited:
      break
  else:
    c += 1
print(c)


# Optimized solution
import itertools

n, m = list(map(int, input().split()))
v = []
for i in range(m):
  tpl = tuple(map(int, input().split()))
  v.append(tpl)
  v.append((tpl[1], tpl[0]))

perms = list(itertools.permutations(list(range(1, n+1))))
c = 0
for i in perms:
  if i[0] != 1:
    continue
  for j in range(len(i)-1):
    if (i[j], i[j+1]) not in v:
      break
  else:
    c += 1
print(c)
# Original solution
from itertools import permutations

n,m = list(map(int,input().split()))
D = [[0]*n for i in range(n)]
for i in range(m):
	a,b = list(map(int,input().split()))
	D[a-1][b-1] = 1
	D[b-1][a-1] = 1
cnt = 0
for a in permutations(list(range(n))):
	if a[0] != 0:
		break
	tmp = 1
	for i in range(n-1):
		tmp = tmp * D[a[i]][a[i+1]]
	cnt += tmp
print(cnt)

# Optimized solution
n,m = list(map(int, input().split()))
D = [list() for i in range(n)]
for i in range(m):
  a,b = list(map(int,input().split()))
  D[a-1].append(b-1)
  D[b-1].append(a-1)

d = [0]*n
d[0] = 1
cnt = 0

def dfs(x):
  global cnt
  if all(d):
    cnt += 1
    return
  for i in D[x]:
    if d[i] == 0:
      d[i] = 1
      dfs(i)
      d[i] = 0

dfs(0)
print(cnt)
# Original solution
def main():
    N, M = (int(i) for i in input().split())
    edge = [set() for _ in range(N)]
    for i in range(M):
        a, b = (int(i) for i in input().split())
        edge[a-1].add(b-1)
        edge[b-1].add(a-1)
    from itertools import permutations
    ans = 0
    for p in permutations(list(range(1, N))):
        u = 0
        for v in p:
            if v not in edge[u]:
                break
            u = v
        else:
            ans += 1
    print(ans)


if __name__ == '__main__':
    main()


# Optimized solution
def main():
    import sys
    input = sys.stdin.buffer.readline
    from itertools import permutations
    N, M = (int(i) for i in input().split())
    edge = [[] for _ in range(N)]
    for i in range(M):
        a, b = (int(i) for i in input().split())
        edge[a-1].append(b-1)
        edge[b-1].append(a-1)

    ans = 0
    for p in permutations(list(range(1, N))):
        v = 0
        for nv in p:
            if nv in edge[v]:
                v = nv
            else:
                break
        else:
            ans += 1
    print(ans)


if __name__ == '__main__':
    main()
# Original solution
import copy

N, M = list(map(int,input().split()))
E = [list(map(int, input().split())) for _ in range(M)]



def dfs(flg, E, i):
	q = []
	for e in E:
		if e[0] == i:
			if flg[e[1]-1] == 0:
				q.append(e[1])
		if e[1] == i:
			if flg[e[0]-1] == 0:
				q.append(e[0])
	if len(q) == 0:
		if flg.count(0) == 0:
			return 1
		else:
			return 0

	ans = 0
	for n in q:
		temp = copy.deepcopy(flg)
		temp[n-1] = 1
		ans += dfs(temp, E, n)

	return ans

flg = [0 for _ in range(N)]
flg[0] = 1
print((dfs(flg, E, 1)))

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

for _ in range(M):
	ta, tb = list(map(int, input().split()))
	E[ta-1].append(tb-1)
	E[tb-1].append(ta-1)


def next_permutation(out, cnt, flg):
	if cnt == N:
		for i in range(N-1):
			if out[i+1] not in E[out[i]]:
				return 0
		return 1

	ans = 0

	for i in range(N):
		if flg[i] == 0:
			out.append(i)
			flg[i] = 1

			ans += next_permutation(out, cnt+1, flg)

			out.pop()
			flg[i] = 0

	return ans

perm = [0]
cnt = 1
flg = [0 for _ in range(N)]
flg[0] = 1

answer = next_permutation(perm, cnt, flg)

print(answer)
# Original solution
import itertools

N, M = list(map(int, input().split()))
ab = []
for _ in range(M):
    a, b = list(map(int, input().split()))
    ab.append({a-1, b-1})

ans = 0
for v in itertools.permutations(list(range(1, N))):
    ok = True
    a = 0
    for b in v:
        if {a, b} not in ab:
            ok = False
            break
        a = b
    if ok:
        ans += 1

print(ans)


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

ans = 0
def dfs(x, visited):
    global ans
    if len(visited) == N:
        ans += 1
        return

    for b in ab[x]:
        if b not in visited:
            dfs(b, visited + [b])
    return

dfs(0, [0])
print(ans)