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

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 *
 * @author aryssoncf
 */
//object Main {
//    @JvmStatic
    fun main(args: Array<String>) {
        val inputStream = System.`in`
        val outputStream: OutputStream = System.out
        val `in` = InputReader(inputStream)
        val out = OutputWriter(outputStream)
        val solver = A2BinaryTableHardVersion()
        val testCount = `in`.next().toInt()
        for (i in 1..testCount) solver.solve(i, `in`, out)
        out.close()
    }

    internal class A2BinaryTableHardVersion {
        var path: Array<Array<IntArray?>>? = null
        fun solve(testNumber: Int, `in`: InputReader, out: OutputWriter) {
            if (path == null) {
                path = init()
            }
            val n = `in`.readInt()
            val m = `in`.readInt()
            val table = `in`.readTable(n, m)
            val res: MutableList<IntArray> = ArrayList()
            var r = 0
            while (r < n) {
                val n2 = if (r + 3 == n) 3 else 2
                var c = 0
                while (c < m) {
                    val m2 = if (c + 3 == m) 3 else 2
                    var start = 0
                    for (i in 0 until n2) {
                        for (j in 0 until m2) {
                            if (table[r + i]!![c + j] == '1') {
                                start = start xor (1 shl i * m2 + j)
                            }
                        }
                    }
                    val p = getPath(start, n2, m2)
                    for (arr in p) {
                        for (i in 0..5) {
                            arr[i] += if (i % 2 == 0) r + 1 else c + 1
                        }
                    }
                    res.addAll(p)
                    c += m2
                }
                r += n2
            }
            out.printLine(res.size)
            for (arr in res) {
                out.printLine(arr)
            }
        }

        fun getPath(u: Int, n: Int, m: Int): List<IntArray> {
            var u = u
            val previous = path!![n - 2][m - 2]
            val res: MutableList<IntArray> = ArrayList()
            val list: EzIntList = EzIntArrayList(6)
            while (u != 0) {
                val v = previous!![u]
                val arr = IntArray(6)
                for (i in 0 until n * m) {
                    if (u xor v and (1 shl i) > 0) {
                        list.add(i / m)
                        list.add(i % m)
                    }
                }
                res.add(list.toArray())
                list.clear()
                u = v
            }
            return res
        }

        fun init(): Array<Array<IntArray?>> {
            val res = Array(2) { arrayOfNulls<IntArray>(2) }
            for (i in 0..1) {
                for (j in 0..1) {
                    res[i][j] = getPath(i + 2, j + 2)
                }
            }
            return res
        }

        fun getPath(n: Int, m: Int): IntArray {
            val graph = Graph(1 shl n * m)
            for (u in 0 until (1 shl n * m)) {
                for (r0 in 0 until n - 1) {
                    for (c0 in 0 until m - 1) {
                        var aux = u
                        for (r in r0 until r0 + 2) {
                            for (c in c0 until c0 + 2) {
                                aux = aux xor (1 shl r * m + c)
                            }
                        }
                        for (r in r0 until r0 + 2) {
                            for (c in c0 until c0 + 2) {
                                val v = aux xor (1 shl r * m + c)
                                graph.addSimpleEdge(u, v)
                            }
                        }
                    }
                }
            }
            val dijkstra = ShortestDistance.bfs(graph, 0)
            if (ArrayUtils.maxElement(dijkstra.first) > n * m) {
                throw UnknownError()
            }
            return Arrays.stream(dijkstra.second).map { i: Int -> if (i == -1) -1 else graph.source(i) }.toArray()
        }
    }

    internal interface EzIntDeque : EzIntQueue, EzIntStack {
        override fun size(): Int
        abstract override val isEmpty: Boolean
        override operator fun iterator(): EzIntIterator
        override fun add(element: Int): Boolean
        override fun equals(`object`: Any?): Boolean
        override fun hashCode(): Int
        override fun toString(): String
        override fun removeFirst(): Int
    }

    internal class Graph @JvmOverloads constructor(protected var vertexCount: Int, edgeCapacity: Int = vertexCount) {
        protected var edgeCount = 0
        private var firstOutbound: IntArray? = null
        private val firstInbound: IntArray? = null
        private var edges: Array<Edge?>? = null
        private var nextInbound: IntArray? = null
        private var nextOutbound: IntArray? = null
        private var from: IntArray? = null
        private var to: IntArray? = null
        private var weight: LongArray? = null
        var capacity: LongArray? = null
        private var reverseEdge: IntArray? = null
        private var flags: IntArray? = null
        fun addEdge(fromID: Int, toID: Int, weight: Long, capacity: Long, reverseEdge: Int): Int {
            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
            }
            from!![edgeCount] = fromID
            to!![edgeCount] = toID
            if (capacity != 0L) {
                if (this.capacity == null) {
                    this.capacity = LongArray(from!!.size)
                }
                this.capacity!![edgeCount] = capacity
            }
            if (weight != 0L) {
                if (this.weight == null) {
                    this.weight = LongArray(from!!.size)
                }
                this.weight!![edgeCount] = weight
            }
            if (reverseEdge != -1) {
                if (this.reverseEdge == null) {
                    this.reverseEdge = IntArray(from!!.size)
                    Arrays.fill(this.reverseEdge, 0, edgeCount, -1)
                }
                this.reverseEdge!![edgeCount] = reverseEdge
            }
            if (edges != null) {
                edges!![edgeCount] = createEdge(edgeCount)
            }
            return edgeCount++
        }

        protected fun createEdge(id: Int): GraphEdge {
            return GraphEdge(id)
        }

        fun addFlowWeightedEdge(from: Int, to: Int, weight: Long, capacity: Long): Int {
            return if (capacity == 0L) {
                addEdge(from, to, weight, 0, -1)
            } else {
                val lastEdgeCount = edgeCount
                addEdge(to, from, -weight, 0, lastEdgeCount + entriesPerEdge())
                addEdge(from, to, weight, capacity, lastEdgeCount)
            }
        }

        protected fun entriesPerEdge(): Int {
            return 1
        }

        fun addWeightedEdge(from: Int, to: Int, weight: Long): Int {
            return addFlowWeightedEdge(from, to, weight, 0)
        }

        fun addSimpleEdge(from: Int, to: Int): Int {
            return addWeightedEdge(from, to, 0)
        }

        fun vertexCount(): Int {
            return vertexCount
        }

        fun firstOutbound(vertex: Int): Int {
            var id = firstOutbound!![vertex]
            while (id != -1 && isRemoved(id)) {
                id = nextOutbound!![id]
            }
            return id
        }

        fun nextOutbound(id: Int): Int {
            var id = id
            id = nextOutbound!![id]
            while (id != -1 && isRemoved(id)) {
                id = nextOutbound!![id]
            }
            return id
        }

        fun source(id: Int): Int {
            return from!![id]
        }

        fun destination(id: Int): Int {
            return to!![id]
        }

        fun flag(id: Int, bit: Int): Boolean {
            return flags!![id] shr bit and 1 != 0
        }

        fun isRemoved(id: Int): Boolean {
            return flag(id, REMOVED_BIT)
        }

        protected fun ensureEdgeCapacity(size: Int) {
            if (from!!.size < size) {
                val newSize = Math.max(size, 2 * from!!.size)
                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 fun resize(array: IntArray?, size: Int): IntArray {
            val newArray = IntArray(size)
            System.arraycopy(array, 0, newArray, 0, array!!.size)
            return newArray
        }

        private fun resize(array: LongArray, size: Int): LongArray {
            val newArray = LongArray(size)
            System.arraycopy(array, 0, newArray, 0, array.size)
            return newArray
        }

        private fun resize(array: Array<Edge?>, size: Int): Array<Edge?> {
            val newArray = arrayOfNulls<Edge>(size)
            System.arraycopy(array, 0, newArray, 0, array.size)
            return newArray
        }

        protected inner class GraphEdge(protected var id: Int) : Edge
        companion object {
            const val REMOVED_BIT = 0
        }

        init {
            firstOutbound = IntArray(vertexCount)
            Arrays.fill(firstOutbound, -1)
            from = IntArray(edgeCapacity)
            to = IntArray(edgeCapacity)
            nextOutbound = IntArray(edgeCapacity)
            flags = IntArray(edgeCapacity)
        }
    }

    internal interface EzIntIterator {
        operator fun hasNext(): Boolean
        operator fun next(): Int
    }

    internal class OutputWriter {
        private val writer: PrintWriter

        constructor(outputStream: OutputStream?) {
            writer = PrintWriter(BufferedWriter(OutputStreamWriter(outputStream)))
        }

        constructor(writer: Writer?) {
            this.writer = PrintWriter(writer)
        }

        fun print(array: IntArray) {
            for (i in array.indices) {
                if (i != 0) {
                    writer.print(' ')
                }
                writer.print(array[i])
            }
        }

        fun printLine(array: IntArray) {
            print(array)
            writer.println()
        }

        fun close() {
            writer.close()
        }

        fun printLine(i: Int) {
            writer.println(i)
        }
    }

    internal object ShortestDistance {
        fun bfs(graph: Graph, source: Int): Pair<IntArray, IntArray> {
            val vertexCount = graph.vertexCount()
            val distance = IntArray(vertexCount)
            val last = IntArray(vertexCount)
            Arrays.fill(distance, Int.MAX_VALUE)
            Arrays.fill(last, -1)
            distance[source] = 0
            val queue: EzIntQueue = EzIntArrayDeque()
            queue.add(source)
            while (!queue.isEmpty) {
                val current = queue.removeFirst()
                var id = graph.firstOutbound(current)
                while (id != -1) {
                    val next = graph.destination(id)
                    val 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)
        }
    }

    internal interface EzIntQueue : EzIntCollection {
        override fun size(): Int
        val isEmpty: Boolean
        override operator fun iterator(): EzIntIterator
        fun add(element: Int): Boolean
        override fun equals(`object`: Any?): Boolean
        override fun hashCode(): Int
        override fun toString(): String
        fun removeFirst(): Int
    }

    internal interface EzIntCollection {
        fun size(): Int
        operator fun iterator(): EzIntIterator
        override fun equals(`object`: Any?): Boolean
        override fun hashCode(): Int
        override fun toString(): String
    }

    internal object ArrayUtils {
        fun maxElement(array: IntArray?): Int {
            return Arrays.stream(array).max().asInt
        }
    }

    internal class InputReader(private val stream: InputStream) {
        private val buf = ByteArray(1024)
        private var curChar = 0
        private var numChars = 0
        private val filter: SpaceCharFilter? = null
        fun readCharArray(size: Int): CharArray {
            val array = CharArray(size)
            for (i in 0 until size) {
                array[i] = readCharacter()
            }
            return array
        }

        fun readTable(rowCount: Int, columnCount: Int): Array<CharArray?> {
            val table = arrayOfNulls<CharArray>(rowCount)
            for (i in 0 until rowCount) {
                table[i] = readCharArray(columnCount)
            }
            return table
        }

        fun read(): Int {
            if (numChars == -1) {
                throw InputMismatchException()
            }
            if (curChar >= numChars) {
                curChar = 0
                numChars = try {
                    stream.read(buf)
                } catch (e: IOException) {
                    throw InputMismatchException()
                }
                if (numChars <= 0) {
                    return -1
                }
            }
            return buf[curChar++].toInt()
        }

        fun readInt(): Int {
            var c = read()
            while (isSpaceChar(c)) {
                c = read()
            }
            var sgn = 1
            if (c == '-'.toInt()) {
                sgn = -1
                c = read()
            }
            var res = 0
            do {
                if (c < '0'.toInt() || c > '9'.toInt()) {
                    throw InputMismatchException()
                }
                res *= 10
                res += c - '0'.toInt()
                c = read()
            } while (!isSpaceChar(c))
            return res * sgn
        }

        fun readString(): String {
            var c = read()
            while (isSpaceChar(c)) {
                c = read()
            }
            val res = StringBuilder()
            do {
                if (Character.isValidCodePoint(c)) {
                    res.appendCodePoint(c)
                }
                c = read()
            } while (!isSpaceChar(c))
            return res.toString()
        }

        fun isSpaceChar(c: Int): Boolean {
            return filter?.isSpaceChar(c) ?: isWhitespace(c)
        }

        fun readCharacter(): Char {
            var c = read()
            while (isSpaceChar(c)) {
                c = read()
            }
            return c.toChar()
        }

        operator fun next(): String {
            return readString()
        }

        interface SpaceCharFilter {
            fun isSpaceChar(ch: Int): Boolean
        }

        companion object {
            fun isWhitespace(c: Int): Boolean {
                return c == ' '.toInt() || c == '\n'.toInt() || c == '\r'.toInt() || c == '\t'.toInt() || c == -1
            }
        }
    }

    internal object PrimitiveHashCalculator {
        fun getHash(x: Int): Int {
            return x
        }
    }

    internal class EzIntArrayList : EzIntList, EzIntStack {
        private var array: IntArray
        private var size: Int

        @JvmOverloads
        constructor(capacity: Int = DEFAULT_CAPACITY) {
            require(capacity >= 0) { "Capacity must be non-negative" }
            array = IntArray(capacity)
            size = 0
        }

        constructor(collection: EzIntCollection) {
            size = collection.size()
            array = IntArray(size)
            var i = 0
            val iterator = collection.iterator()
            while (iterator.hasNext()) {
                array[i++] = iterator.next()
            }
        }

        constructor(srcArray: IntArray) {
            size = srcArray.size
            array = IntArray(size)
            System.arraycopy(srcArray, 0, array, 0, size)
        }

        constructor(javaCollection: Collection<Int>) {
            size = javaCollection.size
            array = IntArray(size)
            var i = 0
            for (element in javaCollection) {
                array[i++] = element
            }
        }

        override fun size(): Int {
            return size
        }

        override fun iterator(): EzIntIterator {
            return EzIntArrayListIterator()
        }

        override fun toArray(): IntArray {
            val result = IntArray(size)
            System.arraycopy(array, 0, result, 0, size)
            return result
        }

        override fun add(element: Int): Boolean {
            if (size == array.size) {
                enlarge()
            }
            array[size++] = element
            return true
        }

        override fun clear() {
            size = 0
        }

        private fun enlarge() {
            val newSize = Math.max(size + 1, (size * ENLARGE_SCALE).toInt())
            val newArray = IntArray(newSize)
            System.arraycopy(array, 0, newArray, 0, size)
            array = newArray
        }

        override fun equals(o: Any?): Boolean {
            if (this === o) return true
            if (o == null || javaClass != o.javaClass) return false
            val that = o as EzIntArrayList
            if (size != that.size) {
                return false
            }
            for (i in 0 until size) {
                if (array[i] != that.array[i]) {
                    return false
                }
            }
            return true
        }

        override fun hashCode(): Int {
            var hash = HASHCODE_INITIAL_VALUE
            for (i in 0 until size) {
                hash = (hash xor PrimitiveHashCalculator.getHash(array[i])) * HASHCODE_MULTIPLIER
            }
            return hash
        }

        override fun toString(): String {
            val sb = StringBuilder()
            sb.append('[')
            for (i in 0 until size) {
                sb.append(array[i])
                if (i < size - 1) {
                    sb.append(", ")
                }
            }
            sb.append(']')
            return sb.toString()
        }

        private inner class EzIntArrayListIterator : EzIntIterator {
            private var curIndex = 0
            override fun hasNext(): Boolean {
                return curIndex < size
            }

            override fun next(): Int {
                if (curIndex == size) {
                    throw NoSuchElementException("Iterator doesn't have more elements")
                }
                return array[curIndex++]
            }
        }

        companion object {
            private const val DEFAULT_CAPACITY = 10
            private const val ENLARGE_SCALE = 2.0
            private const val HASHCODE_INITIAL_VALUE = -0x7ee3623b
            private const val HASHCODE_MULTIPLIER = 0x01000193
        }
    }

    internal interface Edge
    internal interface EzIntList : EzIntCollection {
        override fun size(): Int
        override fun iterator(): EzIntIterator
        fun toArray(): IntArray
        fun add(element: Int): Boolean
        fun clear()
        override fun equals(`object`: Any?): Boolean
        override fun hashCode(): Int
        override fun toString(): String
    }

    internal class Pair<U, V> protected constructor(first: U, second: V) {
        val first: U?
        val second: V?
        override fun equals(o: Any?): Boolean {
            if (this === o) {
                return true
            }
            if (o == null || javaClass != o.javaClass) {
                return false
            }
            val pair = o as Pair<*, *>
            return first == pair.first &&
                    second == pair.second
        }

        override fun hashCode(): Int {
            var result = first?.hashCode() ?: 0
            result = 31 * result + (second?.hashCode() ?: 0)
            return result
        }

        override fun toString(): String {
            return "($first,$second)"
        }

        companion object {
            fun <U, V> makePair(first: U, second: V): Pair<U, V> {
                return Pair(first, second)
            }
        }

        init {
            this.first = first
            this.second = second
        }
    }

    internal interface EzIntStack : EzIntCollection {
        override fun size(): Int
        override fun iterator(): EzIntIterator
        override fun equals(`object`: Any?): Boolean
        override fun hashCode(): Int
        override fun toString(): String
    }

    internal class EzIntArrayDeque @JvmOverloads constructor(capacity: Int = DEFAULT_CAPACITY) : EzIntDeque, EzIntList {
        private var deque: IntArray
        private var size: Int
        private var head: Int
        private var tail: Int
        private var mask: Int

        constructor(collection: EzIntCollection) : this(collection.size() + 1) {
            val iterator = collection.iterator()
            while (iterator.hasNext()) {
                deque[tail++] = iterator.next()
            }
            size = collection.size()
        }

        constructor(srcArray: IntArray) : this(srcArray.size + 1) {
            System.arraycopy(srcArray, 0, deque, 0, srcArray.size)
            tail = srcArray.size
            size = srcArray.size
        }

        constructor(javaCollection: Collection<Int>) : this(javaCollection.size + 1) {
            for (element in javaCollection) {
                deque[tail++] = element
            }
            size = javaCollection.size
        }

        override fun size(): Int {
            return size
        }

        override val isEmpty: Boolean
            get() = size == 0

        override fun iterator(): EzIntIterator {
            return EzIntArrayDequeIterator()
        }

        override fun toArray(): IntArray {
            val result = IntArray(size)
            var i = head
            var j = 0
            while (i != tail) {
                result[j++] = deque[i]
                i = i + 1 and mask
            }
            return result
        }

        override fun add(element: Int): Boolean {
            deque[tail] = element
            tail = tail + 1 and mask
            size++
            if (size == deque.size) {
                enlarge()
            }
            return true
        }

        override fun clear() {
            size = 0
            head = 0
            tail = 0
        }

        override fun removeFirst(): Int {
            if (size == 0) {
                throw NoSuchElementException("Trying to call removeFirst() on empty ArrayDeque")
            }
            val removedElement = deque[head]
            size--
            head = head + 1 and mask
            return removedElement
        }

        private fun enlarge() {
            val newSize = size shl 1
            val newArray = IntArray(newSize)
            System.arraycopy(deque, head, newArray, 0, deque.size - head)
            System.arraycopy(deque, 0, newArray, deque.size - tail, tail)
            deque = newArray
            head = 0
            tail = size
            mask = deque.size - 1
        }

        override fun equals(o: Any?): Boolean {
            if (this === o) return true
            if (o == null || javaClass != o.javaClass) return false
            val that = o as EzIntArrayDeque
            if (size != that.size) {
                return false
            }
            var i = head
            var j = that.head
            while (i != tail) {
                if (deque[i] != that.deque[j]) {
                    return false
                }
                i = i + 1 and mask
                j = j + 1 and that.mask
            }
            return true
        }

        override fun hashCode(): Int {
            var hash = HASHCODE_INITIAL_VALUE
            var i = head
            while (i != tail) {
                hash = (hash xor PrimitiveHashCalculator.getHash(deque[i])) * HASHCODE_MULTIPLIER
                i = i + 1 and mask
            }
            return hash
        }

        override fun toString(): String {
            val sb = StringBuilder()
            sb.append('[')
            var i = head
            while (i != tail) {
                if (i != head) {
                    sb.append(", ")
                }
                sb.append(deque[i])
                i = i + 1 and mask
            }
            sb.append(']')
            return sb.toString()
        }

        private inner class EzIntArrayDequeIterator : EzIntIterator {
            private var curIndex = head
            override fun hasNext(): Boolean {
                return curIndex != tail
            }

            override fun next(): Int {
                if (curIndex == tail) {
                    throw NoSuchElementException("Iterator doesn't have more elements")
                }
                val result = deque[curIndex]
                curIndex = curIndex + 1 and mask
                return result
            }
        }

        companion object {
            private const val DEFAULT_CAPACITY = 8
            private const val HASHCODE_INITIAL_VALUE = -0x7ee3623b
            private const val HASHCODE_MULTIPLIER = 0x01000193
        }

        init {
            var capacity = capacity
            require(capacity >= 0) { "Capacity must be non-negative" }
            if (capacity < 1) {
                capacity = 1
            }
            if (capacity and capacity - 1 != 0) {
                capacity = Integer.highestOneBit(capacity) shl 1
            }
            // Capacity is a power of 2 now
            deque = IntArray(capacity)
            size = 0
            head = 0
            tail = 0
            mask = deque.size - 1
        }
    }
//}


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

/*
polyakoff
*/

public class Main {

    static FastReader in;
    static PrintWriter out;
    static Random rand = new Random();
    static final int oo = (int) 1e9 + 10;
    static final long OO = (long) 2e18 + 20;
    static final int MOD = (int) 1e9 + 7;

    static class Pair {
        int i, j;

        Pair(int i, int j) {
            this.i = i;
            this.j = j;
        }
    }

    static void case1(int i, int j, int[][] a, ArrayList<Pair> ans) {
        int c = 0;
        for (int i2 = i; i2 <= i + 1; i2++) {
            for (int j2 = j; j2 <= j + 1; j2++) {
                if (a[i2][j2] == 1) {
                    a[i2][j2] ^= 1;
                    ans.add(new Pair(i2, j2));
                } else if (c < 2) {
                    a[i2][j2] ^= 1;
                    ans.add(new Pair(i2, j2));
                    c++;
                }
            }
        }
        case2(i, j, a, ans);
    }

    static void case2(int i, int j, int[][] a, ArrayList<Pair> ans) {
        int c = 0;
        for (int i2 = i; i2 <= i + 1; i2++) {
            for (int j2 = j; j2 <= j + 1; j2++) {
                if (a[i2][j2] == 0) {
                    a[i2][j2] ^= 1;
                    ans.add(new Pair(i2, j2));
                } else if (c < 1) {
                    a[i2][j2] ^= 1;
                    ans.add(new Pair(i2, j2));
                    c++;
                }
            }
        }
        case3(i, j, a, ans);
    }

    static void case3(int i, int j, int[][] a, ArrayList<Pair> ans) {
        for (int i2 = i; i2 <= i + 1; i2++) {
            for (int j2 = j; j2 <= j + 1; j2++) {
                if (a[i2][j2] == 1) {
                    a[i2][j2] ^= 1;
                    ans.add(new Pair(i2, j2));
                }
            }
        }
    }

    static void case4(int i, int j, int[][] a, ArrayList<Pair> ans) {
        a[i][j] = a[i][j + 1] = a[i + 1][j] = 0;
        ans.add(new Pair(i, j));
        ans.add(new Pair(i, j + 1));
        ans.add(new Pair(i + 1, j));
        case1(i, j, a, ans);
    }

    static void process(int i, int j, int[][] a, ArrayList<Pair> ans) {
        int sum = a[i][j] + a[i][j + 1] + a[i + 1][j] + a[i + 1][j + 1];
        if (sum == 1)
            case1(i, j, a, ans);
        else if (sum == 2)
            case2(i, j, a, ans);
        else if (sum == 3)
            case3(i, j, a, ans);
        else if (sum == 4)
            case4(i, j, a, ans);
    }

    static void solve() {
        int n = in.nextInt();
        int m = in.nextInt();

        int[][] a = new int[n][m];
        for (int i = 0; i < n; i++) {
            String s = in.next();
            for (int j = 0; j < m; j++) {
                a[i][j] = s.charAt(j) - '0';
            }
        }

        ArrayList<Pair> ans = new ArrayList<>();
        if (n % 2 == 1) {
            for (int j = 0; j + 1 < m; j += 2) {
                if (a[n - 1][j] == 1 && a[n - 1][j + 1] == 1) {
                    a[n - 1][j] ^= 1;
                    ans.add(new Pair(n - 1, j));
                    a[n - 1][j + 1] ^= 1;
                    ans.add(new Pair(n - 1, j + 1));
                    a[n - 2][j] ^= 1;
                    ans.add(new Pair(n - 2, j));
                } else if (a[n - 1][j] == 1) {
                    a[n - 1][j] ^= 1;
                    ans.add(new Pair(n - 1, j));
                    a[n - 2][j] ^= 1;
                    ans.add(new Pair(n - 2, j));
                    a[n - 2][j + 1] ^= 1;
                    ans.add(new Pair(n - 2, j + 1));
                } else if (a[n - 1][j + 1] == 1) {
                    a[n - 1][j + 1] ^= 1;
                    ans.add(new Pair(n - 1, j + 1));
                    a[n - 2][j] ^= 1;
                    ans.add(new Pair(n - 2, j));
                    a[n - 2][j + 1] ^= 1;
                    ans.add(new Pair(n - 2, j + 1));
                }
            }
        }
        if (m % 2 == 1) {
            for (int i = 0; i + 1 < n; i += 2) {
                if (a[i][m - 1] == 1 && a[i + 1][m - 1] == 1) {
                    a[i][m - 1] ^= 1;
                    ans.add(new Pair(i, m - 1));
                    a[i + 1][m - 1] ^= 1;
                    ans.add(new Pair(i + 1, m - 1));
                    a[i][m - 2] ^= 1;
                    ans.add(new Pair(i, m - 2));
                } else if (a[i][m - 1] == 1) {
                    a[i][m - 1] ^= 1;
                    ans.add(new Pair(i, m - 1));
                    a[i][m - 2] ^= 1;
                    ans.add(new Pair(i, m - 2));
                    a[i + 1][m - 2] ^= 1;
                    ans.add(new Pair(i + 1, m - 2));
                } else if (a[i + 1][m - 1] == 1) {
                    a[i + 1][m - 1] ^= 1;
                    ans.add(new Pair(i + 1, m - 1));
                    a[i][m - 2] ^= 1;
                    ans.add(new Pair(i, m - 2));
                    a[i + 1][m - 2] ^= 1;
                    ans.add(new Pair(i + 1, m - 2));
                }
            }
        }
        if (n % 2 == 1 && m % 2 == 1 && a[n - 1][m - 1] == 1) {
            a[n - 2][m - 2] ^= 1;
            ans.add(new Pair(n - 2, m - 2));
            a[n - 1][m - 2] ^= 1;
            ans.add(new Pair(n - 1, m - 2));
            a[n - 2][m - 1] ^= 1;
            ans.add(new Pair(n - 2, m - 1));
            a[n - 1][m - 1] ^= 1;
            ans.add(new Pair(n - 1, m - 1));
            a[n - 1][m - 2] ^= 1;
            ans.add(new Pair(n - 1, m - 2));
            a[n - 2][m - 1] ^= 1;
            ans.add(new Pair(n - 2, m - 1));
        }
        for (int i = 0; i + 1 < n; i += 2) {
            for (int j = 0; j + 1 < m; j += 2) {
                process(i, j, a, ans);
            }
        }

        out.println(ans.size() / 3);
        for (int i = 0; i < ans.size(); i += 3) {
            out.print((ans.get(i).i + 1) + " " + (ans.get(i).j + 1) + " ");
            out.print((ans.get(i + 1).i + 1) + " " + (ans.get(i + 1).j + 1) + " ");
            out.println((ans.get(i + 2).i + 1) + " " + (ans.get(i + 2).j + 1));
        }

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

    }

    public static void main(String[] args) {
        in = new FastReader();
        out = new PrintWriter(System.out);
//        fileInputOutput();

        int T = 1;
        T = in.nextInt();
        while (T-- > 0)
            solve();

        out.flush();
        out.close();
    }

    static void fileInputOutput() {
        try {
            in = new FastReader("input.txt");
            out = new PrintWriter(new FileOutputStream("output.txt"));
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    static void runInThread() {
        Thread thread = new Thread(null, () -> {
            int T = 1;
//            T = in.nextInt();
            while (T-- > 0)
                solve();
        }, "thread1", 1 << 28);
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

        FastReader() {
            this(System.in);
        }
        FastReader(String file) throws FileNotFoundException {
            this(new FileInputStream(file));
        }
        FastReader(InputStream is) {
            br = new BufferedReader(new InputStreamReader(is));
        }

        int nextInt() {
            return Integer.parseInt(next());
        }
        long nextLong() {
            return Long.parseLong(next());
        }
        double nextDouble() {
            return Double.parseDouble(next());
        }
        String next() {
            while (st == null || !st.hasMoreTokens()) {
                st = new StringTokenizer(nextLine());
            }
            return st.nextToken();
        }
        String nextLine() {
            String line;
            try {
                line = br.readLine();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            return line;
        }
    }
}


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

public class Main {
    static PrintWriter out;
    static Reader in;
    public static void main(String[] args) throws IOException {
        //in = new Reader(new FileInputStream(args[0])); // for test.
        input_output();
        Main solver = new Main();
        solver.solve();
        out.close(); // remove when testing.
        out.flush(); // remove when testing.
    }

    static int INF = (int)1e9;
    static int maxn = (int)2e5+5;
    static int Mod = (int)1e9+7;
    static int n, m, q, t, k;

    void solve() throws IOException{
    	t = in.nextInt();

    	while (t --> 0) {
    		n = in.nextInt();
    		m = in.nextInt();
    		int[][] arr = new int[n][m];
    		String s;
    		for (int i = 0; i < n; i++) {
    			s = in.next();
    			for (int j = 0; j < m; j++) {
    				arr[i][j] = s.charAt(j)-'0';
    			}
    		}
    		ArrayList<Integer> oper = new ArrayList<Integer>();

    		int x = 0;
    		for (int i = 0; i < n-2; i++) {
    			for (int j = 0; j < m; j++) {
    				if (j == m-1) x = j-1;
    				else x = j;
    				if (arr[i][j] == 1) {
    					oper.add(i);
    					oper.add(j);
    					oper.add(i+1);
    					oper.add(x);
    					oper.add(i+1);
    					oper.add(x+1);
    				}
    			}
    		}

    		for (int j = 0, i = n-2; j < m-3; j++) {
    			if (arr[i][j] == 1) {
    				oper.add(i);
    				oper.add(j);
    				arr[i][j]^=1;
    				oper.add(i);
    				oper.add(j+1);
    				arr[i][j+1]^=1;
    				oper.add(i+1);
    				oper.add(j+1);
    				arr[i+1][j+1]^=1;
    			}
    		}

    		List<Integer> bad = new ArrayList<Integer>();
    		for (int i = n-2; i < n; i++) {
    			for (int j = m-2; j < m; j++) {
    				if (arr[i][j] == 1) {
    					bad.add(i);
    					bad.add(j);
    				}
    			}
    		}

    		if (bad.size()/2 == 3) {
    			for (int i = 0; i < bad.size(); i++) {
    				oper.add(bad.get(i));
    			}
    		}
    		if (bad.size()/2 == 4) {
    			int atmp = bad.get(0),
    				btmp = bad.get(1);

    			for (int i = 2; i < bad.size(); i++) {
    				oper.add(bad.get(i));
    			}
    			bad.clear();
    			bad.add(atmp);
    			bad.add(btmp);
    		}

    		if (bad.size()/2 == 1) {
    			List<Integer> good = new ArrayList<Integer>();
    			for (int i = n-2; i < n; i++) {
    				for (int j = m-2; j < m; j++) {
    					boolean baad = false;
    					for (int z = 0; z < bad.size(); z+=2) {
    						if (bad.get(z) == i && bad.get(z+1) == j) baad = true;
    					}
    					if (!baad) {
    						good.add(i);
    						good.add(j);
    					}
    				}
    			}

    			oper.add(bad.get(0));
    			oper.add(bad.get(1));
    			oper.add(good.get(0));
    			oper.add(good.get(1));
    			oper.add(good.get(2));
    			oper.add(good.get(3));
    			bad.clear();
    			for (int i = 0; i < 4; i++) bad.add(good.get(i));
    		}
    		if (bad.size()/2 == 2) {
    			List<Integer> good = new ArrayList<Integer>();
    			for (int i = n-2; i < n; i++) {
    				for (int j = m-2; j < m; j++) {
    					boolean baad = false;
    					for (int z = 0; z < bad.size(); z+=2) {
    						if (bad.get(z) == i && bad.get(z+1) == j) baad = true;
    					}
    					if (!baad) {
    						good.add(i);
    						good.add(j);
    					}
    				}
    			}
    			oper.add(bad.get(0));
    			oper.add(bad.get(1));
    			oper.add(good.get(0));
    			oper.add(good.get(1));
    			oper.add(good.get(2));
    			oper.add(good.get(3));

    			oper.add(bad.get(2));
    			oper.add(bad.get(3));
    			oper.add(good.get(0));
    			oper.add(good.get(1));
    			oper.add(good.get(2));
    			oper.add(good.get(3));
    		}

    		out.println(oper.size()/6);
    		for (int i = 0; i < oper.size(); i+=6) {
    			out.print((oper.get(i)+1)+" ");
    			out.print((oper.get(i+1)+1)+" ");
    			out.print((oper.get(i+2)+1)+" ");
    			out.print((oper.get(i+3)+1)+" ");
    			out.print((oper.get(i+4)+1)+" ");
    			out.println((oper.get(i+5)+1));
    		}

    	}

   	}

    //<>

    static class Reader {

        private InputStream mIs;
        private byte[] buf = new byte[1024];
        private int curChar;
        private int numChars;

        public Reader() {
            this(System.in);
        }

        public Reader(InputStream is) {
            mIs = is;
        }

        public int read() {
            if (numChars == -1) {
                throw new InputMismatchException();

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

        public String nextLine() {
            int c = read();
            while (isSpaceChar(c)) {
                c = read();
            }
            StringBuilder res = new StringBuilder();
            do {
                res.appendCodePoint(c);
                c = read();
            } while (!isEndOfLine(c));
            return res.toString();
        }

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

        double nextDouble()
        {
            return Double.parseDouble(next());
        }

        public long nextLong() {
            int c = read();
            while (isSpaceChar(c)) {
                c = read();
            }
            int sgn = 1;
            if (c == '-') {
                sgn = -1;
                c = read();
            }
            long 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 int nextInt() {
            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 boolean isSpaceChar(int c) {
            return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
        }

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

    }
    static void input_output() throws IOException {
        File f = new File("in.txt");
		in = new Reader(new FileInputStream("in.txt"));
        f = new File("out.txt");
        out = new PrintWriter(new File("out.txt"));
    }
}


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

public class Main {
    static PrintWriter out;
    static Reader in;
    public static void main(String[] args) throws IOException {
        //in = new Reader(new FileInputStream(args[0])); // for test.
        input_output();
        Main solver = new Main();
        solver.solve();
        out.close(); // remove when testing.
        out.flush(); // remove when testing.
    }

    static int INF = (int)1e9;
    static int maxn = (int)2e5+5;
    static int Mod = (int)1e9+7;
    static int n, m, q, t, k;

    void solve() throws IOException{
    	t = in.nextInt();

    	while (t --> 0) {
    		n = in.nextInt();
    		m = in.nextInt();
    		int[][] arr = new int[n][m];
    		String s;
    		for (int i = 0; i < n; i++) {
    			s = in.next();
    			for (int j = 0; j < m; j++) {
    				arr[i][j] = s.charAt(j)-'0';
    			}
    		}
    		ArrayList<Integer> oper = new ArrayList<Integer>();

    		int x = 0;
    		for (int i = 0; i < n-2; i++) {
    			for (int j = 0; j < m; j++) {
    				if (j == m-1) x = j-1;
    				else x = j;
    				if (arr[i][j] == 1) {
    					oper.add(i);
    					oper.add(j);
    					arr[i][j] ^=1;
    					oper.add(i+1);
    					oper.add(x);
    					arr[i+1][x] ^=1;
    					oper.add(i+1);
    					oper.add(x+1);
    					arr[i+1][x+1] ^=1;
    				}
    			}
    		}

    		int cnt = 0;
    		for (int j = 0, i = n-2; j < m-2; j++) {
    			if (arr[i][j] == 1 || arr[i+1][j] == 1) {
    				cnt = 0;
    				if (arr[i][j] == 1) {
    					cnt++;
						oper.add(i);
						oper.add(j);
						arr[i][j]^=1;
    				}
    				if (arr[i+1][j] == 1) {
    					cnt++;
    					oper.add(i+1);
    					oper.add(j);
    					arr[i+1][j]^=1;
    				}
    				if(cnt == 1) {
	    				oper.add(i);
	    				oper.add(j+1);
	    				arr[i][j+1]^=1;
	    				oper.add(i+1);
	    				oper.add(j+1);
	    				arr[i+1][j+1]^=1;
	    			} else if (cnt == 2) {
	    				oper.add(i);
	    				oper.add(j+1);
	    				arr[i][j+1]^=1;
	    			}
    			}
    		}

    		List<Integer> bad = new ArrayList<Integer>();
    		for (int i = n-2; i < n; i++) {
    			for (int j = m-2; j < m; j++) {
    				if (arr[i][j] == 1) {
    					bad.add(i);
    					bad.add(j);
    				}
    			}
    		}

    		if (bad.size()/2 == 3) {
    			for (int i = 0; i < bad.size(); i+=2) {
    				oper.add(bad.get(i));
    				oper.add(bad.get(i+1));
    				arr[bad.get(i)][bad.get(i+1)]^=1;
    			}
    		}
    		if (bad.size()/2 == 4) {
    			int atmp = bad.get(0),
    				btmp = bad.get(1);

    			for (int i = 2; i < bad.size(); i+=2) {
    				oper.add(bad.get(i));
    				oper.add(bad.get(i+1));
    				arr[bad.get(i)][bad.get(i+1)]^=1;
    			}
    			bad.clear();
    			bad.add(atmp);
    			bad.add(btmp);
    		}

    		if (bad.size()/2 == 1) {
    			List<Integer> good = new ArrayList<Integer>();
    			for (int i = n-2; i < n; i++) {
    				for (int j = m-2; j < m; j++) {
    					boolean baad = false;
    					for (int z = 0; z < bad.size(); z+=2) {
    						if (bad.get(z) == i && bad.get(z+1) == j) baad = true;
    					}
    					if (!baad) {
    						good.add(i);
    						good.add(j);
    					}
    				}
    			}

    			oper.add(bad.get(0));
    			oper.add(bad.get(1));
    			arr[bad.get(0)][bad.get(1)] ^=1;
    			oper.add(good.get(0));
    			oper.add(good.get(1));
    			arr[good.get(0)][good.get(1)]^=1;
    			oper.add(good.get(2));
    			oper.add(good.get(3));
    			arr[good.get(2)][good.get(3)] ^=1;
    			bad.clear();
    			for (int i = 0; i < 4; i++) bad.add(good.get(i));
    		}
    		if (bad.size()/2 == 2) {
    			List<Integer> good = new ArrayList<Integer>();
    			for (int i = n-2; i < n; i++) {
    				for (int j = m-2; j < m; j++) {
    					boolean baad = false;
    					for (int z = 0; z < bad.size(); z+=2) {
    						if (bad.get(z) == i && bad.get(z+1) == j) baad = true;
    					}
    					if (!baad) {
    						good.add(i);
    						good.add(j);
    					}
    				}
    			}
    			oper.add(bad.get(0));
    			oper.add(bad.get(1));
    			arr[bad.get(0)][bad.get(1)] ^=1;
    			oper.add(good.get(0));
    			oper.add(good.get(1));
    			arr[good.get(0)][good.get(1)]^=1;
    			oper.add(good.get(2));
    			oper.add(good.get(3));
    			arr[good.get(2)][good.get(3)] ^=1;

    			oper.add(bad.get(2));
    			oper.add(bad.get(3));
    			arr[bad.get(2)][bad.get(3)] ^=1;
    			oper.add(good.get(0));
    			oper.add(good.get(1));
    			arr[good.get(0)][good.get(1)]^=1;
    			oper.add(good.get(2));
    			oper.add(good.get(3));
    			arr[good.get(2)][good.get(3)] ^=1;
    		}

    		out.println(oper.size()/6);
    		for (int i = 0; i < oper.size(); i+=6) {
    			out.print((oper.get(i)+1)+" ");
    			out.print((oper.get(i+1)+1)+" ");
    			out.print((oper.get(i+2)+1)+" ");
    			out.print((oper.get(i+3)+1)+" ");
    			out.print((oper.get(i+4)+1)+" ");
    			out.println((oper.get(i+5)+1));
    		}

    	}

   	}

    //<>

    static class Reader {

        private InputStream mIs;
        private byte[] buf = new byte[1024];
        private int curChar;
        private int numChars;

        public Reader() {
            this(System.in);
        }

        public Reader(InputStream is) {
            mIs = is;
        }

        public int read() {
            if (numChars == -1) {
                throw new InputMismatchException();

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

        public String nextLine() {
            int c = read();
            while (isSpaceChar(c)) {
                c = read();
            }
            StringBuilder res = new StringBuilder();
            do {
                res.appendCodePoint(c);
                c = read();
            } while (!isEndOfLine(c));
            return res.toString();
        }

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

        double nextDouble()
        {
            return Double.parseDouble(next());
        }

        public long nextLong() {
            int c = read();
            while (isSpaceChar(c)) {
                c = read();
            }
            int sgn = 1;
            if (c == '-') {
                sgn = -1;
                c = read();
            }
            long 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 int nextInt() {
            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 boolean isSpaceChar(int c) {
            return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
        }

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

    }
    static void input_output() throws IOException {
        File f = new File("in.txt");
        if(f.exists() && !f.isDirectory()) {
            in = new Reader(new FileInputStream("in.txt"));
        } else in = new Reader();
        f = new File("out.txt");
        if(f.exists() && !f.isDirectory()) {
            out = new PrintWriter(new File("out.txt"));
        } else out = new PrintWriter(System.out);
    }
}


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

public class Main {
    static PrintWriter out;
    static Reader in;
    public static void main(String[] args) throws IOException {
        //in = new Reader(new FileInputStream(args[0])); // for test.
        input_output();
        Main solver = new Main();
        solver.solve();
        out.close(); // remove when testing.
        out.flush(); // remove when testing.
    }

    static int INF = (int)1e9;
    static int maxn = (int)2e5+5;
    static int Mod = (int)1e9+7;
    static int n, m, q, t, k;

    void solve() throws IOException{
    	t = in.nextInt();

    	while (t --> 0) {
    		n = in.nextInt();
    		m = in.nextInt();
    		int[][] arr = new int[n][m];
    		String s;
    		for (int i = 0; i < n; i++) {
    			s = in.next();
    			for (int j = 0; j < m; j++) {
    				arr[i][j] = s.charAt(j)-'0';
    			}
    		}
    		ArrayList<Integer> oper = new ArrayList<Integer>();

    		int x = 0;
    		for (int i = 0; i < n-2; i++) {
    			for (int j = 0; j < m; j++) {
    				if (j == m-1) x = j-1;
    				else x = j;
    				if (arr[i][j] == 1) {
    					oper.add(i);
    					oper.add(j);
    					arr[i][j] ^=1;
    					oper.add(i+1);
    					oper.add(x);
    					arr[i+1][x] ^=1;
    					oper.add(i+1);
    					oper.add(x+1);
    					arr[i+1][x+1] ^=1;
    				}
    			}
    		}

    		for (int j = 0, i = n-2; j < m-3; j++) {
    			if (arr[i][j] == 1) {
    				oper.add(i);
    				oper.add(j);
    				arr[i][j]^=1;
    				oper.add(i);
    				oper.add(j+1);
    				arr[i][j+1]^=1;
    				oper.add(i+1);
    				oper.add(j+1);
    				arr[i+1][j+1]^=1;
    			}
    		}

    		List<Integer> bad = new ArrayList<Integer>();
    		for (int i = n-2; i < n; i++) {
    			for (int j = m-2; j < m; j++) {
    				if (arr[i][j] == 1) {
    					bad.add(i);
    					bad.add(j);
    				}
    			}
    		}

    		if (bad.size()/2 == 3) {
    			for (int i = 0; i < bad.size(); i+=2) {
    				oper.add(bad.get(i));
    				oper.add(bad.get(i+1));
    				arr[bad.get(i)][bad.get(i+1)]^=1;
    			}
    		}
    		if (bad.size()/2 == 4) {
    			int atmp = bad.get(0),
    				btmp = bad.get(1);

    			for (int i = 2; i < bad.size(); i+=2) {
    				oper.add(bad.get(i));
    				oper.add(bad.get(i+1));
    				arr[bad.get(i)][bad.get(i+1)]^=1;
    			}
    			bad.clear();
    			bad.add(atmp);
    			bad.add(btmp);
    		}

    		if (bad.size()/2 == 1) {
    			List<Integer> good = new ArrayList<Integer>();
    			for (int i = n-2; i < n; i++) {
    				for (int j = m-2; j < m; j++) {
    					boolean baad = false;
    					for (int z = 0; z < bad.size(); z+=2) {
    						if (bad.get(z) == i && bad.get(z+1) == j) baad = true;
    					}
    					if (!baad) {
    						good.add(i);
    						good.add(j);
    					}
    				}
    			}

    			oper.add(bad.get(0));
    			oper.add(bad.get(1));
    			arr[bad.get(0)][bad.get(1)] ^=1;
    			oper.add(good.get(0));
    			oper.add(good.get(1));
    			arr[good.get(0)][good.get(1)]^=1;
    			oper.add(good.get(2));
    			oper.add(good.get(3));
    			arr[good.get(2)][good.get(3)] ^=1;
    			bad.clear();
    			for (int i = 0; i < 4; i++) bad.add(good.get(i));
    		}
    		if (bad.size()/2 == 2) {
    			List<Integer> good = new ArrayList<Integer>();
    			for (int i = n-2; i < n; i++) {
    				for (int j = m-2; j < m; j++) {
    					boolean baad = false;
    					for (int z = 0; z < bad.size(); z+=2) {
    						if (bad.get(z) == i && bad.get(z+1) == j) baad = true;
    					}
    					if (!baad) {
    						good.add(i);
    						good.add(j);
    					}
    				}
    			}
    			oper.add(bad.get(0));
    			oper.add(bad.get(1));
    			arr[bad.get(0)][bad.get(1)] ^=1;
    			oper.add(good.get(0));
    			oper.add(good.get(1));
    			arr[good.get(0)][good.get(1)]^=1;
    			oper.add(good.get(2));
    			oper.add(good.get(3));
    			arr[good.get(2)][good.get(3)] ^=1;

    			oper.add(bad.get(2));
    			oper.add(bad.get(3));
    			arr[bad.get(2)][bad.get(3)] ^=1;
    			oper.add(good.get(0));
    			oper.add(good.get(1));
    			arr[good.get(0)][good.get(1)]^=1;
    			oper.add(good.get(2));
    			oper.add(good.get(3));
    			arr[good.get(2)][good.get(3)] ^=1;
    		}

    		out.println(oper.size()/6);
    		for (int i = 0; i < oper.size(); i+=6) {
    			out.print((oper.get(i)+1)+" ");
    			out.print((oper.get(i+1)+1)+" ");
    			out.print((oper.get(i+2)+1)+" ");
    			out.print((oper.get(i+3)+1)+" ");
    			out.print((oper.get(i+4)+1)+" ");
    			out.println((oper.get(i+5)+1));
    		}

    	}

   	}

    //<>

    static class Reader {

        private InputStream mIs;
        private byte[] buf = new byte[1024];
        private int curChar;
        private int numChars;

        public Reader() {
            this(System.in);
        }

        public Reader(InputStream is) {
            mIs = is;
        }

        public int read() {
            if (numChars == -1) {
                throw new InputMismatchException();

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

        public String nextLine() {
            int c = read();
            while (isSpaceChar(c)) {
                c = read();
            }
            StringBuilder res = new StringBuilder();
            do {
                res.appendCodePoint(c);
                c = read();
            } while (!isEndOfLine(c));
            return res.toString();
        }

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

        double nextDouble()
        {
            return Double.parseDouble(next());
        }

        public long nextLong() {
            int c = read();
            while (isSpaceChar(c)) {
                c = read();
            }
            int sgn = 1;
            if (c == '-') {
                sgn = -1;
                c = read();
            }
            long 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 int nextInt() {
            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 boolean isSpaceChar(int c) {
            return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
        }

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

    }
    static void input_output() throws IOException {
        File f = new File("in.txt");
        if(f.exists() && !f.isDirectory()) {
            in = new Reader(new FileInputStream("in.txt"));
        } else in = new Reader();
        f = new File("out.txt");
        if(f.exists() && !f.isDirectory()) {
            out = new PrintWriter(new File("out.txt"));
        } else out = new PrintWriter(System.out);
    }
}


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

public class Main {
    static PrintWriter out;
    static Reader in;
    public static void main(String[] args) throws IOException {
        //in = new Reader(new FileInputStream(args[0])); // for test.
        input_output();
        Main solver = new Main();
        solver.solve();
        out.close(); // remove when testing.
        out.flush(); // remove when testing.
    }

    static int INF = (int)1e9;
    static int maxn = (int)2e5+5;
    static int Mod = (int)1e9+7;
    static int n, m, q, t, k;

    void solve() throws IOException{
    	t = in.nextInt();

    	while (t --> 0) {
    		n = in.nextInt();
    		m = in.nextInt();
    		int[][] arr = new int[n][m];
    		String s;
    		for (int i = 0; i < n; i++) {
    			s = in.next();
    			for (int j = 0; j < m; j++) {
    				arr[i][j] = s.charAt(j)-'0';
    			}
    		}
    		ArrayList<Integer> oper = new ArrayList<Integer>();

    		int x = 0;
    		for (int i = 0; i < n-2; i++) {
    			for (int j = 0; j < m; j++) {
    				if (j == m-1) x = j-1;
    				else x = j;
    				if (arr[i][j] == 1) {
    					oper.add(i);
    					oper.add(j);
    					oper.add(i+1);
    					oper.add(x);
    					oper.add(i+1);
    					oper.add(x+1);
    				}
    			}
    		}

    		for (int j = 0, i = n-2; j < m-3; j++) {
    			if (arr[i][j] == 1) {
    				oper.add(i);
    				oper.add(j);
    				arr[i][j]^=1;
    				oper.add(i);
    				oper.add(j+1);
    				arr[i][j+1]^=1;
    				oper.add(i+1);
    				oper.add(j+1);
    				arr[i+1][j+1]^=1;
    			}
    		}

    		List<Integer> bad = new ArrayList<Integer>();
    		for (int i = n-2; i < n; i++) {
    			for (int j = m-2; j < m; j++) {
    				if (arr[i][j] == 1) {
    					bad.add(i);
    					bad.add(j);
    				}
    			}
    		}

    		if (bad.size()/2 == 3) {
    			for (int i = 0; i < bad.size(); i++) {
    				oper.add(bad.get(i));
    			}
    		}
    		if (bad.size()/2 == 4) {
    			int atmp = bad.get(0),
    				btmp = bad.get(1);

    			for (int i = 2; i < bad.size(); i++) {
    				oper.add(bad.get(i));
    			}
    			bad.clear();
    			bad.add(atmp);
    			bad.add(btmp);
    		}

    		if (bad.size()/2 == 1) {
    			List<Integer> good = new ArrayList<Integer>();
    			for (int i = n-2; i < n; i++) {
    				for (int j = m-2; j < m; j++) {
    					boolean baad = false;
    					for (int z = 0; z < bad.size(); z+=2) {
    						if (bad.get(z) == i && bad.get(z+1) == j) baad = true;
    					}
    					if (!baad) {
    						good.add(i);
    						good.add(j);
    					}
    				}
    			}

    			oper.add(bad.get(0));
    			oper.add(bad.get(1));
    			oper.add(good.get(0));
    			oper.add(good.get(1));
    			oper.add(good.get(2));
    			oper.add(good.get(3));
    			bad.clear();
    			for (int i = 0; i < 4; i++) bad.add(good.get(i));
    		}
    		if (bad.size()/2 == 2) {
    			List<Integer> good = new ArrayList<Integer>();
    			for (int i = n-2; i < n; i++) {
    				for (int j = m-2; j < m; j++) {
    					boolean baad = false;
    					for (int z = 0; z < bad.size(); z+=2) {
    						if (bad.get(z) == i && bad.get(z+1) == j) baad = true;
    					}
    					if (!baad) {
    						good.add(i);
    						good.add(j);
    					}
    				}
    			}
    			oper.add(bad.get(0));
    			oper.add(bad.get(1));
    			oper.add(good.get(0));
    			oper.add(good.get(1));
    			oper.add(good.get(2));
    			oper.add(good.get(3));

    			oper.add(bad.get(2));
    			oper.add(bad.get(3));
    			oper.add(good.get(0));
    			oper.add(good.get(1));
    			oper.add(good.get(2));
    			oper.add(good.get(3));
    		}

    		out.println(oper.size()/6);
    		for (int i = 0; i < oper.size(); i+=6) {
    			out.print((oper.get(i)+1)+" ");
    			out.print((oper.get(i+1)+1)+" ");
    			out.print((oper.get(i+2)+1)+" ");
    			out.print((oper.get(i+3)+1)+" ");
    			out.print((oper.get(i+4)+1)+" ");
    			out.println((oper.get(i+5)+1));
    		}

    	}

   	}

    //<>

    static class Reader {

        private InputStream mIs;
        private byte[] buf = new byte[1024];
        private int curChar;
        private int numChars;

        public Reader() {
            this(System.in);
        }

        public Reader(InputStream is) {
            mIs = is;
        }

        public int read() {
            if (numChars == -1) {
                throw new InputMismatchException();

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

        public String nextLine() {
            int c = read();
            while (isSpaceChar(c)) {
                c = read();
            }
            StringBuilder res = new StringBuilder();
            do {
                res.appendCodePoint(c);
                c = read();
            } while (!isEndOfLine(c));
            return res.toString();
        }

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

        double nextDouble()
        {
            return Double.parseDouble(next());
        }

        public long nextLong() {
            int c = read();
            while (isSpaceChar(c)) {
                c = read();
            }
            int sgn = 1;
            if (c == '-') {
                sgn = -1;
                c = read();
            }
            long 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 int nextInt() {
            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 boolean isSpaceChar(int c) {
            return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
        }

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

    }
    static void input_output() throws IOException {
        File f = new File("in.txt");
        if(f.exists() && !f.isDirectory()) {
            in = new Reader(new FileInputStream("in.txt"));
        } else in = new Reader();
        f = new File("out.txt");
        if(f.exists() && !f.isDirectory()) {
            out = new PrintWriter(new File("out.txt"));
        } else out = new PrintWriter(System.out);
    }
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Arrays;
import java.io.IOException;
import java.util.ArrayList;
import java.io.UncheckedIOException;
import java.util.List;
import java.io.Closeable;
import java.io.Writer;
import java.io.OutputStreamWriter;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 */
public class Main {
    public static void main(String[] args) throws Exception {
        Thread thread = new Thread(null, new TaskAdapter(), "", 1 << 29);
        thread.start();
        thread.join();
    }

    static class TaskAdapter implements Runnable {
        @Override
        public void run() {
            InputStream inputStream = System.in;
            OutputStream outputStream = System.out;
            FastInput in = new FastInput(inputStream);
            FastOutput out = new FastOutput(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 {
        Debug debug = new Debug(true);
        int[][] mat;
        List<int[]> seq;

        public void solve(int testNumber, FastInput in, FastOutput out) {
            debug.debug("t", testNumber);
            int n = in.readInt();
            int m = in.readInt();
            mat = new int[n][m];
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < m; j++) {
                    mat[i][j] = in.readChar() - '0';
                }
            }
            seq = new ArrayList<>(m * n);
            IntegerArrayList list = new IntegerArrayList(6);
            if (n % 2 == 1) {
                for (int i = 0; i < m; i++) {
                    if (mat[n - 1][i] == 1) {
                        apply(n - 1, i, n - 2, i, n - 2, i + 1 == m ? i - 1 : i + 1);
                    }
                }
                n--;
            }

            if (m % 2 == 1) {
                for (int i = 0; i < n; i++) {
                    if (mat[i][m - 1] == 1) {
                        apply(i, m - 1, i, m - 2, i + 1 == n ? i - 1 : i + 1, m - 2);
                    }
                }
                m--;
            }

            for (int i = 0; i < n; i += 2) {
                for (int j = 0; j < m; j += 2) {
                    int low = i;
                    int high = i + 1;
                    int left = j;
                    int right = j + 1;
                    debug.debug("low", low);
                    debug.debug("left", left);
                    int sum;
                    while ((sum = mat[low][left] + mat[low][right] + mat[high][left] + mat[high][right]) > 0) {
                        list.clear();
                        int greedy = sum == 2 ? 0 : 1;
                        for (int a = low; a <= high; a++) {
                            for (int b = left; b <= right; b++) {
                                if (mat[a][b] == greedy) {
                                    list.add(a);
                                    list.add(b);
                                }
                            }
                        }
                        for (int a = low; a <= high; a++) {
                            for (int b = left; b <= right; b++) {
                                if (mat[a][b] != greedy && list.size() < 6) {
                                    list.add(a);
                                    list.add(b);
                                }
                            }
                        }
                        assert list.size() == 6;
                        apply(list.toArray());
                    }
                }
            }

            for (int[] row : mat) {
                for (int cell : row) {
                    assert cell == 0;
                }
            }

            out.println(seq.size());
            for (int[] xy : seq) {
                for (int t : xy) {
                    out.append(t + 1).append(' ');
                }
                out.println();
            }
        }

        public void apply(int[] xy) {
            seq.add(xy);
            for (int i = 0; i < 6; i += 2) {
                mat[xy[i]][xy[i + 1]] ^= 1;
            }
        }

        public void apply(int x1, int y1, int x2, int y2, int x3, int y3) {
            apply(new int[]{x1, y1, x2, y2, x3, y3});
        }

    }

    static class IntegerArrayList implements Cloneable {
        private int size;
        private int cap;
        private int[] data;
        private static final int[] EMPTY = new int[0];

        public IntegerArrayList(int cap) {
            this.cap = cap;
            if (cap == 0) {
                data = EMPTY;
            } else {
                data = new int[cap];
            }
        }

        public IntegerArrayList(int[] data) {
            this(0);
            addAll(data);
        }

        public IntegerArrayList(IntegerArrayList list) {
            this.size = list.size;
            this.cap = list.cap;
            this.data = Arrays.copyOf(list.data, size);
        }

        public IntegerArrayList() {
            this(0);
        }

        public void ensureSpace(int req) {
            if (req > cap) {
                while (cap < req) {
                    cap = Math.max(cap + 10, 2 * cap);
                }
                data = Arrays.copyOf(data, cap);
            }
        }

        public void add(int x) {
            ensureSpace(size + 1);
            data[size++] = x;
        }

        public void addAll(int[] x) {
            addAll(x, 0, x.length);
        }

        public void addAll(int[] x, int offset, int len) {
            ensureSpace(size + len);
            System.arraycopy(x, offset, data, size, len);
            size += len;
        }

        public void addAll(IntegerArrayList list) {
            addAll(list.data, 0, list.size);
        }

        public int size() {
            return size;
        }

        public int[] toArray() {
            return Arrays.copyOf(data, size);
        }

        public void clear() {
            size = 0;
        }

        public String toString() {
            return Arrays.toString(toArray());
        }

        public boolean equals(Object obj) {
            if (!(obj instanceof IntegerArrayList)) {
                return false;
            }
            IntegerArrayList other = (IntegerArrayList) obj;
            return SequenceUtils.equal(data, 0, size - 1, other.data, 0, other.size - 1);
        }

        public int hashCode() {
            int h = 1;
            for (int i = 0; i < size; i++) {
                h = h * 31 + Integer.hashCode(data[i]);
            }
            return h;
        }

        public IntegerArrayList clone() {
            IntegerArrayList ans = new IntegerArrayList();
            ans.addAll(this);
            return ans;
        }

    }

    static class Debug {
        private boolean offline;
        private PrintStream out = System.err;

        public Debug(boolean enable) {
            offline = enable && System.getSecurityManager() == null;
        }

        public Debug debug(String name, int x) {
            if (offline) {
                debug(name, "" + x);
            }
            return this;
        }

        public Debug debug(String name, String x) {
            if (offline) {
                out.printf("%s=%s", name, x);
                out.println();
            }
            return this;
        }

    }

    static class FastOutput implements AutoCloseable, Closeable, Appendable {
        private static final int THRESHOLD = 1 << 13;
        private final Writer os;
        private StringBuilder cache = new StringBuilder(THRESHOLD * 2);

        public FastOutput append(CharSequence csq) {
            cache.append(csq);
            return this;
        }

        public FastOutput append(CharSequence csq, int start, int end) {
            cache.append(csq, start, end);
            return this;
        }

        private void afterWrite() {
            if (cache.length() < THRESHOLD) {
                return;
            }
            flush();
        }

        public FastOutput(Writer os) {
            this.os = os;
        }

        public FastOutput(OutputStream os) {
            this(new OutputStreamWriter(os));
        }

        public FastOutput append(char c) {
            cache.append(c);
            afterWrite();
            return this;
        }

        public FastOutput append(int c) {
            cache.append(c);
            afterWrite();
            return this;
        }

        public FastOutput append(String c) {
            cache.append(c);
            afterWrite();
            return this;
        }

        public FastOutput println(int c) {
            return append(c).println();
        }

        public FastOutput println() {
            return append(System.lineSeparator());
        }

        public FastOutput flush() {
            try {
                os.append(cache);
                os.flush();
                cache.setLength(0);
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
            return this;
        }

        public void close() {
            flush();
            try {
                os.close();
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        }

        public String toString() {
            return cache.toString();
        }

    }

    static class SequenceUtils {
        public static boolean equal(int[] a, int al, int ar, int[] b, int bl, int br) {
            if ((ar - al) != (br - bl)) {
                return false;
            }
            for (int i = al, j = bl; i <= ar; i++, j++) {
                if (a[i] != b[j]) {
                    return false;
                }
            }
            return true;
        }

    }

    static class FastInput {
        private final InputStream is;
        private StringBuilder defaultStringBuf = new StringBuilder(1 << 13);
        private byte[] buf = new byte[1 << 13];
        private int bufLen;
        private int bufOffset;
        private int next;

        public FastInput(InputStream is) {
            this.is = is;
        }

        private int read() {
            while (bufLen == bufOffset) {
                bufOffset = 0;
                try {
                    bufLen = is.read(buf);
                } catch (IOException e) {
                    bufLen = -1;
                }
                if (bufLen == -1) {
                    return -1;
                }
            }
            return buf[bufOffset++];
        }

        public void skipBlank() {
            while (next >= 0 && next <= 32) {
                next = read();
            }
        }

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

        public int readInt() {
            int sign = 1;

            skipBlank();
            if (next == '+' || next == '-') {
                sign = next == '+' ? 1 : -1;
                next = read();
            }

            int val = 0;
            if (sign == 1) {
                while (next >= '0' && next <= '9') {
                    val = val * 10 + next - '0';
                    next = read();
                }
            } else {
                while (next >= '0' && next <= '9') {
                    val = val * 10 - next + '0';
                    next = read();
                }
            }

            return val;
        }

        public char readChar() {
            skipBlank();
            char c = (char) next;
            next = read();
            return c;
        }

        public String readString(StringBuilder builder) {
            skipBlank();

            while (next > 32) {
                builder.append((char) next);
                next = read();
            }

            return builder.toString();
        }

        public String readString() {
            defaultStringBuf.setLength(0);
            return readString(defaultStringBuf);
        }

    }
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Arrays;
import java.io.IOException;
import java.util.ArrayList;
import java.io.UncheckedIOException;
import java.util.List;
import java.io.Closeable;
import java.io.Writer;
import java.io.OutputStreamWriter;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 */
public class Main {
    public static void main(String[] args) throws Exception {
        Thread thread = new Thread(null, new TaskAdapter(), "", 1 << 29);
        thread.start();
        thread.join();
    }

    static class TaskAdapter implements Runnable {
        @Override
        public void run() {
            InputStream inputStream = System.in;
            OutputStream outputStream = System.out;
            FastInput in = new FastInput(inputStream);
            FastOutput out = new FastOutput(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 {
        Debug debug = new Debug(true);
        int[][] mat;
        List<int[]> seq;

        public void solve(int testNumber, FastInput in, FastOutput out) {
            debug.debug("t", testNumber);
            int n = in.readInt();
            int m = in.readInt();
            mat = new int[n][m];
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < m; j++) {
                    mat[i][j] = in.readChar() - '0';
                }
            }
            seq = new ArrayList<>(m * n);
            IntegerArrayList list = new IntegerArrayList(6);
            if (n % 2 == 1) {
                for (int i = 0; i < m; i++) {
                    if (mat[n - 1][i] == 1) {
                        apply(n - 1, i, n - 2, i, n - 2, i + 1 == m ? i - 1 : i + 1);
                    }
                }
                n--;
            }

            if (m % 2 == 1) {
                for (int i = 0; i < n; i++) {
                    if (mat[i][m - 1] == 1) {
                        apply(i, m - 1, i, m - 2, i + 1 == n ? i - 1 : i + 1, m - 2);
                    }
                }
                m--;
            }

            for (int i = 0; i < n; i += 2) {
                for (int j = 0; j < m; j += 2) {
                    int low = i;
                    int high = i + 1;
                    int left = j;
                    int right = j + 1;
                    debug.debug("low", low);
                    debug.debug("left", left);
                    int sum;
                    while ((sum = mat[low][left] + mat[low][right] + mat[high][left] + mat[high][right]) > 0) {
                        list.clear();
                        int greedy = sum == 2 ? 0 : 1;
                        for (int a = low; a <= high; a++) {
                            for (int b = left; b <= right; b++) {
                                if (mat[a][b] == greedy && list.size() < 6) {
                                    list.add(a);
                                    list.add(b);
                                }
                            }
                        }
                        for (int a = low; a <= high; a++) {
                            for (int b = left; b <= right; b++) {
                                if (mat[a][b] != greedy && list.size() < 6) {
                                    list.add(a);
                                    list.add(b);
                                }
                            }
                        }
                        assert list.size() == 6;
                        apply(list.toArray());
                    }
                }
            }

            for (int[] row : mat) {
                for (int cell : row) {
                    assert cell == 0;
                }
            }

            out.println(seq.size());
            for (int[] xy : seq) {
                for (int t : xy) {
                    out.append(t + 1).append(' ');
                }
                out.println();
            }
        }

        public void apply(int[] xy) {
            seq.add(xy);
            for (int i = 0; i < 6; i += 2) {
                mat[xy[i]][xy[i + 1]] ^= 1;
            }
        }

        public void apply(int x1, int y1, int x2, int y2, int x3, int y3) {
            apply(new int[]{x1, y1, x2, y2, x3, y3});
        }

    }

    static class IntegerArrayList implements Cloneable {
        private int size;
        private int cap;
        private int[] data;
        private static final int[] EMPTY = new int[0];

        public IntegerArrayList(int cap) {
            this.cap = cap;
            if (cap == 0) {
                data = EMPTY;
            } else {
                data = new int[cap];
            }
        }

        public IntegerArrayList(int[] data) {
            this(0);
            addAll(data);
        }

        public IntegerArrayList(IntegerArrayList list) {
            this.size = list.size;
            this.cap = list.cap;
            this.data = Arrays.copyOf(list.data, size);
        }

        public IntegerArrayList() {
            this(0);
        }

        public void ensureSpace(int req) {
            if (req > cap) {
                while (cap < req) {
                    cap = Math.max(cap + 10, 2 * cap);
                }
                data = Arrays.copyOf(data, cap);
            }
        }

        public void add(int x) {
            ensureSpace(size + 1);
            data[size++] = x;
        }

        public void addAll(int[] x) {
            addAll(x, 0, x.length);
        }

        public void addAll(int[] x, int offset, int len) {
            ensureSpace(size + len);
            System.arraycopy(x, offset, data, size, len);
            size += len;
        }

        public void addAll(IntegerArrayList list) {
            addAll(list.data, 0, list.size);
        }

        public int size() {
            return size;
        }

        public int[] toArray() {
            return Arrays.copyOf(data, size);
        }

        public void clear() {
            size = 0;
        }

        public String toString() {
            return Arrays.toString(toArray());
        }

        public boolean equals(Object obj) {
            if (!(obj instanceof IntegerArrayList)) {
                return false;
            }
            IntegerArrayList other = (IntegerArrayList) obj;
            return SequenceUtils.equal(data, 0, size - 1, other.data, 0, other.size - 1);
        }

        public int hashCode() {
            int h = 1;
            for (int i = 0; i < size; i++) {
                h = h * 31 + Integer.hashCode(data[i]);
            }
            return h;
        }

        public IntegerArrayList clone() {
            IntegerArrayList ans = new IntegerArrayList();
            ans.addAll(this);
            return ans;
        }

    }

    static class Debug {
        private boolean offline;
        private PrintStream out = System.err;

        public Debug(boolean enable) {
            offline = enable && System.getSecurityManager() == null;
        }

        public Debug debug(String name, int x) {
            if (offline) {
                debug(name, "" + x);
            }
            return this;
        }

        public Debug debug(String name, String x) {
            if (offline) {
                out.printf("%s=%s", name, x);
                out.println();
            }
            return this;
        }

    }

    static class FastOutput implements AutoCloseable, Closeable, Appendable {
        private static final int THRESHOLD = 1 << 13;
        private final Writer os;
        private StringBuilder cache = new StringBuilder(THRESHOLD * 2);

        public FastOutput append(CharSequence csq) {
            cache.append(csq);
            return this;
        }

        public FastOutput append(CharSequence csq, int start, int end) {
            cache.append(csq, start, end);
            return this;
        }

        private void afterWrite() {
            if (cache.length() < THRESHOLD) {
                return;
            }
            flush();
        }

        public FastOutput(Writer os) {
            this.os = os;
        }

        public FastOutput(OutputStream os) {
            this(new OutputStreamWriter(os));
        }

        public FastOutput append(char c) {
            cache.append(c);
            afterWrite();
            return this;
        }

        public FastOutput append(int c) {
            cache.append(c);
            afterWrite();
            return this;
        }

        public FastOutput append(String c) {
            cache.append(c);
            afterWrite();
            return this;
        }

        public FastOutput println(int c) {
            return append(c).println();
        }

        public FastOutput println() {
            return append(System.lineSeparator());
        }

        public FastOutput flush() {
            try {
                os.append(cache);
                os.flush();
                cache.setLength(0);
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
            return this;
        }

        public void close() {
            flush();
            try {
                os.close();
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        }

        public String toString() {
            return cache.toString();
        }

    }

    static class SequenceUtils {
        public static boolean equal(int[] a, int al, int ar, int[] b, int bl, int br) {
            if ((ar - al) != (br - bl)) {
                return false;
            }
            for (int i = al, j = bl; i <= ar; i++, j++) {
                if (a[i] != b[j]) {
                    return false;
                }
            }
            return true;
        }

    }

    static class FastInput {
        private final InputStream is;
        private StringBuilder defaultStringBuf = new StringBuilder(1 << 13);
        private byte[] buf = new byte[1 << 13];
        private int bufLen;
        private int bufOffset;
        private int next;

        public FastInput(InputStream is) {
            this.is = is;
        }

        private int read() {
            while (bufLen == bufOffset) {
                bufOffset = 0;
                try {
                    bufLen = is.read(buf);
                } catch (IOException e) {
                    bufLen = -1;
                }
                if (bufLen == -1) {
                    return -1;
                }
            }
            return buf[bufOffset++];
        }

        public void skipBlank() {
            while (next >= 0 && next <= 32) {
                next = read();
            }
        }

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

        public int readInt() {
            int sign = 1;

            skipBlank();
            if (next == '+' || next == '-') {
                sign = next == '+' ? 1 : -1;
                next = read();
            }

            int val = 0;
            if (sign == 1) {
                while (next >= '0' && next <= '9') {
                    val = val * 10 + next - '0';
                    next = read();
                }
            } else {
                while (next >= '0' && next <= '9') {
                    val = val * 10 - next + '0';
                    next = read();
                }
            }

            return val;
        }

        public char readChar() {
            skipBlank();
            char c = (char) next;
            next = read();
            return c;
        }

        public String readString(StringBuilder builder) {
            skipBlank();

            while (next > 32) {
                builder.append((char) next);
                next = read();
            }

            return builder.toString();
        }

        public String readString() {
            defaultStringBuf.setLength(0);
            return readString(defaultStringBuf);
        }

    }
}


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

public class Main{
    public static void main(String[] args) throws IOException{
        In in = new In();
        for(int q = in.pint(); q > 0; q--) {
        	in.tok();;
        	int n = in.pint(), m = in.pint();
        	boolean[][] b = new boolean[n][m];
        	for(int i = 0; i < n; i++) {
        		String s = in.pstr();
        		for(int j = 0; j < m; j++) {
        			b[i][j] = (s.charAt(j) == '1');
        		}
        	}
        	int ct = 0;
        	StringBuilder sb = new StringBuilder();
        	for(int i = n - 1; i > 1; i--) {
        		for(int j = m - 1; j >= 0; j--) {
        			if(b[i][j]) {
        				ct++;
        				b[i][j] = !b[i][j];
        				sb.append((i + 1)); sb.append(' ');
        				sb.append((j + 1)); sb.append(' ');
        				b[i - 1][j] = !b[i - 1][j];
        				sb.append((i)); sb.append(' ');
        				sb.append((j + 1)); sb.append(' ');;
        				sb.append((i)); sb.append(' ');
        				if(j == 0) {
        					b[i - 1][j + 1] = !b[i - 1][j + 1];
            				sb.append((j + 2));
        				}else {
        					b[i - 1][j - 1] = !b[i - 1][j - 1];
        					sb.append((j));
        				}
        				sb.append('\n');
        			}
        		}
        	}
        	for(int j = m - 1; j > 1; j--) {
        		if(b[0][j] || b[1][j]) {
        			ct++;
        			b[0][j - 1] = !b[0][j - 1];
        			sb.append("1 ");
        			sb.append((j)); sb.append(' ');
        			if(b[0][j] ^ b[1][j]) {
        				b[1][j - 1] = !b[1][j - 1];
        				sb.append("2 " );
        				sb.append(j);
        				sb.append(' ');
        			}
        			if(b[0][j]) {
        				b[0][j] = !b[0][j];
        				sb.append("1 ");
        				sb.append((j + 1));
        				sb.append(' ');
        			}
        			if(b[1][j]) {
        				b[1][j] = !b[1][j];
        				sb.append("2 ");
        				sb.append((j + 1));
        				sb.append(' ');
        			}

        			sb.append('\n');

        		}
        	}
        	int k = 0;
        	if(b[0][0]) {k++;}
        	if(b[1][0]) {k++;}
        	if(b[1][1]) {k++;}
        	if(b[0][1]) {k++;}
        	while(k > 0) {
        		ct++;
        		if(k == 4) {
        			sb.append("1 1 1 2 2 1\n");
        			b[0][0] = !b[0][0];
        			b[0][1] = !b[0][1];
        			b[1][0] = !b[1][0];
        			k = 1;
        		}else if(k == 3) {
        			if(b[0][0]) {
        				sb.append("1 1 ");
        				b[0][0] = !b[0][0];
        			}
        			if(b[1][1]) {
        				sb.append("2 2 ");
        				b[1][1] = !b[1][1];
        			}
        			if(b[1][0]) {
        				sb.append("2 1 ");
        				b[1][0] = !b[1][0];
        			}
        			if(b[0][1]) {
        				sb.append("1 2");
        				b[0][1] = !b[0][1];
        			}
        			sb.append('\n');
        			k = 0;
        		}else if(k == 2) {
        			boolean f = true;
        			if(b[0][0]) {
        				f = false;
        				sb.append("1 1 ");
        				b[0][0] = !b[0][0];
        			}
        			if(!b[0][1] || (f && b[0][1])) {
        				if(b[0][1]) {f = false;}
        				sb.append("1 2 ");
        				b[0][1] = !b[0][1];
        			}
        			if(!b[1][1] || (f && b[1][1])) {
        				if(b[1][1]) {f = false;}
        				sb.append("2 2 ");
        				b[1][1] = !b[1][1];
        			}
        			if(!b[1][0] || (f && b[1][0])) {
        				if(b[1][0]) {f = false;}
        				sb.append("2 1");
        				b[1][0] = !b[1][0];
        			}
        			sb.append('\n');
        			k = 3;
        		}else {
        			sb.append("1 1 1 2 ");
        			if(b[0][0] || b[0][1] || b[1][0]) {
        				sb.append("2 1");
        				b[1][0] = !b[1][0];
        			}else {
        				sb.append("2 2");
        				b[1][1] = !b[1][1];
        			}
        			sb.append('\n');
        			b[0][0] = !b[0][0];
        			b[0][1] = !b[0][1];
        			k = 2;
        		}
        	}
        	boolean c = false;
        	for(int i = 0; i < n; i++) {
        		for(int j = 0; j < m; j++) {
        			c |= b[i][j];
        		}
        	}
        	if(c) {ct = killMe();}
        	System.out.println(ct);
        	System.out.print(sb);
        }
    }
    static int killMe() {return 1 + killMe();}
}

class In{
    BufferedReader in;
    StringTokenizer st = new StringTokenizer("");
    public In(){
        in = new BufferedReader(new InputStreamReader(System.in));
    }

    int pint() throws IOException{
        if(st.hasMoreTokens()) {return Integer.parseInt(st.nextToken());}
        else {return Integer.parseInt(in.readLine());}
    }
    double pdbl() throws IOException{
        if(st.hasMoreTokens()) {return Double.parseDouble(st.nextToken());}
        else {return Double.parseDouble(in.readLine());}
    }
    long plng() throws IOException{
        if(st.hasMoreTokens()) {return Long.parseLong(st.nextToken());}
        else {return Long.parseLong(in.readLine());}
    }
    char pchr() throws IOException{
        if(st.hasMoreTokens()) {return st.nextToken().charAt(0);}
        else {return in.readLine().charAt(0);}
    }
    String pstr() throws IOException{
        if(st.hasMoreTokens()) {return st.nextToken();}
        else {return in.readLine();}
    }
    boolean ready() throws IOException {return in.ready();}
    void tok() throws IOException{st = new StringTokenizer(in.readLine());}
}