Problem:
Score : 400 points

Problem Statement

Takahashi has a maze, which is a grid of H \times W squares with H horizontal rows and W vertical columns.

The square at the i-th row from the top and the j-th column is a "wall" square if S_{ij} is #, and a "road" square if S_{ij} is ..

From a road square, you can move to a horizontally or vertically adjacent road square.

You cannot move out of the maze, move to a wall square, or move diagonally.

Takahashi will choose a starting square and a goal square, which can be any road squares, and give the maze to Aoki.

Aoki will then travel from the starting square to the goal square, in the minimum number of moves required.

In this situation, find the maximum possible number of moves Aoki has to make.

Constraints

1 \leq H,W \leq 20

S_{ij} is . or #.

S contains at least two occurrences of ..

Any road square can be reached from any road square in zero or more moves.

Input

Input is given from Standard Input in the following format:

H W
S_{11}...S_{1W}
:
S_{H1}...S_{HW}

Output

Print the maximum possible number of moves Aoki has to make.

Sample Input 1

3 3
...
...
...

Sample Output 1

4

If Takahashi chooses the top-left square as the starting square and the bottom-right square as the goal square, Aoki has to make four moves.

Sample Input 2

3 5
...#.
.#.#.
.#...

Sample Output 2

10

If Takahashi chooses the bottom-left square as the starting square and the top-right square as the goal square, Aoki has to make ten moves.

Solution in Rust (1.42.0) (judge verdict: Wrong Answer)

#![allow(unused_imports)]
#![allow(clippy::many_single_char_names)]

// language: Rust(1.42.0)
// check available crates on AtCoder at "https://atcoder.jp/contests/language-test-202001"
// My Library Repo' is at "https://github.com/Loptall/prcn_lib"

/*
    6月で水色になるぞ〜!!!
*/

// from std...
use std::cmp::{
    max, min,
    Ordering::{Equal, Greater, Less},
    Reverse,
};
use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque};
use std::mem::swap;
use std::ops::*;
use std::process::exit;
use std::{f32, f64, i128, i16, i32, i64, i8, isize, u128, u16, u32, u64, u8, usize};

// Dep' crates are...
use itertools::*;
use itertools_num::*;
use lazy_static::lazy_static;
use maplit::{btreemap, btreeset, hashmap, hashset};
use num_bigint::{BigInt, BigUint};
use num_complex::Complex;
use num_integer::{binomial, gcd, lcm, Integer};
use num_rational::Rational;
use num_traits::{clamp, one, pow, zero, One, Pow, Zero};
use permutohedron::Heap;
use petgraph::unionfind::UnionFind;
use proconio::{fastout, input, is_stdin_empty, marker::*};
use rand::random;

pub const MOD: usize = 1000000007;
pub const MAX: usize = 1usize << 63usize;
pub const MIN: i64 = i64::MIN;
pub const PI: f64 = f64::consts::PI;
pub const A: u8 = 'A' as u8;

use std::convert::TryInto;

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Idx2D {
    pub x: usize,
    pub y: usize,
    h: usize,
    w: usize,
}

impl Idx2D {
    pub fn new<T: TryInto<isize>>(x: T, y: T, w: usize, h: usize) -> Self {
        let x = x.try_into().ok().unwrap();
        let y = y.try_into().ok().unwrap();

        assert!(x >= 0 && y >= 0);
        assert!(x < w as isize && y < h as isize);

        Idx2D {
            x: x as usize,
            y: y as usize,
            w,
            h,
        }
    }

    pub fn try_new<T: TryInto<isize>>(x: T, y: T, w: usize, h: usize) -> Option<Self> {
        let x = x.try_into().ok()?;
        let y = y.try_into().ok()?;

        if x >= 0 && y >= 0 && x < w as isize && y < h as isize {
            Some(Self {
                x: x as usize,
                y: y as usize,
                w,
                h,
            })
        } else {
            None
        }
    }

    pub fn try_add<T: TryInto<isize>>(&self, rhs: (T, T)) -> Option<Self> {
        let dx = rhs.0.try_into().ok().unwrap();
        let dy = rhs.1.try_into().ok().unwrap();
        let x = self.x as isize + dx;
        let y = self.y as isize + dy;

        Self::try_new(x, y, self.w, self.h)
    }

    pub fn try_sub<T: TryInto<isize>>(&self, rhs: (T, T)) -> Option<Self> {
        let dx = rhs.0.try_into().ok().unwrap();
        let dy = rhs.1.try_into().ok().unwrap();
        let x = self.x as isize - dx;
        let y = self.y as isize - dy;

        Self::try_new(x, y, self.w, self.h)
    }

    pub fn neighber4(&self) -> impl Iterator<Item = Idx2D> {
        const VECT: &[(isize, isize)] = &[(-1, 0), (0, -1), (1, 0), (0, 1)];
        let iter = *self;
        VECT.iter().filter_map(move |u| iter.try_add(u.clone()))
    }

    pub fn neighber8(&self) -> impl Iterator<Item = Idx2D> {
        const VECT: &[(isize, isize)] = &[
            (-1, 0),
            (0, -1),
            (1, 0),
            (0, 1),
            (-1, -1),
            (1, -1),
            (-1, 1),
            (1, 1),
        ];
        let iter = *self;
        VECT.iter().filter_map(move |u| iter.try_add(u.clone()))
    }
}

#[derive(Debug, Clone)]
pub struct Grid<T>(pub Vec<Vec<T>>);

impl<T> Grid<T> {
    pub fn new(b: Vec<Vec<T>>) -> Self {
        Self(b)
    }

    pub fn width(&self) -> usize {
        self.0[0].len()
    }

    pub fn height(&self) -> usize {
        self.0.len()
    }

    pub fn idx(&self, x: usize, y: usize) -> Idx2D {
        Idx2D::new(x, y, self.width(), self.height())
    }

    pub fn get<Ix: TryInto<isize>>(&self, x: Ix, y: Ix) -> Option<&T> {
        let x = x.try_into().ok()?;
        let y = y.try_into().ok()?;
        let w = self.width() as isize;
        let h = self.height() as isize;
        if x >= 0 && y >= 0 && x < w && y < h {
            Some(&self.0[y as usize][x as usize])
        } else {
            None
        }
    }

    pub fn get_mut<Ix: TryInto<isize>>(&mut self, x: Ix, y: Ix) -> Option<&mut T> {
        let x = x.try_into().ok()?;
        let y = y.try_into().ok()?;
        let w = self.width() as isize;
        let h = self.height() as isize;
        if x >= 0 && y >= 0 && x < w && y < h {
            Some(&mut self.0[y as usize][x as usize])
        } else {
            None
        }
    }
}

impl<T: PartialEq> Grid<T> {
    pub fn find(&self, v: T) -> Option<Idx2D> {
        for i in 0..self.height() {
            for j in 0..self.width() {
                if *self.get(j, i).unwrap() == v {
                    return Some(self.idx(i, j));
                }
            }
        }
        None
    }
}

impl<T> Index<Idx2D> for Grid<T> {
    type Output = T;

    fn index(&self, ix: Idx2D) -> &Self::Output {
        &self.0[ix.y][ix.x]
    }
}

impl<T> IndexMut<Idx2D> for Grid<T> {
    fn index_mut(&mut self, ix: Idx2D) -> &mut Self::Output {
        &mut self.0[ix.y][ix.x]
    }
}

pub trait Graph<'a> {
    type NodeId: Copy;
    type Iter: Iterator<Item = Self::NodeId>;
    fn len(&self) -> usize;
    fn is_empty(&self) -> bool;
    fn index(&self, a: Self::NodeId) -> usize;
    fn neighbors(&'a self, a: Self::NodeId) -> Self::Iter;
}
/// 重みなしグラフ
pub type UnweightedGraph = Vec<Vec<usize>>;
impl<'a> Graph<'a> for UnweightedGraph {
    type NodeId = usize;
    type Iter = std::iter::Cloned<std::slice::Iter<'a, Self::NodeId>>;
    fn len(&self) -> usize {
        self.len()
    }
    fn is_empty(&self) -> bool {
        self.len() == 0
    }
    fn index(&self, a: Self::NodeId) -> usize {
        a
    }
    fn neighbors(&'a self, a: Self::NodeId) -> Self::Iter {
        self[a].iter().cloned()
    }
}
/// 重みありグラフ
pub type WeightedVertexGraph<W> = Vec<Vec<(usize, W)>>;
impl<'a, W> Graph<'a> for WeightedVertexGraph<W>
where
    W: std::marker::Copy + Clone + 'a,
{
    type NodeId = (usize, W);
    type Iter = std::iter::Cloned<std::slice::Iter<'a, Self::NodeId>>;
    fn len(&self) -> usize {
        self.len()
    }
    fn is_empty(&self) -> bool {
        self.len() == 0
    }
    fn index(&self, a: Self::NodeId) -> usize {
        a.0
    }
    fn neighbors(&'a self, a: Self::NodeId) -> Self::Iter {
        self[a.0].iter().cloned()
    }
}
/// 重みなし有向グラフ
pub fn make_directed_graph(n: usize, edges: &[(usize, usize)]) -> UnweightedGraph {
    let mut g = vec![vec![]; n];
    for &(u, v) in edges.iter() {
        g[u].push(v);
    }
    g
}
/// 重みなし無向グラフ
pub fn make_undirected_graph(n: usize, edges: &[(usize, usize)]) -> UnweightedGraph {
    let mut g = vec![vec![]; n];
    for &(u, v) in edges.iter() {
        g[u].push(v);
        g[v].push(u);
    }
    g
}
/// 重み付き有向グラフ
pub fn make_weighted_directed_graph<W: Clone>(
    n: usize,
    edges: &[(usize, usize, W)],
) -> WeightedVertexGraph<W> {
    let mut g = vec![vec![]; n];
    for &(u, v, ref w) in edges.iter() {
        g[u].push((v, w.clone()));
    }
    g
}
/// 重み付き無向グラフ
pub fn make_weighted_undirected_graph<W: Clone>(
    n: usize,
    edges: &[(usize, usize, W)],
) -> WeightedVertexGraph<W> {
    let mut g = vec![vec![]; n];
    for &(u, v, ref w) in edges.iter() {
        g[u].push((v, w.clone()));
        g[v].push((u, w.clone()));
    }
    g
}

pub struct Dfs<'a, G: Graph<'a>> {
    visited: Vec<bool>,
    s: Vec<(G::NodeId, Option<G::NodeId>)>,
    g: &'a G,
}
impl<'a, G: Graph<'a>> Iterator for Dfs<'a, G> {
    type Item = (G::NodeId, G::NodeId);
    fn next(&mut self) -> Option<Self::Item> {
        if let Some((u, prev)) = self.s.pop() {
            for v in self.g.neighbors(u) {
                if !self.visited[self.g.index(v)] {
                    self.visited[self.g.index(v)] = true;
                    self.s.push((v, Some(u)));
                }
            }
            if let Some(prev) = prev {
                Some((prev, u))
            } else {
                self.next()
            }
        } else {
            None
        }
    }
}
/// `start`からの深さ優先探索時の、
/// 頂点から頂点の接続をイテレートするIteratorを作る
///
/// ```ignore
/// let e = &[(0, 2), (0, 1), (1, 2), (2, 3), (3, 4), (3, 5), (1, 5)];
///
/// let g = make_undirected_graph(6, e);
///
/// for (f, t) in dfs(&g, 0) {
///     println!("{} -> {}", f, t);
/// }
/// ```
///
/// これは
///
/// `0 -> 1`
///
/// `1 -> 5`
///
/// `5 -> 3`
///
/// `3 -> 4`
///
/// `0 -> 2`
///
/// を表示する
pub fn dfs<'a, G: Graph<'a, NodeId = usize>>(g: &'a G, start: G::NodeId) -> Dfs<'a, G> {
    let n = g.len();
    let mut visited = vec![false; n];
    let mut s = Vec::new();
    visited[start] = true;
    s.push((start, None));
    Dfs { visited, s, g }
}

/// グラフを連結成分ごとに分解する。
/// 返り値`r`は、`r[c][i] = 頂点iはグループcに属する`
pub fn classify_into_connected_group<'a, G: Graph<'a, NodeId = usize>>(
    g: &'a G,
) -> Vec<Vec<usize>> {
    let mut classified = vec![None; g.len()];
    let mut cur = 0usize;
    for u in 0..g.len() {
        match classified[u] {
            Some(_) => {}
            None => {
                classified[u] = Some(cur);
                for (_, t) in dfs(g, u) {
                    classified[t] = Some(cur);
                }
                cur += 1;
            }
        }
    }

    let mut res = vec![Vec::new(); cur];
    for (i, c) in classified.into_iter().map(|x| x.unwrap()).enumerate() {
        res[c].push(i);
    }

    res
}

// code...
// #[fastout]
fn main() {
    input! {
        h: usize, w: usize,
        s: [Chars; h]
    }

    // let mut g = vec![Vec::new(); h * w];
    // for i in 0..h {
    //     for j in 0..w {
    //         if !(i == 0 || s[i - 1][j] == '#') {
    //             g[i * w + j].push((i - 1) * w + j);
    //         }
    //         if !(j == 0 || s[i][j - 1] == '#') {
    //             g[i * w + j].push(i * w + j - 1);
    //         }
    //         if !(i == h - 1 || s[i + 1][j] == '#') {
    //             g[i * w + j].push((i + 1) * w + j);
    //         }
    //         if !(j == w - 1 || s[i][j + 1] == '#') {
    //             g[i * w + j].push(i * w + j - 1);
    //         }
    //     }
    // }

    let mut d = vec![vec![MAX / 2 - 1; h * w]; h * w];
    for i in 0..h * w {
        d[i][i] = 0;
    }

    for i in 0..h {
        for j in 0..w {
            if s[i][j] == '#' {
                continue;
            }
            if !(i == 0 || s[i - 1][j] == '#') {
                d[i * w + j][(i - 1) * w + j] = 1;
                d[(i - 1) * w + j][i * w + j] = 1;
            }
            if !(j == 0 || s[i][j - 1] == '#') {
                d[i * w + j][i * w + (j - 1)] = 1;
                d[i * w + (j - 1)][i * w + j] = 1;
            }
            if !(i == h - 1 || s[i + 1][j] == '#') {
                d[i * w + j][(i + 1) * w + j] = 1;
                d[(i + 1) * w + j][i * w + j] = 1;
            }
            if !(j == w - 1 || s[i][j + 1] == '#') {
                d[i * w + j][i * w + (j + 1)] = 1;
                d[i * w + (j + 1)][i * w + j] = 1;
            }
        }
    }

    for i in 0..h * w {
        for j in 0..h * w {
            for k in 0..h * w {
                if d[i][j] > d[i][k] + d[k][j] {
                    d[i][j] = d[i][k] + d[k][j];
                    d[j][i] = d[i][k] + d[k][j];
                }
            }
        }
    }

    // dbg!(&d);

    // let mut ans = 0usize;
    // for i in 0..h * w {
    //     for j in 0..h * w {
    //         if s[i / w][i % w] != '#' && s[j / w][j % w] != '#' {
    //             ans = max(ans, d[i][j]);
    //         }
    //     }
    // }
    let ans = *d
        .iter()
        .map(|i| i.iter().filter(|x| **x != MAX / 2 - 1).max().unwrap())
        .max()
        .unwrap();
    println!("{}", ans);
}