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

#define dump(...) (cerr<<#__VA_ARGS__<<" = "<<(DUMP(),__VA_ARGS__).str()<<endl)

struct DUMP : ostringstream {
	template<class T> DUMP &operator,(const T &t) {
		if(this->tellp()) *this << ", ";
		*this << t;
		return *this;
	}
};

template<class T> inline void chmax(T &a, const T &b) { if(b > a) a = b; }
template<class T> inline void chmin(T &a, const T &b) { if(b < a) a = b; }

template<class Iterator>
ostream &dump_range(ostream &, Iterator, const Iterator &);

template<class T>
ostream &operator<<(ostream &os, const vector<T> &c) {
	return dump_range(os, c.cbegin(), c.cend());
}

template<class Iterator>
ostream &dump_range(ostream &os, Iterator first, const Iterator &last) {
	os << '[';
	for(int i = 0; first != last; ++i, ++first) {
		if(i) os << ", ";
		os << *first;
	}
	return os << ']';
}

struct point {
	int ix, iy, iz;
	double dx, dy, dz;
	point(double x = 0, double y = 0, double z = 0):ix(x), iy(y), iz(z),
													dx(x), dy(y), dz(z){}

	inline bool operator<(const point &p) const {
		return ix != p.ix ? ix < p.ix : iy != p.iy ? iy < p.iy : iz < p.iz;
	}
	inline bool operator==(const point &p) const {
		return ix == p.ix && iy == p.iy && iz == p.iz;
	}

	inline point &operator/=(double d) {
		dx /= d;
		dy /= d;
		dz /= d;
		return *this;
	}
};

inline ostream& operator<<(ostream &os, const point &p) {
	return os << p.ix << ' ' << p.iy << ' ' << p.iz;
}

inline double abs(const point &p) {
	return sqrt(p.dx * p.dx + p.dy * p.dy + p.dz * p.dz);
}

inline double dist(const point &a, const point &b) {
	const double diff_x = a.dx - b.dx;
	const double diff_y = a.dy - b.dy;
	const double diff_z = a.dz - b.dz;
	return sqrt(diff_x * diff_x + diff_y * diff_y + diff_z * diff_z);
}

namespace std {
	template<>
	struct hash<point> {
		inline size_t operator() (const point &p) const {
			return (p.ix << 20) | (p.iy << 10) | p.iz;
		}
	};
}

unordered_multimap<point, point>
make_grid(double width, const vector<point> &points, int n) {
	chmin<int>(n, points.size());
	unordered_multimap<point, point> grid;
	for(int i = 0; i < n; ++i) {
		const int x = points[i].dx / width;
		const int y = points[i].dy / width;
		const int z = points[i].dz / width;
		grid.insert(make_pair(point(x, y, z), points[i]));
	}
	return grid;
}

pair<point, point> make_ans(const point &a, const point &b) {
	return make_tuple(a.ix, a.iy, a.iz) < make_tuple(b.ix, b.iy, b.iz) ?
										  make_pair(a, b) : make_pair(b, a);
}

tuple<int, int, int> to_tuple(const point &a) {
	const int g = __gcd(a.ix, __gcd(a.iy, a.iz));
	return make_tuple(a.ix / g, a.iy / g, a.iz / g);
}

inline bool zero_angle(const point &a, const point &b) {
	return to_tuple(a) == to_tuple(b);
}

inline pair<point, point> nearest_points(vector<point> points) {
	constexpr double EPS = 1e-15;
	const int n = points.size();
	assert(n >= 2);

	random_shuffle(points.begin(), points.end());
	double width = dist(points[0], points[1]);
	while(zero_angle(points[0], points[1])) {
		if(points[0] < points[1])
			points.erase(points.begin() + 1);
		else
			points.erase(points.begin());
		width = dist(points[0], points[1]);
		dump(width);
	}
	auto ans = make_ans(points[0], points[1]);
	auto grid = make_grid(width, points, 2);

	for(int i = 2; i < n; ++i) {
		const int x = points[i].dx / width;
		const int y = points[i].dy / width;
		const int z = points[i].dz / width;

		double nearest_dist = INT_MAX;
		point nearest;
		for(int dx = -1; dx <= 1; ++dx) {
			for(int dy = -1; dy <= 1; ++dy) {
				for(int dz = -1; dz <= 1; ++dz) {
					auto range = grid.equal_range(point(x + dx, y + dy, z + dz));
					while(range.first != range.second) {
						if(zero_angle(points[i], range.first->second)) {
							++range.first;
							continue;
						}

						const double d = dist(points[i], range.first->second);
						if(d + EPS < nearest_dist) {
							nearest_dist = d;
							nearest = range.first->second;
						}
						else if(abs(nearest_dist - d) < EPS && range.first->second < nearest) {
							nearest = range.first->second;
						}
						++range.first;
					}
				}
			}
		}

		auto candidate = make_ans(points[i], nearest);

		if(nearest_dist + EPS < width) {
			ans = move(candidate);
			width = nearest_dist;
			grid = make_grid(width, points, i + 1);
		}
		else {
			if(abs(width - nearest_dist) < EPS && candidate < ans)
				ans = move(candidate);
			grid.insert(make_pair(point(x, y, z), points[i]));
		}
	}
	return ans;
}

int main() {
	cin.tie(nullptr);
	ios::sync_with_stdio(false);

	for(int m, n, s, w; cin >> m >> n >> s >> w && s;) {
		vector<point> points;
		points.reserve(m + n);
		for(int i = 0; i < m; ++i) {
			int x, y, z;
			cin >> x >> y >> z;
			points.emplace_back(x, y, z);
		}

		int g = s;
		for(int i = 0; i < n; ++i) {
			int x = (g / 7) % 100 + 1;
			int y = (g / 700) % 100 + 1;
			int z = (g / 70000) % 100 + 1;
			points.emplace_back(x, y, z);
			if(g & 1) g	= (g / 2) ^ w;
			else	  g = (g / 2);
		}

		for_each(points.begin(), points.end(), [](point &p) { p /= abs(p); });

		auto ans = nearest_points(move(points));
		cout << ans.first << " " << ans.second << endl;
	}

	return EXIT_SUCCESS;
}

Measured memory_bytes: 25732