unittest
{
//	assert( [ "2 3", "1 2 3", "0 1 1" ].parse.expand.solve == "3\n2 2 2 3\n1 1 1 2\n1 3 1 2" );
	assert( [ "2 3", "1 2 3", "0 1 1" ].parse.expand.solve == "3\n1 2 1 1\n1 3 1 2\n2 2 2 3" );		// 別解
//	assert( [ "3 2", "1 0", "2 1", "1 0" ].parse.expand.solve == "3\n1 1 1 2\n1 2 2 2\n3 1 3 2" );
	assert( [ "3 2", "1 0", "2 1", "1 0" ].parse.expand.solve == "2\n2 2 1 2\n1 2 1 1" );		// 別解
//	assert( [ "1 5", "9 9 9 9 9" ].parse.expand.solve == "2\n1 1 1 2\n1 3 1 4" );
	assert( [ "1 5", "9 9 9 9 9" ].parse.expand.solve == "2\n1 2 1 1\n1 4 1 3" );		// 別解
}



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

auto solve( long h, long w, long[][] ass )
{
	auto n = 0L;
	string[] ops;
	
	foreach( y; 0 .. ass.length )
	{
		if( y % 2 == 0 )
		{
			// 偶数行目は左のマスから順に操作
			foreach( x; 0 .. ass[ y ].length )
			{
				if( ass[ y ][ x ] % 2 == 0 ) continue;		// 偶数枚のマスは移動不要
				auto t = moveCoin( ass, y, x );
				n += t[ 0 ];
				ops ~= t[ 1 ];
			}
		}
		else
		{
			// 奇数行目は右のマスから順に操作
			foreach_reverse( x; 0 .. ass[ y ].length )
			{
				if( ass[ y ][ x ] % 2 == 0 ) continue;		// 偶数枚のマスは移動不要
				auto t = moveCoin( ass, y, x );
				n += t[ 0 ];
				ops ~= t[ 1 ];
			}
		}
	}
	
	// 出力フォーマットを整える
	return format( "%d\n%s", n, ops.join( "\n" ) );
}

Tuple!( long, string[] ) moveCoin( ref long[][] ass, long dst_y, long dst_x )
{
	auto n = 0L;
	string[] ops;
	auto src_y = dst_y;
	auto src_x = dst_x;
	
	// 移動元を判定
	if( dst_y % 2 == 0 )
	{
		// 偶数行目は右から左に移動（右端だったら下の行から移動）
		if( dst_x < ass[ dst_y ].length - 1 ) src_x++;
		else                                  src_y++;
	}
	else
	{
		// 奇数行目は左から右に移動（左端だったら下の行から移動）
		if( 0 < dst_x ) src_x--;
		else            src_y++;
	}
	
	// 移動元が盤面をはみ出てるのであきらめる
	if( ass.length <= src_y ) return tuple( n, ops );
	
	// 移動実行（移動元が0枚だったら更に一つ先から移動する）
	if( ass[ src_y ][ src_x ] == 0 )
	{
		auto t = moveCoin( ass, src_y, src_x );
		if( t[ 0 ] == 0 ) return tuple( n, ops );		// 移動できなかったのであきらめる
		n += t[ 0 ];
		ops ~= t[ 1 ];
	}
	ass[ dst_y ][ dst_x ]++;
	ass[ src_y ][ src_x ]--;
	n++;
	ops ~= format( "%d %d %d %d", src_y + 1, src_x + 1, dst_y + 1, dst_x + 1 );
	
	return tuple( n, ops );
}
