E=enumerate
F=lambda x,l,c=0,k=0:F(x[1:],l,-~c*(T:=x[0]==l),max(k,c+T))if x else k
def f(n,b):
B=[(x,y)for x,r in E(b)for y,_ in E(r)];Z=lambda z:[(x,y)for x,y in B if z==b[x][y]];D=Z('#');q=Z('.');Q=[q.pop(0)];S=[Q[0]]
for x,y in Q:
for X,Y in[(x+1,y),(x-1,y),(x,y-1),(x,y+1)]:
if(u:=(X,Y))in set(B)-set(S)and'.'==b[X][Y]:Q+=u,;S+=u,
return all((n+~x,n+~y)in D for x,y in D)*(not{*q}-{*S})*all(F(i,'.')>2and n>F(i,'#')for i in b)*all(F(i,'.')>2and n>F(i,'#')for i in zip(*b))
def to_board(s):
n=int(len(s)**.5);return n,[s[i:i+n]for i in range(0,len(s),n)]
TEST = """
`.........`|True|Valid grid
`#..............#`|True|Valid grid
`...#........#...`|True|Valid grid
`...#........#...`|True|Valid grid
`.........`|True|Valid grid
`#...#......#...#`|False|Valid grid
`.........................`|True|Valid grid
`##...#.............#...##`|True|Valid grid
`.................................................`|True|Valid grid
`........................#........................`|True|Valid grid
`....###.....##......##.....##......##.....###....`|True|Valid grid
`................................................................`|True|Valid grid
`##....####....##...........##......##...........##....####....##`|True|Valid grid
`...##.......#...........##.....##.....##...........#.......##...`|True|Valid grid
`#...............`|False|No 180 degree symmetry
`#..##..##..##..#`|False|2-letter entries, filled-in columns
`#........................`|False|No 180 degree symmetry
`.......#...###...#.......`|False|1-letter and 1-letter entries
`######....#....#....#....`|False|No 180 degree symmetry, filled-in column & row
`######...##...##...######`|False|Filled-in columns & rows
`...#......#......#......#......#......#......#...`|False|White squares not contiguous, filled-in column
`.................###....#....###.................`|False|1-letter entries
`...#......#...............##.....................`|False|No 180-degree symmetry
`....#.......#.......#........######........#.......#.......#....`|False|White squares not contiguous
`..#.........#.......#......##......#.......#.......#.........#..`|False|1-letter and 2-letter entries
`.#......#..............................................#......#.`|False|1-letter entries, white squares not contiguous
`...........................##......#............................`|False|No 180-degree symmetry
`####............................................................`|False|No 180-degree symmetry
`#......##......##......##......##......##......##......##......#`|False|Filled-in columns
"""
for i in filter(None, TEST.split('\n')):
a,b,_ = i.split('|')
assert f(*to_board(a[1:-1])) == eval(b)
print('tests passed!')
RT1lbnVtZXJhdGUKRj1sYW1iZGEgeCxsLGM9MCxrPTA6Rih4WzE6XSxsLC1+YyooVDo9eFswXT09bCksbWF4KGssYytUKSlpZiB4IGVsc2UgawpkZWYgZihuLGIpOgogQj1bKHgseSlmb3IgeCxyIGluIEUoYilmb3IgeSxfIGluIEUocildO1o9bGFtYmRhIHo6Wyh4LHkpZm9yIHgseSBpbiBCIGlmIHo9PWJbeF1beV1dO0Q9WignIycpO3E9WignLicpO1E9W3EucG9wKDApXTtTPVtRWzBdXQogZm9yIHgseSBpbiBROgogIGZvciBYLFkgaW5bKHgrMSx5KSwoeC0xLHkpLCh4LHktMSksKHgseSsxKV06CiAgIGlmKHU6PShYLFkpKWluIHNldChCKS1zZXQoUylhbmQnLic9PWJbWF1bWV06USs9dSw7Uys9dSwKIHJldHVybiBhbGwoKG4rfngsbit+eSlpbiBEIGZvciB4LHkgaW4gRCkqKG5vdHsqcX0teypTfSkqYWxsKEYoaSwnLicpPjJhbmQgbj5GKGksJyMnKWZvciBpIGluIGIpKmFsbChGKGksJy4nKT4yYW5kIG4+RihpLCcjJylmb3IgaSBpbiB6aXAoKmIpKQogCmRlZiB0b19ib2FyZChzKToKCW49aW50KGxlbihzKSoqLjUpO3JldHVybiBuLFtzW2k6aStuXWZvciBpIGluIHJhbmdlKDAsbGVuKHMpLG4pXQoKVEVTVCA9ICIiIgpgLi4uLi4uLi4uYHxUcnVlfFZhbGlkIGdyaWQKYCMuLi4uLi4uLi4uLi4uLiNgfFRydWV8VmFsaWQgZ3JpZApgLi4uIy4uLi4uLi4uIy4uLmB8VHJ1ZXxWYWxpZCBncmlkCmAuLi4jLi4uLi4uLi4jLi4uYHxUcnVlfFZhbGlkIGdyaWQKYC4uLi4uLi4uLmB8VHJ1ZXxWYWxpZCBncmlkCmAjLi4uIy4uLi4uLiMuLi4jYHxGYWxzZXxWYWxpZCBncmlkCmAuLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uYHxUcnVlfFZhbGlkIGdyaWQKYCMjLi4uIy4uLi4uLi4uLi4uLi4jLi4uIyNgfFRydWV8VmFsaWQgZ3JpZApgLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLmB8VHJ1ZXxWYWxpZCBncmlkCmAuLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4jLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uYHxUcnVlfFZhbGlkIGdyaWQKYC4uLi4jIyMuLi4uLiMjLi4uLi4uIyMuLi4uLiMjLi4uLi4uIyMuLi4uLiMjIy4uLi5gfFRydWV8VmFsaWQgZ3JpZApgLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLmB8VHJ1ZXxWYWxpZCBncmlkCmAjIy4uLi4jIyMjLi4uLiMjLi4uLi4uLi4uLi4jIy4uLi4uLiMjLi4uLi4uLi4uLi4jIy4uLi4jIyMjLi4uLiMjYHxUcnVlfFZhbGlkIGdyaWQKYC4uLiMjLi4uLi4uLiMuLi4uLi4uLi4uLiMjLi4uLi4jIy4uLi4uIyMuLi4uLi4uLi4uLiMuLi4uLi4uIyMuLi5gfFRydWV8VmFsaWQgZ3JpZApgIy4uLi4uLi4uLi4uLi4uLmB8RmFsc2V8Tm8gMTgwIGRlZ3JlZSBzeW1tZXRyeQpgIy4uIyMuLiMjLi4jIy4uI2B8RmFsc2V8Mi1sZXR0ZXIgZW50cmllcywgZmlsbGVkLWluIGNvbHVtbnMKYCMuLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi5gfEZhbHNlfE5vIDE4MCBkZWdyZWUgc3ltbWV0cnkKYC4uLi4uLi4jLi4uIyMjLi4uIy4uLi4uLi5gfEZhbHNlfDEtbGV0dGVyIGFuZCAxLWxldHRlciBlbnRyaWVzCmAjIyMjIyMuLi4uIy4uLi4jLi4uLiMuLi4uYHxGYWxzZXxObyAxODAgZGVncmVlIHN5bW1ldHJ5LCBmaWxsZWQtaW4gY29sdW1uICYgcm93CmAjIyMjIyMuLi4jIy4uLiMjLi4uIyMjIyMjYHxGYWxzZXxGaWxsZWQtaW4gY29sdW1ucyAmIHJvd3MKYC4uLiMuLi4uLi4jLi4uLi4uIy4uLi4uLiMuLi4uLi4jLi4uLi4uIy4uLi4uLiMuLi5gfEZhbHNlfFdoaXRlIHNxdWFyZXMgbm90IGNvbnRpZ3VvdXMsIGZpbGxlZC1pbiBjb2x1bW4KYC4uLi4uLi4uLi4uLi4uLi4uIyMjLi4uLiMuLi4uIyMjLi4uLi4uLi4uLi4uLi4uLi5gfEZhbHNlfDEtbGV0dGVyIGVudHJpZXMKYC4uLiMuLi4uLi4jLi4uLi4uLi4uLi4uLi4uIyMuLi4uLi4uLi4uLi4uLi4uLi4uLi5gfEZhbHNlfE5vIDE4MC1kZWdyZWUgc3ltbWV0cnkKYC4uLi4jLi4uLi4uLiMuLi4uLi4uIy4uLi4uLi4uIyMjIyMjLi4uLi4uLi4jLi4uLi4uLiMuLi4uLi4uIy4uLi5gfEZhbHNlfFdoaXRlIHNxdWFyZXMgbm90IGNvbnRpZ3VvdXMKYC4uIy4uLi4uLi4uLiMuLi4uLi4uIy4uLi4uLiMjLi4uLi4uIy4uLi4uLi4jLi4uLi4uLiMuLi4uLi4uLi4jLi5gfEZhbHNlfDEtbGV0dGVyIGFuZCAyLWxldHRlciBlbnRyaWVzCmAuIy4uLi4uLiMuLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uIy4uLi4uLiMuYHxGYWxzZXwxLWxldHRlciBlbnRyaWVzLCB3aGl0ZSBzcXVhcmVzIG5vdCBjb250aWd1b3VzCmAuLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4jIy4uLi4uLiMuLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uYHxGYWxzZXxObyAxODAtZGVncmVlIHN5bW1ldHJ5CmAjIyMjLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uYHxGYWxzZXxObyAxODAtZGVncmVlIHN5bW1ldHJ5CmAjLi4uLi4uIyMuLi4uLi4jIy4uLi4uLiMjLi4uLi4uIyMuLi4uLi4jIy4uLi4uLiMjLi4uLi4uIyMuLi4uLi4jYHxGYWxzZXxGaWxsZWQtaW4gY29sdW1ucwoiIiIKCgpmb3IgaSBpbiBmaWx0ZXIoTm9uZSwgVEVTVC5zcGxpdCgnXG4nKSk6CglhLGIsXyA9IGkuc3BsaXQoJ3wnKQoJYXNzZXJ0IGYoKnRvX2JvYXJkKGFbMTotMV0pKSA9PSBldmFsKGIpCgpwcmludCgndGVzdHMgcGFzc2VkIScp