/// <summary>
        /// Calculation method.
        /// </summary>
        /// <param name="chainCharacteristicsIds">
        /// Dictionary with chain ids as a key
        /// and characteristicLink ids array as value.
        /// </param>
        /// <returns>
        /// The <see cref="T:double[][]"/>.
        /// </returns>
        public static Dictionary <long, Dictionary <short, double> > Calculate(Dictionary <long, short[]> chainCharacteristicsIds)
        {
            var newCharacteristics = new List <CharacteristicValue>();
            var allCharacteristics = new Dictionary <long, Dictionary <short, double> >();

            using (var db = new LibiadaWebEntities())
            {
                short[] characteristicLinkIds            = chainCharacteristicsIds.SelectMany(c => c.Value).Distinct().ToArray();
                var     characteristicTypeLinkRepository = FullCharacteristicRepository.Instance;
                var     calculators = new Dictionary <short, LinkedFullCalculator>();
                foreach (short characteristicLinkId in characteristicLinkIds)
                {
                    Link link = characteristicTypeLinkRepository.GetLinkForCharacteristic(characteristicLinkId);
                    FullCharacteristic characteristic = characteristicTypeLinkRepository.GetCharacteristic(characteristicLinkId);
                    calculators.Add(characteristicLinkId, new LinkedFullCalculator(characteristic, link));
                }

                var    commonSequenceRepository = new CommonSequenceRepository(db);
                long[] sequenceIds = chainCharacteristicsIds.Keys.ToArray();
                foreach (long sequenceId in sequenceIds)
                {
                    short[] sequenceCharacteristicLinkIds      = chainCharacteristicsIds[sequenceId];
                    Dictionary <short, double> characteristics = db.CharacteristicValue
                                                                 .Where(c => sequenceId == c.SequenceId && sequenceCharacteristicLinkIds.Contains(c.CharacteristicLinkId))
                                                                 .ToDictionary(ct => ct.CharacteristicLinkId, ct => ct.Value);

                    allCharacteristics.Add(sequenceId, characteristics);

                    if (characteristics.Count < sequenceCharacteristicLinkIds.Length)
                    {
                        Chain sequence = commonSequenceRepository.GetLibiadaChain(sequenceId);

                        foreach (short sequenceCharacteristicLinkId in sequenceCharacteristicLinkIds)
                        {
                            if (!characteristics.ContainsKey(sequenceCharacteristicLinkId))
                            {
                                LinkedFullCalculator calculator = calculators[sequenceCharacteristicLinkId];
                                double characteristicValue      = calculator.Calculate(sequence);
                                var    characteristic           = new CharacteristicValue
                                {
                                    SequenceId           = sequenceId,
                                    CharacteristicLinkId = sequenceCharacteristicLinkId,
                                    Value = characteristicValue
                                };

                                characteristics.Add(sequenceCharacteristicLinkId, characteristicValue);
                                newCharacteristics.Add(characteristic);
                            }
                        }
                    }
                }

                var characteristicRepository = new CharacteristicRepository(db);
                characteristicRepository.TrySaveCharacteristicsToDatabase(newCharacteristics);

                return(allCharacteristics);
            }
        }
        /// <summary>
        /// Calculates subsequences characteristics.
        /// </summary>
        /// <param name="characteristicIds">
        /// The ids of characteristic types, arrangement types and links as <see cref="FullCharacteristicLink"/>.
        /// </param>
        /// <param name="features">
        /// The  features ids of subsequences to extract.
        /// </param>
        /// <param name="parentSequenceId">
        /// The parent sequence id.
        /// </param>
        /// <param name="filters">
        /// Textual search filters for subsequences products.
        /// </param>
        /// <returns>
        /// The <see cref="T:SubsequenceData[]"/> .
        /// </returns>
        public static SubsequenceData[] CalculateSubsequencesCharacteristics(
            short[] characteristicIds,
            Feature[] features,
            long parentSequenceId,
            string[] filters = null)
        {
            Dictionary <long, Chain> sequences;

            long[]            subsequenceIds;
            SubsequenceData[] subsequenceData;
            Dictionary <long, Dictionary <short, double> > characteristics;
            var calculators        = new IFullCalculator[characteristicIds.Length];
            var links              = new Link[characteristicIds.Length];
            var newCharacteristics = new List <CharacteristicValue>();

            // creating local context to avoid memory overflow due to possibly big cache of characteristics
            using (var db = new LibiadaWebEntities())
            {
                var subsequenceExtractor = new SubsequenceExtractor(db);

                Subsequence[] subsequences = filters == null?
                                             subsequenceExtractor.GetSubsequences(parentSequenceId, features) :
                                                 subsequenceExtractor.GetSubsequences(parentSequenceId, features, filters);

                subsequenceData = subsequences.Select(s => new SubsequenceData(s)).ToArray();

                // converting to libiada sequences
                subsequenceIds = subsequences.Select(s => s.Id).ToArray();



                characteristics = db.CharacteristicValue
                                  .Where(c => characteristicIds.Contains(c.CharacteristicLinkId) && subsequenceIds.Contains(c.SequenceId))
                                  .ToArray()
                                  .GroupBy(c => c.SequenceId)
                                  .ToDictionary(c => c.Key, c => c.ToDictionary(ct => ct.CharacteristicLinkId, ct => ct.Value));
                if (characteristics.Count == subsequences.Length && characteristics.All(c => c.Value.Count == characteristicIds.Length))
                {
                    sequences = new Dictionary <long, Chain>();
                }
                else
                {
                    sequences = subsequenceExtractor.GetSubsequencesSequences(subsequences);
                }
            }

            var characteristicTypeLinkRepository = FullCharacteristicRepository.Instance;

            for (int k = 0; k < characteristicIds.Length; k++)
            {
                short characteristicLinkId        = characteristicIds[k];
                FullCharacteristic characteristic = characteristicTypeLinkRepository.GetCharacteristic(characteristicLinkId);
                calculators[k] = FullCalculatorsFactory.CreateCalculator(characteristic);
                links[k]       = characteristicTypeLinkRepository.GetLinkForCharacteristic(characteristicLinkId);
            }

            // cycle through subsequences
            for (int i = 0; i < subsequenceIds.Length; i++)
            {
                characteristics.TryGetValue(subsequenceIds[i], out Dictionary <short, double> sequenceDbCharacteristics);
                sequenceDbCharacteristics = sequenceDbCharacteristics ?? new Dictionary <short, double>();
                var values = new double[calculators.Length];

                // cycle through characteristics and notations
                for (int j = 0; j < calculators.Length; j++)
                {
                    short characteristicLinkId = characteristicIds[j];
                    if (!sequenceDbCharacteristics.TryGetValue(characteristicLinkId, out values[j]))
                    {
                        values[j] = calculators[j].Calculate(sequences[subsequenceIds[i]], links[j]);
                        var currentCharacteristic = new CharacteristicValue
                        {
                            SequenceId           = subsequenceIds[i],
                            CharacteristicLinkId = characteristicLinkId,
                            Value = values[j]
                        };

                        newCharacteristics.Add(currentCharacteristic);
                    }
                }

                subsequenceData[i].CharacteristicsValues = values;
            }

            using (var db = new LibiadaWebEntities())
            {
                // trying to save calculated characteristics to database
                var characteristicRepository = new CharacteristicRepository(db);
                characteristicRepository.TrySaveCharacteristicsToDatabase(newCharacteristics);
            }

            return(subsequenceData);
        }