Ejemplo n.º 1
0
        /// <summary>Calculate the minimum number of blocks between two character ids in given collection</summary>
        public MinimumProximity CalculateMinimumProximity(ISet <string> characterIdsToCalculate)
        {
            if (!characterIdsToCalculate.Any())
            {
                return(new MinimumProximity(Int32.MaxValue, null, null, null, null));
            }

            RelatedCharactersData relChar          = RelatedCharactersData.Singleton;
            bool          foundFirst               = false;
            int           currentBlockCount        = 0;
            int           minBlockProximity        = Int32.MaxValue;
            string        prevCharacterId          = null;
            ISet <string> prevMatchingCharacterIds = null;
            BookScript    firstBook           = null;
            Block         firstBlock          = null;
            BookScript    secondBook          = null;
            Block         secondBlock         = null;
            BookScript    prevBook            = null;
            Block         prevBlock           = null;
            bool          breakOutOfBothLoops = false;

            bool calculateAnyRelatedCharacters = characterIdsToCalculate.Any(c => relChar.HasMatchingCharacterIdsOfADifferentAge(c));

            foreach (var book in m_booksToConsider)
            {
                if (breakOutOfBothLoops)
                {
                    break;
                }

                var countVersesRatherThanBlocks = !m_referenceText.HasContentForBook(book.BookId);

                var treatAsSameCharacter = m_considerSameExtrabiblicalCharacter[book];
                if (m_narrationByAuthor)
                {
                    var author = BiblicalAuthors.GetAuthorOfBook(book.BookId);
                    if (author.CombineAuthorAndNarrator && !treatAsSameCharacter.Any(set => set.Contains(author.Name)))
                    {
                        if (!m_strictlyAdhereToNarratorPreferences || (characterIdsToCalculate.Contains(book.NarratorCharacterId) && characterIdsToCalculate.Contains(author.Name)))
                        {
                            HashSet <string> charactersToTreatAsOneWithNarrator = treatAsSameCharacter.FirstOrDefault(set => set.Contains(book.NarratorCharacterId));
                            if (charactersToTreatAsOneWithNarrator == null)
                            {
                                charactersToTreatAsOneWithNarrator = new HashSet <string>();
                                treatAsSameCharacter.Add(charactersToTreatAsOneWithNarrator);
                                charactersToTreatAsOneWithNarrator.Add(book.NarratorCharacterId);
                                charactersToTreatAsOneWithNarrator.Add(author.Name);
                            }
                            else
                            {
                                charactersToTreatAsOneWithNarrator.Add(author.Name);
                            }
                        }
                    }
                }

                // We don't want to treat book ends as being directly adjacent but not infinitely distant, either.
                currentBlockCount += kDefaultMinimumBlocks * 5 / 3;                 // The amount of padding is somewhat arbitrary.

                foreach (var block in book.Blocks)
                {
                    var characterId = block.CharacterIdInScript;

                    // The original logic here was NOT split out for the single character vs. multiple character scenarios.
                    // This made the code much more readable, but the performance was atrocious since we were creating
                    // extra hashsets and doing extra intersects. Please consider the performance implications of any
                    // changes to this code.  (I'm sure it could be optimized further, too...)

                    ISet <string> matchingCharacterIds = null;
                    if (calculateAnyRelatedCharacters &&
                        relChar.TryGetMatchingCharacterIdsOfADifferentAge(characterId, out matchingCharacterIds))
                    {
                        if (matchingCharacterIds.Count == 1)
                        {
                            matchingCharacterIds = null;
                        }
                    }
                    else
                    {
                        foreach (var set in treatAsSameCharacter)
                        {
                            if (set.Contains(characterId))
                            {
                                matchingCharacterIds = set;
                                break;
                            }
                        }
                    }

                    if (matchingCharacterIds == null)
                    {
                        if ((prevMatchingCharacterIds == null && prevCharacterId == characterId) ||
                            (prevMatchingCharacterIds != null && prevMatchingCharacterIds.Contains(characterId)))
                        {
                            currentBlockCount = 0;
                            prevBook          = book;
                            prevBlock         = block;
                        }
                        else if (characterIdsToCalculate.Contains(characterId) &&
                                 (!CharacterVerseData.IsCharacterOfType(characterId, CharacterVerseData.StandardCharacter.Narrator) ||
                                  prevCharacterId == null ||
                                  !CharacterVerseData.IsCharacterOfType(prevCharacterId, CharacterVerseData.StandardCharacter.Narrator)))
                        {
                            if (ProcessDifferentCharacter(book, block, characterId, matchingCharacterIds, ref foundFirst,
                                                          ref currentBlockCount, ref minBlockProximity,
                                                          ref firstBook, ref prevBook, ref firstBlock, ref prevBlock,
                                                          ref secondBook, ref secondBlock, ref breakOutOfBothLoops, ref prevCharacterId, ref prevMatchingCharacterIds))
                            {
                                break;
                            }
                        }
                        else
                        {
                            IncrementCount(countVersesRatherThanBlocks, block, ref currentBlockCount);
                        }
                    }
                    else if (prevMatchingCharacterIds != null && matchingCharacterIds.Intersect(prevMatchingCharacterIds).Any())
                    {
                        currentBlockCount        = 0;
                        prevBook                 = book;
                        prevBlock                = block;
                        prevMatchingCharacterIds = matchingCharacterIds;
                    }
                    else if (characterIdsToCalculate.Intersect(matchingCharacterIds).Any())
                    {
                        if (ProcessDifferentCharacter(book, block, characterId, matchingCharacterIds, ref foundFirst,
                                                      ref currentBlockCount, ref minBlockProximity,
                                                      ref firstBook, ref prevBook, ref firstBlock, ref prevBlock,
                                                      ref secondBook, ref secondBlock, ref breakOutOfBothLoops, ref prevCharacterId, ref prevMatchingCharacterIds))
                        {
                            break;
                        }
                    }
                    else
                    {
                        IncrementCount(countVersesRatherThanBlocks, block, ref currentBlockCount);
                    }
                }
            }

            return(new MinimumProximity(minBlockProximity, firstBook, secondBook, firstBlock, secondBlock));
        }
Ejemplo n.º 2
0
        /// <summary>Calculate the minimum number of blocks between two character ids in given collection</summary>
        public MinimumProximity CalculateMinimumProximity(ISet <string> characterIdsToCalculate)
        {
            if (!characterIdsToCalculate.Any())
            {
                return(new MinimumProximity(Int32.MaxValue, null, null, null, null));
            }

            RelatedCharactersData relChar          = RelatedCharactersData.Singleton;
            bool          foundFirst               = false;
            int           currentBlockCount        = 0;
            int           minProximity             = Int32.MaxValue;
            string        prevCharacterId          = null;
            ISet <string> prevMatchingCharacterIds = null;
            BookScript    firstBook           = null;
            Block         firstBlock          = null;
            BookScript    secondBook          = null;
            Block         secondBlock         = null;
            BookScript    prevBook            = null;
            Block         prevBlock           = null;
            bool          breakOutOfBothLoops = false;

            bool calculateAnyRelatedCharacters = characterIdsToCalculate.Any(c => relChar.HasMatchingCharacterIdsOfADifferentAge(c));

            foreach (var book in m_booksToConsider)
            {
                if (breakOutOfBothLoops)
                {
                    break;
                }

                var treatAsSameCharacter = m_considerSameExtrabiblicalCharacter[book];

                currentBlockCount += kDefaultMinimumProximity + 20;                 // 20 is a pretty arbitrary "magic number"

                foreach (var block in book.Blocks)
                {
                    var characterId = block.CharacterIdInScript;

                    // The original logic here was NOT split out for the single character vs. multiple character scenarios.
                    // This made the code much more readable, but the performance was atrocious since we were creating
                    // extra hashsets and doing extra intersects. Please consider the performance implications of any
                    // changes to this code.  (I'm sure it could be optimized further, too...)

                    ISet <string> matchingCharacterIds = null;
                    if (calculateAnyRelatedCharacters && relChar.TryGetMatchingCharacterIdsOfADifferentAge(characterId, out matchingCharacterIds))
                    {
                        if (matchingCharacterIds.Count == 1)
                        {
                            matchingCharacterIds = null;
                        }
                    }
                    else
                    {
                        foreach (var kvp in treatAsSameCharacter)
                        {
                            if (kvp.Value == null)
                            {
                                continue;
                            }

                            if (kvp.Value.Contains((characterId)))
                            {
                                matchingCharacterIds = kvp.Value;
                                break;
                            }
                        }
                    }

                    if (matchingCharacterIds == null)
                    {
                        if ((prevMatchingCharacterIds == null && prevCharacterId == characterId) || (prevMatchingCharacterIds != null && prevMatchingCharacterIds.Contains(characterId)))
                        {
                            currentBlockCount = 0;
                            prevBook          = book;
                            prevBlock         = block;
                        }
                        else if (characterIdsToCalculate.Contains(characterId) &&
                                 (!CharacterVerseData.IsCharacterOfType(characterId, CharacterVerseData.StandardCharacter.Narrator) ||
                                  prevCharacterId == null || !CharacterVerseData.IsCharacterOfType(prevCharacterId, CharacterVerseData.StandardCharacter.Narrator)))
                        {
                            if (ProcessDifferentCharacter(book, block, characterId, matchingCharacterIds, ref foundFirst, ref currentBlockCount, ref minProximity, ref firstBook, ref prevBook, ref firstBlock, ref prevBlock, ref secondBook, ref secondBlock, ref breakOutOfBothLoops, ref prevCharacterId, ref prevMatchingCharacterIds))
                            {
                                break;
                            }
                        }
                        else
                        {
                            currentBlockCount++;
                        }
                    }
                    else if (prevMatchingCharacterIds != null && matchingCharacterIds.Intersect(prevMatchingCharacterIds).Any())
                    {
                        currentBlockCount = 0;
                        prevBook          = book;
                        prevBlock         = block;
                    }
                    else if (characterIdsToCalculate.Intersect(matchingCharacterIds).Any())
                    {
                        if (ProcessDifferentCharacter(book, block, characterId, matchingCharacterIds, ref foundFirst, ref currentBlockCount, ref minProximity, ref firstBook, ref prevBook, ref firstBlock, ref prevBlock, ref secondBook, ref secondBlock, ref breakOutOfBothLoops, ref prevCharacterId, ref prevMatchingCharacterIds))
                        {
                            break;
                        }
                    }
                    else
                    {
                        currentBlockCount++;
                    }
                }
            }

            return(new MinimumProximity(minProximity, firstBook, secondBook, firstBlock, secondBlock));
        }