Problem: The Kingdom of Neva is home to two ethnic groups, the Totata and the Tutete. The biggest feature of the Totata tribe is that they eat sweet and sour pork with pineapple. However, the Tutete tribe eats vinegared pork in pineapple. These two peoples couldn't get along with each other, and the Totata and Tutete have been in conflict for hundreds of years.

One day, a petition arrived from two ethnic groups under King Neva. According to it, the Totata tribe wants to build a road connecting the town A and the town B where they live. On the other hand, the Tutete tribe also wants to build a road connecting the town C and the town D where they live.

To prevent the two races from clashing, the roads of the Totata and Tutete cannot be crossed. Also, due to technical restrictions, it is only possible to build a road that connects the two cities in a straight line. In other words, if necessary, instead of connecting city A and city B directly with a road, it would indirectly connect city A and city B via some Totata towns (of course, of the Tutete tribe). Do not go through the city). At that time, the roads connecting the towns of the Totata tribe may intersect. The same applies to town C and town D.

Building a road costs proportional to its length. Therefore, I would like to make the total length of the roads to be constructed as short as possible while satisfying the conditions. Now, what is the minimum length?



Input

NA NB
xA, 1 yA, 1
xA, 2 yA, 2
..
..
..
xA, NA yA, NA
xB, 1 yB, 1
xB, 2 yB, 2
..
..
..
xB, NB yB, NB


The integer NA (2 ≤ NA ≤ 1,000) and the integer NB (2 ≤ NB ≤ 1,000) are written on the first line of the input, separated by blanks. This means that there are NA towns where the Totata tribe lives and NB towns where the Tutete tribe lives in the Kingdom of Neva. In the initial state, no road has been built anywhere.

The following NA line contains the integers xA, i (-10,000 ≤ xA, i ≤ 10,000) and the integers yA, i (-10,000 ≤ yA, i ≤ 10,000), separated by blanks. The geography of the Kingdom of Neva is represented by a two-dimensional Cartesian coordinate plane, and the integers xA, i and yA, i written on the 1 + i line are the position coordinates of the i-th city where the Totata tribe lives (xA, i, yA). , I). (xA, 1, yA, 1) and (xA, 2, yA, 2) are the coordinates of the two cities to be connected.

On the following NB line, the integers xB, i (-10,000 ≤ xB, i ≤ 10,000) and the integers yB, i (-10,000 ≤ yB, i ≤ 10,000) are written separated by blanks. The integers xB, i and yB, i written on the 1 + NA + i line indicate that the position coordinates of the i-th city where the Ytterbium live are (xB, i, yB, i). (xB, 1, yB, 1) and (xB, 2, yB, 2) are the coordinates of the two cities to be connected.

It can be assumed that the coordinates of the two cities are different and that none of the three cities are on the same straight line.

Output

When the road is constructed so as to meet the conditions of the problem statement, output the minimum value of the total length of the road. The "length" here is the Euclidean distance. However, if you cannot build a road that meets the conditions, output -1 instead. The output may contain errors, but the relative error to the true value must be less than 10-9.

Examples

Input

2 2
0 0
1 1
2 0
2 -1


Output

2.414213562373


Input

2 3
4 0
0 0
2 3
2 -2
3 1


Output

-1


Input

5 2
-2 1
1 2
-1 3
-1 5
1 4
0 4
0 0


Output

12.359173603117

Solution (in CPP):

Solution:
////////////////////////////////////////
///  tu3 pro-con template            ///
////////////////////////////////////////
#include <cassert>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <numeric>
#include <functional>
#include <vector>
#include <queue>
#include <string>
#include <complex>
#include <stack>
#include <set>
#include <map>
#include <list>
#include <unordered_map>
#include <unordered_set>
#include <bitset>
#include <regex>
using namespace std;

//// MACRO ////
#define countof(a) (sizeof(a)/sizeof(a[0]))

#define REP(i,n) for (int i = 0; i < (n); i++)
#define RREP(i,n) for (int i = (n)-1; i >= 0; i--)
#define FOR(i,s,n) for (int i = (s); i < (n); i++)
#define RFOR(i,s,n) for (int i = (n)-1; i >= (s); i--)
#define pos(c,i) c.being() + (i)
#define allof(c) c.begin(), c.end()
#define aallof(a) a, countof(a)
#define partof(c,i,n) c.begin() + (i), c.begin() + (i) + (n)
#define apartof(a,i,n) a + (i), a + (i) + (n)
#define long long long

#define EPS 1e-9
#define INF (1L << 30)
#define LINF (1LL << 60)

#define PREDICATE(t,a) (const t & a) -> bool
#define COMPARISON_T(t) bool(*)(const t &, const t &)
#define COMPARISON(t,a,b) (const t & a, const t & b) -> bool
#define CONVERTER(TSrc,t,TDest) (const TSrc &t)->TDest

//// i/o helper ////

struct _Reader { template <class T> _Reader operator ,(T &rhs) { cin >> rhs; return *this; } };
struct _Writer { bool f; _Writer() : f(false) { } template <class T> _Writer operator ,(const T &rhs) { cout << (f ? " " : "") << rhs; f = true; return *this; } };
#define READ(t,...) t __VA_ARGS__; _Reader(), __VA_ARGS__
#define WRITE(...) _Writer(), __VA_ARGS__; cout << endl
inline int sign(double x) { return (abs(x) < EPS ? 0 : x > 0 ? 1 : -1); }

template <class T> struct vevector : public vector<vector<T>> { vevector(int n = 0, int m = 0, const T &initial = T()) : vector<vector<T>>(n, vector<T>(m, initial)) { } };
template <class T> struct vevevector : public vector<vevector<T>> { vevevector(int n = 0, int m = 0, int l = 0, const T &initial = T()) : vector<vevector<T>>(n, vevector<T>(m, l, initial)) { } };
template <class T> struct vevevevector : public vector<vevevector<T>> { vevevevector(int n = 0, int m = 0, int l = 0, int k = 0, const T &initial = T()) : vector<vevevector<T>>(n, vevevector<T>(m, l, k, initial)) { } };
template <class T> T read() { T t; cin >> t; return t; }
template <class T> vector<T> read(int n) { vector<T> v; REP(i, n) { v.push_back(read<T>()); } return v; }
template <class T> vevector<T> read(int n, int m) { vevector<T> v; REP(i, n) v.push_back(read<T>(m)); return v; }
template <class T> vevector<T> readjag() { return read<T>(read<int>()); }
template <class T> vevector<T> readjag(int n) { vevector<T> v; REP(i, n) v.push_back(readjag<T>()); return v; }

template <class T1, class T2> inline istream & operator >> (istream & in, pair<T1, T2> &p) { in >> p.first >> p.second; return in; }
template <class T1, class T2> inline ostream & operator << (ostream &out, pair<T1, T2> &p) { out << p.first << p.second; return out; }
template <class T> inline ostream & operator << (ostream &out, vector<T> &v)
{
	ostringstream ss;
	for (auto x : v) ss << x << ' ';
	auto s = ss.str();
	out << s.substr(0, s.length() - 1) << endl;
	return out;
}

//// start up ////
void solve();
int main()
{
	solve();
	return 0;
}

//// bit ////
#ifdef _MSC_VER
#pragma push_macro("long")
#undef long
inline unsigned __builtin_ctz(unsigned x) { unsigned long r; _BitScanReverse(&r, x); return r; }
inline unsigned __builtin_clz(unsigned x) { unsigned long r; _BitScanForward(&r, x); return r; }
inline unsigned msb(unsigned v) { v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; return (v >> 1) + 1; }
inline unsigned round_up_pow2(unsigned v) { return msb(v - 1) << 1; }
inline unsigned msb_index(unsigned v) { static const int pos[32] = { 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 }; return pos[(v * 0x077CB531UL) >> 27]; }
#pragma pop_macro("long")
#endif

inline int next_bit_permutation(int x)
{
	int t = x | (x - 1);
	return (t + 1) | (unsigned)((~t & -~t) - 1) >> (__builtin_ctz(x) + 1);
}

// x の p1からlen bitと p2 から len bit を入れ替える
template<class T>
int bit_swap(T x, size_t p1, size_t p2, size_t len)
{
	T mask = (1ul << len) - 1;
	T ope = (x >> p1 ^ x >> p2) & mask; // 入れ替える場所のxorを求める
	return x ^ (ope << p1 | ope << p2); // 入れ替える場所をxor。
}

//// algorithm ////

// compare
template <class InIt1, class InIt2>
int partial_compare(InIt1 first1, InIt1 last1, InIt2 first2, InIt2 last2)
{
	return lexicographical_compare(first1, last1, first2, last2) ? -1
		: lexicographical_compare(first2, last2, first1, last1) ? 1
		: 0;
}

// index_of
template <class InIt, class T>
inline int index_of(InIt first1, InIt last1, const T &val)
{
	InIt f = find(first1, last1, val);
	return f != last1 ? distance(f, first1) : -1;
}

// C# BinarySearch風のindex_of
template <class InIt, class T>
inline int bindex_of(InIt first1, InIt last1, const T &val)
{
	auto it = lower_bound(first1, last1, val);
	if (it != last1 && *it == val) { return distance(it, first1); }
	return ~distance(it, first1);
}

// iota vector 
vector<int> iotav(int begin, int end)
{
	vector<int> r(end - begin);
	iota(allof(r), begin);
	return r;
}

// iota イテレータ
struct iotait
{
	int n;
	iotait(int n = 0) : n(n) { }
	iotait &operator ++() { ++n; return *this; }
	int operator *() { return n; }
};

// 文字列の置換
void inplaceReplace(string &target, const string &from, const string &to, bool global = false)
{
	do
	{
		string::size_type pos = target.find(from);
		if (pos == target.npos) { break; }
		target.replace(pos, from.length(), to);
	} while (true);
}

// 範囲内チェック
inline bool contains(int x, int min, int max) { return x >= min && x < max; }
inline bool contains(int x, int y, int W, int H) { return x >= 0 && x < W && y >= 0 && y < H; }

//// data structure ////

/// 2分セグメント木。RMQとかが解ける。
template<class T = int, class TParentFunc = const T& (*)(const T&, const T&)>
struct SegTree
{
	int size;
	vector<T> tree;
	TParentFunc parent;
	unsigned msb(unsigned v) { REP(i, 5) v |= v >> (1 << i); return (v >> 1) + 1; }

	SegTree(int count, TParentFunc parentBuilder = std::min, T initialFill = INF)
		: size(msb(count - 1) << 1)
		, tree(size * 2)
		, parent(parentBuilder)
	{
		fill(partof(tree, size, size), initialFill);
		rebuild();
	}

	// []を使うと更新されないのでrebuildすること。数が少なければupdateの方がよい。
	T& operator[](int i) { return tree[i + size]; }
	void rebuild() { RREP(i, size) { tree[i] = parent(tree[i * 2], tree[i * 2 + 1]); } }

	void update(int index, T value)
	{
		tree[index + size] = value;
		for (int i = (index + size) / 2; i > 0; i /= 2)
			tree[i] = parent(tree[i * 2], tree[i * 2 + 1]);
	}

	T query(int s, int e, int i, int l, int r, T value)
	{
		if (r <= s || e <= l) return value;
		if (s <= l && r <= e) return parent(value, tree[i]);
		value = query(s, e, i * 2, l, (l + r) / 2, value);
		value = query(s, e, i * 2 + 1, (l + r) / 2, r, value);
		return value;
	}

	T query(int start, int count, T initialValue)
	{
		return query(start, start + count, 1, 0, size, initialValue);
	}
};


//// number ////

// 最大公約数
long gcd(long a, long b) { return b ? gcd(b, a%b) : a; }

// 最小公倍数
long lcm(long a, long b) { return a / gcd(a, b) * b; }

// 拡張ユークリッド互除法
void exgcd(long a, long b, long& x, long& y)
{
	if (b != 0) { exgcd(b, a%b, y, x); y -= a / b*x; }
	else { x = 1; y = 0; }
}

// mを法としたaの逆元を求める。mが素数なら、a**(m-2)がaの逆元。
long modInv(long a, long mod) { long x, y; exgcd(a, mod, x, y); return x; }

// modInvのvector版
vector<int> modInvVector(int n, int mod)
{
	vector<int> ret(n + 1);
	ret[1] = 1;
	for (int i = 2; i <= n; ++i)
		ret[i] = ret[mod % i] * (mod - mod / i) % mod;
	return ret;
}

// エラトステネス
vector<unsigned char> isPrime;
vector<int> primes;
void initPrimes(int n)
{
	isPrime = vector<unsigned char>(n + 1, true);
	isPrime[0] = isPrime[1] = false;
	FOR(i, 2, n + 1)
	{
		if (!isPrime[i]) continue;
		primes.push_back(i);
		for (int j = i * 2; j <= n; j += i)
			isPrime[j] = false;
	}
}

// パスカルの三角形(二項定理) 2種類の並べ替えにつかう。
vector<vector<double>> makePascalTriangle(int n, bool probability = false)
{
	typedef vector<double> VD;
	vector<VD> t;
	if (!t.size()) { t.push_back(VD(1, 1)); }
	FOR(i, t.size(), n + 1)
	{
		t.push_back(VD(i + 1));
		REP(j, i)
		{
			double x = t[i - 1][j] * (probability ? 0.5 : 1);
			t[i][j] += x;
			t[i][j + 1] += x;
		}
	}
	return t;
}

//// geometry ////
/// 3次元
struct P3
{
	double x, y, z;
	P3(double x = 0, double y = 0, double z = 0) : x(x), y(y), z(z) { }
	P3 operator +() const { return *this; }
	P3 operator +(const P3 &_) const { return P3(x + _.x, y + _.y, z + _.z); }
	P3 operator -() const { return P3(-x, -y, -z); }
	P3 operator -(const P3 &_) const { return *this + -_; }
	P3 operator *(double _) const { return P3(x*_, y*_, z*_); }
	P3 operator /(double _) const { return P3(x / _, y / _, z / _); }
	double dot(const P3 &_) const { return x*_.x + y*_.y + z*_.z; } // 内積
	P3 cross(const P3 &_) const { return P3(y*_.z - z*_.y, z*_.x - x*_.z, x*_.y - y*_.x); } // 外積
	double sqlength() const { return x*x + y*y + z*z; } // 二乗長さ
	double length() const { return sqrt(sqlength()); } // 長さ
	P3 direction() const { return *this / length(); } // 方向ベクトル
};
inline istream & operator>>(istream & in, P3 & p) { in >> p.x >> p.y >> p.z; return in; }
inline double abs(P3 p) { return p.length(); }

struct Sphere
{
	P3 c;
	double r;
	Sphere(double x, double y, double z, double r) : c(x, y, z), r(r) { }
	Sphere(P3 c, double r) : c(c), r(r) { }
	bool IntersectWith(const Sphere &rhs) const { return abs(c - rhs.c) - (r + rhs.r) < EPS; } // 接してても真。
};
inline istream & operator>>(istream & in, Sphere & c) { in >> c.c >> c.r; return in; }

/// 2次元
struct P2
{
	double x, y;
	P2(double x = 0, double y = 0) : x(x), y(y) { }
	P2(complex<double> c) : x(c.real()), y(c.imag()) { }
	P2 operator +() const { return *this; }
	P2 operator +(const P2 &_) const { return P2(x + _.x, y + _.y); }
	P2 operator -() const { return P2(-x, -y); }
	P2 operator -(const P2 &_) const { return *this + -_; }
	P2 operator *(double _) const { return P2(x*_, y*_); }
	P2 operator /(double _) const { return P2(x / _, y / _); }
	double dot(const P2 &_) const { return x*_.x + y*_.y; } // 内積
	double cross(const P2 &_) const { return x*_.y - y*_.x; } // 外積
	double sqlength() const { return x*x + y*y; } // 二乗長さ
	double length() const { return sqrt(sqlength()); } // 長さ
	P2 orthogonal() const { return P2(y, -x); }
	P2 direction() const { return *this / length(); } // 方向ベクトル
};
inline istream & operator>>(istream & in, P2 & p) { in >> p.x >> p.y; return in; }
inline ostream & operator<<(ostream & out, const P2 & p) { out << p.x << ' ' << p.y; return out; }
inline double abs(P2 p2) { return p2.length(); }
inline P2 orthogonal(P2 p) { return p.orthogonal(); }
inline complex<double> orthogonal(complex<double> c) { return  c * complex<double>(0, 1); }

// a,b から ちょうど d だけ離れた点。aとbを円周に持つ円の半径。
inline pair<P2, P2> get_same_distance_points(P2 a, P2 b, double d)
{
	assert(abs(a - b) <= 2 * d + EPS);
	auto v = (a + b) / 2.0 - a; // a から aとbの中点
	auto vl = abs(v);
	auto wl = sqrt(d*d - vl*vl); // 直行Vの大きさ
	auto w = orthogonal(v) * (wl / vl); // 直行V
	return make_pair(a + v + w, a + v - w);
}


struct Circle
{
	P2 c;
	double r;
	Circle() : c(), r() { }
	Circle(double x, double y, double r) : c(x, y), r(r) { }
	Circle(P2 c, double r) : c(c), r(r) { }
	bool IntersectWith(const Circle &rhs) const { return abs(c - rhs.c) - (r + rhs.r) < EPS; } // 接してても真。
	bool Contains(const P2 &p) const { return abs(p - c) - r < EPS; } // 接してても真。
};
inline istream & operator>>(istream & in, Circle & c) { in >> c.c >> c.r; return in; }

// 交差している2円の交点を求める。角度の小さい方から出る(右下方向が正なら時計回り)
inline pair<P2, P2> crossPoint(Circle A, Circle B)
{
	P2 v = B.c - A.c;
	P2 dir = v.direction(); // 他方の中心への方向
	double d = v.length(); // 他方の中心への距離
	double lh = (A.r*A.r + d*d - B.r*B.r) / (2 * d); // 垂線の足までの距離
	P2 h = A.c + dir * lh; // 垂線の足
	double lp = sqrt(A.r*A.r - lh*lh); // 垂線の足から交点までの距離
	P2 p = dir.orthogonal() * lp; // 垂線の足から交点へのベクトル
	return make_pair(h + p, h - p); // 交点の組。
}

inline int clockwise(P2 a, P2 b, P2 c)
{
	const P2 u = b - a, v = c - a;
	if (u.cross(v) > EPS) { return 1; }
	if (u.cross(v) < -EPS) { return -1; }
	if (u.dot(v) < -EPS) { return -1; }
	if (abs(u) - abs(v) < EPS) { return 1; }
	return 0;
}

struct Line
{
	P2 p, d;
	explicit Line(P2 pos = P2(), P2 dir = P2()) : p(pos), d(dir) { }
	static Line FromPD(P2 pos, P2 dir) { return Line(pos, dir); }
	static Line From2Point(P2 a, P2 b) { return Line(a, b - a); }
};

struct LineSeg
{
	P2 p, d;
	explicit LineSeg(P2 pos = P2(), P2 dir = P2()) : p(pos), d(dir) { }
	static LineSeg FromPD(P2 pos, P2 dir) { return LineSeg(pos, dir); }
	static LineSeg From2Point(P2 a, P2 b) { return LineSeg(a, b - a); }
};


inline P2 crossPoint(Line a, Line b) { return a.p + a.d * (b.d.cross(b.p - a.p) / b.d.cross(a.d)); }
inline bool contains(LineSeg a, P2 p) { return abs(a.p - p) + abs(a.p + a.d - p) - abs(a.d) < EPS; }

inline bool isCross(Line a, Line b) { return abs(a.d.cross(b.d)) > EPS; }
inline bool isCross(Line a, LineSeg b) { return sign(a.d.cross(b.p - a.p)) * sign(a.d.cross(b.p + b.d - a.d)) <= 0; }
inline bool isCross(LineSeg a, Line b) { return isCross(b, a); }
inline bool isCross(LineSeg a, LineSeg b)
{
	P2 ae = a.p + a.d, be = b.p + b.d;
	return clockwise(a.p, ae, b.p) * clockwise(a.p, ae, be) <= 0
		&& clockwise(b.p, be, a.p) * clockwise(b.p, be, ae) <= 0;
}

inline bool isOverlap(Line a, Line b) { return abs(a.d.cross(b.p - a.p)) < EPS; }
inline bool isOverlap(Line a, LineSeg b) { return isOverlap(a, Line(b.p, b.d)); }
inline bool isOverlap(LineSeg a, Line b) { return isOverlap(b, a); }
inline bool isOverlap(LineSeg a, LineSeg b)
{
	return contains(a, b.p) || contains(a, b.p + b.d)
		|| contains(b, a.p) || contains(b, a.p + a.d);
}


//// graph ////

template <class TCost = double>
struct Path
{
	typedef TCost COST;
	int from;
	int to;
	TCost cost;
	Path(int from = 0, int to = 0, TCost cost = 0)
		: from(from), to(to), cost(cost) { }
	bool operator < (const Path &rhs) const { return cost < rhs.cost; }
	bool operator >(const Path &rhs) const { return cost > rhs.cost; }
};

// prim //
template <class COST>
pair<COST, vector<int>> prim(const vector<vector<COST>> &costTable)
{
	typedef Path<COST> P;
	int N = costTable.size();
	priority_queue<P, vector<P>, greater<P>> q;
	q.push(P(0, 0, 0));
	vector<int> parent(N, -1);
	COST totalCost = 0;
	while (!q.empty())
	{
		P cur = q.top(); q.pop();
		int i = cur.to;
		if (parent[i] != -1) continue;
		parent[i] = cur.from;
		totalCost += cur.cost;
		REP(j, N) if (parent[j] == -1) q.push(P(i, j, costTable[i][j]));
	}
	return make_pair(totalCost, parent);
}

// dijkstra //
template <class COST>
pair<vector<COST>, vector<int>> dijkstra(const vector<vector<Path<COST>>> &routes, int start = 0, int goal = -1)
{
	typedef Path<COST> P;
	int N = routes.size();
	priority_queue<P, vector<P>, greater<P>> q;
	q.push(P(start, start, 0));

	vector<int> prev(N, -1);
	vector<COST> cost(N, INF);
	while (!q.empty())
	{
		P cur = q.top(); q.pop();
		int i = cur.to;
		if (prev[i] != -1) continue;
		prev[i] = cur.from;
		cost[i] = cur.cost;
		if (i == goal) { break; }
		REP(j, routes[i].size())
		{
			P next = P(i, routes[i][j].to, cur.cost + routes[i][j].cost);
			if (prev[i] != -1)
				q.push(next);
		}
	}
	return make_pair(cost, prev);
}

// warshall_floyd //
template <class COST>
void warshall_floyd(vector<vector<COST>> &cost)
{
	int n = cost.size();
	REP(k, n) REP(i, n) REP(j, n) cost[i][j] = min(cost[i][j], cost[i][k] + cost[k][j]);
}


//// dice ////

// さいころ
template<class T>
struct Die
{
	/* v as 天一地六東五西二南三北四
	|3|
	|1|0|4|5|
	|2|
	*/
	T v[6];

	void rt(int x, int y, int z, int w) { swap(v[x], v[y]); swap(v[x], v[z]); swap(v[x], v[w]); }
	void rotateLeft() { rt(0, 4, 5, 1); }
	void rotateRight() { rt(1, 5, 4, 0); }
	void rotateDown() { rt(0, 3, 5, 2); }
	void rotateUp() { rt(2, 5, 3, 0); }
	void rotateCw() { rt(3, 1, 2, 4); }
	void rotateCcw() { rt(4, 2, 1, 3); }
	bool operator < (const Die &rhs) const { return lexicographical_compare(aallof(v), aallof(rhs.v)); }
	bool operator == (const Die &rhs) const { return equal(aallof(v), rhs.v); }
	bool operator != (const Die &rhs) const { return !equal(aallof(v), rhs.v); }
};

// 8面体
template<class T>
struct Octahedra
{
	/* v as
	/0\/1\/2\/3\
	\4/\5/\6/\7/
	*/
	T v[8];

	void rt(int x, int y, int z, int w) { swap(v[x], v[y]); swap(v[x], v[z]); swap(v[x], v[w]); }
	void rotateLeft() { rt(0, 1, 2, 3); rt(4, 5, 6, 7); }
	void rotateRight() { rt(3, 2, 1, 0); rt(7, 6, 5, 4); }
	void rotateDown() { rt(0, 4, 7, 3); rt(1, 5, 6, 2); }
	void rotateUp() { rt(3, 7, 4, 0); rt(2, 6, 5, 2); }
	void rotateCw() { rt(0, 1, 5, 4); rt(3, 2, 6, 7); }
	void rotateCcw() { rt(4, 5, 1, 0); rt(7, 6, 1, 3); }
	bool operator < (const Octahedra &rhs) const { return lexicographical_compare(aallof(v), aallof(rhs.v)); }
	bool operator == (const Octahedra &rhs) const { return equal(aallof(v), rhs.v); }
	bool operator != (const Octahedra &rhs) const { return !equal(aallof(v), rhs.v); }
};


////////////////////
/// template end ///
////////////////////

void solve()
{
	int testcases = 1;
	REP(testcase, testcases)
	{
		READ(int, Na, Nb);
		auto Pa = read<P2>(Na);
		auto Pb = read<P2>(Nb);
		LineSeg ASeg = LineSeg::From2Point(Pa[0], Pa[1]);
		LineSeg BSeg = LineSeg::From2Point(Pb[0], Pb[1]);

		if (!isCross(ASeg, BSeg))
		{
			printf("%.15f", ASeg.d.length() + BSeg.d.length());
		}
		else
		{
			vevector<Path<double> > Ga(Na);
			vevector<Path<double> > Gb(Nb);
			
			REP(i, Na) REP(j, Na)
			{
				LineSeg s = LineSeg::From2Point(Pa[i], Pa[j]);
				if (!isCross(BSeg, s)) Ga[i].push_back({ i, j, s.d.length() });
			}
			
			REP(i, Nb) REP(j, Nb)
			{
				LineSeg s = LineSeg::From2Point(Pb[i], Pb[j]);
				if (!isCross(ASeg, s)) Gb[i].push_back({ i, j, s.d.length() });
			}

			double aconn = ASeg.d.length() + dijkstra(Gb, 0, 1).first[1];
			double bconn = BSeg.d.length() + dijkstra(Ga, 0, 1).first[1];
			double ans = min(aconn, bconn);
			if (ans > INF) { WRITE(-1); }
			else { printf("%.15f", ans); }
		}
	}
}