unittest
{
	assert( [ "7 7", "1 3", "2 7", "3 4", "4 5", "4 6", "5 6", "6 7" ].parse.expand.solve == 4 );
	assert( [ "3 3", "1 2", "1 3", "2 3" ].parse.expand.solve == 0 );
	assert( [ "6 5", "1 2", "2 3", "3 4", "4 5", "5 6" ].parse.expand.solve == 5 );
}



import std.algorithm;
import std.conv;
import std.range;
import std.stdio;
import std.string;
import std.typecons;

void main()
{
	stdin.byLineCopy.parse.expand.solve.writeln;
}

auto parse( Range )( Range input )
if( isInputRange!Range && is( ElementType!Range == string ) )
{
	auto nm = input.front.split.to!( long[] );
	auto lss = new long[][ nm[ 1 ] ];
	foreach( ref ls; lss )
	{
		input.popFront;
		ls = input.front.split.to!( long[] );
	}
	return tuple( nm[ 0 ], lss );
}

auto solve( long n, long[][] lss )
{
	// 各点に接続している辺を分別
	auto css = new long[][ n ];
	foreach( ls; lss )
	{
		css[ ls[ 0 ] - 1 ] ~= ls[ 1 ] - 1;
		css[ ls[ 1 ] - 1 ] ~= ls[ 0 ] - 1;
	}
	foreach( ref cs; css )
	{
		cs.sort();
	}
	
	// ノードのループ有無を識別
	auto ns = new byte[ n ];		// 0=通ってない、1=通った（ループしてない）、2=通った（ループしてる）
	ns = ns.search( css, [ 0 ] );
	
	// ループしてないノードに接続している辺を数える
	auto r = 0L;
	foreach( ls; lss )
	{
		if( ns[ ls[ 0 ] - 1 ] == 1 || ns[ ls[ 1 ] - 1 ] == 1 ) r++;
	}
	
	return r;
}

byte[] search( ref byte[] ns, long[][] css, long[] rs )
{
	auto rf = rs[ $ - 1 ];
	auto rp = ( 2 <= rs.length ) ? rs[ $ - 2 ] : -1;
	
	switch( ns[ rf ] )
	{
		case 0:
			// 今のノードに通ったフラグを立てて次のノードに進む
			ns[ rf ] = 1;
			foreach( c; css[ rf ] )
			{
				if( rp == c ) continue;		// 移動元ノードは見ない
				ns = ns.search( css, rs ~ c );
			}
			break;
		
		case 1,2:
			// 今のノードと通ってきたノードにループフラグを立てる
			foreach( ref r; rs.find( rf ) )
			{
				ns[ r ] = 2;
			}
			break;
		
		default:
			assert( false );
	}
	
	return ns;
}
