Problem: Anchored Balloon

A balloon placed on the ground is connected to one or more anchors on the ground with ropes. Each rope is long enough to connect the balloon and the anchor. No two ropes cross each other. Figure E-1 shows such a situation.

<image>
Figure E-1: A balloon and ropes on the ground

Now the balloon takes off, and your task is to find how high the balloon can go up with keeping the rope connections. The positions of the anchors are fixed. The lengths of the ropes and the positions of the anchors are given. You may assume that these ropes have no weight and thus can be straightened up when pulled to whichever directions. Figure E-2 shows the highest position of the balloon for the situation shown in Figure E-1.

<image>
Figure E-2: The highest position of the balloon

Input

The input consists of multiple datasets, each in the following format.

> n
>  x1 y1 l1
>  ...
>  xn yn ln
>

The first line of a dataset contains an integer n (1 ≤ n ≤ 10) representing the number of the ropes. Each of the following n lines contains three integers, xi, yi, and li, separated by a single space. Pi = (xi, yi) represents the position of the anchor connecting the i-th rope, and li represents the length of the rope. You can assume that −100 ≤ xi ≤ 100, −100 ≤ yi ≤ 100, and 1 ≤ li ≤ 300. The balloon is initially placed at (0, 0) on the ground. You can ignore the size of the balloon and the anchors.

You can assume that Pi and Pj represent different positions if i ≠ j. You can also assume that the distance between Pi and (0, 0) is less than or equal to li−1. This means that the balloon can go up at least 1 unit high.

Figures E-1 and E-2 correspond to the first dataset of Sample Input below.

The end of the input is indicated by a line containing a zero.

Output

For each dataset, output a single line containing the maximum height that the balloon can go up. The error of the value should be no greater than 0.00001. No extra characters should appear in the output.

Sample Input


3
10 10 20
10 -10 20
-10 10 120
1
10 10 16
2
10 10 20
10 -10 20
2
100 0 101
-90 0 91
2
0 0 53
30 40 102
3
10 10 20
10 -10 20
-10 -10 20
3
1 5 13
5 -3 13
-3 -3 13
3
98 97 168
-82 -80 193
-99 -96 211
4
90 -100 160
-80 -80 150
90 80 150
80 80 245
4
85 -90 290
-80 -80 220
-85 90 145
85 90 170
5
0 0 4
3 0 5
-3 0 5
0 3 5
0 -3 5
10
95 -93 260
-86 96 211
91 90 177
-81 -80 124
-91 91 144
97 94 165
-90 -86 194
89 85 167
-93 -80 222
92 -84 218
0


Output for the Sample Input


17.3205081
16.0000000
17.3205081
13.8011200
53.0000000
14.1421356
12.0000000
128.3928757
94.1879092
131.1240816
4.0000000
72.2251798






Example

Input

3
10 10 20
10 -10 20
-10 10 120
1
10 10 16
2
10 10 20
10 -10 20
2
100 0 101
-90 0 91
2
0 0 53
30 40 102
3
10 10 20
10 -10 20
-10 -10 20
3
1 5 13
5 -3 13
-3 -3 13
3
98 97 168
-82 -80 193
-99 -96 211
4
90 -100 160
-80 -80 150
90 80 150
80 80 245
4
85 -90 290
-80 -80 220
-85 90 145
85 90 170
5
0 0 4
3 0 5
-3 0 5
0 3 5
0 -3 5
10
95 -93 260
-86 96 211
91 90 177
-81 -80 124
-91 91 144
97 94 165
-90 -86 194
89 85 167
-93 -80 222
92 -84 218
0


Output

17.3205081
16.0000000
17.3205081
13.8011200
53.0000000
14.1421356
12.0000000
128.3928757
94.1879092
131.1240816
4.0000000
72.2251798

Solution (in CPP):

Solution:
#include <bits/stdc++.h>

using namespace std;

#define REP(i, n) for (int i = 0; i < (n); i++)
#define ALL(x) x.begin(), x.end()
#define FOR(i, m, n) for (int i = (m); i < (n); i++)

using ll = long long;
typedef pair<int, int> pii;

//幾何 Geometry
const double PI = acos(-1);
const double EPS = 1e-10;
const bool DEBUG = false;

typedef complex<double> point;
istream &operator>>(istream &is, point &p) {
    double a, b;
    is >> a >> b;
    p = point(a, b);
    return is;
}
point operator*(const point &p, const double &d) { return point(real(p) * d, imag(p) * d); }
namespace std {
bool operator<(const point &a, const point &b) { return real(a) != real(b) ? real(a) < real(b) : imag(a) < imag(b); }
}  // namespace std

//直線or線分
struct Line : public vector<point> {
    Line(const point &a, const point &b) {
        push_back(a);
        push_back(b);
    }
};
typedef vector<Line> Lines;

point direction(const point &p) {  //同じ向きの単位ベクトル
    return p / abs(p);
}
point orthogonal(const point &p) {  //法線ベクトル
    return point(-imag(p), real(p));
}
double cross(const point &a, const point &b) { return real(a) * imag(b) - imag(a) * real(b); }
double dot(const point &a, const point &b) { return real(a) * real(b) + imag(a) * imag(b); }
int ccw(point a, point b, point c) {
    b = b - a;
    c = c - a;
    if (cross(b, c) > EPS) return +1;   // counter clockwise
    if (cross(b, c) < -EPS) return -1;  // clockwise
    if (dot(b, c) < -EPS) return +2;    // c--a--b on line
    if (norm(b) < norm(c)) return -2;   // a--b--c on line
    return 0;
}
//交差判定
bool intersectLL(const Line &l1, const Line &l2) {
    return abs(cross(l1[1] - l1[0], l2[1] - l2[0])) > EPS ||  // non-parallel
           abs(cross(l1[1] - l1[0], l2[0] - l1[0])) < EPS;    // same line
}
bool intersectLS(const Line &l, const Line &s) {
    return cross(l[1] - l[0], s[0] - l[0]) *  // s[0] is left of l
               cross(l[1] - l[0], s[1] - l[0]) <
           EPS;  // s[1] is right of l
}
bool intersectLP(const Line &l, const point &p) { return abs(cross(l[1] - p, l[0] - p)) < EPS; }
//端点に乗ってたらfalse
bool intersectSP_without_endpoint(const Line &s, const point &p) {
    if (abs(s[0] - p) < EPS || abs(s[1] - p) < EPS) return false;
    return abs(s[0] - p) + abs(s[1] - p) - abs(s[1] - s[0]) < EPS;  // triangle inequality
}

bool intersectSP(const Line &s, const point &p) {
    // verified on 2020/04/03
    // https://onlinejudge.u-aizu.ac.jp/status/users/Mojumbo/submissions/1/1157/judge/4316978/C++14
    return abs(s[0] - p) + abs(s[1] - p) - abs(s[1] - s[0]) < EPS;  // triangle inequality
}
bool isOnSegment(const Line &s, const Line &t) {
    if (intersectSP(s, t[0]) || intersectSP(s, t[1]) || intersectSP(t, s[0]) || intersectSP(t, s[1]))
        return true;
    else
        return false;
}
bool isSamePoint(const point &p1, const point &p2) { return abs(p1 - p2) < EPS; }
bool connectSS(const Line &s, const Line &t) {
    REP(i, 2) REP(j, 2) {
        if (s[i] == t[j]) return true;
    }
    return false;
}
bool intersectSS(const Line &s, const Line &t) {
    return ccw(s[0], s[1], t[0]) * ccw(s[0], s[1], t[1]) <= 0 && ccw(t[0], t[1], s[0]) * ccw(t[0], t[1], s[1]) <= 0;
}
//端点が線分上に乗っている場合はfalseにする
bool intersectSS_strict(const Line &s, const Line &t) {
    if (isOnSegment(s, t)) return false;
    return ccw(s[0], s[1], t[0]) * ccw(s[0], s[1], t[1]) <= 0 && ccw(t[0], t[1], s[0]) * ccw(t[0], t[1], s[1]) <= 0;
}

//距離,交点
//射影 直線lにpから下した垂線との交点
point projection(
    const Line &l,
    const point &p) {  // verified on 2020/04/03
                       // https://onlinejudge.u-aizu.ac.jp/status/users/Mojumbo/submissions/1/1157/judge/4316978/C++14
    double t = dot(p - l[0], l[0] - l[1]) / norm(l[0] - l[1]);
    return l[0] + (l[0] - l[1]) * t;
}
//射影 直線lを対象軸としてpと線対称にある点
point reflection(const Line &l, const point &p) { return p + (projection(l, p) - p) * 2; }
double distancePP(const point &a, const point &b) { return sqrt(norm(a - b)); }
double distanceLP(const Line &l, const point &p) { return abs(p - projection(l, p)); }
double distanceLL(const Line &l, const Line &m) { return intersectLL(l, m) ? 0 : distanceLP(l, m[0]); }
double distanceLS(const Line &l, const Line &s) {
    if (intersectLS(l, s)) return 0;
    return min(distanceLP(l, s[0]), distanceLP(l, s[1]));
}
double distanceSP(const Line &s, const point &p) {
    const point r = projection(s, p);
    if (intersectSP(s, r)) return abs(r - p);
    return min(abs(s[0] - p), abs(s[1] - p));
}
double distanceSS(const Line &s, const Line &t) {
    // verified on 2020/04/03
    // https://onlinejudge.u-aizu.ac.jp/status/users/Mojumbo/submissions/1/1157/judge/4316978/C++14
    if (intersectSS(s, t)) return 0;
    return min({distanceSP(s, t[0]), distanceSP(s, t[1]), distanceSP(t, s[0]), distanceSP(t, s[1])});
}
point crosspoint(const Line &l, const Line &m) {
    double A = cross(l[1] - l[0], m[1] - m[0]);
    double B = cross(l[1] - l[0], l[1] - m[0]);
    if (abs(A) < EPS && abs(B) < EPS) return m[0];  // same line
    if (abs(A) < EPS) assert(false);                // Precondition not satisfied
    return m[0] + B / A * (m[1] - m[0]);
}
double dist(point &p1, point &p2) { return abs(p1 - p2); }

struct rope {
    point p;
    double l;
    rope(point p_, double l_) : p(p_), l(l_) {}
};
struct point3d {
    point p;
    double z;
    point3d(point p_, double z_) : p(p_), z(z_) {}
};

point3d two_ropes(int i, int j, vector<rope> &ropes, int N) {
    if (ropes[i].l > ropes[j].l) swap(i, j);
    point p = ropes[i].p, q = ropes[j].p;
    point v = q - p;
    double t = (pow(ropes[i].l, 2) - pow(ropes[j].l, 2) + pow(abs(v), 2)) / (2 * abs(v));
    point m = p + direction(v) * t;
    double h = sqrt(pow(ropes[i].l, 2) - pow(abs(m - p), 2));
    bool ok = true;
    // cout << ropes[i].p << ropes[j].p << m << h << endl;

    if (ropes[j].l > ropes[i].l + dist(p, q) + EPS) ok = false;
    if (ok) {
        point3d ret(m, h);
        return ret;
    } else {
        point3d ret(0, -1);
        return ret;
    }
}

void solve() {
    int N;
    cin >> N;
    if (N == 0) exit(0);
    vector<rope> ropes;
    REP(i, N) {
        double x, y, l;
        cin >> x >> y >> l;
        point p(x, y);
        rope r(p, l);
        ropes.push_back(r);
    }
    double ans = 0;

    // 1本が張る
    REP(i, N) {
        point p = ropes[i].p;
        double h = ropes[i].l;
        bool ok = true;
        REP(j, N) {
            if (i == j) continue;
            if (pow(dist(ropes[j].p, p), 2) + pow(h, 2) > pow(ropes[j].l, 2) + EPS) ok = false;
        }
        if (ok) ans = max(ans, h);
    }

    // 2本が張る
    REP(i, N) {
        FOR(j, i + 1, N) {
            point3d po = two_ropes(i, j, ropes, N);

            point m = po.p;
            // cout << ropes[i].p << ropes[j].p << m << po.z << endl;
            double h = po.z;
            bool ok = true;
            REP(k, N) {
                // if (k == i || k == j) continue;
                double d = sqrt(pow(dist(ropes[k].p, m), 2) + pow(h, 2));
                if (ropes[k].l + EPS < d) ok = false;
            }
            if (ok) ans = max(ans, h);
        }
    }

    // 3本が張る
    REP(i, N) {
        FOR(j, i + 1, N) {
            point3d po = two_ropes(i, j, ropes, N);
            point p = ropes[i].p, q = ropes[j].p, m = po.p;
            if (po.z < 0) continue;
            //
            REP(k, N) {
                if (k == i || k == j) continue;

                {
                    double ok = 0, ng = PI / 2;
                    double L = po.z;
                    REP(_, 50) {
                        double mid = (ok + ng) / 2;
                        double z = L * sin(mid);
                        point v = direction(m - p);
                        //時計回りに90度回転
                        point v2 = v * point(0, -1);
                        point M = m + v2 * L * cos(mid);
                        double d = sqrt(pow(abs(M - ropes[k].p), 2) + pow(z, 2));
                        if (d <= ropes[k].l + EPS)
                            ok = mid;
                        else
                            ng = mid;
                        //
                    }
                    double mid = ok;
                    double z = L * sin(mid);
                    point v = direction(m - p);
                    //時計回りに90度回転
                    point v2 = v * point(0, -1);
                    point M = m + v2 * L * cos(mid);
                    bool possible = true;
                    REP(idx, N) {
                        // if (idx == i || idx == j || idx == k) continue;
                        double d = sqrt(pow(abs(M - ropes[idx].p), 2) + z * z);
                        if (d > ropes[idx].l + EPS) possible = false;
                    }
                    if (possible) ans = max(ans, z);
                }
                {
                    double ok = 0, ng = PI / 2;
                    double L = po.z;
                    REP(_, 50) {
                        double mid = (ok + ng) / 2;
                        double z = L * sin(mid);
                        point v = direction(m - p);
                        //反時計回りに90度回転
                        point v2 = v * point(0, 1);
                        point M = m + v2 * L * cos(mid);
                        double d = sqrt(pow(abs(M - ropes[k].p), 2) + pow(z, 2));
                        if (d <= ropes[k].l + EPS)
                            ok = mid;
                        else
                            ng = mid;
                        //
                    }
                    double mid = ok;
                    double z = L * sin(mid);
                    point v = direction(m - p);
                    //反時計回りに90度回転
                    point v2 = v * point(0, 1);
                    point M = m + v2 * L * cos(mid);
                    bool possible = true;
                    REP(idx, N) {
                        // if (idx == i || idx == j || idx == k) continue;
                        double d = sqrt(pow(abs(M - ropes[idx].p), 2) + z * z);
                        if (d > ropes[idx].l + EPS) possible = false;
                    }
                    if (possible) ans = max(ans, z);
                }
            }
        }
    }
    cout << fixed << setprecision(6) << ans << endl;
}

signed main() {
    while (true) solve();
}