#include <bits/stdc++.h>
#include <boost/range/algorithm.hpp>
#include <boost/range/numeric.hpp>
#include <boost/range/irange.hpp>
#include <boost/range/adaptor/indexed.hpp>
#define FOR(i,k,n)  for(int i = (k);i < (n);++i)
#define REP(i,n)    FOR(i,0,n)
#define ALL(x) begin(x),end(x)

using namespace std;
using namespace boost::adaptors;
using namespace std::string_literals;
using ll = int64_t;
using vecint = vector<int>;
using vecll = vector<ll>;
using boost::irange;

using Weight = double;
using Data = pair<Weight,int>;
// enable reduce
struct Heap {
  shared_ptr<Heap> l,r,p;
  Data val;
  Heap(Data v) : val(v) {}
  Data top() { return val; }
};
shared_ptr<Heap> meld(
    shared_ptr<Heap> a, shared_ptr<Heap> b, shared_ptr<Heap> p = nullptr) {
  if (!a) {
    if (b) b->p = p;
    return b;
  }
  if (!b) {
    if (a) a->p = p;
    return a;
  }
  if (a->val > b->val) swap(a, b);
  a->p = p;
  a->r = meld(a->r, b, a);
  swap(a->l, a->r);
  return a;
}
pair<shared_ptr<Heap>, shared_ptr<Heap>> push(shared_ptr<Heap> h, Data v) {
  auto n = make_shared<Heap>(v);
  return make_pair(meld(h, n), n);
}
shared_ptr<Heap> pop(shared_ptr<Heap> h) {
  if (h->l) h->l->p.reset();
  if (h->r) h->r->p.reset();
  return meld(h->l, h->r);
}
shared_ptr<Heap> reduce(shared_ptr<Heap> h, shared_ptr<Heap> root, Data v) {
  if (v > h->val) return root;
  h->val = v;
  if (!h->p) return root;
  if (h->val < h->p->val) {
    if (h->p->l && h->p->l == h) {
      h->p->l.reset();
      return meld(h, root);
    } else {
      h->p->r.reset();
      return meld(h, root);
    }
  } else return root;
}

Weight INF = 1e18;
struct Edge{
  int src, dest; Weight weight;
  bool operator < (const Edge &rhs) const {return weight > rhs.weight;}
};

using Edges = vector<Edge>;
using Graph = vector<Edges>;
using Array = vector<Weight>;
using Matrix = vector<Array>;

void add_edge(Graph &g, int src, int dest, Weight weight) {
  g[src].push_back((Edge){src, dest, weight});
  g[dest].push_back((Edge){dest, src, weight});
}

//O(V)-space dijkstra
//G: function object
template<typename G>
void dijkstra(G &g, Array &d, int s) {
  int n=g.size();
  d.assign(n, INF);
  d[s] = 0;
  shared_ptr<Heap> que;
  vector<shared_ptr<Heap>> nodes(n);
  auto p = push(que, Data(0, s));
  que = p.first;
  nodes[s] = p.second;
  while (que) {
    Weight dist = que->top().first;
    int v = que->top().second;
    que = pop(que);
    nodes[v].reset();
    if (d[v] < dist) continue;
    REP(i, g[v].size()) {
      Edge e = g[v][i];
      if (d[e.dest] > d[v] + e.weight) {
        d[e.dest] = d[v] + e.weight;
        if (!nodes[e.dest]) {
          auto p = push(que, Data(d[e.dest], e.dest));
          que = p.first;
          nodes[e.dest] = p.second;
        } else {
          que = reduce(nodes[e.dest], que, Data(d[e.dest], e.dest));
        }
      }
    }
  }
}

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  using C = tuple<ll,ll,ll>;
  vector<C> vc;
  ll xs,ys,xt,yt;
  cin>>xs>>ys>>xt>>yt;
  vc.emplace_back(xs,ys,0);
  vc.emplace_back(xt,yt,0);
  int n;
  cin>>n;
  REP(i,n) {
    ll x,y,r;
    cin>>x>>y>>r;
    vc.emplace_back(x,y,r);
  }
  n += 2;
  Graph g(n);
  REP(i,n) g[i].reserve(n);
  REP(i,n)REP(j,i) {
    ll x0,y0,r0,x1,y1,r1;
    tie(x0,y0,r0) = vc[i];
    tie(x1,y1,r1) = vc[j];
    double d = max(0., __builtin_sqrt((x0-x1)*(x0-x1)+(y0-y1)*(y0-y1)) - r0 - r1);
    g[i].push_back(Edge{i,j,d});
    g[j].push_back(Edge{j,i,d});
  }
  Array dist;
  dijkstra(g, dist, 0);
  cout << fixed << setprecision(10) << dist[1] << endl;
  return 0;
}


Measured memory_bytes: 16128