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



import std.algorithm;
import std.conv;
import std.range;
import std.stdio;
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 xys = new long[][]( nm[ 0 ] );
	for( auto i = 0L; i < nm[ 1 ]; i++ )
	{
		input.popFront;
		auto xy = input.front.split.to!( long[] );
		xys[ xy[ 0 ] - 1 ] ~= xy[ 1 ] - 1;
	}
	return tuple( nm[ 0 ], xys );
}

auto solve( long n, long[][] xys )
{
	auto ans = 0L;
	memo = memo.init;
	foreach( x, xy; xys )
	{
		ans = max( ans, xys.f( x ) );
	}
	return ans;
}

static long[ long ] memo;

long f( long[][] xys, long x )
{
	if( x !in memo )
	{
		auto ans = 0L;
		foreach( y; xys[ x ] )
		{
			ans = max( ans, 1 + xys.f( y ) );
		}
		memo[ x ] = ans;
	}
	
	return memo[ x ];
}
