#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 {
  Heap* l;
  Heap* r;
  Heap* p;
  Data val;
  Heap(Data v) : l(nullptr), r(nullptr), p(nullptr), val(v) {}
  Data top() { return val; }
};
Heap* meld(Heap* a, Heap* b, 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<Heap*, Heap*> push(Heap* h, Data v) {
  Heap* n = (Heap*)malloc(sizeof(Heap));
  *n = Heap(v);
  return make_pair(meld(h, n), n);
}
Heap* pop(Heap* h) {
  if (h->l) h->l->p = nullptr;
  if (h->r) h->r->p = nullptr;
  auto res = meld(h->l, h->r);
  free(h);
  return res;
}
Heap* reduce(Heap* h, 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 = nullptr;
      return meld(h, root);
    } else {
      h->p->r = nullptr;
      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 Array = vector<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;
  Heap* que = nullptr;
  vector<Heap*> nodes(n, nullptr);
  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] = nullptr;
    if (d[v] < dist) continue;
    const auto ve = g(v);
    REP(i, ve.size()) {
      Edge e = ve[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));
        }
      }
    }
  }
}

using C = tuple<int,int,int>;
struct online_edge_gen {
  vector<C> vc;
  Edges operator()(int v) const {
    size_t n = vc.size();
    Edges res;
    res.reserve(n-1);
    ll xv, yv, rv;
    tie(xv, yv, rv) = vc[v];
    REP(i,n) {
      if (i == v) continue;
      ll xi, yi, ri;
      tie(xi, yi, ri) = vc[i];
      double d = max(0., sqrt((xv-xi)*(xv-xi)+(yv-yi)*(yv-yi)) - rv - ri);
      res.push_back(Edge{v,i,d});
    }
    return res;
  }
  size_t size() const { return vc.size(); }
};

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  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;
  vc.reserve(n+2);
  REP(i,n) {
    ll x,y,r;
    cin>>x>>y>>r;
    vc.emplace_back(x,y,r);
  }
  n += 2;
  online_edge_gen oeg;
  oeg.vc = std::move(vc);
  Array dist;
  dijkstra(oeg, dist, 0);
  cout << fixed << setprecision(10) << dist[1] << endl;
  return 0;
}


Measured memory_bytes: 384