use std::io::{stdin, Read, StdinLock};
use std::str::FromStr;
use std::iter::FromIterator;
use std::cmp::max;

fn main() {
    let cin = stdin();
    let mut scan = Scanner::new(cin.lock());
    let start = (scan.read::<f64>(), scan.read::<f64>());
    let end = (scan.read::<f64>(), scan.read::<f64>());
    let n = scan.read::<usize>();
    let mut xyr = arr(n, |_| (scan.read::<f64>(), scan.read::<f64>(), scan.read::<f64>()));
    xyr.push((start.0, start.1, 0.0));
    xyr.push((end.0, end.1, 0.0));
    let mut g = vec![vec![];n+2];
    for i in 0..n + 2 {
        for j in 0..n + 2 {
            g[i].push((max(Total(0.0), Total(dist(xyr[i], xyr[j]) - xyr[i].2 - xyr[j].2)).0, j));
            g[j].push((max(Total(0.0), Total(dist(xyr[i], xyr[j]) - xyr[i].2 - xyr[j].2)).0, i));
        }
    }
    let d = dijkstra(&g, n);
    println!("{}", d[n+1]);
}

fn dist(p1: (f64, f64, f64), p2: (f64, f64, f64))-> f64 {
    ((p1.0 - p2.0).powi(2) + (p1.1 - p2.1).powi(2)).sqrt()
}

#[allow(dead_code)]
fn dijkstra(graph: &[Vec<(f64, usize)>], s: usize) -> Vec<f64> {
    let n = graph.len();
    let mut d = vec![(1i64 << 60) as f64; n];
    d[s] = 0.0;
    let mut heap = std::collections::BinaryHeap::new();
    heap.push(Rev((Total(0.0), s)));
    while let Some(Rev((Total(p_cost), p))) = heap.pop() {
        if d[p] < p_cost {
            continue;
        }
        for &(v_cost, v) in &graph[p] {
            if d[v] > d[p] + v_cost {
                d[v] = d[p] + v_cost;
                heap.push(Rev((Total(d[v]), v)));
            }
        }
    }
    d
}

#[derive(Eq, PartialEq, Clone, Debug)]
pub struct Rev<T>(pub T);

impl<T: PartialOrd> PartialOrd for Rev<T> {
    fn partial_cmp(&self, other: &Rev<T>) -> Option<std::cmp::Ordering> {
        other.0.partial_cmp(&self.0)
    }
}

impl<T: Ord> Ord for Rev<T> {
    fn cmp(&self, other: &Rev<T>) -> std::cmp::Ordering {
        other.0.cmp(&self.0)
    }
}

#[derive(PartialEq, PartialOrd)]
pub struct Total<T>(pub T);

impl<T: PartialEq> Eq for Total<T> {}

impl<T: PartialOrd> Ord for Total<T> {
    fn cmp(&self, other: &Total<T>) -> std::cmp::Ordering {
        self.0.partial_cmp(&other.0).unwrap()
    }
}


// region template
#[allow(dead_code)]
fn iu(i: f64) -> usize { i as usize }

#[allow(dead_code)]
fn ui(i: usize) -> f64 { i as f64 }

#[allow(dead_code, deprecated)]
fn join<T: std::fmt::Display>(slice: &[T], sep: &str) -> String {
    let strings = slice.iter().map(|t| format!("{}", t)).collect::<Vec<_>>();
    strings.connect(sep)
}

#[allow(dead_code)]
fn arr<'a, S, T>(n: usize, mut f: S) -> Vec<T> where S: FnMut(usize) -> T + 'a {
    (0..n).map(|i| f(i)).collect::<Vec<T>>()
}

#[allow(dead_code)]
fn alt<S, T>(v: Vec<S>) -> T where T: FromIterator<S> {
    v.into_iter().collect::<T>()
}

#[allow(dead_code)]
fn dup<S, T>(v: &[S]) -> T where T: FromIterator<S>, S: Clone {
    v.iter().cloned().collect::<T>()
}

struct Scanner<'a> {
    cin: StdinLock<'a>,
}

#[allow(dead_code)]
impl<'a> Scanner<'a> {
    fn new(cin: StdinLock<'a>) -> Scanner<'a> {
        Scanner { cin: cin }
    }

    fn read1<T: FromStr>(&mut self) -> Option<T> {
        let token = self.cin.by_ref().bytes().map(|c| c.unwrap() as char)
            .skip_while(|c| c.is_whitespace())
            .take_while(|c| !c.is_whitespace())
            .collect::<String>();
        token.parse::<T>().ok()
    }

    fn read<T: FromStr>(&mut self) -> T {
        self.read1().unwrap()
    }

    fn readn<T: FromStr>(&mut self, n: usize) -> Vec<T> {
        (0..n).map(|_| self.read::<T>()).collect()
    }

    fn chars(&mut self) -> Vec<char> {
        self.read::<String>().chars().collect()
    }
}
// endregion

Measured memory_bytes: 49404