// Java solution for problem A // Author: Grzegorz Guspiel import java.io.*; import java.util.*; class U { static boolean debug = false; static void out(String s) { System.out.println(s); } static void out_(String s) { System.out.print(s); } static void d(String s) { if (debug) out(s); } static void d_(String s) { if (debug) out_(s); } static void azzert(boolean b) { if (!b) throw new RuntimeException(); } } class IntReader { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer tokenizer = new StringTokenizer(""); int next() throws IOException { while (!tokenizer.hasMoreTokens()) { tokenizer = new StringTokenizer(reader.readLine()); } return Integer.parseInt(tokenizer.nextToken()); } } public class a { class Move { final char line; final int number; Move(char line, int number) { this.line = line; this.number = number; } } static IntReader reader = new IntReader(); int[][] t; int[][] bt; int[][] todo; ArrayList moves = new ArrayList(); int n, m; int mirror(int a, int n) { return n + 1 - a; } void tSwap(int ai, int aj, int bi, int bj) { int tmp = t[ai][aj]; t[ai][aj] = t[bi][bj]; t[bi][bj] = tmp; } Move applyToT(Move move) { if (move.line == 'R') { for (int i = 1; i <= m / 2; i++) { tSwap(move.number, i, move.number, mirror(i, m)); } } else { for (int i = 1; i <= n / 2; i++) { tSwap(i, move.number, mirror(i, n), move.number); } } return move; } Move applyToBt(Move move) { if (move.line == 'R') { for (int i = 1; i <= m / 2; i++) { bt[move.number][i] = 1 - bt[move.number][i]; } } else { for (int i = 1; i <= n / 2; i++) { bt[i][move.number] = 1 - bt[i][move.number]; } } return move; } int parity(int[] t) { int inv = 0; for (int i = 0; i < t.length; i++) for (int j = i + 1; j < t.length; j++) if (t[i] > t[j]) inv++; return inv % 2; } boolean isBtClean() { for (int i = 1; i <= n / 2; i++) for (int j = 1; j <= m / 2; j++) if (bt[i][j] == 1) return false; return true; } boolean setParity() { for (int attempt = 0; attempt < 2; attempt++) { for (int j = 1; j <= m / 2; j++) { if (bt[1][j] == 1) { moves.add(applyToBt(new Move('C', j))); } } for (int i = 2; i <= n / 2; i++) { if (bt[i][1] == 1) { moves.add(applyToBt(new Move('R', i))); } } if (isBtClean()) { return true; } moves.add(applyToBt(new Move('R', 1))); } return false; } boolean isMiddleRowOk() { if (n % 2 == 0) return true; int middle = n / 2 + 1; for (int j = 1; j <= m; j++) if (t[middle][j] != todo[middle][j]) return false; return true; } boolean isMiddleColumnOk() { if (m % 2 == 0) return true; int middle = m / 2 + 1; for (int i = 1; i <= n; i++) if (t[i][middle] != todo[i][middle]) return false; return true; } void dump() { if (U.debug) { for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) U.out_(t[i][j] + " "); U.out(""); } U.out(""); } } void rotate(int row, int column) { moves.add(applyToT(new Move('C', column))); moves.add(applyToT(new Move('R', row))); moves.add(applyToT(new Move('C', column))); moves.add(applyToT(new Move('R', row))); } boolean possible() { todo = new int[n + 1][]; for (int i = 1; i <= n; i++) todo[i] = new int[m + 1]; int counter = 1; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) todo[i][j] = counter++; if (n > 1 && m > 1) { bt = new int[n / 2 + 1][]; for (int i = 1; i <= n / 2; i++) bt[i] = new int[m / 2 + 1]; for (int i = 1; i <= n / 2; i++) for (int j = 1; j <= m / 2; j++) { int[] values = new int[]{t[i][j], t[i][mirror(j, m)], t[mirror(i, n)][j], t[mirror(i, n)][mirror(j, m)]}; int[] todoValues = new int[]{todo[i][j], todo[i][mirror(j, m)], todo[mirror(i, n)][j], todo[mirror(i, n)][mirror(j, m)]}; bt[i][j] = parity(values); Arrays.sort(values); for (int k = 0; k < 4; k++) if (values[k] != todoValues[k]) { return false; } } if (!setParity()) { return false; } for (Move move : moves) applyToT(move); } if (!isMiddleRowOk()) { moves.add(applyToT(new Move('R', n / 2 + 1))); } if (!isMiddleRowOk()) { return false; } if (!isMiddleColumnOk()) { moves.add(applyToT(new Move('C', m / 2 + 1))); } if (!isMiddleColumnOk()) { return false; } for (int i = 1; i <= n / 2; i++) for (int j = 1; j <= m / 2; j++) { if (t[i][j] != todo[i][j]) { rotate(i, mirror(j, m)); } if (t[i][j] != todo[i][j]) { rotate(i, mirror(j, m)); } if (t[i][j] != todo[i][j]) { rotate(mirror(i, n), j); } if (t[i][j] != todo[i][j]) { rotate(mirror(i, n), j); } while (t[i][mirror(j, m)] != todo[i][mirror(j, m)]) { rotate(mirror(i, n), mirror(j, m)); } } if (U.debug) { for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) U.azzert(t[i][j] == todo[i][j]); } return true; } void solve() throws IOException { m = reader.next(); n = reader.next(); t = new int[n + 1][]; for (int i = 1; i <= n; i++) t[i] = new int[m + 1]; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) t[i][j] = reader.next(); if (possible()) { U.out_("POSSIBLE " + moves.size()); for (Move move : moves) U.out_(" " + move.line + move.number); U.out(""); } else { U.out("IMPOSSIBLE"); } } public static void main(String[] args) throws IOException { int z = reader.next(); for (int zz = 0; zz < z; zz++) { new a().solve(); } } }