public ActionResult Index(
            long[] matterIds,
            int characteristicTypeLinkId,
            int notationId,
            int[] featureIds,
            string maxDifference,
            string excludeType)
        {
            return Action(() =>
            {
                if (matterIds.Length != 2)
                {
                    throw new ArgumentException("Count of selected matters must be 2.", "matterIds");
                }

                var firstMatterId = matterIds[0];
                var firstParentSequenceId = db.CommonSequence.Single(c => c.MatterId == firstMatterId && c.NotationId == notationId).Id;
                Subsequence[] firstSequenceSubsequences = subsequenceExtractor.GetSubsequences(firstParentSequenceId, featureIds);
                var firstSequences = subsequenceExtractor.ExtractChains(firstSequenceSubsequences, firstParentSequenceId);
                var firstSequenceCharacteristics = CalculateCharacteristic(characteristicTypeLinkId, firstSequences, firstSequenceSubsequences);
                var firstDbSubsequencesAttributes = sequenceAttributeRepository.GetAttributes(firstSequenceSubsequences.Select(s => s.Id));
                var firstSequenceAttributes = new List<AttributeValue[]>();
                foreach (var subsequence in firstSequenceSubsequences)
                {
                    AttributeValue[] attributes;
                    if (!firstDbSubsequencesAttributes.TryGetValue(subsequence.Id, out attributes))
                    {
                        attributes = new AttributeValue[0];
                    }

                    firstSequenceAttributes.Add(attributes);
                }

                var secondMatterId = matterIds[1];
                var secondParentSequenceId = db.CommonSequence.Single(c => c.MatterId == secondMatterId && c.NotationId == notationId).Id;
                Subsequence[] secondSequenceSubsequences = subsequenceExtractor.GetSubsequences(secondParentSequenceId, featureIds);
                var secondSequences = subsequenceExtractor.ExtractChains(secondSequenceSubsequences, secondParentSequenceId);
                var secondSequenceCharacteristics = CalculateCharacteristic(characteristicTypeLinkId, secondSequences, secondSequenceSubsequences);
                var secondDbSubsequencesAttributes = sequenceAttributeRepository.GetAttributes(secondSequenceSubsequences.Select(s => s.Id));
                var secondSequenceAttributes = new List<AttributeValue[]>();
                foreach (var subsequence in secondSequenceSubsequences)
                {
                    AttributeValue[] attributes;
                    if (!secondDbSubsequencesAttributes.TryGetValue(subsequence.Id, out attributes))
                    {
                        attributes = new AttributeValue[0];
                    }

                    secondSequenceAttributes.Add(attributes);
                }

                double difference = double.Parse(maxDifference, CultureInfo.InvariantCulture);

                var similarSubsequences = new List<IntPair>();

                for (int i = 0; i < firstSequenceCharacteristics.Count; i++)
                {
                    for (int j = 0; j < secondSequenceCharacteristics.Count; j++)
                    {
                        if (Math.Abs(firstSequenceCharacteristics[i] - secondSequenceCharacteristics[j]) <= difference)
                        {
                            similarSubsequences.Add(new IntPair(i, j));

                            if (excludeType == "Exclude")
                            {
                                firstSequenceCharacteristics[i] = double.NaN;
                                secondSequenceCharacteristics[j] = double.NaN;
                            }
                        }
                    }
                }

                var characteristicName = characteristicTypeLinkRepository.GetCharacteristicName(characteristicTypeLinkId, notationId);

                var similarity = similarSubsequences.Count * 200d / (firstSequenceSubsequences.Length + secondSequenceSubsequences.Length);

                var firstSequenceSimilarity = similarSubsequences.Count * 100d / firstSequenceSubsequences.Length;

                var secondSequenceSimilarity = similarSubsequences.Count * 100d / secondSequenceSubsequences.Length;

                return new Dictionary<string, object>
                {
                    { "firstSequenceName", db.Matter.Single(m => m.Id == firstMatterId).Name },
                    { "secondSequenceName", db.Matter.Single(m => m.Id == secondMatterId).Name },
                    { "characteristicName", characteristicName },
                    { "similarSubsequences", similarSubsequences },
                    { "similarity", similarity },
                    { "firstSequenceSimilarity", firstSequenceSimilarity },
                    { "secondSequenceSimilarity", secondSequenceSimilarity },
                    { "firstSequenceSubsequences", firstSequenceSubsequences },
                    { "secondSequenceSubsequences", secondSequenceSubsequences },
                    { "firstSequenceAttributes", firstSequenceAttributes },
                    { "secondSequenceAttributes", secondSequenceAttributes }
                };
            });
        }
        /// <summary>
        /// Calculates subsequences characteristics.
        /// </summary>
        /// <param name="characteristicTypeLinkIds">
        /// The characteristic type link ids.
        /// </param>
        /// <param name="featureIds">
        /// The features ids.
        /// </param>
        /// <param name="parentSequenceId">
        /// The parent sequence id.
        /// </param>
        /// <param name="calculators">
        /// The calculators.
        /// </param>
        /// <param name="links">
        /// The links.
        /// </param>
        /// <param name="attributeValues">
        /// Nonredundant array of all attributes.
        /// </param>
        /// <param name="filters">
        /// Textual search filters for subsequences products.
        /// </param>
        /// <returns>
        /// The <see cref="T:SubsequenceData[]"/>.
        /// </returns>
        public static SubsequenceData[] CalculateSubsequencesCharacteristics(
            int[] characteristicTypeLinkIds,
            int[] featureIds,
            long parentSequenceId,
            IFullCalculator[] calculators,
            Link[] links,
            List<AttributeValue> attributeValues,
            string[] filters = null)
        {
            // creating local context to avoid memory overflow due to possibly big cache of characteristics
            using (var context = new LibiadaWebEntities())
            {
                var subsequenceExtractor = new SubsequenceExtractor(context);
                var sequenceAttributeRepository = new SequenceAttributeRepository(context);
                var attributeRepository = new AttributeRepository();
                var newCharacteristics = new List<Characteristic>();

                // extracting data from database
                var dbSubsequences = filters == null ? subsequenceExtractor.GetSubsequences(parentSequenceId, featureIds) : subsequenceExtractor.GetSubsequences(parentSequenceId, featureIds, filters);
                var subsequenceIds = dbSubsequences.Select(s => s.Id).ToArray();
                var dbSubsequencesAttributes = sequenceAttributeRepository.GetAttributes(subsequenceIds);

                var dbCharacteristics = context.Characteristic.Where(c => characteristicTypeLinkIds.Contains(c.CharacteristicTypeLinkId) && subsequenceIds.Contains(c.SequenceId))
                        .ToArray()
                        .GroupBy(c => c.SequenceId)
                        .ToDictionary(c => c.Key, c => c.ToDictionary(ct => ct.CharacteristicTypeLinkId, ct => ct.Value));

                // converting to libiada sequences
                var sequences = subsequenceExtractor.ExtractChains(dbSubsequences, parentSequenceId);
                var subsequenceData = new SubsequenceData[sequences.Length];

                // cycle through subsequences
                for (int i = 0; i < sequences.Length; i++)
                {
                    var values = new double[characteristicTypeLinkIds.Length];
                    Dictionary<int, double> sequenceDbCharacteristics;
                    if (!dbCharacteristics.TryGetValue(dbSubsequences[i].Id, out sequenceDbCharacteristics))
                    {
                        sequenceDbCharacteristics = new Dictionary<int, double>();
                    }

                    // cycle through characteristics and notations
                    for (int j = 0; j < characteristicTypeLinkIds.Length; j++)
                    {
                        int characteristicTypeLinkId = characteristicTypeLinkIds[j];
                        if (!sequenceDbCharacteristics.TryGetValue(characteristicTypeLinkId, out values[j]))
                        {
                            values[j] = calculators[j].Calculate(sequences[i], links[j]);
                            var currentCharacteristic = new Characteristic
                            {
                                SequenceId = dbSubsequences[i].Id,
                                CharacteristicTypeLinkId = characteristicTypeLinkId,
                                Value = values[j]
                            };

                            newCharacteristics.Add(currentCharacteristic);
                        }
                    }

                    AttributeValue[] attributes;
                    if (!dbSubsequencesAttributes.TryGetValue(dbSubsequences[i].Id, out attributes))
                    {
                        attributes = new AttributeValue[0];
                    }

                    var attributeIndexes = new int[attributes.Length];
                    for (int j = 0; j < attributes.Length; j++)
                    {
                        if (!attributeValues.Contains(attributes[j]))
                        {
                            attributeValues.Add(attributes[j]);
                        }

                        attributeIndexes[j] = attributeValues.IndexOf(attributes[j]);
                    }

                    subsequenceData[i] = new SubsequenceData(dbSubsequences[i], values, attributeIndexes);
                }

                // trying to save calculated characteristics to database
                var characteristicRepository = new CharacteristicRepository(context);
                characteristicRepository.TrySaveCharacteristicsToDatabase(newCharacteristics);

                return subsequenceData;
            }
        }