internal IEnumerable <SymmetricGroup> Join(
        Dictionary <char, List <Word> > words1,
        Dictionary <char, List <Word> > words2,
        DifferenceNode differenceRoot1,
        DifferenceNode differenceRoot2)
    {
        int len1 = Sentence.Length;
        int len2 = ReverseSentence.Length;

        if (len1 < len2)
        {
            List <Word> words;
            if (!words1.TryGetValue(Difference.CharAtFromEnd(0), out words))
            {
                yield break;
            }
            foreach (Word word in words)
            {
                bool canJoin          = true;
                int  charCountToCheck = Math.Min(word.OriginWord.Length, len2 - len1);
                for (int i = 1; i < charCountToCheck; i++)
                {
                    if (word.OriginWord[i] != Difference.CharAtFromEnd(i))
                    {
                        canJoin = false;
                        break;
                    }
                }

                if (canJoin)
                {
                    Difference newDifference;
                    if (len2 - len1 >= word.OriginWord.Length)
                    {
                        string baseWord = ReverseSentence.Words.First.OriginWord;
                        newDifference = new Difference(baseWord, 0, len2 - len1 - word.OriginWord.Length);
                        if (!differenceRoot2.AddReverse(newDifference, 0))
                        {
                            continue;
                        }
                    }
                    else
                    {
                        newDifference = new Difference(word.OriginWord, len2 - len1, word.OriginWord.Length - len2 + len1);
                        if (!differenceRoot1.Add(newDifference, 0))
                        {
                            continue;
                        }
                    }
                    yield return(new SymmetricGroup(Sentence.Append(word), ReverseSentence, newDifference));
                }
            }
        }
        if (len1 > len2)
        {
            List <Word> words;
            if (!words2.TryGetValue(Difference.CharAt(0), out words))
            {
                yield break;
            }
            foreach (Word word in words)
            {
                bool canJoin          = true;
                int  charCountToCheck = Math.Min(word.OriginWord.Length, len1 - len2);
                for (int i = 1; i < charCountToCheck; i++)
                {
                    if (word.OriginWord[word.OriginWord.Length - 1 - i] != Difference.CharAt(i))
                    {
                        canJoin = false;
                        break;
                    }
                }

                if (canJoin)
                {
                    Difference newDifference;
                    if (len1 - len2 >= word.OriginWord.Length)
                    {
                        string baseWord = Sentence.Words.Last.OriginWord;
                        newDifference = new Difference(baseWord, baseWord.Length - len1 + len2 + word.OriginWord.Length, len1 - len2 - word.OriginWord.Length);
                        if (!differenceRoot1.Add(newDifference, 0))
                        {
                            continue;
                        }
                    }
                    else
                    {
                        newDifference = new Difference(word.OriginWord, 0, word.OriginWord.Length - len1 + len2);
                        if (!differenceRoot2.AddReverse(newDifference, 0))
                        {
                            continue;
                        }
                    }
                    yield return(new SymmetricGroup(Sentence, ReverseSentence.Prepend(word), newDifference));
                }
            }
        }
    }