import std.stdio;
import std.array;
import std.string;
import std.conv;
import std.algorithm;
import std.typecons;
import std.range;
import std.random;
import std.math;
import std.container;
import std.numeric;
import std.bigint;


struct UnionFind {
  int n;
  int[] table;
  int[] group;

  this (int N) {
    n = N;
    table = new int[](n);
    foreach (i; 0..n)
      table[i] = -1;
    group = new int[](n);
  }

  void unite(int x, int y) {
    x = find(x);
    y = find(y);
    if (x == y) return;
    if (table[x] < table[y]) swap(x, y);
    table[y] += table[x];
    table[x] = y;
  }

  int find(int x) {
    if (table[x] < 0)
      return x;
    return table[x] = find(table[x]);
  }

  int grouping() {
    int[int] g;
    int j = 0;
    foreach (i; 0..n) {
      if (table[i] < 0) {
        g[i] = j;
        j += 1;
      }
    }
    foreach (i; 0..n)
      group[i] = g[find(i)];
    return j;
  }
}


void main() {
  auto input = readln.split.map!(to!long);
  auto Sx = input[0];
  auto Sy = input[1];
  auto Gx = input[2];
  auto Gy = input[3];
  auto N = readln.chomp.to!int;
  auto x = new long[](N);
  auto y = new long[](N);
  auto r = new long[](N);
  foreach (i; 0..N) {
    input = readln.split.map!(to!long);
    x[i] = input[0];
    y[i] = input[1];
    r[i] = input[2];
  }

  bool touches(long x1, long y1, long r1, long x2, long y2, long r2) {
    long d = (x1-x2)^^2+(y1-y2)^^2;
    return ((r1-r2)^^2 <= d && d <= (r1+r2)^^2);
  }

  auto uf = new UnionFind(N);
  foreach (i; 0..N)
    foreach (j; i+1..N)
      if (touches(x[i], y[i], r[i], x[j], y[j], r[j]))
        uf.unite(i, j);

  auto M = uf.grouping;
  auto dist = new real[][](M+2, M+2);
  foreach (i; 0..M+2) foreach (j; 0..M+2) dist[i][j] = (i == j) ? 0 : real.max;
  foreach (i; 0..N)
    foreach (j; i+1..N)
      if (uf.find(i) != uf.find(j)) {
        auto g1 = uf.group[i];
        auto g2 = uf.group[j];
        real d = sqrt((x[i]-x[j])^^2.to!real + (y[i]-y[j])^^2.to!real) - r[i] - r[j];
        dist[g1][g2] = min(dist[g1][g2], d);
        dist[g2][g1] = min(dist[g2][g1], d);
      }
  foreach (i; 0..N) {
    auto g = uf.group[i];
    real ds = sqrt((x[i]-Sx)^^2.to!real + (y[i]-Sy)^^2.to!real) - r[i];
    real dg = sqrt((x[i]-Gx)^^2.to!real + (y[i]-Gy)^^2.to!real) - r[i];
    dist[g][M] = max(min(ds, dist[g][M]), 0);
    dist[M][g] = max(min(ds, dist[g][M]), 0);
    dist[g][M+1] = max(min(dg, dist[g][M+1]), 0);
    dist[M+1][g] = max(min(dg, dist[g][M+1]), 0);
  }
  dist[M][M+1] = sqrt((Gx-Sx)^^2.to!real + (Gy-Sy)^^2.to!real);
  dist[M+1][M] = sqrt((Gx-Sx)^^2.to!real + (Gy-Sy)^^2.to!real);

  alias Tuple!(real, "cost" , int, "n") node;
  auto dijk = new real[](M+2);
  auto queue = new BinaryHeap!(Array!node, "a < b");
  foreach (i; 0..M+2) dijk[i] = (i == M) ? 0 : 10L^^15;
  queue.insert(node(0, M));
  while (!queue.empty) {
    auto n = queue.front;
    queue.popFront;
    if (dijk[n.n] < n.cost)
      continue;
    foreach (m; 0..M+2) {
      if (m == n.n)
        continue;
      if (dijk[n.n] + dist[n.n][m] < dijk[m]) {
        dijk[m] = dijk[n.n] + dist[n.n][m];
        queue.insert(node(dijk[m], m));
      }
    }
  }
  writefln("%.9f", dijk[M+1]);
}
