import java.util.*;
import java.lang.*;
import java.io.*;

public class Main
{
	PrintWriter out = new PrintWriter(System.out);
	BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    StringTokenizer tok = new StringTokenizer("");
    String next() throws IOException {
        if (!tok.hasMoreTokens()) { tok = new StringTokenizer(in.readLine()); }
        return tok.nextToken();
    }
    int ni() throws IOException { return Integer.parseInt(next()); }
    long nl() throws IOException { return Long.parseLong(next()); }

    long mod=1000000007;
    ArrayList<Integer>B;
    int[][]A;

    void solve() throws IOException {
        for (int tc=ni();tc>0;tc--) {
            int n=ni(),m=ni();
            A=new int[n][m];
            for (int i=0;i<n;i++) {
                String s=next();
                for (int j=0;j<m;j++) {
                    if (s.charAt(j)=='1') A[i][j]=1;
                }
            }

            /*for (int i=0;i<n;i++) {
                for (int j=0;j<m;j++) out.print(A[i][j]+" ");
                out.println();
            }*/

            B=new ArrayList();

            for (int i=0;i<n-1;i++) {
                int p=0;
                while (p<m) {
                    if (A[i][p]==0) { p++; continue; }
                    if (p<m-1) {
                        go(i,p);
                        p++;
                        if (A[i][p]==0) { go(i+1,p-1); go(i+1,p); }
                        else {
                            go (i,p);
                            if (A[i+1][p-1]==1) go(i+1,p-1);
                            else go(i+1,p);
                        }
                        p++;
                    }
                    else { go(i,p); go(i+1,p-1); go(i+1,p); p++; }
                }
            }

            int p=0;
            while (p<m) {
                if (A[n-1][p]==0) { p++; continue; }
                if (p<m-1) {
                    if (A[n-1][p+1]==1) {
                        go(n-1,p);
                        go(n-2,p);
                        go(n-2,p+1);
                        go(n-1,p+1);
                        go(n-2,p);
                        go(n-2,p+1);
                        p+=2;
                    }
                    else {
                        if (p<m-2) {
                            go(n-1,p);
                            go(n-1,p+1);
                            go(n-2,p+1);
                            go(n-1,p+1);
                            go(n-1,p+2);
                            go(n-2,p+1);
                        }
                        else fin1(n-1,p);

                        p+=2;
                    }
                }
                else {
                    fin2(n-1,m-1);
                    p++;
                }
            }

            out.println(B.size()/6);
            for (int i=0;i<B.size();i+=6) {
                for (int j=0;j<6;j++) out.print(B.get(i+j)+" ");
                out.println();
            }
        }
        out.flush();
    }

    void fin1(int u,int v) {
        go(u,v); go(u-1,v); go(u-1,v+1);
        go(u,v); go(u-1,v+1); go(u,v+1);
        go(u,v); go(u-1,v); go(u,v+1);
    }

    void fin2(int u,int v) {
        go(u,v); go(u-1,v); go(u-1,v-1);
        go(u,v); go(u-1,v-1); go(u,v-1);
        go(u,v); go(u-1,v); go(u,v-1);
    }

    void go(int u,int v) {
        B.add(u+1);
        B.add(v+1);
        if (A[u][v]==1) A[u][v]=0;
        else A[u][v]=1;
    }

    int gcd(int a,int b) { return(b==0?a:gcd(b,a%b)); }
    long gcd(long a,long b) { return(b==0?a:gcd(b,a%b)); }
    long mp(long a,long p) { long r=1; while(p>0) { if ((p&1)==1) r=(r*a)%mod; p>>=1; a=(a*a)%mod; } return r; }

    public static void main(String[] args) throws IOException {
        new Main().solve();
    }
}


import java.util.*;
import java.io.*;

public class Main {
    public static void main(String args[]) {new Main().run();}

    FastReader in = new FastReader();
    PrintWriter out = new PrintWriter(System.out);
    void run(){
        for(int q=ni();q>0;q--){
            work();
        }
        out.flush();
    }
    long mod=998244353;
    long gcd(long a,long b) {
        return a==0?b:gcd(b%a,a);
    }
    void work() {
        int n=ni(),m=ni();
        int[][] A=new int[n][m];
        for(int i=0;i<n;i++){
            String str=ns();
            for(int j=0;j<m;j++){
                A[i][j]=str.charAt(j)-'0';
            }
        }
        ArrayList<int[]> list=new ArrayList<>();
        for(int i=0;i<n-2;i++){
            for(int j=0;j<m;j+=2){
                if(j==m-1)j--;
                if(A[i][j]==0&&A[i][j+1]==1){
                    list.add(new int[]{i,j+1,i+1,j,i+1,j+1});
                    reverse(A,i,j+1,i+1,j,i+1,j+1);
                }else if(A[i][j]==1&&A[i][j+1]==0){
                    list.add(new int[]{i,j,i+1,j,i+1,j+1});
                    reverse(A,i,j,i+1,j,i+1,j+1);
                }else if(A[i][j]==1&&A[i][j+1]==1){
                    list.add(new int[]{i,j,i,j+1,i+1,j+1});
                    reverse(A,i,j,i,j+1,i+1,j+1);
                }
            }
        }
        for(int j=0;j<m-1;j++){
            if(A[n-1][j]==1&&A[n-2][j]==1){
                list.add(new int[]{n-2,j,n-1,j,n-1,j+1});
                reverse(A,n-2,j,n-1,j,n-1,j+1);
            }else if(A[n-1][j]==0&&A[n-2][j]==1){
                list.add(new int[]{n-2,j,n-2,j+1,n-1,j+1});
                reverse(A,n-2,j,n-2,j+1,n-1,j+1);
            }else if(A[n-1][j]==1&&A[n-2][j]==0){
                list.add(new int[]{n-1,j,n-2,j+1,n-1,j+1});
                reverse(A,n-1,j,n-2,j+1,n-1,j+1);
            }
        }
        if(A[n-2][m-1]==1&&A[n-1][m-1]==0){
            list.add(new int[]{n-2,m-1,n-2,m-2,n-1,m-2});
            list.add(new int[]{n-2,m-1,n-1,m-1,n-2,m-2});
            list.add(new int[]{n-2,m-1,n-1,m-1,n-1,m-2});
        }else if(A[n-2][m-1]==1&&A[n-1][m-1]==1){
            list.add(new int[]{n-2,m-1,n-2,m-2,n-1,m-2});
            list.add(new int[]{n-1,m-1,n-2,m-2,n-1,m-2});
        }else if(A[n-2][m-1]==0&&A[n-1][m-1]==1){
            list.add(new int[]{n-1,m-1,n-2,m-2,n-1,m-2});
            list.add(new int[]{n-2,m-1,n-1,m-1,n-2,m-2});
            list.add(new int[]{n-2,m-1,n-1,m-1,n-1,m-2});
        }
        out.println(list.size());
        for(int[] l:list){
            for(int a:l){
                out.print((a+1)+" ");
            }
            out.println();
        }
    }

    private void reverse(int[][] A, int i1, int j1, int i2, int j2, int i3, int j3) {
        A[i1][j1]^=1;
        A[i2][j2]^=1;
        A[i3][j3]^=1;
    }

    //input
    @SuppressWarnings("unused")
    private ArrayList<Integer>[] ng(int n, int m) {
        ArrayList<Integer>[] graph=(ArrayList<Integer>[])new ArrayList[n];
        for(int i=0;i<n;i++) {
            graph[i]=new ArrayList<>();
        }
        for(int i=1;i<=m;i++) {
            int s=in.nextInt()-1,e=in.nextInt()-1;
            graph[s].add(e);
            graph[e].add(s);
        }
        return graph;
    }

    private ArrayList<long[]>[] ngw(int n, int m) {
        ArrayList<long[]>[] graph=(ArrayList<long[]>[])new ArrayList[n];
        for(int i=0;i<n;i++) {
            graph[i]=new ArrayList<>();
        }
        for(int i=1;i<=m;i++) {
            long s=in.nextLong()-1,e=in.nextLong()-1,w=in.nextLong();
            graph[(int)s].add(new long[] {e,w});
//            graph[(int)e].add(new long[] {s,w});
        }
        return graph;
    }

    private int ni() {
        return in.nextInt();
    }

    private long nl() {
        return in.nextLong();
    }

    private String ns() {
        return in.next();
    }

    private long[] na(int n) {
        long[] A=new long[n];
        for(int i=0;i<n;i++) {
            A[i]=in.nextLong();
        }
        return A;
    }
    private int[] nia(int n) {
        int[] A=new int[n];
        for(int i=0;i<n;i++) {
            A[i]=in.nextInt();
        }
        return A;
    }
}

class FastReader
{
    BufferedReader br;
    StringTokenizer st;

    public FastReader()
    {
        br=new BufferedReader(new InputStreamReader(System.in));
    }

    public String next()
    {
        while(st==null || !st.hasMoreElements())//回车，空行情况
        {
            try {
                st = new StringTokenizer(br.readLine());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return st.nextToken();
    }

    public int nextInt()
    {
        return Integer.parseInt(next());
    }

    public long nextLong()
    {
        return Long.parseLong(next());
    }
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.stream.IntStream;
import java.util.Arrays;
import java.util.InputMismatchException;
import java.util.ArrayList;
import java.util.OptionalLong;
import java.io.OutputStreamWriter;
import java.util.NoSuchElementException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.stream.LongStream;
import java.util.Iterator;
import java.io.BufferedWriter;
import java.util.Collection;
import java.io.IOException;
import java.util.Objects;
import java.util.List;
import java.io.Writer;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 *
 * @author aryssoncf
 */
public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        InputReader in = new InputReader(inputStream);
        OutputWriter out = new OutputWriter(outputStream);
        A2BinaryTableHardVersion solver = new A2BinaryTableHardVersion();
        int testCount = Integer.parseInt(in.next());
        for (int i = 1; i <= testCount; i++)
            solver.solve(i, in, out);
        out.close();
    }

    static class A2BinaryTableHardVersion {
        int[][][] path = null;

        public void solve(int testNumber, InputReader in, OutputWriter out) {
            if (path == null) {
                path = init();
            }
            int n = in.readInt(), m = in.readInt();
            char[][] table = in.readTable(n, m);
            List<int[]> res = new ArrayList<>();
            for (int r = 0; r < n; ) {
                int n2 = r + 3 == n ? 3 : 2;
                for (int c = 0; c < m; ) {
                    int m2 = c + 3 == m ? 3 : 2;
                    int start = 0;
                    for (int i = 0; i < n2; i++) {
                        for (int j = 0; j < m2; j++) {
                            if (table[r + i][c + j] == '1') {
                                start ^= 1 << (i * m2 + j);
                            }
                        }
                    }
                    List<int[]> p = getPath(start, n2, m2);
                    for (int[] arr : p) {
                        for (int i = 0; i < 6; i++) {
                            arr[i] += i % 2 == 0 ? r + 1 : c + 1;
                        }
                    }
                    res.addAll(p);
                    c += m2;
                }
                r += n2;
            }
            out.printLine(res.size());
            for (int[] arr : res) {
                out.printLine(arr);
            }
        }

        List<int[]> getPath(int u, int n, int m) {
            int[] previous = path[n - 2][m - 2];
            List<int[]> res = new ArrayList<>();
            EzIntList list = new EzIntArrayList(6);
            while (u != 0) {
                int v = previous[u];
                int[] arr = new int[6];
                for (int i = 0; i < n * m; i++) {
                    if (((u ^ v) & (1 << i)) > 0) {
                        list.add(i / m);
                        list.add(i % m);
                    }
                }
                res.add(list.toArray());
                list.clear();
                u = v;
            }
            return res;
        }

        int[][][] init() {
            int[][][] res = new int[2][2][];
            for (int i = 0; i < 2; i++) {
                for (int j = 0; j < 2; j++) {
                    res[i][j] = getPath(i + 2, j + 2);
                }
            }
            return res;
        }

        int[] getPath(int n, int m) {
            Graph graph = new Graph(1 << (n * m));
            for (int u = 0; u < (1 << (n * m)); u++) {
                for (int r0 = 0; r0 < n - 1; r0++) {
                    for (int c0 = 0; c0 < m - 1; c0++) {
                        int aux = u;
                        for (int r = r0; r < r0 + 2; r++) {
                            for (int c = c0; c < c0 + 2; c++) {
                                aux ^= 1 << (r * m + c);
                            }
                        }
                        for (int r = r0; r < r0 + 2; r++) {
                            for (int c = c0; c < c0 + 2; c++) {
                                int v = aux ^ (1 << (r * m + c));
                                graph.addSimpleEdge(u, v);
                            }
                        }
                    }
                }
            }
            Pair<long[], int[]> dijkstra = ShortestDistance.dijkstraAlgorithm(graph, 0);
            if (ArrayUtils.maxElement(dijkstra.first) > n * m) {
                throw new UnknownError();
            }
            return Arrays.stream(dijkstra.second).map(i -> i == -1 ? -1 : graph.source(i)).toArray();
        }

    }

    static class Pair<U, V> implements Comparable<Pair<U, V>> {
        public final U first;
        public final V second;

        public static <U, V> Pair<U, V> makePair(U first, V second) {
            return new Pair<>(first, second);
        }

        private Pair(U first, V second) {
            this.first = first;
            this.second = second;
        }

        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            Pair pair = (Pair) o;

            return Objects.equals(first, pair.first) &&
                    Objects.equals(second, pair.second);
        }

        public int hashCode() {
            int result = first != null ? first.hashCode() : 0;
            result = 31 * result + (second != null ? second.hashCode() : 0);
            return result;
        }

        public String toString() {
            return "(" + first + "," + second + ")";
        }

        public int compareTo(Pair<U, V> o) {
            int value = ((Comparable<U>) first).compareTo(o.first);
            if (value != 0) {
                return value;
            }
            return ((Comparable<V>) second).compareTo(o.second);
        }

    }

    static class Graph {
        public static final int REMOVED_BIT = 0;
        protected int vertexCount;
        protected int edgeCount;
        private int[] firstOutbound;
        private int[] firstInbound;
        private Edge[] edges;
        private int[] nextInbound;
        private int[] nextOutbound;
        private int[] from;
        private int[] to;
        private long[] weight;
        public long[] capacity;
        private int[] reverseEdge;
        private int[] flags;

        public Graph(int vertexCount) {
            this(vertexCount, vertexCount);
        }

        public Graph(int vertexCount, int edgeCapacity) {
            this.vertexCount = vertexCount;
            firstOutbound = new int[vertexCount];
            Arrays.fill(firstOutbound, -1);

            from = new int[edgeCapacity];
            to = new int[edgeCapacity];
            nextOutbound = new int[edgeCapacity];
            flags = new int[edgeCapacity];
        }

        public int addEdge(int fromID, int toID, long weight, long capacity, int reverseEdge) {
            ensureEdgeCapacity(edgeCount + 1);
            if (firstOutbound[fromID] != -1) {
                nextOutbound[edgeCount] = firstOutbound[fromID];
            } else {
                nextOutbound[edgeCount] = -1;
            }
            firstOutbound[fromID] = edgeCount;
            if (firstInbound != null) {
                if (firstInbound[toID] != -1) {
                    nextInbound[edgeCount] = firstInbound[toID];
                } else {
                    nextInbound[edgeCount] = -1;
                }
                firstInbound[toID] = edgeCount;
            }
            this.from[edgeCount] = fromID;
            this.to[edgeCount] = toID;
            if (capacity != 0) {
                if (this.capacity == null) {
                    this.capacity = new long[from.length];
                }
                this.capacity[edgeCount] = capacity;
            }
            if (weight != 0) {
                if (this.weight == null) {
                    this.weight = new long[from.length];
                }
                this.weight[edgeCount] = weight;
            }
            if (reverseEdge != -1) {
                if (this.reverseEdge == null) {
                    this.reverseEdge = new int[from.length];
                    Arrays.fill(this.reverseEdge, 0, edgeCount, -1);
                }
                this.reverseEdge[edgeCount] = reverseEdge;
            }
            if (edges != null) {
                edges[edgeCount] = createEdge(edgeCount);
            }
            return edgeCount++;
        }

        protected final GraphEdge createEdge(int id) {
            return new GraphEdge(id);
        }

        public final int addFlowWeightedEdge(int from, int to, long weight, long capacity) {
            if (capacity == 0) {
                return addEdge(from, to, weight, 0, -1);
            } else {
                int lastEdgeCount = edgeCount;
                addEdge(to, from, -weight, 0, lastEdgeCount + entriesPerEdge());
                return addEdge(from, to, weight, capacity, lastEdgeCount);
            }
        }

        protected int entriesPerEdge() {
            return 1;
        }

        public final int addWeightedEdge(int from, int to, long weight) {
            return addFlowWeightedEdge(from, to, weight, 0);
        }

        public final int addSimpleEdge(int from, int to) {
            return addWeightedEdge(from, to, 0);
        }

        public final int vertexCount() {
            return vertexCount;
        }

        public final int firstOutbound(int vertex) {
            int id = firstOutbound[vertex];
            while (id != -1 && isRemoved(id)) {
                id = nextOutbound[id];
            }
            return id;
        }

        public final int nextOutbound(int id) {
            id = nextOutbound[id];
            while (id != -1 && isRemoved(id)) {
                id = nextOutbound[id];
            }
            return id;
        }

        public final int source(int id) {
            return from[id];
        }

        public final int destination(int id) {
            return to[id];
        }

        public final long weight(int id) {
            if (weight == null) {
                return 0;
            }
            return weight[id];
        }

        public final boolean flag(int id, int bit) {
            return (flags[id] >> bit & 1) != 0;
        }

        public final boolean isRemoved(int id) {
            return flag(id, REMOVED_BIT);
        }

        protected void ensureEdgeCapacity(int size) {
            if (from.length < size) {
                int newSize = Math.max(size, 2 * from.length);
                if (edges != null) {
                    edges = resize(edges, newSize);
                }
                from = resize(from, newSize);
                to = resize(to, newSize);
                nextOutbound = resize(nextOutbound, newSize);
                if (nextInbound != null) {
                    nextInbound = resize(nextInbound, newSize);
                }
                if (weight != null) {
                    weight = resize(weight, newSize);
                }
                if (capacity != null) {
                    capacity = resize(capacity, newSize);
                }
                if (reverseEdge != null) {
                    reverseEdge = resize(reverseEdge, newSize);
                }
                flags = resize(flags, newSize);
            }
        }

        protected final int[] resize(int[] array, int size) {
            int[] newArray = new int[size];
            System.arraycopy(array, 0, newArray, 0, array.length);
            return newArray;
        }

        private long[] resize(long[] array, int size) {
            long[] newArray = new long[size];
            System.arraycopy(array, 0, newArray, 0, array.length);
            return newArray;
        }

        private Edge[] resize(Edge[] array, int size) {
            Edge[] newArray = new Edge[size];
            System.arraycopy(array, 0, newArray, 0, array.length);
            return newArray;
        }

        public final boolean isSparse() {
            return vertexCount == 0 || edgeCount * 20 / vertexCount <= vertexCount;
        }

        protected class GraphEdge implements Edge {
            protected int id;

            protected GraphEdge(int id) {
                this.id = id;
            }

        }

    }

    static interface EzIntList extends EzIntCollection {
        int size();

        EzIntIterator iterator();

        int[] toArray();

        boolean add(int element);

        void clear();

        boolean equals(Object object);

        int hashCode();

        String toString();

    }

    static class OutputWriter {
        private final PrintWriter writer;

        public OutputWriter(OutputStream outputStream) {
            writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream)));
        }

        public OutputWriter(Writer writer) {
            this.writer = new PrintWriter(writer);
        }

        public void print(int[] array) {
            for (int i = 0; i < array.length; i++) {
                if (i != 0) {
                    writer.print(' ');
                }
                writer.print(array[i]);
            }
        }

        public void printLine(int[] array) {
            print(array);
            writer.println();
        }

        public void close() {
            writer.close();
        }

        public void printLine(int i) {
            writer.println(i);
        }

    }

    static interface IntCollection extends egork_generated_collections_IntStream {
    }

    static class EzIntArrayList implements EzIntList, EzIntStack {
        private static final int DEFAULT_CAPACITY = 10;
        private static final double ENLARGE_SCALE = 2.0;
        private static final int HASHCODE_INITIAL_VALUE = 0x811c9dc5;
        private static final int HASHCODE_MULTIPLIER = 0x01000193;
        private int[] array;
        private int size;

        public EzIntArrayList() {
            this(DEFAULT_CAPACITY);
        }

        public EzIntArrayList(int capacity) {
            if (capacity < 0) {
                throw new IllegalArgumentException("Capacity must be non-negative");
            }
            array = new int[capacity];
            size = 0;
        }

        public EzIntArrayList(EzIntCollection collection) {
            size = collection.size();
            array = new int[size];
            int i = 0;
            for (EzIntIterator iterator = collection.iterator(); iterator.hasNext(); ) {
                array[i++] = iterator.next();
            }
        }

        public EzIntArrayList(int[] srcArray) {
            size = srcArray.length;
            array = new int[size];
            System.arraycopy(srcArray, 0, array, 0, size);
        }

        public EzIntArrayList(Collection<Integer> javaCollection) {
            size = javaCollection.size();
            array = new int[size];
            int i = 0;
            for (int element : javaCollection) {
                array[i++] = element;
            }
        }

        public int size() {
            return size;
        }

        public EzIntIterator iterator() {
            return new EzIntArrayListIterator();
        }

        public int[] toArray() {
            int[] result = new int[size];
            System.arraycopy(array, 0, result, 0, size);
            return result;
        }

        public boolean add(int element) {
            if (size == array.length) {
                enlarge();
            }
            array[size++] = element;
            return true;
        }

        public void clear() {
            size = 0;
        }

        private void enlarge() {
            int newSize = Math.max(size + 1, (int) (size * ENLARGE_SCALE));
            int[] newArray = new int[newSize];
            System.arraycopy(array, 0, newArray, 0, size);
            array = newArray;
        }

        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            EzIntArrayList that = (EzIntArrayList) o;

            if (size != that.size) {
                return false;
            }
            for (int i = 0; i < size; i++) {
                if (array[i] != that.array[i]) {
                    return false;
                }
            }
            return true;
        }

        public int hashCode() {
            int hash = HASHCODE_INITIAL_VALUE;
            for (int i = 0; i < size; i++) {
                hash = (hash ^ PrimitiveHashCalculator.getHash(array[i])) * HASHCODE_MULTIPLIER;
            }
            return hash;
        }

        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append('[');
            for (int i = 0; i < size; i++) {
                sb.append(array[i]);
                if (i < size - 1) {
                    sb.append(", ");
                }
            }
            sb.append(']');
            return sb.toString();
        }

        private class EzIntArrayListIterator implements EzIntIterator {
            private int curIndex = 0;

            public boolean hasNext() {
                return curIndex < size;
            }

            public int next() {
                if (curIndex == size) {
                    throw new NoSuchElementException("Iterator doesn't have more elements");
                }
                return array[curIndex++];
            }

        }

    }

    static interface EzIntIterator {
        boolean hasNext();

        int next();

    }

    static class ShortestDistance {
        public static Pair<long[], int[]> dijkstraAlgorithm(Graph graph, int source) {
            int vertexCount = graph.vertexCount();
            final long[] distance = new long[vertexCount];
            int[] last = new int[vertexCount];
            Arrays.fill(distance, Long.MAX_VALUE);
            Arrays.fill(last, -1);
            distance[source] = 0;
            if (graph.isSparse()) {
                Heap heap = new Heap(vertexCount, (first, second) -> IntegerUtils.longCompare(distance[first], distance[second]), vertexCount);
                heap.add(source);
                while (!heap.isEmpty()) {
                    int current = heap.poll();
                    int id = graph.firstOutbound(current);
                    while (id != -1) {
                        int next = graph.destination(id);
                        long total = graph.weight(id) + distance[current];
                        if (distance[next] > total) {
                            distance[next] = total;
                            if (heap.getIndex(next) == -1) {
                                heap.add(next);
                            } else {
                                heap.shiftUp(heap.getIndex(next));
                            }
                            last[next] = id;
                        }
                        id = graph.nextOutbound(id);
                    }
                }
            } else {
                boolean[] visited = new boolean[vertexCount];
                for (int i = 0; i < vertexCount; i++) {
                    int index = -1;
                    long length = Long.MAX_VALUE;
                    for (int j = 0; j < vertexCount; j++) {
                        if (!visited[j] && distance[j] < length) {
                            length = distance[j];
                            index = j;
                        }
                    }
                    if (index == -1) {
                        break;
                    }
                    visited[index] = true;
                    int id = graph.firstOutbound(index);
                    while (id != -1) {
                        int next = graph.destination(id);
                        if (!visited[next]) {
                            long total = graph.weight(id) + length;
                            if (distance[next] > total) {
                                distance[next] = total;
                                last[next] = id;
                            }
                        }
                        id = graph.nextOutbound(id);
                    }
                }
            }
            return Pair.makePair(distance, last);
        }

    }

    static class InputReader {
        private final InputStream stream;
        private final byte[] buf = new byte[1024];
        private int curChar;
        private int numChars;
        private InputReader.SpaceCharFilter filter;

        public InputReader(InputStream stream) {
            this.stream = stream;
        }

        public char[] readCharArray(int size) {
            char[] array = new char[size];
            for (int i = 0; i < size; i++) {
                array[i] = readCharacter();
            }
            return array;
        }

        public char[][] readTable(int rowCount, int columnCount) {
            char[][] table = new char[rowCount][];
            for (int i = 0; i < rowCount; i++) {
                table[i] = this.readCharArray(columnCount);
            }
            return table;
        }

        public int read() {
            if (numChars == -1) {
                throw new InputMismatchException();
            }
            if (curChar >= numChars) {
                curChar = 0;
                try {
                    numChars = stream.read(buf);
                } catch (IOException e) {
                    throw new InputMismatchException();
                }
                if (numChars <= 0) {
                    return -1;
                }
            }
            return buf[curChar++];
        }

        public int readInt() {
            int c = read();
            while (isSpaceChar(c)) {
                c = read();
            }
            int sgn = 1;
            if (c == '-') {
                sgn = -1;
                c = read();
            }
            int res = 0;
            do {
                if (c < '0' || c > '9') {
                    throw new InputMismatchException();
                }
                res *= 10;
                res += c - '0';
                c = read();
            } while (!isSpaceChar(c));
            return res * sgn;
        }

        public String readString() {
            int c = read();
            while (isSpaceChar(c)) {
                c = read();
            }
            StringBuilder res = new StringBuilder();
            do {
                if (Character.isValidCodePoint(c)) {
                    res.appendCodePoint(c);
                }
                c = read();
            } while (!isSpaceChar(c));
            return res.toString();
        }

        public boolean isSpaceChar(int c) {
            if (filter != null) {
                return filter.isSpaceChar(c);
            }
            return isWhitespace(c);
        }

        public static boolean isWhitespace(int c) {
            return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
        }

        public char readCharacter() {
            int c = read();
            while (isSpaceChar(c)) {
                c = read();
            }
            return (char) c;
        }

        public String next() {
            return readString();
        }

        public interface SpaceCharFilter {
            boolean isSpaceChar(int ch);

        }

    }

    static interface Edge {
    }

    static final class PrimitiveHashCalculator {
        private PrimitiveHashCalculator() {
        }

        public static int getHash(int x) {
            return x;
        }

    }

    static interface IntComparator {
        IntComparator DEFAULT = Integer::compare;

        int compare(int first, int second);

    }

    static class ArrayUtils {
        public static long maxElement(long[] array) {
            return Arrays.stream(array).max().getAsLong();
        }

    }

    static class Heap implements IntQueue {
        private IntComparator comparator;
        private int size = 0;
        private int[] elements;
        private int[] at;

        public Heap(int maxElement) {
            this(10, maxElement);
        }

        public Heap(IntComparator comparator, int maxElement) {
            this(10, comparator, maxElement);
        }

        public Heap(int capacity, int maxElement) {
            this(capacity, IntComparator.DEFAULT, maxElement);
        }

        public Heap(int capacity, IntComparator comparator, int maxElement) {
            this.comparator = comparator;
            elements = new int[capacity];
            at = new int[maxElement];
            Arrays.fill(at, -1);
        }

        public boolean isEmpty() {
            return size == 0;
        }

        public void add(int element) {
            ensureCapacity(size + 1);
            elements[size] = element;
            at[element] = size;
            shiftUp(size++);
        }

        public void shiftUp(int index) {
            //		if (index < 0 || index >= size)
            //			throw new IllegalArgumentException();
            int value = elements[index];
            while (index != 0) {
                int parent = (index - 1) >>> 1;
                int parentValue = elements[parent];
                if (comparator.compare(parentValue, value) <= 0) {
                    elements[index] = value;
                    at[value] = index;
                    return;
                }
                elements[index] = parentValue;
                at[parentValue] = index;
                index = parent;
            }
            elements[0] = value;
            at[value] = 0;
        }

        public void shiftDown(int index) {
            if (index < 0 || index >= size) {
                throw new IllegalArgumentException();
            }
            while (true) {
                int child = (index << 1) + 1;
                if (child >= size) {
                    return;
                }
                if (child + 1 < size && comparator.compare(elements[child], elements[child + 1]) > 0) {
                    child++;
                }
                if (comparator.compare(elements[index], elements[child]) <= 0) {
                    return;
                }
                swap(index, child);
                index = child;
            }
        }

        public int getIndex(int element) {
            return at[element];
        }

        private void swap(int first, int second) {
            int temp = elements[first];
            elements[first] = elements[second];
            elements[second] = temp;
            at[elements[first]] = first;
            at[elements[second]] = second;
        }

        private void ensureCapacity(int size) {
            if (elements.length < size) {
                int[] oldElements = elements;
                elements = new int[Math.max(2 * elements.length, size)];
                System.arraycopy(oldElements, 0, elements, 0, this.size);
            }
        }

        public int poll() {
            if (isEmpty()) {
                throw new IndexOutOfBoundsException();
            }
            int result = elements[0];
            at[result] = -1;
            if (size == 1) {
                size = 0;
                return result;
            }
            elements[0] = elements[--size];
            at[elements[0]] = 0;
            shiftDown(0);
            return result;
        }

        public IntIterator intIterator() {
            return new IntIterator() {
                private int at;

                public int value() throws NoSuchElementException {
                    return elements[at];
                }

                public boolean advance() throws NoSuchElementException {
                    return ++at < size;
                }

                public boolean isValid() {
                    return at < size;
                }

                public void remove() throws NoSuchElementException {
                    throw new UnsupportedOperationException();
                }
            };
        }

    }

    static interface IntQueue extends IntCollection {
    }

    static interface EzIntStack extends EzIntCollection {
        int size();

        EzIntIterator iterator();

        boolean equals(Object object);

        int hashCode();

        String toString();

    }

    static interface egork_generated_collections_IntStream extends Iterable<Integer>, Comparable<egork_generated_collections_IntStream> {
        IntIterator intIterator();

        default Iterator<Integer> iterator() {
            return new Iterator<Integer>() {
                private IntIterator it = intIterator();

                public boolean hasNext() {
                    return it.isValid();
                }

                public Integer next() {
                    int result = it.value();
                    it.advance();
                    return result;
                }
            };
        }

        default int compareTo(egork_generated_collections_IntStream c) {
            IntIterator it = intIterator();
            IntIterator jt = c.intIterator();
            while (it.isValid() && jt.isValid()) {
                int i = it.value();
                int j = jt.value();
                if (i < j) {
                    return -1;
                } else if (i > j) {
                    return 1;
                }
                it.advance();
                jt.advance();
            }
            if (it.isValid()) {
                return 1;
            }
            if (jt.isValid()) {
                return -1;
            }
            return 0;
        }

    }

    static class IntegerUtils {
        public static int longCompare(long a, long b) {
            return Long.compare(a, b);
        }

    }

    static interface IntIterator {
        public int value() throws NoSuchElementException;

        public boolean advance();

        public boolean isValid();

    }

    static interface EzIntCollection {
        int size();

        EzIntIterator iterator();

        boolean equals(Object object);

        int hashCode();

        String toString();

    }
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.stream.IntStream;
import java.util.Arrays;
import java.util.InputMismatchException;
import java.util.OptionalInt;
import java.util.ArrayList;
import java.io.OutputStreamWriter;
import java.util.NoSuchElementException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Iterator;
import java.io.BufferedWriter;
import java.util.Collection;
import java.io.IOException;
import java.util.Objects;
import java.util.List;
import java.io.Writer;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 *
 * @author aryssoncf
 */
public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        InputReader in = new InputReader(inputStream);
        OutputWriter out = new OutputWriter(outputStream);
        A2BinaryTableHardVersion solver = new A2BinaryTableHardVersion();
        int testCount = Integer.parseInt(in.next());
        for (int i = 1; i <= testCount; i++)
            solver.solve(i, in, out);
        out.close();
    }

    static class A2BinaryTableHardVersion {
        int[][][] path = null;

        public void solve(int testNumber, InputReader in, OutputWriter out) {
            if (path == null) {
                path = init();
            }
            int n = in.readInt(), m = in.readInt();
            char[][] table = in.readTable(n, m);
            List<int[]> res = new ArrayList<>();
            for (int r = 0; r < n; ) {
                int n2 = r + 3 == n ? 3 : 2;
                for (int c = 0; c < m; ) {
                    int m2 = c + 3 == m ? 3 : 2;
                    int start = 0;
                    for (int i = 0; i < n2; i++) {
                        for (int j = 0; j < m2; j++) {
                            if (table[r + i][c + j] == '1') {
                                start ^= 1 << (i * m2 + j);
                            }
                        }
                    }
                    List<int[]> p = getPath(start, n2, m2);
                    for (int[] arr : p) {
                        for (int i = 0; i < 6; i++) {
                            arr[i] += i % 2 == 0 ? r + 1 : c + 1;
                        }
                    }
                    res.addAll(p);
                    c += m2;
                }
                r += n2;
            }
            out.printLine(res.size());
            for (int[] arr : res) {
                out.printLine(arr);
            }
        }

        List<int[]> getPath(int u, int n, int m) {
            int[] previous = path[n - 2][m - 2];
            List<int[]> res = new ArrayList<>();
            EzIntList list = new EzIntArrayList(6);
            while (u != 0) {
                int v = previous[u];
                int[] arr = new int[6];
                for (int i = 0; i < n * m; i++) {
                    if (((u ^ v) & (1 << i)) > 0) {
                        list.add(i / m);
                        list.add(i % m);
                    }
                }
                res.add(list.toArray());
                list.clear();
                u = v;
            }
            return res;
        }

        int[][][] init() {
            int[][][] res = new int[2][2][];
            for (int i = 0; i < 2; i++) {
                for (int j = 0; j < 2; j++) {
                    res[i][j] = getPath(i + 2, j + 2);
                }
            }
            return res;
        }

        int[] getPath(int n, int m) {
            Graph graph = new Graph(1 << (n * m));
            for (int u = 0; u < (1 << (n * m)); u++) {
                for (int r0 = 0; r0 < n - 1; r0++) {
                    for (int c0 = 0; c0 < m - 1; c0++) {
                        int aux = u;
                        for (int r = r0; r < r0 + 2; r++) {
                            for (int c = c0; c < c0 + 2; c++) {
                                aux ^= 1 << (r * m + c);
                            }
                        }
                        for (int r = r0; r < r0 + 2; r++) {
                            for (int c = c0; c < c0 + 2; c++) {
                                int v = aux ^ (1 << (r * m + c));
                                graph.addSimpleEdge(u, v);
                            }
                        }
                    }
                }
            }
            Pair<int[], int[]> dijkstra = ShortestDistance.bfs(graph, 0);
            if (ArrayUtils.maxElement(dijkstra.first) > n * m) {
                throw new UnknownError();
            }
            return Arrays.stream(dijkstra.second).map(i -> i == -1 ? -1 : graph.source(i)).toArray();
        }

    }

    static class Pair<U, V> implements Comparable<Pair<U, V>> {
        public final U first;
        public final V second;

        public static <U, V> Pair<U, V> makePair(U first, V second) {
            return new Pair<>(first, second);
        }

        private Pair(U first, V second) {
            this.first = first;
            this.second = second;
        }

        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            Pair pair = (Pair) o;

            return Objects.equals(first, pair.first) &&
                    Objects.equals(second, pair.second);
        }

        public int hashCode() {
            int result = first != null ? first.hashCode() : 0;
            result = 31 * result + (second != null ? second.hashCode() : 0);
            return result;
        }

        public String toString() {
            return "(" + first + "," + second + ")";
        }

        public int compareTo(Pair<U, V> o) {
            int value = ((Comparable<U>) first).compareTo(o.first);
            if (value != 0) {
                return value;
            }
            return ((Comparable<V>) second).compareTo(o.second);
        }

    }

    static class Graph {
        public static final int REMOVED_BIT = 0;
        protected int vertexCount;
        protected int edgeCount;
        private int[] firstOutbound;
        private int[] firstInbound;
        private Edge[] edges;
        private int[] nextInbound;
        private int[] nextOutbound;
        private int[] from;
        private int[] to;
        private long[] weight;
        public long[] capacity;
        private int[] reverseEdge;
        private int[] flags;

        public Graph(int vertexCount) {
            this(vertexCount, vertexCount);
        }

        public Graph(int vertexCount, int edgeCapacity) {
            this.vertexCount = vertexCount;
            firstOutbound = new int[vertexCount];
            Arrays.fill(firstOutbound, -1);

            from = new int[edgeCapacity];
            to = new int[edgeCapacity];
            nextOutbound = new int[edgeCapacity];
            flags = new int[edgeCapacity];
        }

        public int addEdge(int fromID, int toID, long weight, long capacity, int reverseEdge) {
            ensureEdgeCapacity(edgeCount + 1);
            if (firstOutbound[fromID] != -1) {
                nextOutbound[edgeCount] = firstOutbound[fromID];
            } else {
                nextOutbound[edgeCount] = -1;
            }
            firstOutbound[fromID] = edgeCount;
            if (firstInbound != null) {
                if (firstInbound[toID] != -1) {
                    nextInbound[edgeCount] = firstInbound[toID];
                } else {
                    nextInbound[edgeCount] = -1;
                }
                firstInbound[toID] = edgeCount;
            }
            this.from[edgeCount] = fromID;
            this.to[edgeCount] = toID;
            if (capacity != 0) {
                if (this.capacity == null) {
                    this.capacity = new long[from.length];
                }
                this.capacity[edgeCount] = capacity;
            }
            if (weight != 0) {
                if (this.weight == null) {
                    this.weight = new long[from.length];
                }
                this.weight[edgeCount] = weight;
            }
            if (reverseEdge != -1) {
                if (this.reverseEdge == null) {
                    this.reverseEdge = new int[from.length];
                    Arrays.fill(this.reverseEdge, 0, edgeCount, -1);
                }
                this.reverseEdge[edgeCount] = reverseEdge;
            }
            if (edges != null) {
                edges[edgeCount] = createEdge(edgeCount);
            }
            return edgeCount++;
        }

        protected final GraphEdge createEdge(int id) {
            return new GraphEdge(id);
        }

        public final int addFlowWeightedEdge(int from, int to, long weight, long capacity) {
            if (capacity == 0) {
                return addEdge(from, to, weight, 0, -1);
            } else {
                int lastEdgeCount = edgeCount;
                addEdge(to, from, -weight, 0, lastEdgeCount + entriesPerEdge());
                return addEdge(from, to, weight, capacity, lastEdgeCount);
            }
        }

        protected int entriesPerEdge() {
            return 1;
        }

        public final int addWeightedEdge(int from, int to, long weight) {
            return addFlowWeightedEdge(from, to, weight, 0);
        }

        public final int addSimpleEdge(int from, int to) {
            return addWeightedEdge(from, to, 0);
        }

        public final int vertexCount() {
            return vertexCount;
        }

        public final int firstOutbound(int vertex) {
            int id = firstOutbound[vertex];
            while (id != -1 && isRemoved(id)) {
                id = nextOutbound[id];
            }
            return id;
        }

        public final int nextOutbound(int id) {
            id = nextOutbound[id];
            while (id != -1 && isRemoved(id)) {
                id = nextOutbound[id];
            }
            return id;
        }

        public final int source(int id) {
            return from[id];
        }

        public final int destination(int id) {
            return to[id];
        }

        public final boolean flag(int id, int bit) {
            return (flags[id] >> bit & 1) != 0;
        }

        public final boolean isRemoved(int id) {
            return flag(id, REMOVED_BIT);
        }

        protected void ensureEdgeCapacity(int size) {
            if (from.length < size) {
                int newSize = Math.max(size, 2 * from.length);
                if (edges != null) {
                    edges = resize(edges, newSize);
                }
                from = resize(from, newSize);
                to = resize(to, newSize);
                nextOutbound = resize(nextOutbound, newSize);
                if (nextInbound != null) {
                    nextInbound = resize(nextInbound, newSize);
                }
                if (weight != null) {
                    weight = resize(weight, newSize);
                }
                if (capacity != null) {
                    capacity = resize(capacity, newSize);
                }
                if (reverseEdge != null) {
                    reverseEdge = resize(reverseEdge, newSize);
                }
                flags = resize(flags, newSize);
            }
        }

        protected final int[] resize(int[] array, int size) {
            int[] newArray = new int[size];
            System.arraycopy(array, 0, newArray, 0, array.length);
            return newArray;
        }

        private long[] resize(long[] array, int size) {
            long[] newArray = new long[size];
            System.arraycopy(array, 0, newArray, 0, array.length);
            return newArray;
        }

        private Edge[] resize(Edge[] array, int size) {
            Edge[] newArray = new Edge[size];
            System.arraycopy(array, 0, newArray, 0, array.length);
            return newArray;
        }

        protected class GraphEdge implements Edge {
            protected int id;

            protected GraphEdge(int id) {
                this.id = id;
            }

        }

    }

    static interface EzIntList extends EzIntCollection {
        int size();

        EzIntIterator iterator();

        int[] toArray();

        boolean add(int element);

        void clear();

        boolean equals(Object object);

        int hashCode();

        String toString();

    }

    static class OutputWriter {
        private final PrintWriter writer;

        public OutputWriter(OutputStream outputStream) {
            writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream)));
        }

        public OutputWriter(Writer writer) {
            this.writer = new PrintWriter(writer);
        }

        public void print(int[] array) {
            for (int i = 0; i < array.length; i++) {
                if (i != 0) {
                    writer.print(' ');
                }
                writer.print(array[i]);
            }
        }

        public void printLine(int[] array) {
            print(array);
            writer.println();
        }

        public void close() {
            writer.close();
        }

        public void printLine(int i) {
            writer.println(i);
        }

    }

    static interface IntCollection extends egork_generated_collections_IntStream {
        public int size();

        default public boolean isEmpty() {
            return size() == 0;
        }

        default public void add(int value) {
            throw new UnsupportedOperationException();
        }

        default public IntCollection addAll(egork_generated_collections_IntStream values) {
            for (IntIterator it = values.intIterator(); it.isValid(); it.advance()) {
                add(it.value());
            }
            return this;
        }

    }

    static class EzIntArrayList implements EzIntList, EzIntStack {
        private static final int DEFAULT_CAPACITY = 10;
        private static final double ENLARGE_SCALE = 2.0;
        private static final int HASHCODE_INITIAL_VALUE = 0x811c9dc5;
        private static final int HASHCODE_MULTIPLIER = 0x01000193;
        private int[] array;
        private int size;

        public EzIntArrayList() {
            this(DEFAULT_CAPACITY);
        }

        public EzIntArrayList(int capacity) {
            if (capacity < 0) {
                throw new IllegalArgumentException("Capacity must be non-negative");
            }
            array = new int[capacity];
            size = 0;
        }

        public EzIntArrayList(EzIntCollection collection) {
            size = collection.size();
            array = new int[size];
            int i = 0;
            for (EzIntIterator iterator = collection.iterator(); iterator.hasNext(); ) {
                array[i++] = iterator.next();
            }
        }

        public EzIntArrayList(int[] srcArray) {
            size = srcArray.length;
            array = new int[size];
            System.arraycopy(srcArray, 0, array, 0, size);
        }

        public EzIntArrayList(Collection<Integer> javaCollection) {
            size = javaCollection.size();
            array = new int[size];
            int i = 0;
            for (int element : javaCollection) {
                array[i++] = element;
            }
        }

        public int size() {
            return size;
        }

        public EzIntIterator iterator() {
            return new EzIntArrayListIterator();
        }

        public int[] toArray() {
            int[] result = new int[size];
            System.arraycopy(array, 0, result, 0, size);
            return result;
        }

        public boolean add(int element) {
            if (size == array.length) {
                enlarge();
            }
            array[size++] = element;
            return true;
        }

        public void clear() {
            size = 0;
        }

        private void enlarge() {
            int newSize = Math.max(size + 1, (int) (size * ENLARGE_SCALE));
            int[] newArray = new int[newSize];
            System.arraycopy(array, 0, newArray, 0, size);
            array = newArray;
        }

        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            EzIntArrayList that = (EzIntArrayList) o;

            if (size != that.size) {
                return false;
            }
            for (int i = 0; i < size; i++) {
                if (array[i] != that.array[i]) {
                    return false;
                }
            }
            return true;
        }

        public int hashCode() {
            int hash = HASHCODE_INITIAL_VALUE;
            for (int i = 0; i < size; i++) {
                hash = (hash ^ PrimitiveHashCalculator.getHash(array[i])) * HASHCODE_MULTIPLIER;
            }
            return hash;
        }

        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append('[');
            for (int i = 0; i < size; i++) {
                sb.append(array[i]);
                if (i < size - 1) {
                    sb.append(", ");
                }
            }
            sb.append(']');
            return sb.toString();
        }

        private class EzIntArrayListIterator implements EzIntIterator {
            private int curIndex = 0;

            public boolean hasNext() {
                return curIndex < size;
            }

            public int next() {
                if (curIndex == size) {
                    throw new NoSuchElementException("Iterator doesn't have more elements");
                }
                return array[curIndex++];
            }

        }

    }

    static interface EzIntIterator {
        boolean hasNext();

        int next();

    }

    static class ShortestDistance {
        public static Pair<int[], int[]> bfs(Graph graph, int source) {
            int vertexCount = graph.vertexCount();
            final int[] distance = new int[vertexCount];
            int[] last = new int[vertexCount];
            Arrays.fill(distance, Integer.MAX_VALUE);
            Arrays.fill(last, -1);
            distance[source] = 0;
            IntQueue queue = new IntArrayQueue();
            queue.add(source);
            while (!queue.isEmpty()) {
                int current = queue.poll();
                int id = graph.firstOutbound(current);
                while (id != -1) {
                    int next = graph.destination(id);
                    int total = 1 + distance[current];
                    if (distance[next] > total) {
                        distance[next] = total;
                        queue.add(next);
                        last[next] = id;
                    }
                    id = graph.nextOutbound(id);
                }
            }
            return Pair.makePair(distance, last);
        }

    }

    static class InputReader {
        private final InputStream stream;
        private final byte[] buf = new byte[1024];
        private int curChar;
        private int numChars;
        private InputReader.SpaceCharFilter filter;

        public InputReader(InputStream stream) {
            this.stream = stream;
        }

        public char[] readCharArray(int size) {
            char[] array = new char[size];
            for (int i = 0; i < size; i++) {
                array[i] = readCharacter();
            }
            return array;
        }

        public char[][] readTable(int rowCount, int columnCount) {
            char[][] table = new char[rowCount][];
            for (int i = 0; i < rowCount; i++) {
                table[i] = this.readCharArray(columnCount);
            }
            return table;
        }

        public int read() {
            if (numChars == -1) {
                throw new InputMismatchException();
            }
            if (curChar >= numChars) {
                curChar = 0;
                try {
                    numChars = stream.read(buf);
                } catch (IOException e) {
                    throw new InputMismatchException();
                }
                if (numChars <= 0) {
                    return -1;
                }
            }
            return buf[curChar++];
        }

        public int readInt() {
            int c = read();
            while (isSpaceChar(c)) {
                c = read();
            }
            int sgn = 1;
            if (c == '-') {
                sgn = -1;
                c = read();
            }
            int res = 0;
            do {
                if (c < '0' || c > '9') {
                    throw new InputMismatchException();
                }
                res *= 10;
                res += c - '0';
                c = read();
            } while (!isSpaceChar(c));
            return res * sgn;
        }

        public String readString() {
            int c = read();
            while (isSpaceChar(c)) {
                c = read();
            }
            StringBuilder res = new StringBuilder();
            do {
                if (Character.isValidCodePoint(c)) {
                    res.appendCodePoint(c);
                }
                c = read();
            } while (!isSpaceChar(c));
            return res.toString();
        }

        public boolean isSpaceChar(int c) {
            if (filter != null) {
                return filter.isSpaceChar(c);
            }
            return isWhitespace(c);
        }

        public static boolean isWhitespace(int c) {
            return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
        }

        public char readCharacter() {
            int c = read();
            while (isSpaceChar(c)) {
                c = read();
            }
            return (char) c;
        }

        public String next() {
            return readString();
        }

        public interface SpaceCharFilter {
            boolean isSpaceChar(int ch);

        }

    }

    static interface Edge {
    }

    static final class PrimitiveHashCalculator {
        private PrimitiveHashCalculator() {
        }

        public static int getHash(int x) {
            return x;
        }

    }

    static class ArrayUtils {
        public static int maxElement(int[] array) {
            return Arrays.stream(array).max().getAsInt();
        }

    }

    static interface IntQueue extends IntCollection {
        public int poll();

    }

    static class IntArrayQueue implements IntQueue {
        private int[] data;
        private int from;
        private int to;

        public IntArrayQueue(int capacity) {
            data = new int[Integer.highestOneBit(capacity) << 1];
        }

        public IntArrayQueue() {
            this(3);
        }

        public IntArrayQueue(int[] array) {
            this(array.length);
            to = array.length;
            System.arraycopy(array, 0, data, 0, to);
        }

        public IntArrayQueue(egork_generated_collections_IntStream s) {
            addAll(s);
        }

        public IntArrayQueue(IntCollection c) {
            this(c.size());
            addAll(c);
        }

        public int size() {
            return (to - from) & (data.length - 1);
        }

        public void add(int v) {
            ensureCapacity(size() + 1);
            data[to++] = v;
            to &= data.length - 1;
        }

        public int poll() {
            if (from == to) {
                throw new NoSuchElementException();
            }
            int result = data[from++];
            from &= data.length - 1;
            return result;
        }

        public IntIterator intIterator() {
            return new IntIterator() {
                private int at = from;

                public int value() {
                    if (at == to) {
                        throw new NoSuchElementException();
                    }
                    return data[at];
                }

                public boolean advance() {
                    if (!isValid()) {
                        throw new NoSuchElementException();
                    }
                    at++;
                    at &= data.length - 1;
                    return isValid();
                }

                public boolean isValid() {
                    return at != to;
                }

                public void remove() {
                    throw new UnsupportedOperationException();
                }
            };
        }

        private void ensureCapacity(int capacity) {
            if (data.length <= capacity) {
                int[] newData = new int[Integer.highestOneBit(capacity) << 1];
                if (from <= to) {
                    System.arraycopy(data, from, newData, 0, size());
                } else {
                    System.arraycopy(data, from, newData, 0, data.length - from);
                    System.arraycopy(data, 0, newData, data.length - from, to);
                }
                to = size();
                from = 0;
                data = newData;
            }
        }

    }

    static interface EzIntStack extends EzIntCollection {
        int size();

        EzIntIterator iterator();

        boolean equals(Object object);

        int hashCode();

        String toString();

    }

    static interface egork_generated_collections_IntStream extends Iterable<Integer>, Comparable<egork_generated_collections_IntStream> {
        IntIterator intIterator();

        default Iterator<Integer> iterator() {
            return new Iterator<Integer>() {
                private IntIterator it = intIterator();

                public boolean hasNext() {
                    return it.isValid();
                }

                public Integer next() {
                    int result = it.value();
                    it.advance();
                    return result;
                }
            };
        }

        default int compareTo(egork_generated_collections_IntStream c) {
            IntIterator it = intIterator();
            IntIterator jt = c.intIterator();
            while (it.isValid() && jt.isValid()) {
                int i = it.value();
                int j = jt.value();
                if (i < j) {
                    return -1;
                } else if (i > j) {
                    return 1;
                }
                it.advance();
                jt.advance();
            }
            if (it.isValid()) {
                return 1;
            }
            if (jt.isValid()) {
                return -1;
            }
            return 0;
        }

    }

    static interface IntIterator {
        public int value() throws NoSuchElementException;

        public boolean advance();

        public boolean isValid();

    }

    static interface EzIntCollection {
        int size();

        EzIntIterator iterator();

        boolean equals(Object object);

        int hashCode();

        String toString();

    }
}


#pragma GCC optimize("Ofast")
#pragma GCC target ("sse4")

#include<iostream>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<algorithm>
#include<functional>
#include<iomanip>
#include<queue>
#include<ciso646>
#include<random>
#include<map>
#include<set>
#include<bitset>
#include<stack>
#include<unordered_map>
#include<unordered_set>
#include<utility>
#include<cassert>
#include<complex>
#include<numeric>
#include<array>
using namespace std;

//#define int long long
typedef long long ll;

typedef unsigned long long ul;
typedef unsigned int ui;
constexpr ll mod = 1000000007;
const ll INF = mod * mod;
typedef pair<int, int>P;
#define stop char nyaa;cin>>nyaa;
#define rep(i,n) for(int i=0;i<n;i++)
#define per(i,n) for(int i=n-1;i>=0;i--)
#define Rep(i,sta,n) for(int i=sta;i<n;i++)
#define rep1(i,n) for(int i=1;i<=n;i++)
#define per1(i,n) for(int i=n;i>=1;i--)
#define Rep1(i,sta,n) for(int i=sta;i<=n;i++)
#define all(v) (v).begin(),(v).end()
typedef pair<ll, ll> LP;
typedef double ld;
typedef pair<ld, ld> LDP;
const ld eps = 1e-12;
const ld pi = acosl(-1.0);

ll mod_pow(ll x, ll n, ll m = mod) {
    ll res = 1;
    while (n) {
        if (n & 1)res = res * x % m;
        x = x * x % m; n >>= 1;
    }
    return res;
}
struct modint {
    ll n;
    modint() :n(0) { ; }
    modint(ll m) :n(m) {
        if (n >= mod)n %= mod;
        else if (n < 0)n = (n % mod + mod) % mod;
    }
    operator int() { return n; }
};
bool operator==(modint a, modint b) { return a.n == b.n; }
modint operator+=(modint& a, modint b) { a.n += b.n; if (a.n >= mod)a.n -= mod; return a; }
modint operator-=(modint& a, modint b) { a.n -= b.n; if (a.n < 0)a.n += mod; return a; }
modint operator*=(modint& a, modint b) { a.n = ((ll)a.n * b.n) % mod; return a; }
modint operator+(modint a, modint b) { return a += b; }
modint operator-(modint a, modint b) { return a -= b; }
modint operator*(modint a, modint b) { return a *= b; }
modint operator^(modint a, ll n) {
    if (n == 0)return modint(1);
    modint res = (a * a) ^ (n / 2);
    if (n % 2)res = res * a;
    return res;
}

ll inv(ll a, ll p) {
    return (a == 1 ? 1 : (1 - p * inv(p % a, a)) / a + p);
}
modint operator/(modint a, modint b) { return a * modint(inv(b, mod)); }

const int max_n = 1 << 18;
modint fact[max_n], factinv[max_n];
void init_f() {
    fact[0] = modint(1);
    for (int i = 0; i < max_n - 1; i++) {
        fact[i + 1] = fact[i] * modint(i + 1);
    }
    factinv[max_n - 1] = modint(1) / fact[max_n - 1];
    for (int i = max_n - 2; i >= 0; i--) {
        factinv[i] = factinv[i + 1] * modint(i + 1);
    }
}
modint comb(int a, int b) {
    if (a < 0 || b < 0 || a < b)return 0;
    return fact[a] * factinv[b] * factinv[a - b];
}

void solve() {
    int n, m; cin >> n >> m;
    vector<string> s(n);
    rep(i, n)cin >> s[i];
    vector<vector<P>> vs;
    auto tra = [&](char c)->char {
        if (c == '0')return '1';
        else return '0';
    };
    for (int i = n - 1; i >= 2; i--)for (int j = m - 1; j >= 2; j--) {
        if (s[i][j] == '1') {
            s[i][j] = '0';
            s[i - 1][j] = tra(s[i - 1][j]);
            s[i][j - 1] = tra(s[i][j - 1]);
            vs.push_back({ {i,j},{i - 1,j},{i,j - 1} });
        }
    }
    for (int i = n - 1; i >= 2; i--) {
        vector<P> cur;
        if (s[i][0] == '1')cur.push_back({ i,0 });
        if (s[i][1] == '1')cur.push_back({ i,1 });
        if (cur.size() == 0)continue;
        else if (cur.size() == 1) {
            cur.push_back({ i - 1,0 });
            cur.push_back({ i - 1,1 });
            s[i - 1][0] = tra(s[i - 1][0]);
            s[i - 1][1] = tra(s[i - 1][1]);
        }
        else {
            cur.push_back({ i - 1,0 });
            s[i - 1][0] = tra(s[i - 1][0]);
        }
        vs.push_back(cur);
    }
    for (int j = m - 1; j >= 2; j--) {
        vector<P> cur;
        if (s[0][j] == '1')cur.push_back({ 0,j });
        if (s[1][j] == '1')cur.push_back({ 1,j });
        if (cur.size() == 0)continue;
        rep(i, 2) {
            if (cur.size() == 3)continue;
            s[i][j - 1] = tra(s[i][j-1]);
            cur.push_back({ i,j - 1 });
        }
        vs.push_back(cur);
    }
    while (true) {
        vector<P> v[2];
        rep(i, 2)rep(j, 2)v[s[i][j] - '0'].push_back({ i,j });
        if (v[1].size()==0)break;
        else if (v[1].size()==1) {
            vector<P> nex = { v[1][0],v[0][0],v[0][1] };
            vs.push_back(nex);
            for (P p : nex) {
                s[p.first][p.second] = tra(s[p.first][p.second]);
            }
        }
        else if (v[1].size() == 2) {
            vector<P> nex = { v[0][0],v[0][1],v[1][0] };
            vs.push_back(nex);
            for (P p : nex) {
                s[p.first][p.second] = tra(s[p.first][p.second]);
            }
        }
        else if (v[1].size() == 3) {
            vector<P> nex = { v[1][0],v[1][1],v[1][2] };
            vs.push_back(nex);
            for (P p : nex) {
                s[p.first][p.second] = tra(s[p.first][p.second]);
            }
        }
        else {
            vector<P> nex = { v[1][0],v[1][1],v[1][2] };
            vs.push_back(nex);
            for (P p : nex) {
                s[p.first][p.second] = tra(s[p.first][p.second]);
            }
        }
    }
    cout << vs.size() << "\n";
    rep(i, vs.size()) {
        rep(j, 3) {
            if (j > 0)cout << " ";
            cout << vs[i][j].first + 1 << " " << vs[i][j].second + 1;
        }
        cout << "\n";
    }
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    // cout << fixed << setprecision(15);
     //init_f();
     //init();
     //expr();
     int t; cin >> t; rep(i, t)
    solve();
    return 0;
}


#include <iostream>
#include <stdio.h>
#include <string.h>
#include <map>
#include <unordered_map>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <math.h>
#include <random>
#include <chrono>
#include <assert.h>
#include <ctime>

using namespace std;

#define x first
#define y second
#define vi vector<int>
#define pb push_back
#define mp make_pair
#define pii pair<int, int>
#define all(x) x.begin(),x.end()
#define SZ(x) int(x.size())
#define rep(i, a, b) for(int i = a; i < b; i++)
#define per(i, a, b) for(int i = b-1; i >= a; i--)
#define DBG(x) cerr << (#x) << "=" << x << "\n";
#define ll long long
#define inf 1000000007
#define mod 1000000007
#define N 30005

template<typename U, typename V> void Min(U &a, const V & b){if(a > b)a = b;}
template<typename U, typename V> void Max(U &a, const V & b){if(a < b)a = b;}
template<typename U, typename V> void add(U &a, const V & b){a = (a + b) % mod;}
template<typename U> U gcd(U a, U b){
    if(a == 0)return b;
    if(b == 0)return a;
    if(a >= b)return gcd(a % b, b);
    else return gcd(a, b % a);
}
int pow(int a, int b){
    int ans = 1;
    while(b){
        if(b & 1)ans = 1LL * ans * a % mod;
        a = 1LL * a * a % mod;
        b >>= 1;
    }
    return ans;
}
int pow(int a, ll b, int c){
    int ans = 1;
    while(b){
        if(b & 1)ans = 1LL * ans * a % c;
        a = 1LL * a * a % c;
        b >>= 1;
    }
    return ans;
}

struct node{
    int x1, y1, x2, y2, x3, y3;
}ans[N];
int sz;
char s[101][101];
void flip(int x, int y, int x2, int y2, int x3, int y3){
    ans[sz++] = {x, y, x2, y2, x3, y3};
    s[x][y] = ((s[x][y] - '0') ^ 1) + '0';
    s[x2][y2] = ((s[x2][y2] - '0') ^ 1) + '0';
    s[x3][y3] = ((s[x3][y3] - '0') ^ 1) + '0';
}

void solve(int x, int y, int x2, int y2, int x3, int y3, int x4, int y4){
    flip(x, y, x2, y2, x4, y4);
    flip(x, y, x3, y3, x4, y4);
    flip(x, y, x2, y2, x3, y3);
}
int main(){
    int ca = 0, T, i, j, k, K, m, n;
    scanf("%d", &T);
    while(T--){
        scanf("%d%d", &n, &m);
        rep(i, 0, n)scanf("%s", s[i]);
        sz = 0;
        rep(i, 0, n-2){
            rep(j, 0, m-1)if(s[i][j] == '1'){
                if(s[i][j+1] == '1')flip(i, j, i, j+1, i+1, j+1);
                else flip(i, j, i+1, j, i+1, j+1);
            }
            if(s[i][m-1] == '1'){
                flip(i, m-1, i+1, m-1, i+1, m-2);
            }
        }
        rep(j, 0, m-2){
            if(s[n-2][j] == '1' && s[n-1][j] == '1'){
                flip(n-2, j, n-2, j+1, n-1, j);
            }
            else if(s[n-2][j] == '1'){
                flip(n-2, j, n-2, j+1, n-1, j+1);
            }
            else if(s[n-1][j] == '1'){
                flip(n-1, j, n-1, j+1, n-2, j+1);
            }
        }
        int t = 0, num = 0;
        pii v[4];
        v[t++] = {n-2, m-2}, v[t++] = {n-2, m-1};
        v[t++] = {n-1, m-1}, v[t++] = {n-1, m-2};
        rep(i, 0, t){
            int x = v[i].x, y = v[i].y;
            if(s[x][y] == '1')num++;
        }
        if(num == 1){
            rep(i, 0, t){
                int x = v[i].x, y = v[i].y;
                if(s[x][y] == '1'){
                    int l = (i+1)%4, r = (i+3) % 4, k = (i+2) % 4;
                    solve(x, y, v[l].x, v[l].y, v[r].x, v[r].y, v[k].x, v[k].y);
                }
            }
        }
        else if(num == 2){
            rep(i, 0, t){
                int x = v[i].x, y = v[i].y;
                if(s[x][y] == '1'){
                    int l = (i+1)%4, r = (i+2) % 4, k = (i+3) % 4;
                    int nx = v[l].x, ny = v[l].y;
                    if(s[nx][ny] == '1'){
                        flip(x, y, v[l].x, v[l].y, v[r].x, v[r].y);
                        solve(v[r].x, v[r].y, v[l].x, v[l].y, v[k].x, v[k].y, x, y);
                        break;
                    }
                    nx = v[r].x, ny = v[r].y;
                    if(s[nx][ny] == '1'){
                        flip(x, y, v[l].x, v[l].y, v[r].x, v[r].y);
                        solve(v[l].x, v[l].y, v[r].x, v[r].y, x, y, v[k].x, v[k].y);
                        break;

                    }
                }
            }
        }
        else if(num == 3){
            rep(i, 0, t){
                int x = v[i].x, y = v[i].y;
                if(s[x][y] == '0'){
                    int l = (i+1)%4, r = (i+2) % 4, k = (i+3) % 4;
                    flip(v[l].x, v[l].y, v[r].x, v[r].y, v[k].x, v[k].y);
                    break;
                }

            }
        }
        else if(num == 4){
            flip(n-1, m-1, n-1, m-2, n-2, m-1);
            solve(n-2, m-2, n-1, m-2, n-2, m-1, n-1, m-1);
        }
        printf("%d\n", sz);
        rep(i, 0, sz){
            printf("%d %d %d %d %d %d\n", ans[i].x1+1, ans[i].y1+1, ans[i].x2+1, ans[i].y2+1, ans[i].x3+1, ans[i].y3+1);
        }
        //rep(i, 0, n)puts(s[i]);
    }
}


#include <iostream>
#include <stdio.h>
#include <string.h>
#include <map>
#include <unordered_map>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <math.h>
#include <random>
#include <chrono>
#include <assert.h>
#include <ctime>

using namespace std;

#define x first
#define y second
#define vi vector<int>
#define pb push_back
#define mp make_pair
#define pii pair<int, int>
#define all(x) x.begin(),x.end()
#define SZ(x) int(x.size())
#define rep(i, a, b) for(int i = a; i < b; i++)
#define per(i, a, b) for(int i = b-1; i >= a; i--)
#define DBG(x) cerr << (#x) << "=" << x << "\n";
#define ll long long
#define inf 1000000007
#define mod 1000000007
#define N 30005

template<typename U, typename V> void Min(U &a, const V & b){if(a > b)a = b;}
template<typename U, typename V> void Max(U &a, const V & b){if(a < b)a = b;}
template<typename U, typename V> void add(U &a, const V & b){a = (a + b) % mod;}
template<typename U> U gcd(U a, U b){
    if(a == 0)return b;
    if(b == 0)return a;
    if(a >= b)return gcd(a % b, b);
    else return gcd(a, b % a);
}
int pow(int a, int b){
    int ans = 1;
    while(b){
        if(b & 1)ans = 1LL * ans * a % mod;
        a = 1LL * a * a % mod;
        b >>= 1;
    }
    return ans;
}
int pow(int a, ll b, int c){
    int ans = 1;
    while(b){
        if(b & 1)ans = 1LL * ans * a % c;
        a = 1LL * a * a % c;
        b >>= 1;
    }
    return ans;
}

struct node{
    int x1, y1, x2, y2, x3, y3;
}ans[N];
int sz;
void solve(int x, int y, int x2, int y2, int x3, int y3, int x4, int y4){
    ans[sz++] = {x, y, x2, y2, x4, y4};
    ans[sz++] = {x, y, x3, y3, x4, y4};
    ans[sz++] = {x, y, x2, y2, x3, y3};
}
char s[101][101];
void flip(int x, int y, int x2, int y2, int x3, int y3){
    ans[sz++] = {x, y, x2, y2, x3, y3};
    s[x][y] = ((s[x][y] - '0') ^ 1) + '0';
    s[x2][y2] = ((s[x2][y2] - '0') ^ 1) + '0';
    s[x3][y3] = ((s[x3][y3] - '0') ^ 1) + '0';
}
int main(){
    int ca = 0, T, i, j, k, K, m, n;
    scanf("%d", &T);
    while(T--){
        scanf("%d%d", &n, &m);
        rep(i, 0, n)scanf("%s", s[i]);
        sz = 0;
        rep(i, 0, n-2){
            rep(j, 0, m-1)if(s[i][j] == '1'){
                if(s[i][j+1] == '1')flip(i, j, i, j+1, i+1, j+1);
                else flip(i, j, i+1, j, i+1, j+1);
            }
            if(s[i][m-1] == '1'){
                flip(i, m-1, i+1, m-1, i+1, m-2);
            }
        }
        rep(j, 0, m-2){
            if(s[n-2][j] == '1' && s[n-1][j] == '1'){
                flip(n-2, j, n-2, j+1, n-1, j);
            }
            else if(s[n-2][j] == '1'){
                flip(n-2, j, n-2, j+1, n-1, j+1);
            }
            else if(s[n-1][j] == '1'){
                flip(n-1, j, n-1, j+1, n-2, j+1);
            }
        }
        int t = 0, num = 0;
        pii v[4];
        v[t++] = {n-2, m-2}, v[t++] = {n-2, m-1};
        v[t++] = {n-1, m-1}, v[t++] = {n-1, m-2};
        rep(i, 0, t){
            int x = v[i].x, y = v[i].y;
            if(s[x][y] == '1')num++;
        }
        if(num == 1){
            rep(i, 0, t){
                int x = v[i].x, y = v[i].y;
                if(s[x][y] == '1'){
                    int l = (i+1)%4, r = (i+3) % 4, k = (i+2) % 4;
                    solve(x, y, v[l].x, v[l].y, v[r].x, v[r].y, v[k].x, v[k].y);
                }
            }
        }
        else if(num == 2){
            rep(i, 0, t){
                int x = v[i].x, y = v[i].y;
                if(s[x][y] == '1'){
                    int l = (i+1)%4, r = (i+2) % 4, k = (i+3) % 4;
                    int nx = v[l].x, ny = v[l].y;
                    if(s[nx][ny] == '1'){
                        flip(x, y, v[l].x, v[l].y, v[r].x, v[r].y);
                        solve(v[r].x, v[r].y, v[l].x, v[l].y, v[k].x, v[k].y, x, y);
                        break;
                    }
                    nx = v[r].x, ny = v[r].y;
                    if(s[nx][ny] == '1'){
                        flip(x, y, v[l].x, v[l].y, v[r].x, v[r].y);
                        solve(v[l].x, v[l].y, v[r].x, v[r].y, x, y, v[k].x, v[k].y);
                        break;

                    }
                }
            }
        }
        else if(num == 3){
            rep(i, 0, t){
                int x = v[i].x, y = v[i].y;
                if(s[x][y] == '0'){
                    int l = (i+1)%4, r = (i+2) % 4, k = (i+3) % 4;
                    flip(v[l].x, v[l].y, v[r].x, v[r].y, v[k].x, v[k].y);
                    break;
                }

            }
        }
        printf("%d\n", sz);
        rep(i, 0, sz){
            printf("%d %d %d %d %d %d\n", ans[i].x1+1, ans[i].y1+1, ans[i].x2+1, ans[i].y2+1, ans[i].x3+1, ans[i].y3+1);
        }
    }
}


#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define dd double
#define endl "\n"
#define pb push_back
#define all(v) v.begin(),v.end()
#define mp make_pair
#define fi first
#define se second
#define vll vector<ll>
#define pll pair<ll,ll>
#define fo(i,n) for(int i=0;i<n;i++)
#define fo1(i,n) for(int i=1;i<=n;i++)
ll mod=1000000007;
ll n,k,t,m,q,flag=0;
ll power(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
// #include <ext/pb_ds/assoc_container.hpp>
// #include <ext/pb_ds/tree_policy.hpp>
// using namespace __gnu_pbds;
// #define ordered_set tree<int, null_type,less<int>, rb_tree_tag,tree_order_statistics_node_update>
// ordered_set s ; s.order_of_key(a) -- no. of elements strictly less than a
// s.find_by_order(i) -- itertor to ith element (0 indexed)
ll min(ll a,ll b){if(a>b)return b;else return a;}
ll max(ll a,ll b){if(a>b)return a;else return b;}
ll gcd(ll a , ll b){ if(b > a) return gcd(b , a) ; if(b == 0) return a ; return gcd(b , a%b) ;}
int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    #ifdef NOOBxCODER
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    #else
    #define NOOBxCODER 0
    #endif
    cin>>t;
    //t=1;
    while(t--){
        cin>>n>>m;
        char a[n][m];
        fo(i,n)fo(j,m)cin>>a[i][j];
        vector<vll>v;
        for(int i=0;i<n-1;i++){
            for(int j=0;j<m-1;j+=2){

                if(a[i][j]=='0' && a[i][j+1]=='0')continue;
                vll v1;
                v1.pb(i+1);
                v1.pb(j);
                a[i+1][j]='1'+'0'-a[i+1][j];

                if(a[i][j]=='1' && a[i][j+1]=='0'){
                    v1.pb(i);
                    v1.pb(j);
                    v1.pb(i+1);
                    v1.pb(j+1);
                    a[i+1][j+1]='1'+'0'-a[i+1][j+1];
                }
                else if(a[i][j]=='0' && a[i][j+1]=='1'){
                    v1.pb(i);
                    v1.pb(j+1);
                    v1.pb(i+1);
                    v1.pb(j+1);
                    a[i+1][j+1]='1'+'0'-a[i+1][j+1];
                }
                else {
                    v1.pb(i);
                    v1.pb(j);
                    v1.pb(i);
                    v1.pb(j+1);
                }
                a[i][j]='0';a[i][j+1]='0';
                v.pb(v1);
                //cout<<i<<j<<endl;
                //fo(i,n){fo(j,m)cout<<a[i][j]; cout<<endl;}cout<<endl;
            }
        }
        //fo(i,n){fo(j,m)cout<<a[i][j]; cout<<endl;}

        if(n%2==1 && m%2==1){
            if(a[n-1][m-1]=='1'){
                //cout<<444<<endl;
                vll v1;
                v1.pb(n-1);
                v1.pb(m-1);
                v1.pb(n-2);
                v1.pb(m-1);
                v1.pb(n-1);
                v1.pb(m-2);
                v.pb(v1);
                a[n-1][m-1]='0';
                a[n-2][m-1]='1'+'0'-a[n-2][m-1];
                a[n-1][m-2]='1'+'0'-a[n-1][m-2];
            }

        }
            if(m%2==1)for(int i=0;i<n-1;i+=2){
                if(i==n-2){
                    if(a[i][m-1]=='1' && a[i+1][m-1]=='1'){
                    vll v1;
                    v1.pb(i+1);v1.pb(m-2);v1.pb(i+1);v1.pb(m-1);v1.pb(i);v1.pb(m-1);
                    v.pb(v1);
                    a[i+1][m-2]='1'+'0'-a[i+1][m-2];

                }
                else if(a[i][m-1]=='0' && a[i+1][m-1]=='1'){
                    vll v1;
                    v1.pb(i);v1.pb(m-2);v1.pb(i);v1.pb(m-1);v1.pb(i+1);v1.pb(m-1);
                    v.pb(v1);
                    v1.clear();
                    v1.pb(i);v1.pb(m-2);v1.pb(i);v1.pb(m-1);v1.pb(i+1);v1.pb(m-2);
                    v.pb(v1);
                    a[i+1][m-2]='1'+'0'-a[i+1][m-2];
                }
                else if(a[i][m-1]=='1' && a[i+1][m-1]=='0'){
                    vll v1;
                    v1.pb(i);v1.pb(m-2);v1.pb(i+1);v1.pb(m-1);v1.pb(i);v1.pb(m-1);
                    v.pb(v1);
                    v1.clear();
                    v1.pb(i);v1.pb(m-2);v1.pb(i+1);v1.pb(m-1);v1.pb(i+1);v1.pb(m-2);
                    v.pb(v1);
                    a[i+1][m-2]='1'+'0'-a[i+1][m-2];
                }
                    continue;
                }
                if(a[i][m-1]=='1' && a[i+1][m-1]=='1'){
                    vll v1;
                    v1.pb(i);v1.pb(m-2);v1.pb(i+1);v1.pb(m-2);v1.pb(i);v1.pb(m-1);
                    v.pb(v1);
                    v1.clear();
                    v1.pb(i);v1.pb(m-2);v1.pb(i+1);v1.pb(m-2);v1.pb(i+1);v1.pb(m-1);
                    v.pb(v1);
                }
                else if(a[i][m-1]=='0' && a[i+1][m-1]=='1'){
                    vll v1;
                    v1.pb(i);v1.pb(m-2);v1.pb(i+1);v1.pb(m-2);v1.pb(i+1);v1.pb(m-1);
                    v.pb(v1);
                    v1.clear();
                    v1.pb(i);v1.pb(m-1);v1.pb(i+1);v1.pb(m-2);v1.pb(i+1);v1.pb(m-1);
                    v.pb(v1);
                    v1.clear();
                    v1.pb(i);v1.pb(m-2);v1.pb(i);v1.pb(m-1);v1.pb(i+1);v1.pb(m-1);
                    v.pb(v1);
                }
                else if(a[i][m-1]=='1' && a[i+1][m-1]=='0'){
                    vll v1;
                    v1.pb(i);v1.pb(m-2);v1.pb(i+1);v1.pb(m-2);v1.pb(i);v1.pb(m-1);
                    v.pb(v1);
                    v1.clear();
                    v1.pb(i);v1.pb(m-1);v1.pb(i+1);v1.pb(m-2);v1.pb(i+1);v1.pb(m-1);
                    v.pb(v1);
                    v1.clear();
                    v1.pb(i);v1.pb(m-2);v1.pb(i);v1.pb(m-1);v1.pb(i+1);v1.pb(m-1);
                    v.pb(v1);
                }
                //a[i][m-1]=0;  a[i+1][m-1]=0;
            }

            for(int i=0;i<m-1;i+=2){
                if(a[n-1][i]=='1'  && a[n-1][i+1]=='1'){
                    vll v1;
                    v1.pb(n-2);v1.pb(i);v1.pb(n-2);v1.pb(i+1);v1.pb(n-1);v1.pb(i);
                    v.pb(v1);
                    v1.clear();
                    v1.pb(n-2);v1.pb(i);v1.pb(n-2);v1.pb(i+1);v1.pb(n-1);v1.pb(i+1);
                    v.pb(v1);
                }
                else if(a[n-1][i]=='0' && a[n-1][i+1]=='1'){
                    vll v1;
                    v1.pb(n-2);v1.pb(i);v1.pb(n-1);v1.pb(i+1);v1.pb(n-1);v1.pb(i);
                    v.pb(v1);
                    v1.clear();
                    v1.pb(n-2);v1.pb(i);v1.pb(n-2);v1.pb(i+1);v1.pb(n-1);v1.pb(i+1);
                    v.pb(v1);
                    v1.clear();
                    v1.pb(n-1);v1.pb(i);v1.pb(n-2);v1.pb(i+1);v1.pb(n-1);v1.pb(i+1);
                    v.pb(v1);
                }
                else if(a[n-1][i]=='1' && a[n-1][i+1]=='0'){
                    vll v1;
                    v1.pb(n-2);v1.pb(i);v1.pb(n-1);v1.pb(i+1);v1.pb(n-1);v1.pb(i);
                    v.pb(v1);
                    v1.clear();
                    v1.pb(n-2);v1.pb(i);v1.pb(n-2);v1.pb(i+1);v1.pb(n-1);v1.pb(i);
                    v.pb(v1);
                    v1.clear();
                    v1.pb(n-1);v1.pb(i);v1.pb(n-2);v1.pb(i+1);v1.pb(n-1);v1.pb(i+1);
                    v.pb(v1);
                }
            }

            cout<<v.size()<<endl;
            fo(i,v.size()){
                fo(j,6)cout<<v[i][j]+1<<" ";
                cout<<endl;
            }

    }
    cerr << "Time : " << 1000 * ((double)clock()) / (double)CLOCKS_PER_SEC << "ms\n";
    return 0;
}


#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define dd double
#define endl "\n"
#define pb push_back
#define all(v) v.begin(),v.end()
#define mp make_pair
#define fi first
#define se second
#define vll vector<ll>
#define pll pair<ll,ll>
#define fo(i,n) for(int i=0;i<n;i++)
#define fo1(i,n) for(int i=1;i<=n;i++)
ll mod=1000000007;
ll n,k,t,m,q,flag=0;
ll power(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
// #include <ext/pb_ds/assoc_container.hpp>
// #include <ext/pb_ds/tree_policy.hpp>
// using namespace __gnu_pbds;
// #define ordered_set tree<int, null_type,less<int>, rb_tree_tag,tree_order_statistics_node_update>
// ordered_set s ; s.order_of_key(a) -- no. of elements strictly less than a
// s.find_by_order(i) -- itertor to ith element (0 indexed)
ll min(ll a,ll b){if(a>b)return b;else return a;}
ll max(ll a,ll b){if(a>b)return a;else return b;}
ll gcd(ll a , ll b){ if(b > a) return gcd(b , a) ; if(b == 0) return a ; return gcd(b , a%b) ;}
int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    #ifdef NOOBxCODER
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    #else
    #define NOOBxCODER 0
    #endif
    cin>>t;
    //t=1;
    while(t--){
        cin>>n>>m;
        char a[n][m];
        fo(i,n)fo(j,m)cin>>a[i][j];
        vector<vll>v;
        for(int i=0;i<n-1;i++){
            for(int j=0;j<m-1;j+=2){

                if(a[i][j]=='0' && a[i][j+1]=='0')continue;
                vll v1;
                v1.pb(i+1);
                v1.pb(j);
                a[i+1][j]='1'+'0'-a[i+1][j];

                if(a[i][j]=='1' && a[i][j+1]=='0'){
                    v1.pb(i);
                    v1.pb(j);
                    v1.pb(i+1);
                    v1.pb(j+1);
                    a[i+1][j+1]='1'+'0'-a[i+1][j+1];
                }
                else if(a[i][j]=='0' && a[i][j+1]=='1'){
                    v1.pb(i);
                    v1.pb(j+1);
                    v1.pb(i+1);
                    v1.pb(j+1);
                    a[i+1][j+1]='1'+'0'-a[i+1][j+1];
                }
                else {
                    v1.pb(i);
                    v1.pb(j);
                    v1.pb(i);
                    v1.pb(j+1);
                }
                a[i][j]='0';a[i][j+1]='0';
                v.pb(v1);
                //cout<<i<<j<<endl;
                //fo(i,n){fo(j,m)cout<<a[i][j]; cout<<endl;}cout<<endl;
            }
        }
        //fo(i,n){fo(j,m)cout<<a[i][j]; cout<<endl;}

        if(n%2==1 && m%2==1){
            if(a[n-1][m-1]=='1'){
                //cout<<444<<endl;
                vll v1;
                v1.pb(n-1);
                v1.pb(m-1);
                v1.pb(n-2);
                v1.pb(m-1);
                v1.pb(n-1);
                v1.pb(m-2);
                v.pb(v1);
                a[n-1][m-1]='0';
                a[n-2][m-1]='1'+'0'-a[n-2][m-1];
                a[n-1][m-2]='1'+'0'-a[n-1][m-2];
            }

        }

            for(int i=0;i<m-1;i+=2){
                if(a[n-1][i]=='1'  && a[n-1][i+1]=='1'){
                    vll v1;
                    v1.pb(n-2);v1.pb(i);v1.pb(n-2);v1.pb(i+1);v1.pb(n-1);v1.pb(i);
                    v.pb(v1);
                    v1.clear();
                    v1.pb(n-2);v1.pb(i);v1.pb(n-2);v1.pb(i+1);v1.pb(n-1);v1.pb(i+1);
                    v.pb(v1);
                }
                else if(a[n-1][i]=='0' && a[n-1][i+1]=='1'){
                    vll v1;
                    v1.pb(n-2);v1.pb(i);v1.pb(n-1);v1.pb(i+1);v1.pb(n-1);v1.pb(i);
                    v.pb(v1);
                    v1.clear();
                    v1.pb(n-2);v1.pb(i);v1.pb(n-2);v1.pb(i+1);v1.pb(n-1);v1.pb(i+1);
                    v.pb(v1);
                    v1.clear();
                    v1.pb(n-1);v1.pb(i);v1.pb(n-2);v1.pb(i+1);v1.pb(n-1);v1.pb(i+1);
                    v.pb(v1);
                }
                else if(a[n-1][i]=='1' && a[n-1][i+1]=='0'){
                    vll v1;
                    v1.pb(n-2);v1.pb(i);v1.pb(n-1);v1.pb(i+1);v1.pb(n-1);v1.pb(i);
                    v.pb(v1);
                    v1.clear();
                    v1.pb(n-2);v1.pb(i);v1.pb(n-2);v1.pb(i+1);v1.pb(n-1);v1.pb(i);
                    v.pb(v1);
                    v1.clear();
                    v1.pb(n-1);v1.pb(i);v1.pb(n-2);v1.pb(i+1);v1.pb(n-1);v1.pb(i+1);
                    v.pb(v1);
                }
            }
            if(m%2==1)for(int i=0;i<n-1;i+=2){
                if(a[i][m-1]=='1' && a[i+1][m-1]=='1'){
                    vll v1;
                    v1.pb(i);v1.pb(m-2);v1.pb(i+1);v1.pb(m-2);v1.pb(i);v1.pb(m-1);
                    v.pb(v1);
                    v1.clear();
                    v1.pb(i);v1.pb(m-2);v1.pb(i+1);v1.pb(m-2);v1.pb(i+1);v1.pb(m-1);
                    v.pb(v1);
                }
                else if(a[i][m-1]=='0' && a[i+1][m-1]=='1'){
                    vll v1;
                    v1.pb(i);v1.pb(m-2);v1.pb(i+1);v1.pb(m-2);v1.pb(i+1);v1.pb(m-1);
                    v.pb(v1);
                    v1.clear();
                    v1.pb(i);v1.pb(m-1);v1.pb(i+1);v1.pb(m-2);v1.pb(i+1);v1.pb(m-1);
                    v.pb(v1);
                    v1.clear();
                    v1.pb(i);v1.pb(m-2);v1.pb(i);v1.pb(m-1);v1.pb(i+1);v1.pb(m-1);
                    v.pb(v1);
                }
                else if(a[i][m-1]=='1' && a[i+1][m-1]=='0'){
                    vll v1;
                    v1.pb(i);v1.pb(m-2);v1.pb(i+1);v1.pb(m-2);v1.pb(i);v1.pb(m-1);
                    v.pb(v1);
                    v1.clear();
                    v1.pb(i);v1.pb(m-1);v1.pb(i+1);v1.pb(m-2);v1.pb(i+1);v1.pb(m-1);
                    v.pb(v1);
                    v1.clear();
                    v1.pb(i);v1.pb(m-2);v1.pb(i);v1.pb(m-1);v1.pb(i+1);v1.pb(m-1);
                    v.pb(v1);
                }
                //a[i][m-1]=0;  a[i+1][m-1]=0;
            }
            cout<<v.size()<<endl;
            fo(i,v.size()){
                fo(j,6)cout<<v[i][j]+1<<" ";
                cout<<endl;
            }

    }
    cerr << "Time : " << 1000 * ((double)clock()) / (double)CLOCKS_PER_SEC << "ms\n";
    return 0;
}