use std::ops::Sub;
use std::collections::BinaryHeap;
use std::fmt::Debug;

fn read_line() -> String {
    let mut buffer = String::new();
    std::io::stdin().read_line(&mut buffer).expect("No Line");
    buffer
}
fn read_lines<R, T: Fn(&str)->R> (count: usize, transformer: T) -> Vec<R> {
    let mut buffer = String::new();
    let mut vec = Vec::with_capacity(count);
    for _ in 0 .. count {
        std::io::stdin().read_line(&mut buffer).expect("No Line");
        vec.push(transformer(buffer.trim()));
        buffer.clear();
    }
    vec
}
fn read_value<T: std::str::FromStr>() -> T {
    read_line().trim().parse().ok().unwrap()
}
fn read_values<T: std::str::FromStr>() -> Vec<T> {
    read_line().trim().split(' ').map(|x| x.parse().ok().expect("Can't Parse")).collect::<Vec<T>>()
}
fn read_tabulate<T: std::str::FromStr>(count: usize) -> Vec<T> {
    let mut buffer = String::new();
    let mut vec = Vec::with_capacity(count);
    for _ in 0 .. count {
        std::io::stdin().read_line(&mut buffer).expect("No Line");
        vec.push(buffer.trim().parse().ok().expect("Can't Parse"));
        buffer.clear();
    }
    vec
}
macro_rules! define {
    () => {};
    ($($token:tt)*) => {
        let mut iter = read_values::<String>().into_iter();
        define_iter!(iter, $($token)*);
    };
}
macro_rules! define_iter {
    ($iter:expr) =>{};
    ($iter:expr,) =>{};
    ($iter:expr, $id:ident:$t:tt $($tail:tt)*) => {
        let $id: $t = $iter.next().unwrap().parse().ok().expect("Can't Parse");
        define_iter!($iter, $($tail)*);
    };
    ($iter:expr, mut $id:ident:$t:tt $($tail:tt)*) => {
        let mut $id: $t = $iter.next().unwrap().parse().ok().expect("Can't Parse");
        define_iter!($iter, $($tail)*)
    };
}
macro_rules! freeze {
    ($($id:ident), *) => {
        $(let $id = $id;)*
    };
}
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
struct Point {
    x: i32, y: i32
}
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
struct Vector {
    dx: i32, dy: i32
}
impl Sub for Point {
    type Output = Vector;
    fn sub(self, rhs: Self) -> Self::Output {
        Vector{dx: self.x - rhs.x, dy: self.y - rhs.y}
    }
}
impl Point {
    fn distance(&self, rhs: &Point) -> f64 {
        self.sub(*rhs).length()
    }
}
impl Vector {
    fn length(&self) -> f64 {
        ((self.dx * self.dx + self.dy * self.dy) as f64).sqrt()
    }
    fn cross(&self, that: &Vector) -> i32 {
        self.dx * that.dy - self.dy * that.dx 
    }
    fn dot(&self, that: &Vector) -> i32 {
        self.dx * that.dx + self.dy * that.dy
    }
}
#[derive(Copy, Clone, Default, Debug)]
struct Line {
    end_a: Point, end_b: Point
}
impl Line {
    fn is_cross(&self, that: &Self) -> bool {
        self.separate_by_line(that.end_a, that.end_b) && 
            that.separate_by_line(self.end_a, self.end_b)
    }
    fn separate_by_line(&self, c: Point, d: Point) -> bool {
        let ab = self.end_a - self.end_b;
        let cb = c - self.end_b;
        let db = d - self.end_b;
        ab.cross(&cb).signum() * ab.cross(&db).signum() <= 0
    }
}
struct KeyValue<K, V> {
    key: K,
    value: V
}
impl <K: PartialOrd, V> PartialEq for KeyValue<K, V> {
    fn eq(&self, other: &Self) -> bool {
        self.key.eq(&other.key)
    }
}
impl <K: PartialOrd, V> Eq for KeyValue<K, V> {}
impl <K: PartialOrd, V> PartialOrd for KeyValue<K, V> {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        self.key.partial_cmp(&other.key)
    }
}
impl <K: PartialOrd, V> Ord for KeyValue<K, V> {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.key.partial_cmp(&other.key).ok_or("Can't Compare").unwrap()
    }
} 
#[derive(PartialEq, Eq, Debug, Copy, Clone, Default, Hash)]
struct Reverse<T>(T);
impl <T: PartialOrd> PartialOrd for Reverse<T> {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        other.0.partial_cmp(&self.0)
    }
}
impl <T: Ord> Ord for Reverse<T> {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        other.0.cmp(&self.0)
    }
}

fn min_length(points: &Vec<Point>, barrier: Line) -> Option<f64> {
    let max = 1e100;
    let mut min_len = vec![max; points.len()];
    let mut heap = BinaryHeap::new();
    min_len[0] = 0f64;
    heap.push(Reverse(KeyValue{key: 0f64, value: 0}));
    while let Some(Reverse(KeyValue{key: cost, value: current})) = heap.pop() {
        if min_len[current] > cost {continue;}
        for next in 0usize .. points.len() {
            if next != current {
                let line = Line{ end_a: points[current], end_b: points[next] };
                if line.is_cross(&barrier) {continue;}
                let len = points[current].distance(&points[next]);
                if min_len[next] > len + cost {
                    min_len[next] = len + cost;
                    heap.push(Reverse(KeyValue{key: len + cost, value: next}));
                }
            }
        }
    }
    if min_len[1] != max {
        Some(min_len[1] + barrier.end_a.distance(&barrier.end_b))
    }else {
        None
    }
}
fn main() {
    define! {
        na: usize
        nb: usize
    }
    let tutete = (0 .. na).map(|_| {
        define!{
            x: i32
            y: i32
        }
        Point{x, y}
    }).collect::<Vec<_>>();
    let totata = (0 .. nb).map(|_| {
        define!{
            x: i32
            y: i32
        }
        Point{x, y}
    }).collect::<Vec<_>>();
    let for_tutete = min_length(&tutete, Line{end_a: totata[0], end_b: totata[1]});
    let for_totata = min_length(&totata, Line{end_a: tutete[0], end_b: tutete[1]});
    if let Some(a) = for_tutete {
        if let Some(b) = for_totata {
            println!("{:.15}", if a < b { a } else { b });
        }else {
            println!("{:.15}", a);
        }
    }else if let Some(a) = for_totata {
        println!("{:.15}", a);
    }else {
        println!("-1");
    }
}


Measured memory_bytes: 5088