using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BFS2
{
    class Program
    {
        public class DoneTile
        {
            public DoneTile(int[] thisPos,DoneTile from,int order)
            {
                ThisPos = thisPos;
                From = from;
                Order = order;
            }

            public int[] ThisPos { get; }
            public DoneTile From { get; }
            public int Order;
        }

        static void Main(string[] args)
        {
            int H,W;
            char[][] map = ReadMap(out H,out W);

            List<int[]> roadPoses = new List<int[]>();

            //一度選んだstart goal のペア
            //[S[0]][S[1]][G[0]][G[1]] = 0 or 1
            int[][][][] withExperience = new int[H][][][];

            //通れるタイルを探す(と同時にwithEcperienceを初期化)
            for (int i = 0; i < H; i++)
            {
                withExperience[i] = new int[W][][];
                for (int j = 0; j < W; j++)
                {
                    withExperience[i][j] = new int[H][];
                    for (int k = 0; k < H; k++)
                    {
                        withExperience[i][j][k] = new int[W];
                    }
                    if (map[i][j] == '.')
                    {
                        roadPoses.Add(new int[2] { i, j });
                    }
                }
            }



            int[][] deltas = { new int[2] { 0, 1 }, new int[2] { 1, 0 }, new int[2] { 0, -1 }, new int[2] { -1, 0 } };

            DoneTile currentTile = null;


            int maxCount = 0;

            //startとgoalの位置を動かす
            for (int i = 0; i < roadPoses.Count-1; i++)
            {
                for (int j = i+1; j < roadPoses.Count; j++)
                {

                    int[] startPos = new int[3] { roadPoses[i][0], roadPoses[i][1], 0 };
                    int[] goalPos = roadPoses[j];

                    //startとgoalの位置が実験済みなら飛ばす
                    if (withExperience[startPos[0]][startPos[1]][goalPos[0]][goalPos[1]]==0)
                    {

                        Queue<DoneTile> queue = new Queue<DoneTile>();
                        DoneTile startDoneTile = new DoneTile(startPos, null, 0);

                        //計算済みのタイルを記憶する配列(とその初期化)
                        int[][] done = new int[H][];
                        for (int k = 0; k < H; k++)
                        {
                            done[k] = new int[W];
                        }

                        //スタート地点は計算済み
                        done[startDoneTile.ThisPos[0]][startDoneTile.ThisPos[1]] = 1;

                        //スタートタイルから計算を初める
                        queue.Enqueue(startDoneTile);
                        while (true)
                        {
                            currentTile = queue.Dequeue();

                            //ゴールの位置に等しいなら
                            if (currentTile.ThisPos[0] == goalPos[0] && currentTile.ThisPos[1] == goalPos[1])
                            {
                                //スタートまで逆走する
                                //その過程で踏んだタイルは、絶対にmax(答え)にはならない
                                DoneTile returnTile = currentTile;
                                while (returnTile != startDoneTile)
                                {
                                    withExperience[startDoneTile.ThisPos[0]][startDoneTile.ThisPos[1]][returnTile.ThisPos[0]][returnTile.ThisPos[1]] = 1;
                                    returnTile = returnTile.From;
                                }
                                break;
                            }

                            //通れるなら
                            if (map[currentTile.ThisPos[0]][currentTile.ThisPos[1]] == '.')
                            {
                                //この次に議論するタイルを選ぶ
                                foreach (var delta in deltas)
                                {
                                    int[] aroundPos = new int[] { currentTile.ThisPos[0] + delta[0], currentTile.ThisPos[1] + delta[1] };
                                    if (aroundPos[0] >= 0 && aroundPos[0] < H && aroundPos[1] >= 0 && aroundPos[1] < W)
                                    {
                                        if (done[aroundPos[0]][aroundPos[1]] == 0)
                                        {
                                            DoneTile next = new DoneTile(aroundPos, currentTile, currentTile.Order + 1);
                                            queue.Enqueue(next);
                                            done[aroundPos[0]][aroundPos[1]] = 1;
                                        }
                                    }
                                }
                            }
                        }
                    }
                    maxCount = Math.Max(maxCount, currentTile.Order);

                }

            }

            Console.WriteLine(maxCount);
            Console.ReadLine();
        }

        static private char[][] ReadMap(out int H,out int W)
        {
            string[] HW = Console.ReadLine().Split(' ');
            H = int.Parse(HW[0]);
            W = int.Parse(HW[1]);

            char[][] map = new char[H][];
            for (int i = 0; i < H; i++)
            {
                map[i] = Console.ReadLine().ToArray();
            }
            return map;
        }
    }
}


Measured memory_bytes: 21396