This is an easier version of the problem. In this version, $n \le 2000$.

There are $n$ distinct points in three-dimensional space numbered from $1$ to $n$. The $i$-th point has coordinates $(x_i, y_i, z_i)$. The number of points $n$ is even.

You'd like to remove all $n$ points using a sequence of $\frac{n}{2}$ snaps. In one snap, you can remove any two points $a$ and $b$ that have not been removed yet and form a perfectly balanced pair. A pair of points $a$ and $b$ is perfectly balanced if no other point $c$ (that has not been removed yet) lies within the axis-aligned minimum bounding box of points $a$ and $b$.

Formally, point $c$ lies within the axis-aligned minimum bounding box of points $a$ and $b$ if and only if $\min(x_a, x_b) \le x_c \le \max(x_a, x_b)$, $\min(y_a, y_b) \le y_c \le \max(y_a, y_b)$, and $\min(z_a, z_b) \le z_c \le \max(z_a, z_b)$. Note that the bounding box might be degenerate. 

Find a way to remove all points in $\frac{n}{2}$ snaps.


-----Input-----

The first line contains a single integer $n$ ($2 \le n \le 2000$; $n$ is even), denoting the number of points.

Each of the next $n$ lines contains three integers $x_i$, $y_i$, $z_i$ ($-10^8 \le x_i, y_i, z_i \le 10^8$), denoting the coordinates of the $i$-th point.

No two points coincide.


-----Output-----

Output $\frac{n}{2}$ pairs of integers $a_i, b_i$ ($1 \le a_i, b_i \le n$), denoting the indices of points removed on snap $i$. Every integer between $1$ and $n$, inclusive, must appear in your output exactly once.

We can show that it is always possible to remove all points. If there are many solutions, output any of them.


-----Examples-----
Input
6
3 1 0
0 3 0
2 2 0
1 0 0
1 3 0
0 1 0

Output
3 6
5 1
2 4

Input
8
0 1 1
1 0 1
1 1 0
1 1 1
2 2 2
3 2 2
2 3 2
2 2 3

Output
4 5
1 6
2 7
3 8



-----Note-----

In the first example, here is what points and their corresponding bounding boxes look like (drawn in two dimensions for simplicity, as all points lie on $z = 0$ plane). Note that order of removing matters: for example, points $5$ and $1$ don't form a perfectly balanced pair initially, but they do after point $3$ is removed.  [Image]

Difficulty: MEDIUM_HARD
Tags: Geometry

Constructive algorithms

Greedy algorithms
Skill types: Greedy algorithms

Starter code:

Solutions:
n = int(input())
d = {}
c = 1
while n:
	n -= 1
	l = tuple(map(int, input().split()))
	d[c] = l
	c += 1
for i in d:
	if d[i] != -1:
		ans = 1e+18
		k = [-1, -1]
		x1 = d[i][0]
		y1 = d[i][1]
		z1 = d[i][2]
		for j in d:
			if d[j] != -1 and i != j:
				x2 = d[j][0]
				y2 = d[j][1]
				z2 = d[j][2]
				aa = abs(x1 - x2) + abs(y1 - y2) + abs(z1 - z2)
				if aa < ans:
					ans = aa
					k[0] = i
					k[1] = j
		print(k[0], k[1])
		d[k[0]] = -1
		d[k[1]] = -1


n = int(input())
pts = []
for i in range(n):
	(a, b, c) = map(int, input().split())
	pts.append((a, b, c, i))
dicter = [(float('inf'), 0)] * n
was2 = [0] * n
for el1 in pts:
	if not was2[el1[3]]:
		for el2 in pts:
			if not was2[el2[3]]:
				if el1[3] != el2[3]:
					ku = abs(el1[0] - el2[0]) + abs(el1[1] - el2[1]) + abs(el1[2] - el2[2])
					if ku < dicter[el1[3]][0]:
						dicter[el1[3]] = (ku, el2[3])
		was2[el1[3]] = 1
		was2[dicter[el1[3]][1]] = 1
was = [0] * n
for i in range(n):
	if dicter[i][0] != float('inf'):
		print(dicter[i][1] + 1, i + 1)


import os
import sys
from io import BytesIO, IOBase
BUFSIZE = 8192

class FastIO(IOBase):
	newlines = 0

	def __init__(self, file):
		self._fd = file.fileno()
		self.buffer = BytesIO()
		self.writable = 'x' in file.mode or 'r' not in file.mode
		self.write = self.buffer.write if self.writable else None

	def read(self):
		while True:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			if not b:
				break
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines = 0
		return self.buffer.read()

	def readline(self):
		while self.newlines == 0:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			self.newlines = b.count(b'\n') + (not b)
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines -= 1
		return self.buffer.readline()

	def flush(self):
		if self.writable:
			os.write(self._fd, self.buffer.getvalue())
			(self.buffer.truncate(0), self.buffer.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')
(sys.stdin, sys.stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))
input = lambda : sys.stdin.readline().rstrip('\r\n')
n = int(input())
l = []
for i in range(n):
	(a, b, c) = map(int, input().split())
	l.append([a, b, c])
s = 0
n = len(l)
f = set()
while s < n:
	if s in f:
		s += 1
		continue
	f.add(s)
	t = 0
	ind = 0
	(xa, ya, za) = (l[s][0], l[s][1], l[s][2])
	for i in range(n):
		if i in f:
			continue
		elif t == 0:
			(xb, yb, zb) = (l[i][0], l[i][1], l[i][2])
			ind = i
			f.add(i)
			t = 1
		elif t == 1:
			(xc, yc, zc) = (l[i][0], l[i][1], l[i][2])
			if min(xa, xb) <= xc <= max(xa, xb) and min(ya, yb) <= yc <= max(ya, yb) and (min(za, zb) <= zc <= max(za, zb)):
				(xb, yb, zb) = (l[i][0], l[i][1], l[i][2])
				f.remove(ind)
				ind = i
				f.add(i)
	print(s + 1, ind + 1)
	s += 1


n = int(input())
arr = []
for _ in range(n):
	(a, b, c) = map(int, input().split())
	arr.append([a, b, c] + [_ + 1])
arr.sort()
ans = []
for i in range(2, -1, -1):
	st = []
	for x in arr:
		if st and all((st[-1][itr] <= x[itr] for itr in range(i + 1))):
			ans.append((st.pop()[-1], x[-1]))
		else:
			st.append(x)
	arr = st
for i in ans:
	print(i[0], i[1])


import sys
input = sys.stdin.readline
write = sys.stdout.write
n = int(input())
point = []
for i in range(n):
	point.append(list(map(int, input().split())) + [i + 1])
for _ in range(n // 2):
	(ax, ay, az, a) = point.pop()
	temp = 10 ** 18
	next = -1
	for i in range(len(point)):
		(bx, by, bz, _) = point[i]
		dist = (bx - ax) * (bx - ax) + (by - ay) * (by - ay) + (bz - az) * (bz - az)
		if temp >= dist:
			next = i
			temp = dist
	b = point.pop(next)
	print(a, b[3])


import sys
input = lambda : sys.stdin.readline().rstrip()
N = int(input())
D = {}
ans = []
for i in range(N):
	(x, y, z) = map(int, input().split())
	if z in D:
		if y in D[z]:
			D[z][y].append((x, i))
		else:
			D[z][y] = [(x, i)]
	else:
		D[z] = {y: [(x, i)]}
E = {}
for z in D:
	for y in D[z]:
		D[z][y] = sorted(D[z][y])
		while len(D[z][y]) >= 2:
			(a, b) = (D[z][y].pop(), D[z][y].pop())
			ans.append((a[1], b[1]))
		if len(D[z][y]):
			if z in E:
				E[z].append((y, D[z][y][0][1]))
			else:
				E[z] = [(y, D[z][y][0][1])]
F = []
for z in E:
	E[z] = sorted(E[z])
	while len(E[z]) >= 2:
		(a, b) = (E[z].pop(), E[z].pop())
		ans.append((a[1], b[1]))
	if len(E[z]):
		F.append((z, E[z][0][1]))
F = sorted(F)
while len(F) >= 2:
	(a, b) = (F.pop(), F.pop())
	ans.append((a[1], b[1]))
ans = [(a[0] + 1, a[1] + 1) for a in ans]
sans = [' '.join(map(str, a)) for a in ans]
print('\n'.join(sans))


import sys
from operator import itemgetter

def input():
	return sys.stdin.readline().strip()

def list2d(a, b, c):
	return [[c] * b for i in range(a)]

def list3d(a, b, c, d):
	return [[[d] * c for j in range(b)] for i in range(a)]

def list4d(a, b, c, d, e):
	return [[[[e] * d for j in range(c)] for j in range(b)] for i in range(a)]

def ceil(x, y=1):
	return int(-(-x // y))

def INT():
	return int(input())

def MAP():
	return map(int, input().split())

def LIST(N=None):
	return list(MAP()) if N is None else [INT() for i in range(N)]

def Yes():
	print('Yes')

def No():
	print('No')

def YES():
	print('YES')

def NO():
	print('NO')
sys.setrecursionlimit(10 ** 9)
INF = float('inf')
MOD = 10 ** 9 + 7
N = INT()
XYZ = []
IDs = {}
for i in range(N):
	(x, y, z) = MAP()
	XYZ.append((x, y, z))
	IDs[x, y, z] = i + 1
XYZ.sort(key=itemgetter(2))
Z_XY = []
(x, y, z) = XYZ[0]
XY = [(x, y)]
prevz = z
for i in range(1, N):
	(x, y, z) = XYZ[i]
	if z == prevz:
		XY.append((x, y))
	else:
		Z_XY.append((prevz, XY))
		XY = [(x, y)]
	prevz = z
Z_XY.append((prevz, XY))
Z_Y_X = []
for (z, xys) in Z_XY:
	xys.sort(key=itemgetter(1))
	Y_X = []
	(x, y) = xys[0]
	X = [x]
	prevy = y
	for i in range(1, len(xys)):
		(x, y) = xys[i]
		if y == prevy:
			X.append(x)
		else:
			Y_X.append((prevy, X))
			X = [x]
		prevy = y
	Y_X.append((prevy, X))
	Z_Y_X.append((z, Y_X))
ans = []
tmp = []
for (z, xys) in Z_Y_X:
	for (y, xs) in xys:
		xs.sort()
		for i in range(0, len(xs) - len(xs) % 2, 2):
			x1 = xs[i]
			x2 = xs[i + 1]
			ans.append(IDs[x1, y, z])
			ans.append(IDs[x2, y, z])
	for (y, xs) in xys:
		if len(xs) % 2 == 1:
			x = xs[-1]
			ans.append(IDs[x, y, z])
	if len(ans) % 2 == 1:
		tmp.append(ans.pop())
for i in tmp:
	ans.append(i)
for i in range(0, N, 2):
	(a, b) = (ans[i], ans[i + 1])
	print(a, b)


import sys
import math
input = sys.stdin.readline
n = int(input())
arr = []
for i in range(n):
	arr.append(list(map(int, input().split())))
	arr[-1].append(i + 1)
ans = []
arr.sort()
done = [0] * n
i = 0
j = 1
while i < n and j < n:
	if arr[i][0] == arr[j][0] and arr[i][1] == arr[j][1]:
		ans.append([arr[i][3], arr[j][3]])
		done[arr[i][3] - 1] += 1
		done[arr[j][3] - 1] += 1
		i += 2
		j += 2
	else:
		i += 1
		j += 1
arr1 = []
for i in range(n):
	if done[arr[i][3] - 1] == 0:
		arr1.append(arr[i])
n = len(arr1)
i = 0
j = 1
while i < n and j < n:
	if arr1[i][0] == arr1[j][0]:
		ans.append([arr1[i][3], arr1[j][3]])
		done[arr1[i][3] - 1] += 1
		done[arr1[j][3] - 1] += 1
		i += 2
		j += 2
	else:
		i += 1
		j += 1
arr2 = []
for i in range(n):
	if done[arr1[i][3] - 1] == 0:
		arr2.append(arr1[i])
n = len(arr2)
for i in range(n):
	if i % 2 == 0:
		ans.append([arr2[i][3], arr2[i + 1][3]])
for i in range(len(ans)):
	print(*ans[i])


def main():
	n = int(input())
	lkz = {}
	lky = {}
	for i in range(n):
		(px, py, pz) = map(int, input().split())
		if pz not in lkz:
			lkz[pz] = set()
			lkz[pz].add(py)
			lky[pz, py] = [(pz, py, px, i + 1)]
		else:
			lkz[pz].add(py)
			if (pz, py) not in lky:
				lky[pz, py] = [(pz, py, px, i + 1)]
			else:
				lky[pz, py].append((pz, py, px, i + 1))
	ans = []
	final_list = []
	for pz in lkz:
		curr_list = []
		for py in lkz[pz]:
			if len(lky[pz, py]) > 1:
				lky[pz, py].sort()
				for i in range(0, len(lky[pz, py]) // 2):
					p1 = lky[pz, py][2 * i]
					p2 = lky[pz, py][2 * i + 1]
					ans.append((p1[3], p2[3]))
			if len(lky[pz, py]) % 2 == 1:
				curr_list.append(lky[pz, py][-1])
		if len(curr_list) > 1:
			curr_list.sort()
			for i in range(0, len(curr_list) // 2):
				ans.append((curr_list[2 * i][3], curr_list[2 * i + 1][3]))
		if len(curr_list) % 2 == 1:
			final_list.append(curr_list[-1])
	final_list.sort()
	for i in range(0, len(final_list) // 2):
		ans.append((final_list[2 * i][3], final_list[2 * i + 1][3]))
	for pair in ans:
		print(*pair)
main()


n = int(input())
l = [list(map(int, input().split())) for i in range(n)]
a = [[l[i], i + 1] for i in range(n)]
used = [0] * (n + 1)
a.sort()
d = {}
for i in a:
	d[i[0][0]] = []
for i in a:
	d[i[0][0]].append([i[0][1], i[0][2], i[1]])
for x in d:
	l = d[x]
	b = {}
	for i in l:
		b[i[0]] = []
	for i in l:
		b[i[0]].append([i[1], i[2]])
	for i in l:
		b[i[0]].sort()
	for y in b:
		pom = 0
		while pom + 1 < len(b[y]):
			print(b[y][pom][1], b[y][pom + 1][1])
			used[b[y][pom][1]] = 1
			used[b[y][pom + 1][1]] = 1
			pom += 2
	c = []
	for gg in l:
		if used[gg[2]] == 0:
			c.append(gg)
	cc = [[c[gg][0], c[gg][2]] for gg in range(len(c))]
	cc.sort()
	g = 0
	while g < len(cc) - 1:
		print(cc[g][-1], cc[g + 1][-1])
		used[cc[g][-1]] = 1
		used[cc[g + 1][-1]] = 1
		g += 2
c = []
for i in range(0, n):
	if used[a[i][1]] == 0:
		c.append([a[i][0][0], a[i][1]])
	c.sort()
i = 0
while i < len(c) - 1:
	print(c[i][1], c[i + 1][1])
	i += 2


import sys
n = -1
data = []
for line in sys.stdin:
	if n == -1:
		n = int(line)
		continue
	data.append(tuple([int(i) for i in line.split()]))
	if len(data) == n:
		break
data = list(zip(data, range(1, n + 1)))
data.sort()
data2 = []
removedLast = False
for i in range(n):
	if removedLast:
		removedLast = False
		continue
	if i + 1 < n and data[i][0][0] == data[i + 1][0][0] and (data[i][0][1] == data[i + 1][0][1]):
		print('%d %d' % (data[i][1], data[i + 1][1]))
		removedLast = True
	else:
		data2.append(data[i])
data3 = []
removedLast = False
for i in range(len(data2)):
	if removedLast:
		removedLast = False
		continue
	if i + 1 < len(data2) and data2[i][0][0] == data2[i + 1][0][0]:
		print('%d %d' % (data2[i][1], data2[i + 1][1]))
		removedLast = True
	else:
		data3.append(data2[i])
for i in range(0, len(data3), 2):
	print('%d %d' % (data3[i][1], data3[i + 1][1]))


n = int(input())
lol = []
d = dict()
d1 = dict()
for i in range(n):
	(x, y, z) = map(int, input().split())
	lol.append([x, y, z, i + 1])
lol.sort()
for ii in range(n):
	x = lol[ii][0]
	y = lol[ii][1]
	z = lol[ii][2]
	i = lol[ii][3] - 1
	try:
		d[str([x, y])].append(i + 1)
	except:
		d[str([x, y])] = [i + 1]
	try:
		d1[x].append(i + 1)
	except:
		d1[x] = [i + 1]
vis = [False] * n
ans = []
for i in d.values():
	if len(i) >= 2:
		for j in range(len(i) // 2):
			vis[i[j * 2] - 1] = True
			vis[i[j * 2 + 1] - 1] = True
			ans.append([i[j * 2], i[j * 2 + 1]])
for i in d1.values():
	n1 = len(i)
	if n1 >= 2:
		j = 0
		k = 1
		while k < n1:
			if vis[i[j] - 1] == True:
				j += 1
			if vis[i[k] - 1] == True or j == k:
				k += 1
			if k < n1 and vis[i[k] - 1] == False and (vis[i[j] - 1] == False):
				ans.append([i[k], i[j]])
				vis[i[k] - 1] = True
				vis[i[j] - 1] = True
i = 0
j = 1
c = 0
while j < n:
	if vis[lol[i][3] - 1] == True:
		i += 1
	if vis[lol[j][3] - 1] == True or j == i:
		j += 1
	if j < n and vis[lol[i][3] - 1] == False and (vis[lol[j][3] - 1] == False):
		ans.append([lol[i][3], lol[j][3]])
		vis[lol[i][3] - 1] = True
		vis[lol[j][3] - 1] = True
for i in ans:
	print(*i)


n = int(input())
m = []
for _ in range(n):
	m.append(tuple(map(int, input().split())))
m = list(enumerate(m))
while len(m) > 0:
	p = m[0]
	(x, y, z) = p[1]
	q = sorted(m, key=lambda i: abs(i[1][0] - x) + abs(i[1][1] - y) + abs(i[1][2] - z))[1]
	print(p[0] + 1, q[0] + 1)
	m.remove(q)
	del m[0]


n = int(input())
li = []
for i in range(n):
	(a, b, c) = map(int, input().split())
	li.append([a, b, c, i + 1])
li.sort()
i = 0
l = len(li)
a = []
while i < l:
	if i < l - 1 and li[i][:2] == li[i + 1][:2]:
		print(li[i][3], li[i + 1][3])
		i += 2
	else:
		a.append(li[i])
		i += 1
li = list(a)
l = len(li)
i = 0
a = []
while i < l:
	if i < l - 1 and li[i][0] == li[i + 1][0]:
		print(li[i][3], li[i + 1][3])
		i += 2
	else:
		a.append(li[i])
		i += 1
li = list(a)
l = len(li)
i = 0
while i < l - 1:
	print(li[i][3], li[i + 1][3])
	i += 2


import sys
from collections import defaultdict
readline = sys.stdin.readline
N = int(readline())
XYZ = [tuple(map(int, readline().split())) for _ in range(N)]
XYZI = [(x, y, z, i) for (i, (x, y, z)) in enumerate(XYZ, 1)]
XYZI.sort()
(X, Y, Z, IDX) = map(list, zip(*XYZI))
Di = defaultdict(list)
for i in range(N):
	(x, y, z, idx) = (X[i], Y[i], Z[i], IDX[i])
	Di[x].append((y, z, idx))
Ans = []
Ama = []
for L in Di.values():
	D2 = defaultdict(int)
	for (y, _, _) in L:
		D2[y] += 1
	pre = None
	st = None
	for (y, z, i) in L:
		if st is None and D2[y] == 1:
			if pre is not None:
				Ans.append((pre, i))
				pre = None
			else:
				pre = i
		elif st is not None:
			Ans.append((st, i))
			st = None
		else:
			st = i
		D2[y] -= 1
	if st:
		Ama.append(st)
	if pre:
		Ama.append(pre)
Le = len(Ama)
for i in range(Le // 2):
	Ans.append((Ama[2 * i], Ama[2 * i + 1]))
for a in Ans:
	sys.stdout.write('{} {}\n'.format(*a))


from collections import Counter
import os
import sys
from io import BytesIO, IOBase
BUFSIZE = 8192

class FastIO(IOBase):
	newlines = 0

	def __init__(self, file):
		self._fd = file.fileno()
		self.buffer = BytesIO()
		self.writable = 'x' in file.mode or 'r' not in file.mode
		self.write = self.buffer.write if self.writable else None

	def read(self):
		while True:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			if not b:
				break
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines = 0
		return self.buffer.read()

	def readline(self):
		while self.newlines == 0:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			self.newlines = b.count(b'\n') + (not b)
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines -= 1
		return self.buffer.readline()

	def flush(self):
		if self.writable:
			os.write(self._fd, self.buffer.getvalue())
			(self.buffer.truncate(0), self.buffer.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')
(sys.stdin, sys.stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))
input = lambda : sys.stdin.readline().rstrip('\r\n')
import sys
import math
n = int(input())
c = []
v = [False] * n
for i in range(n):
	(x, y, z) = map(int, input().split())
	c.append((x, y, z))
for i in range(n):
	dis = math.inf
	if v[i] == True:
		continue
	for j in range(i + 1, n):
		d = abs(c[i][0] - c[j][0]) + abs(c[i][1] - c[j][1]) + abs(c[i][2] - c[j][2])
		if d < dis and v[j] == False:
			dis = d
			ans = j
	v[ans] = True
	print(i + 1, ans + 1)


n = int(input())
arr = []
for _ in range(n):
	arr.append(list(map(int, input().split())) + [_ + 1])
arr.sort(key=lambda x: (x[0], x[1], x[2]))
res = []

def valid(pt1, pt2, axis):
	flag = 1
	for k in range(axis + 1):
		if pt1[k] > pt2[k]:
			flag = 0
			break
	return flag
for axis in range(2, -1, -1):
	stc = []
	for pt in arr:
		if stc and valid(stc[-1], pt, axis):
			res.append([stc.pop()[3], pt[3]])
		else:
			stc.append(pt)
	arr = stc
for pr in res:
	print(pr[0], pr[1])


def solve(n, p):
	p = sorted(p, key=lambda x: (x[0], x[1], x[2]))
	p.append([10 ** 20, 10 ** 20, 10 ** 20, 10 ** 20])
	c = 1
	pn = []
	for i in range(1, len(p)):
		if p[i][0] == p[i - 1][0]:
			c += 1
		else:
			if c >= 2:
				s = solve_2d(p[i - c:i])
				if s:
					pn.append(s)
			else:
				pn.append(p[i - 1])
			c = 1
	for i in range(0, len(pn) - 1, 2):
		print(pn[i][3], pn[i + 1][3])

def solve_2d(p1):
	p1.append([10 ** 20, 10 ** 20, 10 ** 20, 10 ** 20])
	c = 1
	p1n = []
	i = 1
	while i < len(p1):
		if p1[i][1] == p1[i - 1][1]:
			print(p1[i][3], p1[i - 1][3])
			i += 2
		else:
			p1n.append(p1[i - 1])
			i += 1
	for i in range(0, len(p1n) - 1, 2):
		print(p1n[i][3], p1n[i + 1][3])
	if len(p1n) % 2 == 1:
		return p1n[-1]
	else:
		return 0

def main():
	n = int(input())
	p = []
	for i in range(n):
		pt = [int(i) for i in input().split()]
		p.append(pt + [i + 1])
	solve(n, p)
main()


n = int(input())
coords = [list(map(int, input().split())) for i in range(n)]
indexes = list(range(1, n + 1))
(coords, indexes) = list(map(list, zip(*sorted(zip(coords, indexes)))))
result = []
while len(coords) > 0:
	a = coords[0]
	b = coords[1]
	aind = 0
	bind = 1
	for i in range(len(coords)):
		if i == aind or i == bind:
			pass
		elif min(a[0], b[0]) <= coords[i][0] <= max(a[0], b[0]) and min(a[1], b[1]) <= coords[i][1] <= max(a[1], b[1]) and (min(a[2], b[2]) <= coords[i][2] <= max(a[2], b[2])):
			b = coords[i]
			bind = i
	result.append(str(indexes[aind]) + ' ' + str(indexes[bind]))
	indexes.pop(aind)
	indexes.pop(bind if aind > bind else bind - 1)
	coords.pop(aind)
	coords.pop(bind if aind > bind else bind - 1)
print('\n'.join(result))


import math

def distance(a, b):
	d = 0.0
	for i in range(3):
		d += (a[i] - b[i]) ** 2
	d = math.sqrt(d)
	return d
t = int(input())
points = []
visited = [0] * t
for z in range(t):
	delta = [float(x) for x in input().split()]
	points.append(delta)
for i in range(t):
	if visited[i] == 1:
		continue
	d = float('inf')
	pair = -1
	for j in range(i + 1, t):
		if visited[j] == 1:
			continue
		delta = distance(points[i], points[j])
		if delta < d:
			pair = j
			d = delta
	visited[i] = 1
	visited[pair] = 1
	print(i + 1, pair + 1)


def solve(n, points):
	points = [p + (i,) for (i, p) in enumerate(points, 1)]
	points += [(10 ** 9,) * 3]
	points.sort()
	ans = []
	rem = []
	i = 0
	while i < n:
		if points[i][:2] == points[i + 1][:2]:
			ans.append((points[i][-1], points[i + 1][-1]))
			i += 2
		else:
			rem.append(i)
			i += 1
	rem += [n]
	rem2 = []
	n = len(rem)
	i = 0
	while i < n - 1:
		if points[rem[i]][0] == points[rem[i + 1]][0]:
			ans.append((points[rem[i]][-1], points[rem[i + 1]][-1]))
			i += 2
		else:
			rem2.append(points[rem[i]][-1])
			i += 1
	for e in ans:
		print(*e)
	for i in range(0, len(rem2), 2):
		print(rem2[i], rem2[i + 1])

def main():
	n = int(input())
	points = [tuple((int(c) for c in input().split())) for _ in range(n)]
	solve(n, points)
main()


from collections import defaultdict

def main():
	points = []
	n = int(input())
	z_to_xy = defaultdict(lambda : [])
	for i in range(n):
		(x, y, z) = [int(x) for x in input().split()]
		points.append([x, y, z, i])
		z_to_xy[z].append([x, y, i])
	rem_global = []
	for z in z_to_xy:
		y_to_x = defaultdict(lambda : [])
		for p in z_to_xy[z]:
			y_to_x[p[1]].append([p[0], p[2]])
		rem = []
		for y in y_to_x:
			temp = sorted(y_to_x[y])
			i = 0
			while i < len(temp) - 1:
				print(temp[i][1] + 1, temp[i + 1][1] + 1)
				i += 2
			if i == len(temp) - 1:
				rem.append([y, temp[i][0], temp[i][1]])
		temp2 = sorted(rem)
		i = 0
		while i < len(temp2) - 1:
			print(temp2[i][-1] + 1, temp2[i + 1][-1] + 1)
			i += 2
		if i == len(temp2) - 1:
			rem_global.append([z, temp2[i][1], temp2[i][0], temp2[i][-1]])
	temp = sorted(rem_global)
	i = 0
	while i < len(temp) - 1:
		print(temp[i][-1] + 1, temp[i + 1][-1] + 1)
		i += 2
py2 = round(0.5)
if py2:
	from future_builtins import ascii, filter, hex, map, oct, zip
	range = xrange
import os, sys
from io import IOBase, BytesIO
BUFSIZE = 8192

class FastIO(BytesIO):
	newlines = 0

	def __init__(self, file):
		self._file = file
		self._fd = file.fileno()
		self.writable = 'x' in file.mode or 'w' in file.mode
		self.write = super(FastIO, self).write if self.writable else None

	def _fill(self):
		s = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
		self.seek((self.tell(), self.seek(0, 2), super(FastIO, self).write(s))[0])
		return s

	def read(self):
		while self._fill():
			pass
		return super(FastIO, self).read()

	def readline(self):
		while self.newlines == 0:
			s = self._fill()
			self.newlines = s.count(b'\n') + (not s)
		self.newlines -= 1
		return super(FastIO, self).readline()

	def flush(self):
		if self.writable:
			os.write(self._fd, self.getvalue())
			(self.truncate(0), self.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		if py2:
			self.write = self.buffer.write
			self.read = self.buffer.read
			self.readline = self.buffer.readline
		else:
			self.write = lambda s: self.buffer.write(s.encode('ascii'))
			self.read = lambda : self.buffer.read().decode('ascii')
			self.readline = lambda : self.buffer.readline().decode('ascii')
(sys.stdin, sys.stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))
input = lambda : sys.stdin.readline().rstrip('\r\n')
main()


import sys
input = sys.stdin.readline
n = int(input())
P = [list(map(int, input().split())) + [i + 1] for i in range(n)]
P.sort(reverse=True)
P2 = []
while P:
	(x, y, z, w) = P.pop()
	if len(P) > 0 and x == P[-1][0] and (y == P[-1][1]):
		(x2, y2, z2, w2) = P.pop()
		print(w, w2)
	else:
		P2.append((x, y, z, w))
P = P2
P.sort(reverse=True)
P2 = []
while P:
	(x, y, z, w) = P.pop()
	if len(P) > 0 and x == P[-1][0]:
		(x2, y2, z2, w2) = P.pop()
		print(w, w2)
	else:
		P2.append((x, y, z, w))
P = P2
for i in range(0, len(P), 2):
	print(P[i][3], P[i + 1][3])


def solve(points, ans, coords, x):
	arr = []
	curr = []
	y = points[0][0]
	for p in points:
		if p[0] == y:
			curr.append((y, p[1], p[2]))
		else:
			arr.append(curr)
			y = p[0]
			curr = [(y, p[1], p[2])]
	arr.append(curr)
	arr1 = []
	for i in arr:
		while len(i) >= 2:
			ans.append((i.pop(0)[2], i.pop(0)[2]))
		if len(i) == 1:
			arr1.append((i[0][0], i[0][1], i[0][2]))
	while len(arr1) >= 2:
		ans.append((arr1.pop(0)[2], arr1.pop(0)[2]))
	coords[x] = arr1[:]

def main():
	n = int(input())
	points = []
	coords = {}
	for i in range(n):
		(x, y, z) = map(int, input().split())
		if x not in coords.keys():
			coords[x] = [(y, z, i + 1)]
		else:
			coords[x].append((y, z, i + 1))
	ans = []
	for i in coords.keys():
		coords[i].sort()
		if len(coords[i]) > 1:
			solve(coords[i], ans, coords, i)
		if len(coords[i]) == 1:
			points.append((i, coords[i][0][0], coords[i][0][1], coords[i][0][2]))
	points.sort()
	for i in range(0, len(points), 2):
		(a, b) = (points[i][3], points[i + 1][3])
		ans.append((a, b))
	for i in ans:
		print(i[0], i[1])
main()


import sys
input = sys.stdin.readline
INF = float('inf')

def compress(array):
	array2 = sorted(set(array))
	memo = {value: index for (index, value) in enumerate(array2)}
	for i in range(len(array)):
		array[i] = memo[array[i]]
	return array

def distance(a, b):
	(ax, ay, az) = a
	(bx, by, bz) = b
	dist = ((ax - bx) ** 2 + (ay - by) ** 2 + (az - bz) ** 2) ** 0.5
	return dist
n = int(input())
a = [list(map(int, input().split())) for i in range(n)]
a_t = list(map(list, zip(*a)))
a_t[0] = compress(a_t[0])
a_t[1] = compress(a_t[1])
a_t[2] = compress(a_t[2])
a = list(map(list, zip(*a_t)))
used = [False] * n
for i in range(n):
	if used[i]:
		continue
	used[i] = True
	dist = INF
	ind = -1
	for j in range(n):
		if used[j]:
			continue
		tmp_dist = distance(a[i], a[j])
		if tmp_dist < dist:
			ind = j
			dist = tmp_dist
	used[ind] = True
	print(i + 1, ind + 1)


import sys
n = int(input())
d = [tuple([int(i) for i in sys.stdin.readline().split()]) for j in range(n)]
index = {}
for i in range(n):
	index[d[i]] = i + 1
d.sort()
untaken = []
dic = {}
i = 0
while i < n:
	first = d[i][0]
	if first not in dic:
		dic[first] = []
	dic[first].append((d[i][1], d[i][2]))
	i += 1
for dd in dic:
	e = dic[dd]
	nn = len(e)
	unt = []
	i = 0
	while i < nn:
		if i < nn - 1 and e[i][0] == e[i + 1][0]:
			sys.stdout.write(str(index[(dd,) + e[i]]) + ' ' + str(index[(dd,) + e[i + 1]]) + '\n')
			i += 2
		else:
			unt.append(i)
			i += 1
	for j in range(0, len(unt) - 1, 2):
		sys.stdout.write(str(index[(dd,) + e[unt[j]]]) + ' ' + str(index[(dd,) + e[unt[j + 1]]]) + '\n')
	if len(unt) & 1:
		untaken.append((dd,) + e[unt[-1]])
for i in range(0, len(untaken), 2):
	sys.stdout.write(str(index[untaken[i]]) + ' ' + str(index[untaken[i + 1]]) + '\n')


import math

def modinv(n, p):
	return pow(n, p - 2, p)

def dist(x1, y1, z1, x2, y2, z2):
	return abs(x2 - x1) + abs(y2 - y1) + abs(z2 - z1)

def main():
	n = int(input())
	points = []
	for i in range(n):
		pts = [int(x) for x in input().split()]
		pts.append(i)
		points.append(pts)
	answers = []
	visited = [0] * (n + 1)
	for i in range(n):
		if visited[i] == 1:
			continue
		min_d = 1e+18
		min_j = -1
		(x1, y1, z1, temp) = points[i]
		for j in range(i + 1, n):
			if visited[j] == 1:
				continue
			(x2, y2, z2, temp2) = points[j]
			if dist(x1, y1, z1, x2, y2, z2) < min_d:
				min_d = dist(x1, y1, z1, x2, y2, z2)
				min_j = j
		if min_j != -1:
			answers.append([points[i][-1], points[min_j][-1]])
			visited[points[i][-1]] = 1
			visited[points[min_j][-1]] = 1
	for p in answers:
		print(p[0] + 1, p[1] + 1)
py2 = round(0.5)
if py2:
	from future_builtins import ascii, filter, hex, map, oct, zip
	range = xrange
import os, sys
from io import IOBase, BytesIO
BUFSIZE = 8192

class FastIO(BytesIO):
	newlines = 0

	def __init__(self, file):
		self._file = file
		self._fd = file.fileno()
		self.writable = 'x' in file.mode or 'w' in file.mode
		self.write = super(FastIO, self).write if self.writable else None

	def _fill(self):
		s = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
		self.seek((self.tell(), self.seek(0, 2), super(FastIO, self).write(s))[0])
		return s

	def read(self):
		while self._fill():
			pass
		return super(FastIO, self).read()

	def readline(self):
		while self.newlines == 0:
			s = self._fill()
			self.newlines = s.count(b'\n') + (not s)
		self.newlines -= 1
		return super(FastIO, self).readline()

	def flush(self):
		if self.writable:
			os.write(self._fd, self.getvalue())
			(self.truncate(0), self.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		if py2:
			self.write = self.buffer.write
			self.read = self.buffer.read
			self.readline = self.buffer.readline
		else:
			self.write = lambda s: self.buffer.write(s.encode('ascii'))
			self.read = lambda : self.buffer.read().decode('ascii')
			self.readline = lambda : self.buffer.readline().decode('ascii')
(sys.stdin, sys.stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))
input = lambda : sys.stdin.readline().rstrip('\r\n')
main()


import os
import sys
from io import BytesIO, IOBase
from collections import defaultdict

def main():
	n = int(input())
	b = defaultdict(list)
	for i in range(n):
		(x, y, z) = map(int, input().split())
		b[z].append((x, y, i + 1))
	dd = []
	ans = []
	for i in sorted(b.keys()):
		c = defaultdict(list)
		for j in b[i]:
			c[j[1]].append((j[0], j[2]))
		d = []
		for j in sorted(c.keys()):
			z = c[j]
			z.sort(key=lambda x: x[0])
			if len(z) % 2:
				d.append(z.pop()[-1])
			for k in range(0, len(z), 2):
				ans.append((z[k][-1], z[k + 1][-1]))
		if len(d) % 2:
			dd.append(d.pop())
		for j in range(0, len(d), 2):
			ans.append((d[j], d[j + 1]))
	for i in range(0, len(dd), 2):
		ans.append((dd[i], dd[i + 1]))
	for i in ans:
		print(*i)
BUFSIZE = 8192

class FastIO(IOBase):
	newlines = 0

	def __init__(self, file):
		self._fd = file.fileno()
		self.buffer = BytesIO()
		self.writable = 'x' in file.mode or 'r' not in file.mode
		self.write = self.buffer.write if self.writable else None

	def read(self):
		while True:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			if not b:
				break
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines = 0
		return self.buffer.read()

	def readline(self):
		while self.newlines == 0:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			self.newlines = b.count(b'\n') + (not b)
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines -= 1
		return self.buffer.readline()

	def flush(self):
		if self.writable:
			os.write(self._fd, self.buffer.getvalue())
			(self.buffer.truncate(0), self.buffer.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')
(sys.stdin, sys.stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))
input = lambda : sys.stdin.readline().rstrip('\r\n')
main()


n = int(input())
p = [list(map(int, input().split())) + [i] for i in range(n)] + [[10 ** 9, 10 ** 9, 10 ** 9]]
p.sort(key=lambda x: tuple(x[:-1]))
rem = []
i = 0
while i < n:
	if p[i][:2] == p[i + 1][:2]:
		print(p[i][-1] + 1, p[i + 1][-1] + 1)
		i += 2
	else:
		rem.append(p[i])
		i += 1
rem += [p[n]]
rem2 = []
n = len(rem)
i = 0
while i < n - 1:
	if rem[i][0] == rem[i + 1][0]:
		print(rem[i][-1] + 1, rem[i + 1][-1] + 1)
		i += 2
	else:
		rem2.append(rem[i])
		i += 1
for i in range(0, len(rem2), 2):
	print(rem2[i][-1] + 1, rem2[i + 1][-1] + 1)


def calcManhattan(i, j, coords):
	if j == -1:
		return inf
	(x1, y1, z1) = coords[i]
	(x2, y2, z2) = coords[j]
	return abs(x1 - x2) + abs(y1 - y2) + abs(z1 - z2)

def main():
	n = int(input())
	coords = []
	for i in range(1, n + 1):
		(x, y, z) = readIntArr()
		coords.append([x, y, z])
	visited = [False for _ in range(n)]
	ans = []
	for i in range(n):
		if visited[i]:
			continue
		J = -1
		for j in range(i + 1, n):
			if visited[j]:
				continue
			if calcManhattan(i, j, coords) < calcManhattan(i, J, coords):
				J = j
		visited[i] = visited[J] = True
		ans.append([i + 1, J + 1])
	multiLineArrayOfArraysPrint(ans)
	return
import sys
input = lambda : sys.stdin.readline().rstrip('\r\n')

def oneLineArrayPrint(arr):
	print(' '.join([str(x) for x in arr]))

def multiLineArrayPrint(arr):
	print('\n'.join([str(x) for x in arr]))

def multiLineArrayOfArraysPrint(arr):
	print('\n'.join([' '.join([str(x) for x in y]) for y in arr]))

def readIntArr():
	return [int(x) for x in input().split()]

def makeArr(defaultValFactory, dimensionArr):
	dv = defaultValFactory
	da = dimensionArr
	if len(da) == 1:
		return [dv() for _ in range(da[0])]
	else:
		return [makeArr(dv, da[1:]) for _ in range(da[0])]

def queryInteractive(i, j):
	print('? {} {}'.format(i, j))
	sys.stdout.flush()
	return int(input())

def answerInteractive(ans):
	print('! {}'.format(' '.join([str(x) for x in ans])))
	sys.stdout.flush()
inf = float('inf')
MOD = 10 ** 9 + 7
for _abc in range(1):
	main()


n = int(input())
coords = n * [-1]
for i in range(n):
	coords[i] = [int(i) for i in input().split()] + [i]
coords.sort()
i = 0
while i < len(coords) - 1:
	if coords[i][:2] == coords[i + 1][:2]:
		print(coords[i][3] + 1, coords[i + 1][3] + 1)
		coords.pop(i)
		coords.pop(i)
		i -= 1
	i += 1
i = 0
while i < len(coords) - 1:
	if coords[i][:1] == coords[i + 1][:1]:
		print(coords[i][3] + 1, coords[i + 1][3] + 1)
		coords.pop(i)
		coords.pop(i)
		i -= 1
	i += 1
while len(coords) > 0:
	print(coords.pop()[3] + 1, coords.pop()[3] + 1)


n = int(input())
coord = []
for i in range(n):
	coord.append(tuple(list(map(int, input().split())) + [i + 1]))
coord.sort()

def odind(l):
	l.sort()
	for i in range(len(l) // 2):
		print(l[2 * i][-1], l[2 * i + 1][-1])
	if len(l) % 2 == 0:
		return []
	return l[-1]

def dvad(l):
	cx = {}
	ans1 = []
	for (x, y, n) in l:
		if x in cx:
			cx[x].append([y, n])
		else:
			cx[x] = [[y, n]]
	for (x, j) in cx.items():
		v = odind(j)
		if len(v) != 0:
			ans1.append(tuple([x] + v))
	ans1.sort()
	for i in range(len(ans1) // 2):
		print(ans1[2 * i][-1], ans1[2 * i + 1][-1])
	if len(ans1) % 2 == 0:
		return []
	return ans1[-1]

def trid(l):
	cx = {}
	ans1 = []
	for (x, y, z, n) in l:
		if x in cx:
			cx[x].append((y, z, n))
		else:
			cx[x] = [(y, z, n)]
	for (x, j) in cx.items():
		v = list(dvad(j))
		if len(v) != 0:
			ans1.append(tuple([x] + v))
	ans1.sort()
	for i in range(len(ans1) // 2):
		print(ans1[2 * i][-1], ans1[2 * i + 1][-1])
trid(coord)


n = int(input())
a = [tuple(map(int, input().split())) for _ in range(n)]
s = {a[q]: q + 1 for q in range(n)}
a.sort()
(q, q1) = (0, 1)
(d, d1, d2) = ([[[a[0]]]], [], [])
while q1 < n:
	while q1 < n and a[q][0] == a[q1][0]:
		while q1 < n and a[q][1] == a[q1][1]:
			d[-1][-1].append(a[q1])
			q1 += 1
		if q1 < n and a[q][0] == a[q1][0]:
			d[-1].append([a[q1]])
			q = q1
			q1 += 1
	if q1 < n:
		d.append([[a[q1]]])
		q = q1
		q1 += 1
for q in range(len(d)):
	for q1 in range(len(d[q])):
		for q2 in range(1, len(d[q][q1]), 2):
			print(s[d[q][q1][q2 - 1]], s[d[q][q1][q2]])
		if len(d[q][q1]) % 2 == 1:
			d[q][q1] = d[q][q1][-1]
		else:
			d[q][q1] = -1
for q in range(len(d)):
	d1.append([])
	for q1 in range(len(d[q])):
		if d[q][q1] != -1:
			d1[-1].append(d[q][q1])
for q in range(len(d1)):
	for q1 in range(1, len(d1[q]), 2):
		print(s[d1[q][q1 - 1]], s[d1[q][q1]])
	if len(d1[q]) % 2 == 1:
		d2.append(d1[q][-1])
for q in range(1, len(d2), 2):
	print(s[d2[q - 1]], s[d2[q]])


import sys, os, io
from sys import stdin
from math import log, gcd, ceil
from collections import defaultdict, deque, Counter
from heapq import heappush, heappop, heapify
from bisect import bisect_left, bisect_right
import math
alphabets = list('abcdefghijklmnopqrstuvwxyz')
from types import GeneratorType

def bootstrap(f, stack=[]):

	def wrappedfunc(*args, **kwargs):
		if stack:
			return f(*args, **kwargs)
		else:
			to = f(*args, **kwargs)
			while True:
				if type(to) is GeneratorType:
					stack.append(to)
					to = next(to)
				else:
					stack.pop()
					if not stack:
						break
					to = stack[-1].send(to)
			return to
	return wrappedfunc

def ncr(n, r, p):
	num = den = 1
	for i in range(r):
		num = num * (n - i) % p
		den = den * (i + 1) % p
	return num * pow(den, p - 2, p) % p

def primeFactors(n):
	l = []
	while n % 2 == 0:
		l.append(2)
		n = n / 2
	for i in range(3, int(math.sqrt(n)) + 1, 2):
		while n % i == 0:
			l.append(int(i))
			n = n / i
	if n > 2:
		l.append(n)
	c = dict(Counter(l))
	return list(set(l))

def power(x, y, p):
	res = 1
	x = x % p
	if x == 0:
		return 0
	while y > 0:
		if y & 1 == 1:
			res = res * x % p
		y = y >> 1
		x = x * x % p
	return res

def sieveForSmallestPrimeFactor():
	MAXN = 100001
	spf = [0 for i in range(MAXN)]
	spf[1] = 1
	for i in range(2, MAXN):
		spf[i] = i
	for i in range(4, MAXN, 2):
		spf[i] = 2
	for i in range(3, math.ceil(math.sqrt(MAXN))):
		if spf[i] == i:
			for j in range(i * i, MAXN, i):
				if spf[j] == j:
					spf[j] = i
	return spf

def getPrimeFactorizationLOGN(x):
	spf = sieveForSmallestPrimeFactor()
	ret = list()
	while x != 1:
		ret.append(spf[x])
		x = x // spf[x]
	return ret

def SieveOfEratosthenes(n):
	prime = [True for i in range(n + 1)]
	p = 2
	while p * p <= n:
		if prime[p] == True:
			for i in range(p * p, n + 1, p):
				prime[i] = False
		p += 1
	return prime

def si():
	return input()

def divideCeil(n, x):
	if n % x == 0:
		return n // x
	return n // x + 1

def ii():
	return int(input())

def li():
	return list(map(int, input().split()))
if os.path.exists('input.txt'):
	sys.stdin = open('input.txt', 'r')
	sys.stdout = open('output.txt', 'w')
else:
	input = io.BytesIO(os.read(0, os.fstat(0).st_size)).readline

def dis(x1, y1, z1, x2, y2, z2):
	x = abs(x1 - x2)
	y = abs(y1 - y2)
	z = abs(z1 - z2)
	ans = x * x + y * y + z * z
	ans = ans ** 0.5
	return ans

def solve():
	n = ii()
	s = set()
	l = []
	for i in range(n):
		l.append(tuple(li()))
	ans = []
	for i in range(n):
		if i in s:
			continue
		s.add(i)
		mindis = float('inf')
		minind = 0
		for j in range(n):
			if j not in s:
				distance = dis(l[i][0], l[i][1], l[i][2], l[j][0], l[j][1], l[j][2])
				if distance < mindis:
					mindis = distance
					minind = j
		ans.append([i + 1, minind + 1])
		s.add(minind)
	for i in ans:
		print(*i)
t = 1
for _ in range(t):
	solve()


n = int(input())
d = {}
f = {}
a = []
for i in range(n):
	x = [p for p in input().split()]
	l = []
	for j in range(3):
		l.append(int(x[j]))
	a.append(l)
	d['*'.join(x)] = i + 1
a.sort()
i = 0
for k in range(0, n // 2):
	for i in range(0, len(a) - 1):
		flag = 0
		ymin = min(a[i + 1][1], a[i][1])
		ymax = max(a[i + 1][1], a[i][1])
		zmin = min(a[i][2], a[i + 1][2])
		zmax = max(a[i][2], a[i + 1][2])
		for j in range(i + 2, len(a)):
			if a[j][0] > a[i + 1][0]:
				break
			if ymin <= a[j][1] <= ymax and zmin <= a[j][2] <= zmax:
				flag = 1
				break
		if flag == 0:
			b = []
			for ii in range(3):
				b.append(str(a[i][ii]))
			c = []
			for ii in range(3):
				c.append(str(a[i + 1][ii]))
			print(d['*'.join(b)], d['*'.join(c)])
			p = a[i + 1]
			a.remove(a[i])
			a.remove(p)
			break


from math import *
from sys import *
from heapq import *
from collections import defaultdict
import os, sys
from io import IOBase, BytesIO
M = 10 ** 9 + 7

def graph_as_tree_1_root():
	n = int(input())
	dict = defaultdict(list)
	for _ in range(n - 1):
		(x, y) = list(map(int, input().split()))
		dict[x].append(y)
		dict[y].append(x)
	tree = [[1]]
	temp = [1]
	vall = 0
	seen = set([i for i in range(2, n + 1)])
	lol = []
	while len(seen) > 0:
		lol = list(seen)
		tt = []
		for x in temp:
			aa = []
			for val in dict[x]:
				if val in seen:
					aa += [val]
					seen.remove(val)
			dict[x] = aa
			tt += aa
		tree.append(tt)
		temp = tt
		vall += 1
	for x in lol:
		dict[x] = []
	print(tree, dict)

def pow(a, b):
	res = 1
	while b > 0:
		if b & 1:
			res *= a
		a *= a
		b >>= 1
	return res

def powmod(a, b, m):
	res = 1
	while b > 0:
		if b & 1:
			res = res * a % m
		a *= a
		b >>= 1
	return res

def inv(a, m):
	return powmod(a, m - 2, m)

def alldivisors(n):
	list = []
	arr = []
	for i in range(1, int(sqrt(n) + 1)):
		if n % i == 0:
			if n / i == i:
				arr += [i]
			else:
				arr += [i]
				list.append(n // i)
	arr += list[::-1]
	return arr

def primefactorisation(n):
	potentional_p = 3
	itog_list = defaultdict(int)
	if n % 2 == 0:
		itog_list[2] = 0
	while n % 2 == 0:
		n = n // 2
		itog_list[2] += 1
	while n - 1:
		if potentional_p > n ** 0.5:
			itog_list[n] += 1
			return itog_list
		while n % potentional_p == 0:
			n = n // potentional_p
			itog_list[potentional_p] += 1
		potentional_p += 2
	return itog_list

def main():
	n = int(input())
	arr = []
	i = 1
	for _ in range(n):
		a = list(map(int, input().split()))
		arr.append(a + [i])
		i += 1
	arr.sort()
	ln = n
	i = 0
	j = 1
	while j < ln:
		if arr[i][0] == arr[j][0] and arr[i][1] == arr[j][1]:
			print(arr[i][-1], arr[j][-1])
			arr.pop(i)
			arr.pop(i)
			ln -= 2
		else:
			i += 1
			j += 1
	i = 0
	j = 1
	while j < ln:
		if arr[i][2] == arr[j][2] and arr[i][1] == arr[j][1]:
			print(arr[i][-1], arr[j][-1])
			arr.pop(i)
			arr.pop(i)
			ln -= 2
		else:
			i += 1
			j += 1
	i = 0
	j = 1
	while j < ln:
		if arr[i][2] == arr[j][2] and arr[i][0] == arr[j][0]:
			print(arr[i][-1], arr[j][-1])
			arr.pop(i)
			arr.pop(i)
			ln -= 2
		else:
			i += 1
			j += 1
	i = 0
	j = 1
	while j < ln:
		if arr[i][0] == arr[j][0]:
			print(arr[i][-1], arr[j][-1])
			arr.pop(i)
			arr.pop(i)
			ln -= 2
		else:
			i += 1
			j += 1
	i = 0
	j = 1
	while j < ln:
		if arr[i][1] == arr[j][1]:
			print(arr[i][-1], arr[j][-1])
			arr.pop(i)
			arr.pop(i)
			ln -= 2
		else:
			i += 1
			j += 1
	i = 0
	j = 1
	while j < ln:
		if arr[i][2] == arr[j][2]:
			print(arr[i][-1], arr[j][-1])
			arr.pop(i)
			arr.pop(i)
			ln -= 2
		else:
			i += 1
			j += 1
	for i in range(0, ln, 2):
		print(arr[i][-1], arr[i + 1][-1])
BUFSIZE = 8192

class FastIO(BytesIO):
	newlines = 0

	def __init__(self, file):
		self._file = file
		self._fd = file.fileno()
		self.writable = 'x' in file.mode or 'w' in file.mode
		self.write = super(FastIO, self).write if self.writable else None

	def _fill(self):
		s = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
		self.seek((self.tell(), self.seek(0, 2), super(FastIO, self).write(s))[0])
		return s

	def read(self):
		while self._fill():
			pass
		return super(FastIO, self).read()

	def readline(self):
		while self.newlines == 0:
			s = self._fill()
			self.newlines = s.count(b'\n') + (not s)
		self.newlines -= 1
		return super(FastIO, self).readline()

	def flush(self):
		if self.writable:
			os.write(self._fd, self.getvalue())
			(self.truncate(0), self.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')
(sys.stdin, sys.stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))
input = lambda : sys.stdin.readline().rstrip('\r\n')
main()


n = int(input())
points = []
s = set()

def dist(a, b):
	return abs(a[0] - b[0]) + abs(a[1] - b[1]) + abs(a[2] - b[2])
for i in range(n):
	arr = map(int, input().split())
	points.append(list(arr))
	s.add(i)
dis = {}
for i in range(n):
	if i in s:
		max = 1e+18
		x = -1
		for j in range(i + 1, n):
			if max > dist(points[i], points[j]) and j in s:
				max = dist(points[i], points[j])
				x = j
		s.remove(i)
		s.remove(x)
		print(i + 1, x + 1)


n = int(input())
arr = []
for i in range(n):
	(x, y, z) = map(int, input().split(' '))
	arr.append([x, y, z, i + 1])
v = set()
inf = 10 ** 18
res = []
for i in range(n):
	if arr[i][3] not in v:
		curr = inf
		for j in range(i + 1, n):
			if arr[j][3] not in v:
				d = abs(arr[i][0] - arr[j][0]) + abs(arr[i][1] - arr[j][1]) + abs(arr[i][2] - arr[j][2])
				if d < curr:
					curr = d
					ans = arr[j][3]
			else:
				pass
		v.add(arr[i][3])
		v.add(ans)
		res.append([arr[i][3], ans])
	else:
		pass
for item in res:
	print(item[0], item[1])


import os
import sys
from io import BytesIO, IOBase
BUFSIZE = 8192

class FastIO(IOBase):
	newlines = 0

	def __init__(self, file):
		self._fd = file.fileno()
		self.buffer = BytesIO()
		self.writable = 'x' in file.mode or 'r' not in file.mode
		self.write = self.buffer.write if self.writable else None

	def read(self):
		while True:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			if not b:
				break
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines = 0
		return self.buffer.read()

	def readline(self):
		while self.newlines == 0:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			self.newlines = b.count(b'\n') + (not b)
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines -= 1
		return self.buffer.readline()

	def flush(self):
		if self.writable:
			os.write(self._fd, self.buffer.getvalue())
			(self.buffer.truncate(0), self.buffer.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')
(sys.stdin, sys.stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))
input = lambda : sys.stdin.readline().rstrip('\r\n')

def dist(p1, p2):
	a1 = (p1[0] - p2[0]) * (p1[0] - p2[0])
	a2 = (p1[1] - p2[1]) * (p1[1] - p2[1])
	a3 = (p1[2] - p2[2]) * (p1[2] - p2[2])
	return a1 + a2 + a3
n = int(input())
coords = set([])
dict2 = {}
for s in range(n):
	(x, y, z) = map(int, input().split())
	coords.add((x, y, z))
	dict2[x, y, z] = s + 1
while len(coords) > 0:
	coord = coords.pop()
	magnitude = 2 * 10 ** 17
	cord1 = coord
	for s in coords:
		cord2 = s
		d1 = dist(coord, cord2)
		if dist(coord, cord2) < magnitude:
			magnitude = d1
			cord1 = cord2
	ind1 = dict2[coord]
	ind2 = dict2[cord1]
	coords.remove(cord1)
	ans = [ind1, ind2]
	print(*ans)


arr = [tuple(map(int, input().split())) for _ in range(int(input()))]
arr = list(enumerate(arr, 1))
while len(arr):
	p = arr[0]
	q = sorted(arr, key=lambda i: abs(p[1][0] - i[1][0]) + abs(p[1][1] - i[1][1]) + abs(p[1][2] - i[1][2]))[1]
	print(p[0], q[0])
	arr.remove(p)
	arr.remove(q)


from sys import stdin, setrecursionlimit
setrecursionlimit(10 ** 7)

def iin():
	return int(stdin.readline())

def lin():
	return list(map(int, stdin.readline().split()))

def main():
	n = iin()
	point = [lin() + [i + 1] for i in range(n)]
	point.sort(reverse=True)
	p1 = []
	while point:
		(x, y, z, pos) = point.pop()
		if len(point) > 0 and x == point[-1][0] and (y == point[-1][1]):
			(x1, y1, z1, pos1) = point.pop()
			print(pos, pos1)
		else:
			p1.append([x, y, z, pos])
	point = p1
	point.sort(reverse=True)
	p1 = []
	while point:
		(x, y, z, pos) = point.pop()
		if len(point) > 0 and x == point[-1][0]:
			p2 = point.pop()
			print(pos, p2[3])
		else:
			p1.append([x, y, z, pos])
	point = p1
	point.sort(reverse=True)
	for i in range(0, len(point), 2):
		print(point[i][3], point[i + 1][3])
main()


from sys import stdin, stdout
n = int(stdin.readline())
l = []
for i in range(n):
	x = list(map(int, stdin.readline().split()))
	l.append([x, i + 1])
l.sort()
deleted = [0] * n
for i in range(n - 1):
	if deleted[i]:
		continue
	if l[i][0][0] == l[i + 1][0][0] and l[i][0][1] == l[i + 1][0][1]:
		print(l[i][1], l[i + 1][1])
		deleted[i] = deleted[i + 1] = 1
remaining = []
for i in range(n):
	if deleted[i] == 0:
		remaining.append(l[i])
del deleted
n = len(remaining)
deleted = [0] * n
for i in range(n - 1):
	if deleted[i]:
		continue
	if remaining[i][0][0] == remaining[i + 1][0][0]:
		print(remaining[i][1], remaining[i + 1][1])
		deleted[i] = deleted[i + 1] = 1
last = []
for i in range(n):
	if deleted[i] == 0:
		last.append(remaining[i])
for i in range(0, len(last), 2):
	print(last[i][1], last[i + 1][1])


from collections import deque
n = int(input())
points = [0] * n
planes = {}
other = []
for i in range(n):
	(x, y, z) = map(float, input().split())
	points[i] = (x, y, z, i + 1)
	planes[z] = []
for i in range(n):
	(x, y, z, index) = points[i]
	planes[points[i][2]].append((x, y, z, index))
for i in planes.keys():
	now = deque(sorted(planes[i], key=lambda x: (x[0], x[1])))
	while len(now) >= 3:
		first = now.popleft()
		second = now.popleft()
		third = now.popleft()
		if second[0] == third[0]:
			while second[0] == third[0] and len(now) >= 3:
				print(second[3], third[3])
				second = now.popleft()
				third = now.popleft()
			if second[0] == third[0]:
				print(second[3], third[3])
				now.appendleft(first)
			else:
				now.appendleft(third)
				print(first[3], second[3])
		else:
			now.appendleft(third)
			print(first[3], second[3])
	if len(now) == 2:
		first = now.popleft()
		second = now.popleft()
		print(first[3], second[3])
	else:
		first = now.popleft()
		other.append(first)
other.sort(key=lambda x: x[2])
for i in range(len(other) // 2):
	print(other[2 * i][3], other[2 * i + 1][3])


from __future__ import division, print_function
import bisect
import math
import heapq
import itertools
import sys
from collections import deque
from atexit import register
from collections import Counter
from functools import reduce
sys.setrecursionlimit(10000000)
if sys.version_info[0] < 3:
	from io import BytesIO as stream
else:
	from io import StringIO as stream
if sys.version_info[0] < 3:

	class dict(dict):

		def items(self):
			return dict.iteritems(self)

		def keys(self):
			return dict.iterkeys(self)

		def values(self):
			return dict.itervalues(self)
	input = raw_input
	range = xrange
	filter = itertools.ifilter
	map = itertools.imap
	zip = itertools.izip

def sync_with_stdio(sync=True):
	global input, flush
	if sync:
		flush = sys.stdout.flush
	else:
		sys.stdin = stream(sys.stdin.read())
		input = lambda : sys.stdin.readline().rstrip('\r\n')
		sys.stdout = stream()
		register(lambda : sys.__stdout__.write(sys.stdout.getvalue()))

def main():
	n = int(input())
	lol = []
	d = dict()
	d1 = dict()
	for i in range(n):
		(x, y, z) = map(int, input().split())
		lol.append([x, y, z, i + 1])
	lol.sort()
	for ii in range(n):
		x = lol[ii][0]
		y = lol[ii][1]
		z = lol[ii][2]
		i = lol[ii][3] - 1
		try:
			d[str([x, y])].append(i + 1)
		except:
			d[str([x, y])] = [i + 1]
		try:
			d1[x].append(i + 1)
		except:
			d1[x] = [i + 1]
	vis = [False] * n
	ans = []
	for i in d.values():
		if len(i) >= 2:
			for j in range(len(i) // 2):
				vis[i[j * 2] - 1] = True
				vis[i[j * 2 + 1] - 1] = True
				ans.append([i[j * 2], i[j * 2 + 1]])
	for i in d1.values():
		n1 = len(i)
		if n1 >= 2:
			j = 0
			k = 1
			while k < n1:
				if vis[i[j] - 1] == True:
					j += 1
				if vis[i[k] - 1] == True or j == k:
					k += 1
				if k < n1 and vis[i[k] - 1] == False and (vis[i[j] - 1] == False):
					ans.append([i[k], i[j]])
					vis[i[k] - 1] = True
					vis[i[j] - 1] = True
	i = 0
	j = 1
	c = 0
	while j < n:
		if vis[lol[i][3] - 1] == True:
			i += 1
		if vis[lol[j][3] - 1] == True or j == i:
			j += 1
		if j < n and vis[lol[i][3] - 1] == False and (vis[lol[j][3] - 1] == False):
			ans.append([lol[i][3], lol[j][3]])
			vis[lol[i][3] - 1] = True
			vis[lol[j][3] - 1] = True
	for i in ans:
		print(*i)
sync_with_stdio(False)
main()


import sys
input = sys.stdin.readline

def solve1d(a):
	a.sort()
	for i in range(0, len(a) - 1, 2):
		print(a[i][-1] + 1, a[i + 1][-1] + 1)
	if len(a) % 2:
		return a[-1]
	return False

def solve2d(start, end):
	d = {}
	for i in range(start, end + 1):
		if coor[i][1] not in d:
			d[coor[i][1]] = [[coor[i][0], coor[i][2], coor[i][3]]]
		else:
			d[coor[i][1]].append([coor[i][0], coor[i][2], coor[i][3]])
	left = []
	for i in d:
		x = solve1d(d[i])
		if x:
			left.append([i, x[1], x[-1]])
	left.sort()
	for i in range(0, len(left) - 1, 2):
		print(left[i][-1] + 1, left[i + 1][-1] + 1)
	if len(left) % 2:
		return left[-1]
	return False

def solve3d():
	coor.sort(key=lambda x: x[2])
	prev = 0
	left = []
	for i in range(1, n):
		if coor[i][2] != coor[prev][2]:
			x = solve2d(prev, i - 1)
			if x:
				left.append([x[1], x[-1]])
			prev = i
	x = solve2d(prev, n - 1)
	if x:
		left.append([x[1], x[-1]])
	left.sort()
	for i in range(0, len(left), 2):
		print(left[i][-1] + 1, left[i + 1][-1] + 1)
n = int(input())
coor = []
for i in range(n):
	coor.append(list(map(int, input().split())))
	coor[-1].append(i)
solve3d()


import sys
reader = (s.rstrip() for s in sys.stdin)
input = reader.__next__
n = int(input())
points = []
for i in range(n):
	(x, y, z) = map(int, input().split())
	points.append((x, y, z, i + 1))
points.sort()
ans = []
for i in range(n - 1):
	if points[i] and points[i][0] == points[i + 1][0] and (points[i][1] == points[i + 1][1]):
		ans.append([points[i][-1], points[i + 1][-1]])
		points[i] = points[i + 1] = None
remain = [i for i in range(n) if points[i]]
for i in range(len(remain) - 1):
	j1 = remain[i]
	j2 = remain[i + 1]
	if points[j1] and points[j1][0] == points[j2][0]:
		ans.append([points[j1][-1], points[j2][-1]])
		points[j1] = points[j2] = None
remain = [i for i in range(n) if points[i]]
for i in range(0, len(remain), 2):
	ans.append([points[remain[i]][-1], points[remain[i + 1]][-1]])
for i in ans:
	print(*i)


from sys import stdin

def input():
	return stdin.readline()[:-1]

def intput():
	return int(input())

def sinput():
	return input().split()

def intsput():
	return list(map(int, sinput()))

def solve1d(points):
	points.sort()
	rem = False
	if len(points) % 2:
		rem = points.pop()
	return [points, rem]

def solve2d(points):
	solut = []
	rem = False
	groups = {}
	for p in points:
		if p[1] not in groups:
			groups[p[1]] = []
		groups[p[1]].append(p)
	gg = sorted(groups.values(), key=lambda x: x[0][1])
	for gridx in range(len(gg)):
		gr = gg[gridx]
		abc = solve1d(gr)
		solut += abc[0]
		if abc[1] and gridx < len(gg) - 1:
			testing = sorted(gg[gridx + 1], key=lambda x: abs(x[0] - abc[1][0]))
			tget = testing[0]
			solut.append(abc[1])
			solut.append(tget)
			gg[gridx + 1].remove(tget)
		elif abc[1]:
			rem = abc[1]
	return [solut, rem]

def solve3d(points):
	solut = []
	rem = False
	groups = {}
	for p in points:
		if p[2] not in groups:
			groups[p[2]] = []
		groups[p[2]].append(p)
	gg = sorted(groups.values(), key=lambda x: x[0][2])
	for gridx in range(len(gg)):
		gr = gg[gridx]
		abc = solve2d(gr)
		solut += abc[0]
		if abc[1] and gridx < len(gg) - 1:
			testing = sorted(gg[gridx + 1], key=lambda x: (abs(x[0] - abc[1][0]), abs(x[1] - abc[1][1])))
			tget = testing[0]
			solut.append(abc[1])
			solut.append(tget)
			gg[gridx + 1].remove(tget)
		elif abc[1]:
			rem = abc[1]
	return [solut, rem]
n = intput()
cod = [intsput() for _ in range(n)]
(pp, r) = solve3d(cod)
for i in range(len(pp) // 2):
	print(cod.index(pp[i * 2]) + 1, cod.index(pp[i * 2 + 1]) + 1)


def solve(n, points):
	points = sorted((p + (i,) for (i, p) in enumerate(points, 1)))
	points += [(float('inf'),) * 3]
	rem = []
	i = 0
	while i < n:
		if points[i][:2] == points[i + 1][:2]:
			print(points[i][3], points[i + 1][3])
			i += 2
		else:
			rem.append(i)
			i += 1
	rem.append(n)
	rem2 = []
	i = 0
	while i < len(rem) - 1:
		if points[rem[i]][0] == points[rem[i + 1]][0]:
			print(points[rem[i]][3], points[rem[i + 1]][3])
			i += 2
		else:
			rem2.append(points[rem[i]][3])
			i += 1
	for i in range(0, len(rem2), 2):
		print(rem2[i], rem2[i + 1])

def main():
	n = int(input())
	points = [tuple((int(c) for c in input().split())) for _ in range(n)]
	solve(n, points)
main()


n = int(input())
a = [[]]
for i in range(n):
	(x, y, z) = map(int, input().split())
	a.append([x, y, z])
from collections import defaultdict

def solve(lst, k):
	if k == 3:
		return lst[0]
	mp = defaultdict(list)
	for i in lst:
		mp[a[i][k]].append(i)
	ans = []
	p = []
	for i in mp:
		p.append(i)
	p.sort()
	for i in p:
		t = solve(mp[i], k + 1)
		if t != -1:
			ans.append(t)
	for i in range(0, len(ans) - 1, 2):
		print(ans[i], ans[i + 1])
	if len(ans) % 2:
		return ans[-1]
	return -1
solve([i for i in range(1, n + 1)], 0)


n = int(input())
coors = []
for i in range(n):
	coors.append(list(map(int, input().split())) + [i + 1])
coors = sorted(coors, key=lambda x: (x[0], x[1], x[2]))
left = []

def oneD(co, remain):
	if len(co) % 2 == 0:
		for i in range(0, len(co), 2):
			print(co[i][3], co[i + 1][3])
	else:
		remain.append(co[-1])
		for i in range(0, len(co) - 1, 2):
			print(co[i][3], co[i + 1][3])
	return remain

def twoD(co):
	if len(co) == 1:
		left.append(*co)
		return
	remain = []
	l = 1
	for i in range(len(co) - 1):
		if co[i][1] == co[i + 1][1]:
			l += 1
		else:
			remain = oneD(co[i + 1 - l:i + 1], remain)
			l = 1
	remain = oneD(co[len(co) - l:], remain)
	if len(remain) % 2 == 0:
		for i in range(0, len(remain), 2):
			print(remain[i][3], remain[i + 1][3])
	else:
		left.append(remain[-1])
		for i in range(0, len(remain) - 1, 2):
			print(remain[i][3], remain[i + 1][3])
l = 1
for i in range(n - 1):
	if coors[i][0] == coors[i + 1][0]:
		l += 1
	else:
		twoD(coors[i + 1 - l:i + 1])
		l = 1
twoD(coors[n - l:])
for i in range(0, len(left), 2):
	print(left[i][3], left[i + 1][3])


n = int(input())
x = []
y = []
z = []
for i in range(n):
	(X, Y, Z) = map(int, input().split())
	x.append(X)
	y.append(Y)
	z.append(Z)
remove = 0
Re = []
for i in range(2001):
	Re.append(0)
ad = 2 * 3 * 10 ** 8 + 1
for i in range(n):
	dis = ad
	if Re[i] == 0:
		for j in range(i + 1, n):
			if Re[j] == 0:
				m = abs(x[i] - x[j]) + abs(y[i] - y[j]) + abs(z[i] - z[j])
				if m < dis:
					dis = m
					a = i
					b = j
		print(a + 1, b + 1)
		Re[a] = 1
		Re[b] = 1


from math import *
from pprint import *
from sys import *
from collections import *
from itertools import *
from functools import *
from random import *

def getl(t=int):
	return [t(x) for x in input().split()]

def get(t=int):
	return t(input())
alphabet = [chr(x) for x in range(ord('a'), ord('z') + 1)]
alnum = lambda x: ord(x) - ord('a')

def main():
	all_points = []
	for i in range(get()):
		(x, y, z) = getl()
		all_points.append((x, y, z, i + 1))
	all_points.sort()
	solve(all_points, 0)

def solve(points, dimension):
	if dimension == 2:
		for i in range(0, len(points), 2):
			if i + 1 == len(points):
				break
			print(points[i][3], points[i + 1][3])
		if len(points) % 2 == 1:
			return [points[-1]]
		else:
			return []
	else:
		remaining_pts = []
		pts_map = defaultdict(list)
		for pt in points:
			pts_map[pt[dimension]].append(pt)
		for (d, pts) in sorted(pts_map.items()):
			if len(pts) > 1:
				remaining_pts += solve(pts, dimension + 1)
			else:
				remaining_pts.append(pts[0])
		for i in range(0, len(remaining_pts), 2):
			if i + 1 == len(remaining_pts):
				break
			print(remaining_pts[i][3], remaining_pts[i + 1][3])
		if len(remaining_pts) % 2 == 1:
			return [remaining_pts[-1]]
		else:
			return []
main()


from collections import defaultdict

def solver(arr, vis, ans):
	mpy = defaultdict(list)
	uniy = set()
	for i in arr:
		(y, rem) = (i[0], i[1:])
		mpy[y].append(rem)
		uniy.add(y)
	uniy = list(uniy)
	rem = []
	for i in uniy:
		temp = mpy[i]
		temp.sort()
		for j in range(0, len(temp), 2):
			idx1 = temp[j][1]
			if j + 1 >= len(temp):
				break
			idx2 = temp[j + 1][1]
			vis[idx1] = True
			vis[idx2] = True
			ans.append([idx1 + 1, idx2 + 1])
		if len(temp) % 2 != 0:
			last = [i] + temp[-1]
			rem.append(last)
	rem.sort()
	for j in range(0, len(rem), 2):
		idx1 = rem[j][2]
		if j + 1 >= len(rem):
			break
		idx2 = rem[j + 1][2]
		vis[idx1] = True
		vis[idx2] = True
		ans.append([idx1 + 1, idx2 + 1])
	if len(rem) % 2 != 0:
		last = rem[-1]
		return last
	else:
		return -1
n = int(input())
(arr, ans) = ([], [])
vis = [False for i in range(n)]
for i in range(n):
	(x, y, z) = list(map(int, input().split()))
	arr.append([z, y, x, i])
mpz = defaultdict(list)
uniz = set()
for i in arr:
	(z, rem) = (i[0], i[1:])
	mpz[z].append(rem)
	uniz.add(z)
uniz = list(uniz)
uniz.sort()
rem = []
for i in uniz:
	points = mpz[i]
	points.sort()
	x = solver(points, vis, ans)
	if x != -1:
		rem.append([i] + x)
rem.sort()
for j in range(0, len(rem), 2):
	idx1 = rem[j][3]
	if j + 1 >= len(rem):
		break
	idx2 = rem[j + 1][3]
	vis[idx1] = True
	vis[idx2] = True
	ans.append([idx1 + 1, idx2 + 1])
for i in ans:
	print(*i)


n = int(input())
coordinates = []
for i in range(n):
	coordinates.append([int(i) for i in input().split()])
	coordinates[i].append(i + 1)
coordinates.sort()
remaining = []
i = 0
while i < len(coordinates):
	if i < len(coordinates) - 1 and coordinates[i][:2] == coordinates[i + 1][:2]:
		print(coordinates[i][3], coordinates[i + 1][3])
		i += 2
	else:
		remaining.append(coordinates[i])
		i += 1
coordinates = list(remaining)
remaining = []
i = 0
while i < len(coordinates):
	if i < len(coordinates) - 1 and coordinates[i][0] == coordinates[i + 1][0]:
		print(coordinates[i][3], coordinates[i + 1][3])
		i += 2
	else:
		remaining.append(coordinates[i])
		i += 1
coordinates = list(remaining)
for i in range(int(len(coordinates)) // 2):
	print(coordinates[2 * i][3], coordinates[2 * i + 1][3])


import math

def dist(x, y):
	return abs(x[0] - y[0]) + abs(x[1] - y[1]) + abs(x[2] - y[2])
n = int(input())
l = []
for _ in range(n):
	l += [tuple(map(int, input().split()))]
i = 1
se = dict()
hh = [0] * n
for i in range(n):
	mi = 100000000000000
	if hh[i] == 0:
		lol = 0
		for j in range(n):
			if hh[j] == 0 and i != j:
				lol = 1
				d = dist(l[i], l[j])
				if d < mi:
					ind = j
					mi = d
		if lol == 1:
			print(i + 1, ind + 1)
			hh[i] = 1
			hh[ind] = 1


from math import *
import sys
input = sys.stdin.readline
global flag

def inp():
	return int(input())

def st():
	return input().rstrip('\n')

def lis():
	return list(map(int, input().split()))

def ma1():
	return map(int, input().split())
t = 1
while t:
	t -= 1
	n = inp()
	r = []
	for i in range(n):
		a = lis()
		r.append(a)
	vis = [0] * n
	for i in range(n):
		if vis[i]:
			continue
		(x1, y1, z1) = (r[i][0], r[i][1], r[i][2])
		fl = 0
		pos = i
		for j in range(i + 1, n):
			if vis[j]:
				continue
			if fl == 0:
				(x2, y2, z2) = (r[j][0], r[j][1], r[j][2])
				fl = 1
				pos1 = j
			elif min(x1, x2) <= r[j][0] and max(x1, x2) >= r[j][0] and (min(y1, y2) <= r[j][1]) and (max(y1, y2) >= r[j][1]) and (min(z1, z2) <= r[j][2]) and (max(z1, z2) >= r[j][2]):
				(x2, y2, z2) = (r[j][0], r[j][1], r[j][2])
				pos1 = j
		vis[pos] = 1
		vis[pos1] = 1
		print(pos + 1, pos1 + 1)


n = int(input())
array = []
dic = {}
for x in range(n):
	(a, b, c) = list(map(int, input().split()))
	array.append([a, b, c])
	flag = 5
index = 5 - 5
for it in array:
	dic[str(it)] = index
	index += 1
while True:
	lenn = len(array)
	if lenn == 2:
		break
	for x in range(lenn):
		flag = 5
		tam = array[x]
		for y in range(lenn):
			if x == y:
				flag = 6
			else:
				flag = 5
				kam = array[y]
				for z in range(lenn):
					if z == x or z == y:
						pass
					else:
						current = array[z]
						if min(tam[0], kam[0]) <= current[0] and max(tam[0], kam[0]) >= current[0] and (min(tam[1], kam[1]) <= current[1]) and (max(tam[1], kam[1]) >= current[1]) and (min(tam[2], kam[2]) <= current[2]) and (max(tam[2], kam[2]) >= current[2]):
							flag = 6
							break
				break
		if flag == 6:
			pass
		else:
			break
	if flag == 5:
		print(dic[str(array[x])] + 1, dic[str(array[y])] + 1)
		if x > y:
			array.remove(array[x])
			array.remove(array[y])
		else:
			array.remove(array[x])
			array.remove(array[y - 1])
	else:
		break
	if array == list():
		break
print(dic[str(array[0])] + 1, dic[str(array[1])] + 1)


import math
pi = 3.14159265359
n = int(input())
points = []
seq = []
for i in range(n):
	(x, y, z) = map(int, input().split())
	points.append([x, y, z, i])
points = sorted(points, key=lambda x: [x[2], x[1], x[0], x[3]])
points = points[::-1]
finalrest = []

def focusy(y, temp):
	newtemp = []
	rest = []
	cout = 0
	while temp and temp[-1][1] == y:
		newtemp.append(temp.pop())
	if len(newtemp) % 2 == 1:
		rest.append(newtemp[-1])
	num = len(newtemp) // 2
	for i in range(num):
		seq.append(newtemp[2 * i][3])
		seq.append(newtemp[2 * i + 1][3])
	return rest

def focusz(z, temp):
	newtemp = []
	while temp and temp[-1][2] == z:
		newtemp.append(points.pop())
	newtemp = newtemp[::-1]
	rest = []
	finalrest = []
	while newtemp:
		rest = rest + focusy(newtemp[-1][1], newtemp)
	if len(rest) % 2 == 1:
		finalrest.append(rest[-1])
	num = len(rest) // 2
	for i in range(num):
		seq.append(rest[2 * i][3])
		seq.append(rest[2 * i + 1][3])
	return finalrest
while points:
	finalrest += focusz(points[-1][2], points)
num = len(finalrest) // 2
for i in range(num):
	seq.append(finalrest[2 * i][3])
	seq.append(finalrest[2 * i + 1][3])
for i in range(len(seq) // 2):
	print(seq[2 * i] + 1, seq[2 * i + 1] + 1)


n = int(input())
points = []
for i in range(n):
	points.append(list(map(int, input().split())))
selected = {}
ans = []
j = 0
k = 0
while len(selected) != n:
	if j not in selected:
		first = points[j]
	else:
		j += 1
		continue
	if k not in selected and k != j:
		second = points[k]
		temp = k
	else:
		k += 1
		continue
	for i in range(2, n):
		if i not in selected and i != j:
			if min(first[0], second[0]) <= points[i][0] and points[i][0] <= max(first[0], second[0]) and (min(first[1], second[1]) <= points[i][1]) and (points[i][1] <= max(first[1], second[1])) and (min(first[2], second[2]) <= points[i][2]) and (points[i][2] <= max(first[2], second[2])):
				second = points[i]
				temp = i
	selected[j] = 1
	selected[temp] = 1
	ans.append([j + 1, temp + 1])
for i in ans:
	print(i[0], i[1])


import sys
input = sys.stdin.buffer.readline

def dis(xx, yy):
	val = abs(xx[0] - yy[0]) + abs(xx[1] - yy[1]) + abs(xx[2] - yy[2])
	return val
n = int(input())
ar = []
for i in range(n):
	ar.append(list(map(int, input().split())))
se = set({})
for i in range(n):
	if not i in se:
		se.add(i)
		dist = float('inf')
		ind = -1
		for j in range(n):
			if not j in se:
				if dist > dis(ar[i], ar[j]):
					dist = dis(ar[i], ar[j])
					ind = j
		se.add(ind)
		sys.stdout.write(str(i + 1) + ' ' + str(ind + 1) + '\n')


class HiIAmClass:

	def __init__(self, p, x, y, z):
		self.p = p
		self.x = x
		self.y = y
		self.z = z

	def __lt__(self, other):
		return (self.x, self.y, self.z) < (other.x, other.y, other.z)
n = int(input())
l = []
for i in range(1, n + 1):
	(x, y, z) = [int(x) for x in input().split()]
	l.append(HiIAmClass(i, x, y, z))
res = []
for i in range(n):
	if l[i].p == 0:
		continue
	suitable_pair_p = i + 1
	min_d = 1000000000
	for j in range(i + 1, n):
		if l[j].p == 0:
			continue
		d = abs(l[i].x - l[j].x) + abs(l[i].y - l[j].y) + abs(l[i].z - l[j].z)
		if d < min_d:
			min_d = d
			suitable_pair_p = j
	res.append([l[i].p, l[suitable_pair_p].p])
	(l[i].p, l[suitable_pair_p].p) = (0, 0)
print('\n'.join((' '.join((str(y) for y in x)) for x in res)))


import sys
input = sys.stdin.readline
n = int(input())
it = set()
for _ in range(n):
	(a, b, c) = map(int, input().split())
	it.add((a, b, c, _))
for i in range(n // 2):
	aa = it.pop()
	b = it.pop()
	mi = b[:]
	it.add(b)
	for j in it:
		st = True
		for k in range(3):
			if min(aa[k], mi[k]) <= j[k] <= max(aa[k], mi[k]):
				pass
			else:
				st = False
		if st:
			mi = j[:]
	it.remove(mi)
	print(aa[3] + 1, mi[3] + 1)


n = int(input())
arr = []
ans = []
for _ in range(n):
	(x, y, z) = map(int, input().split())
	arr.append([x, y, z])
visited = [False] * n
for i in range(n):
	distance = 10 ** 10
	if visited[i] == False:
		for j in range(i + 1, n):
			if not visited[i] and (not visited[j]) and (i != j):
				(x1, y1, z1) = (arr[i][0], arr[i][1], arr[i][2])
				(x2, y2, z2) = (arr[j][0], arr[j][1], arr[j][2])
				d = abs(x1 - x2) + abs(y1 - y2) + abs(z1 - z2)
				if d < distance:
					distance = d
					ind = j
		if not visited[i] and (not visited[ind]):
			visited[i] = True
			visited[ind] = True
			ans.append([i + 1, ind + 1])
for i in ans:
	print(*i)


import math
points = []
q = int(input())
for i in range(q):
	vector = [i + 1] + [int(x) for x in input().split(' ')]
	points.append(vector)

def distance(a, b):
	return math.sqrt((a[1] - b[1]) ** 2 + (a[2] - b[2]) ** 2 + (a[3] - b[3]) ** 2)

def resolve(a, b):
	global points
	points = [x for x in points if x[0] != a and x[0] != b]
	print(a, b)

def process_region(local_points):
	local_q = len(local_points)
	if local_q <= 1:
		return local_points
	elif local_q == 2:
		resolve(*[x[0] for x in local_points])
		return []
	mid_x = 0
	mid_y = 0
	mid_z = 0
	for point in local_points:
		mid_x += point[1]
		mid_y += point[2]
		mid_z += point[3]
	mid_x = mid_x / local_q
	mid_y = mid_y / local_q
	mid_z = mid_z / local_q
	subregions = [[], [], [], [], [], [], [], []]
	for point in local_points:
		if point[1] < mid_x and point[2] < mid_y and (point[3] < mid_z):
			subregions[0].append(point)
		elif point[1] >= mid_x and point[2] < mid_y and (point[3] < mid_z):
			subregions[1].append(point)
		elif point[1] < mid_x and point[2] >= mid_y and (point[3] < mid_z):
			subregions[2].append(point)
		elif point[1] >= mid_x and point[2] >= mid_y and (point[3] < mid_z):
			subregions[3].append(point)
		elif point[1] < mid_x and point[2] < mid_y and (point[3] >= mid_z):
			subregions[4].append(point)
		elif point[1] >= mid_x and point[2] < mid_y and (point[3] >= mid_z):
			subregions[5].append(point)
		elif point[1] < mid_x and point[2] >= mid_y and (point[3] >= mid_z):
			subregions[6].append(point)
		elif point[1] >= mid_x and point[2] >= mid_y and (point[3] >= mid_z):
			subregions[7].append(point)
	left = []
	for subregion in subregions:
		if subregion:
			left += process_region(subregion)
	while len(left) > 1:
		here = left.pop(0)
		there = left[0]
		min_distance = distance(here, there)
		for other in left:
			other_distance = distance(here, other)
			if other_distance < min_distance:
				min_distance = other_distance
				there = other
		resolve(here[0], there[0])
		left = [x for x in left if x[0] != here[0] and x[0] != there[0]]
	return left
process_region(points)


def dist(p1, p2):
	return (p1[0] - p2[0]) * (p1[0] - p2[0]) + (p1[1] - p2[1]) * (p1[1] - p2[1]) + (p1[2] - p2[2]) * (p1[2] - p2[2])
n = int(input())
seq = set([tuple([int(c) for c in input().split()] + [i]) for i in range(n)])
while seq:
	p1 = seq.pop()
	mn = float('infinity')
	p2 = -1
	for e in seq:
		d = dist(p1, e)
		if d < mn:
			mn = d
			p2 = e
	print(p1[3] + 1, p2[3] + 1)
	seq.remove(p2)


import sys
input = sys.stdin.buffer.readline
con = 10 ** 9 + 7
INF = float('inf')

def getlist():
	return list(map(int, input().split()))

def main():
	N = int(input())
	n = N // 2
	node = []
	for i in range(N):
		(x, y, z) = getlist()
		node.append([x, y, z, i])
	for i in range(n - 1):
		ans = []
		x1 = node[-1][0]
		y1 = node[-1][1]
		z1 = node[-1][2]
		ans.append(node[-1][3] + 1)
		val = INF
		ind = 0
		q = 0
		for j in range(N - 2 * i - 1):
			x2 = node[j][0]
			y2 = node[j][1]
			z2 = node[j][2]
			if abs(x1 - x2) + abs(y1 - y2) + abs(z1 - z2) < val:
				ind = j
				q = node[j][3] + 1
				val = abs(x1 - x2) + abs(y1 - y2) + abs(z1 - z2)
		ans.append(q)
		print(' '.join(list(map(str, ans))))
		del node[-1]
		del node[ind]
	print(node[0][3] + 1, node[1][3] + 1)
main()


from collections import defaultdict

def filterOut(ans, removed, mapp):
	for v in mapp.values():
		arr = list(v)
		n = len(arr)
		arr.sort()
		i = 0
		while i + 1 < n:
			(idx1, idx2) = (arr[i][1], arr[i + 1][1])
			ans.append([idx1, idx2])
			removed[idx1] = removed[idx2] = True
			i += 2
	return

def filterOut2(ans, removed, mapp):
	for v in mapp.values():
		arr = list(v)
		n = len(arr)
		arr.sort()
		i = 0
		while i + 1 < n:
			(idx1, idx2) = (arr[i][2], arr[i + 1][2])
			ans.append([idx1, idx2])
			removed[idx1] = removed[idx2] = True
			i += 2
	return

def main():
	n = int(input())
	coords = []
	for i in range(1, n + 1):
		(x, y, z) = readIntArr()
		coords.append([x, y, z, i])
	ans = []
	removed = [False for _ in range(n + 1)]
	xyMap = defaultdict(lambda : [])
	for i in range(n):
		(x, y, z, idx) = coords[i]
		xyMap[x, y].append([z, idx])
	filterOut(ans, removed, xyMap)
	coords2 = []
	for i in range(n):
		if not removed[coords[i][3]]:
			coords2.append(coords[i])
	coords = coords2
	n = len(coords)
	yzMap = defaultdict(lambda : [])
	for i in range(n):
		(x, y, z, idx) = coords[i]
		yzMap[y, z].append([x, idx])
	filterOut(ans, removed, yzMap)
	coords2 = []
	for i in range(n):
		if not removed[coords[i][3]]:
			coords2.append(coords[i])
	coords = coords2
	n = len(coords)
	zxMap = defaultdict(lambda : [])
	for i in range(n):
		(x, y, z, idx) = coords[i]
		zxMap[z, x].append([y, idx])
	filterOut(ans, removed, zxMap)
	coords2 = []
	for i in range(n):
		if not removed[coords[i][3]]:
			coords2.append(coords[i])
	coords = coords2
	n = len(coords)
	xMap = defaultdict(lambda : [])
	for i in range(n):
		(x, y, z, idx) = coords[i]
		xMap[x].append([y, z, idx])
	filterOut2(ans, removed, xMap)
	coords2 = []
	for i in range(n):
		if not removed[coords[i][3]]:
			coords2.append(coords[i])
	coords = coords2
	n = len(coords)
	yMap = defaultdict(lambda : [])
	for i in range(n):
		(x, y, z, idx) = coords[i]
		yMap[y].append([x, z, idx])
	filterOut2(ans, removed, yMap)
	coords2 = []
	for i in range(n):
		if not removed[coords[i][3]]:
			coords2.append(coords[i])
	coords = coords2
	n = len(coords)
	zMap = defaultdict(lambda : [])
	for i in range(n):
		(x, y, z, idx) = coords[i]
		zMap[x].append([y, z, idx])
	filterOut2(ans, removed, zMap)
	coords2 = []
	for i in range(n):
		if not removed[coords[i][3]]:
			coords2.append(coords[i])
	coords = coords2
	n = len(coords)
	coords.sort()
	for i in range(0, n, 2):
		ans.append([coords[i][3], coords[i + 1][3]])
	multiLineArrayOfArraysPrint(ans)
	return
import sys
input = lambda : sys.stdin.readline().rstrip('\r\n')

def oneLineArrayPrint(arr):
	print(' '.join([str(x) for x in arr]))

def multiLineArrayPrint(arr):
	print('\n'.join([str(x) for x in arr]))

def multiLineArrayOfArraysPrint(arr):
	print('\n'.join([' '.join([str(x) for x in y]) for y in arr]))

def readIntArr():
	return [int(x) for x in input().split()]

def makeArr(defaultValFactory, dimensionArr):
	dv = defaultValFactory
	da = dimensionArr
	if len(da) == 1:
		return [dv() for _ in range(da[0])]
	else:
		return [makeArr(dv, da[1:]) for _ in range(da[0])]

def queryInteractive(i, j):
	print('? {} {}'.format(i, j))
	sys.stdout.flush()
	return int(input())

def answerInteractive(ans):
	print('! {}'.format(' '.join([str(x) for x in ans])))
	sys.stdout.flush()
inf = float('inf')
MOD = 10 ** 9 + 7
for _abc in range(1):
	main()


import sys
input = sys.stdin.readline

def d(le):
	for i in range(0, len(le), 2):
		print(le[i][3], le[i + 1][3])

def dd(it):
	global do
	tt = {}
	ddd = []
	it.sort(key=lambda x: x[1])
	l = list(set([i[1] for i in it]))
	l.sort()
	for i in l:
		tt[i] = []
	for i in it:
		tt[i[1]].append(i)
	for i in l:
		x = tt[i]
		if len(x) % 2 == 1:
			ddd.append(x[-1][:])
			x = x[:-1]
		if len(x) > 0:
			d(x[:])
	if len(ddd) % 2 == 1:
		do.append(ddd[-1][:])
		ddd = ddd[:-1]
	if len(ddd) > 0:
		d(ddd)
n = int(input())
it = []
for i in range(n):
	it.append(list(map(int, input().split())) + [i + 1])
ss = {}
it.sort(key=lambda x: [x[2], x[1], x[0], x[3]])
l = list(set([i[2] for i in it]))
l.sort()
for i in l:
	ss[i] = []
for i in it:
	ss[i[2]].append(i)
do = []
for i in l:
	x = ss[i]
	dd(x[:])
d(do)


n = int(input())
coords = n * [-1]
coordsy = list()
coordsz = list()
for i in range(n):
	coords[i] = [int(i) for i in input().split()] + [i]
coords.sort()
i = 0
while i < len(coords) - 1:
	if coords[i][:2] == coords[i + 1][:2]:
		print(coords[i][3] + 1, coords[i + 1][3] + 1)
		i += 1
	else:
		coordsy.append(coords[i])
	i += 1
	if i == len(coords) - 1:
		coordsy.append(coords[i])
i = 0
while i < len(coordsy) - 1:
	if coordsy[i][:1] == coordsy[i + 1][:1]:
		print(coordsy[i][3] + 1, coordsy[i + 1][3] + 1)
		i += 1
	else:
		coordsz.append(coordsy[i])
	i += 1
	if i == len(coordsy) - 1:
		coordsz.append(coordsy[i])
for i in range(0, len(coordsz), 2):
	print(coordsz[i][3] + 1, coordsz[i + 1][3] + 1)


def solve(n, points):
	for i in range(n):
		points[i].append(i + 1)
	points.sort()
	points += [(float('inf'),) * 3]
	rem = []
	i = 0
	while i < n:
		if points[i][:2] == points[i + 1][:2]:
			print(points[i][3], points[i + 1][3])
			i += 2
		else:
			rem.append(i)
			i += 1
	rem.append(n)
	rem2 = []
	i = 0
	while i < len(rem) - 1:
		if points[rem[i]][0] == points[rem[i + 1]][0]:
			print(points[rem[i]][3], points[rem[i + 1]][3])
			i += 2
		else:
			rem2.append(points[rem[i]][3])
			i += 1
	for i in range(0, len(rem2), 2):
		print(rem2[i], rem2[i + 1])

def main():
	n = int(input())
	points = [[int(c) for c in input().split()] for _ in range(n)]
	solve(n, points)
main()


from sys import stdin
input = stdin.readline
R = lambda : list(map(int, input().split()))
(n,) = R()
a = []
delete = n * [0]
for i in range(n):
	a.append(R())

def test(x1, x2, x):
	if min(x1, x2) <= x and x <= max(x1, x2):
		return 1
	return 0
for i in range(n // 2):
	(x1, y1, z1) = ('0', '0', '0')
	(x2, y2, z2) = ('0', '0', '0')
	for i2 in range(n):
		if delete[i2] == 1:
			continue
		elif x1 == '0':
			(x1, y1, z1) = (a[i2][0], a[i2][1], a[i2][2])
			num1 = i2
			continue
		elif x2 == '0':
			(x2, y2, z2) = (a[i2][0], a[i2][1], a[i2][2])
			num2 = i2
			continue
		elif test(x1, x2, a[i2][0]) == 1 and test(y1, y2, a[i2][1]) == 1 and (test(z1, z2, a[i2][2]) == 1):
			x2 = a[i2][0]
			y2 = a[i2][1]
			z2 = a[i2][2]
			num2 = i2
	(delete[num1], delete[num2]) = (1, 1)
	print(num1 + 1, num2 + 1, flush=False)


from math import *

def distance(a, b):
	x = a[0] - b[0]
	y = a[1] - b[1]
	z = a[2] - b[2]
	return sqrt(x * x + y * y + z * z)
n = int(input())
point = []
mark = []
for i in range(n):
	mark.append(False)
	(x, y, z) = map(int, input().split())
	point.append((x, y, z))
jj = 0
min_dis = 0
for i in range(n):
	if mark[i] == True:
		continue
	jj = -1
	for j in range(i + 1, n):
		if mark[j] == True:
			continue
		if jj == -1:
			min_dis = distance(point[i], point[j])
			jj = j
		else:
			d = distance(point[i], point[j])
			if d < min_dis:
				jj = j
				min_dis = d
	print(str(i + 1) + ' ' + str(jj + 1))
	mark[i] = True
	mark[jj] = True


n = int(input())
sentinel = [(10 ** 9, 10 ** 9, 10 ** 9)]
points = sorted((tuple(map(int, input().split())) + (i,) for i in range(1, n + 1))) + sentinel
ans = []
rem = []
i = 0
while i < n:
	if points[i][:2] == points[i + 1][:2]:
		ans.append(f'{points[i][-1]} {points[i + 1][-1]}')
		i += 2
	else:
		rem.append(i)
		i += 1
rem += [n]
rem2 = []
n = len(rem)
i = 0
while i < n - 1:
	if points[rem[i]][0] == points[rem[i + 1]][0]:
		ans.append(f'{points[rem[i]][-1]} {points[rem[i + 1]][-1]}')
		i += 2
	else:
		rem2.append(points[rem[i]][-1])
		i += 1
print(*ans, sep='\n')
for i in range(0, len(rem2), 2):
	print(rem2[i], rem2[i + 1])


n = int(input())
points = []
for i in range(n):
	points.append(list(map(int, input().split())))
	points[i].append(i + 1)
points.sort()
rem = []
i = 0
while i < len(points):
	if i < len(points) - 1 and points[i][:2] == points[i + 1][:2]:
		print(points[i][3], points[i + 1][3])
		i += 2
	else:
		rem.append(points[i])
		i += 1
points = list(rem)
rem = []
i = 0
while i < len(points):
	if i < len(points) - 1 and points[i][0] == points[i + 1][0]:
		print(points[i][3], points[i + 1][3])
		i += 2
	else:
		rem.append(points[i])
		i += 1
points = list(rem)
rem = []
i = 0
while i < len(points) - 1:
	print(points[i][3], points[i + 1][3])
	i += 2


import math
import random
import heapq, bisect
import sys
from collections import deque, defaultdict
from fractions import Fraction
import sys
import threading
from collections import defaultdict
threading.stack_size(10 ** 8)
mod = 10 ** 9 + 7
mod1 = 998244353
import os
import sys
from io import BytesIO, IOBase
sys.setrecursionlimit(300000)
BUFSIZE = 8192

class FastIO(IOBase):
	newlines = 0

	def __init__(self, file):
		self._fd = file.fileno()
		self.buffer = BytesIO()
		self.writable = 'x' in file.mode or 'r' not in file.mode
		self.write = self.buffer.write if self.writable else None

	def read(self):
		while True:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			if not b:
				break
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines = 0
		return self.buffer.read()

	def readline(self):
		while self.newlines == 0:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			self.newlines = b.count(b'\n') + (not b)
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines -= 1
		return self.buffer.readline()

	def flush(self):
		if self.writable:
			os.write(self._fd, self.buffer.getvalue())
			(self.buffer.truncate(0), self.buffer.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')
(sys.stdin, sys.stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))
input = lambda : sys.stdin.readline().rstrip('\r\n')

class TreeNode:

	def __init__(self, k, v):
		self.key = k
		self.value = v
		self.left = None
		self.right = None
		self.parent = None
		self.height = 1
		self.num_left = 1
		self.num_total = 1

class AvlTree:

	def __init__(self):
		self._tree = None

	def add(self, k, v):
		if not self._tree:
			self._tree = TreeNode(k, v)
			return
		node = self._add(k, v)
		if node:
			self._rebalance(node)

	def _add(self, k, v):
		node = self._tree
		while node:
			if k < node.key:
				if node.left:
					node = node.left
				else:
					node.left = TreeNode(k, v)
					node.left.parent = node
					return node.left
			elif node.key < k:
				if node.right:
					node = node.right
				else:
					node.right = TreeNode(k, v)
					node.right.parent = node
					return node.right
			else:
				node.value = v
				return

	@staticmethod
	def get_height(x):
		return x.height if x else 0

	@staticmethod
	def get_num_total(x):
		return x.num_total if x else 0

	def _rebalance(self, node):
		n = node
		while n:
			lh = self.get_height(n.left)
			rh = self.get_height(n.right)
			n.height = max(lh, rh) + 1
			balance_factor = lh - rh
			n.num_total = 1 + self.get_num_total(n.left) + self.get_num_total(n.right)
			n.num_left = 1 + self.get_num_total(n.left)
			if balance_factor > 1:
				if self.get_height(n.left.left) < self.get_height(n.left.right):
					self._rotate_left(n.left)
				self._rotate_right(n)
			elif balance_factor < -1:
				if self.get_height(n.right.right) < self.get_height(n.right.left):
					self._rotate_right(n.right)
				self._rotate_left(n)
			else:
				n = n.parent

	def _remove_one(self, node):
		replacement = node.left or node.right
		if node.parent:
			if AvlTree._is_left(node):
				node.parent.left = replacement
			else:
				node.parent.right = replacement
			replacement.parent = node.parent
			node.parent = None
		else:
			self._tree = replacement
			replacement.parent = None
		node.left = None
		node.right = None
		node.parent = None
		self._rebalance(replacement)

	def _remove_leaf(self, node):
		if node.parent:
			if AvlTree._is_left(node):
				node.parent.left = None
			else:
				node.parent.right = None
			self._rebalance(node.parent)
		else:
			self._tree = None
		node.parent = None
		node.left = None
		node.right = None

	def remove(self, k):
		node = self._get_node(k)
		if not node:
			return
		if AvlTree._is_leaf(node):
			self._remove_leaf(node)
			return
		if node.left and node.right:
			nxt = AvlTree._get_next(node)
			node.key = nxt.key
			node.value = nxt.value
			if self._is_leaf(nxt):
				self._remove_leaf(nxt)
			else:
				self._remove_one(nxt)
			self._rebalance(node)
		else:
			self._remove_one(node)

	def get(self, k):
		node = self._get_node(k)
		return node.value if node else -1

	def _get_node(self, k):
		if not self._tree:
			return None
		node = self._tree
		while node:
			if k < node.key:
				node = node.left
			elif node.key < k:
				node = node.right
			else:
				return node
		return None

	def get_at(self, pos):
		x = pos + 1
		node = self._tree
		while node:
			if x < node.num_left:
				node = node.left
			elif node.num_left < x:
				x -= node.num_left
				node = node.right
			else:
				return (node.key, node.value)
		raise IndexError('Out of ranges')

	@staticmethod
	def _is_left(node):
		return node.parent.left and node.parent.left == node

	@staticmethod
	def _is_leaf(node):
		return node.left is None and node.right is None

	def _rotate_right(self, node):
		if not node.parent:
			self._tree = node.left
			node.left.parent = None
		elif AvlTree._is_left(node):
			node.parent.left = node.left
			node.left.parent = node.parent
		else:
			node.parent.right = node.left
			node.left.parent = node.parent
		bk = node.left.right
		node.left.right = node
		node.parent = node.left
		node.left = bk
		if bk:
			bk.parent = node
		node.height = max(self.get_height(node.left), self.get_height(node.right)) + 1
		node.num_total = 1 + self.get_num_total(node.left) + self.get_num_total(node.right)
		node.num_left = 1 + self.get_num_total(node.left)

	def _rotate_left(self, node):
		if not node.parent:
			self._tree = node.right
			node.right.parent = None
		elif AvlTree._is_left(node):
			node.parent.left = node.right
			node.right.parent = node.parent
		else:
			node.parent.right = node.right
			node.right.parent = node.parent
		bk = node.right.left
		node.right.left = node
		node.parent = node.right
		node.right = bk
		if bk:
			bk.parent = node
		node.height = max(self.get_height(node.left), self.get_height(node.right)) + 1
		node.num_total = 1 + self.get_num_total(node.left) + self.get_num_total(node.right)
		node.num_left = 1 + self.get_num_total(node.left)

	@staticmethod
	def _get_next(node):
		if not node.right:
			return node.parent
		n = node.right
		while n.left:
			n = n.left
		return n

class SegmentTree1:

	def __init__(self, data, default=2 ** 51, func=lambda a, b: a & b):
		self._default = default
		self._func = func
		self._len = len(data)
		self._size = _size = 1 << (self._len - 1).bit_length()
		self.data = [default] * (2 * _size)
		self.data[_size:_size + self._len] = data
		for i in reversed(range(_size)):
			self.data[i] = func(self.data[i + i], self.data[i + i + 1])

	def __delitem__(self, idx):
		self[idx] = self._default

	def __getitem__(self, idx):
		return self.data[idx + self._size]

	def __setitem__(self, idx, value):
		idx += self._size
		self.data[idx] = value
		idx >>= 1
		while idx:
			self.data[idx] = self._func(self.data[2 * idx], self.data[2 * idx + 1])
			idx >>= 1

	def __len__(self):
		return self._len

	def query(self, start, stop):
		if start == stop:
			return self.__getitem__(start)
		stop += 1
		start += self._size
		stop += self._size
		res = self._default
		while start < stop:
			if start & 1:
				res = self._func(res, self.data[start])
				start += 1
			if stop & 1:
				stop -= 1
				res = self._func(res, self.data[stop])
			start >>= 1
			stop >>= 1
		return res

	def __repr__(self):
		return 'SegmentTree({0})'.format(self.data)

class SegmentTree:

	def __init__(self, data, default=0, func=lambda a, b: a + b):
		self._default = default
		self._func = func
		self._len = len(data)
		self._size = _size = 1 << (self._len - 1).bit_length()
		self.data = [default] * (2 * _size)
		self.data[_size:_size + self._len] = data
		for i in reversed(range(_size)):
			self.data[i] = func(self.data[i + i], self.data[i + i + 1])

	def __delitem__(self, idx):
		self[idx] = self._default

	def __getitem__(self, idx):
		return self.data[idx + self._size]

	def __setitem__(self, idx, value):
		idx += self._size
		self.data[idx] = value
		idx >>= 1
		while idx:
			self.data[idx] = self._func(self.data[2 * idx], self.data[2 * idx + 1])
			idx >>= 1

	def __len__(self):
		return self._len

	def query(self, start, stop):
		if start == stop:
			return self.__getitem__(start)
		stop += 1
		start += self._size
		stop += self._size
		res = self._default
		while start < stop:
			if start & 1:
				res = self._func(res, self.data[start])
				start += 1
			if stop & 1:
				stop -= 1
				res = self._func(res, self.data[stop])
			start >>= 1
			stop >>= 1
		return res

	def __repr__(self):
		return 'SegmentTree({0})'.format(self.data)

class Factorial:

	def __init__(self, MOD):
		self.MOD = MOD
		self.factorials = [1, 1]
		self.invModulos = [0, 1]
		self.invFactorial_ = [1, 1]

	def calc(self, n):
		if n <= -1:
			print('Invalid argument to calculate n!')
			print('n must be non-negative value. But the argument was ' + str(n))
			exit()
		if n < len(self.factorials):
			return self.factorials[n]
		nextArr = [0] * (n + 1 - len(self.factorials))
		initialI = len(self.factorials)
		prev = self.factorials[-1]
		m = self.MOD
		for i in range(initialI, n + 1):
			prev = nextArr[i - initialI] = prev * i % m
		self.factorials += nextArr
		return self.factorials[n]

	def inv(self, n):
		if n <= -1:
			print('Invalid argument to calculate n^(-1)')
			print('n must be non-negative value. But the argument was ' + str(n))
			exit()
		p = self.MOD
		pi = n % p
		if pi < len(self.invModulos):
			return self.invModulos[pi]
		nextArr = [0] * (n + 1 - len(self.invModulos))
		initialI = len(self.invModulos)
		for i in range(initialI, min(p, n + 1)):
			next = -self.invModulos[p % i] * (p // i) % p
			self.invModulos.append(next)
		return self.invModulos[pi]

	def invFactorial(self, n):
		if n <= -1:
			print('Invalid argument to calculate (n^(-1))!')
			print('n must be non-negative value. But the argument was ' + str(n))
			exit()
		if n < len(self.invFactorial_):
			return self.invFactorial_[n]
		self.inv(n)
		nextArr = [0] * (n + 1 - len(self.invFactorial_))
		initialI = len(self.invFactorial_)
		prev = self.invFactorial_[-1]
		p = self.MOD
		for i in range(initialI, n + 1):
			prev = nextArr[i - initialI] = prev * self.invModulos[i % p] % p
		self.invFactorial_ += nextArr
		return self.invFactorial_[n]

class Combination:

	def __init__(self, MOD):
		self.MOD = MOD
		self.factorial = Factorial(MOD)

	def ncr(self, n, k):
		if k < 0 or n < k:
			return 0
		k = min(k, n - k)
		f = self.factorial
		return f.calc(n) * f.invFactorial(max(n - k, k)) * f.invFactorial(min(k, n - k)) % self.MOD

def powm(a, n, m):
	if a == 1 or n == 0:
		return 1
	if n % 2 == 0:
		s = powm(a, n // 2, m)
		return s * s % m
	else:
		return a * powm(a, n - 1, m) % m

def sort_list(list1, list2):
	zipped_pairs = zip(list2, list1)
	z = [x for (_, x) in sorted(zipped_pairs)]
	return z

def product(l):
	por = 1
	for i in range(len(l)):
		por *= l[i]
	return por

def binarySearchCount(arr, n, key):
	left = 0
	right = n - 1
	count = 0
	while left <= right:
		mid = int((right + left) / 2)
		if arr[mid] < key:
			count = mid + 1
			left = mid + 1
		else:
			right = mid - 1
	return count

def countdig(n):
	c = 0
	while n > 0:
		n //= 10
		c += 1
	return c

def binary(x, length):
	y = bin(x)[2:]
	return y if len(y) >= length else '0' * (length - len(y)) + y

def countGreater(arr, n, k):
	l = 0
	r = n - 1
	leftGreater = n
	while l <= r:
		m = int(l + (r - l) / 2)
		if arr[m] >= k:
			leftGreater = m
			r = m - 1
		else:
			l = m + 1
	return n - leftGreater
n = int(input())
d = defaultdict(list)

def solve(e):
	e.sort()
	for i in range(len(e) // 2):
		print(e[2 * i][3], e[2 * i + 1][3])
	if len(e) % 2 == 1:
		return e[-1]
	else:
		return -1

def solve2(l):
	de = defaultdict(list)
	for i in range(len(l)):
		de[l[i][1]].append((l[i][0], l[i][1], l[i][2], l[i][3]))
	e = []
	for i in de:
		r = solve(de[i])
		if r != -1:
			e.append(r)
	e.sort()
	for i in range(len(e) // 2):
		print(e[2 * i][3], e[2 * i + 1][3])
	if len(e) % 2 == 1:
		return e[-1]
	else:
		return -1
for i in range(n):
	(a, b, c) = map(int, input().split())
	d[a].append((a, b, c, i + 1))
e = []
for i in d:
	r = solve2(d[i])
	if r != -1:
		e.append(r)
e.sort()
for i in range(len(e) // 2):
	print(e[2 * i][3], e[2 * i + 1][3])


n = int(input())
dot = []
for i in range(n):
	(x, y, z) = map(int, input().split())
	dot.append([x, y, z, i + 1])
dot.sort(key=lambda x: (x[2], x[0], x[1]))
m = {}
for i in range(n):
	if dot[i][2] not in m:
		m[dot[i][2]] = [dot[i]]
	else:
		m[dot[i][2]].append(dot[i])
new_m = []
for key in m:
	pl = m[key]
	g = {}
	for i in range(len(pl)):
		if pl[i][0] not in g:
			g[pl[i][0]] = [pl[i]]
		else:
			g[pl[i][0]].append(pl[i])
	e = []
	for gkey in g:
		for i in range(0, len(g[gkey]) - 1, 2):
			print(g[gkey][i][-1], g[gkey][i + 1][-1])
		if len(g[gkey]) % 2 == 1:
			e.append(g[gkey][-1])
	for i in range(0, len(e) - 1, 2):
		print(e[i][-1], e[i + 1][-1])
	if len(e) % 2 == 1:
		new_m.append(e[-1])
for i in range(0, len(new_m), 2):
	print(new_m[i][-1], new_m[i + 1][-1])


import sys
import os
from io import IOBase, BytesIO

def solve(n, points):
	for i in range(n):
		points[i].append(i + 1)
	points.sort()
	points += [(float('inf'),) * 3]
	rem = []
	i = 0
	while i < n:
		if points[i][:2] == points[i + 1][:2]:
			print(points[i][3], points[i + 1][3])
			i += 2
		else:
			rem.append(i)
			i += 1
	rem.append(n)
	rem2 = []
	i = 0
	while i < len(rem) - 1:
		if points[rem[i]][0] == points[rem[i + 1]][0]:
			print(points[rem[i]][3], points[rem[i + 1]][3])
			i += 2
		else:
			rem2.append(points[rem[i]][3])
			i += 1
	for i in range(0, len(rem2), 2):
		print(rem2[i], rem2[i + 1])

def main():
	n = int(input())
	points = [[int(c) for c in input().split()] for _ in range(n)]
	solve(n, points)
BUFSIZE = 8192

class FastIO(BytesIO):
	newlines = 0

	def __init__(self, file):
		self._file = file
		self._fd = file.fileno()
		self.writable = 'x' in file.mode or 'w' in file.mode
		self.write = super(FastIO, self).write if self.writable else None

	def _fill(self):
		s = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
		self.seek((self.tell(), self.seek(0, 2), super(FastIO, self).write(s))[0])
		return s

	def read(self):
		while self._fill():
			pass
		return super(FastIO, self).read()

	def readline(self):
		while self.newlines == 0:
			s = self._fill()
			self.newlines = s.count(b'\n') + (not s)
		self.newlines -= 1
		return super(FastIO, self).readline()

	def flush(self):
		if self.writable:
			os.write(self._fd, self.getvalue())
			(self.truncate(0), self.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')
(sys.stdin, sys.stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))

def input():
	return sys.stdin.readline().rstrip('\r\n')
main()


import os, sys
from io import BytesIO, IOBase
from collections import defaultdict
left = []

def fun(lst, co):
	global left
	dctx = defaultdict(list)
	leftx = []
	for i in lst:
		dctx[co[i][0]].append(i)
	for i in dctx.values():
		for j in range(len(i) // 2):
			print(i[2 * j] + 1, i[2 * j + 1] + 1)
		if len(i) & 1:
			leftx.append(i[-1])
	leftx.sort(key=lambda xx: co[xx][0])
	for i in range(len(leftx) // 2):
		print(leftx[2 * i] + 1, leftx[2 * i + 1] + 1)
	if len(leftx) & 1:
		left.append(leftx[-1])

def main():
	global left
	n = int(input())
	co = [list(map(int, input().split())) for _ in range(n)]
	orde = sorted(range(n), key=lambda xx: co[xx][1])
	dctz = defaultdict(list)
	for i in orde:
		dctz[co[i][2]].append(i)
	for i in dctz.values():
		fun(i, co)
	left.sort(key=lambda xx: co[xx][2])
	for i in range(len(left) // 2):
		print(left[2 * i] + 1, left[2 * i + 1] + 1)
BUFSIZE = 8192

class FastIO(IOBase):
	newlines = 0

	def __init__(self, file):
		self._fd = file.fileno()
		self.buffer = BytesIO()
		self.writable = 'x' in file.mode or 'r' not in file.mode
		self.write = self.buffer.write if self.writable else None

	def read(self):
		while True:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			if not b:
				break
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines = 0
		return self.buffer.read()

	def readline(self):
		while self.newlines == 0:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			self.newlines = b.count(b'\n') + (not b)
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines -= 1
		return self.buffer.readline()

	def flush(self):
		if self.writable:
			os.write(self._fd, self.buffer.getvalue())
			(self.buffer.truncate(0), self.buffer.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')
(sys.stdin, sys.stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))
input = lambda : sys.stdin.readline().rstrip('\r\n')
main()


from collections import defaultdict, deque, Counter
from sys import stdin, stdout
from heapq import heappush, heappop
import math
import io
import os
import math
import bisect

def isPrime(x):
	for i in range(2, x):
		if i * i > x:
			break
		if x % i == 0:
			return False
	return True

def ncr(n, r, p):
	num = den = 1
	for i in range(r):
		num = num * (n - i) % p
		den = den * (i + 1) % p
	return num * pow(den, p - 2, p) % p

def primeFactors(n):
	l = []
	while n % 2 == 0:
		l.append(2)
		n = n / 2
	for i in range(3, int(math.sqrt(n)) + 1, 2):
		while n % i == 0:
			l.append(int(i))
			n = n / i
	if n > 2:
		l.append(n)
	return list(set(l))

def power(x, y, p):
	res = 1
	x = x % p
	if x == 0:
		return 0
	while y > 0:
		if y & 1 == 1:
			res = res * x % p
		y = y >> 1
		x = x * x % p
	return res

def sieve(n):
	prime = [True for i in range(n + 1)]
	p = 2
	while p * p <= n:
		if prime[p] == True:
			for i in range(p * p, n + 1, p):
				prime[i] = False
		p += 1
	return prime

def digits(n):
	c = 0
	while n > 0:
		n //= 10
		c += 1
	return c

def ceil(n, x):
	if n % x == 0:
		return n // x
	return n // x + 1

def mapin():
	return map(int, input().split())
n = int(input())
d = []
for i in range(n):
	(a, b, c) = mapin()
	d.append([a, b, c, i + 1])
while len(d) > 0:
	d.sort()
	temp = d[-1]
	d.pop(-1)
	ind = 0
	ma = 1e+20
	for i in range(len(d)):
		dd = abs(d[i][0] - temp[0])
		dd += abs(d[i][1] - temp[1])
		dd += abs(d[i][2] - temp[2])
		if dd < ma:
			ma = dd
			ind = i
	print(temp[-1], d[ind][-1])
	d.pop(ind)


from bisect import bisect

def solve(points):

	def it():
		Z = sorted(set((z for (x, y, z) in points)))
		z_bin = [[] for _ in range(len(Z))]
		for (i, (x, y, z)) in enumerate(points):
			z_bin[bisect(Z, z) - 1].append(i)
		last_z = -1
		for (zi, plane) in enumerate(z_bin):
			Y = sorted(set((points[i][1] for i in plane)))
			y_bin = [[] for _ in range(len(Y))]
			for i in plane:
				(x, y, z) = points[i]
				y_bin[bisect(Y, y) - 1].append(i)
			last_y = -1
			for (yi, line) in enumerate(y_bin):
				line.sort(key=lambda i: points[i][0])
				m = iter(line)
				for (a, b) in zip(m, m):
					yield (a, b)
				if len(line) % 2:
					if last_y < 0:
						last_y = line[-1]
					else:
						yield (last_y, line[-1])
						last_y = -1
			if last_y >= 0:
				if last_z < 0:
					last_z = last_y
				else:
					yield (last_z, last_y)
					last_z = -1
	return it()
import sys
read = sys.stdin.buffer.read
readline = sys.stdin.buffer.readline
N = int(readline())
m = map(int, read().split())
res = solve(tuple(zip(m, m, m)))
print('\n'.join((f'{a + 1} {b + 1}' for (a, b) in res)))


import sys
input = sys.stdin.readline
n = int(input())
p = [[*map(int, input().split())] for i in range(n)]
a = p[0]
b = p[1]
pt1 = 0
pt2 = 1
d = dict()
while pt1 < n and pt2 < n:
	a = p[pt1]
	b = p[pt2]
	yy = pt2 + 1
	for i in range(yy, n):
		if d.get(i, -1) == -1 and min(a[0], b[0]) <= p[i][0] <= max(a[0], b[0]) and (min(a[1], b[1]) <= p[i][1] <= max(a[1], b[1])) and (min(a[2], b[2]) <= p[i][2] <= max(a[2], b[2])):
			b = p[i]
			pt2 = i
	print(pt1 + 1, pt2 + 1)
	d[pt1] = 1
	d[pt2] = 1
	while pt1 < n and d.get(pt1, -1) == 1:
		pt1 += 1
	pt2 = pt1 + 1
	while pt2 < n and d.get(pt2, -1) == 1:
		pt2 += 1


import sys

def main():
	n = int(sys.stdin.readline())
	points = [None] * n
	for i in range(n):
		(x, y, z) = map(int, sys.stdin.readline().split())
		points[i] = [x, y, z, i]
	points.sort()
	for _ in range(n // 2):
		best = 1
		for i in range(2, len(points)):
			x1 = abs(points[best][0] - points[0][0])
			y1 = abs(points[best][1] - points[0][1])
			z1 = abs(points[best][2] - points[0][2])
			x2 = abs(points[i][0] - points[0][0])
			y2 = abs(points[i][1] - points[0][1])
			z2 = abs(points[i][2] - points[0][2])
			if x2 * y2 * z2 < x1 * y1 * z1 or (min(points[best][0], points[0][0]) <= points[i][0] <= max(points[best][0], points[0][0]) and min(points[best][1], points[0][1]) <= points[i][1] <= max(points[best][1], points[0][1]) and (min(points[best][2], points[0][2]) <= points[i][2] <= max(points[best][2], points[0][2]))):
				best = i
		print(points[0][3] + 1, points[best][3] + 1)
		del points[best]
		del points[0]
main()


n = int(input())
pts = {}
for i in range(1, n + 1):
	(x, y, z) = map(int, input().split())
	pts[i] = (x, y, z)
ans = []
(x, y, z) = pts[1]
pair = [1]
del pts[1]
while pts:
	mn = float('inf')
	for i in pts:
		(x1, y1, z1) = pts[i]
		dist = abs(x - x1) + abs(y - y1) + abs(z - z1)
		if dist < mn:
			mn = dist
			p = i
	pair.append(p)
	ans.append(pair)
	del pts[pair[-1]]
	if pts:
		for i in pts:
			(x, y, z) = pts[i]
			pair = [i]
			del pts[i]
			break
for a in ans:
	print(*a)


import sys, os, io
from collections import defaultdict
input = io.BytesIO(os.read(0, os.fstat(0).st_size)).readline
n = int(input())
a = []
for i in range(n):
	(xi, yi, zi) = [int(i) for i in input().split()]
	a.append([xi, yi, zi, i + 1])
a.sort()
rem = []
ans = []
prev = 0
done = [0] * n
while 1:
	i = 1
	while i <= n - 1:
		if prev >= i:
			break
		lx = min(a[i][0], a[prev][0])
		ly = min(a[i][1], a[prev][1])
		lz = min(a[i][2], a[prev][2])
		ux = max(a[i][0], a[prev][0])
		uy = max(a[i][1], a[prev][1])
		uz = max(a[i][2], a[prev][2])
		flag = -1
		for j in range(i + 1, n):
			if done[j]:
				continue
			if a[j][0] >= lx and a[j][0] <= ux and (a[j][1] >= ly) and (a[j][1] <= uy) and (a[j][2] >= lz) and (a[j][2] <= uz):
				i = j
				lx = min(a[i][0], a[prev][0])
				ly = min(a[i][1], a[prev][1])
				lz = min(a[i][2], a[prev][2])
				ux = max(a[i][0], a[prev][0])
				uy = max(a[i][1], a[prev][1])
				uz = max(a[i][2], a[prev][2])
			if a[j][0] > ux or (a[j][0] == ux and a[j][1] > uy) or (a[j][0] == ux and a[j][1] == uy and (a[j][2] > uz)):
				break
		for j in range(prev - 1, -1, -1):
			if done[j]:
				continue
			if a[j][0] >= lx and a[j][0] <= ux and (a[j][1] >= ly) and (a[j][1] <= uy) and (a[j][2] >= lz) and (a[j][2] <= uz):
				i = j
				lx = min(a[i][0], a[prev][0])
				ly = min(a[i][1], a[prev][1])
				lz = min(a[i][2], a[prev][2])
				ux = max(a[i][0], a[prev][0])
				uy = max(a[i][1], a[prev][1])
				uz = max(a[i][2], a[prev][2])
			if a[j][0] < ux or (a[j][0] == ux and a[j][1] < uy) or (a[j][0] == ux and a[j][1] == uy and (a[j][2] < uz)):
				break
		done[prev] = 1
		done[i] = 1
		ans.append([a[prev][3], a[i][3]])
		done[prev] = 1
		done[i] = 1
		for j in range(prev + 1, n):
			prev = j
			if done[j] == 0:
				break
		for j in range(prev + 1, n):
			i = j
			if done[j] == 0:
				break
	if prev >= i:
		break
	if len(rem) == 0:
		break
	a = rem[:]
	n = len(a)
	rem = []
for i in ans:
	print(*i, flush=0)


from collections import OrderedDict

def SortPoint(p, ids, k, D):
	if k == D:
		return ids[0]
	maps = OrderedDict()
	for i in ids:
		if p[i][k] not in maps:
			maps[p[i][k]] = []
		maps[p[i][k]].append(i)
	a = []
	for i in sorted(maps.keys()):
		cnt = SortPoint(p, maps[i], k + 1, D)
		if cnt != -1:
			a.append(cnt)
	for i in range(0, len(a), 2):
		if i + 1 < len(a):
			print(a[i] + 1, a[i + 1] + 1)
	return -1 if len(a) % 2 == 0 else a[-1]

def solver():
	n = int(input())
	p = []
	ids = [i for i in range(n)]
	for i in range(n):
		point = list(map(int, input().split()))
		p.append(point)
	SortPoint(p, ids, 0, 3)

def main():
	while 1:
		try:
			solver()
		except EOFError:
			break
main()


def solve_z(points):
	cur = None
	d = {}
	for p in points:
		if p[0] not in d:
			d[p[0]] = []
		d[p[0]].append(p)
	for k in d:
		d[k] = sorted(d[k], key=lambda x: x[1])
	for k in sorted(d.keys()):
		n = len(d[k])
		for i in range(0, n, 2):
			if i + 1 == n:
				if cur is None:
					cur = str(d[k][i][3])
				else:
					print(cur + ' ' + str(d[k][i][3]))
					cur = None
			else:
				(p1, p2) = (d[k][i][3], d[k][i + 1][3])
				print(str(p1) + ' ' + str(p2))
	return cur
n = int(input())
P = [list(map(int, input().split())) + [i + 1] for i in range(n)]
d_z = {}
for p in P:
	if p[2] not in d_z:
		d_z[p[2]] = []
	d_z[p[2]].append(p)
cur = None
for k in sorted(d_z.keys()):
	next_ = solve_z(d_z[k])
	if next_ is not None:
		if cur is None:
			cur = next_
		else:
			print(cur + ' ' + next_)
			cur = None


n = int(input())
points = []
for i in range(n):
	(x, y, z) = map(int, input().split())
	points.append((x, y, z, i + 1))
points.sort()
x_points = {}
for p in points:
	(x, y, z, i) = p
	if x in x_points:
		x_points[x].append(p)
	else:
		x_points[x] = [p]
x_to_remove = []
for (x, pp) in x_points.items():
	if len(pp) >= 2:
		y_points = {}
		for p in pp:
			(x, y, z, i) = p
			if y in y_points:
				y_points[y].append(p)
			else:
				y_points[y] = [p]
		y_to_remove = []
		for (y, ppp) in y_points.items():
			if len(ppp) >= 2:
				ppp.sort()
				for i in range(0, len(ppp) - 1, 2):
					print(ppp[i][3], ppp[i + 1][3])
				if len(ppp) % 2:
					y_points[y] = [ppp[-1]]
				else:
					y_to_remove.append(y)
		for y in y_to_remove:
			del y_points[y]
		indexes = [(ppp[0][3], y) for (y, ppp) in sorted(y_points.items())]
		for i in range(0, len(indexes) - 1, 2):
			print(indexes[i][0], indexes[i + 1][0])
		if len(indexes) % 2:
			x_points[x] = y_points[indexes[-1][1]]
		else:
			x_to_remove.append(x)
for x in x_to_remove:
	del x_points[x]
indexes = [pp[0][3] for (x, pp) in sorted(x_points.items())]
for i in range(0, len(indexes), 2):
	print(indexes[i], indexes[i + 1])