/*
 * Copyright (c) 2004 Kamo Hiroyasu
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 *  A solution of "Building Space Station"
 *   (Boruvka's algorithm with BFS)
 *   Author: Kamo Hiroyasu <wd@ics.nara-wu.ac.jp>
 */

#include <limits.h>
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifndef	MAXNBALLS
#define MAXNBALLS	1000
#endif

#define hypot3(x,y,z)	hypot((x), hypot((y), (z)))
#define getballs(b,n)	fgetballs((b), (n), stdin)

struct point {
	double		x, y, z;
};

struct ball {
	struct point	center;
	double		radius;
};

size_t		fgetballs(struct ball *, size_t, FILE *);
double		ball_distance(const struct ball *, const struct ball *);
double		total_length(const struct ball *, size_t);

size_t
fgetballs(struct ball *balls, size_t n_max, FILE *fp)
{
	size_t		i, n;
	double		x, y, z, r;

	if (fscanf(fp, "%zu", &n) != 1 || n > n_max) {
		return 0;
	}
	for (i = 0; i < n; i ++) {
		if (fscanf(fp, "%lf%lf%lf%lf", &x, &y, &z, &r) != 4) {
			return 0;
		}
		if (r < 0) {
			return 0;
		}
		balls[i].center.x = x;
		balls[i].center.y = y;
		balls[i].center.z = z;
		balls[i].radius = r;
	}
	return n;
}

double
ball_distance(const struct ball *b1, const struct ball *b2)
{
	double		d, r;

	d = hypot3(b1->center.x - b2->center.x, b1->center.y - b2->center.y,
		   b1->center.z - b2->center.z);
	r = b1->radius + b2->radius;
	return d >= r ? d - r : 0;
}

struct node {
	struct edge	*lightest;
	struct adjac	*adjhead;
	struct node	*leader;
};

struct adjac {
	struct adjac	*next;
	struct node	*oppend;
};

struct edge {
	double		length;
	struct node	*ends[2];
};

static void
add_adjacency(struct node *u, struct node *v, struct adjac *p)
{
	p->oppend = v;
	p->next = u->adjhead;
	u->adjhead = p;
}

static size_t
classify(struct node *nodes, size_t nv, struct node **buf)
{
	size_t		nc = 0;
	struct node	**top, **bot;
	size_t		i;
	struct node	*u, *v;
	struct adjac	*p;

	for (i = 0; i < nv; i ++) {
		nodes[i].leader = NULL;
	}
	for (i = 0; i < nv; i ++) {
		if (nodes[i].leader == NULL) {
			nc ++;
			v = &nodes[i];
			buf[0] = v->leader = v;
			bot = buf;
			top = buf + 1;
			do {
				for (p = (*(bot ++))->adjhead;
				     p != NULL; p = p->next) {
					u = p->oppend;
					if (u->leader == NULL) {
						u->leader = v;
						*(top ++) = u;
					}
				}
			} while (bot < top);
		}
	}
	return nc;
}

static void
setup_edges(struct edge *edges, struct node *nodes,
	    const struct ball *balls, size_t num)
{
	size_t		i, j;
	size_t		k;

	k = 0;
	for (i = 0; i < num; i ++) {
		nodes[i].adjhead = NULL;
		nodes[i].leader = &nodes[i];
		for (j = i + 1; j < num; j ++) {
			edges[k].ends[0] = &nodes[i];
			edges[k].ends[1] = &nodes[j];
			edges[k].length = ball_distance(&balls[i], &balls[j]);
			k ++;
		}
	}
}

static void
init_nodes(struct node *nodes, size_t num, struct edge *e_infinity)
{
	size_t		i;

	for (i = 0; i < num; i ++) {
		nodes[i].lightest = e_infinity;
	}
}

static void
pick_edges(struct edge *edges, size_t ne)
{
	size_t		k;
	struct node	*s[2];
	double		w;

	for (k = 0; k < ne; k ++) {
		s[0] = edges[k].ends[0]->leader;
		s[1] = edges[k].ends[1]->leader;
		if (s[0] != s[1]) {
			w = edges[k].length;
			if (w < s[0]->lightest->length) {
				s[0]->lightest = &edges[k];
			}
			if (w < s[1]->lightest->length) {
				s[1]->lightest = &edges[k];
			}
		}
	}
}

double
total_length(const struct ball *balls, size_t num)
{
	double		w_total = 0;
	struct node	*nodes;
	struct edge	*edges, e_infinity;
	struct adjac	*adjbase, *adjtop;
	struct node	**buf;
	size_t		i;
	size_t		nc;
	size_t		ne;
	struct node	*v;
	struct edge	*e;

	nodes = malloc(num * sizeof(struct node));
	ne = num * (num - 1) / 2;
	edges = malloc(ne * sizeof(struct edge));
	setup_edges(edges, nodes, balls, num);
	e_infinity.length = HUGE_VAL;
	e_infinity.ends[0] = e_infinity.ends[1] = NULL;
	adjbase = malloc((num - 1) * 2 * sizeof(struct adjac));
	adjtop = adjbase;
	buf = malloc(num * sizeof(struct node *));
	for (nc = num; nc > 1; nc = classify(nodes, num, buf)) {
		init_nodes(nodes, num, &e_infinity);
		pick_edges(edges, ne);
		for (i = 0; i < num; i ++) {
			v = &nodes[i];
			if (v->leader == v) {
				e = v->lightest;
				if (e->ends[0]->leader == v ||
				    e->ends[1]->leader == v &&
				    e->ends[0]->leader->lightest != e) {
					w_total += e->length;
					add_adjacency(e->ends[0], e->ends[1],
						      adjtop);
					adjtop ++;
					add_adjacency(e->ends[1], e->ends[0],
						      adjtop);
					adjtop ++;
				}
			}
		}
	}
	free(buf);
	free(adjbase);
	free(edges);
	free(nodes);
	return w_total;
}

int
main(int argc, char *argv[])
{
	size_t			n;
	double			w;
	static struct ball	balls[MAXNBALLS];

	switch (argc) {
	case 2:
		if (freopen(argv[1], "r", stdin) == NULL) {
			perror(argv[1]);
			return 1;
		}
	case 1:
		break;
	default:
		fprintf(stderr, "%s: too many arguments\n", argv[0]);
		return 1;
	}
	while ((n = getballs(balls, MAXNBALLS)) > 0) {
		w = total_length(balls, n);
		printf("%.3f\n", w);
	}
	return 0;
}
