public ColumnStats GetDomainValueBarChart(int domainColumn, int dataColumn, int denominatorColumn, StatTypes statType)
        {
            CheckState();
            var stats = new ColumnStats();
            stats.Computed = true;
            var domainIdList = new Dictionary<string, int>();
            stats.DomainValues = GetDomainValues(domainColumn);
            stats.Buckets = stats.DomainValues.Length;
            stats.Histogram = new double[stats.Buckets];
            stats.TargetColumn = dataColumn;
            stats.DomainColumn = domainColumn;
            stats.DomainStatType = statType;
            stats.DemoninatorColumn = denominatorColumn;

            var sortLists = new List<double>[stats.Buckets];
            var min = new double[stats.Buckets];
            var max = new double[stats.Buckets];
            var sum = new double[stats.Buckets];
            var denominatorSum = new double[stats.Buckets];
            stats.Selected = new bool[stats.Buckets];

            for (var i = 0; i < stats.Buckets; i++)
            {
                domainIdList.Add(stats.DomainValues[i], i);
                max[i] = double.MinValue;
                min[i] = double.MaxValue;
                sortLists[i] = new List<double>();
                stats.Selected[i] = false;
            }

            table.Lock();

            // First Pass - Basic statistics..
            foreach (var row in table.Rows)
            {
                var selected = Filters.Count == 0;
                var firstFilter = true;
                foreach (var fgt in Filters)
                {
                    var filter = fgt.Stats;
                    if (filter.Computed && (selected || firstFilter))
                    {
                        if (filter.IsSelected(row))
                        {
                            selected = true;
                        }
                        else
                        {
                            selected = false;
                        }
                        firstFilter = false;

                    }
                }
                if (selected)
                {
                    try
                    {
                        if (dataColumn > -1)
                        {
                            var domainID = domainIdList[row[domainColumn]];

                            var sucsess = false;
                            double val = 0;
                            sucsess = double.TryParse(row[dataColumn], out val);
                            if (sucsess)
                            {
                                if (val > max[domainID])
                                {
                                    max[domainID] = val;
                                }

                                if (val < min[domainID])
                                {
                                    min[domainID] = val;
                                }
                                stats.Count++;
                                stats.Sum += val;
                                sum[domainID] += val;
                                sortLists[domainID].Add(val);
                            }
                            else if (statType == StatTypes.Count) // && domainColumn == dataColumn)
                            {
                                sortLists[domainID].Add(0);
                            }

                            if (statType == StatTypes.Ratio && denominatorColumn != -1)
                            {
                                sucsess = double.TryParse(row[denominatorColumn], out val);
                                if (sucsess)
                                {
                                    denominatorSum[domainID] += val;
                                }
                            }

                        }
                    }
                    catch
                    {
                    }
                }
            }
            table.Unlock();

            //2nd pass does not use the table anymore. working from sortList

            for (var i = 0; i < stats.Buckets; i++)
            {
                if (sortLists[i].Count > 0)
                {
                    var average = sum[i] / sortLists[i].Count;
                    double median = 0;
                    sortLists[i].Sort();
                    var midPoint = Math.Max(0, (sortLists[i].Count / 2) - 1);

                    try
                    {
                        if (sortLists[i].Count % 2 == 0)
                        {
                            median = (sortLists[i][midPoint] + sortLists[i][midPoint + 1]) / 2;
                        }
                        else
                        {
                            median = (sortLists[i][midPoint]);
                        }
                    }
                    catch
                    {
                    }
                    switch (stats.DomainStatType)
                    {
                        case StatTypes.Count:
                            stats.Histogram[i] = sortLists[i].Count;
                            break;
                        case StatTypes.Average:
                            stats.Histogram[i] = average;
                            break;
                        case StatTypes.Median:
                            stats.Histogram[i] = median;
                            break;
                        case StatTypes.Sum:
                            stats.Histogram[i] = sum[i];
                            break;
                        case StatTypes.Min:
                            stats.Histogram[i] = min[i];
                            break;
                        case StatTypes.Max:
                            stats.Histogram[i] = max[i];
                            break;
                        case StatTypes.Ratio:
                            stats.Histogram[i] = sum[i] / denominatorSum[i];
                            break;
                        default:
                            break;
                    }
                }
            }

            foreach (var j in stats.Histogram)
            {
                if (stats.HistogramMax < j)
                {
                    stats.HistogramMax = j;
                }
            }

            return stats;
        }
        public ColumnStats GetDateHistogram(int column, DateFilter type)
        {
            var stats = new ColumnStats();
            stats.Computed = true;
            stats.Buckets = 20;
            stats.TargetColumn = column;
            stats.DomainColumn = -1;
            table.Lock();
            stats.Max = double.MinValue;
            stats.Min = double.MaxValue;

            switch (type)
            {
                case DateFilter.Year:
                    ComputeDateRange(column, -1, out stats.BeginDate, out stats.EndDate);
                    stats.BeginDate = stats.BeginDate.Date;
                    stats.EndDate = stats.EndDate.Date;
                    var ts = stats.EndDate - stats.BeginDate;

                    stats.Buckets = (int)(ts.TotalDays + 1);
                    stats.DomainValues = new string[stats.Buckets];
                    var now = stats.BeginDate;
                    for(var i = 0; i < stats.Buckets; i++)
                    {
                        stats.DomainValues[i]=now.ToShortDateString();
                        now = now.AddDays(1);
                    }

                    break;
                case DateFilter.Month:
                    stats.Buckets = 12;
                    stats.DomainValues = new string[stats.Buckets];

                    for(var i = 0; i < stats.Buckets; i++)
                    {
                        stats.DomainValues[i] = UiTools.GetMonthName(i, false);
                    }
                    break;
                case DateFilter.Day:
                    stats.Buckets = 31;
                    stats.DomainValues = new string[stats.Buckets];

                    for(var i = 0; i < stats.Buckets; i++)
                    {
                        stats.DomainValues[i] = (i+1).ToString();
                    }
                    break;
                case DateFilter.DayOfWeek:
                    stats.Buckets = 7;
                    stats.DomainValues = new string[stats.Buckets];

                    for(var i = 0; i < stats.Buckets; i++)
                    {
                        stats.DomainValues[i] = UiTools.GetDayName(i, false);
                    }
                    break;
                case DateFilter.DayOfYear:
                    stats.Buckets = 366;
                    stats.DomainValues = new string[stats.Buckets];
                    for (var i = 0; i < stats.Buckets; i++)
                    {
                        stats.DomainValues[i] = (i + 1).ToString();
                    }
                    break;
                case DateFilter.Hour:
                    stats.Buckets = 24;
                    stats.DomainValues = new string[stats.Buckets];

                    for(var i = 0; i < stats.Buckets; i++)
                    {
                        stats.DomainValues[i] = UiTools.GetHourName(i);
                    }
                    break;
                case DateFilter.Minute:
                    stats.Buckets = 60;
                    stats.DomainValues = new string[stats.Buckets];

                    for(var i = 0; i < stats.Buckets; i++)
                    {
                        stats.DomainValues[i] = i.ToString();
                    }
                    break;
                case DateFilter.Second:
                    stats.Buckets = 60;
                    stats.DomainValues = new string[stats.Buckets];

                    for(var i = 0; i < stats.Buckets; i++)
                    {
                        stats.DomainValues[i] = i.ToString();
                    }
                    break;
                default:
                    break;
            }

            stats.Histogram = new double[stats.Buckets];
            stats.BucketWidth = 1;
            stats.DateFilter = type;
            // First Pass - Basic statistics..
            foreach (var row in table.Rows)
            {
                var selected = Filters.Count == 0;
                var firstFilter = true;
                foreach (var fgt in Filters)
                {
                    var filter = fgt.Stats;
                    if (filter.Computed && (selected || firstFilter))
                    {
                        if (filter.IsSelected(row))
                        {
                            selected = true;
                        }
                        else
                        {
                            selected = false;
                        }
                        firstFilter = false;

                    }
                }
                if (selected)
                {
                    try
                    {
                        if (column > -1)
                        {

                            var date = ParseDate(row[column]);
                            var bucket = 0;
                            switch (type)
                            {
                                case DateFilter.Year:
                                    bucket = (date.Date - stats.BeginDate).Days;
                                    break;
                                case DateFilter.Month:
                                    bucket = date.Month - 1;
                                    break;
                                case DateFilter.Day:
                                    bucket = date.Day - 1;
                                    break;
                                case DateFilter.DayOfWeek:
                                    bucket = (int)date.DayOfWeek;
                                    break;
                                case DateFilter.DayOfYear:
                                    bucket = date.DayOfYear - 1;
                                    break;
                                case DateFilter.Hour:
                                    bucket = date.Hour;
                                    break;
                                case DateFilter.Minute:
                                    bucket = date.Minute;
                                    break;
                                case DateFilter.Second:
                                    bucket = date.Second;
                                    break;
                                default:
                                    break;
                            }

                            stats.Count++;
                            stats.Sum += 1;
                            stats.Histogram[bucket]++;
                        }

                    }
                    catch
                    {
                    }
                }
            }

            stats.Selected = new bool[stats.Buckets];
            for (var i = 0; i < stats.Buckets; i++)
            {
                stats.Selected[i] = false;
            }

            foreach (int max in stats.Histogram)
            {
                if (stats.HistogramMax < max)
                {
                    stats.HistogramMax = max;
                }
            }

            table.Unlock();

            return stats;
        }
        public Bitmap GetBarChartBitmap(ColumnStats stats)
        {
            int Chrome = 25;

            int height = 150;
            int count = Math.Min(MaxUnits, stats.Buckets);
            int border = 10;
            int colWidth = Math.Min(30, (int)(1000 / count));
            int width = count * colWidth;
            Width = width + 2 * border;
            Bitmap bmp = new Bitmap(Width, height + border * 2 + 20 + Chrome);
            Graphics g = Graphics.FromImage(bmp);
            g.Clear(Color.FromArgb(128, 5, 75, 35));

            bool anythingSelected = false;
            double selectedAmount = 0;
            double totalAmount = 0;

            if (stats.Selected != null)
            {
                for (int i = 0; i < stats.Buckets; i++)
                {
                    if (stats.Selected[i])
                    {
                        anythingSelected = true;
                        selectedAmount += stats.Histogram[i];
                    }
                    totalAmount += stats.Histogram[i];
                }
            }

            // Draw title
            string text = (stats.DomainColumn > -1 ? layer.Table.Header[stats.DomainColumn] + " : " : "" )+ layer.Table.Header[stats.TargetColumn] + " " + stats.DomainStatType.ToString() + ((stats.DomainStatType == StatTypes.Ratio) ? (" to " + layer.Table.Header[stats.DemoninatorColumn]) : "");

            title = text;

            if (anythingSelected && stats.DomainStatType != StatTypes.Ratio)
            {
                text += String.Format("       {0:p1} Selected", selectedAmount / totalAmount);
            }
            g.DrawString(text, UiTools.StandardGargantuan, Brushes.White, new PointF(border, 0));

            string sort = "AZ";

            switch (sortType)
            {
                case 0:
                    sort = "AZ";
                    break;
                case 1:
                    sort = "ZA";
                    break;
                case 2:
                    sort = "09";
                    break;
                case 3:
                    sort = "90";
                    break;
            }

            if (chartType == ChartTypes.BarChart)
            {
                g.DrawString(sort, UiTools.StandardLarge, Brushes.White, new PointF(Width - 25, 0));
            }

            System.Drawing.StringFormat drawFormat = new System.Drawing.StringFormat();
            drawFormat.FormatFlags = StringFormatFlags.NoWrap | StringFormatFlags.DirectionVertical;
            drawFormat.Alignment = StringAlignment.Near;
            drawFormat.LineAlignment = StringAlignment.Center;

            SolidBrush brush = new SolidBrush(Color.FromArgb(20, 128, 255));
            Brush selectedBrush = Brushes.Yellow;
            //Brushes.White;
            Pen pen = new Pen(Color.FromArgb(20, 128, 255));
            double logMax = Math.Log(stats.HistogramMax);

            int end = Math.Min(stats.Buckets, ScrollPosition + MaxUnits);

            if (stats.Histogram != null)
            {
                barHitTest = new Rectangle[stats.Buckets];
                for (int i = ScrollPosition; i < end; i++)
                {
                    int pos = i - ScrollPosition;

                    double val = stats.Histogram[i] / (stats.HistogramMax * 1.05);
                    if (val < 0)
                    {
                        val = 0;
                    }

                    barHitTest[i] = new Rectangle((int)(pos * colWidth) + border, border + Chrome, colWidth, (int)(height));
                    Rectangle rect = new Rectangle((int)(pos * colWidth) + border, (int)(height - (val * height)) + border + Chrome, colWidth, (int)(val * height));
                    if (stats.Selected[i])
                    {
                        g.FillRectangle(selectedBrush,rect);
                    }
                    else
                    {
                        g.FillRectangle(brush, rect);
                    }

                    if (stats.DomainColumn > -1 || (stats.DomainValues != null && stats.DomainValues.Length > pos))
                    {
                        g.DrawString(stats.DomainValues[i], UiTools.StandardLarge, Brushes.White, new RectangleF((pos * colWidth) + border, border + Chrome, colWidth, height), drawFormat);
                    }
                }

                ScrollBarVisible = false;
                if (MaxUnits < stats.Buckets)
                {
                    int ScrollAreaWidth = Width - (2 * border);
                    // Scroll bars are needed
                    ScrollBarVisible = true;

                    int scrollWidth = (int)((double)MaxUnits / (double)stats.Buckets * ScrollAreaWidth) +2;

                    int scrollStart = (int)((double)ScrollPosition/ (double)stats.Buckets * ScrollAreaWidth);

                    scrollUnitPixelRatio = (double)ScrollAreaWidth / (double)stats.Buckets;

                    g.DrawLine(Pens.White, new Point(border, height + 22 + Chrome), new Point(border + ScrollAreaWidth, height + 22 + Chrome));

                    Rectangle rect = new Rectangle(border + scrollStart, height + 15 + Chrome, scrollWidth, 15);
                    g.FillRectangle(brush, rect);

                }

            }

            brush.Dispose();
            pen.Dispose();
            g.Flush();
            g.Dispose();

            return bmp;
        }
        public ColumnStats GetSingleColumnHistogram(int column)
        {
            var stats = new ColumnStats();
            stats.Computed = true;
            stats.Buckets = 20;
            stats.TargetColumn = column;
            stats.DomainColumn = -1;
            var sortList = new List<double>();
            table.Lock();
            stats.Max = double.MinValue;
            stats.Min = double.MaxValue;
            if (column > -1)
            {
                // First Pass - Basic statistics..
                foreach (var row in table.Rows)
                {
                    var selected = Filters.Count == 0;
                    var firstFilter = true;
                    foreach (var fgt in Filters)
                    {
                        var filter = fgt.Stats;
                        if (filter.Computed && (selected || firstFilter))
                        {
                            if (filter.IsSelected(row))
                            {
                                selected = true;
                            }
                            else
                            {
                                selected = false;
                            }
                            firstFilter = false;

                        }
                    }
                    if (selected)
                    {
                        try
                        {

                            var sucsess = false;
                            double val = 0;
                            sucsess = double.TryParse(row[column], out val);
                            if (sucsess)
                            {
                                if (val > stats.Max)
                                {
                                    stats.Max = val;
                                }

                                if (val < stats.Min)
                                {
                                    stats.Min = val;
                                }
                                stats.Count++;
                                stats.Sum += val;
                                sortList.Add(val);
                            }
                        }

                        catch
                        {
                        }
                    }
                }
            }
            table.Unlock();

            //2nd pass does not use the table anymore. working from sortList
            if (stats.Count > 0)
            {
                stats.Average = stats.Sum / stats.Count;
                sortList.Sort();
                var midPoint = stats.Count / 2;
                if (stats.Count % 2 == 0)
                {
                    stats.Median = (sortList[midPoint] + sortList[midPoint + 1]) / 2;
                }
                else
                {
                    stats.Median = (sortList[midPoint]);
                }

                stats.BucketWidth = (stats.Max - stats.Min) / stats.Buckets;

                stats.Histogram = new double[stats.Buckets];

                foreach (var v in sortList)
                {
                    var bucket = (int)((v - stats.Min) / stats.BucketWidth);
                    stats.Histogram[Math.Min(stats.Buckets - 1, bucket)]++;
                }

                stats.Selected = new bool[stats.Buckets];
                for (var i = 0; i < stats.Buckets; i++)
                {
                    stats.Selected[i] = false;
                }

                foreach (int max in stats.Histogram)
                {
                    if (stats.HistogramMax < max)
                    {
                        stats.HistogramMax = max;
                    }
                }
            }

            return stats;
        }
        public static Bitmap GetHistogramBitmap(ColumnStats stats)
        {
            Bitmap bmp = new Bitmap(stats.Buckets, 150);
            Graphics g = Graphics.FromImage(bmp);
            g.Clear(Color.FromArgb(128, 68, 82, 105));
            Pen pen = new Pen(Color.FromArgb(127, 137, 157));
            double logMax = Math.Log(stats.HistogramMax);
            if (stats.Histogram != null)
            {
                for (int i = 0; i < stats.Histogram.Length; i++)
                {
                    double height = Math.Log(stats.Histogram[i]) / logMax;
                    if (height < 0)
                    {
                        height = 0;
                    }

                    g.DrawLine(Pens.White, new System.Drawing.Point(i, 150), new System.Drawing.Point(i, (int)(150 - (height * 150))));
                }
            }
            pen.Dispose();
            g.Flush();
            g.Dispose();

            return bmp;
        }