public AggregateRegionResults GetAggregateRegionResults(ConcurrentDictionary <string, IndelEvidence> indelLookup,
                                                                int startPosition,
                                                                int endPosition, bool isFinalTask, RegionDataForAggregation regionData)
        {
            if (_geminiOptions.LightDebug)
            {
                Logger.WriteToLog(
                    $"Started processing for region {_chrom}:{startPosition}-{endPosition}.");
            }

            var adjustedStartPosition = regionData.EffectiveMinPosition;
            var edgeThresholdOrig     = Math.Max(1, regionData.EffectiveMaxPosition - 5000);
            var finalIndelLookup      = GetAndSyncFinalIndelLookup(indelLookup, _masterIndelLookup);
            var edgeState             = regionData.EdgeState;
            var nextEdgeMinPosition   = int.MaxValue;

            var finalizedIndels         = FinalizeIndels(finalIndelLookup, _chrReference, regionData.EffectiveMaxPosition);
            var finalizedIndelsForChrom = GetFinalizedIndelsForChrom(_chrom, finalizedIndels, edgeState);

            IChromosomeIndelSource indelSource = null;

            var       messySiteWidth = _geminiOptions.MessySiteWidth;
            const int binsToExtendTo = 2; // Treated as <, so 2 means we get to extend status to one on either side

            var binEvidence    = regionData.BinEvidence;
            var binConclusions = new BinConclusions(binEvidence, _geminiOptions.CollectDepth, trackDirectionalMess: _geminiOptions.SilenceDirectionalMessReads, trackMapqMess: _geminiOptions.SilenceMessyMapMessReads);
            var numBins        = binEvidence.NumBins;

            bool shouldRealignAtAll = finalizedIndelsForChrom.Any();

            var imperfectFreqThreshold   = _geminiOptions.ImperfectFreqThreshold;
            var indelRegionfreqThreshold = _geminiOptions.IndelRegionFreqThreshold;
            var messySiteThreshold       = _geminiOptions.MessySiteThreshold;

            var numRetrievedFromLastBlock = 0;
            var numPairsSentToNextBlock   = 0;
            var pairResultsForNextBlock   = new Dictionary <PairClassification, List <PairResult> >();

            var pairResultLookup = new Dictionary <PairClassification, List <PairResult> >();

            foreach (var key in regionData.PairResultLookup.Keys)
            {
                if (!pairResultLookup.ContainsKey(key))
                {
                    pairResultLookup.Add(key, new List <PairResult>());
                }

                pairResultLookup[key].AddRange(regionData.PairResultLookup[key]);
            }

            foreach (var category in pairResultLookup)
            {
                var isMessy          = TypeClassifier.MessyTypes.Contains(category.Key);
                var isIndel          = TypeClassifier._indelTypes.Contains(category.Key);
                var isSingleMismatch = _geminiOptions.AvoidLikelySnvs &&
                                       (category.Key == PairClassification.SingleMismatchStitched ||
                                        category.Key == PairClassification.UnstitchSingleMismatch);
                var isForwardOnlyMessy = IsForwardMessy(category.Key);
                var isReverseOnlyMessy = IsReverseMessy(category.Key);
                var isMapMessy         = IsSuspiciousMapping(category.Key);
                foreach (var pairResult in category.Value)
                {
                    // If on the edge, kick it over to the edge lookup.
                    if (!isFinalTask && pairResult.ReadPair.MaxPosition > edgeThresholdOrig)
                    {
                        numPairsSentToNextBlock++;
                        if (!pairResultsForNextBlock.ContainsKey(category.Key))
                        {
                            pairResultsForNextBlock.Add(category.Key, new List <PairResult>());
                        }

                        pairResultsForNextBlock[category.Key].Add(pairResult);

                        nextEdgeMinPosition = Math.Min(nextEdgeMinPosition, pairResult.ReadPair.MinPosition);
                    }
                    // Still collect evidence even if it's edge, because that could impact this block as well as next block.

                    binEvidence.AddMessEvidence(isMessy, pairResult, isIndel, isSingleMismatch, isForwardOnlyMessy,
                                                isReverseOnlyMessy, isMapMessy);
                }
            }

            numRetrievedFromLastBlock = AddAlignmentsFromEdgeState(edgeState, pairResultLookup, numRetrievedFromLastBlock);

            var finalizedBins = new UsableBins(binConclusions);

            if (shouldRealignAtAll)
            {
                binConclusions.AddIndelEvidence(finalizedIndelsForChrom, binsToExtendTo);
                binConclusions.ProcessRegions(messySiteThreshold, imperfectFreqThreshold,
                                              _geminiOptions.RegionDepthThreshold, indelRegionfreqThreshold, binsToExtendTo, _geminiOptions.DirectionalMessThreshold);
                finalizedBins.FinalizeConclusions(binsToExtendTo);
            }

            using (var snippetSource = _dataSourceFactory.CreateGenomeSnippetSource(_chrom, _chrReference))
            {
                indelSource =
                    _dataSourceFactory.GetChromosomeIndelSource(finalizedIndelsForChrom, snippetSource);
            }


            foreach (var kvp in pairResultsForNextBlock)
            {
                foreach (var pairResult in kvp.Value)
                {
                    pairResultLookup[kvp.Key].Remove(pairResult);
                }
            }

            var allAlignments  = new List <BamAlignment>();
            var outcomesLookup = new Dictionary <HashableIndel, int[]>();

            var numSkippedDueToSites = 0;
            var numKept      = 0;
            var numRealigned = 0;
            var numSilenced  = 0;

            var snowballCategories = _realignmentOptions.CategoriesForSnowballing;
            var doSnowball         = snowballCategories.Any();


            foreach (var category in snowballCategories)
            {
                if (pairResultLookup.ContainsKey(category))
                {
                    pairResultLookup.Remove(category, out var categoryReads);
                    allAlignments.AddRange(ProcessCategory(_categoriesForRealignment,
                                                           indelSource, shouldRealignAtAll,
                                                           outcomesLookup, ref numSkippedDueToSites, ref numKept, ref numRealigned, ref numSilenced,
                                                           categoryReads, category, binEvidence, _progressTracker, binConclusions, finalizedBins, startPosition, endPosition));
                }
            }

            List <HashableIndel> superFinalizedIndels;

            if (doSnowball)
            {
                superFinalizedIndels = GetSuperFinalizedIndelsAfterSnowball(finalizedIndelsForChrom, outcomesLookup);

                if (_geminiOptions.Debug)
                {
                    Logger.WriteToLog(
                        $"After snowballing for region {_chrom}:{startPosition}-{endPosition}, filtered down to {superFinalizedIndels.Count} indels from {finalizedIndelsForChrom.Count} ({finalIndelLookup.Count} preliminary indels).");
                }

                using (var snippetSource = _dataSourceFactory.CreateGenomeSnippetSource(_chrom, _chrReference))
                {
                    indelSource =
                        _dataSourceFactory.GetChromosomeIndelSource(superFinalizedIndels, snippetSource);
                }

                if (_geminiOptions.RecalculateUsableSitesAfterSnowball)
                {
                    binConclusions.ResetIndelRegions();

                    foreach (var indel in superFinalizedIndels)
                    {
                        var bin = (indel.ReferencePosition - adjustedStartPosition) / messySiteWidth;
                        binConclusions.SetIndelRegionTrue(bin);

                        for (int j = 0; j < binsToExtendTo; j++)
                        {
                            var binIndex = bin - j;
                            if (binIndex >= 0)
                            {
                                binConclusions.SetIndelRegionTrue(binIndex);
                            }
                            else
                            {
                                break;
                            }
                        }

                        for (int j = 0; j < binsToExtendTo; j++)
                        {
                            var binIndex = bin + j;
                            if (!binConclusions.SetIndelRegionTrue(binIndex))
                            {
                                break;
                            }
                        }
                    }

                    finalizedBins.FinalizeConclusions(binsToExtendTo);
                }
            }
            else
            {
                superFinalizedIndels = finalizedIndelsForChrom;
            }

            // TODO pull out the allocs below, or ideally actually remove them from realign pair handler or use something different altogether
            foreach (var category in pairResultLookup)
            {
                if (snowballCategories.Contains(category.Key))
                {
                    continue;
                }

                allAlignments.AddRange(ProcessCategory(_categoriesForRealignment, indelSource,
                                                       shouldRealignAtAll,
                                                       outcomesLookup, ref numSkippedDueToSites, ref numKept, ref numRealigned, ref numSilenced, category.Value,
                                                       category.Key, binEvidence, _progressTracker, binConclusions, finalizedBins, startPosition, endPosition));
            }

            var edgeHits = new Dictionary <int, int>();
            var edgeSingleMismatchHits = new Dictionary <int, int>();
            var edgeIndelHits          = new Dictionary <int, int>();
            var edgeMessyHits          = new Dictionary <int, int>();

            PopulateEdgeHitsAndLogBins(numBins, adjustedStartPosition, messySiteWidth, nextEdgeMinPosition, binEvidence,
                                       edgeHits, edgeSingleMismatchHits, edgeIndelHits, edgeMessyHits, startPosition, binConclusions, finalizedBins);

            UpdateMasterOutcomes(_masterOutcomesLookup, outcomesLookup);

            foreach (var hashableIndel in superFinalizedIndels)
            {
                _masterFinalIndels.AddOrUpdate(hashableIndel, 1, (h, n) => { return(n + 1); });
            }

            _progressTracker.AddOrUpdate("Flushed", allAlignments.Count(),
                                         (x, currentCount) => { return(currentCount + allAlignments.Count()); });
            _progressTracker.AddOrUpdate("Sent To Next Block", numPairsSentToNextBlock,
                                         (x, currentCount) => { return(currentCount + numPairsSentToNextBlock); });
            _progressTracker.AddOrUpdate("Retrieved from Past Block", numRetrievedFromLastBlock,
                                         (x, currentCount) => { return(currentCount + numRetrievedFromLastBlock); });
            _progressTracker.AddOrUpdate("Realigned", numRealigned,
                                         (x, currentCount) => { return(currentCount + numRealigned); });
            _progressTracker.AddOrUpdate("Attempts", numKept,
                                         (x, currentCount) => { return(currentCount + numKept); });
            _progressTracker.AddOrUpdate("Skipped", numSkippedDueToSites,
                                         (x, currentCount) => { return(currentCount + numSkippedDueToSites); });
            _progressTracker.AddOrUpdate("Silenced", numSilenced,
                                         (x, currentCount) => { return(currentCount + numSilenced); });

            pairResultLookup.Clear();
            Logger.WriteToLog(
                $"Finished processing for region {_chrom}:{startPosition}-{endPosition}. {allAlignments.Count()} alignments flushed, " +
                $"{numPairsSentToNextBlock} sent to next block, {numRetrievedFromLastBlock} retrieved from {regionData.EdgeState?.Name}. " +
                $"Realigned {numRealigned}/{numKept} attempts ({numSkippedDueToSites} pairs skipped realignment), silenced {numSilenced} messy mates.");


            return(new AggregateRegionResults()
            {
                EdgeState = isFinalTask
                    ? new EdgeState()
                {
                    Name = "Final"
                }
                    : new EdgeState()
                {
                    EdgeAlignments = pairResultsForNextBlock,
                    EdgeIndels = finalizedIndelsForChrom.Where(y => y.ReferencePosition > nextEdgeMinPosition)
                                 .ToList(),
                    EffectiveMinPosition = nextEdgeMinPosition,
                    Name = $"{startPosition}-{endPosition}",
                    BinEvidence = binEvidence
                },
                AlignmentsReadyToBeFlushed = allAlignments
            });
        }
        private void PopulateEdgeHitsAndLogBins(int numBins, int adjustedStartPosition, int messySiteWidth, int edgeThreshold,
                                                IBinEvidence binEvidence, Dictionary <int, int> edgeHits, Dictionary <int, int> edgeSingleMismatchHits, Dictionary <int, int> edgeIndelHits,
                                                Dictionary <int, int> edgeMessyHits, int startPosition, BinConclusions binConclusions, UsableBins usableBins)
        {
            for (int binId = 0; binId < numBins; binId++)
            {
                var inEdge   = false;
                var binStart = adjustedStartPosition + (binId * messySiteWidth);

                if (_geminiOptions.LogRegionsAndRealignments)
                {
                    if (binEvidence.GetAllHits(binId) > 10 && !inEdge)
                    {
                        var binCounts =
                            $"{binId},{inEdge},{binStart},{binStart + messySiteWidth},{binEvidence.GetAllHits(binId)},{usableBins.IsPositionUsable(binStart)},{binEvidence.GetSingleMismatchHit(binId)}," +
                            $"{binConclusions.GetProbableTrueSnvRegion(binId)},{binEvidence.GetIndelHit(binId)},{binConclusions.GetIndelRegionHit(binId)}," +
                            $"{binEvidence.GetMessyHit(binId)},{binConclusions.GetIsMessyEnough(binId)},{binEvidence.GetForwardMessyRegionHit(binId)},{binConclusions.GetFwdMessyStatus(binId)},{binEvidence.GetReverseMessyRegionHit(binId)},{binConclusions.GetRevMessyStatus(binId)},{binEvidence.GetMapqMessyHit(binId)},{binConclusions.GetMapqMessyStatus(binId)}";

                        // TODO consider writing this to a proper output file
                        if (_geminiOptions.LogRegionsAndRealignments)
                        {
                            Logger.WriteToLog("BINCOUNTS\t" + binCounts);
                        }
                    }
                }
            }
        }
        private int ReadsToSilence(PairClassification classification, BinConclusions binEvidence, PairResult pairResult)
        {
            if (_geminiOptions.SilenceSuspiciousMdReads && classification == PairClassification.UnstitchMessySuspiciousMd)
            {
                return(3);
            }

            var isForwardMessy      = IsForwardMessy(classification);
            var isReverseMessy      = IsReverseMessy(classification);
            var isSuspiciousMapping = IsSuspiciousMapping(classification);

            if (!isForwardMessy && !isReverseMessy && !isSuspiciousMapping)
            {
                return(0);
            }

            var silenced     = 0;
            var doSilenceFwd = false;
            var doSilenceRev = false;

            var r1IsReverse = pairResult.ReadPair.Read1.IsReverseStrand();

            // This assumes that there is exactly one forward and one reverse read.
            var fwdRead = r1IsReverse ? pairResult.ReadPair.Read2 : pairResult.ReadPair.Read1;
            var revRead = r1IsReverse ? pairResult.ReadPair.Read1 : pairResult.ReadPair.Read2;

            if (isForwardMessy)
            {
                var binId = binEvidence.GetBinId(fwdRead.Position);
                doSilenceFwd = binEvidence.GetFwdMessyStatus(binId) || binEvidence.GetMapqMessyStatus(binId);
            }
            else if (isReverseMessy)
            {
                var binId = binEvidence.GetBinId(revRead.Position);
                doSilenceRev = binEvidence.GetRevMessyStatus(binId) || binEvidence.GetMapqMessyStatus(binId);
            }
            else if (isSuspiciousMapping)
            {
                var binId       = binEvidence.GetBinId(revRead.Position);
                var isMapqMessy = binEvidence.GetMapqMessyStatus(binId);

                doSilenceFwd = isMapqMessy;
                doSilenceRev = isMapqMessy;
            }

            if (doSilenceFwd)
            {
                silenced = r1IsReverse ? 2 : 1;
            }

            if (doSilenceRev)
            {
                silenced = r1IsReverse ? 1 : 2;
            }

            if (doSilenceFwd && doSilenceRev)
            {
                silenced = 3;
            }

            return(silenced);
        }
        private List <BamAlignment> ProcessCategory(
            List <PairClassification> categoriesForRealignment, IChromosomeIndelSource indelSource,
            bool shouldRealignAtAll, Dictionary <HashableIndel, int[]> outcomesLookup, ref int numSkippedDueToSites,
            ref int numKept, ref int numRealigned, ref int numSilenced,
            List <PairResult> pairResults, PairClassification classification, IBinEvidence binEvidence,
            ConcurrentDictionary <string, int> progressTracker, BinConclusions binConclusions, UsableBins usableBins, int startPosition, int endPosition)
        {
            var allAlignments = new List <BamAlignment>();
            var isHighLikelihoodForRealign = false;

            if (_geminiOptions.ForceHighLikelihoodRealigners)
            {
                var highLikelihoodCategories = new List <PairClassification>()
                {
                    PairClassification.Disagree,
                    PairClassification.MessyStitched,
                    PairClassification.MessySplit,
                    PairClassification.UnstitchMessy,
                    PairClassification.UnstitchIndel
                };
                isHighLikelihoodForRealign = highLikelihoodCategories.Contains(classification);
            }

            int alignmentsCount = 0;

            var doRealign = false;
            ReadPairRealignerAndCombiner realignHandler = null;
            var alreadyStitched       = ClassificationIsStitched(classification);
            var doStitch              = !_geminiOptions.SkipStitching && TypeClassifier.ClassificationIsStitchable(classification);
            var categoryIsRealignable = categoriesForRealignment.Contains(classification);

            if (categoryIsRealignable || doStitch)
            {
                doRealign = true;

                realignHandler = _bamRealignmentFactory.GetRealignPairHandler(doStitch,
                                                                              alreadyStitched,
                                                                              _realignmentOptions.PairAwareEverything ||
                                                                              ClassificationIsPairAwareRealignable(classification),
                                                                              _refIdMapping,
                                                                              new ReadStatusCounter(), false, indelSource, _chrom, new Dictionary <string, IndelEvidence>(),
                                                                              ClassificationHasIndels(classification), outcomesLookup
                                                                              , SkipRestitchIfUnchanged(classification));
            }

            using (var snippetSource = _dataSourceFactory.CreateGenomeSnippetSource(_chrom, _chrReference))
                using (var singleSnippetSource = new ReusableSnippetSource(snippetSource))
                {
                    var nmCalculator = new NmCalculator(singleSnippetSource);

                    var classificationString = classification.ToString();
                    foreach (var pairResult in pairResults)
                    {
                        int toSilence = 0;

                        IEnumerable <BamAlignment> alignments;
                        if (!doRealign)
                        {
                            alignments = pairResult.Alignments;
                        }
                        else
                        {
                            bool doRealignPair =
                                shouldRealignAtAll && (isHighLikelihoodForRealign ||
                                                       (categoryIsRealignable &&
                                                        (usableBins.IsPositionUsable(pairResult.ReadPair.MinPosition) ||
                                                         usableBins.IsPositionUsable(pairResult.ReadPair.MaxPosition))));


                            if (!doRealignPair)
                            {
                                numSkippedDueToSites++;
                            }
                            else
                            {
                                numKept++;
                            }

                            toSilence = ReadsToSilence(classification, binConclusions, pairResult);
                            if (toSilence > 0)
                            {
                                numSilenced++;
                            }

                            alignments = realignHandler.ExtractReads(pairResult, nmCalculator, doRealignPair, toSilence);

                            if (pairResult.ReadPair.Realigned || pairResult.ReadPair.RealignedR1 ||
                                pairResult.ReadPair.RealignedR2)
                            {
                                numRealigned++;
                            }
                        }

                        var silencedR1    = (toSilence == 1 || toSilence == 3) && !pairResult.ReadPair.RealignedR1;
                        var silencedR2    = (toSilence == 2 || toSilence == 3) && !pairResult.ReadPair.RealignedR2;
                        var readTreatment = ReadTreatment(silencedR1, silencedR2, pairResult);

                        progressTracker.AddOrUpdate(classificationString + ":" + readTreatment, 1,
                                                    (x, currentCount) => { return(currentCount + 1); });

                        var alignmentsList = alignments.ToList();
                        foreach (var bamAlignment in alignmentsList)
                        {
                            if (_geminiOptions.LightDebug)
                            {
                                AddMdTagCountsTags(bamAlignment, pairResult);
                            }

                            bamAlignment.ReplaceOrAddStringTag("XT", readTreatment);
                            bamAlignment.ReplaceOrAddStringTag("XP", classificationString);
                        }

                        alignmentsCount += alignmentsList.Count();
                        allAlignments.AddRange(alignmentsList);
                    }
                }

            if (realignHandler != null)
            {
                realignHandler.Finish();
            }

            pairResults.Clear();
            return(allAlignments);
        }
        public void ProcessRegions()
        {
            var binEvidence = new Mock <IBinEvidence>();

            binEvidence.SetupGet(x => x.NumBins).Returns(1000);

            // Bin 10, 10% mess and 10% indel
            binEvidence.Setup(x => x.GetMessyHit(10)).Returns(10);
            binEvidence.Setup(x => x.GetAllHits(10)).Returns(100);
            binEvidence.Setup(x => x.GetIndelHit(10)).Returns(10);

            // Bin 45, 2% mess and 5% indel - edge positive
            binEvidence.Setup(x => x.GetAllHits(45)).Returns(100);
            binEvidence.Setup(x => x.GetMessyHit(45)).Returns(2);
            binEvidence.Setup(x => x.GetIndelHit(45)).Returns(5);

            // Bin 55, 1% mess and 10% indel - mess edge negative
            binEvidence.Setup(x => x.GetAllHits(55)).Returns(100);
            binEvidence.Setup(x => x.GetMessyHit(55)).Returns(1);
            binEvidence.Setup(x => x.GetIndelHit(55)).Returns(5);

            // Bin 65, 10% mess and 5% indel - indel edge positive
            binEvidence.Setup(x => x.GetAllHits(65)).Returns(100);
            binEvidence.Setup(x => x.GetMessyHit(65)).Returns(10);
            binEvidence.Setup(x => x.GetIndelHit(65)).Returns(5);

            // Bin 75, 10% mess and 4% indel - indel edge negative
            binEvidence.Setup(x => x.GetAllHits(75)).Returns(100);
            binEvidence.Setup(x => x.GetMessyHit(75)).Returns(10);
            binEvidence.Setup(x => x.GetIndelHit(75)).Returns(4);

            // Bin 85, 50% mess and 50% indel, but depth too low
            binEvidence.Setup(x => x.GetAllHits(85)).Returns(8);
            binEvidence.Setup(x => x.GetMessyHit(85)).Returns(4);
            binEvidence.Setup(x => x.GetIndelHit(85)).Returns(4);

            // Bin 95, negative, but neighbor 96 is positive and neighbor bins are therefore turned on
            binEvidence.Setup(x => x.GetAllHits(95)).Returns(8);
            binEvidence.Setup(x => x.GetMessyHit(95)).Returns(0);
            binEvidence.Setup(x => x.GetIndelHit(95)).Returns(0);
            binEvidence.Setup(x => x.GetAllHits(96)).Returns(100);
            binEvidence.Setup(x => x.GetMessyHit(96)).Returns(10);
            binEvidence.Setup(x => x.GetIndelHit(96)).Returns(10);

            // Bin 105, a little mess and it's all reverse
            binEvidence.Setup(x => x.GetAllHits(105)).Returns(100);
            binEvidence.Setup(x => x.GetMessyHit(105)).Returns(5);
            binEvidence.Setup(x => x.GetIndelHit(105)).Returns(0);
            binEvidence.Setup(x => x.GetForwardMessyRegionHit(105)).Returns(0);
            binEvidence.Setup(x => x.GetReverseMessyRegionHit(105)).Returns(5);

            // Bin 115, a little mess and it's mostly forward
            binEvidence.Setup(x => x.GetAllHits(115)).Returns(100);
            binEvidence.Setup(x => x.GetMessyHit(115)).Returns(5);
            binEvidence.Setup(x => x.GetIndelHit(115)).Returns(0);
            binEvidence.Setup(x => x.GetForwardMessyRegionHit(115)).Returns(3);
            binEvidence.Setup(x => x.GetReverseMessyRegionHit(115)).Returns(0);

            // Bin 125, a little mess and it's from a lot of low mapq
            binEvidence.Setup(x => x.GetAllHits(125)).Returns(100);
            binEvidence.Setup(x => x.GetMessyHit(125)).Returns(20);
            binEvidence.Setup(x => x.GetIndelHit(125)).Returns(0);
            binEvidence.Setup(x => x.GetForwardMessyRegionHit(125)).Returns(1);
            binEvidence.Setup(x => x.GetReverseMessyRegionHit(125)).Returns(0);
            binEvidence.Setup(x => x.GetMapqMessyHit(125)).Returns(3);

            // Bin 135, all mess is reverse but there's not enough of if to trigger the filter
            binEvidence.Setup(x => x.GetAllHits(135)).Returns(100);
            binEvidence.Setup(x => x.GetMessyHit(135)).Returns(3);
            binEvidence.Setup(x => x.GetIndelHit(135)).Returns(0);
            binEvidence.Setup(x => x.GetForwardMessyRegionHit(135)).Returns(0);
            binEvidence.Setup(x => x.GetReverseMessyRegionHit(135)).Returns(3);


            var binConclusions = new BinConclusions(binEvidence.Object, true, true, true);

            binConclusions.ProcessRegions(3, 0.07, 10, 0.05, 2, 0.1f);

            // 10 and neighbors - positive
            Assert.True(binConclusions.GetIsMessyEnough(10));
            Assert.True(binConclusions.GetIsMessyEnough(9));
            Assert.True(binConclusions.GetIsMessyEnough(11));

            Assert.True(binConclusions.GetIsMessyEnough(45));
            Assert.True(binConclusions.GetIsMessyEnough(44));
            Assert.True(binConclusions.GetIsMessyEnough(46));

            Assert.False(binConclusions.GetIsMessyEnough(55));
            Assert.False(binConclusions.GetIsMessyEnough(54));
            Assert.False(binConclusions.GetIsMessyEnough(56));

            Assert.True(binConclusions.GetIsMessyEnough(65));
            Assert.True(binConclusions.GetIsMessyEnough(64));
            Assert.True(binConclusions.GetIsMessyEnough(66));

            Assert.False(binConclusions.GetIsMessyEnough(75));
            Assert.False(binConclusions.GetIsMessyEnough(74));
            Assert.False(binConclusions.GetIsMessyEnough(76));

            Assert.False(binConclusions.GetIsMessyEnough(85));
            Assert.False(binConclusions.GetIsMessyEnough(84));
            Assert.False(binConclusions.GetIsMessyEnough(86));

            Assert.False(binConclusions.GetIsMessyEnough(94));
            Assert.True(binConclusions.GetIsMessyEnough(95));
            Assert.True(binConclusions.GetIsMessyEnough(96));
            Assert.True(binConclusions.GetIsMessyEnough(97));

            Assert.True(binConclusions.GetRevMessyStatus(105));
            Assert.False(binConclusions.GetIsMessyEnough(105)); // doesn't have to be messy to be rev-messy
            Assert.False(binConclusions.GetFwdMessyStatus(105));
            Assert.False(binConclusions.GetMapqMessyStatus(105));

            Assert.False(binConclusions.GetRevMessyStatus(115));
            Assert.False(binConclusions.GetIsMessyEnough(115)); // doesn't have to be messy to be rev-messy
            Assert.True(binConclusions.GetFwdMessyStatus(115));
            Assert.False(binConclusions.GetMapqMessyStatus(115));

            Assert.False(binConclusions.GetRevMessyStatus(125));
            Assert.False(binConclusions.GetIsMessyEnough(125)); // doesn't have to be messy to be rev-messy
            Assert.False(binConclusions.GetFwdMessyStatus(125));
            Assert.True(binConclusions.GetMapqMessyStatus(125));

            // Mess too low to trigger filter
            Assert.False(binConclusions.GetRevMessyStatus(135));
            Assert.False(binConclusions.GetIsMessyEnough(135));
            Assert.False(binConclusions.GetFwdMessyStatus(135));
            Assert.False(binConclusions.GetMapqMessyStatus(135));

            //// No mapq mess - same as original, but never should have true for mapq messy
            var binConclusionsNoMapqMess = new BinConclusions(binEvidence.Object, true, true, false);

            binConclusionsNoMapqMess.ProcessRegions(3, 0.07, 10, 0.05, 2, 0.1f);

            // 10 and neighbors - positive
            Assert.True(binConclusionsNoMapqMess.GetIsMessyEnough(10));
            Assert.True(binConclusionsNoMapqMess.GetIsMessyEnough(9));
            Assert.True(binConclusionsNoMapqMess.GetIsMessyEnough(11));

            Assert.True(binConclusionsNoMapqMess.GetIsMessyEnough(45));
            Assert.True(binConclusionsNoMapqMess.GetIsMessyEnough(44));
            Assert.True(binConclusionsNoMapqMess.GetIsMessyEnough(46));

            Assert.False(binConclusionsNoMapqMess.GetIsMessyEnough(55));
            Assert.False(binConclusionsNoMapqMess.GetIsMessyEnough(54));
            Assert.False(binConclusionsNoMapqMess.GetIsMessyEnough(56));

            Assert.True(binConclusionsNoMapqMess.GetIsMessyEnough(65));
            Assert.True(binConclusionsNoMapqMess.GetIsMessyEnough(64));
            Assert.True(binConclusionsNoMapqMess.GetIsMessyEnough(66));

            Assert.False(binConclusionsNoMapqMess.GetIsMessyEnough(75));
            Assert.False(binConclusionsNoMapqMess.GetIsMessyEnough(74));
            Assert.False(binConclusionsNoMapqMess.GetIsMessyEnough(76));

            Assert.False(binConclusionsNoMapqMess.GetIsMessyEnough(85));
            Assert.False(binConclusionsNoMapqMess.GetIsMessyEnough(84));
            Assert.False(binConclusionsNoMapqMess.GetIsMessyEnough(86));

            Assert.False(binConclusionsNoMapqMess.GetIsMessyEnough(94));
            Assert.True(binConclusionsNoMapqMess.GetIsMessyEnough(95));
            Assert.True(binConclusionsNoMapqMess.GetIsMessyEnough(96));
            Assert.True(binConclusionsNoMapqMess.GetIsMessyEnough(97));

            Assert.True(binConclusionsNoMapqMess.GetRevMessyStatus(105));
            Assert.False(binConclusionsNoMapqMess.GetIsMessyEnough(105)); // doesn't have to be messy to be rev-messy
            Assert.False(binConclusionsNoMapqMess.GetFwdMessyStatus(105));
            Assert.False(binConclusionsNoMapqMess.GetMapqMessyStatus(105));

            Assert.False(binConclusionsNoMapqMess.GetRevMessyStatus(115));
            Assert.False(binConclusionsNoMapqMess.GetIsMessyEnough(115)); // doesn't have to be messy to be rev-messy
            Assert.True(binConclusionsNoMapqMess.GetFwdMessyStatus(115));
            Assert.False(binConclusionsNoMapqMess.GetMapqMessyStatus(115));

            Assert.False(binConclusionsNoMapqMess.GetRevMessyStatus(125));
            Assert.False(binConclusionsNoMapqMess.GetIsMessyEnough(125)); // doesn't have to be messy to be rev-messy
            Assert.False(binConclusionsNoMapqMess.GetFwdMessyStatus(125));
            Assert.False(binConclusionsNoMapqMess.GetMapqMessyStatus(125));

            // Mess too low to trigger filter
            Assert.False(binConclusionsNoMapqMess.GetRevMessyStatus(135));
            Assert.False(binConclusionsNoMapqMess.GetIsMessyEnough(135));
            Assert.False(binConclusionsNoMapqMess.GetFwdMessyStatus(135));
            Assert.False(binConclusionsNoMapqMess.GetMapqMessyStatus(135));

            //// No directional mess - should be same as original except fwd and reverse always false
            var binConclusionsNoDirectional = new BinConclusions(binEvidence.Object, true, false, true);

            binConclusionsNoDirectional.ProcessRegions(3, 0.07, 10, 0.05, 2, 0.1f);

            // 10 and neighbors - positive
            Assert.True(binConclusionsNoDirectional.GetIsMessyEnough(10));
            Assert.True(binConclusionsNoDirectional.GetIsMessyEnough(9));
            Assert.True(binConclusionsNoDirectional.GetIsMessyEnough(11));

            Assert.True(binConclusionsNoDirectional.GetIsMessyEnough(45));
            Assert.True(binConclusionsNoDirectional.GetIsMessyEnough(44));
            Assert.True(binConclusionsNoDirectional.GetIsMessyEnough(46));

            Assert.False(binConclusionsNoDirectional.GetIsMessyEnough(55));
            Assert.False(binConclusionsNoDirectional.GetIsMessyEnough(54));
            Assert.False(binConclusionsNoDirectional.GetIsMessyEnough(56));

            Assert.True(binConclusionsNoDirectional.GetIsMessyEnough(65));
            Assert.True(binConclusionsNoDirectional.GetIsMessyEnough(64));
            Assert.True(binConclusionsNoDirectional.GetIsMessyEnough(66));

            Assert.False(binConclusionsNoDirectional.GetIsMessyEnough(75));
            Assert.False(binConclusionsNoDirectional.GetIsMessyEnough(74));
            Assert.False(binConclusionsNoDirectional.GetIsMessyEnough(76));

            Assert.False(binConclusionsNoDirectional.GetIsMessyEnough(85));
            Assert.False(binConclusionsNoDirectional.GetIsMessyEnough(84));
            Assert.False(binConclusionsNoDirectional.GetIsMessyEnough(86));

            Assert.False(binConclusionsNoDirectional.GetIsMessyEnough(94));
            Assert.True(binConclusionsNoDirectional.GetIsMessyEnough(95));
            Assert.True(binConclusionsNoDirectional.GetIsMessyEnough(96));
            Assert.True(binConclusionsNoDirectional.GetIsMessyEnough(97));

            Assert.False(binConclusionsNoDirectional.GetRevMessyStatus(105));
            Assert.False(binConclusionsNoDirectional.GetIsMessyEnough(105)); // doesn't have to be messy to be rev-messy
            Assert.False(binConclusionsNoDirectional.GetFwdMessyStatus(105));
            Assert.False(binConclusionsNoDirectional.GetMapqMessyStatus(105));

            Assert.False(binConclusionsNoDirectional.GetRevMessyStatus(115));
            Assert.False(binConclusionsNoDirectional.GetIsMessyEnough(115)); // doesn't have to be messy to be rev-messy
            Assert.False(binConclusionsNoDirectional.GetFwdMessyStatus(115));
            Assert.False(binConclusionsNoDirectional.GetMapqMessyStatus(115));

            Assert.False(binConclusionsNoDirectional.GetRevMessyStatus(125));
            Assert.False(binConclusionsNoDirectional.GetIsMessyEnough(125)); // doesn't have to be messy to be rev-messy
            Assert.False(binConclusionsNoDirectional.GetFwdMessyStatus(125));
            Assert.True(binConclusionsNoDirectional.GetMapqMessyStatus(125));

            // Mess too low to trigger filter
            Assert.False(binConclusionsNoDirectional.GetRevMessyStatus(135));
            Assert.False(binConclusionsNoDirectional.GetIsMessyEnough(135));
            Assert.False(binConclusionsNoDirectional.GetFwdMessyStatus(135));
            Assert.False(binConclusionsNoDirectional.GetMapqMessyStatus(135));


            //// No directional or mapq mess - should be same as original except for fwd, rev, and mapq statuses always false
            var binConclusionsNoDirectionalOrMapqMess = new BinConclusions(binEvidence.Object, true, false, false);

            binConclusionsNoDirectionalOrMapqMess.ProcessRegions(3, 0.07, 10, 0.05, 2, 0.1f);

            // 10 and neighbors - positive
            Assert.True(binConclusionsNoDirectionalOrMapqMess.GetIsMessyEnough(10));
            Assert.True(binConclusionsNoDirectionalOrMapqMess.GetIsMessyEnough(9));
            Assert.True(binConclusionsNoDirectionalOrMapqMess.GetIsMessyEnough(11));

            Assert.True(binConclusionsNoDirectionalOrMapqMess.GetIsMessyEnough(45));
            Assert.True(binConclusionsNoDirectionalOrMapqMess.GetIsMessyEnough(44));
            Assert.True(binConclusionsNoDirectionalOrMapqMess.GetIsMessyEnough(46));

            Assert.False(binConclusionsNoDirectionalOrMapqMess.GetIsMessyEnough(55));
            Assert.False(binConclusionsNoDirectionalOrMapqMess.GetIsMessyEnough(54));
            Assert.False(binConclusionsNoDirectionalOrMapqMess.GetIsMessyEnough(56));

            Assert.True(binConclusionsNoDirectionalOrMapqMess.GetIsMessyEnough(65));
            Assert.True(binConclusionsNoDirectionalOrMapqMess.GetIsMessyEnough(64));
            Assert.True(binConclusionsNoDirectionalOrMapqMess.GetIsMessyEnough(66));

            Assert.False(binConclusionsNoDirectionalOrMapqMess.GetIsMessyEnough(75));
            Assert.False(binConclusionsNoDirectionalOrMapqMess.GetIsMessyEnough(74));
            Assert.False(binConclusionsNoDirectionalOrMapqMess.GetIsMessyEnough(76));

            Assert.False(binConclusionsNoDirectionalOrMapqMess.GetIsMessyEnough(85));
            Assert.False(binConclusionsNoDirectionalOrMapqMess.GetIsMessyEnough(84));
            Assert.False(binConclusionsNoDirectionalOrMapqMess.GetIsMessyEnough(86));

            Assert.False(binConclusionsNoDirectionalOrMapqMess.GetIsMessyEnough(94));
            Assert.True(binConclusionsNoDirectionalOrMapqMess.GetIsMessyEnough(95));
            Assert.True(binConclusionsNoDirectionalOrMapqMess.GetIsMessyEnough(96));
            Assert.True(binConclusionsNoDirectionalOrMapqMess.GetIsMessyEnough(97));

            Assert.False(binConclusionsNoDirectionalOrMapqMess.GetRevMessyStatus(105));
            Assert.False(binConclusionsNoDirectionalOrMapqMess.GetIsMessyEnough(105)); // doesn't have to be messy to be rev-messy
            Assert.False(binConclusionsNoDirectionalOrMapqMess.GetFwdMessyStatus(105));
            Assert.False(binConclusionsNoDirectionalOrMapqMess.GetMapqMessyStatus(105));

            Assert.False(binConclusionsNoDirectionalOrMapqMess.GetRevMessyStatus(115));
            Assert.False(binConclusionsNoDirectionalOrMapqMess.GetIsMessyEnough(115)); // doesn't have to be messy to be rev-messy
            Assert.False(binConclusionsNoDirectionalOrMapqMess.GetFwdMessyStatus(115));
            Assert.False(binConclusionsNoDirectionalOrMapqMess.GetMapqMessyStatus(115));

            Assert.False(binConclusionsNoDirectionalOrMapqMess.GetRevMessyStatus(125));
            Assert.False(binConclusionsNoDirectionalOrMapqMess.GetIsMessyEnough(125)); // doesn't have to be messy to be rev-messy
            Assert.False(binConclusionsNoDirectionalOrMapqMess.GetFwdMessyStatus(125));
            Assert.False(binConclusionsNoDirectionalOrMapqMess.GetMapqMessyStatus(125));

            // Mess too low to trigger filter
            Assert.False(binConclusionsNoDirectionalOrMapqMess.GetRevMessyStatus(135));
            Assert.False(binConclusionsNoDirectionalOrMapqMess.GetIsMessyEnough(135));
            Assert.False(binConclusionsNoDirectionalOrMapqMess.GetFwdMessyStatus(135));
            Assert.False(binConclusionsNoDirectionalOrMapqMess.GetMapqMessyStatus(135));
        }