private DecisionNode <TObj> trainID3Core(IEnumerable <Predicate <TObj> > patterns, ClassifiedSample <TObj> sample, IInformativityIndex <TObj> informativity)
        {
            if (!sample.Any())
            {
                throw new MLException("Empty sample");
            }

            var cls = sample.First().Value;

            if (sample.All(kvp => kvp.Value.Equals(cls)))
            {
                return(new LeafNode <TObj>(cls));
            }

            var pattern   = informativity.Max(patterns, sample);
            var negSample = new ClassifiedSample <TObj>();
            var posSample = new ClassifiedSample <TObj>();

            foreach (var pData in sample)
            {
                if (pattern(pData.Key))
                {
                    posSample.Add(pData.Key, pData.Value);
                }
                else
                {
                    negSample.Add(pData.Key, pData.Value);
                }
            }

            if (!negSample.Any() || !posSample.Any())
            {
                var majorClass = sample.GroupBy(pd => pd.Value)
                                 .Select(g => new KeyValuePair <Class, int>(g.Key, g.Count()))
                                 .OrderByDescending(c => c.Value)
                                 .First();
                return(new LeafNode <TObj>(majorClass.Key));
            }

            var node    = new InnerNode <TObj>(pattern);
            var negNode = trainID3Core(patterns, negSample, informativity);
            var posNode = trainID3Core(patterns, posSample, informativity);

            node.SetNegativeNode(negNode);
            node.SetPositiveNode(posNode);

            return(node);
        }