package main

import "fmt"

type point struct {
	name int
	pos_h int
	pos_w int
	right int
	left int
	up int
	down int
	deep int
}

func pop(p []int) (int, []int) {
	//fmt.Println("pop! name is ", p[0].name)
	return p[0], p[1:]
}

func push(data int, p []int) []int {
	//fmt.Println("push! name is ", data.name)
	//p = append(p, data)
	//fmt.Println(p)
	return append(p,data)
}

func reset(p []point) []point {
	for i := 0 ; i < len(p) ; i++ {
		p[i].deep = 0
	}
	return p
}

func address(p []point, h int, w int) point {
	var re point
	for i := 0 ; i < len(p) ; i++ {
		if p[i].pos_h == h && p[i].pos_w == w {
			re = p[i]
		}
	}
	return re
}

func searchName(n int, p []point) point {
	var c point
	for i := 0 ; i < len(p) ; i++ {
		if p[i].name == n {
			c = p[i]
			break
		}
	}
	return c
}




func main(){
	//var queue []point
	var h,w int
	fmt.Scan(&h,&w)
	route := make([][]bool, h)
	for i := 0 ; i < h ; i++ {
		route[i] = make([]bool, w)
	}
	var d string
	//fmt.Println(route)
	count := 0
	for i := 0 ; i < h ; i++ {
		fmt.Scan(&d)
		for j := 0 ; j < w ; j++ {
			if d[j:j+1] == "." {
				route[i][j] = true
				count++
			}
		}
	}
	//fmt.Println(route)
//	check := make([]int, count)
	data := make([]point,count+1)
	count = 1
	for i := 0 ; i < h ; i++ {
		for j := 0 ; j < w ; j++ {
			if route[i][j] {
				data[count].name = count
				data[count].pos_h = i
				data[count].pos_w = j
				count++
			}
		}
	}
	count--
	for i := 1 ; i <= count ; i++{
		if data[i].pos_h != 0 && route[data[i].pos_h-1][data[i].pos_w] {
			temp := address(data,data[i].pos_h-1,data[i].pos_w)
			data[i].up = temp.name
		}
		if data[i].pos_h != h-1 && route[data[i].pos_h+1][data[i].pos_w] {
			temp := address(data,data[i].pos_h+1,data[i].pos_w)
			data[i].down = temp.name
		}
		if data[i].pos_w != 0 && route[data[i].pos_h][data[i].pos_w-1] {
			temp := address(data,data[i].pos_h,data[i].pos_w-1)
			data[i].left = temp.name
		}
		if data[i].pos_w != w-1 && route[data[i].pos_h][data[i].pos_w+1] {
			temp := address(data,data[i].pos_h,data[i].pos_w+1)
			data[i].right = temp.name
		}
	}
	//fmt.Println(data)

	max := 0
	var leaf int
	var root int
//	var pointer int
	for i := 1 ; i < count ; i++ {
		for j := i+1 ; j <= count ; j++ {
			//fmt.Println("Search now : ",i,j)
			data = reset(data)
//			check = make([]int,count)
			var queue []int
			//data[i] -> data[j] distance
			//BFS
			queue = push(data[i].name , queue) //i
			for ;; {
				//printQueue(queue)
				//fmt.Println(queue)
				//fmt.Println("Search now : ",i,j)
				if len(queue) == 0{
					//fmt.Println("deadrock")
					break
				}
				root,queue = pop(queue)
				//fmt.Println("root name is ", root)
				if root == j {
					//fmt.Println("Distance is ",root.deep)
					if max < data[root].deep {
						max = data[root].deep
					}
					break
				}
				if data[root].up != 0 {
					//checked?
					leaf = data[data[root].up].name
					if leaf != i && data[leaf].deep == 0 {
						data[leaf].deep = data[root].deep + 1
						queue = push(leaf , queue)
					}
				}

				if data[root].right != 0 {
					leaf = data[data[root].right].name
					if leaf != i && data[leaf].deep == 0 {
						data[leaf].deep = data[root].deep + 1
						queue = push(leaf , queue)
					}
				}
				if data[root].down != 0 {
					leaf = data[data[root].down].name
					if leaf != i && data[leaf].deep == 0 {
						data[leaf].deep = data[root].deep + 1
						queue = push(leaf , queue)
					}
				}
				if data[root].left != 0 {
					leaf = data[data[root].left].name
					if leaf != i && data[leaf].deep == 0 {
						data[leaf].deep = data[root].deep + 1
						queue = push(leaf , queue)
					}
				}
			}
		}
	}
	fmt.Println(max)
}


Measured memory_bytes: 6656