#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#include<tuple>

using namespace std;

struct UnionFind{
  vector<int> par;
  vector<int> sizes;

  UnionFind(int n) : par(n), sizes(n, 1) {
    for(int i=0;i<n;i++) par[i] = i;
  }

  int find(int x) {
    if (x == par[x]) return x;
    return par[x] = find(par[x]);  // 根を張り替えながら再帰的に根ノードを探す
  }

  void unite(int x, int y) {
    x = find(x);
    y = find(y);
    if (x == y) return;
    if (sizes[x] < sizes[y]) swap(x, y);
    par[y] = x;
    sizes[x] += sizes[y];
  }

  bool same(int x, int y) {
    return find(x) == find(y);
  }

  long long int size(int x) {
    return sizes[find(x)];
  }
};

int main(){

  int n;
  cin >> n;

  vector<double> p;

  while(n!=0){
    long double x[n],y[n],z[n],r[n];

    for(int i=0;i<n;i++){
      cin >> x[i] >> y[i] >> z[i] >> r[i];
    }

    UnionFind tree(n);
    vector<tuple<long double,int,int> > G;
    for(int i=0;i<n;i++){
      for(int j=i+1;j<n;j++){
        long double l=sqrt(pow(x[i]-x[j],2)+pow(y[i]-y[j],2)+pow(z[i]-z[j],2))-r[i]-r[j];
        if(l<=0)tree.unite(i,j);
        else{
          G.push_back(make_tuple(l,i,j));
        }
      }
    }

    sort(G.begin(),G.end());

    double s=0;

    for(int i=0;i<G.size();i++){
      if(tree.same(get<1>(G[i]),get<2>(G[i])));
      else{
        tree.unite(get<1>(G[i]),get<2>(G[i]));
        s+=get<0>(G[i]);
      }
    }

    p.push_back(s);


    cin >> n;
  }

  for(int i=0;i<p.size();i++) printf("%.3lf\n",p[i]);

}
