#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

#define REP(i,n) for(ll i=0;i<n;++i)
#define SORT(name) sort(name.begin(), name.end())
#define ZERO(p) memset(p, 0, sizeof(p))
#define MINUS(p) memset(p, -1, sizeof(p))
#if 1
#  define DBG(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
#  define DBG(fmt, ...)
#endif

const ll LLINF = (1LL<<60);
const int INF = (1LL<<30);
const int MOD = 1000000007;
#define MAX_N 1010

// --- 基本セット --- //
const double EPS = 1e-8;        // 非常に小さな値
const double PI = acos(-1);     // 円周率

// a == b なら true (誤差に寛容)
inline bool Eq(double a, double b) { return fabs(b - a) < EPS; }

// ユークリッド平面上の点を表す構造体
class Point
{
    public:
        double x, y;
    public:
        Point() {}
        Point(double x, double y) : x(x), y(y) {}
        Point operator+(const Point &b) const { return Point(x + b.x, y + b.y); }
        Point operator-(const Point &b) const { return Point(x - b.x, y - b.y); }
        Point operator*(const double b) const { return Point(x * b, y * b); }
        Point operator*(const Point &b) const { return Point(x * b.x - y * b.y, x * b.y + y * b.x); }

        Point operator/(const double b) const { return Point(x / b, y / b); }
        bool operator<(const Point &b) const { return x != b.x ? x < b.x : y < b.y; }
        bool operator==(const Point &b) const { return Eq(x, b.x) && Eq(y, b.y); }

        // 大きさの二乗を返します
        double Norm() { return x * x + y * y; }
        // 逆正接(y = 0 からの rad を [-π, π] の範囲で表したもの)を返します
        double Atan2() { return atan2(y, x); }
        // 大きさを返します
        double Abs() { return sqrt(Norm()); }
        // 原点を中心として反時計周りに theta (rad)度回転した座標を返します
        Point Rotate(double theta) { return Point(cos(theta) * x - sin(theta) * y, sin(theta) * x + cos(theta) * y); }
        // 原点を反時計周りに π/2 回転した座標を返します
        Point Rotate90() { return Point(-y, x); }
};

// 2 点の外積
double Cross(const Point &a, const Point &b) { return a.x * b.y - a.y * b.x; }
// 2 点の内積
double Dot(const Point &a, const Point &b) { return a.x * b.x + a.y * b.y; }
// ラジアンを角度に
double RadianToDegree(double r) { return (r * 180.0 / acos(-1)); }
// 角度をラジアンに
double DegreeToRadian(double d) { return (d * acos(-1) / 180.0); }
// a->b->c とたどったときの b の角度(ラジアン [0, π])
double GetAngle(const Point &a, const Point &b, const Point &c)
{
  const Point v = a - b, w = c - b;
  double alpha = atan2(v.y, v.x), beta = atan2(w.y, w.x);
  if(alpha > beta) { swap(alpha, beta); }
  double theta = (beta - alpha);
  return min(theta, 2 * acos(-1) - theta);
}

// 直線を表すクラス
class Line
{
    public:
        Point a, b;
    public:
        Line() {};
        Line(Point a, Point b) : a(a), b(b) {};
        // Ax + By = C という式が与えられたときの直線上の任意の 2 点を保存
        Line(double A, double B, double C)
        {
            if(Eq(A, 0)) { a = Point(0, C / B); b = Point(1, C / B); }
            else if(Eq(B, 0)) { a = Point(C / A, 0); b = Point(C / A, 1); }
            else { a = Point(0, C / B); b = Point(C / A, 0); }
        }
        // 傾きとの取得
        enum LineResult {
            INF_SLOPE,  // 傾き無限のグラフ (x = n のグラフ)
            NO_SLOPE,   // 切片のみのグラフ (y = b のグラフ)
            VALID_SLOPE // 傾きと切片のあるグラフ
        };
        // out_a に傾きが、 out_b に切片が、返り値として LineResult が返る
        // INF_SLOPE の時は out_b に x = n の n が入る
        int GetAB(double& out_a, double& out_b) {
            if(abs(a.x - b.x) < EPS) { return INF_SLOPE; }
            if(abs(a.y - b.y) < EPS) {
                out_b = a.y;
                return NO_SLOPE;
            }
            out_a = (a.y - b.y) / (a.x - b.x);
            out_b = a.y - out_a * a.x;
            return VALID_SLOPE;
        }
        // x 座標を渡した時に対応する y 座標を返します
        double GetY(double x) {
            double slope, intercept;
            // x = a の場合は適当な点を返す
            if(GetAB(slope, intercept) == INF_SLOPE) { return a.y; }
            return slope * x + intercept;
        }
        // y 座標を渡した時に対応する x 座標を返します
        double GetX(double y) {
            double slope, intercept;
            // y = b の場合は適当な点を返す
            if(GetAB(slope, intercept) == INF_SLOPE) { return a.x; }
            return (y - intercept) / slope;
        }
};

// 線分を表すクラス
// 直線と違い端点が定義できる場合はこちら
class Segment
{
    public:
        Point a, b;
        Segment() {};
        Segment(Point a, Point b) : a(a), b(b) {};
        // 中点を求める
        Point Center() { return Point((a.x + b.x) / 2, (a.y + b.y) / 2); }
        // 垂直二等分線を求める
        Line PerpBisector() {
            Point center = this->Center();
            return Line((a - center).Rotate90() + center, center);
        }
};

// 円を表すクラス
class Circle
{
    public:
        Point p;  // 中心
        double r; // 半径

        Circle() {};
        Circle(Point p, double r) : p(p), r(r) {};
};

// その他図形を表す型定義
typedef vector< Point > Polygon;            // 多角形
typedef vector< Segment > Segments;         // 線分集合
typedef vector< Line > Lines;               // 直線集合
typedef vector< Circle > Circles;           // 円集合
typedef pair< Point, Point > PointPoint;    // 2 点

// 点の進行方向を調べる
enum DotDir {
    Counterclockwise = 1,   // a -> b 反時計周りの方向に c
    Clockwise = -1,         // a -> b 時計周りの方向に c
    CAB = 2,                // c -> a -> b の順に並ぶ
    ABC = -2,               // a -> b -> c の順に並ぶ
    ACB = 0                 // a -> c -> b の順に並ぶ
};
// a -> b -> c の順に点をたどったときの進行方向 DotDir を返す
int Ccw(const Point &a, Point b, Point c)
{
  b = b - a, c = c - a;
  if(Cross(b, c) > EPS) { return Counterclockwise; }
  if(Cross(b, c) < -EPS) { return Clockwise; }
  if(Dot(b, c) < 0) { return CAB; }
  if(b.Norm() < c.Norm()) { return ABC; }
  return ACB;
}

// --- 基本セットここまで --- //

// --- 直線の平行・交差判定 --- //

// 直線の平行判定 a, b が平行なら true
bool Parallel(const Line& a,const Line& b){
  return abs(Cross(a.b - a.a, b.b - b.a)) < EPS;
}
// 直線の直交判定 a, b が直交していれば true
bool Orthogonal(const Line& a,const Line& b){
  return abs(Dot(a.a - a.b, b.a - b.b)) < EPS;
}

// --- 点から直線・線分への射影・反射 --- //
// ※射影と反射の定義、少し違う気がするが気にしないことにする

// 射影
// 点 p から直線 l に対して垂線をおろしたときの交点 x を射影と呼ぶことにする
// その x を返す
Point Projection(const Line& l, const Point& p) {
  double t = Dot(p - l.a, l.a - l.b) / (l.a - l.b).Norm();
  return l.a + (l.a - l.b) * t;
}
// 線分に対しても直線と同じ式()
Point Projection(const Segment& l, const Point& p) {
  double t = Dot(p - l.a, l.a - l.b) / (l.a - l.b).Norm();
  return l.a + (l.a - l.b) * t;
}

// 反射
// 直線 l を対象軸として点 p と線対称の位置にある点 x を p の反射と呼ぶ
Point Reflection(const Line& l, const Point& p) {
  return p + (Projection(l, p) - p) * 2.0;
}

// --- 点・直線・線分の距離 --- //

// いくつかの距離算出に交点を使うので前もって宣言
// これらの実装は「点・直線・線分・円の交差判定」にて
bool Intersect(const Line &l, const Point &p);
bool Intersect(const Line &l, const Line &m);
bool Intersect(const Segment &s, const Point &p);
bool Intersect(const Line &l, const Segment &s);
bool Intersect(const Segment &s, const Segment &t);
bool Intersect(const Circle &c, const Line &l);
bool Intersect(const Circle &c, const Point &p);
bool Intersect(const Circle &c1, const Circle& c2);
int Intersect(const Circle &c, const Segment &l);

// 2 点 a, b 間の距離
double Distance(const Point& a,const Point& b) { return (a - b).Abs(); }

// 点 p から直線 l に対して下した垂線の距離
// = 点 p と点 p の直線 l に対する射影の距離
double Distance(const Line& l,const Point& p) { return (p - Projection(l, p)).Abs(); }

// 直線同士の距離
// 平行である -> 直線 m 上の任意点からの l に対する射影への距離
// 平行でない -> 0
double Distance(const Line& l,const Line& m) {
  return Intersect(l, m) ? 0 : Distance(l, m.a);
}

// 線分と点の距離
// 点 p の線分 l に対する射影 r を求め、それが l 上にあるならそれと点 p の距離
// ないなら線分 l の両端のどっちか短い方が距離
double Distance(const Segment& s,const Point& p){
  Point r = Projection(s, p);
  if (Intersect(s, r)) { return (r - p).Abs(); }
  return min((s.a - p).Abs(), (s.b - p).Abs());
}

// 線分同士の距離
// 線分が交差している -> 0
// 交差していない -> 線分の端点の組み合わせパターンの最小が距離
double Distance(const Segment& a,const Segment& b){
  if(Intersect(a, b)) { return 0; }
  return min({ Distance(a, b.a), Distance(a, b.b), Distance(b, a.a), Distance(b, a.b) });
}

// 直線と線分の距離
// 交差している -> 0
// 交差していない -> 線分の端点 2 つと直線の距離の最小
double Distance(const Line& l,const Segment& s) {
  if (Intersect(l, s)) { return 0; }
  return min(Distance(l, s.a), Distance(l, s.b));
}

// --- 点・直線・線分の交差判定 --- //

// 直線 l 上に点 p があるなら true
bool Intersect(const Line &l, const Point &p) { return abs(Ccw(l.a, l.b, p)) != 1; }
// 直線同士が交わっているなら true
bool Intersect(const Line &l, const Line &m) {
  return abs(Cross(l.b - l.a, m.b - m.a)) > EPS || abs(Cross(l.b - l.a, m.b - l.a)) < EPS;
}
// 線分 s 上に点 p があるなら true
bool Intersect(const Segment &s, const Point &p) { return Ccw(s.a, s.b, p) == ACB; }
// 直線と線分が交わっているなら true
bool Intersect(const Line &l, const Segment &s) {
    return Cross(l.b - l.a, s.a - l.a) * Cross(l.b - l.a, s.b - l.a) < EPS;
}
// 線分同士が交わっているなら true
bool Intersect(const Segment &s, const Segment &t) {
  return Ccw(s.a, s.b, t.a) * Ccw(s.a, s.b, t.b) <= 0 && Ccw(t.a, t.b, s.a) * Ccw(t.a, t.b, s.b) <= 0;
}
// 直線と円が接しているなら true
bool Intersect(const Circle &c, const Line &l) { return Distance(l, c.p) <= c.r + EPS; }
// 点が円周上にあるなら true
bool Intersect(const Circle &c, const Point &p) { return abs((p - c.p).Abs() - c.r) < EPS; }
// 線分が円周と交差する数を返す
// ただし円の接線の場合も 2 を返す
int Intersect(const Circle &c, const Segment &l)
{
    // 円の中心から線分に向かっての射影までの距離が
    // 円の半径より大きいなら円と線分は交わっていない
    if((Projection(l, c.p) - c.p).Norm() - c.r * c.r > EPS) { return 0; }
    // 線分の端点から円の中心までの距離
    const double d1 = (c.p - l.a).Abs(), d2 = (c.p - l.b).Abs();
    // 両方の端点が円の内部にある場合は円に内包されているため交わっていない
    if(d1 < c.r + EPS && d2 < c.r + EPS) { return 0; }
    // 端点のどちらか片方だけ円に入っている
    if(d1 < c.r - EPS && d2 > c.r + EPS || d1 > c.r + EPS && d2 < c.r - EPS) { return 1; }
    // 円の中心から線分への射影
    const Point h = Projection(l, c.p);
    // 射影から各端点への内積が負 = 射影は線分の中にある
    // -> 線分は円を通過する
    if(Dot(l.a - h, l.b - h) < 0) { return 2; }
    return 0;
}

// 円周どうしの交差判定
bool Intersect(const Circle &c1, const Circle &c2) {
    double d = Distance(c1.p, c2.p);
    if(d > c1.r + c2.r) { return false; }
    if(d + min(c1.r, c2.r) < max(c1.r, c2.r)) { return false; }
    return true;
}

// --- 点・直線・線分の交点 --- //

// 直線どうしの交点
// 交点があった場合は true を返し out に交点の値が入る
// 交点がなかった場合は false を返す
bool Crosspoint(Point& out, const Line &l, const Line &m)
{
    if(!Intersect(l, m)) { return false; }
    double A = Cross(l.b - l.a, m.b - m.a);
    double B = Cross(l.b - l.a, l.b - m.a);
    if(abs(A) < EPS && abs(B) < EPS) { out = m.a; }
    else { out = m.a + (m.b - m.a) * B / A; }
    return true;
}

// 線分どうしの交点
// 交点があった場合は true を返し out に交点の値が入る
// 交点がなかった場合は false を返す
bool Crosspoint(Point& out, const Segment &l, const Segment &m)
{
    if(!Intersect(l, m)) { return false; }
    double A = Cross(l.b - l.a, m.b - m.a);
    double B = Cross(l.b - l.a, l.b - m.a);
    if(abs(A) < EPS && abs(B) < EPS) { out = m.a; }
    else { out = m.a + (m.b - m.a) * B / A; }
    return true;
}

// 円と直線の交点
// 交点があった場合は true を返し out に交点の値が入る
// 直線が円の接線だった場合、 out の first と second には同じ値が入る
// 交点がなかった場合は false を返す
bool Crosspoint(PointPoint& out, const Circle &c, const Line l)
{
    if(!Intersect(c, l)) { return false; }
    Point hp = Projection(l, c.p), h = hp - c.p;
    const double d2 = h.Norm();
    Point v = (l.b - l.a) * sqrt(c.r * c.r - d2) / (l.b - l.a).Abs();
    out = PointPoint(hp - v, hp + v);
    return true;
}

// 円と線分の交点
// 交点の数を返し out に交点の値が入る
// 線分が円の接線だった場合、 out の first と second には同じ値が入る
// 交点が 1 つだった場合も、 out の first と second には同じ値が入る
int Crosspoint(PointPoint& out, const Circle &c, const Segment &l)
{
    Line aa(l.a, l.b);
    if(Intersect(c, l) == 2) {
        Crosspoint(out, c, aa);
        return 2;
    }
    else if(Intersect(c, l) == 0) { return 0; }
    Crosspoint(out, c, aa);
    if(Dot(l.a - out.first, l.b - out.first) < 0) { out.second = out.first; }
    else { out.first = out.second; }
    return 1;
}

// 円どうしの交点
// 交点があった場合は true を返し out に交点の値が入る
// 交点が 1 つだった場合、 first と second には同じ値が入る
bool Crosspoint(PointPoint& out, const Circle &c1, const Circle &c2)
{
    if(!Intersect(c1, c2)) { return false; }
    double d = (c1.p - c2.p).Abs();
    double a = acos((c1.r * c1.r + d * d - c2.r * c2.r) / (2 * c1.r * d));
    double t = atan2(c2.p.y - c1.p.y, c2.p.x - c1.p.x);
    Point p1 = c1.p + Point(cos(t + a) * c1.r, sin(t + a) * c1.r);
    Point p2 = c1.p + Point(cos(t - a) * c1.r, sin(t - a) * c1.r);
    out = (PointPoint(p1, p2));
    return true;
}

// 点 p から円 c の円周の一番近いところまでの距離
double get_dist_p_c(const Point& p, const Circle& c) {
    // 円に点が内包されるなら距離 0
    if(Distance(p, c.p) <= c.r) { return 0; }
    // 円の中心と p の距離から半径を引いたものが距離
    double ret = Distance(p, c.p) - c.r;
    return (ret < 0) ? 0 : ret;
}

// 円 c1 と円 c2 の円周の一番近いところどうしの距離
double get_dist_c_c(const Circle& c1, const Circle& c2) {
    // 円どうしが接しているなら距離 0
    if(Intersect(c1, c2)) { return 0; }
    // 中心点どうしの距離から、各円の半径を引いたものが距離
    double ret = Distance(c1.p, c2.p) - c1.r - c2.r;
    return (ret < 0) ? 0 : ret;
}

class Dijkstra {
    private:
        struct Edge {
            int to;      // 有向辺の行先頂点
            double cost;    // 辺の重み
        };
        typedef pair<double, int> P;       // first: 最短距離 second: 頂点 id
    public:
        Dijkstra(int v, double inf, bool isEnablePath) : m_V(v), m_Inf(inf), m_IsEnablePath(isEnablePath) {
            m_G = new vector<Edge>[m_V];
            m_D = new double[m_V];
            if(m_IsEnablePath) { m_PrevP = new int[m_V]; }
        }
        ~Dijkstra() {
            delete[] m_G;
            delete[] m_D;
            if(m_IsEnablePath) { delete[] m_PrevP; }
        }
        // 有向辺の追加
        void AddDirEdge(int from, int to, double cost) { m_G[from].push_back({to, cost}); }
        // 無向辺の追加
        void AddUndirEdge(int from, int to, double cost) {
            AddDirEdge(from, to, cost);
            AddDirEdge(to, from, cost);
        }
        // 始点 s から各頂点への最短経路の導出 O(E log V)
        void Calc(int s) {
            // greater<P>を指定することでfirstが小さい順に取り出せるようにする
            priority_queue<P, vector<P>, greater<P> > que;
            fill(m_D, m_D + m_V, m_Inf);
            if(m_IsEnablePath) { fill(m_PrevP, m_PrevP + m_V, -1); }
            m_D[s] = 0;
            que.push(P(0, s));
            while(!que.empty()) {
                P p = que.top(); que.pop();
                int v = p.second;
                if(m_D[v] < p.first){ continue; }
                for(int i = 0; i < m_G[v].size(); ++i) {
                    Edge e = m_G[v][i];
                    if(m_D[e.to] > m_D[v] + e.cost) {
                        m_D[e.to] = m_D[v] + e.cost;
                        if(m_IsEnablePath) { m_PrevP[e.to] = v; }
                        que.push(P(m_D[e.to], e.to));
                    }
                }
            }
        }
        // 始点 s から頂点 t への最短距離を返す O(1)
        // 前処理として Calc(s) が必要
        double GetDist(int t) { return m_D[t]; }
        // 始点 s から頂点 t までの経路を vector に入れて返す (s と t 含む)
        // O(V') … V' は最短経路の頂点数
        // 前処理として Calc(s) が必要
        vector<int> GetPath(int t) {
            assert(m_IsEnablePath);
            vector<int> path;
            for(; t != -1; t = m_PrevP[t]) { path.push_back(t); }  // t が s になるまで prev_p[t] を辿っていく
            // このままだと t->s の順になっているので逆順にする
            reverse(path.begin(), path.end());
            return path;
        }
    private:
        const int m_V;                // 頂点の数
        const double m_Inf;              // 無限の代わりとして扱う値
        const bool m_IsEnablePath;  // 経路も求める場合は true
        vector<Edge>* m_G;          // 頂点 i から延びている辺の隣接リスト
        double* m_D;                     // 各頂点への最短コストが入る配列
        int* m_PrevP;                 // 最短経路の各頂点を保存
};

ll N;
Circles C;
Point S, T;

signed main()
{
    double x, y, r;
    cin >> x >> y;
    S.x = x; S.y = y;
    cin >> x >> y;
    T.x = x; T.y = y;
    cin >> N;
    REP(i, N) {
        cin >> x >> y >> r;
        C.push_back( Circle( Point(x, y), r) );
    }

    // s と t のノード id
    int s_id = N, t_id = N+1;

    Dijkstra dk(N+2, std::numeric_limits<double>::infinity(), false);
    dk.AddUndirEdge(s_id, t_id, Distance(S, T));
    REP(i, N) {
        dk.AddDirEdge(s_id, i, get_dist_p_c(S, C[i]));
        dk.AddDirEdge(i, t_id, get_dist_p_c(T, C[i]));
        for(int j = i + 1; j < N; ++j) {
            dk.AddUndirEdge(i, j, get_dist_c_c(C[i], C[j]));
        }
    }

    dk.Calc(s_id);

    printf("%.10f\n", dk.GetDist(t_id));

    return 0;
}


Measured memory_bytes: 22000