#[allow(unused_macros)]
macro_rules ! read_value {($ next : expr , ($ ($ t : tt ) ,* ) ) => {($ (read_value ! ($ next , $ t ) ) ,* ) } ; ($ next : expr , [$ t : tt ; $ len : expr ] ) => {(0 ..$ len ) . map (| _ | read_value ! ($ next , $ t ) ) . collect ::< Vec < _ >> () } ; ($ next : expr , chars ) => {read_value ! ($ next , String ) . chars () . collect ::< Vec < char >> () } ; ($ next : expr , usize1 ) => {read_value ! ($ next , usize ) - 1 } ; ($ next : expr , $ t : ty ) => {$ next () . parse ::<$ t > () . expect ("Parse error" ) } ; }
#[allow(unused_macros)]
macro_rules ! input_inner {($ next : expr ) => {} ; ($ next : expr , ) => {} ; ($ next : expr , $ var : ident : $ t : tt $ ($ r : tt ) * ) => {let $ var = read_value ! ($ next , $ t ) ; input_inner ! {$ next $ ($ r ) * } } ; }
#[allow(unused_macros)]
macro_rules ! input {(source = $ s : expr , $ ($ r : tt ) * ) => {let mut iter = $ s . split_whitespace () ; let mut next = || {iter . next () . unwrap () } ; input_inner ! {next , $ ($ r ) * } } ; ($ ($ r : tt ) * ) => {let stdin = std :: io :: stdin () ; let mut bytes = std :: io :: Read :: bytes (std :: io :: BufReader :: new (stdin . lock () ) ) ; let mut next = move || -> String {bytes . by_ref () . map (| r | r . unwrap () as char ) . skip_while (| c | c . is_whitespace () ) . take_while (| c |! c . is_whitespace () ) . collect () } ; input_inner ! {next , $ ($ r ) * } } ; }

use std::cmp::Ordering;
use std::ops::Index;
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Edge {
    src: usize,
    dst: usize,
    weight: i32,
}
impl Ord for Edge {
    fn cmp(&self, other: &Self) -> Ordering {
        if self.weight != other.weight {
            self.weight.cmp(&other.weight)
        } else if self.src != other.src {
            self.src.cmp(&other.src)
        } else {
            self.dst.cmp(&other.dst)
        }
    }
}
impl PartialOrd for Edge {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq, Eq)]
struct Graph {
    g: Vec<Vec<Edge>>,
}
#[allow(dead_code)]
impl Graph {
    fn new(n: usize) -> Self {
        Graph { g: vec![vec![]; n] }
    }
    fn order(&self) -> usize {
        self.g.len()
    }
    fn add_directed_edge(&mut self, src: usize, dst: usize, weight: i32) {
        self.g[src].push(Edge { src, dst, weight })
    }
    fn add_undirected_edge(&mut self, src: usize, dst: usize, weight: i32) {
        self.add_directed_edge(src, dst, weight);
        self.add_directed_edge(dst, src, weight);
    }
}
impl Index<usize> for Graph {
    type Output = Vec<Edge>;
    fn index(&self, v: usize) -> &Self::Output {
        &self.g[v]
    }
}

#[allow(dead_code)]
#[derive(Debug)]
struct UnionFind {
    parent: Vec<usize>,
    sizes: Vec<usize>, // if i is a root, sizes[i] is the order of tree containing i
    size: usize, // number of connected components
}

#[allow(dead_code)]
impl UnionFind {
    fn new(n: usize) -> Self {
        UnionFind {
            parent: (0..n).map(|i| i).collect(),
            sizes: vec![1; n],
            size: n,
        }
    }

    fn root(&mut self, x: usize) -> usize {
        if self.parent[x] == x {
            x
        } else {
            let r = self.root(self.parent[x]);
            self.parent[x] = r;
            r
        }
    }

    fn find(&mut self, x: usize, y: usize) -> bool {
        self.root(x) == self.root(y)
    }

    fn union(&mut self, x: usize, y: usize) -> bool {
        let rx = self.root(x);
        let ry = self.root(y);
        if rx == ry {
            return false;
        }

        let (large, small) = if rx < ry {
            (ry, rx)
        } else {
            (rx, ry)
        };

        self.parent[small] = large;
        self.sizes[large] += self.sizes[small];
        self.sizes[small] = 0;
        self.size -= 1;
        true
    }
}

fn main() {
    input! {
        n: usize,
        m: usize,
        ab: [(i32, i32); m],
    }

    let mut ret = 0;
    for i in 0..m {
        let mut uf = UnionFind::new(n);
        for j in 0..m {
            if j == i {
                continue;
            }
            uf.union((ab[j].0 - 1) as usize, (ab[j].1 - 1) as usize);
        }
        if uf.size > 1 {
            ret += 1;
        }
    }

    println!("{}", ret);
}


Measured memory_bytes: 2112