// Original C++ solution (slower):
#include <bits/stdc++.h>



using namespace std;

#define _GLIBCXX_DEBUG

#define rep(i, n) for (int i = 0; i < (int)(n); i++)

using ll = long long;

using ld = long double;

#define rep2(i, s, n) for (int i = (s); i < (int)(n); i++)

#define rep3(i, n) for (int i = 1; i < (int)(n+1); i++)

#define rep4(i, s, n) for (int i = (s); i < (int)(n+1); i++)

#define repr(i,n) for (int i = (n-1); i>=0;i--)

#define repr3(i,n) for(int i = (n);i>0;i--)

#define stlen(s) ll s.size()-1

#define all(v) v.begin(), v.end()

#define cout(n) cout<<std::fixed<<std::setprecision(n)

using Graph = vector<vector<int>>;

const ll INF = 1001001000100010;

const ld PI = 3.14159265358979323846;





int main() {

// 入力

    int N,M; 

    cin >> N>>M;

    vector<pair<int,int>> path(M);

    rep(i,M){

        cin>>path[i].first>>path[i].second;

    }

    





    // 初期順列

    vector<int> order(N+1);

    rep3(i,N) order[i] = i;



    int count=0;

    bool judge_all;

    bool judge[10];





    // 探索

    

    do {

        if(order[1]!=1){

            continue;

        }

        judge_all=true;

        rep4(i,2,N){

            judge[i]=false;

        }

        rep(i,M){

            rep4(j,2,N){

            if(((path[i].first==order[j-1])&&(path[i].second==order[j]))||((path[i].first==order[j])&&(path[i].second==order[j-1]))){

                judge[j]=true;

            }

            }



        }

        rep4(i,2,N){

            if(!judge[i]){

                judge_all=false;



            }

        }

        if(judge_all){

            count++;

        }



        

    } while (next_permutation(order.begin(), order.end()));



    // 答えを出力

    cout << count << endl;

return 0;

}




// Optimized solution to the same problem (faster):
/*#include <boost/multiprecision/cpp_dec_float.hpp>

#include <boost/multiprecision/cpp_int.hpp>



#include <boost/rational.hpp>

*/

#include <bits/stdc++.h>

using namespace std;

#define _GLIBCXX_DEBUG

#define rep(i, n) for (ll i = 0; i < (ll)(n); i++)

using ll = long long;

using ld = long double;

#define rep2(i, s, n) for (ll i = (s); i < (ll)(n); i++)

#define rep3(i, n) for (ll i = 1; i < (ll)(n+1); i++)

#define rep4(i, s, n) for (ll i = (s); i < (ll)(n+1); i++)

#define repr(i,n) for (ll i = (n-1); i>=0;i--)

#define repr3(i,n) for(ll i = (n);i>0;i--)

#define repr4(i,s,n) for(ll i = (n);i>=(s);i--)

#define stlen(s) ll s.size()-1

#define all(v) v.begin(), v.end()

#define allr(v) v.rbegin(), v.rend()

#define cout(n) cout<<std::fixed<<std::setprecision(n)

using Graph = vector<vector<int>>;

using Graphw = vector<vector<pair<ll,ll>>>;

#define INF1  INT_MAX;

#define INF2  LLONG_MAX;

#define PI  3.14159265358979323846;

#define MOD 1000000007;

/*

namespace mp = boost::multiprecision;

// 任意長整数型

using Bint = mp::cpp_int;

// 仮数部長が32の浮動小数点数型

using Real32 = mp::number<mp::cpp_dec_float<32>>;

// 仮数部長が1024の浮動小数点数型

using Real1024 = mp::number<mp::cpp_dec_float<1024>>;

// 有理数型

using Rat = boost::rational<Bint>;

*/

const int mod = 1000000007;







int main(){



int N,M;

cin>>N>>M;

bool path[N+1][N+1];

memset(path,false,sizeof(path));

rep(i,M){

    int a,b;

    cin>>a>>b;

    path[a][b]=true;

    path[b][a]=true;

}



vector<int> order(N-1);

int count=0;

    for (int i = 2; i <= N; ++i) order[i-2] = i;



    // 探索

    

    do {

        bool judge=true;

        if(!path[1][order[0]])judge=false;

        rep(i,N-2)if(!path[order[i]][order[i+1]])judge=false;

        if(judge)count++;



    } while (next_permutation(order.begin(), order.end()));



cout<<count<<endl;

return 0;

}

// Fastest known solution for this problem: