#include <bits/stdc++.h>
using namespace std;

struct Sphere
{
  double x, y, z, r;
  double getDist(const Sphere &other)
  {
    double res = sqrt(pow(x - other.x, 2) + pow(y - other.y, 2) + pow(z - other.z, 2)) - r - other.r;
    if (res <= 0)
    {
      return 0;
    }
    return res;
  }
};

struct UnionFind
{
  vector<int> rank, p;
  UnionFind(int n)
  {
    rank.resize(n, 0);
    p.resize(n, 0);
    for (int i = 0; i < n; i++)
    {
      p.at(i) = i;
    }
  }

  int find(int x)
  {
    if (x != p.at(x))
    {
      p.at(x) = find(p.at(x));
    }
    return p.at(x);
  }

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

  void link(int x, int y)
  {
    if (rank.at(x) > rank.at(y))
    {
      p.at(y) = x;
    }
    else
    {
      p.at(x) = y;
      if (rank.at(x) == rank.at(y))
      {
        rank.at(y)++;
      }
    }
  }

  void unite(int x, int y)
  {
    link(find(x), find(y));
  }
};

int main()
{
  while (1)
  {
    int n;
    cin >> n;
    if (!n)
    {
      break;
    }
    vector<Sphere> sp(n);
    for (int i = 0; i < n; i++)
    {
      double x, y, z, r;
      cin >> x >> y >> z >> r;
      sp.at(i) = Sphere{x, y, z, r};
    }
    UnionFind uf(n);
    vector<tuple<double, int, int>> edge;
    for (int i = 0; i < n; i++)
    {
      for (int j = i + 1; j < n; j++)
      {
        double d = sp.at(i).getDist(sp.at(j));
        if (d == 0)
        {
          uf.unite(i, j);
          continue;
        }
        edge.push_back(make_tuple(d, i, j));
      }
    }
    sort(edge.begin(), edge.end());
    double ans = 0;
    for (int i = 0; i < edge.size(); i++)
    {
      double d;
      int s, t;
      tie(d, s, t) = edge.at(i);
      if (uf.same(s, t))
      {
        continue;
      }
      ans += d;
      uf.unite(s, t);
    }
    printf("%.3f\n", ans);
  }
}
