/*
 * 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"
 *   (Kruskal's algorithm with trees)
 *   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 ufnode {
	size_t		count;
	struct ufnode	*parent;
};

struct joint {
	double		length;
	struct ufnode	*ends[2];
};

static void
uf_init(struct ufnode *s, size_t n)
{
	struct ufnode	*p;

	for (p = s; p < &s[n]; p ++) {
		p->count = 1;
		p->parent = p;
	}
}

static struct ufnode *
uf_find(struct ufnode *cur)
{
	struct ufnode	*parent, *root;

	root = cur;
	while (root->count == 0) {
		root = root->parent;
	}
	while (cur != root) {
		parent = cur->parent;
		cur->parent = root;
		cur = parent;
	}
	return root;
}

static void
uf_union(struct ufnode *s0, struct ufnode *s1)
{
	struct ufnode	*source, *target;

	if (s0->count < s1->count) {
		source = s0;
		target = s1;
	} else {
		source = s1;
		target = s0;
	}
	source->parent = target;
	target->count += source->count;
	source->count = 0;
}

static void
heap_append(struct joint *a, size_t n,
	    struct ufnode *u, struct ufnode *v, double w)
{
	size_t		i, j;

	for (i = n; i > 0 && a[j = (i - 1) / 2].length > w; i = j) {
		a[i] = a[j];
	}
	a[i].ends[0] = u;
	a[i].ends[1] = v;
	a[i].length = w;
}

static void
heap_remove_top(struct joint *a, size_t n)
{
	size_t		i, j, k;
	double		w, wj, wk;

	w = a[n].length;
	for (i = 0; (j = i * 2 + 1) < n; i = j) {
		wj = a[j].length;
		if ((k = j + 1) < n && wj > (wk = a[k].length)) {
			j = k;
			wj = wk;
		}
		if (w > wj) {
			a[i] = a[j];
		} else {
			break;
		}
	}
	a[i] = a[n];
}

double
total_length(const struct ball *balls, size_t num)
{
	double		w_total = 0;
	struct ufnode	*nodes;
	struct joint	*heap_array;
	size_t		heap_size;
	size_t		i, j;
	size_t		deficiency;
	struct ufnode	*s[2];

	nodes = malloc(num * sizeof(struct ufnode));
	uf_init(nodes, num);
	heap_array = malloc(num * (num - 1) / 2 * sizeof(struct joint));
	heap_size = 0;
	for (i = 1; i < num; i ++) {
		for (j = 0; j < i; j ++) {
			heap_append(heap_array, heap_size,
				    &nodes[j], &nodes[i],
				    ball_distance(&balls[j], &balls[i]));
			heap_size ++;
		}
	}
	deficiency = num - 1;
	if (deficiency > 0 && heap_size > 0) {
		for (;;) {
			s[0] = uf_find(heap_array[0].ends[0]);
			s[1] = uf_find(heap_array[0].ends[1]);
			if (s[0] != s[1]) {
				w_total += heap_array[0].length;
				uf_union(s[0], s[1]);
				deficiency --;
				if (deficiency == 0) break;
			}
			heap_size --;
			if (heap_size == 0) break;
			heap_remove_top(heap_array, heap_size);
		}
	}
	free(heap_array);
	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;
}
