Problem:
Score: 400 points

Problem statement

We have an H \times W grid whose squares are painted black or white. The square at the i-th row from the top and the j-th column from the left is denoted as (i, j).

Snuke would like to play the following game on this grid. At the beginning of the game, there is a character called Kenus at square (1, 1). The player repeatedly moves Kenus up, down, left or right by one square. The game is completed when Kenus reaches square (H, W) passing only white squares.

Before Snuke starts the game, he can change the color of some of the white squares to black. However, he cannot change the color of square (1, 1) and (H, W). Also, changes of color must all be carried out before the beginning of the game.

When the game is completed, Snuke's score will be the number of times he changed the color of a square before the beginning of the game. Find the maximum possible score that Snuke can achieve, or print -1 if the game cannot be completed, that is, Kenus can never reach square (H, W) regardless of how Snuke changes the color of the squares.

The color of the squares are given to you as characters s_{i, j}. If square (i, j) is initially painted by white, s_{i, j} is .; if square (i, j) is initially painted by black, s_{i, j} is #.

Constraints

H is an integer between 2 and 50 (inclusive).

W is an integer between 2 and 50 (inclusive).

s_{i, j} is . or # (1 \leq i \leq H, 1 \leq j \leq W).

s_{1, 1} and s_{H, W} are ..

Input

Input is given from Standard Input in the following format:

H W
s_{1, 1}s_{1, 2}s_{1, 3} ... s_{1, W}
s_{2, 1}s_{2, 2}s_{2, 3} ... s_{2, W}
: :
s_{H, 1}s_{H, 2}s_{H, 3} ... s_{H, W}

Output

Print the maximum possible score that Snuke can achieve, or print -1 if the game cannot be completed.

Sample Input 1

3 3
..#
#..
...

Sample Output 1

2

The score 2 can be achieved by changing the color of squares as follows:

Sample Input 2

10 37
.....................................
...#...####...####..###...###...###..
..#.#..#...#.##....#...#.#...#.#...#.
..#.#..#...#.#.....#...#.#...#.#...#.
.#...#.#..##.#.....#...#.#.###.#.###.
.#####.####..#.....#...#..##....##...
.#...#.#...#.#.....#...#.#...#.#...#.
.#...#.#...#.##....#...#.#...#.#...#.
.#...#.####...####..###...###...###..
.....................................

Sample Output 2

209

Solution in C++14 (GCC 5.4.1) (judge verdict: Wrong Answer)

////////////////////////////////////////////////////////////////////
// This source code is for Visual Studio
////////////////////////////////////////////////////////////////////
#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <queue>
#include <map>
#include <set>
#include <algorithm>
#include <sstream>
#include <iterator>
#include <stack>
#include <functional>
#include <iomanip>
#include <string>
#include <cstring>
#include <deque>
#include <math.h>
#include <assert.h>

#define	numberof(a)	(sizeof(a) / sizeof(a[0]))
#define	INF		INT32_MAX
#define Rep(i,n) for(int i = 0; i < (n); ++i )

using namespace std;

typedef vector< vector<int> > MAT;
typedef pair<int, int> PINT;
typedef long long ll;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int32_t s32;
typedef int64_t s64;

//constant
//--------------------------------------------
const double EPS = 1e-10;
const double PI  = acos(-1.0);

struct edge
{
	ll from;
	ll to;
	ll cost;

	edge(ll inp_from, ll inp_to, ll inp_cost) {
		from = inp_from;
		to = inp_to;
		cost = inp_cost;
	}

	// コストの大小で順序定義
	bool operator<(const edge& o) const {
		return cost < o.cost;
	}
};

bool edge_comp(const edge& e1, const edge& e2)
{
	return e1.cost < e2.cost;
}

////////////////////
// UnionFind
////////////////////
class UnionFind
{
public:
	vector<int> parent;
	vector<int> rank;

	UnionFind(int n)
	{
		Rep(i, n) {
			parent.push_back(i);
			rank.push_back(1);
		}
	}
	int root(int x)
	{
		if (parent[x] != x) parent[x] = root(parent[x]);
		return parent[x];
	}
	// is same union between x and y
	bool same(int x, int y)
	{
		return root(x) == root(y);
	}
	void unite(int x, int y)
	{
		int rx = root(x);
		int ry = root(y);
		if (rx == ry) return;
		if (rank[rx] > rank[ry]) {
			parent[ry] = rx;
			rank[rx] += rank[ry];
		}
		if (rank[rx] <= rank[ry]) {
			parent[rx] = ry;
			rank[ry] += rank[rx];
		}
	}
};

////////////////////
// kruskal(最小全域木)
////////////////////
vector< edge > shortest_edges;
ll kruskal(vector<edge> & edges, ll V)	// edgesは辺の集合、Vは頂点数
{
	UnionFind uf(V);// Union-Findの初期化
	ll res = 0;

	sort(edges.begin(), edges.end(), edge_comp); // edge.costの小さい順

	for (int i = 0; i < edges.size(); i++)
	{
		edge e = edges[i];
		if (!uf.same(e.from, e.to))
		{
			uf.unite(e.from, e.to);
			res += e.cost;

			// save
			shortest_edges.push_back(e);
		}
	}

	return res;	// 最小の重み
}

////////////////////
// 回文判定 O(N)
////////////////////
bool isPalindrome(string input_str)
{
	bool ret = true;
	ll i = 0;
	ll j = input_str.length() - 1;

	while (ret && i <= j) {
		ret = ret & (input_str[i] == input_str[j]);
		i++;
		j--;
	}
	return ret;
}

////////////////////
// 2分探索 O(logN)
// ※ソートされていることが前提
////////////////////
ll binary_search(ll target, vector<ll> & search_table)
{
	const ll loop_cnt = 1000;	// 要調整
	ll N = search_table.size();
	ll lb = 0;
	ll ub = N;

	for (int i = 0; i < loop_cnt; ++i) {
		ll mid = (lb + ub) / 2;

		if (target == search_table[mid]) {
			return mid;
		}
		else if (target >= search_table[mid]) {
			lb = mid;
		}
		else {
			ub = mid;
		}
	}

	return ub;
}

bool throw_comp(const pair<ll, ll> & p1, const pair<ll, ll> & p2)
{
	// sort(item.begin(), item.end(), throw_comp)のように使う
	return p1.second > p2.second;
}

bool tmp_comp(const pair<ll, ll> & p1, const pair<ll, ll> & p2)
{
	// sort(item.begin(), item.end(), tmp_comp)のように使う
	return ((double)p1.first / p1.second) > ((double)p2.first / p2.second);
}

////////////////////
// しゃくとり法
// 要素数Nの配列A[N]
////////////////////
ll shakutori( ll N, vector<ll> & A )
{
	ll ans = 0;
	ll head = 0;
	ll tail = 0;
	set<ll> memo;

	for (tail = 0; tail < N; tail++) {
		// [head, tail]に同じ数字が2つ以上含まれているときheadを進める
		while (memo.count(A[tail]) != 0) {
			memo.erase(A[head]);
			head++;
		}

		ans = max(ans, tail - head + 1);
		memo.insert(A[tail]);
	}

	return ans;
}

// 数学：		最大公約数、最小公倍数、素因数分解
// 探索：		貪欲法(全探索)、2分探索、ソート
//				BFS(幅優先探索)、DFS(深さ優先探索)
// 探索(発展)：	貪欲法、しゃくとり法、区間累積和
// データ構造：	スタック、キュー、deque
//				ハッシュ、map、リスト
// DP:			桁DP、ビットDP、メモ化再帰
// グラフ:		最短経路(ダイクストラ、ベルマンフォード、ワーシャルフロイト)
//				最小全域木(クラスカル法、プリム法、Union-Find)
//				三角不等式
// その他：		最大フロー、マッチング問題
//				セグメント木、RMQ、BIT、平方分割
// 
typedef pair<ll, ll> iPair;
struct Graph
{
	/*
	example).
	int V = 9;
	Graph g(V);

	//  making above shown graph
	g.addEdge(0, 1, 4);
	g.addEdge(0, 7, 8);
	g.addEdge(1, 2, 8);
	g.addEdge(1, 7, 11);
	g.addEdge(2, 3, 7);
	g.addEdge(2, 8, 2);
	g.addEdge(2, 5, 4);
	g.addEdge(3, 4, 9);
	g.addEdge(3, 5, 14);
	g.addEdge(4, 5, 10);
	g.addEdge(5, 6, 2);
	g.addEdge(6, 7, 1);
	g.addEdge(6, 8, 6);
	g.addEdge(7, 8, 7);

	g.shortestPath(0);// start_node = 0
	*/
	ll V;    // No. of vertices
			  // In a weighted graph, we need to store vertex
			  // and weight pair for every edge
	vector< vector< pair<ll, ll> > > adj;// adjacent list. each element is pair(dist, vertex)

								 // Constructor
	Graph(ll V)
	{
		this->V = V;
		adj.resize(V);
	}

	// function to add an edge to graph
	void addEdge(ll u, ll v, ll weight)
	{
		adj[u].push_back(make_pair(v, weight));
		adj[v].push_back(make_pair(u, weight));
	}

	// prints shortest path from s
	vector<ll> shortestPath(ll src)
	{
		priority_queue< iPair, vector <iPair>, greater<iPair> > pq;

		// initialize all distances as infinite (INF)
		vector<ll> dist(V, INF);

		// Insert source itself in priority queue and initialize
		pq.push(make_pair(0, src));
		dist[src] = 0;

		while (!pq.empty())
		{
			// The first vertex in pair is the minimum distance vertex, extract it from priority queue.
			// (it has to be done this way to keep the vertices sorted distance)
			// (distance must be first item in pair. vertex label is stored in second of pair)
			ll u = pq.top().second;// (dist, vertex)
			pq.pop();

			for (ll i = 0; i < adj[u].size(); ++i)
			{
				// Get vertex label and weight of current adjacent of u.
				ll v = adj[u][i].first;
				ll weight = adj[u][i].second;

				//  If there is shorted path to v through u.
				if (dist[v] > dist[u] + weight)
				{
					dist[v] = dist[u] + weight;
					pq.push(make_pair(dist[v], v));
				}
			}
		}

		//printf("Vertex Distance from Source\n");
		//for (int i = 0; i < V; ++i) printf("%d \t\t %d\n", i, dist[i]);

		return dist;
	}
};

int main()
{
	ll H = 0;
	ll W = 0;
	ll ans = 0;
	ll black = 0;

	cin >> H >> W;
	vector< vector<char> > floor(H, vector<char>(W, '.'));
	Rep(y, H) {
		Rep(x, W) {
			cin >> floor[y][x];
			if ('#' == floor[y][x]) black++;
		}
	}

	vector< vector< pair<ll, ll> > > G(H * W);// (x, y) -> node (y * W + x)
	int dx[] = { 0, 0,  -1, 1 };
	int dy[] = { 1, -1,  0, 0 };
	Graph g(H * W);

	Rep(y, H) {
		Rep(x, W) {
			// check (x, y)
			ll node = y * W + x;

			// search around node
			Rep(k, 4) {
				ll new_x = x + dx[k];
				ll new_y = y + dy[k];
				ll new_node = new_y * W + new_x;
				if (new_x < 0 || new_x >= W) continue;
				if (new_y < 0 || new_y >= H) continue;

				if ('#' == floor[new_y][new_x]) {
					//G[node].push_back(make_pair(new_node, INF));
				}
				else if ('.' == floor[new_y][new_x]) {
					g.addEdge(node, new_node, 1);
				}
			}
		}
	}

	// solve
	// (0, 0) - (W - 1, H - 1)
	vector<ll> result = g.shortestPath(0);// start_node = 0
	if (result[(H - 1) * W + W - 1] == INF) {
		ans = -1;
	}
	else {
		ll black_enable = H * W - result[(H - 1) * W + W - 1] - 1;
		ans = black_enable - black;
	}

	cout << ans << endl;

	return 0;
}