public double Apply(NumericVariable numericVariable, MembershipFunction msf)
        {
            if (msf.Count == 0)
                return (numericVariable.MaxValue - numericVariable.MinValue) / 2.0;
            if (msf[0].Y > 0 && msf[0].X > numericVariable.MinValue)
            {
                msf.Add(numericVariable.MinValue, msf[0].Y);
            }
            if (msf[msf.Count - 1].Y > 0 && msf[msf.Count - 1].X < numericVariable.MaxValue)
            {
                msf.Add(numericVariable.MaxValue, msf[msf.Count - 1].Y);
            }

            var numerator = 0d;
            var denominator = 0d;
            for (var i = 0; i < msf.Count - 1; i++)
            {
                var value1 = msf[i];
                var value2 = msf[i + 1];

                var min = Math.Min(value1.Y, value2.Y);
                var max = Math.Max(value1.Y, value2.Y);
                
                var line = new LineSegment(value1, value2);
                var m = line.Gradient.Value;
                var b = line.B.Value;
                numerator += ((m / 3.0) * (Math.Pow(value2.X, 3) - Math.Pow(value1.X, 3))) + ((b / 2.0) * (Math.Pow(value2.X, 2) - Math.Pow(value1.X, 2)));
                denominator += ((max + min) / 2.0) * (value2.X - value1.X);

            }
            return numerator / denominator;
        }
        /// <summary>
        /// Mamdani-implication
        /// </summary>
        /// <param name="msf"></param>
        /// <param name="minValue"></param>
        /// <returns></returns>
        public MembershipFunction Apply(MembershipFunction msf, double minValue)
        {
            var result = new MembershipFunction();

            var count = msf.Count;

            if(count == 0)
                return msf;

            if (count == 1)
            {
                foreach (var point in msf)
                    result.Add(point.Key, Math.Min(point.Value, minValue));
            }
            else
            {
                for (var i = 0; i < count - 1; i++)
                {
                    var key = msf.Keys[i];
                    var nextKey = msf.Keys[i + 1];
                    var value = msf.Values[i];
                    var nextValue = msf.Values[i + 1];

                    if (value <= minValue)
                    {
                        result.Add(key, value);
                        if (nextValue > minValue)
                        {
                            var m = (nextValue - value)/(nextKey - key);

                            var newKey = key + minValue/m;
                            if(!result.Keys.Contains(newKey))
                                result.Add(newKey, minValue);
                        }
                    }
                    else if (nextValue < minValue)
                    {
                        var m = (nextValue - value)/(nextKey - key);

                        var newKey = key + (minValue - value)/m;

                        result.Add(newKey, minValue);
                    }
                }
                if (msf.Values[count - 1] <= minValue)
                {
                    result.Add(msf.Keys[count - 1], msf.Values[count - 1]);
                }
            }

            return result.ClearUp();
        }
        private MembershipFunction combine(MembershipFunction first, MembershipFunction second)
        {
            if (!first.Any())
                return second;
            if (!second.Any())
                return first;


            var scanPoints = new List<double>();
            scanPoints.AddRange(first.Keys);
            foreach (var key in second.Keys.Where(key => !scanPoints.Contains(key)))
                scanPoints.Add(key);
            scanPoints.Sort();



            var result = new MembershipFunction();
            foreach (var scanPoint in scanPoints)
            {
                var valueFirst = first.Apply(scanPoint);
                var valueSecond = second.Apply(scanPoint);

                result.Add(scanPoint, valueFirst + valueSecond);
            }

            return result.ClearUp();
        }
        public MembershipFunction Apply(MembershipFunction msf, double value)
        {
            var result = new MembershipFunction();

            foreach (var point in msf)
            {
                result.Add(point.Key, value*point.Value);
            }

            return result;
        }
        private static MembershipFunction combine(MembershipFunction first, MembershipFunction second)
        {
            if (!first.Any())
                return second;
            if (!second.Any())
                return first;
            
            var scanPoints = getScanPoints(first, second);
            
            var result = new MembershipFunction();
            for (var i = 0; i < scanPoints.Count; i++)
            {
                var scanPoint = scanPoints[i];

                var valueFirst = first.Apply(scanPoint);
                var valueSecond = second.Apply(scanPoint);

                result.Add(scanPoint, Math.Max(valueFirst, valueSecond));
                
                // Check if there is an intersection between this scan point and the next
                // and add it to the list of scan points:
                var nextKeyFirst = getNextKey(first, scanPoint);
                var nextKeySecond = getNextKey(second, scanPoint);
                if (nextKeyFirst.HasValue && nextKeySecond.HasValue)
                {
                    var nextValueFirst = first[nextKeyFirst.Value];
                    var nextValueSecond = second[nextKeySecond.Value];

                    if (valueFirst >= valueSecond && nextValueFirst < nextValueSecond ||
                        valueFirst < valueSecond && nextValueFirst >= nextValueSecond)
                    {
                        var lineFirst = new LineSegment(new Point(scanPoint, valueFirst),
                            new Point(nextKeyFirst.Value, nextValueFirst));
                        var lineSecond = new LineSegment(new Point(scanPoint, valueSecond),
                            new Point(nextKeySecond.Value, nextValueSecond));

                        var intersection = lineFirst.Intersect(lineSecond);
                        if (intersection != null && !scanPoints.Contains(intersection.X) && intersection.X > scanPoint)
                            scanPoints.Insert(i + 1, intersection.X);
                    }
                }
            }

            return result.ClearUp();
        }