#include #include #include #include #include #include using namespace std; const double EPS = 1.e-8; #define LT(x, y) ((x) < (y) - EPS) typedef complex xy; typedef vector line; enum { ONEDGE = 0, INSIDE = -1, OUTSIDE = 1 }; #define isnan(x) {if((x) != (x) ) { cout << "NAN at " << __LINE__ << endl; exit(1);}} struct circle{ xy center; double r; int which(xy p){ p -= center; double r2 = norm(p); if(LT(r2, r*r)) return INSIDE; if(LT(r*r, r2)) return OUTSIDE; return ONEDGE; } vector cross_circle(circle o) { o.center -= center; double d = abs(o.center); if(abs(d) < EPS || LT(o.r + r, d) || LT(d, fabs(o.r - r))) return vector(); double l = 0.5 * ((r * r - o.r * o.r) / d + d); double h = sqrt(abs(r * r - l * l)); isnan(l); isnan(h); vector ret(2); ret[0] = xy(l, +h) * o.center / d + center; ret[1] = xy(l, -h) * o.center / d + center; return ret; } }; typedef int point; typedef double weight; struct edge{ point dest; weight w; edge(){} edge(const point &dest, const weight &w):dest(dest),w(w){} }; bool operator>(const edge&lhs, const edge&rhs) { return lhs.w > rhs.w; } typedef vector edges; typedef map graph; typedef map potential; void makeedge(graph &g, point from, point to, weight w) { g[from].push_back(edge(to, w)); g[to] .push_back(edge(from, w)); } potential dijkstra(graph &g, point p) { potential pot; priority_queue > toBeProcessed; toBeProcessed.push(edge(p,0)); while(!toBeProcessed.empty()) { edge top=toBeProcessed.top(); point p=top.dest; weight curr=top.w; toBeProcessed.pop(); if ( pot.count(p)!=0 ) continue; pot[p]=curr; edges& es=g[p]; for ( int i=0 ; i cs; xy start, goal; bool is_covered(xy& from, xy& to){ bool from_c = false, to_c = false; for(int i = 0; i < cs.size(); ++i){ bool w1, w2; if(w1 = (cs[i].which(from) <= 0)){ from_c = true; } if(w2 = (cs[i].which(to) <= 0)){ to_c = true; } if(w1 && w2) return true; } if(!from_c || !to_c) return false; xy mid = (from + to) * .5; return (is_covered(from, mid) && is_covered(mid, to)); } double solve(){ // generate all circle's cross point // except that is covered vector known; known.push_back(start); known.push_back(goal); for(int i = 0; i < cs.size(); ++i){ for(int j = i + 1; j < cs.size(); ++j){ vector cross = cs[i].cross_circle(cs[j]); for(int k = 0; k < cross.size(); ++k){ int l; for(l = 0; l < cs.size(); ++l) if(cs[l].which(cross[k]) < 0) break; if(l == cs.size()) known.push_back(cross[k]); } } } graph g; // generate graph for(int i = 0; i < known.size(); ++i) for(int j = i + 1; j < known.size(); ++j) if(is_covered(known[i], known[j])) makeedge(g, i, j, abs(known[i] - known[j])); potential pot = dijkstra(g, 0); // start return pot[1]; // goal } }; template xy read(T& in) { double x, y; in >> x >> y; return xy(x, y); } int main(void) { // ifstream cin("phone.in"); int T; cin >> T; while(T--){ problem p; p.start = read(cin); p.goal = read(cin); int K; cin >> K; circle cc; for(int i = 0; i < K; ++i){ cc.center = read(cin); cin >> cc.r; p.cs.push_back(cc); } cout.setf(ios::fixed); cout << setprecision(6); cout << p.solve() << endl; } return 0; }