unittest
{
	assert( [ "3 3", "1 2 1", "2 3 1", "1 3 2" ].parse.expand.solve == "Yes" );
	assert( [ "3 3", "1 2 1", "2 3 1", "1 3 5" ].parse.expand.solve == "No" );
	assert( [ "4 3", "2 1 1", "2 3 5", "3 4 2" ].parse.expand.solve == "Yes" );
	assert( [ "10 3", "8 7 100", "7 9 100", "9 8 100" ].parse.expand.solve == "No" );
	assert( [ "100 0" ].parse.expand.solve == "Yes" );
}



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 lrds = new long[][]( nm[ 1 ], 3 );
	foreach( ref lrd; lrds )
	{
		input.popFront;
		lrd = input.front.split.to!( long[] );
		lrd[ 0 ]--;
		lrd[ 1 ]--;
	}
	return tuple( nm[ 0 ], lrds );
}

auto solve( long n, long[][] lrds )
{
	const INF = 100_000L * 10_000;
	
	auto dss = new long[][]( n, n );
	foreach( i, ref ds; dss )
	{
		ds[] = INF;
		ds[ i ] = 0;
	}
	foreach( lrd; lrds )
	{
		dss[ lrd[ 0 ] ][ lrd[ 1 ] ] =  lrd[ 2 ];
		dss[ lrd[ 1 ] ][ lrd[ 0 ] ] = -lrd[ 2 ];
	}
	foreach( p; 0 .. n )
	{
		foreach( f; 0L .. n )
		{
			foreach( t; 0 .. n )
			{
				if( dss[ f ][ p ] == INF || dss[ p ][ t ] == INF ) continue;
				auto d = dss[ f ][ p ] + dss[ p ][ t ];
				if(      dss[ f ][ t ] == INF ) dss[ f ][ t ] = d;
				else if( dss[ f ][ t ] != d ) return "No";
			}
		}
	}
	
	return "Yes";
}
