private IOrderedEnumerable<SentenceElement> _compileMainPart(Stage4ResultElement mainPart)
        {
            var result = new List<SentenceElement> { mainPart };

            result.AddRange(mainPart.ServiceParts);
            result.AddRange(mainPart.AddedWordsCase1);
            result.AddRange(mainPart.AddedWordsCase2);

            return result.OrderBy(word => word.Order);
        }
        public override void ProcessSentence(Sentence sentence)
        {
            _sentence = sentence;
            Debug.Assert(sentence.ElementList != null);
            Debug.Assert(sentence.ElementList.Count > 0);
            List<SentenceElement> words = sentence.ElementList;

            // разворачиваем все, что добавили на 3-м этапе в список резльтатов 4-го и после этого плюсуем к ним СЧР
            foreach (var itemStage3 in _mainPartsStage3Result.Items.FindAll(x => x.SyntacticRole.Value != SyntacticRole.Predicate.Value))
            {
                Stage4ResultElement item = new Stage4ResultElement();
                item.CopyFromSourceWord(itemStage3);
                item.SimpleSentenceNr = itemStage3.SimpleSentenceNr;

                Result.Items.Add(item);

                foreach (var substantivatorItem in itemStage3.AddedWordsCase1)
                {
                    Stage4ResultElement sItem = new Stage4ResultElement();
                    sItem.CopyFromSourceWord(substantivatorItem);
                    sItem.IsSubstantivator = true;
                    // проверяем, что элемента еще нет
                    if (Result.Items.Find(x => x.Id == sItem.Id) == null)
                        Result.Items.Add(sItem);
                }

                foreach (var quantativeItem in itemStage3.AddedWordsCase2)
                {
                    Stage4ResultElement qItem = new Stage4ResultElement();
                    qItem.CopyFromSourceWord(quantativeItem);
                    qItem.IsQuantativePart = true;
                    // проверяем, что элемента еще нет
                    if (Result.Items.Find(x => x.Id == qItem.Id) == null)
                        Result.Items.Add(qItem);
                }
            }

            // для сказуемых сложнее, другая обработка
            foreach (var itemStage3 in _mainPartsStage3Result.Items.FindAll(x => x.SyntacticRole.Value == SyntacticRole.Predicate.Value))
            {
                Stage4ResultElement item = new Stage4ResultElement();
                item.CopyFromSourceWord(itemStage3);
                item.SimpleSentenceNr = itemStage3.SimpleSentenceNr;
                item.AddedWordsCase1 = itemStage3.AddedWordsCase1;
                item.AddedWordsCase2 = itemStage3.AddedWordsCase2;

                Result.Items.Add(item);
            }

            foreach (var itemStage4 in Result.Items.FindAll(x => x.SyntacticRole.Value == SyntacticRole.Predicate.Value))
            {
                // находим id элемента, который должен остаться в текущем сказуемом
                var addedWords = itemStage4.AddedWordsCase1.Concat(itemStage4.AddedWordsCase2).ToList();
                string specialId;
                if (addedWords.Count() > 0)
                {
                    SentenceElement e = addedWords.OrderBy(x => System.Math.Abs(x.Order - itemStage4.Order)).First();
                    specialId = sentence.ElementList.Find(y => y.Order == e.Order).Id;
                }
                else
                    specialId = "-1";

                // все элементы, которые не являются специальными добавляем в корень результатов и удаляем из доп слов
                var addedWordsIds = new List<string>();
                addedWordsIds.AddRange(itemStage4.AddedWordsCase1.Concat(itemStage4.AddedWordsCase2).Select(x => x.Id));

                foreach (var additionalItem in addedWordsIds)
                {
                    Stage4ResultElement sItem = new Stage4ResultElement();
                    sItem.CopyFromSourceWord(_sentence.ElementList.Find(x => x.Id == additionalItem));
                    // проверяем, что элемент может быть добавлен как отдельный
                    if (sItem.Id != specialId)
                    {
                        if (Result.Items.Find(x => x.Id == sItem.Id) == null)
                        {
                            // удаляем элемент из AddedWords
                            if (itemStage4.AddedWordsCase1.Find(x => x.Id == sItem.Id) != null)
                                itemStage4.AddedWordsCase1.RemoveAll(x => x.Id == sItem.Id);
                            if (itemStage4.AddedWordsCase2.Find(x => x.Id == sItem.Id) != null)
                                itemStage4.AddedWordsCase2.RemoveAll(x => x.Id == sItem.Id);

                            Result.Items.Add(sItem);
                        }
                    }
                }
            }

            // для всех элементов делаем двойной проход по СЧР с учетом addedwords
            foreach (var itemStage4 in Result.Items.FindAll(x => !x.IsRestored))
            {
                itemStage4.ServiceParts.AddRange(words.FindAll(x =>
                    (x.SyntacticParentWordId == itemStage4.Id || itemStage4.AddedWordsCase1.Concat(itemStage4.AddedWordsCase2).ToList().Find(y => y.Id == x.SyntacticParentWordId) != null)
                && (x.IsServicePart)));

                // добавляем второй проход по выявленным СЧР, чтобы выявить ситуации "не были удовлетворены" (частица + вспом.глагол + СЧР)
                List<SentenceElement> servicePartsSecondLevel = new List<SentenceElement>();
                foreach (var servicePart in itemStage4.ServiceParts)
                    servicePartsSecondLevel.AddRange(words.FindAll(x => (x.SyntacticParentWordId == servicePart.Id) && (x.IsServicePart)));
                itemStage4.ServiceParts.AddRange(servicePartsSecondLevel);

                itemStage4.ServiceParts.AddRange(itemStage4.AddedWordsCase1);
                itemStage4.ServiceParts.AddRange(itemStage4.AddedWordsCase2);
            }

            // определяем финальный тип
            foreach (var itemStage4 in Result.Items)
            {

                if (itemStage4.IsQuantativePart)
                    itemStage4.FinalSyntacticRole = "Подлежащее(Q)";
                else if (itemStage4.IsSubstantivator)
                    itemStage4.FinalSyntacticRole = "Подлежащее(S)";
                else if (itemStage4.SyntacticRole.Value == SyntacticRole.Predicate.Value)
                    itemStage4.FinalSyntacticRole = "Сказуемое";
                else if ((itemStage4.SyntacticRole.Value == SyntacticRole.Subject.Value) || (itemStage4.GrammarInfo.PartOfSpeech.Value == GrammarInfoPartOfSpeech.Noun.Value))
                    itemStage4.FinalSyntacticRole = "Подлежащее";
                else
                    itemStage4.FinalSyntacticRole = "";
            }

            // добавляем номера ПП и удаляем addedwords
            foreach (var resultItem in Result.Items)
            {
                resultItem.AddedWordsCase1.Clear();
                resultItem.AddedWordsCase2.Clear();

                var allIdsOfSimpleSentences = new List<string>();
                resultItem.SimpleSentenceNr = -1;

                foreach (var ssItem in _simpleSentenceStage3Result.Items.FindAll(x => x.IsCorrect))
                {
                    allIdsOfSimpleSentences.Clear();
                    allIdsOfSimpleSentences.Add(ssItem.Id);
                    allIdsOfSimpleSentences.AddRange(ssItem.ChainWordsIds.All.Values);

                    if (allIdsOfSimpleSentences.Contains(resultItem.Id))
                    {
                        resultItem.SimpleSentenceNr = ssItem.SimpleSentenceNr;
                        break;
                    }
                }
            }
        }