//Copyright (C) Troy Magennis

using System;
using System.Collections;   
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using System.Xml.Linq;
using SampleSupport;
using QuerySamples;
using System.Xml;
using System.Data.SqlClient;

namespace SampleQueries
{
    [Title("Statistical Operators")]
    [Prefix("Stats_")]
    public class StatisticOperatorSamples : SampleHarness
    {
        public IEnumerable<int> GetRandomSet(int min, int max, int count)
        {
            Random r = new Random();

            for (int i = 0; i < count; i++)
                yield return r.Next(min, max);
        }

        [Category("Mode")]
        [Title("Stat Sample")]
        [Description("")]
        [LinkedClass("StatisticalExtensions")]
        public void Stats_Sample()
        {
            int[] values = GetRandomSet(0, 100, 100).ToArray<int>();
            var modes = values.Mode();

            Console.WriteLine("Number of modes = {0}", modes.Count());
            
            
            foreach (var m in modes) {
                Console.WriteLine("Mode = {0}", m);
            }

            Console.WriteLine("Average = {0}", values.Average());
            Console.WriteLine("Median = {0}", values.Median());
            Console.WriteLine("Standard Deviation = {0}", values.StandardDeviation());
            Console.WriteLine("Range = {0} to {1}", values.Range().LowBound, values.Range().HighBound);
        }


        [Category("Mode")]
        [Title("Mode Sample 1")]
        [Description("Returns the Mode(s) of a set of values.")]
        [LinkedClass("StatisticalExtensions")]
        public void Stats_ModeTest1()
        {
            int[] values = GetRandomSet(0, 100, 100).ToArray<int>();
            var modes = values.Mode();

            var q = Enumerable.Empty<int>().Mode();

            Console.WriteLine("Number of modes = {0}", modes.Count());
            foreach (var m in modes)
            {
                Console.WriteLine("Mode = {0}", m);
            }
        }


        [Category("Median")]
        [Title("Median Sample 1")]
        [Description("Returns the Median of a set of int values.")]
        [LinkedClass("StatisticalExtensions")]
        public void Stats_MedianTest1()
        {
            Console.WriteLine("Median = {0}", GetRandomSet(0, 100, 100).Median());

            Console.WriteLine("Median = {0}", GetRandomSet(0, 100, 99).Median());
        }

        [Category("Standard Deviation")]
        [Title("SD Sample 1")]
        [Description("Returns the SD of a set of int values.")]
        [LinkedClass("StatisticalExtensions")]
        public void Stats_SDTest1()
        {
            int[] data = new int[] { 183,163,152,157,157 };
            Console.WriteLine("SD = {0}", data.StandardDeviation());
        }

        [Category("Variance")]
        [Title("Variance")]
        [Description("Returns the naive variance of a set of int values.")]
        [LinkedClass("StatisticalExtensions")]
        public void Stats_Variance()
        {
            int[] data = new int[] { 4, 7, 13, 16 };
            Console.WriteLine("Variance = {0}", data.AsParallel().Variance());
        }
    }

    public static class StatisticalExtensions
    {
        public static IEnumerable<int> RandomData(int lower, int upper, int count)
        {
            Random r = new Random();

            for (int i = 0; i < count; i++)
                yield return r.Next(lower, upper);
        }

        public class RangeData<T>
        {
            public T LowBound { get; set; }
            public T HighBound { get; set; }
        }

        public static RangeData<T> Range<T>(this IEnumerable<T> source)
        {
            if (source == null)
                throw new ArgumentNullException("source");

            var sorted = from element in source
                         orderby element ascending
                         select element;

            return new RangeData<T>
            {
                LowBound = sorted.FirstOrDefault(),
                HighBound = sorted.LastOrDefault()
            };
        }

        public static RangeData<T> Range<T>(this ParallelQuery<T> source)
        {
            if (source == null)
                throw new ArgumentNullException("source");

            var sorted = from element in source
                         orderby element ascending
                         select element;

            return new RangeData<T>
            {
                LowBound = sorted.FirstOrDefault(),
                HighBound = sorted.LastOrDefault()
            };
        }
        
        public static IEnumerable<T> Mode<T>(this IEnumerable<T> source)
        {
            if (source == null)
                throw new ArgumentNullException("source");

            // group the elements, and determine each count
            var groupings = from element in source
                            group element by element into groups
                            select new { Key = groups.Key, Count = groups.Count() };

            // return the entries with the highest count
            var modes = from grp in groupings
                        where grp.Count == groupings.Max(m => m.Count)
                        select grp.Key;

            return modes;
        }

        public static double Median(this IEnumerable<int> source)
        {
            if (source == null)
                throw new ArgumentNullException("source");

            double median = 0;

            // first, sort the entries in ascending order
            var sorted = from element in source
                         orderby element ascending
                         select element;
            
            // Take the middle element if there is an
            // odd number of entries, otherwise average
            // the middle pair of entries.
            if (sorted.Count() % 2 == 0)
                median = source
                         .Skip(sorted.Count() / 2 - 1)
                         .Take(2)
                         .Average();
            else
                median = source
                         .ElementAt(sorted.Count() / 2);

            return median;
        }

        public static double Median<T>(
            this IEnumerable<T> source,
            Func<T, int> selector)
        {
            return Median(
                Enumerable.Select(source, selector));
        }
    }
}