#include <bits/stdc++.h>
#include <boost/range/algorithm.hpp>
#include <boost/range/numeric.hpp>
#include <boost/integer/common_factor.hpp>
#include <boost/integer/common_factor_rt.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <boost/multiprecision/cpp_int.hpp>

using std::string;
using std::vector;
using std::set;
using std::multiset;
using std::unordered_set;
using std::map;
using std::multimap;
using std::unordered_map;
using std::pair;
using std::cin;
using std::cout;

using boost::multiprecision::cpp_int;
using cpp_dec_float_1000 = boost::multiprecision::number<boost::multiprecision::cpp_dec_float<1000>>;

typedef uintmax_t    ull;
typedef intmax_t     ll;
typedef uint64_t     ul;
typedef uint32_t     ui;
typedef uint8_t      uc;

constexpr char CRLF  = '\n';
constexpr char SPACE = ' ';
constexpr char VECTOR_COUT_SEPARATOR = SPACE;
constexpr ll INF = 1000'000'007;
constexpr int MOD = 1000'000'007;

template<class T> inline bool chmax(T& a, T b) { if (a < b) { a = b; return true; } return false; }
template<class T> inline bool chmin(T& a, T b) { if (a > b) { a = b; return true; } return false; }
template<class T> std::ostream& operator<< (std::ostream& os, const std::vector<T>& vc) { for(auto it = vc.begin(); it != vc.end(); ++it) { if (std::next(it) == vc.end()) os << *it; else os << *it << VECTOR_COUT_SEPARATOR; } return os; }
template<class T1, class T2> inline std::ostream & operator<< (std::ostream & os, const std::pair<T1, T2> & p) { return os << p.first << ' ' << p.second; }
template<class T> T modinv(T a, T m) { T b = m, u = 1, v = 0; while (b) { T t = a / b; a -= t * b; boost::swap(a, b); u -= t * v; boost::swap(u, v); } u %= m; if (u < 0) u += m; return u; }

struct Tmp
{
public:

  int mH, mW;

  vector<vector<char>> table;
  vector<vector<int>> memo;
  set<int> res;

  const vector<pair<int, int>> dxdy = {
    {-1, 0}, {1, 0},
    {0, 1}, {0, -1}
  };

  Tmp(int H, int W);
  void query(int h, int w);
  void reset();
  void rec(int h, int w, int x);
};

Tmp::Tmp(int H, int W)
  : mH(H)
  , mW(W)
  , table(H, vector<char>(W))
  , memo(H, vector<int>(W, -1))
  , res()
{}

void Tmp::query(int h, int w)
{
  memo[h][w] = 0;
  rec(h, w, 0);
  int ret{};
  for (auto& r : memo) {
    for (auto& c : r) {
      chmax(ret, c);
    }
  }
  res.emplace(ret);
  reset();
}

void Tmp::rec(int h, int w, int x)
{
  if (h < 0 || w < 0 || h >= mH || w >= mW) {
    return;
  }
  if (table[h][w] == '#') {
    return;
  }
  if (memo[h][w] == -1) {
    memo[h][w] = x;
  }
  else {
    if (!chmin(memo[h][w], x)) {
      if (x) {
        return;
      }
    }
  }
  auto r = memo[h][w];

  for (auto d : dxdy) {
    rec(h + d.first, w + d.second, r + 1);
  }
}

void Tmp::reset()
{
  for (auto& row : memo) {
    std::fill(row.begin(), row.end(), -1);
  }
}


void solve(void)
{
  int H, W; cin >> H >> W;
  Tmp t(H, W);
  for (auto& row : t.table) {
    for (auto& cell : row) {
      cin >> cell;
    }
  }
  for (int i = 0; i < H; ++i) {
    for (int j = 0; j < W; ++j) {
      t.query(i, j);
    }
  }
  auto res = t.res.end();
  --res;
  auto ans = *res;
  cout << ans << CRLF;
  return;
}

int main(void)
{
  std::ios_base::sync_with_stdio(false);
  std::cin.tie(0);

  solve();

  return 0;
}


Measured memory_bytes: 256