{-# LANGUAGE OverloadedStrings #-}
import Control.Monad
import Data.Array
import Data.Char
import Data.List
import Data.Maybe
import Data.Tuple
import qualified Data.ByteString.Char8 as BS

main :: IO ()
main = do
  [n, m] <- getIntList
  edges <- accumArray (||) False ((1, 1), (n, n)) . flip zip (repeat True) <$> replicateM m getIntTuple
  let paths = map ((\ns -> zip ns (tail ns)) . (1:)) $ permutations [2..n]
  let check p = edges!p || edges!(swap p)
  print . length . filter id . map (all check) $ paths

-- IO
readInt :: BS.ByteString -> Maybe (Int, BS.ByteString)
readInt = BS.readInt . BS.dropWhile isSpace

getInt :: IO Int
getInt = fst . fromJust . BS.readInt <$> BS.getLine

getIntList :: IO [Int]
getIntList = unfoldr readInt <$> BS.getLine

getIntTuple :: IO (Int, Int)
getIntTuple = do
  (a, s) <- fromJust . readInt <$> BS.getLine
  let (b, _) = fromJust . readInt $ s
  return (a, b)