static void Grouper()
        {
            // Set up complements map
            map = new byte[256];
            for (byte b = 0; b < 255; b++)
            {
                map[b] = b;
            }
            map[(byte)'A'] = (byte)'T';
            map[(byte)'B'] = (byte)'V';
            map[(byte)'C'] = (byte)'G';
            map[(byte)'D'] = (byte)'H';
            map[(byte)'G'] = (byte)'C';
            map[(byte)'H'] = (byte)'D';
            map[(byte)'K'] = (byte)'M';
            map[(byte)'M'] = (byte)'K';
            map[(byte)'R'] = (byte)'Y';
            map[(byte)'T'] = (byte)'A';
            map[(byte)'V'] = (byte)'B';
            map[(byte)'Y'] = (byte)'R';
            map[(byte)'a'] = (byte)'T';
            map[(byte)'b'] = (byte)'V';
            map[(byte)'c'] = (byte)'G';
            map[(byte)'d'] = (byte)'H';
            map[(byte)'g'] = (byte)'C';
            map[(byte)'h'] = (byte)'D';
            map[(byte)'k'] = (byte)'M';
            map[(byte)'m'] = (byte)'K';
            map[(byte)'r'] = (byte)'Y';
            map[(byte)'t'] = (byte)'A';
            map[(byte)'v'] = (byte)'B';
            map[(byte)'y'] = (byte)'R';

            var  startHeader = 0;
            var  i           = 0;
            bool afterFirst  = false;
            var  data        = new List <byte[]>();

            byte[] bytes;
            while (tryTake(readQue, out bytes))
            {
                data.Add(bytes);
                while ((i = Array.IndexOf <byte>(bytes, GT, i + 1)) != -1)
                {
                    var sequence = new RevCompSequence
                    {
                        Pages        = data,
                        StartHeader  = startHeader,
                        EndExclusive = i
                    };
                    if (afterFirst)
                    {
                        (sequence.ReverseThread = new Thread(() => Reverse(sequence))).Start();
                    }
                    else
                    {
                        afterFirst = true;
                    }
                    writeQue.Add(sequence);
                    startHeader = i;
                    data        = new List <byte[]> {
                        bytes
                    };
                }
            }
            i = Array.IndexOf <byte>(data[data.Count - 1], 0, 0);
            var lastSequence = new RevCompSequence
            {
                Pages        = data,
                StartHeader  = startHeader,
                EndExclusive = i == -1 ? data[data.Count - 1].Length : i
            };

            Reverse(lastSequence);
            writeQue.Add(lastSequence);
            writeQue.CompleteAdding();
        }
        static void Reverse(RevCompSequence sequence)
        {
            var startPageId = 0;
            var startBytes  = sequence.Pages[0];
            var startIndex  = sequence.StartHeader;

            // Skip header line
            while ((startIndex = Array.IndexOf <byte>(startBytes, LF, startIndex)) == -1)
            {
                startBytes = sequence.Pages[++startPageId];
                startIndex = 0;
            }

            var endPageId = sequence.Pages.Count - 1;
            var endIndex  = sequence.EndExclusive - 1;

            if (endIndex == -1)
            {
                endIndex = sequence.Pages[--endPageId].Length - 1;
            }
            var endBytes = sequence.Pages[endPageId];

            // Swap in place across pages
            do
            {
                var startByte = startBytes[startIndex];
                if (startByte < SP)
                {
                    if (++startIndex == startBytes.Length)
                    {
                        startBytes = sequence.Pages[++startPageId];
                        startIndex = 0;
                    }
                    if (startIndex == endIndex && startPageId == endPageId)
                    {
                        break;
                    }
                    startByte = startBytes[startIndex];
                }
                var endByte = endBytes[endIndex];
                if (endByte < SP)
                {
                    if (--endIndex == -1)
                    {
                        endBytes = sequence.Pages[--endPageId];
                        endIndex = endBytes.Length - 1;
                    }
                    if (startIndex == endIndex && startPageId == endPageId)
                    {
                        break;
                    }
                    endByte = endBytes[endIndex];
                }

                startBytes[startIndex] = map[endByte];
                endBytes[endIndex]     = map[startByte];

                if (++startIndex == startBytes.Length)
                {
                    startBytes = sequence.Pages[++startPageId];
                    startIndex = 0;
                }
                if (--endIndex == -1)
                {
                    endBytes = sequence.Pages[--endPageId];
                    endIndex = endBytes.Length - 1;
                }
            } while (startPageId < endPageId || (startPageId == endPageId && startIndex < endIndex));
            if (startIndex == endIndex)
            {
                startBytes[startIndex] = map[startBytes[startIndex]];
            }
        }