description: Vlad has n friends, for each of whom he wants to buy one gift for the New Year.
  
  There are m shops in the city, in each of which he can buy a gift for any of his friends. If the j-th friend (1 ≤ j ≤ n) receives a gift bought in the shop with the number i (1 ≤ i ≤ m), then the friend receives p_{ij} units of joy. The rectangular table p_{ij} is given in the input.
  
  Vlad has time to visit at most n-1 shops (where n is the number of friends). He chooses which shops he will visit and for which friends he will buy gifts in each of them.
  
  Let the j-th friend receive a_j units of joy from Vlad's gift. Let's find the value α=min\\{a_1, a_2, ..., a_n\}. Vlad's goal is to buy gifts so that the value of α is as large as possible. In other words, Vlad wants to maximize the minimum of the joys of his friends.
  
  For example, let m = 2, n = 2. Let the joy from the gifts that we can buy in the first shop: p_{11} = 1, p_{12}=2, in the second shop: p_{21} = 3, p_{22}=4.
  
  Then it is enough for Vlad to go only to the second shop and buy a gift for the first friend, bringing joy 3, and for the second — bringing joy 4. In this case, the value α will be equal to min\{3, 4\} = 3
  
  Help Vlad choose gifts for his friends so that the value of α is as high as possible. Please note that each friend must receive one gift. Vlad can visit at most n-1 shops (where n is the number of friends). In the shop, he can buy any number of gifts.
  
  Input
  
  The first line of the input contains an integer t (1 ≤ t ≤ 10^4) — the number of test cases in the input.
  
  An empty line is written before each test case. Then there is a line containing integers m and n (2 ≤ n, 2 ≤ n ⋅ m ≤ 10^5) separated by a space — the number of shops and the number of friends, where n ⋅ m is the product of n and m.
  
  Then m lines follow, each containing n numbers. The number in the i-th row of the j-th column p_{ij} (1 ≤ p_{ij} ≤ 10^9) is the joy of the product intended for friend number j in shop number i.
  
  It is guaranteed that the sum of the values n ⋅ m over all test cases in the test does not exceed 10^5.
  
  Output
  
  Print t lines, each line must contain the answer to the corresponding test case — the maximum possible value of α, where α is the minimum of the joys from a gift for all of Vlad's friends.
  
  Example
  
  Input
  
  
  5
  
  2 2
  1 2
  3 4
  
  4 3
  1 3 1
  3 1 1
  1 2 2
  1 1 3
  
  2 3
  5 3 4
  2 5 1
  
  4 2
  7 9
  8 1
  9 6
  10 8
  
  2 4
  6 5 2 1
  7 9 7 2
  
  
  Output
  
  
  3
  2
  4
  8
  2
solution: import java.io.BufferedReader;
  import java.io.BufferedWriter;
  import java.io.IOException;
  import java.io.InputStreamReader;
  import java.io.OutputStreamWriter;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.Comparator;
  import java.util.Deque;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.LinkedList;
  import java.util.Queue;
  import java.util.Random;
  import java.util.Scanner;
  import java.util.StringTokenizer;
  
  public class Main {
      static int mod = (int) (1e9) + 7;
  
      /* ======================DSU===================== */
      static class dsu {
          static int parent[], n;// min[],value[];
          static long size[];
  
          dsu(int n) {
              parent = new int[n + 1];
              size = new long[n + 1];
              // min=new int[n+1];
              // value=new int[n+1];
              Main.dsu.n = n;
              makeSet();
          }
  
          static void makeSet() {
              for (int i = 1; i <= n; i++) {
                  parent[i] = i;
                  size[i] = 1;
                  // min[i]=i;
              }
          }
  
          static int find(int a) {
              if (parent[a] == a)
                  return a;
              else {
                  return parent[a] = find(parent[a]);// Path Compression
              }
          }
  
          static void union(int a, int b) {
              int setA = find(a);
              int setB = find(b);
              if (setA == setB)
                  return;
              if (size[setA] >= size[setB]) {
                  parent[setB] = setA;
                  size[setA] += size[setB];
              } else {
                  parent[setA] = setB;
                  size[setB] += size[setA];
              }
          }
      }
  
      /* ======================================================== */
      static class Pair<X extends Number, Y extends Number> implements Comparator<Pair> {
          X x;
          Y y;
  
          // Constructor
          public Pair(X x, Y y) {
              this.x = x;
              this.y = y;
          }
  
          public Pair() {
  
          }
  
          @Override
          public int compare(Main.Pair o1, Main.Pair o2) {
              return ((int) (o1.y.intValue() - o2.y.intValue()));// Ascending Order based on 'y'
          }
      }
  
      /* ===============================Tries================================= */
      static class TrieNode {
          private HashMap<Character, TrieNode> children = new HashMap<>();
          public int size;
          boolean endOfWord;
  
          public void putChildIfAbsent(char ch) {
              children.putIfAbsent(ch, new TrieNode());
          }
  
          public TrieNode getChild(char ch) {
              return children.get(ch);
          }
      }
  
      static private TrieNode root;
  
      public static void insert(String str) {
          TrieNode curr = root;
          for (char ch : str.toCharArray()) {
              curr.putChildIfAbsent(ch);
              curr = curr.getChild(ch);
              curr.size++;
          }
          // mark the current nodes endOfWord as true
          curr.endOfWord = true;
      }
  
      public static int search(String word) {
          TrieNode curr = root;
  
          for (char ch : word.toCharArray()) {
              curr = curr.getChild(ch);
              if (curr == null) {
                  return 0;
              }
          }
          // size contains words starting with prefix- word
          return curr.size;
      }
  
      public boolean delete(TrieNode current, String word, int index) {
          if (index == word.length()) {
              // when end of word is reached only delete if currrent.endOfWord is true.
              if (!current.endOfWord) {
                  return false;
              }
              current.endOfWord = false;
              // if current has no other mapping then return true
              return current.children.size() == 0;
          }
          char ch = word.charAt(index);
          TrieNode node = current.children.get(ch);
          if (node == null) {
              return false;
          }
          boolean shouldDeleteCurrentNode = delete(node, word, index + 1);
  
          // if true is returned then delete the mapping of character and trienode
          // reference from map.
          if (shouldDeleteCurrentNode) {
              current.children.remove(ch);
              // return true if no mappings are left in the map.
              return current.children.size() == 0;
          }
          return false;
      }
  
      /* ================================================================= */
  
      static boolean isPrime(long n) {
          if (n <= 1)
              return false;
          if (n <= 3)
              return true;
          if (n % 2 == 0 || n % 3 == 0)
              return false;
          for (long i = 5; i * i <= n; i = i + 6)
              if (n % i == 0 || n % (i + 2) == 0)
                  return false;
          return true;
      }
  
      static Pair<Integer, Integer> lowerBound(long[] a, long x) { // x is the target, returns lowerBound. If not found
                                                                   // return -1
          int l = -1, r = a.length;
          while (l + 1 < r) {
              int m = (l + r) >>> 1;
              if (a[m] == x) {
                  return new Pair<>(m, 1);
              }
              if (a[m] >= x)
                  r = m;
              else
                  l = m;
          }
          return new Pair<>(r, 0);
      }
  
      static Pair<Integer, Integer> upperBound(long[] a, long x) {// x is the target, returns upperBound. If not found
          int l = -1, r = a.length;
          while (l + 1 < r) {
              int m = (l + r) >>> 1;
              if (a[m] == x) {
                  return new Pair<>(m, 1);
              }
              if (a[m] <= x)
                  l = m;
              else
                  r = m;
          }
          return new Pair<>(l + 1, 0);
      }
  
      static long gcd(long a, long b) {
          if (a == 0)
              return b;
          return gcd(b % a, a);
      }
  
      static long lcm(long a, long b) {
          return (a * b) / gcd(a, b);
      }
  
      static long power(long x, long y) {
          long res = 1;
          while (y > 0) {
              if (y % 2 == 1)
                  res = (res * x);
              y >>= 1;
              x = (x * x);
          }
          return res;
      }
  
      public double binPow(double x, int n) {// binary exponentiation with negative power as well
          if (n == 0)
              return 1.0;
          double binPow = binPow(x, n / 2);
          if (n % 2 == 0) {
              return binPow * binPow;
          } else {
              return n > 0 ? (binPow * binPow * x) : (binPow * binPow / x);
          }
      }
  
      static long ceil(long x, long y) {
          return (x % y == 0 ? x / y : (x / y + 1));
      }
  
      /*
       * ===========Modular Operations==================
       */
      static long modPower(long x, long y, long p) {
          long res = 1;
          x = x % p;
          while (y > 0) {
              if (y % 2 == 1)
                  res = (res * x) % p;
              y = y >> 1; // y = y/2
              x = (x * x) % p;
          }
          return res;
      }
  
      static long modInverse(long n, long p) {
          return modPower(n, p - 2, p);
      }
  
      static long modAdd(long a, long b) {
          return ((a + b + mod) % mod);
      }
  
      static long modMul(long a, long b) {
          return ((a % mod) * (b % mod)) % mod;
      }
  
      static long[] fac = new long[200000 + 5];
      static long[] invFac = new long[200000 + 5];
  
      static long nCrModPFermat(int n, int r) {
          if (r == 0)
              return 1;
          // return (fac[n] * modInverse(fac[r], mod) % mod * modInverse(fac[n - r], mod)
          // % mod) % mod;
          return (fac[n] * invFac[r] % mod * invFac[n - r] % mod) % mod;
      }
  
      /*
       * ===============================================
       */
  
      static void ruffleSort(long[] a) {
          int n = a.length;
          Random r = new Random();
          for (int i = 0; i < a.length; i++) {
              int oi = r.nextInt(n);
              long temp = a[i];
              a[i] = a[oi];
              a[oi] = temp;
          }
          Arrays.sort(a);
      }
  
      static void ruffleSort(int[] a) {
          int n = a.length;
          Random r = new Random();
          for (int i = 0; i < a.length; i++) {
              int oi = r.nextInt(n);
              int temp = a[i];
              a[i] = a[oi];
              a[oi] = temp;
          }
          Arrays.sort(a);
      }
  
      static String binString32(int n) {
          StringBuilder str = new StringBuilder("");
          String bin = Integer.toBinaryString(n);
          if (bin.length() != 32) {
              for (int k = 0; k < 32 - bin.length(); k++) {
                  str.append("0");
              }
              str.append(bin);
          }
          return str.toString();
      }
  
      static class sparseTable {
          public static int st[][];
          public static int log = 4;
  
          static int func(int a, int b) {// make func as per question(here min range query)
              return (int) gcd(a, b);
          }
  
          void makeTable(int n, int a[]) {
  
              st = new int[n][log];
              for (int i = 0; i < n; i++) {
                  st[i][0] = a[i];
              }
              for (int j = 1; j < log; j++) {
                  for (int i = 0; i + (1 << j) <= n; i++) {
                      st[i][j] = func(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
                  }
              }
          }
  
          static int query(int l, int r) {
              int length = r - l + 1;
              int k = 0;
              while ((1 << (k + 1)) <= length) {
                  k++;
              }
              return func(st[l][k], st[r - (1 << k) + 1][k]);
          }
  
          static void printTable(int n) {
              for (int j = 0; j < log; j++) {
                  for (int i = 0; i < n; i++) {
                      System.out.print(st[i][j] + " ");
                  }
                  System.out.println();
              }
          }
      }
  
      /*
       * ====================================Main=================================
       */
  
      // static int st[], a[];
  
      // static void buildTree(int treeIndex, int lo, int hi) {
      // if (hi == lo) {
      // st[treeIndex] = a[lo];
      // return;
      // }
      // int mid = (lo + hi) / 2;
      // buildTree(treeIndex * 2 + 1, lo, mid);
      // buildTree(treeIndex * 2 + 2, mid + 1, hi);
      // st[treeIndex] = merge(st[treeIndex * 2 + 1], st[treeIndex * 2 + 2]);
      // }
  
      // static void update(int treeIndex, int lo, int hi, int arrIndex, int val) {
      // if (hi == lo) {
      // st[treeIndex] = val;
      // a[arrIndex] = val;
      // return;
      // }
      // int mid = (hi + lo) / 2;
      // if (mid < arrIndex) {
      // update(treeIndex * 2 + 2, mid + 1, hi, arrIndex, val);
      // } else {
      // update(treeIndex * 2 + 1, lo, mid, arrIndex, val);
      // }
      // st[treeIndex] = merge(st[treeIndex * 2 + 1], st[treeIndex * 2 + 2]);
      // }
  
      // static int query(int treeIndex, int lo, int hi, int l, int r) {
      // if (l <= lo && r >= hi) {
      // return st[treeIndex];
      // }
      // if (l > hi || r < lo) {
      // return 0;
      // }
      // int mid = (hi + lo) / 2;
      // return query(treeIndex * 2 + 1, lo, mid, l, Math.min(mid, r));
      // }
  
      // static int merge(int a, int b) {
      // return a + b;
      // }
  
      public static long findKthPositive(long[] A, long k) {
          int l = 0, r = A.length, m;
          while (l < r) {
              m = (l + r) / 2;
              if (A[m] - 1 - m < k)
                  l = m + 1;
              else
                  r = m;
          }
          return l + k;
      }
  
      static int[] z_function(char ar[]) {
          int[] z = new int[ar.length];
          z[0] = ar.length;
          int l = 0;
          int r = 0;
          for (int i = 1; i < ar.length; i++) {
              if (r < i) {
                  l = i;
                  r = i;
                  while (r < ar.length && ar[r - l] == ar[r])
                      r++;
                  z[i] = r - l;
                  r--;
              } else {
                  int k = i - l;
                  if (z[k] < r - i + 1) {
                      z[i] = z[k];
                  } else {
                      l = i;
                      while (r < ar.length && ar[r - l] == ar[r])
                          r++;
                      z[i] = r - l;
                      r--;
                  }
              }
          }
          return z;
      }
  
      static void mergeSort(int a[]) {
          int n = a.length;
          if (n >= 2) {
              int mid = n / 2;
              int left[] = new int[mid];
              int right[] = new int[n - mid];
              for (int i = 0; i < mid; i++) {
                  left[i] = a[i];
              }
              for (int i = mid; i < n; i++) {
                  right[i - mid] = a[i];
              }
              mergeSort(left);
              mergeSort(right);
              mergeSortedArray(left, right, a);
          }
      }
  
      static void mergeSortedArray(int left[], int right[], int a[]) {
          int i = 0, j = 0, k = 0, n = left.length, m = right.length;
          while (i != n && j != m) {
              if (left[i] < right[j]) {
                  a[k++] = left[i++];
              } else {
                  a[k++] = right[j++];
              }
          }
          while (i != n) {
              a[k++] = left[i++];
          }
          while (j != m) {
              a[k++] = right[j++];
          }
      }
  
      // BINARY SEARCH
      // count of set bits in a particular position
      // suffix max array/ suffix sum array/ prefix
      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;
          }
  
          int[] intArr(int n) {
              int res[] = new int[n];
              for (int i = 0; i < n; i++)
                  res[i] = nextInt();
              return res;
          }
  
          long[] longArr(int n) {
              long res[] = new long[n];
              for (int i = 0; i < n; i++)
                  res[i] = nextLong();
              return res;
          }
      }
  
      static FastReader f = new FastReader();
      static BufferedWriter w = new BufferedWriter(new OutputStreamWriter(System.out));
      static ArrayList<String> strArr = new ArrayList<>();
  
      public static void main(String args[]) throws Exception {
          int t = 1;
          t = f.nextInt();
          int tc = 1;
          // fac[0] = 1;
          // for (int i = 1; i <= 200000; i++) {
          // fac[i] = fac[i - 1] * i % mod;
          // invFac[i] = modInverse(fac[i], mod);
          // }
          while (t-- != 0) {
              int shops=f.nextInt();
              int frens=f.nextInt();
              int p[][]=new int[shops][frens];
              for(int i=0;i<shops;i++){
                  p[i]=f.intArr(frens);
              }
              int lo=0,hi=(int)(1e9);
              int ans=0;
              while(lo<=hi){
                  int mid=lo+(hi-lo)/2;
                  boolean ok1=false,ok2=true;
                  int hash[]=new int[shops];
                  for(int i=0;i<shops;i++){
                      int cnt1=0;
                      for(int j=0;j<frens;j++){
                          if(p[i][j]>=mid){
                              cnt1++;
                          }
                      }
                      if(cnt1>=2){
                          ok1|=true;
                      }
                  }
                  for(int i=0;i<frens;i++){
                      int cnt2=0;
                      for(int j=0;j<shops;j++){
                          if(p[j][i]>=mid){
                              cnt2++;
                          }
                      }
                      ok2&=(cnt2>0);
                  }
                  if(ok1&&ok2){
                      ans=mid;
                      lo=mid+1;
                  }else{
                      hi=mid-1;
                  }
              }
              w.write(ans+"\n");
          }
  
          w.flush();
      }
  
  }
  /*
  
   */