let input = readLine()!.split(separator: " ").map { Int($0)! }
let N = input[0]
let M = input[1]

struct UndirectedPath: Equatable {
    let node0: Int
    let node1: Int

    init(_ node0: Int, _ node1: Int) {
        self.node0 = node0
        self.node1 = node1
    }
}
func ==(lhs: UndirectedPath, rhs: UndirectedPath) -> Bool {
    return lhs.node0 == rhs.node0 && lhs.node1 == rhs.node1
      || lhs.node0 == rhs.node1 && lhs.node1 == rhs.node0
}

var paths = [UndirectedPath]()
for _ in stride(from: 0, to: M, by: 1) {
    let path = readLine()!.split(separator: " ").map { Int($0)! }
    paths.append(UndirectedPath(path[0], path[1]))
}

func depthFirstSearch(_ visitedNodes: [Int] = [1]) -> Int {
    guard visitedNodes.count < N else {
        return 1
    }

    var numberOfHamiltonianPath = 0
    for node in stride(from: 1, through: N, by: 1) {
        guard !visitedNodes.contains(node) else { continue }

        if paths.contains(UndirectedPath(visitedNodes.last!, node)) {
            numberOfHamiltonianPath += depthFirstSearch(visitedNodes + [node])
        }
    }

    return numberOfHamiltonianPath
}


print(depthFirstSearch())
