/// <summary>
            /// Get a random second word weighted as likely to occur between a specified first and third word. Essentially acts as a pseudo Trigram lookup.
            /// </summary>
            /// <param name="firstWord">The word before the word being queried for.</param>
            /// <param name="secondWordPOS">The part of speech of the word being queried for.</param>
            /// <param name="thirdWord">The word after the word being queried for.</param>
            /// <returns>A word most likely to appear between the specified first and third words.</returns>
            public Word GetRandomBetweenTwoWords(Word firstWord, PartOfSpeech secondWordPOS, Word thirdWord)
            {
                Word             randomWord       = null;
                CachedBigramList firstBigramList  = BigramDictionary.Instance.GetBigramListByPOS(firstWord.PoS, secondWordPOS);
                CachedBigramList secondBigramList = BigramDictionary.Instance.GetBigramListByPOS(secondWordPOS, thirdWord.PoS);

                if ((firstBigramList != null && secondBigramList != null) && (firstBigramList.entries.Count > 0 && secondBigramList.entries.Count > 0))
                {
                    CachedBigramList narrowedFirstBigramList  = NarrowListToWord(firstBigramList, firstWord, true);
                    CachedBigramList narrowedSecondBigramList = NarrowListToWord(secondBigramList, thirdWord, false);
                    if (narrowedFirstBigramList != null && narrowedSecondBigramList != null)
                    {
                        firstBigramList  = narrowedFirstBigramList;
                        secondBigramList = narrowedSecondBigramList;
                        List <Word> intersectingWords = IntersectedBigramLists(firstBigramList, secondBigramList);
                        if (intersectingWords != null && intersectingWords.Count != 0)
                        {
                            randomWord = wordDictLookup.GetWeightedRandomWordFromList(intersectingWords, intersectingWords.Sum(w => w.Frequency));
                        }
                    }
                }
                if (randomWord == null)
                {
                    if ((firstBigramList == null || secondBigramList == null) || (firstBigramList.entries.Count == 0 || secondBigramList.entries.Count == 0))
                    {
                        randomWord = wordDictLookup.GetRandomWordByPOS(secondWordPOS);
                    }
                    else
                    {
                        randomWord = ((float)rand.NextDouble() > 0.5f) ?
                                     GetWeightedRandomEntryFromList(firstBigramList.entries, firstBigramList.totalFrequency).Second
                            : GetWeightedRandomEntryFromList(secondBigramList.entries, secondBigramList.totalFrequency).First;
                    }
                }

                return(randomWord);
            }
            public void Populate(Dictionary <int, Word> populatedGroupWords, BigramDictionaryLookup bigramLookup, WordDictionaryLookup wordLookup)
            {
                foreach (TemplateWord word in words)
                {
                    if (word.IsHook)
                    {
                        word.Word = null;
                    }
                }

                List <int> indicesOfCurrentPassToExecute = new List <int>();

                do
                {
                    foreach (int index in indicesOfCurrentPassToExecute)
                    {
                        TemplateWord currentWord = words[index];
                        TemplateWord wordBefore  = (index > 0 && !words[index - 1].NeedsHookPopulated) ? words[index - 1] : null;
                        TemplateWord wordAfter   = (index < words.Count - 1 && !words[index + 1].NeedsHookPopulated) ? words[index + 1] : null;

                        bool populatedByGroup = false;
                        if (currentWord.group != 0 && populatedGroupWords.Count > 0)
                        {
                            Word wordForGroup = null;
                            if (populatedGroupWords.TryGetValue(currentWord.group, out wordForGroup))
                            {
                                populatedByGroup = true;
                                currentWord.Word = wordForGroup;
                            }
                        }
                        if (!populatedByGroup)
                        {
                            if (wordBefore != null && wordAfter != null)
                            {
                                currentWord.Word = bigramLookup.GetRandomBetweenTwoWords(wordBefore.Word, currentWord.PoS, wordAfter.Word);
                            }
                            else if (wordBefore != null)
                            {
                                currentWord.Word = bigramLookup.GetRandomSecondWord(wordBefore.Word, currentWord.PoS);
                            }
                            else if (wordAfter != null)
                            {
                                currentWord.Word = bigramLookup.GetRandomFirstWord(currentWord.PoS, wordAfter.Word);
                            }
                            else
                            {
                                currentWord.Word = wordLookup.GetRandomWordByPOS(currentWord.PoS);
                            }

                            if (currentWord.group != 0)
                            {
                                populatedGroupWords.Add(currentWord.group, currentWord.Word);
                            }
                        }
                        //TODO: yield return for coroutine implementation if needed
                    }
                    indicesOfCurrentPassToExecute.Clear();

                    bool areStillHooks   = false;
                    bool lastWordWasHook = false;
                    for (int i = 0; i < words.Count; i++)
                    {
                        TemplateWord tWord = words[i];

                        bool currentWordIsHook = tWord.NeedsHookPopulated;
                        areStillHooks |= currentWordIsHook;

                        if (currentWordIsHook ^ lastWordWasHook)
                        {
                            if (currentWordIsHook && i > 0)
                            {
                                indicesOfCurrentPassToExecute.Add(i);
                            }

                            if (lastWordWasHook)
                            {
                                indicesOfCurrentPassToExecute.Add(i - 1);
                            }
                        }

                        lastWordWasHook = currentWordIsHook;
                    }

                    if (areStillHooks && indicesOfCurrentPassToExecute.Count == 0)
                    {
                        int times = (int)(rand.NextDouble() * (words.Count - 1)) + 1;
                        for (int i = 0; i < times; i++)
                        {
                            indicesOfCurrentPassToExecute.Add((int)(rand.NextDouble() * words.Count));
                        }
                    }
                }while (indicesOfCurrentPassToExecute.Count != 0);
            }