// Arup Guha
// 3/21/2026
// Solution to 2026 UCF HS Contest Problem B: Conveyor Confuse-inator

import java.util.*;
import java.io.*;

public class conveyor {

	// Movement constants.
	final public static int[] DR = {-1,0,0,1};
	final public static int[] DC = {0,-1,1,0};
	final public static String DIRS = "^<>v";

	public static int nR;
	public static int nC;
	public static char[][] grid;
	public static boolean[][] visited;
	public static data[][] info;
	
	public static void main(String[] args) throws Exception {
	
		// Get grid.
		BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer tok = new StringTokenizer(stdin.readLine());
		nR = Integer.parseInt(tok.nextToken());
		nC = Integer.parseInt(tok.nextToken());
		int q = Integer.parseInt(tok.nextToken());
		grid = new char[nR][];
		for (int i=0; i<nR; i++)
			grid[i] = stdin.readLine().trim().toCharArray();
			
		// Get this ready.
		visited = new boolean[nR][nC];
		info = new data[nR][nC];
		for (int i=0; i<nR; i++)
			for (int j=0; j<nC; j++)
				info[i][j] = new data();
				
		/*** We pre-process most things. For each square, we'll
		     determine if you get out from that square, but if you don't,
			 the corresponding min, max r and c we'll ever hit.
			 We do this through a number of searches, sort of like a DFS
			 from every unvisited square.
		***/
		for (int i=0; i<nR; i++) {
			for (int j=0; j<nC; j++) {
			
				// Been here before.
				if (visited[i][j]) continue;
				
				// This just returns everything unique we hit starting at i, j.
				Stack<Integer> path = getPath(i, j);
				
				// This gets us out, so all we need to mark for all of these is that
				// they are free.
				if ( leadsOut(path) ) 
					markOut(path);
					
				// This is trickier...either this has a loop or leads to a loop
				// If it has a loop, we need to pop off all of the loop and compute
				// its min and max r and c and store that for all the loop squares.
				// then, we must process the path coming into the loop.
				else
					updateMinMax(path);
			
			}
		}
		
		// Now, let's build up the answers.
		StringBuffer sb = new StringBuffer();
		
		// Process queries.
		for (int i=0; i<q; i++) {
			
			tok = new StringTokenizer(stdin.readLine());
			int rS = Integer.parseInt(tok.nextToken())-1;
			int cS = Integer.parseInt(tok.nextToken())-1;
			int r1 = Integer.parseInt(tok.nextToken())-1;
			int c1 = Integer.parseInt(tok.nextToken())-1;
			int r2 = Integer.parseInt(tok.nextToken())-1;
			int c2 = Integer.parseInt(tok.nextToken())-1;
			
			// Cases for yes.
			if (info[rS][cS].out || info[rS][cS].beyond(r1,c1,r2,c2))
				sb.append("YES\n");
			
			// Otherwise must be no.
			else
				sb.append("NO\n");
		}
		
		// Ta da!
		System.out.print(sb);
	}
	
	public static boolean inbounds(int r, int c) {
		return r>=0 && r<nR && c>=0 && c <nC;
	}
	
	// Returns where we go from r, c.
	public static int[] getNext(int r, int c) {
		
		// Find our direction and return accordingly.
		for (int i=0; i<DR.length; i++) 
			if (grid[r][c] == DIRS.charAt(i)) 
				return new int[]{r+DR[i], c+DC[i]};
			
		// Should never get here.
		return null;
	}
	
	public static Stack<Integer> getPath(int r, int c) {
		
		// Start with first item.
		Stack<Integer> res = new Stack<Integer>();
		visited[r][c] = true;
		res.push(r*nC+c);
		
		// I'll break out!
		while (true) {
			
			// Get to the next place.
			int[] next = getNext(r, c);
			
			// One reason to get out and not add to stack.
			if (!inbounds(next[0], next[1])) break;
			
			// Now, we always get out for a revisited square.
			if (visited[next[0]][next[1]]) {
				res.push(next[0]*nC+next[1]);
				break;
			}
			
			// Add it and update where we're coming from.
			r = next[0];
			c = next[1];
			res.push(r*nC+c);
			visited[r][c] = true;
		}
		
		// Promise, we'll always get here!
		return res;
	}
	
	// Returns true iff this path leads out.
	public static boolean leadsOut(Stack<Integer> path) {
	
		Integer tmp = path.peek();
		int r = tmp/nC;
		int c = tmp%nC;
		int[] next = getNext(r, c);
		
		// This is easy.
		if (!inbounds(next[0], next[1])) return true;
		
		// Otherwise, it depends on if the square the top leads to leads out.
		return info[next[0]][next[1]].out;
	}
	
	// This is straight-forward. All we have to do is pop everything 
	// and mark that they lead out.
	public static void markOut(Stack<Integer> path) {
	
		while (path.size() > 0) {
			Integer tmp = path.pop();
			int r = tmp/nC;
			int c = tmp%nC;
			info[r][c].out = true;
		}
	
	}
	
	public static void updateMinMax(Stack<Integer> path) {
		
		// Probably a method to do this but faster for me to just do it.
		int[] items = new int[path.size()];
		for (int z=path.size()-1; z>=0; z--)
			items[z] = path.pop();
		int n = items.length;
		
		// I am looking for a cycle with a repeat at the last term.
		int i = n-2;
		while (i>=0 && items[i]!=items[n-1]) i--;
		
		// There's a cycle.
		if (i >= 0) {
		
			int value = items[i];
			int curR = value/nC;
			int curC = value%nC;
			int minR = curR, maxR = curR;
			int minC = curC, maxC = curC;
			
			// Go through the rest of the cycle.
			for (int z=i+1; z<n; z++) {
				value = items[z];
				curR = value/nC;
				curC = value%nC;
				minR = Math.min(minR, curR);
				maxR = Math.max(maxR, curR);
				minC = Math.min(minC, curC);
				maxC = Math.max(maxC, curC);
			}
			
			// Now we have the answers for the rest of the cycle.
			for (int z=i; z<n; z++) {
				value = items[z];
				curR = value/nC;
				curC = value%nC;
				info[curR][curC].minr = minR;
				info[curR][curC].maxr = maxR;
				info[curR][curC].minc = minC;
				info[curR][curC].maxc = maxC;
			}	
		} // end if
		
		// Otherwise this is my starting point, or it leads into a cycle.
		else {
			int value = items[n-1];
			int curR = value/nC;
			int curC = value%nC;
			
			// If this triggers, this means that our square leads out and not into
			// a previously marked square. So in my trace, I add a previously marked
			// square to end the path, so this triggers when that wasn't done.
			if (info[curR][curC].minr == data.NONE) {
				info[curR][curC].minr = curR;
				info[curR][curC].maxr = curR;
				info[curR][curC].minc = curC;
				info[curR][curC].maxc = curC;
			}
			
			/*** if we didn't do the if, that means that the top of the stack starts
			     a path that leads to a cycle and I've already marked it with min/max
				 data, so I shouldn't overwrite that data.
			***/
			
			// Okay, this is bad, I know...
			i = n-1;
		}
		
		// Reset this.
		int value = items[i];
		int prevR = value/nC;
		int prevC = value%nC;
		
		// Go backwards.
		for (int z=i-1; z>=0; z--) {
			value = items[z];
			int curR = value/nC;
			int curC = value%nC;
			info[curR][curC].minr = Math.min(info[prevR][prevC].minr, curR);
			info[curR][curC].maxr = Math.max(info[prevR][prevC].maxr, curR);
			info[curR][curC].minc = Math.min(info[prevR][prevC].minc, curC);
			info[curR][curC].maxc = Math.max(info[prevR][prevC].maxc, curC);
			prevR = curR;
			prevC = curC;
		}
	
	}
}

class data {
	
	final public static int NONE = -2;

	public int minr;
	public int minc;
	public int maxr;
	public int maxc;
	public boolean out;
	
	// A dummy constructor. We'll change values as needed when we search.
	public data() {
		minr = NONE;
		minc = NONE;
		maxr = NONE;
		maxc = NONE;
		out = false;
	}
	
	// Returns true iff this data is beyond the rectangle defined by min (r1, c1)
	// max coordinates (r2, c2).
	public boolean beyond(int r1, int c1, int r2, int c2) {
		return minr < r1 || maxr > r2 || minc < c1 || maxc > c2;
	}
}