Problem: Very soon Berland will hold a School Team Programming Olympiad. From each of the m Berland regions a team of two people is invited to participate in the olympiad. The qualifying contest to form teams was held and it was attended by n Berland students. There were at least two schoolboys participating from each of the m regions of Berland. The result of each of the participants of the qualifying competition is an integer score from 0 to 800 inclusive.

The team of each region is formed from two such members of the qualifying competition of the region, that none of them can be replaced by a schoolboy of the same region, not included in the team and who received a greater number of points. There may be a situation where a team of some region can not be formed uniquely, that is, there is more than one school team that meets the properties described above. In this case, the region needs to undertake an additional contest. The two teams in the region are considered to be different if there is at least one schoolboy who is included in one team and is not included in the other team. It is guaranteed that for each region at least two its representatives participated in the qualifying contest.

Your task is, given the results of the qualifying competition, to identify the team from each region, or to announce that in this region its formation requires additional contests.

Input

The first line of the input contains two integers n and m (2 ≤ n ≤ 100 000, 1 ≤ m ≤ 10 000, n ≥ 2m) — the number of participants of the qualifying contest and the number of regions in Berland.

Next n lines contain the description of the participants of the qualifying contest in the following format: Surname (a string of length from 1 to 10 characters and consisting of large and small English letters), region number (integer from 1 to m) and the number of points scored by the participant (integer from 0 to 800, inclusive).

It is guaranteed that all surnames of all the participants are distinct and at least two people participated from each of the m regions. The surnames that only differ in letter cases, should be considered distinct.

Output

Print m lines. On the i-th line print the team of the i-th region — the surnames of the two team members in an arbitrary order, or a single character "?" (without the quotes) if you need to spend further qualifying contests in the region.

Examples

Input

5 2
Ivanov 1 763
Andreev 2 800
Petrov 1 595
Sidorov 1 790
Semenov 2 503


Output

Sidorov Ivanov
Andreev Semenov


Input

5 2
Ivanov 1 800
Andreev 2 763
Petrov 1 800
Sidorov 1 800
Semenov 2 503


Output

?
Andreev Semenov

Note

In the first sample region teams are uniquely determined.

In the second sample the team from region 2 is uniquely determined and the team from region 1 can have three teams: "Petrov"-"Sidorov", "Ivanov"-"Sidorov", "Ivanov" -"Petrov", so it is impossible to determine a team uniquely.

Solution (in JAVA):

Solution:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.StringTokenizer;
		public class Solution2{
			static int sum[] = new int[1000000];
			public static void main(String[] args) throws IOException{
				FastReader sc = new FastReader();
				int n = sc.nextInt();
				int m  =sc.nextInt();
				ArrayList<pair>[] reg = new ArrayList[m];
				for (int i = 0; i < reg.length; i++) {
					reg[i] = new  ArrayList<pair>();
				}
				
				for (int i = 0; i < n; i++) {
					String name = sc.next();
					int r = sc.nextInt()-1;
					int score = sc.nextInt();
					pair p = new pair(name,score);
					reg[r].add(p);
				}
				for (int i = 0; i < reg.length; i++) {
					Collections.sort(reg[i]);
					if(reg[i].size() >= 3 && reg[i].get(2).score == reg[i].get(1).score) 
						System.out.println("?");
					else 
						System.out.println(reg[i].get(0).name + " " + reg[i].get(1).name);
					
				}
			}
			
			static class pair implements Comparable<pair>{
				String name;
				int score;
				public pair(String name, int score) {
					this.name = name;
					this.score = score;
				}
				
				public int compareTo(pair o ){
					return o.score - score;
				}
				
				
			}
			static class MyDs{
				ArrayList<Integer> arr;
				HashMap<Integer,LinkedList<Integer>> hmap;
				public MyDs(){
					arr = new ArrayList<Integer>();
					hmap = new HashMap<Integer,LinkedList<Integer>>();
				}
				void add(int x){
					if(!hmap.containsKey(x)){
						hmap.put(x,new LinkedList());
					}
					int s = arr.size();
					arr.add(x);
					hmap.get(x).add(s);
				}
				void remove(int x){
					if(!hmap.containsKey(x)){
						return;
					}
					Integer index = hmap.get(x).get(0);
					hmap.get(x).poll();
					int size = arr.size();
					Integer last = arr.get(arr.size() - 1);
					Collections.swap(arr,index,arr.size() - 1);
					arr.remove(size - 1);
					hmap.get(last).set(hmap.get(last).indexOf(size),index);
				}
				LinkedList<Integer> search(int x){
					return hmap.get(x);
				}
			}
			public static boolean compare(int[] arr,int[] arr2){
				boolean ans = true;
				for(int i = 0;i < 26;i++){
					if(arr[i] < arr2[i]){
						ans = false;
						break;
					}
				}
				return ans;
			}
			int gcd(int a, int b)
			{
			    if (b==0)
			        return a;
			    return gcd(b, a%b);
			}
			 
			 // Returns LCM of array elements
			static long findlcm(int arr[], int n)
			{
			    // Initialize result
			    long ans = arr[0];
			 
			    // ans contains LCM of arr[0],..arr[i]
			    // after i'th iteration,
			    for (int i=1; i<n; i++)
			        ans = ( ((arr[i]*ans)) /
			                (gcd(arr[i], ans)) );
			 
			    return ans;
			}
			public static boolean isPalindrome(String s){
				StringBuilder st = new StringBuilder(s);
				if(st.reverse().toString().equals(s)){
					return true;
				}
				return false;
			}
			public static int digits(int a){
				int count = 0;
				while(a != 0){
					count++;
					a = a/10;
				}
				return count;
			}
			public static long min(long n,long k){
				long ans = 0;
				long calc = n/k;
				long calc2 = n % k;
				long recalc = k - calc2;
				ans += calc * (calc - 1)/2 * recalc;
				ans += (calc + 1) * calc/2 * calc2;
				return ans;
			}
			public static long max(long n,long k){
				long ans = 0;
				long calc = n - k + 1;
				ans += calc * (calc - 1)/2;
				return ans;
			}
			public static boolean isOk(int[] arr,int size){
				if(size % 2 == 0){
					for(int i = 0;i < arr.length;i++){
						if(arr[i] % 2 != 0){
							return false;
						}
					}
					return true;
				}else{
					int count = 0;
					for(int i = 0;i < arr.length;i++){
						if(arr[i] % 2 != 0){
							count++;
						}
					}
					if(count > 1){
						return false;
					}
					return true;
				}
			}
			public static int complement(int index){
				if(index == 0){
					return 1;
				}
				return 0;
			}
			private static long gcd(long a, long b)
			{
			    while (b > 0)
			    {
			        long temp = b;
			        b = a % b; // % is remainder
			        a = temp;
			    }
			    return a;
			}
			void convertDegree(){
				
			}
			private static long gcd(long[] input)
			{
			    long result = input[0];
			    for(int i = 1; i < input.length; i++) result = gcd(result, input[i]);
			    return result;
			}
			private static long lcm(long a, long b)
			{
			    return a * (b / gcd(a, b));
			}
		
			private static long lcm(long[] input)
			{
			    long result = input[0];
			    for(int i = 1; i < input.length; i++) result = lcm(result, input[i]);
			    return result;
			}
			public static boolean[] sieveAlgo(int n){
				boolean isPrime[] = new boolean[n + 1];
				for (int i = 2; i <= n; i++) {
		            isPrime[i] = true;
		        }
		
		        // mark non-primes <= n using Sieve of Eratosthenes
		        for (int factor = 2; factor*factor <= n; factor++) {
		
		            // if factor is prime, then mark multiples of factor as nonprime
		            // suffices to consider mutiples factor, factor+1, ...,  n/factor
		            if (isPrime[factor]) {
		                for (int j = factor; factor*j <= n; j++) {
		                    isPrime[factor*j] = false;
		                }
		            }
		        }
		        return isPrime;
			}
			public static int binarySearch(int[] arr,int low,int high,int key){
				while(low <= high){
					int mid = (low + high)/2;
					if(arr[mid] == key){
						return mid;
					}else if(arr[mid] < key){
						low = mid + 1;
					}else{
						high = mid - 1;
					}
				}
				return -1;
			}
			public static int ternarySearch(int[] arr,int l,int r,int key){
				if(r >= l){
					int mid1 = l + (r-1)/3;
					int mid2 = r - (r-1)/3;
					if(arr[mid1] == key){
						return mid1;
					}else if(arr[mid2] == key){
						return mid2;
					}
					if(key < arr[mid1]){
						return ternarySearch(arr,l,mid1-1,key);
					}else if(key > arr[mid2]){
						return ternarySearch(arr,mid2 + 1,r,key);
					}else{
						return ternarySearch(arr,mid1 + 1,mid2 - 1,key);
					}
				}
				return -1;
			}
			public static double fun(double x){
				return 2 * x * x - 12 * x + 7;
			}
			
			public static double tsModified(double start,double end){
				double l = start;
				double r = end;
				for(int i = 0;i < 200;i++){
					double mid1 = (l * 2 + r)/3;
					double mid2 = (l + 2 * r)/3;
					if(fun(mid1) < fun(mid2)){
						r = mid2;
					}else{
						l = mid1;
					}
				}
				double x = l;
				return fun(x);
			}
			public static String fmt(double d)
			{
			    if(d == (long) d)
			        return String.format("%d",(long)d);
			    else
			        return String.format("%s",d);
			}
			public static String[][] comparator(String[][] arr){
				Arrays.sort(arr, new Comparator<String[]>() {
		            @Override
		            public int compare(String[] a,String[] b) {
		                Integer a1 = Integer.parseInt(a[1]);
		                Integer b1 = Integer.parseInt(b[1]);
		            	return b1.compareTo(a1);
		            }
		        });
				return arr;
			}
			public static StringBuilder[][] comparator2(StringBuilder[][] arr){
				Arrays.sort(arr, new Comparator<StringBuilder[]>() {
		            @Override
		            public int compare(StringBuilder[] a,StringBuilder[] b) {
		                StringBuilder a1 = a[0];
		                StringBuilder b1 = b[0];
		            	return a1.toString().compareTo(b1.toString());
		            }
		        });
				return arr;
			}
			public static int[][] comparator2(int[][] arr){
				Arrays.sort(arr, new Comparator<int[]>() {
		            @Override
		            public int compare(int[] a,int[] b) {
		                Integer a1 = a[0];
		                Integer b1 = b[0];
		            	return b1.compareTo(a1);
		            }
		        });
				return arr;
			}
			static class FastReader
		    {
		        BufferedReader br;
		        StringTokenizer st;
		 
		        public FastReader()
		        {
		            br = new BufferedReader(new
		                     InputStreamReader(System.in));
		        }
		 
		        String next()
		        {
		            while (st == null || !st.hasMoreElements())
		            {
		                try
		                {
		                    st = new StringTokenizer(br.readLine());
		                }
		                catch (IOException  e)
		                {
		                    e.printStackTrace();
		                }
		            }
		            return st.nextToken();
		        }
		 
		        int nextInt()
		        {
		            return Integer.parseInt(next());
		        }
		 
		        long nextLong()
		        {
		            return Long.parseLong(next());
		        }
		 
		        double nextDouble()
		        {
		            return Double.parseDouble(next());
		        }
		 
		        String nextLine()
		        {
		            String str = "";
		            try
		            {
		                str = br.readLine();
		            }
		            catch (IOException e)
		            {
		                e.printStackTrace();
		            }
		            return str;
		        }
		    }
			public static String min(int m,int n){
				int[] arr = new int[m];
				arr[0] = 1;
				int sum = 1;
				int j = m - 1;
				for(int i = 1;i <= 18* m + 10;i++){
					if(sum != n){
						if(arr[j] == 9){
							j--;
						}else{
							arr[j]++;
							sum++;
						}
					}
					else{
						break;
					}
				}
				StringBuilder min = new StringBuilder("");
				for(int i = 0;i < arr.length;i++){
					min.append(arr[i]);
				}
				return min.toString();
			}
			
			public static String max(int m,int n){
				int[] arr = new int[m];
				arr[0] = 1;
				int sum = 1;
				int j = 0;
				for(int i = 1;i <= 9 * m + 10;i++){
					if(sum != n){
						if(arr[j] == 9){
							j++;
						}else{
							arr[j]++;
							sum++;
						}
					}
					else{
						break;
					}
				}
				StringBuilder min = new StringBuilder("");
				for(int i = 0;i < arr.length;i++){
					min.append(arr[i]);
				}
				return min.toString();
			}
		    public static int KMPSearch(String txt,String pat){
				int m = pat.length();
				int n = txt.length();
				int lps[] = new int[m];
				int j = 0;
				int count = 0;
				PrefixTable(pat,m,lps);
				int i = 0;
				while (i < n)
			        {
			            if (pat.charAt(j) == txt.charAt(i))
			            {
			                j++;
			                i++;
			            }
			            if (j == m)
			            {
			            	count++;
			                j = lps[j-1];
			            }
			            else if (i < n && pat.charAt(j) != txt.charAt(i))
			            {
			                if (j != 0)
			                    j = lps[j-1];
			                else
			                    i = i+1;
			            }
			        }
				return count;
			}
			public static void PrefixTable(String pat,int m,int[] lps){
				int len = 0;
				int i = 1;
				lps[0] = 0;
				while(i < m){
					if(pat.charAt(i) == pat.charAt(len)){
						lps[i] = len + 1;
						i++;
						len++;
					}else if(len > 0){
						len = lps[len - 1];
					}else{
						lps[i] = 0;
						i++;
					}
				}
			}
		    public int digit(int x){
		    	int temp = x;
		    	int count = 0;
		    	while(temp != 0){
		    		count++;
		    		temp = temp/10;
		    	}
		    	return count;
		    }
		    public int sum(int x){
		    	int temp = x;
		    	int count = 0;
		    	while(temp != 0){
		    		count += temp % 10;
		    		temp = temp/10;
		    	}
		    	return count;
		    }
		    public int product(int x){
		    	int temp = x;
		    	int count = 1;
		    	while(temp != 0){
		    		count*=temp % 10;
		    		temp = temp/10;
		    	}
		    	return count;
		    }
		    public boolean isValid2(int x){
		    	int temp = x;
		    	while(temp != 0){
		    		if(temp % 10 == 0){
		    			return false;
		    		}
		    		temp = temp/10;
		    	}
		    	return true;
		    }
		    public boolean isValid(int x){
		    	if(!isValid2(x)){
		    		return false;
		    	}else{
		    		if(product(x) <= sum(x)){
		    			return true;
		    		}
		    		return false; 
		    	}
		    }
		}