Problem: You are given k sequences of integers. The length of the i-th sequence equals to n_i.

You have to choose exactly two sequences i and j (i ≠ j) such that you can remove exactly one element in each of them in such a way that the sum of the changed sequence i (its length will be equal to n_i - 1) equals to the sum of the changed sequence j (its length will be equal to n_j - 1).

Note that it's required to remove exactly one element in each of the two chosen sequences.

Assume that the sum of the empty (of the length equals 0) sequence is 0.

Input

The first line contains an integer k (2 ≤ k ≤ 2 ⋅ 10^5) — the number of sequences.

Then k pairs of lines follow, each pair containing a sequence.

The first line in the i-th pair contains one integer n_i (1 ≤ n_i < 2 ⋅ 10^5) — the length of the i-th sequence. The second line of the i-th pair contains a sequence of n_i integers a_{i, 1}, a_{i, 2}, ..., a_{i, n_i}.

The elements of sequences are integer numbers from -10^4 to 10^4.

The sum of lengths of all given sequences don't exceed 2 ⋅ 10^5, i.e. n_1 + n_2 + ... + n_k ≤ 2 ⋅ 10^5.

Output

If it is impossible to choose two sequences such that they satisfy given conditions, print "NO" (without quotes). Otherwise in the first line print "YES" (without quotes), in the second line — two integers i, x (1 ≤ i ≤ k, 1 ≤ x ≤ n_i), in the third line — two integers j, y (1 ≤ j ≤ k, 1 ≤ y ≤ n_j). It means that the sum of the elements of the i-th sequence without the element with index x equals to the sum of the elements of the j-th sequence without the element with index y.

Two chosen sequences must be distinct, i.e. i ≠ j. You can print them in any order.

If there are multiple possible answers, print any of them.

Examples

Input

2
5
2 3 1 3 2
6
1 1 2 2 2 1


Output

YES
2 6
1 2


Input

3
1
5
5
1 1 1 1 1
2
2 3


Output

NO


Input

4
6
2 2 2 2 2 2
5
2 2 2 2 2
3
2 2 2
5
2 2 2 2 2


Output

YES
2 2
4 1

Note

In the first example there are two sequences [2, 3, 1, 3, 2] and [1, 1, 2, 2, 2, 1]. You can remove the second element from the first sequence to get [2, 1, 3, 2] and you can remove the sixth element from the second sequence to get [1, 1, 2, 2, 2]. The sums of the both resulting sequences equal to 8, i.e. the sums are equal.

Solution (in JAVA):

Solution:
// Code Author: Shrey Shah
    // Institute : DA-IICT
    // Quote : Think twice , Code Once
 
import java.io.*;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.*;



 public class Tester1{

    static double eps=(double)1e-6;
    static long mod=(long)1e9+7;
    static int length = (int)1e6;
    static long d,x ,y;
    public static void main(String args[]){
        InputReader sc = new InputReader(System.in);
        OutputStream outputStream = System.out;
        PrintWriter out = new PrintWriter(outputStream);
        //----------Code Starts Here----
        	int  k = sc.nextInt();
        	ArrayList<Long> a[] = new ArrayList[k];
        	ArrayList<Pair>s[] = new ArrayList[k];
        	for(int i=0;i<k;i++)
        	{
        		a[i]=new ArrayList<Long>();
        		s[i]=new ArrayList<Pair>();
        		long sum=0;
        		int n = sc.nextInt();
        		for(int j=0;j<n;j++)
        		{
        			long x = sc.nextLong();
        			a[i].add(x);
        			sum+=x;
        		}
        		
        		for(int t=0;t<n;t++)
        		{
        			s[i].add(new Pair(sum-a[i].get(t),t));
        		}
        	}
        	
        	HashMap<Long,Pair> hm = new HashMap<Long,Pair>();
        	boolean flag=false;
        	for(int i=0;i<k;i++)
        	{
        		int size = a[i].size();
        		for(int j=0;j<size;j++)
        		{
        			Pair summ = s[i].get(j);
        			if(!hm.containsKey(summ.a))
        				hm.put(summ.a,new Pair(i,j));
        			else
        			{
        				Pair p = hm.get(summ.a);
        				if(p.a!=i)
        				{
        					out.println("YES");
        					out.println((i+1)+" "+(j+1));
        					out.println((p.a+1)+" "+(p.b+1));
        					flag=true;
        					break;
        				}
        			}
        				
        		}
        		if(flag==true)
        			break;
        	}
        	if(flag==false)
        		out.println("NO");
        //----------The End-------------
        out.close();
    }
    
    public static boolean isSubstring(String s1, String s2)
    {
        int M = s1.length();
        int N = s2.length();
     
        /* A loop to slide pat[] one by one */
        for (int i = 0; i <= N - M; i++) {
            int j;
     
            /* For current index i, check for
            pattern match */
            for (j = 0; j < M; j++)
                if (s2.charAt(i + j) != s1.charAt(j))
                    break;
     
            if (j == M)
                return true;
        }
        return false;
    }
    static int getMaxArea(int hist[], int n) 
    {
        // Create an empty stack. The stack holds indexes of hist[] array
        // The bars stored in stack are always in increasing order of their
        // heights.
        Stack<Integer> s = new Stack<>();
         
        int max_area = 0; // Initialize max area
        int tp;  // To store top of stack
        int area_with_top; // To store area with top bar as the smallest bar
      
        // Run through all bars of given histogram
        int i = 0;
        while (i < n)
        {
            // If this bar is higher than the bar on top stack, push it to stack
            if (s.empty() || hist[s.peek()] <= hist[i])
                s.push(i++);
      
            // If this bar is lower than top of stack, then calculate area of rectangle 
            // with stack top as the smallest (or minimum height) bar. 'i' is 
            // 'right index' for the top and element before top in stack is 'left index'
            else
            {
                tp = s.peek();  // store the top index
                s.pop();  // pop the top
      
                // Calculate the area with hist[tp] stack as smallest bar
                area_with_top = hist[tp] * (s.empty() ? i : i - s.peek() - 1);
                System.out.println(area_with_top);
                // update max area, if needed
                if (max_area < area_with_top)
                    max_area = area_with_top;
            }
        }
      
        // Now pop the remaining bars from stack and calculate area with every
        // popped bar as the smallest bar
        while (s.empty() == false)
        {
            tp = s.peek();
            s.pop();
            area_with_top = hist[tp] * (s.empty() ? i : i - s.peek() - 1);
            System.out.println(area_with_top);
            if (max_area < area_with_top)
                max_area = area_with_top;
        }
      
        return max_area;
 
    }
    private static void reverse(Stack<Integer> s)
    {
    	if(s.isEmpty())
    		return;
    	int top = s.pop();
    	reverse(s);
    	insertbottom(s,top);
    }
    private static void insertbottom(Stack<Integer> s,int t)
    {
    	if(s.isEmpty())
    		s.push(t);
    	else
    	{
    		int top = s.pop();
    		insertbottom(s,t);
    		s.push(top);
    	}
    }
    private static void swap(int[] a , int x,int y) 
    {
    	int temp = a[x];
    	a[x]=a[y];
    	a[y]=temp;
	}
	static int[] spf(int n)
    {
        int spf[] = new int[n+1];
        Arrays.fill(spf,0);
        spf[1] = 1;
     
        for (int i = 2; i <= n; i++)
        {
            if (spf[i] == 0)
            {
                spf[i] = i;
                for (int j = 2*i; j <= n; j += i)
                    if (spf[j] == 0)
                       spf[j] = i;
               
            } 
        }
        return spf;
    }
    private static boolean isPrime(long temp) {
        if(temp==0 || temp==1)
            return false;
        for(long i=2;i<Math.sqrt(temp);i++)
        {
            if(temp%i==0)
                return false;
        }
        return true;
    }
    static long modInverse(long A, long M)
    {
        extendedEuclid(A,M);
        return (x%M+M)%M;    //x may be negative
    }
    static void extendedEuclid(long A, long B) {
       
        if(B == 0) {
            d = A;
            x = 1;
            y = 0;
        }
        else {
            extendedEuclid(B, A%B);
            long temp = x;
            x = y;
            y = temp - (A/B)*y;
        }
    }
   
    static void topologicalSortUtil(int v, boolean visited[],Stack stack,ArrayList<Integer>[] adj)
        {
            // Mark the current node as visited.
                visited[v] = true;
                Integer i;
                for(i=0;i<adj[v].size();i++)
                {
                        int temp = adj[v].get(i);
                        if (!visited[temp])
                            topologicalSortUtil(temp, visited, stack,adj);
                }

                stack.push(new Integer(v));
        }

static void topologicalSort(int top[], int V,ArrayList<Integer>[] adj)
{
Stack stack = new Stack();


boolean visited[] = new boolean[V+1];
for (int i = 0; i <=V; i++)
visited[i] = false;


for (int i = 1; i <=V; i++)
if (visited[i] == false)
topologicalSortUtil(i, visited, stack,adj);

int count=0;
while (stack.empty()==false)
{
    top[count]=(int) stack.pop();
    count++;
}
}
       
        public int find(int parent[],int x)
        {
            if(parent[x]==-1)
                return x;
            return find(parent,parent[x]);
        }
       
        public void union(int parent[],int x,int y)
        {
            int xset = find(parent,x);
            int yset = find(parent,y);
            parent[xset] = yset;
        }
   
   
    private static String  xor(String string, String string2) {
         String temp = "";
         for(int i=0;i<string.length();i++)
         {
             if((string.charAt(i)=='1' && string2.charAt(i)=='1') || (string2.charAt(i)=='0' && string2.charAt(i)=='0'))
                     temp+="0";
             else
                 temp+="1";   
         }
         return temp;
    }

    private static int count_ones(String temp) {
        // TODO Auto-generated method stub
         int count=0;
         for(int i=0;i<temp.length();i++)
                if(temp.charAt(i)=='1')
                    count++;
        return count;
    }

    static int power(int x,int n)
        {
            if(n==0)
                return 1;
            else if(n%2 == 0)        //n is even
                return power(x*x,n/2);
            else                             //n is odd
                return x*power(x*x,(n-1)/2);
        }
    static long ce(long x)
    {
        if(x%2!=0)
            return x/2+1;
        return x/2;
    }
    static boolean isSubSequence(String str1, String str2, int m, int n)
    {
       int j = 0;
       for (int i=0; i<n&&j<m; i++)
           if (str1.charAt(j) == str2.charAt(i))
             j++;
     
       return (j==m);
    }
   
    static long sort(int a[]) {
        int n = a.length;
        int b[] = new int[n];
        return mergeSort(a, b, 0, n - 1);
    }

    static long mergeSort(int a[], int b[], long left, long right) {
        long c = 0;
        if (left < right) {
            long mid = left + (right - left) / 2;
            c = mergeSort(a, b, left, mid);
            c += mergeSort(a, b, mid + 1, right);
            c += merge(a, b, left, mid + 1, right);
        }
        return c;
    }

    static long merge(int a[], int b[], long left, long mid, long right) {
        long c = 0;
        int i = (int) left;
        int j = (int) mid;
        int k = (int) left;
        while (i <= (int) mid - 1 && j <= (int) right) {
            if (a[i] <= a[j]) {
                b[k++] = a[i++];
            } else {
                b[k++] = a[j++];
                c += mid - i;
            }
        }
        while (i <= (int) mid - 1)
            b[k++] = a[i++];
        while (j <= (int) right)
            b[k++] = a[j++];
        for (i = (int) left; i <= (int) right; i++)
            a[i] = b[i];
        return c;
    }
   
       
    static long fibo(long l) {
        if (l < 0) {
            throw new IllegalArgumentException("The fibo value cannot be negative");
        }

        if (l <= 1) return l;

        long[][] result = {{1, 0}, {0, 1}}; // identity matrix.
        long[][] fiboM = {{1, 1}, {1, 0}};

        while (l > 0) {
            if (l%2 == 1) {
                multMatrix(result, fiboM);
            }
            l = l / 2;
            multMatrix(fiboM, fiboM);
        }

        return result[1][0];
    }

    private static void multMatrix(long[][] result, long[][] fiboM) {
        long a = (result[0][0]%mod * fiboM[0][0]%mod)%mod +  (result[0][1]%mod * fiboM[1][0]%mod)%mod;
        long b = (result[0][0]%mod * fiboM[0][1]%mod)%mod + ( result[0][1]%mod * fiboM[1][1]%mod)%mod;
        long c = (result[1][0]%mod * fiboM[0][0]%mod)%mod + ( result[1][1]%mod * fiboM[0][1]%mod)%mod;
        long d = (result[1][0]%mod * fiboM[0][1]%mod)%mod +  (result[1][1]%mod * fiboM[1][1]%mod)%mod;

        result[0][0] = a;
        result[0][1] = b;
        result[1][0] = c;
        result[1][1] = d;
    }
   
   
    static int[] sieve(int n)
    {
        int prime[] = new int[n+1];
        for(int i=0;i<n;i++)
            prime[i] = 1;
        prime[0]=0;
        prime[1]=0;
        for(int p = 2; p*p <=n; p++)
        {
            // If prime[p] is not changed, then it is a prime
            if(prime[p] == 1)
            {
                // Update all multiples of p
                for(int i = p*2; i <= n; i += p)
                    prime[i] = 0;
            }
       
        }
        return prime;
    }
   
    static boolean isPal(String s)
    {
        for(int i=0, j=s.length()-1;i<=j;i++,j--)
        {
            s=s.toLowerCase();
            if(s.charAt(i)!=s.charAt(j)) return false;
        }
        return true;
    }
    public static String rev(String s)
    {
        StringBuilder sb=new StringBuilder(s);
        sb.reverse();
        return sb.toString();
    }
       
    static long lcm(long a, long b)
    {
        return a * (b / gcd(a, b));
    }
   
    static long gcd(long a, long b)
    {
        while (b > 0)
        {
            long temp = b;
            b = a % b; // % is remainder
            a = temp;
        }
        return a;
    }
    static long modulo(long a,long b,long c) {
        long x=1;
        long y=a;
        while(b > 0){
            if(b%2 == 1){
                x=(x*y)%c;
            }
            y = (y*y)%c; // squaring the base
            b /= 2;
        }
        return  x%c;
    }
    public static void debug(Object... o) {
        System.out.println(Arrays.deepToString(o));
    }
    static class Pair implements Comparable<Pair> {
        long a, b;

        Pair(long l, long b) {
            this.a = l;
            this.b = b;
        }

        public int compareTo(Pair o) {
            // TODO Auto-generated method stub
            return Long.compare(this.b,o.b);
            //return 0;
        }

        public boolean equals(Object o) {
            if (o instanceof Pair) {
                Pair p = (Pair) o;
                return p.a == a && p.b == b;
            }
            return false;
        }

        /*public int hashCode() {
            return new Integer(a).hashCode() * 31 + new Integer(b).hashCode();
        }*/

        public String toString() {
            return a + " " + b;
        }
    }
    static class InputReader {
        public BufferedReader reader;
        public StringTokenizer tokenizer;

        public InputReader(InputStream inputstream) {
            reader = new BufferedReader(new InputStreamReader(inputstream));
            tokenizer = null;
        }

        public String nextLine(){
            String fullLine=null;
            while (tokenizer == null || !tokenizer.hasMoreTokens()) {
                try {
                    fullLine=reader.readLine();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                return fullLine;
            }
            return fullLine;
        }
        public String next() {
            while (tokenizer == null || !tokenizer.hasMoreTokens()) {
                try {
                    tokenizer = new StringTokenizer(reader.readLine());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return tokenizer.nextToken();
        }
        public long nextLong() {
            return Long.parseLong(next());
        }
        public int nextInt() {
            return Integer.parseInt(next());
        }
    }
}
class Graph
{
    int V;
    int E;
    Edge edge[];
    class Edge
    {
        int src,dest;
    }
    public Graph(int V,int E)
    {
        this.V=V;
        this.E=E;
        edge = new Edge[E];
        for(int i=0;i<E;i++)
            edge[i]=new Edge();
    }
}

class SegmentTree
{
    int st[]; // The array that stores segment tree nodes
 
    /* Constructor to construct segment tree from given array. This
       constructor  allocates memory for segment tree and calls
       constructSTUtil() to  fill the allocated memory */
    SegmentTree(int arr[], int n)
    {
        // Allocate memory for segment tree
        //Height of segment tree
        int x = (int) (Math.ceil(Math.log(n) / Math.log(2)));
 
        //Maximum size of segment tree
        int max_size = 2 * (int) Math.pow(2, x) - 1;
 
        st = new int[max_size]; // Memory allocation
 
        constructSTUtil(arr, 0, n - 1, 0);
    }
 
    // A utility function to get the middle index from corner indexes.
    int getMid(int s, int e) {
        return s + (e - s) / 2;
    }
 
    /*  A recursive function to get the sum of values in given range
        of the array.  The following are parameters for this function.
 
      st    --> Pointer to segment tree
      si    --> Index of current node in the segment tree. Initially
                0 is passed as root is always at index 0
      ss & se  --> Starting and ending indexes of the segment represented
                    by current node, i.e., st[si]
      qs & qe  --> Starting and ending indexes of query range */
    int getSumUtil(int ss, int se, int qs, int qe, int si)
    {
        // If segment of this node is a part of given range, then return
        // the sum of the segment
     //   if (qs <= ss && qe >= se)
       //     return st[si];
        if(qs==qe)
        // If segment of this node is outside the given range
        if (se < qs || ss > qe)
            return 0;
 
        // If a part of this segment overlaps with the given range
        int mid = getMid(ss, se);
        return getSumUtil(ss, mid, qs, qe, 2 * si + 1) +
                getSumUtil(mid + 1, se, qs, qe, 2 * si + 2);
    }
 
    /* A recursive function to update the nodes which have the given
       index in their range. The following are parameters
        st, si, ss and se are same as getSumUtil()
        i    --> index of the element to be updated. This index is in
                 input array.
       diff --> Value to be added to all nodes which have i in range */
    void updateValueUtil(int ss, int se, int i, int diff, int si)
    {
        // Base Case: If the input index lies outside the range of
        // this segment
        if (i < ss || i > se)
            return;
 
        // If the input index is in range of this node, then update the
        // value of the node and its children
        st[si] = st[si] + diff;
        if (se != ss) {
            int mid = getMid(ss, se);
            updateValueUtil(ss, mid, i, diff, 2 * si + 1);
            updateValueUtil(mid + 1, se, i, diff, 2 * si + 2);
        }
    }
 
    // The function to update a value in input array and segment tree.
   // It uses updateValueUtil() to update the value in segment tree
    void updateValue(int arr[], int n, int i, int new_val)
    {
        // Check for erroneous input index
        if (i < 0 || i > n - 1) {
            System.out.println("Invalid Input");
            return;
        }
 
        // Get the difference between new value and old value
        int diff = new_val - arr[i];
 
        // Update the value in array
        arr[i] = new_val;
 
        // Update the values of nodes in segment tree
        updateValueUtil(0, n - 1, i, diff, 0);
    }
 
    // Return sum of elements in range from index qs (quey start) to
   // qe (query end).  It mainly uses getSumUtil()
    int getSum(int n, int qs, int qe)
    {
        // Check for erroneous input values
        if (qs < 0 || qe > n - 1 || qs > qe) {
            System.out.println("Invalid Input");
            return -1;
        }
        return getSumUtil(0, n - 1, qs, qe, 0);
    }
 
    // A recursive function that constructs Segment Tree for array[ss..se].
    // si is index of current node in segment tree st
    int constructSTUtil(int arr[], int ss, int se, int si)
    {
        // If there is one element in array, store it in current node of
        // segment tree and return
        if (ss == se) {
            st[si] = arr[ss];
            return arr[ss];
        }
 
        // If there are more than one elements, then recur for left and
        // right subtrees and store the sum of values in this node
        int mid = getMid(ss, se);
        st[si] = constructSTUtil(arr, ss, mid, si * 2 + 1) +
                 constructSTUtil(arr, mid + 1, se, si * 2 + 2);
        return st[si];
    }
}


class sortbylength implements Comparator<String>
{
	@Override
	public int compare(String o1, String o2) {
		if(o1.length()<o2.length())
			return -1;
		else if(o1.length()>o2.length())
			return +1;
		return 0;
	}
	
}