#include <iostream>
#include <fstream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <string>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <deque>
#include <bitset>
#include <algorithm>
#include <complex>
#include <array>
#include <iomanip>
using namespace std;

#define REP(i,n) for(int i=0; i<n; ++i)
#define FOR(i,a,b) for(int i=a; i<=b; ++i)
#define FORR(i,a,b) for (int i=a; i>=b; --i)
#define ALL(c) (c).begin(), (c).end()

typedef long long ll;
typedef vector<int> VI;
typedef vector<ll> VL;
typedef vector<long double> VD;
typedef vector<VI> VVI;
typedef vector<VL> VVL;
typedef vector<VD> VVD;
typedef pair<int,int> P;
typedef pair<ll,ll> PL;

template<typename T> void chmin(T &a, T b) { if (a > b) a = b; }
template<typename T> void chmax(T &a, T b) { if (a < b) a = b; }

int in() { int x; scanf("%d", &x); return x; }
ll lin() { ll x; scanf("%lld", &x); return x; }
#define INF 1LL<<60

struct cell {
    double x, y, z, r;
};

struct edge {
    int from;
    int to;
    double d;

    bool operator<(const edge& other) const {
        if (d < other.d) return true;
        else return false;
    }
};

class UnionFind {
public:
    int rank;
    vector <int> par; // 各元の親を表す配列
    vector <int> siz; // 素集合のサイズを表す配列(1 で初期化)

    // Constructor
    UnionFind(int sz_): par(sz_), siz(sz_, 1) {
        rank = sz_;
        for (int i = 0; i < sz_; ++i) par[i] = i; // 初期では親は自分自身
    }
    void init(int sz_) {
        rank = sz_;
        par.resize(sz_);
        siz.assign(sz_, 1);  // resize だとなぜか初期化されなかった
        for (int i = 0; i < sz_; ++i) par[i] = i; // 初期では親は自分自身
    }

    // Member Function
    // Find
    int root(int x) { // 根の検索
        while (par[x] != x) {
            x = par[x] = par[par[x]]; // x の親の親を x の親とする
        }
        return x;
    }

    // Union(Unite, Merge)
    bool merge(int x, int y) {
        x = root(x);
        y = root(y);
        if (x == y) return false;
        // merge technique（データ構造をマージするテク．小を大にくっつける）
        if (siz[x] < siz[y]) swap(x, y);
        siz[x] += siz[y];
        par[y] = x;
        rank--;
        return true;
    }

    bool issame(int x, int y) { // 連結判定
        return root(x) == root(y);
    }

    int size(int x) { // 素集合のサイズ
        return siz[root(x)];
    }
};

int main() {
    while(true) {
        int n;
        cin >> n;
        if(n == 0) break;

        vector<cell> cells(n);
        REP(i, n) {
            double x, y, z, r;
            cin >> x >> y >> z >> r;
            cell c = {x, y, z, r};
            cells[i] = c;
        }

        UnionFind uf = UnionFind(n);

        vector<edge> edges;
        REP(i, n)
            FOR(j, i + 1, n-1) {
                cell c1 = cells[i];
                cell c2 = cells[j];
                double d2 = pow(c1.x - c2.x, 2) + pow(c1.y - c2.y, 2) + pow(c1.z - c2.z, 2);
                double d = sqrt(d2);

                if(d <= c1.r + c2.r)
                    uf.merge(i, j);
                else {
                    edge e = {i, j, d - c1.r - c2.r};
                    edges.push_back(e);
                }
            }
        sort(ALL(edges));

        vector<edge> tree;
        for(edge e: edges) {
            if(!uf.issame(e.from, e.to)) {
                uf.merge(e.from, e.to);
                tree.push_back(e);
            }
        }

        double ans = 0;
        for(edge e: tree)
            ans += e.d;
        ans = round(1000 * ans) / 1000;
        // cout << ans << endl;
        printf("%.3f\n", ans);
    }
    return 0;
}


Measured memory_bytes: 3416