#include <iostream>
#include <iomanip>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <memory>
#include <cstring>
#include <cassert>
#include <numeric>
#include <sstream>
#include <complex>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <cctype>
#include <unordered_map>
#include <unordered_set>
using namespace std;

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

template <typename T, typename E>
ostream &operator<<(std::ostream &os, const std::pair<T, E> &p){
  return os << "(" << p.first << ", " << p.second << ")";
}

typedef long long ll;

struct xyz_t {
  double x;
  double y;
  double z;
  friend bool operator<(const xyz_t &p, const xyz_t &q){
    return p.x != q.x ? p.x < q.x : p.y != q.y ? p.y < q.y : p.z < q.z;
  }
  friend ostream &operator<<(ostream &os, const xyz_t &p){
    os << "(" << p.x << ", " << p.y << ", " << p.z << ")";
    return os;
  }
};

inline double dot(const xyz_t &p1, const xyz_t &p2){
  return p1.x * p2.x + p1.y * p2.y + p1.z * p2.z;
}

inline double norm(const xyz_t &p){
  return dot(p, p);
}

inline double abs(const xyz_t &p){
  return sqrt(norm(p));
}

xyz_t direction(const xyz_t &p1, const xyz_t &p2){
  xyz_t vec;
  vec.x = p2.x - p1.x;
  vec.y = p2.y - p1.y;
  vec.z = p2.z - p1.z;
  return vec;
}

void normalize(xyz_t &p){
  double l = abs(p);
  p.x /= l; p.y /= l; p.z /= l;
}

xyz_t operator+(const xyz_t &p1, const xyz_t &p2){
  xyz_t p;
  p.x = p1.x + p2.x;
  p.y = p1.y + p2.y;
  p.z = p1.z + p2.z;
  return p;
}

xyz_t operator-(const xyz_t &p1, const xyz_t &p2){
  xyz_t p;
  p.x = p1.x - p2.x;
  p.y = p1.y - p2.y;
  p.z = p1.z - p2.z;
  return p;
}

xyz_t operator*(const xyz_t &p1, double scale){
  xyz_t p;
  p.x = p1.x * scale;
  p.y = p1.y * scale;
  p.z = p1.z * scale;
  return p;
}


double distance(const xyz_t &p1, const xyz_t &p2){
  return abs(p2 - p1);
}

bool intersect(xyz_t curr, xyz_t dir, const pair<xyz_t, double> &C){
  normalize(dir);
  double t = dot(direction(curr, C.first), dir);
  xyz_t  p = curr + dir * t;
  // cout << t << " " << p << endl;
  return t > 0 && distance(p, C.first) < C.second;
}

xyz_t crossing_point(xyz_t curr, xyz_t dir, const pair<xyz_t, double> &C){
  normalize(dir);
  double t = dot(direction(curr, C.first), dir);
  xyz_t  p = curr + dir * t;
  double d = sqrt(C.second * C.second - norm(p - C.first));
  return curr + dir * (t - d);
  
}

int main(){
  ios::sync_with_stdio(false);
  int N;
  while (cin >> N && N){
    xyz_t vec;
    vector<pair<xyz_t, double> > cs(N);
    cin >> vec.x >> vec.y >> vec.z;
    
    REP(i, N){
      cin >> cs[i].first.x >> cs[i].first.y >> cs[i].first.z >> cs[i].second;
    }
    
    xyz_t last;
    last.x = last.y = last.z = 0;
    
    while (true){
      // cout << last << endl;
      vector<tuple<double, int, xyz_t> > ps;
      
      REP(i, N) if (intersect(last, vec, cs[i])){
        // cout << last << " " << vec << " " << cs[i].first << " " << cs[i].second << endl;
        xyz_t p = crossing_point(last, vec, cs[i]);
        // cout << p << endl;
        ps.push_back(make_tuple(distance(p, last), i, p));
      }
      sort(ALL(ps));
      if (ps.empty()){
        break;
      }

      xyz_t a = get<2>(ps[0]) - cs[get<1>(ps[0])].first;
      xyz_t v = last - get<2>(ps[0]);
      vec = a * 2 * (dot(a, v) / dot(a, a)) - v;
      normalize(vec);
      // cout << a << " " << v << " " << dot(a, v) << " " << get<2>(ps[0]) << " " << vec << endl;
      // break;
      
      last = get<2>(ps[0]);
    }
    
    printf("%.10lf %.10lf %.10lf\n", last.x, last.y, last.z);
  }
  return 0;
}

Measured memory_bytes: 1276