import qualified Data.Text as T
import qualified Data.Text.IO as T
import qualified Data.Text.Read as T
import qualified Data.Vector.Unboxed as VU
import Data.Bool (bool)

main :: IO ()
main = do
  (h : w : _) <- map unsafeTextToInt . T.words <$> T.getLine :: IO [Int]
  let
    inRange :: (Int, Int) -> Bool
    inRange (i, j) = 0 <= i && i < h && 0 <= j && j < w
    decode :: Int -> (Int, Int)
    decode ij = (i, j)
      where
        (i, j) = ij `divMod` w
    encode :: (Int, Int) -> Int
    encode (i, j) = w * i + j
    candidates :: (Int, Int) -> [(Int, Int)]
    candidates (i, j) = filter inRange [(pred i, j), (succ i, j), (i, pred j), (i, succ j)]
  sijs <- sequence . replicate h $ map (== '.') <$> getLine :: IO [[Bool]]
  let
    sijs' :: VU.Vector Bool
    sijs' = VU.fromList . concat $ sijs
    true :: (Int, Int) -> Bool
    true = (sijs' VU.!) . encode
    ds0 :: VU.Vector Int
    ds0 = VU.map (bool 0 (h * w)) sijs'
    maxs :: [Int]
    maxs = map (VU.maximum . minsFrom) . filter true $ (,) <$> [0..(pred h)] <*> [0..(pred w)]
    minsFrom :: (Int, Int) -> VU.Vector Int
    minsFrom = \ ij -> VU.accum min ds0 . map ((,) <$> encode . fst <*> snd) . takeWhile ((< h * w) . snd) . fix $ ((ij, 0) :) . walk 1
      where
        walk 0 _ = []
        walk l ((ij, k) : ijks) = ((++) <$> fst <*> flip walk ijks . snd) . ((,) <$> map (flip (,) (succ k)) <*> length) $ cs
          where
            cs = filter ((sijs' VU.!) . encode) . candidates $ ij
  print . maximum $ maxs

fix :: (a -> a) -> a
fix f = x
  where
    x = f x

unsafeTextToInt :: T.Text -> Int
unsafeTextToInt s = case T.signed T.decimal s of
  Right (n, _) -> n
