#include <stdio.h>

#include <algorithm>
#include <cmath>
#include <iostream>
#include <map>
#include <set>
#include <string>
#include <vector>

using namespace std;

void __print(int x) { cerr << x; }
void __print(long x) { cerr << x; }
void __print(long long x) { cerr << x; }
void __print(unsigned x) { cerr << x; }
void __print(unsigned long x) { cerr << x; }
void __print(unsigned long long x) { cerr << x; }
void __print(float x) { cerr << x; }
void __print(double x) { cerr << x; }
void __print(long double x) { cerr << x; }
void __print(char x) { cerr << '\'' << x << '\''; }
void __print(const char *x) { cerr << '\"' << x << '\"'; }
void __print(const string &x) { cerr << '\"' << x << '\"'; }
void __print(bool x) { cerr << (x ? "true" : "false"); }

template <typename T, typename V>
void __print(const pair<T, V> &x) {
    cerr << '{';
    __print(x.first);
    cerr << ',';
    __print(x.second);
    cerr << '}';
}
template <typename T>
void __print(const T &x) {
    int f = 0;
    cerr << '{';
    for (auto &i : x) cerr << (f++ ? "," : ""), __print(i);
    cerr << "}";
}
void _print() { cerr << "]\n"; }
template <typename T, typename... V>
void _print(T t, V... v) {
    __print(t);
    if (sizeof...(v)) cerr << ", ";
    _print(v...);
}
#ifdef LOCAL
#define debug(x...)               \
    cerr << "[" << #x << "] = ["; \
    _print(x)
#else
#define debug(x...)
#endif

#define UNIQUE(v) v.erase(unique(v.begin(), v.end()), v.end());
#define rep(i, n) for (int i = 0; i < (int)(n); i++)

typedef long long ll;

// union by size + path having
class UnionFind {
    /*
    par：素集合の要素の親（木構造の根）を配列で保持している．初期状態では頂点間の辺は存在せず，孤立した状態のため，自分自身を根として初期化している
    siz：各集合（木）の大きさ．根をたどるとその木の大きさが返されるように実装されている．この配列を用意しておくとある頂点の連結成分の大きさを簡単に求められる．
    */
   public:
    vector<ll> par;  // 各元の親を表す配列
    vector<ll> siz;  // 素集合のサイズを表す配列(1 で初期化)

    // Constructor
    UnionFind(ll sz_) : par(sz_), siz(sz_, 1LL) {
        for (ll i = 0; i < sz_; ++i) par[i] = i;  // 初期では親は自分自身
    }
    void init(ll sz_) {
        par.resize(sz_);
        siz.assign(sz_, 1LL);  // resize だとなぜか初期化されなかった
        for (ll i = 0; i < sz_; ++i) par[i] = i;  // 初期では親は自分自身
    }

    // Member Function
    // Find
    ll root(ll x) {  // 根の検索
        while (par[x] != x) {
            x = par[x] = par[par[x]];  // x の親の親を x の親とする
        }
        return x;
    }

    // Union(Unite, Merge)
    bool merge(ll x, ll y) {
        x = root(x);
        y = root(y);
        if (x == y) return false;
        // merge technique（データ構造をマージするテク．小を大にくっつける）
        if (siz[x] < siz[y]) swap(x, y);
        siz[x] += siz[y];
        par[y] = x;
        return true;
    }

    bool issame(ll x, ll y) {  // 連結判定
        return root(x) == root(y);
    }

    ll size(ll x) {  // 素集合のサイズ
        return siz[root(x)];
    }
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int N, M;
    cin >> N >> M;
    vector<vector<int>> ed(M, vector<int>(2));

    rep(i, M) { cin >> ed[i][0] >> ed[i][1]; }
    int ans = 0;
    UnionFind uf(N);
    rep(skip_index, M) {
        uf.init(N);
        rep(i, M) {
            if (i != skip_index) {
                uf.merge(ed[i][0]-1, ed[i][1]-1);
            }
        }
        rep(i, N) { uf.root(i); }
        if (uf.size(0) < N) {
            ans++;
        }
    }

    cout << ans << endl;
    return 0;
}


Measured memory_bytes: 256