private static void FlushLowRepMersInPartition(object threadParams)
        {
            FlushingThreadParams flushingParams = (FlushingThreadParams)threadParams;
            MerTables            merTable       = flushingParams.merTable;
            int partitionNo = flushingParams.partitionNo;
            int culledMers  = merTable.repeatedMers[partitionNo].Reduce(minKeepDepth, merTable);

            // and it can't possibly be full if we've just culled some entries for it
            if (culledMers > 0)
            {
                merTable.repeatedMersFull[partitionNo] = false;
            }
            //Console.WriteLine("flushed " + culledMers + " from partition " + partitionNo + ". " + merTable.repeatedMers[partitionNo].Count + " left");
        }
        // flush the low-rep mers from the repeat tables, condense the remaining repeated mers and fold in the per-thread repeats. Can only be called after all the
        // threads have finished for a seq data file. This code is *not* thread-safe.
        public void FlushLowRepMers(MerTables merTable, int fileNo)
        {
            // allocate a buffer to hold the flushed low-rep mers
            //int initialBufferLength = 500000;
            int initialBufferLength = this.repeatedMers[0].Capacity;

            culledBuffer              = new LowRepMerBuffer();
            culledBuffer.keys         = new ulong[initialBufferLength + noOfPartitions];
            culledBuffer.values       = new long[initialBufferLength + noOfPartitions];
            culledBuffer.idx          = 0;
            culledBuffer.bufferActive = true;
            culledBuffer.bufferNo     = 1;
            culledBuffer.limit        = initialBufferLength;
            culledLock = new object();

            FlushingThreadParams[] flushingParams = new FlushingThreadParams[noOfPartitions];
            Thread[] flushingThreads = new Thread[noOfPartitions];

            for (int p = 0; p < noOfPartitions; p++)
            {
                flushingParams[p]             = new FlushingThreadParams();
                flushingParams[p].merTable    = merTable;
                flushingParams[p].partitionNo = p;
                flushingThreads[p]            = new Thread(new ParameterizedThreadStart(MerTables.FlushLowRepMersInPartition));
                flushingThreads[p].Priority   = ThreadPriority.BelowNormal;
                flushingThreads[p].Start(flushingParams[p]);
            }

            for (int p = 0; p < noOfPartitions; p++)
            {
                flushingThreads[p].Join();
                flushingThreads[p] = null;
            }

            // write out any filled culled buffers
            int bufferNo = 0;

            if (filledCulledBuffers != null)
            {
                for (int i = 0; i < filledCulledBuffers.Count; i++)
                {
                    WriteLowRepMers(fileNo, bufferNo, filledCulledBuffers[i], filledCulledBuffers[i].keys.Length);
                    bufferNo++;
                    filledCulledBuffers[i] = null;
                }
                filledCulledBuffers = null;
            }
            // finally write out the remaining culled low-rep mers
            WriteLowRepMers(fileNo, bufferNo, culledBuffer, culledBuffer.idx);

            // return the temporary buffers
            culledBuffer = null;

            // finally push the per-thread dictionaries to the shared dictionary
            for (int t = 0; t < overflowMers.Length; t++)
            {
                if (overflowMers[t] == null)
                {
                    continue;
                }

                MerDictionary currentOverflow     = overflowMers[t];
                MerDictionary replacementOverflow = new MerDictionary(currentOverflow.Capacity, fullMerMask);

                foreach (KeyValuePair <ulong, long> kvp in currentOverflow)
                {
                    int absMerHashCode = kvp.Key.GetHashCode() & int31Mask;
                    int partitionNo    = absMerHashCode % noOfPartitions;

                    if (repeatedMersFull[partitionNo])
                    {
                        replacementOverflow.Add(kvp.Key, kvp.Value);
                    }
                    else
                    {
                        bool OK = repeatedMers[partitionNo].Add(kvp.Key, kvp.Value);
                        if (!OK)
                        {
                            repeatedMersFull[partitionNo] = true;
                        }
                    }
                }

                overflowMers[t] = replacementOverflow;
            }
        }