private bool BlocksEndWithSameVerse(int bookNum, Block vernBlock, Block refBlock, ScrVers vernacularVersification) { var lastVernVerse = new VerseRef(bookNum, vernBlock.ChapterNumber, vernBlock.LastVerseNum, vernacularVersification); var lastRefVerse = new VerseRef(bookNum, refBlock.ChapterNumber, refBlock.LastVerseNum, Versification); return(lastVernVerse.CompareTo(lastRefVerse) == 0); }
private bool BlocksMatch(int bookNum, Block vernBlock, Block refBlock, ScrVers vernacularVersification) { var vernInitStartVerse = new VerseRef(bookNum, vernBlock.ChapterNumber, vernBlock.InitialStartVerseNumber, vernacularVersification); var refInitStartVerse = new VerseRef(bookNum, refBlock.ChapterNumber, refBlock.InitialStartVerseNumber, Versification); return(vernInitStartVerse.CompareTo(refInitStartVerse) == 0 && (vernBlock.CharacterId == refBlock.CharacterId || vernBlock.CharacterIsUnclear()) && BlocksEndWithSameVerse(bookNum, vernBlock, refBlock, vernacularVersification)); }
public int CompareTo(Verse other) { return(VerseRef.CompareTo(other.VerseRef)); }
private void MatchVernBlocksToReferenceTextBlocks(IReadOnlyList <Block> vernBlockList, string bookId, ScrVers vernacularVersification, bool forceMatch = false) { int bookNum = BCVRef.BookToNumber(bookId); var refBook = Books.Single(b => b.BookId == bookId); var refBlockList = refBook.GetScriptBlocks(); int iRefBlock = 0; int iRefBlockMemory = -1; // The following is a minor performance enhancement to help when just hooking up one verse's worth of blocks. // In it's current form, it doesn't work for tests that have partial reference texts that don't start at verse 1. // Also, it doesn't take versification mappings into consideration. //if (vernBlockList[0].ChapterNumber > 1) //{ // iRefBlock = refBook.GetIndexOfFirstBlockForVerse(vernBlockList[0].ChapterNumber, ); // if (iRefBlock < 0) // { // // Apparently there is no reference text for the verse(s) we're interested in. // return; // } //} for (int iVernBlock = 0; iVernBlock < vernBlockList.Count; iVernBlock++, iRefBlock++) { if (iRefBlock >= refBlockList.Count && iRefBlockMemory < 0) { // We still have more vernacular verses to consider. Most likely, this is an // additional alternate ending (in Mark). It's not very efficient, but we'll just // start back at the beginning of the reference text block collection. iRefBlock = 0; } var currentVernBlock = vernBlockList[iVernBlock]; // TODO: This handles the only case I know of (and for which there is a test) where a versification pulls in verses // from the end of the book to an earlier spot, namely Romans 14:24-26 <- Romans 16:25-27. If we ever have this same // kind of behavior that is pulling from somewhere other than the *end* of the book, the logic to reset iRefBlock // based on iRefBlockMemory will need to be moved/added elsewhere. if (iRefBlockMemory >= 0 && iRefBlock >= refBlockList.Count) { iRefBlock = iRefBlockMemory; iRefBlockMemory = -1; } var currentRefBlock = refBlockList[iRefBlock]; var vernInitStartVerse = new VerseRef(bookNum, currentVernBlock.ChapterNumber, currentVernBlock.InitialStartVerseNumber, vernacularVersification); var refInitStartVerse = new VerseRef(bookNum, currentRefBlock.ChapterNumber, currentRefBlock.InitialStartVerseNumber, Versification); var type = CharacterVerseData.GetStandardCharacterType(currentVernBlock.CharacterId); switch (type) { case CharacterVerseData.StandardCharacter.BookOrChapter: if (currentVernBlock.IsChapterAnnouncement) { var refChapterBlock = new Block(currentVernBlock.StyleTag, currentVernBlock.ChapterNumber); refChapterBlock.BlockElements.Add(new ScriptText(GetFormattedChapterAnnouncement(bookId, currentVernBlock.ChapterNumber))); if (HasSecondaryReferenceText) { if (currentRefBlock.IsChapterAnnouncement && currentRefBlock.MatchesReferenceText) { refChapterBlock.SetMatchedReferenceBlock(currentRefBlock.ReferenceBlocks.Single().Clone()); } else if (!currentRefBlock.IsChapterAnnouncement) { // Find the reference text's chapter announcement to get the secondary reference text chapter announcement var i = iRefBlock + 1; while (i < refBlockList.Count) { var workingRefBlock = refBlockList[i]; var workingRefInitStartVerse = new VerseRef(bookNum, workingRefBlock.ChapterNumber, workingRefBlock.InitialStartVerseNumber, Versification); var compareResult = workingRefInitStartVerse.CompareTo(vernInitStartVerse); if (compareResult > 0) // break out early; we passed the verse reference, so there is no chapter label { break; } if (compareResult == 0 && workingRefBlock.IsChapterAnnouncement && workingRefBlock.MatchesReferenceText) { refChapterBlock.SetMatchedReferenceBlock(workingRefBlock.ReferenceBlocks.Single().Clone()); break; } i++; } } } currentVernBlock.SetMatchedReferenceBlock(refChapterBlock); if (currentRefBlock.IsChapterAnnouncement) { continue; } } goto case CharacterVerseData.StandardCharacter.ExtraBiblical; case CharacterVerseData.StandardCharacter.ExtraBiblical: if (type == CharacterVerseData.GetStandardCharacterType(currentRefBlock.CharacterId)) { currentVernBlock.SetMatchedReferenceBlock(currentRefBlock); continue; } goto case CharacterVerseData.StandardCharacter.Intro; case CharacterVerseData.StandardCharacter.Intro: // This will be re-incremented in the for loop, so it effectively allows // the vern index to advance while keeping the ref index the same. iRefBlock--; continue; default: if (refInitStartVerse.CompareTo(vernInitStartVerse) > 0 || currentVernBlock.MatchesReferenceText) { iRefBlock--; continue; } break; } while (CharacterVerseData.IsCharacterExtraBiblical(currentRefBlock.CharacterId) || vernInitStartVerse > refInitStartVerse) { iRefBlock++; if (iRefBlock == refBlockList.Count) { return; // couldn't find a ref block to use at all. } currentRefBlock = refBlockList[iRefBlock]; refInitStartVerse = new VerseRef(bookNum, currentRefBlock.ChapterNumber, currentRefBlock.InitialStartVerseNumber, vernacularVersification); } var indexOfVernVerseStart = iVernBlock; var indexOfRefVerseStart = iRefBlock; BlockMatchup.AdvanceToCleanVerseBreak(vernBlockList, i => true, ref iVernBlock); var lastVernVerseFound = new VerseRef(bookNum, vernBlockList[iVernBlock].ChapterNumber, vernBlockList[iVernBlock].LastVerseNum, vernacularVersification); FindAllScriptureBlocksThroughVerse(refBlockList, lastVernVerseFound, ref iRefBlock, Versification); int numberOfVernBlocksInVerseChunk = iVernBlock - indexOfVernVerseStart + 1; int numberOfRefBlocksInVerseChunk = iRefBlock - indexOfRefVerseStart + 1; for (int i = indexOfVernVerseStart; i <= iVernBlock; i++) { if (vernBlockList[i].CharacterIs(bookId, CharacterVerseData.StandardCharacter.ExtraBiblical)) { numberOfVernBlocksInVerseChunk--; } } if (numberOfVernBlocksInVerseChunk == 1 && numberOfRefBlocksInVerseChunk > 1) { var lastRefBlockInVerseChunk = refBlockList[iRefBlock]; var refVerse = new VerseRef(bookNum, lastRefBlockInVerseChunk.ChapterNumber, lastRefBlockInVerseChunk.InitialStartVerseNumber, Versification); if (lastVernVerseFound.CompareTo(refVerse) == 0 && lastVernVerseFound.BBBCCCVVV < refVerse.BBBCCCVVV) { // A versification difference has us pulling a verse from later in the text, so we need to get out and look beyond our current // index in the ref text, but remember this spot so we can come back to it. iRefBlockMemory = indexOfRefVerseStart; iRefBlock--; iVernBlock--; continue; } // Since there's only one vernacular block for this verse (or verse bridge), just combine all // ref blocks into one and call it a match. vernBlockList[indexOfVernVerseStart].SetMatchedReferenceBlock(bookNum, vernacularVersification, this, refBlockList.Skip(indexOfRefVerseStart).Take(numberOfRefBlocksInVerseChunk)); continue; } for (int i = 0; i < numberOfVernBlocksInVerseChunk && i < numberOfRefBlocksInVerseChunk; i++) { var vernBlockInVerseChunk = vernBlockList[indexOfVernVerseStart + i]; var refBlockInVerseChunk = refBlockList[indexOfRefVerseStart + i]; if (BlocksMatch(bookNum, vernBlockInVerseChunk, refBlockInVerseChunk, vernacularVersification) || (numberOfVernBlocksInVerseChunk == 1 && numberOfRefBlocksInVerseChunk == 1 && BlocksEndWithSameVerse(bookNum, vernBlockInVerseChunk, refBlockInVerseChunk, vernacularVersification))) { if (i == numberOfVernBlocksInVerseChunk - 1 && i < numberOfRefBlocksInVerseChunk - 1) { vernBlockInVerseChunk.MatchesReferenceText = false; vernBlockInVerseChunk.ReferenceBlocks = new List <Block>(refBlockList.Skip(indexOfRefVerseStart + i).Take(numberOfRefBlocksInVerseChunk - i)); break; } vernBlockInVerseChunk.SetMatchedReferenceBlock(refBlockList[indexOfRefVerseStart + i]); } else { iVernBlock = indexOfVernVerseStart + i; if (vernBlockList[iVernBlock].CharacterIs(bookId, CharacterVerseData.StandardCharacter.ExtraBiblical)) { iVernBlock++; } int j = 0; if (numberOfVernBlocksInVerseChunk - i >= 2) { // Look from the bottom up for (; j + 1 < numberOfVernBlocksInVerseChunk && j + i < numberOfRefBlocksInVerseChunk; j++) { vernBlockInVerseChunk = vernBlockList[indexOfVernVerseStart + numberOfVernBlocksInVerseChunk - j - 1]; refBlockInVerseChunk = refBlockList[indexOfRefVerseStart + numberOfRefBlocksInVerseChunk - j - 1]; if (BlocksMatch(bookNum, vernBlockInVerseChunk, refBlockInVerseChunk, vernacularVersification)) { vernBlockInVerseChunk.SetMatchedReferenceBlock(refBlockInVerseChunk); } else { break; } } } var numberOfUnmatchedRefBlocks = numberOfRefBlocksInVerseChunk - i - j; var remainingRefBlocks = refBlockList.Skip(indexOfRefVerseStart + i).Take(numberOfUnmatchedRefBlocks); if (numberOfVernBlocksInVerseChunk == 1 && numberOfUnmatchedRefBlocks > 1) { // Since there's only one vernacular block for this verse (or verse bridge), just combine all // ref blocks into one and call it a match. vernBlockInVerseChunk.SetMatchedReferenceBlock(bookNum, vernacularVersification, this, remainingRefBlocks); } else { var remainingRefBlocksList = remainingRefBlocks.ToList(); if (!remainingRefBlocksList.Any()) { // do nothing (PG-1085) } else if (forceMatch) { var refBlock = remainingRefBlocksList[0]; for (int rb = 1; rb < remainingRefBlocksList.Count; rb++) { refBlock.CombineWith(remainingRefBlocksList[rb]); } vernBlockList[iVernBlock].SetMatchedReferenceBlock(refBlock); } else { vernBlockList[iVernBlock].MatchesReferenceText = false; vernBlockList[iVernBlock].ReferenceBlocks = remainingRefBlocksList; } iRefBlock = indexOfRefVerseStart + numberOfRefBlocksInVerseChunk - 1; } break; } } var indexOfLastVernVerseInVerseChunk = indexOfVernVerseStart + numberOfVernBlocksInVerseChunk - 1; if (vernBlockList[indexOfLastVernVerseInVerseChunk].MatchesReferenceText) { iVernBlock = indexOfLastVernVerseInVerseChunk; } } }
private void MatchVernBlocksToReferenceTextBlocks(IReadOnlyList <Block> vernBlockList, string bookId, ScrVers vernacularVersification, bool forceMatch = false) { int bookNum = BCVRef.BookToNumber(bookId); var refBook = Books.Single(b => b.BookId == bookId); var refBlockList = refBook.GetScriptBlocks(); int iRefBlock = 0; int iRefBlockMemory = -1; // The following is a minor performance enhancement to help when just hooking up one verse's worth of blocks. // In it's current form, it doesn't work for tests that have partial reference texts that don't start at verse 1. // Also, it doesn't take versification mappings into consideration. //if (vernBlockList[0].ChapterNumber > 1) //{ // iRefBlock = refBook.GetIndexOfFirstBlockForVerse(vernBlockList[0].ChapterNumber, ); // if (iRefBlock < 0) // { // // Apparently there is no reference text for the verse(s) we're interested in. // return; // } //} for (int iVernBlock = 0; iVernBlock < vernBlockList.Count; iVernBlock++, iRefBlock++) { var currentVernBlock = vernBlockList[iVernBlock]; if (iRefBlock >= refBlockList.Count) { if (iRefBlockMemory < 0) { Debug.Assert(iVernBlock > 0); if (currentVernBlock.LastVerseNum == vernBlockList[iVernBlock - 1].LastVerseNum) { // Either this is a section head, in which case we don't care until we get to the // next actual Scripture block; or the vernacular happens to have more blocks in the // last verse at the end of the book, in which case they will just go unmatched. continue; } // We still have more vernacular verses to consider. Most likely, this is an // additional alternate ending (in Mark). if (bookId != "MRK") { Logger.WriteMinorEvent("Reference text matching went off end of ref block list for " + $"vern block {currentVernBlock}"); } iRefBlock = refBook.GetIndexOfFirstBlockForVerse(currentVernBlock.ChapterNumber, currentVernBlock.InitialStartVerseNumber); } else { // This handles the only case I know of (and for which there is a test) where a versification pulls in verses // from the end of the book to an earlier spot, namely Romans 14:24-26 <- Romans 16:25-27. If we ever have this same // kind of behavior that is pulling from somewhere other than the *end* of the book, the logic to reset iRefBlock // based on iRefBlockMemory will need to be moved/added elsewhere. iRefBlock = iRefBlockMemory; iRefBlockMemory = -1; } } var currentRefBlock = refBlockList[iRefBlock]; var vernInitStartVerse = currentVernBlock.StartRef(bookNum, vernacularVersification); var refInitStartVerse = currentRefBlock.StartRef(bookNum, Versification); var type = CharacterVerseData.GetStandardCharacterType(currentVernBlock.CharacterId); switch (type) { case CharacterVerseData.StandardCharacter.BookOrChapter: if (currentVernBlock.IsChapterAnnouncement) { var refChapterBlock = new Block(currentVernBlock.StyleTag, currentVernBlock.ChapterNumber) { CharacterId = currentVernBlock.CharacterId }; refChapterBlock.BlockElements.Add(new ScriptText(GetFormattedChapterAnnouncement(bookId, currentVernBlock.ChapterNumber))); if (HasSecondaryReferenceText) { if (currentRefBlock.IsChapterAnnouncement && currentRefBlock.MatchesReferenceText) { refChapterBlock.SetMatchedReferenceBlock(currentRefBlock.ReferenceBlocks.Single().Clone()); } else if (!currentRefBlock.IsChapterAnnouncement) { // Find the reference text's chapter announcement to get the secondary reference text chapter announcement var i = iRefBlock + 1; while (i < refBlockList.Count) { var workingRefBlock = refBlockList[i]; var workingRefInitStartVerse = workingRefBlock.StartRef(bookNum, Versification); var compareResult = workingRefInitStartVerse.CompareTo(vernInitStartVerse); if (compareResult > 0) // break out early; we passed the verse reference, so there is no chapter label { break; } if (compareResult == 0 && workingRefBlock.IsChapterAnnouncement && workingRefBlock.MatchesReferenceText) { refChapterBlock.SetMatchedReferenceBlock(workingRefBlock.ReferenceBlocks.Single().Clone()); break; } i++; } } } currentVernBlock.SetMatchedReferenceBlock(refChapterBlock); if (!currentRefBlock.IsChapterAnnouncement && currentRefBlock.ChapterNumber == currentVernBlock.ChapterNumber) { iRefBlock--; } continue; } goto case CharacterVerseData.StandardCharacter.ExtraBiblical; // Book title case CharacterVerseData.StandardCharacter.ExtraBiblical: if (type == CharacterVerseData.GetStandardCharacterType(currentRefBlock.CharacterId)) { currentVernBlock.SetMatchedReferenceBlock(currentRefBlock); continue; } goto case CharacterVerseData.StandardCharacter.Intro; case CharacterVerseData.StandardCharacter.Intro: // This will be re-incremented in the for loop, so it effectively allows // the vern index to advance while keeping the ref index the same. iRefBlock--; continue; default: if (refInitStartVerse.CompareTo(vernInitStartVerse) > 0 || currentVernBlock.MatchesReferenceText) { iRefBlock--; continue; } break; } while (CharacterVerseData.IsCharacterExtraBiblical(currentRefBlock.CharacterId) || vernInitStartVerse > refInitStartVerse) { iRefBlock++; if (iRefBlock == refBlockList.Count) { return; // couldn't find a ref block to use at all. } currentRefBlock = refBlockList[iRefBlock]; refInitStartVerse = currentRefBlock.StartRef(bookNum, vernacularVersification); } var indexOfVernVerseStart = iVernBlock; var indexOfRefVerseStart = iRefBlock; BlockMatchup.AdvanceToCleanVerseBreak(vernBlockList, i => true, ref iVernBlock); var lastVernVerseFound = new VerseRef(bookNum, vernBlockList[iVernBlock].ChapterNumber, vernBlockList[iVernBlock].LastVerseNum, vernacularVersification); FindAllScriptureBlocksThroughVerse(refBlockList, lastVernVerseFound, ref iRefBlock, Versification); int numberOfVernBlocksInVerseChunk = iVernBlock - indexOfVernVerseStart + 1; int numberOfRefBlocksInVerseChunk = iRefBlock - indexOfRefVerseStart + 1; for (int i = indexOfVernVerseStart; i <= iVernBlock; i++) { if (vernBlockList[i].CharacterIs(bookId, CharacterVerseData.StandardCharacter.ExtraBiblical)) { numberOfVernBlocksInVerseChunk--; } } if (numberOfVernBlocksInVerseChunk == 1 && numberOfRefBlocksInVerseChunk > 1) { var lastRefBlockInVerseChunk = refBlockList[iRefBlock]; var refVerse = lastRefBlockInVerseChunk.StartRef(bookNum, Versification); if (lastVernVerseFound.CompareTo(refVerse) == 0 && lastVernVerseFound.BBBCCCVVV < refVerse.BBBCCCVVV) { // A versification difference has us pulling a verse from later in the text, so we need to get out and look beyond our current // index in the ref text, but remember this spot so we can come back to it. iRefBlockMemory = indexOfRefVerseStart; iRefBlock--; iVernBlock--; continue; } // Since there's only one vernacular block for this verse (or verse bridge), just combine all // ref blocks into one and call it a match. vernBlockList[indexOfVernVerseStart].SetMatchedReferenceBlock(bookNum, vernacularVersification, this, refBlockList.Skip(indexOfRefVerseStart).Take(numberOfRefBlocksInVerseChunk)); continue; } for (int i = 0; i < numberOfVernBlocksInVerseChunk && i < numberOfRefBlocksInVerseChunk; i++) { var vernBlockInVerseChunk = vernBlockList[indexOfVernVerseStart + i]; var refBlockInVerseChunk = refBlockList[indexOfRefVerseStart + i]; if (BlocksMatch(bookNum, vernBlockInVerseChunk, refBlockInVerseChunk, vernacularVersification) || (numberOfVernBlocksInVerseChunk == 1 && numberOfRefBlocksInVerseChunk == 1 && BlocksEndWithSameVerse(bookNum, vernBlockInVerseChunk, refBlockInVerseChunk, vernacularVersification))) { if (i == numberOfVernBlocksInVerseChunk - 1 && i < numberOfRefBlocksInVerseChunk - 1) { // This is the last vernacular block in the list, but we have more than one ref block to account for. if (numberOfRefBlocksInVerseChunk - i == 2 && // Exactly 2 reference blocks remaining to be assigned i > 0 && // There is a preceding vernacular block // The following line could be uncommented to constrain this only to the original intended condition // if we find cases where we are coming in here but shouldn't: //vernBlockInVerseChunk.CharacterIs(bookId, CharacterVerseData.StandardCharacter.Narrator) && // Can only safely combine reference blocks if they are for the same character: vernBlockList[indexOfVernVerseStart + i - 1].ReferenceBlocks.All(r => r.CharacterId == refBlockList[indexOfRefVerseStart + i + 1].CharacterId) && BlocksMatch(bookNum, vernBlockList[indexOfVernVerseStart + i - 1], // Preceding vern block's character & end ref are compatible refBlockList[indexOfRefVerseStart + i + 1], vernacularVersification)) // with following ref block { // This code was specifically written for PG-794, the case where the vernacular has the narrator announcing // the speech afterwards instead of beforehand (as is typically the case in the reference text). In that // case we want to assign the "he said" reference text to the current vernacular block and attach the // following reference text block to the preceding vernacular block. // Because we are not explicitly checking to see if this block is a narrator block, this condition can // also be matched in other rare cases (for a somewhat contrived example where the reference // text has a trailing "he said" but the vernacular does not, see unit test // ApplyTo_MultipleSpeakersInVerse_SpeakersBeginCorrespondingThenDoNotCorrespond_ReferenceTextCopiedIntoBestMatchedVerseBlocks.) // Even in such cases, it seems likely that we would want to attach the following reference text // block to the preceding vernacular block if it is a better match. var precedingVernBlock = vernBlockList[indexOfVernVerseStart + i - 1]; precedingVernBlock.AppendUnmatchedReferenceBlock(refBlockList[indexOfRefVerseStart + i + 1]); vernBlockInVerseChunk.SetMatchedReferenceBlock(refBlockList[indexOfRefVerseStart + i]); } else { vernBlockInVerseChunk.SetUnmatchedReferenceBlocks(refBlockList.Skip(indexOfRefVerseStart + i).Take(numberOfRefBlocksInVerseChunk - i)); } break; } vernBlockInVerseChunk.SetMatchedReferenceBlock(refBlockList[indexOfRefVerseStart + i]); } else { iVernBlock = indexOfVernVerseStart + i; if (vernBlockList[iVernBlock].CharacterIs(bookId, CharacterVerseData.StandardCharacter.ExtraBiblical)) { iVernBlock++; } int j = 0; var iLastVernBlockMatchedFromBottomUp = -1; if (numberOfVernBlocksInVerseChunk - i >= 2) { // Look from the bottom up for (; j < numberOfVernBlocksInVerseChunk && j + i < numberOfRefBlocksInVerseChunk; j++) { var iCurrVernBottomUp = indexOfVernVerseStart + numberOfVernBlocksInVerseChunk - j - 1; vernBlockInVerseChunk = vernBlockList[iCurrVernBottomUp]; if (vernBlockInVerseChunk.MatchesReferenceText) { break; } refBlockInVerseChunk = refBlockList[indexOfRefVerseStart + numberOfRefBlocksInVerseChunk - j - 1]; if (BlocksMatch(bookNum, vernBlockInVerseChunk, refBlockInVerseChunk, vernacularVersification)) { vernBlockInVerseChunk.SetMatchedReferenceBlock(refBlockInVerseChunk); iLastVernBlockMatchedFromBottomUp = iCurrVernBottomUp; } else { break; } } } var numberOfUnmatchedRefBlocks = numberOfRefBlocksInVerseChunk - i - j; var remainingRefBlocks = refBlockList.Skip(indexOfRefVerseStart + i).Take(numberOfUnmatchedRefBlocks); if (numberOfVernBlocksInVerseChunk == 1 && numberOfUnmatchedRefBlocks > 1) { // Since there's only one vernacular block for this verse (or verse bridge), just combine all // ref blocks into one and call it a match. vernBlockInVerseChunk.SetMatchedReferenceBlock(bookNum, vernacularVersification, this, remainingRefBlocks); } else { var remainingRefBlocksList = remainingRefBlocks.ToList(); if (!remainingRefBlocksList.Any()) { // do nothing (PG-1085) } else if (forceMatch) { var refBlock = remainingRefBlocksList[0]; for (int rb = 1; rb < remainingRefBlocksList.Count; rb++) { refBlock.CombineWith(remainingRefBlocksList[rb]); } vernBlockList[iVernBlock].SetMatchedReferenceBlock(refBlock); } else { if (remainingRefBlocksList.Count == 1 && vernBlockList[iVernBlock].MatchesReferenceText && vernBlockList[iVernBlock].ReferenceBlocks.Single().CharacterId != remainingRefBlocksList[0].CharacterId) { // See if the immediately following or preceding block is a better match var otherIndicesToTry = (i <= iVernBlock) ? new [] { iVernBlock - 1, iVernBlock + 1 } : new [] { iVernBlock + 1, iVernBlock - 1 }; foreach (var iPreOrPost in otherIndicesToTry.Where(o => vernBlockList.Count > o && o >= 0)) { if (vernBlockList[iPreOrPost].ReferenceBlocks.FirstOrDefault()?.CharacterId == remainingRefBlocksList[0].CharacterId || vernBlockList[iPreOrPost].CharacterId == vernBlockList[iVernBlock].CharacterId) { if (!vernBlockList[iPreOrPost].ReferenceBlocks.Any()) { vernBlockList[iPreOrPost].SetUnmatchedReferenceBlocks(remainingRefBlocksList); } else if (iPreOrPost < iVernBlock) // Pre { vernBlockList[iPreOrPost].AppendUnmatchedReferenceBlocks(remainingRefBlocksList); } else // Post { vernBlockList[iPreOrPost].InsertUnmatchedReferenceBlocks(0, remainingRefBlocksList); } remainingRefBlocksList = null; break; } } } if (remainingRefBlocksList != null) { if (vernBlockList[iVernBlock].ReferenceBlocks.Any()) { // After matching things up as best we could from the top down and the bottom up, we // ran out of suitable "holes" in the vernacular. Since our "target" vernacular block // already has a reference block, we either need to prepend or append any other // unmatched ref blocks. We don't want to change the order of the ref blocks, so we // have to be careful. The variable i represents where we got to in our (top-down) // matching, so generally if we didn't get all the way down to our target block, we // insert the remaining ref blocks before and if we got past it, then we append to // the end. But if we attached the reference block to our target block in the bottom- // up matching, then the remaining blocks are actually *before* the existing matched // block, so we need to insert. Really, iLastVernBlockMatchedFromBottomUp could just // be a Boolean flag: fMatchedTargetVernBlockDuringBottomUpMatching. if (i < iVernBlock || i == iLastVernBlockMatchedFromBottomUp) { vernBlockList[iVernBlock].InsertUnmatchedReferenceBlocks(0, remainingRefBlocksList); } else { vernBlockList[iVernBlock].AppendUnmatchedReferenceBlocks(remainingRefBlocksList); } } else { vernBlockList[iVernBlock].SetUnmatchedReferenceBlocks(remainingRefBlocksList); } } } iRefBlock = indexOfRefVerseStart + numberOfRefBlocksInVerseChunk - 1; } break; } } var indexOfLastVernVerseInVerseChunk = indexOfVernVerseStart + numberOfVernBlocksInVerseChunk - 1; if (vernBlockList[indexOfLastVernVerseInVerseChunk].ReferenceBlocks.Any()) { iVernBlock = indexOfLastVernVerseInVerseChunk; } } }