public IEnumerable <IRating> RecommendSubjects(IRater rater, IEnumerable <ISubject> subjects, int take = -1, int skip = 0)
        {
            var cachedResults     = new ConcurrentBag <IRating>();
            var notCachedSubjects = new ConcurrentBag <ISubject>();
            var options           = new ParallelOptions()
            {
                MaxDegreeOfParallelism = Environment.ProcessorCount
            };

            Parallel.ForEach(subjects, options, subject =>
            {
                var key   = CreateCacheKey(rater, subject);
                var value = 0D;
                if (_cache.TryGetValue(key, out value))
                {
                    var ranking = new SimpleRating(rater, subject, value);
                    cachedResults.Add(ranking);
                }
                else
                {
                    notCachedSubjects.Add(subject);
                }
            });

            var partial = _algorithm.RecommendSubjects(rater, notCachedSubjects);

            var results = partial.ToList();

            results.AddRange(cachedResults);

            var sorted = results.OrderByDescending(p => p.Value).AsEnumerable();

            if (skip > 0)
            {
                sorted = sorted.Skip(skip);
            }
            if (take > 0)
            {
                sorted = sorted.Take(take);
            }

            return(sorted);
        }
        public override double CalculateError(IRecommendation recommendation, IEnumerable <IRating> ratings, IEnumerable <IRater> raters, IEnumerable <ISubject> subjects)
        {
            double error = 0.0;

            foreach (var rater in raters)
            {
                var topList = recommendation.RecommendSubjects(rater, subjects, 0, 10);

                foreach (var result in topList)
                {
                    if (!ratings.Any(r => r.Subject == result.Subject && r.Rater == result.Rater))
                    {
                        error += 1.0;
                    }
                }
            }

            return(error);
        }