Problem: You are given n arrays a_1, a_2, ..., a_n; each array consists of exactly m integers. We denote the y-th element of the x-th array as a_{x, y}.

You have to choose two arrays a_i and a_j (1 ≤ i, j ≤ n, it is possible that i = j). After that, you will obtain a new array b consisting of m integers, such that for every k ∈ [1, m] b_k = max(a_{i, k}, a_{j, k}).

Your goal is to choose i and j so that the value of min _{k = 1}^{m} b_k is maximum possible.

Input

The first line contains two integers n and m (1 ≤ n ≤ 3 ⋅ 10^5, 1 ≤ m ≤ 8) — the number of arrays and the number of elements in each array, respectively.

Then n lines follow, the x-th line contains the array a_x represented by m integers a_{x, 1}, a_{x, 2}, ..., a_{x, m} (0 ≤ a_{x, y} ≤ 10^9).

Output

Print two integers i and j (1 ≤ i, j ≤ n, it is possible that i = j) — the indices of the two arrays you have to choose so that the value of min _{k = 1}^{m} b_k is maximum possible. If there are multiple answers, print any of them.

Example

Input


6 5
5 0 3 1 2
1 8 9 1 3
1 2 3 4 5
9 1 0 3 7
2 3 0 6 3
6 4 1 7 0


Output


1 5

Solution (in JAVA):

Solution:
import java.util.*;

import java.io.*;
import java.math.BigInteger;

public class dsu {

	public static class UnionFind {
		int[] p, rank, setSize;
		int numSets;
		Queue<Integer> subs[];

		public UnionFind(int N) {
			p = new int[numSets = N];
			rank = new int[N];
			setSize = new int[N];
			subs = new LinkedList[N];
			for (int i = 0; i < N; i++) {
				subs[i] = new LinkedList<Integer>();
				subs[i].add(i);
				p[i] = i;
				setSize[i] = 1;

			}
		}

		public int findSet(int i) {
			return p[i] == i ? i : (p[i] = findSet(p[i]));
		}

		public boolean isSameSet(int i, int j) {
			return findSet(i) == findSet(j);
		}

		public void unionSet(int i, int j) {
			if (isSameSet(i, j))
				return;
			numSets--;
			int x = findSet(i), y = findSet(j);
			if (rank[x] > rank[y]) {
				p[y] = x;
				while (!subs[y].isEmpty())
					subs[x].add(subs[y].poll());
				setSize[x] += setSize[y];
			} else {
				p[x] = y;
				setSize[y] += setSize[x];
				while (!subs[x].isEmpty())
					subs[y].add(subs[x].poll());
				if (rank[x] == rank[y])
					rank[y]++;
			}
		}

		public int numDisjointSets() {
			return numSets;
		}

		public int sizeOfSet(int i) {
			return setSize[findSet(i)];
		}
	}

	static class pair implements Comparable<pair> {
		int x;
		int y;

		public pair(int d, int k) {
			x = d;
			y = k;
		}

		@Override
		public int compareTo(pair arg0) {

			return x - arg0.x;
		}
	}

	static ArrayList<Integer> primes;
	static int[] isComposite;

	static void sieve(int N) // O(N log log N)
	{
		isComposite = new int[N + 1];
		isComposite[0] = isComposite[1] = 1; // 0 indicates a prime number
		primes = new ArrayList<Integer>();

		for (int i = 2; i <= N; ++i) // can loop till i*i <= N if primes array is not needed O(N log log sqrt(N))
			if (isComposite[i] == 0) // can loop in 2 and odd integers for slightly better performance
			{
				primes.add(i);
				if (1l * i * i <= N)
					for (int j = i * i; j <= N; j += i) // j = i * 2 will not affect performance too much, may alter in
														// modified sieve
						isComposite[j] = 1;
			}
	}

	static boolean isPrime(int N) {
		if (N < isComposite.length)
			return isComposite[N] == 0;
		for (int p : primes) // may stop if p * p > N
			if (N % p == 0)
				return false;
		return true;
	}

	static int m;
	static ArrayList<pair> mask;

	public static void main(String[] args) throws NumberFormatException, IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		PrintWriter pw = new PrintWriter(System.out);
		StringTokenizer st = new StringTokenizer(br.readLine());

		int n = Integer.parseInt(st.nextToken());
		m = Integer.parseInt(st.nextToken());
		int a[][] = new int[n][m];

		for (int i = 0; i < a.length; i++) {
			st = new StringTokenizer(br.readLine());
			for (int j = 0; j < m; j++) {
				a[i][j] = Integer.parseInt(st.nextToken());
			}
		}
		int lo = 0;
		int hi = (int) 1e9;
		pair ans = new pair(-1, -1);
		while (lo <= hi) {
			int mid = (lo + hi) >> 1;
			mask = new ArrayList<>();
			HashSet<Integer> h = new HashSet<>();
			for (int i = 0; i < n; i++) {
				int x = 0;
				for (int j = 0; j < m; j++) {
					if (a[i][j] >= mid) {
						x = (x << 1) | 1;
					} else
						x = (x << 1);
				}
				if (!h.contains(x)) {
					mask.add(new pair(x, i));
					h.add(x);
				}
			}
			if (check().x != -1) {
				ans = check();
				lo = mid + 1;
			} else {
				hi = mid - 1;
			}

		}
		pw.print(ans.x + " " + ans.y);
		pw.flush();
	}

	public static pair check() {
		int x = -1;
		int y = -1;
		for (int i = 0; i < mask.size(); i++) {
			for (int j = 0; j < mask.size(); j++) {
				if ((mask.get(i).x | mask.get(j).x) == (int) Math.pow(2, m) - 1) {
					x = mask.get(i).y + 1;
					y = mask.get(j).y + 1;
				}
			}
		}
		return new pair(x, y);
	}

	public static int stringCompare(String str1, String str2) {

		int l1 = str1.length();
		int l2 = str2.length();
		int lmin = Math.min(l1, l2);

		for (int i = 0; i < lmin; i++) {
			int str1_ch = (int) str1.charAt(i);
			int str2_ch = (int) str2.charAt(i);

			if (str1_ch != str2_ch) {
				return str1_ch - str2_ch;
			}
		}

		if (l1 != l2) {
			return l1 - l2;
		}

		// If none of the above conditions is true,
		// it implies both the strings are equal
		else {
			return 0;
		}
	}

	static long gcd(long a, long b) {
		if (b == 0)
			return a;
		return gcd(b, a % b);
	}

	static long lcm(long a, long b) {
		return (a * b / gcd(a, b));
	}

}