#include <iostream> // cin, cout, cerr
#include <algorithm> // minmax, sort, swap
#include <numeric> // iota
#include <cstdio> // printf, scanf
#include <string> // string, stoi, to_string
#include <vector> // vector
#include <queue> // queue, priority_queue
#include <deque> // deque
#include <map> // key-value pairs sorted by keys
#include <set> // set
#include <iomanip> // cout<<setprecision(n)
#include <functional> // function<void(int)>
#include <cmath>
#include <cassert>
#include <bitset>

#ifdef DEBUG
#include "debug.hpp"
#else
#define debug(...)
#endif

#define int long long // at least int64 > 9*10^18
#define EL '\n'
#define rep(i,n) for(int i = 0; i < (n); i++)
#define print(i) std::cout << (i) << '\n'
#define all(v) (v).begin(), (v).end()
/* libraries */

struct UnionFind
{
  int n;
  std::vector<int> par,size;
  UnionFind(int sz) : n(sz), par(sz,0), size(sz,1) {
    std::iota(par.begin(),par.end(),0);
  }
  int find(int x) {
    if(par[x]==x) return x;
    return par[x] = find(par[x]);
  }
  void unite(int x, int y) {
    x=find(x);y=find(y);
    if(x==y) return;
    if(size[x]<size[y]) std::swap(x,y);
    size[x]+=size[y];
    par[y]=x;
  }
};


signed main() {
	int clar[4][4] = {
		6,3,1,4,
		2,-1,2,-1,
		1,3,6,4,
		5,-1,5,-1
	};
	int h,w;
	std::cin >> h >> w;
	std::vector<std::string> g(h);
	rep(i,h) std::cin >> g[i];
	auto enc = [&w](int x, int y) -> int {
		return x*w+y;
	};
	UnionFind uf(h*w);
	rep(i,h) {
		rep(j,w) {
			if(g[i][j]-'0'!=clar[i%4][j%4]) continue;
			rep(dx,2) rep(dy,2) {
				debug(dx,dy);
				if(dx==0 && dy==0) continue;
				if(dx*dy!=0) continue;
				if(i+dx>=h || j+dy>=w) continue;
				if(g[i+dx][j+dy]-'0'!=clar[(i+dx)%4][(j+dy)%4]) continue;
				debug(enc(i,j),enc(j+dx,j+dy));
				uf.unite(enc(i,j),enc(i+dx,j+dy));
			}
		}
	}
	debug(uf.par);
	if(uf.find(0)==uf.find(h*w-1)) print("YES");
	else print("NO");

	return 0;
}

